Transaction

80E3F6B6F6C85A…1551AC6AECA2

Block 77,250 · index 0 · indexed

Summary

Hash
80E3F6B6F6C85A089B4026ABD91299EA523E79E32A6A5CE234181551AC6AECA2
Block
77,250
Size
39749 bytes
Gas used
53,549,672 / 64,259,571
Fee
64260ugnot
Status
success

Messages

#1AddPackagegno.land/r/gnoswap/gns24 arguments

Arguments · 24

  1. #1gns
  2. #2README.md
  3. #3# GNS GnoSwap governance and utility token. ## Overview GNS is the native governance token of GnoSwap, featuring a deflationary emission schedule with halvings every 2 years over 12 years total. ## Token Economics - **Symbol**: GNS - **Decimals**: 6 - **Max Supply**: 1,000,000,000 GNS - **Initial Mint**: 100,000,000 GNS, pre-minted to the configured `ADMIN` role address during GNS realm initialization - **Total Emission**: 900,000,000 GNS over 12 years ## Emission Schedule | Years | Annual Emission | Rate | | ----- | --------------- | ----- | | 1-2 | 225,000,000 | 100% | | 3-4 | 112,500,000 | 50% | | 5-6 | 56,250,000 | 25% | | 7-8 | 28,125,000 | 12.5% | | 9-12 | 14,062,500 | 6.25% | ## Core Functions ### `Transfer` Transfers tokens between addresses. ### `TransferFrom` Transfers with allowance. ### `Approve` Approves spending allowance. ### `InitEmissionState` Initializes the emission schedule with start height and timestamp. Sets up the 12-year emission schedule with halving periods. Only callable by emission contract. ### `MintGns` Mints new tokens per emission schedule. Only callable by emission contract. Calculates tokens to mint based on elapsed time and updates halving year state. ## Usage These snippets call the GNS realm from a realm function with a current `cur` token. Import the package and qualify its function names in integrating code. ```go // Transfer tokens Transfer(cross(cur), to, amount) // Approve and transfer Approve(cross(cur), spender, amount) TransferFrom(cross(cur), from, to, amount) // Mint per emission schedule (called by emission contract) MintGns(cross(cur), recipientAddress) ``` ## Distribution See [emission contract](../emission) for distribution details.
  4. #4consts.gno
  5. #5package gns const ( SECONDS_IN_YEAR = 31536000 HALVING_START_YEAR = int64(1) HALVING_END_YEAR = int64(12) ) // MUST BE IMMUTABLE, DO NOT MODIFY. // Annual halving amount - maximum issuance per year var halvingAmountsPerYear = [HALVING_END_YEAR]int64{ 18_750_000_000_000 * 12, // Year 1: 225000000000000 18_750_000_000_000 * 12, // Year 2: 225000000000000 9_375_000_000_000 * 12, // Year 3: 112500000000000 9_375_000_000_000 * 12, // Year 4: 112500000000000 4_687_500_000_000 * 12, // Year 5: 56250000000000 4_687_500_000_000 * 12, // Year 6: 56250000000000 2_343_750_000_000 * 12, // Year 7: 28125000000000 2_343_750_000_000 * 12, // Year 8: 28125000000000 1_171_875_000_000 * 12, // Year 9: 14062500000000 1_171_875_000_000 * 12, // Year 10: 14062500000000 1_171_875_000_000 * 12, // Year 11: 14062500000000 1_171_875_000_000 * 12, // Year 12: 14062500000000 }
  6. #6doc.gno
  7. #7// Package gns implements the GNS governance and utility token for GnoSwap. // // GNS is a GRC20-compliant token with a deflationary emission schedule. // The emission follows a 12-year schedule with halving every 2 years: // - Years 1-2: 225,000,000 GNS per year (100%) // - Years 3-4: 112,500,000 GNS per year (50%) // - Years 5-6: 56,250,000 GNS per year (25%) // - Years 7-8: 28,125,000 GNS per year (12.5%) // - Years 9-12: 14,062,500 GNS per year (6.25%) // // Token Economics: // - Maximum Supply: 1,000,000,000 GNS // - Initial Mint: 100,000,000 GNS // - Total Emission: 900,000,000 GNS // // Key Functions: // - InitEmissionState: Initializes emission schedule (emission contract only) // - MintGns: Mints tokens per emission schedule (emission contract only) // - Transfer/TransferFrom/Approve: Standard GRC20 operations // // The emission state tracks accumulated and remaining amounts per halving year, // ensuring precise token distribution according to the schedule. package gns
  8. #8emission.gno
  9. #9package gns import ( "chain" "time" "gno.land/p/gnoswap/utils/v1" "gno.land/r/gnoswap/access/v1" ) // InitEmissionState initializes the emission schedule. // It creates the 12-year schedule with two-year halving periods. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - createdHeight: block height when the schedule is initialized // - startTimestamp: Unix timestamp when emission begins // // Only callable by the emission contract. func InitEmissionState(cur realm, createdHeight int64, startTimestamp int64) { previousRealm := cur.Previous() caller := previousRealm.Address() access.AssertIsEmission(caller) emissionState = NewEmissionState(createdHeight, startTimestamp) chain.Emit( "InitEmissionState", "prevAddr", caller.String(), "prevRealm", previousRealm.PkgPath(), "height", utils.FormatInt(createdHeight), "timestamp", utils.FormatInt(time.Now().Unix()), "startTimestamp", utils.FormatInt(emissionState.getStartTimestamp()), "endTimestamp", utils.FormatInt(emissionState.getEndTimestamp()), ) }
  10. #10emission_state.gno
  11. #11package gns import ( "chain/runtime" "time" gnsmath "gno.land/p/gnoswap/gnsmath/v1" ufmt "gno.land/p/nt/ufmt/v0" ) var emissionState *EmissionState func init() { emissionState = NewEmissionState(0, 0) } // EmissionState manages emission state and halving data. // Tracks emission timing, status, and halving year information for 12-year schedule. type EmissionState struct { createdHeight int64 startTimestamp int64 endTimestamp int64 halvingData *HalvingData } // isInitialized returns true if emission state has been initialized with valid height and timestamp. func (e *EmissionState) isInitialized() bool { return e.createdHeight != 0 && e.startTimestamp != 0 } // isActive returns true if emission is currently active at the given timestamp. // Returns false if not initialized or timestamp is outside emission period. func (e *EmissionState) isActive(timestamp int64) bool { if !e.isInitialized() { return false } if e.startTimestamp > timestamp { return false } if e.endTimestamp < timestamp { return false } return true } // isEnded returns true if emission has ended at the given timestamp. func (e *EmissionState) isEnded(timestamp int64) bool { return e.endTimestamp < timestamp } // getCurrentYear returns the halving year (1-12) for the given timestamp, or 0 if outside emission period. func (e *EmissionState) getCurrentYear(timestamp int64) int64 { if timestamp < e.startTimestamp { return 0 } if timestamp > e.endTimestamp { return 0 } year := (timestamp - e.startTimestamp) / SECONDS_IN_YEAR return year + 1 } // getCreatedHeight returns the blockchain height when emission started. func (e *EmissionState) getCreatedHeight() int64 { return e.createdHeight } // getStartTimestamp returns the timestamp when emission started. func (e *EmissionState) getStartTimestamp() int64 { return e.startTimestamp } // getEndTimestamp returns the timestamp when emission ends. func (e *EmissionState) getEndTimestamp() int64 { return e.endTimestamp } // getHalvingData returns the halving data containing emission schedule details. func (e *EmissionState) getHalvingData() *HalvingData { return e.halvingData } // getHalvingYearStartTimestamp returns the start timestamp for the specified halving year. func (e *EmissionState) getHalvingYearStartTimestamp(year int64) int64 { return e.halvingData.getStartTimestamp(year) } // getHalvingYearEndTimestamp returns the end timestamp for the specified halving year. func (e *EmissionState) getHalvingYearEndTimestamp(year int64) int64 { return e.halvingData.getEndTimestamp(year) } // getHalvingYearAmountPerSecond returns the emission rate per second for the specified halving year. func (e *EmissionState) getHalvingYearAmountPerSecond(year int64) int64 { return e.halvingData.getAmountPerSecond(year) } // getHalvingYearMintedAmount returns the minted emission amount for the specified halving year. func (e *EmissionState) getHalvingYearMintedAmount(year int64) int64 { return e.halvingData.getMintedAmount(year) } // getHalvingYearLeftAmount returns the remaining emission amount for the specified halving year. func (e *EmissionState) getHalvingYearLeftAmount(year int64) int64 { return e.halvingData.getLeftAmount(year) } // addHalvingYearMintedAmount adds to the minted emission amount for the specified halving year. // Returns error if year is invalid (0 or outside 1-12 range). func (e *EmissionState) addHalvingYearMintedAmount(year int64, amount int64) error { if year == 0 { return makeErrorWithDetails(errInvalidYear, ufmt.Sprintf("year: %d", year)) } mintedAmount := e.halvingData.getMintedAmount(year) mintedAmount = gnsmath.SafeAddInt64(mintedAmount, amount) return e.halvingData.setMintedAmount(year, mintedAmount) } // subHalvingYearLeftAmount subtracts from the remaining emission amount for the specified halving year. // Returns error if year is invalid (0 or outside 1-12 range). func (e *EmissionState) subHalvingYearLeftAmount(year int64, amount int64) error { if year == 0 { return makeErrorWithDetails(errInvalidYear, ufmt.Sprintf("year: %d", year)) } leftAmount := e.halvingData.getLeftAmount(year) leftAmount = gnsmath.SafeSubInt64(leftAmount, amount) return e.halvingData.setLeftAmount(year, leftAmount) } // Clone returns a deep copy of the emission state, including independent halving data. // // Returns: // - clone: independent EmissionState copy with the same schedule values. func (e *EmissionState) Clone() *EmissionState { return &EmissionState{ createdHeight: e.createdHeight, startTimestamp: e.startTimestamp, endTimestamp: e.endTimestamp, halvingData: e.halvingData.Clone(), } } // NewEmissionState creates an emission state and its 12-year halving schedule. // // Parameters: // - createdHeight: blockchain height recorded as the schedule's creation height. // - startTimestamp: Unix timestamp at which the first halving year begins. // // Returns: // - state: newly allocated emission state with an end timestamp and initialized halving data. func NewEmissionState(createdHeight int64, startTimestamp int64) *EmissionState { emissionEndTime := gnsmath.SafeAddInt64(startTimestamp, SECONDS_IN_YEAR*HALVING_END_YEAR-1) return &EmissionState{ createdHeight: createdHeight, startTimestamp: startTimestamp, endTimestamp: emissionEndTime, halvingData: NewHalvingData(startTimestamp), } } // getEmissionState returns the singleton emission state instance. func getEmissionState() *EmissionState { if emissionState == nil { emissionState = NewEmissionState(runtime.ChainHeight(), time.Now().Unix()) } return emissionState }
  12. #12errors.gno
  13. #13package gns import ( ufmt "gno.land/p/nt/ufmt/v0" ) const ( errInvalidYear = "[GNOSWAP-GNS-001] invalid year" errTooManyEmission = "[GNOSWAP-GNS-002] too many emission reward" errInvalidEmissionAmount = "[GNOSWAP-GNS-003] invalid emission amount" ) func makeErrorWithDetails(message string, details string) error { return ufmt.Errorf("%s || %s", message, details) }
  14. #14getter.gno
  15. #15package gns import ( "time" gnsmath "gno.land/p/gnoswap/gnsmath/v1" ) // GetMaxEmissionAmount returns the configured lifetime GNS emission cap in base units. // // Returns: // - maxAmount: maximum number of GNS base units available for emission func GetMaxEmissionAmount() int64 { return MAX_EMISSION_AMOUNT } // GetMaximumSupply returns the maximum possible GNS token supply in base units. // // Returns: // - maximumSupply: configured total supply cap, including the initial mint and emissions func GetMaximumSupply() int64 { return MAXIMUM_SUPPLY } // GetInitialMintAmount returns the initial GNS allocation minted before emissions. // // Returns: // - initialAmount: number of GNS base units in the initial mint func GetInitialMintAmount() int64 { return INITIAL_MINT_AMOUNT } // IsEmissionInitialized reports whether the emission state has non-zero creation // height and start timestamp. // // Returns: // - initialized: true when both initialization fields are set; false otherwise func IsEmissionInitialized() bool { return getEmissionState().isInitialized() } // IsEmissionActive reports whether emission is initialized and the current Unix // time lies within the inclusive emission schedule. // // Returns: // - active: true during the configured schedule, including its endpoints; false otherwise func IsEmissionActive() bool { return getEmissionState().isActive(time.Now().Unix()) } // IsEmissionEnded reports whether the current Unix time is after the emission end timestamp. // // Returns: // - ended: true after the configured schedule's end; false at or before that timestamp func IsEmissionEnded() bool { return getEmissionState().isEnded(time.Now().Unix()) } // GetHalvingYear returns the halving year containing timestamp. // // Parameters: // - timestamp: Unix timestamp to classify against the inclusive 12-year emission schedule // // Returns: // - year: halving year in [1, 12], or 0 when timestamp is before the start or after the end func GetHalvingYear(timestamp int64) int64 { return getEmissionState().getCurrentYear(timestamp) } // GetCurrentYear returns the halving year containing the current Unix time. // // Returns: // - year: current halving year in [1, 12], or 0 when the current time is outside the schedule func GetCurrentYear() int64 { return getEmissionState().getCurrentYear(time.Now().Unix()) } // GetEmissionAmountPerSecondInRange returns paired emission-rate change points // whose timestamps fall within the requested range. // // Parameters: // - fromTime: inclusive Unix timestamp lower bound // - toTime: inclusive Unix timestamp upper bound; a lower value yields nil slices // // Returns: // - timestamps: schedule timestamps in the range where the rate changes, including the post-end zero-rate point when applicable // - amounts: GNS base units emitted per second at each corresponding timestamp; same length and order as timestamps func GetEmissionAmountPerSecondInRange(fromTime, toTime int64) ([]int64, []int64) { if fromTime > toTime { return nil, nil } halvingData := getEmissionState().getHalvingData() halvingTimes := make([]int64, 0, HALVING_END_YEAR+1) halvingEmissions := make([]int64, 0, HALVING_END_YEAR+1) for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ { startTimestamp := halvingData.getStartTimestamp(year) if startTimestamp < fromTime { continue } if toTime < startTimestamp { break } halvingTimes = append(halvingTimes, startTimestamp) halvingEmissions = append(halvingEmissions, halvingData.getAmountPerSecond(year)) } emissionEndTimestamp := halvingData.getEndTimestamp(HALVING_END_YEAR) if fromTime <= emissionEndTimestamp && emissionEndTimestamp < toTime { halvingTimes = append(halvingTimes, gnsmath.SafeAddInt64(emissionEndTimestamp, 1)) halvingEmissions = append(halvingEmissions, 0) } return halvingTimes, halvingEmissions } // GetEmissionAmountPerSecondByTimestamp returns the configured GNS emission // rate for the halving year containing timestamp. // // Parameters: // - timestamp: Unix timestamp whose schedule rate is queried // // Returns: // - amount: GNS base units emitted per second for timestamp's halving year, or 0 outside the schedule func GetEmissionAmountPerSecondByTimestamp(timestamp int64) int64 { state := getEmissionState() year := state.getCurrentYear(timestamp) return state.getHalvingYearAmountPerSecond(year) } // GetEmissionLeftAmountByTimestamp returns the stored unminted allocation for // the halving year containing timestamp. // // Parameters: // - timestamp: Unix timestamp used to select a halving year // // Returns: // - amount: remaining GNS base units recorded for that halving year, or 0 outside the schedule func GetEmissionLeftAmountByTimestamp(timestamp int64) int64 { state := getEmissionState() year := state.getCurrentYear(timestamp) return state.getHalvingYearLeftAmount(year) } // GetEmissionAccumulatedAmountByTimestamp returns the stored minted allocation // for the halving year containing timestamp. // // Parameters: // - timestamp: Unix timestamp used to select a halving year // // Returns: // - amount: minted GNS base units recorded for that halving year, or 0 outside the schedule func GetEmissionAccumulatedAmountByTimestamp(timestamp int64) int64 { state := getEmissionState() year := state.getCurrentYear(timestamp) return state.getHalvingYearMintedAmount(year) } // GetHalvingYearStartTimestamp returns the inclusive start Unix timestamp for // a configured halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - timestamp: inclusive start timestamp for year, or 0 when year is invalid func GetHalvingYearStartTimestamp(year int64) int64 { halvingData := getEmissionState().getHalvingData() return halvingData.getStartTimestamp(year) } // GetHalvingYearEndTimestamp returns the inclusive end Unix timestamp for a // configured halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - timestamp: inclusive end timestamp for year, or 0 when year is invalid func GetHalvingYearEndTimestamp(year int64) int64 { halvingData := getEmissionState().getHalvingData() return halvingData.getEndTimestamp(year) } // GetHalvingYearMaxAmount returns the maximum GNS allocation for a halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - amount: maximum GNS base units allocated to year, or 0 when year is invalid func GetHalvingYearMaxAmount(year int64) int64 { halvingData := getEmissionState().getHalvingData() return halvingData.getMaxAmount(year) } // GetHalvingYearLeftAmount returns the remaining GNS allocation recorded for a // halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - amount: unminted GNS base units for year, or 0 when year is invalid func GetHalvingYearLeftAmount(year int64) int64 { halvingData := getEmissionState().getHalvingData() return halvingData.getLeftAmount(year) } // GetHalvingYearMintedAmount returns the minted GNS allocation recorded for a // halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - amount: minted GNS base units for year, or 0 when year is invalid func GetHalvingYearMintedAmount(year int64) int64 { halvingData := getEmissionState().getHalvingData() return halvingData.getMintedAmount(year) } // GetAmountPerSecondPerHalvingYear returns the configured GNS emission rate for // a halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - amountPerSecond: GNS base units emitted per second in year, or 0 when year is invalid func GetAmountPerSecondPerHalvingYear(year int64) int64 { halvingData := getEmissionState().getHalvingData() return halvingData.getAmountPerSecond(year) } // GetHalvingAmountsPerYear returns the total GNS allocation configured for a // halving year. // // Parameters: // - year: halving year number in [1, 12] // // Returns: // - amount: total GNS base units allocated to year, or 0 when year is invalid func GetHalvingAmountsPerYear(year int64) int64 { if validYear(year) != nil { return 0 } return halvingAmountsPerYear[year-1] } // GetEmissionCreatedHeight returns the blockchain height recorded when the // emission schedule was created. // // Returns: // - height: schedule creation height, or the uninitialized state's stored value func GetEmissionCreatedHeight() int64 { return getEmissionState().getCreatedHeight() } // GetEmissionStartTimestamp returns the inclusive Unix timestamp at which the // configured emission schedule begins. // // Returns: // - timestamp: schedule start timestamp func GetEmissionStartTimestamp() int64 { return getEmissionState().getStartTimestamp() } // GetEmissionEndTimestamp returns the inclusive Unix timestamp at which the // configured emission schedule ends. // // Returns: // - timestamp: schedule end timestamp func GetEmissionEndTimestamp() int64 { return getEmissionState().getEndTimestamp() } // GetHalvingYearInfo returns the configured halving interval containing timestamp. // // Parameters: // - timestamp: Unix timestamp to classify against the inclusive emission schedule // // Returns: // - year: halving year in [1, 12], or 0 outside the schedule // - startTimestamp: inclusive start timestamp of year, or 0 when timestamp is outside the schedule // - endTimestamp: inclusive end timestamp of year, or 0 when timestamp is outside the schedule func GetHalvingYearInfo(timestamp int64) (int64, int64, int64) { state := getEmissionState() year := state.getCurrentYear(timestamp) // If outside emission period, return 0 values if year == 0 { return 0, 0, 0 } // Use cached timestamps from HalvingData halvingData := state.getHalvingData() return year, halvingData.getStartTimestamp(year), halvingData.getEndTimestamp(year) } // GetHalvingInfo returns an independent copy of the configured 12-year halving schedule. // // Returns: // - halvingData: deep copy of schedule timestamps, allocations, minted amounts, remaining amounts, and rates func GetHalvingInfo() *HalvingData { return getEmissionState().getHalvingData().Clone() } // CalculateMintGnsAmount calculates the GNS base units allocated over an // inclusive timestamp range without mutating the live emission state. // // Parameters: // - fromTimestamp: inclusive Unix timestamp at which allocation begins // - toTimestamp: inclusive Unix timestamp at which allocation ends // // Returns: // - amount: GNS base units allocated in the overlapping schedule interval, or 0 for an invalid or non-overlapping range func CalculateMintGnsAmount(fromTimestamp, toTimestamp int64) int64 { state := getEmissionState().Clone() amountToMint, err := calculateAmountToMint(state, fromTimestamp, toTimestamp) if err != nil { return 0 } return amountToMint }
  16. #16gnomod.toml
  17. #17module = "gno.land/r/gnoswap/gns" gno = "0.9"
  18. #18gns.gno
  19. #19package gns import ( "chain" "strings" "time" "gno.land/p/nt/grc20/v0" "gno.land/p/nt/ufmt/v0" "gno.land/r/gnoswap/access/v1" "gno.land/r/nt/grc20reg/v0" "gno.land/p/gnoswap/gnsmath/v1" "gno.land/p/gnoswap/utils/v1" _ "gno.land/r/gnoswap/rbac/v1" ) const ( tokenID = 0 MAXIMUM_SUPPLY = int64(1_000_000_000_000_000) INITIAL_MINT_AMOUNT = int64(100_000_000_000_000) MAX_EMISSION_AMOUNT = int64(900_000_000_000_000) // MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT ) var ( token *grc20.Token privateLedger *grc20.PrivateLedger userTeller grc20.Teller leftEmissionAmount int64 // amount of GNS can be minted for emission mintedEmissionAmount int64 // amount of GNS that has been minted for emission lastMintedTimestamp int64 // last block time that gns was minted for emission ) func init(cur realm) { token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6, tokenID, cur) userTeller = privateLedger.CallerTeller() grc20reg.Register(cross(cur), token, "") // Initial amount set to 900_000_000_000_000 (MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT). // leftEmissionAmount will decrease as tokens are minted. setLeftEmissionAmount(MAX_EMISSION_AMOUNT) setMintedEmissionAmount(0) setLastMintedTimestamp(0) // Pre-mint the initial GNS supply. setupPreMint(cur) } // Name returns the name of the GNS token. // // Returns: // - name: token name func Name() string { return token.GetName() } // Symbol returns the symbol of the GNS token. // // Returns: // - symbol: token symbol func Symbol() string { return token.GetSymbol() } // Decimals returns the number of decimal places for GNS token. // // Returns: // - decimals: number of decimal places func Decimals() int { return token.GetDecimals() } // TotalSupply returns the total supply of GNS tokens in circulation. // // Returns: // - supply: total token supply func TotalSupply() int64 { return token.TotalSupply() } // KnownAccounts returns the number of addresses that have held GNS. // // Returns: // - count: number of known accounts func KnownAccounts() int { return token.KnownAccounts() } // BalanceOf returns the GNS balance of a specific address. // // Parameters: // - owner: address to check balance for // // Returns: // - balance: token balance func BalanceOf(owner address) int64 { return token.BalanceOf(owner) } // Allowance returns the amount of GNS that a spender is allowed to transfer from an owner. // // Parameters: // - owner: token owner address // - spender: spender address // // Returns: // - allowance: approved amount func Allowance(owner, spender address) int64 { return token.Allowance(owner, spender) } // MintGns mints new GNS tokens according to the emission schedule. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - address: recipient address for minted tokens // // Returns: // - amount: GNS minted for the current timestamp; zero when emission is // already processed for that timestamp or the schedule has ended // // Only callable by emission contract. // // Note: Halt check is performed by the caller (emission.MintAndDistributeGns) // to allow graceful handling. This function assumes caller has already verified // halt status before invoking. func MintGns(cur realm, address address) int64 { previousRealm := cur.Previous() caller := previousRealm.Address() access.AssertIsEmission(caller) lastGNSMintedTimestamp := LastMintedTimestamp() currentTime := time.Now().Unix() // Skip if already minted this timestamp or emission ended. if lastGNSMintedTimestamp == currentTime || lastGNSMintedTimestamp >= GetEmissionEndTimestamp() { return 0 } amountToMint, err := calculateAmountToMint(getEmissionState(), lastGNSMintedTimestamp+1, currentTime) if err != nil { panic(err) } err = validEmissionAmount(amountToMint) if err != nil { panic(err) } setLastMintedTimestamp(currentTime) setMintedEmissionAmount(gnsmath.SafeAddInt64(MintedEmissionAmount(), amountToMint)) setLeftEmissionAmount(gnsmath.SafeSubInt64(LeftEmissionAmount(), amountToMint)) err = privateLedger.Mint(address, amountToMint) if err != nil { panic(err.Error()) } chain.Emit( "MintGNS", "prevAddr", caller.String(), "prevRealm", previousRealm.PkgPath(), "mintedBlockTime", utils.FormatInt(currentTime), "mintedGNSAmount", utils.FormatInt(amountToMint), "accumMintedGNSAmount", utils.FormatInt(MintedEmissionAmount()), "accumLeftMintGNSAmount", utils.FormatInt(LeftEmissionAmount()), ) return amountToMint } // Transfer transfers GNS tokens from caller to recipient. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - to: recipient address // - amount: number of GNS base units to transfer func Transfer(cur realm, to address, amount int64) { checkErr(userTeller.Transfer(0, cur, to, amount)) } // Approve allows spender to transfer GNS tokens from caller's account. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - spender: address authorized to spend // - amount: maximum number of GNS base units spender can transfer func Approve(cur realm, spender address, amount int64) { checkErr(userTeller.Approve(0, cur, spender, amount)) } // TransferFrom transfers GNS tokens on behalf of owner. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - from: token owner address // - to: recipient address // - amount: number of GNS base units to transfer func TransferFrom(cur realm, from, to address, amount int64) { checkErr(userTeller.TransferFrom(0, cur, from, to, amount)) } // Render returns token information for web interface. // // Parameters: // - path: render path for specific views // // Returns: // - output: formatted token information func Render(path string) string { parts := strings.Split(path, "/") c := len(parts) switch { case path == "": return token.RenderHome() case c == 2 && parts[0] == "balance": owner := address(parts[1]) balance := token.BalanceOf(owner) return ufmt.Sprintf("%d\n", balance) default: return "404\n" } } // checkErr panics if error is not nil. func checkErr(err error) { if err != nil { panic(err.Error()) } } // calculateAmountToMint calculates and allocates GNS tokens to mint for given timestamp range. // This function has side effects: it updates the accumulated and remaining amounts // for each halving year in the emission state. func calculateAmountToMint(state *EmissionState, fromTimestamp, toTimestamp int64) (int64, error) { // Cache state to avoid repeated lookups endTimestamp := state.getEndTimestamp() if toTimestamp > endTimestamp { toTimestamp = endTimestamp } if fromTimestamp > toTimestamp { return 0, nil } startTimestamp := state.getStartTimestamp() if fromTimestamp < startTimestamp { fromTimestamp = startTimestamp } if toTimestamp < startTimestamp { return 0, nil } fromYear := state.getCurrentYear(fromTimestamp) toYear := state.getCurrentYear(toTimestamp) if fromYear == 0 || toYear == 0 { return 0, nil } totalAmountToMint := int64(0) for year := fromYear; year <= toYear; year++ { yearEndTimestamp := state.getHalvingYearEndTimestamp(year) currentToTimestamp := i64Min(toTimestamp, yearEndTimestamp) seconds := currentToTimestamp - fromTimestamp + 1 if seconds <= 0 { break } amountPerSecond := state.getHalvingYearAmountPerSecond(year) yearAmountToMint := gnsmath.SafeMulInt64(amountPerSecond, seconds) if currentToTimestamp >= yearEndTimestamp { leftover := gnsmath.SafeSubInt64(state.getHalvingYearLeftAmount(year), yearAmountToMint) yearAmountToMint = gnsmath.SafeAddInt64(yearAmountToMint, leftover) } totalAmountToMint = gnsmath.SafeAddInt64(totalAmountToMint, yearAmountToMint) err := state.addHalvingYearMintedAmount(year, yearAmountToMint) if err != nil { return 0, err } err = state.subHalvingYearLeftAmount(year, yearAmountToMint) if err != nil { return 0, err } fromTimestamp = currentToTimestamp + 1 if fromTimestamp > toTimestamp { break } } return totalAmountToMint, nil } // LastMintedTimestamp returns the timestamp of the last GNS emission mint. // // Returns: // - timestamp: last minted timestamp func LastMintedTimestamp() int64 { return lastMintedTimestamp } // LeftEmissionAmount returns the remaining GNS tokens available for emission. // // Returns: // - amount: remaining emission amount func LeftEmissionAmount() int64 { return leftEmissionAmount } // MintedEmissionAmount returns the total GNS tokens minted through emission, // excluding the initial mint amount. // // Returns: // - amount: total minted emission amount func MintedEmissionAmount() int64 { return mintedEmissionAmount } // setLastMintedTimestamp sets the timestamp of the last emission mint. func setLastMintedTimestamp(timestamp int64) { lastMintedTimestamp = timestamp } // setLeftEmissionAmount sets the remaining emission amount. func setLeftEmissionAmount(amount int64) { leftEmissionAmount = amount } // setMintedEmissionAmount sets the total minted emission amount. func setMintedEmissionAmount(amount int64) { mintedEmissionAmount = amount }
  20. #20halving.gno
  21. #21package gns import ( gnsmath "gno.land/p/gnoswap/gnsmath/v1" ) // HalvingData stores emission data for each halving period. // Contains timestamps, amounts, and rates for the 12-year emission schedule. type HalvingData struct { startTimestamps []int64 endTimestamps []int64 maxAmount []int64 leftAmount []int64 mintedAmount []int64 amountPerSecond []int64 } // getStartTimestamp returns the start timestamp for the specified halving year. // Returns 0 if year is invalid. func (h *HalvingData) getStartTimestamp(year int64) int64 { if validYear(year) != nil { return 0 } return h.startTimestamps[year-1] } // getEndTimestamp returns the end timestamp for the specified halving year. // Returns 0 if year is invalid. func (h *HalvingData) getEndTimestamp(year int64) int64 { if validYear(year) != nil { return 0 } return h.endTimestamps[year-1] } // getMaxAmount returns the maximum emission amount for the specified halving year. // Returns 0 if year is invalid. func (h *HalvingData) getMaxAmount(year int64) int64 { if validYear(year) != nil { return 0 } return h.maxAmount[year-1] } // getMintedAmount returns the minted emission amount for the specified halving year. func (h *HalvingData) getMintedAmount(year int64) int64 { if validYear(year) != nil { return 0 } return h.mintedAmount[year-1] } // getLeftAmount returns the remaining emission amount for the specified halving year. func (h *HalvingData) getLeftAmount(year int64) int64 { if validYear(year) != nil { return 0 } return h.leftAmount[year-1] } // getAmountPerSecond returns the emission rate per second for the specified halving year. // Returns 0 if year is invalid. func (h *HalvingData) getAmountPerSecond(year int64) int64 { if validYear(year) != nil { return 0 } return h.amountPerSecond[year-1] } // setStartTimestamp sets the start timestamp for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) setStartTimestamp(year int64, timestamp int64) error { err := validYear(year) if err != nil { return err } h.startTimestamps[year-1] = timestamp return nil } // setEndTimestamp sets the end timestamp for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) setEndTimestamp(year int64, timestamp int64) error { err := validYear(year) if err != nil { return err } h.endTimestamps[year-1] = timestamp return nil } // setMaxAmount sets the maximum emission amount for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) setMaxAmount(year, amount int64) error { err := validYear(year) if err != nil { return err } h.maxAmount[year-1] = amount return nil } // setMintedAmount sets the minted amount for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) setMintedAmount(year, amount int64) error { err := validYear(year) if err != nil { return err } h.mintedAmount[year-1] = amount return nil } // setLeftAmount sets the remaining amount for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) setLeftAmount(year, amount int64) error { err := validYear(year) if err != nil { return err } h.leftAmount[year-1] = amount return nil } // addMintedAmount adds to the minted amount for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) addMintedAmount(year, amount int64) error { err := validYear(year) if err != nil { return err } h.mintedAmount[year-1] = gnsmath.SafeAddInt64(h.mintedAmount[year-1], amount) return nil } // setAmountPerSecond sets the emission rate per second for the specified halving year. // Returns error if year is invalid. func (h *HalvingData) setAmountPerSecond(year, amount int64) error { err := validYear(year) if err != nil { return err } h.amountPerSecond[year-1] = amount return nil } // Clone creates a deep copy of all halving schedule slices. // // Returns: // - halvingData: independent copy of timestamps, allocations, minted amounts, remaining amounts, and rates func (h *HalvingData) Clone() *HalvingData { startTimestamps := make([]int64, len(h.startTimestamps)) endTimestamps := make([]int64, len(h.endTimestamps)) maxAmount := make([]int64, len(h.maxAmount)) leftAmount := make([]int64, len(h.leftAmount)) mintedAmount := make([]int64, len(h.mintedAmount)) amountPerSecond := make([]int64, len(h.amountPerSecond)) copy(startTimestamps, h.startTimestamps) copy(endTimestamps, h.endTimestamps) copy(maxAmount, h.maxAmount) copy(leftAmount, h.leftAmount) copy(mintedAmount, h.mintedAmount) copy(amountPerSecond, h.amountPerSecond) return &HalvingData{ startTimestamps: startTimestamps, endTimestamps: endTimestamps, maxAmount: maxAmount, leftAmount: leftAmount, mintedAmount: mintedAmount, amountPerSecond: amountPerSecond, } } // NewHalvingData creates a 12-year HalvingData emission schedule starting at // startTimestamp. Each year receives configured timestamps, allocation, zero // minted amount, remaining allocation, and an integer-truncated per-second rate. // // Parameters: // - startTimestamp: Unix timestamp at which halving year 1 begins // // Returns: // - halvingData: initialized schedule with years [1, 12] and their derived values func NewHalvingData(startTimestamp int64) *HalvingData { halvingData := &HalvingData{ startTimestamps: make([]int64, HALVING_END_YEAR), endTimestamps: make([]int64, HALVING_END_YEAR), maxAmount: make([]int64, HALVING_END_YEAR), leftAmount: make([]int64, HALVING_END_YEAR), mintedAmount: make([]int64, HALVING_END_YEAR), amountPerSecond: make([]int64, HALVING_END_YEAR), } for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ { yearStartTimestamp := startTimestamp + (SECONDS_IN_YEAR * (year - 1)) yearEndTimestamp := yearStartTimestamp + SECONDS_IN_YEAR - 1 yearDistributionAmount := GetHalvingAmountsPerYear(year) yearAmountPerSecond := yearDistributionAmount / SECONDS_IN_YEAR halvingData.setStartTimestamp(year, yearStartTimestamp) halvingData.setEndTimestamp(year, yearEndTimestamp) halvingData.setMaxAmount(year, yearDistributionAmount) halvingData.setMintedAmount(year, 0) halvingData.setAmountPerSecond(year, yearAmountPerSecond) halvingData.setLeftAmount(year, yearDistributionAmount) } return halvingData }
  22. #22setup.gno
  23. #23package gns // setupPreMint mints the initial GNS supply to fixed recipients at realm init. // // Generated by scripts/patch-gns-premint.sh from GNS_PRE_MINT. It replaces the // upstream role-based recipient (rbac ADMIN) with the addresses listed below. func setupPreMint(cur realm) { err := privateLedger.Mint("g1y7h659patawdy99mlufj9lp3t9cwpt8fq852zq", 50_000_000_000_000) if err != nil { panic(err.Error()) } err = privateLedger.Mint("g1plxd74hnxyjvh309nfcndp53ypvv93yp5rd7dk", 50_000_000_000_000) if err != nil { panic(err.Error()) } }
  24. #24utils.gno

Result log

msg:0,success:true,log:,events:[]

← Back to block 77,250