> For a complete page index, fetch https://docs.transak.com/llms.txt

# Biconomy MEE — Simplified Onboarding via Smart Accounts

Biconomy's **Modular Execution Environment (MEE)** lets you build seamless cross-chain experiences on top of Transak's fiat on-ramp — with no smart wallet setup required for users.

## EVM Account Types

Before diving into MEE, it helps to understand the two foundational account types on Ethereum:

<table>
  <tbody>
    <tr>
      <td>
        <strong>
          Externally Owned Accounts (EOAs)
        </strong>

        Controlled by a private key (e.g. MetaMask). Can send transactions and interact with contracts, but cannot execute complex logic or batch operations.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Contract Accounts (CAs)
        </strong>

        Smart contract code deployed on-chain. Can execute complex logic and interact with other contracts, but require deployment and cannot initiate transactions on their own.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          EIP-4337 Smart Accounts
        </strong>

        Combine the best of both worlds — smart contract functionality within a single wallet. Enable account abstraction across DeFi, GameFi, DAOs, and more.
      </td>
    </tr>
  </tbody>
</table>

## MEE: Beyond ERC-4337

Biconomy's MEE extends standard ERC-4337 to enable **true cross-chain composability** — with no extra setup for users.

<table>
  <tbody>
    <tr>
      <td>
        <strong>
          True Composability
        </strong>

        Dynamic execution where each step can reference outputs from previous steps.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Cross-Chain Orchestration
        </strong>

        A single signature authorizes complex flows spanning multiple chains simultaneously.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Universal Gas Abstraction
        </strong>

        Pay for gas on any chain using tokens from any other supported chain.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          EOA Wallet Support
        </strong>

        Works with standard wallets like MetaMask — no smart wallet deployment required.
      </td>
    </tr>
  </tbody>
</table>

## How MEE Works: Fusion Execution

MEE uses a **Fusion** execution model with four core components:

<table>
  <tbody>
    <tr>
      <td>
        <strong>
          Orchestrator
        </strong>

        A Companion Smart Account that represents the user's wallet. Invisible to users — handles batching, permissions, and fee payments.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          MEE Client
        </strong>

        Collects instructions, bundles them, and coordinates execution across chains.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Instructions
        </strong>

        Transaction objects created by dApps. Built using composable patterns that allow dynamic, multi-step execution.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Fee Token
        </strong>

        Specifies which token to use for gas, allowing users to pay fees with any supported token on any chain.
      </td>
    </tr>
  </tbody>
</table>

The user signs a single quote authorizing their Companion account to pull tokens and execute instructions.

The Companion executes the batched instructions, using the pulled tokens to pay for gas and perform the requested actions.

## Why Integrate MEE with Transak?

<table>
  <tbody>
    <tr>
      <td>
        <strong>
          Easy User Onboarding
        </strong>

        Works with existing EOA wallets — no smart wallet setup required for your users.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Fiat On-Ramp
        </strong>

        Let users buy crypto directly within your dApp via Transak's on-ramp.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Single Signature UX
        </strong>

        One signature covers complex multi-step and multi-chain operations.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Gasless Transactions
        </strong>

        Users pay transaction fees with any supported token — no native gas token required.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Chain Agnostic
        </strong>

        Natively orchestrates operations across chains with a single authorization.
      </td>
    </tr>

    <tr>
      <td>
        <strong>
          Custom Transaction Bundling
        </strong>

        Batch multiple actions across chains in one transaction (e.g. Approve + Deposit).
      </td>
    </tr>
  </tbody>
</table>

## Getting Started

You can start testing MEE without any API key. Add your Biconomy API key only when you

### Biconomy API Key (Production only)

Visit [dashboard.biconomy.io](https://dashboard.biconomy.io/) and create an account or log in.

Set up a new project and copy your API key for production use.

## Project Setup

```shell
bun create vite my-mee-app --template react-ts
cd my-mee-app
```

```shell
bun add viem wagmi @biconomy/abstractjs @tanstack/react-query
```

Create `src/wagmi.ts`:

```typescript
import { baseSepolia } from 'wagmi/chains';
import { createConfig, http } from 'wagmi';

export const config = createConfig({
  chains: [baseSepolia],
  transports: {
    [baseSepolia.id]: http()
  }
});
```

Edit `src/main.tsx`:

```typescript
import ReactDOM from 'react-dom/client';
import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { config } from './wagmi';
import App from './App';

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <WagmiProvider config={config}>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </WagmiProvider>
);
```

```shell
bun dev
```

You should see the Vite starter page — setup is complete.

## Implementing MEE in `App.tsx`

Open `src/App.tsx` and add the following imports:

```typescript
import { useState } from 'react';
import {
  createWalletClient,
  custom,
  erc20Abi,
  http,
  type Hex,
  formatUnits
} from 'viem';
import { baseSepolia } from 'viem/chains';
import {
  createMeeClient,
  toMultichainNexusAccount,
  getMeeScanLink,
  getMEEVersion,
  MEEVersion,
  type MeeClient,
  type MultichainSmartAccount
} from '@biconomy/abstractjs';
import { useReadContract } from 'wagmi';
```

Create a `connectAndInit` function that sets up the Orchestrator and MEE client:

```typescript
const usdcAddress = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';

const [account, setAccount] = useState<string | null>(null);
const [walletClient, setWalletClient] = useState<WalletClient | null>(null);
const [meeClient, setMeeClient] = useState<MeeClient | null>(null);
const [orchestrator, setOrchestrator] = useState<MultichainSmartAccount | null>(null);

const connectAndInit = async () => {
  if (typeof window.ethereum === 'undefined') {
    alert('MetaMask not detected');
    return;
  }

  const wallet = createWalletClient({
    chain: baseSepolia,
    transport: custom(window.ethereum)
  });
  setWalletClient(wallet);

  const [address] = await wallet.requestAddresses();
  setAccount(address);

  const multiAccount = await toMultichainNexusAccount({
    chainConfigurations: [
      {
        chain: baseSepolia,
        transport: http(),
        version: getMEEVersion(MEEVersion.V2_1_0)
      }
    ],
    signer: createWalletClient({
      account: address,
      transport: custom(window.ethereum)
    })
  });
  setOrchestrator(multiAccount);

  const mee = await createMeeClient({ account: multiAccount });
  setMeeClient(mee);
};
```

| Function                   | Purpose                                                   |
| -------------------------- | --------------------------------------------------------- |
| `toMultichainNexusAccount` | Creates the Orchestrator (Companion Smart Account)        |
| `chainConfigurations`      | Array of chains to support — add more for cross-chain ops |
| `getMEEVersion`            | Retrieves the correct MEE version for compatibility       |
| `createMeeClient`          | Creates the MEE client that coordinates execution         |

Create an `executeTransfers` function that builds composable instructions and executes them with a single signature:

```typescript
const executeTransfers = async () => {
  if (!orchestrator || !meeClient || !account) {
    alert('Account not initialized');
    return;
  }

  try {
    await walletClient?.switchChain({ id: baseSepolia.id });

    const recipients = [
      '0x322Af0da66D00be980C7aa006377FCaaEee3BDFD',
      '0x1234567890123456789012345678901234567890'
    ];

    // Build composable instructions for each recipient
    const transfers = await Promise.all(
      recipients.map((recipient) =>
        orchestrator.buildComposable({
          type: 'default',
          data: {
            abi: erc20Abi,
            chainId: baseSepolia.id,
            to: usdcAddress,
            functionName: 'transfer',
            args: [recipient as Hex, 1_000_000n] // 1 USDC (6 decimals)
          }
        })
      )
    );

    const totalAmount = BigInt(transfers.length) * 1_000_000n;

    // Get a Fusion quote with fee token specification
    const fusionQuote = await meeClient.getFusionQuote({
      instructions: transfers,
      trigger: {
        chainId: baseSepolia.id,
        tokenAddress: usdcAddress,
        amount: totalAmount
      },
      feeToken: {
        address: usdcAddress,
        chainId: baseSepolia.id
      }
    });

    // Execute with a single user signature
    const { hash } = await meeClient.executeFusionQuote({ fusionQuote });

    // Wait for on-chain confirmation
    const transactionDetail = await meeClient.waitForSupertransactionReceipt({ hash });

    console.log('Transaction completed!');
    console.log(getMeeScanLink(hash));
  } catch (error) {
    console.log(error);
  }
};
```

## Complete Code Sample

The full `App.tsx` below includes wallet connection, balance display, dynamic recipient management, live status updates, and a MEE Scan link.

```typescript
import { useState } from 'react';
import {
  createWalletClient,
  custom,
  erc20Abi,
  http,
  type WalletClient,
  type Hex,
  formatUnits
} from 'viem';
import { baseSepolia } from 'viem/chains';
import {
  createMeeClient,
  toMultichainNexusAccount,
  getMeeScanLink,
  getMEEVersion,
  MEEVersion,
  type MeeClient,
  type MultichainSmartAccount
} from '@biconomy/abstractjs';
import { useReadContract } from 'wagmi';

export default function App() {
  const [account, setAccount] = useState<string | null>(null);
  const [walletClient, setWalletClient] = useState<WalletClient | null>(null);
  const [meeClient, setMeeClient] = useState<MeeClient | null>(null);
  const [orchestrator, setOrchestrator] = useState<MultichainSmartAccount | null>(null);
  const [status, setStatus] = useState<string | null>(null);
  const [meeScanLink, setMeeScanLink] = useState<string | null>(null);
  const [recipients, setRecipients] = useState<string[]>(['']);

  const usdcAddress = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';

  const { data: balance } = useReadContract({
    abi: erc20Abi,
    address: usdcAddress,
    chainId: baseSepolia.id,
    functionName: 'balanceOf',
    args: account ? [account as Hex] : undefined,
    query: { enabled: !!account }
  });

  const connectAndInit = async () => {
    if (typeof (window as any).ethereum === 'undefined') {
      alert('MetaMask not detected');
      return;
    }

    const wallet = createWalletClient({
      chain: baseSepolia,
      transport: custom((window as any).ethereum)
    });
    setWalletClient(wallet);

    const [address] = await wallet.requestAddresses();
    setAccount(address);

    const multiAccount = await toMultichainNexusAccount({
      chainConfigurations: [
        {
          chain: baseSepolia,
          transport: http(),
          version: getMEEVersion(MEEVersion.V2_1_0)
        }
      ],
      signer: createWalletClient({
        account: address,
        transport: custom((window as any).ethereum)
      })
    });
    setOrchestrator(multiAccount);

    const mee = await createMeeClient({ account: multiAccount });
    setMeeClient(mee);
  };

  const executeTransfers = async () => {
    if (!orchestrator || !meeClient || !account) {
      alert('Account not initialized');
      return;
    }

    try {
      setStatus('Encoding instructions…');
      await walletClient?.switchChain({ id: baseSepolia.id });

      const transfers = await Promise.all(
        recipients
          .filter((r) => r)
          .map((recipient) =>
            orchestrator.buildComposable({
              type: 'default',
              data: {
                abi: erc20Abi,
                chainId: baseSepolia.id,
                to: usdcAddress,
                functionName: 'transfer',
                args: [recipient as Hex, 1_000_000n]
              }
            })
          )
      );

      const totalAmount = BigInt(transfers.length) * 1_000_000n;

      setStatus('Requesting quote…');
      const fusionQuote = await meeClient.getFusionQuote({
        instructions: transfers,
        trigger: {
          chainId: baseSepolia.id,
          tokenAddress: usdcAddress,
          amount: totalAmount
        },
        feeToken: {
          address: usdcAddress,
          chainId: baseSepolia.id
        }
      });

      setStatus('Please sign the transaction…');
      const { hash } = await meeClient.executeFusionQuote({ fusionQuote });

      const link = getMeeScanLink(hash);
      setMeeScanLink(link);
      setStatus('Waiting for completion…');

      await meeClient.waitForSupertransactionReceipt({ hash });
      setStatus('Transaction completed!');
    } catch (err: any) {
      console.error(err);
      setStatus(`Error: ${err.message ?? err}`);
    }
  };

  return (
    <main style={{ padding: 40, fontFamily: 'sans-serif' }}>
      <h1>Biconomy MEE Quickstart (Base Sepolia)</h1>

      <button onClick={connectAndInit} disabled={!!account}>
        {account ? 'Connected' : 'Connect Wallet'}
      </button>

      {account && (
        <div style={{ marginTop: 20 }}>
          <p><strong>Address:</strong> {account}</p>
          <p>USDC Balance: {balance ? `${formatUnits(balance, 6)} USDC` : '–'}</p>

          <h3>Recipients</h3>
          {recipients.map((recipient, idx) => (
            <input
              key={idx}
              type="text"
              value={recipient}
              onChange={(e) => {
                const updated = [...recipients];
                updated[idx] = e.target.value;
                setRecipients(updated);
              }}
              placeholder="0x..."
              style={{ display: 'block', margin: '8px 0', padding: '6px', width: '100%' }}
            />
          ))}

          <button onClick={() => setRecipients([...recipients, ''])}>
            Add Recipient
          </button>
        </div>
      )}

      {meeClient && (
        <>
          <p style={{ marginTop: 20 }}>
            <strong>MEE client ready</strong> – you can now orchestrate multichain transactions!
          </p>
          <button onClick={executeTransfers}>
            Send 1 USDC to each recipient
          </button>
        </>
      )}

      {status && <p style={{ marginTop: 20 }}>{status}</p>}

      {meeScanLink && (
        <p style={{ marginTop: 10 }}>
          <a href={meeScanLink} target="_blank" rel="noopener noreferrer">
            View on MEE Scan
          </a>
        </p>
      )}
    </main>
  );
}
```

```typescript
import { baseSepolia } from 'wagmi/chains';
import { createConfig, http } from 'wagmi';

export const config = createConfig({
  chains: [baseSepolia],
  transports: {
    [baseSepolia.id]: http()
  }
});
```

```typescript
import ReactDOM from 'react-dom/client';
import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { config } from './wagmi';
import App from './App';

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <WagmiProvider config={config}>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </WagmiProvider>
);
```

For the complete Biconomy MEE documentation, visit [docs.biconomy.io](https://docs.biconomy.io).