Sending Transactions with EIP-7702
The guide below demonstrates how to send EIP-7702 Transactions to invoke Contract functions on an Externally Owned Account (EOA).
Overview
Here is an end-to-end overview of how to execute an EIP-7702 Transaction to emit a simple event on the EOA's designated contract. We will break it down into Steps below.
import { encodeFunctionData } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { walletClient } from './config'
import { abi, contractAddress } from './contract'
const eoa = privateKeyToAccount('0x...')
// 1. Authorize designation of the Contract onto the EOA.
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress,
})
// 2. Designate the Contract on the EOA, and invoke the
// `initialize` function.
const hash = await walletClient.sendTransaction({
authorizationList: [authorization],
// ↑ 3. Pass the Authorization as a parameter.
data: encodeFunctionData({
abi,
functionName: 'initialize',
}),
to: eoa.address,
})
Steps
1. Set up Smart Contract
We will need to set up a Smart Contract to designate on the Account. For the purposes of this guide, we will create and deploy a simple demonstration Delegation.sol
contract, however, you can use any existing deployed contract.
Firstly, deploy a Contract to the Network with the following source:
pragma solidity ^0.8.20;
contract Delegation {
event Log(string message);
function initialize() external payable {
emit Log('Hello, world!');
}
function ping() external pure {
emit Log('Pong!');
}
}
2. Set up Client & Account
Next, we will need to set up a Client and a "Relay Account" that will be responsible for executing the EIP-7702 Transaction.
import { createWalletClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
export const relay = privateKeyToAccount('0x...')
export const walletClient = createWalletClient({
account: relay,
chain: sepolia,
transport: http(),
})
3. Authorize Contract Designation
We will need to sign an Authorization to designate the Contract to the Account.
In the example below, we are instantiating an existing EOA (account
) and using it to sign the Authorization – this will be the Account that will be used for delegation.
import { walletClient } from './config'
import { contractAddress } from './contract'
const eoa = privateKeyToAccount('0x...')
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress,
})
4. Execute Transaction
We can now designate the Contract on the Account (and execute the initialize
function) by sending an EIP-7702 Transaction.
import { encodeFunctionData } from 'viem'
import { walletClient } from './config'
import { contractAddress } from './contract'
const eoa = privateKeyToAccount('0x...')
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress,
})
const hash = await walletClient.sendTransaction({
authorizationList: [authorization],
data: encodeFunctionData({
abi,
functionName: 'initialize',
}),
to: eoa.address,
})
5. (Optional) Interact with the Delegated Account
Now that we have designated a Contract onto the Account, we can interact with it by invoking its functions.
Note that we no longer need to use an Authorization!
import { encodeFunctionData } from 'viem'
import { walletClient } from './config'
const eoa = privateKeyToAccount('0x...')
const hash = await walletClient.sendTransaction({
data: encodeFunctionData({
abi,
functionName: 'ping',
}),
to: eoa.address,
})
Note: Self-executing EIP-7702
If the signer of the Authorization (ie. the EOA) is also executing the Transaction, you will need to pass executor: 'self'
to signAuthorization
.
This is because authorization.nonce
must be incremented by 1 over transaction.nonce
, so we will need to hint to signAuthorization
that this is the case.
import { encodeFunctionData } from 'viem'
import { walletClient } from './config'
import { contractAddress } from './contract'
const authorization = await walletClient.signAuthorization({
account: eoa,
contractAddress,
executor: 'self',
})
const hash = await walletClient.sendTransaction({
authorizationList: [authorization],
data: encodeFunctionData({
abi,
functionName: 'initialize',
}),
to: eoa.address,
to: walletClient.account.address,
})