Skip to content

Latest commit

 

History

History
102 lines (64 loc) · 3.57 KB

160.md

File metadata and controls

102 lines (64 loc) · 3.57 KB

Flat Gingerbread Cobra

Medium

liquidity amount depends on the pool's balance which can be manipulated

Summary

To add liquidity to the pool, an address with the AMO_ROLE granted can call the addLiquidity() function which relies on _addLiquidity]( ) . However, the function relies on the current USD balance in the pool, which can be easily manipulated.

Root Cause

In the _addLiquidity function , the amount of boost to mint based on the usdAmount and boostMultiplier is given by uint256 boostAmount = (toBoostAmount(usdAmount) * boostMultiplier) / FACTOR; . The usdAmount is user supplied .

The SolidlyV3AMO contract approves the liquidity pool of values boostAmount and usdAmount to allow the transfer of the specified amount of tokens from the contract.

Before the amount of liquidity is minted from the pool to the SolidlyV3AMO contract , liquidity is given by liquidity = liquidity = (usdAmount * currentLiquidity) / IERC20Upgradeable(usd).balanceOf(pool);

The issue here is that if the IERC20Upgradeable(usd).balanceOf(pool) changes, it can lead to either more or less of liquidity amount. Since this balance serves as the denominator in the liquidity calculation, any changes to it can significantly affect how much liquidity is received.

Internal pre-conditions

Lets consider the following scenario based on liquidity = (usdAmount * currentLiquidity) / IERC20Upgradeable(usd).balanceOf(pool)

Note that the contract is only approving boostAmount and usdAmount to be transferred out

Assuming under normal circumstance : usdAmount = 1000 currentLiquidity = 50000 IERC20Upgradeable(usd).balanceOf(pool) = 1000

The liquidity calculation would be:

usdAmount x currentLiquidity / IERC20Upgradeable(usd).balanceOf(pool)

(1000 x 50000)/ 1000 and the contract will receive 50,000 liquidity units.

An attacker perfroms a swap to increase the balance to IERC20Upgradeable(usd).balanceOf(pool) = 10000

(1000 x 50000)/ 10000 and the contract will receive 25,000 liquidity units for the same amount approved initially

Protocol losses 25,000.

External pre-conditions

No response

Attack Path

No response

Impact

loss of funds

PoC

   function _addLiquidity(
        uint256 usdAmount,
        uint256 minBoostSpend,
        uint256 minUsdSpend,
        uint256 deadline
    ) internal override returns (uint256 boostSpent, uint256 usdSpent, uint256 liquidity) {
        // Calculate the amount of BOOST to mint based on the usdAmount and boostMultiplier
@>        uint256 boostAmount = (toBoostAmount(usdAmount) * boostMultiplier) / FACTOR;

....
....
        // Approve the transfer of BOOST and USD tokens to the pool
@>        IERC20Upgradeable(boost).approve(pool, boostAmount);
@>        IERC20Upgradeable(usd).approve(pool, usdAmount);

....
....

@>        uint128 currentLiquidity = ISolidlyV3Pool(pool).liquidity();
@>      liquidity = (usdAmount * currentLiquidity) / IERC20Upgradeable(usd).balanceOf(pool);
  
        // Add liquidity to the BOOST-USD pool within the specified tick range
        (uint256 amount0, uint256 amount1) = ISolidlyV3Pool(pool).mint(
            address(this),
            tickLower,
            tickUpper,
@>        uint128(liquidity),
            amount0Min,
            amount1Min,
            deadline
        );
.....
}

Mitigation

No response