The totalStaked
function does not keep track of tokens that were transferred into the contract directly in some accounting and that can skew/mess up certain functionality.
The withdraw function in the staking pool can revert even if there are enough tokens required to satisfy the withdrawal request due to the totalStake accounting is handled.
The StakingPool canDeposit
and the canWithdraw
can represent skewed values.
The withdraw
function in the staking pool contains this logic
Where a check is made to make sure that the contract has sufficient balance to satisfy the withdrawal from the priority pool for a certain account.
If it does, it skips withdrawing the liquidity from startegies and then burns the LST.
The totalStaked is reduced and the tokens are transfered to the account or withdrawal pool to be used to finalize withdrawals.
The problem arises due to the totalStaked
value only updated when:
a deposit is made into the staking pool by the priority pool by calling the deposit
function
tokens are donated to the stakingPool through the donateTokens
function
a new set of rewards have been updated in the _updateStrategyRewards
the withdraw
function in the staking pool by the prioruty pool
It does not consider tokens that were transferred directly into the stakingPool even though when its time to deposit those tokens we deposit them with the _depositLiquidity
function as part of the contract's balance, we also account for them as part of our unused balance and other forms of accounting.
A situation where tokens were transferred directly into the staking pool will allow the require check to pass,
Assuming the toWithdraw is 32 staked tokens and the balance of the stakingPool is 35 ( 30 due to the natural ways totalStaked
is updated i.e deposits, withdrawals, updating strategy rewards while the remaining 5 are the tokens that were transferred directly into the contract )
Since the balance of the stakingPool
contract(35 tokens) is greater than the toWithdraw
(32 tokens) amount, the initial check passes, but further down the code, the reduction of the totalStaked
(30) by the toWithdraw
would revert due to overflow even though there are enough tokens in the stakingPool to satisfy the withdrawal requests and these tokens would still end up being used in other forms of accounting.
The canDeposit
and canWithdraw
functions in the stakingPool also make use of the totalStaked
to keep track of how much tokens can be deposited into and withdrawn from the contract. The priorityPool uses these functions as well.
The canDeposit
function using the totalStaked
, uses the totalStaked value to know if there is still space for the depositing available in the stakingPool.
The prioriyPool
uses the canDeposit
here where it makes sure it deposits what it can deposit, if the canDeposit returns a skewed value e.g getMaxDeposits
is 2000 tokens and totalStaked
= 1900 tokens but the tokens transferred directly into the contract plus the totalStaked, i.e contract balance = 2100 tokens. The canDeposit
would return 100 tokens worth of deposit space. The same logic applies for the canWithdraw
.
Withdrawals in the staking pool by the priority pool can still revert even if there are enough tokens to satisfy withdrawal.
The canWithdraw
and the canDeposit
function return values used in the priorityPool deposit and withdrawal calculations are skewed.
When using the totalStaked
for some accounting, include the balance of the stakingPool contract as well.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.