Skip to content

Contract Writes with EIP-7702

The guide below demonstrates how to perform Contract Writes with EIP-7702 to invoke Contract functions on an Externally Owned Account.

Overview

Here is an end-to-end overview of how to perform a Contract Write to send a batch of Calls. We will break it down into Steps below.

example.ts
import { parseEther } from 'viem'
import { walletClient } from './config'
import { abi, contractAddress } from './contract'
 
// 1. Authorize designation of the Contract onto the Account.
const authorization = await walletClient.signAuthorization({
  contractAddress,
})
 
// 2. Invoke the Contract's `execute` function to perform batch calls.
const hash = await walletClient.writeContract({
  abi,
  address: walletClient.account.address,
  functionName: 'execute',
  args: [[
    {
      data: '0x',
      to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', 
      value: parseEther('0.001'), 
    }, {
      data: '0x',
      to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', 
      value: parseEther('0.002'), 
    }
  ]],
  authorizationList: [authorization],
  //                  ↑ 3. Pass the Authorization as an option.
})

Steps

1. Set up Smart Contract

We will need to set up a Smart Contract to interact with. For the purposes of this guide, we will create and deploy a BatchCallDelegation.sol contract, however, you can use any existing deployed contract.

Firstly, deploy a Contract to the Network with the following source:

BatchCallDelegation.sol
pragma solidity ^0.8.20;
 
contract BatchCallDelegation {
  struct Call {
    bytes data;
    address to;
    uint256 value;
  }
 
  function execute(Call[] calldata calls) external payable {
    require(address(this) == msg.sender);
    for (uint256 i = 0; i < calls.length; i++) {
      Call memory call = calls[i];
      (bool success, ) = call.to.call{value: call.value}(call.data);
      require(success, "call reverted");
    }
  }
}

2. Set up Client & Account

Next, we will need to set up a Client and Externally Owned Account to sign EIP-7702 Authorizations.

config.ts
import { createWalletClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
 
export const account = privateKeyToAccount('0x...')
 
export const walletClient = createWalletClient({
  account,
  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 using the account attached to the walletClient 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 authorization = await walletClient.signAuthorization({ 
  contractAddress, 
}) 

4. Invoke Contract Function

We can now call the execute function on our Contract to perform batch calls.

example.ts
import { parseEther } from 'viem'
import { walletClient } from './config'
import { abi, contractAddress } from './contract'
 
const authorization = await walletClient.signAuthorization({
  contractAddress,
})
 
const hash = await walletClient.writeContract({ 
  abi, 
  address: walletClient.account.address, 
  functionName: 'execute', 
  args: [[ 
    { 
      data: '0x', 
      to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',  
      value: parseEther('0.001'),  
    }, { 
      data: '0x', 
      to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',  
      value: parseEther('0.002'),  
    } 
  ]], 
  authorizationList: [authorization], 
}) 

5. Optional: Use a Sponsor

We can also utilize an Sponsor Account to execute a call on behalf of the authorizing Account. This is useful for cases where we want to "sponsor" the Transaction for the user (i.e. pay for their gas fees).

example.ts
import { parseEther } from 'viem'
import { walletClient } from './config'
import { abi, contractAddress } from './contract'
 
const authorization = await walletClient.signAuthorization({
  contractAddress,
  sponsor: true, 
})
 
const sponsor = privateKeyToAccount('0x...') 
 
const hash = await walletClient.writeContract({
  account: sponsor, 
  abi,
  address: walletClient.account.address,
  functionName: 'execute',
  args: [[
    {
      data: '0x',
      to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', 
      value: parseEther('0.001'), 
    }, {
      data: '0x',
      to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', 
      value: parseEther('0.002'), 
    }
  ]],
  authorizationList: [authorization],
})