Base USDC drain from malicious transferFrom spender approvals
Exploit Transactions
0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57Victim Addresses
0x833589fcd6edb6e08f4c7c32d4f71b54bda02913BaseLoss Breakdown
Similar Incidents
USDC drain via unchecked Uniswap V3-style callback
37%Base DUCKVADER infinite mint + Uniswap drain
35%Treasury allowance-router abuse drains USDC/USDT/WBTC cross-chain
34%BSC WBNB allowance drain from unsafe spender approvals
33%MPRO Staking Proxy unwrapWETH Flash-Loan Exploit (Base)
32%Veil01ETH forged-proof drain on Base
32%Root Cause Analysis
Base USDC drain from malicious transferFrom spender approvals
1. Incident Overview TL;DR
An unprivileged adversary on Base deploys an orchestrator contract and, in the same transaction, calls public helper proxy 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e with selector 0x87395540 so that its implementation 0xdC3914cA7b18A2BF41B43A263258B71e32296D7D uses a very large existing USDC allowance from 0xba15e9b644685cb845af18a738abd40c6bcd78ed to transfer 13342433.169249 USDC to EOA 0x6caad74121bf602e71386505a4687f310e0d833e.
Helper proxy 0x6160…57e holds a large USDC allowance from 0xba15…78ed and exposes a permissionless 0x87395540 entrypoint that any EOA can call; its implementation 0xdC39…6D7D uses this allowance to execute FiatTokenV2_2::transferFrom without binding the spend to a transaction or signature from the victim address, creating an ACT opportunity.
2. Key Background
- FiatTokenV2_2 (USDC) on Base is deployed behind FiatTokenProxy 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 and correctly enforces ERC20 allowance and balance invariants in transferFrom.
- Address 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e is an unverified proxy whose Etherscan metadata identifies implementation 0xdC3914cA7b18A2BF41B43A263258B71e32296D7D; decompilation shows only admin-only upgrade functions, consistent with a transparent proxy whose user-facing entrypoints (including 0x87395540) are handled by fallback.
- The tx history for 0x6160…57e contains many successful top-level transactions from distinct EOAs with methodId 0x87395540, confirming that this entrypoint is permissionless rather than restricted to a single router or owner.
3. Vulnerability Analysis & Root Cause Summary
A proxy helper with a very large USDC allowance from a victim exposes a public 0x87395540 entrypoint that any unprivileged EOA can call, and its implementation uses that allowance to perform FiatTokenV2_2::transferFrom from the victim to an arbitrary recipient without any on-chain link to a transaction or signature from the victim.
Key invariant:
Allowance-bearing helper/executor contracts that can spend user USDC via transferFrom should only execute spends when the corresponding user (or a clearly authorized on-chain account) initiates the helper call or provides a verifiable authorization, so that an unrelated EOA cannot unilaterally trigger transferFrom using the helper's allowance.
Breakpoint in the seed transaction:
In tx 0xc15d…dd57, orchestrator 0xcCE2…25b, created and controlled by EOA 0x6caa…833e, calls public proxy 0x6160…57e::87395540; the proxy forwards to executor 0xdC39…6D7D, which issues FiatTokenV2_2::transferFrom(0xba15…78ed, 0x6caa…833e, 13342433169249) using 0x6160…57e's large allowance from 0xba15…78ed even though 0xba15…78ed does not appear as the tx sender or an on-chain authorizer in the sequence.
Vulnerable components:
- Helper proxy 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e on Base (unverified proxy holding a large USDC allowance and exposing a public 0x87395540 entrypoint).
- Executor implementation 0xdC3914cA7b18A2BF41B43A263258B71e32296D7D on Base (unverified router that constructs the FiatTokenV2_2::transferFrom call using the helper's allowance).
- FiatTokenV2_2 USDC contract at 0x2ce6311ddae708829bc0784c967b7d77d19fd779 (correctly enforced ERC20 logic but serves as the drained asset).
4. Detailed Root Cause Analysis
Trace.cast.log for the seed tx shows the call chain EOA 0x6caa…833e → constructor of 0xcCE2…25b → helper proxy 0x6160…57e (selector 0x87395540) → delegatecall into executor 0xdC39…6D7D → call into FiatTokenProxy 0x8335…2913, which delegates to FiatTokenV2_2::transferFrom. Allowance snapshots confirm that immediately before the tx, owner 0xba15…78ed had a huge allowance to spender 0x6160…57e and zero allowance to other candidate contracts; after the tx, this allowance is reduced by exactly 13342433169249 units, matching the USDC balance diffs. Decompilation and Etherscan metadata for 0x6160…57e show it is a proxy whose non-admin selectors are forwarded to 0xdC39…6D7D, and the full txlist demonstrates that selector 0x87395540 is callable by many unrelated EOAs. FiatTokenV2_2 itself behaves correctly, but the combination of a public helper entrypoint and a very large allowance from 0xba15…78ed yields an ACT opportunity: any unprivileged adversary can craft calldata to 0x6160…57e::87395540 that causes the executor to invoke transferFrom from 0xba15…78ed to an adversary-controlled account.
Seed transaction trace excerpt for tx 0xc15df1d1…dd57 (EOA → orchestrator → helper proxy → executor → USDC):
Executing previous transactions from the block.
depth:1, PC:0, gas:0xd3c1a(867354), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[], Data size:0
depth:1, PC:2, gas:0xd3c17(867351), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[128], Data size:0
depth:1, PC:4, gas:0xd3c14(867348), OPCODE: "MSTORE"(82) refund:0x0(0) Stack:[128, 64], Data size:0
depth:1, PC:5, gas:0xd3c08(867336), OPCODE: "CALLVALUE"(52) refund:0x0(0) Stack:[], Data size:96
depth:1, PC:6, gas:0xd3c06(867334), OPCODE: "DUP1"(128) refund:0x0(0) Stack:[0], Data size:96
depth:1, PC:7, gas:0xd3c03(867331), OPCODE: "ISZERO"(21) refund:0x0(0) Stack:[0, 0], Data size:96
depth:1, PC:8, gas:0xd3c00(867328), OPCODE: "PUSH2"(97) refund:0x0(0) Stack:[0, 1], Data size:96
depth:1, PC:11, gas:0xd3bfd(867325), OPCODE: "JUMPI"(87) refund:0x0(0) Stack:[0, 1, 16], Data size:96
depth:1, PC:16, gas:0xd3bf3(867315), OPCODE: "JUMPDEST"(91) refund:0x0(0) Stack:[0], Data size:96
depth:1, PC:17, gas:0xd3bf2(867314), OPCODE: "POP"(80) refund:0x0(0) Stack:[0], Data size:96
depth:1, PC:18, gas:0xd3bf0(867312), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[], Data size:96
depth:1, PC:20, gas:0xd3bed(867309), OPCODE: "MLOAD"(81) refund:0x0(0) Stack:[64], Data size:96
depth:1, PC:21, gas:0xd3bea(867306), OPCODE: "PUSH2"(97) refund:0x0(0) Stack:[128], Data size:96
depth:1, PC:24, gas:0xd3be7(867303), OPCODE: "CODESIZE"(56) refund:0x0(0) Stack:[128, 4978], Data size:96
depth:1, PC:25, gas:0xd3be5(867301), OPCODE: "SUB"(3) refund:0x0(0) Stack:[128, 4978, 5202], Data size:96
depth:1, PC:26, gas:0xd3be2(867298), OPCODE: "DUP1"(128) refund:0x0(0) Stack:[128, 224], Data size:96
depth:1, PC:27, gas:0xd3bdf(867295), OPCODE: "PUSH2"(97) refund:0x0(0) Stack:[128, 224, 224], Data size:96
depth:1, PC:30, gas:0xd3bdc(867292), OPCODE: "DUP4"(131) refund:0x0(0) Stack:[128, 224, 224, 4978], Data size:96
depth:1, PC:31, gas:0xd3bd9(867289), OPCODE: "CODECOPY"(57) refund:0x0(0) Stack:[128, 224, 224, 4978, 128], Data size:96
depth:1, PC:32, gas:0xd3ba9(867241), OPCODE: "DUP2"(129) refund:0x0(0) Stack:[128, 224], Data size:352
depth:1, PC:33, gas:0xd3ba6(867238), OPCODE: "ADD"(1) refund:0x0(0) Stack:[128, 224, 128], Data size:352
depth:1, PC:34, gas:0xd3ba3(867235), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[128, 352], Data size:352
depth:1, PC:36, gas:0xd3ba0(867232), OPCODE: "DUP2"(129) refund:0x0(0) Stack:[128, 352, 64], Data size:352
depth:1, PC:37, gas:0xd3b9d(867229), OPCODE: "SWAP1"(144) refund:0x0(0) Stack:[128, 352, 64, 352], Data size:352
depth:1, PC:38, gas:0xd3b9a(867226), OPCODE: "MSTORE"(82) refund:0x0(0) Stack:[128, 352, 352, 64], Data size:352
depth:1, PC:39, gas:0xd3b97(867223), OPCODE: "PUSH2"(97) refund:0x0(0) Stack:[128, 352], Data size:352
depth:1, PC:42, gas:0xd3b94(867220), OPCODE: "SWAP2"(145) refund:0x0(0) Stack:[128, 352, 47], Data size:352
depth:1, PC:43, gas:0xd3b91(867217), OPCODE: "PUSH2"(97) refund:0x0(0) Stack:[47, 352, 128], Data size:352
depth:1, PC:46, gas:0xd3b8e(867214), OPCODE: "JUMP"(86) refund:0x0(0) Stack:[47, 352, 128, 2803], Data size:352
depth:1, PC:2803, gas:0xd3b86(867206), OPCODE: "JUMPDEST"(91) refund:0x0(0) Stack:[47, 352, 128], Data size:352
depth:1, PC:2804, gas:0xd3b85(867205), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[47, 352, 128], Data size:352
depth:1, PC:2806, gas:0xd3b82(867202), OPCODE: "DUP1"(128) refund:0x0(0) Stack:[47, 352, 128, 0], Data size:352
depth:1, PC:2807, gas:0xd3b7f(867199), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[47, 352, 128, 0, 0], Data size:352
depth:1, PC:2809, gas:0xd3b7c(867196), OPCODE: "PUSH1"(96) refund:0x0(0) Stack:[47, 352, 128, 0, 0, 0], Data size:352
depth:1, PC:2811, gas:0xd3b79(867193), OPCODE: "DUP5"(132) refund:0x0(0) Stack:[47, 352, 128, 0, 0, 0, 96], Data size:352
depth:1, PC:2812, gas:0xd3b76(867190), OPCODE: "DUP7"(134) refund:0x0(0) Stack:[47, 352, 128, 0, 0, 0, 96, 128], Data size:352
depth:1, PC:2813, gas:0xd3b73(867187), OPCODE: "SUB"(3) refund:0x0(0) Stack:[47, 352, 128, 0, 0, 0, 96, 128, 352], Data size:352
depth:1, PC:2814, gas:0xd3b70(867184), OPCODE: "SLT"(18) refund:0x0(0) Stack:[47, 352, 128, 0, 0, 0, 96, 224], Data size:352
depth:1, PC:2815, gas:0xd3b6d(867181), OPCODE: "ISZERO"(21) refund:0x0(0) Stack:[47, 352, 128, 0, 0, 0, 0], Data size:352
Balance diff for FiatTokenV2_2 (USDC) in tx 0xc15df1d1…dd57, showing the exact drained amount:
[
{
"token": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"holder": "0xba15e9b644685cb845af18a738abd40c6bcd78ed",
"before": "13342433169249",
"after": "0",
"delta": "-13342433169249",
"balances_slot": "9",
"slot_key": "0xfddc8278706ad8a156e4ce2af13d83720a3fe42597f234189ab34e28195ae66e",
"layout_address": "0x2ce6311ddae708829bc0784c967b7d77d19fd779",
"contract_name": "FiatTokenV2_2"
},
{
"token": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"holder": "0x6caad74121bf602e71386505a4687f310e0d833e",
"before": "0",
"after": "13342433169249",
"delta": "13342433169249",
"balances_slot": "9",
"slot_key": "0x3ffc073c570bce704de8fdf099fb0eda254a3d44563a38035531c97a565bb8a3",
"layout_address": "0x2ce6311ddae708829bc0784c967b7d77d19fd779",
"contract_name": "FiatTokenV2_2"
}
]
Allowance snapshots around block 0x2760870–0x2760871 for victim 0xba15…78ed and helper proxy 0x6160…57e:
[
{
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"owner": "0xba15e9b644685cb845af18a738abd40c6bcd78ed",
"spender": "0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e",
"blockNumber": "0x2760870",
"allowance": "115792089237316195423570985008687907853269984665640564039457584007911205440175"
},
{
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"owner": "0xba15e9b644685cb845af18a738abd40c6bcd78ed",
"spender": "0x6ff5693b99212da76ad316178a184ab56d299b43",
"blockNumber": "0x2760870",
"allowance": "0"
},
{
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"owner": "0xba15e9b644685cb845af18a738abd40c6bcd78ed",
"spender": "0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e",
"blockNumber": "0x2760871",
"allowance": "115792089237316195423570985008687907853269984665640564039457583994568772270926"
},
{
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"owner": "0xba15e9b644685cb845af18a738abd40c6bcd78ed",
"spender": "0x6ff5693b99212da76ad316178a184ab56d299b43",
"blockNumber": "0x2760871",
"allowance": "0"
}
]
Helper proxy 0x6160…57e decompiled proxy code (admin-only selectors, user calls delegated through fallback):
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @title Decompiled Contract
/// @author Jonathan Becker <jonathan@jbecker.dev>
/// @custom:version heimdall-rs v0.9.2
///
/// @notice This contract was decompiled using the heimdall-rs decompiler.
/// It was generated directly by tracing the EVM opcodes from this contract.
/// As a result, it may not compile or even be valid solidity code.
/// Despite this, it should be obvious what each function does. Overall
/// logic should have been preserved throughout decompiling.
///
/// @custom:github You can find the open-source decompiler here:
/// https://heimdall.rs
contract DecompiledContract {
address store_b;
address store_a;
event AdminChanged(address, address);
event Upgraded(address);
/// @custom:selector 0x5c60da1b
/// @custom:signature implementation() public payable returns (uint256)
function implementation() public payable returns (uint256) {
require(msg.value);
require((msg.data.length + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc) < 0);
require(0 == (msg.sender == (address(store_a))));
require(msg.sender == (address(store_a)));
var_a = msg.data[0:4];
(bool success, bytes memory ret0) = address(store_b).Unresolved_(var_b); // delegatecall
return ;
var_c = 0x08c379a000000000000000000000000000000000000000000000000000000000;
var_d = 0x20;
var_e = 0x32;
var_f = 0x43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e206672;
var_g = 0x6f6d207468652070726f78792061646d696e0000000000000000000000000000;
address var_c = address(store_b);
return address(store_b);
}
/// @custom:selector 0x4f1ef286
/// @custom:signature Unresolved_4f1ef286(address arg0, uint256 arg1) public payable returns (uint256)
/// @param arg0 ["address", "uint160", "bytes20", "int160"]
/// @param arg1 ["uint256", "bytes32", "int256"]
function Unresolved_4f1ef286(address arg0, uint256 arg1) public payable returns (uint256) {
require((msg.data.length + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc) < 0x40);
require(arg0 - (address(arg0)));
require(arg1 > 0xffffffffffffffff);
require(!(arg1 + 0x23) < msg.data.length);
require(arg1 > 0xffffffffffffffff);
require(((arg1 + (arg1)) + 0x24) > msg.data.length);
require(0 == (msg.sender == (address(store_a))));
require(msg.sender == (address(store_a)));
var_a = msg.data[0:4];
(bool success, bytes memory ret0) = address(store_b).Unresolved_(var_b); // delegatecall
return ;
var_c = 0x08c379a000000000000000000000000000000000000000000000000000000000;
var_d = 0x20;
var_e = 0x32;
var_f = 0x43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e206672;
var_g = 0x6f6d207468652070726f78792061646d696e0000000000000000000000000000;
store_b = arg0;
address var_c = address(arg0);
emit Upgraded(address(arg0));
var_h = msg.data[36:36];
var_c = 0;
(bool success, bytes memory ret0) = address(this).transfer(msg.value);
require(!ret0.length);
require(!success == 0x01);
require(ret0.length > 0xffffffffffffffff);
var_a = 0x4e487b7100000000000000000000000000000000000000000000000000000000;
var_i = 0x41;
require(((var_j + (uint248((ret0.length + 0x1f) + 0x3f))) > 0xffffffffffffffff) | ((var_j + (uint248((ret0.length + 0x1f) + 0x3f))) < var_j));
var_a = 0x4e487b7100000000000000000000000000000000000000000000000000000000;
var_i = 0x41;
}
/// @custom:selector 0x3659cfe6
Executor 0xdC39…6D7D decompiled fragment showing construction of a 0x23b872dd (transferFrom) call using the helper's allowance:
var_f = 0x5e7bb104d84c7cb9b682aac2f3d509f5f406809a;
var_g = 0x5af43d82803e903d91602b57fd5bf3ff;
var_h = 0xec8e5342b19977b4ef8892e02d8daecfa1315831;
var_c = 0x3d602d80600a3d3981f3363d3d373d3d3d363d73;
var_i = keccak256(var_j);
var_k = keccak256(var_l);
require(msg.sender - (address(keccak256(var_m))), "TRANSFER_FAILED");
require((arg2 + 0x20) < 0xff, "TRANSFER_FAILED");
require(0x01 == (arg0 > 0), "TRANSFER_FAILED");
var_c = 0xa9059cbb00000000000000000000000000000000000000000000000000000000;
address var_n = msg.sender;
uint256 var_g = arg0;
(bool success, bytes memory ret0) = address((arg2 + 0x20) >> 0x40).transfer(var_n, var_g); // call
require(!(((var_a == 0x01) & (ret0.length > 0x1f) | !ret0.length) & (success)), "TRANSFER_FAILED");
var_c = 0x08c379a000000000000000000000000000000000000000000000000000000000;
var_n = 0x20;
var_g = 0x0f;
var_o = 0x5452414e534645525f4641494c45440000000000000000000000000000000000;
require((arg2 + 0x20) < 0xfe, "TRANSFER_FROM_FAILED");
require(0x01 == (arg0 > 0), "TRANSFER_FROM_FAILED");
var_c = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
var_n = ((arg2 + 0x20) + 0x2f) >> 0x60;
var_g = msg.sender;
uint256 var_o = arg0;
(bool success, bytes memory ret0) = address((arg2 + 0x20) >> 0x40).gasprice_bit_ether(var_n); // call
require(!(((var_a == 0x01) & (ret0.length > 0x1f) | !ret0.length) & (success)), "TRANSFER_FROM_FAILED");
var_c = 0x08c379a000000000000000000000000000000000000000000000000000000000;
var_n = 0x20;
var_g = 0x14;
var_o = 0x5452414e534645525f46524f4d5f4641494c4544000000000000000000000000;
require(0 < (arg2 + 0x20));
require(!(((arg2 + 0x20) + 0x2f) + 0x20) < 0x26);
var_a = 0x4e487b7100000000000000000000000000000000000000000000000000000000;
var_b = 0x21;
require(!(((arg2 + 0x20) + 0x2f) + 0x20) == 0x01);
require(!(((arg2 + 0x20) + 0x2f) + 0x20) == 0x01);
require(!((arg2 + 0x20) + 0x2f) + 0x60);
require(((var_p + 0x20) > 0xffffffffffffffff) | ((var_p + 0x20) < var_p));
var_a = 0x4e487b7100000000000000000000000000000000000000000000000000000000;
var_b = 0x41;
uint256 var_p = var_p + 0x20;
var_c = 0;
var_q = msg.data[4:4];
require(!address(((arg2 + 0x20) + 0x2f) + 0x40).code.length);
var_d = 0x022c0d9f00000000000000000000000000000000000000000000000000000000;
var_g = 0;
var_o = (((arg2 + 0x20) + 0x2f) + 0x80);
address var_r = address(msg.sender);
var_s = 0x80;
uint256 var_t = var_p.length;
uint256 var_u = 0;
(bool success, bytes memory ret0) = address(((arg2 + 0x20) + 0x2f) + 0x40).Unresolved_022c0d9f(var_g, var_o); // call
var_a = 0x0429e62000000000000000000000000000000000000000000000000000000000;
require(!((((arg2 + 0x20) + 0x2f) + 0x20) < 0x26), CustomError_0429e620());
var_a = 0x4e487b7100000000000000000000000000000000000000000000000000000000;
var_b = 0x21;
uint256 var_b = (((arg2 + 0x20) + 0x2f) + 0x20);
require(!(uint24((arg2 + 0x20) >> 0xe0) < 0x26), CustomError_0429e620());
var_a = 0x4e487b7100000000000000000000000000000000000000000000000000000000;
var_b = 0x21;
Tx history excerpts for 0x6160…57e showing selector 0x87395540 called successfully by multiple distinct EOAs:
"hash": "0x8ea44a48bcf2056a0dc83106d71484806595a89370813f522ba8c03c12492b2f",
"from": "0x9cb8d9bae84830b7f5f11ee5048c04a80b8514ba",
"hash": "0xec6c5d4bfae096a8bf0c0a15c2450e881c65b0981b57daf12f07f6f3657c59ec",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x558861778192159c373f68e00f047ee52ce870d9c5b0ab0c1ee670deb9d01e27",
"from": "0xbc15e53ba89bbc08da94626cc624d6bd0d62ebb7",
"hash": "0xf72204881afd53b6f35e5406b58e78f1a9066d87e644a7580ec9d907fa806f59",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xd1d6101febdac17a3cbd1f28c54a5b2f1d46d4c3428553d616d66e9757d4981c",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x462147e0b9c8bb188a2acc873f8bb757c0cb4ba56b1dcbb468a5fe7abc3c2ca4",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xbb3a174b0d8274d7007b0ae5075c29374a33714786fcf7da7d9a62ce14e0c9b4",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x0f21fad5034b48e52b0cda440a116351b6241f4c8f842df187af43b36747a327",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x74af7ed8ebf60139cd861dfb12585f3817e699d0a41cd25cc750aa86f15dcf41",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x680fb65c13a1fff4af4d92173daa5b1ca80294db9c93b0294481e321638b8429",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x681b4cf91aafed080902fa80e61fe8806445c065c09f7bd75b377b0355c33d93",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x78828767982c7ce64665f828bc71b50417ac44b4cc5c052b007aaa3345c9ef76",
"from": "0x62ce22a0aaf6c557f3019c427dec22308f1ce9ed",
"hash": "0x170ae1df1c2946e9167a5ec1eb08150ed1abc336032bd7c20337eec0ed2f0fec",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xb598a636fee2238677375166209c37b6722dc16afdfc697deff3746049820689",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x487ebf80a856ca86f687396a59ec2924d6164679ce47cb3a5c454b1eb609af49",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x5ee3337b8e5d6cf276b8377b084092e9b871d8b4012a23d5db48742e39f7e3af",
"from": "0x62ce22a0aaf6c557f3019c427dec22308f1ce9ed",
"hash": "0x4ab330dc405915b8a3d646442531b43d49ac75b63beaa14b414d342a38bc4298",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x1d20a68fd1725760f04036b0f90eff656e432ab2a1b54e5b865840d1a7b013d3",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xce54602e7ea506d3173499ffa63f1739ab7482b952ac0804599dd2b202694e84",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x90e6703ee151dc611f55a46165e248520ddcd7eb502e4cafddd3a45524ca6b67",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xd331d8ed84cce28d87465337a2ed74f8a2c4b5b15376ad8e38eb9723dd312923",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x6fefd2646ad708392740c5a9301ad8044685e4541ec37dba4a314f5bcd314439",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xd05c53634d95177d208d6e1e5ef02a3a8fd08602b966d105b449d4601b4f966d",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x782cb6752cbe1a98ffe0f482419bc7a9b144a96c54b5c02334395a5c880977df",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x406bad027157c5dc6bf4677cbbfc618acefb33d3267c45b37c180a81987f1b79",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xf168343afb4b923bc9aacf9ada68ba2c4ce429066b71a1a4575464597086b5ce",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x844b121da0a36edc7d6a28db452a13860215357a47cab74b27ed83444000c4a9",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x407140bfa83a53abe36ec8af694620a07ebbb93acfbd69662ee69e91e1dfe31c",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0xebae38e615b692eec16f5a5c6015c2fcc53e68e25dca8b13364ccc7b28375772",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
"hash": "0x5f8421fae1e5d40690da53e1f61dfcdb2ebe5cd64fa17ea67de18fce5bc8f3d7",
"from": "0x8f761f127ae9daa245b4203c9cc2cc5285fe25a8",
5. Adversary Flow Analysis
Single-tx allowance drain using a public helper proxy: deploy an orchestrator, call 0x6160…57e::87395540, and rely on its implementation to spend the victim's large USDC allowance via transferFrom into the adversary EOA.
Adversary-related cluster accounts:
- 0x6caad74121bf602e71386505a4687f310e0d833e on Base (EOA=true, contract=false): Sender of the attacker-crafted tx and direct USDC profit recipient.
- 0xcCE2E1a23194bD50d99eB830af580Df0B7e3225b on Base (EOA=false, contract=true): Contract created in the seed tx and used as the immediate caller into helper proxy 0x6160…57e.
- 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e on Base (EOA=false, contract=true): Proxy helper/spender holding the large USDC allowance and forwarding 0x87395540 calls into executor 0xdC39…6D7D.
- 0xdC3914cA7b18A2BF41B43A263258B71e32296D7D on Base (EOA=false, contract=true): Executor implementation that constructs and issues the FiatTokenV2_2::transferFrom consuming the victim allowance.
Victim-related accounts:
- USDC holder 0xba15e9b644685cb845af18a738abd40c6bcd78ed at 0xba15e9b644685cb845af18a738abd40c6bcd78ed on Base (is_verified=unknown)
- FiatTokenV2_2 USDC proxy at 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 on Base (is_verified=true)
Lifecycle stages:
- Orchestrator deployment: Adversary EOA deploys orchestrator contract 0xcCE2E1a23194bD50d99eB830af580Df0B7e3225b in a contract-creation tx. (tx 0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57 in block 41289841)
- Helper/executor invocation: During constructor execution, 0xcCE2…25b calls helper proxy 0x6160…57e with selector 0x87395540, which forwards to executor 0xdC39…6D7D to build a FiatTokenV2_2::transferFrom using the proxy's allowance from 0xba15…78ed. (tx 0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57 in block 41289841)
- USDC transfer and profit realization: FiatTokenV2_2::transferFrom moves 13342433169249 USDC units (13342433.169249 USDC) from 0xba15…78ed to 0x6caa…833e, increasing the adversary's USDC holdings with no compensating outflow in the same tx. (tx 0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57 in block 41289841)
6. Impact & Losses
- Total loss: 13342433.169249 USDC (Base)
Victim address 0xba15e9b644685cb845af18a738abd40c6bcd78ed loses 13342433.169249 USDC on Base in the analyzed transaction, while adversary EOA 0x6caad74121bf602e71386505a4687f310e0d833e gains the same amount; no broader protocol-level disruption is visible in the local artifacts.
7. References
- [1] Seed tx metadata, trace, and balance diffs: artifacts/root_cause/seed/8453/0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57/
- [2] FiatTokenV2_2 implementation source: artifacts/root_cause/data_collector/iter_3/contract/8453/0x2ce6311ddae708829bc0784c967b7d77d19fd779/source
- [3] Helper proxy 0x6160…57e decompilation and tx history: artifacts/root_cause/data_collector/iter_4/contract/8453/0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e/
- [4] Executor 0xdC39…6D7D decompilation: artifacts/root_cause/data_collector/iter_2/contract/8453/0xdC3914cA7b18A2BF41B43A263258B71e32296D7D/decompile/