EIP-7702 Quickstart — Send a user operation from an EOA
This guide showcases a simple demo that uses ERC-4337 and EIP-7702 to send a sponsored user operation from a EOA. We will use SimpleSmartAccount as our smart account implementation of choice, other ERC-7702 compatible smart accounts will work as well.
For a high level overview of EIP-7702, checkout our EIP-7702 conceptual guide and for a more technical overview, please refer to the EIP-7702 proposal.
Installation
Make sure you have viem^2.28.0
and permissionless^0.2.24
installed.
npm install permissionless viem
Steps
Setup the smart account
The setup process follows the typical flow of sending a userOperation. The only difference is that when creating the Simple smart account instance, we set the sender address as our EOA's address.
import { createPublicClient, Hex, http } from "viem";
import { toSimple7702SmartAccount } from "viem/account-abstraction";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
// This is your EOA's private key
const privateKey = generatePrivateKey();
const eoa7702 = privateKeyToAccount(privateKey);
const client = createPublicClient({
chain: sepolia,
transport: http("https://sepolia.drpc.org"),
});
const simple7702Account = await toSimple7702SmartAccount({
client,
owner: eoa7702,
});
Setup paymaster (optional)
The paymaster client will be responsible for interacting with Pimlico's verifying paymaster endpoint and requesting sponsorship. Make sure to replace YOUR_PIMLICO_API_KEY in the code below with your actual Pimlico API key.
import { createPimlicoClient } from "permissionless/clients/pimlico";
const pimlicoAPIKey = "<YOUR_PIMLICO_API_KEY>";
const pimlicoClient = createPimlicoClient({
chain: sepolia,
transport: http(
`https://api.pimlico.io/v2/11155111/rpc?apikey=${pimlicoAPIKey}`,
),
});
Create Smart Account Client
A Smart Account Client is an almost drop-in replacement for a standard viem walletClient but for managing smart accounts instead of EOA accounts. In addition, a Smart Account Client also contains, depending on the underlying smart account implementation used, a few extra actions that are specific to smart accounts.
To create a Smart Account Client, use the createSmartAccountClient function.
import { createSmartAccountClient } from "permissionless";
import { zeroAddress } from "viem";
const smartAccountClient = createSmartAccountClient({
client,
chain: sepolia,
account: simple7702Account,
paymaster: pimlicoClient,
bundlerTransport: http(
`https://api.pimlico.io/v2/11155111/rpc?apikey=${pimlicoAPIKey}`,
),
});
Sending the transaction
Make sure you set the signed authorization for the first time when the EOA does not have the authorization code set.
const isSmartAccountDeployed = await smartAccountClient.account.isDeployed();
let transactionHash: Hex;
// We only have to add the authorization field if the EOA does not have the authorization code set
if (!isSmartAccountDeployed) {
transactionHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
value: 0n,
data: "0x",
authorization: await eoa7702.signAuthorization({
address: "0xe6Cae83BdE06E4c305530e199D7217f42808555B",
chainId: sepolia.id,
nonce: await client.getTransactionCount({
address: eoa7702.address,
}),
}),
});
} else {
transactionHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
value: 0n,
data: "0x",
});
}
Batch multiple transactions
The process is the same as sending a single transaction, the only difference is that we pass an array of calls to the sendTransaction function.
let transactionHash_2: Hex;
// We only have to add the authorization field if the EOA does not have the authorization code set
if (!isSmartAccountDeployed) {
transactionHash_2 = await smartAccountClient.sendTransaction({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
authorization: await eoa7702.signAuthorization({
address: "0xe6Cae83BdE06E4c305530e199D7217f42808555B",
chainId: sepolia.id,
nonce: await client.getTransactionCount({
address: eoa7702.address,
}),
}),
});
} else {
transactionHash_2 = await smartAccountClient.sendTransaction({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
});
}
Review
Congratulations! You have successfully sent a sponsored userOperation from your EOA, if you review the transaction on the blockchain explorer, you will see that the userOperation's sender address is equal to your EOA's address.