Functions¶
Note
The gauge system spans three contracts. GaugeController holds votes, weights and the emergency switches; GaugeRewardsDistributor holds the reward token and hands each gauge its per-epoch share; BaseGauge (extended by RAACGauge and RWAGauge) holds stake and streams rewards. Read Gauge States first — almost every gate on this page depends on them.
whenGaugeLive means both switches off
On a gauge, whenGaugeLive requires the controller's global pause and the gauge's own pause to be off. It is not OpenZeppelin's whenNotPaused. withdraw is the one user entry point that carries neither.
GaugeController — Voting¶
vote¶
vote(address gaugeAddress, uint256 voteWeight)
Summary
Allocates voteWeight basis points of the caller's veRAAC power to a gauge, replacing any previous allocation on that gauge. The vote is snapshotted per underlying veRAAC lock so it decays on its own as those locks expire.
Guarded Method
Callable by any veRAAC holder. Subject to whenNotPaused (the controller's global switch) and nonReentrant.
Parameters
| Name | Type | Description |
|---|---|---|
gaugeAddress |
address | Registered gauge to vote on |
voteWeight |
uint256 | Basis points, 0 or MIN_VOTE_WEIGHT–MAX_VOTE_WEIGHT |
Reverts
| Error | Condition |
|---|---|
VoteDelayNotElapsed() |
less than WEIGHT_VOTE_DELAY (10 days) since the caller's last vote on this gauge |
GaugePaused() |
non-zero weight on an emergency-paused gauge |
GaugeNotActive() |
the gauge is not registered |
InvalidWeight() |
weight outside the allowed range |
Leaving a gauge is always allowed
voteWeight = 0 skips the paused check entirely, so a voter can exit a paused gauge and reallocate elsewhere.
batchVote¶
batchVote(address[] calldata gaugeAddresses, uint256[] calldata voteWeights)
Summary
Votes on several gauges in one transaction. The caller's lock set and epoch context are read once for the whole batch rather than per gauge, which is what makes it cheaper than repeated vote calls.
Guarded Method — whenNotPaused, nonReentrant.
Reverts
| Error | Condition |
|---|---|
ArrayLengthMismatch() |
empty arrays, or lengths differ |
InvalidWeight() |
the weights sum above MAX_VOTE_WEIGHT |
VoteDelayNotElapsed() |
the cooldown is still running on any gauge in the batch |
All-or-nothing
The cooldown is checked per gauge inside the loop, so one gauge still in cooldown reverts the entire batch.
kick¶
kick(address user)
Summary Removes the phantom votes of a user whose veRAAC power has gone to zero through a ragequit. Without it, their slope keeps decaying gauge aggregates until the originally scheduled expiries fire.
Guarded Method
Permissionless. Subject to whenNotPaused and nonReentrant.
Reverts — UserStillHasPower() unless the user has an open veRAAC ragequit request.
Only ragequitters are kickable
The function reads veToken.ragequitRequests(user) and refuses anyone without a pending request. A user who has called finalizeRagequit has their power back and is no longer kickable.
removePowerFromGauge¶
removePowerFromGauge(address gauge)
Summary
Releases the caller's allocation on a removed gauge so it can be reused elsewhere. It clears only the caller's own bookkeeping — the gauge's weight was already stripped from the aggregate by removeGauge.
Guarded Method — whenNotPaused, nonReentrant.
Reverts
| Error | Condition |
|---|---|
ActiveGauge() |
the gauge is still registered — use vote(gauge, 0) instead |
NoPower() |
the caller has no allocation on it |
Removed gauges only, never paused ones
A paused gauge is still active and was never kicked. Calling this on one would double-subtract; the guard prevents it.
forceCompleteCheckpoint¶
forceCompleteCheckpoint(address gauge) → bool
Summary
Advances a gauge's weight checkpoint and the global sum to the current epoch. Permissionless, whenNotPaused, always returns true — the lazy walk completes in one call.
GaugeController — Administration¶
| Function | Gate | Effect |
|---|---|---|
addGauge(gauge) |
GAUGE_ADMIN, whenNotPaused |
registers a gauge, bootstraps its checkpoint pointer and its distributor epoch |
removeGauge(gauge, recipient) |
GAUGE_ADMIN |
kicks the weight, unregisters, and redirects the unclaimed entitlement to recipient |
setDistributor(addr) |
GAUGE_ADMIN |
points the controller at a rewards distributor |
setMaxVoteBuckets(k) |
GAUGE_ADMIN |
0 = exact per-lock scheduling; 2–64 = bucketed |
setBoostAmplificationEnabled(gauge, bool) |
GAUGE_ADMIN |
per-gauge boost amplification toggle |
setEmergencyGaugePause(gauge, bool) |
EMERGENCY_ADMIN |
flips one gauge's own switch |
setEmergencyGaugePauseBatch(gauges[], status[]) |
EMERGENCY_ADMIN |
same, for a caller-chosen list |
setEmergencyPause(bool) |
EMERGENCY_ADMIN |
the global switch |
addGauge refuses a paused gauge
A gauge that is removed while paused must be unpaused first — which is why setEmergencyGaugePause(gauge, false) is allowed on an unregistered gauge. Without that exception the gauge would be permanently stuck.
removeGauge and the pause functions are not whenNotPaused
Both are deliberately usable during a global pause, so a broken gauge can be removed or isolated while everything else is frozen. setMaxVoteBuckets(1) reverts with InvalidWeight — K = 1 is the degenerate single-average case the per-lock design exists to avoid.
The batch pause is all-or-nothing
setEmergencyGaugePauseBatch reverts the whole call on a zero address, an unregistered gauge being paused, or any gauge already in its requested state. In an incident, a list containing one already-paused gauge fails entirely — build the list from current state.
GaugeController — Views¶
| Function | Returns | Notes |
|---|---|---|
getActiveGauges() |
(addresses, weights, totalWeight) | registered gauges, projected to the next epoch; paused gauges are included |
activeGaugeCount() |
uint256 | count of registered gauges |
isGaugeActive(gauge) |
bool | registered, not "unpaused" |
getRegisteredGaugeList() |
address[] | raw list |
getGaugeRelativeWeight(gauge, epoch) |
(weight, ...) | state-modifying — checkpoints lazily; whenNotPaused |
getGaugeRelativeWeightView(gauge, epoch) |
(weight, ...) | pure view; projects forward in memory with the same decay logic |
calculateBoost(user, gauge, amount) |
(boost, ...) | ungated view; reverts for an unregistered gauge |
getUserVotePowerRemaining(user) |
uint256 | unallocated basis points |
getUserGaugeVotePower(user, gauge) |
uint256 | the caller's allocation on a gauge |
getExpiredGaugeVotePower(user) |
(power, gauges[]) | allocation sitting on expired locks |
getCurrentEpoch() / getNextEpoch() |
uint256 | week boundaries |
getGaugeRelativeWeight writes
Despite the name it is not a view: it advances the gauge's checkpoint. Use getGaugeRelativeWeightView from off-chain callers and anything that must not mutate state.
BaseGauge — Staking¶
stake¶
stake(uint256 amount)
Summary Pulls staking tokens from the caller, settles their pending rewards, and refreshes their working balance at the current boost.
Guarded Method — whenGaugeLive, gaugeStarted, nonReentrant.
Reverts
| Error | Condition |
|---|---|
InvalidAmount() |
amount == 0 |
GaugeNotActive() |
the gauge is not registered on the controller |
GaugeNotStarted() |
before gaugeInitialStartTime |
EnforcedPause() |
either pause switch is on |
Blocked for the whole of either pause
Staking is frozen during a pause on purpose: the reward backlog accruing behind the pause stays with the stakers who were already there, rather than going to whoever enters to catch it.
withdraw¶
withdraw(uint256 amount)
Summary
Settles rewards into claimable, updates the working balance, and returns the staking tokens.
Guarded Method
gaugeStarted and nonReentrant only — deliberately not whenGaugeLive, so a pause can never trap principal.
Reverts — InvalidAmount() on zero; InsufficientStakedBalance() if amount exceeds the stake.
No distributor pull
withdraw settles rewards without pulling from the distributor, so it touches only ungated controller views and works in every pause state.
BaseGauge — Rewards¶
claimRewardToken¶
claimRewardToken() → uint256
Summary
Advances the period, pulls the gauge's share from the distributor if the gauge is registered, settles the caller's rewards and transfers them. RAAC rewards above raacAutolockMin are deposited into the Liquid Locker instead of transferred, while depositRAACOnClaim is set.
Guarded Method — whenGaugeLive, gaugeStarted, nonReentrant.
pullRewardsFromDistributor¶
pullRewardsFromDistributor() → uint256 transferred
Summary Permissionless. Advances the period and pulls this gauge's accrued share from the distributor, rolling it into the active period's rate.
Guarded Method — whenGaugeLive, gaugeStarted, nonReentrant.
Nothing is stranded by a pause
The gauge's share is held in the distributor across a pause. The first call after the pause is lifted walks every missed epoch and collects the backlog.
claimOnBehalfOf¶
claimOnBehalfOf(address token, uint256 amount, address user) → uint256
Summary Lets the Liquid Locker claim from its pooled position and route the tokens to an individual user. Works for the main reward token and for registered extra tokens.
Guarded Method — CLAIM_FOR_ROLE, whenGaugeLive, gaugeStarted.
Reverts — InvalidAmount() on zero or when amount exceeds the claimable; InvalidAddress() for a zero user or an unregistered token.
Reward state settles against the caller, not user
The reward accounting (_updateUserReward, the claimable ledger, _updateWorkingBalance) is applied to msg.sender — the locker — while the tokens are transferred to user. That is correct for a pooled position, where the locker holds the stake and user is only a payout destination, but integrators should not read it as a per-user claim.
claimRewardsFor / updateUserRewards¶
| Function | Gate | Effect |
|---|---|---|
claimRewardsFor(user) |
DISTRIBUTOR_ROLE, whenGaugeLive |
batch-claims the main reward token for a user |
updateUserRewards(user) |
permissionless, whenGaugeLive |
settles a user at their current working balance, then refreshes it |
updateUserRewards is also the kick
Refreshing a staker whose veRAAC lock has decayed writes their working balance down, so they stop accruing on a stale boost. It is gated by whenGaugeLive so it cannot be used against stakers during a pause, and it reverts with NotKickable for the Liquid Locker.
Extra reward tokens¶
| Function | Gate | Notes |
|---|---|---|
addRewardToken(token) |
GAUGE_ADMIN |
max MAX_EXTRA_REWARDS (8); reverts FailedToGetTokenDecimals if decimals() is missing |
depositRewardToken(token, amount) |
an approved distributor of this gauge | funds an extra-token stream |
claimExtraRewards() |
staker, whenGaugeLive |
claims every extra token |
claimExtraReward(token) |
staker, whenGaugeLive |
claims one |
Extra rewards stream on raw stake, not working balance
Extra reward tokens are distributed against staked LP balances rather than boosted working balances, so veRAAC boost does not apply to them.
BaseGauge — Administration¶
| Function | Gate | Effect |
|---|---|---|
manageDistributor(distributor, bool) |
GAUGE_ADMIN |
adds or removes an approved extra-reward distributor |
setController(addr) |
GAUGE_ADMIN |
repoints the gauge at a controller |
setDepositRAACOnClaim(bool) |
DEFAULT_ADMIN_ROLE |
toggles RAAC autolock on claim |
setRaacAutolockMin(uint256) |
DEFAULT_ADMIN_ROLE |
threshold below which RAAC is transferred raw |
setGaugeRewardDistributor(addr) |
DEFAULT_ADMIN_ROLE |
moves DISTRIBUTOR_ROLE to a new distributor |
setEmergencyPaused(bool) |
CONTROLLER_ROLE |
this gauge's own switch; reverts AlreadyPaused/NotPaused |
BaseGauge — Views¶
| Function | Returns | Notes |
|---|---|---|
balanceOf(account) |
uint256 | working balance — what rewards accrue on |
rawBalanceOf(account) |
uint256 | actual staked amount |
totalSupply() / rawTotalSupply() |
uint256 | working supply / raw staked supply |
earnedReward(token, user) |
uint256 | settled main-token rewards |
pendingRewards(token, account) |
uint256 | main-token rewards not yet settled |
pendingExtraRewards(token, user) |
uint256 | one extra token |
allPendingExtraRewards(user) |
(tokens[], amounts[]) | every extra token at once |
getPeriodBoundaries() |
(start, end) | current period |
periodDuration() |
uint256 | 7 days or 14 days |
getRewardTokens() / getExtraRewardTokens() |
address[] | main token first |
getUserWeight(account) |
uint256 | the account's voting weight on this gauge |
isPaused() |
bool | this gauge's own switch only |
balanceOf is the working balance
BaseGauge.balanceOf does not return the staked amount. Integrators wanting the stake must call rawBalanceOf.
GaugeRewardsDistributor¶
deposit¶
deposit(address token, uint256 amount)
Summary
Permissionless. Records amount of the silo's reward token against the current epoch. It is not split across gauges — each gauge computes its own share when it claims.
Reverts
| Error | Condition |
|---|---|
TokenNotApproved() |
token is not this silo's reward token |
InvalidAmount() |
zero amount |
Unauthorized() |
no gauge is registered on the controller |
claimForGauge¶
claimForGauge(address gauge) → uint256 totalClaimed
Summary
Walks every epoch since the gauge's last claim, sizing its share at each as deposited × relativeWeight / 1e18, and transfers the total.
Guarded Method
Callable only by the gauge itself (msg.sender != gauge reverts). nonReentrant.
Reverts — Unauthorized() for any other caller; GaugeNotActive() if the gauge is unregistered.
Double-claim guard
Each claim snapshots depositedAtEpoch in depositedSnapshotAtLastClaim. An epoch whose deposit has not grown since the last claim is skipped, so a gauge cannot drain the same deposit twice.
The walk is unbounded unless capped
With enableMaxClaimBound off, a gauge with a very stale lastClaimedEpoch walks every epoch in one call. setMaxClaimBound(true) caps it at MAX_EPOCHS (52), at the cost of needing repeated calls to catch up.
Administration & recovery¶
| Function | Gate | Effect |
|---|---|---|
setLastClaimedEpoch(gauge) |
CONTROLLER_ROLE |
initialises a new gauge's pointer; no-op if already set |
setMaxClaimBound(bool) |
DISTRIBUTOR_ADMIN |
caps the epoch walk at MAX_EPOCHS |
setController(addr) |
DEFAULT_ADMIN_ROLE |
repoints at a controller |
withdrawRemovedGaugeRewards(gauge, recipient) |
CONTROLLER_ROLE |
pays a removed gauge's unclaimed entitlement out; called by removeGauge |
withdrawDust(recipient) |
DEFAULT_ADMIN_ROLE |
sweeps deposits from epochs every registered gauge has already passed |
claimAll(gauges[]) |
permissionless | calls claimRewardsFor(msg.sender) on each gauge |
getClaimableForGauge(gauge) |
view | estimate of a gauge's unclaimed share |
getRewardTokenInfo() |
view | totals deposited, distributed, claimable and the live balance |
withdrawDust is bounded by the laggard
The sweep only covers epochs below the lowest lastClaimedEpoch across every registered gauge. One gauge that never claims holds the sweep back indefinitely. It reverts with Unauthorized when there is nothing new to process.
Errors¶
| Error | Meaning |
|---|---|
EnforcedPause() |
a gauge entry point was called while either switch is on |
GaugePaused() |
non-zero vote on an emergency-paused gauge |
GaugeNotActive() |
the gauge is not registered |
GaugeNotStarted() |
before the gauge's initial start time |
ActiveGauge() |
removePowerFromGauge on a still-registered gauge |
VoteDelayNotElapsed() |
inside the 10-day per-gauge vote cooldown |
UserStillHasPower() |
kick on a user with no pending ragequit |
NoPower() |
no allocation to release |
NotKickable() |
updateUserRewards on the Liquid Locker |
InvalidWeight() |
weight out of range, or setMaxVoteBuckets(1) / > 64 |
InvalidVeToken() |
veRAAC's max lock is not 52 weeks |
InsufficientStakedBalance() |
withdrawing more than staked |
MaxExtraRewardsReached() |
more than 8 extra reward tokens |
TokenAlreadyInitialized() / NotSupportedToken() |
extra-token registry |
FailedToGetTokenDecimals() |
the token has no decimals() |
DistributorNotAdded() / DistributorNotApproved() |
depositRewardToken from an unapproved source |
TokenNotApproved() |
wrong token deposited into the distributor |
AlreadyPaused() / NotPaused() |
gauge already in the requested pause state |
ArrayLengthMismatch() |
batch arrays differ in length |
Unauthorized() / InvalidAddress() / InvalidAmount() / InvalidGauge() |
argument and caller validation |