emission source realm
Package emission manages GNS token emission and distribution for GnoSwap.
View source
Emission
GNS token emission and distribution system.
Overview
The emission system controls creation and distribution of new GNS tokens with a deflationary model featuring periodic halvings, ensuring predictable and decreasing supply growth over 12 years. For more details, check out docs.
Token Economics
- Total Supply Cap: 1,000,000,000 GNS
- Initial Minted: 100,000,000 GNS, pre-minted to the configured
ADMINrole address during GNS realm initialization - To Be Minted: 900,000,000 GNS over 12 years
- Halving Period: Every 2 years (63,072,000 seconds)
- Halving Reduction: 50% decrease in emission rate
- Distribution: Automatic during protocol activity
Configuration
- Distribution Ratios (modifiable by admin or governance):
- Liquidity Staker: 75% (default)
- DevOps: 20% (default)
- Community Pool: 5% (default)
- Governance Staker: 0% (default)
- Start Time: Unix timestamp. It may be changed while the configured timestamp is still in the future; once active, it cannot be changed.
Core Features
Emission Schedule
Implements Bitcoin-style halving model:
- Year 1-2: 100% emission rate
- Year 3-4: 50% emission rate
- Year 5-6: 25% emission rate
- Year 7-8: 12.5% emission rate
- Year 9-12: 6.25% emission rate
Distribution Mechanism
When triggered by protocol activity:
- Calculates elapsed time since last distribution
- Mints GNS based on the current timestamp range and halving-year rates
- Distributes to targets per configured ratios
- Carries forward any undistributed amounts
If emission is halted, MintAndDistributeGns returns (0, false) without
panicking. A caller that requires emission must explicitly handle that result.
Key Functions
MintAndDistributeGns
Mints and distributes GNS tokens automatically.
SetDistributionStartTime
Sets or reschedules the emission start timestamp before distribution is active. The timestamp must be positive and in the future; after the configured start time has been reached, the timestamp is immutable.
ChangeDistributionPct
Updates distribution percentages (admin or governance only).
GetDistributionBpsPct
Returns current distribution percentage in basis points for a target, or an error if the target is invalid.
Gnoweb
Render("") shows distribution dates in UTC, GNS amounts with six decimal places,
allocation percentages, and cumulative distributions for the four recipients.
Staker and governance-staker counters since their last accounting clear are shown
separately: these allocations have already been transferred and are not wallet
balances or user-claimable rewards. Unsupported paths return 404.
Technical Details
Timestamp-Based Emission
The following is a conceptual view of the schedule:
emissionPerSecond = baseEmission / (2^halvingCount)
amountToMint = emissionPerSecond * elapsedSeconds
The implementation uses integer, piecewise rates. For each halving year
intersecting the inclusive mint range [fromTimestamp, toTimestamp], it
initializes yearAmountPerSecond as
floor(yearDistributionAmount / SECONDS_IN_YEAR) and multiplies that rate by
the inclusive number of seconds. The mint range is clamped to the 12-year
schedule end. When a range reaches a year end, the remaining integer amount
(including division dust) is added so that the year's allocation is exhausted.
Halving Calculation
Halving years are determined by the schedule's year boundaries; the conceptual form is:
halvingCount = floor(timeSinceStart / halvingPeriod)
Distribution Targets
- Liquidity Staker: Rewards for LP providers
- DevOps: Development and operations fund
- Community Pool: Community-governed treasury
- Governance Staker: GNS staking rewards (currently 0%)
Usage
1// Set emission start (admin/governance; timestamp must be in the future)
2SetDistributionStartTime(cross(cur), futureStartTimestamp)
3
4// Trigger emission (called automatically by protocol flows)
5amount, ok := MintAndDistributeGns(cross(cur))
6
7// Update distribution ratios
8ChangeDistributionPct(
9 cross(cur),
10 7000, // 70% to liquidity stakers
11 2000, // 20% to devops
12 1000, // 10% to community pool
13 0, // 0% to governance stakers
14)
15
16// Query distribution info
17stakerPct, err := GetDistributionBpsPct(LIQUIDITY_STAKER)
18if err != nil {
19 panic(err)
20}
21accumulated := GetAccuDistributedToStaker()
22rate, err := GetStakerEmissionAmountPerSecond()
23if err != nil {
24 panic(err)
25}
Security
- Start time may be rescheduled while still in the future and is immutable once active
- Distribution percentages must sum to 10000 (100%)
- A halted
MintAndDistributeGnscall returnsfalse; no automatic cross-module cascade occurs - If staker cache invalidation is required, keep the optional distribution-change callback registered
- Leftover tracking carries undistributed amounts forward
- Halving is enforced at protocol level
Package emission manages GNS token emission and distribution for GnoSwap.
The emission system controls creation and distribution of new GNS tokens with a deflationary model featuring periodic halvings over 12 years.
Emission Schedule:
- Year 1-2: 100% emission rate (225,000,000 GNS/year)
- Year 3-4: 50% emission rate (112,500,000 GNS/year)
- Year 5-6: 25% emission rate (56,250,000 GNS/year)
- Year 7-8: 12.5% emission rate (28,125,000 GNS/year)
- Year 9-12: 6.25% emission rate (14,062,500 GNS/year)
Distribution Targets (configurable via admin or governance):
- LIQUIDITY_STAKER: Rewards for LP providers (default 75%)
- DEVOPS: Development and operations fund (default 20%)
- COMMUNITY_POOL: Community treasury (default 5%)
- GOV_STAKER: GNS staking rewards (default 0%)
Key Functions:
- MintAndDistributeGns: Mints and distributes GNS per emission schedule; returns false only when emission is halted.
- SetDistributionStartTime: Sets or reschedules the start timestamp while it is still in the future; the timestamp is immutable once active.
- ChangeDistributionPct: Updates distribution percentages
- ClearDistributedToStaker/GovStaker: Resets pending distribution amounts
2
const totalDistributionDuration, DefaultInitialPoolTierPath
1const (
2 totalDistributionDuration = 12 * 365 * 24 * 60 * 60 // 12 years
3
4 // DefaultInitialPoolTierPath is the canonical default initial pool that must
5 // exist before emission distribution can start. The pool contract registers a
6 // checker (SetDefaultInitialPoolChecker) that verifies this pool exists.
7 DefaultInitialPoolTierPath = "gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:3000"
8)29
func AccumulateDistributedInfo
ActionAccumulateDistributedInfo returns the current pending allocation for every distribution target.
Returns:
- toStaker: pending GNS allocation for liquidity stakers, in token base units.
- toDevOps: accumulated GNS allocation for DevOps, in token base units.
- toCommunityPool: accumulated GNS allocation for the community pool, in token base units.
- toGovStaker: pending GNS allocation for governance stakers, in token base units.
func ChangeDistributionPct
crossing ActionChangeDistributionPct changes distribution percentages for emission targets.
This function redistributes how newly minted GNS tokens are allocated across protocol components. Before applying new ratios, it distributes any accumulated emissions using the current ratios, ensuring emissions are distributed according to the ratios in effect when they were generated. This prevents retroactive application of new ratios to past emissions.
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
- liquidityStakerPct: percentage for liquidity stakers in basis points (100 = 1%, 10000 = 100%).
- devOpsPct: percentage for DevOps in basis points (100 = 1%, 10000 = 100%).
- communityPoolPct: percentage for the community pool in basis points (100 = 1%, 10000 = 100%).
- govStakerPct: percentage for governance stakers in basis points (100 = 1%, 10000 = 100%).
Requirements:
- Percentages must sum to exactly 10000 (100%).
- Each percentage must be between 0 and 10000 inclusive.
Example:
Example
1ChangeDistributionPct(
2 7000, // 70% to liquidity stakers
3 2000, // 20% to devops
4 1000, // 10% to community pool
5 0, // 0% to governance stakers
6)
Only callable by admin or governance.
func ClearDistributedToGovStaker
crossing ActionClearDistributedToGovStaker resets the pending distribution amount for governance stakers.
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
Only callable by governance staker contract.
func ClearDistributedToStaker
crossing ActionClearDistributedToStaker resets the pending distribution amount for liquidity stakers.
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
Only callable by staker contract.
func GetAccuDistributedToCommunityPool
ActionGetAccuDistributedToCommunityPool returns the total historical GNS amount allocated to the community pool.
Returns:
- amount: cumulative community-pool allocation in GNS base units; this value is not cleared.
func GetAccuDistributedToDevOps
ActionGetAccuDistributedToDevOps returns the total historical GNS amount allocated to DevOps.
Returns:
- amount: cumulative DevOps allocation in GNS base units; this value is not cleared.
func GetAccuDistributedToGovStaker
ActionGetAccuDistributedToGovStaker returns the total historical GNS amount allocated to governance stakers.
Returns:
- amount: cumulative governance-staker allocation in GNS base units; this value is not cleared.
func GetAccuDistributedToStaker
ActionGetAccuDistributedToStaker returns the total historical GNS amount allocated to liquidity stakers.
Returns:
- amount: cumulative liquidity-staker allocation in GNS base units; this value is not cleared.
func GetAllDistributionBpsPct
ActionGetAllDistributionBpsPct returns all distribution percentages in basis points.
Returns:
- percentages: map of target to percentage in basis points
func GetDistributableAmount
ActionGetDistributableAmount returns distribution amounts by target and the remainder. If timestamp is outside the distribution window, it returns an empty map and the full amount as left.
Parameters:
- amount: total amount to distribute
- timestamp: timestamp to check distribution window
Returns:
- distributions: map of target to distribution amount
- remainder: undistributed amount
func GetDistributedToCommunityPool
ActionGetDistributedToCommunityPool returns the GNS amount currently accumulated for the community pool.
Returns:
- amount: accumulated community-pool allocation in GNS base units.
func GetDistributedToDevOps
ActionGetDistributedToDevOps returns the GNS amount currently accumulated for DevOps.
Returns:
- amount: accumulated DevOps allocation in GNS base units.
func GetDistributedToGovStaker
ActionGetDistributedToGovStaker returns the pending GNS amount allocated to governance stakers.
Returns:
- amount: pending governance-staker allocation in GNS base units since the last clear.
func GetDistributedToStaker
ActionGetDistributedToStaker returns the pending GNS amount allocated to liquidity stakers.
Returns:
- amount: pending liquidity-staker allocation in GNS base units since the last clear.
func GetDistributionBpsPct
ActionGetDistributionBpsPct returns the configured distribution percentage in basis points for a target.
Parameters:
- target: distribution target constant whose configured share is queried.
Returns:
- pct: configured target share in basis points (100 = 1%, 10000 = 100%).
- error: nil when the target exists in the initialized map; otherwise an invalid-target error.
func GetDistributionEndTimestamp
ActionGetDistributionEndTimestamp returns the timestamp when emission distribution ends.
Returns:
- timestamp: distribution end timestamp, or 0 if not started
func GetDistributionStartTimestamp
ActionGetDistributionStartTimestamp returns the configured emission start timestamp. The value may be in the future; it is 0 when no start timestamp is configured.
Returns:
- timestamp: configured distribution start timestamp, or 0 if unconfigured
func GetEmissionAmountPerSecondBy
ActionGetEmissionAmountPerSecondBy returns the GNS emission rate per second for a timestamp and target share.
Parameters:
- timestamp: Unix timestamp at which the base emission rate is queried.
- distributionPct: target share in basis points, from 0 through 10000 inclusive.
Returns:
- amountPerSecond: target's GNS emission rate in token base units per second, or zero outside the schedule.
func GetLastExecutedTimestamp
ActionGetLastExecutedTimestamp returns the timestamp of the last emission distribution execution.
Returns:
- timestamp: last execution timestamp
func GetLeftGNSAmount
ActionGetLeftGNSAmount returns the amount of undistributed GNS tokens from previous distributions.
Returns:
- amount: undistributed GNS amount
func GetStakerEmissionAmountPerSecond
ActionGetStakerEmissionAmountPerSecond returns the current GNS emission rate allocated to liquidity stakers.
Returns:
- amountPerSecond: current liquidity-staker emission rate in GNS base units per second.
- error: nil when the liquidity-staker distribution share is available; otherwise the target lookup error.
func GetStakerEmissionAmountPerSecondInRange
ActionGetStakerEmissionAmountPerSecondInRange returns liquidity-staker emission-rate change points in a time range.
Parameters:
- start: inclusive start Unix timestamp for the range.
- end: inclusive end Unix timestamp for the range.
Returns:
- timestamps: ordered timestamps in the range where the base emission rate changes.
- amountsPerSecond: liquidity-staker GNS rates corresponding to timestamps, in token base units per second.
- error: nil when the liquidity-staker distribution share is available; otherwise the target lookup error.
func GetTotalAccuDistributed
ActionGetTotalAccuDistributed returns the total accumulated distributed GNS amount.
Returns:
- amount: total accumulated distributed GNS
func GetTotalDistributed
ActionGetTotalDistributed returns the total pending distributed GNS amount.
Returns:
- amount: total pending distributed GNS
func MintAndDistributeGns
crossing ActionMintAndDistributeGns mints and distributes GNS tokens according to the emission schedule.
This function is called automatically by protocol contracts during user interactions to trigger periodic GNS emission. It mints new tokens based on elapsed time since last distribution and distributes them to predefined targets (staker, devops, etc.).
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
Returns:
- distributedAmount: total amount of GNS distributed in this call
- success: false only when emission is halted; true when processing completes, even if no tokens are distributed
Note: Distribution only occurs if start timestamp is set and reached. Any undistributed tokens from previous calls are carried forward.
func Render
Render returns the current GNS emission and distribution state.
func SetDefaultInitialPoolChecker
crossing ActionSetDefaultInitialPoolChecker registers the callback that verifies the canonical default initial pool exists before emission starts.
The checker receives the pool path as a parameter so emission owns the canonical path policy (DefaultInitialPoolTierPath) while pool supplies only the generic "does this pool exist" capability.
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
- checker: nonnil function returning true when the supplied pool path exists; it is called with DefaultInitialPoolTierPath and nil causes a panic.
Only callable by the pool contract.
func SetDistributionStartTime
crossing ActionSetDistributionStartTime sets the timestamp when emission distribution starts.
This function controls when GNS emission begins. Before the configured timestamp is reached, an admin or governance caller may reschedule it. Once the timestamp is reached, the start time is immutable.
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
- startTimestamp: positive future Unix timestamp when emission should begin.
Requirements:
- Must be called before distribution starts.
- Timestamp must be in the future and leave room for the 12-year schedule.
Effects:
- Sets global distribution start time.
- Initializes GNS emission state if not already started.
- Emission begins automatically when timestamp is reached.
Only callable by admin or governance.
func SetOnDistributionPctChangeCallback
crossing Action1func SetOnDistributionPctChangeCallback(cur realm, callback func(cur realm, emissionAmountPerSecond int64))SetOnDistributionPctChangeCallback registers the optional callback invoked when distribution percentages change. A nil callback clears the registration. This allows external contracts (like staker) to update their internal caches when governance or admin changes emission rates.
Parameters:
- cur: Current realm context; callers use cross(cur) when crossing into this realm.
- callback: nil to clear the callback, or a function receiving its callback realm context and the current staker GNS emission rate per second; a nonnil callback is invoked immediately after registration.
14
- chain stdlib
- chain/runtime stdlib
- gno.land/p/gnoswap/gnsmath/v1 package
- gno.land/p/gnoswap/rbac/v1 package
- gno.land/p/gnoswap/utils/v1 package
- gno.land/p/moul/md/v0 package
- gno.land/p/moul/mdtable/v0 package
- gno.land/p/nt/ufmt/v0 package
- gno.land/r/gnoswap/access/v1 realm
- gno.land/r/gnoswap/gns realm
- gno.land/r/gnoswap/halt/v1 realm
- math stdlib
- strings stdlib
- time stdlib