Calculated from recorded token losses using historical USD prices at the incident time.
0x758efef41e60c0f218682e2fa027c54d8b67029d193dd7277d6a881a24b9a5610x1bbf25e71ec48b84d773809b4ba55b6f4be946fbEthereum0x0fc6C0465C9739d4a42dAca22eB3b2CB0Eb9937AEthereum0x97BE09f2523B39B835Da9EA3857CfA1D3C660cBbEthereumAn 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).
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.
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.
ACT exploit conditions.
Security principles violated.
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.
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.
Victim contracts and components.
Adversary execution stages.
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).