This is a lower bound: only assets with reliable historical USD prices are counted, so the actual loss may be higher.
0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd0xeFF23B4bE1091b53205E35f3AfCD9C7182bf3062Arbitrum0x73504eaCB100c7576146618DC306c97454CB3620ArbitrumOn Arbitrum block 187381785, transaction 0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd let an unprivileged attacker drain value from WooPPV2 at 0xeFF23B4bE1091b53205E35f3AfCD9C7182bf3062. The attacker used only permissionless liquidity sources: a Uniswap V3 USDC flash loan, a Trader Joe LB WOO flash loan, and a temporary overcollateralized Silo WOO borrow. The loss landed on WooPPV2, while Silo and the flash lenders were fully repaid within the same transaction.
The root cause is a closed pricing loop between WooPPV2 and Wooracle. WooPPV2 computes a new base price directly from the attacker-chosen trade amount and immediately calls postPrice on Wooracle. Wooracle then treats that posted WOO price as feasible because WOO has no Chainlink anchor configured, so the manipulated price remains usable for the very next swap. The attacker used this to collapse WOO price to 9, then bought back an outsized amount of WOO and extracted WETH, USDC, and WOO profit.
WooPPV2 prices swaps through Wooracle state and updates that same Wooracle state during swap execution. In the verified WooPPV2 source, _sellBase, _sellQuote, and _swapBaseToBase all derive a newPrice from swap math and immediately call IWooracleV2(wooracle).postPrice(...).
The verified Wooracle implementation makes the anchor check critical. In WooracleV2_1::price, the bound check is:
bool woPriceInBound = cloPrice_ == 0 ||
((cloPrice_ * (1e18 - bound)) / 1e18 <= woPrice_ &&
woPrice_ <= (cloPrice_ * (1e18 + bound)) / 1e18);
If cloPrice_ == 0, any nonzero fresh Wooracle price is treated as in-bounds. The seed trace and validator PoC both confirm cloAddress(WOO) == address(0), so WOO had no external Chainlink anchor at exploit time.
Silo matters only as temporary inventory. The attacker deposited 7000000000000 USDC as collateral-only and borrowed 5092663209802375827262435 WOO, but the debt was fully repaid by the end of the transaction, leaving WooPPV2 as the value-losing component.
This is an ATTACK-class ACT opportunity caused by self-referential pricing, not by stolen keys or privileged access. WooPPV2 let an arbitrary trader rewrite WOO pricing state with trade-size-driven math, and Wooracle accepted that rewritten state as feasible because WOO lacked an external anchor. The attacker first raised the internal WOO state modestly, then sold a very large amount of WOO into WooPPV2 so that _calcQuoteAmountSellBase produced a newPrice of 9. WooPPV2 posted that value into Wooracle before the transaction ended. Because Wooracle still reported state(WOO).woFeasible == true, the attacker immediately executed the reverse path and bought WOO from WooPPV2 at the fabricated price. The exploit is fully permissionless because every leg used public contracts and public liquidity. The broken invariant is that the execution price consumed by a swap must not be rewritable by the same untrusted trader immediately before that price is consumed.
The vulnerable WooPPV2 logic is explicit in the verified source:
function _sellBase(...) private returns (uint256 quoteAmount) {
IWooracleV2.State memory state = IWooracleV2(wooracle).state(baseToken);
(quoteAmount, newPrice) = _calcQuoteAmountSellBase(baseToken, baseAmount, state);
IWooracleV2(wooracle).postPrice(baseToken, uint128(newPrice));
}
function _sellQuote(...) private returns (uint256 baseAmount) {
IWooracleV2.State memory state = IWooracleV2(wooracle).state(baseToken);
(baseAmount, newPrice) = _calcBaseAmountSellQuote(baseToken, quoteAmount, state);
IWooracleV2(wooracle).postPrice(baseToken, uint128(newPrice));
}
The pricing formulas are also attacker-sensitive:
newPrice =
((uint256(1e18) - (uint256(2) * state.coeff * state.price * baseAmount) / decs.priceDec / decs.baseDec) *
state.price) /
1e18;
That means a sufficiently large baseAmount can force newPrice sharply downward. The seed trace shows the exploit following exactly that path:
Silo::deposit(USDC, 7000000000000, true)
Silo::borrow(WOO, 5092663209802375827262435)
WooracleV2_1_ZipInherit::postPrice(WOO, 57853248)
WooracleV2_1_ZipInherit::postPrice(WOO, 9)
WooracleV2_1_ZipInherit::state(WOO) -> State({ price: 9, ..., woFeasible: true })
The attacker sequence was:
10580749131947 USDC from the Uniswap V3 pool at 0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443.2704558014737261187877737 WOO from the Trader Joe LB pair at 0xB87495219C432fc85161e4283DfF131692A528BD.7000000000000 USDC into Silo 0x5C2B80214c1961dB06f69DD4128BcfFc6423d44F and borrow 5092663209802375827262435 WOO.2000000000000 USDC to WooPPV2 and swap USDC to WETH.100000000000 USDC to WooPPV2 and swap USDC to WOO, increasing WOO state to 57853248.7856868800000000000000000 WOO to WooPPV2 and swap WOO to USDC, which drives the posted WOO price to 9.926342 USDC to WooPPV2 and immediately swap USDC back to WOO while state(WOO).price == 9 and woFeasible == true.The balance-diff artifact confirms the victim-side depletion and attacker-side profit:
{
"holder": "0xeff23b4be1091b53205e35f3afcd9c7182bf3062",
"token": "WOO",
"delta": "-2549723890734172915151516"
}
{
"holder": "0xeff23b4be1091b53205e35f3afcd9c7182bf3062",
"token": "WETH",
"delta": "-522295261244159469020"
}
{
"holder": "0x9961190b258897bca7a12b8f37f415e689d281c4",
"token": "WOO",
"delta": "2549710367944099228845575"
}
This matches the code-level breakpoint: WooPPV2 accepted an attacker-crafted WOO trade, posted the manipulated newPrice, and then allowed the attacker to consume that same manipulated price before any external anchor rejected it.
The adversary EOA was 0x9961190b258897bca7a12b8f37f415e689d281c4. The transaction deployed and used helper contract 0x1759f791214168E0292ab6B2180Da1c4cf9B764e, but the opportunity itself remained permissionless because the helper was created by the attacker inside the same transaction.
The seed metadata shows a single adversary-crafted transaction, and the trace shows the entire exploit completing atomically. No privileged role changes, private calldata, or off-chain secrets appear in the flow. The critical decision point was choosing a WOO dump size large enough to make WooPPV2 post newPrice = 9 while still leaving the state feasible. Once that state update landed, the recovery-leg USDC-to-WOO swap became trivially profitable. The attacker then exited cleanly by repaying the temporary Silo borrow, repaying Trader Joe LB flash liquidity, repaying the Uniswap flash loan plus fee, swapping excess WETH to ETH, and retaining leftover WOO.
WooPPV2 directly lost three assets:
2549723890734172915151516 WOO (decimal = 18)522295261244159469020 WETH (decimal = 18)146891759665 USDC (decimal = 6)The attacker EOA gained 559354504555832856907 wei of native ETH net of starting ETH balance and retained 2549710367944099228845575 WOO after repaying all temporary debt. The exploit therefore realized a concrete, measurable ACT profit and depleted WooPPV2 inventory in the same transaction.
0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd/workspace/session/artifacts/collector/seed/42161/0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd/trace.cast.log/workspace/session/artifacts/collector/seed/42161/0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd/balance_diff.json0xeFF23B4bE1091b53205E35f3AfCD9C7182bf30620x73504eaCB100c7576146618DC306c97454CB3620/workspace/session/artifacts/validator/forge-test.log