Git Repo
viem project: https://github.com/songb2/ts-eip7702
foundry project: https://github.com/songb2/eip7702
Introduction
EIP-7702 introduces account delegation on Ethereum, allowing externally owned accounts (EOAs) to delegate certain operations to smart contracts or relayers. This feature enables gas sponsorship, meta-transactions, and more flexible account management, improving developer experience and user onboarding.
In this article, we will explore the concept of EIP-7702, its practical applications, and provide a step-by-step hands-on tutorial using Viem and a local Anvil fork.
What is EIP-7702?
Traditionally, EOAs directly execute transactions and pay gas fees. With EIP-7702:
- EOAs can authorize a smart contract or relayer to act on their behalf.
- The relay account can pay gas, allowing the EOA to perform actions without holding ETH.
- Events emitted from delegated operations still appear under the original EOA, maintaining transparency.
This enables:
- Meta-transactions (users interact with dApps without owning ETH)
- Flexible account management in multi-sig or contract wallets
- Gasless onboarding for new users
Practical Use Cases
- Gasless Transactions
- Users can sign an authorization, and relayers execute the transaction on their behalf.
- Ideal for onboarding users who have no ETH in their wallets.
- Delegated Account Management
- EOAs can delegate certain smart contract interactions to trusted contracts.
- Useful in DeFi, gaming, or NFT minting platforms.
- Meta-Transactions in dApps
- Enable interactions without requiring users to pay gas.
- Can be combined with token-gated or subscription-based platforms.
Hands-on Tutorial: Using EIP-7702 Locally
Step 1: Create a foundry project and start a Local Anvil Fork
anvil --mnemonic "test test test test test test test test test test test junk" --fork-url https://reth-ethereum.ithaca.xyz/rpc
This creates a local Ethereum fork of the mainnet. You can use Anvil’s accounts as EOAs and relayers.
Step 2: Deploy a Sample Delegation Contract
https://github.com/songb2/eip7702/blob/master/src/Delegation.sol
// Delegation.sol
pragma solidity ^0.8.20;
contract Delegation {
event Log(string message);
function initialize() external payable {
emit Log('Hello, world!');
}
function ping() external {
emit Log('Pong!');
}
}
Deploy this contract on the local Anvil node.
Step 3: Create viem project and Configure Viem Clients
// config.ts
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
export const relay = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80')
// local customiezed fork chain (Chain ID = 1)
const anvilFork = {
id: 1,
name: 'Anvil Mainnet Fork',
network: 'anvil',
nativeCurrency: {
decimals: 18,
name: 'Ether',
symbol: 'ETH',
},
rpcUrls: {
default: { http: ['http://127.0.0.1:8545'] },
},
}
export const walletClient = createWalletClient({
account: relay,
chain: anvilFork,
transport: http('http://127.0.0.1:8545'),
})
// query state/event
export const publicClient = createPublicClient({
chain: anvilFork,
transport: http('http://127.0.0.1:8545'),
})
Step 4: Define contract abi
// contract.ts
export const abi = [
{
"type": "function",
"name": "initialize",
"stateMutability": "payable",
"inputs": [],
"outputs": []
},
{
"type": "function",
"name": "ping",
"stateMutability": "nonpayable",
"inputs": [],
"outputs": []
},
{
"type": "event",
"name": "Log",
"inputs": [{ "name": "message", "type": "string", "indexed": false }],
"anonymous": false
}
]
// Deployed EIP-7702 Contract Address on Anvil Mainnet Fork
export const contractAddress = '0xD946246695A9259F3B33a78629026F61B3Ab40aF'
Step 5: Delegate and Execute Transactions
// example.ts
import { privateKeyToAccount } from 'viem/accounts'
import { publicClient, walletClient } from './config'
import { abi, contractAddress } from './contract'
import { decodeEventLog } from 'viem'
// EOA to be delegated – private key from Anvil accounts, for example account[1] from Anvil
const eoa = privateKeyToAccount(
'0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'
)
async function main() {
// 1️⃣ Sign authorization (EOA → Delegation contract)
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress,
})
console.log('Authorization signed ✅')
// 2️⃣ Execute initialize() on Delegation via EOA
const initTxHash = await walletClient.writeContract({
abi,
address: eoa.address, // delegatee address
authorizationList: [authorization],
functionName: 'initialize',
})
console.log('Initialize Tx hash:', initTxHash)
// Wait for transaction receipt
const initReceipt = await publicClient.waitForTransactionReceipt({ hash: initTxHash })
console.log('Initialize Tx receipt:', initReceipt)
// Fetch and decode logs
const initLogs = await publicClient.getLogs({
address: eoa.address,
fromBlock: initReceipt.blockNumber,
toBlock: initReceipt.blockNumber,
})
const logEventAbi = abi.find(item => item.type === 'event' && item.name === 'Log')
if (!logEventAbi) throw new Error('Log event ABI not found')
console.log('--- Initialize() Decoded Logs ---')
for (const log of initLogs) {
const decoded = decodeEventLog({
abi: [logEventAbi],
data: log.data,
topics: log.topics,
})
console.log(decoded) // { message: "Hello, world!" }
}
// 3️⃣ Call ping() — no authorization required
const pingTxHash = await walletClient.writeContract({
abi,
address: eoa.address,
functionName: 'ping',
})
console.log('Ping Tx hash:', pingTxHash)
// Wait for ping transaction receipt
const pingReceipt = await publicClient.waitForTransactionReceipt({ hash: pingTxHash })
// Fetch and decode ping logs
const pingLogs = await publicClient.getLogs({
address: eoa.address,
fromBlock: pingReceipt.blockNumber,
toBlock: pingReceipt.blockNumber,
})
console.log('--- ping() Decoded Logs ---')
for (const log of pingLogs) {
const decoded = decodeEventLog({
abi: [logEventAbi],
data: log.data,
topics: log.topics,
})
console.log(decoded) // { message: "Pong!" }
}
}
main().catch(console.error)
Conclusion
EIP-7702 opens up new possibilities for gasless transactions, delegated operations, and better user onboarding in Ethereum dApps. Using Viem and a local Anvil fork, developers can experiment and integrate delegated accounts into their applications safely and efficiently.
💡 Tip: You can combine this with relayers or meta-transaction frameworks to build fully gasless dApps or enhanced UX for new Ethereum users.
Reference: https://viem.sh/docs/eip7702



