Tutorial: Paying for Gas With ERC-20 Tokens From an EOA Account
This tutorial demonstrates how to execute a transaction on Base while paying for gas fees with USDC on Optimism. We'll use Fusion execution to create a simple transaction that users can sign without needing native tokens for gas.
Overview
In blockchain applications, users typically need to hold native tokens on each chain they interact with. With Biconomy's Fusion execution, users can:
- Execute transactions on one chain (Base) while paying for gas with tokens on another chain (Optimism)
- Use ERC20 tokens like USDC instead of native tokens for gas fees
- Create seamless cross-chain experiences without managing multiple gas tokens
Prerequisites
- Node.js installed
- A wallet with some USDC on Optimism
- Basic understanding of EVM transactions
Steps to Implement Fusion Execution
Setting Up Your Environment
First, let's import the necessary dependencies and set up our account:
import { createMeeClient, executeFusionQuote, getMeeScanLink, mcUSDC, toMultichainNexusAccount, waitForSupertransactionReceipt, type Instruction, type Trigger } from "@biconomy/abstractjs-canary";
import { http, zeroAddress } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base, optimism } from "viem/chains";
Initialize Accounts and Client
Now we'll set up the EOA (Externally Owned Account) and create a Nexus account for orchestration:
const eoa = privateKeyToAccount('0xEOA_ADDRESS')
const oNexus = await toMultichainNexusAccount({
chains: [optimism, base],
transports: [http()],
signer: eoa,
})
const meeClient = await createMeeClient({
account: oNexus
})
In this code:
- We create an EOA from a private key (in production, use environment variables)
- We create a Nexus account that can operate on both Optimism and Base
- We initialize the MEE client which will handle orchestration
Define the Trigger Transaction
The trigger transaction is what the user will actually sign. Since USDC supports ERC20Permit, we can use it directly for gas payment:
// We'll use the fact that USDC supports ERC20Permit
// to enable the Biconomy Stack to use it to pay for gas.
const trigger: Trigger = {
// Amount refers to the amount of USDC our instructions
// would use. In this case it'll be zero, since we're not
// doing anything useful in our instructions, but if you're
// going to be using USDC in your instructions, just set this
// to the amount of USDC the instructions will need
amount: 0n,
chainId: optimism.id,
tokenAddress: mcUSDC.addressOn(optimism.id)
}
This trigger defines:
- Which token to use (USDC on Optimism)
- How much of that token our instructions need (0 in this example)
- Which chain the trigger executes on (Optimism)
Define the Instruction
Next, we define what we want to execute. In this example, we're creating a simple instruction that doesn't do anything useful - it just sends 0 ETH to the zero address on Base:
const instruction: Instruction = {
// One instruction can contain multiple
// function calls. This will be a demo
// instruction that does nothing - it just
// sends 0 ETH to 0x000....
calls: [{
to: zeroAddress,
value: 0n
}],
// The chain where the instruction will be executed
chainId: base.id
}
Note that while our trigger is on Optimism, the instruction executes on Base - demonstrating cross-chain functionality.
Get a Quote for Execution
Now we need to get a quote for executing our instruction using USDC on Optimism as the fee token:
const fusionQuote = await meeClient.getFusionQuote({
trigger: trigger,
// Important: Fee token *has* to be the same as the trigger
// token, when using this flow!
feeToken: {
address: mcUSDC.addressOn(optimism.id),
chainId: optimism.id
},
instructions: [instruction]
})
Important note: When using this flow, the fee token must be the same as the trigger token.
Execute the Transaction
With our quote ready, we can now execute the transaction:
const { hash } = await meeClient.executeFusionQuote({
fusionQuote
})
This creates the transaction that the user will sign. When the user signs this transaction, it will:
- Approve the usage of USDC on Optimism for fees
- Execute the instruction on Base
Wait for Completion and Track Status
Finally, we wait for the transaction to complete and get a link to track it:
const receipt = await meeClient.waitForSupertransactionReceipt({
hash
})
// Get link to https://meescan.biconomy.io explorer
// which will allow you to track it
const meeScanLink = getMeeScanLink(hash)
The waitForSupertransactionReceipt
function will pause execution until the transaction is confirmed. The getMeeScanLink
function provides a URL to Biconomy's explorer where you can track the transaction's status.
Making Smart Contract Account Invisible
For a truly seamless user experience, you can include a transfer instruction as the final step in your instructions array. This automatically returns any resulting assets (tokens, NFTs, positions) from the smart account back to the user's original EOA. Users simply sign one transaction from their familiar wallet and later receive the end results directly there, without ever needing to know about or interact with the intermediary smart account that facilitated the cross-chain operations.
How It Works Behind the Scenes
When executing this cross-chain flow:
- The user signs a transaction with USDC on Optimism
- The Biconomy infrastructure uses this to pay for gas fees
- The system handles the cross-chain orchestration
- The instruction is executed on Base
- All of this happens without the user needing to hold ETH on Base
Real-World Applications
While this example is simple, you can use this pattern to:
- Allow users to interact with Base dApps without holding ETH on Base
- Enable cross-chain DeFi interactions using only USDC on Optimism
- Create multi-chain experiences with simplified token management
- Reduce onboarding friction for new users
Next Steps
To extend this example for real applications:
- Replace the empty instruction with meaningful contract interactions on Base
- Add proper error handling
- Implement a UI for users to interact with
- Consider adding multiple instructions across different chains
By leveraging cross-chain Fusion execution with ERC20 gas payments, you can create a much smoother user experience that eliminates one of the major friction points in blockchain applications.