๐ 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.