VOW/VSC usdRate-DEX divergence arbitrage drains Uniswap V2 liquidity
Exploit Transactions
0x758efef41e60c0f218682e2fa027c54d8b67029d193dd7277d6a881a24b9a561Victim Addresses
0x1bbf25e71ec48b84d773809b4ba55b6f4be946fbEthereum0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937AEthereum0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBbEthereumLoss Breakdown
Similar Incidents
HANA tax-wallet MEV arbitrage on Uniswap V2
35%SASHA cross-DEX MEV arbitrage extracts ETH pricing spread
35%MachineShare CurveStableSwapNG mispricing arbitrage extracts ETH-side liquidity
33%SorbettoFragola Aave/Uniswap route arbitrage extracts USDC via fee collection
33%DOGGO/WETH cross-pool arbitrage MEV extracts WETH spread
32%Multi-venue stablecoin/WETH MEV arbitrage on Ethereum mainnet
31%Root Cause Analysis
VOW/VSC usdRate-DEX divergence arbitrage drains Uniswap V2 liquidity
1. Incident Overview TL;DR
An unprivileged EOA used router 0xB7F2... to perform a single-transaction arbitrage that burns VOWToken for VSC at a fixed usdRate-based price (1 VOW = 100 VSC) via oracle/adapter 0x1844..., then swaps the minted VSC into underpriced VOW on Uniswap V2 pair 0x97BE..., and finally converts excess VOW into WETH/ETH, realizing ~1.75e20 wei of ETH profit after repaying the flash swap and gas.
The protocol exposes a fixed on-chain VOW→VSC conversion at usdRate_VOW=[1,100] and usdRate_VSC=[1,1] without any coupling to the external VSC/VOW Uniswap price, allowing anyone to mint VSC at the usdRate-defined price and immediately dump it into a severely mispriced VSC/VOW pool for deterministic profit (a MEV-style ACT opportunity, not an arithmetic bug).
2. Key Background
- VOWToken is an ERC777/20 token whose usdRate = [numTokens,numUSD] encodes the intended USD price of VOW (e.g., [1,100] means 1 VOW is treated as 100 USD) and which supports minting and burning via partner contracts and a usdRateSetter.
- VSC (0x0fc6...) is an ERC777/20-style stablecoin implemented via delegatecalls into logic contract 0x60ca4e..., with its own usdRate = [numTokens,numUSD] and usdRateSetter; 0x1844... is configured to set VSC’s usdRate based on an external Provable oracle and VOW’s state.
- Oracle/adapter 0x1844... receives VOWToken via ERC777 tokensReceived, burns it at VOWToken, queries usdRate_VSC and usdRate_VOW, and mints VSC to callers so that the minted VSC’s notional USD value matches the burned VOW’s USD value according to those usdRate pairs.
- Uniswap V2 pair 0x97BE... holds reserves of VSC and VOW and enforces the standard x*y=k invariant; immediately before the seed tx, its reserves price 1 VSC at roughly 1.21 VOW, implying that markets were treating VSC as roughly equal (or superior) to VOW, despite the internal accounting that values 1 VOW at 100 VSC.
3. Vulnerability Analysis & Root Cause Summary
A fixed-rate on-chain conversion between VOW and VSC, defined by usdRate_VOW and usdRate_VSC and implemented in 0x1844..., is left unhedged against the external VSC/VOW Uniswap price; when the pool becomes severely mispriced (1 VSC ≈ 1.21 VOW instead of ≈0.01 VOW), anyone can burn VOW for VSC at the generous internal rate and then swap the VSC back into far more VOW on the DEX.
Invariant and Breakpoint.
- Invariant: Given usdRate_VOW = [T_VOW, U_VOW] and usdRate_VSC = [T_VSC, U_VSC], the intended accounting invariant of the VOW/VSC system is that burning A VOW at adapter 0x1844... and minting B VSC should preserve USD value at the contract-defined rates: B * (U_VSC / T_VSC) = A * (U_VOW / T_VOW). Additionally, for economic safety, any liquid on-chain VSC↔VOW market used by the protocol (e.g., Uniswap pair 0x97BE...) should not allow a user to combine the burn-and-mint path with AMM swaps to obtain net VOW or ETH value exceeding their initial VOW deposit at current market prices.
- Breakpoint: At σ_B, usdRate_VSC = [1,1] and usdRate_VOW = [1,100], so adapter 0x1844... correctly mints B = A * 100 VSC when burning A VOW (preserving USD value at internal rates), but the Uniswap V2 VSC/VOW pool 0x97BE... is priced around 1 VSC ≈ 1.21 VOW instead of the consistent price 1 VSC ≈ 0.01 VOW implied by usdRate_VSC / usdRate_VOW. The first concrete breakpoint is the call from 0x1844... to 0x0fc6...mint(EOA, 148662529615049936043166400) in the seed trace, followed by sending that VSC into the mispriced 0x97BE... swap; together these operations violate the broader economic safety invariant by letting the adversary turn A VOW into significantly more than A VOW-worth of assets at prevailing DEX prices.
4. Detailed Root Cause Analysis
The logic contract 0x60ca4e... provides generic ERC777/20 bookkeeping and does not reference usdRate or any VOW/VSC-specific fields; its functions are invoked via delegatecall from VSC 0x0fc6... to update balances and totalSupply. VOWToken’s source shows usdRate as a simple [numTokens,numUSD] pair with a private doSetUSDRate(numTokens,numUSD) that enforces both components are nonzero but imposes no further constraints or linkage to external markets. VSC 0x0fc6... similarly maintains its own usdRate and an onlyUSDRateSetter-gated setUSDRate(numTokens,numUSD). Oracle/adapter 0x1844... integrates a Provable oracle to compute a new VSC usdRate and calls 0x0fc6...setUSDRate(var_y,var_c) in its callback, then, during ERC777 tokensReceived when it receives VOW, burns the incoming VOW via VOWToken::burn and mints VSC by calling 0x0fc6...mint based on the current usdRate_VOW and usdRate_VSC values. The seed trace shows explicit usdRate reads: VSC.usdRate(1) → 1, VSC.usdRate(0) → 1, VOWToken.usdRate(0) → 1, VOWToken.usdRate(1) → 100 immediately before minting 148662529615049936043166400 VSC for 1486625296150499360431664 VOW. This matches the formula B = A * (U_VOW / T_VOW) * (T_VSC / U_VSC) with A = 1.4866e24, U_VOW/T_VOW = 100, and T_VSC/U_VSC = 1, demonstrating that the contracts are faithfully implementing a fixed USD-value conversion between VOW and VSC at the configured usdRates. However, the Uniswap V2 VSC/VOW pair 0x97BE... has reserves (≈7.4044e25 VSC, ≈8.9719e25 VOW) implying price(VSC in VOW) ≈ 1.21, i.e., markets value 1 VSC at slightly more than 1 VOW rather than 0.01 VOW. By sending the freshly minted 1.4866e26 VSC into 0x97BE..., the adversary obtains a much larger VOW amount than the 1.4866e24 VOW initially burned, repays the flash-swapped VOW from 0x7FdEB46..., and routes the surplus VOW through the VOW/USDT pool and WETH to their EOA as ETH. No arithmetic or access-control mistakes are required: the vulnerability is that the protocol exposes a generous, on-chain, usdRate-based conversion between VOW and VSC without any guardrails tying it to the external VSC/VOW market price, creating a pure MEV arbitrage opportunity whenever the pool drifts far from the internal rates.
Vulnerable components.
- VOWToken.usdRate and setUSDRate/doSetUSDRate in artifacts/root_cause/data_collector/iter_2/contract/1/0x1bbf25e71ec48b84d773809b4ba55b6f4be946fb/source/src/Contract.sol (the internal USD pricing of VOW).
- VSC 0x0fc6...usdRate, setUSDRate, and mint in artifacts/root_cause/data_collector/iter_2/contract/1/0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A/decompile/0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A-decompiled.sol (the internal USD pricing and minting of VSC).
- Oracle/adapter 0x1844... Provable callback and tokensReceived/burn-and-mint flow in artifacts/root_cause/data_collector/iter_2/contract/1/0x184497031808F2b6A2126886C712CC41f146E5dC/decompile/0x184497031808F2b6A2126886C712CC41f146E5dC-decompiled.sol, which uses usdRate_VOW and usdRate_VSC to mint VSC when VOW is burned but does not check or respond to external VSC/VOW market prices.
- Uniswap V2 pair 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb (artifacts/root_cause/data_collector/iter_3/contract/1/0x97be09f2523b39b835da9ea3857cfa1d3c660cbb/decompile/0x97be09f2523b39b835da9ea3857cfa1d3c660cbb-decompiled.sol) as the liquidity venue whose mispriced reserves enable the arbitrage, although its code behaves as designed.
ACT exploit conditions.
- usdRate_VOW and usdRate_VSC are configured such that burning A VOW via 0x1844... and minting VSC produces a meaningful USD-value uplift relative to the prevailing VSC/VOW DEX price—for example, usdRate_VOW=[1,100] and usdRate_VSC=[1,1] while the Uniswap V2 VSC/VOW pool prices 1 VSC ≈ 1 VOW.
- The VSC/VOW Uniswap pool 0x97BE... must have sufficient VSC and VOW liquidity and a mispricing large enough that swapping freshly minted VSC into VOW yields more VOW than was originally burned, after accounting for AMM fees and slippage.
- The adversary must have access to flash liquidity in VOW or WETH (e.g., via Uniswap V2 pair 0x7FdEB46...), but only as a capability to source initial VOW; no special privileges are needed.
- Gas and pool fee costs must be small relative to the arbitrage spread so that the resulting ETH profit after fees is strictly positive.
Security principles violated.
- Lack of price-consistency between protocol-level accounting and external markets: the protocol relies on usdRate-based internal prices for VOW and VSC but does not enforce or monitor consistency between these and the DEX prices at which its tokens trade.
- Missing circuit breakers or caps around privileged mint/burn conversions: 0x1844... can mint large amounts of VSC based on usdRate configuration without any check that swapping those tokens into VOW on available DEX pools would cause outsized, essentially risk-free profit.
- Economic design assumption that market prices will track usdRate configuration is not enforced or defended at the contract level, leaving the system exposed to anyone who notices and arbitrages a large divergence.
Key Code and Trace Evidence
contracts)
* - has a USD price
*/
contract VOWToken is Token, IERC777Recipient, Owned {
mapping (bytes32 => bool) public proxyProofs;
uint256[2] public usdRate;
address public usdRateSetter;
mapping(bytes32 => address payable) public partnerContracts;
// precalculated hash - see https://github.com/ethereum/solidity/issues/4024
// keccak256("ERC777TokensRecipien
Seed contract source for VOWToken showing usdRate, usdRateSetter, and setUSDRate / doSetUSDRate. These fields encode the internal USD price of VOW without referencing external markets.
│ │ │ │ ├─ emit Sent(operator: 0xB7F221e373e3F44409F91C233477ec2859261758, from: 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, to: 0x184497031808F2b6A2126886C712CC41f146E5dC, amount: 1486625296150499360431664 [1.486e24], data: 0x, operatorData: 0x)
│ │ │ │ ├─ emit Transfer(from: 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, to: 0x184497031808F2b6A2126886C712CC41f146E5dC, value: 1486625296150499360431664 [1.486e24])
│ │ │ │ ├─ [2942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0x184497031808F2b6A2126886C712CC41f146E5dC, 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x000000000000000000000000184497031808f2b6a2126886c712cc41f146e5dc
│ │ │ │ ├─ [75895] 0x184497031808F2b6A2126886C712CC41f146E5dC::tokensReceived(0xB7F221e373e3F44409F91C233477ec2859261758, 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, 0x184497031808F2b6A2126886C712CC41f146E5dC, 1486625296150499360431664 [1.486e24], 0x, 0x)
│ │ │ │ │ ├─ [16985] VOWToken::burn(1486625296150499360431664 [1.486e24], 0x)
│ │ │ │ │ │ ├─ [15403] LToken::24394467(0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000184497031808f2b6a2126886c712cc41f146e5dc000000000000000000000000184497031808f2b6a2126886c712cc41f146e5dc000000000000000000000000000000000000000000013ace1e68e27d5252de3000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ │ │ │ ├─ [2942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0x184497031808F2b6A2126886C712CC41f146E5dC, 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895) [staticcall]
│ │ │ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ │ │ │ ├─ emit Burned(operator: 0x184497031808F2b6A2126886C712CC41f146E5dC, from: 0x184497031808F2b6A2126886C712CC41f146E5dC, amount: 1486625296150499360431664 [1.486e24], data: 0x, operatorData: 0x)
│ │ │ │ │ │ │ ├─ emit Transfer(from: 0x184497031808F2b6A2126886C712CC41f146E5dC, to: 0x0000000000000000000000000000000000000000, value: 1486625296150499360431664 [1.486e24])
│ │ │ │ │ │ │ ├─ storage changes:
│ │ │ │ │ │ │ │ @ 2: 0x000000000000000000000000000000000000000002a62884d97d3c9884cc0000 → 0x000000000000000000000000000000000000000002a4edb6bb145a1b327921d0
│ │ │ │ │ │ │ │ @ 0xe8be5022a05add857fa245ee838519909345ade3e5354103ba25b4c157b9d2e6: 0x000000000000000000000000000000000000000000013ace1e68e27d5252de30 → 0
│ │ │ │ │ │ │ └─ ← [Stop]
│ │ │ │ │ │ └─ ← [Stop]
│ │ │ │ │ ├─ [2474] 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A::usdRate(1) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 1
│ │ │ │ │ ├─ [2518] VOWToken::usdRate(0) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 1
│ │ │ │ │ ├─ [2474] 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A::usdRate(0) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 1
│ │ │ │ │ ├─ [2518] VOWToken::usdRate(1) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 100
│ │ │ │ │ ├─ [38103] 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A::mint(0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, 148662529615049936043166400 [1.486e26])
│ │ │ │ │ │ ├─ [35105] LToken::6eeb9e0f(000000000000000000000000000000000000000000000000000000000000000200000000000000000000000048de6bf9e301946b0a32b053804c61dc5f00c0c30000000000000000000000000000000000000000007af883e0f878f4285ecac0) [delegatecall]
│ │ │ │ │ │ │ ├─ [942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b) [staticcall]
│ │ │ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ │ │ │ ├─ emit Minted(operator: 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A, to: 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, amount: 148662529615049936043166400 [1.486e26], data: 0x, operatorData: 0x)
│ │ │ │ │ │ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, value: 148662529615049936043166400 [1.486e26])
│ │ │ │ │ │ │ ├─ storage changes:
│ │ │ │ │ │ │ │ @ 2: 0x000000000000000000000000000000000000000000808b6495b97bb14cd26395 → 0x000000000000000000000000000000000000000000fb83e876b1f4a575312e55
│ │ │ │ │ │ │ │ @ 0x721c83cffcd84686c5efa12cd02eaa8634693b7d2407156d66269fed683d70e5: 0 → 0x0000000000000000000000000000000000000000007af883e0f878f4285ecac0
│ │ │ │ │ │ │ └─ ← [Stop]
│ │ │ │ │ │ └─ ← [Stop]
│ │ │ │ │ ├─ emit LogBurnAndMint(param0: 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3, param1: 1486625296150499360431664 [1.486e24], param2: 148662529615049936043166400 [1.486e26])
│ │ │ │ │ └─ ← [Stop]
│ │ │ │ ├─ storage changes:
│ │ │ │ │ @ 0xe8be5022a05add857fa245ee838519909345ade3e5354103ba25b4c157b9d2e6: 0 → 0x000000000000000000000000000000000000000000013ace1e68e27d5252de30
│ │ │ │ │ @ 0x64f695f99468789df34aadbbf666f461724d6ac9f8cf95ea939b54e4a90a4138: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff → 0xfffffffffffffffffffffffffffffffffffffffffffec531e1971d82adad21cf
Seed transaction trace for tx 0x758efef4…561 showing VOWToken::burn of 1.4866e24 VOW, consecutive usdRate() calls returning [1,1] for VSC and [1,100] for VOW, and minting of 1.4866e26 VSC to the attacker EOA.
│ │ │ └─ ← [Revert] unknown selector `0x9710a9d0` for ConsoleCalls
│ │ ├─ [2504] 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb::getReserves() [staticcall]
│ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000003d3f71261fc9a9ea7b24e30000000000000000000000000000000000000000004a36aede1d61489641480f0000000000000000000000000000000000000000000000000000000066bb3b77
│ │ ├─ [2569] 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A::balanceOf(0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb) [staticcall]
│ │ │ └─ ← [Return] 74044071314129525824759011 [7.404e25]
│ │ ├─ [36024] 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A::transfer(0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, 148662529615049936043166400 [1.486e26])
│ │ │ ├─ [8603] LToken::24394467(0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b7f221e373e3f44409f91c233477ec2859261758000000000000000000000000b7f221e373e3f44409f91c233477ec285926175800000000000000000000000000000000000000000001f7b030a7d0c883b7c9e600000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ ├─ [942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0xB7F221e373e3F44409F91C233477ec2859261758, 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ ├─ emit Burned(operator: 0xB7F221e373e3F44409F91C233477ec2859261758, from: 0xB7F221e373e3F44409F91C233477ec2859261758, amount: 2378600473840798976690662 [2.378e24], data: 0x, operatorData: 0x)
│ │ │ │ ├─ emit Transfer(from: 0xB7F221e373e3F44409F91C233477ec2859261758, to: 0x0000000000000000000000000000000000000000, value: 2378600473840798976690662 [2.378e24])
│ │ │ │ ├─ storage changes:
│ │ │ │ │ @ 0xe939b9fb7d1348361637532f162b715f264a3d0a77498f9f2f5f57493d221715: 0x0000000000000000000000000000000000000000007af883e0f878f4285ecac0 → 0x0000000000000000000000000000000000000000007900d3b050a82ba4a700da
│ │ │ │ │ @ 2: 0x000000000000000000000000000000000000000000fb83e876b1f4a575312e55 → 0x000000000000000000000000000000000000000000f98c38460a23dcf179646f
│ │ │ │ └─ ← [Stop]
│ │ │ ├─ [15645] LToken::b750dc46(0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b7f221e373e3f44409f91c233477ec2859261758000000000000000000000000b7f221e373e3f44409f91c233477ec285926175800000000000000000000000097be09f2523b39b835da9ea3857cfa1d3c660cbb0000000000000000000000000000000000000000007900d3b050a82ba4a700da00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ ├─ [942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0xB7F221e373e3F44409F91C233477ec2859261758, 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ ├─ emit Sent(operator: 0xB7F221e373e3F44409F91C233477ec2859261758, from: 0xB7F221e373e3F44409F91C233477ec2859261758, to: 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, amount: 146283929141209137066475738 [1.462e26], data: 0x, operatorData: 0x)
│ │ │ │ ├─ emit Transfer(from: 0xB7F221e373e3F44409F91C233477ec2859261758, to: 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, value: 146283929141209137066475738 [1.462e26])
│ │ │ │ ├─ [2942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ ├─ storage changes:
│ │ │ │ │ @ 0xe939b9fb7d1348361637532f162b715f264a3d0a77498f9f2f5f57493d221715: 0x0000000000000000000000000000000000000000007900d3b050a82ba4a700da → 0
│ │ │ │ │ @ 0xb69b91eb4a7b4411ee3f68d8e1694b8552404cf1c606a9a1467f9d933704ecf4: 0x0000000000000000000000000000000000000000003d3f71261fc9a9ea7b24e3 → 0x000000000000000000000000000000000000000000b64044d67071d58f2225bd
│ │ │ │ └─ ← [Stop]
│ │ │ └─ ← [Return] true
│ │ ├─ [569] 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A::balanceOf(0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb) [staticcall]
│ │ │ └─ ← [Return] 220328000455338662891234749 [2.203e26]
│ │ ├─ [71483] 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb::swap(0, 59507425738366327370721501 [5.95e25], 0xB7F221e373e3F44409F91C233477ec2859261758, 0x)
│ │ │ ├─ [38978] VOWToken::transfer(0xB7F221e373e3F44409F91C233477ec2859261758, 59507425738366327370721501 [5.95e25])
│ │ │ │ ├─ [37545] LToken::b750dc46(000000000000000000000000000000000000000000000000000000000000000200000000000000000000000097be09f2523b39b835da9ea3857cfa1d3c660cbb00000000000000000000000097be09f2523b39b835da9ea3857cfa1d3c660cbb000000000000000000000000b7f221e373e3f44409f91c233477ec28592617580000000000000000000000000000000000000000003139300362e62827eea8dd00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ │ ├─ [2942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ │ ├─ emit Sent(operator: 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, from: 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, to: 0xB7F221e373e3F44409F91C233477ec2859261758, amount: 59507425738366327370721501 [5.95e25], data: 0x, operatorData: 0x)
│ │ │ │ │ ├─ emit Transfer(from: 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb, to: 0xB7F221e373e3F44409F91C233477ec2859261758, value: 59507425738366327370721501 [5.95e25])
│ │ │ │ │ ├─ [942] 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24::getInterfaceImplementer(0xB7F221e373e3F44409F91C233477ec2859261758, 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ │ │ ├─ storage changes:
│ │ │ │ │ │ @ 0xe939b9fb7d1348361637532f162b715f264a3d0a77498f9f2f5f57493d221715: 0 → 0x0000000000000000000000000000000000000000003139300362e62827eea8dd
Seed transaction trace for tx 0x758efef4…561 showing UniswapV2 pair 0x97BE…::getReserves returning reserves ≈7.4044e25 VSC and ≈8.9719e25 VOW and performing a swap of ≈1.462e26 VSC into ≈5.95e25 VOW to the router.
5. Adversary Flow Analysis
Single-transaction MEV arbitrage: use a flash swap to source VOW, burn it for VSC at an overly generous usdRate-based price via 0x1844..., dump VSC into a mispriced VSC/VOW Uniswap pool to recover far more VOW than was burned, then convert surplus VOW to ETH and repay the flash swap.
Adversary-related cluster accounts.
- 0x48de6bF9e301946b0a32b053804c61DC5f00c0c3 (chainid 1): Sender of the seed transaction, net receiver of 175471309900400035442 wei of ETH per balance_diff.json, and immediate profit beneficiary.
- 0xB7F221e373e3F44409F91C233477ec2859261758 (chainid 1): Router/orchestrator directly called by the EOA in the seed tx; its decompiled code (artifacts/root_cause/data_collector/iter_2/contract/1/0xB7F221e373e3F44409F91C233477ec2859261758/decompile/...) coordinates the flash swap, VOW transfer to 0x1844..., VSC minting, and subsequent swaps, indicating it is an adversary-controlled helper contract.
Victim contracts and components.
- VOWToken at 0x1bbf25e71ec48b84d773809b4ba55b6f4be946fb (chainid 1): Core asset whose usdRate and burn/mint hooks underpin the VOW↔VSC conversion; loses supply in the burn step and is drawn from Uniswap pools to pay out VSC-originated swaps.
- VSC Stablecoin (0x0fc6...) at 0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A (chainid 1): Stablecoin minted in large quantity during the attack and dumped into the VSC/VOW pool; its usdRate configuration and interaction with 0x1844... define the internal conversion rate exploited by the adversary.
- VSC/VOW Uniswap V2 Pair at 0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBb (chainid 1): Liquidity pool that is economically victimized: it sells VOW too cheaply for VSC relative to the protocol’s internal usdRate, transferring value from LPs to the adversary.
Adversary execution stages.
- Flash swap VOW from VOW/WETH pool: Router 0xB7F2... calls Uniswap V2 pair 0x7FdEB46...::swap to flash-swap ≈1.4866e24 VOW into 0xB7F2..., which is then forwarded to the EOA and subsequently to 0x1844... for burning.
- Burn VOW and mint VSC via usdRate-based conversion: The EOA transfers the flash-swapped VOW to 0x1844..., triggering VOWToken::burn(1.4866e24) and a sequence of usdRate calls (VSC.usdRate, VOWToken.usdRate), after which 0x1844... calls 0x0fc6...mint(EOA, 1.4866e26) to mint VSC whose USD value equals that of the burned VOW at internal rates.
- Swap VSC into underpriced VOW and unwind into ETH: Router 0xB7F2... transfers the minted VSC into Uniswap V2 pair 0x97BE..., whose reserves price 1 VSC ≈ 1.21 VOW; the pair swaps the VSC into a much larger VOW balance, which is then traded through the VOW/USDT pool 0x1E4976... and WETH, repaying the initial VOW flash swap and leaving the EOA with ~1.75e20 wei of net ETH profit.
6. Impact & Losses
From balance_diff.json, the attacker EOA’s native ETH balance increases by 175471309900400035442 wei (~175.47 ETH), funded by a corresponding loss from the WETH contract and value transferred out of VOW and LP positions in the VSC/VOW and VOW/USDT Uniswap pools. The core impact is value drained from LPs and VOW/VSC ecosystem participants via a large, one-shot arbitrage across the burn-and-mint path and mispriced DEX pool.
Quantitative summary (reference asset ETH).
- ETH: 175471309900400035442 (wei units)
7. References
- [1] Seed tx metadata: artifacts/root_cause/seed/1/0x758efef41e60c0f218682e2fa027c54d8b67029d193dd7277d6a881a24b9a561/metadata.json
- [2] Seed tx trace: artifacts/root_cause/seed/1/0x758efef41e60c0f218682e2fa027c54d8b67029d193dd7277d6a881a24b9a561/trace.cast.log
- [3] VOWToken source: artifacts/root_cause/data_collector/iter_2/contract/1/0x1bbf25e71ec48b84d773809b4ba55b6f4be946fb/source/src/Contract.sol
- [4] VSC 0x0fc6... decompiled: artifacts/root_cause/data_collector/iter_2/contract/1/0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A/decompile/0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937A-decompiled.sol
- [5] Oracle/adapter 0x1844... decompiled: artifacts/root_cause/data_collector/iter_2/contract/1/0x184497031808F2b6A2126886C712CC41f146E5dC/decompile/0x184497031808F2b6A2126886C712CC41f146E5dC-decompiled.sol
- [6] VSC/VOW Uniswap pair 0x97BE... decompiled: artifacts/root_cause/data_collector/iter_3/contract/1/0x97be09f2523b39b835da9ea3857cfa1d3c660cbb/decompile/0x97be09f2523b39b835da9ea3857cfa1d3c660cbb-decompiled.sol