Renzo - Greed'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: 106/122

Findings: 1

Award: $0.00

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

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

Vulnerability details

Impact

All functions relying on RestakeManager::calculateTVLs() are broken resulting in multiple side-effects in the protocol, one of them being :

  • users are able/unable to deposit collateral in the protocol while they shouldn't/should

Proof of concept

The RestakeManager::calculateTVLs() calculates the TVL (as ETH) of all the collateral tokens held by the operatorDelegators and also includes the funds pending in the withdrawQueue contract.

To do so, the function is looping through all the operatorDelegators using the i variable and for each of them, is looping through all the collateralTokens using the j variable.

It is during the very first iteration of the i loop that the withdrawQueue funds is calculated using an oracle

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

// record token value of withdraw queue
if (!withdrawQueueTokenBalanceRecorded) {
    totalWithdrawalQueueValue += renzoOracle.lookupTokenValue(
        collateralTokens[i],
        collateralTokens[j].balanceOf(withdrawQueue)
    );
}

The withdrawQueueTokenBalanceRecorded variable is set to true right before incrementing i meaning the contract enters the previous section only on the first iteration, when i == 0

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

// Set withdrawQueueTokenBalanceRecorded flag to true
withdrawQueueTokenBalanceRecorded = true;

unchecked {
    ++i;
}

The oracle takes as parameters :

  • a collateral token
  • the balance of the collateral token an address holds (in this case, withdrawQueue)

Since i will always be equal to 0 when the contract calculates the withdrawQueue TVL, the oracle is going to return the price of the first collateral tokens BUT using another token's balance and this for every collateral tokens supported by the protocol.

Tools used

Manual analysis

Change the i iterator to j like such :

if (!withdrawQueueTokenBalanceRecorded) {
    totalWithdrawalQueueValue += renzoOracle.lookupTokenValue(
        collateralTokens[j],
        collateralTokens[j].balanceOf(withdrawQueue)
    );
}

Assessed type

Error

#0 - c4-judge

2024-05-16T10:31:35Z

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)

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