Skip to content

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.

example.ts
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:

Delegation.sol
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.

config.ts
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.

example.ts
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.

example.ts
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!

example.ts
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.

example.ts
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, 
})