Sending User Operations
The guide below demonstrates how to send User Operations with a Smart Account.
Overview
Here is an end-to-end overview of how to broadcast a User Operation with a Smart Account. We will break it down into Steps below.
import { } from 'viem'
import { } from './config.js'
const = await .sendUserOperation({
,
: [{
: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
: ('0.001')
}]
})
const = await .waitForUserOperationReceipt({ }) Steps
1. Set up a Client
A Smart Account needs access to the Network to query for information about its state (e.g. nonce, address, etc). Let's set up a Client that we can use for the Smart Account:
import { , } from 'viem'
import { } from 'viem/chains'
const = ({
: ,
: (),
})2. Set up a Bundler Client
Next, we will need to set up a Bundler Client. A Bundler is required to submit User Operations to the Blockchain for the Smart Account.
import { , } from 'viem'
import { } from 'viem/account-abstraction' // [!code ++]
import { } from 'viem/chains'
const = ({
: ,
: (),
})
const = ({ // [!code ++]
, // [!code ++]
: ('https://public.pimlico.io/v2/1/rpc'), // [!code ++]
}) // [!code ++]3. Set up an Owner
We will also need to set up an Owner for the Smart Account which will be used to sign User Operations (transactions) for the Smart Account.
import { , } from 'viem'
import { } from 'viem/account-abstraction'
import { } from 'viem/chains'
import { } from 'viem/accounts' // [!code ++]
const = ({
: ,
: (),
})
const = ({
,
: ('https://public.pimlico.io/v2/1/rpc'),
})
const = ('0x...') // [!code ++]4. Create a Smart Account
Next, we will instantiate a Smart Account. For this example, we will use toCoinbaseSmartAccount (Coinbase Smart Wallet).
import { , } from 'viem'
import { // [!code ++]
, // [!code ++]
// [!code ++]
} from 'viem/account-abstraction' // [!code ++]
import { } from 'viem/chains'
import { } from 'viem/accounts'
const = ({
: ,
: (),
})
const = ({
,
: ('https://public.pimlico.io/v2/1/rpc'),
})
const = ('0x...')
const = await ({ // [!code ++]
, // [!code ++]
: [], // [!code ++]
: '1.1', // [!code ++]
}) // [!code ++]See toCoinbaseSmartAccount Docs
5. Send User Operation
Next, we will send a User Operation to the Bundler. For the example below, we will send 0.001 ETH to a random address.
import { , , } from 'viem'
import {
,
} from 'viem/account-abstraction'
import { } from 'viem/chains'
import { } from 'viem/accounts'
const = ({
: ,
: (),
})
const = ({
,
: ('https://public.pimlico.io/v2/1/rpc'),
})
const = ('0x...')
const = await ({
,
: [],
: '1.1',
})
const = await .sendUserOperation({ // [!code ++]
, // [!code ++]
: [{ // [!code ++]
: '0xcb98643b8786950F0461f3B0edf99D88F274574D', // [!code ++]
: ('0.001') // [!code ++]
}] // [!code ++]
}) // [!code ++]
const = await .waitForUserOperationReceipt({ }) // [!code ++]6. Optional: Hoist the Account
If you do not wish to pass an account around to every Action that requires an account, you can also hoist the account onto a Wallet Client.
import { , , } from 'viem'
import { , } from 'viem/account-abstraction'
import { } from 'viem/chains'
import { } from 'viem/accounts'
const = ({
: ,
: (),
})
const = ('0x...')
const = await ({
,
: [],
: '1.1',
})
const = ({
,
,
: ('https://public.pimlico.io/v2/1/rpc'),
})
const = await .sendUserOperation({
,
: [{
: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
: ('0.001')
}]
})7. Optional: Sponsor User Operation
By using a Paymaster, we can add sponsorship of User Operation fees.
Viem exposes a paymaster property on both the Bundler Client ("on Client" tab) and User Operation Action ("on Action" tab) to add User Operation sponsorship capabilities.
The paymaster property accepts a Paymaster Client (among others), which is used to fetch the necessary data for User Operation sponsorship.
import { } from 'viem'
import {
,
,
} from 'viem/account-abstraction'
import { , } from './config.js'
const = ({
: ('https://public.pimlico.io/v2/11155111/rpc'),
})
const = ({
,
,
: ,
: ('https://public.pimlico.io/v2/1/rpc'),
})
const = await .sendUserOperation({
: [{
: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
: parseEther('0.001')
}]
})