approve the
spender, then send the swap. With EIP-5792 (wallet_sendCalls) — executed
via an EIP-7702 delegation for plain EOAs — supporting wallets run both calls
as one atomic batch in a single signature. Fewer clicks, no stuck-approval
state, and the approval can’t be left dangling or front-run.
Hypermid’s hosted widget already does this
automatically. This guide is for partners building their own execution flow
on top of POST /v1/execute who want the same one-tap
experience.
This is a progressive enhancement. Atomic batching is per
(wallet, chain)
and not yet universal — you must detect support at runtime and fall back to
the sequential approve-then-swap path when it’s unavailable.When it applies
| Input | Batchable? | Why |
|---|---|---|
| ERC20 token (USDC, USDT, WETH…) | ✅ Yes | Needs an approve; that’s the call we batch with the swap |
Native token (zero address 0x0000…0000) | ⛔ No batch needed | Native inputs require no approval — send the swap directly |
| Non-EVM (Solana, Bitcoin, XRP, TON…) | ❌ N/A | No EVM approve semantics; use the deposit-address flow |
fromToken is an EVM ERC20.
The flow
What to approve
Always approve the quote’sestimate.approvalAddress (for SuperSwap
cross-chain this is the source-chain DiamondShell), not
transactionRequest.to. The two can differ, and approving the wrong address
leaves the real spender without allowance — the swap then reverts.
Step 1 — Detect support
getCapabilities reports whether the connected account supports atomic batching
on the target chain. Treat it as best-effort — return false on any error so
you fall back safely.
Step 2 — Send the batch
When supported, encodeapprove(spender, amount) and send it together with the
swap transactionRequest in one sendCalls. The last call’s receipt is your
swap tx hash for status polling.
Step 3 — Wire it up with a fallback
Caveats
- Probe per chain, not once. Support is
(account, chain)-scoped. A wallet may batch on Base but not on another chain — re-check on every execute. - Don’t hard-fail on capability errors. Older wallets and connectors throw or
return nothing from
getCapabilities; that’s a fall-back signal, not an error. - Tolerant status checks.
waitForCallsStatusstatus values have varied across viem and wallet versions. Only treat an explicitfailure/revertedas a failure — anything else with a receipt is done. - Native inputs skip all of this — there’s nothing to approve.
See also
- Execute Swap — the response you’re batching
- Cross-Chain Swap — the full quote → execute → track lifecycle
- Widget Integration — the hosted widget that does this for you