Skip to content

Gauges

fact Sources contracts/src/governance/GaugeController.sol and contracts/src/governance/gauges/. The controller is a Solidity 0.8 port of Curve’s GaugeController with custom errors and one addition: a weekly syncCaps() that turns series-gauge weights into per-underlying Desk caps.

Thu 00:00 UTC Thu 00:00 UTC Thu 00:00 UTC week W − 1: voteForGaugeWeights changes recorded for the next boundary week W: checkpoint, mintWeekly, syncCaps emissions stream over 7 days caps pushed to the Desk once per week
Weeks run Thursday 00:00 UTC to Thursday 00:00 UTC. A vote cast at any point in week W − 1 affects the weights read for week W. Nothing changes mid-week.
  1. Vote. Anyone with a live lock calls voteForGaugeWeights(gauge, userWeightBps) once per gauge. Your veNOTE slope is allocated across gauges in proportion to the bps you give each; Σ userWeightBps ≤ 10,000 (PowerExceeded(used)). The lock must end after the next week boundary (LockExpiresTooSoon).
  2. Boundary. At Thursday 00:00 UTC every gauge’s bias for the new week is fixed. checkpoint() and checkpointGauge(gauge) are permissionless and fill the histories; gaugeRelativeWeight(gauge, ts) returns the WAD share once the week is checkpointed (gaugeRelativeWeightWrite checkpoints first).
  3. Mint. Anyone calls Minter.mintWeekly(). The budget is min(EmissionSchedule.weeklyEmission(week), NOTE.epochMintRemaining()); each gauge receives budget × relativeWeight / 1e18, is minted to directly and notified in the same call.
  4. Caps. Anyone calls GaugeController.syncCaps() once per week (AlreadySynced(week) otherwise). Per underlying: bps = totalDeployedCapBps × weight_u / Σ series-gauge weights, clamped to [CAP_FLOOR_BPS = 100, CAP_CEILING_BPS = 4,000], pushed with Desk.setPerUnderlyingCapFromGauge(underlying, bps) and evented as CapSynced(underlying, relativeWeight, bps, week).
  5. Stream. Each gauge streams its NOTE over DURATION = 7 days to stakers, Synthetix-style (rewardRate = amount × 1e18 / 604,800 per second).

fact Three types are registered by the constructor, each with type weight 1e18, so a gauge’s relative weight is simply its bias over the total bias.

Type idGaugeStakeWhat a vote here does
0SeriesGauge (one per COUPON series)COUPON units, LEG_ID = seriesId << 1, pulled by safeTransferFromDirects NOTE to holders who stake that series’ COUPON leg, and raises the Desk cap for the series’ underlying
1DeskGaugedNOTE (Desk shares)Directs NOTE to Desk depositors
2StakingGaugesNOTEDirects NOTE to stakers

Gauges are added by governance proposal: addGauge(gauge, type, weight, underlying) with underlying non-zero only for series gauges. Admin weights (changeGaugeWeight) are rejected on a gauge with live votes (GaugeHasVotes) because overriding a voted bias while keeping its slope would let Σ relative weights exceed 1e18; admin weights remain available to bootstrap an un-voted gauge.

Staked COUPON units belong to the gauge for NoteLegs’ purposes, so coupons paid while units are staked accrue to the gauge contract in v1. Unstake before an observation you want to be paid for directly. withdraw, exit and getReward are never pausable; a paused gauge blocks stake only.

  • voteForGaugeWeights records voteUserSlopes[user][gauge] = {slope, power, end}; your total voteUserPower may not exceed 10,000 bps. Setting a gauge to 0 bps releases that power.
  • 10-day cooldown per gauge. WEIGHT_VOTE_DELAY = 10 days: after voting on a gauge you cannot change that gauge’s allocation until 10 days have passed (VoteCooldown(availableAt)). Other gauges are unaffected. Because the cooldown exceeds a week, every vote is guaranteed to be counted for at least one full weekly boundary.
  • Votes persist. Your allocation stays in force and decays with your lock; you do not need to re-vote weekly. As your lock decays the gauge bias contributed by your vote decays with it; when the lock ends the slope is removed from the sums at end.
  • WeightTooHigh if a single allocation exceeds 10,000 bps; UnknownGauge for an unregistered address.

Events: VoteForGauge(time, user, gauge, weight), NewGauge, NewGaugeWeight, AddType, NewTypeWeight, DeskUpdated, CapSynced.

The Desk holds three caps as bps of totalAssets: perSeriesCapBps, perUnderlyingCapBps and totalDeployedCapBps (launch values 1,000 / 2,500 / 6,000). syncCaps() replaces the flat per-underlying figure with a per-underlying value derived from votes:

StepRule
Series sharew_u / Σ w_series where w are the series-gauge relative weights for the week, summed per underlying
Raw captotalDeployedCapBps × share
Clampat least CAP_FLOOR_BPS = 100 (1%), at most CAP_CEILING_BPS = 4,000 (40%)
No votesevery underlying gets the 100 bps floor
Desk sidesetPerUnderlyingCapFromGauge accepts only the gauge controller (NotGaugeController) and only values in 100 to 4,000 bps (MIN_UNDERLYING_CAP_BPS, MAX_UNDERLYING_CAP_BPS, else CapOutOfBounds); emits PerUnderlyingCapSet
Binding capeffectivePerUnderlyingCapBps is the lower of the governed perUnderlyingCapBps and the gauge value. Votes can only tighten the governed default (2,500 bps at launch); loosening an underlying above it needs a governance change to setCaps. totalDeployedCapBps still bounds the sum

Clamping means the pushed caps need not sum to totalDeployedCapBps; the Desk’s global cap remains the binding total. Vote weight redirects Desk capital between underlyings within governance’s ceilings, it does not increase the Desk’s total exposure.

illustrative Taken from the contract documentation (contracts/docs/governance/Overview.md). Week W starts Thursday 00:00 UTC; Desk totalDeployedCapBps = 6,000; four gauges: S-A and S-B (series gauges on underlyings A and B), D (DeskGauge), K (StakingGauge).

1. Locks (week W − 1). Alice locks 100,000 NOTE for 4 years, weight ≈ 100,000 veNOTE (slope 100,000e18 / 126,144,000 ≈ 7.93e11). Bob locks 60,000 NOTE for 2 years, weight ≈ 30,000 veNOTE (slope ≈ 4.76e11).

2. Votes (week W − 1). Alice: 50% S-A, 30% D, 20% K. Bob: 100% S-B.

GaugeSlope from WBias at W (veNOTE units)
S-A3.96e11≈ 50,000
S-B4.76e11≈ 30,000
D2.38e11≈ 30,000
K1.59e11≈ 20,000

3. Week W opens. checkpoint() fills the points for W. Total 130,000. Relative weights: S-A 0.3846, S-B 0.2308, D 0.2308, K 0.1538 (sum 1e18 less at most 4 wei of truncation).

4. Emission. Week W is in the first 4-week period, so weeklyEmission = 300,000 NOTE; the NOTE epoch cap is 1,000,000 with nothing else minted, so budget = 300,000. mintWeekly() mints and notifies:

GaugeNOTE mintedrewardRate (per second)
S-A115,384.6115,384.6e18 / 604,800
S-B69,230.869,230.8e18 / 604,800
D69,230.869,230.8e18 / 604,800
K46,153.846,153.8e18 / 604,800

5. Caps. syncCaps(): series weights sum to 0.6154. A’s share is 0.3846 / 0.6154 = 62.5%, so 6,000 × 0.625 = 3,750 bps; B gets 2,250 bps. Both inside [100, 4,000], so the gauge values are 37.5% for A and 22.5% for B. With the launch perUnderlyingCapBps of 2,500, the Desk’s effective cap is min(2,500, 3,750) = 2,500 bps for A and 2,250 bps for B: votes can shift the Desk toward A but not past the governed 25% ceiling.

6. During the week. Carol stakes 40% of all S-A COUPON units in the S-A gauge on Monday. From then she earns 40% of the remaining stream. Rewards streamed while nobody was staked (Thursday to Monday) accumulate in unallocated and are folded into next week’s stream, so nothing is stranded.

7. Week W + 1. Biases have decayed by one week of slope (Alice’s by 7.93e11 × 604,800 ≈ 480 veNOTE, Bob’s by ≈ 288). Relative weights shift slightly toward Alice’s gauges because Bob’s shorter lock decays faster in relative terms. In week W + 4 the schedule steps down to 294,000 NOTE. A proposal to change the schedule takes about 8 days end to end; see Emissions.

  • For the current and next week, Σ gaugeRelativeWeightWrite(g) ∈ [1e18 − nGauges, 1e18], or exactly 0 when the total is 0.
  • voteUserPower[u] == Σ voteUserSlopes[u][g].power ≤ 10,000.
  • Per gauge: rewardsPaid ≤ rewardsNotified, REWARD.balanceOf(gauge) == rewardsNotified − rewardsPaid, Σ balanceOf == totalStaked == STAKE.balanceOf(gauge).
  • Per week: Minter.mintedForWeek ≤ weeklyEmission(week) and the NOTE epoch headroom; every minted wei sits in exactly one gauge and was notified to it.

Bribe or incentive markets on gauge votes, a veNOTE boost on sNOTE rewards, vote delegation, and forwarding of coupons earned by COUPON units while staked in a series gauge. All are listed as future work in the contract documentation.