For the complete documentation index, see llms.txt. This page is also available as Markdown.

Staking Contracts Reference

Technical reference for StakedUSDx, the V2 rewards vault. For design rationale, see Architecture. For usage instructions, see Stake & Unstake. Terminology follows the Protocol Taxonomy (the V2 single source of truth) rather than being redefined here.

V2 collapses the V1 staking system into a single contract. The V1 layout, a base vault (StakedAxisUSD), a cooldown subclass (StakedAxisUSDV2), a separate escrow (AxisUSDSilo), and an external distributor (StakingRewardsDistributor), no longer exists. In V2 there is no silo: redemption liabilities are held in-vault as pending/claimable buckets. There is no separate distributor: rewards are funded directly into the vault by a REWARD_MANAGER_ROLE holder. Withdrawals are async (ERC-7540) rather than a synchronous cooldown-then-unstake flow.


StakedUSDx

Verified source: read on Etherscan Inherits: Initializable, ERC4626Upgradeable, AccessControlUpgradeable, IStakedUSDx

An upgradeable ERC-4626 rewards vault. Users deposit USDx (the asset) and receive sUSDx (the share, ERC-20 name/symbol "Staked USDx" / "sUSDx"). Shares appreciate as funded rewards vest linearly into vault accounting. Exits are asynchronous: a holder calls requestRedeem, waits out a cooldown, a servicer makes the request claimable, and the holder then claims. The vault is its own ERC-7575 share token, share() returns address(this), and implements the ERC-7540 async-redeem interfaces (IERC7540Redeem, IERC7540Operator).

Deployed behind a TransparentUpgradeableProxy with a governance-owned ProxyAdmin; the implementation disables initializers in its constructor and is set up once via initialize.

Roles

Role
Constant
Authority

DEFAULT_ADMIN_ROLE

bytes32(0)

Admin over every other role; configures rewards, redemption controls, and policies

REWARD_MANAGER_ROLE

keccak256("REWARD_MANAGER_ROLE")

Funds rewards via fundRewards

REDEMPTION_SERVICER_ROLE

keccak256("REDEMPTION_SERVICER_ROLE")

Services the redemption queue (pending → claimable)

PAUSER_ROLE

keccak256("PAUSER_ROLE")

Pauses deposits, reward funding, and each redemption sub-flow

RESTRICTION_ROLE

keccak256("RESTRICTION_ROLE")

Sets/clears soft and full restrictions; redistributes shares and requests off frozen accounts

At initialize(asset_, admin, rewardDuration_), the admin address is granted all five roles. See the taxonomy for the intended production custody of each role at launch.

Constants

Constant
Value
Description

MAX_COOLDOWN_DURATION

90 days

Upper bound for the redemption cooldown

DEFAULT_REDEMPTION_POLICY_ID

bytes32("DEFAULT")

Id of the always-enabled default redemption policy

REQUEST_ID

uint256 (starts at 1)

Public monotonic counter; next request id to be minted

WAD

1e18

Fixed-point scale for exchangeRate (internal)

MAX_SERVICE_SCAN_REQUESTS

256

Per-call scan bound in serviceRedemptions (internal)

Accounting model

sUSDx is priced against accountedAssets, active principal plus already-vested rewards, not the raw USDx balance. Unvested rewards and reserved redemption liabilities are deliberately excluded from the share price so servicing, claims, and vesting can never spend the same USDx twice.

Read
Meaning

totalAssets()

accountedAssets including rewards vested up to now; drives ERC-4626 conversions

accountedAssets()

Active principal + vested rewards backing sUSDx

pendingRedeemAssets()

USDx reserved for PENDING requests (shares already burned, value moved out of accountedAssets)

claimableRedeemAssets()

USDx reserved for serviced CLAIMABLE requests, awaiting claim

exchangeRate()

18-decimal USDx-per-sUSDx (totalAssets · 1e18 / totalSupply, floor); returns 1e18 when supply is 0

getVaultState()

Full snapshot: supply, assets, both liability buckets, redeemLiabilities, requiredAssets, assetBalance, exchangeRate, cooldown, solvent

Solvency invariant: assetBalance >= requiredAssets, where requiredAssets = accountedAssets + pendingRedeemAssets + claimableRedeemAssets + unvestedRewards. redeemLiabilities = pendingRedeemAssets + claimableRedeemAssets.

decimals() is 18. convertToShares/convertToAssets use floor rounding against accountedAssets. previewWithdraw() and previewRedeem() always revert (OperationNotAllowed) because claims are async and share-exact rather than previewable.

Deposit (stake)

Synchronous ERC-4626 entry, USDx in, sUSDx out immediately.

Function
Access
Description

deposit(uint256 assets, address receiver)

external

Pull USDx, checkpoint rewards, credit accountedAssets, mint shares

mint(uint256 shares, address receiver)

external

Same, denominated in shares

maxDeposit / maxMint

view

Return 0 when deposits are paused or the receiver is restricted, else type(uint256).max

Deposits revert while depositPaused is set or when the caller or receiver is restricted.

Reward funding & vesting

Rewards are funded directly into the vault, there is no external distributor. A REWARD_MANAGER_ROLE holder transfers USDx in and folds it into a linear vesting schedule.

Function
Access
Description

fundRewards(uint256 assets, bytes32 sourceRef)

REWARD_MANAGER_ROLE

Checkpoint vested rewards, pull assets, increment unvestedRewards, recompute rewardRate, set periodFinish

setRewardConfig(uint256 duration, uint256 maxRate, bool paused)

DEFAULT_ADMIN_ROLE

Set vesting duration, optional maxRewardRate cap, and funding-pause; reschedules active rewards

pauseRewardFunding()

PAUSER_ROLE

Pause funding and freeze the active vesting schedule

rewardConfig() / rewardState()

view

Report authority/duration/maxRate/paused, and live unvestedRewards/rewardRate/periodFinish

Behavior: fundRewards reverts if reward funding is paused, assets or rewardDuration is zero, or totalSupply() is zero. A top-up folds into the active vesting window (it does not reset the clock, see _activeRewardDuration) and is rejected if the resulting rewardRate would exceed a non-zero maxRewardRate. Vested rewards are materialized into accountedAssets on each checkpoint (deposit, request, or fund); unvested rewards stay outside the share price until they drip in. After periodFinish, rewardRate drops to zero and any remainder is treated as fully vested.

Emits RewardsFunded(funder, assets, sourceRef, rewardRate, periodFinish) and RewardConfigUpdated(authority, duration, maxRate, paused).

Async redemption lifecycle (ERC-7540)

Exits are a three-step async flow. Shares are burned up front at request time; USDx moves through the in-vault pending and claimable liability buckets and is transferred out only at claim.

1, Request. requestRedeem(uint256 shares, address controller, address owner) burns the owner's shares, moves the corresponding assets from accountedAssets to pendingRedeemAssets, and records a RedemptionState (shares, assetsReserved, requestedAt, eligibleAt = requestedAt + policy.cooldown, policyId, status = PENDING). Returns a monotonic requestId. Fully restricted owners/controllers cannot open requests; a soft-restricted owner may only exit to their own controller. Reverts if requests are paused, on zero inputs, if the caller is not the owner/controller or their approved operator, if the owner's balance is short (MinSharesViolation), or if shares exceed the policy's maxRequestShares. Emits RedeemRequest.

2, Service. A REDEMPTION_SERVICER_ROLE holder moves eligible pending requests to CLAIMABLE, shifting reserved USDx from pendingRedeemAssets to claimableRedeemAssets. This is the in-vault replacement for V1's silo transfer.

Function
Access
Description

serviceRedemptions(uint256 maxShares)

REDEMPTION_SERVICER_ROLE

Batched scan in request-id order; services eligible requests up to maxShares (soft cap) and MAX_SERVICE_SCAN_REQUESTS (256) scanned per call

serviceRedeemRequest(uint256 requestId, address controller)

REDEMPTION_SERVICER_ROLE

Service one specific eligible request by id and controller

serviceRedemptions skips requests still in cooldown but carries a forward-progress guarantee: the first eligible request in a batch is always serviced, so a single oversized request can never permanently stall the queue (maxShares is a soft batch cap, not a per-request maximum, per-request size is bounded by policy.maxRequestShares). Both forms revert if servicing is paused and emit RedemptionsServiced(serviceId, requestsServiced, sharesMadeClaimable, assetsReserved).

3, Claim. In this async vault withdraw and redeem are claim-only, they release already-serviced (CLAIMABLE) assets rather than exiting live shares (which were burned at request time).

Function
Access
Description

redeem(uint256 shares, address receiver, address controller)

controller or approved operator

Share-exact claim (floor rounding across serviced lots); returns assets

withdraw(uint256 assets, address receiver, address controller)

controller or approved operator

Asset-denominated claim; partial lots round shares up and may revert, use redeem for share-exact partials

maxRedeem(controller) / maxWithdraw(controller)

view

Controller's total claimable shares / assets; return 0 when claims are paused or the controller is fully restricted

withdraw(maxWithdraw(controller)) (every lot consumed in full) always succeeds; some intermediate asset amounts below the max may revert on per-lot share rounding. Claims revert while claims are paused, on zero inputs, or on restriction violations (fully restricted controller/receiver/owner; soft-restricted parties may only settle to themselves). Both emit Withdraw(sender, receiver, controller, assets, shares).

Cancel. cancelRedeemRequest(uint256 requestId, address controller) cancels a still-PENDING request: it sets status CANCELLED, releases reserved assets back to accountedAssets, and re-mints the burned shares to the owner. Blocked when cancellations are paused or when the controller/owner is fully restricted (shares would return to a frozen holder). Emits RedeemRequestCancelled.

Operators. setOperator(address operator, bool approved) lets a controller authorize an operator to act on its redemption requests and claims; isOperator(controller, operator) reads the flag. Emits OperatorSet.

Request reads: pendingRedeemRequest(requestId, controller), claimableRedeemRequest(requestId, controller), getRedeemRequest(controller) (aggregate RedeemRequestState), redemptionState(requestId, controller) (full RedemptionState).

Redemption controls & policies

Cooldown, servicing bounds, and pause flags are global controls; per-account overrides ride on named policies.

Function
Access
Description

setRedemptionControls(cooldown, maxServiceBatchShares, requestPaused, claimPaused, servicePaused, cancelPaused, serviceAuthority)

DEFAULT_ADMIN_ROLE

Set global cooldown, batch cap, all pause flags, and service-authority metadata

setCooldownDuration(uint256 duration)

DEFAULT_ADMIN_ROLE

Update the default cooldown (≤ MAX_COOLDOWN_DURATION) while preserving other controls

setRedemptionPolicy(bytes32 policyId, RedemptionPolicy policy)

DEFAULT_ADMIN_ROLE

Create/update a named policy (cooldown, maxRequestShares, enabled); default policy cannot be disabled

setAccountRedemptionPolicy(address account, bytes32 policyId)

DEFAULT_ADMIN_ROLE

Assign an enabled policy to an account, or clear to the default

pauseRedeemRequests / pauseRedeemClaims / pauseRedemptionServicing / pauseRedeemCancellations

PAUSER_ROLE

Pause each redemption sub-flow independently

redemptionControls() / redemptionPolicy(policyId) / accountRedemptionPolicy(account)

view

Read current controls, a policy, or an account's assigned policy

A request snapshots its eligibleAt from the policy in force at request time, so later cooldown changes affect only future requests. The cooldown is configurable up to MAX_COOLDOWN_DURATION (90 days); the current value is 7 days. Emits RedemptionControlsUpdated, RedemptionPolicyUpdated, AccountRedemptionPolicyUpdated, CooldownDurationUpdated, and RedemptionPauseUpdated.

Restrictions

Two tiers, soft (blocks entry and non-self exit) and full (freezes the account), gated by RESTRICTION_ROLE. Enforcement is centralized in the _update share-transfer hook plus per-action checks.

Function
Access
Description

addToBlacklist(address target, bool isFullRestriction)

RESTRICTION_ROLE

Apply soft or full restriction; emits RestrictionUpdated

removeFromBlacklist(address target, bool)

RESTRICTION_ROLE

Clear all restriction flags

redistributeLockedAmount(address from, address to)

RESTRICTION_ROLE

Move all liquid shares off a fully restricted account to an unrestricted recipient

redistributeRedeemRequest(uint256 requestId, address controller, address to)

RESTRICTION_ROLE

Relocate an in-flight pending/claimable request away from a frozen account

getAccountState(account) reports shareBalance, assetsValue, both liability buckets, restrictionStatus (CLEAR / RESTRICTED / FULL_RESTRICTION), blockedActions (NONE / NON_EXIT / ALL), and restrictionBasis.

Admin & introspection

Function
Access
Description

setDepositPause(bool paused)

DEFAULT_ADMIN_ROLE

Set the deposit pause flag; emits DepositPauseUpdated

pauseDeposits()

PAUSER_ROLE

Emergency deposit pause

rescueTokens(address token, uint256 amount, address to)

DEFAULT_ADMIN_ROLE

Rescue unrelated tokens or surplus USDx; reverts on the share token (InvalidToken) and cannot dip into requiredAssets

share()

view

Returns address(this) (ERC-7575 vault-is-share)

supportsInterface(bytes4)

view

Advertises ERC-7540 (IERC7540Operator, IERC7540Redeem), ERC-7575, IRedemptionManager, IRewardManager, IVaultShareToken, IStakedUSDx

Types

Events

Event
Source
Parameters

Deposit / Withdraw

ERC-4626

Standard deposit and claim events

RedeemRequest

ERC-7540

controller, owner, requestId, sender, shares

OperatorSet

ERC-7540

controller, operator, approved

RewardsFunded

IRewardManager

funder, assets, sourceRef, rewardRate, periodFinish

RewardConfigUpdated

IRewardManager

authority, duration, maxRate, paused

RedemptionsServiced

IRedemptionManager

serviceId, requestsServiced, sharesMadeClaimable, assetsReserved

RedeemRequestCancelled

IRedemptionManager

requestId, controller, owner, sharesReturned, assetsReleased

RedemptionControlsUpdated

IRedemptionManager

cooldown, batch cap, four pause flags, service authority

RedemptionPolicyUpdated

IRedemptionManager

policyId, cooldown, maxRequestShares, enabled

AccountRedemptionPolicyUpdated

IRedemptionManager

account, policyId

RedemptionPauseUpdated

IRedemptionManager

pause (which flag), paused

CooldownDurationUpdated

StakedUSDx

previousDuration, newDuration

RestrictionUpdated

StakedUSDx

target, status, basis, authorityRef

LockedAmountRedistributed

StakedUSDx

from, to, shares

RedeemRequestRedistributed

StakedUSDx

requestId, from, to, shares, assets, status

DepositPauseUpdated

StakedUSDx

paused

Rescue

StakedUSDx

token, to, amount

Errors

Error
Trigger

InvalidZeroAddress()

Zero address parameter

InvalidAmount()

Zero amount, zero reward duration, over-policy request, or over-surplus rescue

OperationNotAllowed()

Paused flow, restriction violation, disabled preview, or unauthorized caller

MinSharesViolation()

Redeem request exceeds the owner's share balance

InsufficientClaimable()

Claim exceeds the controller's serviced/claimable balance

InvalidToken()

rescueTokens targeting the share token

InvalidCooldown()

Cooldown greater than MAX_COOLDOWN_DURATION


Interfaces

StakedUSDx composes several V2 interfaces, each a frozen review surface:

Interface
Role

IStakedUSDx

Top-level vault ABI (composes the below)

IRewardManager

fundRewards, rewardConfig, rewardState, reward events

IRedemptionManager

Async redemption lifecycle, controls, policies, and events

IAsyncRedeemVault

Marker is IERC7540Redeem, the async-redeem surface

IVaultShareToken

Accounting reads (accountedAssets, requiredAssets, redeemLiabilities, …)

IERC7540Redeem / IERC7540Operator

ERC-7540 async-redeem and operator standard surfaces

Last updated