# Viem > Build reliable Ethereum apps & libraries with lightweight, composable, & type-safe modules from viem. ## Getting Started with Account Abstraction **[Account Abstraction (ERC-4337)](https://eips.ethereum.org/EIPS/eip-4337)** is a proposal within the Ethereum ecosystem which aims to standardize Smart Contract Accounts (SCA) and their operations without the need to modify or upgrade the protocol. Smart Contract Accounts can send calls on the Network via a "meta-transaction" called a **User Operation.** Users can send User Operations to **Bundlers** which aggregate User Operations into single transactions and submit them to the Network via an **EntryPoint** contract. Key features that Account Abstraction enables are: * **Batching:** Group multiple calls into a single transaction. * **Fee Sponsorship:** Allow third parties to pay for gas fees, or pay for gas via ERC20 tokens. * **Arbitrary Signature Verification:** Smart Contract Accounts can contain arbitrary signature verification logic (e.g. WebAuthn, secp256r1, secp256k1, ed25519, etc). * **Multi-Owner Wallets:** Enable multiple owners to control a single account, and set rules for the owners. * **Recovery Mechanisms:** A Smart Contract Account can assign multiple entities or services as trusted recovery agents for the Account. * and many more... :::note **Compatibility Note** As ERC-4337 is not enshrined on the protocol, this means that Smart Accounts are incompatible with Viem's Transaction APIs such as `sendTransaction` and `writeContract`. Sending "transactions" can be achieved by broadcasting a **User Operation** to a **Bundler**, which will then broadcast it to the Network shortly after. The most common Actions for **User Operations** are: * [`sendUserOperation`](/account-abstraction/actions/bundler/sendUserOperation) (also supports [Contract Writes](/account-abstraction/actions/bundler/sendUserOperation#contract-calls)) * [`estimateUserOperationGas`](/account-abstraction/actions/bundler/estimateUserOperationGas) * [`getUserOperation`](/account-abstraction/actions/bundler/getUserOperation) * [`getUserOperationReceipt`](/account-abstraction/actions/bundler/getUserOperationReceipt) Once Account Abstraction is enshrined on the protocol, we anticipate the above Actions will become redundant in favor of Viem's Transaction APIs. ::: ### Sending your first 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 // @noErrors import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ chain: mainnet, transport: http(), }) ``` [See `createPublicClient` Docs](/docs/clients/public) #### 2. Set up a Bundler Client Next, we need to set up a Bundler Client. A Bundler is required to submit User Operations to the Network for the Smart Account. ```ts twoslash import { createPublicClient, http } from 'viem' import { createBundlerClient } from 'viem/account-abstraction' // [!code focus] import { mainnet } from 'viem/chains' const client = createPublicClient({ chain: mainnet, transport: http(), }) const bundlerClient = createBundlerClient({ // [!code focus] client, // [!code focus] transport: http('https://public.pimlico.io/v2/1/rpc'), // [!code focus] }) // [!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 `createBundlerClient` Docs](/account-abstraction/clients/bundler) #### 3. Set up an Owner We 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 // @noErrors import { createPublicClient, http } from 'viem' import { createBundlerClient } from 'viem/account-abstraction' import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' // [!code focus] const client = createPublicClient({ chain: mainnet, transport: http(), }) const bundlerClient = createBundlerClient({ client, transport: http('https://public.pimlico.io/v2/1/rpc'), }) const owner = privateKeyToAccount('0x...') // [!code focus] ``` [See `privateKeyToAccount` Docs](/docs/accounts/local/privateKeyToAccount) #### 4. Create a Smart Account Next, we instantiate a Smart Account. For this example, we will use [`toCoinbaseSmartAccount`](/account-abstraction/accounts/smart/toCoinbaseSmartAccount) (Coinbase Smart Wallet). ```ts twoslash // @noErrors import { createPublicClient, http } from 'viem' import { // [!code focus] createBundlerClient, // [!code focus] toCoinbaseSmartAccount // [!code focus] } from 'viem/account-abstraction' // [!code focus] import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' const client = createPublicClient({ chain: mainnet, transport: http(), }) const bundlerClient = createBundlerClient({ client, transport: http('https://public.pimlico.io/v2/1/rpc'), }) const owner = privateKeyToAccount('0x...') const account = await toCoinbaseSmartAccount({ // [!code focus] client, // [!code focus] owners: [owner] // [!code focus] }) // [!code focus] ``` :::tip **Tip:** `toCoinbaseSmartAccount` also accepts [Passkey (WebAuthn) Accounts](/account-abstraction/accounts/webauthn) as an `owner`. ::: [See `toCoinbaseSmartAccount` Docs](/account-abstraction/accounts/smart/toCoinbaseSmartAccount) #### 5. Send User Operation Next, we send a User Operation to the Bundler. For the example below, we will send 0.001 ETH to a random address. ```ts twoslash import { createPublicClient, http, parseEther } from 'viem' import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction' import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' const client = createPublicClient({ chain: mainnet, transport: http(), }) const bundlerClient = createBundlerClient({ client, transport: http('https://public.pimlico.io/v2/1/rpc'), }) const owner = privateKeyToAccount('0x...') const account = await toCoinbaseSmartAccount({ client, owners: [owner] }) const hash = await bundlerClient.sendUserOperation({ // [!code focus] account, // [!code focus] calls: [{ // [!code focus] to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', // [!code focus] value: parseEther('0.001') // [!code focus] }] // [!code focus] }) // [!code focus] const receipt = await bundlerClient.waitForUserOperationReceipt({ hash }) // [!code focus] ``` :::tip **Tip:** The `calls` property also accepts [Contract Write calls](/account-abstraction/actions/bundler/sendUserOperation). ::: [See `sendUserOperation` Docs](/account-abstraction/actions/bundler/sendUserOperation) #### 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 { createPublicClient, http, parseEther } from 'viem' import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction' import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' const client = createPublicClient({ chain: mainnet, transport: http(), }) const owner = privateKeyToAccount('0x...') const account = await toCoinbaseSmartAccount({ client, owners: [owner] }) const bundlerClient = createBundlerClient({ account, // [!code ++] client, transport: http('https://public.pimlico.io/v2/1/rpc'), }) const hash = await bundlerClient.sendUserOperation({ account, // [!code --] calls: [{ to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', value: parseEther('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](#TODO)), 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 { createBundlerClient, createPaymasterClient, } from 'viem/account-abstraction' import { account, client } from './config.ts' const paymasterClient = createPaymasterClient({ // [!code ++] transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code ++] }) // [!code ++] const bundlerClient = createBundlerClient({ account, client, paymaster: paymasterClient, // [!code ++] transport: http('https://public.pimlico.io/v2/1/rpc'), }) const hash = await bundlerClient.sendUserOperation({ calls: [{ to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', value: parseEther('0.001') }] }) ``` ```ts twoslash [example.ts (on Action)] import { http } from 'viem' import { createBundlerClient, createPaymasterClient, } from 'viem/account-abstraction' import { account, client } from './config.ts' const paymasterClient = createPaymasterClient({ // [!code ++] transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code ++] }) // [!code ++] const bundlerClient = createBundlerClient({ account, client, transport: http('https://public.pimlico.io/v2/1/rpc'), }) const hash = await bundlerClient.sendUserOperation({ calls: [{ to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', value: parseEther('0.001') }] paymaster: paymasterClient, // [!code ++] }) ``` ```ts twoslash [config.ts] filename="config.ts" // @noErrors import { createPublicClient, http, parseEther } from 'viem' import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction' import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' export const client = createPublicClient({ chain: mainnet, transport: http(), }) const owner = privateKeyToAccount('0x...') export const account = await toCoinbaseSmartAccount({ client, owners: [owner] }) ``` ::: ::::tip If your Bundler also accepts Paymaster sponsorship (like [Pimlico](https://www.pimlico.io)), 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 { createBundlerClient, createPaymasterClient, } from 'viem/account-abstraction' import { account, client } from './config.ts' const paymasterClient = createPaymasterClient({ // [!code --] transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code --] }) // [!code --] const bundlerClient = createBundlerClient({ account, client, paymaster: paymasterClient, // [!code --] paymaster: true, // [!code ++] transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), }) ``` ```ts twoslash [example.ts (on Action)] import { http } from 'viem' import { createBundlerClient, createPaymasterClient, } from 'viem/account-abstraction' import { account, client } from './config.ts' const paymasterClient = createPaymasterClient({ // [!code --] transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code --] }) // [!code --] const bundlerClient = createBundlerClient({ account, client, transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), }) const hash = await bundlerClient.sendUserOperation({ calls: [{ to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', value: parseEther('0.001') }] paymaster: paymasterClient, // [!code --] paymaster: true, // [!code ++] }) ``` ```ts twoslash [config.ts] filename="config.ts" // @noErrors import { createPublicClient, http, parseEther } from 'viem' import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction' import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' export const client = createPublicClient({ chain: mainnet, transport: http(), }) const owner = privateKeyToAccount('0x...') export const account = await toCoinbaseSmartAccount({ client, owners: [owner] }) ``` ::: :::: ## Getting Started Viem provides a set of experimental features through the `viem/experimental` entrypoint. Such features could include: * implementation of non-finalized EIP proposals. * features that have not been adopted by the wider ecosystem. * features that are not considered stable. :::warning[Warning] It is recommended to not solely use experimental features in production. You should always have a fallback mechanism for unsupported features. ::: ### Quick Start #### 1. Set up your Client & Transport Firstly, set up your [Client](/docs/clients/intro) with a desired [Transport](/docs/clients/intro) & [Chain](/docs/chains/introduction). ```ts import { createWalletClient, custom } from 'viem' import { mainnet } from 'viem/chains' const client = createWalletClient({ // [!code focus] chain: mainnet, // [!code focus] transport: custom(window.ethereum!), // [!code focus] }) // [!code focus] ``` :::info In a production app, it is highly recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc). If no URL is provided, viem will default to a public RPC provider. [Read more](/docs/clients/transports/http#usage). ::: #### 2. Extend Client with Experimental Actions Now that you have a Client set up, you can extend it with Experimental Actions! [Read more.](/experimental/client) ```ts import { createWalletClient, custom } from 'viem' import { base } from 'viem/chains' import { eip5792Actions } from 'viem/experimental' // [!code focus] const client = createWalletClient({ chain: base, transport: custom(window.ethereum!), }).extend(eip5792Actions()) // [!code focus] ``` #### 3. Consume Experimental Actions Now that you have an Experimental Client set up, you can now and consume [Actions](/experimental/eip5792/sendCalls)! ```tsx import { createWalletClient, custom, parseEther } from 'viem' import { mainnet } from 'viem/chains' import { eip5792Actions } from 'viem/experimental' const client = createWalletClient({ chain: mainnet, transport: custom(window.ethereum!), }).extend(eip5792Actions()) const id = await client.sendCalls({ // [!code focus] account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] calls: [// [!code focus] {// [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus] value: parseEther('1'), // [!code focus] },// [!code focus] {// [!code focus] data: '0xdeadbeef'// [!code focus] to: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', // [!code focus] }, // [!code focus] ] // [!code focus] }) // [!code focus] ``` ## Getting Started with OP Stack Viem provides first-class support for chains implemented on the [OP Stack](https://docs.optimism.io/stack/getting-started). The OP Stack is a set of modular open-source software that enable developers to build fast, secure, and scalable Layer 2 Ethereum protocols & applications. [Read more.](https://docs.optimism.io/stack/getting-started) ### Quick Start #### 1. Set up your Client & Transport Firstly, set up your [Client](/docs/clients/intro) with a desired [Transport](/docs/clients/intro) & [OP Stack Chain](/op-stack/chains). ```ts import { createPublicClient, http } from 'viem' import { base } from 'viem/chains' const client = createPublicClient({ // [!code focus] chain: base, // [!code focus] transport: http(), // [!code focus] }) // [!code focus] ``` :::info In a production app, it is highly recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc). If no URL is provided, viem will default to a public RPC provider. [Read more](/docs/clients/transports/http#usage). ::: #### 2. Extend Client with the OP Stack Now that you have a Client set up, you can extend it with OP Stack Actions! [Read more.](/op-stack/client) ```ts import { createPublicClient, http } from 'viem' import { base } from 'viem/chains' import { publicActionsL2 } from 'viem/op-stack' // [!code hl] const client = createPublicClient({ chain: base, transport: http(), }).extend(publicActionsL2()) // [!code hl] ``` #### 3. Consume OP Stack Actions Now that you have an OP Stack Client set up, you can now interact with the OP Stack and consume [Actions](/op-stack/actions/estimateL1Gas)! ```tsx import { createPublicClient, http, parseEther } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ chain: mainnet, transport: http(), }).extend(publicActionsL2()) const l1Gas = await client.estimateL1Gas({ // [!code hl] account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code hl] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code hl] value: parseEther('1') // [!code hl] }) // [!code hl] ``` ## Getting Started with ZKsync :::warning **Note:** This extension is maintained by ZKsync collaborators. ::: Viem provides first-class support for the [ZKsync](https://zksync.io) chain. ZKsync is a Layer-2 protocol that scales Ethereum with cutting-edge ZK tech. ### Quick Start #### 1. Set up your Client & Transport Firstly, set up your [Client](/docs/clients/intro) with a desired [Transport](/docs/clients/intro) & [ZKsync Chain](./zksync/chains.md) and extend it with ZKsync EIP712 actions. ```ts twoslash import 'viem/window' // ---cut--- import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { eip712WalletActions } from 'viem/zksync' const walletClient = createWalletClient({ // [!code hl] chain: zksync, // [!code hl] transport: custom(window.ethereum!), // [!code hl] }).extend(eip712WalletActions()) // [!code hl] ``` #### 2. Use Actions Now that you have a Client set up, you can [send a transaction](./zksync/actions/sendTransaction.md) on ZKsync using a [paymaster](https://docs.zksync.io/build/developer-reference/account-abstraction.html#paymasters)! ```ts twoslash import 'viem/window' import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { eip712WalletActions } from 'viem/zksync' const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum!), }).extend(eip712WalletActions()) // ---cut--- const hash = await walletClient.sendTransaction({ account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n, paymaster: '0xFD9aE5ebB0F6656f4b77a0E99dCbc5138d54b0BA', paymasterInput: '0x123abc...' }) ``` ...and even write to contracts: ```ts twoslash import 'viem/window' import { createWalletClient, custom, parseAbi } from 'viem' import { zksync } from 'viem/chains' import { eip712WalletActions } from 'viem/zksync' const walletClient = createWalletClient({ account: '0x', chain: zksync, transport: custom(window.ethereum!), }).extend(eip712WalletActions()) // ---cut--- const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: parseAbi(['function mint(uint32 tokenId) nonpayable']), functionName: 'mint', args: [69420], }) ``` ## Platform Compatibility \[Platforms compatible with Viem] **Viem supports all modern browsers (Chrome, Edge, Firefox, etc) & runtime environments (Node 18+, Deno, Bun, etc).** Viem uses modern EcmaScript features such as: * [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) * [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) * Error [`cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) * TextEncoder [`encode`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encode) You can check support for these features on [Can I use...](https://caniuse.com/) ### Polyfills If your platform does not support one of the required features, it is also possible to import a polyfill. #### `fetch` * [isomorphic-unfetch](https://github.com/developit/unfetch/tree/main/packages/isomorphic-unfetch) * [node-fetch](https://github.com/node-fetch/node-fetch#providing-global-access) #### Error `cause` * [core-js](https://github.com/zloirock/core-js) #### `TextEncoder` * [FastestSmallestTextEncoderDecoder](https://github.com/anonyco/FastestSmallestTextEncoderDecoder) ## Error Handling Every module in viem exports an accompanying error type which you can use to strongly type your `catch` statements. These types come in the form of `ErrorType`. For example, the `getBlockNumber` action exports a `GetBlockNumberErrorType` type. Unfortunately, [TypeScript doesn't have an abstraction for typed exceptions](https://github.com/microsoft/TypeScript/issues/13219), so the most pragmatic & vanilla approach would be to explicitly cast error types in the `catch` statement. :::code-group ```ts [example.ts] twoslash // @noErrors // @filename: client.ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' export const client = createPublicClient({ chain: mainnet, transport: http() }) // @filename: index.ts // ---cut--- import { type GetBlockNumberErrorType } from 'viem' import { client } from './client' try { const blockNumber = await client.getBlockNumber() } catch (e) { const error = e as GetBlockNumberErrorType error.name // ^? if (error.name === 'InternalRpcError') error.code // ^? if (error.name === 'HttpRequestError') { error.headers // ^? error.status // ^? } } ``` ```ts [client.ts] import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' export const publicClient = createPublicClient({ chain: mainnet, transport: http() }) ``` ::: ## Ethers v5 → viem Migration Guide \[Migrate from Ethers v5 to viem] This is a long document. Feel free to use the search bar above (⌘ K) or the Table of Contents to the side. If there is an API you need which is missing or cannot find, create a [Parity Request here](https://github.com/wagmi-dev/viem/discussions/new?category=feature-request\&title=Parity%20Request:). You may notice some of the APIs in viem are a little more verbose than Ethers. We prefer boring code and we want to strongly embrace [clarity & composability](/docs/introduction#developer-experience). We believe that [verbose APIs are more flexible](https://www.youtube.com/watch?v=4anAwXYqLG8\&t=789s) to move, change and remove compared to code that is prematurely abstracted and hard to change. ### Provider → Client #### getDefaultProvider ##### Ethers ```ts import { getDefaultProvider } from 'ethers' const provider = getDefaultProvider() // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http() // [!code hl] }) // [!code hl] ``` > We are more verbose here – we want to be explicit and clear what chain you are connecting to & what transport you are using to avoid any confusion. :) #### JsonRpcProvider ##### Ethers This is also interchangeable with `StaticJsonRpcProvider`. ```ts import { providers } from 'ethers' const provider = new providers.JsonRpcProvider('https://cloudflare-eth.com') // [!code hl] ``` Custom Chain: ```ts import { providers } from 'ethers' const provider = new providers.JsonRpcProvider('https://rpc.ankr.com/fantom/​', { // [!code hl] name: 'Fantom', // [!code hl] id: 250 // [!code hl] }) // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http('https://cloudflare-eth.com') // [!code hl] }) // [!code hl] ``` Custom Chain: ```ts import { createPublicClient, http } from 'viem' import { fantom } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: fantom, // [!code hl] transport: http('https://rpc.ankr.com/fantom/​') // [!code hl] }) // [!code hl] ``` > viem exports custom EVM chains in the `viem/chains` entrypoint. #### InfuraProvider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.InfuraProvider('homestead', '') // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http('https://mainnet.infura.io/v3/') // [!code hl] }) // [!code hl] ``` > viem does not have custom API Provider clients – you can just pass in their RPC URL. #### AlchemyProvider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.AlchemyProvider('homestead', '') // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http('https://eth-mainnet.g.alchemy.com/v2/') // [!code hl] }) // [!code hl] ``` > viem does not have custom API Provider clients – you can just pass in their RPC URL. #### CloudflareProvider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.CloudflareProvider() // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http('https://cloudflare-eth.com/') // [!code hl] }) // [!code hl] ``` > viem does not have custom API Provider clients – you can just pass in their RPC URL. #### PocketProvider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.PocketProvider('homestead', '') // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http('https://eth-mainnet.gateway.pokt.network/v1/lb/') // [!code hl] }) // [!code hl] ``` > viem does not have custom API Provider clients – you can just pass in their RPC URL. #### AnkrProvider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.AnkrProvider('homestead', '') // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: http('https://rpc.ankr.com/eth/') // [!code hl] }) // [!code hl] ``` > viem does not have custom API Provider clients – you can just pass in their RPC URL. #### FallbackProvider ##### Ethers ```ts import { providers } from 'ethers' const alchemy = new providers.AlchemyProvider('homestead', '') // [!code hl] const infura = new providers.InfuraProvider('homestead', '') // [!code hl] const provider = new providers.FallbackProvider([alchemy, infura]) // [!code hl] ``` ##### viem ```ts import { createPublicClient, http, fallback } from 'viem' import { mainnet } from 'viem/chains' const alchemy = http('https://eth-mainnet.g.alchemy.com/v2/') // [!code hl] const infura = http('https://mainnet.infura.io/v3/') // [!code hl] const client = createPublicClient({ chain: mainnet, transport: fallback([alchemy, infura]) // [!code hl] }) ``` #### IpcProvider Coming soon. #### JsonRpcBatchProvider Coming soon. #### Web3Provider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.Web3Provider(window.ethereum) // [!code hl] ``` ##### viem ```ts import { createWalletClient, custom } from 'viem' import { mainnet } from 'viem/chains' const client = createWalletClient({ // [!code hl] chain: mainnet, // [!code hl] transport: custom(window.ethereum) // [!code hl] }) // [!code hl] ``` #### WebSocketProvider ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.WebSocketProvider('wss://eth-mainnet.g.alchemy.com/v2/') // [!code hl] ``` ##### viem ```ts import { createPublicClient, webSocket } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code hl] chain: mainnet, // [!code hl] transport: webSocket('wss://eth-mainnet.g.alchemy.com/v2/') // [!code hl] }) // [!code hl] ``` ### Signers → Accounts #### JsonRpcSigner ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.Web3Provider(window.ethereum) const [address] = await provider.listAccounts() // [!code hl] const signer = provider.getSigner(address) // [!code hl] signer.sendTransaction({ ... }) ``` ##### viem ```ts import { createWalletClient, custom } from 'viem' import { mainnet } from 'viem/chains' const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) // [!code hl] const client = createWalletClient({ account, // [!code hl] chain: mainnet, transport: custom(window.ethereum) }) client.sendTransaction({ ... }) ``` > viem uses the term ["Account"](https://ethereum.org/en/developers/docs/accounts/) rather than "Signer". #### Wallet ##### Ethers ```ts import { providers, Wallet } from 'ethers' const provider = new providers.Web3Provider(window.ethereum) const wallet = new Wallet('0x...', provider) // [!code hl] wallet.sendTransaction({ ... }) ``` ##### viem ```ts import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { mainnet } from 'viem/chains' const account = privateKeyToAccount('0x...') // [!code hl] const client = createWalletClient({ account, // [!code hl] chain: mainnet, transport: custom(window.ethereum) }) client.sendTransaction({ ... }) ``` > viem uses the term ["Account"](https://ethereum.org/en/developers/docs/accounts/) rather than "Signer". ### Provider Methods ##### Ethers ```ts import { getDefaultProvider } from 'ethers' const provider = getDefaultProvider() provider.getBlock(...) // [!code hl] provider.getTransaction(...) // [!code hl] ... ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ chain: mainnet, transport: http() }) client.getBlock(...) // [!code hl] client.getTransaction(...) // [!code hl] ... ``` > Methods that extend off the Public Client are **Public Actions**. [Read more](/docs/actions/public/introduction). > There are API differences in all of these methods. Use the search bar at the top of the page to learn more about them. ### Signer Methods #### JsonRpcSigner ##### Ethers ```ts import { providers } from 'ethers' const provider = new providers.Web3Provider(window.ethereum) const [address] = await provider.listAccounts() const signer = provider.getSigner(address) signer.sendTransaction(...) // [!code hl] signer.signMessage(...) // [!code hl] ... ``` ##### viem ```ts import { createWalletClient, custom } from 'viem' import { mainnet } from 'viem/chains' const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) const client = createWalletClient({ account, chain: mainnet, transport: custom(window.ethereum) }) client.sendTransaction({ ... }) // [!code hl] client.signMessage({ ... }) // [!code hl] ... ``` > Methods that extend off the Wallet Client are **Wallet Actions**. [Read more](/docs/actions/wallet/introduction). > There are API differences in all of these methods. Use the search bar at the top of the page to learn more about them. ### Contract Interaction #### Reading from Contracts ##### Ethers ```ts import { getDefaultProvider } from 'ethers' import { wagmiContractConfig } from './abi' const provider = getDefaultProvider() const { abi, address } = wagmiContractConfig // [!code hl] const contract = new Contract(address, abi, provider) // [!code hl] const supply = await contract.totalSupply() // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { wagmiContractConfig } from './abi' const client = createPublicClient({ chain: mainnet, transport: http() }) const supply = await client.readContract({ // [!code hl] ...wagmiContractConfig, // [!code hl] functionName: 'totalSupply' // [!code hl] }) // [!code hl] ``` #### Writing to Contracts ##### Ethers ```ts import { Contract, providers } from 'ethers' import { wagmiContractConfig } from './abi' const provider = new providers.Web3Provider(window.ethereum) const [address] = await provider.listAccounts() const signer = provider.getSigner(address) const { abi, address } = wagmiContractConfig // [!code hl] const contract = new Contract(address, abi, signer) // [!code hl] const hash = await contract.mint() // [!code hl] ``` ##### viem ```ts import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet } from 'viem/chains' import { wagmiContractConfig } from './abi' const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) }) const [address] = await walletClient.getAddresses() const hash = await walletClient.writeContract({ // [!code hl] ...wagmiContractConfig, // [!code hl] functionName: 'mint', // [!code hl] account: address, // [!code hl] }) // [!code hl] ``` #### Deploying Contracts ##### Ethers ```ts import { ContractFactory, providers } from 'ethers' import { abi, bytecode } from './abi' const provider = new providers.Web3Provider(window.ethereum) const [address] = await provider.listAccounts() const signer = provider.getSigner(address) const contract = new ContractFactory(abi, bytecode, signer) // [!code hl] await contract.deploy() // [!code hl] ``` ##### viem ```ts import { createWalletClient, http } from 'viem' import { mainnet } from 'viem/chains' import { abi, bytecode } from './abi' const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) }) const [address] = await walletClient.getAddresses() await walletClient.deployContract({ // [!code hl] abi, // [!code hl] account: address, // [!code hl] bytecode, // [!code hl] }) // [!code hl] ``` #### Contract Events ##### Ethers ```ts import { getDefaultProvider } from 'ethers' import { wagmiContractConfig } from './abi' const provider = getDefaultProvider() const { abi, address } = wagmiContractConfig // [!code hl] const contract = new Contract(address, abi, provider) // [!code hl] const listener = (from, to, amount, event) => { // [!code hl] // ... // [!code hl] } // [!code hl] contract.on('Transfer', listener) // [!code hl] // unsubscribe // [!code hl] contract.off('Transfer', listener) // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { wagmiContractConfig } from './abi' const client = createPublicClient({ chain: mainnet, transport: http() }) const unwatch = client.watchContractEvent({ // [!code hl] ...wagmiContractConfig, // [!code hl] eventName: 'Transfer', // [!code hl] onLogs: logs => { // [!code hl] const { args: { from, to, amount }, eventName } = logs[0] // [!code hl] // ... // [!code hl] }, // [!code hl] }) // [!code hl] // unsubscribe // [!code hl] unwatch() // [!code hl] ``` > Note: Logs are batched between polling intervals in viem to avoid excessive callback invocations. You can disable this behavior with `batch: false` however. #### Gas Estimation ##### Ethers ```ts import { getDefaultProvider } from 'ethers' import { wagmiContractConfig } from './abi' const provider = getDefaultProvider() const { abi, address } = wagmiContractConfig // [!code hl] const contract = new Contract(address, abi, provider) // [!code hl] const gas = await contract.estimateGas.mint() // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { wagmiContractConfig } from './abi' const client = createPublicClient({ chain: mainnet, transport: http() }) const gas = await client.estimateContractGas({ // [!code hl] ...wagmiContractConfig, // [!code hl] functionName: 'mint' // [!code hl] }) // [!code hl] ``` #### Call ##### Ethers ```ts import { getDefaultProvider } from 'ethers' import { wagmiContractConfig } from './abi' const provider = getDefaultProvider() const { abi, address } = wagmiContractConfig // [!code hl] const contract = new Contract(address, abi, provider) // [!code hl] await contract.callStatic.mint() // [!code hl] ``` ##### viem ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { wagmiContractConfig } from './abi' const client = createPublicClient({ chain: mainnet, transport: http() }) await client.simulateContract({ // [!code hl] ...wagmiContractConfig, // [!code hl] functionName: 'mint' // [!code hl] }) // [!code hl] ``` #### Contract Instances ##### Ethers ```ts import { getDefaultProvider } from 'ethers' import { wagmiContractConfig } from './abi' const provider = getDefaultProvider() const { abi, address } = wagmiContractConfig // [!code hl] const contract = new Contract(address, abi, provider) // [!code hl] const supply = await contract.totalSupply() const listener = (from, to, amount, event) => { // ... } contract.on('Transfer', listener) contract.off('Transfer', listener) ``` ##### viem ```ts import { createPublicClient, http, getContract } from 'viem' import { mainnet } from 'viem/chains' import { wagmiContractConfig } from './abi' const client = createPublicClient({ chain: mainnet, transport: http() }) const contract = getContract({ // [!code hl] ...wagmiContractConfig, // [!code hl] client, // [!code hl] }) // [!code hl] const supply = await contract.read.totalSupply() const unwatch = contract.watchEvent.Transfer({ onLogs: logs => { const { args: { from, to, amount }, eventName } = logs[0] // ... }, }) unwatch() ``` ### ABI Utilities #### abiCoder.encode ##### Ethers ```ts import { utils } from 'ethers' const abiCoder = utils.defaultAbiCoder() // Object abiCoder.encode( [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }], [1234, 'Hello world'] ) // Human Readable abiCoder.encode( ['uint', 'string'], [1234, 'Hello World'] ); ``` ##### viem ```ts import { encodeAbiParameters, parseAbiParameters } from 'viem' // Object encodeAbiParameters( [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }], [1234, 'Hello world'] ) // Human Readable encodeAbiParameters( parseAbiParameters('uint, string'), [1234, 'Hello world'] ) ``` #### abiCoder.decode ##### Ethers ```ts import { utils } from 'ethers' const abiCoder = utils.defaultAbiCoder() // Object abiCoder.decode( [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }], '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000' ) // Human Readable abiCoder.decode( ['uint', 'string'], '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000' ); ``` ##### viem ```ts import { decodeAbiParameters, parseAbiParameters } from 'viem' // Object decodeAbiParameters( [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }], '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000' ) // Human Readable decodeAbiParameters( parseAbiParameters('uint, string'), '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000' ) ``` Notice: different from ethers, viem only supports [standard tuple expression](https://docs.soliditylang.org/en/latest/grammar#a4.SolidityParser.tupleExpression) for Human Readable. example: `(uint a, string b)` is valid, but `tuple(uint a, string b)` is not. #### Fragments & Interfaces In viem, there is no concept of "fragments" & "interfaces". We want to stick as close to the wire as possible and not introduce middleware abstractions and extra layers over ABIs. Instead of working with "fragments", we encourage you to work with the ABI itself. We provide utilities such as `getAbiItem`, `parseAbi` `parseAbiItem`, `parseAbiParameters` and `parseAbiParameter` which covers the use cases of interfaces & fragments. #### Interface.format viem only supports Human Readable → Object format. ##### Ethers ```ts import { utils } from 'ethers' const interface = new Interface([ // [!code hl] 'constructor(string symbol, string name)', // [!code hl] 'function transferFrom(address from, address to, uint amount)', // [!code hl] 'function transferFrom(address from, address to, uint amount, bool x)', // [!code hl] 'function mint(uint amount) payable', // [!code hl] 'function balanceOf(address owner) view returns (uint)' // [!code hl] ]) // [!code hl] const json = interface.format(utils.FormatTypes.json) // [!code hl] ``` ##### viem ```ts import { parseAbi } from 'viem' const json = parseAbi([ // [!code hl] 'constructor(string symbol, string name)', // [!code hl] 'function transferFrom(address from, address to, uint amount)', // [!code hl] 'function transferFrom(address from, address to, uint amount, bool x)', // [!code hl] 'function mint(uint amount) payable', // [!code hl] 'function balanceOf(address owner) view returns (uint)', // [!code hl] 'event Transfer(address indexed from, address indexed to, uint256 amount)' // [!code hl] ]) // [!code hl] ``` #### Fragment.from ##### ethers ```ts import { utils } from 'ethers' const fragment = utils.Fragment.from('function balanceOf(address owner) view returns (uint)') // [!code hl] ``` ##### viem ```ts import { parseAbiItem } from 'viem' const abiItem = parseAbiItem('function balanceOf(address owner) view returns (uint)') // [!code hl] ``` #### ParamType.from ##### ethers ```ts import { utils } from 'ethers' const param = utils.ParamType.from('address owner') // [!code hl] ``` ##### viem ```ts import { parseAbiParameter } from 'viem' const param = parseAbiParameter('address owner') // [!code hl] ``` #### Fragment Access ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const interface = new utils.Interface(abi) // [!code hl] interface.getFunction('transferFrom') // [!code hl] interface.getEvent('Transfer') // [!code hl] ``` ##### viem ```ts import { getAbiItem } from 'viem' import { abi } from './abi' getAbiItem({ abi, name: 'transferFrom' }) // [!code hl] getAbiItem({ abi, name: 'Transfer' }) // [!code hl] ``` #### Interface.encodeDeploy ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const data = iface.encodeDeploy(['SYM', 'Some Name']) // [!code hl] ``` ##### viem ```ts import { encodeDeployData } from 'viem' import { abi, bytecode } from './abi' const data = encodeDeployData({ // [!code hl] abi, // [!code hl] bytecode, // [!code hl] args: ['SYM', 'Some Name'] // [!code hl] }) // [!code hl] ``` > Note: viem concatenates the contract bytecode onto the ABI encoded data. #### Interface.encodeErrorResult ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const data = iface.encodeErrorResult('AccountLocked', [ // [!code hl] '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // [!code hl] utils.parseEther('1.0') // [!code hl] ]); // [!code hl] ``` ##### viem ```ts import { encodeErrorResult, parseEther } from 'viem' import { abi } from './abi' const data = encodeErrorResult({ // [!code hl] abi: wagmiAbi, // [!code hl] errorName: 'AccountLocked', // [!code hl] args: [ // [!code hl] '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // [!code hl] parseEther('1.0') // [!code hl] ] // [!code hl] }) // [!code hl] ``` #### Interface.encodeFilterTopics ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const data = iface.encodeFilterTopics('Transfer', [ // [!code hl] null, // [!code hl] '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // [!code hl] ]) // [!code hl] ``` ##### viem ```ts import { encodeEventTopics } from 'viem' import { abi } from './abi' const data = encodeEventTopics({ // [!code hl] abi, // [!code hl] eventName: 'Transfer', // [!code hl] args: { // [!code hl] to: '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // [!code hl] } // [!code hl] }) // [!code hl] ``` #### Interface.encodeFunctionData ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const data = iface.encodeFunctionData('transferFrom', [ // [!code hl] '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // [!code hl] '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C', // [!code hl] parseEther('1.0') // [!code hl] ]) // [!code hl] ``` ##### viem ```ts import { encodeFunctionData, parseEther } from 'viem' import { abi } from './abi' const data = encodeFunctionData({ // [!code hl] abi, // [!code hl] functionName: 'transferFrom', // [!code hl] args: [ // [!code hl] '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // [!code hl] '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C', // [!code hl] parseEther('1.0') // [!code hl] ] // [!code hl] }) // [!code hl] ``` #### Interface.encodeFunctionResult ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const data = iface.encodeFunctionResult('balanceOf', [ // [!code hl] '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // [!code hl] ]) // [!code hl] ``` ##### viem ```ts import { encodeFunctionResult, parseEther } from 'viem' import { abi } from './abi' const data = encodeFunctionResult({ // [!code hl] abi, // [!code hl] functionName: 'balanceOf', // [!code hl] value: ['0x8ba1f109551bD432803012645Ac136ddd64DBA72'] // [!code hl] }) // [!code hl] ``` #### Interface.decodeErrorResult ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const result = iface.decodeErrorResult("AccountLocked", '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000') // [!code hl] ``` ##### viem ```ts import { decodeErrorResult, parseEther } from 'viem' import { abi } from './abi' const result = decodeErrorResult({ // [!code hl] abi, // [!code hl] data: '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000' // [!code hl] }) // [!code hl] ``` #### Interface.decodeEventLog ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const result = iface.decodeEventLog( // [!code hl] 'Transfer', // [!code hl] data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000', // [!code hl] topics: [ // [!code hl] '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // [!code hl] '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72', // [!code hl] '0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c' // [!code hl] ] // [!code hl] ); // [!code hl] ``` ##### viem ```ts import { decodeEventLog, parseEther } from 'viem' import { abi } from './abi' const result = decodeEventLog({ // [!code hl] abi, // [!code hl] data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000', // [!code hl] topics: [ // [!code hl] '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // [!code hl] '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72', // [!code hl] '0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c' // [!code hl] ] // [!code hl] }) // [!code hl] ``` #### Interface.decodeFunctionData ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const result = iface.decodeFunctionData('transferFrom', '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000'); // [!code hl] ``` ##### viem ```ts import { decodeFunctionData, parseEther } from 'viem' import { abi } from './abi' const result = decodeFunctionData({ // [!code hl] abi, // [!code hl] data: '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000', // [!code hl] }) // [!code hl] ``` #### Interface.decodeFunctionResult ##### Ethers ```ts import { utils } from 'ethers' import { abi } from './abi' const iface = new utils.Interface(abi); // [!code hl] const result = iface.decodeFunctionResult('balanceOf', '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000'); // [!code hl] ``` ##### viem ```ts import { decodeFunctionResult, parseEther } from 'viem' import { abi } from './abi' const result = decodeFunctionResult({ // [!code hl] abi, // [!code hl] functionName: 'balanceOf', // [!code hl] data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000', // [!code hl] }) // [!code hl] ``` #### Interface.getSighash ##### Ethers ```ts import { Interface, FunctionFragment } from '@ethersproject/abi'; const hash = Interface.getSighash(FunctionFragment.from('function ownerOf(uint256)')); // [!code hl] ``` ##### viem ```ts import { toFunctionHash } from 'viem' const hash = toFunctionHash('function ownerOf(uint256)') // [!code hl] ``` ### Address Utilities #### getAddress ##### Ethers ```ts import { utils } from 'ethers' const address = utils.getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72') // [!code hl] ``` ##### viem ```ts import { getAddress } from 'viem' const address = getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72') // [!code hl] ``` #### isAddress ##### Ethers ```ts import { utils } from 'ethers' const address = utils.isAddress('0x8ba1f109551bd432803012645ac136ddd64dba72') // [!code hl] ``` ##### viem ```ts import { isAddress } from 'viem' const address = isAddress('0x8ba1f109551bd432803012645ac136ddd64dba72') // [!code hl] ``` #### getContractAddress ##### Ethers ```ts import { utils } from 'ethers' const address = utils.getContractAddress({ from: '0x...', nonce: 5 }); // [!code hl] ``` ##### viem ```ts import { getContractAddress } from 'viem' const address = getContractAddress({ from: '0x...', nonce: 5 }) // [!code hl] ``` #### getCreate2Address ##### Ethers ```ts import { utils } from 'ethers' const from = '0x8ba1f109551bD432803012645Ac136ddd64DBA72'; // [!code hl] const salt = '0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331'; // [!code hl] const initCode = '0x6394198df16000526103ff60206004601c335afa6040516060f3'; // [!code hl] const initCodeHash = utils.keccak256(initCode); // [!code hl] const address = utils.getCreate2Address(from, salt, initCodeHash); // [!code hl] ``` ##### viem ```ts import { getContractAddress } from 'viem' const address = getContractAddress({ // [!code hl] bytecode: '0x6394198df16000526103ff60206004601c335afa6040516060f3', // [!code hl] from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // [!code hl] opcode: 'CREATE2', // [!code hl] salt: '0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331', // [!code hl] }); // [!code hl] ``` ### BigNumber Utilities #### Ethers Many. #### viem None. We use browser native [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). ### Byte Manipulation Utilities #### isBytes ##### Ethers ```ts import { utils } from 'ethers' utils.isBytes(new Uint8Array([1, 69, 420])) // [!code hl] ``` ##### viem ```ts import { isBytes } from 'viem' isBytes(new Uint8Array([1, 69, 420])) // [!code hl] ``` #### isHexString ##### Ethers ```ts import { utils } from 'ethers' utils.isHexString('0xdeadbeef') // [!code hl] ``` ##### viem ```ts import { isHex } from 'viem' isHex('0xdeadbeef') // [!code hl] ``` #### isBytesLike ##### Ethers ```ts import { utils } from 'ethers' utils.isBytesLike('0xdeadbeef') // [!code hl] ``` ##### viem ```ts import { isBytes, isHex } from 'viem' isBytes('0xdeadbeef') || isHex('0xdeadbeef') // [!code hl] ``` #### arrayify ##### Ethers ```ts import { utils } from 'ethers' utils.arrayify('0xdeadbeef') // [!code hl] ``` ##### viem ```ts import { toBytes } from 'viem' toBytes('0xdeadbeef') // [!code hl] ``` #### hexlify ##### Ethers ```ts import { utils } from 'ethers' utils.hexlify(new Uint8Array([1, 69, 420])) // [!code hl] ``` ##### viem ```ts import { toHex } from 'viem' toHex(new Uint8Array([1, 69, 420])) // [!code hl] ``` #### hexValue ##### Ethers ```ts import { utils } from 'ethers' utils.hexValue(1) // [!code hl] ``` ##### viem ```ts import { toHex } from 'viem' toHex(1) // [!code hl] ``` #### formatBytes32String ##### Ethers ```ts import { utils } from 'ethers' utils.formatBytes32String('Hello world') // [!code hl] // 0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000 ``` ##### viem ```ts import { stringToHex } from 'viem' stringToHex('Hello world', { size: 32 }) // [!code hl] // 0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000 ``` #### parseBytes32String ##### Ethers ```ts import { utils } from 'ethers' utils.parseBytes32String('0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000') // [!code hl] // "Hello world" ``` ##### viem ```ts import { hexToString } from 'viem' hexToString('0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000', { size: 32 }) // [!code hl] // "Hello world" ``` #### concat ##### Ethers ```ts import { utils } from 'ethers' utils.concat([new Uint8Array([69]), new Uint8Array([420])]) // [!code hl] ``` ##### viem ```ts import { concat, toBytes } from 'viem' concat([new Uint8Array([69]), new Uint8Array([420])]) // [!code hl] ``` #### stripZeros ##### Ethers ```ts import { utils } from 'ethers' utils.stripZeros(new Uint8Array([0, 0, 0, 0, 0, 69])) // [!code hl] ``` ##### viem ```ts import { trim } from 'viem' trim(new Uint8Array([0, 0, 0, 0, 0, 69])) // [!code hl] ``` #### zeroPad ##### Ethers ```ts import { utils } from 'ethers' utils.zeroPad(new Uint8Array([69]), 32) // [!code hl] ``` ##### viem ```ts import { pad } from 'viem' pad(new Uint8Array([69]), { size: 32 }) // [!code hl] ``` #### hexConcat ##### Ethers ```ts import { utils } from 'ethers' utils.hexConcat(['0x00000069', '0x00000420']) // [!code hl] ``` ##### viem ```ts import { concat, toBytes } from 'viem' concat(['0x00000069', '0x00000420']) // [!code hl] ``` #### hexDataLength ##### Ethers ```ts import { utils } from 'ethers' utils.hexDataLength('0x00000069') // [!code hl] ``` ##### viem ```ts import { size } from 'viem' size('0x00000069') // [!code hl] ``` #### hexDataSlice ##### Ethers ```ts import { utils } from 'ethers' utils.hexDataSlice('0x00000069', 4) // [!code hl] ``` ##### viem ```ts import { slice } from 'viem' slice('0x00000069', 4) // [!code hl] ``` #### hexStripZeros ##### Ethers ```ts import { utils } from 'ethers' utils.hexStripZeros('0x00000069') // [!code hl] ``` ##### viem ```ts import { trim } from 'viem' trim('0x00000069') // [!code hl] ``` #### hexZeroPad ##### Ethers ```ts import { utils } from 'ethers' utils.hexZeroPad('0x69', 32) // [!code hl] ``` ##### viem ```ts import { pad } from 'viem' pad('0x69', { size: 32 }) // [!code hl] ``` ### Display Logic & Input Utilities #### formatUnits ##### Ethers ```ts import { utils } from 'ethers' utils.formatUnits(BigNumber.from('1000000000'), 9) // [!code hl] ``` ##### viem ```ts import { formatUnits } from 'viem' formatUnits(1000000000n, 9) // [!code hl] ``` #### formatEther ##### Ethers ```ts import { utils } from 'ethers' utils.formatEther(BigNumber.from('1000000000000000000')) // [!code hl] ``` ##### viem ```ts import { formatEther } from 'viem' formatEther(1000000000000000000n) // [!code hl] ``` #### parseUnits ##### Ethers ```ts import { utils } from 'ethers' utils.parseUnits('1.0', 18) // [!code hl] ``` ##### viem ```ts import { parseUnits } from 'viem' parseUnits('1', 18) // [!code hl] ``` #### parseEther ##### Ethers ```ts import { utils } from 'ethers' utils.parseEther('1.0') // [!code hl] ``` ##### viem ```ts import { parseEther } from 'viem' parseEther('1') // [!code hl] ``` ### Encoding Utilities #### RLP.encode ##### Ethers ```ts import { utils } from 'ethers' utils.RLP.encode('0x12345678') // [!code hl] ``` ##### viem ```ts import { toRlp } from 'viem' toRlp('0x12345678') // [!code hl] ``` #### RLP.decode ##### Ethers ```ts import { utils } from 'ethers' utils.RLP.decode('0x8412345678') // [!code hl] ``` ##### viem ```ts import { fromRlp } from 'viem' fromRlp('0x8412345678') // [!code hl] ``` ### Hashing Utilities #### id ##### Ethers ```ts import { utils } from 'ethers' utils.id('function ownerOf(uint256 tokenId)') // [!code hl] // hash utf-8 data utils.id('hello world') // [!code hl] ``` ##### viem ```ts import { toFunctionSelector, keccak256, toHex } from 'viem' toFunctionSelector('function ownerOf(uint256 tokenId)') // [!code hl] // hash utf-8 data keccak256(toHex('hello world')) // [!code hl] ``` #### keccak256 ##### Ethers ```ts import { utils } from 'ethers' utils.keccak256(utils.toUtf8Bytes('hello world')) // [!code hl] ``` ##### viem ```ts import { keccak256, toBytes } from 'viem' keccak256(toBytes('hello world')) // [!code hl] ``` #### encodeBase64/decodeBase64 viem does not provide Base64 encoding utilities. You can use browser native [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/atob) and [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) instead. #### encodeBase58/decodeBase58 viem does not provide Base58 encoding utilities. You can use libraries such as [`base58-js`](https://www.npmjs.com/package/base58-js) or [`bs58`](https://github.com/cryptocoinjs/bs58) instead. #### namehash ##### Ethers ```ts import { utils } from 'ethers' utils.namehash('awkweb.eth') // [!code hl] ``` ##### viem ```ts import { namehash } from 'viem' namehash('awkweb.eth') // [!code hl] ``` #### solidityPack & solidityKeccak256 ##### Ethers ```ts import { utils } from 'ethers' utils.solidityPack(['int16', 'uint48'], [-1, 12]) // [!code hl] utils.solidityKeccak256(['int16', 'uint48'], [-1, 12]) // [!code hl] ``` ##### viem ```ts import { encodePacked, keccak256 } from 'viem' encodePacked(['int16', 'uint48'], [-1, 12]) // [!code hl] keccak256(encodePacked(['int16', 'uint48'], [-1, 12])) // [!code hl] ``` ### String Utilities #### toUtf8Bytes ##### Ethers ```ts import { utils } from 'ethers' utils.toUtf8Bytes('Hello World') // [!code hl] ``` ##### viem ```ts import { stringToBytes } from 'viem' stringToBytes('Hello World') // [!code hl] ``` #### toUtf8String ##### Ethers ```ts import { utils } from 'ethers' utils.toUtf8String(new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])) // [!code hl] ``` ##### viem ```ts import { bytesToString } from 'viem' bytesToString(new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])) // [!code hl] ``` ### Transaction Utilities #### serializeTransaction ##### Ethers ```ts import { utils } from 'ethers' const serialized = utils.serializeTransaction({ chainId: 1, maxFeePerGas: utils.parseGwei('20'), maxPriorityFeePerGas: utils.parseGwei('2'), nonce: 69, to: "0x1234512345123451234512345123451234512345", type: 2, value: utils.parseEther('0.01'), }) ``` ##### viem ```ts import { serializeTransaction, parseEther, parseGwei } from 'viem' const serialized = serializeTransaction({ chainId: 1, gas: 21001n, maxFeePerGas: parseGwei('20'), maxPriorityFeePerGas: parseGwei('2'), nonce: 69, to: "0x1234512345123451234512345123451234512345", value: parseEther('0.01'), }) ``` #### parseTransaction ##### Ethers ```ts import { utils } from 'ethers' const transaction = utils.parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') ``` ##### viem ```ts import { parseTransaction } from 'viem' const transaction = parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') ``` ## Frequently Asked Questions Frequently asked questions related to viem. **TL;DR: viem tries to avoid creating unnecessary abstractions on top of existing systems.** Feel free to add to this document if you notice frequently asked questions that are not covered here. {/* [[toc]] */} ### Why use the terms "Wallet" & "Account" instead of "Signer" viem attempts to align to the "Wallet" and "Account" [terminology on Ethereum.org](https://ethereum.org/en/glossary/). The term "Signer" was adapted from ethers.js. Let's clear up on some terms before we dive in. * Wallet: An application or interface that holds Account(s). * Account: An object that represents an address, balance, nonce, and optional storage and code. * Private Key: Proves ownership of an Account, and can sign messages & transactions. In the context of viem, a Wallet Client is an interface that can hold an Account. The Account may or may not hold a Private Key. In viem, there are two types of Accounts: * Local Account: can **synchronously & directly** sign messages and transactions using its Private Key. A signature is guaranteed. * JSON-RPC Account: **asynchronously requests** signing of messages and transactions from the target Wallet over JSON-RPC (e.g. Browser Extension or WalletConnect). The target Wallet holds the Account & Private Key. A signature is not guaranteed (the target Wallet may not have permitted the Account, or the Wallet may have rejected the request). We do not use the term "Signer" because there are noticeable behavioral differences between signing locally and signing over JSON-RPC. ### Why are contract function `args` with fully-named inputs represented as unnamed tuple types instead of object types? Let's look at an example! Suppose I have the following function in my contract: ```solidity function transferFrom(address sender, address recipient, uint256 amount) returns (bool) ``` All the inputs are named (`sender`, `recipient`, and `amount`) so I might be tempted to represent the parameters as the following TypeScript type: ```ts type Args = { sender: `0x${string}`; recipient: `0x${string}`; amount: bigint; } ``` This improves developer experience a bit because now I can see the names of the parameters in my editor. ```ts import { createWalletClient, parseAbi } from 'viem' const client = createWalletClient(…) client.writeContract({ address: '0x…', abi: parseAbi([ 'function transferFrom(address sender, address recipient, uint256 amount) returns (bool)', ]), functionName: 'transferFrom', args: { sender: '0x…', recipient: '0x…', amount: 100n, }, }) ``` However, this only works if all the inputs are named (some compilers will strip names from inputs). If any of the inputs are unnamed, then you'll have to use a tuple instead: ```ts client.writeContract({ address: '0x…', abi: parseAbi([ 'function transferFrom(address, address, uint256) returns (bool)', ]), functionName: 'transferFrom', args: ['0x…', '0x…', 100n], }) ``` This can get even more complicated when a function has overloads: ```solidity function safeTransferFrom(address, address, uint256) {} function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) {} ``` In this case, the type of the overload parameters start to diverge from each other: ```ts type Args = | [`0x${string}`, `0x${string}`, bigint] | { from: `0x${string}`; to: `0x${string}`; tokenId: bigint; data: string; } ``` If you want to switch between the two overloads in your code, you'll need to completely change the type instead of just adding or removing a single positional argument from the end. (Objects also don't enforce type-level ordering so you can put them in whatever order you want. This would also mean that viem would also need to internally validate order during runtime, adding some extra overhead.) ```diff client.writeContract({ address: '0x…', abi: parseAbi([ 'function safeTransferFrom(address, address, uint256)', 'function safeTransferFrom(address from, address to, uint256 tokenId, bytes data)', ]), functionName: 'safeTransferFrom', - args: ['0x…', '0x…', 100n], + args: { + from: '0x…', + to: '0x…', + tokenId: 100n, + data: '0x…', + }, }) ``` Even though overloads are an edge case, it would be sufficiently [astonishing](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) to come across this behavior. So what's the best way to represent `args`? Well, they are positional at the contract-level so it makes sense to represent them that way in viem too. Not all is lost when it comes to developer experience though! Tuple types in TypeScript can have [names](https://www.typescriptlang.org/play?ts=4.0.2#example/named-tuples) attached to them: ```ts type Args = [from: `0x${string}`, to: `0x${string}`, tokenId: bigint] ``` These names show up in your editor so you get nice developer experience when using autocomplete, etc. Unfortunately, TypeScript doesn't support dynamic named tuples right now, but we are watching [this issue](https://github.com/microsoft/TypeScript/issues/44939) closely and once it is implemented, we will add it to viem. In the meantime, hang tight! ### Why is a contract function return type returning an array instead of an object? Suppose your ABI looks like this: ```ts [ { inputs: [], name: "latestRoundData", outputs: [ { name: "roundId", type: "uint80" }, { name: "answer", type: "int256" }, { name: "startedAt", type: "uint256" }, { name: "updatedAt", type: "uint256" }, { name: "answeredInRound", type: "uint80" }, ], stateMutability: "view", type: "function", } ] ``` You might be confused why the following does not return an object: ```ts import { createPublicClient, parseAbi } from 'viem' const client = createPublicClient(…) const res = await client.readContract({ address: '0x…', abi: […], // abi from above functionName: 'latestRoundData', }) res // ^? const res: [bigint, bigint, bigint, bigint, bigint] ``` This is expected. `"latestRoundData"` `outputs` is an array of types, so you get an array of decoded values as the return type. viem only maps explicitly typed tuples as objects Why does viem follow this approach? Here is the contract function definition for `latestRoundData` with two different return types: ```solidity function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); struct Data { uint80 roundId; uint256 answer; uint256 startedAt; uint256 updatedAt; uint80 answeredInRound } function latestRoundData() external view returns (Data data); ``` The first function returns a set of five items, so viem maps it to an array. The reason why we don't convert it to an object is because things get ambiguous when we come to decode structs. How do you determine the difference between a "return" tuple (first function) and a "struct" tuple (second function). Another reason is that folks might expect it to be an array (because it is a set of return items). Other libraries, like ethers, mitigate this by returning a hybrid Array/Object type, but that kind of type is not serializable in JavaScript, and viem prefers to not try and "hack" JavaScript types. ### Why doesn't Wallet Client support public actions? Wallet Client doesn't support public actions because wallet providers (Injected `window.ethereum`, WalletConnect v2, etc.) may not provide a large majority of "node"/"public" RPC methods like `eth_call`, `eth_newFilter`, `eth_getLogs`, etc. This is because these methods are not required for a wallet provider to function properly. For example, a wallet provider may only support `eth_sendTransaction` and `eth_sign` and nothing else. ## Getting Started \[Get started with viem in just a few lines of code.] ### Overview viem is a TypeScript interface for Ethereum that provides low-level stateless primitives for interacting with Ethereum. viem is focused on developer experience, stability, bundle size, and performance: * **Developer experience** Automatic [type safety and inference](/docs/typescript), comprehensive documentation, composable APIs. * **Stability** Test suite runs against forked Ethereum networks, complete [test coverage](https://app.codecov.io/gh/wevm/viem). * **Bundle size** Tree-shakable lightweight modules. * **Performance** Optimized encoding/parsing, async tasks only when necessary. You can learn more about the rationale behind the project in the [Why viem](/docs/introduction) section. ### Installation :::code-group ```bash [npm] npm i viem ``` ```bash [pnpm] pnpm i viem ``` ```bash [bun] bun i viem ``` ::: ### Quick Start #### 1. Set up your Client & Transport Firstly, set up your [Client](/docs/clients/intro) with a desired [Transport](/docs/clients/intro) & [Chain](/docs/chains/introduction). ```ts twoslash import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ // [!code focus] chain: mainnet, // [!code focus] transport: http(), // [!code focus] }) // [!code focus] ``` :::info In a production app, it is highly recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc). If no URL is provided, viem will default to a public RPC provider. [Read more](/docs/clients/transports/http#usage). ::: #### 2. Consume Actions Now that you have a Client set up, you can now interact with Ethereum and consume [Actions](/docs/actions/public/introduction)! ```ts twoslash import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ chain: mainnet, transport: http(), }) const blockNumber = await client.getBlockNumber() // [!code focus] ``` ### Live example