Refund 18.277 USDC and 40,023.228375 cNGN of protocol fees from the Base IntentGateway to the two accounts they were taken from. One paid the placement fee on three orders that never filled; the other had the protocol keep its 60% surplus share on a fill.
The whitelisted call is a Utility.batchAll of two IntentsCoprocessor.sweepDust calls against StateMachine::Evm(8453). It is atomic: both apply or neither does. Nothing else rides along in this batch.
0x2009c64589dFB42bc26bf9E29bb61F28a79275aA placed three same chain orders on Base on 2026-09-05, USDC in and cNGN out, each with a deadline about 56 blocks out. All three expired without a fill and were refunded from escrow.
The gateway takes protocolFeeBps off the input at placement, before any fill happens, and Base is configured at 5 bps. Cancelling returns the escrow, which is already net of that fee, so the fee stays with the protocol even though nothing was swapped.
| Order | Gross in (USDC) | Fee taken (USDC) | Outcome |
|---|---|---|---|
0x9be5c5e8… |
22,194.000000 | 11.097000 | Never filled, 22,182.903000 refunded |
0xdd3a86dd… |
12,183.000000 | 6.091500 | Never filled, 12,176.908500 refunded |
0xee2fda0e… |
2,177.000000 | 1.088500 | Never filled, 2,175.911500 refunded |
| Total | 36,554.000000 | 18.277000 |
Every fee figure is the gateway's own DustCollected(address,uint256) log in the placement transaction, topic 0x51eb5aa877b3d80e186f9a323c6dce052491963271d09ee40615d0bb182359d5, and each is exactly 5 bps of the gross input to the last unit. Every refund is an EscrowRefunded log returning the full escrow.
0x8d11E9AA67863d555332207F7F5eC296b63aD066 placed order nonce 799 on 2026-09-07 at Base block 51,001,709, escrowing 97,451.25 USDC for a minimum of 133,344,055.869375 cNGN. It filled at block 51,001,726 in 0x0eb1a814….
The filler paid in more cNGN than the minimum, and the gateway split the difference on surplus_share_bps, which reads 6000 on Base.
| cNGN | |
|---|---|
| Gross output the filler paid in | 133,410,761.250000 |
| Order minimum | 133,344,055.869375 |
| Surplus | 66,705.380625 |
| To the user, 40% | 26,682.152250 |
| To the protocol, 60% | 40,023.228375 |
The protocol's share went to the gateway as a cNGN DustCollected in the same transaction. This order filled, so the 48.75 USDC placement fee on its input stays with the protocol and is not part of this refund.
At the rate this order implies, about 1,368 cNGN per USDC, the two legs together are roughly $48.
Utility.batchAll(calls), pallet index 4, call index 2, with two entries:
| # | Call | Arguments |
|---|---|---|
| 0 | IntentsCoprocessor.sweepDust (94/4) |
Evm(8453), beneficiary 0x2009c64589dFB42bc26bf9E29bb61F28a79275aA, outputs [{ USDC, 11097000 }, { USDC, 6091500 }, { USDC, 1088500 }] |
| 1 | IntentsCoprocessor.sweepDust (94/4) |
Evm(8453), beneficiary 0x8d11E9AA67863d555332207F7F5eC296b63aD066, outputs [{ cNGN, 40023228375 }] |
USDC is 0x833589fCd6EDb6E08f4c7C32D4f71b54bdA02913 and cNGN is 0x46C85152bFe9f96829aA94755D9f915F9B10EF5F, both 6 decimals, both left padded to H256.
Call 0 lists three outputs rather than one summed entry because _sweepDust loops over outputs and does one safeTransfer and one DustSwept per entry. Three transfers, same total, and each line maps to one of that user's three orders.
Each call builds an ISMP post request addressed to the registered gateway 0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716, with timeout: 0 and fee: 0, carrying a one-byte RequestKind::SweepDust discriminator followed by abi.encode(SweepDust).
Request from call 0, 321 bytes:
0x03
0000000000000000000000000000000000000000000000000000000000000020 offset to the struct
0000000000000000000000002009c64589dfb42bc26bf9e29bb61f28a79275aa beneficiary
0000000000000000000000000000000000000000000000000000000000000040 offset to outputs
0000000000000000000000000000000000000000000000000000000000000003 outputs.length
000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913 token, USDC
0000000000000000000000000000000000000000000000000000000000a953a8 amount, 11.097 USDC
000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913 token, USDC
00000000000000000000000000000000000000000000000000000000005cf2ec amount, 6.0915 USDC
000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913 token, USDC
0000000000000000000000000000000000000000000000000000000000109bf4 amount, 1.0885 USDC
Request from call 1, 193 bytes:
0x03
0000000000000000000000000000000000000000000000000000000000000020 offset to the struct
0000000000000000000000008d11e9aa67863d555332207f7f5ec296b63ad066 beneficiary
0000000000000000000000000000000000000000000000000000000000000040 offset to outputs
0000000000000000000000000000000000000000000000000000000000000001 outputs.length
00000000000000000000000046c85152bfe9f96829aa94755d9f915f9b10ef5f token, cNGN
000000000000000000000000000000000000000000000000000000095191ffd7 amount, 40,023.228375 cNGN
The leading offset word is there because abi.decode(body[1:], (SweepDust)) on the gateway expects abi.encode of a single dynamic struct.
ExtrinsicIntents.onAccept requires keccak256(request.source) == keccak256(host.hyperbridge()) for this request kind. host.hyperbridge() on Base returns POLKADOT-3367, which is what Nexus dispatches as, so the check passes. The gateway then runs safeTransfer(token, beneficiary, amount) per output and emits DustSwept for each.
The two requests are independent once they leave Nexus. Atomicity is on the Nexus side only. On Base they can be relayed and executed at different times.
Threshold
Threshold