Unrestricted LP burn enables price inflation to exploit deposits
The LPToken contract allows unrestricted burning of LP tokens via burn() and burnFrom().
Severity
Impact: High
Likelihood: Low
Description
The LPToken contract allows unrestricted burning of LP tokens via burn() and burnFrom(). This enables an attack where the first depositor manipulates the LP price by burning nearly all of their LP tokens. This skews the pool state such that any subsequent deposit may mint less or 0 LP tokens, either causing a denial of service (if slippage checks are used) or silent fund loss (if not).
function _calcAddLiquidity() internal view returns (uint256 netAmount, uint256 pFee, uint256 lpAmount) {--- SNIPPED ---if (lpSupply == 0) {lpAmount = netAmount * tokenPrice;lpAmount = lpAmount.toDecimals(tokenDecimals + 8, plpDecimals);} else {@1> lpAmount = netAmount.mulDiv(tokenPrice * lpSupply, _getPoolValue(false, false) /*18, USD*/);@2> lpAmount = lpAmount.toDecimals(tokenDecimals + 8, plpDecimals);}}
//> src/utils/tokens/lp/LPToken.solfunction burn(uint256 value) public override(ILPToken, ERC20Burnable) {ERC20Burnable.burn(value);}function burnFrom(address _account, uint256 _amount) public override(ILPToken, ERC20Burnable) {ERC20Burnable.burnFrom(_account, _amount);}
Consider the following scenario: 0. Initial states: - ETH Oracle price: 1500e8 (8 decimals, 1500 USD). - USDC Oracle price: 1e8 (8 decimals, 1 USD). - LP token decimals: 18. - 0% fee.
- Bob attempts to add 1 ETH liquidity, which is equivalent to 1500 USD.
- Alice front-runs Bob's deposit with 1501 USDC, which is equivalent to 1501 USD:
- LP mint amount:
1501e6 * 1e8 = 1501e14-- to 18 decimals -->1501e14 * 1e(18-14) = 1501e18 LPs. - PoolValue: 1501e18 ≈ 1501 USD.
- LP mint amount:
- Alice burns all but 1 wei of LP tokens, so now 1 LP is worth 1501 USD.
- Bob's transaction gets executed:
- PoolValue: 1501e18 ≈ 1501 USD.
- LP mint amount:
(1e18 * 1500e8) / 1501e18 = 99999999-- to 18 decimals -->99999999 / 1e(26-18) = 99999999 / 1e8 = 0 (rounding down). - Due to rounding down in
lpAmountcalculation, it causes thelpAmountto be 0.
- Bob also specifies the slippage
_minLpAmount. This attack will create a DoS if_minLpAmountis specified. If not, this attack will steal Bob's deposits.
Recommendation
Restrict burning to protocol roles: Pool contract for removing liquidity or disallowing public burn() and burnFrom() if lpSupply is less than the minimum LP supply.
Report provenance
Published by Pashov Audit Group, with Carlos (Bloqarl) participating as a contractor. The source report states that its fixes review was not fully completed; no final remediation disposition is recorded for this finding. Read the full report.