The staked token is a token deployed on Ethereum, with the main utility of participating in the safety module.
There are currently two proxy contracts which utilize the stake token:
- stkAAVE with the StakedTokenV2Rev3 implementation
- stkABPT with the StakedTokenBptRev2 implementation
Together with all the standard ERC20 functionalities, the current implementation includes extra logic for:
- entering and exiting the safety module
- management & accounting for safety module rewards
- management & accounting of voting and proposition power (only is the case of stkAAVE)
With the new iteration the StakedTokenV3 adds enhanced mechanics for slashing in the case of shortfall events.
Therefore a new exchangeRate
is introduced which will reflect the ratio between stkToken and Token.
Initially this exchange rate will be initialized as 1e18
which reflects a 1:1
peg.
To account for a vulnerability discovered by certora which would allow for gaming the cooldown eventually leading to prolonged withdrawal windows, this revision also contains refined cooldown mechanics. The cooldown no longer allows for redeeming balanceOf(account)
at redeem, but is limited to balanceOf(account)
at cooldown start.
In the case of StakedAaveV3 it also adds a hook for managing GHO discounts.
The new iteration will update the revision of:
- stkABPT from
2
to3
via StakedTokenV3.sol contract - stkAAVE from
3
to4
via StakedAaveV3.sol contract
The new iteration also updates:
- solidity to version
0.8
and therefore removesSafeMath
- open zeppelin contracts to latest 0.8 compatible versions resulting in deprecation of
_decimals
onERC20
- the
_governance
storage slot has been deprecated, as the transfer hook utilizing it has never been used
account: Ethereum address involved in a transaction on the system.
exchange rate: the rate to which you can redeem stkToken
for Token
total staked: the amount of underlying staked into the stkToken
(ignoring airdrops)
LOWER_BOUND: 1 unit of the staked asset (1 ether for 18 decimal assets)
staking: entering the pool
redeeming: leaving the pool
slashing: transfers specified amount of Token
to the destination (altering the exchangeRate in favour of the pool)
returnFunds: injects amount of Token
into the pool (altering the exchangeRate in favour of participants)
-
$Account1 \ne Account2$ -
$assets = {shares \times 1e18 \over exchangeRate}$ -
$shares = {assets \times exchangeRate \over 1e18}$ -
initializer and constructor only initialize variables not previously initialized
-
cooldown
should persist the accounts currentbalance
and theblock.timestamp
withinstakersCooldowns[account]
. -
Calling
cooldown
whilst in an active cooldown will reset the cooldown-
The exchange rate is initialized as slash
and returnFunds
actions.
This design was chosen opposed to a dynamic exchangeRate based on underlying as it:
- allows to clearly determine the voting & proposition power at any given block without totalSupply snapshots of every action.
- allows to clearly distinguish between participants on the pool and accounts who transferred funds by accident, so tokens can be rescued without altering the exchange rate.
-
slash
should transferamount
of the staked tokens to thedestination
address.- The maximum
amount
is determined by amaxSlashingPercentage
which describes the ratio of total staked assets being available to slash. - If the specified
amount
exceeds the maximum, the maximum will be slashed instead. - The slashing will revert when less than
LOWER_BOUND
would be remaining after the slashing.
- The maximum
-
After the slashing event occurred the
exchangeRate
should reflect the discount in the redeemable amount:$$exchangeRate_{t1}={totalStaked_{t0} - amount \over totalSupply_{t0}}$$ . -
A slashing even will set
inPostSlashingPeriod
totrue
. The post slashing period lasts until it is settled by the slashing admin. For the post lashing period the following rules apply:- accounts can exit the pool immediately without a cooldown period.
- no account can enter the pool.
- no other slashing can occur.
stake(amount)
should scale up the amount, so the pool always guarantees fair entry
stake(amount)
with a pending cooldown will no longer adjust the cooldown time.
-
The redeemable amount is determined as
stakersCooldowns[account].amount
orbalanceOf(account)
in case of an unsetteled slashing event (inPostSlashingPeriod == true
)
-
The redeemable amount should be scaled down, so the pool always guarantees fair exit
Transfers need to account for pending cooldown so you cannot use transfer in/out
to game the cooldown.
Therefore:
transfer in
does not affect thestakersCooldowns[account]
. If an account activated cooldown forx
and receivesy
he should still only be eligible to redeemx
as it was persisted intostakersCooldowns[account].amount
transfer out
needs to account for reduced funds onredeem
as otherwise one couldbank
different exit windows on differentaccounts
and use transfers for an early exit. Therefore givencooldown()
att0
and end of withdrawal windowt1
thestakersCooldowns[account].amount
needs to be down adjusted to the lowestbalanceOf
within the range oft0
andt1
. To archive thistransfer out
will discount thestakersCooldowns[account].amount
tomin(stakersCooldowns[account].amount, balanceOf(account))
. In practice that means when callingcooldown
with a balanceOf100
, receiving50
, sending70
and receiving another50
thestakersCooldowns[account].amount
will be discounted to80
as the minimum withint0-t1
was100 + 50 - 70 = 80
.
-
returnFunds
allows permissionless returning of funds to the system (e.g. when not all slashed funds were utilized to recover). Returned funds are injected into the exchangeRate and therefore mutualized over all accounts. -
To ensure only accounts affected by slashing will profit from
returnFunds
, entering the system is not possible until the slashing is settled by the slashingAdmin. -
To prevent spamming the system, and prevent extensive rounding errors, both the minimum amount returned, as well as the current shares (totalSupply of the stkToken) need to exceed
LOWER_BOUND
.
-
The total power (of one type) of all users in the system is less or equal than the sum of balances of all stkAAVE holders (total staked):
$$\sum powerOfAccount_i <= \sum balanceOf(account_i)$$ . -
The governance voting and proposition power of an
address
is defined by thepowerAtBlock
adjusted by theexchange rate at block
:$$power_{t0} = {stkAmount_{t0} * 1e18 \over exchangeRate_{t0}}$$ . -
If an account is not receiving delegation of power (one type) from anybody, and that account is not delegating that power to anybody, the power of that account must be equal to its proportional AAVE balance.
- The stkToken will only consider tokens staked via
stake
and injected viareturnFunds
for the exchangeRate. Tokens accidentallyairdropped
to the staking contract via transfer will not be mutualized and can in theory be rescued by governance.
Staked
andRedeem
now both emit bothassets
andshares
to be closer to eip-4616 standardCooldown
now emits bothaccount, amount
as the cooldown is for a specific amount
- we decided to return actual types and not cast to e.g.
uint256
in view methods. This decision was taken to not allow false assumptions around max values when using these methods inside external contracts.