> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://v3.viem.sh/api/mcp` to find what you need.

# Sending User Operations

The guide below demonstrates how to send User Operations with a [Smart Account](/account-abstraction/accounts/smart).

## Overview

Here is an end-to-end overview of how to broadcast a User Operation with a Smart Account. The [recipe](#send-a-user-operation) below breaks down each part.

:::code-group
```ts twoslash [example.ts]
import { Value } from 'viem/utils'
import { account, bundlerClient } from './config.js'

const hash = await bundlerClient.userOperation.send({
  account,
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: Value.fromEther('0.001')
  }]
})

const receipt = await bundlerClient.userOperation.waitForReceipt({ hash })
```

```ts twoslash [config.ts] filename="config.ts"
import { Client, http } from 'viem'
import {
  BundlerClient,
  CoinbaseSmartAccount
} from 'viem/erc4337'
import { mainnet } from 'viem/chains'
import { Account } from 'viem'

const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const owner = Account.fromPrivateKey('0x...')

export const account = await CoinbaseSmartAccount.from({
  client,
  owners: [owner],
  version: '1.1',
})

export const bundlerClient = BundlerClient.create({
  account,
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})
```
:::

## Recipes

### Send a User Operation

#### 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:

```ts twoslash
import { Client, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = Client.create({
  chain: mainnet,
  transport: http(),
})
```

[See `Client.create` Docs](/docs/clients/create)

#### 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.

```ts twoslash
import { Client, http } from 'viem'
import { BundlerClient } from 'viem/erc4337' // [!code ++] // [!code focus]
import { mainnet } from 'viem/chains'

const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = BundlerClient.create({ // [!code ++] // [!code focus]
  client, // [!code ++] // [!code focus]
  transport: http('https://public.pimlico.io/v2/1/rpc'), // [!code ++] // [!code focus]
}) // [!code ++] // [!code focus]
```

:::info
The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::

[See `BundlerClient.create` Docs](/account-abstraction/clients/bundler)

#### 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.

```ts twoslash
import { Client, http } from 'viem'
import { BundlerClient } from 'viem/erc4337'
import { mainnet } from 'viem/chains'
import { Account } from 'viem' // [!code ++] // [!code focus]

const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = BundlerClient.create({
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const owner = Account.fromPrivateKey('0x...') // [!code ++] // [!code focus]
```

[See `Account.fromPrivateKey` Docs](/docs/accounts/local/private-key)

#### 4. Create a Smart Account

Next, we will instantiate a Smart Account. For this example, we will use [`CoinbaseSmartAccount.from`](/account-abstraction/accounts/coinbase) (Coinbase Smart Wallet).

```ts twoslash
import { Client, http } from 'viem'
import { // [!code ++] // [!code focus]
  BundlerClient, // [!code ++] // [!code focus]
  CoinbaseSmartAccount // [!code ++] // [!code focus]
} from 'viem/erc4337' // [!code ++] // [!code focus]
import { mainnet } from 'viem/chains'
import { Account } from 'viem'

const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = BundlerClient.create({
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const owner = Account.fromPrivateKey('0x...')

const account = await CoinbaseSmartAccount.from({ // [!code ++] // [!code focus]
  client, // [!code ++] // [!code focus]
  owners: [owner], // [!code ++] // [!code focus]
  version: '1.1', // [!code ++] // [!code focus]
}) // [!code ++] // [!code focus]
```

:::tip
**Tip:** `CoinbaseSmartAccount.from` also accepts [Passkey (WebAuthn) Accounts](/account-abstraction/accounts/webauthn) as an `owner`.
:::

[See `CoinbaseSmartAccount.from` Docs](/account-abstraction/accounts/coinbase)

#### 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.

```ts twoslash
import { Client, http } from 'viem'
import { Value } from 'viem/utils'
import {
  BundlerClient,
  CoinbaseSmartAccount
} from 'viem/erc4337'
import { mainnet } from 'viem/chains'
import { Account } from 'viem'

const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = BundlerClient.create({
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const owner = Account.fromPrivateKey('0x...')

const account = await CoinbaseSmartAccount.from({
  client,
  owners: [owner],
  version: '1.1',
})

const hash = await bundlerClient.userOperation.send({ // [!code ++] // [!code focus]
  account, // [!code ++] // [!code focus]
  calls: [{ // [!code ++] // [!code focus]
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', // [!code ++] // [!code focus]
    value: Value.fromEther('0.001') // [!code ++] // [!code focus]
  }] // [!code ++] // [!code focus]
}) // [!code ++] // [!code focus]

const receipt = await bundlerClient.userOperation.waitForReceipt({ hash }) // [!code ++] // [!code focus]
```

:::tip
**Tip:** The `calls` property also accepts [Contract Write calls](/account-abstraction/actions/userOperation.send#contract-calls).
:::

[See `userOperation.send` Docs](/account-abstraction/actions/userOperation.send)

#### 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 Bundler Client.

```ts twoslash
import { Client, http } from 'viem'
import { Value } from 'viem/utils'
import { BundlerClient, CoinbaseSmartAccount } from 'viem/erc4337'
import { mainnet } from 'viem/chains'
import { Account } from 'viem'

const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const owner = Account.fromPrivateKey('0x...')

const account = await CoinbaseSmartAccount.from({
  client,
  owners: [owner],
  version: '1.1',
})

const bundlerClient = BundlerClient.create({
  account, // [!code ++]
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.userOperation.send({
  account, // [!code --]
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: Value.fromEther('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](/account-abstraction/clients/paymaster) ([among others](/account-abstraction/actions/userOperation.send#paymaster-optional)), which is used to fetch the necessary data for User Operation sponsorship.

:::info
The example below uses [Pimlico's Paymaster API](https://docs.pimlico.io/infra/paymaster) – allowing consumers to sponsor gas fees for users on over 30+ chains.
:::

:::code-group
```ts twoslash [example.ts (on Client)]
import { http } from 'viem'
import { Value } from 'viem/utils'
import {
  BundlerClient,
  PaymasterClient,
} from 'viem/erc4337'
import { account, client } from './config'

const paymasterClient = PaymasterClient.create({ // [!code ++]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code ++]
}) // [!code ++]

const bundlerClient = BundlerClient.create({
  account,
  client,
  paymaster: paymasterClient, // [!code ++]
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.userOperation.send({
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: Value.fromEther('0.001')
  }]
})
```

```ts twoslash [example.ts (on Action)]
import { http } from 'viem'
import { Value } from 'viem/utils'
import {
  BundlerClient,
  PaymasterClient,
} from 'viem/erc4337'
import { account, client } from './config'

const paymasterClient = PaymasterClient.create({ // [!code ++]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code ++]
}) // [!code ++]

const bundlerClient = BundlerClient.create({
  account,
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.userOperation.send({
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: Value.fromEther('0.001')
  }],
  paymaster: paymasterClient, // [!code ++]
})
```

```ts twoslash [config.ts] filename="config.ts"
import { Account, Client, http } from 'viem'
import { CoinbaseSmartAccount } from 'viem/erc4337'
import { mainnet } from 'viem/chains'

export const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const owner = Account.fromPrivateKey('0x...')

export const account = await CoinbaseSmartAccount.from({
  client,
  owners: [owner],
  version: '1.1',
})
```
:::

::::tip
If your Bundler also supports Paymaster sponsorship (`pm_` JSON-RPC methods), you can set `paymaster: true` instead of declaring a separate Paymaster Client.

:::code-group
```ts twoslash [example.ts (on Client)]
import { http } from 'viem'
import { BundlerClient } from 'viem/erc4337'
import { account, client } from './config'

const bundlerClient = BundlerClient.create({
  account,
  client,
  paymaster: true, // [!code ++]
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})
```

```ts twoslash [example.ts (on Action)]
import { http } from 'viem'
import { Value } from 'viem/utils'
import { BundlerClient } from 'viem/erc4337'
import { account, client } from './config'

const bundlerClient = BundlerClient.create({
  account,
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.userOperation.send({
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: Value.fromEther('0.001')
  }],
  paymaster: true, // [!code ++]
})
```

```ts twoslash [config.ts] filename="config.ts"
import { Account, Client, http } from 'viem'
import { CoinbaseSmartAccount } from 'viem/erc4337'
import { mainnet } from 'viem/chains'

export const client = Client.create({
  chain: mainnet,
  transport: http(),
})

const owner = Account.fromPrivateKey('0x...')

export const account = await CoinbaseSmartAccount.from({
  client,
  owners: [owner],
  version: '1.1',
})
```
:::
::::

## Best Practices

Use authenticated Bundler and Paymaster endpoints in production. Hoist the Smart Account when one account is shared across Actions, and pass it per Action when account selection is dynamic.

## See More

* [Bundler Client](/account-abstraction/clients/bundler)
* [`userOperation.send`](/account-abstraction/actions/userOperation.send)
* [Paymaster Client](/account-abstraction/clients/paymaster)
