Functions¶
Note
Collateral in the locker lives in epoch buckets. Every function that changes a position first calls _updateUnlocked to mature any buckets whose epoch has passed, then _updateReward to settle the caller's share of the reward stream against their debt. Read Unlock Scheduling before using requestUnlock and withdrawUnlocked.
Everything here is whenNotPaused
Every state-changing entry point on this page — including repay and withdrawUnlocked — is gated by the pause. PAUSER_ROLE can pause; only the owner can unpause.
Collateral¶
deposit¶
deposit(uint256 _amount)
Summary
Pulls _amount RAAC from the caller, locks it into veRAAC for lockDurationEpochs, and records an epoch bucket maturing at the resulting week boundary. If the caller's most recent bucket already sits at that epoch, the amounts merge.
Guarded Method
Callable by any address. Subject to whenNotPaused, nonReentrant and a non-zero amount check.
Parameters
| Name | Type | Description |
|---|---|---|
_amount |
uint256 | RAAC to deposit; must clear veRAAC's minLockAmount |
Emits — Deposit(address indexed depositor, address indexed recipient, uint256 amount)
Reverts
| Error | Condition |
|---|---|
NonZeroValue() |
_amount == 0 |
AmountBelowMinimum() |
propagated from veRAAC when below minLockAmount |
Typescript / ethers
depositOnBehalfOf¶
depositOnBehalfOf(address _user, uint256 _amount)
Summary
Same as deposit, but credits the position to _user while pulling the RAAC from the caller. This is how a gauge converts a user's claimed RAAC emissions straight into a locker position.
Guarded Method
Callable by _user themselves, or by any address holding ON_BEHALF_OF_ROLE. Subject to whenNotPaused and nonReentrant.
Parameters
| Name | Type | Description |
|---|---|---|
_user |
address | Account credited with the position |
_amount |
uint256 | RAAC to deposit, pulled from msg.sender |
Emits — Deposit(address indexed depositor, address indexed recipient, uint256 amount)
Reverts — Unauthorized() when the caller is neither _user nor a role holder.
No zero-amount guard
Unlike deposit, this function carries no checkNotZeroValue modifier. A zero amount reaches veRAAC, which rejects it.
requestUnlock¶
requestUnlock(uint256 _amount)
Summary
Reserves _amount of the caller's locked RAAC for withdrawal, consuming soonest-maturing buckets first and partial-reserving the boundary bucket. Reserved collateral stops backing debt immediately; the open remainder of each bucket continues to auto-relock.
If reserving only part of a bucket would leave an open remainder below veRAAC's minLockAmount, the whole bucket is reserved instead — which means the amount actually scheduled can exceed _amount.
Guarded Method
Callable by any depositor. Subject to whenNotPaused, nonReentrant and a non-zero amount check.
Parameters
| Name | Type | Description |
|---|---|---|
_amount |
uint256 | RAAC to schedule for unlock |
Emits — Unlock(address indexed account, uint256 amount) carrying the amount actually scheduled
Reverts
| Error | Condition |
|---|---|
NonZeroValue() |
_amount == 0 |
InsufficientCollateral() |
reserving would leave unreserved locked below 2 × debt |
InsufficientToUnlock() |
scheduled less than _amount, or overshot it by more than minLockAmount |
Matured buckets cannot be reserved
_scheduleUnlock skips any bucket whose unlockEpoch <= currentEpoch. Collateral already past its epoch boundary has auto-relocked into a new bucket and must be reserved there.
withdrawUnlocked¶
withdrawUnlocked()
Summary
Withdraws the caller's matured reserved RAAC. Before paying out, the function computes how much collateral the outstanding debt still requires and relocks the shortfall through _deposit, transferring only the safe remainder.
Guarded Method
Callable by any depositor with matured reservations. Subject to whenNotPaused and nonReentrant.
Emits
Withdraw(address indexed account, uint256 amount)— for the payout legDeposit(...)— when part of the amount is relocked
Reverts
| Error | Condition |
|---|---|
InsufficientUnlocked() |
nothing has matured |
AmountBelowMinimum() |
propagated from veRAAC when the relock leg is below minLockAmount |
A dust relock can block the withdrawal entirely
If the amount that must be relocked is smaller than veRAAC's minLockAmount, the inner lock reverts and the whole withdrawal fails. The position self-heals as the reward stream repays the debt, and repaying manually clears it immediately. This is accepted — see Design Trade-offs.
previewWithdraw¶
previewWithdraw(address _account) → (uint256 payout, uint256 relock)
Summary
Projects the split withdrawUnlocked would produce at the account's current debt, using the identical _splitWithdraw helper so the preview always matches the outcome. Returns (0, 0) when nothing has matured.
Debt¶
borrow¶
borrow(uint256 _amount, bool _depositMaturityVault)
Summary
Mints _amount of leRAAC to the caller, or deposits it into the Maturity Vault on their behalf. Accrued rewards are consumed first and do not become debt; only the shortfall increases totalDebt and is health-checked against unreserved collateral.
Guarded Method
Callable by any depositor. Subject to whenNotPaused, nonReentrant and a non-zero amount check.
Parameters
| Name | Type | Description |
|---|---|---|
_amount |
uint256 | leRAAC to borrow |
_depositMaturityVault |
bool | true routes the minted leRAAC into the maturity vault for the caller |
Emits — Borrow(address indexed caller, address indexed recipient, uint256 amount)
Reverts
| Error | Condition |
|---|---|
NonZeroValue() |
_amount == 0 |
InsufficientCollateral() |
unreserved locked would fall below 2 × debt |
Borrowing after a reservation is allowed
Reserved collateral is netted out of the health check, but a position can still end up undercollateralised later as buckets mature. That is deliberate — see Design Trade-offs.
repay¶
repay(uint256 _leRAACAmount)
Summary
Repays outstanding debt by burning the caller's leRAAC. The amount is capped at the caller's debt. A repayFeePercentage cut is transferred to the treasury as leRAAC and only the remainder is burned and deducted from the debt.
Guarded Method
Callable by any borrower. Subject to whenNotPaused, nonReentrant and a non-zero amount check.
Parameters
| Name | Type | Description |
|---|---|---|
_leRAACAmount |
uint256 | leRAAC to repay; capped at outstanding debt |
Emits — Repay(address indexed account, uint256 amount) carrying the net amount burned
A non-zero repay fee makes full repayment impossible
Because the fee is taken out of the repayment rather than charged on top, only amount - fee ever reduces the debt. The parameter is held at 0 in production for this reason. See Design Trade-offs.
Silent no-op when debt-free
The whole body is wrapped in if (_totalDebt > 0). Calling repay with no debt succeeds, emits nothing and moves no tokens.
Rewards¶
harvest¶
harvest(address _recipient, uint256 _minimunOut) → uint256
Summary Claims every veRAAC reward token, swaps non-RAAC rewards to the intermediate token and then to RAAC through the Zap, applies the treasury fee and harvest bounty, and distributes the remainder to depositors and the maturity vault.
Guarded Method
Requires HARVEST_ROLE. Subject to whenNotPaused.
Parameters
| Name | Type | Description |
|---|---|---|
_recipient |
address | Receives the harvest bounty |
_minimunOut |
uint256 | Minimum RAAC out of the Zap leg |
Returns — total RAAC harvested, pre-fees, including RAAC-denominated rewards.
Emits
Harvest(address indexed caller, uint256 reward, uint256 platformFee, uint256 harvestBounty)ClaimRewardClaimed(address indexed token, uint256 amountClaimed)/ClaimRewardFailed(address indexed token, string reason)per token
Reverts
| Error | Condition |
|---|---|
InvalidLength() |
claim results do not match the reward-token list |
InsufficientOutput() |
Zap output below _minimunOut |
BotLockerHarvestError() |
bot-locker distribution failed with an unrecognised reason |
_minimunOut does not cover RAAC-denominated rewards
The check runs on the swapped amount before direct RAAC rewards are added. Rewards already in RAAC bypass the Zap and are outside the slippage bound.
donate¶
donate(uint256 _amount)
Summary Pulls RAAC from the caller and pushes it straight into the reward stream, repaying depositor debt without going through a harvest. Fees and the bounty are not applied.
Guarded Method
Requires DEFAULT_ADMIN_ROLE. Subject to whenNotPaused, nonReentrant and a non-zero amount check.
Gauge Operations¶
depositToGauge¶
depositToGauge(address _token, address _gauge, uint256 _amount)
Summary Pulls LP tokens from the caller and stakes them into a gauge from the locker's address, so the locker's boost applies. The token is checked against veRAAC's reward-token list and rejected if it appears there.
Guarded Method — BOOSTER_ROLE, whenNotPaused, nonReentrant, non-zero amount.
Emits — DepositedToGauge(address indexed gauge, uint256 amount, address token)
Reverts — InvalidToken() if _token is a registered veRAAC reward token.
No fee-on-transfer support
The staked amount is the requested amount, not the observed balance delta.
withdrawFromGauge¶
withdrawFromGauge(address _token, address _gauge, uint256 _amount, uint256 _minOut, address _to)
Summary
Withdraws _amount of LP tokens from a gauge and forwards them to _to.
Guarded Method — BOOSTER_ROLE, whenNotPaused, nonReentrant, non-zero amount.
Reverts — ZeroAddress() for a zero _to; InsufficientOutput() when _amount < _minOut.
_minOut compares against the requested amount
The gauge withdrawal is assumed to return exactly _amount, so the check is _amount < _minOut rather than a comparison against an observed balance delta.
claimRewardFor¶
claimRewardFor(address _user, uint256 _amount, address _gauge, address _token) → uint256
Summary
Calls claimOnBehalfOf on a gauge so a user's rewards are claimed through the locker.
Guarded Method — BOOSTER_ROLE, whenNotPaused, non-zero amount.
Emits — RewardClaimedFor(address indexed user, address gauge, address token, uint256 claimedAmount)
Operations¶
processVaultExpiredLocks¶
processVaultExpiredLocks()
Summary Matures the locker's own veRAAC position: advances global epoch accounting, remaps expired lock ends, withdraws whatever veRAAC has matured, and relocks everything above the liquidity buffer.
The buffer retained is totalUnlockedGlobal + pendingCumulatedAmount. The excess is relocked only if it clears veRAAC's minLockAmount.
Guarded Method
Requires the keeper whitelist (isKeeper). Subject to whenNotPaused and nonReentrant.
Emits — VaultExpiredLocksProcessed(uint256 amount)
Never blocks on an empty maturity
veRAAC.withdraw() reverts when nothing has expired, so the call is wrapped in a try and returns early instead of failing the keeper's transaction.
updateGlobals¶
updateGlobals()
Summary
Permissionless. Advances totalLockedGlobal and totalUnlockedGlobal across every scheduled epoch end that has passed. Called internally by _distribute and processVaultExpiredLocks; exposed so anyone can keep global accounting current.
Voting¶
| Function | Gate | Effect |
|---|---|---|
voteGauge(controller, gauge, weight) |
DELEGATE_ROLE |
casts the pooled position's gauge weight vote |
voteGovernance(governance, proposalId, support) |
DELEGATE_ROLE |
casts a governance vote, returns the power used |
removePowerFromGauge(controller, gauge) |
DELEGATE_ROLE |
clears the locker's weight from a retired gauge |
addGauge(gauge) |
OPERATOR_ROLE |
grants ON_BEHALF_OF_ROLE to a gauge |
All four revert with ZeroAddress() on a zero argument; the first three are whenNotPaused.
Administration¶
| Function | Gate | Validation |
|---|---|---|
pause() |
PAUSER_ROLE |
— |
unpause() |
onlyOwner |
— |
updateWhitelist(keeper, status) |
onlyOwner |
none |
updateZap(zap) |
onlyOwner |
non-zero |
updateTreasury(treasury) |
onlyOwner |
non-zero |
updateBotLocker(locker) |
onlyOwner |
non-zero |
updateCleverFeeRecipient(recipient) |
onlyOwner |
non-zero |
updateIntermediateRewardToken(token) |
onlyOwner |
non-zero, and the Zap must have a route from the new token to RAAC and from every reward token to it |
updateRepayFeePercentage(pct) |
onlyOwner |
<= MAX_REPAY_FEE (10 %) |
updateHarvestBountyPercentage(pct) |
onlyOwner |
<= MAX_HARVEST_BOUNTY (10 %) |
updateFeeShares(bots, clever, treasury, total) |
onlyOwner |
total <= MAX_TREASURY_FEE (50 %) and the three shares must sum to total |
toggleMaxDistributions(enable) |
DEFAULT_ADMIN_ROLE |
— |
setMaxHarvestDistribution(n) |
DEFAULT_ADMIN_ROLE |
1 <= n <= 100 |
setMaxHarvestDistribution bounds a count, not an amount
Despite the name, maxDistribution caps the number of veRAAC distributions processed per reward token in one harvest, which is what keeps harvest inside the block gas limit. It only applies while toggleMaxDistributions(true) is set.
Views¶
| Function | Returns | Notes |
|---|---|---|
getUserInfo(account) |
5 × uint256 | totalDeposited, totalPendingUnlocked, totalUnlocked, totalBorrowed, totalReward — simulates maturity and reward settlement, so it is current without a write |
checkAccountHealth(account) |
bool | false means unreserved collateral no longer covers 2 × debt |
previewWithdraw(account) |
(payout, relock) | mirrors withdrawUnlocked exactly |
getUserLocks(account) |
EpochUnlockInfo[] |
future buckets only; matured ones are excluded |
poolLockedUnderlying() |
uint256 | reads veRAAC.lockedBalanceOf(locker) — authoritative, drift-free |
totalRaacInPool() |
uint256 | raw RAAC balance held by the locker |
getCurrentEpoch() |
uint256 | block.timestamp / 7 days |
userInfo(account) |
struct fields | raw storage; does not simulate maturity — prefer getUserInfo |
userInfo is raw, getUserInfo is settled
The public userInfo mapping returns stored values that may be stale by several epochs. Integrators should read getUserInfo, which replays maturity and reward settlement in memory.
Data Structures¶
EpochUnlockInfo¶
| Field | Type | Description |
|---|---|---|
pendingUnlock |
uint128 | total RAAC in this bucket |
reservedAmount |
uint128 | portion reserved for withdrawal; 0 = fully open, == pendingUnlock = fully reserved |
unlockEpoch |
uint64 | week index at which the bucket matures |
UserInfo¶
| Field | Type | Description |
|---|---|---|
totalDebt |
uint128 | outstanding leRAAC debt |
rewards |
uint128 | accrued rewards not yet spent against debt |
rewardPerSharePaid |
uint256 | reward-per-share checkpoint |
totalLocked |
uint112 | RAAC currently locked |
totalUnlocked |
uint112 | matured reserved RAAC awaiting withdrawal |
nextUnlockIndex |
uint32 | cursor past consumed buckets |
lastProcessedEpoch |
uint64 | last epoch settled for this account |
pendingUnlockList |
EpochUnlockInfo[] |
the buckets |
Errors¶
| Error | Meaning |
|---|---|
NonZeroValue() |
a zero amount was supplied |
Unauthorized() |
caller lacks the keeper whitelist or ON_BEHALF_OF_ROLE |
InsufficientCollateral() |
the 2 × debt health check failed |
InsufficientToUnlock() |
the scheduled reservation missed the requested amount |
InsufficientUnlocked() |
nothing matured to withdraw |
InsufficientOutput() |
swap or gauge withdrawal below the minimum |
FeeTooLarge() |
a fee parameter exceeded its cap |
FeeSharesNotMatchingTreasuryFee() |
the three fee shares do not sum to the treasury fee |
NoRoutesFound() |
the Zap has no route for a reward token |
InvalidToken() |
a veRAAC reward token was passed to depositToGauge |
InvalidLength() |
claim results did not match the reward-token list |
LockParamsMismatch() |
veRAAC's epoch duration or max lock epochs did not match at initialize |
BotLockerHarvestError() |
bot-locker distribution reverted with an unrecognised reason |
ZeroAddress() / InvalidAmount() / InvalidAddress() |
argument validation |