# 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] version: '1.1', // [!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], version: '1.1', }) 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], version: '1.1', }) 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], version: '1.1', }) ``` ::: ::::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], version: '1.1', }) ``` ::: :::: ## USDC (Circle) :::info This extension and guides are maintained by [Circle](https://www.circle.com). ::: [USDC](https://www.circle.com/usdc) is a digital dollar issued by Circle, also known as a stablecoin, running on many of the world's leading blockchains. Designed to represent US dollars on the internet, USDC is backed 100% by highly liquid cash and cash-equivalent assets so that it's always redeemable 1:1 for USD. This section provides a set of tutorials that demonstrate how to integrate and interact with USDC using the Viem library. These guides walk through core patterns and advanced capabilities that developers can build on top of USDC — including basic token operations, cross-chain transfers, and gas abstraction via stablecoins. Whether you're building a wallet, protocol integration, or a developer tool, this section will help you implement USDC support using Viem's type-safe primitives and modern client architecture. ### Included Guides * [**Integrating USDC into Your Application**](/circle-usdc/guides/integrating)\ Learn how to fetch balances, transfer tokens, approve smart contracts, and listen to USDC events. * [**Cross-Chain USDC Transfers (CCTP V2)**](/circle-usdc/guides/cross-chain)\ Transfer USDC between chains using Circle's Cross-Chain Transfer Protocol v2. Useful for enabling seamless asset (USDC) movement across ecosystems. * [**Paying Gas with USDC (Circle Paymaster)**](/circle-usdc/guides/paymaster)\ Use Circle's Paymaster to abstract ETH gas fees and sponsor transactions using USDC. Ideal for onboarding and UX-focused flows. * [**Circle Smart Account**](/circle-usdc/guides/smart-account)\ Create passkey-based smart accounts using Circle's Modular Wallets SDK and Viem. Supports gasless transactions, bundler integration, and secure WebAuthn authentication. ## 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. ::: ## 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 (Infura, thirdweb, 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], }) ``` ## Chains The following ZKsync chains are supported in Viem: ```ts twoslash import { zksync, // [!code hl] zksyncSepoliaTestnet, // [!code hl] } from 'viem/chains' ``` ### Configuration Viem exports ZKsync's chain [formatters](/docs/chains/formatters) & [serializers](/docs/chains/serializers) via `chainConfig`. This is useful if you need to define another chain which is implemented on ZKsync. ```ts twoslash // @noErrors import { defineChain } from 'viem' import { chainConfig } from 'viem/zksync' export const zkStackExample = defineChain({ ...chainConfig, name: 'ZKsync Example', // ... }) ``` ## Client To use the ZKsync functionality of Viem, you must extend your existing (or new) Viem Client with ZKsync Actions. ### Usage ```ts twoslash import 'viem/window' // ---cut--- import { createPublicClient, createWalletClient, custom, http } from 'viem' import { zksync } from 'viem/chains' import { eip712WalletActions } from 'viem/zksync' const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum!), }).extend(eip712WalletActions()) // [!code hl] const publicClient = createPublicClient({ chain: zksync, transport: http() }) ``` ### Extensions #### `eip712WalletActions` A suite of [Wallet Actions](/zksync/actions/sendTransaction) for suited for development with ZKsync chains. ```ts twoslash import { eip712WalletActions } from 'viem/zksync' ``` ##### Sending transactions using paymaster [Read more](./actions/sendTransaction.md) ```ts const hash = await walletClient.sendTransaction({ account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n, paymaster: '0xFD9aE5ebB0F6656f4b77a0E99dCbc5138d54b0BA', paymasterInput: '0x123abc...' }) ``` ##### Calling contracts [Read more](../docs/contract/writeContract.md) ```ts import { simulateContract } from 'viem/contract' const { request } = await publicClient.simulateContract(walletClient, { address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: parseAbi(['function mint(uint32 tokenId) nonpayable']), functionName: 'mint', args: [69420], }); const hash = await walletClient.writeContract(request) ``` #### `publicActionsL1` A suite of [Public Actions](/zksync/actions/getL1Allowance) suited for development with **Layer 1** chains. These actions provide functionalities specific to public clients operating at the Layer 1 level, enabling them to interact seamlessly with Layer 2 protocols. ```ts import { publicActionsL1 } from 'viem/zksync' ``` ## parseEip712Transaction Parses a serialized EIP712 transaction. ### Import ```ts import { parseEip712Transaction } from 'viem/zksync' ``` ### Usage ```ts import { parseEip712Transaction } from 'viem/zksync' const serializedTransaction = '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0' const transaction = parseEip712Transaction(serializedTransaction) ``` ### Returns `ZksyncTransactionSerializableEIP712` The ZKsync EIP712 transaction. ### Parameters #### tx * **Type:** [`Hex`](/docs/glossary/types#hex) The serialized EIP712 transaction. ```ts const serializedTransaction = '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0' const transaction = parseEip712Transaction(serializedTransaction) ``` ## getApprovalBasedPaymasterInput Returns encoded formatted approval-based paymaster params. ### Import ```ts import { getApprovalBasedPaymasterInput } from 'viem/zksync' ``` ### Usage ```ts import { getApprovalBasedPaymasterInput } from 'viem/zksync' const data = getApprovalBasedPaymasterInput({ innerInput: '0x', minAllowance: 1n, token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964", }) ``` ### Returns `EncodeFunctionDataReturnType` The `Hex` value of the provided approval-based paymaster inputs. ### Parameters #### token * **Type:** `Address` The token address. ```ts const data = getApprovalBasedPaymasterInput({ innerInput: '0x', minAllowance: 1n, token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964", // [!code focus] }) ``` #### minAllowance * **Type:** `bigint` Minimum allowance (in wei) of token that can be sent towards the paymaster. ```ts const data = getApprovalBasedPaymasterInput({ innerInput: new Uint8Array(), minAllowance: 1n, // [!code focus] token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964", }) ``` #### innerInput * **Type:** `Hex | ByteArray` Additional payload that can be sent to the paymaster to implement any logic . ```ts const data = getApprovalBasedPaymasterInput({ innerInput: "0x0005040302010", // [!code focus] minAllowance: 1n, token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964", }) ``` ## getGeneralPaymasterInput Returns encoded formatted general-based paymaster params. ### Import ```ts import { getGeneralPaymasterInput } from 'viem/zksync' ``` ### Usage ```ts import { getGeneralPaymasterInput } from 'viem/zksync' const data = getGeneralPaymasterInput({ innerInput: '0x', }) ``` ### Returns `EncodeFunctionDataReturnType` The `Hex` value of the provided general-based paymaster inputs. ### Parameters #### innerInput Additional payload that can be sent to the paymaster to implement any logic * **Type:** `Hex` or `ByteArray` ```ts const data = getGeneralPaymasterInput({ innerInput: new Uint8Array([0, 1, 2, 3, 4, 5]), // [!code focus] }) ``` ```ts const data = getGeneralPaymasterInput({ innerInput: "0x0005040302010", // [!code focus] }) ``` ## getL2HashFromPriorityOp Returns the hash of the L2 priority operation from a given L1 transaction receipt. ### Import ```ts import { getL2HashFromPriorityOp } from 'viem/zksync' ``` ### Usage :::code-group ```ts [example.ts] import { client, zksyncClient } from './config' import { getL2HashFromPriorityOp } from 'viem/zksync' const receipt = await client.waitForTransactionReceipt({ hash: '0x...' }) const l2Hash = getL2HashFromPriorityOp( receipt, await zksyncClient.getMainContractAddress() ) ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync, mainnet } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: mainnet, transport: http(), }) export const zksyncClient = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Hash` The hash of the L2 priority operation. ### Parameters #### receipt * **Type:** [`TransactionReceipt`](/docs/glossary/types#transactionreceipt) The L1 transaction receipt. ```ts const l2Hash = getL2HashFromPriorityOp( receipt, // [!code focus] '0x14b947814912c71bdbc3275c143a065d2ecafaba' ) ``` #### zksync * **Type:** `Address` The address of the ZKsync Era main contract. ```ts const l2Hash = getL2HashFromPriorityOp( receipt, '0x14b947814912c71bdbc3275c143a065d2ecafaba' // [!code focus] ) ``` ## claimFailedDeposit Withdraws funds from the initiated deposit, which failed when finalizing on L2. If the deposit L2 transaction has failed, it sends an L1 transaction calling `claimFailedDeposit` method of the L1 bridge, which results in returning L1 tokens back to the depositor. ### Usage :::code-group ```ts [example.ts] import { account, walletClient, zksyncClient } from './config' import { legacyEthAddress } from 'viem/zksync' const hash = await walletClient.claimFailedDeposit({ account, client: zksyncClient, depositHash: '' }) ``` ```ts [config.ts] import { createWalletClient, createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync, mainnet } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `claimFailedDeposit`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient, zksyncClient } from './config' import { legacyEthAddress } from 'viem/zksync' const hash = await walletClient.claimFailedDeposit({ client: zksyncClient, depositHash: '' }) ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) // Retrieve Account from an EIP-1193 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(walletActionsL1()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(walletActionsL1()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### client * **Type:** `Client` The L2 client for fetching data from L2 chain. ```ts const hash = await walletClient.claimFailedDeposit({ client: zksyncClient, // [!code focus] depositHash: '' }) ``` #### depositHash * **Type:** `Hash` The L2 transaction hash of the failed deposit. ```ts const hash = await walletClient.claimFailedDeposit({ client: zksyncClient, depositHash: '', // [!code focus] }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.claimFailedDeposit({ chain: zksync, // [!code focus] client: zksyncClient, depositHash: '' }) ``` ## deployContract Deploys a contract to the network, given bytecode & constructor arguments by using EIP712 transaction. ### Usage :::code-group ```ts [example.ts] import { wagmiAbi } from './abi' import { account, walletClient } from './config' const hash = await walletClient.deployContract({ abi, account, bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', }) ``` ```ts [abi.ts] export const wagmiAbi = [ ... { inputs: [], stateMutability: "nonpayable", type: "constructor", }, ... ] as const; ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712Actions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(eip712WalletActions()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Deploying with Constructor Args :::code-group ```ts [example.ts] {8} import { deployContract } from 'viem' import { wagmiAbi } from './abi' import { account, walletClient } from './config' const hash = await walletClient.deployContract({ abi, account, args: [69420], bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', }) ``` ```ts [abi.ts] {4} export const wagmiAbi = [ ... { inputs: [{ name: "x", type: "uint32" }], stateMutability: "nonpayable", type: "constructor", }, ... ] as const; ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712Actions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(eip712WalletActions()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Deploying with Factory Deps :::code-group ```ts [example.ts] {8} import { deployContract } from 'viem' import { wagmiAbi } from './abi' import { account, walletClient } from './config' const hash = await walletClient.deployContract({ abi, account, args: [69420], bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', factoryDeps: [ '0x702040405260405161083e38038061083e833981016040819123456...', '0x102030405260405161083e38038061083e833981016040819112233...' ] }) ``` ```ts [abi.ts] {4} export const wagmiAbi = [ ... { inputs: [{ name: "x", type: "uint32" }], stateMutability: "nonpayable", type: "constructor", }, ... ] as const; ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712Actions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(eip712WalletActions()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### abi * **Type:** [`Abi`](/docs/glossary/types#abi) The contract's ABI. ```ts const hash = await walletClient.deployContract({ abi: wagmiAbi, // [!code focus] account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', }) ``` #### account * **Type:** `Account | Address` The Account to deploy the contract from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const hash = await walletClient.deployContract({ abi: wagmiAbi, account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', }) ``` #### bytecode * **Type:** [`Hex`](/docs/glossary/types#hex) The contract's bytecode. ```ts const hash = await walletClient.deployContract({ abi: wagmiAbi, account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', // [!code focus] }) ``` #### args * **Type:** Inferred from ABI. Constructor arguments to call upon deployment. ```ts const hash = await walletClient.deployContract({ abi: wagmiAbi, account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', args: [69] // [!code focus] }) ``` #### deploymentType (optional) * **Type:** `'create' | 'create2' | 'createAccount' | 'create2Account'` Specifies the type of contract deployment. Defaults to 'create'. ```ts const hash = await walletClient.deployContract({ abi: wagmiAbi, account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', args: [69], deploymentType: 'create2' // [!code focus] }) ``` #### salt (optional) * **Type:** [`Hash`](/docs/glossary/types#hash) Specifies a unique identifier for the contract deployment. ```ts const hash = await walletClient.deployContract({ abi: wagmiAbi, account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', args: [69], salt: '0x201050...' // [!code focus] }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', gasPerPubdata: 50000, // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the fees. The `paymasterInput` field is required with this one. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` ## deposit Transfers the specified token from the associated account on the L1 network to the target account on the L2 network. The token can be either ETH or any ERC20 token. For ERC20 tokens, enough approved tokens must be associated with the specified L1 bridge (default one or the one defined in `bridgeAddress`). In this case, depending on is the chain ETH-based or not `approveToken` or `approveBaseToken` can be enabled to perform token approval. If there are already enough approved tokens for the L1 bridge, token approval will be skipped. ### Usage :::code-group ```ts [example.ts] import { account, walletClient, zksyncClient } from './config' import { legacyEthAddress } from 'viem/zksync' // deposit ETH const hash = await walletClient.deposit({ account, client: zksyncClient, token: legacyEthAddress, amount: 7_000_000_000n, to: account.address, refundRecipient: account.address, }) // deposit ERC20 const txHash = await walletClient.deposit({ account, client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: account.address, approveToken: true, refundRecipient: account.address, }) ``` ```ts [config.ts] import { createWalletClient, createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync, mainnet } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `deposit`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient, zksyncClient } from './config' import { legacyEthAddress } from 'viem/zksync' // deposit ETH const hash = await walletClient.deposit({ client: zksyncClient, token: legacyEthAddress, amount: 7_000_000_000n, to: walletClient.account.address, refundRecipient: walletClient.account.address, }) // deposit ERC20 const txHash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, }) ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) // Retrieve Account from an EIP-1193 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(walletActionsL1()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(walletActionsL1()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### client * **Type:** `Client` The L2 client for fetching data from L2 chain. ```ts const hash = await walletClient.deposit({ client: zksyncClient, // [!code focus] token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, }) ``` #### token * **Type:** `Address` The address of the token to deposit. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', // [!code focus] amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, }) ``` #### amount * **Type:** `bigint` The amount of the token to deposit. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, // [!code focus] to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, }) ``` #### to (optional) * **Type:** `Address` * **Default:** `walletClient.account` The address that will receive the deposited tokens on L2. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, // [!code focus] approveToken: true, refundRecipient: walletClient.account.address, }) ``` #### operatorTip (optional) * **Type:** `bigint` The tip the operator will receive on top of the base cost of the transaction. Currently, ZKsync node do not consider this tip. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, operatorTip: 100_000n, // [!code focus] approveToken: true, refundRecipient: walletClient.account.address, }) ``` #### l2GasLimit (optional) * **Type:** `bigint` Maximum amount of L2 gas that transaction can consume during execution on L2. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, l2GasLimit: 900_000n, // [!code focus] approveToken: true, refundRecipient: walletClient.account.address, }) ``` #### gasPerPubdataByte (optional) * **Type:** `bigint` The L2 gas price for each published L1 calldata byte. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, gasPerPubdataByte: 250_000_000_000n // [!code focus] }) ``` #### refundRecipient (optional) * **Type:** `Address` * **Default:** `walletClient.account` The address on L2 that will receive the refund for the transaction. If the transaction fails, it will also be the address to receive `amount`. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, // [!code focus] }) ``` #### bridgeAddress (optional) * **Type:** `Address` * **Default:** ZKsync L1 shared bridge The address of the bridge contract to be used. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, bridgeAddress: '0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF' // [!code focus] }) ``` #### customBridgeData (optional) * **Type:** `Hex` Additional data that can be sent to a bridge. ```ts const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, bridgeAddress: '0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF', customBridgeData: '0x...' // [!code focus], }) ``` #### approveToken (optional) * **Type:** `boolean | TransactionRequest` Whether token approval should be performed under the hood. Set this flag to true (or provide transaction overrides) if the bridge does not have sufficient allowance. The approval transaction is executed only if the bridge lacks sufficient allowance; otherwise, it is skipped. \::: code-group ```ts [boolean.ts] const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, // [!code focus], refundRecipient: walletClient.account.address, bridgeAddress: '0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF', }) ``` ```ts [overrides.ts] const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: { maxFeePerGas: 200_000_000_000n // [!code focus], }, refundRecipient: walletClient.account.address, bridgeAddress: '0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF', }) ``` \::: #### approveBaseToken (optional) * **Type:** `boolean | TransactionRequest` Whether base token approval should be performed under the hood. Set this flag to true (or provide transaction overrides) if the bridge does not have sufficient allowance. The approval transaction is executed only if the bridge lacks sufficient allowance; otherwise, it is skipped. \::: code-group ```ts [boolean.ts] const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveBaseToken: true, // [!code focus], refundRecipient: walletClient.account.address, bridgeAddress: '0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF', }) ``` ```ts [overrides.ts] const hash = await walletClient.deposit({ client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveBaseToken: { maxFeePerGas: 200_000_000_000n // [!code focus], }, refundRecipient: walletClient.account.address, bridgeAddress: '0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF', }) ``` \::: #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.deposit({ chain: zksync, // [!code focus] client: zksyncClient, token: '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55', amount: 20n, to: walletClient.account.address, approveToken: true, refundRecipient: walletClient.account.address, }) ``` ## estimateFee Returns an estimated Fee for requested transaction. :::warning **This Action is being deprecated.** This method calls an RPC method that [will be removed in a future release](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1066). Please use the alternatives mentioned below. **Alternatives / Workaround** The original returned `Fee` object contained `gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas` and `gasPerPubData`. Use the following methods to retrieve these values: * `gasLimit`: use `eth_estimateGas` or [its correspondent viem action](/docs/actions/public/estimateGas). * `maxFeePerGas`: use `eth_gasPrice` or [its correspondent viem action](/docs/actions/public/getGasPrice). * `maxPriorityFeePerGas`: set to `0`. * `gasPerPubData`: use `zks_gasPerPubdata` or [its correspondent viem action](/zksync/actions/getGasPerPubData). ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const fee = await client.estimateFee({ account: '0x636A122e48079f750d44d13E5b39804227E1467e', to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", value: 0n }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Fee` The fee values. ### Parameters #### account * **Type:** `Account | Address` The Account to send the transaction from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const fee = await walletClient.estimateFee({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### to * **Type:** `0x${string}` The transaction recipient or contract address. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus] value: 1000000000000000000n, nonce: 69 }) ``` #### data (optional) * **Type:** `0x${string}` A contract hashed method call with encoded args. ```ts const fee = await walletClient.estimateFee({ data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // [!code focus] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### gasPrice (optional) * **Type:** `bigint` The price (in wei) to pay per gas. Only applies to [Legacy Transactions](/docs/glossary/terms#legacy-transaction). ```ts const fee = await walletClient.estimateFee({ account, gasPrice: parseGwei('20'), // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### nonce (optional) * **Type:** `number` Unique number identifying this transaction. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n, nonce: 69 // [!code focus] }) ``` #### value (optional) * **Type:** `bigint` Value in wei sent with this transaction. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: parseEther('1'), // [!code focus] nonce: 69 }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', gasPerPubdata: 50000, // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### factoryDeps (optional) * **Type:** `[0x${string}]` Contains bytecode of the deployed contract. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', factoryDeps: ['0xcde...'], // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the fees. The `paymasterInput` field is required with this one. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const fee = await walletClient.estimateFee({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` ## estimateGasL1ToL2 Returns an estimated gas for L1 to L2 execution ### Usage :::code-group ```ts [example.ts] import { client } from './config' const gas = await client.estimateGasL1ToL2({ account: '0x636A122e48079f750d44d13E5b39804227E1467e', to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618', value: 0n }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `bigint` The estimated gas value. ### Parameters #### account * **Type:** `Account | Address` The Account to send the transaction from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const gas = await walletClient.estimateGasL1ToL2({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### to * **Type:** `0x${string}` The transaction recipient or contract address. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus] value: 1000000000000000000n, nonce: 69 }) ``` #### data (optional) * **Type:** `0x${string}` A contract hashed method call with encoded args. ```ts const gas = await walletClient.estimateGasL1ToL2({ data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // [!code focus] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### gasPrice (optional) * **Type:** `bigint` The price (in wei) to pay per gas. Only applies to [Legacy Transactions](/docs/glossary/terms#legacy-transaction). ```ts const gas = await walletClient.estimateGasL1ToL2({ account, gasPrice: parseGwei('20'), // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### nonce (optional) * **Type:** `number` Unique number identifying this transaction. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n, nonce: 69 // [!code focus] }) ``` #### value (optional) * **Type:** `bigint` Value in wei sent with this transaction. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: parseEther('1'), // [!code focus] nonce: 69 }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', gasPerPubdata: 50000, // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### factoryDeps (optional) * **Type:** `[0x${string}]` Contains bytecode of the deployed contract. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', factoryDeps: ['0xcde...'], // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the gas. The `paymasterInput` field is required with this one. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const gas = await walletClient.estimateGasL1ToL2({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` ## finalizeWithdrawal Proves the inclusion of the `L2->L1` withdrawal message. ### Usage :::code-group ```ts [example.ts] import { account, walletClient, zksyncClient } from './config' const hash = await walletClient.finalizeWithdrawal({ account, client: zksyncClient, hash: '0x…', }) ``` ```ts [config.ts] import { createWalletClient, createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync, mainnet } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `finalizeWithdrawal`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient, zksyncClient } from './config' const hash = await walletClient.finalizeWithdrawal({ client: zksyncClient, hash: '0x…', }) ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) // Retrieve Account from an EIP-1193 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(walletActionsL1()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(walletActionsL1()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### client * **Type:** `Client` The L2 client for fetching data from L2 chain. ```ts const hash = await walletClient.finalizeWithdrawal({ client: zksyncClient, // [!code focus] hash: '0x…', }) ``` #### hash * **Type:** `Hex` Hash of the L2 transaction where the withdrawal was initiated. ```ts const hash = await walletClient.finalizeWithdrawal({ client: zksyncClient, hash: '0x…', // [!code focus] }) ``` #### index (optional) * **Type:** `number` * **Default:** `0` In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. ```ts const hash = await walletClient.finalizeWithdrawal({ client: zksyncClient, hash: '0x…', index: 0n, // [!code focus] }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.finalizeWithdrawal({ chain: zksync, // [!code focus] client: zksyncClient, hash: '0x…', }) ``` ## getAllBalances Returns all known balances for a given account. :::warning **This Action has been deprecated.** This method calls an RPC method that [has been removed from the node API](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1019). ::: ### Usage :::code-group ```ts [example.ts] import { client, account } from './config' const balances = await client.getAllBalances({ account }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) // JSON-RPC Account export const account = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // Local Account export const account = privateKeyToAccount(...) ``` ::: ### Returns `GetAllBalancesReturnType` Array of all known balances for an address. ### Parameters #### account * **Type:** `Account | Address` The Account used for check. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const balances = await client.getAllBalances({ account: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" // [!code focus] }); ``` ## getBaseTokenL1Address Returns the address of the ZKsync Chain's base L1 token. :::warning **This Action is being deprecated.** This method calls an RPC method that [will be removed in a future release](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1066). Please use the alternatives mentioned below. **Alternatives / Workaround** This method returned the address of the chain's base token contract in L1. This can be retrieved from a call to the BridgeHub contract. 1. Retrieve the BridgeHub contract address using the [`getBridgeHubContractAddress`](/zksync/actions/getBridgehubContractAddress) action. 2. Call the `baseToken(chainId)` method on the BridgeHub contract. ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const address = await client.getBaseTokenL1Address(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Address` Base Token L1 address. ## getBlockDetails Returns additional ZKsync-specific information about the L2 block. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const blockDetails = await client.getBlockDetails({ number: 1 }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `BaseBlockDetails` Structure that represent ZKsync-specific information about L2 block. ### Parameters #### number Block Number * **Type** `number` ```ts const blockDetails = await client.getBlockDetails({ number: 1 // [!code focus] }); ``` ## getBridgehubContractAddress Returns the Bridgehub smart contract address. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const address = await client.getBridgehubContractAddress(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Address` Bridgehub smart contract address. ## getDefaultBridgeAddresses Returns the addresses of the default ZKsync Era bridge contracts on both L1 and L2. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const addresses = await client.getDefaultBridgeAddresses(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `GetDefaultBridgeAddressesReturnType` Addresses of the default ZKsync Era bridge contracts on both L1 and L2. ## getGasPerPubData Returns the scaled gas per pubdata limit for the currently open batch. :::info This method is only available ZKsync chains running node API version 28.7.0 and above. ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const gasPerPubDataLimit = await client.getGasPerPubData(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Hex` Pubdata limit for the current batch. ## getL1Allowance Determines the amount of approved tokens for a specific L1 bridge. ### Usage :::code-group ```ts [example.ts] import { account, publicClient } from './config' const allowance = await publicClient.getL1Allowance({ account token: '0x5C221E77624690fff6dd741493D735a17716c26B', bridgeAddress: '0x84DbCC0B82124bee38e3Ce9a92CdE2f943bab60D', }) ``` ```ts [config.ts] import { createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { mainnet } from 'viem/chains' import { publicActionsL1 } from 'viem/zksync' export const publicClient = createPublicClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(publicActionsL1()) // JSON-RPC Account export const account = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // Local Account export const account = privateKeyToAccount(...) ``` ::: ### Returns `bigint` Returns the amount of approved tokens. ### Parameters #### account * **Type:** `Account | Address` The Account used for check. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const allowance = await publicClient.getL1Allowance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // [!code focus] blockTag: 'latest', bridgeAddress: '0x84DbCC0B82124bee38e3Ce9a92CdE2f943bab60D', token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### blockTag (optional) * **Type:** `BlockTag | undefined` In which block an allowance should be checked on. The latest processed one is the default option. ```ts const allowance = await publicClient.getL1Allowance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' blockTag: 'latest', // [!code focus] bridgeAddress: '0x84DbCC0B82124bee38e3Ce9a92CdE2f943bab60D', token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### bridgeAddress * **Type:** `Address` The address of the bridge contract to be used. ```ts const allowance = await publicClient.getL1Allowance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', blockTag: 'latest', bridgeAddress: '0x84DbCC0B82124bee38e3Ce9a92CdE2f943bab60D', // [!code focus] token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### token * **Type:** `Address` The Ethereum address of the token. ```ts const allowance = await publicClient.getL1Allowance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', blockTag: 'latest', bridgeAddress: '0x84DbCC0B82124bee38e3Ce9a92CdE2f943bab60D', token: '0x5C221E77624690fff6dd741493D735a17716c26B', // [!code focus] }) ``` ## getL1Balance Returns the amount of the token held by the account on the L1 network. ### Usage :::code-group ```ts [example.ts (token balance)] import { account, publicClient } from './config' const balance = await publicClient.getL1Balance({ account token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` ```ts [example.ts (ETH balance)] import { account, publicClient } from './config' const balance = await publicClient.getL1Balance({ account }) ``` ```ts [config.ts] import { createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { mainnet } from 'viem/chains' import { publicActionsL1 } from 'viem/zksync' export const publicClient = createPublicClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(publicActionsL1()) // JSON-RPC Account export const account = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // Local Account export const account = privateKeyToAccount(...) ``` ::: ### Returns `bigint` Returns the amount of the tokens. ### Parameters #### account (optional) * **Type:** `Account | Address` The Account used for check. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const balance = await publicClient.getL1Balance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // [!code focus] blockTag: 'latest', token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### blockTag (optional) * **Type:** `BlockTag | undefined` In which block an balance should be checked on. The latest processed one is the default option. ```ts const balance = await publicClient.getL1Balance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' blockTag: 'latest', // [!code focus] token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### token (optional) * **Type:** `Address` The address of the token. Defaults to ETH if not provided. ```ts const balance = await publicClient.getL1Balance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', blockTag: 'latest', token: '0x5C221E77624690fff6dd741493D735a17716c26B', // [!code focus] }) ``` ## getL1BatchBlockRange Returns the range of blocks contained within a batch given by batch number. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const batchBlockRange = await client.getL1BatchBlockRange({ number: 1 }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `GetL1BatchBlockRangeReturnType` Array of two elements representing the range of blocks within a batch. ### Parameters #### number L1 Batch Number * **Type** `number` ```ts const batchBlockRange = await client.getL1BatchBlockRange({ number: 1 // [!code focus] }); ``` ## getL1BatchDetails Returns data pertaining to a given batch. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const batchDetails = await client.getL1BatchDetails({ number: 1 }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `GetL1BatchDetailsReturnType` Batch details. ### Parameters #### number L1 Batch Number * **Type** `number` ```ts const batchDetails = await client.getL1BatchDetails({ number: 1 // [!code focus] }); ``` ## getL1BatchNumber Returns the latest L1 batch number. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const latestNumber = await client.getL1BatchNumber(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Hex` Latest L1 batch number. ## getL1ChainId Returns the Chain Id of underlying L1 network. :::warning **This Action is being deprecated.** This method calls an RPC method that [will be removed in a future release](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1066). Please use the alternatives mentioned below. **Alternatives / Workaround** The returned value can be retrieve via an onchain call to the `L1_CHAIN_ID()` method on the `L2AssetRouter` contract (deployed on `0x0000000000000000000000000000000000010003` address) ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const chainId = await client.getL1ChainId(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Hex` L1 Chain ID. ## getL1TokenAddress Returns the L1 token address equivalent for a L2 token address as they are not equal. :::info Only works for tokens bridged on default ZKsync Era bridges. ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const address = await client.getL1TokenAddress({ token: '0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b' }) ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Address` Returns the L1 token address equivalent for a L2 token address. ### Parameters #### token * **Type:** `Address` The address of the token on L2. ```ts const address = await client.getL1TokenAddress({ token: '0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b' }) ``` ## getL1TokenBalance Retrieve the token balance held by the contract on L1. ### Usage :::code-group ```ts [example.ts] import { account, publicClient } from './config' const balance = await publicClient.getL1TokenBalance({ account token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` ```ts [config.ts] import { createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { mainnet } from 'viem/chains' import { publicActionsL1 } from 'viem/zksync' export const publicClient = createPublicClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(publicActionsL1()) // JSON-RPC Account export const account = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // Local Account export const account = privateKeyToAccount(...) ``` ::: ### Returns `bigint` Returns the amount of the tokens. ### Parameters #### account * **Type:** `Account | Address` The Account used for check. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const balance = await publicClient.getL1TokenBalance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' // [!code focus] blockTag: 'latest', token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### blockTag (optional) * **Type:** `BlockTag | undefined` In which block an balance should be checked on. The latest processed one is the default option. ```ts const balance = await publicClient.getL1TokenBalance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' blockTag: 'latest', // [!code focus] token: '0x5C221E77624690fff6dd741493D735a17716c26B', }) ``` #### token * **Type:** `Address` The address of the token. ```ts const balance = await publicClient.getL1TokenBalance({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', blockTag: 'latest', token: '0x5C221E77624690fff6dd741493D735a17716c26B', // [!code focus] }) ``` ## getL2TokenAddress Returns the L2 token address equivalent for a L1 token address as they are not equal. :::info Only works for tokens bridged on default ZKsync Era bridges. ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const address = await client.getL2TokenAddress({ token: '0x5C221E77624690fff6dd741493D735a17716c26B' }) ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Address` Returns the L2 token address equivalent for a L1 token address. ### Parameters #### token * **Type:** `Address` The address of the token on L1. ```ts const address = await client.getL2TokenAddress({ token: '0x5C221E77624690fff6dd741493D735a17716c26B' }) ``` #### bridgeAddress (optional) * **Type:** `Address` The address of custom bridge, which will be used to get l2 token address. ```ts const address = await client.getL2TokenAddress({ token: '0x5C221E77624690fff6dd741493D735a17716c26B', bridgeAddress: '0xf8c919286126ccf2e8abc362a15158a461429c82' // [!code focus] }) ``` ## getLogProof Returns the proof for the corresponding L2 to L1 log. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const proof = await client.getLogProof({ txHash: '0x...', index: 1 }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `GetLogProofReturnType` Proof of the corresponding L2 to L1 log ### Parameters #### txHash Hash of the L2 transaction the L2 to L1 log was produced within. ```ts const proof = await client.getLogProof({ txHash: '0x...', // [!code focus] index: 1 }); ``` #### index (optional) The index of the L2 to L1 log in the transaction. ```ts const proof = await client.getLogProof({ txHash: '0x...', index: 1 // [!code focus] }); ``` ## getMainContractAddress Returns the address of a main ZKsync Chain Contract. :::warning **This Action is being deprecated.** This method calls an RPC method that [will be removed in a future release](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1066). Please use the alternatives mentioned below. **Alternatives / Workaround** This method returned the address of the chain contract in L1. This can be retrieved from a call to the BridgeHub contract. 1. Retrieve the BridgeHub contract address using the [`getBridgeHubContractAddress`](/zksync/actions/getBridgehubContractAddress) action. 2. Call the `getZKChain(chainId)` method on the BridgeHub contract. ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const address = await client.getMainContractAddress(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Address` Main ZKsync Era smart contract address. ## getRawBlockTransaction Returns data of transactions in a block. :::warning **This Action is being deprecated.** This method calls an RPC method that [will be removed in a future release](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1066). Please use the alternatives mentioned below. **Alternatives / Workaround** The returned value can be retrieved using `debug_getRawTransaction` and `debug_getRawTransactions`. ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const rawTx = await client.getRawBlockTransaction({ number: 1 }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `RawBlockTransactions` Data of transactions in a block. ### Parameters #### number Block number. ```ts const rawTx = await client.getRawBlockTransaction({ number: 1 // [!code focus] }); ``` ## getTestnetPaymasterAddress (depreated) Returns the address of the testnet Paymaster. :::warning **This Action is being deprecated.** This method calls an RPC method that [will be removed in a future release](https://github.com/zkSync-Community-Hub/zksync-developers/discussions/1066). Please use the alternatives mentioned below. **Alternatives / Workaround** The returned value can be found on each ZKsync chain technical documentation (e.g [ZKsync Era docs](https://docs.zksync.io/zksync-era/unique-features/paymaster#testnet-paymaster)). ::: ### Usage :::code-group ```ts [example.ts] import { client } from './config' const address = await client.getTestnetPaymasterAddress(); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `Address | null` Testnet paymaster address if available, or `null`. ## getTransactionDetails Returns data from a specific transaction given by the transaction hash. ### Usage :::code-group ```ts [example.ts] import { client } from './config' const details = await client.getTransactionDetails({ txHash: '0x...' }); ``` ```ts [config.ts] import { createPublicClient, http } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2 } from 'viem/zksync' export const client = createPublicClient({ chain: zksync, transport: http(), }).extend(publicActionsL2()) ``` ::: ### Returns `TransactionDetails` Data from a specific transaction given by the transaction hash. ### Parameters `GetTransactionDetailsParameters` #### hash Transaction hash ```ts const details = await client.getTransactionDetails({ txHash: '0x...' // [!code focus] }); ``` ## isWithdrawalFinalized Returns whether the withdrawal transaction is finalized on the L1 network. ### Usage :::code-group ```ts [example.ts] import { client, zksyncClient } from './config' const hash = await client.isWithdrawalFinalized({ client: zksyncClient, hash: '0x…', }) ``` ```ts [config.ts] import { createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync, mainnet } from 'viem/chains' import { publicActionsL2, publicActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const client = createPublicClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(publicActionsL1()) ``` ::: ### Returns `boolean` Whether the withdrawal transaction is finalized on the L1 network. ### Parameters #### client * **Type:** `Client` The L2 client for fetching data from L2 chain. ```ts const hash = await client.isWithdrawalFinalized({ client: zksyncClient, // [!code focus] hash: '0x…', }) ``` #### hash * **Type:** `Hex` Hash of the L2 transaction where the withdrawal was initiated. ```ts const hash = await client.isWithdrawalFinalized({ client: zksyncClient, hash: '0x…', // [!code focus] }) ``` #### index (optional) * **Type:** `number` * **Default:** `0` In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. ```ts const hash = await client.isWithdrawalFinalized({ client: zksyncClient, hash: '0x…', index: 0n, // [!code focus] }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `client.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await client.isWithdrawalFinalized({ chain: zksync, // [!code focus] client: zksyncClient, hash: '0x…', }) ``` ## requestExecute Requests execution of a L2 transaction from L1. ### Usage :::code-group ```ts [example.ts] import { account, walletClient, zksyncClient } from './config' const hash = await walletClient.requestExecute({ account, client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n }) ``` ```ts [config.ts] import { createWalletClient, createPublicClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync, mainnet } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `requestExecute`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient, zksyncClient } from './config' const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n }) ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) // Retrieve Account from an EIP-1193 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(walletActionsL1()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { zksync } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' import { publicActionsL2, walletActionsL1 } from 'viem/zksync' export const zksyncClient = createPublicClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(walletActionsL1()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### client * **Type:** `Client` The L2 client for fetching data from L2 chain. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, // [!code focus] contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n }) ``` #### contractAddress * **Type:** `Address` The L2 contract to be called. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), // [!code focus] calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n }) ``` #### calldata * **Type:** `Hex` The input of the L2 transaction. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', // [!code focus] l2Value: 7_000_000_000n, l2GasLimit: 900_000n }) ``` #### l2Value (optional) * **Type:** `bigint` The `msg.value` of L2 transaction. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, // [!code focus] l2GasLimit: 900_000n }) ``` #### l2GasLimit (optional) * **Type:** `bigint` Maximum amount of L2 gas that transaction can consume during execution on L2. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n // [!code focus] }) ``` #### mintValue (optional) * **Type:** `bigint` The amount of base token that needs to be minted on non-ETH-based L2. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n, mintValue: 100_000n // [!code focus] }) ``` #### factoryDeps (optional) * **Type:** `Hex[]` An array of L2 bytecodes that will be marked as known on L2. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n, factoryDeps: ['0x...'] // [!code focus] }) ``` #### operatorTip (optional) * **Type:** `bigint` The tip the operator will receive on top of the base cost of the transaction. Currently, ZKsync node do not consider this tip. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n, operatorTip: 100_000n // [!code focus] }) ``` #### gasPerPubdataByte (optional) * **Type:** `bigint` The L2 gas price for each published L1 calldata byte. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n, gasPerPubdataByte: 250_000_000_000n // [!code focus] }) ``` #### refundRecipient (optional) * **Type:** `Address` * **Default:** `walletClient.account` The address on L2 that will receive the refund for the transaction. If the transaction fails, it will also be the address to receive `l2Value`. ```ts const hash = await walletClient.requestExecute({ client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n, refundRecipient: '0x...' // [!code focus] }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.requestExecute({ chain: zksync, // [!code focus] client: zksyncClient, contractAddress: await zksyncClient.getBridgehubContractAddress(), calldata: '0x', l2Value: 7_000_000_000n, l2GasLimit: 900_000n }) ``` ## sendTransaction Creates, signs, and sends a new transaction to the network, with EIP712 transaction support. ### Usage :::code-group ```ts [example.ts] import { account, walletClient } from './config' const hash = await walletClient.sendTransaction({ // [!code focus:99] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) // '0x...' ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712Actions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(eip712WalletActions()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `sendTransaction`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient } from './config' const hash = await walletClient.sendTransaction({ // [!code focus:99] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) // '0x...' ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { eip712Actions } from 'viem/zksync' // Retrieve Account from an EIP-712 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(eip712WalletActions()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { eip712Actions } from 'viem/zksync' export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(eip712WalletActions()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### account * **Type:** `Account | Address` The Account to send the transaction from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const hash = await walletClient.sendTransaction({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### to * **Type:** `0x${string}` The transaction recipient or contract address. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus] value: 1000000000000000000n, nonce: 69 }) ``` #### accessList (optional) * **Type:** [`AccessList`](/docs/glossary/types#accesslist) The access list. ```ts const hash = await walletClient.sendTransaction({ accessList: [ // [!code focus:6] { address: '0x1', storageKeys: ['0x1'], }, ], account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. The chain is also used to infer its request type (e.g. the Celo chain has a `gatewayFee` that you can pass through to `sendTransaction`). ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.sendTransaction({ chain: zksync, // [!code focus] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### data (optional) * **Type:** `0x${string}` A contract hashed method call with encoded args. ```ts const hash = await walletClient.sendTransaction({ data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // [!code focus] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### gasPrice (optional) * **Type:** `bigint` The price (in wei) to pay per gas. Only applies to [Legacy Transactions](/docs/glossary/terms#legacy-transaction). ```ts const hash = await walletClient.sendTransaction({ account, gasPrice: parseGwei('20'), // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### nonce (optional) * **Type:** `number` Unique number identifying this transaction. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n, nonce: 69 // [!code focus] }) ``` #### value (optional) * **Type:** `bigint` Value in wei sent with this transaction. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: parseEther('1'), // [!code focus] nonce: 69 }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', gasPerPubdata: 50000, // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### factoryDeps (optional) * **Type:** `[0x${string}]` Contains bytecode of the deployed contract. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', factoryDeps: ['0xcde...'], // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the fees. The `paymasterInput` field is required with this one. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const hash = await walletClient.sendTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69, value: 1000000000000000000n }) ``` ## signTransaction Signs a transaction, with EIP712 transaction support. ### Usage :::code-group ```ts [example.ts] import { account, walletClient } from './config' const request = await walletClient.prepareTransactionRequest({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) const signature = await walletClient.signTransaction(request) // [!code focus:2] // 0x02f850018203118080825208808080c080a04012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1a04e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33 const hash = await walletClient.sendRawTransaction(signature) ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712WalletActions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(eip712WalletActions()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `prepareTransactionRequest`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient } from './config' const request = await walletClient.prepareTransactionRequest({ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) const signature = await walletClient.signTransaction(request) // [!code focus:2] // 0x02f850018203118080825208808080c080a04012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1a04e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33 const hash = await client.sendRawTransaction(signature) ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { eip712WalletActions } from 'viem/zksync' // Retrieve Account from an EIP-712 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, // [!code focus] transport: custom(window.ethereum) }).extend(eip712WalletActions()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { eip712WalletActions } from 'viem/zksync' export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(eip712WalletActions()) ``` ::: ### Returns [`Hex`](/docs/glossary/types#hex) The signed serialized transaction. ### Parameters #### account * **Type:** `Account | Address` The Account to send the transaction from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const signature = await walletClient.signTransaction({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### to * **Type:** `0x${string}` The transaction recipient or contract address. ```ts const signature = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus] value: 1000000000000000000n, nonce: 69 }) ``` #### accessList (optional) * **Type:** [`AccessList`](/docs/glossary/types#accesslist) The access list. ```ts const signature = await walletClient.signTransaction({ accessList: [ // [!code focus:6] { address: '0x1', storageKeys: ['0x1'], }, ], account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. The chain is also used to infer its request type (e.g. the Celo chain has a `gatewayFee` that you can pass through to `signTransaction`). ```ts import { zksync } from 'viem/chains' // [!code focus] const signature = await walletClient.signTransaction({ chain: zksync, // [!code focus] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### data (optional) * **Type:** `0x${string}` A contract hashed method call with encoded args. ```ts const signature = await walletClient.signTransaction({ data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // [!code focus] account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n }) ``` #### gasPrice (optional) * **Type:** `bigint` The price (in wei) to pay per gas. Only applies to [Legacy Transactions](/docs/glossary/terms#legacy-transaction). ```ts const signature = await walletClient.signTransaction({ account, gasPrice: parseGwei('20'), // [!code focus] to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: parseEther('1') }) ``` #### nonce (optional) * **Type:** `number` Unique number identifying this transaction. ```ts const signature = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: 1000000000000000000n, nonce: 69 // [!code focus] }) ``` #### value (optional) * **Type:** `bigint` Value in wei sent with this transaction. ```ts const signature = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', value: parseEther('1'), // [!code focus] nonce: 69 }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const hash = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', gasPerPubdata: 50000, // [!code focus] nonce: 69 }) ``` #### factoryDeps (optional) * **Type:** `[0x${string}]` Contains bytecode of the deployed contract. ```ts const hash = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', factoryDeps: ['0xcde...'], // [!code focus] nonce: 69 }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the fees. The `paymasterInput` field is required with this one. ```ts const hash = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69 }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const hash = await walletClient.signTransaction({ account, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] nonce: 69 }) ``` ## withdraw Initiates the withdrawal process which withdraws ETH or any ERC20 token from the associated account on L2 network to the target account on L1 network. ### Usage :::code-group ```ts [example.ts] import { account, walletClient } from './config' import { legacyEthAddress } from 'viem/zksync' const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, }) ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712Actions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(publicActionsL2()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `withdraw`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient } from './config' import { legacyEthAddress } from 'viem/zksync' const hash = await walletClient.withdraw({ // [!code focus:99] amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, }) // '0x...' ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { publicActionsL2 } from 'viem/zksync' // Retrieve Account from an EIP-712 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { publicActionsL2 } from 'viem/zksync' export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(publicActionsL2()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### account * **Type:** `Account | Address` The Account to send the transaction from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts const hash = await walletClient.withdraw({ account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus] amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, }) ``` #### amount * **Type:** `bigint` The amount of the token to withdraw. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, // [!code focus] token: legacyEthAddress, }) ``` #### token * **Type:** `Address` The address of the token on L2. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, // [!code focus] }) ``` #### bridgeAddress (optional) * **Type:** `Address` The address of the bridge contract to be used. By default, uses shared bridge. ```ts const address = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, bridgeAddress: '0xf8c919286126ccf2e8abc362a15158a461429c82' // [!code focus] }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. The chain is also used to infer its request type. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.withdraw({ chain: zksync, // [!code focus] account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, gasPerPubdata: 50000, // [!code focus] }) ``` #### gasPrice (optional) * **Type:** `bigint` The price (in wei) to pay per gas. Only applies to [Legacy Transactions](/docs/glossary/terms#legacy-transaction). ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, gasPrice: parseGwei('20'), // [!code focus] }) ``` #### maxFeePerGas (optional) * **Type:** `bigint` Total fee per gas (in wei), inclusive of `maxPriorityFeePerGas`. Only applies to [EIP-1559 Transactions](/docs/glossary/terms#eip-1559-transaction) ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, maxFeePerGas: parseGwei('20'), // [!code focus] }) ``` #### maxPriorityFeePerGas (optional) * **Type:** `bigint` Max priority fee per gas (in wei). Only applies to [EIP-1559 Transactions](/docs/glossary/terms#eip-1559-transaction) ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, maxFeePerGas: parseGwei('20'), maxPriorityFeePerGas: parseGwei('2'), // [!code focus] }) ``` #### nonce (optional) * **Type:** `number` Unique number identifying this transaction. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, nonce: 69 // [!code focus] }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the fees. The `paymasterInput` field is required with this one. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] }) ``` #### to (optional) * **Type:** `Address` The address of the recipient on L1. Defaults to the sender address. ```ts const hash = await walletClient.withdraw({ account, amount: 1_000_000_000_000_000_000n, token: legacyEthAddress, to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus] }) ``` ## writeContract Executes a write function on a contract, with EIP712 transaction support. ### Usage :::code-group ```ts [example.ts] import { account, walletClient } from './config' const hash = await walletClient.writeContract({ // [!code focus:99] account, address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', }) // '0x...' ``` ```ts [config.ts] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { zksync } from 'viem/chains' import { eip712WalletActions } from 'viem/zksync' export const walletClient = createWalletClient({ chain: zksync, transport: custom(window.ethereum) }).extend(eip712WalletActions()) // JSON-RPC Account export const [account] = await walletClient.getAddresses() // Local Account export const account = privateKeyToAccount(...) ``` ::: #### Account Hoisting If you do not wish to pass an `account` to every `sendTransaction`, you can also hoist the Account on the Wallet Client (see `config.ts`). [Learn more](/docs/clients/wallet#account). :::code-group ```ts [example.ts] import { walletClient } from './config' const hash = await walletClient.writeContract({ // [!code focus:99] account, address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', }) // '0x...' ``` ```ts [config.ts (JSON-RPC Account)] import { createWalletClient, custom } from 'viem' import { eip712WalletActions } from 'viem/zksync' // Retrieve Account from an EIP-712 Provider. // [!code focus] const [account] = await window.ethereum.request({ // [!code focus] method: 'eth_requestAccounts' // [!code focus] }) // [!code focus] export const walletClient = createWalletClient({ account, transport: custom(window.ethereum) // [!code focus] }).extend(eip712WalletActions()) ``` ```ts [config.ts (Local Account)] import { createWalletClient, custom } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { eip712WalletActions } from 'viem/zksync' export const walletClient = createWalletClient({ account: privateKeyToAccount('0x...'), // [!code focus] transport: custom(window.ethereum) }).extend(eip712WalletActions()) ``` ::: ### Returns [`Hash`](/docs/glossary/types#hash) The [Transaction](/docs/glossary/terms#transaction) hash. ### Parameters #### address * **Type:** [`Address`](/docs/glossary/types#address) The contract address. ```ts await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus] abi: wagmiAbi, functionName: 'mint', args: [69420] }) ``` #### abi * **Type:** [`Abi`](/docs/glossary/types#abi) The contract's ABI. ```ts await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, // [!code focus] functionName: 'mint', args: [69420] }) ``` #### functionName * **Type:** `string` A function to extract from the ABI. ```ts await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', // [!code focus] args: [69420] }) ``` #### account * **Type:** `Account | Address` The Account to send the transaction from. Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). ```ts await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus] abi: wagmiAbi, functionName: 'mint', args: [69420] }) ``` #### accessList (optional) * **Type:** [`AccessList`](/docs/glossary/types#accesslist) The access list. ```ts const hash = await walletClient.writeContract({ accessList: [ // [!code focus:6] { address: '0x1', storageKeys: ['0x1'], }, ], address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420] }) ``` #### chain (optional) * **Type:** [`Chain`](/docs/glossary/types#chain) * **Default:** `walletClient.chain` The target chain. If there is a mismatch between the wallet's current chain & the target chain, an error will be thrown. ```ts import { zksync } from 'viem/chains' // [!code focus] const hash = await walletClient.writeContract({ chain: zksync, // [!code focus] address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420] }) ``` #### data (optional) * **Type:** `0x${string}` A contract hashed method call with encoded args. ```ts const hash = await walletClient.writeContract({ data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // [!code focus] address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420] }) ``` #### gasPrice (optional) * **Type:** `bigint` The price (in wei) to pay per gas. Only applies to [Legacy Transactions](/docs/glossary/terms#legacy-transaction). ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], gasPrice: parseGwei('20'), // [!code focus] }) ``` #### nonce (optional) * **Type:** `number` Unique number identifying this transaction. ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], nonce: 69 // [!code focus] }) ``` #### value (optional) * **Type:** `bigint` Value in wei sent with this transaction. ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], value: parseEther('1'), // [!code focus] }) ``` #### gasPerPubdata (optional) * **Type:** `bigint` The amount of gas for publishing one byte of data on Ethereum. ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], gasPerPubdata: 50000, // [!code focus] }) ``` #### factoryDeps (optional) * **Type:** `[0x${string}]` Contains bytecode of the deployed contract. ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], factoryDeps: ['0xcde...'], // [!code focus] }) ``` #### paymaster (optional) * **Type:** `Account | Address` Address of the paymaster account that will pay the fees. The `paymasterInput` field is required with this one. ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] }) ``` #### paymasterInput (optional) * **Type:** `0x${string}` Input data to the paymaster. The `paymaster` field is required with this one. ```ts const hash = await walletClient.writeContract({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', abi: wagmiAbi, functionName: 'mint', args: [69420], paymaster: '0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021', // [!code focus] paymasterInput: '0x8c5a...' // [!code focus] }) ``` ## toMultisigSmartAccount (ZKsync) Creates a multi-signature [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and the Private Key of the owner. ### Usage ```ts twoslash import { toMultisigSmartAccount } from 'viem/zksync' const account = toMultisigSmartAccount({ address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266', privateKeys: ['0x...', '0x...'] }) ``` ### Parameters #### address * **Type:** `Hex` Address of the deployed Account's Contract implementation. ```ts const account = toMultisigSmartAccount({ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus] privateKeys: ['0x...', '0x...'] }) ``` #### privateKeys * **Type:** `Hex[]` Private Keys of the owners. ```ts const account = toMultisigSmartAccount({ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', privateKeys: ['0x...', '0x...'] // [!code focus] }) ``` ## toSinglesigSmartAccount (ZKsync) Creates a single-signature [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and the Private Key of the owner. ### Usage ```ts twoslash import { toSinglesigSmartAccount } from 'viem/zksync' const account = toSinglesigSmartAccount({ address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266', privateKey: '0x...' }) ``` ### Parameters #### address * **Type:** `Hex` Address of the deployed Account's Contract implementation. ```ts const account = toSinglesigSmartAccount({ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus] privateKey: '0x...' }) ``` #### privateKey * **Type:** `Hex` Private Key of the owner. ```ts const account = toSinglesigSmartAccount({ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', privateKey: '0x...' // [!code focus] }) ``` ## toSmartAccount (ZKsync) Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and a custom sign function. ### Usage ```ts twoslash import { toSmartAccount } from 'viem/zksync' const account = toSmartAccount({ address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266', async sign({ hash }) { // ... signing logic return '0x...' } }) ``` ### Parameters #### address * **Type:** `Hex` Address of the deployed Account's Contract implementation. ```ts const account = toSmartAccount({ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus] async sign({ hash }) { // ... } }) ``` #### sign * **Type:** `({ hash: Hex }) => Hex` Custom sign function for the Smart Account. ```ts const account = toSmartAccount({ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', async sign({ hash }) { // [!code focus] // ... // [!code focus] } // [!code focus] }) ``` ## Chains The following Viem chains are implemented on the OP Stack: ```ts import { base, // [!code hl] baseGoerli, // [!code hl] baseSepolia, // [!code hl] fraxtal, // [!code hl] fraxtalTestnet, // [!code hl] ink, // [!code hl] inkSepolia, // [!code hl] optimism, // [!code hl] optimismGoerli, // [!code hl] optimismSepolia, // [!code hl] soneium, // [!code hl] soneiumMinato, // [!code hl] unichain, // [!code hl] unichainSepolia, // [!code hl] zircuit, // [!code hl] zircuitGarfieldTestnet, // [!code hl] zora, // [!code hl] zoraSepolia, // [!code hl] zoraTestnet, // [!code hl] } from 'viem/chains' ``` ### Configuration Viem exports OP Stack's chain [formatters](/docs/chains/formatters) & [serializers](/docs/chains/serializers) via `chainConfig`. This is useful if you need to define another chain which is implemented on the OP Stack. ```ts import { defineChain } from 'viem' import { chainConfig } from 'viem/op-stack' export const opStackExample = defineChain({ ...chainConfig, name: 'OP Stack Example', // ... }) ``` ## Client \[Setting up your Viem Client with the OP Stack] To use the OP Stack functionality of Viem, you must extend your existing (or new) Viem Client with OP Stack Actions. ### Usage #### Layer 1 Extensions ```ts import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet } from 'viem/chains' import { walletActionsL1 } from 'viem/op-stack' // [!code hl] const walletClient = createWalletClient({ chain: mainnet, transport: http(), }).extend(walletActionsL1()) // [!code hl] const hash = await walletClient.depositTransaction({/* ... */}) ``` #### Layer 2 Extensions ```ts import { createPublicClient, http } from 'viem' import { base } from 'viem/chains' import { publicActionsL2 } from 'viem/op-stack' // [!code hl] const publicClient = createPublicClient({ chain: base, transport: http(), }).extend(publicActionsL2()) // [!code hl] const l1Gas = await publicClient.estimateL1Gas({/* ... */}) ``` ### Extensions #### `walletActionsL1` A suite of [Wallet Actions](/op-stack/actions/estimateL1Gas) for suited for development with **Layer 1** chains that interact with **Layer 2 (OP Stack)** chains. ```ts import { walletActionsL1 } from 'viem/op-stack' ``` #### `publicActionsL1` A suite of [Public Actions](/op-stack/actions/getTimeToProve) suited for development with **Layer 1** chains. These actions provide functionalities specific to public clients operating at the Layer 1 level, enabling them to interact seamlessly with Layer 2 protocols. ```ts import { publicActionsL1 } from 'viem/op-stack' ``` #### `walletActionsL2` A suite of [Wallet Actions](/op-stack/actions/estimateL1Fee) suited for development with **Layer 2 (OP Stack)** chains. These actions are tailored for wallets operating on Layer 2, providing advanced features and integrations necessary for Layer 2 financial operations. ```ts import { walletActionsL2 } from 'viem/op-stack' ``` #### `publicActionsL2` A suite of [Public Actions](/op-stack/actions/estimateL1Gas) for suited for development with **Layer 2 (OP Stack)** chains. ```ts import { publicActionsL2 } from 'viem/op-stack' ``` ## extractTransactionDepositedLogs Extracts `TransactionDeposited` logs from an opaque array of logs. ### Import ```ts import { extractTransactionDepositedLogs } from 'viem' ``` ### Usage ```ts import { extractTransactionDepositedLogs } from 'viem' const receipt = await client.getTransactionReceipt({ hash: '0xc9c0361bc3da9cd3560e48b469d0d6aac0e633e4897895edfd26a287f7c578ec', }) const logs = extractTransactionDepositedLogs(receipt) // [ // { args: { ... }, blockHash: '0x...', eventName: 'TransactionDeposited' }, // { args: { ... }, blockHash: '0x...', eventName: 'TransactionDeposited' }, // { args: { ... }, blockHash: '0x...', eventName: 'TransactionDeposited' }, // ] ``` ### Returns `Log[]` The `TransactionDeposited` logs. ### Parameters #### logs * **Type:** `Log[]` An array of opaque logs. ```ts const logs = extractTransactionDepositedLogs({ logs: receipt.logs // [!code focus] }) ``` ## extractWithdrawalMessageLogs Extracts [`MessagePassed` logs](https://github.com/ethereum-optimism/optimism/blob/9f73402cb4341f7cfa83bf79769c8dddd9b014c0/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol#L29-L45) from a withdrawal initialization from an opaque array of logs. ### Import ```ts import { extractWithdrawalMessageLogs } from 'viem' ``` ### Usage ```ts import { extractWithdrawalMessageLogs } from 'viem' const receipt = await client.getTransactionReceipt({ hash: '0xc9c0361bc3da9cd3560e48b469d0d6aac0e633e4897895edfd26a287f7c578ec', }) const logs = extractWithdrawalMessageLogs(receipt) // [ // { args: { ... }, blockHash: '0x...', eventName: 'MessagePassed' }, // { args: { ... }, blockHash: '0x...', eventName: 'MessagePassed' }, // { args: { ... }, blockHash: '0x...', eventName: 'MessagePassed' }, // ] ``` ### Returns `Log[]` The `MessagePassed` logs. ### Parameters #### logs * **Type:** `Log[]` An array of opaque logs. ```ts const logs = extractWithdrawalMessageLogs({ logs: receipt.logs // [!code focus] }) ``` ## opaqueDataToDepositData Decodes opaque deposit data found in the `TransactionDeposited` event log data. ### Import ```ts import { opaqueDataToDepositData } from 'viem' ``` ### Usage ```ts import { opaqueDataToDepositData } from 'viem' const data = opaqueDataToDepositData('0x00000000000000000000000000000000000000000000000000000000000001a40000000000000000000000000000000000000000000000000000000000000045000000000000526c01deadbeef') ``` ### Returns ``` { mint: bigint value: bigint gas: bigint isCreation: boolean data: Hex } ``` The decoded opaque data. ### Parameters #### opaqueData * **Type:** `Hex` The ABI (packed) encoded opaque data. ## getL2TransactionHash Computes the L2 transaction hash from an L1 `TransactionDeposited` log. :::warning For the general case of retrieving an L2 transaction hash from an L1 transaction receipt, you probably want to use [getL2TransactionHashes](/op-stack/utilities/getL2TransactionHashes). ::: ### Import ```ts import { getL2TransactionHash } from 'viem' ``` ### Usage ```ts import { extractTransactionDepositedLogs, getL2TransactionHash } from 'viem' const receipt = await client.getTransactionReceipt({ hash: '0xa08acae48f12243bccd7153c88d892673d5578cce4ee9988c0332e8bba47436b', }) const [log] = extractTransactionDepositedLogs(receipt) const l2Hash = getL2TransactionHash({ log }) // [!code hl] ``` ### Returns `Hex` The L2 transaction hash. ### Parameters #### log * **Type:** `Log` An L1 `TransactionDeposited` log. ```ts const l2Hash = getL2TransactionHash({ log: { // [!code focus] args: { // [!code focus] from: '0x1a1E021A302C237453D3D45c7B82B19cEEB7E2e6', // [!code focus] opaqueData: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045000000000000520800', // [!code focus] to: '0x1a1E021A302C237453D3D45c7B82B19cEEB7E2e6', // [!code focus] version: 0n, // [!code focus] }, // [!code focus] blockHash: '0x634c52556471c589f42db9131467e0c9484f5c73049e32d1a74e2a4ce0f91d57', // [!code focus] eventName: 'TransactionDeposited', // [!code focus] logIndex: 109, // [!code focus] } // [!code focus] }) ``` ## getL2TransactionHashes Computes the L2 transaction hashes from an array of L1 `TransactionDeposited` logs. Useful for extracting L2 hashes from an **L1 Transaction Receipt**. ### Import ```ts import { getL2TransactionHashes } from 'viem' ``` ### Usage ```ts import { extractTransactionDepositedLogs, getL2TransactionHashes } from 'viem' const receipt = await client.getTransactionReceipt({ hash: '0xa08acae48f12243bccd7153c88d892673d5578cce4ee9988c0332e8bba47436b', }) const l2Hashes = getL2TransactionHashes(receipt) // [!code hl] ``` ### Returns `Hex` The L2 transaction hash. ### Parameters #### logs * **Type:** `Log[]` An array of L1 logs. ```ts const l2Hashes = getL2TransactionHash({ logs: receipt.logs // [!code focus] }) ``` ## getSourceHash Computes the [source hash](https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md#source-hash-computation) of a deposit transaction. ### Import ```ts import { getSourceHash } from 'viem' ``` ### Usage ```ts import { getSourceHash } from 'viem' // User Deposit const sourceHash = getSourceHash({ domain: 'userDeposit', l1BlockHash: '0x9ba3933dc6ce43c145349770a39c30f9b647f17668f004bd2e05c80a2e7262f7', l1LogIndex: 196, }) // L1 attributes deposited const sourceHash = getSourceHash({ domain: 'l1InfoDeposit', l1BlockHash: '0x9ba3933dc6ce43c145349770a39c30f9b647f17668f004bd2e05c80a2e7262f7', sequenceNumber: 1, }) ``` ### Returns `Hex` The source hash of the deposit transaction. ### Parameters #### domain * **Type:** `"userDeposit" | "l1InfoDeposit"` The domain of the deposit transaction. ```ts const sourceHash = getSourceHash({ domain: 'userDeposit', // [!code focus] l1BlockHash: '0x9ba3933dc6ce43c145349770a39c30f9b647f17668f004bd2e05c80a2e7262f7', l1LogIndex: 196, }) ``` #### l1BlockHash * **Type:** `Hex` The hash of the L1 block the deposit transaction was included in. ```ts const sourceHash = getSourceHash({ domain: 'userDeposit', l1BlockHash: '0x9ba3933dc6ce43c145349770a39c30f9b647f17668f004bd2e05c80a2e7262f7', // [!code focus] l1LogIndex: 196, }) ``` #### l1LogIndex * **Type:** `number` The index of the L1 log. **Only required for `"userDeposit"` domain.** ```ts const sourceHash = getSourceHash({ domain: 'userDeposit', l1BlockHash: '0x9ba3933dc6ce43c145349770a39c30f9b647f17668f004bd2e05c80a2e7262f7', l1LogIndex: 196, // [!code focus] }) ``` #### sequenceNumber * **Type:** `number` The sequence number (difference between L2 block number and first L2 epoch block number). **Only required for `"l1InfoDeposit"` domain.** ```ts const sourceHash = getSourceHash({ domain: 'l1InfoDeposit', l1BlockHash: '0x9ba3933dc6ce43c145349770a39c30f9b647f17668f004bd2e05c80a2e7262f7', sequenceNumber: 1, // [!code focus] }) ``` ## getWithdrawalHashStorageSlot Computes the withdrawal hash storage slot to be used when proving a withdrawal. ### Import ```ts import { getWithdrawalHashStorageSlot } from 'viem' ``` ### Usage ```ts import { getWithdrawalHashStorageSlot } from 'viem' const slot = getWithdrawalHashStorageSlot({ // [!code hl] withdrawalHash: '0xB1C3824DEF40047847145E069BF467AA67E906611B9F5EF31515338DB0AABFA2' // [!code hl] }) // [!code hl] ``` ### Returns `Hex` The storage slot. ### Parameters #### withdrawalHash * **Type:** `Hash` Hash emitted from the L2 withdrawal `MessagePassed` event. ```ts const slot = getWithdrawalHashStorageSlot({ withdrawalHash: '0xB1C3824DEF40047847145E069BF467AA67E906611B9F5EF31515338DB0AABFA2' // [!code focus] }) ``` ## getWithdrawals Gets withdrawal messages emitted from the [`MessagePassed` log](https://github.com/ethereum-optimism/optimism/blob/9f73402cb4341f7cfa83bf79769c8dddd9b014c0/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol#L29-L45) from a withdrawal initialization. ### Import ```ts import { getWithdrawals } from 'viem' ``` ### Usage ```ts import { extractTransactionDepositedLogs, getWithdrawals } from 'viem' const receipt = await client.getTransactionReceipt({ hash: '0xa08acae48f12243bccd7153c88d892673d5578cce4ee9988c0332e8bba47436b', }) const withdrawals = getWithdrawals(receipt) // [!code hl] ``` ### Returns `Hex` The L2 transaction hash. ### Parameters #### logs * **Type:** `Log[]` An array of L2 logs. ```ts const withdrawals = getWithdrawals({ logs: receipt.logs // [!code focus] }) ``` ## opaqueDataToDepositData Converts an opaque data into a structured deposit data object. This includes extracting and converting the `mint`, `value`, `gas`, `isCreation` flag, and `data` from a hex string. ### Import ```ts import { opaqueDataToDepositData } from "viem"; ``` ### Usage ```ts import { opaqueDataToDepositData } from "viem"; const opaqueData = "0x00000000000000000000000000000000000000000000000000470DE4DF82000000000000000000000000000000000000000000000000000000470DE4DF82000000000000000186A00001"; const depositData = opaqueDataToDepositData(opaqueData); // { // mint: 20000000000000000n, // value: 20000000000000000n, // gas: 100000n, // isCreation: false, // data: '0x01', // } ``` ### Returns `OpaqueDataToDepositDataReturnType` An object containing the parsed deposit data. ### Parameters #### opaqueData * **Type:** `Hex` The opaque hex-encoded data. ### Errors `OpaqueDataToDepositDataErrorType` An error type that includes potential slice, size, and generic errors encountered during the parsing process. ## parseTransaction (OP Stack) Parses a serialized RLP-encoded transaction. Supports signed & unsigned Deposit, EIP-1559, EIP-2930 and Legacy Transactions. ### Import ```ts import { parseTransaction } from 'viem' ``` ### Usage ```ts import { parseTransaction } from 'viem' const transaction = parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') ``` #### Deposit Transactions The `parseTransaction` module from `viem/op-stack` also supports parsing deposit transactions (`0x7e`-prefixed): ```ts import { parseTransaction } from 'viem' const transaction = parseTransaction('0x7ef83ca018040f35752170c3339ddcd850f185c9cc46bdef4d6e1f2ab323f4d3d710431994977f82a600a1414e583f7f13623f1ac5d58b1c0b808080808080') ``` ### Returns `TransactionSerializable` The parsed transaction object. ### Parameters #### serializedTransaction * **Type:** `Hex` The serialized transaction. ## serializeTransaction (OP Stack) Serializes a transaction object, with support for OP Stack transactions. Supports Deposit, EIP-1559, EIP-2930, and Legacy transactions. ### Import ```ts import { serializeTransaction } from 'viem/op-stack' ``` ### Usage ```ts import { serializeTransaction } from 'viem/op-stack' const serialized = serializeTransaction({ chainId: 1, gas: 21001n, maxFeePerGas: parseGwei('20'), maxPriorityFeePerGas: parseGwei('2'), nonce: 69, to: "0x1234512345123451234512345123451234512345", value: parseEther('0.01'), }) ``` #### Deposit Transactions The `serializeTransaction` module from `viem/op-stack` also supports serializing deposit transactions: ```ts import { parseEther } from 'viem' import { serializeTransaction } from 'viem/op-stack' const serialized = serializeTransaction({ from: '0x977f82a600a1414e583f7f13623f1ac5d58b1c0b', gas: 21000n, mint: parseEther('1'), sourceHash: '0x18040f35752170c3339ddcd850f185c9cc46bdef4d6e1f2ab323f4d3d7104319', value: parseEther('1'), type: 'deposit' }) ``` ### Returns Returns a template `Hex` value based on transaction type: * `deposit`: [TransactionSerializedDeposit](/docs/glossary/types#TransactionSerializedDeposit) * `eip1559`: [TransactionSerializedEIP1559](/docs/glossary/types#TransactionSerializedEIP1559) * `eip2930`: [TransactionSerializedEIP2930](/docs/glossary/types#TransactionSerializedEIP2930) * `legacy`: [TransactionSerializedLegacy](/docs/glossary/types#TransactionSerializedLegacy) ### Parameters #### transaction * **Type:** `TransactionSerializable` The transaction object to serialize. ```ts const serialized = serializeTransaction({ chainId: 1, gas: 21001n, maxFeePerGas: parseGwei('20'), maxPriorityFeePerGas: parseGwei('2'), nonce: 69, to: '0x1234512345123451234512345123451234512345', value: parseEther('0.01'), }) ``` #### signature * **Type:** `Hex` Optional signature to include. **Ignored for deposit transactions.** ```ts const serialized = serializeTransaction({ chainId: 1, gas: 21001n, maxFeePerGas: parseGwei('20'), maxPriorityFeePerGas: parseGwei('2'), nonce: 69, to: '0x1234512345123451234512345123451234512345', value: parseEther('0.01'), }, { // [!code focus:5] r: '0x123451234512345123451234512345123451234512345123451234512345', s: '0x123451234512345123451234512345123451234512345123451234512345', yParity: 1 }) ``` ## Deposits This guide will demonstrate how to deposit (bridge) **1 Ether** from **Mainnet** to **[Optimism (OP Mainnet)](https://www.optimism.io/)**. ### Overview Here is an end-to-end overview of how to execute a deposit transaction. We will break it down into [Steps](#steps) below. :::code-group ```ts [deposit.ts] import { getL2TransactionHashes } from 'viem/op-stack' import { account, publicClientL1, publicClientL2, walletClientL1 } from './config' // Build parameters for the transaction on the L2. const args = await publicClientL2.buildDepositTransaction({ mint: parseEther('1'), to: account.address, }) // Execute the deposit transaction on the L1. const hash = await walletClientL1.depositTransaction(args) // Wait for the L1 transaction to be processed. const receipt = await publicClientL1.waitForTransactionReceipt({ hash }) // Get the L2 transaction hash from the L1 transaction receipt. const [l2Hash] = getL2TransactionHashes(receipt) // Wait for the L2 transaction to be processed. const l2Receipt = await publicClientL2.waitForTransactionReceipt({ hash: l2Hash }) ``` ```ts [config.ts (JSON-RPC Account)] import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: ### Steps #### 1. Set up Viem Clients First, we will set up our Viem Clients for the Mainnet and Optimism chains, including the necessary extensions for the OP Stack. We will place these in a `config.ts` file. :::info The example belows how to set up a Client for either a **JSON-RPC Account (Browser Extension, WalletConnect, etc)** or **Local Account (Private Key)** ::: :::code-group ```ts [config.ts (JSON-RPC Account)] // Import Viem modules. import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] // Import Viem modules. import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: #### 2. Build the Deposit Transaction Next, we will build the deposit transaction on the Optimism (L2) chain using the Clients that we created in the previous step. In the example below, we want to deposit **1 Ether** (via `mint`) onto the Optimism chain, to ourselves (`account.address`). :::info The `mint` value is the value to deposit (mint) on the Optimism (L2) chain. It is debited from the account's Mainnet (L1) balance. You can also use someone else's address as the `to` value if you wanted to. ::: :::code-group ```ts [deposit.ts] // Import Viem Clients. import { publicClientL2 } from './config' // Build parameters for the transaction on the L2. const args = await publicClientL2.buildDepositTransaction({ mint: parseEther('1'), to: account.address, }) ``` ```ts [config.ts (JSON-RPC Account)] // Import Viem modules. import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: custom(window.ethereum) }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] // Import Viem modules. import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: #### 3. Execute the Deposit Transaction After that, we will execute the deposit transaction on the Mainnet (L1) chain. :::code-group ```ts [deposit.ts] // Import Viem Clients. import { account, publicClientL2, walletClientL1 } from './config' // Build parameters for the transaction on the L2. const args = await publicClientL2.buildDepositTransaction({ mint: parseEther('1'), to: account.address, }) // Execute the deposit transaction on the L1. // [!code focus] const hash = await walletClientL1.depositTransaction(args) // [!code focus] ``` ```ts [config.ts (JSON-RPC Account)] // Import Viem modules. import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const clientL1 = createClient({ account, chain: mainnet, transport: fallback([custom(window.ethereum), http()]) }) .extend(publicActions) .extend(walletActionsL1()) export const clientL2 = createClient({ chain: optimism, transport: http() }) .extend(publicActions) .extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] // Import Viem modules. import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: #### 4. Wait for Transaction to be Processed Once we have broadcast the transaction to the Mainnet (L1) chain, we need to wait for it to be processed on a block so we can extract the transaction receipt. We will need the transaction receipt to extract the transaction on the Optimism (L2) chain. :::info When the transaction has been processed, the `mint` value (1 Ether) will be debited from the account's Mainnet (L1) balance. ::: :::code-group ```ts [deposit.ts] // Import Viem Clients. import { account, publicClientL1, publicClientL2, walletClientL1 } from './config' // Build parameters for the transaction on the L2. const args = await publicClientL2.buildDepositTransaction({ mint: parseEther('1'), to: account.address, }) // Execute the deposit transaction on the L1. const hash = await walletClientL1.depositTransaction(args) // Wait for the L1 transaction to be processed. // [!code focus] const receipt = await publicClientL1.waitForTransactionReceipt({ hash }) // [!code focus] ``` ```ts [config.ts (JSON-RPC Account)] // Import Viem modules. import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const clientL1 = createClient({ account, chain: mainnet, transport: fallback([custom(window.ethereum), http()]) }) .extend(publicActions) .extend(walletActionsL1()) export const clientL2 = createClient({ chain: optimism, transport: http() }) .extend(publicActions) .extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] // Import Viem modules. import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: #### 5. Compute the L2 Transaction Hash Once we have the transaction receipt from the Mainnet (L1) chain, we can extract the Optimism (L2) transaction hash from the logs in the transaction receipt. :::code-group ```ts [deposit.ts] // Import Viem Clients. import { account, publicClientL1, publicClientL2, walletClientL1 } from './config' // Build parameters for the transaction on the L2. const args = await publicClientL2.buildDepositTransaction({ mint: parseEther('1'), to: account.address, }) // Execute the deposit transaction on the L1. const hash = await walletClientL1.depositTransaction(args) // Wait for the L1 transaction to be processed. const receipt = await publicClientL1.waitForTransactionReceipt({ hash }) // Get the L2 transaction hash from the L1 transaction receipt. // [!code focus] const [l2Hash] = getL2TransactionHashes(receipt) // [!code focus] ``` ```ts [config.ts (JSON-RPC Account)] // Import Viem modules. import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const clientL1 = createClient({ account, chain: mainnet, transport: fallback([custom(window.ethereum), http()]) }) .extend(publicActions) .extend(walletActionsL1()) export const clientL2 = createClient({ chain: optimism, transport: http() }) .extend(publicActions) .extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] // Import Viem modules. import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: #### 6. Wait for Transaction to be Processed Now that we have the Optimism (L2) transaction hash, we can wait for the transaction to be processed on the Optimism (L2) chain. Once the `waitForTransactionReceipt` call resolves, the transaction has been processed and you should now be credited with 1 Ether on the Optimism (L2) chain 🥳. :::code-group ```ts [deposit.ts] // Import Viem Clients. import { account, publicClientL1, publicClientL2, walletClientL1 } from './config' // Build parameters for the transaction on the L2. const args = await publicClientL2.buildDepositTransaction({ mint: parseEther('1'), to: account.address, }) // Execute the deposit transaction on the L1. const hash = await walletClientL1.depositTransaction(args) // Wait for the L1 transaction to be processed. const receipt = await publicClientL1.waitForTransactionReceipt({ hash }) // Get the L2 transaction hash from the L1 transaction receipt. const [l2Hash] = getL2TransactionHashes(receipt) // Wait for the L2 transaction to be processed. // [!code focus] const l2Receipt = await publicClientL2.waitForTransactionReceipt({ // [!code focus] hash: l2Hash // [!code focus] }) // [!code focus] ``` ```ts [config.ts (JSON-RPC Account)] // Import Viem modules. import { createPublicClient, createWalletClient, custom, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' // Retrieve Account from an EIP-1193 Provider. export const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }) export const clientL1 = createClient({ account, chain: mainnet, transport: fallback([custom(window.ethereum), http()]) }) .extend(publicActions) .extend(walletActionsL1()) export const clientL2 = createClient({ chain: optimism, transport: http() }) .extend(publicActions) .extend(publicActionsL2()) ``` ```ts [config.ts (Local Account)] // Import Viem modules. import { createPublicClient, createWalletClient, http } from 'viem' import { mainnet, optimism } from 'viem/chains' import { publicActionsL2, walletActionsL1 } from 'viem/op-stack' export const account = privateKeyToAccount('0x...') export const publicClientL1 = createPublicClient({ chain: mainnet, transport: http() }) export const walletClientL1 = createWalletClient({ account, chain: mainnet, transport: http() }).extend(walletActionsL1()) export const publicClientL2 = createPublicClient({ chain: optimism, transport: http() }).extend(publicActionsL2()) ``` ::: ### Example