Skip to content
· 5 min LIVE

$5 of USDC Was Stuck on Base With Zero ETH. We Rescued It Cross-Chain for $0.21.

An agent burner wallet held 5 USDC on Base mainnet and 0 ETH — a trap: ERC-20 transfers need native gas the wallet cannot pay. Live rescue on 2026-10-24: Relay refuel (~11 s ETH delivery) then Circle CCTP burn→attestation→mint into our operator on Arc. Every txhash and every failure included.

AB
AgentBadge Team
Agency for the Agentic Web
$5 of USDC Was Stuck on Base With Zero ETH. We Rescued It Cross-Chain for $0.21.

On 2026-10-24 we rescued 5 USDC from a Base burner wallet with zero ETH: Relay Protocol refueled 0.0000691 ETH in ~11 s (cost 0.185 USDC — GasZip rejected us, its LiFi floor is ~0.5 USDC), then CCTP v2 burned on Base, Iris attested in ~24 min, and a permissionless receiveMessage minted 5 USDC to our operator on Arc. Total cost ~0.21 USDC (~4%), end-to-end ~26 min.

An agent burner wallet ended up holding 5 USDC on Base mainnet and exactly 0 ETH. That combination is a trap: moving ERC-20 tokens requires native gas, and the wallet that holds the money cannot pay for the transaction that would free it. On 2026-10-24 we ran the rescue live — a ~$0.17 gas refuel via a bridge aggregator, then Circle's CCTP to carry the 5 USDC from Base to Arc. Twenty-six minutes later the funds were back on our operator wallet, on a chain that pays gas in USDC itself. This is the field report, with every transaction hash and every failure included.

Relay refuel — 0.185 USDC on Arc became 0.0000691 ETH on Base in ~11 seconds

The trap

The burner 0x3bF7…b89f is a throwaway key we use for agent experiments. It accumulated 5 USDC on Base mainnet — and held zero ETH. Every exit path needs gas: USDC.approve() then TokenMessengerV2.depositForBurn() for CCTP, or a plain transfer(). All of them are ERC-20 calls the burner signs — and Base charges ETH for that signature, not USDC.

The fix is a two-hop strategy:

  1. Refuel: get a drip of ETH to the burner on Base, paid for with USDC we hold elsewhere (on Arc, where USDC is the gas token).
  2. Rescue: CCTP v2 depositForBurn on Base → Circle Iris attestation → receiveMessage mint on Arc, recipient = our operator wallet.

Plan A died on a provider minimum

Our first candidate was GasZip through the LI.FI aggregator — purpose-built for exactly this: send USDC on one chain, receive native gas on another. Quotes looked perfect: 0.5 USDC → 0.000211 ETH, ~2 s execution.

GasZip rejected — have 0.190 USDC, want 0.504

insufficient funds for gas * price + value:
  have 190242293492133071   (0.190 USDC)
  want 504019005393246480   (0.504 USDC)

Probing li.quest/v1/quote at 0.05 / 0.1 / 0.15 / 0.2 / 0.25 / 0.3 / 0.35 / 0.4 / 0.45 USDC — every amount below ~0.5 returns No available quotes. Our operator held 0.190242 USDC on Arc. All our Arc wallets combined held ~0.295. GasZip's floor was simply unreachable.

Lesson one: on dust-balance source chains, a bridge's minimum size matters more than its quoted rate. Check your balance against the floor before picking the provider, not after the RPC rejection.

The pivot: Relay took what GasZip wouldn't

Relay Protocol (api.relay.link) quoted the same route — USDC on Arc → ETH on Base — starting from amounts GasZip refused. We sent 0.185 USDC through its two-step flow (ERC-20 approve + deposit, since Relay uses the token interface rather than GasZip's native value):

  • approve 0xf307371832133bd3c8d8d3b4679127a400ce28a64554230ca708f0abca56c601 (Arc)
  • deposit 0x7a01bca83dfb1aae5d1aab8205ce467e0b0d015f2e79e7c1b8c537ac29600f5b (Arc)

Eleven seconds after the deposit confirmed, the burner had 0.0000691 ETH on Base — fee ~0.02 USDC. More than enough: Base gas for approve + depositForBurn costs roughly 0.00001 ETH.

Sequence diagram — operator, GasZip/LiFi rejection, Relay approve+deposit, burner burn on Base, Iris attestation ~24 min, permissionless receiveMessage mint on Arc

CCTP: burn, wait, mint

With gas in place, the standard Circle CCTP path ran:

  • USDC.approve(TokenMessengerV2, 5_000_000) — 0xb37458778f83a8c45c762f6dd09bf45e6f25aef7d2d22405be2ca6ac763d5af1 (Base)
  • depositForBurn — 0x2c60097d665bf5234de80bb1189efe20166b108819e124f129033ceae44dbc8c (Base, mined 11:00:50 UTC)

CCTP burn submitted on Base

Then the part nobody can speed up: Circle's Iris attestation. Our script polled /v2/messages/6?transactionHash=… and hit its 20-minute timeout while status was still pending_confirmations. The burn was mined, nothing was lost — the resume mode re-polled and found status: complete at ~24 minutes. Attestation waits are a scheduling fact, not a failure mode: build resume, don't rebuild the burn.

One more production wrinkle: by the time attestation landed, our operator wallet had ~0.003 USDC left on Arc — spent on the Relay steps — not enough for the mint's gas. receiveMessage is permissionless, so a different funded wallet (ARC_OPS_KEY) paid the mint. The USDC still went to the mintRecipient encoded in the message — the caller only pays gas.

Mint confirmed — OPERATOR Arc balance 0.19 → 5.002 USDC

  • mint 0xe9057fd74703296c76b09f28c3687e83d7005890ab59c6b55e8dcf9286bbb8ea (Arc, confirmed 11:24:43 UTC)

Full rescue process flowchart — stranded state, aggregator probe, Relay refuel, CCTP burn, attestation wait with resume loop, permissionless mint, final balances

The whole run in one picture: probe → dead plan A → Relay refuel → burn → attestation wait (with resume) → permissionless mint.

The ledger

ItemValue
Rescued5.00 USDC (burner Base → OPERATOR Arc)
Refuel spend0.185 USDC in, ~0.02 fee + ~0.005 gas
Delivered gas0.0000691 ETH (~$0.17) on Base
Dust left0.000068 ETH on burner Base
Burn→attestation~24 min (finality threshold, unskippable)
Attestation→mint~1 s
E2E mainnet~26 min, ~0.21 USDC total (~4%)
Net on Arc0.190 → 5.002 USDC

Friction table — spec vs. production

#What the docs impliedWhat actually happenedFix
1GasZip handles any dust amountLiFi route floor ≈0.5 USDC — quotes <0.45 don't existRelay API, accepts 0.185
2Refuel is one txRelay needs ERC-20 approve + deposit (unlike GasZip's native value)Two steps, both on Arc
3Attestation ~15–20 min~24 min — script timed out at 20resume <burnTx> mainnet re-polls, no re-burn
4Flag style --mainnetPositional arg mainnet; --mainnet parsed as testnet flag → Iris sandbox 404Pass bare mainnet
5Operator pays the mintOperator had ~0.003 USDC left, mint needs ~0.003Permissionless receiveMessage — paid by second key

Why this pattern matters

Any agent fleet will eventually hold stranded ERC-20 balances — the wallet dies before its tokens do. The recovery recipe that worked here is small enough to script: probe aggregator quotes with minimums in mind, refuel ~$0.20 of native gas, then let CCTP do the heavy lifting while you wait for attestation. And a detail that makes Arc interesting for this exact problem: gas is paid in USDC there, so the rescued funds land on a chain where the wallet can never fall into the zero-native-token trap again.

Full recap — five transactions, one pivot, 5 USDC saved

All transactions, timings and raw logs: docs/SWAP/ in our monorepo — SPEC, CASE-LOG, structured events, and the ten terminal screenshots used in this post. The script (swap-rescue.mts) is a Bun/viem CLI with quote | relay | gaszip | testnet-cctp | cctp | resume modes.