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: 92/122
Findings: 3
Award: $0.04
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: pauliax
Also found by: 0rpse, 0x73696d616f, 0xAadi, 0xCiphky, 0xPwned, 0xhacksmithh, 0xnev, 0xnightfall, 0xordersol, 14si2o_Flint, Aamir, Aymen0909, BiasedMerc, DanielArmstrong, Fassi_Security, FastChecker, GoatedAudits, Greed, KupiaSec, LessDupes, Maroutis, NentoR, OMEN, SBSecurity, Stefanov, TheFabled, adam-idarrha, ak1, aman, araj, aslanbek, b0g0, baz1ka, bigtone, blutorque, carlitox477, carrotsmuggler, crypticdefense, eeshenggoh, fyamf, gesha17, gjaldon, grearlake, guhu95, honey-k12, hunter_w3b, ilchovski, josephdara, kinda_very_good, lanrebayode77, m_Rassska, maxim371, mt030d, mussucal, oakcobalt, p0wd3r, peanuts, rbserver, shui, siguint, t0x1c, tapir, twcctop, ustazz, xg, zhaojohnson, zigtur, zzykxx
0.0026 USDC - $0.00
If there are more Operator Delegators than collateral tokens, there will be an out of bound error due to how calculateTVLs()
was implemented and user exposed function like deposit()
will revert.
In RestakeManager.calculateTVLs()
, the function loops through the entire Operator delegators to get each operator's tvl.
The problem here is that, when the function tries to increment totalWithdrawalQueueValue
by calling Renzo oracle to lookup value of the token in withdrawQueue
, it looks at the wrong index to pass the token instance.
// record token value of withdraw queue if (!withdrawQueueTokenBalanceRecorded) { totalWithdrawalQueueValue += renzoOracle.lookupTokenValue( collateralTokens[i], //@audit should be j not i; collateralTokens[j].balanceOf(withdrawQueue) ); }
From the code snippet above, it's observed that the index of the operator delegators was used instead of tokens index.
Other functions that will be affected include; depositTokenRewardsFromProtocol()
and depositEth()
.
If for instance, there are 10 operators and just 3 EERC20 tokens, looking at index 3 and above for a collateral token instance will lead to out of bound error and function will revert.
The likelihood of this happening is high and the severity is high as well, since deposit()
will remain DOSed.
function deposit( IERC20 _collateralToken, uint256 _amount, uint256 _referralId ) public nonReentrant notPaused { // Verify collateral token is in the list - call will revert if not found uint256 tokenIndex = getCollateralTokenIndex(_collateralToken); // Get the TVLs for each operator delegator and the total TVL ( uint256[][] memory operatorDelegatorTokenTVLs, uint256[] memory operatorDelegatorTVLs, uint256 totalTVL ) = calculateTVLs(); //@audit-issue will cause revert
At the moment only stEth and wbEth seems to be the only accepted tokens, with three or more operator validators, deposit()
will remain DOSed.
Manual review.
// record token value of withdraw queue if (!withdrawQueueTokenBalanceRecorded) { totalWithdrawalQueueValue += renzoOracle.lookupTokenValue( -- collateralTokens[i], ++ collateralTokens[j], collateralTokens[j].balanceOf(withdrawQueue) ); }
DoS
#0 - c4-judge
2024-05-16T10:28:33Z
alcueca marked the issue as satisfactory
#1 - c4-judge
2024-05-16T10:38:47Z
alcueca changed the severity to 2 (Med Risk)
#2 - c4-judge
2024-05-16T10:39:08Z
alcueca changed the severity to 3 (High Risk)
#3 - c4-judge
2024-05-20T04:26:26Z
alcueca changed the severity to 2 (Med Risk)
#4 - c4-judge
2024-05-23T13:47:20Z
alcueca changed the severity to 3 (High Risk)
🌟 Selected for report: 0xCiphky
Also found by: 0rpse, 0x007, 0xAadi, 14si2o_Flint, ADM, Aamir, Aymen0909, BiasedMerc, DanielArmstrong, Fassi_Security, FastChecker, KupiaSec, LessDupes, MaslarovK, Neon2835, RamenPeople, SBSecurity, Shaheen, Tendency, ZanyBonzy, adam-idarrha, araj, b0g0, baz1ka, bigtone, bill, blutorque, carrotsmuggler, cu5t0mpeo, fyamf, gesha17, gumgumzum, hunter_w3b, inzinko, jokr, josephdara, kennedy1030, kinda_very_good, lanrebayode77, m_Rassska, mt030d, mussucal, tapir, underdog, xg, zzykxx
0.0402 USDC - $0.04
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L547-L549 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L561-L562 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Delegation/OperatorDelegator.sol#L147-L148
RestakeManager.deposit()
will revert whenever amount being deposited is lower or equal to buffer deficit.
if (bufferToFill > 0) { bufferToFill = (_amount <= bufferToFill) ? _amount : bufferToFill; // update amount to send to the operator Delegator @> _amount -= bufferToFill; //@audit-issue if _amount <= bufferToFill, _amount = 0 after subtraction, deposit in OD will revert // safe Approve for depositQueue _collateralToken.safeApprove(address(depositQueue), bufferToFill); // fill Withdraw Buffer via depositQueue depositQueue.fillERC20withdrawBuffer(address(_collateralToken), bufferToFill); }
OD.deposit()
is called without zero check!// Approve the tokens to the operator delegator _collateralToken.safeApprove(address(operatorDelegator), _amount); // Call deposit on the operator delegator operatorDelegator.deposit(_collateralToken, _amount); //@audit-issue revert with 0 amoount!
deposit()
reverts in OD.function deposit( IERC20 token, uint256 tokenAmount ) external nonReentrant onlyRestakeManager returns (uint256 shares) { @> if (address(tokenStrategyMapping[token]) == address(0x0) || tokenAmount == 0) //@audit-issue revert InvalidZeroInput();
Manual review
Add a zero check in RestakeManager.deposit()
// Approve the tokens to the operator delegator _collateralToken.safeApprove(address(operatorDelegator), _amount); -- // Call deposit on the operator delegator ++ // Call deposit on the operator delegator if _amount > 0 if(_amount > 0) { operatorDelegator.deposit(_collateralToken, _amount); //@audit-ok }
DoS
#0 - c4-judge
2024-05-20T05:12:04Z
alcueca marked the issue as satisfactory
🌟 Selected for report: Sathish9098
Also found by: 0x73696d616f, 0xCiphky, 0xmystery, ABAIKUNANBAEV, Bauchibred, BiasedMerc, Fassi_Security, FastChecker, GalloDaSballo, GoatedAudits, K42, KupiaSec, LessDupes, Limbooo, ReadyPlayer2, Rhaydden, SBSecurity, Sabit, Sparrow, WildSniper, ZanyBonzy, adam-idarrha, adeolu, araj, aslanbek, atoko, b0g0, carlitox477, crypticdefense, fyamf, gesha17, gjaldon, grearlake, gumgumzum, hihen, honey-k12, hunter_w3b, inzinko, jesjupyter, jokr, kennedy1030, kind0dev, kinda_very_good, ladboy233, lanrebayode77, oakcobalt, oualidpro, pauliax, rbserver, t0x1c, tapir, underdog, xg, zzykxx
0 USDC - $0.00
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Deposits/DepositQueue.sol#L252-L277 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L286-L287 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L351-L355
calculateTVLs()
fails to account for erc20 tokens in DepositQueue
contract leading to incorrect TVL calculation.
This will lead to an incorrect check in deposit()
and TVL limit will not actually hold, since a lower value is being returned than the actual value.
// Enforce TVL limit if set, 0 means the check is not enabled if (maxDepositTVL != 0 && totalTVL + collateralTokenValue > maxDepositTVL) { revert MaxTVLReached(); }
Total value of each erc20 tokens in withdrawQueue contract was included in tvl calculation
// record token value of withdraw queue if (!withdrawQueueTokenBalanceRecorded) { totalWithdrawalQueueValue += renzoOracle.lookupTokenValue( collateralTokens[i], //@audit should be j not i; collateralTokens[j].balanceOf(withdrawQueue) //@audit should include depositQueue balance ); }
value of ETH in withdrawQueue was also included in tvl total
// Add native ETH help in withdraw Queue and totalWithdrawalQueueValue to totalTVL totalTVL += (address(withdrawQueue).balance + totalWithdrawalQueueValue);
Now, tvl total included ETH in depositQueue
// Get the value of native ETH held in the deposit queue and add it to the total TVL totalTVL += address(depositQueue).balance;
But failed to include depositQueue
erc20 tokens value to the total TVL.
However, it is possible that there are erc20 tokens present in the depositQueue
waiting to be swept by an admin.
/// @dev Sweeps any accumulated ERC20 tokens in this contract to the RestakeManager /// Only callable by a permissioned account function sweepERC20(IERC20 token) external onlyERC20RewardsAdmin { uint256 balance = IERC20(token).balanceOf(address(this)); if (balance > 0) { uint256 feeAmount = 0; // Sweep fees if configured if (feeAddress != address(0x0) && feeBasisPoints > 0) { feeAmount = (balance * feeBasisPoints) / 10000; IERC20(token).safeTransfer(feeAddress, feeAmount); emit ProtocolFeesPaid(token, feeAmount, feeAddress); } // Approve and deposit the rewards token.approve(address(restakeManager), balance - feeAmount); restakeManager.depositTokenRewardsFromProtocol(token, balance - feeAmount); // Add to the total earned totalEarned[address(token)] = totalEarned[address(token)] + balance - feeAmount; // Emit the rewards event emit RewardsDeposited(IERC20(address(token)), balance - feeAmount); } }
Manual review
Include erc20 tokens value present in depositQueue
contract the way it was done for withdrawQueue
-- // record token value of withdraw queue ++ // record token value of withdraw and deposit queue if (!withdrawQueueTokenBalanceRecorded) { totalWithdrawalQueueValue += renzoOracle.lookupTokenValue( collateralTokens[i], -- collateralTokens[j].balanceOf(withdrawQueue) ++ collateralTokens[j].balanceOf(withdrawQueue) + collateralTokens[j].balanceOf(depositQueue) ); }
Context
#0 - C4-Staff
2024-05-15T14:43:02Z
CloudEllie marked the issue as duplicate of #381
#1 - c4-judge
2024-05-16T05:39:55Z
alcueca marked the issue as not a duplicate
#2 - c4-judge
2024-05-16T05:47:07Z
alcueca marked the issue as duplicate of #381
#3 - c4-judge
2024-05-16T06:01:04Z
alcueca marked the issue as satisfactory
#4 - c4-judge
2024-05-27T09:30:29Z
alcueca changed the severity to QA (Quality Assurance)