Skip to content

๐Ÿ” EIP-7702 Gas Abstracted Transactions with Privy

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)

๐Ÿ”‘ Setup Imports and Constants

import {
  createWalletClient,
  http,
  custom,
  erc20Abi,
} from "viem";
import { optimism, base } from "viem/chains";
import {
  createMeeClient,
  toMultichainNexusAccount,
  runtimeERC20BalanceOf,
  greaterThanOrEqualTo,
} from "@biconomy/abstractjs";
import { useWallets, useSignAuthorization } from "@privy-io/react-auth";
 
const NEXUS_IMPLEMENTATION = "0x000000004F43C49e93C970E84001853a70923B03";
const USDC_ADDRESS = "0xUSDC..."; // Replace with actual USDC address

๐Ÿ‘ค 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),
});

โœ๏ธ 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,
});

๐Ÿงฑ Create Multichain Smart Account

const nexusAccount = await toMultichainNexusAccount({
  chains: [optimism, base],
  transports: [http(), http()],
  signer: walletClient.account,
  accountAddress: embeddedWallet.address,
});

๐ŸŒ Create MEE Client

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

๐Ÿ”„ 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)],
      }),
    ],
  },
});

๐Ÿš€ 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);

๐Ÿ“ฌ 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.