Skip to content

Sponsored Userop

This guide will help understanding how to broadcast a 7702 sponsored userop using smart wallet implemented here using free bundler client and a paymaster client.

1. Create Smart Account Client

import { commonClient } from './client'
import { toSimple7702SmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
const owner = privateKeyToAccount('0x...') // add private key here

export const smartAccount = await toSimple7702SmartAccount({
    client: commonClient,
    owner,
});
import { createFreeBundler } from '@etherspot/free-bundler'
import { publicActions, walletActions } from 'viem'
import { mainnet } from 'viem/chains'
const chain = mainnet

export const commonClient = createFreeBundler({chain})
                                .extend(publicActions)
                                .extend(walletActions)

2. Create Paymaster client

Refer here for more options and detailed docs for paymaster client

import { createPaymasterClient } from "viem/account-abstraction"

export const paymasterClient = createPaymasterClient({
    transport: http("") // add paymaster url here
});

// NOTE: this can change according to the paymaster implementation.
// use the corresponding paymaster's docs to understand what has to used for context.
export const paymasterContext = { policyId: "" } // add policy id here 

3. Send Userop

import { commonClient } from './client'
import { toSimple7702SmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { SignAuthorizationReturnType } from 'viem'
import { paymasterClient, paymasterContext } from './paymaster'

const owner = privateKeyToAccount('0x...') // add private key here

const smartAccount = await toSimple7702SmartAccount({
    client: commonClient,
    owner,
})

console.log("wallet:: ", smartAccount.address)

// check sender's code to decide if eip7702Auth tuple is necessary for userOp.
const senderCode = await commonClient.getCode({
    address: smartAccount.address
})

let authorization: SignAuthorizationReturnType | undefined
const { address: delegateAddress } = smartAccount.authorization

if(senderCode !== `0xef0100${delegateAddress.toLowerCase().substring(2)}`) {
    authorization = await commonClient.signAuthorization(smartAccount.authorization)
}

const userOpHash = await commonClient.sendUserOperation({
    account: smartAccount,
    authorization,
    calls: [
        {to: "0x09FD4F6088f2025427AB1e89257A44747081Ed59", value: parseUnits('0.0000001', 18)}
    ],
    paymaster: paymasterClient,
    paymasterContext: paymasterContext ? JSON.parse(paymasterContext) : undefined,
});

console.log('userOpHash:: ', userOpHash)
import { createFreeBundler } from '@etherspot/free-bundler'
import { publicActions, walletActions } from 'viem'
import { mainnet } from 'viem/chains'
const chain = mainnet

export const commonClient = createFreeBundler({chain})
                                .extend(publicActions)
                                .extend(walletActions)
import { createPaymasterClient } from "viem/account-abstraction"

export const paymasterClient = createPaymasterClient({
    transport: http("") // add paymaster url here
});

// NOTE: this can change according to the paymaster implementation.
// use the corresponding paymaster's docs to understand what has to used for context.
export const paymasterContext = { policyId: "" } // add policy id here 

Open in StackBlitz

After opening StackBlitz, run:

npx tsx examples/index.ts \
--private-key 0x... \
--paymaster-url 'https://....' \
--paymaster-context ''
For more run options, see the GitHub examples usage.