Share on XShare on LinkedInShare on Telegram
Hack Analysis

$5.87M drained from TrustedVolumes in one tx via a permissionless RFQ signer bug. See how the attacker bypassed auth and drained a 1inch market maker's full inventory.

Author
QuillAudits Team
May 11, 2026
Share on XShare on LinkedInShare on Telegram

$5.87M drained from TrustedVolumes in a single transaction via three chained authorization bugs in a custom 1inch RFQ proxy. Here is how the attacker turned a permissionless signer registration function into a mechanism to drain a market maker's entire inventory with no victim signature required.

What Is an RFQ System, and How Does TrustedVolumes Work?

TrustedVolumes operates as a 1inch market maker and resolver, providing on-chain liquidity through a custom Request-for-Quote (RFQ) proxy deployed at  0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756. In an RFQ model, a maker pre-signs orders, quoting a specific price for a specific token pair. A taker presents that signed quote to the settlement contract, which verifies the signature and executes the swap atomically.

The system relies on three guarantees working in concert, the maker must have authorized who can sign orders on its behalf, each signed order must be filled only once (replay protection), and the token source for the fill must be the authenticated maker's own inventory, not an arbitrary third-party address. In the TrustedVolumes implementation, all three guarantees failed simultaneously, and the attacker exploited them in a single composed transaction.

Hack Analysis

The Three Bugs

The exploit was not a single vulnerability. It was a chain of three independent logic failures in the RFQ implementation at 0x88eb28009351Fb414A5746F5d8CA91cdc02760d8, each of which was exploitable in isolation but together enabled total inventory drainage.

Bug 1: Permissionless signer registration

The function registerAllowedOrderSigner(address signer, bool allowed) was exposed as a public, access-controlled function, but the access control was to msg.sender, not to any privileged role.

Screenshot 2026-05-11 at 6.30.43 PM.png

Screenshot 2026-05-11 at 6.30.05 PM.png

This means any externally owned account or contract could call this function and designate an arbitrary EOA as a valid signer for itself. The attacker deployed an exploit contract at 0xD4D5DB5EC65272B26F756712247281515F211E95 and immediately called registerAllowedOrderSigner(0xC3...9100, true) making their own EOA a recognized signer for the exploit contract as maker.

Bug 2: Salt read/write key mismatch replay protection that never fired

The protocol's order status tracking used different storage keys for reading (checking) and writing (marking) fill status:

Screenshot 2026-05-11 at 6.32.06 PM.png

The write never populated the slot that the read checked. The result: an order could be submitted and filled an unlimited number of times. The replay protection compiled, deployed, and did absolutely nothing.

Bug 3: taker field reused as the transferFrom source address

The most critical bug. When the RFQ is filled, the inventory field in the order payload, an address completely controlled by the attacker in calldata was passed directly as the from argument to transferFrom. The authentication check only proved that the attacker's EOA was a valid signer for the attacker-controlled receiver contract. It proved nothing about the inventory address.

Screenshot 2026-05-11 at 6.33.16 PM.png

The attacker set inventory = 0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31  The TrustedVolumes market maker's own custody address, which held unlimited token approvals to the RFQ proxy for WETH, USDT, WBTC, and USDC. The proxy dutifully pulled from the TrustedVolumes inventory because the approval existed, not because TrustedVolumes authorized this specific trade.

Pre-Attack Setup

Before the exploit transaction, the attacker made two preparatory moves. They funded the exploit contract with 4 wei of USDC 1 per order replay, as the RFQ structure required the taker (the exploit contract) to send 1 USDC to the inventory per fill as the maker's quoted payment. They also pre-approved the RFQ proxy from the exploit contract for this nominal USDC amount. Both steps were necessary to pass the protocol's superficial validity checks.

The TrustedVolumes inventory address at the block 25039669, immediately before the attack, held:

TokenInventory BalanceAllowance to RFQ Proxy
WETH1,304.20 WETHUnlimited
USDT208,366 USDTUnlimited
WBTC17.11 WBTCUnlimited
USDC1,281,587 USDCUnlimited

You can verify it using this command.

1INVENTORY="0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31"
2RFQ_PROXY="0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756"
3BLOCK=25039669
4RPC="<https://eth.drpc.org>"
5
6echo "============================================"
7echo " TrustedVolumes Inventory — Block $BLOCK"
8echo "============================================"
9
10for TOKEN in \\
11 "WETH:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2:18" \\
12 "WBTC:0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599:8" \\
13 "USDC:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48:6" \\
14 "USDT:0xdAC17F958D2ee523a2206206994597C13D831ec7:6"; do
15NAME=$(echo $TOKEN | cut -d: -f1)
16  ADDR=$(echo $TOKEN | cut -d: -f2)
17  DECIMALS=$(echo $TOKEN | cut -d: -f3)
18
19RAW_BAL=$(cast call $ADDR "balanceOf(address)(uint256)" \\
20 $INVENTORY --block $BLOCK --rpc-url $RPC | awk '{print $1}')
21
22RAW_APPR=$(cast call $ADDR "allowance(address,address)(uint256)" \\
23 $INVENTORY $RFQ_PROXY --block $BLOCK --rpc-url $RPC | awk '{print $1}')
24
25BALANCE=$(python3 -c "print(f'{int($RAW_BAL) / 10**$DECIMALS:,.4f}')")
26  APPROVAL=$(python3 -c "print(f'{int($RAW_APPR) / 10**$DECIMALS:,.4f}')")
27
28echo ""
29echo "[$NAME]"
30echo " Balance : $BALANCE"
31echo " Approval : $APPROVAL"
32done
33

Screenshot 2026-05-11 at 6.58.00 PM.png

The Exploit Transaction

Attack transaction executed the following sequence in a single atomic call:

Step 1: Deploy the exploit contract. The exploit contract 0xD4D5...1E95 was deployed. Its constructor is immediately called registerAllowedOrderSigner(0xC3...9100, true), registering the attacker EOA as a valid signer for the exploit contract as maker.

Screenshot 2026-05-11 at 7.05.19 PM.png

Step 2 Fill Order 1: 1,291 WETH (~$3.02M). The attacker submitted a signed RFQ order with inventory = TrustedVolumes. Bug 1 made the signature valid. Bug 2 meant replay protection never fired. Bug 3 meant transferFrom(TrustedVolumes, exploit_contract, 1291_WETH) executed. The exploit contract sent 1 wei USDC back to TrustedVolumes as the maker's payment.

Screenshot 2026-05-11 at 7.06.28 PM.png

Screenshot 2026-05-11 at 7.06.09 PM.pngScreenshot 2026-05-11 at 7.05.57 PM.png

Step 3 Fill Order 2: 206,282 USDT (~$206K). Same mechanism, second drain. Replay protection still didn't fire the salt write went to the wrong storage slot.

Screenshot 2026-05-11 at 7.07.47 PM.png

Step 4 Fill Order 3: 16.94 WBTC (~$1.70M). Third drain. Fourth call to the fill function with an identical attack structure.

Screenshot 2026-05-11 at 7.08.09 PM.png

Step 5 Fill Order 4: 1,268,771 USDC (~$1.27M). Final drain. Total time elapsed: a single Ethereum block.

Screenshot 2026-05-11 at 7.08.35 PM.png

Step 6 Forward proceeds. The exploit contract unwrapped received WETH to ETH and forwarded all extracted assets to the attacker EOA at 0xC3EBDdEa4f69df717a8f5c89e7cF20C1c0389100.

Screenshot 2026-05-11 at 7.07.15 PM.png

Root Cause

The root cause is a single authorization boundary failure, signer authorization for a receiver does not constitute authorization to spend from an arbitrary inventory address. The fill function verified allowedOrderSigner[order.receiver][signer] confirming the attacker could act as a signer for the attacker's own contract then used that proof to justify  transferFrom(order.inventory, ...), where inventory was an entirely different address the attacker specified in calldata.

The slippage logic, the Chainlink oracle pricing, and the signature recovery all worked correctly. What failed was the assumption that a valid signature from an authorized signer for receiver X conferred spending authority over inventory address Y. It did not, and the code never checked whether it should.

The secondary failure replay protection writing to a different storage key than it read compounded the damage by allowing the same attack to run four times in the same transaction without any of the fills being recorded as complete.

How QuillAudits Smart Contract Audit Could Have Prevented This

QuillAudits smart contract audit process covers authorization boundaries, replay protection, and adversarial calldata testing across all externally callable paths the exact areas involved in this exploit.

The permissionless registerAllowedOrderSigner function would have been flagged as a critical access control issue, since any contract could self-register and assign arbitrary signers. The replay protection flaw caused by mismatched storage keys would have been identified through nonce and storage path verification.

Most importantly, the audit would have caught that order.inventory was fully caller-controlled and used as a transferFrom source without validating ownership or authorization. Adversarial RFQ simulations would have demonstrated that any approved inventory address could be drained by an attacker-controlled receiver.

The unlimited token approvals further increased the blast radius, and approval scoping would have been recommended as a mitigation.

With a QuillAudits audit, all three bugs could have been identified and patched before deployment.

Get Your Smart Contract Audited Now.

Funds Flow After Attack

The stolen funds are currently sitting in attacker-controlled EOAs.

Screenshot 2026-05-11 at 2.26.23 PM.png

Screenshot 2026-05-11 at 6.18.18 PM.png

Screenshot 2026-05-11 at 6.17.57 PM.png

Post Attack Mitigation

The team acknowledged the incident, but at the time of writing, no further updates have been shared.

Following the incident, the 1inch team stated in a tweet that Trusted Volumes operates independently and that 1inch itself was not impacted by the attack.

Relevant Address and Transactions

Attacker EOAs:

Attack Transaction

0xc5c61b3ac39d854773b9dc34bd0cdbc8b5bbf75f18551802a0b5881fcb990513

TrustedVolumes RFQ proxy (vulnerable):

0xeEeEEe53033F7227d488ae83a27Bc9A9D5051756

RFQ implementation:

0x88eb28009351Fb414A5746F5d8CA91cdc02760d8

TrustedVolumes inventory (victim):

0x9bA0CF1588E1DFA905eC948F7FE5104dD40EDa31

Inventory owner:

0xc493F943a1fd910D01dcaBea86e61415ce43E787

Conclusion

The TrustedVolumes exploit highlights a core RFQ security failure, verifying a signer is not the same as verifying spending authority. The protocol confirmed the attacker could sign for their own contract but never checked whether they controlled the inventory address used in the order. That missing authorization link enabled the $5.87M drain. Broken replay protection amplified the damage, but the root issue was incomplete authorization. In RFQ systems, signer validation and token ownership must always be bound to the same address.

Contents

Tell Us About Your Project
Subscribe to Newsletter
hashing bits image
Loading...
cta-bg

WE SECURE EVERYTHING YOU BUILD.

From day-zero risk mapping to exchange-ready audits — QuillAudits helps projects grow with confidence. Smart contracts, dApps, infrastructure, compliance — secured end-to-end.

QuillAudits Logo


ISO 27001
DeFi Security AllianceplumeUniswap FoundationAethiropt-collectivePolygon SPNBNB Chain Kickstart

All Rights Reserved. © 2026. QuillAudits - LLC