Emissions
On this page
fact Sources contracts/src/governance/EmissionSchedule.sol and contracts/src/governance/Minter.sol. Emissions are the only NOTE that gauges distribute. The schedule is a pure function of time; the Minter is the only path that turns it into tokens, and it can never exceed the NOTE epoch cap.
Schedule
fact Schedule Calculation
START is rounded down to a week boundary at construction and is immutable. The other four parameters are governed through setSchedule(initialWeekly, decayBps, decayPeriodWeeks, floorWeekly) (owner = 48-hour governance), which reverts InvalidSchedule when decayBps ≥ 10,000, decayPeriodWeeks == 0 or floor > initial, and emits ScheduleUpdated(initialWeekly, decayBps, decayPeriodWeeks, floorWeekly).
fact Schedule Parameters
Deployed defaults from DeployGovernance, which reads them back from deploy/emissions.json and reverts with EmissionManifestMismatch if the two disagree, so the script, the manifest and TOKENOMICS.md cannot drift apart silently. Emission is constant inside a 4-week period and steps down 3% at each period boundary until it reaches the floor after 53 periods (week 212, roughly 4.1 years). The decay loop exits early once the floor is reached, so gas is bounded by periods to the floor, not by elapsed time.
The allocation budget is not enforced by EmissionSchedule. Its current parameters and the NOTE epoch cap must be read together; the schedule is not a commitment to future issuance.
Reading the Schedule
Use the formula above for a specified timestamp and configuration. No cumulative multi-year projection is shown because governance can change the schedule and minting remains constrained by available supply and epoch headroom.
The Weekly Mint
fact Minter.mintWeekly() is nonReentrant, whenNotPaused and callable by anyone once per week; mintWeekly(maxGauges) does the same work in slices of at most maxGauges gauges, resuming from weekCursor(week), so the weekly mint always fits in a block however long the gauge list grows:
week = currentWeek(); revertAlreadyMinted(week)if already done.- The first call of the week fixes the budget (
weekBudget(week)):scheduled = schedule.weeklyEmission(week) + emissionShortfall, whereemissionShortfallalso receives the scheduled emission of any weeks sincelastMintedWeekin which nobody calledmintWeekly(SkippedWeeksRecovered(fromWeek, toWeek, amount); weeks skipped sincelastMintedWeekare recovered in a single batch of at mostMAX_CATCHUP_WEEKS= 104 weeks when the next week is opened, and the scheduled emission of earlier skipped weeks is forfeited, so an idle period of more than two years permanently drops the oldest weeks). A week that was opened but whose gauge cursor did not reach the end of the gauge list is closed when the next week opens: the unallocated remainder (weekBudget − weekAllocated) is added to the next week’s budget via the carry,UnfinishedWeekRecovered(week, cursor, remainder)is emitted and the week is marked minted, so nothing scheduled is lost by an interrupted cursor walk;budget = min(scheduled, NOTE.epochMintRemaining()); revertNothingToMint(week)if zero.emissionShortfall = scheduled − budget(EmissionShortfallUpdated(week, scheduled, budget, shortfall)when it changes), so emission clipped by the epoch cap, or missed because no one called, is added to the following weeks’ budgets instead of being lost. - For every registered gauge from the cursor on: a gauge killed in the controller is skipped without any external call; otherwise
rw = controller.gaugeRelativeWeightWrite(gauge, week),amount = budget × rw / 1e18clamped tobudget − allocatedSoFar; if non-zero, the Minter calls its owndistribute(gauge, amount)(NOTE.mint(gauge, amount)followed bygauge.notifyRewardAmount(amount);OnlySelf(caller)for any other caller) in isolation with a fixed gas budget (DISTRIBUTE_GAS= 1,000,000) and at mostMAX_REASON_BYTESof revert data copied back, so a gauge cannot exhaust the caller’s gas. A gauge whose mint or notify reverts is skipped:GaugeSkipped(week, gauge, amount, reason)is emitted and its share is added tocarriedForward[gauge]andtotalCarriedForwardinstead of being minted. The other gauges are unaffected. - Record
mintedForWeek,mintedToGauge,totalMinted; emitGaugeMintedper gauge that received NOTE. When the cursor reaches the gauge count the week is marked minted andWeeklyMint(week, budget, minted)is emitted, wheremintedexcludes any carried amounts; a partial slice emitsWeeklyMintProgress(week, cursor, gaugeCount, minted)instead.
A carried share is delivered later by the permissionless retryGauge(gauge) (nonReentrant, whenNotPaused): it mints and notifies carriedForward[gauge], clipped to the NOTE epoch headroom still available (the rest stays carried), updates mintedToGauge and totalMinted and emits CarryDelivered(gauge, amount). It reverts NothingCarried(gauge) when nothing is owed, GaugeIsKilled(gauge) for a gauge killed in the controller, and reverts, keeping the carry intact, if the gauge still cannot receive.
The carry of a killed or retired gauge can be moved to a live registered gauge or written off through a permissioned setting (reassignCarry, events CarryReassigned(from, to, amount) / CarryWrittenOff(gauge, amount)), so totalCarriedForward never holds NOTE that can no longer be delivered. A gauge killed in the controller has relative weight 0, so its share is simply not minted; see Gauges.
The clamp in step 3 is a second line of defence: totalMinted cannot exceed the schedule even if a future controller version mis-reports weights. setSchedule(schedule) on the Minter (owner) swaps the schedule contract; currentWeek() and mintedWeek(week) are views. The first mintWeekly() is possible from EMISSION_START, which defaults to the week boundary after deployment. NOTE.setMinter(minter, true) must have been executed by the NOTE owner.
Replacing the Minter. A new Minter is deployed with the outgoing one as previousMinter. When the outgoing Minter has already opened the current week (lastMintedWeek() == currentWeek()), the constructor copies that week’s mintedWeek, weekBudget, mintedForWeek, weekCursor and weekAllocated, so the same week is never budgeted twice and the next _openWeek does not treat it as skipped.
The rotation rule still applies: grant the new minter (NOTE.setMinter) and revoke the old one only after mintedWeek(currentWeek) is true on the outgoing Minter, or at the start of a week, so no gauge is minted twice for one week through two half-completed slices.
Interplay with the NOTE Epoch Cap
NOTE has its own brake: mintCapPerEpoch per 7-day epoch counted from GENESIS, with epochMintRemaining() = min(mintCapPerEpoch − mintedInEpoch, CAP − totalSupply). The Minter is one of several minters (bonds and governed Treasury minting are the others) and shares that headroom.
| Situation | What mintWeekly does |
|---|---|
| Epoch headroom ≥ scheduled emission | Mints the full schedule |
| Bonds have used part of the epoch cap | Mints only the remaining headroom; never reverts on the cap and never pre-empts other minters. The clipped remainder is recorded in emissionShortfall and added to the next week's budget, still under that week's epoch headroom |
| Epoch fully used by others | Reverts NothingToMint(week); the week stays open and can be minted later in the same week if headroom appears. If nobody mints it, its scheduled emission is recovered into emissionShortfall when the next week is opened |
| Schedule and epoch windows misaligned | The schedule week is Thursday-anchored while NOTE's epoch is anchored at GENESIS; a partial overlap only makes the effective budget more conservative |
Raising emissions therefore takes two governed steps when the cap binds: EmissionSchedule.setSchedule and NOTE.setMintCapPerEpoch, both through the 48-hour governance. The epoch cap is the ceiling; the schedule is the target.
Where the NOTE Goes
Emissions are split by gauge relative weight, so veNOTE voters decide the allocation between COUPON stakers per series, Desk depositors and sNOTE stakers each week. See Gauges for the weekly cycle and a numerical split of a 100,000 NOTE week, and Desk for how the same votes steer the Desk’s per-underlying caps.
Keep exploring, or revisit this topic from the navigation.