Skip to content

Latest commit

 

History

History
130 lines (86 loc) · 5.3 KB

006.md

File metadata and controls

130 lines (86 loc) · 5.3 KB

Curved Inky Ram

Medium

Incorrect Total Supply Calculation

Summary

https://github.com/sherlock-audit/2025-01-aave-v3-3/blob/main/aave-v3-origin/src/contracts/protocol/libraries/logic/BridgeLogic.sol#L121C3-L155C2

The executeBackUnbacked function adds the treasury’s accrued tokens (interest earned by the protocol itself) to the total aToken supply. reserve.accruedToTreasury represents the amount of interest accrued to the protocol but not yet claimed or converted into actual aTokens. This amount is being scaled by the liquidity index before being added to the total supply. The treasury's accrued interest is not part of the users' aToken holdings. Including it inflates the total supply, which dilutes the fee distribution to actual aToken holders. The treasury's share should be accounted for separately and not impact the calculation for users.

Root Cause

https://github.com/sherlock-audit/2025-01-aave-v3-3/blob/main/aave-v3-origin/src/contracts/protocol/libraries/logic/BridgeLogic.sol#L121C3-L155C2

uint256 feeToProtocol = fee.percentMul(protocolFeeBps);
uint256 feeToLP = fee - feeToProtocol;
uint256 added = backingAmount + fee;


reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
  IERC20(reserveCache.aTokenAddress).totalSupply() +
    uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
  feeToLP
);

In the executeBackUnbacked function, the liquidity index is updated using the cumulateToLiquidityIndex function:

The parameters passed to cumulateToLiquidityIndex are:

The total aToken supply, adjusted by adding the treasury's accrued tokens scaled by the liquidity index. The fee allocated to LPs (feeToLP). However, adding uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex) to the total aToken supply is incorrect because the treasury's accrued tokens are not part of the circulating aToken supply.

reserve.accruedToTreasury represents the amount of interest accrued to the protocol but not yet claimed or converted into actual aTokens. This amount is being scaled by the liquidity index before being added to the total supply. The treasury's accrued interest is not part of the users' aToken holdings. Including it inflates the total supply, which dilutes the fee distribution to actual aToken holders. The treasury's share should be accounted for separately and not impact the calculation for users.

Including the treasury's accrued tokens in the total supply inflates the base used for liquidity index calculation, leading to a lower increase in the liquidity index than intended. The liquidity index does not accurately reflect the additional fees to be distributed to aToken holders. aToken holders receive less interest than they should because the liquidity index underrepresents the actual earnings.

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

No response

Impact

An inflated total supply means the feeToLP is divided among more tokens than actually exist in users' possession. Each user gets a smaller share of the fee than they should. Users receive less interest than entitled. Over time, this results in significant discrepancies in expected yields.

PoC

Assumptions: Users' aToken Total Supply: 1,000 aTokens. Treasury Accrued Tokens (accruedToTreasury): 100 tokens (not yet converted to aTokens). Current Liquidity Index (nextLiquidityIndex): 1.0 (simplified). Fee to Liquidity Providers (feeToLP): 10 tokens.

Incorrect Calculation: Total Supply Used in Calculation:

totalSupply = 1,000 (user aTokens) + (100 (accruedToTreasury) * 1.0 (liquidity index)) = 1,100 aTokens

Increase in Liquidity Index: The liquidity index increase per aToken: liquidityIndexIncrease = feeToLP / totalSupply = 10 / 1,100 ≈ 0.00909

New Liquidity Index: nextLiquidityIndex = currentLiquidityIndex + liquidityIndexIncrease = 1.0 + 0.00909 = 1.00909

Impact on Users: Each user holding 1 aToken sees an increase in value of 0.00909 tokens.

Correct Calculation (Excluding Treasury Tokens): Total Supply Should Be: totalSupply = 1,000 aTokens (users' holdings only)

Correct Increase in Liquidity Index: liquidityIndexIncrease = feeToLP / totalSupply = 10 / 1,000 = 0.01

Correct New Liquidity Index: nextLiquidityIndex = 1.0 + 0.01 = 1.01

Correct Impact on Users: Each user holding 1 aToken sees an increase in value of 0.01 tokens.

Difference in User Earnings: Incorrect Calculation Gain per aToken: ≈ 0.00909 tokens. Correct Calculation Gain per aToken: 0.01 tokens.

Shortfall per aToken: 0.01 - 0.00909 = 0.00091 tokens.

Total Shortfall Across All Users: totalShortfall = shortfall per aToken * total user aTokens = 0.00091 * 1,000 ≈ 0.91 tokens Where Did the 0.91 Tokens Go?

They are effectively unaccounted for due to the miscalculation. Treasury does not gain them; users do not receive them. This represents a loss in the system’s accounting.

Mitigation

Exclude Treasury Accrued Tokens from Total Supply

reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex( IERC20(reserveCache.aTokenAddress).totalSupply(), // Excluding accruedToTreasury feeToLP );