Calculated from recorded token losses using historical USD prices at the incident time.
0xd493c73397952049644c531309df3dd4134bf3db1e64eb6f0b68b016ee0bffde0x786b374b5eef874279f4b7b4de16940e57301a58Ethereum0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7Ethereum0xa464e6dcda8ac41e03616f95f4bc98a13b8922dcEthereumIn Ethereum mainnet transaction 0xd493c73397952049644c531309df3dd4134bf3db1e64eb6f0b68b016ee0bffde at block 17823543, an unprivileged attacker executed a one-transaction MEV sandwich around Curve's public Underlying Burner at 0x786b374b5eef874279f4b7b4de16940e57301a58. The attacker temporarily skewed Curve 3pool, triggered the burner while the pool price was distorted, then unwound the distortion and exited with 36819.398770 USDT. The root cause was a public fee-conversion path that moved protocol-owned DAI, USDC, and USDT through Curve 3pool without any slippage bound.
Curve's Underlying Burner is a public maintenance contract that processes accumulated admin fees. When it holds DAI, USDC, and USDT, it converts those balances into 3CRV and forwards the 3CRV to the fee receiver 0xa464e6dcda8ac41e03616f95f4bc98a13b8922dc.
The critical design property is that the burn path is permissionless and price-sensitive. Curve 3pool itself is publicly accessible and large enough to support temporary intra-transaction reserve distortions when a searcher can assemble enough transient liquidity from flash loans and public lending markets.
The collected victim-code excerpt for the burner shows the vulnerable behavior directly:
@external
def execute() -> bool:
amounts: uint256[3] = [
ERC20(TRIPOOL_COINS[0]).balanceOf(self),
ERC20(TRIPOOL_COINS[1]).balanceOf(),
ERC20(TRIPOOL_COINS[]).balanceOf(),
]
amounts[] != amounts[] != amounts[] != :
StableSwap(TRIPOOL).add_liquidity(amounts, )
amount: uint256 = ERC20(TRIPOOL_LP).balanceOf()
amount != :
ERC20(TRIPOOL_LP).transfer(.receiver, amount)
This incident is a pure MEV exposure, not a broken access-control or arithmetic bug. The vulnerable component is the public execute() path of the Curve Underlying Burner, which converts protocol-owned stablecoins through 3pool at the live spot price and uses min_mint_amount=0. That means the contract accepts whatever exchange rate exists at execution time, even if an attacker has just distorted the pool inside the same transaction. The relevant invariant is that protocol-owned fee balances should not be converted at an adversarially worsened AMM price. The code-level breakpoint is the burner's StableSwap(TRIPOOL).add_liquidity(amounts, 0) call. Once the attacker can skew 3pool and invoke execute() before unwinding, the burner deterministically mints too little 3CRV for the fee receiver and the attacker captures the spread on the unwind.
The pre-state before block 17823543 already contained burner inventory waiting to be processed. The attacker assembled temporary liquidity from Balancer, Aave v3, Aave v2, and Compound, then used that capital to move 3pool away from fair value.
The collected execution trace shows the exact exploit sequence:
Vyper_contract::add_liquidity([0, 0, 155744911000000], 1)
Vyper_contract::remove_liquidity_imbalance(
[48469602256943767357509742, 51636845451630, 0],
151608546662024026882254825
)
0x786B374B5eef874279f4B7b4de16940e57301A58::execute()
Vyper_contract::add_liquidity([100000, 100000, 227079039776], 0)
Vyper_contract::add_liquidity([48469602256943767357509742, 51636845451630, 0], 1)
Vyper_contract::remove_liquidity_one_coin(151630203850836049405088356, 2, 1)
That trace establishes the complete mechanism. First, the helper contract adds one-sided USDT liquidity and then removes imbalanced DAI and USDC, which skews 3pool. Second, while the pool is in that manipulated state, the attacker calls the public burner. Third, the burner deposits its own balances 100000 DAI, 100000 USDC, and 227079039776 raw USDT into 3pool with zero slippage protection, minting 3CRV for the fee receiver at the distorted rate. Finally, the attacker re-adds the extracted DAI and USDC, removes one-sided USDT, repays every transient-liquidity leg, and keeps the residual USDT.
The balance-diff artifact confirms the victim-side transfer and the attacker-side profit. Burner-held USDT decreases by 227079039776, 3CRV held by the fee receiver increases by 138955767859907228697693, and attacker EOA 0xccc526e2433db1eebb9cbf6acd7f03a19408278c gains 36819398770 raw USDT. No invariant inside Aave or Compound was broken; those protocols only provided temporary capital.
The attacker cluster consists of EOA 0xccc526e2433db1eebb9cbf6acd7f03a19408278c and helper contract 0x915dff6707bea63daea1b41aa5d37353229066ba. The EOA deployed the helper in transaction 0x0126e68dc0494d6b25f264d80e15759e150a3d9e8554070410c6c7a40745bd5b, then used the helper for the exploit transaction.
The helper's execution flow was:
155744911000000 raw USDT one-sided and withdrawing large DAI and USDC amounts through remove_liquidity_imbalance.UnderlyingBurner.execute() while the pool is mispriced so the burner performs its deterministic add_liquidity(amounts, 0) step at the manipulated rate.The receipt summary records the terminal profit transfer:
{
"from": "0x915dff6707bea63daea1b41aa5d37353229066ba",
"amount": "36819398770",
"logIndex": "0xab"
}
The realized economic loss was 36819.398770 USDT, encoded on-chain as "36819398770" with decimal=6. The loss is borne by Curve's fee-burning pipeline: the burner converted protocol-owned stablecoins at an adversarially worsened 3pool price, so the fee receiver obtained fewer 3CRV than it would have under a fair execution. The attacker paid gas, but the recorded gas cost of 2466907 units at 33045821804 wei per gas is economically much smaller than the USDT profit.
0xd493c73397952049644c531309df3dd4134bf3db1e64eb6f0b68b016ee0bffde0x0126e68dc0494d6b25f264d80e15759e150a3d9e8554070410c6c7a40745bd5b0x786b374b5eef874279f4b7b4de16940e57301a580xbebc44782c7db0a1a60cb6fe97d0b483032ff1c70x6c3f90f043a72fa612cbac8115ee7e52bde6e4900xa464e6dcda8ac41e03616f95f4bc98a13b8922dc