sendBatch sends calls with as few wallet prompts as the wallet allows, without ever forcing an account upgrade (EIP-7702).
wallet_getCapabilities → atomic.status |
What happens |
|---|---|
ready (MetaMask EOA, would prompt the smart-account upgrade) |
own sequential loop: one eth_sendTransaction per call |
supported, unsupported, missing, or getCapabilities throws |
wallet_sendCalls with forceAtomic: false; if the wallet rejects it as unsupported (4200/57xx) → own sequential loop |
Why not gate on atomic.status === 'supported': Ambire EOAs report unsupported but still batch non-atomically behind one sign screen, on any chain.
Per wallet:
- Ambire EOA: one prompt; Ambire broadcasts the txs in order itself.
- Ambire smart account / Safe / already-upgraded MetaMask: one atomic batch.
- MetaMask EOA on a 7702 chain (
ready): our loop; MetaMask never seessendCalls, so it never shows the upgrade prompt. - MetaMask on a non-7702 chain, or older wallets:
sendCallserrors → our loop.
A user rejection (4001) is thrown, not retried. Partial failures (from a non-atomic batch or from our loop) throw BatchError with the hashes that went through and the index to resume from.
try {
const { mode, hashes } = await sendBatch(walletClient, publicClient, base, [
{ to: token, data: approveData },
{ to: router, data: swapData },
])
} catch (e) {
if (e instanceof BatchError) {
// e.hashes went through; offer "retry from step e.failedIndex + 1"
} else {
throw e // user rejected, wrong chain, …
}
}- Needs viem 2.x with EIP-5792 v2 (
getCapabilities({ chainId }),forceAtomic,waitForCallsStatus). Check thatwaitForCallsStatusacceptstimeoutin your version. WALLET_CANNOT_BATCHis based on the specs. Check which codes MetaMask (on a non-7702 chain) and older wallets actually return.- If you call
wallet_getCapabilitieswithout viem, also check the"0x0"(all-chains) key. - Ambire ignores
atomicRequiredand may upgrade an EOA to 7702 while the user signs, on chains that support it. That's Ambire's own behaviour; the dapp can't turn it off.