The user will receive less money than they should have.
Proof of Concept
The user's withdrawal process involves two steps:
Calling withdraw to initiate a withdraw request and calculate the amount to be received using the formula share * totalAsset / totalShare.
Calling claim to burn ezETH (share) and receive the desired collateral token.
share: user own ezETH amount
totalAsset: TVL
totalShare: ezETH totalSupply
In this process, the amount received includes the funds from rewards. The calculation of TVL does not take into account the amount waiting to be claimed stored in the claimReserve state variable. Therefore, if a user initiates a withdrawal request but does not call the claim function, the TVL will always include the amount waiting to be claimed.
User A calls withdraw to initiate a withdrawal request.
After claimReserve seconds, User A still does not call claim to receive the amount.
Another day passes, and User B initiates a withdrawal request. However, when calculating the amount to be received, TVL still includes the amount waiting to be claimed by User A and totalSupply. In this process, rewards generated by RENZO continue to include User A, but User A cannot claim them (because User A's claimable amount has already been calculated), resulting in User B receiving less reward than expected when claiming the total amount, as User A is included in this process.
Logs:
start...
Before tvl: 100000000000000000000
Before alice ezETH: 10000000000000000000
Before bob ezETH: 10000000000000000000
Before joy ezETH: 10000000000000000000
Step 1 --- alice: 33333333333333333333
Step 1 --- bob: 66666666666666666666
[PASS] testReceiveLess2() (gas: 542203)
Logs:
start...
Before tvl: 100000000000000000000
Before alice ezETH: 10000000000000000000
Before bob ezETH: 10000000000000000000
Before joy ezETH: 10000000000000000000
Step 1 --- alice: 33333333333333333333
Step 1 --- bob: 91666666666666666667
run forge test -vvv
Tools Used
Manual Review
Recommended Mitigation Steps
When calculating TVL, subtract the total value of claimReserve.
Lines of code
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L270-L358
Vulnerability details
Impact
The user will receive less money than they should have.
Proof of Concept
The user's withdrawal process involves two steps:
share * totalAsset / totalShare
.share: user own ezETH amount
totalAsset: TVL
totalShare: ezETH totalSupply
In this process, the amount received includes the funds from rewards. The calculation of
TVL
does not take into account the amount waiting to be claimed stored in theclaimReserve
state variable. Therefore, if a user initiates a withdrawal request but does not call theclaim
function, the TVL will always include the amount waiting to be claimed.TVL:
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L270-L358
Here's an example:
withdraw
to initiate a withdrawal request.claimReserve
seconds, User A still does not callclaim
to receive the amount.poc coding :
mock/mockOracle:
mock/mockStrategyManager.sol:
attack.t.sol:
Results:
run
forge test -vvv
Tools Used
Manual Review
Recommended Mitigation Steps
When calculating TVL, subtract the total value of claimReserve.
Assessed type
Payable