Skip to content
On this page

Build your own Client โ€‹

You can build your own viem Client by using the createClient function and optionally extending (.extend) it โ€“ this is how viem's internal Clients (Public, Wallet, and Test) are built.

Building your own Client is useful if you have specific requirements for how the Client should behave, and if you want to extend that Client with custom functionality (ie. create an EIP-4337 Bundler Client, or geth Debug Client).

The createClient function sets up a base viem Client with a given Transport configured with a Chain. After that, you can extend the Client with custom properties (that could be Actions or other configuration).

Import โ€‹

ts
import { createClient } from 'viem'

Usage โ€‹

Initialize a Client with your desired Chain (e.g. mainnet) and Transport (e.g. http).

ts
import { createClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createClient({ 
  chain: mainnet,
  transport: http()
})

Next, you can either extend your Client with Actions or configuration, or you can use it as-is for the purpose of maximizing tree-shaking in your app.

Extending with Actions or configuration โ€‹

You can extend your Client with custom Actions or configuration by using the .extend function.

Below is a naive implementation of implementing a geth Debug Client with a traceCall Action that uses the debug_traceCall RPC method.

ts
import { 
  createClient, 
  http,
  formatTransactionRequest,
  type CallParameters
} from 'viem'
import { mainnet } from 'viem/chains'

const debugClient = createClient({ 
  chain: mainnet,
  transport: http(),
}).extend(client => ({
  // ...
  async traceCall(args: CallParameters) {
    return client.request({
      method: 'debug_traceCall',
      params: [formatTransactionRequest(args), 'latest', {}]
    })
  },
  // ...
}))

const response = await debugClient.traceCall({
  from: '0xdeadbeef29292929192939494959594933929292',
  to: '0xde929f939d939d393f939393f93939f393929023',
  gas: 69420n,
  data: '0xf00d4b5d00000000000000000000000001291230982139282304923482304912923823920000000000000000000000001293123098123928310239129839291010293810'
})
// { failed: false, gas: 69420, returnValue: '...', structLogs: [] }

For a more succinct implementation of using .extend, check out viem's Public Client implementation extended with Public Actions.

Tree-shaking โ€‹

You can use the Client as-is, with no decorated Actions, to maximize tree-shaking in your app. This is useful if you are pedantic about bundle size and want to only include the Actions you use.

In the example below, instead of calling getBlock from the Public Client, we are importing the Action directly from viem and then injecting our Client as the first parameter to the Action.

ts
import { createClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { getBlock, sendTransaction } from 'viem/actions'

const client = createClient({ 
  chain: mainnet,
  transport: http()
})

const blockNumber = await getBlock(client, { blockTag: 'latest' })
const hash = await sendTransaction(client, { ... })

Parameters โ€‹

transport โ€‹

The Transport of the Public Client.

ts
const client = createClient({
  chain: mainnet,
  transport: http(), 
})

account (optional) โ€‹

  • Type: Account | Address

The Account to use for the Client. This will be used for Actions that require an account as an argument.

Accepts a JSON-RPC Account or Local Account (Private Key, etc).

ts
import { privateKeyToAccount } from 'viem/accounts'

const client = createClient({
  account: privateKeyToAccount('0x...') 
  chain: mainnet,
  transport: http(),
})

chain (optional) โ€‹

The Chain of the Public Client.

ts
const client = createClient({
  chain: mainnet, 
  transport: http(),
})

batch (optional) โ€‹

Flags for batch settings.

batch.multicall (optional) โ€‹

  • Type: boolean | MulticallBatchOptions
  • Default: false

Toggle to enable eth_call multicall aggregation.

ts
const client = createClient({
  batch: {
    multicall: true, 
  },
  chain: mainnet,
  transport: http(),
})

batch.multicall.batchSize (optional) โ€‹

  • Type: number
  • Default: 1_024

The maximum size (in bytes) for each multicall (aggregate3) calldata chunk.

Note: Some RPC Providers limit the amount of calldata that can be sent in a single request. It is best to check with your RPC Provider to see if there are any calldata size limits to eth_call requests.

ts
const client = createClient({
  batch: {
    multicall: {
      batchSize: 512, 
    },
  },
  chain: mainnet,
  transport: http(),
})

batch.multicall.wait (optional) โ€‹

The maximum number of milliseconds to wait before sending a batch.

ts
const client = createClient({
  batch: {
    multicall: {
      wait: 16, 
    },
  },
  chain: mainnet,
  transport: http(),
})

key (optional) โ€‹

  • Type: string
  • Default: "public"

A key for the Client.

ts
const client = createClient({
  chain: mainnet,
  key: 'public', 
  transport: http(),
})

name (optional) โ€‹

  • Type: string
  • Default: "Public Client"

A name for the Client.

ts
const client = createClient({
  chain: mainnet,
  name: 'Public Client', 
  transport: http(),
})

pollingInterval (optional) โ€‹

  • Type: number
  • Default: 4_000

Frequency (in ms) for polling enabled Actions.

ts
const client = createClient({
  chain: mainnet,
  pollingInterval: 10_000, 
  transport: http(),
})

Released under the MIT License.