A vulnerability exists in the WithdrawalPool contract due to the way withdrawals are processed using the withdraw function and WithdrawalBatch mechanism. The contract calculates the amount of tokens users receive upon withdrawal based on a dynamically changing stakePerShares ratio, which is dependent on the total staked tokens and total shares in the system. Since these values can fluctuate between the time a withdrawal is queued and when it is finalized, users may receive more or fewer tokens than expected, leading to unpredictable withdrawal outcomes.
The withdraw function processes withdrawals in batches, using the WithdrawalBatch structure. When withdrawals are finalized in the _finalizeWithdrawals function, the contract calculates the amount of tokens a user can withdraw based on the stakePerShares value.
The stakePerShares is calculated in _finalizeWithdrawals as:
The getStakeByShares function determines the token equivalent of shares based on the current staking pool state:
This calculation uses the current values of totalShares and _totalStaked(). If these values change between the time a withdrawal is queued and the time it is processed, the stakePerShares ratio will also change. Consequently, users may receive a different amount of tokens than they initially expected. If the total staked tokens or shares increase or decrease significantly, it could lead to users receiving more or fewer tokens than they originally intended.
*Someone can donate tokens or burn shares for donate or _totalStaked can be increased by staking and it causes increases getStakeByShares results. So user can receive more tokens. At first user wanted to withdraw exact amount of tokens but he receives more tokens. If user needed exact amount of tokens, user burned too many share.
Or totalStaked can be decreased by slashing, so user can receive less tokens than expected.
Queuing Time: A user requests to withdraw 100 tokens, and at that time, the stakePerShares ratio is 1:1 (1 share equals 1 token).
Processing Time: When the withdrawal is finalized, the totalStaked or totalShares in the system has changed, and the stakePerShares ratio is now 1.2:1. This results in the user receiving 120 tokens instead of the 100 they expected, or conversely, they may receive fewer tokens if the ratio changes unfavorably.
In _finalizeWithdrawals, the stakePerShares is calculated based on the system’s current state:
In getStakeByShares:
Unpredictable Token Withdrawals: Users may receive either more or fewer tokens than they requested due to changes in the stakePerShares ratio.
Loss of User Funds: If the stakePerShares ratio drops between queuing and processing, users could receive fewer tokens than expected, resulting in financial losses.
Excessive Payouts: If the stakePerShares ratio increases, users could receive more tokens than expected, which could deplete the staking pool and harm the system’s liquidity.
Fix stakePerShares Calculation at Queue Time: Instead of calculating the stakePerShares dynamically during finalization, lock in the conversion rate when the withdrawal is queued. This ensures that users receive the exact amount of tokens they requested, regardless of changes in the system state.
Introduce Stability Mechanisms: Implement mechanisms to stabilize the stakePerShares ratio to minimize fluctuations between queuing and processing, ensuring more predictable withdrawals.
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.