# H-01 wstETH-ETH Curve LP Token Price can be manipulated to Cause Unexpected Liquidations

Sentiment's Curve wstETH-ETH LP oracle used manipulable get_virtual_price; reentering remove_liquidity suppresses LP price ~43-90%, forcing unfair liquidations.

- Payout: Contest reward
- Severity: high
- Program: Sentiment
- Credited: Bahurum
- Disclosed: 2022-11-27
- Write-up quality: 8/10
- Original: https://github.com/sherlock-audit/2022-12-sentiment-judging/issues/7
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-12-sentiment%3AH-1
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:chained vuln:flash-loan vuln:liquidation vuln:oracle-manipulation vuln:reentrancy

**Target:** StableCurveEthOracle / RiskEngine on Arbitrum
**Root cause:** LP token priced via Curve pool get_virtual_price, which is manipulable by large add/remove_liquidity; remove_liquidity sends ETH before updating internal wstETH balances, enabling view-only reentrancy to read a depressed virtual_price.
**Steps:**
1. Flash-borrow large WETH (e.g. 14.5k from Aave), unwrap to ETH
2. Call `POOL.add_liquidity{value: amt}([amt,0], 1)` on Curve wstETH-ETH pool
3. Call `POOL.remove_liquidity(LP.balanceOf(this), [0,0])`
4. In `receive()` reentrancy hook, virtual_price is depressed (~9-56% of original); call RiskEngine.liquidate on undercollateralized accounts
5. After remove_liquidity returns, swap seized wstETH/LP back to ETH, repay flashloan
**How found:** Recognized Curve LP virtual_price as a manipulable spot price (ChainSecurity 'view-only reentrancy' pattern); noticed remove_liquidity sends ETH before updating balances, then built Brownie PoC impersonating a WETH whale to measure virtual_price before/during/after.
**Exploitation:** Attacker flash-borrows WETH, converts to ETH, adds massive liquidity to Curve wstETH-ETH pool, then removes it; during the ETH-send callback the pool's virtual_price is temporarily suppressed, making leveraged Sentiment accounts appear insolvent. Attacker liquidates them at a discount, then unwinds after remove_liquidity completes and repays the loan. Cost ~37-60 BPS of capital used.
**Escalation:** Chained oracle manipulation with reentrancy callback to liquidate multiple accounts in one tx; scaling capital via flashloan raises suppression from 11% to 43-90%.
**Insight:** Any oracle reading Curve get_virtual_price is manipulable; the ETH-first transfer in remove_liquidity lets you read a depressed price inside the callback. Test oracles by simulating add/remove_liquidity with flashloan-sized capital and reading price in the receive() hook.
