Skip to main content
Use mode: 'eoa' for users with standard Ethereum wallets like MetaMask, Rabby, Trust Wallet, or any WalletConnect-compatible wallet.

How EOA Mode Works

EOA mode uses Fusion execution—a system that lets regular wallets access Biconomy’s gas abstraction without needing a smart account.
1

User specifies funding tokens

You tell the API which tokens the user will spend
2

API returns permit or approval payload

Depending on token support, user either signs a gasless permit or sends an approval tx
3

User signs

One signature per funding token
4

MEE executes

Biconomy handles the rest

Basic Request

const quote = await fetch('https://api.biconomy.io/v1/quote', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    mode: 'eoa',
    ownerAddress: '0xYourUserAddress',
    fundingTokens: [{
      tokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', // USDC on Base
      chainId: 8453,
      amount: '100000000' // 100 USDC (6 decimals)
    }],
    feeToken: {
      address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
      chainId: 8453
    },
    composeFlows: [{
      type: '/instructions/intent-simple',
      data: {
        srcChainId: 8453,
        dstChainId: 10,
        srcToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
        dstToken: '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58',
        amount: '100000000',
        slippage: 0.01
      }
    }]
  })
}).then(r => r.json());

Required Parameters

ParameterTypeDescription
mode'eoa'Must be 'eoa'
ownerAddressstringUser’s wallet address
fundingTokensarrayTokens user will spend (required for EOA)
composeFlowsarrayOperations to execute

Understanding fundingTokens

This is the key difference from other modes. You must specify exactly which tokens the user is spending:
fundingTokens: [
  {
    tokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', // Token contract
    chainId: 8453,                                              // Chain the token is on
    amount: '100000000'                                         // Amount in wei
  }
]
The amount should cover both the operation AND any fees. If you’re paying fees in the same token, include enough for both.

Multiple Funding Tokens

If your operation involves multiple input tokens, list them all:
fundingTokens: [
  {
    tokenAddress: USDC_BASE,
    chainId: 8453,
    amount: '50000000'  // 50 USDC
  },
  {
    tokenAddress: USDT_BASE,
    chainId: 8453,
    amount: '50000000'  // 50 USDT
  }
]
Each funding token requires a separate signature. 2 tokens = 2 signatures.

Fee Token Rules

When using EOA mode, the feeToken must match one of your fundingTokens:
// ✅ Correct - feeToken matches a fundingToken
{
  fundingTokens: [{ tokenAddress: USDC, chainId: 8453, amount: '100000000' }],
  feeToken: { address: USDC, chainId: 8453 }
}

// ❌ Wrong - feeToken doesn't match any fundingToken
{
  fundingTokens: [{ tokenAddress: USDC, chainId: 8453, amount: '100000000' }],
  feeToken: { address: USDT, chainId: 8453 }  // Error!
}

Quote Response

The API returns different quoteType values based on token capabilities:
{
  "quoteType": "permit",  // or "onchain"
  "payloadToSign": [...],
  "fee": { "amount": "50000", "token": "0x...", "chainId": 8453 }
}
quoteTypeMeaningUser Experience
permitToken supports EIP-2612Gasless—user just signs a message
onchainToken doesn’t support permitUser must send approval transaction first

Signing the Payload

Most modern tokens (USDC, DAI) support permit—user signs typed data:
const payload = quote.payloadToSign[0];

const signature = await walletClient.signTypedData({
  ...payload.signablePayload,
  account
});

Complete Example

import { createWalletClient, createPublicClient, http, parseUnits } from 'viem';
import { base } from 'viem/chains';

const USDC_BASE = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913';
const USDT_OP = '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58';

async function swapWithEOA(userAddress, walletClient, publicClient) {
  // 1. Get quote
  const quote = await fetch('https://api.biconomy.io/v1/quote', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      mode: 'eoa',
      ownerAddress: userAddress,
      fundingTokens: [{
        tokenAddress: USDC_BASE,
        chainId: 8453,
        amount: parseUnits('100', 6).toString()
      }],
      feeToken: {
        address: USDC_BASE,
        chainId: 8453
      },
      composeFlows: [{
        type: '/instructions/intent-simple',
        data: {
          srcChainId: 8453,
          dstChainId: 10,
          srcToken: USDC_BASE,
          dstToken: USDT_OP,
          amount: parseUnits('100', 6).toString(),
          slippage: 0.01
        }
      }]
    })
  }).then(r => r.json());

  // 2. Sign based on quoteType
  const payload = quote.payloadToSign[0];
  let signature;

  if (quote.quoteType === 'permit') {
    signature = await walletClient.signTypedData({
      ...payload.signablePayload,
      account: walletClient.account
    });
  } else {
    // onchain - send approval tx
    const txHash = await walletClient.sendTransaction({
      to: payload.to,
      data: payload.data
    });
    await publicClient.waitForTransactionReceipt({ hash: txHash });
    signature = txHash;
  }

  // 3. Execute
  const result = await fetch('https://api.biconomy.io/v1/execute', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ...quote,
      payloadToSign: [{ ...payload, signature }]
    })
  }).then(r => r.json());

  return result.supertxHash;
}

Important: Withdraw Funds

In EOA mode, swapped tokens land in a temporary Nexus account. Add a withdrawal instruction to send them back to the user’s EOA:
composeFlows: [
  // Swap
  {
    type: '/instructions/intent-simple',
    data: { /* swap params */ }
  },
  // Withdraw to EOA
  {
    type: '/instructions/build',
    data: {
      chainId: 10,  // Destination chain
      to: USDT_OP,  // Token contract
      functionSignature: 'function transfer(address to, uint256 value)',
      args: [
        userAddress,  // Send to user's EOA
        {
          type: 'runtimeErc20Balance',
          tokenAddress: USDT_OP,
          constraints: { gte: '1' }
        }
      ]
    }
  }
]
Without the withdrawal instruction, tokens stay in the Nexus account and user won’t see them in their wallet!

Next Steps