Renzo - Ocean_Sky's results

A protocol that abstracts all staking complexity from the end-user and enables easy collaboration with EigenLayer node operators and a Validated Services (AVSs).

General Information

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

Renzo

Findings Distribution

Researcher Performance

Rank: 72/122

Findings: 2

Award: $1.89

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

0.4071 USDC - $0.41

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_224_group
duplicate-326

External Links

Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L491-L616 https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L206-L262 https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L279-L311

Vulnerability details

Background

The protocol allows native ETH and different LSTs (liquid staking tokens) to be deposited as collateral in exchange of ezETH. In the current status, it accepts stETH and wBETH but it can accept other LSTs as well if configured.

As you can see below, this is a deposit function for LSTs, in line 507, it retrieved the current value of the collateral in which the minted ezETH will depend on.

File: RestakeManager.sol
491:     function deposit(
492:         IERC20 _collateralToken,
493:         uint256 _amount,
494:         uint256 _referralId
495:     ) public nonReentrant notPaused {

~continue
505: 
506:         // Get the value of the collateral token being deposited
507:         uint256 collateralTokenValue = renzoOracle.lookupTokenValue(_collateralToken, _amount);
508: 

~continue
563: 
564:         // Calculate how much ezETH to mint
565:         uint256 ezETHToMint = renzoOracle.calculateMintAmount(
566:             totalTVL,
567:             collateralTokenValue,
568:             ezETH.totalSupply()
569:         );
570: 
571:         // Mint the ezETH
572:         ezETH.mint(msg.sender, ezETHToMint);
573: 
574:         // Emit the deposit event
575:         emit Deposit(msg.sender, _collateralToken, _amount, ezETHToMint, _referralId);
576:     }

See below. This is a deposit function for native ETH. In line 607, it just retrieved the msg.value to get the minted ezETH.

File: RestakeManager.sol
592:     function depositETH(uint256 _referralId) public payable nonReentrant notPaused {
593:         // Get the total TVL
594:         (, , uint256 totalTVL) = calculateTVLs();
595: 
596:         // Enforce TVL limit if set
597:         if (maxDepositTVL != 0 && totalTVL + msg.value > maxDepositTVL) {
598:             revert MaxTVLReached();
599:         }
600: 
601:         // Deposit the remaining ETH into the DepositQueue
602:         depositQueue.depositETHFromProtocol{ value: msg.value }();
603: 
604:         // Calculate how much ezETH to mint
605:         uint256 ezETHToMint = renzoOracle.calculateMintAmount(
606:             totalTVL,
607:             msg.value,
608:             ezETH.totalSupply()
609:         );
610: 
611:         // Mint the ezETH
612:         ezETH.mint(msg.sender, ezETHToMint);
613: 
614:         // Emit the deposit event
615:         emit Deposit(msg.sender, IERC20(address(0x0)), msg.value, ezETHToMint, _referralId);
616:     }

Issue

In the withdrawal function, the user has a choice to withdraw what type of collateral they want to withdraw. In this scenario, there could be an arbitrage opportunity in which a malicious user can deposit lower valued LST and withdraw higher valued LST. As per current market condition, not all LSTs are equal in terms of pricing, so there is a high chance this price difference can be use as arbitrage opportunity to drain the high valued assets from the protocol to the malicious user.

A good example of two LSTs that can have price difference is between wbETH and stETH. wbETH price is always higher than stETH price in terms of USD. See below for the market price links.

wbETH - https://coinmarketcap.com/currencies/wrapped-beacon-eth/ stETH - https://coinmarketcap.com/currencies/steth/#Chart

As of date of writing May 8 wbETH price is $3,145 while stETH price is $3,029

Please see below the withdraw function (line 206, parameter asset out) in which the user is free to withdraw whatever type of collateral assets.

File: WithdrawQueue.sol
201:     /**
202:      * @notice  Creates a withdraw request for user
203:      * @param   _amount  amount of ezETH to withdraw
204:      * @param   _assetOut  output token to receive on claim
205:      */
206:     function withdraw(uint256 _amount, address _assetOut) external nonReentrant { 
207:         // check for 0 values
208:         if (_amount == 0 || _assetOut == address(0)) revert InvalidZeroInput();
209: 
210:         // check if provided assetOut is supported
211:         if (withdrawalBufferTarget[_assetOut] == 0) revert UnsupportedWithdrawAsset();
212: 

Impact

Economic losses to depositors of higher valued LST collaterals

Proof of Concept

This could be the scenario of the exploit.

  1. User A deposited a higher valued LST of 1 wbETH amounting 3,100 usd.
  2. User B sees the transaction of User A and immediately backruns it by depositing a lower valued LST of 1 stETH amounting 3,000 usd.
  3. User A receives a minted 1.06 ezETH in exchange of deposited 1 wbETH.
  4. User B receives a minted 1.02 ezETH in exchange of deposited 1 stETH.
  5. User B seeing the price difference, immediately withdraws 1.02 ezETH but the actual collateral token to be withdraw is in the form of wbETH.
  6. After the cooldown period (exchange rate did no change), User B able to claim 1.02 wbETH amounting 3,162 usd. Let's say there are other user who deposited 0.02 wbETH so user B able to claim the whole 1.02 wbETH. He got a profit of 162 usd (3,162 minus original deposit cost 3,000).
  7. User A also decided to withdraw his asset but the only remaining LST is 1 stETH. If he decided to withdraw it, he will lose 100 usd (1 stETH is equivalent to 3,000 usd versus original deposit of wbETH amounting 3,100 usd).

Tools Used

Manual Review

The protocol should only allow withdrawal of collateral originally deposited by the user.

Assessed type

Other

#0 - c4-judge

2024-05-16T14:03:18Z

alcueca marked the issue as duplicate of #326

#1 - c4-judge

2024-05-17T12:48:31Z

alcueca marked the issue as satisfactory

Awards

1.479 USDC - $1.48

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_19_group
duplicate-484

External Links

Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L491-#L576

Vulnerability details

Background

The deposit function in Restake Manager contract enables a user to deposit collateral token in exchange of ezETH. The returned minted amount of ezETH depends on the retrieved current value of exchanged collateral token through lookupTokenValue. The collateral tokens are LSTs or native ETH.

/File: RestakeManager.sol
506:         // Get the value of the collateral token being deposited
507:         uint256 collateralTokenValue = renzoOracle.lookupTokenValue(_collateralToken, _amount);
508: 
File: RenzoOracle.sol
71:     function lookupTokenValue(IERC20 _token, uint256 _balance) public view returns (uint256) {
72:         AggregatorV3Interface oracle = tokenOracleLookup[_token];
73:         if (address(oracle) == address(0x0)) revert OracleNotFound();
74: 
75:         (, int256 price, , uint256 timestamp, ) = oracle.latestRoundData();
76:         if (timestamp < block.timestamp - MAX_TIME_WINDOW) revert OraclePriceExpired();
77:         if (price <= 0) revert InvalidOraclePrice();
78: 
79:         // Price is times 10**18 ensure value amount is scaled
80:         return (uint256(price) * _balance) / SCALE_FACTOR;
81:     }

Issue

There is potential volatility on collateral LST tokens being exchanged for ezETH. These collateral token price may drop against the ETH. The protocol did not put any slippage protection in regards of this situation. User may be put into a disadvantage position and received unfavorable number of minted ezETH.

Impact

Can lead to unfavorable ezETH minting rates and potential economic losses to users

Proof of Concept

This could be the scenario

  1. User A deposits an LST token with the price of 3,100 usd.
  2. Suddenly this LST token drops to 2,900 usd. This could be cause of penalties imposed due to malicious behavior or network downtime.
  3. The protocol pick-up the 2,900 usd price in minting ezETH token.
  4. User A receives of lower number of ezETH since the collateral drop its price.

Tools Used

Manual Review

Put a parameter input in deposit function in which a user can decide the minimum output it can receive from minting so user can control the slippage.

Assessed type

Other

#0 - c4-judge

2024-05-17T13:28:59Z

alcueca marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter