# Price manipulation by swapping any ````baseToken```` with itself

WooPPV2.swap allows fromToken==toToken==baseToken, so self-swaps drift the oracle price unboundedly, enabling manipulation of WooFI and third-party price consumers.

- Payout: Contest reward
- Severity: medium
- Program: Woofi Swap
- Credited: Ironsidesec
- Disclosed: 2024-03-06
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2024-03-woofi-swap-judging/issues/32
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-03-woofi-swap%3AM-3
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:business-logic vuln:oracle-manipulation

**Target:** WooPPV2.swap / _swapBaseToBase and Wooracle price feed
**Root cause:** _swapBaseToBase lacks a require(baseToken1 != baseToken2); when both are the same token, the cached state is used for the second price calc while postPrice writes the new price, producing a net drift instead of a no-op.
**Steps:**
1. Deploy WooPPV2 with USDT quote and WBTC base; post WBTC price 50_000e8, spread 0.001e18, coeff 1e-9.
2. Deposit USDT and WBTC liquidity into the pool.
3. Attacker obtains WBTC (flash loan in real case) and calls `pool.swap({fromToken: wbtc, toToken: wbtc, fromAmount: balance, minToAmount: 0, to: evil, rebateTo: evil})`.
4. Repeat the self-swap in a loop; each iteration drifts the oracle price by ~0.5% (logs show 0.499%, 0.998%, ... 4.967% after 10 iterations).
5. Use the drifted Wooracle price against WooFI or third-party consumers relying on the feed.
**How found:** Code review of _swapBaseToBase: noticed the require checks only exclude zero and quoteToken, not baseToken1==baseToken2; traced that state is cached at L527-528 but postPrice at L556 writes the recomputed price, so a self-swap yields net drift. Confirmed with Foundry PoC comparing normal swap drift (0.502%) vs looped self-swap drift.
**Exploitation:** Attacker needs WBTC capital (flash-loanable), repeatedly swaps baseToken for itself; each self-swap posts a new drifted price to Wooracle, which downstream consumers use, enabling mispriced trades/liquidations against them.
**Escalation:** Looping self-swaps compounds drift unboundedly; drift propagates to all Wooracle consumers (WooFI components and third parties).
**Insight:** When a swap/AMM function caches oracle state then writes a recomputed price, check whether the from/to pair can be identical — a no-op trade that still mutates the price feed is a free oracle-drift primitive; test loops of degenerate swaps against the price feed.
