On 2026-05-07, transaction 0xc5c61b3ac39d854773b9dc34bd0cdbc8b5bbf75f18551802a0b5881fcb990513 exploited the unverified TrustedVolumes RFQ proxy at 0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756.
The attacker registered their EOA as an allowed signer for a newly created maker contract, then filled four signed RFQ orders that pulled assets from the TrustedVolumes inventory address 0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31. The inventory had unlimited allowances to the RFQ proxy, so the proxy could transfer its WETH, USDT, WBTC, and USDC to the attacker-controlled receiver.
Total direct outflow in the exploit transaction, valued with Chainlink USD feed answers at parent block 25039669:
| Asset | Raw amount | Human amount | USD price | USD value |
|---|---|---|---|---|
| WETH | 1291161105215879179270 |
1291.161105215879179270 |
$2,336.49970000 |
$3,016,797.53 |
| USDT | 206282446876 |
206282.446876 |
$0.99988156 |
$206,258.01 |
| WBTC | 1693910519 |
16.93910519 |
$81,047.28979041 |
$1,372,868.57 |
| USDC | 1268771488879 |
1268771.488879 |
$0.99980000 |
$1,268,517.73 |
| Total | $5,864,441.85 |
The USD tally uses Chainlink latestRoundData() answers at block 25039669: ETH/USD 233649970000, USDT/USD 99988156, BTC/USD 8104728979041, and USDC/USD 99980000, all 8-decimal feed answers.
| Role | Address |
|---|---|
| Exploit transaction | 0xc5c61b3ac39d854773b9dc34bd0cdbc8b5bbf75f18551802a0b5881fcb990513 |
| Attacker EOA / recovered signer | 0xC3EBDdEa4f69df717a8f5c89e7cF20C1c0389100 |
| One-shot exploit contract | 0xD4D5DB5EC65272B26F756712247281515F211E95 |
| TrustedVolumes RFQ proxy | 0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756 |
| RFQ implementation | 0x88eb28009351Fb414A5746F5d8CA91cdc02760d8 |
| TrustedVolumes inventory | 0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31 |
The root cause is an authorization boundary failure in the custom RFQ proxy.
The implementation exposes registerAllowedOrderSigner(address,bool) (0xea7faa61) as a public self-service registration method. Decompilation of the implementation shows the function writes allowedOrderSigner[msg.sender][signer] = allowed. That is not itself unsafe if later fills only spend the maker's own assets.
The exploited fill path did not enforce that the approved maker/signer pair controlled the token source. The signed order was accepted for the attacker-controlled maker/receiver, but the order payload also carried an arbitrary inventory address. During fill, the resolver executed:
- recover signer
0xC3...9100from the RFQ signature; - verify that signer is allowed for the attacker-controlled receiver
0xD4...1E95; - price the requested one-USDC buy amount with Chainlink feeds;
- call
sellToken.transferFrom(inventory, receiver, sellAmount); - call
USDC.transferFrom(receiver, inventory, 1); - mark the order nonce as filled.
Because inventory was 0x9bA0...Da31 and it had unlimited allowances to the RFQ proxy for WETH, USDT, WBTC, and USDC, the proxy transferred inventory assets to the attacker contract. The signer authorization only proved the attacker could sign for the attacker-controlled receiver. It did not prove that the attacker could spend the inventory address.
- The attacker funded and approved the predicted exploit contract address for a few USDC.
- The exploit transaction deployed
0xD4D5...1E95. - Its constructor called
registerAllowedOrderSigner(0xC3...9100, true)on the RFQ proxy. - It submitted four signed RFQ orders, each buying
1raw USDC from the exploit contract and selling a large oracle-priced amount from the TrustedVolumes inventory. - The proxy recovered the attacker EOA signature each time and accepted it because the exploit contract had just registered that EOA as its allowed signer.
- The proxy pulled from
0x9bA0...Da31using pre-existing unlimited allowances. - The exploit contract unwrapped WETH and forwarded the received WETH/USDT/WBTC/USDC to the attacker EOA.
Local cast run against the full block shows the successful calls:
registerAllowedOrderSigner(0xC3EBDdEa4f69df717a8f5c89e7cF20C1c0389100, true)- four calls to unlabelled selector
0x4112e1c2 - each
0x4112e1c2call delegatecalls implementation0x88eb28009351Fb414A5746F5d8CA91cdc02760d8 - each call performs
ecrecoverand recovers0xC3EBDdEa4f69df717a8f5c89e7cF20C1c0389100 - each call transfers one raw USDC from the exploit contract to the inventory
- the four calls transfer the inventory assets listed above to the exploit contract
The inventory balances and allowances at parent block 25039669 confirmed the precondition:
| Token | Inventory balance | Allowance to RFQ proxy |
|---|---|---|
| WETH | 1304203136581696140677 |
type(uint256).max |
| USDT | 208366107956 |
type(uint256).max |
| WBTC | 1711020727 |
type(uint256).max |
| USDC | 1281587362505 |
type(uint256).max |
0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31 is also unverified. A focused disassembly pass shows it is a TrustedVolumes custody/resolver contract, not a passive wallet. The relevant recovered layout is:
| Slot | Meaning |
|---|---|
0 |
owner: 0xc493F943a1fd910D01dcaBea86e61415ce43E787 |
1 |
WETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
2 |
OTC/operator role mapping |
3 |
cashier role mapping |
4 |
withdrawal whitelist mapping |
5 |
OTC/operator role list |
6 |
cashier role list |
7 |
withdrawal whitelist list |
8 |
1inch LimitOrderProtocol: 0x111111125421cA6dc452d289314280a0f8842A65 |
9 |
Reactor: 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4 |
10 |
swapERC20 helper: 0xD82E10B9a4107939e55FcCa9B53a9eDE6cF2fc46, plus a native unwrap guard bit |
Recovered behaviors:
- owner-only setters replace the OTC, cashier, and withdrawal whitelist arrays and rewrite their backing mappings;
- owner-only paths can set the 1inch LimitOrderProtocol, Reactor, WETH, and swapERC20 helper addresses;
- owner-only token paths can set max ERC20 allowances and sweep inventory token balances;
- cashier-only paths can unwrap WETH and send native ETH, gated by the withdrawal whitelist;
- OTC-only paths can forward calls through the configured LimitOrderProtocol and Reactor;
isValidSignature(bytes32,bytes)implements EIP-1271 and accepts signatures from the owner or OTC role accounts;takerInteraction(...)is callable only by the configured LimitOrderProtocol, requires the taker argument to be this resolver, requirestx.originto have the OTC role, then decodes and executes an array of target/call pairs.
This did not change the root cause. The loss still comes from the RFQ proxy accepting attacker-signed orders whose inventory field pointed at this resolver while this resolver had pre-existing unlimited allowances to the RFQ proxy. The resolver reconstruction explains why the address held and routed inventory, and it highlights additional operational exposure: any contract approved by this resolver must enforce its own authorization boundary correctly, because the resolver routinely holds large token balances and grants external allowances.
src/ReconstructedTrustedVolumesResolver.sol contains the high-level reconstruction. It is not byte-for-byte source; uncertain function names are labeled by behavior and selector comments.
Blockaid's public alert says the 2026 TrustedVolumes exploit affected the same operator involved in the March 2025 1inch Fusion V1 incident. On-chain checks support the narrower reading that the same TrustedVolumes-controlled infrastructure was affected in both incidents. They do not establish that the same attacker controlled both exploit flows.
The March 2025 incident is still relevant because it targeted the same operational class: TrustedVolumes acting as a 1inch resolver/market-maker with large balances and protocol approvals. 1inch's post-incident notice describes the old issue as affecting resolver contracts that still used obsolete Fusion V1. Later technical writeups describe a calldata/suffix corruption bug in the Fusion V1 settlement flow that let attacker-controlled order context inherit settlement-level authority and spend resolver-held assets.
On-chain spot checks line up with that narrative:
| Item | March 2025 Fusion V1 | May 2026 RFQ proxy |
|---|---|---|
| Primary attacker tx checked | 0x62734ce80311e64630a009dd101a967ea0a9c012fabbfce8eac90f0f4ca090d6 |
0xc5c61b3ac39d854773b9dc34bd0cdbc8b5bbf75f18551802a0b5881fcb990513 |
| Attacker sender / signer | 0xA7264a43A57Ca17012148c46AdBc15a5F951766e |
0xC3EBDdEa4f69df717a8f5c89e7cF20C1c0389100 |
| Settlement/proxy entry | 0x019BfC71D43c3492926D4A9a6C781F36706970C9 / 1inch: Settlement |
0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756 / custom RFQ proxy |
| TrustedVolumes asset source observed | 0xB02F39e382C90160Eb816DE5e0E428Ac771d77B5 |
0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31 |
| Asset source owner | 0xc493F943a1fd910D01dcaBea86e61415ce43E787 |
0xc493F943a1fd910D01dcaBea86e61415ce43E787 |
The attacker senders, entry contracts, and post-exploit flows differ, so the repo does not claim common attacker control. The commonality we can support locally is the victim/operator surface: both incidents spent assets from TrustedVolumes-owned resolver-style inventory that had granted powerful approvals to 1inch-adjacent settlement/proxy contracts.
References:
- 1inch post-incident notice: https://blog.1inch.com/vulnerability-discovered-in-resolver-contract/
- BlockSec technical retrospective: https://blocksec.com/blog/1inch-incident-from-calldata-corruption-to-forged-settlement-binary-exploitation-goes-on-chain
- Decurity 2025 recap: https://2025-recap.decurity.io/
- Blockaid alert as reported by The Block: https://www.theblock.co/amp/post/400332/1inch-trustedvolumes-exploit
The Foundry test in test/TrustedVolumesExploit.t.sol forks Ethereum at block 25039669, seeds a demo receiver with the four raw USDC needed for the one-USDC RFQ payments, registers a demo signer from that receiver, derives equivalent EIP-712 order digests, and signs them locally with vm.sign. It does not depend on the live attacker receiver or pre-signed exploit blobs.
Run:
forge test --fork-url "$ETH_RPC_URL" --fork-block-number 25039669 -vvExpected result:
testPublicRegistrationEnablesFirstDrainfails before registration and succeeds after public signer registration.testFullExploitReproductiondrains the exact WETH, USDT, WBTC, and USDC amounts observed in the exploit transaction into the demo receiver, unwraps WETH, and forwards the ERC20 proceeds to the attacker EOA.
src/ReconstructedTrustedVolumesRfq.sol contains a high-level reconstruction of the relevant control flow recovered from the unverified implementation. It is intentionally not byte-for-byte equivalent. The executable proof uses the deployed bytecode on a fork.
Immediate containment:
- Revoke all token approvals from inventory and user wallets to
0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756. - Disable routing through the TrustedVolumes custom RFQ proxy.
- If any upgrade/admin path remains, pause the fill selector
0x4112e1c2and public signer registration.
Code-level fixes:
- Bind the spender/source address in every fill to the authenticated maker, or require an explicit authorization from the inventory address.
- Do not let a self-registered maker signer authorize pulls from any third-party token source.
- Include the actual token source and spender semantics in the signed domain/order and verify them before transfer.
- Separate maker signer registration from inventory operator approval, with owner-only or EIP-712 inventory consent.
- Add invariant tests that an order signed by
maker Acan never transfer frominventory BunlessBexplicitly authorized that exact relationship.