Platform: Code4rena
Start Date: 30/04/2024
Pot Size: $112,500 USDC
Total HM: 22
Participants: 122
Period: 8 days
Judge: alcueca
Total Solo HM: 1
Id: 372
League: ETH
Rank: 80/122
Findings: 1
Award: $1.48
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: t0x1c
Also found by: 0xCiphky, 0xDemon, Bauchibred, DanielArmstrong, FastChecker, MSaptarshi, Maroutis, NentoR, Ocean_Sky, PNS, Rhaydden, SBSecurity, Shaheen, Tigerfrake, ZanyBonzy, atoko, btk, carlitox477, crypticdefense, honey-k12, hunter_w3b, ilchovski, jokr, ladboy233, rbserver, twcctop, umarkhatab_465
1.479 USDC - $1.48
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L473 https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L491 https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L592 https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L582
Having no slippage params on deposit functions will lead to following scenario
As of this report being written 3 May 2024 5:38 PM the price of ezETH is 2,975$ however , just 9 minutes ago the price was below 2930$. It can be seen that price is fluctuating but the price of eth right now is 3,014$
So if the transaction initiated by the user falls in the time frame let's say
t1-t2 , user might have bought the eth at 2930$ but if the transaction has been executed at say between this time frame
t3-t4
the user might have paid 3000$ for a RenzoETH which presents a loss scenario for user.
so user might want to add slippage parameters like
minEzAmountOut or deadline parameters so that when the transaction is executed , its executed at a suitable price and satisfy the risk appetite of the investor. Otherwise the transaction should fail.
However , the deposit functions in the RestakeManager contract does not allow the user to flexibly provide these parameters to the contract functions to either accept the execution or reject.
Most probably users would incur loss and we always want to minimize user's loss. The counter situation of user making profit is understandable but that's a good thing . We want to minimize bad things here.
function deposit( IERC20 _collateralToken, uint256 _amount, uint256 _referralId ) public nonReentrant notPaused { } function deposit(IERC20 _collateralToken, uint256 _amount) external { deposit(_collateralToken, _amount, 0); } function depositETH() external payable { } function depositETH(uint256 _referralId) public payable nonReentrant notPaused {}
Manual Review , Special Manual Review Tool
Add slippage params in the deposit functions like deadline
and minAmountOut
Timing
#0 - c4-judge
2024-05-17T13:28:59Z
alcueca marked the issue as satisfactory
#1 - c4-judge
2024-05-24T10:28:59Z
alcueca changed the severity to 2 (Med Risk)
🌟 Selected for report: t0x1c
Also found by: 0xCiphky, 0xDemon, Bauchibred, DanielArmstrong, FastChecker, MSaptarshi, Maroutis, NentoR, Ocean_Sky, PNS, Rhaydden, SBSecurity, Shaheen, Tigerfrake, ZanyBonzy, atoko, btk, carlitox477, crypticdefense, honey-k12, hunter_w3b, ilchovski, jokr, ladboy233, rbserver, twcctop, umarkhatab_465
1.479 USDC - $1.48
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L206
The withdraw method in WithdrawQueue accepts the assetOut address
and asset amount to place order for .
However , it fails to account for slippage scenarios due to calculating of assetOut
amount to be returned by an oracle and the prices returned by oracles vary
plus there can be scenarios when the transactions are just delayed due to network
congestion or other factors which might lead users to placing orders of receiving
less assetOut than anticipated at the time of transaction submission .
Here is the code for withdraw
function withdraw(uint256 _amount, address _assetOut) external nonReentrant { // check for 0 values if (_amount == 0 || _assetOut == address(0)) revert InvalidZeroInput(); // check if provided assetOut is supported if (withdrawalBufferTarget[_assetOut] == 0) revert UnsupportedWithdrawAsset(); // transfer ezETH tokens to this address IERC20(address(ezETH)).safeTransferFrom(msg.sender, address(this), _amount); // calculate totalTVL (, , uint256 totalTVL) = restakeManager.calculateTVLs(); // Calculate amount to Redeem in ETH uint256 amountToRedeem = renzoOracle.calculateRedeemAmount( _amount, ezETH.totalSupply(), totalTVL ); // update amount in claim asset, if claim asset is not ETH if (_assetOut != IS_NATIVE) { // Get ERC20 asset equivalent amount amountToRedeem = renzoOracle.lookupTokenAmountFromValue( IERC20(_assetOut), amountToRedeem ); } // revert if amount to redeem is greater than withdrawBufferTarget if (amountToRedeem > getAvailableToWithdraw(_assetOut)) revert NotEnoughWithdrawBuffer(); // increment the withdrawRequestNonce withdrawRequestNonce++; // add withdraw request for msg.sender withdrawRequests[msg.sender].push( WithdrawRequest( _assetOut, withdrawRequestNonce, amountToRedeem, _amount, block.timestamp ) ); // add redeem amount to claimReserve of claim asset claimReserve[_assetOut] += amountToRedeem; emit WithdrawRequestCreated( withdrawRequestNonce, msg.sender, _assetOut, amountToRedeem, _amount, withdrawRequests[msg.sender].length - 1 ); }
Take following scenario .
of 100 input tokens to 5 output tokens.
The loss escalates when the price surge is high and a lot of assetOut is involved
like 2 billion.
Due to lack of minimum amount out and deadline parameter withdraw
the user has incurred a significant loss of out tokens.
Manual review
Add slippage parameters like minAmountOut
and deadline
and only initiates withdrawal requests if
amountToBeRedeemed >= minAmountOut
and
block.timestamp<=deadline
otherwise, revert the withdraw order.
Timing
#0 - c4-judge
2024-05-17T13:44:12Z
alcueca marked the issue as duplicate of #345
#1 - c4-judge
2024-05-17T13:45:08Z
alcueca marked the issue as satisfactory