<!-- canonical: https://0xsimao.com/findings/renzo-withdraw-queue-tvl-calculation -->

# Incorrect withdraw queue balance in TVL calculation

Crit/High · Code4rena · Liquid restaking · 30th April 2024

Finding H-08 of the Renzo competition.

- Protocol: https://code4rena.com/audits/2024-04-renzo
- Codebase: https://github.com/0xsimao/2024-04-renzo/tree/519e518f2d8dec9acf6482b84a181e403070d22d
- Source: https://github.com/code-423n4/2024-04-renzo-findings/issues/28

---

When calculating TVL it iterates over all the operator delegators and inside it iterates over all the collateral tokens. 

```solidity
        for (uint256 i = 0; i < odLength; ) {
            ...

            // Iterate through the tokens and get the value of each
            uint256 tokenLength = collateralTokens.length;
            for (uint256 j = 0; j < tokenLength; ) {
                ...

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

                unchecked {
                    ++j;
                }
            }

            ...

            unchecked {
                ++i;
            }
        }
```

However, the balance of `withdrawQueue` is incorrectly fetched, specifically this line:

```solidity
                    totalWithdrawalQueueValue += renzoOracle.lookupTokenValue(
                        collateralTokens[i],
                        collateralTokens[j].balanceOf(withdrawQueue)
                    );
```

It uses an incorrect index of the outer loop `i` to access the `collateralTokens`. `i` belongs to the operator delegator index, thus the returned value will not represent the real value of the token. For instance, if there is 1 OD and 3 collateral tokens, it will add the balance of the first token 3 times and neglect the other 2 tokens. If there are more ODs than collateral tokens, the the execution will revert (index out of bounds).

This calculation impacts the TVL which is the essential data when calculating mint/redeem and other critical values. A miscalculation in TVL could have devastating results.

### Proof of Concept

A simplified version of the function to showcase that the same token (in this case `address(1)`) is emitted multiple times and other tokens are untouched:

```solidity
contract RestakeManager {

    address[] public operatorDelegators;

    address[] public collateralTokens;

    event CollateralTokenLookup(address token);

    constructor() {
        operatorDelegators.push(msg.sender);

        collateralTokens.push(address(1));
        collateralTokens.push(address(2));
        collateralTokens.push(address(3));
    }

    function calculateTVLs() public {
        // Iterate through the ODs
        uint256 odLength = operatorDelegators.length;

        for (uint256 i = 0; i < odLength; ) {
            // Iterate through the tokens and get the value of each
            uint256 tokenLength = collateralTokens.length;
            for (uint256 j = 0; j < tokenLength; ) {
                emit CollateralTokenLookup(collateralTokens[i]);

                unchecked {
                    ++j;
                }
            }

            unchecked {
                ++i;
            }
        }
    }
}
```

### Recommended Mitigation Steps

Change to `collateralTokens[j]`.

### Assessed type

Math

---

Related findings:

- [BasicVault::_deposit() should always resolve with idle balance as the redeem queue may be disabled with requests pending](https://0xsimao.com/findings/mitosis-deposit-resolve-redeem-queue): Mitosis
- [Mismatch between yield amount deposited in shares calculation and getAccountYieldBalance()](https://0xsimao.com/findings/benddao-mismatch-yield-deposited-shares): BendDAO
- [Unrealized inflation calculation returns wrong value when balance reaches cap](https://0xsimao.com/findings/m-0-unrealized-inflation-returns-reaches): M^0 Minter Gateway
- [Taker fee is underestimated due to incorrect fee calculation](https://0xsimao.com/findings/1inch-taker-fee-underestimated-calculation): 1inch Fee Extension
