Flat Gingerbread Cobra
Medium
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.
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.
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.
No response
No response
loss of funds
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
);
.....
}
No response