Transaction Batching & EIP-7702
Epoch's SDK combines EIP-5792 wallet batching (wallet_sendCalls) and EIP-7702 smart-account delegation so users can approve, deposit, and execute multi-step intents with fewer wallet prompts — and, on testnet, without paying gas for Compact deposits when using a local signer.
Scope: testnet today for gasless relay. Batching applies wherever the user's wallet supports it (mainnet or testnet).
Related guides: Gasless Deposits (integrator checklist), SDK Reference (method signatures).
At a glance
Gasless relay
Local private-key (account.type === "local")
Relayer pays (testnet)
User signs EIP-7702 auth + delegation; epoch-sio broadcasts type-4 tx
Wallet batch (atomic)
Injected wallet with active smart account
User pays
Single wallet_sendCalls with atomicRequired: true
Wallet batch (non-atomic)
Injected wallet with batch support, plain EOA
User pays
Single wallet_sendCalls without atomic guarantee
Sequential
Plain EOA or local wallet
User pays
Separate eth_sendTransaction per call
User-facing rule: when gasless mode is enabled for a flow, treat the entire experience as gasless — do not tell users only part of the intent is sponsored.
Transaction batching (EIP-5792)
When an ERC-20 Compact deposit needs approve + deposit, or when solveIntent returns multiple execution transactions on the same chain, the SDK tries to batch them into one wallet prompt instead of two or more separate signatures.
Where batching runs
Compact deposit (
depositToCompact) — if allowance is insufficient, the SDK builds[approve, depositAndRegister](or approve-reset + approve + deposit for USDT-style tokens) and batches when the wallet strategy allows it.Multi-transaction intents (
solveIntent,resourceLockRequired: false) — contiguous transactions on the same chain are grouped and sent viaexecuteWalletBatchwhen batching is supported.
Local private-key wallets never use wallet_sendCalls; they always send sequential transactions (or use the gasless relay path when configured).
Batch strategy resolution
The SDK probes the wallet with wallet_getCapabilities and reads the user's on-chain code for EIP-7702 delegation:
Condition
mode
Behavior
Local private-key account
sequential-tx
Never calls wallet_sendCalls
Injected wallet, plain EOA, atomic status ready / unsupported
sequential-tx
SDK does not prompt smart-account upgrade
Injected wallet, EIP-7702 delegated (Epoch or other delegate)
atomic
wallet_sendCalls with forceAtomic: true
Injected wallet, atomic status supported or enabled
atomic
Smart wallet active — atomic batch
canBatchCalls maps the strategy to { supported, atomic, mode } for UI hints.
Execution flow
Supported batch chain objects in the SDK: Base Sepolia (84532), Sepolia (11155111), Optimism Sepolia (11155420), Polygon Amoy (80002).
Execution status callback
When batching multi-leg intent execution, onExecutionStatus reports phase: "batching" before sending. Per-leg receipts are mapped from the bundle via mapBundleReceiptsToLegs.
Low-level batch API
For custom integrations:
EIP-7702 gasless relay (testnet)
EIP-7702 lets an EOA temporarily delegate its code to a Stateless7702 implementation (MetaMask Delegation Toolkit). After delegation, the account behaves like a smart wallet: the user signs delegations off-chain, and a relayer can redeem them on-chain without the user paying gas.
Epoch uses this for Compact approve + deposit on testnet when the integrator opts in with a local signer.
Architecture
EpochIntentSDK (smallocator/sdk)
Builds deposit calls, signs 7702 authorization, delegation, and Compact sponsor signature
Smallocator
Validates relay requests (/relay-enable-delegation, /relay-deposit), forwards to SIO
epoch-sio
Queues and broadcasts type-4 transactions via executeRelay7702Transaction using RELAYER_PRIVATE_KEY
SIO endpoints (internal token required):
POST
/api/v1/queueRelay7702
Enqueue relay transaction
GET
/api/v1/relay7702Status
Poll queue status
Smallocator endpoints (public to SDK):
GET
/gasless-status
{ enabled, supportedChainIds, relayerAddress }
POST
/relay-enable-delegation
One-time 7702 enable
POST
/relay-deposit
Sponsored approve + deposit
Supported testnet chains
Base Sepolia
84532
Ethereum Sepolia
11155111
Optimism Sepolia
11155420
Polygon Amoy
80002
Approved 7702 delegates (either is accepted for existing EOAs):
Canonical: MetaMask Stateless7702
0x63c0c19a282a1B52b07dD5a65b58948A07DAE32BLegacy Epoch deploy:
0x7702CCC6b7bba39a3095537330692B363e4D6e8f
New enables target the MetaMask implementation.
Local wallet vs browser wallet
Gasless relay
Yes — allowGaslessSmartAccount: true + prior convertToSmartAccount
No — user pays gas
Batching
Sequential txs only
wallet_sendCalls when already a smart wallet
Setup
convertToSmartAccount({ chainId }) once per EOA + chain
SDK does not prompt upgrade; batch only if already delegated
Probe before showing gasless UI:
If delegation === "other", the EOA is delegated to an unsupported implementation — use a fresh EOA or revoke before gasless relay.
Gasless deposit request shape
The SDK POSTs to /relay-deposit with (simplified):
Single-call deposits use exactExecution; multi-call (approve + deposit) uses exactExecutionBatch — the SDK keeps delegation caveats and redeem mode in sync.
Operator configuration
Smallocator (.env):
GASLESS_ENABLED=true
Enables relay routes
SIO_API_TOKEN
Auth to epoch-sio internal API
epoch-sio (.env):
RELAYER_PRIVATE_KEY
Required — distinct EOA from executor; broadcasts 7702 txs
RELAYER_MIN_BALANCE_WEI
Optional floor (default 1000000000000000)
Verify live status:
How batching and 7702 interact
These features solve different problems but share the "smart wallet active" signal:
7702 delegation on-chain →
resolveWalletBatchStrategyreturnsatomicfor injected wallets → approve + deposit batch in one wallet prompt (user-paid gas).7702 + local signer + gasless relay → user signs delegation off-chain; relayer submits approve + deposit atomically on-chain (user does not pay gas).
Plain EOA → sequential transactions; two prompts for approve + deposit.
The SDK never auto-converts browser wallets to smart accounts. Gasless relay with strict failure (no silent fallback) requires allowGaslessSmartAccount: true on local wallets only.
End-to-end test script
Use the SDK smoke test to validate smallocator + epoch-sio + local signer wiring.
Script: smallocator/sdk/test/local-wallet-gasless.ts
Prerequisites
Smallocator:
GASLESS_ENABLED=true,SIO_API_TOKENsetepoch-sio: funded
RELAYER_PRIVATE_KEYTest EOA with testnet ETH and ERC-20 balance (for intent leg after deposit)
Environment
Create smallocator/sdk/.env.local (do not commit keys):
Run
Success output:
SKIP_SETUP=1
Skip convertToSmartAccount; run intent only
SKIP_INTENT=1
Setup + verify only
What the script exercises
GET /gasless-statusandgetWalletGaslessStatusconvertToSmartAccount→ SIO-relayed 7702 enableverifySmartAccountWorksgetIntentQuote→solveIntent({ gasless: true, allowGaslessSmartAccount: true })getIntentStatuspolling
Injected wallet — batch probe
Script: scripts/injected-wallet-batch-probe.ts
Copy into a wagmi React app. Probes resolveWalletBatchStrategy, canBatchCalls, and confirms canRelayDeposit === false for injected wallets. Runs solveIntent without allowGaslessSmartAccount — user-paid gas with optional atomic batch.
See Gasless Deposits — Injected wallets: batching for edge cases.
Other examples:
SDK exports reference
Batching
resolveWalletBatchStrategy
Pick atomic vs sequential-tx
canBatchCalls
UI-friendly capability flags
executeWalletBatch
Low-level wallet_sendCalls wrapper
groupContiguousByChain
Group quote transactions by chain
mapBundleReceiptsToLegs
Map batch receipts to per-leg callbacks
isUserWalletRejection
Detect wallet deny / reject
EIP-7702 / gasless
getWalletGaslessStatus
Delegation probe + relay eligibility
convertToSmartAccount
One-time enable via SIO relay
gaslessDepositToCompact
Standalone relayed deposit
GaslessUnavailableError
Strict gasless failure
GASLESS_SUPPORTED_CHAIN_IDS
Testnet chain list
shouldUseGaslessRelay
True for local signers only
mockEip7702DelegatedCode
Test helper for delegation bytecode
signCompactSponsorWithPrivateKey
Manual relay debugging
solveIntent parameters
solveIntent parametersgasless: true + allowGaslessSmartAccount: true
Local wallet: SIO relay; throws on failure
gasless: true (browser, no allow flag)
Wallet-paid; batch when smart wallet active
gasless: false
Standard wallet-paid deposit
See Gasless Deposits for integrator checklist and error handling.
Common failures
Two separate wallet prompts on plain EOA
Expected — batching requires smart wallet or atomic capability
GaslessUnavailableError on local wallet
Chain disabled, EOA not delegated, or delegation === "other"
RELAYER_UNFUNDED
epoch-sio relayer balance below minimum
GASLESS_DISABLED
GASLESS_ENABLED not true on smallocator
Batch fails then sequential succeeds
Wallet rejected atomic batch or RPC issue — check logs for fallback
INVALID_AUTHORIZATION
Wrong delegate impl or chainId mismatch
Next steps
Gasless Deposits — step-by-step integrator guide
SDK Integration Guide — full checklist
SDK Reference — all method signatures
Error Handling —
GaslessUnavailableErrorand retries
Last updated