Staking
Last updated
Last updated
Allowlisted underlying token(s) can be staked in the safety module to help protect the protocol from a short fall event and earn rewards in return. There are 4 possible actions as a staker, aside from those typical of ERC-20 tokens:
Stake
Redeem
Start cooldown
Claim rewards
In the unlikely situation of a short fall event, governance can initiate an auction to sell off a necessary portion of the staked funds. There are 2 possible actions as an auction participant:
Buy lot(s)
Complete an expired auction
Anyone who owns an allowlisted staking token can stake their tokens on Increment by calling the stake
function in the StakedToken contract. The sequence of actions for staking is as follows:
The system first checks whether it is in a post slashing state (ie. the state after an auction has been initiated). If it is, then staking is not allowed and the transaction reverts with an error message, otherwise the check passes.
The shares to mint for the staked tokens is computed from the underlying staked amount and the exchange rate.
The system then checks whether the staked amount is greater than the max cap. If it is greater then the transaction reverts, otherwise the check passes.
The user's cooldown timestamp is updated if they are currently in a cooldown period.
Mint staked tokens and transfer in underlying staked amount.
The system updates user's staking position and rewards in the SafetyModule
The sequence of actions for redeeming the staked amount is as follows:
The system first checks whether it is in a post slashing state. If it is, then redeeming is allowed at any time. If it is not, the system then checks whether the cooldown period is over for the user, if the cooldown period is not over, the transaction reverts otherwise the system proceeds to the next check. The next check is the unstake window, if the unstake window has passed then the transaction reverts otherwise the check passes.
The amount of underlying tokens to redeem is computed from the staked amount and the exchange rate.
The user's cooldown timestamp is updated and if the remaining balance is zero then the cooldown timestamp is reset.
Burn staked tokens and transfer out underlying staked amount.
The system updates user's staking position and rewards in the SafetyModule
Users must start the cooldown period by calling the cooldown
function in the StakedToken contract, and then wait the duration of the period, before they can redeem their tokens. To discourage bad actors from potentially gaming the system by repeatedly entering the cooldown period without redeeming, entering cooldown also resets the user's reward multiplier to 1, though only after accruing any rewards they are due at their current multiplier. Rewards still accrue during the cooldown period and the subsequent unstake window.
After the cooldown period is over, the user has a time window in which to redeem their tokens. If they do not redeem their tokens during this time window, they will have to start the cooldown period over again.
Depending on the amount of reward tokens in the EcosystemReserve on Ethereum mainnet, stakers are eligible to earn rewards for providing economic security to the protocol. The system can support more than one reward token.
The speed of reward accrual for stakers is dependent on the reward distribution parameters set by governance and a multiplier score that is calculated for each user. The multiplier increases the user's reward and is calculated based on the following formula:
Users can redeem staked tokens by calling the redeem
function in the StakedToken contract. The sequence of actions for claiming staking rewards is as follows:
For each market (the system considers StakedToken contracts to be "markets", and still need to loop over them since each has its own reward accumulator), the system checks whether the current staking position is registered for rewards. If not, the transaction reverts.
For each reward token, update the reward accumulator for this market. Then compute the user's reward multiplier based on their staking duration and stake position. Add the rewards accrued by the user.
Check whether the Ecosystem Reserve has enough reward tokens. If so, then transfer all the user's rewards. If not, transfer any remaining reward tokens to user.
Subtract transferred amount from user's accrued rewards.
The auction module handles auctioning tokens slashed by the SafetyModule. In the event of an insolvency in the Vault which cannot be covered by the Insurance contract, the auction module can be triggered via governance by calling the slashAndStartAuction
function in the SafetyModule contract.
The auction mechanism is similar to a Dutch auction, but with the key difference that the price of a lot is fixed while the number of tokens in each lot increases over time, such that the price paid per token decreases over time. The auction ends when all lots are sold, when the auction time limit is reached, or if governance terminates it early. In the case where the auction's time limit expires, no more lots can be sold, but it is necessary for someone to call completeAuction
before the funds raised and unsold tokens can be transferred.
The following parameters are initialized via governance at the start of a new auction:
_token
IERC20
The ERC20 token to auction
_numLots
uint8
Number of lots in the auction
_lotPrice
uint128
Price of each lot of tokens in payment tokens
_initialLotSize
uint128
Initial number of tokens in each lot
_slashPercent
uint64
Percentage of staked tokens to slash
_lotIncreaseIncrement
uint96
Amount of tokens by which the lot size increases each period
_lotIncreasePeriod
uint16
Number of seconds between each lot size increase
_timeLimit
uint32
Number of seconds before the auction ends if all lots are not sold
Anyone can buy one or more lots by calling the buyLots(auctionId, numLots)
function in the AuctionModule contract. The price per lot, which is fixed, can be retrieved by calling the getLotPrice(auctionId)
function. To get the current lot size, which increases over time, call the getCurrentLotSize(auctionId)
function. Similarly, to view the number of lots remaining, call the getRemainingLots(auctionId)
function.
The sequence of actions for buying tokens from an auction is as follows:
The buyer checks whether the auction ID and the number of lots to buy is valid. If not, the transaction will revert.
The buyer computes their payment amount by multiplying the number of lots they wish to buy and the lot price. They will also compute the current lot size to determine whether they like their bid.
The system checks whether the lot size multiplied by remaining number of lots is greater than the auctionable balance. If it is, the system will cap the lot size at auctionable balance divided by remaining number of lots.
If the buyer proceeds with the purchase, the system will subtract the number of lots that the buyer purchased from the remaining lots, add the purchased token amount to the total tokens sold per auction, and add the payment amount to the total funds raised per auction.
The system checks whether the buyer has approved the payment transfer. If not, the transaction will revert.
The system checks whether the buyer has enough payment token. If not, the transaction will revert.
Transfer payment tokens from buyer to auction module and transfer purchased tokens to buyer.
Calling completeAuction(auctionId)
directly on the auction module is only necessary if the auction's time limit has expired before selling out all lots, and is only possible if the auction has not already been completed.
The sequence of actions for completing an expired auction is as follows:
The system checks whether the auction ID is valid. If not, the transaction will revert.
The system checks whether the auction has already been completed. If so, the transaction will revert.
The system checks whether the auction has expired. If not, the transaction will revert.
The system updates the active flag to false for the auction.
If the auction module has any remaining unsold tokens, it will approve the appropriate amount of tokens to be transferred by the StakedToken contract to itself.
If the auction raised any funds, it will approve the appropriate amount of tokens to be transferred by the SafetyModule contract to governance.
The system tells the SafetyModule that the auction has ended.
If there were any unsold tokens, the SafetyModule calls returnFunds
on the StakedToken contract to transfer the unsold tokens to itself and raise its exchange rate.
The SafetyModule calls settleSlashing
on the StakedToken contract to end the post-slashing state, reenabling staking, slashing and cooldown.
Users can withdraw their underlying tokens by calling the redeem
function in the StakedToken contract. Note that, unless the system is in a post slashing state, users must first and wait for it to pass, after which they will have another time window in which to redeem their tokens.
If the auction is sold out after this transaction, the system will complete the auction, starting from step 4 in the section.