EIP-7702 Transaction Batching
Overview
What It Does
EIP-7702 batching lets you combine multiple DeFi operations — fee payment, token approval, and a swap or deposit — into a single transaction that the user signs once. Without batching, each step is a separate transaction requiring its own signature and gas fee.
Who It's For
- Wallet developers integrating with the Blockdaemon DeFi API who want a smoother UX for end users.
- DApp builders looking to reduce the number of signing prompts and transaction confirmations.
- Backend engineers building trading or portfolio management systems that interact with DeFi protocols.
Why It Matters
In a typical DeFi swap with fees enabled, a user must sign and submit up to three separate transactions (fee, approval, swap) — each costing gas and requiring confirmation. EIP-7702 batching collapses all of these into one atomic transaction. If any step fails, the entire batch reverts, so you never end up in a partial state (e.g., fee paid but swap failed).
Key Concepts
Before diving in, here are the core ideas you'll encounter throughout this guide.
EOA (Externally Owned Account)
A standard Ethereum wallet controlled by a private key — the kind most users have (e.g., MetaMask accounts). Unlike smart contract wallets, EOAs can't normally execute multiple operations in one transaction.
EIP-7702
An Ethereum standard that temporarily gives an EOA the ability to act like a smart contract for a single transaction. The EOA signs an authorization that delegates its execution to a smart contract — called the delegation address — runs the batch through that contract's logic, and then returns to normal.
Smart Contract Delegation Address
The smart contract that an EOA delegates its execution to via EIP-7702. In the context of this API, the delegation address is an ERC-7821 batcher contract that exposes one function — execute(mode, calldata) — which runs a list of sub-calls in sequence. Blockdaemon deploys and manages these contracts on each supported chain, but you can also bring your own.
The API response returns this address in the batcherContract field.
Atomic Execution
All sub-calls in the batch either succeed together or fail together. There's no in-between state where some calls executed but others didn't.
EIP-7702 vs. EIP-5792
Both achieve the same outcome (atomic batching), but they work differently:
| EIP-7702 | EIP-5792 | |
|---|---|---|
| Returns | A single transaction object | An array of calls |
| Submission method | eth_sendTransaction | wallet_sendCalls |
| Best for | EOA wallets with type-4 auth support | Smart contract / AA wallets |
| Wallet requirement | Must support EIP-7702 authorization signing | Must support wallet_sendCalls |
If you're unsure which to use: check what your target wallet supports. If it can sign EIP-7702 type-4 authorizations, use this. If it supports wallet_sendCalls (common in smart contract wallets like Safe), use EIP-5792 batching instead.
How It Works
High-Level Flow
Here's what happens when you send a request with batchOption: "EIP-7702":
- You send a request to the DeFi API (e.g., a swap), including
"batchOption": "EIP-7702"in the body. - The API assembles all required operations — fee payment, token approval (if needed), and the main operation — into a single encoded payload targeting the delegation address.
- The API returns one transaction object with the encoded payload as
dataand the delegation address in thebatcherContractfield. - The EOA signs an authorization delegating its execution to the smart contract at
batcherContract. - The transaction is submitted via
eth_sendTransactionwith the signed authorization attached. The delegation address executes all sub-calls atomically on behalf of the EOA.
Visual Summary
You DeFi API Blockchain
│ │ │
├─ POST /dex/swap ────────────►│ │
│ (batchOption: "EIP-7702") │ │
│ ├─ Assemble sub-calls: │
│ │ 1. Fee transfer │
│ │ 2. Token approval │
│ │ 3. Swap │
│ │ │
│◄─ Single transaction ────────┤ │
│ object returned │ │
│ │ │
├─ EOA signs authorization ────┤ │
│ (delegates to batcher) │ │
│ │ │
├─ eth_sendTransaction ────────┼───────────────────────────────►│
│ (with authorizationList) │ Delegation │
│ │ address │
│ │ executes │
│ │ all calls │
│ │ atomically │What Gets Batched
The number of sub-calls depends on the endpoint and whether fees are enabled:
| Scenario | Sub-calls |
|---|---|
| Swap with fee, no approval needed | 2: fee → swap |
| Swap with fee + approval needed | 3: fee → approve → swap |
| Swap without fee | 1: swap only |
| Deposit with fee | 2: fee → deposit |
NoteThe API automatically checks whether a token approval is needed. If the user already has sufficient allowance, the approval step is skipped.
Quick Start
The fastest path to your first batched transaction.
Prerequisites
- A valid Blockdaemon API key. See Send Your First API Request.
- A wallet that supports EIP-7702 authorization signing.
- A supported chain (Ethereum, Optimism, BNB Chain, Polygon, Arbitrum, Base, or Sepolia).
Steps
Step 1 — Add batchOption to your existing request.
Take any supported endpoint request and add "batchOption": "EIP-7702" to the body. That's the only change needed.
{
"dexId": "1300",
"amountIn": "1000000000000000",
"from": "0x6305...1006",
"to": "0x6305...1006",
"path": ["0xC02a...6Cc2", "0x6B17...1d0F"],
"poolFees": "3000",
"enableFee": true,
"batchOption": "EIP-7702"
}Step 2 — Sign the authorization and submit the transaction.
The API returns a single transaction object. Before submitting, the EOA must sign an authorization that delegates its execution to the smart contract at batcherContract (the delegation address). Here's the full flow using viem.
EIP-7702 supports two patterns: a relay (a separate account sponsors gas and sends the transaction on behalf of the EOA) or self-executing (the EOA signs the authorization and sends the transaction itself). Both are shown below.
Option A — Relay pattern (recommended for production)
A relay account sponsors gas and submits the transaction. The EOA only signs the authorization delegating to the smart contract.
import { createWalletClient, http } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// The EOA that will delegate its execution to the batcher contract
const eoa = privateKeyToAccount("0xEOA_PRIVATE_KEY");
const walletClient = createWalletClient({
account: eoa.address,
chain: mainnet,
transport: http(),
});
// 1. Fetch the batched transaction from the DeFi API
const result = await fetch("https://svc.blockdaemon.com/defi/v1/dex/swap-with-approval", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "YOUR_BLOCKDAEMON_API_KEY",
},
body: JSON.stringify({
dexId: "1300",
amountIn: "1000000000000000",
amountOutMin: "0",
path: ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "0x6B175474E89094C44Da98b954EedeAC495271d0F"],
to: eoa.address,
poolFees: "3000",
from: eoa.address,
gas: "173376",
enableFee: true,
batchOption: "EIP-7702",
}),
}).then((r) => r.json());
const { to, value, data, batcherContract } = result.data;
// 2. EOA signs the authorization, delegating to the smart contract
// at batcherContract (the delegation address)
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress: batcherContract,
});
// 3. Relay sends the transaction with the signed authorization
const txHash = await walletClient.sendTransaction({
authorizationList: [authorization],
to,
value: BigInt(value),
data,
});
console.log("Transaction hash:", txHash);Option B — Self-executing (EOA signs and sends)
When the EOA is both the authorization signer and the transaction sender, pass executor: 'self' to signAuthorization. This ensures the authorization nonce is incremented correctly relative to the transaction nonce.
import { createWalletClient, http } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const eoa = privateKeyToAccount("0xEOA_PRIVATE_KEY");
const walletClient = createWalletClient({
account: eoa,
chain: mainnet,
transport: http(),
});
// ... fetch the batched transaction from the DeFi API (same as above) ...
const { to, value, data, batcherContract } = result.data;
// Sign authorization with executor: 'self' since the EOA is also sending
const authorization = await walletClient.signAuthorization({
contractAddress: batcherContract,
executor: "self",
});
const txHash = await walletClient.sendTransaction({
authorizationList: [authorization],
to,
value: BigInt(value),
data,
});That's it. The authorization delegates the EOA to the smart contract, and the single transaction executes the fee payment, any needed approvals, and the swap atomically.
Detailed Guide
Supported Chains
EIP-7702 batching is available on the following networks:
| Chain ID | Network |
|---|---|
1 | Ethereum Mainnet |
10 | Optimism |
56 | BNB Smart Chain |
137 | Polygon |
42161 | Arbitrum One |
8453 | Base |
11155111 | Sepolia Testnet |
Default Smart Contract Delegation Addresses
Blockdaemon deploys and manages an ERC-7821 batcher contract on each supported chain. These serve as the default delegation addresses — the smart contracts that EOAs delegate their execution to when using EIP-7702 batching.
When you send a request with batchOption: "EIP-7702" and don't specify a batcherContract, the API automatically uses the default delegation address for that chain.
| Chain | Chain ID | Delegation Address (batcherContract) |
|---|---|---|
| Ethereum Mainnet | 1 | 0xe386b98Cd06c6aee22D66eDF97Df0476e3514222 |
| Base | 8453 | 0xA1fdd934a977898B72ddBFbeC5525e266b7e9991 |
| Optimism | 10 | 0x9c933f9d2efd76ce25b0d3dde84d3f94e78a50ca |
| Polygon | 137 | 0x997ff2131e194cA66Ba01A550326Fc168508E2B3 |
| BNB Smart Chain | 56 | 0x753d3c43F541639BFa4e115f2fcA87016B027f7A |
| Arbitrum One | 42161 | 0x997ff2131e194cA66Ba01A550326Fc168508E2B3 |
| Ethereum Sepolia | 11155111 | 0x997ff2131e194cA66Ba01A550326Fc168508E2B3 |
| Kaia | 8217 | 0x997ff2131e194cA66Ba01A550326Fc168508E2B3 |
The response always includes a batcherContract field confirming the delegation address that will execute the batch. You can use this to verify before submitting.
Using a Custom Delegation Address
If you need custom execution logic, access controls, or a self-managed deployment, you can provide your own smart contract as the delegation address via the batcherContract parameter:
{
"batchOption": "EIP-7702",
"batcherContract": "0xYourCustomContractAddress"
}Your contract must implement the ERC-7821 execute(bytes32 mode, bytes executionData) function. The API encodes sub-calls to this interface regardless of whether you use the default or a custom delegation address.
WarningIf your contract doesn't implement ERC-7821, the transaction will revert on-chain. Always verify your contract's interface before using it in production.
Supported Endpoints
EIP-7702 batching works across DEX, Bridge, and Lend/Borrow endpoints:
| Domain | Endpoint | Calls (fee on) | Calls (fee off) |
|---|---|---|---|
| DEX | POST /defi/v1/dex/swap | 2 (fee + swap) | 1 |
| DEX | POST /defi/v1/dex/swap-with-approval | 3 (fee + approve + swap) | 2 |
| DEX | POST /defi/v1/dex/swaps | 2 per route | 1 per route |
| Bridge | POST /defi/v1/bridge/swap-with-approval | 3 (fee + approve + swap) | 2 |
| Bridge | POST /defi/v1/bridge/swaps | 2 per route | 1 per route |
| Lend/Borrow | POST /defi/v1/lend-borrow/deposit | 2 (fee + deposit) | 1 |
| Lend/Borrow | POST /defi/v1/lend-borrow/withdraw | 2 (fee + withdraw) | 1 |
| Lend/Borrow | POST /defi/v1/lend-borrow/borrow | 2 (fee + borrow) | 1 |
| Lend/Borrow | POST /defi/v1/lend-borrow/repay | 2 (fee + repay) | 1 |
| Lend/Borrow | POST /defi/v1/lend-borrow/transfer | 2 (fee + transfer) | 1 |
Request Parameters
Add these to any supported endpoint's standard parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
batchOption | string | Yes | Set to "EIP-7702" to enable batching. |
batcherContract | string | No | Address of a custom ERC-7821 smart contract to use as the delegation address. If omitted, the Blockdaemon default for the target chain is used. |
Response Format
With batchOption: "EIP-7702", the API returns a single transaction object instead of separate ones:
{
"status": 200,
"msg": "success",
"data": {
"from": "0x<sender>",
"to": "0x<delegation address>",
"value": "0x0",
"data": "0x<ABI-encoded execute(mode, calldata)>",
"batcherContract": "0x<delegation address>",
"chainId": "1",
"dexName": "UniswapV3",
"amountsOut": ["1000000"],
"referenceId": "abc-123",
"methodUsed": "EIP-7702"
}
}Field reference:
| Field | Description |
|---|---|
from | The sender's EOA address. |
to | The smart contract delegation address — where the transaction is sent. |
value | Always "0x0". ETH values for individual sub-calls are embedded in the encoded calldata. |
data | The ABI-encoded execute(mode, calldata) payload containing all sub-calls. |
batcherContract | The smart contract delegation address (same as to). Pass this as contractAddress when calling signAuthorization in viem. |
chainId | Target chain as a decimal string. Convert to hex if your wallet provider requires it. |
dexName / bridgeName | The protocol used (varies by endpoint). |
amountsOut | Expected output amounts. |
referenceId | Unique ID for tracking and debugging. |
methodUsed | Always "EIP-7702". |
Aggregator Responses
For aggregator endpoints (/dex/swaps, /bridge/swaps), the response data is an array — one transaction object per available route:
{
"status": 200,
"msg": "success",
"data": [
{
"bridgeName": "Stargate",
"amountsOut": ["999000"],
"methodUsed": "EIP-7702",
"to": "0x<delegation address>",
"data": "0x<encoded batch>",
"value": "0x0",
"batcherContract": "0x<delegation address>"
},
{
"bridgeName": "Across",
"amountsOut": ["998500"],
"methodUsed": "EIP-7702",
"to": "0x<delegation address>",
"data": "0x<encoded batch>",
"value": "0x0",
"batcherContract": "0x<delegation address>"
}
]
}Pick the route that fits your criteria (best rate, preferred bridge, etc.) and submit that route's transaction object. See the Quote Aggregator guide for route selection strategies.
Sending the Transaction with viem
Here's the core signing and submission flow using viem. This example uses the relay pattern (a separate account sponsors gas). For the self-executing pattern where the EOA sends its own transaction, see the Quick Start above.
import { createWalletClient, http } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const eoa = privateKeyToAccount("0xEOA_PRIVATE_KEY");
const walletClient = createWalletClient({
account: eoa.address,
chain: mainnet,
transport: http(),
});
// After fetching the batched transaction from the API...
const { to, value, data, batcherContract } = result.data;
// EOA signs the authorization, delegating to the smart contract
// at batcherContract (the delegation address)
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress: batcherContract,
});
// Relay submits the transaction with the signed authorization
const txHash = await walletClient.sendTransaction({
authorizationList: [authorization],
to,
value: BigInt(value),
data,
});Tip: If the EOA is also the sender, pass
executor: 'self'tosignAuthorizationso the authorization nonce is set correctly relative to the transaction nonce. See the viem EIP-7702 docs for details.
Under the Hood
For developers who want to understand the encoding:
The API encodes all sub-calls using the ERC-7821 execute(bytes32 mode, bytes executionData) interface on the delegation address:
mode— Set to0x0100...0000(batch execution mode).executionData— An ABI-encoded array of(address target, uint256 value, bytes data)tuples, one per sub-call, in this exact execution order:- Fee payment (if
enableFee: true) - Token approval (if required)
- Main operation (swap, deposit, borrow, etc.)
- Fee payment (if
When the EOA signs an EIP-7702 authorization for the delegation address, the EOA temporarily gains the contract's code. The execute function then runs atomically on behalf of the EOA — each sub-call executes as if the EOA itself made the call. If any sub-call reverts, the entire batch reverts.
Ordering MattersSub-calls execute in the order they're encoded. Do not reorder the encoded data.
Wallet Compatibility
EIP-7702 requires the wallet to support signing type-4 authorization tuples that delegate the EOA to a smart contract. Check your target wallet before integrating.
| Wallet Type | Support |
|---|---|
| Wallets with native EIP-7702 support | ✅ Supported |
| Smart contract wallets (e.g., Safe, ERC-4337) | ✅ Use EIP-5792 instead |
| Legacy EOA wallets without EIP-7702 | ❌ Not supported — use individual transactions |
Fallback for unsupported wallets: Omit batchOption entirely. The API returns separate transaction objects that you submit one by one, in order:
// Without batchOption — separate transactions
const { createFeeTxn, swapTxn } = result.data;
// Always submit in order: fee first, then swap
await walletProvider.request({ method: "eth_sendTransaction", params: [createFeeTxn] });
await walletProvider.request({ method: "eth_sendTransaction", params: [swapTxn] });
ImportantWhen falling back to individual transactions, always submit them in order: fee → approval → main operation.
Examples
Example 1: DEX Swap with Fee (Ethereum Mainnet)
A WETH → DAI swap on Uniswap V3 with fees enabled. Uses the default Ethereum delegation address.
Request:
POST https://svc.blockdaemon.com/defi/v1/dex/swap
Content-Type: application/json
X-API-Key: <your-key>
{
"dexId": "1300",
"amountIn": "1000000000000000",
"amountOutMin": "0",
"path": [
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
],
"to": "0x63056E00436Da25BcF48A40dfBbDcc7089351006",
"poolFees": "3000",
"from": "0x63056E00436Da25BcF48A40dfBbDcc7089351006",
"gas": "173376",
"enableFee": true,
"batchOption": "EIP-7702"
}Response:
{
"status": 200,
"msg": "success",
"data": {
"from": "0x63056E00436Da25BcF48A40dfBbDcc7089351006",
"to": "0xe386b98Cd06c6aee22D66eDF97Df0476e3514222",
"value": "0x0",
"data": "0x6943...0000",
"batcherContract": "0xe386b98Cd06c6aee22D66eDF97Df0476e3514222",
"chainId": "1",
"referenceId": "6106186031fb415bbeb3e45fd707ad18",
"methodUsed": "EIP-7702"
}
}The batcherContract confirms the default Ethereum Mainnet delegation address was used.
Example 2: Lend/Borrow Deposit with Fee
Deposit USDC into Aave V3 on Ethereum with fees enabled.
Request:
POST https://svc.blockdaemon.com/defi/v1/lend-borrow/deposit
Content-Type: application/json
X-API-Key: <your-key>
{
"lendborrowId": "1200",
"asset": "0x65afadd39029741b3b8f0756952c74678c9cec93",
"amount": "10000000000000000",
"from": "0x829bFB482331b9Dc2BEcb5483ecA79c0578c3A45",
"onBehalfOf": "0x829bFB482331b9Dc2BEcb5483ecA79c0578c3A45",
"gas": "408298",
"batchOption": "EIP-7702"
}Response:
{
"status": 200,
"msg": "success",
"data": {
"from": "0x829bFB482331b9Dc2BEcb5483ecA79c0578c3A45",
"chainId": "1",
"referenceId": "557009b1dbbb4bc4a32124ea2804ce2e",
"methodUsed": "EIP-7702",
"to": "0xe386b98Cd06c6aee22D66eDF97Df0476e3514222",
"data": "0xe9ae5c[...]00000000",
"value": "0x0",
"batcherContract": "0xe386b98Cd06c6aee22D66eDF97Df0476e3514222"
}
}Example 3: Bridge Swap with a Custom Delegation Address
Cross-chain USDT (Ethereum) → USDC (Polygon) bridge using your own smart contract as the delegation address.
Request:
POST https://svc.blockdaemon.com/defi/v1/bridge/swap-with-approval
Content-Type: application/json
X-API-Key: <your-key>
{
"bridgeId": "600",
"srcChainId": "1",
"dstChainId": "137",
"srcTokenSymbol": "USDT",
"dstTokenSymbol": "USDC",
"amountIn": "10000000",
"amountOutMin": "0",
"from": "0xdAe2F6EdDdA6fb4fb60cc02633DE27e2b431B402",
"to": "0xdAe2F6EdDdA6fb4fb60cc02633DE27e2b431B402",
"gasPriority": "low",
"srcChainSymbol": "ETH",
"enableFee": true,
"batchOption": "EIP-7702",
"batcherContract": "0xYourCustomContractAddress"
}
NoteYour custom contract must implement ERC-7821. The API encodes sub-calls to
execute(bytes32 mode, bytes executionData)regardless of which delegation address is used.
Common Mistakes and Troubleshooting
"My transaction reverted on-chain"
Likely cause: You supplied a custom batcherContract (delegation address) that doesn't implement the ERC-7821 execute interface.
Fix: Verify your contract exposes execute(bytes32 mode, bytes executionData). Test on Sepolia first.
"The wallet rejected the transaction"
Likely cause: The wallet doesn't support EIP-7702 type-4 authorization signing.
Fix: Confirm your wallet supports EIP-7702. If not, omit batchOption and submit transactions individually. For smart contract wallets, try EIP-5792 batching instead.
"I got separate transaction objects instead of a single one"
Likely cause: You forgot to include "batchOption": "EIP-7702" in the request body.
Fix: Add "batchOption": "EIP-7702" to the request. This is the only parameter needed to enable batching.
"The chainId is in decimal but my wallet expects hex"
Explanation: The API returns chainId as a decimal string (e.g., "1"). Some wallet providers expect hex (e.g., "0x1").
Fix: Convert before submitting:
const hexChainId = `0x${parseInt(chainId).toString(16)}`;"I submitted individual transactions in the wrong order"
Explanation: When falling back to individual transactions (no batchOption), order matters. Fee must come first, then approval, then the main operation.
Fix: Always submit in sequence: createFeeTxn → approval (if present) → swapTxn / main operation.
"The response doesn't include an approval transaction"
Explanation: This is expected. The API checks the user's existing token allowance. If it already covers the required amount, the approval step is skipped — reducing gas costs.
Summary
What to remember:
- Add
"batchOption": "EIP-7702"to any supported endpoint request to enable batching. That's the only required change. - The API returns a single transaction object targeting the smart contract delegation address. The EOA signs an authorization delegating to this contract, and the transaction is submitted with
eth_sendTransaction. - All sub-calls (fee, approval, swap/deposit/etc.) execute atomically — all succeed or all revert.
- Blockdaemon manages default delegation addresses on every supported chain (returned in the
batcherContractresponse field). You can also supply your own via thebatcherContractrequest parameter, as long as it implements ERC-7821. - The wallet must support EIP-7702 authorization signing. For smart contract wallets, use EIP-5792 instead. For legacy wallets, fall back to individual transactions.
- Supported across DEX swap, Bridge, and Lend/Borrow endpoints on 7 chains (Ethereum, Optimism, BNB Chain, Polygon, Arbitrum, Base, Sepolia).
Related Resources
| Resource | Description |
|---|---|
| Getting Started | Set up your API key and make your first DeFi API call |
| EIP-5792 Batching | Alternative batching via wallet_sendCalls for smart contract wallets |
| DEX Swap Aggregator | Multi-route aggregator for best-rate swaps |
| Bridge Swap | Cross-chain bridge endpoint reference |
| Lend/Borrow Deposit Guide | Depositing and withdrawing in vaults |
| Fee Configuration | Setting up basis points and collector addresses |
| viem EIP-7702 Guide | viem's guide to sending EIP-7702 transactions |
| EIP-7702 Specification | Official EIP-7702 standard |
| ERC-7821 Specification | Minimal batch execution interface used by the delegation address |
Updated about 3 hours ago
