This guide demonstrates how to integrate Privy with Biconomy’s AbstractJS to enable EIP-7702 gas abstracted transactions. You’ll learn how to:
  • Create smart accounts from embedded wallets
  • Sign EIP-7702 authorizations
  • Use runtime-injected parameters
  • Execute transactions across chains

1. Install dependencies

npm i @privy-io/react-auth @biconomy/abstractjs viem

2. Configure Privy

Ensure your app creates embedded wallets upon login. For a detailed guide on how to initialize Privy, follow their docs
<PrivyProvider
  appId={import.meta.env.VITE_PRIVY_APP_ID}
  config={{
    loginMethods: ["email"],
    embeddedWallets: {
      createOnLogin: true,
      noPromptOnSignature: true,
    },
  }}
>
  {children}
</PrivyProvider>

3. Full Flow Implementation (Step-by-Step)

1

Setup Imports and Constants

import {
  createWalletClient,
  http,
  custom,
  erc20Abi,
} from "viem";
import { optimism, base } from "viem/chains";
import {
  createMeeClient,
  toMultichainNexusAccount,
  runtimeERC20BalanceOf,
  greaterThanOrEqualTo,
  getMEEVersion,
  MEEVersion
} from "@biconomy/abstractjs";
import { useWallets, useSignAuthorization } from "@privy-io/react-auth";

const NEXUS_IMPLEMENTATION = "0x000000004F43C49e93C970E84001853a70923B03";
const USDC_ADDRESS = "0xUSDC..."; // Replace with actual USDC address
2

Access Embedded Wallet and Create Wallet Client

const { wallets } = useWallets();
const { signAuthorization } = useSignAuthorization();

const embeddedWallet = wallets?.[0];
if (!embeddedWallet) throw new Error("No embedded wallet found");

await embeddedWallet.switchChain(optimism.id);
const provider = await embeddedWallet.getEthereumProvider();

const walletClient = createWalletClient({
  account: embeddedWallet.address,
  chain: optimism,
  transport: custom(provider),
});
3

Sign EIP-7702 Authorization

In this step we will be signing the EIP-7702 authorization, effectivelly installing the code of the Biconomy Nexus smart account onto the address of our Privy EOA! Learn more about it here
const authorization = await signAuthorization({
  contractAddress: NEXUS_IMPLEMENTATION,
  chainId: 0,
});
4

Create Multichain Smart Account

const nexusAccount = await toMultichainNexusAccount({
  chainConfigurations: [
    {
      chain: optimism,
      transport: http(),
      version: getMEEVersion(MEEVersion.V2_1_0)
    },
    {
      chain: base,
      transport: http(),
      version: getMEEVersion(MEEVersion.V2_1_0)
    }
  ],
  signer: walletClient.account,
  accountAddress: embeddedWallet.address,
});
5

Create MEE Client

const meeClient = await createMeeClient({ account: nexusAccount });
6

Build Runtime-Injected Instruction

const runtimeInstruction = await nexusAccount.buildComposable({
  type: "default",
  data: {
    abi: erc20Abi,
    functionName: "transfer",
    chainId: optimism.id,
    to: USDC_ADDRESS,
    args: [
      embeddedWallet.address,
      runtimeERC20BalanceOf({
        targetAddress: nexusAccount.addressOn(optimism.id, true),
        tokenAddress: USDC_ADDRESS,
        constraints: [greaterThanOrEqualTo(1n)],
      }),
    ],
  },
});
7

Execute Gasless Composable Transaction

Learn More About EIP-7702 Authorizations
const { hash } = await meeClient.execute({

  // Must pass authorization and set delegate to
  // true when using EIP-7702 flows.
  authorization,
  delegate: true,


  // Gas paid with USDC on Optimism
  feeToken: {
    address: USDC_ADDRESS,
    chainId: optimism.id,
  },
  
  instructions: [runtimeInstruction],
});

console.log("Submitted tx hash:", hash);
8

Wait for Transaction Receipt

const receipt = await meeClient.waitForSupertransactionReceipt({ hash });
console.log("Tx complete:", receipt.hash);

Summary

This implementation demonstrates:
  • How to derive a smart account at the EOA address using EIP-7702
  • Inject dynamic values using runtimeERC20BalanceOf
  • Abstract gas fees using any ERC-20 token (USDC in this case)
  • Execute safe, constraint-driven transactions using MEE
Use this pattern as a base for more advanced orchestration or cross-chain strategies.