DocumentationGovernance

Emissions

Documentation snapshot 18 October 2026. Deployment · Audit Status
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

weeklyEmission(t) = 0 if t < START = max(floor, initial × (1 − decayBps / 10,000) ^ periods) otherwise periods = (roundWeek(t) − START) / (decayPeriodWeeks × 1 week)

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

initialWeekly
100,000 NOTE
decayBps
300 (3%)
decayPeriodWeeks
4
floorWeekly
20,000 NOTE

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:

  1. week = currentWeek(); revert AlreadyMinted(week) if already done.
  2. The first call of the week fixes the budget (weekBudget(week)): scheduled = schedule.weeklyEmission(week) + emissionShortfall, where emissionShortfall also receives the scheduled emission of any weeks since lastMintedWeek in which nobody called mintWeekly (SkippedWeeksRecovered(fromWeek, toWeek, amount); weeks skipped since lastMintedWeek are recovered in a single batch of at most MAX_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()); revert NothingToMint(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.
  3. 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 / 1e18 clamped to budget − allocatedSoFar; if non-zero, the Minter calls its own distribute(gauge, amount) (NOTE.mint(gauge, amount) followed by gauge.notifyRewardAmount(amount); OnlySelf(caller) for any other caller) in isolation with a fixed gas budget (DISTRIBUTE_GAS = 1,000,000) and at most MAX_REASON_BYTES of 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 to carriedForward[gauge] and totalCarriedForward instead of being minted. The other gauges are unaffected.
  4. Record mintedForWeek, mintedToGauge, totalMinted; emit GaugeMinted per gauge that received NOTE. When the cursor reaches the gauge count the week is marked minted and WeeklyMint(week, budget, minted) is emitted, where minted excludes any carried amounts; a partial slice emits WeeklyMintProgress(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.

fact Interplay with the NOTE Epoch Cap
SituationWhat mintWeekly does
Epoch headroom ≥ scheduled emissionMints the full schedule
Bonds have used part of the epoch capMints 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 othersReverts 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 misalignedThe 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.

A little more clarity?

Keep exploring, or revisit this topic from the navigation.