Skip to main content
External wallets like MetaMask, Rabby, and Trust Wallet don’t allow apps to install smart account logic directly on your address. Biconomy solves this with Fusion Mode—a passthrough mechanism that enables the same powerful orchestration features through a temporary Companion Account.

What We’re Building

By the end of this tutorial, your users will be able to:
  1. Connect their existing MetaMask, Rabby, or Trust wallet
  2. Execute orchestrated transactions (swaps, bridges, multicalls)
  3. Pay gas with ERC-20 tokens instead of ETH
  4. Receive all resulting tokens back to their original wallet
How it works: When a user signs a “trigger” transaction, Biconomy’s MEE temporarily moves funds to a Companion Account, executes all operations, and returns the results to the user’s wallet—all in one seamless flow.

Understanding Fusion Mode

Before diving into code, let’s understand how Fusion Mode differs from the EIP-7702 approach used with embedded wallets.

Why Fusion Mode?

External wallets are browser extensions that maintain their own security model. They don’t permit apps to “upgrade” the wallet to a smart account using EIP-7702. Fusion Mode works around this limitation by:
  1. Using a trigger transaction to authorize the orchestration
  2. Routing execution through a Companion Account (fully owned by the user)
  3. Returning all assets to the user’s original wallet

The Fusion Flow

1

User Signs Trigger

The user signs a transaction (often a permit or approve) that contains the hash of all orchestration instructions. This single signature authorizes the entire flow.
2

Funds Move to Companion Account

Funds are pulled into a Companion Account that the user fully owns. This account is non-custodial and stateless—nothing remains after execution.
3

Instructions Execute

All operations (swaps, bridges, multicalls) execute using the Companion Account. MEE nodes handle the complexity.
4

Assets Return to EOA

Final assets are sent back to the user’s original wallet address. The Companion Account is left clean.
5

Automatic Cleanup (if needed)

If any step fails, cleanup logic can revert intermediate steps and return funds safely.
Invisible to Users: Your users never need to know about the Companion Account. From their perspective, they sign once and receive tokens in their wallet.

Trigger Types

Fusion Mode supports two trigger types, automatically selected based on your input token:
Trigger TypeHow it WorksGas RequiredToken Support
ERC20PermitUses ERC-2612 signature to permit spending. Orchestration hash is packed into the deadline field.❌ NoERC-2612 tokens only
Onchain TxUser sends an approve() transaction that includes orchestration data.✅ YesAll ERC-20 tokens
The SDK automatically detects whether your token supports ERC-2612 and chooses the appropriate trigger type.

Fusion Constraints

Before implementing, understand these limitations:
  • One token per signature: Fusion can only consume one input token per user action
  • Same token for gas: The trigger token must also pay for gas fees
  • Same-chain gas only: Unlike EIP-7702, you can’t pay gas on Chain A for execution on Chain B
  • Sponsorship available: You can sponsor gas to bypass the gas requirement entirely

Step 1: Install Dependencies

npm install @biconomy/abstractjs viem wagmi @tanstack/react-query
What these packages do:
  • @biconomy/abstractjs — Biconomy’s SDK for MEE and Fusion Mode
  • viem — Ethereum library for building transactions
  • wagmi — React hooks for wallet connections
  • @tanstack/react-query — Required peer dependency for wagmi

Step 2: Configure Wallet Connection

Set up wagmi to support popular external wallets:
import { WagmiProvider, createConfig, http } from "wagmi";
import { optimism, base, arbitrum } from "wagmi/chains";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { injected, walletConnect } from "wagmi/connectors";

const config = createConfig({
  chains: [optimism, base, arbitrum],
  connectors: [
    injected(), // MetaMask, Rabby, etc.
    walletConnect({ projectId: "YOUR_WC_PROJECT_ID" }),
  ],
  transports: {
    [optimism.id]: http(),
    [base.id]: http(),
    [arbitrum.id]: http(),
  },
});

const queryClient = new QueryClient();

function App({ children }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  );
}

Step 3: Connect the Wallet

Use wagmi hooks to connect and access the user’s wallet:
import { useAccount, useConnect, useWalletClient } from "wagmi";

function WalletConnection() {
  const { address, isConnected } = useAccount();
  const { connect, connectors } = useConnect();
  const { data: walletClient } = useWalletClient();

  if (!isConnected) {
    return (
      <div>
        {connectors.map((connector) => (
          <button key={connector.id} onClick={() => connect({ connector })}>
            Connect {connector.name}
          </button>
        ))}
      </div>
    );
  }

  return <p>Connected: {address}</p>;
}

Step 4: Create the Fusion Account

For Fusion Mode, create a toFusionAccount instead of the standard Nexus account:
import { 
  mcUSDC,
  toFusionAccount, 
  createMeeClient 
} from "@biconomy/abstractjs";
import { optimism, base } from "viem/chains";
import { http } from "viem";

async function setupFusion(walletClient) {
  // Create a Fusion account for the connected wallet
  const fusionAccount = await toFusionAccount({
    // The user's EOA address from their external wallet
    address: walletClient.account.address,
    
    // The client for signing (from wagmi)
    client: walletClient,
    
    // Chains where Fusion will operate
    chains: [optimism, base],
  });

  // Create the MEE client
  const meeClient = await createMeeClient({
    account: fusionAccount,
  });

  return { fusionAccount, meeClient };
}

Step 5: Build the Trigger

The trigger defines which token the user will “spend” to initiate the orchestration:
import { parseUnits } from "viem";
import { mcUSDC } from "@biconomy/abstractjs";

// Define the trigger: user will spend USDC on Optimism
const trigger = {
  chainId: optimism.id,
  tokenAddress: mcUSDC.addressOn(optimism.id),
  amount: parseUnits("10", 6), // 10 USDC
};
The trigger token must be the same token used for gas payment in Fusion Mode. This is a key constraint.

Step 6: Define Instructions

Instructions describe what operations to execute. Here’s an example that transfers USDC:
import { encodeFunctionData, erc20Abi, parseUnits } from "viem";

// Example: Transfer some USDC to another address
const instructions = [
  {
    chainId: optimism.id,
    calls: [
      {
        to: mcUSDC.addressOn(optimism.id),
        data: encodeFunctionData({
          abi: erc20Abi,
          functionName: "transfer",
          args: ["0xRecipientAddress", parseUnits("5", 6)],
        }),
      },
    ],
  },
];

Step 7: Get a Fusion Quote

Request a quote that includes the trigger and instructions:
const { quote, trigger: signableTrigger } = await meeClient.getFusionQuote({
  trigger,
  instructions,
  feeToken: {
    address: trigger.tokenAddress,
    chainId: trigger.chainId,
  },
});

console.log("Estimated gas cost:", quote.paymentInfo.tokenWei);

Step 8: Execute the Fusion Transaction

Execute the quote—the SDK handles trigger signing automatically:
const { hash } = await meeClient.executeFusionQuote({ 
  fusionQuote: quote 
});

console.log("Supertransaction submitted:", hash);

// Wait for confirmation
const receipt = await meeClient.waitForSupertransactionReceipt({ hash });
console.log("Completed:", receipt);

Complete Example

Here’s a full React component for Fusion Mode with MetaMask:
import { useState } from "react";
import { useAccount, useWalletClient } from "wagmi";
import { parseUnits, encodeFunctionData, erc20Abi } from "viem";
import { optimism } from "viem/chains";
import { 
  createMeeClient, 
  toFusionAccount,
  mcUSDC 
} from "@biconomy/abstractjs";

const USDC_OPTIMISM = "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85";

export function FusionTransfer() {
  const { address } = useAccount();
  const { data: walletClient } = useWalletClient();
  const [status, setStatus] = useState("");
  const [recipient, setRecipient] = useState("");
  const [amount, setAmount] = useState("");

  async function executeFusionTransaction() {
    if (!walletClient || !address) {
      setStatus("Please connect your wallet first");
      return;
    }

    try {
      setStatus("Setting up Fusion account...");

      // Create Fusion account for external wallet
      const fusionAccount = await toFusionAccount({
        address,
        client: walletClient,
        chains: [optimism],
      });

      // Create MEE client
      const meeClient = await createMeeClient({
        account: fusionAccount,
      });

      setStatus("Getting quote...");

      // The amount to trigger with (includes buffer for gas)
      const triggerAmount = parseUnits(amount, 6);
      const transferAmount = parseUnits(
        (parseFloat(amount) * 0.95).toFixed(6), // 95% goes to recipient
        6
      );

      // Define trigger
      const trigger = {
        chainId: optimism.id,
        tokenAddress: USDC_OPTIMISM,
        amount: triggerAmount,
      };

      // Define what we want to do
      const instructions = [
        {
          chainId: optimism.id,
          calls: [
            {
              to: USDC_OPTIMISM,
              data: encodeFunctionData({
                abi: erc20Abi,
                functionName: "transfer",
                args: [recipient, transferAmount],
              }),
            },
          ],
        },
      ];

      // Get quote
      const { quote } = await meeClient.getFusionQuote({
        trigger,
        instructions,
        feeToken: {
          address: USDC_OPTIMISM,
          chainId: optimism.id,
        },
      });

      setStatus("Please sign the transaction in your wallet...");

      // Execute (user will be prompted to sign)
      const { hash } = await meeClient.executeFusionQuote({
        fusionQuote: quote,
      });

      setStatus(`Transaction submitted! Hash: ${hash}`);

      // Wait for confirmation
      const receipt = await meeClient.waitForSupertransactionReceipt({ hash });
      
      setStatus(`✅ Success! Transferred to ${recipient}`);
      
    } catch (error) {
      setStatus(`Error: ${error.message}`);
    }
  }

  return (
    <div className="fusion-transfer">
      <h2>Send USDC (Gasless with Fusion)</h2>
      
      <input
        type="text"
        placeholder="Recipient address"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
      />
      
      <input
        type="text"
        placeholder="Amount (USDC)"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />
      
      <button onClick={executeFusionTransaction}>
        Send USDC
      </button>
      
      <p>{status}</p>
      
      <small>
        Gas will be paid from your USDC. If your token supports ERC-2612,
        this will be completely gasless.
      </small>
    </div>
  );
}

Comparison: Fusion vs EIP-7702

FeatureFusion ModeEIP-7702
Wallet SupportAll wallets (MetaMask, Rabby, Trust, etc.)Embedded wallets only (Privy, Turnkey, etc.)
Gas PaymentSame chain onlyCross-chain supported
Input TokensOne token per signatureMultiple tokens possible
User ExperienceSign once (permit) or send approve txSign authorization once
Companion AccountRequired (transparent)Not needed

When to Use Fusion Mode

Use Fusion When

  • Users have MetaMask, Rabby, Trust, or similar
  • You need maximum wallet compatibility
  • Single-token input flows (swaps, deposits)
  • Same-chain gas payment is acceptable

Consider EIP-7702 When

  • You control the wallet (embedded wallets)
  • Cross-chain gas payment is needed
  • Multiple input tokens per operation
  • Direct smart account features required

Key Takeaways

  1. Fusion enables any wallet — MetaMask, Rabby, Trust, and all browser extensions work
  2. One signature, full orchestration — Users sign once to authorize complex operations
  3. Transparent Companion Account — Users never see it; assets always return to their wallet
  4. Automatic trigger detection — SDK chooses gasless permit or onchain tx based on token

Next Steps