We do not have a reliable USD price for the recorded assets yet.
0x35a73969f582872c25c96c48d8bb31c23eab8a49c19282c67509b96186734e600x00000000fdac7708d0d360bddc1bc7d097f47439Ethereum0x0cc396f558aae5200bb0abb23225accafca31e27EthereumOn Ethereum mainnet block 19470561, transaction 0x35a73969f582872c25c96c48d8bb31c23eab8a49c19282c67509b96186734e60 exploited ParaSwap AugustusV6 at 0x00000000fdac7708d0d360bddc1bc7d097f47439. The attacker called uniswapV3SwapCallback directly with crafted calldata that named victim 0x0cc396F558aAE5200bb0aBB23225aCcafCA31E27 as the payer and targeted the canonical OPSEC/WETH Uniswap V3 pool 0x45f4d60405b797a2b0e5eA581fe6EA445CB46b8f. AugustusV6 then drove the pool swap itself, and on the pool's callback it paid the pool by pulling 12479437103825582622058 OPSEC from the victim via the victim's pre-existing allowance to AugustusV6. The attacker-controlled helper received 6463332789527457985 WETH-equivalent output and remained net profitable after gas and builder payment.
The root cause is an authorization failure in AugustusV6's Uniswap V3 callback path. uniswapV3SwapCallback accepted an attacker-controlled payer from calldata and later used that address in transferFrom(payer, pool, amount) after checking only that msg.sender matched the canonical pool derived from calldata. The callback was therefore bound to pool identity, but not to an authenticated swap context or authenticated payer.
Uniswap V3 pools call uniswapV3SwapCallback on the contract that initiated pool.swap. If the callback reports a positive token delta, the callback implementer must transfer that token amount to the pool before the swap completes.
ParaSwap AugustusV6 exposes this callback because its router supports direct Uniswap V3 pool interactions. That design is only safe if the callback can prove both:
ERC20 approvals are spender-scoped, not caller-scoped. Once a user approves AugustusV6, any AugustusV6 code path that reaches transferFrom(user, ...) can spend that approval unless AugustusV6 enforces additional authorization.
The OPSEC token is a conventional ERC20 for this purpose. Its transferFrom decreases allowance[sender][msg.sender] after moving funds, so any successful unauthorized transfer here implies misuse by the spender contract, not a token-side allowance bug.
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
Label: OPSEC verified token source, confirming ordinary allowance semantics.
This is an ACT-style contract exploit, not a token bug and not a privileged-adversary case. The vulnerable component is ParaSwap AugustusV6's Uniswap V3 helper path, specifically uniswapV3SwapCallback(int256,int256,bytes) in the UniswapV3Utils logic used by the proxy at 0x00000000fdac7708d0d360bddc1bc7d097f47439.
The safety invariant is straightforward: AugustusV6 must only settle a Uniswap V3 callback for the payer that authorized the active swap execution path. The collected AugustusV6 source bundle shows that the callback derives the payer from callback calldata, validates only that msg.sender equals the canonical pool computed from the calldata pool descriptor, and then pays the pool from that derived payer. That means any external caller can nominate a third-party address that has granted AugustusV6 allowance, as long as the callback path routes through a real Uniswap V3 pool.
The code-level breakpoint is the callback settlement branch that executes transferFrom(fromAddress, poolAddress, amount) after loading fromAddress from calldata. There is no swap-context nonce, no pending-swap mapping, no signature check at callback time, and no requirement that the payer equal the original caller. Pool authenticity alone is insufficient because the payer identity remains attacker-controlled.
The exploit pre-state at block 19470560 already satisfied all required conditions:
0x0cc396F558aAE5200bb0aBB23225aCcafCA31E27 held 37415569780101599881831 OPSEC,24000000000000000000000 OPSEC,0x45f4d60405b797a2b0e5eA581fe6EA445CB46b8f.The attacker did not need any private key, attacker-side artifact from the original incident, or privileged contract role. The exploit transaction came from EOA 0xFDe0d1575Ed8E06FBf36256bcdfA1F359281455A, which called helper contract 0x6980a47beE930a4584B09Ee79eBe46484FbDBDD0. That helper then called AugustusV6's external callback entrypoint with calldata encoding:
0x6980...,0x0cc396...,10000.The trace shows the end-to-end mechanism directly:
0x6980...::9846cd9e(...)
0x00000000FdAC...::uniswapV3SwapCallback(-33000000000, 6463332789527457985, ...)
0x45f4d60405b7...::swap(0x6980..., true, -6463332789527457985, 4295128740, ...)
WETH9::transfer(0x6980..., 6463332789527457985)
0x00000000FdAC...::uniswapV3SwapCallback(12479437103825582622058, -6463332789527457985, ...)
OpSec::transferFrom(0x0cc396..., 0x45f4d60405b7..., 12479437103825582622058)
Label: Exploit trace showing direct callback abuse, pool swap, and victim-funded settlement.
That trace resolves the exploit sequence:
uniswapV3SwapCallback.OpSec.transferFrom(victim, pool, amount) because the victim previously approved AugustusV6.The balance diff matches the trace precisely:
{
"token": "0x6a7eff1e2c355ad6eb91bebb5ded49257f3fed98",
"holder": "0x0cc396f558aae5200bb0abb23225accafca31e27",
"before": "37415569780101599881831",
"after": "24936132676276017259773",
"delta": "-12479437103825582622058"
}
Label: Victim OPSEC balance delta from the exploit transaction.
The same transaction emitted the allowance update proving AugustusV6, not the attacker EOA, spent the victim's approval:
emit Approval(
owner: 0x0cc396F558aAE5200bb0aBB23225aCcafCA31E27,
spender: 0x00000000FdAC7708D0D360BDDc1bc7d097F47439,
value: 11520562896174417377942
)
Label: Trace evidence that the victim's AugustusV6 allowance was consumed during settlement.
The adversary flow was a single transaction with three functional stages.
First, the attacker EOA 0xFDe0... submitted transaction 0x35a73969... and paid gas. The helper contract 0x6980... served only as a convenient recipient and post-exploit profit handler; it was not required for exploitability because the root issue sits in AugustusV6's public callback.
Second, the helper invoked AugustusV6.uniswapV3SwapCallback directly with crafted recursive exact-output calldata. The calldata selected:
10000 fee tier.Third, the canonical pool executed the swap and sent 6463332789527457985 WETH to the helper, after which AugustusV6 satisfied the pool by spending the victim's OPSEC approval. The balance diff and trace together show that the helper cluster received WETH/ETH value, paid the builder 6450406123948403069 wei, absorbed 7856855050498775 wei in sender-side gas and call value, and still retained positive net value.
This satisfies the ACT definition because any unprivileged actor could repeat the sequence on a mainnet fork using only public chain state, public code, and an allowance-bearing victim address. No secret attacker calldata, no private order flow, and no privileged control path were necessary.
The directly observed victim loss was:
12479437103825582622058 OPSEC stolen from 0x0cc396F558aAE5200bb0aBB23225aCcafCA31E27The incident also demonstrates a broader protocol impact. Any user who had approved AugustusV6 for a token with a usable Uniswap V3 route was exposed to third-party allowance theft through this callback path. The exploit success predicate was monetary: the attacker-controlled helper received 6463332789527457985 WETH-equivalent output and remained net positive after execution costs.
0x35a73969f582872c25c96c48d8bb31c23eab8a49c19282c67509b96186734e600x00000000fdac7708d0d360bddc1bc7d097f474390x0cc396F558aAE5200bb0aBB23225aCcafCA31E270x6a7eff1e2c355ad6eb91bebb5ded49257f3fed980x45f4d60405b797a2b0e5eA581fe6EA445CB46b8f/workspace/session/artifacts/collector/seed/1/0x35a73969f582872c25c96c48d8bb31c23eab8a49c19282c67509b96186734e60/trace.cast.log/workspace/session/artifacts/collector/seed/1/0x35a73969f582872c25c96c48d8bb31c23eab8a49c19282c67509b96186734e60/balance_diff.json/workspace/session/artifacts/collector/seed/1/0x6a7eff1e2c355ad6eb91bebb5ded49257f3fed98/src/Contract.sol/tmp/fdac-source.fixed.json