From e2f68775201ffeab3f79afbb800a5a9c0d3300d8 Mon Sep 17 00:00:00 2001 From: Danijel Radakovic <129277218+danijelTxFusion@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:47:13 +0100 Subject: [PATCH] feat: ethers release update (#882) --- docs/build/sdks/go/accounts.md | 22 +- docs/build/sdks/go/clients.md | 12 +- docs/build/sdks/js/accounts-l1-l2.md | 259 +- docs/build/sdks/js/accounts.md | 2510 ++++++++++++++--- docs/build/sdks/js/features.md | 5 +- docs/build/sdks/js/front-end.md | 3 +- docs/build/sdks/js/getting-started.md | 167 +- docs/build/sdks/js/paymaster-utils.md | 65 +- docs/build/sdks/js/providers.md | 1220 +++++--- docs/build/sdks/js/types.md | 40 + docs/build/sdks/js/utils.md | 748 +++-- .../sdks/js/zksync-ethers/accounts-l1-l2.md | 12 +- docs/build/sdks/js/zksync-ethers/accounts.md | 1387 +++++++-- .../sdks/js/zksync-ethers/getting-started.md | 5 +- .../sdks/js/zksync-ethers/paymaster-utils.md | 34 +- docs/build/sdks/js/zksync-ethers/providers.md | 121 +- docs/build/sdks/js/zksync-ethers/types.md | 40 + docs/build/sdks/js/zksync-ethers/utils.md | 341 ++- 18 files changed, 5167 insertions(+), 1824 deletions(-) diff --git a/docs/build/sdks/go/accounts.md b/docs/build/sdks/go/accounts.md index bec4921572..ff04c3ad1a 100644 --- a/docs/build/sdks/go/accounts.md +++ b/docs/build/sdks/go/accounts.md @@ -234,7 +234,7 @@ AllowanceL1(opts *CallOpts, token common.Address, bridgeAddress common.Address) ```go ZkSyncEraProvider := "https://testnet.era.zksync.dev" // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") client, err := clients.Dial(ZkSyncEraProvider) if err != nil { @@ -301,7 +301,7 @@ ApproveERC20(auth *TransactOpts, token common.Address, amount *big.Int, bridgeAd ```go ZkSyncEraProvider := "https://testnet.era.zksync.dev" // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") client, err := clients.Dial(ZkSyncEraProvider) if err != nil { @@ -971,7 +971,7 @@ CallContract(ctx context.Context, msg CallMsg, blockNumber *big.Int) ([]byte, er ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") tokenAbi, err := erc20.IERC20MetaData.GetAbi() if err != nil { @@ -1020,9 +1020,9 @@ PopulateTransaction(ctx context.Context, tx Transaction) (*zkTypes.Transaction71 ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Paymaster for Crown token on testnet -PaymasterAddress := common.HexToAddress("0xd660c2F92d3d0634e5A20f26821C43F1b09298fe") +PaymasterAddress := common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") ReceiptAddress := common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") abi, err := erc20.IERC20MetaData.GetAbi() @@ -1062,9 +1062,9 @@ SignTransaction(tx *zkTypes.Transaction712) ([]byte, error) ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Paymaster for Crown token on testnet -PaymasterAddress := common.HexToAddress("0xd660c2F92d3d0634e5A20f26821C43F1b09298fe") +PaymasterAddress := common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") ReceiptAddress := common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") abi, err := erc20.IERC20MetaData.GetAbi() @@ -1109,9 +1109,9 @@ SendTransaction(ctx context.Context, tx *Transaction) (common.Hash, error) ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Paymaster for Crown token on testnet -PaymasterAddress := common.HexToAddress("0xd660c2F92d3d0634e5A20f26821C43F1b09298fe") +PaymasterAddress := common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") ReceiptAddress := common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") abi, err := erc20.IERC20MetaData.GetAbi() @@ -1247,7 +1247,7 @@ DeployAccount(auth *TransactOpts, tx Create2Transaction) (common.Hash, error) ```go # Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") _, paymasterAbi, bytecode, err := utils.ReadStandardJson("Paymaster.json") if err != nil { @@ -1291,7 +1291,7 @@ DeployAccountWithCreate(auth *TransactOpts, tx CreateTransaction) (common.Hash, ```go # Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") _, paymasterAbi, bytecode, err := utils.ReadStandardJson("Paymaster.json") if err != nil { diff --git a/docs/build/sdks/go/clients.md b/docs/build/sdks/go/clients.md index 1f0d647dfa..041f29c9dc 100644 --- a/docs/build/sdks/go/clients.md +++ b/docs/build/sdks/go/clients.md @@ -182,7 +182,7 @@ CallContractL2(ctx context.Context, msg zkTypes.CallMsg, blockNumber *big.Int) ( ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") tokenAbi, err := erc20.IERC20MetaData.GetAbi() if err != nil { @@ -264,9 +264,9 @@ EstimateGasL2(ctx context.Context, msg zkTypes.CallMsg) (uint64, error) ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Paymaster for Crown token on testnet -PaymasterAddress := common.HexToAddress("0xd660c2F92d3d0634e5A20f26821C43F1b09298fe") +PaymasterAddress := common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") ReceiptAddress := common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") abi, err := erc20.IERC20MetaData.GetAbi() @@ -326,9 +326,9 @@ SendRawTransaction(ctx context.Context, tx []byte) (common.Hash, error) ```go // The Crown token on testnet -TokenAddress := common.HexToAddress("0xCd9BDa1d0FC539043D4C80103bdF4f9cb108931B") +TokenAddress := common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Paymaster for Crown token on testnet -PaymasterAddress := common.HexToAddress("0xd660c2F92d3d0634e5A20f26821C43F1b09298fe") +PaymasterAddress := common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") ReceiptAddress := common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") w, err := accounts.NewWallet(common.Hex2Bytes(), &client, nil) @@ -543,7 +543,7 @@ ContractAccountInfo(ctx context.Context, address common.Address) (*zkTypes.Contr ```go // Paymaster for Crown token on testnet -PaymasterAddress := common.HexToAddress("0xd660c2F92d3d0634e5A20f26821C43F1b09298fe") +PaymasterAddress := common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") accountInfo, err := client.ContractAccountInfo(context.Background(), PaymasterAddress) if err != nil { log.Panic() diff --git a/docs/build/sdks/js/accounts-l1-l2.md b/docs/build/sdks/js/accounts-l1-l2.md index fdc14b0df0..14689a8acb 100644 --- a/docs/build/sdks/js/accounts-l1-l2.md +++ b/docs/build/sdks/js/accounts-l1-l2.md @@ -5,257 +5,40 @@ head: content: JS SDK L1/L2 Transactions | zkSync Docs --- -# Accounts: L1->L2 Transactions +# Accounts: L1<->L2 Transactions -This section explores the methods which allow the [account](./accounts.md) classes to send transactions from L1 to L2. +This section explores the methods which allow the [account](./accounts.md) to send transactions among both L1 to L2 networks. -If you want to get some background on how L1 -> L2 interaction works on zkSync, go through the [L1 to L2 interoperability doc](../../developer-reference/l1-l2-interop.md). +If you want some background on how L1<->L2 interaction works on zkSync, go through the [introduction](../../developer-reference/l1-l2-interop.md). -## Supported classes +## Deposit -The following account classes support sending transactions from L1 to L2: +`Wallet` and `L1Signer` objects provide a deposit workflow. For more information, please refer to the method specification [`Deposit`](accounts.md#deposit). -- `Wallet` (if connected to an L1 provider) -- `L1Signer` +For a complete example of how to execute the deposit workflow, take a look at the following: [Deposit ETH and ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/01_deposit.ts). -## Approving deposit of tokens +## Request execute -Bridging ERC20 tokens from Ethereum requires approving the tokens to the zkSync Ethereum smart contract. +`Wallet` and `L1Signer` objects provide an option to request execution of L2 transaction from L1. For more information, please refer +to the method specification [`requestExecute`](accounts.md#requestexecute). -```typescript -async approveERC20( - token: Address, - amount: BigNumberish, - overrides?: ethers.Overrides & { bridgeAddress?: Address } -): Promise -``` +## Base cost -### Inputs and outputs +`Wallet` and `L1Signer` objects provide an option to calculate base cost for L2 transaction. For more information, please refer to the +method specification [`getBaseCost`](accounts.md#getbasecost). -| Name | Description | -| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| token | The Ethereum address of the token. | -| amount | The amount of the token to be approved. | -| overrides? | **Ethereum** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, etc. You can also provide a custom address of the L1 bridge to use (the bridge provided by the `Matter Labs` team is used by default). | -| returns | `ethers.providers.TransactionResponse` object. | +## Claim failed deposit -> Example +`Wallet` and `L1Signer` objects provide a claim fail deposit workflow. For more information, please refer to the method specification +[`claimFailedDeposit`](accounts.md#claimfaileddeposit). -```typescript -import { Wallet, Provider } from "zksync-ethers"; -import { ethers } from "ethers"; +## Finalize withdraw -const PRIVATE_KEY = ""; +`Wallet` and `L1Signer` objects provide a finalize withdraw workflow. For more information, please refer to the method specification +[`finalizeWithdrawal`](accounts.md#finalizewithdrawal). -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); +## Withdrawal -const USDC_ADDRESS = ""; -const txHandle = await wallet.approveERC20( - USDC_ADDRESS, - "10000000" // 10.0 USDC -); +`Wallet` and `Signer` objects provide a withdrawal workflow. For more information, please refer to the method specification [`Deposit`](accounts.md#deposit). -await txHandle.wait(); -``` - -## Depositing tokens to zkSync - -```typescript -async deposit(transaction: { - token: Address; - amount: BigNumberish; - to?: Address; - operatorTip?: BigNumberish; - bridgeAddress?: Address; - approveERC20?: boolean; - overrides?: ethers.PayableOverrides; - approveOverrides?: ethers.Overrides; -}): Promise -``` - -#### Inputs and outputs - -| Name | Description | -| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| transaction.token | The address of the token to deposit. | -| transaction.amount | The amount of the token to be deposited. | -| transaction.to? | The address that will receive the deposited tokens on L2. | -| transaction.operatorTip? | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| transaction.bridgeAddress? | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`). | -| transaction.approveERC20? | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand. | -| transaction.overrides? | **Ethereum** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, etc. | -| transaction.approveOverrides? | **Ethereum** transaction overrides of the approval transaction. May be used to pass `gasLimit`, `gasPrice`, etc. | -| returns | `PriorityOpResponse` object. | - -> Example - -```typescript -import { Wallet, Provider, utils } from "zksync-ethers"; -import { ethers } from "ethers"; - -const PRIVATE_KEY = ""; - -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); - -const USDC_ADDRESS = ""; -const usdcDepositHandle = await wallet.deposit({ - token: USDC_ADDRESS, - amount: "10000000", - approveERC20: true, -}); -// Note that we wait not only for the L1 transaction to complete but also for it to be -// processed by zkSync. If we want to wait only for the transaction to be processed on L1, -// we can use `await usdcDepositHandle.waitL1Commit()` -await usdcDepositHandle.wait(); - -const ethDepositHandle = await wallet.deposit({ - token: utils.ETH_ADDRESS, - amount: "10000000", -}); -// Note that we wait not only for the L1 transaction to complete but also for it to be -// processed by zkSync. If we want to wait only for the transaction to be processed on L1, -// we can use `await ethDepositHandle.waitL1Commit()` -await ethDepositHandle.wait(); -``` - -## Adding native token to zkSync - -New tokens are added automatically the first time they are deposited. - -## Finalizing withdrawals - -Withdrawals are executed in 2 steps - initiated on L2 and finalized on L1. - -```typescript -async finalizeWithdrawal(withdrawalHash: BytesLike, index: number = 0): Promise -``` - -#### Inputs and outputs - -| Name | Description | -| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -| withdrawalHash | Hash of the L2 transaction where the withdrawal was initiated. | -| index? | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize (defaults to 0). | - -## Force-executing transactions on L2 - -### Getting the base cost for L2 transaction - -```ts -async getBaseCost(params: { - gasLimit: BigNumberish; - gasPerPubdataByte?: BigNumberish; - gasPrice?: BigNumberish; -}): Promise -``` - -#### Inputs and outputs - -| Name | Description | -| ------------------------ | ------------------------------------------------------------------------------------- | -| params.gasLimit | The `gasLimit` for the L2 contract call. | -| params.gasPerPubdataByte | The L2 gas price for each published L1 calldata byte. | -| params.gasPrice? | The L1 gas price of the L1 transaction that will send the request for an execute call | -| returns | The base cost in ETH for requesting the L2 contract call. | - -## Claim Failed Deposit - -The `claimFailedDeposit` method 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, otherwise throws the error. - -```ts -async claimFailedDeposit(depositHash: BytesLike): Promise -``` - -### Input Parameters - -| Parameter | Type | Description | -| ----------- | --------- | ---------------------------------------------- | -| depositHash | `bytes32` | The L2 transaction hash of the failed deposit. | - -### Requesting transaction execution - -```ts -async requestExecute(transaction: { - contractAddress: Address; - calldata: BytesLike; - l2GasLimit: BigNumberish; - l2Value?: BigNumberish; - factoryDeps?: ethers.BytesLike[]; - operatorTip?: BigNumberish; - gasPerPubdataByte?: BigNumberish; - refundRecipient?: Address; - overrides?: ethers.PayableOverrides; -}): Promise -``` - -#### Inputs and outputs - -| Name | Description | -| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| transaction.contractAddress | The L2 contract to be called. | -| transaction.calldata | The input of the L2 transaction. | -| transaction.l2GasLimit | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| transaction.l2Value? | `msg.value` of L2 transaction. | -| transaction.factoryDeps? | An array of L2 bytecodes that will be marked as known on L2. | -| transaction.operatorTip? | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| transaction.gasPerPubdataByte? | The L2 gas price for each published L1 calldata byte. | -| transaction.refundRecipient? | 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`. | -| transaction.overrides | **Ethereum** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, `value`, etc. | -| returns | `PriorityOpResponse` object. | - -> Example - -```typescript -import { Wallet, Provider } from "zksync-ethers"; -import { BigNumber, ethers } from "ethers"; - -const PRIVATE_KEY = ""; -const CONTRACT_ADDRESS = ""; - -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); - -const gasPrice = await wallet.providerL1.getGasPrice(); - -// The calldata can be encoded the same way as for Ethereum. -// Here is an example of how to get the calldata from an ABI: -const abi = [ - { - inputs: [], - name: "increment", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, -]; -const contractInterface = new ethers.utils.Interface(abi); -const calldata = contractInterface.encodeFunctionData("increment", []); -const l2GasLimit = BigNumber.from(1000); - -const txCostPrice = await wallet.getBaseCost({ - gasPrice, - calldataLength: ethers.utils.arrayify(calldata).length, - l2GasLimit, -}); - -console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); - -const executeTx = await wallet.requestExecute({ - contractAddress: CONTRACT_ADDRESS, - calldata, - l2Value: 1, - l2GasLimit, - overrides: { - gasPrice, - value: txCostPrice, - }, -}); - -await executeTx.wait(); -``` +For a complete example of how to execute the deposit workflow, take a look at the following: [Withdraw ETH and ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/03_withdraw.ts). diff --git a/docs/build/sdks/js/accounts.md b/docs/build/sdks/js/accounts.md index 9e9df5c9a1..bcd3c340c4 100644 --- a/docs/build/sdks/js/accounts.md +++ b/docs/build/sdks/js/accounts.md @@ -9,7 +9,7 @@ head: ## Overview -`zksync-ethers` exports four classes that can sign transactions on zkSync: +`zksync-ethers` exports the following classes that can sign transactions on zkSync: - `Wallet` class is an extension of the `ethers.Wallet` with additional zkSync features. - `EIP712Signer` class that is used to sign `EIP712`_-typed_ zkSync transactions. @@ -17,529 +17,2409 @@ head: ## `Wallet` -### Creating wallet from a private key +### `constructor` -Just like `ethers.Wallet`, the `Wallet` object from `zksync-ethers` can be created from Ethereum private key. +#### Inputs -```typescript - constructor Wallet( - privateKey: ethers.utils.BytesLike | ethers.utils.SigningKey, +| Parameter | Type | Description | +| ------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `privateKey` | `BytesLike` or [`ethers.utils.SigningKey`](https://docs.ethers.org/v6/api/crypto/#SigningKey) | The private key of the Ethereum account. | +| `providerL2?` | [`Provider`](./providers.md#provider) | A zkSync node provider. Needed for interaction with zkSync (optional). | +| `providerL1?` | [`ethers.providers.Provider`](https://docs.ethers.org/v5/api/providers/provider/#Provider) | An Ethereum node provider. Needed for interaction with L1 (optional). | + +```ts + constructor (privateKey: ethers.utils.BytesLike | ethers.utils.SigningKey, providerL2?: Provider, - providerL1?: ethers.providers.Provider): Wallet + providerL1?: ethers.providers.Provider) ``` -#### Inputs and outputs - -| Name | Description | -| ----------- | ----------------------------------------------------------- | -| privateKey | The private key of the Ethereum account. | -| providerL2? | A zkSync node provider. Needed for interaction with zkSync. | -| providerL1? | An Ethereum node provider. Needed for interaction with L1. | -| returns | The new `Wallet` object. | - -> Example +#### Example -```typescript +```ts import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; const PRIVATE_KEY = ""; -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); ``` -### Other ways to create `Wallet` instances +### `fromMnemonic` -The `Wallet` class supports all the methods from `ethers.Wallet` for creating wallets, e.g. creating from mnemonic, creating from encrypted JSON, creating a random wallet, etc. All these methods take the same parameters as `ethers.Wallet`, so you should refer to its documentation on how to use them. +Creates a `Wallet` with the `provider` as L1 provider and a private key that is built from the `mnemonic` passphrase. -### Connecting to the zkSync provider +#### Inputs -To interact with the zkSync network, the `Wallet` object should be connected to a `Provider` by either passing it to the constructor or with the `connect` method. +| Parameter | Type | Description | +| ----------- | ------------------ | ----------------------------------------------------------------------- | +| `mnemonic` | `string` | The mnemonic of the private key. | +| `path?` | `string` | If path is not specified, the Ethereum default path is used (optional). | +| `wordlist?` | `ethers.Worldlist` | If wordlist is not specified, the English Wordlist is used (optional). | -```typescript -Wallet.connect(provider: Provider): Wallet +```ts +static fromMnemonic(mnemonic: string, path?: string, wordlist?: ethers.Wordlist) ``` -#### Inputs and outputs +#### Example -| Name | Description | -| -------- | ------------------------------- | -| provider | A zkSync node provider. | -| returns | A new zkSync `Wallet` instance. | +```ts +import { Wallet } from "zksync-ethers"; -> Example +const MNEMONIC = "stuff slice staff easily soup parent arm payment cotton hammer scatter struggle"; -```typescript -import { Wallet, Provider } from "zksync-ethers"; +const wallet = Wallet.fromMnemonic(MNEMONIC); +``` + +### `fromEncryptedJson` + +Creates a `Wallet` from encrypted `json` file using provided `password`. + +#### Inputs + +| Parameter | Type | Description | +| ----------- | -------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `json` | `string` | Encrypted json file. | +| `password` | `string` or `ethers.Bytes` | Password for encrypted json file. | +| `callback?` | `ProgressCallback` | If callback is provided, it is called periodically during decryption so that any UI can be updated (optional). | + +```ts +static async fromEncryptedJsonSync(json: string, password?: string | ethers.Bytes) +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import * as fs from "fs"; + +const wallet = await Wallet.fromEncryptedJson(fs.readFileSync("wallet.json", "utf8"), "password"); +``` + +### `fromEncryptedJsonSync` + +Creates a `Wallet` from encrypted `json` file using provided `password`. + +#### Inputs + +| Parameter | Type | Description | +| ---------- | -------------------------- | --------------------------------- | +| `json` | `string` | Encrypted json file. | +| `password` | `string` or `ethers.Bytes` | Password for encrypted json file. | + +```ts +static fromEncryptedJsonSync(json: string, password?: string | ethers.Bytes) +``` + +#### Example + +```ts +import { Wallet } from "zksync-ethers"; +import * as fs from "fs"; + +const wallet = Wallet.fromEncryptedJsonSync(fs.readFileSync("tests/files/wallet.json", "utf8"), "password"); +``` + +### `connect` + +To interact with the zkSync network, the `Wallet` object should be connected to a `Provider` by either passing it to the constructor or with the `connect` method. + +#### Inputs + +| Parameter | Type | Description | +| ---------- | ------------------------------------- | ----------------------- | +| `provider` | [`Provider`](./providers.md#provider) | A zkSync node provider. | + +```ts +Wallet.connect(provider:Provider): Wallet +``` +#### Example + +```ts +import { Wallet, Provider, types } from "zksync-ethers"; + +const PRIVATE_KEY = ""; const unconnectedWallet = new Wallet(PRIVATE_KEY); -const provider = new Provider("https://sepolia.era.zksync.dev"); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); const wallet = unconnectedWallet.connect(provider); ``` -### Connecting to the Ethereum provider - -To perform L1 operations, the `Wallet` object needs to be connected to an `ethers.providers.Provider` object. +It is possible to chain `connect` and `connectToL1` methods: -```typescript -Wallet.connectToL1(provider: ethers.providers.Provider): Wallet +```ts +const wallet = unconnectedWallet.connect(zkSyncProvider).connectToL1(ethProvider); ``` -#### Inputs and outputs +### `connectToL1` -| Name | Description | -| -------- | ----------------------------------------------------------------- | -| provider | An Ethereum node provider. | -| returns | A new zkSync `Wallet` instance that is connected to L1 `provider` | +To perform L1 operations, the `Wallet` object needs to be connected to an `ethers.Provider` object. -> Example +#### Inputs -```typescript +| Parameter | Type | Description | +| ---------- | ------------------------------------------------------------------------------------------ | -------------------------- | +| `provider` | [`ethers.providers.Provider`](https://docs.ethers.org/v5/api/providers/provider/#Provider) | An Ethereum node provider. | + +```ts +Wallet.connectToL1(provider: ethers.Provider): Wallet +``` + +#### Example + +```ts import { Wallet } from "zksync-ethers"; import { ethers } from "ethers"; +const PRIVATE_KEY = ""; const unconnectedWallet = new Wallet(PRIVATE_KEY); -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = unconnectedWallet.connectToL1(ethProvider); ``` It is possible to chain `connect` and `connectToL1` methods: -```typescript +```ts const wallet = unconnectedWallet.connect(zkSyncProvider).connectToL1(ethProvider); ``` -### Getting the zkSync L1 smart contract +### `getMainContract` -```typescript +Returns `Contract` wrapper of the zkSync smart contract. + +```ts async getMainContract(): Promise ``` -#### Inputs and outputs +#### Example -| Name | Description | -| ------- | ------------------------------------------------ | -| returns | `Contract` wrapper of the zkSync smart contract. | - -> Example - -```typescript -import { Wallet, Provider } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; const PRIVATE_KEY = ""; -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const contract = await wallet.getMainContract(); -console.log(contract.address); +console.log(`Main contract: ${await wallet.getMainContract()}`); ``` -### Getting token balance +### `getL1BridgeContracts` -```typescript -async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise +Returns L1 bridge contracts. + +```ts +async getL1BridgeContracts(): Promise<{ erc20: IL1Bridge; weth: IL1Bridge }> ``` -#### Inputs and outputs +:::note + +There is no separate Ether bridge contract, [Main contract](./accounts.md#getmaincontract) is used instead. -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------------------------- | -| token? | The address of the token. ETH by default. | -| blockTag? | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | -| returns | The amount of the token the `Wallet` has. | +::: -> Example +#### Example -```typescript -import { Wallet, Provider } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; const PRIVATE_KEY = ""; -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const l1BridgeContracts = await wallet.getL1BridgeContracts(); +``` -const USDC_L2_ADDRESS = ""; +### `getL2BridgeContracts` -// Get balance in Big Number -console.log(await wallet.getBalance(USDC_L2_ADDRESS)); +Returns L2 bridge contracts. -// Get balance in ETH formatted -console.log(ethers.utils.formatEther(await wallet.getBalance())); +```ts +async getL2BridgeContracts(): Promise<{ erc20: IL2Bridge; weth: IL2Bridge }> ``` -### Getting token balance on L1 +#### Example -```typescript -async getBalanceL1(token?: Address, blockTag?: ethers.providers.BlockTag): Promise -``` +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; -#### Inputs and outputs +const PRIVATE_KEY = ""; -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------- | -| token? | The address of the token. ETH by default. | -| blockTag? | The block the balance should be checked on. The latest processed one is the default option. | -| returns | The amount of the token the `Wallet` has on Ethereum. | +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -> Example +const l2BridgeContracts = await wallet.getL2BridgeContracts(); +``` -```typescript -import { Wallet, Provider } from "zksync-ethers"; -import { ethers, utils } from "ethers"; +### `getAddress` -const PRIVATE_KEY = ""; +Returns the wallet address. -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const unconnectedWallet = new Wallet(PRIVATE_KEY); -const wallet = unconnectedWallet.connect(zkSyncProvider).connectToL1(ethereumProvider); -const USDC_ADDRESS = ""; +```ts +async getAddress(): Promise
; +``` + +### Example -async function getBalance() { - // Get balance in Big Number - console.log(await wallet.getBalanceL1(USDC_ADDRESS)); +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; - // Get balance in ETH formatted - console.log(utils.formatEther(await wallet.getBalanceL1())); -} +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -getBalance(); +console.log(`Address: ${await wallet.getAddress()}`); ``` -### Getting a nonce +### `getBalance` -`Wallet` also provides the `getNonce` method which is an alias for [getTransactionCount](https://docs.ethers.io/v5/api/signer/#Signer-getTransactionCount). +Returns the amount of the token the `Wallet` has. -```typescript -async getNonce(blockTag?: BlockTag): Promise -``` +#### Inputs -#### Inputs and outputs +| Parameter | Type | Description | +| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default (optional). | +| `blockTag` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------------------- | -| blockTag? | The block the nonce should be got on. `committed`, i.e. the latest processed one is the default option. | -| returns | Account's nonce number. | +```ts +async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise +``` -> Example +#### Example -```typescript -import { Wallet, Provider } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; const PRIVATE_KEY = ""; -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -// Note that we don't need ethereum provider to get the nonce -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider); +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; -console.log(await wallet.getNonce()); +console.log(`Token balance: ${await wallet.getBalance(tokenL2)}`); ``` -### Transferring tokens inside zkSync Era +### `getBalanceL1` -For convenience, the `Wallet` class has `transfer` method, which can transfer `ETH` or any `ERC20` token within the same interface. +Returns the amount of the token the `Wallet` has on Ethereum. -```typescript -async transfer(tx: { - to: Address; - amount: BigNumberish; - token?: Address; - overrides?: ethers.CallOverrides; -}): Promise -``` +#### Inputs -#### Inputs and outputs +| Parameter | Type | Description | +| ----------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default (optional). | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | -| Name | Description | -| ---------- | ---------------------------------------------------------------------------------------------- | -| tx.to | The address of the recipient. | -| tx.amount | The amount of the token to transfer. | -| token? | The address of the token. `ETH` by default. | -| overrides? | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | -| returns | A `TransactionResponse` object | +```ts +async getBalanceL1(token?: Address, blockTag?: BlockTag): Promise +``` -> Example +#### Example -```typescript -import { Wallet, Provider } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; const PRIVATE_KEY = ""; -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const recipient = Wallet.createRandom(); +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; -// We transfer 0.01 ETH to the recipient and pay the fee in USDC -const transferHandle = wallet.transfer({ - to: recipient.address, - amount: ethers.utils.parseEther("0.01"), -}); +console.log(`Token balance: ${await wallet.getBalanceL1(tokenL1)}`); +``` -const tx = await transferHandle; +### `getAllBalances` -console.log(`The sum of ${ethers.utils.formatEther(tx.value)} ETH was transferred to ${tx.to}`); +Returns all balances for confirmed tokens given by an account address. + +```ts +async getAllBalances(): Promise ``` -### Initiating a withdrawal to L1 +#### Example -```typescript -async withdraw(transaction: { - token: Address; - amount: BigNumberish; - to?: Address; - bridgeAddress?: Address; - overrides?: ethers.CallOverrides; -}): Promise +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const allBalances = await wallet.getAllBalances(); ``` -| Name | Description | -| -------------- | ---------------------------------------------------------------------------------- | -| tx.to | The address of the recipient on L1. | -| tx.amount | The amount of the token to transfer. | -| token? | The address of the token. `ETH` by default. | -| bridgeAddress? | The address of the bridge contract to be used. | -| overrides? | **zkSync** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, etc. | -| returns | A `TransactionResponse` object | +### `getNonce` -### Retrieving the underlying L1 wallet +Returns account's nonce number. -You can get an `ethers.Wallet` object with the same private key with `ethWallet()` method. +#### Inputs -#### Inputs and outputs +| Parameter | Type | Description | +| ----------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | -| Name | Description | -| ------- | ---------------------------------------------------- | -| returns | An `ethers.Wallet` object with the same private key. | +```ts +async getNonce(blockTag?: BlockTag): Promise +``` -> Example +#### Example -```typescript +```ts import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; const PRIVATE_KEY = ""; -const zkSyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const ethereumProvider = ethers.getDefaultProvider("sepolia"); -const wallet = new Wallet(PRIVATE_KEY, zkSyncProvider, ethereumProvider); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const ethWallet = wallet.ethWallet(); +console.log(`Nonce: ${await wallet.getNonce()}`); ``` -## `EIP712Signer` +### `getDeploymentNonce` -The methods of this class are mostly used internally. The examples of using this class are coming soon! +Returns account's deployment nonce number. -## `Signer` +```ts +async getDeploymentNonce(): Promise +``` -This class is to be used in a browser environment. The easiest way to construct it is to use the `getSigner` method of the `Web3Provider`. This structure extends `ethers.providers.JsonRpcSigner` and so supports all the methods available for it. +#### Example -```typescript -import { Web3Provider } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; -const provider = new Web3Provider(window.ethereum); -const signer = provider.getSigner(); -``` +const PRIVATE_KEY = ""; -### Getting token balance +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -```typescript -async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise +console.log(`Nonce: ${await wallet.getDeploymentNonce()}`); ``` -#### Inputs and outputs +### `ethWallet` -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------------------------- | -| token? | The address of the token. ETH by default. | -| blockTag? | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | -| returns | The amount of the token the `Signer` has. | +You can get an `ethers.Wallet` object with the same private key with `ethWallet()` method. -> Example +```ts +ethWallet(): ethers.Wallet +``` -```typescript -import { Web3Provider } from "zksync-ethers"; +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; -const provider = new Web3Provider(window.ethereum); -const signer = provider.getSigner(); +const PRIVATE_KEY = ""; -const USDC_L2_ADDRESS = "0x852a4599217e76aa725f0ada8bf832a1f57a8a91"; -// Getting balance in USDC -console.log(await signer.getBalance(USDC_L2_ADDRESS)); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -// Getting balance in ETH -console.log(await signer.getBalance()); +const ethWallet = wallet.ethWallet(); ``` -### Getting a nonce +### `l2TokenAddress` -The `Wallet` class also provides the `getNonce` method which is an alias for [getTransactionCount](https://docs.ethers.io/v5/api/signer/#Signer-getTransactionCount). +Returns the L2 token address equivalent for a L1 token address as they are not equal. ETH's address is set to zero address. -```typescript -async getNonce(blockTag?: BlockTag): Promise +:::warning +Only works for tokens bridged on default zkSync Era bridges. +::: + +#### Inputs + +| Parameter | Type | Description | +| --------- | --------- | ------------------------------- | +| `token` | `Address` | The address of the token on L1. | + +```ts +async l2TokenAddress(token: Address): Promise ``` -#### Inputs and outputs +#### Example -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------------------- | -| blockTag? | The block the nonce should be got on. `committed`, i.e. the latest processed one is the default option. | -| returns | The amount of the token the `Wallet` has. | +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; -> Example +const PRIVATE_KEY = ""; -```typescript -import { Web3Provider } from "zksync-ethers"; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const provider = new Web3Provider(window.ethereum); -const signer = provider.getSigner(); +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; -console.log(await signer.getNonce()); +console.log(`Token L2 address: ${await wallet.l2TokenAddress(tokenL1)}`); ``` -### Transferring tokens inside zkSync +### `populateTransaction` -Please note that for now, unlike Ethereum, zkSync does not support native transfers, i.e. the `value` field of all transactions is equal to `0`. All the token transfers are done through ERC20 `transfer` function calls. +Designed for users who prefer a simplified approach by providing only the necessary data to create a valid transaction. +The only required fields are `transaction.to` and either `transaction.data` or `transaction.value` (or both, if the method is payable). +Any other fields that are not set will be prepared by this method. -But for convenience, the `Wallet` class has `transfer` method, which can transfer any `ERC20` tokens. +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | -```typescript -async transfer(tx: { - to: Address; - amount: BigNumberish; - token?: Address; - overrides?: ethers.CallOverrides; -}): Promise +```ts +override async populateTransaction(transaction: TransactionRequest): Promise ``` -#### Inputs and outputs +#### Example -| Name | Description | -| ---------- | ------------------------------------------------------------------------------------- | -| tx.to | The address of the recipient. | -| tx.amount | The amount of the token to transfer. | -| token? | The address of the token. `ETH` by default. | -| overrides? | **zkSync** transaction overrides. May be used to pass L2 `gasLimit`, `gasPrice`, etc. | -| returns | An `ethers.ContractTransaction` object. | - -> Example - -```typescript -import { Wallet, Web3Provider } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; -const provider = new Web3Provider(window.ethereum); -const signer = provider.getSigner(); +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); const recipient = Wallet.createRandom(); -// We transfer 0.01 ETH to the recipient and pay the fee in USDC -const transferHandle = signer.transfer({ +const tx = wallet.populateTransaction({ to: recipient.address, amount: ethers.utils.parseEther("0.01"), }); ``` -## `L1Signer` +### `signTransaction` -This class is to be used in a browser environment to do zkSync-related operations on layer 1. This class extends `ethers.providers.JsonRpcSigner` and so supports all the methods available for it. +Signs the transaction and serializes it to be ready to be broadcast to the network. +Throws an error when `transaction.from` is mismatched from the private key. -The easiest way to construct it is from an `Web3Provider` object. +#### Inputs -```typescript -import { Web3Provider, Provider, L1Signer } from "zksync-ethers"; +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | -const provider = new ethers.Web3Provider(window.ethereum); -const zksyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const signer = L1Signer.from(provider.getSigner(), zksyncProvider); +```ts +async signTransaction(transaction: TransactionRequest): Promise ``` -### Getting the zkSync L1 smart contract +#### Example -```typescript -async getMainContract(): Promise -``` +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; -### Getting bridge contracts +const PRIVATE_KEY = ""; -ERC-20 bridge `Contract` object: +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider); -```typescript -async getL1BridgeContracts(): Promise<{ - erc20: IL1Bridge; -}> +const recipient = Wallet.createRandom(); + +const tx = await wallet.signTransaction({ + type: utils.EIP712_TX_TYPE, + to: recipient.address, + value: ethers.utils.parseEther("1"), +}); ``` -:::note +### `sendTransaction` -there is no separate Ether bridge contract, [Main contract](./accounts.md#getting-the-zksync-l1-smart-contract) is used instead. +Broadcast the transaction to the network. +Throws an error when `tx.from` is mismatched from the private key. -::: +#### Inputs -#### Inputs and outputs +| Parameter | Type | Description | +| --------- | ------------------------------------- | -------------------- | +| `tx` | `ethers.providers.TransactionRequest` | Transaction request. | -| Name | Description | -| ------- | ------------------------------------------------ | -| returns | `Contract` wrapper of the zkSync smart contract. | +```ts +async sendTransaction(tx: ethers.providers.TransactionRequest): Promise +``` -> Example +#### Example -```typescript -import { Web3Provider, Provider, L1Signer } from "zksync-ethers"; +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; -const provider = new ethers.Web3Provider(window.ethereum); -const zksyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const signer = L1Signer.from(provider.getSigner(), zksyncProvider); +const PRIVATE_KEY = ""; -const mainContract = await signer.getMainContract(); -console.log(mainContract.address); -``` +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider); -### Getting token balance on L1 +const recipient = Wallet.createRandom(); -```typescript -async getBalanceL1(token?: Address, blockTag?: ethers.providers.BlockTag): Promise +const tx = await wallet.sendTransaction({ + type: utils.EIP712_TX_TYPE, + to: recipient.address, + value: ethers.utils.parseEther("1"), +}); ``` -#### Inputs and outputs +### `transfer` + +For convenience, the `Wallet` class has `transfer` method, which can transfer `ETH` or any `ERC20` token within the same interface. -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------- | -| token? | The address of the token. ETH by default. | -| blockTag? | The block the balance should be checked on. The latest processed one is the default option. | -| returns | The amount of the token the `L1Signer` has on Ethereum. | +#### Inputs -> Example +| Parameter | Type | Description | +| ------------ | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `tx.to` | `Address` | The address of the recipient. | +| `tx.amount` | `BigNumberish` | The amount of the token to transfer (optional). | +| `tx.token?` | `Address` | The address of the token. `ETH` by default (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | -```typescript -import { Web3Provider, Provider, L1Signer } from "zksync-ethers"; +```ts +async transfer(tx: { + to: Address; + amount: BigNumberish; + token?: Address; + overrides?: ethers.CallOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; import { ethers } from "ethers"; -const provider = new ethers.Web3Provider(window.ethereum); -const zksyncProvider = new Provider("https://sepolia.era.zksync.dev"); -const signer = L1Signer.from(provider.getSigner(), zksyncProvider); +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "
"; +const recipient = Wallet.createRandom(); + +const transferHandle = await wallet.transfer({ + to: recipient.address, + amount: ethers.utils.parseEther("0.01"), +}); -// Getting balance in USDC -console.log(await signer.getBalanceL1(USDC_ADDRESS)); +const tx = await transferHandle.wait(); -// Getting balance in ETH -console.log(await signer.getBalanceL1()); +console.log(`The sum of ${tx.value} ETH was transferred to ${tx.to}`); +``` + +### `getAllowanceL1` + +Returns the amount of approved tokens for a specific L1 bridge. + +#### Inputs + +| Parameter | Type | Description | +| ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge, either `L1EthBridge` or `L1Erc20Bridge` (optional). | +| `blockTag?` | `BlockTag` | In which block an allowance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | + +```ts +async getAllowanceL1( + token: Address, + bridgeAddress?: Address, + blockTag?: ethers.BlockTag +): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +console.log(`Token allowance: ${await wallet.getAllowanceL1(tokenL1)}`); +``` + +### `approveERC20` + +Bridging ERC20 tokens from Ethereum requires approving the tokens to the zkSync Ethereum smart contract. + +#### Inputs + +| Parameter | Type | Description | +| ------------ | ------------------- | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `amount` | `BigNumberish` | The amount of the token to be approved. | +| `overrides?` | `ethers.Overrides?` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async approveERC20( + token: Address, + amount: BigNumberish, + overrides?: ethers.Overrides & { bridgeAddress?: Address } +): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const txHandle = await wallet.approveERC20(tokenL1, "10000000"); + +await txHandle.wait(); +``` + +### `getBaseCost` + +Returns base cost for L2 transaction. + +#### Inputs + +| Name | Type | Description | +| --------------------------- | -------------- | ------------------------------------------------------------------------------------------------- | +| `params.gasLimit` | `BigNumberish` | The `gasLimit` for the L2 contract call. | +| `params.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `params.gasPrice?` | `BigNumberish` | The L1 gas price of the L1 transaction that will send the request for an execute call (optional). | + +```ts +async getBaseCost(params: { + gasLimit: BigNumberish; + gasPerPubdataByte?: BigNumberish; + gasPrice?: BigNumberish; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +console.log(`Base cost: ${await wallet.getBaseCost({ gasLimit: 100_000 })}`); +``` + +### `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 `transaction.bridgeAddress`). In this case, `transaction.approveERC20` can be enabled to perform token approval. If there are +already enough approved tokens for the L1 bridge, token approval will be skipped. To check the amount of approved tokens for a specific bridge, +use the [`allowanceL1`](#getallowancel1) method. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.approveERC20?` | `boolean` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.approveOverrides?` | `ethers.Overrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | + +```ts +async deposit(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + approveERC20?: boolean; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; + approveOverrides?: ethers.Overrides; + customBridgeData?: BytesLike; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tokenDepositHandle = await wallet.deposit({ + token: tokenL1, + amount: "10000000", + approveERC20: true, +}); +// Note that we wait not only for the L1 transaction to complete but also for it to be +// processed by zkSync. If we want to wait only for the transaction to be processed on L1, +// we can use `await tokenDepositHandle.waitL1Commit()` +await tokenDepositHandle.wait(); + +const ethDepositHandle = await wallet.deposit({ + token: utils.ETH_ADDRESS, + amount: "10000000", +}); +// Note that we wait not only for the L1 transaction to complete but also for it to be +// processed by zkSync. If we want to wait only for the transaction to be processed on L1, +// we can use `await ethDepositHandle.waitL1Commit()` +await ethDepositHandle.wait(); +``` + +### `getDepositTx` + +Returns populated deposit transaction. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getDepositTx(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + customBridgeData?: BytesLike; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tx = await wallet.getDepositTx({ + token: tokenL1, + amount: "10000000", +}); +``` + +### `estimateGasDeposit` + +Estimates the amount of gas required for a deposit transaction on L1 network. Gas of approving ERC20 token is not included in estimation. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async estimateGasDeposit(transaction: + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + customBridgeData?: BytesLike; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; + }): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const gas = await wallet.estimateGasDeposit({ + token: tokenL1, + amount: "10000000", +}); +console.log(`Gas: ${gas}`); +``` + +### `getFullRequiredDepositFee` + +Retrieves the full needed ETH fee for the deposit. Returns the L1 fee and the L2 fee [`FullDepositFee`](./types.md#fulldepositfee). + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getFullRequiredDepositFee(transaction: { + token: Address; + to?: Address; + bridgeAddress?: Address; + customBridgeData?: BytesLike; + gasPerPubdataByte?: BigNumberish; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const fee = await wallet.getFullRequiredDepositFee({ + token: tokenL1, + to: await wallet.getAddress(), +}); +console.log(`Fee: ${fee}`); +``` + +### `claimFailedDeposit` + +The `claimFailedDeposit` method 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, otherwise throws the error. + +#### Inputs + +| Parameter | Type | Description | +| ----------- | --------- | ---------------------------------------------- | +| depositHash | `bytes32` | The L2 transaction hash of the failed deposit. | + +```ts +async claimFailedDeposit(depositHash: BytesLike): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const FAILED_DEPOSIT_HASH = ""; +const claimFailedDepositHandle = await wallet.claimFailedDeposit(FAILED_DEPOSIT_HASH); +``` + +### `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. + +#### Inputs + +| Parameter | Type | Description | +| ---------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address of the recipient on L1 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async withdraw(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + bridgeAddress?: Address; + overrides?: ethers.CallOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; +const tokenWithdrawHandle = await wallet.withdraw({ + token: tokenL2, + amount: "10000000", +}); +``` + +### `finalizeWithdrawal` + +Proves the inclusion of the L2 -> L1 withdrawal message. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | + +```ts +async finalizeWithdrawal(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const WITHDRAWAL_HASH = ""; +const finalizeWithdrawHandle = await wallet.finalizeWithdrawal(WITHDRAWAL_HASH); +``` + +### `isWithdrawalFinalized` + +Returns weather the withdrawal transaction is finalized on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | + +```ts +async isWithdrawalFinalized(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const WITHDRAWAL_HASH = ""; +const isFinalized = await wallet.isWithdrawalFinalized(WITHDRAWAL_HASH); +``` + +### `finalizeWithdrawalParams` + +Returns the [parameters](./types.md#finalizewithdrawalparams) required for finalizing a withdrawal from the withdrawal transaction's log on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | + +```ts +async finalizeWithdrawalParams(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const WITHDRAWAL_HASH = ""; +const params = await wallet.finalizeWithdrawalParams(WITHDRAWAL_HASH); +``` + +### `requestExecute` + +Request execution of L2 transaction from L1. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async requestExecute(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; +const CONTRACT_ADDRESS = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const gasPrice = await wallet.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = BigNumber.from(1000); + +const txCostPrice = await wallet.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await wallet.requestExecute({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); + +await executeTx.wait(); +``` + +### `getRequestExecuteTx` + +Returns populated deposit transaction. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getRequestExecuteTx(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit?: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; +const CONTRACT_ADDRESS = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const gasPrice = await wallet.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = BigNumber.from(1000); + +const txCostPrice = await wallet.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await wallet.getRequestExecuteTx({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); +``` + +### `estimateGasRequestExecute` + +Estimates the amount of gas required for a request execute transaction. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async estimateGasRequestExecute(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit?: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; +const CONTRACT_ADDRESS = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const gasPrice = await wallet.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = BigNumber.from(1000); + +const txCostPrice = await wallet.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await wallet.getRequestExecuteTx({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); +``` + +## `EIP712Signer` + +Provides support for an EIP712 transaction. The methods of this class are mostly used internally. + +### `getSignInput` + +Generates the EIP-712 typed data from provided transaction. Optional fields are populated by zero values. + +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | + +```ts +static getSignInput(transaction: TransactionRequest) +``` + +#### Example + +```ts +const tx = EIP712Signer.getSignInput({ + type: utils.EIP712_TX_TYPE, + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: BigNumber.from(7_000_000), + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + nonce: 0, + chainId: BigNumber.from(270), + gasPrice: BigNumber.from(250_000_000), + gasLimit: BigNumber.from(21_000), + customData: {}, +}); +``` + +### `sign` + +Signs an Ethereum transaction using EIP-712. + +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | + +```ts +async sign(transaction: TransactionRequest): Promise +``` + +### `getSignedDigest` + +Generates the signed digest of an Ethereum transaction using EIP-712. + +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | + +```ts +static getSignedDigest(transaction: TransactionRequest): ethers.BytesLike +``` + +### `getDomain` + +Returns the EIP712 domain. + +```ts +async getDomain(): Promise +``` + +## `Signer` + +This class is to be used in a browser environment. The easiest way to construct it is to use the `getSigner` method of the `Web3Provider`. This structure extends `ethers.providers.JsonRpcSigner` and so supports all the methods available for it. + +```typescript +import { Web3Provider } from "zksync-ethers"; + +const provider = new Web3Provider(window.ethereum); +const signer = provider.getSigner(); +``` + +### `getBalance` + +Returns the amount of the token the `Signer` has. + +#### Inputs + +| Parameter | Type | Description | +| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default. | +| `blockTag` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | + +```ts +async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise +``` + +#### Example + +```ts +import { Web3Provider } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new Web3Provider(window.ethereum); +const signer = provider.getSigner(); + +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; +// Getting token balance +console.log(await signer.getBalance(tokenL2)); + +// Getting ETH balance +console.log(await signer.getBalance()); +``` + +### `getNonce` + +Returns account's nonce number. + +#### Inputs + +| Parameter | Type | Description | +| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | + +```ts +async getNonce(blockTag?: BlockTag): Promise +``` + +#### Example + +```ts +import { Web3Provider } from "zksync-ethers"; + +const provider = new Web3Provider(window.ethereum); +const signer = provider.getSigner(); + +console.log(await signer.getNonce()); +``` + +### `transfer` + +Please note that for now, unlike Ethereum, zkSync does not support native transfers, i.e. the `value` field of all transactions is equal to `0`. All the token transfers are done +through ERC20 `transfer` function calls. + +But for convenience, the `Wallet` class has `transfer` method, which can transfer any `ERC20` tokens. + +#### Inputs + +| Parameter | Type | Description | +| ------------ | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `tx.to` | `Address` | The address of the recipient. | +| `tx.amount` | `BigNumberish` | The amount of the token to transfer. | +| `token?` | `Address` | The address of the token. `ETH` by default. | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async transfer(tx: { + to: Address; + amount: BigNumberish; + token?: Address; + overrides?: ethers.CallOverrides; +}): Promise +``` + +#### Example + +```ts +import { Wallet, Web3Provider } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new Web3Provider(window.ethereum); +const signer = provider.getSigner(); + +const recipient = Wallet.createRandom(); + +const transferHandle = signer.transfer({ + to: recipient.address, + amount: ethers.utils.parseEther("0.01"), +}); +``` + +### `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. + +#### Inputs + +| Parameter | Type | Description | +| ---------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address of the recipient on L1 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async withdraw(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + bridgeAddress?: Address; + overrides?: ethers.CallOverrides; +}): Promise +``` + +#### Example + +```ts +import { Web3Provider } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new Web3Provider(window.ethereum); +const signer = provider.getSigner(); + +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; +const tokenWithdrawHandle = await signer.withdraw({ + token: tokenL2, + amount: "10000000", +}); +``` + +## `L1Signer` + +This class is to be used in a browser environment to do zkSync-related operations on layer 1. This class extends `ethers.providers.JsonRpcSigner` and so supports all the methods available for it. + +The easiest way to construct it is from an `Web3Provider` object. + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); +``` + +### `getMainContract` + +Returns the main zkSync Era smart contract address. + +```ts +async getMainContract(): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const mainContract = await signer.getMainContract(); +console.log(mainContract.address); +``` + +### `getL1BridgeContracts` + +Returns L1 bridge contracts. + +```ts +async getL1BridgeContracts(): Promise<{ erc20: IL1Bridge; weth: IL1Bridge }> +``` + +:::note + +there is no separate Ether bridge contract, [Main contract](./accounts.md#getmaincontract) is used instead. + +::: + +### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const l1BridgeContracts = await signer.getL1BridgeContracts(); +``` + +### `getBalanceL1` + +Returns the amount of the token the `L1Signer` has on Ethereum. + +#### Inputs + +| Parameter | Type | Description | +| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default. | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | + +```ts +async getBalanceL1(token?: Address, blockTag?: BlockTag): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; + +// Getting token balance +console.log(await signer.getBalanceL1(tokenL1)); + +// Getting ETH balance +console.log(await signer.getBalanceL1()); +``` + +### `l2TokenAddress` + +Returns the L2 token address equivalent for a L1 token address as they are not equal. ETH's address is set to zero address. + +:::warning +Only works for tokens bridged on default zkSync Era bridges. +::: + +#### Inputs + +| Parameter | Type | Description | +| --------- | --------- | ------------------------------- | +| `token` | `Address` | The address of the token on L1. | + +```ts +async l2TokenAddress(token: Address): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; + +console.log(`Token L2 address: ${await signer.l2TokenAddress(tokenL1)}`); +``` + +### `getAllowanceL1` + +Returns the amount of approved tokens for a specific L1 bridge. + +#### Inputs + +| Parameter | Type | Description | +| ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge, either `L1EthBridge` or `L1Erc20Bridge` (optional). | +| `blockTag?` | `BlockTag` | In which block an allowance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | + +```ts +async getAllowanceL1( + token: Address, + bridgeAddress?: Address, + blockTag?: ethers.BlockTag +): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +console.log(`Token allowance: ${await signer.getAllowanceL1(tokenL1)}`); +``` + +### `approveERC20` + +Bridging ERC20 tokens from Ethereum requires approving the tokens to the zkSync Ethereum smart contract. + +#### Inputs + +| Parameter | Type | Description | +| ------------ | ------------------- | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `amount` | `BigNumberish` | The amount of the token to be approved. | +| `overrides?` | `ethers.Overrides?` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async approveERC20( + token: Address, + amount: BigNumberish, + overrides?: ethers.Overrides & { bridgeAddress?: Address } +): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const txHandle = await signer.approveERC20(tokenL1, "10000000"); + +await txHandle.wait(); +``` + +### `getBaseCost` + +Returns base cost for L2 transaction. + +#### Inputs + +| Name | Type | Description | +| --------------------------- | -------------- | ------------------------------------------------------------------------------------------------- | +| `params.gasLimit` | `BigNumberish` | The `gasLimit` for the L2 contract call. | +| `params.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `params.gasPrice?` | `BigNumberish` | The L1 gas price of the L1 transaction that will send the request for an execute call (optional). | + +```ts +async getBaseCost(params: { + gasLimit: BigNumberish; + gasPerPubdataByte?: BigNumberish; + gasPrice?: BigNumberish; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +console.log(`Base cost: ${await signer.getBaseCost({ gasLimit: 100_000 })}`); +``` + +### `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 `transaction.bridgeAddress`). In this case, `transaction.approveERC20` can be enabled to perform token approval. If there are +already enough approved tokens for the L1 bridge, token approval will be skipped. To check the amount of approved tokens for a specific bridge, +use the [`allowanceL1`](#getallowancel1) method. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.approveERC20?` | `boolean` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.approveOverrides?` | `ethers.Overrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | + +```ts +async deposit(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + approveERC20?: boolean; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; + approveOverrides?: ethers.Overrides; + customBridgeData?: BytesLike; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tokenDepositHandle = await signer.deposit({ + token: tokenL1, + amount: "10000000", + approveERC20: true, +}); +// Note that we wait not only for the L1 transaction to complete but also for it to be +// processed by zkSync. If we want to wait only for the transaction to be processed on L1, +// we can use `await tokenDepositHandle.waitL1Commit()` +await tokenDepositHandle.wait(); + +const ethDepositHandle = await signer.deposit({ + token: utils.ETH_ADDRESS, + amount: "10000000", +}); +// Note that we wait not only for the L1 transaction to complete but also for it to be +// processed by zkSync. If we want to wait only for the transaction to be processed on L1, +// we can use `await ethDepositHandle.waitL1Commit()` +await ethDepositHandle.wait(); +``` + +### `getDepositTx` + +Returns populated deposit transaction. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getDepositTx(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + customBridgeData?: BytesLike; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tx = await signer.getDepositTx({ + token: tokenL1, + amount: "10000000", +}); +``` + +### `estimateGasDeposit` + +Estimates the amount of gas required for a deposit transaction on L1 network. Gas of approving ERC20 token is not included in estimation. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async estimateGasDeposit(transaction: + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + customBridgeData?: BytesLike; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; + }): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const gas = await signer.estimateGasDeposit({ + token: tokenL1, + amount: "10000000", +}); +console.log(`Gas: ${gas}`); +``` + +### `getFullRequiredDepositFee` + +Retrieves the full needed ETH fee for the deposit. Returns the L1 fee and the L2 fee [`FullDepositFee`](./types.md#fulldepositfee). + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getFullRequiredDepositFee(transaction: { + token: Address; + to?: Address; + bridgeAddress?: Address; + customBridgeData?: BytesLike; + gasPerPubdataByte?: BigNumberish; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const fee = await signer.getFullRequiredDepositFee({ + token: tokenL1, + to: await wallet.getAddress(), +}); +console.log(`Fee: ${fee}`); +``` + +### `claimFailedDeposit` + +The `claimFailedDeposit` method 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, otherwise throws the error. + +#### Inputs + +| Parameter | Type | Description | +| ----------- | --------- | ---------------------------------------------- | +| depositHash | `bytes32` | The L2 transaction hash of the failed deposit. | + +```ts +async claimFailedDeposit(depositHash: BytesLike): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const FAILED_DEPOSIT_HASH = ""; +const claimFailedDepositHandle = await signer.claimFailedDeposit(FAILED_DEPOSIT_HASH); +``` + +### `finalizeWithdrawal` + +Proves the inclusion of the L2 -> L1 withdrawal message. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | + +```ts +async finalizeWithdrawal(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const WITHDRAWAL_HASH = ""; +const finalizeWithdrawHandle = await signer.finalizeWithdrawal(WITHDRAWAL_HASH); +``` + +### `isWithdrawalFinalized` + +Returns weather the withdrawal transaction is finalized on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0) (optional). | + +```ts +async isWithdrawalFinalized(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const WITHDRAWAL_HASH = ""; +const isFinalized = await signer.isWithdrawalFinalized(WITHDRAWAL_HASH); +``` + +### `finalizeWithdrawalParams` + +Returns the [parameters](./types.md#finalizewithdrawalparams) required for finalizing a withdrawal from the withdrawal transaction's log on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0) (optional). | + +```ts +async finalizeWithdrawalParams(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const WITHDRAWAL_HASH = ""; +const params = await signer.finalizeWithdrawalParams(WITHDRAWAL_HASH); +``` + +### `requestExecute` + +Request execution of L2 transaction from L1. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async requestExecute(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const CONTRACT_ADDRESS = ""; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const gasPrice = await signer.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = BigNumber.from(1000); + +const txCostPrice = await signer.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await signer.requestExecute({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); + +await executeTx.wait(); +``` + +### `getRequestExecuteTx` + +Returns populated deposit transaction. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getRequestExecuteTx(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit?: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const CONTRACT_ADDRESS = ""; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const gasPrice = await signer.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = BigNumber.from(1000); + +const txCostPrice = await signer.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await signer.getRequestExecuteTx({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); +``` + +### `estimateGasRequestExecute` + +Estimates the amount of gas required for a request execute transaction. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async estimateGasRequestExecute(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit?: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.PayableOverrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const CONTRACT_ADDRESS = ""; + +const provider = new ethers.providers.Web3Provider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const gasPrice = await wallet.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = BigNumber.from(1000); + +const txCostPrice = await signer.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await signer.getRequestExecuteTx({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); ``` diff --git a/docs/build/sdks/js/features.md b/docs/build/sdks/js/features.md index 119a5c4d91..09621b4d8b 100644 --- a/docs/build/sdks/js/features.md +++ b/docs/build/sdks/js/features.md @@ -13,7 +13,8 @@ While zkSync is mostly Web3-compatible, it has some differences compared to Ethe - Deployment transactions require the contracts' bytecode to be passed in a separate field. - The fee system is somewhat different. -These require us to extend standard Ethereum transactions with new custom fields. Such extended transactions are called EIP712 transactions since [EIP712](https://eips.ethereum.org/EIPS/eip-712) is used to sign them. You can look at the internal structure of the EIP712 transactions [here](../../../zk-stack/concepts/transaction-lifecycle.md#eip-712-0x71). +These require us to extend standard Ethereum transactions with new custom fields. Such extended transactions are called EIP712 transactions since +[EIP712](https://eips.ethereum.org/EIPS/eip-712) is used to sign them. You can look at the internal structure of the EIP712 transactions [here](../../../zk-stack/concepts/transaction-lifecycle.md#eip-712-0x71). This document will focus solely on how to pass these arguments to the SDK. @@ -81,7 +82,7 @@ zkSync SDK provides a utility method that can be used to get the correctly forme If you want to call the method `setGreeting` of an ethers `Contract` object called `greeter`, this would look the following way, while paying fees with the [testnet paymaster](../../developer-reference/account-abstraction.md#testnet-paymaster): -```javascript +```ts // The `setGreeting` method has a single parameter -- new greeting // We will set its value as `a new greeting`. const greeting = "a new greeting"; diff --git a/docs/build/sdks/js/front-end.md b/docs/build/sdks/js/front-end.md index c465b34f13..18f7067ab2 100644 --- a/docs/build/sdks/js/front-end.md +++ b/docs/build/sdks/js/front-end.md @@ -15,7 +15,6 @@ If your front-end code does not deploy new smart contracts, then no changes to t ## Enabling zkSync features -If you want to deploy smart contracts or enable advanced zkSync features, like account abstraction, then you need to use the `zksync-ethers` library for that. You can read about -the basics [here](./features.md). +If you want to deploy smart contracts or enable advanced zkSync features, like account abstraction, then you need to use the `zksync-ethers` library. You can read about the basics [here](./features.md). If you want to see some code, check out our basic [tutorial](../../quick-start/hello-world.md) for full mini-dApp. diff --git a/docs/build/sdks/js/getting-started.md b/docs/build/sdks/js/getting-started.md index 9be7d7753b..13c52bc04f 100644 --- a/docs/build/sdks/js/getting-started.md +++ b/docs/build/sdks/js/getting-started.md @@ -7,166 +7,73 @@ head: # Getting Started -::: warning +This is a short introduction to `zksync-ethers` SDK, but covers many of the most common operations that developers require and provides a +starting point for those newer to zkSync Era. -- Please note that with the system update released in Feb 2023, the `ergs` concept is only used by the VM while the API layer operates with `gas`. -- For more information, read the [changelog]. - ::: +## Getting zksync-ethers -## Concept +### Prerequisites -While most of the existing SDKs should work out of the box, deploying smart contracts or using unique zkSync features, like account abstraction, requires providing additional fields to those that Ethereum transactions have by default. +- Node: >=v18 ([installation guide](https://nodejs.org/en/download/package-manager)) -To provide easy access to all the features of zkSync Era, the `zksync-ethers` JavaScript SDK was created, which is made in a way that has an interface very similar to those of -[ethers](https://docs.ethers.io/v5/). In fact, `ethers` is a peer dependency of our library and most of the objects exported by `zksync-ethers` (e.g. `Wallet`, `Provider` etc.) -inherit from the corresponding `ethers` objects and override only the fields that need to be changed. - -The library is made in such a way that after replacing `ethers` with `zksync-ethers` most client apps will work out of box. - -## Adding dependencies +In order to install SDK for zkSync Era, run the command below in your terminal. ```bash yarn add zksync-ethers@5 yarn add ethers@5 # ethers is a peer dependency of zksync-ethers ``` -Then you can import all the content of the `ethers` library and the `zksync-ethers` library with the following statement: - -```typescript -import * as zksync from "zksync-ethers"; -import * as ethers from "ethers"; -``` - -## Connecting to zkSync - -To interact with the zkSync network users need to know the endpoint of the operator node. - -```typescript -// Currently, only one environment is supported. -import { Wallet, Provider } from "zksync-ethers"; - -const provider = new Provider("https://sepolia.era.zksync.dev"); -// Private key of the account to connect -const wallet = new Wallet("").connect(provider); -``` - -**Note:** Currently, `Sepolia` and `Goerli` networks are supported. - -Some operations require access to the Ethereum network. `ethers` library should be used to interact with -Ethereum. - -```typescript -const ethProvider = ethers.getDefaultProvider("sepolia"); -``` - -## Creating a wallet - -To control your account in zkSync, use the `zksync.Wallet` object. It can sign transactions with keys stored in -`ethers.Wallet` and send transaction to the zkSync network using `zksync.Provider`. - -```typescript -// Derive zksync.Wallet from ethereum private key. -// zkSync's wallets support all of the methods of ethers' wallets. -// Also, both providers are optional and can be connected to later via `connect` and `connectToL1`. -const zkSyncWallet = new zksync.Wallet(PRIVATE_KEY, zkSyncProvider, ethProvider); -``` - -## Depositing funds +## Overview -Let's deposit `1.0 ETH` to our zkSync account. +While most of the existing SDKs should work out of the box, deploying smart contracts or using unique zkSync features, like account abstraction, requires providing additional +fields to those that Ethereum transactions have by default. -```typescript -const deposit = await zkSyncWallet.deposit({ - token: zksync.utils.ETH_ADDRESS, - amount: ethers.utils.parseEther("1.0"), -}); -``` +To begin, it is useful to have a basic understanding of the types of objects available and what they are responsible for, at a high level: -**NOTE:** Each token inside zkSync has an address. If `ERC-20` tokens are being bridged, you should supply the token's L1 address in the `deposit` function, or zero address (`0x0000000000000000000000000000000000000000`) if you want to deposit ETH. Note, that for the `ERC-20` tokens the address of their corresponding L2 token will be different from the one on Ethereum. +- `Provider` provides connection to the zkSync Era blockchain, which allows querying the blockchain state, such as account, block or transaction details, + querying event logs or evaluating read-only code using call. Additionally, the client facilitates writing to the blockchain by sending + transactions. +- `Wallet` wraps all operations that interact with an account. An account generally has a private key, which can be used to sign a variety of + types of payloads. It provides easy usage of the most common features. -After the transaction is submitted to the Ethereum node, its status can be tracked using the transaction handle: +## Examples -```typescript -// Await processing of the deposit on L1 -const ethereumTxReceipt = await deposit.waitL1Commit(); +The following examples could be easily run by writing the code snippets in a file and executing them as follows: -// Await processing the deposit on zkSync -const depositReceipt = await deposit.wait(); +```shell +npx ts-node src/zksync.ts ``` -## Checking zkSync account balance - -```typescript -// Retrieving the current (committed) zkSync ETH balance of an account -const committedEthBalance = await zkSyncWallet.getBalance(zksync.utils.ETH_ADDRESS); - -// Retrieving the ETH balance of an account in the last finalized zkSync block. -const finalizedEthBalance = await zkSyncWallet.getBalance(zksync.utils.ETH_ADDRESS, "finalized"); -``` +Connect to the zkSync Era network: -You can read more about what committed and finalized blocks are [here](../../../zk-stack/concepts/blocks.md) +```ts +import { Provider, utils, types } from "zksync-ethers"; +import { ethers } from "ethers"; -## Performing a transfer - -Now, let's create a second wallet and transfer some funds into it. Note that it is possible to send assets to any fresh Ethereum -account, without preliminary registration! - -```typescript -const zkSyncWallet2 = new zksync.Wallet(PRIVATE_KEY2, zkSyncProvider, ethProvider); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); ``` -Let's transfer `1 ETH` to another account: - -The `transfer` method is a helper method that enables transferring `ETH` or any `ERC-20` token within a single interface. +Get the network (helper function [toJSON](./providers.md#tojson)): -```typescript -const amount = ethers.utils.parseEther("1.0"); - -const transfer = await zkSyncWallet.transfer({ - to: zkSyncWallet2.address, - token: zksync.utils.ETH_ADDRESS, - amount, -}); +```ts +console.log(`Network: ${toJSON(await provider.getNetwork())}`); ``` -To track the status of this transaction: - -```typescript -// Await commitment -const committedTxReceipt = await transfer.wait(); +Get the latest block number: -// Await finalization on L1 -const finalizedTxReceipt = await transfer.waitFinalize(); +```ts +console.log(`Block number: ${await provider.getBlockNumber()}`); ``` -## Withdrawing funds - -There are two ways to withdraw funds from zkSync to Ethereum, calling the operation through L2 or L1. If the -withdrawal operation is called through L1, then the operator has a period of time during which he must process -the transaction, otherwise `PriorityMode` will be turned on. This ensures that the operator cannot stage the -transaction. But in most cases, a call via L2 is sufficient. +Get the block by hash (helper function [toJSON](./providers.md#tojson)): -```typescript -const withdrawL2 = await zkSyncWallet.withdraw({ - token: zksync.utils.ETH_ADDRESS, - amount: ethers.utils.parseEther("0.5"), -}); +```ts +console.log(`Block: ${toJSON(await provider.getBlock("b472c070c9e121ba42702f6c322b7b266e287a4d8b5fa426ed265b105430c397", true))}`); ``` -Assets will be withdrawn to the target wallet(if do not define the `to` address in the `withdraw` method's argument - the sender address will be chosen as a destination) after the validity proof of the zkSync block with this transaction is generated and verified by the mainnet contract. +Get the transaction by hash (helper function [toJSON](./providers.md#tojson)): -It is possible to wait until the validity proof verification is complete: - -```typescript -await withdrawL2.waitFinalize(); +```ts +console.log(`Block: ${toJSON(await provider.getTransaction("0x9af27afed9a4dd018c0625ea1368afb8ba08e4cfb69b3e76dfb8521c8a87ecfc"))}`); ``` - -## Deploying a contract - -A guide on deploying smart contracts using our hardhat plugin is available [here](../../tooling//hardhat/getting-started.md). - -## Adding tokens to the standard bridge - -Adding tokens to the zkSync standard bridge can be done in a permissionless way. After adding a token to zkSync, it can be used in all types of transactions. - -The documentation on adding tokens to zkSync can be found [here](./accounts-l1-l2.md#adding-native-token-to-zksync). diff --git a/docs/build/sdks/js/paymaster-utils.md b/docs/build/sdks/js/paymaster-utils.md index 3cc0c6a1b9..992df50fee 100644 --- a/docs/build/sdks/js/paymaster-utils.md +++ b/docs/build/sdks/js/paymaster-utils.md @@ -7,21 +7,15 @@ head: # Paymaster Utilities -The [paymaster utilities library](https://github.com/zksync-sdk/zksync-ethers/blob/ethers-v5/src/paymaster-utils.ts) contains essential utilities for using paymasters on zkSync Era. +The [paymaster utilities library](https://github.com/zksync-sdk/zksync-ethers/blob/main/src/paymaster-utils.ts) contains essential utilities for using paymasters on zkSync Era. ## Contract interfaces -### `IPaymasterFlow` Deprecated - -::: warning Deprecated - -- This method is deprecated in favor of [utils.PAYMASTER_FLOW_ABI](./utils.md#paymasterflow). - ::: - -Constant ABI definition for the [Paymaster Flow Interface](https://github.com/matter-labs/era-contracts/blob/87cd8d7b0f8c02e9672c0603a821641a566b5dd8/l2-contracts/contracts/interfaces/IPaymasterFlow.sol). +Constant ABI definition for +the [Paymaster Flow Interface](https://github.com/matter-labs/era-contracts/blob/583cb674a2b942dda34e9f46edb5a9f5b696b90a/l2-contracts/contracts/interfaces/IPaymasterFlow.sol). ```typescript -export const IPaymasterFlow = new ethers.utils.Interface(require("../../abi/IPaymasterFlow.json").abi); +export const PAYMASTER_FLOW_ABI = new ethers.Interface(require("../abi/IPaymasterFlow.json")); ``` ## Functions @@ -32,14 +26,12 @@ Returns encoded input for an approval-based paymaster. #### Inputs -| Parameter | Type | Description | -| ---------------- | ------------------------------------ | -------------------------------- | -| `paymasterInput` | `ApprovalBasedPaymasterInput` object | The input data to the paymaster. | +| Parameter | Type | Description | +| ---------------- | ----------------------------------------------------------------------- | -------------------------------- | +| `paymasterInput` | [`ApprovalBasedPaymasterInput`](./types.md#approvalbasedpaymasterinput) | The input data to the paymaster. | ```ts -export function getApprovalBasedPaymasterInput(paymasterInput: ApprovalBasedPaymasterInput): BytesLike { - return IPaymasterFlow.encodeFunctionData("approvalBased", [paymasterInput.token, paymasterInput.minimalAllowance, paymasterInput.innerInput]); -} +export function getApprovalBasedPaymasterInput(paymasterInput: ApprovalBasedPaymasterInput): BytesLike; ``` ### `getGeneralPaymasterInput` @@ -48,14 +40,12 @@ As above but for general-based paymaster. #### Inputs -| Parameter | Type | Description | -| ---------------- | ------------------------------ | -------------------------------- | -| `paymasterInput` | `GeneralPaymasterInput` object | The input data to the paymaster. | +| Parameter | Type | Description | +| ---------------- | ----------------------------------------------------------- | -------------------------------- | +| `paymasterInput` | [`GeneralPaymasterInput`](./types.md#generalpaymasterinput) | The input data to the paymaster. | ```ts -export function getGeneralPaymasterInput(paymasterInput: GeneralPaymasterInput): BytesLike { - return IPaymasterFlow.encodeFunctionData("general", [paymasterInput.innerInput]); -} +export function getGeneralPaymasterInput(paymasterInput: GeneralPaymasterInput): BytesLike; ``` ### `getPaymasterParams` @@ -64,13 +54,36 @@ Returns a correctly-formed `paymasterParams` object for common [paymaster flows] #### Inputs -| Parameter | Type | Description | -| ------------------ | ----------------------- | --------------------------------- | -| `paymasterAddress` | `Address` string | The non-zero `paymaster` address. | -| `paymasterInput` | `PaymasterInput` object | The input data to the paymaster. | +| Parameter | Type | Description | +| ------------------ | --------------------------------------------- | --------------------------------- | +| `paymasterAddress` | `Address` | The non-zero `paymaster` address. | +| `paymasterInput` | [`PaymasterInput`](./types.md#paymasterinput) | The input data to the paymaster. | ```typescript export function getPaymasterParams(paymasterAddress: Address, paymasterInput: PaymasterInput): PaymasterParams; ``` Find out more about the [`PaymasterInput` type](./types.md). + +#### Examples + +Creating `General` paymaster parameters. + +```ts +const paymasterAddress = "0x0a67078A35745947A37A552174aFe724D8180c25"; +const paymasterParams = utils.getPaymasterParams(paymasterAddress, { + type: "General", + innerInput: new Uint8Array(), +}); +``` + +Creating `ApprovalBased` paymaster parameters. + +```ts +const result = utils.getPaymasterParams("0x0a67078A35745947A37A552174aFe724D8180c25", { + type: "ApprovalBased", + token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964", + minimalAllowance: BigInt(1), + innerInput: new Uint8Array(), +}); +``` diff --git a/docs/build/sdks/js/providers.md b/docs/build/sdks/js/providers.md index 005ea7ad7d..734c72777e 100644 --- a/docs/build/sdks/js/providers.md +++ b/docs/build/sdks/js/providers.md @@ -9,7 +9,8 @@ head: A Web3 Provider object provides application-layer access to underlying blockchain networks. -The [`zksync-ethers`](https://www.npmjs.com/package/zksync-ethers/v/5.0.0) library supports provider methods from the [`ethers.js`](https://docs.ethers.io/v5/api/providers) library and supplies additional functionality. +The [`zksync-ethers`](https://www.npmjs.com/package/zksync-ethers/v/5.0.0) library supports provider methods from the [`ethers.js`](https://docs.ethers.io/v5/api/providers) library and +supplies additional functionality. Two providers are available: @@ -42,26 +43,14 @@ Returns a zkSync Era `Provider` object. | `network?` | `ethers.providers.Networkish` | Network name (optional) | ```ts -constructor(url?: ConnectionInfo | string, network?: ethers.providers.Networkish) { - super(url, network); - this.pollingInterval = 500; - - const blockTag = this.formatter.blockTag.bind(this.formatter); - this.formatter.blockTag = (tag: any) => { - if (tag == 'committed' || tag == 'finalized') { - return tag; - } - return blockTag(tag); - }; - this.contractAddresses = {}; - this.formatter.transaction = parseTransaction; -} +constructor(url?: ConnectionInfo | string, network?: ethers.providers.Networkish) ``` #### Example -```typescript +```ts import { Provider } from "zksync-ethers"; + const provider = new Provider("https://sepolia.era.zksync.dev"); ``` @@ -76,16 +65,77 @@ Returns an estimated [`Fee`](./types.md#fee) for requested transaction. | `transaction` | [`TransactionRequest`](https://docs.ethers.org/v5/api/providers/types/#providers-TransactionRequest) | Transaction request. | ```ts -async estimateFee(transaction: TransactionRequest): Promise { - return await this.send("zks_estimateFee", [transaction]); -} +async estimateFee(transaction: TransactionRequest): Promise +``` + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const fee = await provider.estimateFee({ + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: `0x${BigNumber.from(7_000_000_000).toString(16)}`, +}); +console.log(`Fee: ${toJSON(fee)}`); ``` ### `estimateGas` Returns an estimate of the amount of gas required to submit a transaction to the network. -[Ethers implementation.](https://docs.ethers.org/v5/api/providers/provider/#Provider-estimateGas) +| Parameter | Type | Description | +| ------------- | -------------------- | -------------------- | +| `transaction` | `TransactionRequest` | Transaction request. | + +```ts +async estimateGas(transaction: utils.Deferrable): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const gasTokenApprove = await provider.estimateGas({ + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + to: "0x765F5AF819D818a8e8ee6ff63D8d0e8056DBE150", + data: utils.IERC20.encodeFunctionData("approve", [RECEIVER, 1]), +}); +console.log(`Gas for token approval tx: ${gasTokenApprove}`); +``` + +```ts +import { Provider, types } from "zksync-ethers"; +import { ethers, BigNumber } from "ethers"; + +const ADDRESS = "
"; +const RECEIVER = ""; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const tokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free +const paymasterAddress = "0x13D0D8550769f59aa241a41897D4859c87f7Dd46"; // Paymaster for Crown token + +const paymasterParams = utils.getPaymasterParams(paymasterAddress, { + type: "ApprovalBased", + token: tokenAddress, + minimalAllowance: BigNumber.from(1), + innerInput: new Uint8Array(), +}); + +const tokenApprove = await provider.estimateGas({ + from: ADDRESS, + to: tokenAddress, + data: utils.IERC20.encodeFunctionData("approve", [RECEIVER, 1]), + customData: { + gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, + paymasterParams, + }, +}); +console.log(`Gas for token approval using EIP-712 tx: ${tokenApprove}`); +``` ### `estimateGasL1` @@ -93,6 +143,33 @@ Returns an estimate of the amount of gas required to submit a transaction from L Calls the [`zks_estimateL1ToL2`](../../api.md#zks-estimategasl1tol2) JSON-RPC method. +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | + +```ts +async estimateGasL1(transaction: TransactionRequest): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const gasL1 = await provider.estimateGasL1({ + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + to: await provider.getMainContractAddress(), + value: 7_000_000_000, + customData: { + gasPerPubdata: 800, + }, +}); +console.log(`L1 gas: ${BigNumber.from(gasL1)}`); +``` + ### `estimateGasTransfer` Returns the gas estimation for a transfer transaction. @@ -101,13 +178,13 @@ Calls internal method [`getTransferTx`](https://github.com/zksync-sdk/zksync-eth #### Inputs -| Parameter | Type | Description | -| ------------ | ---------------------- | ---------------------------------------- | -| `token` | Address string | Token address. | -| `amount` | `BigNumberish` | Amount of token. | -| `from?` | Address string | From address (optional). | -| `to?` | Address string | To address (optional). | -| `overrides?` | `ethers.CallOverrides` | Ethers call overrides object (optional). | +| Parameter | Type | Description | +| ------------ | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async estimateGasTransfer(transaction: { @@ -116,28 +193,41 @@ async estimateGasTransfer(transaction: { from?: Address; token?: Address; overrides?: ethers.CallOverrides; -}): Promise { - const transferTx = await this.getTransferTx(transaction); - return await this.estimateGas(transferTx); -} +}): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const gasTransfer = await provider.estimateGasTransfer({ + token: utils.ETH_ADDRESS, + amount: 7_000_000_000, + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", +}); +console.log(`Gas for transfer tx: ${gasTransfer}`); ``` ### `estimateGasWithdraw` Returns the gas estimation for a withdrawal transaction. -Calls internal method [`getWithdrawTx`](https://github.com/zksync-sdk/zksync-ethers/blob/59f2df60900b644669ec7a4cfc25e72c337a6325/src/provider.ts#L682) to get the withdrawal transaction and sends it to the [`estimateGas`](#estimategas) method. +Calls internal method [`getWithdrawTx`](#getwithdrawtx) to get the +withdrawal transaction and sends it to the [`estimateGas`](#estimategas) method. #### Inputs -| Parameter | Type | Description | -| ---------------- | ---------------------- | ---------------------------------------- | -| `token` | Address string | Token address. | -| `amount` | `BigNumberish` | Amount of token. | -| `from?` | Address string | From address (optional). | -| `to?` | Address string | To address (optional). | -| `bridgeAddress?` | Address string | Bridge address (optional). | -| `overrides?` | `ethers.CallOverrides` | Ethers call overrides object (optional). | +| Parameter | Type | Description | +| ---------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `bridgeAddress?` | `Address` | Bridge address (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async estimateGasWithdraw(transaction: { @@ -147,10 +237,21 @@ async estimateGasWithdraw(transaction: { to?: Address; bridgeAddress?: Address; overrides?: ethers.CallOverrides; -}): Promise { - const withdrawTx = await this.getWithdrawTx(transaction); - return await this.estimateGas(withdrawTx); -} +}): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const gasWithdraw = await provider.estimateGasWithdraw({ + token: utils.ETH_ADDRESS, + amount: 7_000_000, + to: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", +}); ``` ### `estimateL1ToL2Execute` @@ -159,15 +260,15 @@ Returns gas estimation for an L1 to L2 execute operation. #### Inputs -| Parameter | Type | Description | -| -------------------- | ------------------------- | ---------------------------------------------------------------- | -| `contractAddress` | Address string | Address of contract. | -| `calldata` | `BytesLike` | The transaction call data. | -| `caller?` | Address string | Caller address (optional). | -| `l2Value?` | `BigNumberish` | Current L2 gas value (optional). | -| `factoryDeps?` | `BytesLike[]` | Byte array containing contract bytecode. | -| `gasPerPubdataByte?` | `BigNumberish` | Constant representing current amount of gas per byte (optional). | -| `overrides?` | `ethers.PayableOverrides` | Ethers payable overrides object (optional). | +| Parameter | Type | Description | +| -------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------- | +| `contractAddress` | `Address` | Address of contract. | +| `calldata` | `BytesLike` | The transaction call data. | +| `caller?` | `Address` | Caller address (optional). | +| `l2Value?` | `BigNumberish` | Current L2 gas value (optional). | +| `factoryDeps?` | `BytesLike[]` | Byte array containing contract bytecode. | +| `gasPerPubdataByte?` | `BigNumberish` | Constant representing current amount of gas per byte (optional). | +| `overrides?` | `ethers.PayableOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async estimateL1ToL2Execute(transaction: { @@ -178,31 +279,22 @@ async estimateL1ToL2Execute(transaction: { factoryDeps?: ethers.BytesLike[]; gasPerPubdataByte?: BigNumberish; overrides?: ethers.PayableOverrides; -}): Promise { - transaction.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; - - // If the `from` address is not provided, we use a random address, because - // due to storage slot aggregation, the gas estimation will depend on the address - // and so estimation for the zero address may be smaller than for the sender. - transaction.caller ??= ethers.Wallet.createRandom().address; - - const customData = { - gasPerPubdataByte: transaction.gasPerPubdataByte - }; - if (transaction.factoryDeps) { - Object.assign(customData, { factoryDeps: transaction.factoryDeps }); - } +}): Promise +``` - const fee = await this.estimateGasL1({ - from: transaction.caller, - data: transaction.calldata, - to: transaction.contractAddress, - value: transaction.l2Value, - customData - }); +#### Example - return fee; -} +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const gasL1ToL2 = await provider.estimateL1ToL2Execute({ + contractAddress: await provider.getMainContractAddress(), + calldata: "0x", + caller: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + l2Value: 7_000_000_000, +}); +console.log(`Gas L1 to L2: ${BigNumber.from(gasL1ToL2)}`); ``` ### `getAllAccountBalances` @@ -211,6 +303,28 @@ Returns all balances for confirmed tokens given by an account address. Calls the [`zks_getAllAccountBalances`](../../api.md#zks-getallaccountbalances) JSON-RPC method. +#### Inputs + +| Parameter | Type | Description | +| --------- | --------- | ---------------- | +| `address` | `Address` | Account address. | + +```ts +async getAllAccountBalances(address: Address): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const balances = await provider.getAllAccountBalances("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049"); +console.log(`All balances: ${toJSON(balances)}`); +``` + ### `getBalance` Returns the user's balance as a `BigNumber` object for an (optional) block tag and (optional) token. @@ -219,51 +333,46 @@ When block and token are not supplied, `committed` and `ETH` are the default val #### Inputs -| Name | Description | -| ------------- | -------------------------------------------------------------------------- | -| address | User's address. | -| blockTag? | Block tag for getting the balance on. Latest `committed` block is default. | -| tokenAddress? | The address of the token. ETH is default. | -| | +| Parameter | Type | Description | +| --------------- | ---------- | ------------------------------------------------------------------------------------- | +| `address` | `Address` | Account address. | +| `blockTag?` | `BlockTag` | Block tag for getting the balance on. Latest `committed` block is default (optional). | +| `tokenAddress?` | `Address` | Token address. ETH is default (optional). | -```typescript -override async getBalance(address: Address, blockTag?: BlockTag, tokenAddress?: Address) { - const tag = this.formatter.blockTag(blockTag); - if (tokenAddress == null || isETH(tokenAddress)) { - // requesting ETH balance - return await super.getBalance(address, tag); - } else { - try { - let token = IERC20MetadataFactory.connect(tokenAddress, this); - return await token.balanceOf(address, { blockTag: tag }); - } catch { - return BigNumber.from(0); - } - } -} +```ts +async getBalance(address: Address, blockTag?: BlockTag, tokenAddress?: Address) ``` #### Example -```typescript -import { Provider } from "zksync-ethers"; - -const provider = new Provider("https://sepolia.era.zksync.dev"); - -//Find the USDC ADDRESS in https://zksync2-testnet.zkscan.io/address/0x0faF6df7054946141266420b43783387A78d82A9/transactions -const USDC_L2_ADDRESS = ""; -// Get the USDC balance from your account using your address. Get your address from https://zksync2-testnet.zkscan.io/txs -console.log(await provider.getBalance("", "latest", USDC_L2_ADDRESS)); +```ts +import { Provider, types } from "zksync-ethers"; -// Getting ETH balance -console.log(await provider.getBalance("")); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const account = "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049"; +const tokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free +console.log(`ETH balance: ${await provider.getBalance(account)}`); +console.log(`Token balance: ${await provider.getBalance(account, "latest", tokenAddres)}`); ``` ### `getBlock` Returns block from the network, or false if there is no block. -[Ethers implementation.](https://docs.ethers.org/v5/api/providers/provider/#Provider-getBlock) +```ts +async getBlock(blockHashOrBlockTag: BlockTag | string | Promise): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Block: ${toJSON(await provider.getBlock("latest", true))}`); +``` ### `getBlockDetails` @@ -271,11 +380,57 @@ Returns additional zkSync-specific information about the L2 block. Calls the [`zks_getBlockDetails`](../../api.md#zks-getblockdetails) JSON-RPC method. -### `getBlockWithTransactions` +#### Inputs + +| Parameter | Type | Description | +| --------- | -------- | ------------- | +| `number` | `number` | Block number. | + +```ts +async getBlockDetails(number:number): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Block details: ${toJSON(await provider.getBlockDetails(90_000))}`); +``` + +### `getBytecodeByHash` + +Returns bytecode of a contract given by its hash. + +Calls the [`zks_getBytecodeByHash`](../../api.md#zks-getbytecodebyhash) JSON-RPC method. + +#### Inputs + +| Parameter | Type | Description | +| -------------- | ----------- | -------------- | +| `bytecodeHash` | `BytesLike` | Bytecode hash. | -Returns an array of `TransactionResponse` objects. +```ts +async getBytecodeByHash(bytecodeHash: BytesLike): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +// Bytecode hash can be computed by following these steps: +// const testnetPaymasterBytecode = await provider.getCode(await provider.getTestnetPaymasterAddress()); +// const testnetPaymasterBytecodeHash = ethers.utils.hexlify(utils.hashBytecode(testnetPaymasterBytecode)); -[Ethers implementation.](https://docs.ethers.org/v5/api/providers/provider/#Provider-getBlockWithTransactions) +const testnetPaymasterBytecodeHash = "0x010000f16d2b10ddeb1c32f2c9d222eb1aea0f638ec94a81d4e916c627720e30"; + +const provider = Provider.getDefaultProvider(types.Network.Goerli); +console.log(`Bytecode: ${await provider.getBytecodeByHash(testnetPaymasterBytecodeHash)}`); +``` ### `getContractAccountInfo` @@ -283,107 +438,128 @@ Returns the version of the supported account abstraction and nonce ordering from #### Inputs -| Name | Description | -| ------- | ---------------- | -| address | Contract address | +| Parameter | Type | Description | +| --------- | --------- | ----------------- | +| `address` | `Address` | Contract address. | -```typescript -async getContractAccountInfo(address: Address): Promise { - const deployerContract = new Contract(CONTRACT_DEPLOYER_ADDRESS, CONTRACT_DEPLOYER, this); - const data = await deployerContract.getAccountInfo(address); - - return { - supportedAAVersion: data.supportedAAVersion, - nonceOrdering: data.nonceOrdering - }; -} +```ts +async getContractAccountInfo(address:Address): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const tokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free +console.log(`Contract account info: ${toJSON(await provider.getContractAccountInfo(tokenAddress))}`); ``` ### `getDefaultBridgeAddresses` Returns the addresses of the default zkSync Era bridge contracts on both L1 and L2. -```typescript -async getDefaultBridgeAddresses() { - if (!this.contractAddresses.erc20BridgeL1) { - let addresses = await this.send('zks_getBridgeContracts', []); - this.contractAddresses.erc20BridgeL1 = addresses.l1Erc20DefaultBridge; - this.contractAddresses.erc20BridgeL2 = addresses.l2Erc20DefaultBridge; - } - return { - erc20L1: this.contractAddresses.erc20BridgeL1, - erc20L2: this.contractAddresses.erc20BridgeL2 - }; -} +```ts +async getDefaultBridgeAddresses(): Promise<{erc20L1: string, erc20L2: string, wethL1: string, wethL2: string}>; +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Default bridges: ${toJSON(await provider.getDefaultBridgeAddresses())}`); ``` ### `getDefaultProvider` Static method which returns a Provider object from the RPC URL or localhost. +#### Inputs + +| Parameter | Type | Description | +| --------------- | ------------------------------------- | ------------------------------------------------- | +| `zksyncNetwork` | [`ZkSyncNetwork`](./types.md#network) | Type of zkSync network. `Localhost` _by default_. | + ```ts -static getDefaultProvider() { - return new Provider(process.env.ZKSYNC_WEB3_API_URL || 'http://localhost:3050'); -} +static getDefaultProvider(zksyncNetwork: ZkSyncNetwork = ZkSyncNetwork.Localhost) +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const providerMainnet = Provider.getDefaultProvider(types.Network.Mainnet); +const providerTestnet = Provider.getDefaultProvider(types.Network.Sepolia); +const providerLocalnet = Provider.getDefaultProvider(types.Network.Localhost); ``` ### `getFilterChanges` Returns an array of logs by calling Ethereum method [`eth_getFilterChanges`.](https://ethereum.github.io/execution-apis/api-documentation/) +#### Inputs + +| Parameter | Type | Description | +| --------- | ----------- | ------------- | +| `idx` | `BigNumber` | Filter index. | + +```ts +async getFilterChanges(idx: BigNumber): Promise> +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const filter = await provider.newFilter({ + address: utils.L2_ETH_TOKEN_ADDRESS, + topics: [ethers.id("Transfer(address,address,uint256)")], +}); +const result = await provider.getFilterChanges(filter); +``` + ### `getFormatter` Static utility method that returns a `Formatter` object for processing readable block data. ```ts -static override getFormatter(): Formatter { - if (defaultFormatter == null) { - defaultFormatter = new Formatter(); - const number = defaultFormatter.number.bind(defaultFormatter); - const boolean = defaultFormatter.boolean.bind(defaultFormatter); - const hash = defaultFormatter.hash.bind(defaultFormatter); - const address = defaultFormatter.address.bind(defaultFormatter); - - defaultFormatter.formats.receiptLog.l1BatchNumber = Formatter.allowNull(number); - - (defaultFormatter.formats as any).l2Tol1Log = { - blockNumber: number, - blockHash: hash, - l1BatchNumber: Formatter.allowNull(number), - transactionIndex: number, - shardId: number, - isService: boolean, - sender: address, - key: hash, - value: hash, - transactionHash: hash, - logIndex: number - }; - - defaultFormatter.formats.receipt.l1BatchNumber = Formatter.allowNull(number); - defaultFormatter.formats.receipt.l1BatchTxIndex = Formatter.allowNull(number); - defaultFormatter.formats.receipt.l2ToL1Logs = Formatter.arrayOf((value) => - Formatter.check((defaultFormatter.formats as any).l2Tol1Log, value) - ); - - defaultFormatter.formats.block.l1BatchNumber = Formatter.allowNull(number); - defaultFormatter.formats.block.l1BatchTimestamp = Formatter.allowNull(number); - defaultFormatter.formats.blockWithTransactions.l1BatchNumber = Formatter.allowNull(number); - defaultFormatter.formats.blockWithTransactions.l1BatchTimestamp = Formatter.allowNull(number); - defaultFormatter.formats.transaction.l1BatchNumber = Formatter.allowNull(number); - defaultFormatter.formats.transaction.l1BatchTxIndex = Formatter.allowNull(number); - - defaultFormatter.formats.filterLog.l1BatchNumber = Formatter.allowNull(number); - } - return defaultFormatter; -} +static override getFormatter(): Formatter +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const formatter = Provider.getFormatter(); ``` ### `getGasPrice` Returns an estimate (best guess) of the gas price to use in a transaction. -[Ethers implementation.](https://docs.ethers.org/v5/api/providers/provider/#Provider-getGasPrice) +```ts +async getGasPrice(): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Gas price: ${await provider.getGasPrice()}`); +``` ### `getL1BatchBlockRange` @@ -391,14 +567,26 @@ Returns the range of blocks contained within a batch given by batch number. Calls the [`zks_getL1BatchBlockRange`](../../api.md#zks-getl1batchblockrange) JSON-RPC method. -```typescript -async getL1BatchBlockRange(l1BatchNumber: number): Promise<[number, number] | null> { - const range = await this.send('zks_getL1BatchBlockRange', [l1BatchNumber]); - if (range == null) { - return null; - } - return [parseInt(range[0], 16), parseInt(range[1], 16)]; -} +#### Inputs + +| Parameter | Type | Description | +| --------------- | -------- | ---------------- | +| `l1BatchNumber` | `number` | L1 batch number. | + +```ts +async getL1BatchBlockRange(l1BatchNumber: number): Promise<[number, number] | null> +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const l1BatchNumber = await provider.getL1BatchNumber(); +console.log(`L1 batch block range: ${toJSON(await provider.getL1BatchBlockRange(l1BatchNumber))}`); ``` ### `getL1BatchDetails` @@ -407,36 +595,74 @@ Returns data pertaining to a given batch. Calls the [`zks_getL1BatchDetails`](../../api.md#zks-getl1batchdetails) JSON-RPC method. +#### Inputs + +| Parameter | Type | Description | +| --------- | -------- | ---------------- | +| `number` | `number` | L1 batch number. | + +```ts +async getL1BatchDetails(number: number): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const l1BatchNumber = await provider.getL1BatchNumber(); +console.log(`L1 batch details: ${toJSON(await provider.getL1BatchDetails(l1BatchNumber))}`); +``` + ### `getL1BatchNumber` Returns the latest L1 batch number. Calls the [`zks_getL1BatchNumber`](../../api.md#zks-l1batchnumber) JSON-RPC method. +```ts +async getL1BatchNumber(): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`L1 batch number: ${await provider.getL1BatchNumber()}`); +``` + ### `getL2TransactionFromPriorityOp` -Returns a transaction object from a given Ethers [`TransactionResponse`](https://docs.ethers.org/v5/api/providers/types/#providers-TransactionResponse) object. +Returns a L2 transaction from L1 transaction response. #### Inputs -| Name | Description | -| ------------ | ------------------------------------ | -| l1TxResponse | Ethers `TransactionResponse` object. | +| Parameter | Type | Description | +| -------------- | ------------------------------------------------------------------------------------------------------ | ------------------------ | +| `l1TxResponse` | [`TransactionResponse`](https://docs.ethers.org/v5/api/providers/types/#providers-TransactionResponse) | L1 transaction response. | ```ts -async getL2TransactionFromPriorityOp( - l1TxResponse: ethers.providers.TransactionResponse -): Promise { - const receipt = await l1TxResponse.wait(); - const l2Hash = getL2HashFromPriorityOp(receipt, await this.getMainContractAddress()); +async getL2TransactionFromPriorityOp(l1TxResponse: ethers.TransactionResponse): Promise +``` + +#### Example - let status = null; - do { - status = await this.getTransactionStatus(l2Hash); - await sleep(this.pollingInterval); - } while (status == TransactionStatus.NotFound); +Helper function: [toJSON](#tojson). - return await this.getTransaction(l2Hash); +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const l1Tx = "0xcca5411f3e514052f4a4ae1c2020badec6e0998adb52c09959c5f5ff15fba3a8"; +const l1TxResponse = await ethProvider.getTransaction(l1Tx); +if (l1TxResponse) { + console.log(`Tx: ${toJSON(await provider.getL2TransactionFromPriorityOp(l1TxResponse))}`); } ``` @@ -446,95 +672,149 @@ Returns the proof for a transaction's L2 to L1 log sent via the L1Messenger syst Calls the [`zks_getL2ToL1LogProof`](../../api.md#zks-getl2tol1logproof) JSON-RPC method. +#### Inputs + +| Parameter | Type | Description | +| --------- | ----------- | ---------------------------------------------------------------- | +| `txHash` | `BytesLike` | Hash of the L2 transaction the L2 to L1 log was produced within. | +| `index?` | `number` | The index of the L2 to L1 log in the transaction (optional). | + +```ts +async getLogProof(txHash: BytesLike, index ? : number): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +// Any L2 -> L1 transaction can be used. +// In this case, withdrawal transaction is used. +const tx = "0x2a1c6c74b184965c0cb015aae9ea134fd96215d2e4f4979cfec12563295f610e"; +console.log(`Log ${toJSON(await provider.getLogProof(tx, 0))}`); +``` + ### `getLogs` Returns an array of all logs that match a filter with a given id by calling Ethereum method [`eth_getLogs.`](https://ethereum.github.io/execution-apis/api-documentation/) +#### Inputs + +| Parameter | Type | Description | +| --------- | --------------------------------------------------------------------------- | ------------- | +| `filter` | [`Filter`] or [`FilterByBlockHash`] or `Promise` | Filter query. | + +```ts +getLogs(filter: Filter | FilterByBlockHash | Promise): Promise> +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Logs: ${toJSON(await provider.getLogs({ fromBlock: 0, toBlock: 5, address: utils.L2_ETH_TOKEN_ADDRESS }))}`); +``` + ### `getMainContractAddress` Returns the main zkSync Era smart contract address. Calls the [`zks_getMainContract`](../../api.md#zks-getmaincontract) JSON-RPC method. -```typescript -async getMainContractAddress(): Promise
{ - if (!this.contractAddresses.mainContract) { - this.contractAddresses.mainContract = await this.send('zks_getMainContract', []); - } - return this.contractAddresses.mainContract; -} +```ts +async getMainContractAddress(): Promise
``` -### `getMessageProof` +#### Example -Returns the proof for a message sent via the L1Messenger system contract. +Helper function: [toJSON](#tojson). -Calls the [`zks_getL2ToL1MsgProof`](../../api.md#zks-getl2tol1msgproof) JSON-RPC method. +```ts +import { Provider, types } from "zksync-ethers"; -```typescript -async getMessageProof( - blockNumber: ethers.BigNumberish, - sender: Address, - messageHash: BytesLike, - logIndex?: number -): Promise { - return await this.send('zks_getL2ToL1MsgProof', [ - BigNumber.from(blockNumber).toNumber(), - sender, - ethers.utils.hexlify(messageHash), - logIndex - ]); -} +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Main contract: ${await provider.getMainContractAddress()}`); ``` ### `getPriorityOpResponse` -Returns an Ethers [`TransactionResponse`](https://docs.ethers.org/v5/api/providers/types/#providers-TransactionResponse) as a `PriorityOpResponse` object. +Returns a [`TransactionResponse`](https://docs.ethers.org/v5/api/providers/types/#providers-TransactionResponse) as a `PriorityOpResponse` object. #### Inputs -| Name | Description | -| ------------ | ------------------------------------ | -| l1TxResponse | Ethers `TransactionResponse` object. | +| Parameter | Type | Description | +| -------------- | ------------------------------------------------------------------------------------------------------ | ------------------------ | +| `l1TxResponse` | [`TransactionResponse`](https://docs.ethers.org/v5/api/providers/types/#providers-TransactionResponse) | L1 transaction response. | ```ts -async getPriorityOpResponse(l1TxResponse: ethers.providers.TransactionResponse): Promise { - const l2Response = { ...l1TxResponse } as PriorityOpResponse; +async getPriorityOpResponse(l1TxResponse: ethers.TransactionResponse): Promise +``` - l2Response.waitL1Commit = l2Response.wait; - l2Response.wait = async () => { - const l2Tx = await this.getL2TransactionFromPriorityOp(l1TxResponse); - return await l2Tx.wait(); - }; - l2Response.waitFinalize = async () => { - const l2Tx = await this.getL2TransactionFromPriorityOp(l1TxResponse); - return await l2Tx.waitFinalize(); - }; +#### Example + +Helper function: [toJSON](#tojson). - return l2Response; +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const l1Tx = "0xcca5411f3e514052f4a4ae1c2020badec6e0998adb52c09959c5f5ff15fba3a8"; +const l1TxResponse = await ethProvider.getTransaction(l1Tx); +if (l1TxResponse) { + console.log(`Tx: ${toJSON(await provider.getPriorityOpResponse(l1TxResponse))}`); } ``` +### `getRawBlockTransactions` + +Returns data of transactions in a block. + +Calls the [`zks_getRawBlockTransactions`](../../api.md#zks-getrawblocktransactions) JSON-RPC method. + +#### Inputs + +| Parameter | Type | Description | +| --------- | -------- | ------------- | +| `number` | `number` | Block number. | + +```ts +async getRawBlockTransactions(number: number): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Goerli); +console.log(`Raw block transactions: ${toJSON(await provider.getRawBlockTransactions(90_000))}`); +``` + ### `getTestnetPaymasterAddress` Returns the [testnet paymaster](../../developer-reference/account-abstraction.md#paymasters) address if available, or null. -```typescript -async getTestnetPaymasterAddress(): Promise
{ - // Unlike contract's addresses, the testnet paymaster is not cached, since it can be trivially changed - // on the fly by the server and should not be relied to be constant - return await this.send('zks_getTestnetPaymaster', []); -} +```ts +async getTestnetPaymasterAddress(): Promise
``` #### Example -```typescript -import { Provider } from "zksync-ethers"; - -const provider = new Provider("https://sepolia.era.zksync.dev"); +```ts +import { Provider, types } from "zksync-ethers"; -console.log(await provider.getTestnetPaymasterAddress()); +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`Testnet paymaster: ${await provider.getTestnetPaymasterAddress()}`); ``` ### `getTransaction` @@ -543,23 +823,20 @@ Returns a specified L2 transaction response object by overriding the [Ethers imp #### Inputs -| Name | Description | -| ---- | ----------- | -| hash | string | +| Parameter | Type | Description | +| --------- | -------- | ----------------- | +| `txHash` | `string` | Transaction hash. | ```typescript -override async getTransaction(hash: string | Promise): Promise { - hash = await hash; - const tx = await super.getTransaction(hash); - return tx ? this._wrapTransaction(tx, hash) : null; -} +override async getTransaction(hash: string | Promise): Promise ``` #### Example -```typescript -import { Provider } from "zksync-ethers"; -const provider = new Provider("https://sepolia.era.zksync.dev"); +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); const TX_HASH = ""; const txHandle = await provider.getTransaction(TX_HASH); @@ -576,47 +853,156 @@ Returns data from a specific transaction given by the transaction hash. Calls the [`getTransactionDetails`](../../api.md#zks-gettransactiondetails) JSON-RPC method. +#### Inputs + +| Parameter | Type | Description | +| --------- | ----------- | ----------------- | +| `txHash` | `BytesLike` | Transaction hash. | + +```ts +async getTransactionDetails(txHash: BytesLike): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); + +const TX_HASH = ""; +console.log(`Transaction details: ${toJSON(await provider.getTransactionDetails(TX_HASH))}`); +``` + ### `getTransactionReceipt` Returns the transaction receipt from a given hash number. [Ethers implementation.](https://docs.ethers.org/v5/api/providers/provider/#Provider-getTransactionReceipt) +#### Inputs + +| Parameter | Type | Description | +| --------- | -------- | ----------------- | +| `txHash` | `string` | Transaction hash. | + +```ts +async getTransactionReceipt(transactionHash: string | Promise): Promise +``` + +#### Example + +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const TX_HASH = ""; +console.log(`Transaction receipt: ${toJSON(await provider.getTransactionReceipt(TX_HASH))}`); +``` + ### `getTransactionStatus` Returns the status of a specified transaction. #### Inputs -| Name | Description | -| ------ | ----------- | -| txHash | string | +| Parameter | Type | Description | +| --------- | -------- | ----------------- | +| `txHash` | `string` | Transaction hash. | -```typescript -async getTransactionStatus(txHash: string) { - const tx = await this.getTransaction(txHash); - if (tx == null) { - return TransactionStatus.NotFound; - } - if (tx.blockNumber == null) { - return TransactionStatus.Processing; - } - const verifiedBlock = await this.getBlock('finalized'); - if (tx.blockNumber <= verifiedBlock.number) { - return TransactionStatus.Finalized; - } - return TransactionStatus.Committed; -} +```ts +async getTransactionStatus(txHash: string): Promise ``` #### Example -```typescript -import { Provider } from "zksync-ethers"; -const provider = new Provider("https://sepolia.era.zksync.dev"); +Helper function: [toJSON](#tojson). + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); + +const TX_HASH = ""; +console.log(`Transaction status: ${toJSON(await provider.getTransactionStatus(TX_HASH))}`); +``` + +### `getTransferTx` + +Returns the populated transfer transaction. + +#### Inputs + +| Parameter | Type | Description | +| ------------ | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getTransferTx(transaction: { + to: Address; + amount: BigNumberish; + from ? : Address; + token ? : Address; + overrides?: ethers.CallOverrides; +}): Promise +``` + +#### Example + +```ts +const tx = await provider.getTransferTx({ + token: utils.ETH_ADDRESS, + amount: 7_000_000_000, + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", +}); +console.log(`Gas for transfer tx: ${tx}`); +``` + +### `getWithdrawTx` -const TX_HASH = "YOUR_TX_HASH_ADDRESS"; -console.log(await provider.getTransactionStatus(TX_HASH)); +Returns the populated withdrawal transaction. + +#### Inputs + +| Parameter | Type | Description | +| ---------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `bridgeAddress?` | `Address` | Bridge address (optional). | +| `overrides?` | `ethers.CallOverrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getWithdrawTx(transaction: { + token: Address; + amount: BigNumberish; + from ? : Address; + to ? : Address; + bridgeAddress ? : Address; + overrides?: ethers.CallOverrides; +}): Promise +``` + +#### Example + +```ts +const tx = await provider.getWithdrawTx({ + token: utils.ETH_ADDRESS, + amount: 7_000_000_000, + to: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", +}); +console.log(`Gas for withdrawal tx: ${tx}`); ``` ### `l1ChainId` @@ -625,6 +1011,19 @@ Returns the chain id of the underlying L1. Calls the [`zks_L1ChainId`](../../api.md#zks-l1chainid) JSON-RPC method. +```ts +async l1ChainId(): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`L1 chain ID: ${await provider.l1ChainId()}`); +``` + ### `l1TokenAddress` Returns the L1 token address equivalent for a L2 token address as they are not equal. ETH's address is set to zero address. @@ -635,20 +1034,21 @@ Only works for tokens bridged on default zkSync Era bridges. #### Inputs -| Name | Description | -| ----- | ------------------------------- | -| token | The address of the token on L2. | +| Parameter | Type | Description | +| --------- | --------- | ------------------------------- | +| `token` | `Address` | The address of the token on L2. | -```typescript -async l1TokenAddress(token: Address) { - if (token == ETH_ADDRESS) { - return ETH_ADDRESS; - } else { - const erc20BridgeAddress = (await this.getDefaultBridgeAddresses()).erc20L2; - const erc20Bridge = IL2BridgeFactory.connect(erc20BridgeAddress, this); - return await erc20Bridge.l1TokenAddress(token); - } -} +```ts +async l1TokenAddress(token: Address): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`L1 token address: ${await provider.l1TokenAddress("0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b")}`); ``` ### `l2TokenAddress` @@ -661,38 +1061,119 @@ Only works for tokens bridged on default zkSync Era bridges. #### Inputs -| Name | Description | -| ----- | ------------------------------- | -| token | The address of the token on L1. | +| Parameter | Type | Description | +| --------- | --------- | ------------------------------- | +| `token` | `Address` | The address of the token on L1. | -```typescript -async l2TokenAddress(token: Address) { - if (token == ETH_ADDRESS) { - return ETH_ADDRESS; - } else { - const erc20BridgeAddress = (await this.getDefaultBridgeAddresses()).erc20L2; - const erc20Bridge = IL2BridgeFactory.connect(erc20BridgeAddress, this); - return await erc20Bridge.l2TokenAddress(token); - } -} +```ts +async l2TokenAddress(token: Address): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`L2 token address: ${await provider.l2TokenAddress("0x5C221E77624690fff6dd741493D735a17716c26B")}`); ``` ### `newBlockFilter` Returns a new block filter by calling Ethereum method [`eth_newBlockFilter.`](https://ethereum.github.io/execution-apis/api-documentation/) +```ts +async newBlockFilter(): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`New block filter: ${await provider.newBlockFilter()}`); +``` + ### `newFilter` Returns a new filter by calling Ethereum method [`eth_newFilter`](https://ethereum.github.io/execution-apis/api-documentation/) and passing a filter object. +#### Inputs + +| Parameter | Type | Description | +| --------- | --------------------------------------------------------------- | ------------- | +| `filter` | [`EventFilter`](types.md#eventfilter) or `Promise` | Filter query. | + +```ts +async newFilter(filter: EventFilter | Promise): Promise +``` + +#### Example + +```ts +import { Provider, types, utils } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log( + `New filter: ${await provider.newFilter({ + fromBlock: 0, + toBlock: 5, + address: utils.L2_ETH_TOKEN_ADDRESS, + })}` +); +``` + ### `newPendingTransactionFilter` -Returns a new pending transaction filter by calling Ethereum method [`eth_newPendingTransactionFilter`](https://ethereum.github.io/execution-apis/api-documentation/) and passing a filter object. +Returns a new pending transaction filter by calling Ethereum method [`eth_newPendingTransactionFilter`](https://ethereum.github.io/execution-apis/api-documentation/) and passing a +filter object. + +```ts +async newPendingTransactionsFilter(): Promise +``` + +#### Example + +```ts +import { Provider, types } from "zksync-ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +console.log(`New pending transaction filter: ${await provider.newPendingTransactionsFilter()}`); +``` ### `sendTransaction` Override of [Ethers implementation.](https://docs.ethers.org/v5/api/providers/provider/#Provider-sendTransaction) +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------------------- | ------------------- | +| `transaction` | `string` or `Promise` | Signed transaction. | + +```ts +async sendTransaction(transaction: string | Promise): Promise +``` + +#### Example + +```ts +import { Provider, types, Wallet } from "zksync-ethers"; + +const PRIVATE_KEY = ""; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const wallet = new Wallet(PRIVATE_KEY, provider); + +const signedTx = await wallet.signTransaction({ + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: ethers.utils.parseEther("0.01"), +}); + +const tx = await provider.sendTransaction(signedTx); +console.log(tx.hash); +``` + ## `Web3Provider` Use this provider for Web3 browser wallet integrations for easy compatibility with Metamask, WalletConnect, and other popular browser wallets. @@ -701,31 +1182,20 @@ Use this provider for Web3 browser wallet integrations for easy compatibility wi Returns a provider object by extending the constructor of the `Provider` class and accepting an `ExternalProvider` instead of a node URL. -#### Inputs and outputs +#### Inputs -| Name | Description | -| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| provider | The [`ethers.providers.ExternalProvider`](https://docs.ethers.org/v5/api/providers/other/#Web3Provider--ExternalProvider) class instance. For instance, Metamask is `window.ethereum`. | -| network? | The description of the network. | +| Parameter | Type | Description | +| ---------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `provider` | [`ExternalProvider`](https://docs.ethers.org/v5/api/providers/other/#Web3Provider--ExternalProvider)) | The `ethers.providers.ExternalProvider` class instance. For instance, Metamask is `window.ethereum`. | +| `network?` | `Networkish` | The description of the network (optional). | ```typescript -constructor(provider: ExternalProvider, network?: ethers.providers.Networkish) { - if (provider == null) { - throw new Error('missing provider'); - } - if (!provider.request) { - throw new Error('provider must implement eip-1193'); - } - - let path = provider.host || provider.path || (provider.isMetaMask ? 'metamask' : 'eip-1193:'); - super(path, network); - this.provider = provider; -} +constructor(provider: ExternalProvider, network?: ethers.providers.Networkish) ``` #### Example -```typescript +```ts import { Web3Provider } from "zksync-ethers"; const provider = new Web3Provider(window.ethereum); @@ -735,28 +1205,46 @@ const provider = new Web3Provider(window.ethereum); Returns gas estimate by overriding the zkSync Era [`estimateGas`](#estimategas) method. -#### Inputs and outputs +#### Inputs -| Name | Description | -| ----------- | ----------------------------------------------- | -| transaction | Deferrable object of `TransactionRequest` type. | +| Parameter | Type | Description | +| ------------- | -------------------- | -------------------- | +| `transaction` | `TransactionRequest` | Transaction request. | -```typescript -override async estimateGas(transaction: ethers.utils.Deferrable) { - const gas: BigNumber = await super.estimateGas(transaction); - const metamaskMinimum = BigNumber.from(21000); - const isEIP712 = transaction.customData != null || transaction.type == EIP712_TX_TYPE; - return gas.gt(metamaskMinimum) || isEIP712 ? gas : metamaskMinimum; -} +```ts +override async estimateGas(transaction: ethers.utils.Deferrable) +``` + +#### Example + +```ts +import { Web3Provider } from "zksync-ethers"; + +const provider = new Web3Provider(window.ethereum); +const gas = await provider.estimateGas({ + to: "", + amount: ethers.parseEther("0.01"), +}); +console.log(`Gas: ${gas}`); ``` ### `getSigner` Override of [Ethers implementation](https://docs.ethers.org/v5/api/providers/jsonrpc-provider/#JsonRpcProvider-getSigner). +#### Inputs + +| Parameter | Type | Description | +| ----------------- | -------------------- | ------------------------------------ | +| `addressOrIndex?` | `number` or `string` | Account address or index (optional). | + +```ts +getSigner(addressOrIndex?: number | string): Signer +``` + #### Example -```typescript +```ts import { Web3Provider } from "zksync-ethers"; const provider = new Web3Provider(window.ethereum); @@ -767,26 +1255,28 @@ const signer = provider.getSigner(); Returns a provider request object by overriding the [Ethers implementation](https://docs.ethers.org/v5/api/providers/jsonrpc-provider/#JsonRpcProvider-send). -#### Inputs and outputs +#### Inputs -| Name | Description | -| ------- | ----------------------------- | -| method | Request method name as string | -| params? | Optional array of any type | +| Parameter | Type | Description | +| --------- | ------------ | ---------------------------------- | +| `method` | `Address` | Request method name as string. | +| `params?` | `Array` | Parameters of any type (optional). | -```typescript -override async send(method: string, params?: Array): Promise { - params ??= []; - // Metamask complains about eth_sign (and on some versions hangs) - if (method == 'eth_sign' && (this.provider.isMetaMask || this.provider.isStatus)) { - // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign - method = 'personal_sign'; - params = [params[1], params[0]]; - } - return await this.provider.request({ method, params }); -} +```ts +async send(method: string, params?: Array): Promise +``` + +## Appendix -override getSigner(addressOrIndex?: number | string): Signer { - return Signer.from(super.getSigner(addressOrIndex) as any); +### toJSON + +```ts +function toJSON(object: any): string { + return JSON.stringify(object, (key, value) => { + if (typeof value === "bigint") { + return value.toString(); // Convert BigInt to string + } + return value; + }); } ``` diff --git a/docs/build/sdks/js/types.md b/docs/build/sdks/js/types.md index 1ed10ae3c8..43db14606d 100644 --- a/docs/build/sdks/js/types.md +++ b/docs/build/sdks/js/types.md @@ -162,6 +162,17 @@ Interface representation of transaction fee. - `maxPriorityFeePerGas`: `BigNumber`; - `maxFeePerGas`: `BigNumber`; +## `FinalizeWithdrawalParams` + +Interface representation of finalize withdrawal parameters. + +- `l1BatchNumber`: `number | null`; +- `l2MessageIndex`: `number`; +- `l2TxNumberInBlock`: `number | null`; +- `message`: `any`; +- `sender`: `string`; +- `proof`: `string[]`; + ## `FullDepositFee` Interface representation of full deposit fee containing various mandatory and optional fields. @@ -240,6 +251,35 @@ Interface representation of priority op response that extends [`TransactionRespo - `waitL1Commit(confirmation?: number)`: `Promise`; +## `RawBlockTransaction` + +Interface representation of raw block with transactions + +- `common_data`: + - `L2`: + - `nonce`: `number`; + - `fee`: + - `gas_limit`: `BigInt`; + - `max_fee_per_gas`: `BigInt`; + - `max_priority_fee_per_gas`: `BigInt`; + - `gas_per_pubdata_limit`: `BigInt`; + - `initiatorAddress`: `Address`; + - `signature`: `Uint8Array`; + - `transactionType`: `string`; + - `input` + - `hash`: `string`; + - `data`: `Uint8Array`; + - `paymasterParams`: + - `paymaster`: `Address`; + - `paymasterInput`: `Uint8Array`; +- `execute`: + - `calldata`: `string`; + - `contractAddress`: `Address`; + - `factoryDeps`: `BytesLike[]`; + - `value`: `BigInt`; +- `received_timestamp_ms`: `number`; +- `raw_bytes`: `string`; + ## `Signature` 0x-prefixed, hex-encoded, ECDSA signature as string. diff --git a/docs/build/sdks/js/utils.md b/docs/build/sdks/js/utils.md index 7f32beea18..956d557af1 100644 --- a/docs/build/sdks/js/utils.md +++ b/docs/build/sdks/js/utils.md @@ -31,7 +31,7 @@ import { utils } from "zksync-ethers"; #### zkSync Era main contract ```typescript -export const ZKSYNC_MAIN_ABI = new utils.Interface(require("../../abi/IZkSync.json").abi); +export const ZKSYNC_MAIN_ABI = new utils.Interface(require("../abi/IZkSync.json")); ``` #### IERC20 @@ -39,13 +39,15 @@ export const ZKSYNC_MAIN_ABI = new utils.Interface(require("../../abi/IZkSync.js For interacting with native tokens. ```typescript -export const IERC20 = new utils.Interface(require("../../abi/IERC20.json").abi); +export const IERC20 = new utils.Interface(require("../abi/IERC20.json")); ``` #### IERC1271 +For interacting with contract which implements ERC1271. + ```ts -export const IERC1271 = new utils.Interface(require("../../abi/IERC1271.json").abi); +export const IERC1271 = new utils.Interface(require("../abi/IERC1271.json")); ``` #### Contract deployer @@ -53,7 +55,7 @@ export const IERC1271 = new utils.Interface(require("../../abi/IERC1271.json").a Used for deploying smart contracts. ```ts -export const CONTRACT_DEPLOYER = new utils.Interface(require("../../abi/ContractDeployer.json").abi); +export const CONTRACT_DEPLOYER = new utils.Interface(require("../abi/ContractDeployer.json")); ``` #### L1 messenger @@ -61,7 +63,7 @@ export const CONTRACT_DEPLOYER = new utils.Interface(require("../../abi/Contract Used for sending messages from zkSync Era to Ethereum. ```ts -export const L1_MESSENGER = new utils.Interface(require("../../abi/IL1Messenger.json").abi); +export const L1_MESSENGER = new utils.Interface(require("../abi/IL1Messenger.json")); ``` #### L1 and L2 bridges @@ -69,8 +71,8 @@ export const L1_MESSENGER = new utils.Interface(require("../../abi/IL1Messenger. Bridge interface ABIs for L1 and L2. ```ts -export const L1_BRIDGE_ABI = new utils.Interface(require("../../abi/IL1Bridge.json").abi); -export const L2_BRIDGE_ABI = new utils.Interface(require("../../abi/IL2Bridge.json").abi); +export const L1_BRIDGE_ABI = new utils.Interface(require("../abi/IL1Bridge.json")); +export const L2_BRIDGE_ABI = new utils.Interface(require("../abi/IL2Bridge.json")); ``` #### NonceHolder @@ -78,15 +80,7 @@ export const L2_BRIDGE_ABI = new utils.Interface(require("../../abi/IL2Bridge.js Used for managing deployment nonce. ```ts -export const NONCE_HOLDER_ABI = new ethers.Interface(require("../abi/INonceHolder.json").abi); -``` - -#### PaymasterFlow - -Used for encoding paymaster flows. - -```ts -export const PAYMASTER_FLOW_ABI = new ethers.Interface(require("../abi/IPaymasterFlow.json").abi); +export const NONCE_HOLDER_ABI = new ethers.Interface(require("../abi/INonceHolder.json")); ``` #### L1 to L2 alias offset @@ -113,6 +107,30 @@ Constant representing an EIP712 transaction type. export const EIP712_TX_TYPE = 0x71; ``` +#### EIP712 structures + +Collection of EIP712 structures with their types. + +```ts +export const EIP712_TYPES = { + Transaction: [ + { name: "txType", type: "uint256" }, + { name: "from", type: "uint256" }, + { name: "to", type: "uint256" }, + { name: "gasLimit", type: "uint256" }, + { name: "gasPerPubdataByteLimit", type: "uint256" }, + { name: "maxFeePerGas", type: "uint256" }, + { name: "maxPriorityFeePerGas", type: "uint256" }, + { name: "paymaster", type: "uint256" }, + { name: "nonce", type: "uint256" }, + { name: "value", type: "uint256" }, + { name: "data", type: "bytes" }, + { name: "factoryDeps", type: "bytes32[]" }, + { name: "paymasterInput", type: "bytes" }, + ], +}; +``` + #### Priority op transaction on L2 Constant representing a priority transaction operation on L2. @@ -197,14 +215,6 @@ export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800; Converts the address that submitted a transaction to the inbox on L1 to the `msg.sender` viewed on L2. -#### Inputs - -| Parameter | Type | Description | -| --------- | ------ | ----------------- | -| `address` | string | Contract address. | - -#### Outputs - Returns the `msg.sender` of the L1->L2 transaction as the `address` of the contract that initiated the transaction. :::tip More info @@ -214,14 +224,53 @@ Returns the `msg.sender` of the L1->L2 transaction as the `address` of the contr 3. During L1->L2 communication, if a contract A calls contract B, the `msg.sender` is `applyL1ToL2Alias(A)`. ::: +#### Inputs + +| Parameter | Type | Description | +| --------- | ------ | ----------------- | +| `address` | string | Contract address. | + ```ts -export function applyL1ToL2Alias(address: string): string { - return ethers.utils.hexlify(ethers.BigNumber.from(address).add(L1_TO_L2_ALIAS_OFFSET).mod(ADDRESS_MODULO)); -} +export function applyL1ToL2Alias(address: string): string; +``` + +#### Example + +```ts +const l1ContractAddress = "0x702942B8205E5dEdCD3374E5f4419843adA76Eeb"; +const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress); +// l2ContractAddress = "0x813A42B8205E5DedCd3374e5f4419843ADa77FFC" ``` See also [`undol1tol2alias`](#undol1tol2alias). +### `checkBaseCost` + +Checks if the transaction's base cost is greater than the provided value, which covers the transaction's cost. Throws an error if it is not. + +#### Inputs + +| Parameter | Type | Description | +| ---------- | -------------- | ------------------------------------------ | +| `baseCost` | `BigNumberish` | transaction's base cost. | +| `value` | `BigNumberish` | value which covers the transaction's cost. | + +```ts +async function checkBaseCost(baseCost: ethers.BigNumberish, value: ethers.BigNumberish | Promise): Promise; +``` + +### Example + +```ts +const baseCost = 100; +const value = 99; +try { + await utils.checkBaseCost(baseCost, value); +} catch (e) { + // e.message = `The base cost of performing the priority operation is higher than the provided value parameter for the transaction: baseCost: ${baseCost}, provided value: ${value}`, +} +``` + ### `create2Address` Generates a future-proof contract address using salt plus bytecode which allows determination of an address before deployment. @@ -233,24 +282,22 @@ Generates a future-proof contract address using salt plus bytecode which allows #### Inputs -| Parameter | Type | Description | -| -------------- | ------------------ | ---------------------------------- | -| `sender` | string | Sender address. | -| `bytecodeHash` | `BytesLike` object | Output from zkSolc. | -| `salt` | `BytesLike` object | Randomization element. | -| `input` | `BytesLike` object | ABI encoded constructor arguments. | +| Parameter | Type | Description | +| -------------- | ----------- | ---------------------------------- | +| `sender` | `Address` | Sender address. | +| `bytecodeHash` | `BytesLike` | Output from zkSolc. | +| `salt` | `BytesLike` | Randomization element. | +| `input` | `BytesLike` | ABI encoded constructor arguments. | -#### Outputs +```ts +export function create2Address(sender: Address, bytecodeHash: BytesLike, salt: BytesLike, input: BytesLike): string; +``` -- Returns an `Address` object. +#### Example ```ts -export function create2Address(sender: Address, bytecodeHash: BytesLike, salt: BytesLike, input: BytesLike) { - const prefix = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("zksyncCreate2")); - const inputHash = ethers.utils.keccak256(input); - const addressBytes = ethers.utils.keccak256(ethers.utils.concat([prefix, ethers.utils.zeroPad(sender, 32), salt, bytecodeHash, inputHash])).slice(26); - return ethers.utils.getAddress(addressBytes); -} +const address = utils.create2Address("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", "0x010001cb6a6e8d5f6829522f19fa9568660e0a9cd53b2e8be4deb0a679452e41", "0x01", "0x01"); +// address = "0x29bac3E5E8FFE7415F97C956BFA106D70316ad50" ``` :::tip @@ -263,89 +310,35 @@ Generates a contract address from deployer's account and nonce. #### Inputs -| Parameter | Type | Description | -| ------------- | --------------------- | --------------- | -| `sender` | string | Sender address. | -| `senderNonce` | `BigNumberish` object | Sender nonce. | - -#### Outputs - -- Returns an `Address` object. +| Parameter | Type | Description | +| ------------- | -------------- | --------------- | +| `sender` | `Address` | Sender address. | +| `senderNonce` | `BigNumberish` | Sender nonce. | ```ts -export function createAddress(sender: Address, senderNonce: BigNumberish) { - const prefix = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("zksyncCreate")); - const addressBytes = ethers.utils - .keccak256(ethers.utils.concat([prefix, ethers.utils.zeroPad(sender, 32), ethers.utils.zeroPad(ethers.utils.hexlify(senderNonce), 32)])) - .slice(26); - - return ethers.utils.getAddress(addressBytes); -} +export function createAddress(sender: Address, senderNonce: BigNumberish): string; ``` -### `eip712TxHash` - -Returns the hash of an EIP712 transaction. - -#### Inputs - -| Parameter | Type | Description | -| --------------- | ------------------------------ | ----------------------------------- | -| `transaction` | any | EIP-712 transaction. | -| `ethSignature?` | `EthereumSignature` (optional) | ECDSA signature of the transaction. | +#### Example ```ts -function eip712TxHash(transaction: any, ethSignature?: EthereumSignature) { - const signedDigest = EIP712Signer.getSignedDigest(transaction); - const hashedSignature = ethers.utils.keccak256(getSignature(transaction, ethSignature)); - - return ethers.utils.keccak256(ethers.utils.hexConcat([signedDigest, hashedSignature])); -} +const address = utils.createAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", 1); +// address = "0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021" ``` -### `estimateCustomBridgeDepositL2Gas` - -Used by `estimateDefaultBridgeDepositL2Gas` to estimate L2 gas required for token bridging via a custom ERC20 bridge. - -::: tip More info +### `eip712TxHash` -- See the [default bridges documentation](../../developer-reference/bridging-asset.md#default-bridges) - ::: +Returns the hash of an EIP712 transaction. #### Inputs -| Parameter | Type | Description | -| -------------------- | -------------------------------- | -------------------------------- | -| `providerL2` | `Provider` object | zkSync provider. | -| `l1BridgeAddress` | string | L1 bridge address. | -| `l2BridgeAddress` | string | L2 bridge address. | -| `token` | string | Token address. | -| `amount` | `BigNumberish` object | Deposit amount. | -| `to` | string | Recipient address. | -| `bridgeData` | `BytesLike` object | Bridge data. | -| `from?` | string (optional) | Sender address. | -| `gasPerPubdataByte?` | `BigNumberish` object (optional) | Current gas per byte of pubdata. | +| Parameter | Type | Description | +| --------------- | --------------------------------------------------- | ---------------------------------------------- | +| `transaction` | `any` | EIP-712 transaction. | +| `ethSignature?` | [`EthereumSignature`](./types.md#ethereumsignature) | ECDSA signature of the transaction (optional). | ```ts -export async function estimateCustomBridgeDepositL2Gas( - providerL2: Provider, - l1BridgeAddress: Address, - l2BridgeAddress: Address, - token: Address, - amount: BigNumberish, - to: Address, - bridgeData: BytesLike, - from?: Address, - gasPerPubdataByte?: BigNumberish -): Promise { - const calldata = await getERC20BridgeCalldata(token, from, to, amount, bridgeData); - return await providerL2.estimateL1ToL2Execute({ - caller: applyL1ToL2Alias(l1BridgeAddress), - contractAddress: l2BridgeAddress, - gasPerPubdataByte: gasPerPubdataByte, - calldata: calldata, - }); -} +function eip712TxHash(transaction: any, ethSignature?: EthereumSignature): string; ``` ### `estimateDefaultBridgeDepositL2Gas` @@ -359,15 +352,15 @@ Returns an estimation of L2 gas required for token bridging via the default ERC2 #### Inputs -| Parameter | Type | Description | -| -------------------- | -------------------------------- | -------------------------------- | -| `providerL1` | Provider object | Ethers provider. | -| `providerL2` | Provider object | zkSync provider. | -| `token` | string | Token address. | -| `amount` | `BigNumberish` object | Deposit amount. | -| `to` | string | Recipient address. | -| `from?` | string (optional) | Sender address. | -| `gasPerPubdataByte?` | `BigNumberish` object (optional) | Current gas per byte of pubdata. | +| Parameter | Type | Description | +| -------------------- | -------------- | ------------------------------------------- | +| `providerL1` | `Provider` | Ethers provider. | +| `providerL2` | `Provider` | zkSync provider. | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Deposit amount. | +| `to` | `Address` | Recipient address. | +| `from?` | `Address` | Sender address (optional). | +| `gasPerPubdataByte?` | `BigNumberish` | Current gas per byte of pubdata (optional). | ```ts export async function estimateDefaultBridgeDepositL2Gas( @@ -378,27 +371,7 @@ export async function estimateDefaultBridgeDepositL2Gas( to: Address, from?: Address, gasPerPubdataByte?: BigNumberish -): Promise { - // If the `from` address is not provided, we use a random address, because - // due to storage slot aggregation, the gas estimation will depend on the address - // and so estimation for the zero address may be smaller than for the sender. - from ??= ethers.Wallet.createRandom().address; - - if (token == ETH_ADDRESS) { - return await providerL2.estimateL1ToL2Execute({ - contractAddress: to, - gasPerPubdataByte: gasPerPubdataByte, - caller: from, - calldata: "0x", - l2Value: amount, - }); - } else { - const l1ERC20BridgeAddresses = (await providerL2.getDefaultBridgeAddresses()).erc20L1; - const erc20BridgeAddress = (await providerL2.getDefaultBridgeAddresses()).erc20L2; - const bridgeData = await getERC20DefaultBridgeData(token, providerL1); - return await estimateCustomBridgeDepositL2Gas(providerL2, l1ERC20BridgeAddresses, erc20BridgeAddress, token, amount, to, bridgeData, from, gasPerPubdataByte); - } -} +): Promise; ``` ### `getDeployedContracts` @@ -407,86 +380,30 @@ Returns a log containing details of all deployed contracts related to a transact #### Inputs -| Parameter | Type | Description | -| --------- | --------------------------- | -------------------- | -| `receipt` | `TransactionReceipt` object | Transaction receipt. | - -```ts -export function getDeployedContracts(receipt: ethers.providers.TransactionReceipt): DeploymentInfo[] { - const addressBytesLen = 40; - const deployedContracts = receipt.logs - .filter((log) => log.topics[0] == utils.id("ContractDeployed(address,bytes32,address)") && log.address == CONTRACT_DEPLOYER_ADDRESS) - // Take the last topic (deployed contract address as U256) and extract address from it (U160). - .map((log) => { - const sender = `0x${log.topics[1].slice(log.topics[1].length - addressBytesLen)}`; - const bytesCodehash = log.topics[2]; - const address = `0x${log.topics[3].slice(log.topics[3].length - addressBytesLen)}`; - return { - sender: utils.getAddress(sender), - bytecodeHash: bytesCodehash, - deployedAddress: utils.getAddress(address), - }; - }); - - return deployedContracts; -} -``` - -### `getERC20BridgeCalldata` - -Returns the calldata sent by an L1 ERC20 bridge to its L2 counterpart during token-bridging. - -#### Inputs - -| Parameter | Type | Description | -| ---------------- | --------------------- | ------------------------------------------- | -| `l1TokenAddress` | string | Token address on L1. | -| `l1Sender` | string | Sender address on L1. | -| `l2Receiver` | string | Recipient address on L2. | -| `amount` | `BigNumberish` object | Gas fee for the number of tokens to bridge. | -| `bridgeData` | `BytesLike` object | Data | +| Parameter | Type | Description | +| --------- | ----------------------------------------------------- | -------------------- | +| `receipt` | [`TransactionReceipt`](./types.md#transactionreceipt) | Transaction receipt. | ```ts -export async function getERC20BridgeCalldata(l1TokenAddress: string, l1Sender: string, l2Receiver: string, amount: BigNumberish, bridgeData: BytesLike): Promise { - return L2_BRIDGE_ABI.encodeFunctionData("finalizeDeposit", [l1Sender, l2Receiver, l1TokenAddress, amount, bridgeData]); -} +export function getDeployedContracts(receipt: ethers.providers.TransactionReceipt): DeploymentInfo[]; ``` -### `getERC20DefaultBridgeData` +### `getERC20BridgeCalldata` -Returns the data needed for correct initialization of an L1 token counterpart on L2. +Returns the calldata sent by an L1 ERC20 bridge to its L2 counterpart during token-bridging. #### Inputs -| Parameter | Type | Description | -| ---------------- | --------------- | -------------------- | -| `l1TokenAddress` | string | Token address on L1. | -| `provider` | Provider object | Ethers provider. | - -#### Outputs - -An ABI-encoded array of: - -- `nameBytes`: `bytes` object representation of token name. -- `symbolBytes`: `bytes` object representation of token symbol. -- `decimalBytes`: `bytes` object representation of token decimal representation. +| Parameter | Type | Description | +| ---------------- | -------------- | ------------------------------------------- | +| `l1TokenAddress` | `Address` | Token address on L1. | +| `l1Sender` | `Address` | Sender address on L1. | +| `l2Receiver` | `Address` | Recipient address on L2. | +| `amount` | `BigNumberish` | Gas fee for the number of tokens to bridge. | +| `bridgeData` | `BytesLike` | Data | ```ts -export async function getERC20DefaultBridgeData(l1TokenAddress: string, provider: ethers.providers.Provider): Promise { - const token = IERC20MetadataFactory.connect(l1TokenAddress, provider); - - const name = await token.name(); - const symbol = await token.symbol(); - const decimals = await token.decimals(); - - const coder = new AbiCoder(); - - const nameBytes = coder.encode(["string"], [name]); - const symbolBytes = coder.encode(["string"], [symbol]); - const decimalsBytes = coder.encode(["uint256"], [decimals]); - - return coder.encode(["bytes", "bytes", "bytes"], [nameBytes, symbolBytes, decimalsBytes]); -} +export async function getERC20BridgeCalldata(l1TokenAddress: string, l1Sender: string, l2Receiver: string, amount: BigNumberish, bridgeData: BytesLike): Promise; ``` ### `getL2HashFromPriorityOp` @@ -495,32 +412,13 @@ Returns the hash of the L2 priority operation from a given transaction receipt a #### Inputs -| Parameter | Type | Description | -| --------------- | --------------------------- | ------------------------------------ | -| `txReceipt` | `TransactionReceipt` object | Receipt of the L1 transaction. | -| `zkSyncAddress` | `Address` as string | Address of zkSync Era main contract. | - -```ts -export function getL2HashFromPriorityOp(txReceipt: ethers.providers.TransactionReceipt, zkSyncAddress: Address): string { - let txHash: string = null; - for (const log of txReceipt.logs) { - if (log.address.toLowerCase() != zkSyncAddress.toLowerCase()) { - continue; - } - - try { - const priorityQueueLog = ZKSYNC_MAIN_ABI.parseLog(log); - if (priorityQueueLog && priorityQueueLog.args.txHash != null) { - txHash = priorityQueueLog.args.txHash; - } - } catch {} - } - if (!txHash) { - throw new Error("Failed to parse tx logs"); - } - - return txHash; -} +| Parameter | Type | Description | +| --------------- | ----------------------------------------------------- | ------------------------------------ | +| `txReceipt` | [`TransactionReceipt`](./types.md#transactionreceipt) | Receipt of the L1 transaction. | +| `zkSyncAddress` | `Address` | Address of zkSync Era main contract. | + +```ts +export function getL2HashFromPriorityOp(txReceipt: ethers.providers.TransactionReceipt, zkSyncAddress: Address): string; ``` ### `getHashedL2ToL1Msg` @@ -529,25 +427,22 @@ Returns a keccak encoded message with a given sender address and block number fr #### Inputs -| Parameter | Type | Description | -| ----------------- | ------------------- | -------------------------------------- | -| `sender` | `Address` as string | The sender of the message on L2. | -| `msg` | `BytesLike` object | Encoded message. | -| `txNumberInBlock` | number | Index of the transaction in the block. | +| Parameter | Type | Description | +| ----------------- | ----------- | -------------------------------------- | +| `sender` | `Address` | The sender of the message on L2. | +| `msg` | `BytesLike` | Encoded message. | +| `txNumberInBlock` | number | Index of the transaction in the block. | ```ts -export function getHashedL2ToL1Msg(sender: Address, msg: BytesLike, txNumberInBlock: number) { - const encodedMsg = new Uint8Array([ - 0, // l2ShardId - 1, // isService - ...ethers.utils.zeroPad(ethers.utils.hexlify(txNumberInBlock), 2), - ...ethers.utils.arrayify(L1_MESSENGER_ADDRESS), - ...ethers.utils.zeroPad(sender, 32), - ...ethers.utils.arrayify(ethers.utils.keccak256(msg)), - ]); +export function getHashedL2ToL1Msg(sender: Address, msg: BytesLike, txNumberInBlock: number): string; +``` - return ethers.utils.keccak256(encodedMsg); -} +#### Example + +```ts +const withdrawETHMessage = "0x6c0960f936615cf349d7f6344891b1e7ca7c72883f5dc04900000000000000000000000000000000000000000000000000000001a13b8600"; +const withdrawETHMessageHash = utils.getHashedL2ToL1Msg("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", withdrawETHMessage, 0); +// withdrawETHMessageHash = "0xd8c80ecb64619e343f57c3b133c6c6d8dd0572dd3488f1ca3276c5b7fd3a938d" ``` ### `hashBytecode` @@ -556,161 +451,160 @@ Returns the hash of given bytecode. #### Inputs -| Parameter | Type | Description | -| ---------- | ------------------ | ----------- | -| `bytecode` | `BytesLike` object | Bytecode. | +| Parameter | Type | Description | +| ---------- | ----------- | ----------- | +| `bytecode` | `BytesLike` | Bytecode. | ```ts -export function hashBytecode(bytecode: ethers.BytesLike): Uint8Array { - // For getting the consistent length we first convert the bytecode to UInt8Array - const bytecodeAsArray = ethers.utils.arrayify(bytecode); +export function hashBytecode(bytecode: ethers.BytesLike): Uint8Array; +``` - if (bytecodeAsArray.length % 32 != 0) { - throw new Error("The bytecode length in bytes must be divisible by 32"); - } +#### Examples - if (bytecodeAsArray.length > MAX_BYTECODE_LEN_BYTES) { - throw new Error(`Bytecode can not be longer than ${MAX_BYTECODE_LEN_BYTES} bytes`); - } +```ts +const bytecode = + "0x000200000000000200010000000103550000006001100270000000130010019d0000008001000039000000400010043f0000000101200190000000290000c13d0000000001000031000000040110008c000000420000413d0000000101000367000000000101043b000000e001100270000000150210009c000000310000613d000000160110009c000000420000c13d0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000200310008c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000420000c13d00000004010000390000000101100367000000000101043b000000000010041b0000000001000019000000490001042e0000000001000416000000000110004c000000420000c13d0000002001000039000001000010044300000120000004430000001401000041000000490001042e0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000000310004c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000440000613d00000000010000190000004a00010430000000000100041a000000800010043f0000001801000041000000490001042e0000004800000432000000490001042e0000004a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000006d4ce63c0000000000000000000000000000000000000000000000000000000060fe47b18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000000000000000000000009c8c8fa789967eb514f3ec9def748480945cc9b10fcbd1a19597d924eb201083"; +const hashedBytecode = utils.hashBytecode(bytecode); +/* +hashedBytecode = new Uint8Array([ + 1, 0, 0, 27, 57, 231, 154, 55, 0, 164, 201, 96, 244, 120, 23, 112, 54, 34, 224, 133, + 160, 122, 88, 164, 112, 80, 0, 134, 48, 138, 74, 16, + ]), +); + */ +``` - const hashStr = ethers.utils.sha256(bytecodeAsArray); - const hash = ethers.utils.arrayify(hashStr); +### `isECDSASignatureCorrect` - // Note that the length of the bytecode - // should be provided in 32-byte words. - const bytecodeLengthInWords = bytecodeAsArray.length / 32; - if (bytecodeLengthInWords % 2 == 0) { - throw new Error("Bytecode length in 32-byte words must be odd"); - } +Validates signatures from non-contract account addresses (EOA). Provides similar functionality in `ethers.js` but returns `true` if the +validation process succeeds, otherwise returns `false`. - const bytecodeLength = ethers.utils.arrayify(bytecodeLengthInWords); +Called from [`isSignatureCorrect`](#isSignatureCorrect) for non-contract account addresses. - // The bytecode should always take the first 2 bytes of the bytecode hash, - // so we pad it from the left in case the length is smaller than 2 bytes. - const bytecodeLengthPadded = ethers.utils.zeroPad(bytecodeLength, 2); +#### Inputs - const codeHashVersion = new Uint8Array([1, 0]); - hash.set(codeHashVersion, 0); - hash.set(bytecodeLengthPadded, 2); +| Parameter | Type | Description | +| ----------- | --------------- | ------------------------------ | +| `address` | `string` | Address which signs `msgHash`. | +| `msgHash` | `Address` | Hash of the message. | +| `signature` | `SignatureLike` | Ethers signature. | - return hash; -} +```ts +function isECDSASignatureCorrect(address: string, msgHash: string, signature: SignatureLike): boolean; ``` -### `isECDSASignatureCorrect` +#### Examples -Like similar functionality in `ethers.js` but with added try/catch facility. The function returns true if the validation process succeeds. +```ts +import { Wallet, utils } from "zksync-ethers"; -Called from [`isSignatureCorrect`](#isSignatureCorrect) for non-contract account addresses. +const ADDRESS = ""; +const PRIVATE_KEY = ""; -#### Inputs +const message = "Hello, world!"; +const signature = await new Wallet(PRIVATE_KEY).signMessage(message); +// ethers.Wallet can be used as well +// const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message); -| Parameter | Type | Description | -| ----------- | ---------------------- | ------------------------------ | -| `address` | string | Address which signs `msgHash`. | -| `msgHash` | string | Hash of the message. | -| `signature` | `SignatureLike` object | Ethers signature. | - -```ts -function isECDSASignatureCorrect(address: string, msgHash: string, signature: SignatureLike): boolean { - try { - return address == ethers.utils.recoverAddress(msgHash, signature); - } catch { - // In case ECDSA signature verification throws an error, - // we consider the signature incorrect. - return false; - } -} +const isValidSignature = await utils.isECDSASignatureCorrect(ADDRESS, message, signature); +// isValidSignature = true ``` +See also [`isMessageSignatureCorrect()`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect()`](#istypeddatasignaturecorrect) +to validate signatures. + ### `isEIP1271SignatureCorrect` Called from [`isSignatureCorrect`](#isSignatureCorrect) for contract account addresses, the function returns true if the validation process results in the `EIP1271_MAGIC_VALUE`. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------------------- | ------------------------ | -| `provider` | `Provider` object | Provider. | -| `address` | string | Sender address. | -| `msgHash` | string | The hash of the message. | -| `signature` | `SignatureLike` object | Ethers signature. | +| Parameter | Type | Description | +| ----------- | --------------- | ------------------------ | +| `provider` | `Provider` | Provider. | +| `address` | `string` | Sender address. | +| `msgHash` | `string` | The hash of the message. | +| `signature` | `SignatureLike` | Ethers signature. | ```ts -async function isEIP1271SignatureCorrect(provider: Provider, address: string, msgHash: string, signature: SignatureLike): Promise { - const accountContract = new ethers.Contract(address, IERC1271, provider); - - // This line may throw an exception if the contract does not implement the EIP1271 correctly. - // But it may also throw an exception in case the internet connection is lost. - // It is the caller's responsibility to handle the exception. - const result = await accountContract.isValidSignature(msgHash, signature); - - return result == EIP1271_MAGIC_VALUE; -} +async function isEIP1271SignatureCorrect(provider: Provider, address: string, msgHash: string, signature: SignatureLike): Promise; ``` +See also [`isMessageSignatureCorrect()`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect()`](#istypeddatasignaturecorrect) +to validate signatures. + ### `isETH` Returns true if token represents ETH on L1 or L2. #### Inputs -| Parameter | Type | Description | -| --------- | ---------------- | ------------------ | -| `token` | `Address` object | The token address. | +| Parameter | Type | Description | +| --------- | --------- | ------------------ | +| `token` | `Address` | The token address. | ```ts -export function isETH(token: Address) { - return token.toLowerCase() == ETH_ADDRESS || token.toLowerCase() == L2_ETH_TOKEN_ADDRESS; -} +export function isETH(token: Address); +``` + +#### Example + +```ts +const isL1ETH = utils.isETH(utils.ETH_ADDRESS); // true +const isL2ETH = utils.isETH(utils.L2_ETH_TOKEN_ADDRESS); // true ``` ### `isMessageSignatureCorrect` -Returns true if account abstraction EIP712 signature is correct. +Returns true if account abstraction signature is correct. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------------------- | ------------------------ | -| `provider` | `Provider` object | Provider. | -| `address` | string | Sender address. | -| `message` | string | The hash of the message. | -| `signature` | `SignatureLike` object | Ethers signature. | +| Parameter | Type | Description | +| ----------- | --------------- | ------------------------ | +| `provider` | `Provider` | Provider. | +| `address` | `string` | Sender address. | +| `message` | `string` | The hash of the message. | +| `signature` | `SignatureLike` | Ethers signature. | ```ts -export async function isMessageSignatureCorrect(provider: Provider, address: string, message: ethers.Bytes | string, signature: SignatureLike): Promise { - const msgHash = ethers.utils.hashMessage(message); - return await isSignatureCorrect(provider, address, msgHash, signature); -} +export async function isMessageSignatureCorrect(provider: Provider, address: string, message: ethers.Bytes | string, signature: SignatureLike): Promise; +``` + +#### Example + +```ts +import { Wallet, utils, Provider } from "zksync-ethers"; + +const ADDRESS = ""; +const PRIVATE_KEY = ""; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); + +const message = "Hello, world!"; +const signature = await new Wallet(PRIVATE_KEY).signMessage(message); +// ethers.Wallet can be used as well +// const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message); + +const isValidSignature = await utils.isMessageSignatureCorrect(provider, ADDRESS, message, signature); +// isValidSignature = true ``` ### `isSignatureCorrect` -Called from [`isMessageSignatureCorrect`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect`](#istypeddatasignaturecorrect). Returns true if account abstraction EIP712 signature is correct. +Called from [`isMessageSignatureCorrect`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect`](#istypeddatasignaturecorrect). Returns true if account abstraction EIP712 +signature is correct. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------------------- | ------------------------ | -| `provider` | `Provider` object | Provider. | -| `address` | string | Sender address. | -| `msgHash` | string | The hash of the message. | -| `signature` | `SignatureLike` object | Ethers signature. | +| Parameter | Type | Description | +| ----------- | --------------- | ------------------------ | +| `provider` | `Provider` | Provider. | +| `address` | `string` | Sender address. | +| `msgHash` | `string` | The hash of the message. | +| `signature` | `SignatureLike` | Ethers signature. | ```ts -async function isSignatureCorrect(provider: Provider, address: string, msgHash: string, signature: SignatureLike): Promise { - let isContractAccount = false; - - const code = await provider.getCode(address); - isContractAccount = ethers.utils.arrayify(code).length != 0; - - if (!isContractAccount) { - return isECDSASignatureCorrect(address, msgHash, signature); - } else { - return await isEIP1271SignatureCorrect(provider, address, msgHash, signature); - } -} +async function isSignatureCorrect(provider: Provider, address: string, msgHash: string, signature: SignatureLike): Promise; ``` ### `isTypedDataSignatureCorrect` @@ -721,12 +615,12 @@ Returns true if account abstraction EIP712 signature is correct. | Parameter | Type | Description | | ----------- | ----------------------------- | ------------------------------------------------------ | -| `provider` | `Provider` object | Provider. | -| `address` | string | Sender address. | -| `domain` | `TypedDataDomain` object | Domain data. | +| `provider` | `Provider` | Provider. | +| `address` | `string` | Sender address. | +| `domain` | `TypedDataDomain` | Domain data. | | `types` | `Map` | Map of records pointing from field name to field type. | | `value` | `Record` | A single record value. | -| `signature` | `SignatureLike` object | Ethers signature. | +| `signature` | `SignatureLike` | Ethers signature. | ```ts export async function isTypedDataSignatureCorrect( @@ -736,23 +630,128 @@ export async function isTypedDataSignatureCorrect( types: Record>, value: Record, signature: SignatureLike -): Promise { - const msgHash = ethers.utils._TypedDataEncoder.hash(domain, types, value); - return await isSignatureCorrect(provider, address, msgHash, signature); -} +): Promise; +``` + +#### Example + +```ts +import { Wallet, utils, Provider, EIP712Signer } from "zksync-ethers"; + +const ADDRESS = ""; +const PRIVATE_KEY = ""; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); + +const tx: types.TransactionRequest = { + type: 113, + chainId: 270, + from: ADDRESS, + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: BigInt(7_000_000), +}; + +const eip712Signer = new EIP712Signer( + new Wallet(PRIVATE_KEY), // or new ethers.Wallet(PRIVATE_KEY), + Number((await provider.getNetwork()).chainId) +); + +const signature = await eip712Signer.sign(tx); + +const isValidSignature = await utils.isTypedDataSignatureCorrect(provider, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), signature); +// isValidSignature = true ``` ### `parseTransaction` -Common parsing transaction function used by internal teams. +Parses an EIP712 transaction from a payload. + +#### Inputs + +| Parameter | Type | Description | +| --------- | ----------- | ----------- | +| `payload` | `BytesLike` | Payload. | -Please see the [utilities library definition](https://github.com/zksync-sdk/zksync-ethers/blob/ethers-v5/src/utils.ts) for more info. +```ts +export function parseTransaction(payload: ethers.BytesLike): ethers.Transaction; +``` + +### Example + +```ts +import { types } from "zksync-ethers"; + +const serializedTx = + "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"; +const tx: types.TransactionLike = utils.parseEip712(serializedTx); +/* +tx = { + type: 113, + nonce: 0, + maxPriorityFeePerGas: BigNumber.from(0), + maxFeePerGas: BigNumber.from(0), + gasLimit: BigNumber.from(0), + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: BigNumber.from(1000000), + data: "0x", + chainId: BigNumber.from(270), + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + customData: { + gasPerPubdata: BigNumber.from(50000), + factoryDeps: [], + customSignature: "0x", + paymasterParams: null, + }, + hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee", +}; + */ +``` ### `serialize` -Common serialize function used by internal teams. +Serializes an EIP712 transaction and include a signature if is provided. Throws an error if: -Please see the [utilities library definition](https://github.com/zksync-sdk/zksync-ethers/blob/ethers-v5/src/utils.ts) for more info. +- `transaction.customData.customSignature` is an empty `string`. The transaction should be signed and the + `transaction.customData.customSignature` field should be populated with the signature. It should not be specified if the transaction is not signed. +- `transaction.chainId` is not provided. +- `transaction.from` is not provided. + +#### Inputs + +| Parameter | Type | Description | +| ------------- | -------------------- | ---------------------------------------------------------------------- | +| `transaction` | `TransactionRequest` | Transaction that needs to be serialized. | +| `signature?` | `SignatureLike` | Ethers signature that needs to be included in transactions (optional). | + +```ts +export function serialize(transaction: ethers.providers.TransactionRequest, signature?: SignatureLike); +``` + +#### Examples + +Serialize EIP712 transaction without signature. + +```ts +const serializedTx = utils.serializeEip712({ chainId: 270, from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" }, null); + +// serializedTx = "0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0" +``` + +Serialize EIP712 transaction with signature. + +```ts +const signature = ethers.Signature.from("0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a"); + +const serializedTx = utils.serializeEip712( + { + chainId: 270, + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: 1_000_000, + }, + signature +); +// serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0" +``` ### `sleep` @@ -765,9 +764,7 @@ Common sleep function that pauses execution for a number of milliseconds. | `millis` | number | Number of milliseconds. | ```ts -export function sleep(millis: number) { - return new Promise((resolve) => setTimeout(resolve, millis)); -} +export function sleep(millis: number); ``` ### `undoL1ToL2Alias` @@ -781,14 +778,15 @@ Converts and returns the `msg.sender` viewed on L2 to the address that submitted | `address` | string | Sender address. | ```ts -export function undoL1ToL2Alias(address: string): string { - let result = ethers.BigNumber.from(address).sub(L1_TO_L2_ALIAS_OFFSET); - if (result.lt(BigNumber.from(0))) { - result = result.add(ADDRESS_MODULO); - } +export function undoL1ToL2Alias(address: string): string; +``` - return ethers.utils.hexlify(result); -} +#### Example + +```ts +const l2ContractAddress = "0x813A42B8205E5DedCd3374e5f4419843ADa77FFC"; +const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress); +// const l1ContractAddress = "0x702942B8205E5dEdCD3374E5f4419843adA76Eeb" ``` See also [`applyl1tol2alias`](#applyl1tol2alias). diff --git a/docs/build/sdks/js/zksync-ethers/accounts-l1-l2.md b/docs/build/sdks/js/zksync-ethers/accounts-l1-l2.md index 08a592bdb9..5c9c9b7229 100644 --- a/docs/build/sdks/js/zksync-ethers/accounts-l1-l2.md +++ b/docs/build/sdks/js/zksync-ethers/accounts-l1-l2.md @@ -15,32 +15,32 @@ Full examples of actions below are available on the [getting started](./getting- ## Deposit -`Wallet`, `L1Signer` and `L1VoidSigner` objects provide a deposit workflow. For more information, please refer to the method specification [`Deposit`](accounts.md#deposit). +`Wallet` and `L1Signer` objects provide a deposit workflow. For more information, please refer to the method specification [`Deposit`](accounts.md#deposit). For a complete example of how to execute the deposit workflow, take a look at the following: [Deposit ETH and ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/01_deposit.ts). ## Request execute -`Wallet`, `L1Signer` and `L1VoidSigner` objects provide an option to request execution of L2 transaction from L1. For more information, please refer +`Wallet` and `L1Signer` objects provide an option to request execution of L2 transaction from L1. For more information, please refer to the method specification [`requestExecute`](accounts.md#requestexecute). ## Base cost -`Wallet`, `L1Signer` and `L1VoidSigner` objects provide an option to calculate base cost for L2 transaction. For more information, please refer to the +`Wallet` and `L1Signer` objects provide an option to calculate base cost for L2 transaction. For more information, please refer to the method specification [`getBaseCost`](accounts.md#getbasecost). ## Claim failed deposit -`Wallet`, `L1Signer` and `L1VoidSigner` objects provide a claim fail deposit workflow. For more information, please refer to the method specification +`Wallet` and `L1Signer` objects provide a claim fail deposit workflow. For more information, please refer to the method specification [`claimFailedDeposit`](accounts.md#claimfaileddeposit). ## Finalize withdraw -`Wallet`, `Signer` and `L2VoidSigner` objects provide a finalize withdraw workflow. For more information, please refer to the method specification +`Wallet` and `L1Signer` objects provide a finalize withdraw workflow. For more information, please refer to the method specification [`finalizeWithdrawal`](accounts.md#finalizewithdrawal). ## Withdrawal -`Wallet`, `Signer` and `L2VoidSigner` objects provide a withdrawal workflow. For more information, please refer to the method specification [`Deposit`](accounts.md#deposit). +`Wallet` and `Signer` objects provide a withdrawal workflow. For more information, please refer to the method specification [`Deposit`](accounts.md#deposit). For a complete example of how to execute the deposit workflow, take a look at the following: [Withdraw ETH and ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/03_withdraw.ts). diff --git a/docs/build/sdks/js/zksync-ethers/accounts.md b/docs/build/sdks/js/zksync-ethers/accounts.md index a28d27fd34..06dc76045f 100644 --- a/docs/build/sdks/js/zksync-ethers/accounts.md +++ b/docs/build/sdks/js/zksync-ethers/accounts.md @@ -13,7 +13,7 @@ head: - `Wallet` class is an extension of the `ethers.Wallet` with additional zkSync features. - `EIP712Signer` class that is used to sign `EIP712`_-typed_ zkSync transactions. -- `Signer`, `L1Signer`, `L2VoidSigner`, `L1VoidSigner` classes, which should be used for browser integration. +- `Signer` and `L1Signer` classes, which should be used for browser integration. ## `Wallet` @@ -44,7 +44,7 @@ const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); ``` -### fromMnemonic +### `fromMnemonic` Creates a `Wallet` with the `provider` as L1 provider and a private key that is built from the `mnemonic` passphrase. @@ -71,20 +71,20 @@ const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = Wallet.fromMnemonic(MNEMONIC, ethProvider); ``` -### fromEncryptedJson +### `fromEncryptedJson` Creates a `Wallet` from encrypted `json` file using provided `password`. #### Inputs -| Parameter | Type | Description | -| ----------- | ------------------------ | --------------------------------------------------------------------------------------------------- | -| `json` | `string` | Encrypted json file. | -| `password` | `string` or `Uint8Array` | Password for encrypted json file. | -| `callback?` | `ProgressCallback` | If callback is provided, it is called periodically during decryption so that any UI can be updated. | +| Parameter | Type | Description | +| ----------- | ------------------------ | -------------------------------------------------------------------------------------------------------------- | +| `json` | `string` | Encrypted json file. | +| `password` | `string` or `Uint8Array` | Password for encrypted json file. | +| `callback?` | `ProgressCallback` | If callback is provided, it is called periodically during decryption so that any UI can be updated (optional). | ```ts -static override async fromEncryptedJson(json: string, password: string | Uint8Array, callback? : ProgressCallback): Promise +static async fromEncryptedJson(json: string, password: string | Uint8Array, callback? : ProgressCallback): Promise ``` #### Example @@ -96,7 +96,7 @@ import * as fs from "fs"; const wallet = await Wallet.fromEncryptedJson(fs.readFileSync("wallet.json", "utf8"), "password"); ``` -### fromEncryptedJsonSync +### `fromEncryptedJsonSync` Creates a `Wallet` from encrypted `json` file using provided `password`. @@ -108,7 +108,7 @@ Creates a `Wallet` from encrypted `json` file using provided `password`. | `password` | `string` or `Uint8Array` | Password for encrypted json file. | ```ts -static override fromEncryptedJsonSync(json: string, password: string | Uint8Array): Wallet +static fromEncryptedJsonSync(json: string, password: string | Uint8Array): Wallet ``` #### Example @@ -120,7 +120,7 @@ import * as fs from "fs"; const wallet = Wallet.fromEncryptedJsonSync(fs.readFileSync("tests/files/wallet.json", "utf8"), "password"); ``` -### connect +### `connect` To interact with the zkSync network, the `Wallet` object should be connected to a `Provider` by either passing it to the constructor or with the `connect` method. @@ -139,6 +139,7 @@ Wallet.connect(provider:Provider): Wallet ```ts import { Wallet, Provider, types } from "zksync-ethers"; +const PRIVATE_KEY = ""; const unconnectedWallet = new Wallet(PRIVATE_KEY); const provider = Provider.getDefaultProvider(types.Network.Sepolia); @@ -151,7 +152,7 @@ It is possible to chain `connect` and `connectToL1` methods: const wallet = unconnectedWallet.connect(zkSyncProvider).connectToL1(ethProvider); ``` -### connectToL1 +### `connectToL1` To perform L1 operations, the `Wallet` object needs to be connected to an `ethers.Provider` object. @@ -171,6 +172,7 @@ Wallet.connectToL1(provider: ethers.Provider): Wallet import { Wallet } from "zksync-ethers"; import { ethers } from "ethers"; +const PRIVATE_KEY = ""; const unconnectedWallet = new Wallet(PRIVATE_KEY); const ethProvider = ethers.getDefaultProvider("sepolia"); @@ -183,7 +185,7 @@ It is possible to chain `connect` and `connectToL1` methods: const wallet = unconnectedWallet.connect(zkSyncProvider).connectToL1(ethProvider); ``` -### getMainContract +### `getMainContract` Returns `Contract` wrapper of the zkSync smart contract. @@ -206,17 +208,17 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); console.log(`Main contract: ${await wallet.getMainContract()}`); ``` -### getL1BridgeContracts +### `getL1BridgeContracts` Returns L1 bridge contracts. ```ts -async getL1BridgeContracts(): Promise<{ erc20: IL1Bridge }> +async getL1BridgeContracts(): Promise<{ erc20: IL1Bridge; weth: IL1Bridge }> ``` :::note -there is no separate Ether bridge contract, [Main contract](./accounts.md#getmaincontract) is used instead. +There is no separate Ether bridge contract, [Main contract](./accounts.md#getmaincontract) is used instead. ::: @@ -235,12 +237,12 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); const l1BridgeContracts = await wallet.getL1BridgeContracts(); ``` -### getL2BridgeContracts +### `getL2BridgeContracts` Returns L2 bridge contracts. ```ts -async getL2BridgeContracts(): Promise<{ erc20: IL2Bridge }> +async getL2BridgeContracts(): Promise<{ erc20: IL2Bridge; weth: IL2Bridge }> ``` #### Example @@ -258,7 +260,7 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); const l2BridgeContracts = await wallet.getL2BridgeContracts(); ``` -### getAddress +### `getAddress` Returns the wallet address. @@ -281,16 +283,16 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); console.log(`Address: ${await wallet.getAddress()}`); ``` -### getBalance +### `getBalance` Returns the amount of the token the `Wallet` has. #### Inputs -| Parameter | Type | Description | -| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------- | -| `token?` | `Address` | The address of the token. ETH by default. | -| `blockTag` | `BlockTag` | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | +| Parameter | Type | Description | +| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default (optional). | +| `blockTag` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | ```ts async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise @@ -308,21 +310,21 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_L2_ADDRESS = "0x0faF6df7054946141266420b43783387A78d82A9"; +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; -console.log(`USDC balance: ${await wallet.getBalance(USDC_L2_ADDRESS)}`); +console.log(`Token balance: ${await wallet.getBalance(tokenL2)}`); ``` -### getBalanceL1 +### `getBalanceL1` Returns the amount of the token the `Wallet` has on Ethereum. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------- | ------------------------------------------------------------------------------------------------------------- | -| `token?` | `Address` | The address of the token. ETH by default. | -| `blockTag?` | `BlockTag` | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | +| Parameter | Type | Description | +| ----------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default (optional). | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | ```ts async getBalanceL1(token?: Address, blockTag?: BlockTag): Promise @@ -340,12 +342,12 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_L1_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; -console.log(`USDC balance: ${await wallet.getBalanceL1(USDC_L1_ADDRESS)}`); +console.log(`Token balance: ${await wallet.getBalanceL1(tokenL1)}`); ``` -### getAllBalances +### `getAllBalances` Returns all balances for confirmed tokens given by an account address. @@ -368,15 +370,15 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); const allBalances = await wallet.getAllBalances(); ``` -### getNonce +### `getNonce` Returns account's nonce number. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------- | ------------------------------------------------------------------------------------------------------------- | -| `blockTag?` | `BlockTag` | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | +| Parameter | Type | Description | +| ----------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | ```ts async getNonce(blockTag?: BlockTag): Promise @@ -397,7 +399,7 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); console.log(`Nonce: ${await wallet.getNonce()}`); ``` -### getDeploymentNonce +### `getDeploymentNonce` Returns account's deployment nonce number. @@ -420,7 +422,7 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); console.log(`Nonce: ${await wallet.getDeploymentNonce()}`); ``` -### ethWallet +### `ethWallet` You can get an `ethers.Wallet` object with the same private key with `ethWallet()` method. @@ -473,12 +475,12 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_L1_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; -console.log(`USDC L2 address: ${await wallet.l2TokenAddress(USDC_L1_ADDRESS)}`); +console.log(`Token L2 address: ${await wallet.l2TokenAddress(tokenL1)}`); ``` -### populateTransaction +### `populateTransaction` Designed for users who prefer a simplified approach by providing only the necessary data to create a valid transaction. The only required fields are `transaction.to` and either `transaction.data` or `transaction.value` (or both, if the method is payable). @@ -512,18 +514,90 @@ const tx = wallet.populateTransaction({ }); ``` -### transfer +### `signTransaction` + +Signs the transaction and serializes it to be ready to be broadcast to the network. +Throws an error when `transaction.from` is mismatched from the private key. + +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------------------------------------------- | -------------------- | +| `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | + +```ts +async signTransaction(transaction: TransactionRequest): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider); + +const recipient = Wallet.createRandom(); + +const tx = await wallet.signTransaction({ + type: utils.EIP712_TX_TYPE, + to: recipient.address, + value: ethers.parseEther("1"), +}); +``` + +### `sendTransaction` + +Broadcast the transaction to the network. +Throws an error when `tx.from` is mismatched from the private key. + +#### Inputs + +| Parameter | Type | Description | +| --------- | ----------------------------------------------------- | -------------------- | +| `tx` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | + +```ts +async sendTransaction(tx: TransactionRequest): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider); + +const recipient = Wallet.createRandom(); + +const tx = await wallet.sendTransaction({ + type: utils.EIP712_TX_TYPE, + to: recipient.address, + value: ethers.parseEther("1"), +}); +``` + +### `transfer` For convenience, the `Wallet` class has `transfer` method, which can transfer `ETH` or any `ERC20` token within the same interface. #### Inputs -| Parameter | Type | Description | -| --------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -| `tx.to` | `Address` | The address of the recipient. | -| `tx.amount` | `BigNumberish` | The amount of the token to transfer. | -| `tx.token?` | `Address` | The address of the token. `ETH` by default. | -| `tx.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| --------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `tx.to` | `Address` | The address of the recipient. | +| `tx.amount` | `BigNumberish` | The amount of the token to transfer (optional). | +| `tx.token?` | `Address` | The address of the token. `ETH` by default (optional). | +| `tx.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async transfer(tx: { @@ -558,17 +632,17 @@ const tx = await transferHandle.wait(); console.log(`The sum of ${tx.value} ETH was transferred to ${tx.to}`); ``` -### getAllowanceL1 +### `getAllowanceL1` Returns the amount of approved tokens for a specific L1 bridge. #### Inputs -| Parameter | Type | Description | -| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------- | -| `token` | `Address` | The Ethereum address of the token. | -| `bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`). | -| `blockTag?` | `BlockTag` | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge, either `L1EthBridge` or `L1Erc20Bridge` (optional). | +| `blockTag?` | `BlockTag` | In which block an allowance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | ```ts async getAllowanceL1( @@ -590,21 +664,21 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_L1_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; -console.log(`USDC allowance: ${await wallet.getAllowanceL1(USDC_L1_ADDRESS)}`); +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +console.log(`Token allowance: ${await wallet.getAllowanceL1(tokenL1)}`); ``` -### approveERC20 +### `approveERC20` Bridging ERC20 tokens from Ethereum requires approving the tokens to the zkSync Ethereum smart contract. #### Inputs -| Parameter | Type | Description | -| ------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -| `token` | `Address` | The Ethereum address of the token. | -| `amount` | `BigNumberish` | The amount of the token to be approved. | -| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| ------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `amount` | `BigNumberish` | The amount of the token to be approved. | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async approveERC20( @@ -626,26 +700,23 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; -const txHandle = await wallet.approveERC20( - USDC_ADDRESS, - "10000000" // 10.0 USDC -); +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const txHandle = await wallet.approveERC20(tokenL1, "10000000"); await txHandle.wait(); ``` -### getBaseCost +### `getBaseCost` Returns base cost for L2 transaction. #### Inputs -| Name | Type | Description | -| --------------------------- | -------------- | ------------------------------------------------------------------------------------------------ | -| `params.gasLimit` | `BigNumberish` | The `gasLimit` for the L2 contract call. | -| `params.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | -| `params.gasPrice?` | `BigNumberish` | The L1 gas price of the L1 transaction that will send the request for an execute call (optional. | +| Name | Type | Description | +| --------------------------- | -------------- | ------------------------------------------------------------------------------------------------- | +| `params.gasLimit` | `BigNumberish` | The `gasLimit` for the L2 contract call. | +| `params.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `params.gasPrice?` | `BigNumberish` | The L1 gas price of the L1 transaction that will send the request for an execute call (optional). | ```ts async getBaseCost(params: { @@ -670,7 +741,7 @@ const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); console.log(`Base cost: ${await wallet.getBaseCost({ gasLimit: 100_000 })}`); ``` -### deposit +### `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 @@ -680,20 +751,20 @@ use the [`allowanceL1`](#getallowancel1) method. #### Inputs -| Parameter | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | -| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | -| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2. | -| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`). | -| `transaction.approveERC20?` | `boolean` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand. | -| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand. | -| `transaction.refundRecipient?` | `Address` | 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. | -| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | -| `transaction.approveOverrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides of the approval transaction. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | -| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge. | +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.approveERC20?` | `boolean` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.approveOverrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | ```ts async deposit(transaction: { @@ -724,16 +795,16 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; -const usdcDepositHandle = await wallet.deposit({ - token: USDC_ADDRESS, +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tokenDepositHandle = await wallet.deposit({ + token: tokenL1, amount: "10000000", approveERC20: true, }); // Note that we wait not only for the L1 transaction to complete but also for it to be // processed by zkSync. If we want to wait only for the transaction to be processed on L1, -// we can use `await usdcDepositHandle.waitL1Commit()` -await usdcDepositHandle.wait(); +// we can use `await tokenDepositHandle.waitL1Commit()` +await tokenDepositHandle.wait(); const ethDepositHandle = await wallet.deposit({ token: utils.ETH_ADDRESS, @@ -745,24 +816,24 @@ const ethDepositHandle = await wallet.deposit({ await ethDepositHandle.wait(); ``` -### getDepositTx +### `getDepositTx` Returns populated deposit transaction. #### Inputs -| Parameter | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | -| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | -| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2. | -| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`). | -| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand. | -| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge. | -| `transaction.refundRecipient?` | `Address` | 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. | -| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async getDepositTx(transaction: { @@ -791,31 +862,31 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; const tx = await wallet.getDepositTx({ - token: USDC_ADDRESS, + token: tokenL1, amount: "10000000", }); ``` -### estimateGasDeposit +### `estimateGasDeposit` Estimates the amount of gas required for a deposit transaction on L1 network. Gas of approving ERC20 token is not included in estimation. #### Inputs -| Parameter | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | -| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | -| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2. | -| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`). | -| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand. | -| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge. | -| `transaction.refundRecipient?` | `Address` | 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. | -| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async estimateGasDeposit(transaction: @@ -844,28 +915,28 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; const gas = await wallet.estimateGasDeposit({ - token: USDC_ADDRESS, + token: tokenL1, amount: "10000000", }); console.log(`Gas: ${gas}`); ``` -### getFullRequiredDepositFee +### `getFullRequiredDepositFee` Retrieves the full needed ETH fee for the deposit. Returns the L1 fee and the L2 fee [`FullDepositFee`](./types.md#fulldepositfee). #### Inputs -| Parameter | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | -| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2. | -| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`). | -| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand. | -| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async getFullRequiredDepositFee(transaction: { @@ -890,15 +961,15 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; const fee = await wallet.getFullRequiredDepositFee({ - token: USDC_ADDRESS, + token: tokenL1, to: await wallet.getAddress(), }); console.log(`Fee: ${fee}`); ``` -### claimFailedDeposit +### `claimFailedDeposit` The `claimFailedDeposit` method 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 @@ -930,20 +1001,20 @@ const FAILED_DEPOSIT_HASH = ""; const claimFailedDepositHandle = await wallet.claimFailedDeposit(FAILED_DEPOSIT_HASH); ``` -### withdraw +### `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. #### Inputs -| Parameter | Type | Description | -| ---------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -| `transaction.token` | `Address` | The address of the token. `ETH` by default. | -| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | -| `transaction.to` | `Address` | The address of the recipient on L1. | -| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. | -| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| ---------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address of the recipient on L1 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async withdraw(transaction: { @@ -967,24 +1038,24 @@ const provider = Provider.getDefaultProvider(types.Network.Sepolia); const ethProvider = ethers.getDefaultProvider("sepolia"); const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); -const USDC_ADDRESS = "0x0faF6df7054946141266420b43783387A78d82A9"; -const usdcWithdrawHandle = await wallet.withdraw({ - token: USDC_ADDRESS, +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; +const tokenWithdrawHandle = await wallet.withdraw({ + token: tokenL2, amount: "10000000", }); ``` -### finalizeWithdrawal +### `finalizeWithdrawal` Proves the inclusion of the L2 -> L1 withdrawal message. #### Inputs -| Name | Type | Description | -| ---------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | -| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | -| `index?` | `hash` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize (defaults to 0). | -| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Name | Type | Description | +| ---------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async finalizeWithdrawal(withdrawalHash: BytesLike, index: number = 0, overrides?: ethers.Overrides): Promise @@ -1006,23 +1077,85 @@ const WITHDRAWAL_HASH = ""; const finalizeWithdrawHandle = await wallet.finalizeWithdrawal(WITHDRAWAL_HASH); ``` -### requestExecute +### `isWithdrawalFinalized` + +Returns whether the withdrawal transaction is finalized on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | + +```ts +async isWithdrawalFinalized(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const WITHDRAWAL_HASH = ""; +const isFinalized = await wallet.isWithdrawalFinalized(WITHDRAWAL_HASH); +``` + +### `finalizeWithdrawalParams` + +Returns the [parameters](./types.md#finalizewithdrawalparams) required for finalizing a withdrawal from the withdrawal transaction's log on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0 (optional). | + +```ts +async finalizeWithdrawalParams(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Wallet, Provider, utils } from "zksync-ethers"; +import { ethers } from "ethers"; + +const PRIVATE_KEY = ""; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +const WITHDRAWAL_HASH = ""; +const params = await wallet.finalizeWithdrawalParams(WITHDRAWAL_HASH); +``` + +### `requestExecute` Request execution of L2 transaction from L1. #### Inputs -| Name | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.contractAddress` | `Address` | The L2 contract to be called. | -| `transaction.calldata` | `BytesLike` | The input of the L2 transaction. | -| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction. | -| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2. | -| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte. | -| `transaction.refundRecipient?` | `Address` | 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`. | -| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **Ethereum** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, `value`, etc. | +| Name | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async requestExecute(transaction: { @@ -1066,7 +1199,7 @@ const abi = [ ]; const contractInterface = new ethers.utils.Interface(abi); const calldata = contractInterface.encodeFunctionData("increment", []); -const l2GasLimit = BigNumber.from(1000); +const l2GasLimit = 1000n; const txCostPrice = await wallet.getBaseCost({ gasPrice, @@ -1090,23 +1223,23 @@ const executeTx = await wallet.requestExecute({ await executeTx.wait(); ``` -### getRequestExecuteTx +### `getRequestExecuteTx` Returns populated deposit transaction. #### Inputs -| Name | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.contractAddress` | `Address` | The L2 contract to be called. | -| `transaction.calldata` | `BytesLike` | The input of the L2 transaction. | -| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction. | -| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2. | -| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte. | -| `transaction.refundRecipient?` | `Address` | 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`. | -| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **Ethereum** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, `value`, etc. | +| Name | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async getRequestExecuteTx(transaction: { @@ -1150,7 +1283,7 @@ const abi = [ ]; const contractInterface = new ethers.utils.Interface(abi); const calldata = contractInterface.encodeFunctionData("increment", []); -const l2GasLimit = BigNumber.from(1000); +const l2GasLimit = 1000n; const txCostPrice = await wallet.getBaseCost({ gasPrice, @@ -1172,23 +1305,23 @@ const executeTx = await wallet.getRequestExecuteTx({ }); ``` -### estimateGasRequestExecute +### `estimateGasRequestExecute` Estimates the amount of gas required for a request execute transaction. #### Inputs -| Name | Type | Description | -| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transaction.contractAddress` | `Address` | The L2 contract to be called. | -| `transaction.calldata` | `BytesLike` | The input of the L2 transaction. | -| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2. | -| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction. | -| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2. | -| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction. | -| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte. | -| `transaction.refundRecipient?` | `Address` | 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`. | -| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **Ethereum** transaction overrides. May be used to pass `gasLimit`, `gasPrice`, `value`, etc. | +| Name | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async estimateGasRequestExecute(transaction: { @@ -1232,7 +1365,7 @@ const abi = [ ]; const contractInterface = new ethers.utils.Interface(abi); const calldata = contractInterface.encodeFunctionData("increment", []); -const l2GasLimit = BigNumber.from(1000); +const l2GasLimit = 1000n; const txCostPrice = await wallet.getBaseCost({ gasPrice, @@ -1256,11 +1389,11 @@ const executeTx = await wallet.getRequestExecuteTx({ ## `EIP712Signer` -The methods of this class are mostly used internally. +Provides support for an EIP712 transaction. The methods of this class are mostly used internally. -### getSignInput +### `getSignInput` -Generates the EIP-712 typed data from provided transaction. +Generates the EIP-712 typed data from provided transaction. Optional fields are populated by zero values. #### Inputs @@ -1272,7 +1405,23 @@ Generates the EIP-712 typed data from provided transaction. static getSignInput(transaction: TransactionRequest) ``` -### sign +#### Example + +```ts +const tx = EIP712Signer.getSignInput({ + type: utils.EIP712_TX_TYPE, + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: BigInt(7_000_000), + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + nonce: 0, + chainId: BigInt(270), + gasPrice: BigInt(250_000_000), + gasLimit: BigInt(21_000), + customData: {}, +}); +``` + +### `sign` Signs an Ethereum transaction using EIP-712. @@ -1286,7 +1435,9 @@ Signs an Ethereum transaction using EIP-712. async sign(transaction: TransactionRequest): Promise ``` -### getSignedDigest +### `getSignedDigest` + +Generates the signed digest of an Ethereum transaction using EIP-712. #### Inputs @@ -1294,12 +1445,18 @@ async sign(transaction: TransactionRequest): Promise | ------------- | ----------------------------------------------------- | -------------------- | | `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | -Generates the signed digest of an Ethereum transaction using EIP-712. - ```ts static getSignedDigest(transaction: TransactionRequest): ethers.BytesLike ``` +### `getDomain` + +Returns the EIP712 domain. + +```ts +async getDomain(): Promise +``` + ## `Signer` This class is to be used in a browser environment. The easiest way to construct it is to use the `getSigner` method of the `BrowserProvider`. This structure @@ -1312,16 +1469,16 @@ const provider = new BrowserProvider(window.ethereum); const signer = provider.getSigner(); ``` -### getBalance +### `getBalance` Returns the amount of the token the `Signer` has. #### Inputs -| Parameter | Type | Description | -| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------- | -| `token?` | `Address` | The address of the token. ETH by default. | -| `blockTag` | `BlockTag` | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | +| Parameter | Type | Description | +| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default. | +| `blockTag` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | ```ts async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise @@ -1335,23 +1492,23 @@ import { BrowserProvider } from "zksync-ethers"; const provider = new BrowserProvider(window.ethereum); const signer = provider.getSigner(); -const USDC_L2_ADDRESS = "0x0faF6df7054946141266420b43783387A78d82A9"; -// Getting balance in USDC -console.log(await signer.getBalance(USDC_L2_ADDRESS)); +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; +// Getting token balance +console.log(await signer.getBalance(tokenL2)); -// Getting balance in ETH +// Getting ETH balance console.log(await signer.getBalance()); ``` -### getNonce +### `getNonce` Returns account's nonce number. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------- | ------------------------------------------------------------------------------------------------------------- | -| `blockTag?` | `BlockTag` | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | +| Parameter | Type | Description | +| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | ```ts async getNonce(blockTag?: BlockTag): Promise @@ -1368,21 +1525,21 @@ const signer = provider.getSigner(); console.log(await signer.getNonce()); ``` -### transfer +### `transfer` Please note that for now, unlike Ethereum, zkSync does not support native transfers, i.e. the `value` field of all transactions is equal to `0`. All the token transfers are done through ERC20 `transfer` function calls. But for convenience, the `Wallet` class has `transfer` method, which can transfer any `ERC20` tokens. -#### Inputs and outputs +#### Inputs -| Parameter | Type | Description | -| ------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -| `tx.to` | `Address` | The address of the recipient. | -| `tx.amount` | `BigNumberish` | The amount of the token to transfer. | -| `token?` | `Address` | The address of the token. `ETH` by default. | -| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | **zkSync** transaction overrides. May be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc. | +| Parameter | Type | Description | +| ------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `tx.to` | `Address` | The address of the recipient. | +| `tx.amount` | `BigNumberish` | The amount of the token to transfer. | +| `token?` | `Address` | The address of the token. `ETH` by default. | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async transfer(tx: { @@ -1410,6 +1567,47 @@ const transferHandle = signer.transfer({ }); ``` +### `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. + +#### Inputs + +| Parameter | Type | Description | +| ---------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address of the recipient on L1 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used (optional). | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async withdraw(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + bridgeAddress?: Address; + overrides?: ethers.Overrides; +}): Promise +``` + +#### Example + +```ts +import { BrowserProvider } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new BrowserProvider(window.ethereum); +const signer = provider.getSigner(); + +const tokenL2 = "0x6a4Fb925583F7D4dF82de62d98107468aE846FD1"; +const tokenWithdrawHandle = await signer.withdraw({ + token: tokenL2, + amount: "10000000", +}); +``` + ## `L1Signer` This class is to be used in a browser environment to do zkSync-related operations on layer 1. This class extends `ethers.JsonRpcSigner` and so supports all the methods @@ -1426,7 +1624,7 @@ const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); const signer = L1Signer.from(provider.getSigner(), zksyncProvider); ``` -### getMainContract +### `getMainContract` Returns the main zkSync Era smart contract address. @@ -1448,12 +1646,12 @@ const mainContract = await signer.getMainContract(); console.log(mainContract.address); ``` -### getL1BridgeContracts +### `getL1BridgeContracts` Returns L1 bridge contracts. ```ts -async getL1BridgeContracts(): Promise<{ erc20: IL1Bridge }> +async getL1BridgeContracts(): Promise<{ erc20: IL1Bridge; weth: IL1Bridge }> ``` :::note @@ -1475,16 +1673,16 @@ const signer = L1Signer.from(provider.getSigner(), zksyncProvider); const l1BridgeContracts = await signer.getL1BridgeContracts(); ``` -### getBalanceL1 +### `getBalanceL1` Returns the amount of the token the `L1Signer` has on Ethereum. #### Inputs -| Parameter | Type | Description | -| ----------- | ---------- | ------------------------------------------------------------------------------------------------------------- | -| `token?` | `Address` | The address of the token. ETH by default. | -| `blockTag?` | `BlockTag` | The block the balance should be checked on. `committed`, i.e. the latest processed one is the default option. | +| Parameter | Type | Description | +| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | +| `token?` | `Address` | The address of the token. ETH by default. | +| `blockTag?` | `BlockTag` | In which block a balance should be checked on. `committed`, i.e. the latest processed one is the default option. | ```ts async getBalanceL1(token?: Address, blockTag?: BlockTag): Promise @@ -1500,21 +1698,730 @@ const provider = new ethers.BrowserProvider(window.ethereum); const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); const signer = L1Signer.from(provider.getSigner(), zksyncProvider); -const USDC_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; -// Getting balance in USDC -console.log(await signer.getBalanceL1(USDC_ADDRESS)); +// Getting token balance +console.log(await signer.getBalanceL1(tokenL1)); -// Getting balance in ETH +// Getting ETH balance console.log(await signer.getBalanceL1()); ``` -## L2VoidSigner +### `l2TokenAddress` + +Returns the L2 token address equivalent for a L1 token address as they are not equal. ETH's address is set to zero address. + +:::warning +Only works for tokens bridged on default zkSync Era bridges. +::: + +#### Inputs + +| Parameter | Type | Description | +| --------- | --------- | ------------------------------- | +| `token` | `Address` | The address of the token on L1. | + +```ts +async l2TokenAddress(token: Address): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; + +console.log(`Token L2 address: ${await signer.l2TokenAddress(tokenL1)}`); +``` + +### `getAllowanceL1` + +Returns the amount of approved tokens for a specific L1 bridge. + +#### Inputs + +| Parameter | Type | Description | +| ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge, either `L1EthBridge` or `L1Erc20Bridge` (optional). | +| `blockTag?` | `BlockTag` | In which block an allowance should be checked on. `committed`, i.e. the latest processed one is the default option (optional). | + +```ts +async getAllowanceL1( + token: Address, + bridgeAddress?: Address, + blockTag?: ethers.BlockTag +): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +console.log(`Token allowance: ${await signer.getAllowanceL1(tokenL1)}`); +``` + +### `approveERC20` + +Bridging ERC20 tokens from Ethereum requires approving the tokens to the zkSync Ethereum smart contract. + +#### Inputs + +| Parameter | Type | Description | +| ------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | The Ethereum address of the token. | +| `amount` | `BigNumberish` | The amount of the token to be approved. | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async approveERC20( + token: Address, + amount: BigNumberish, + overrides?: ethers.Overrides & { bridgeAddress?: Address } +): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const txHandle = await signer.approveERC20(tokenL1, "10000000"); + +await txHandle.wait(); +``` + +### `getBaseCost` + +Returns base cost for L2 transaction. -`L2VoidSigner` is identical to [`Signer`](#signer) but is designed to allow an address to be used in any API that accepts a `Signer`, -even when there are no credentials available to perform any actual signing. +#### Inputs + +| Name | Type | Description | +| --------------------------- | -------------- | ------------------------------------------------------------------------------------------------ | +| `params.gasLimit` | `BigNumberish` | The `gasLimit` for the L2 contract call. | +| `params.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `params.gasPrice?` | `BigNumberish` | The L1 gas price of the L1 transaction that will send the request for an execute call (optional. | + +```ts +async getBaseCost(params: { + gasLimit: BigNumberish; + gasPerPubdataByte?: BigNumberish; + gasPrice?: BigNumberish; +}): Promise +``` -## L1VoidSigner +#### Example -`L1VoidSigner` is identical to [`L1Signer`](#l1signer) but is designed to allow an address to be used in any API that accepts a `Signer`, -even when there are no credentials available to perform any actual signing. +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +console.log(`Base cost: ${await signer.getBaseCost({ gasLimit: 100_000 })}`); +``` + +### `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 `transaction.bridgeAddress`). In this case, `transaction.approveERC20` can be enabled to perform token approval. If there are +already enough approved tokens for the L1 bridge, token approval will be skipped. To check the amount of approved tokens for a specific bridge, +use the [`allowanceL1`](#getallowancel1) method. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.approveERC20?` | `boolean` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.approveOverrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | + +```ts +async deposit(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + approveERC20?: boolean; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.Overrides; + approveOverrides?: ethers.Overrides; + customBridgeData?: BytesLike; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tokenDepositHandle = await signer.deposit({ + token: tokenL1, + amount: "10000000", + approveERC20: true, +}); +// Note that we wait not only for the L1 transaction to complete but also for it to be +// processed by zkSync. If we want to wait only for the transaction to be processed on L1, +// we can use `await tokenDepositHandle.waitL1Commit()` +await tokenDepositHandle.wait(); + +const ethDepositHandle = await signer.deposit({ + token: utils.ETH_ADDRESS, + amount: "10000000", +}); +// Note that we wait not only for the L1 transaction to complete but also for it to be +// processed by zkSync. If we want to wait only for the transaction to be processed on L1, +// we can use `await ethDepositHandle.waitL1Commit()` +await ethDepositHandle.wait(); +``` + +### `getDepositTx` + +Returns populated deposit transaction. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getDepositTx(transaction: { + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + customBridgeData?: BytesLike; + refundRecipient?: Address; + overrides?: ethers.Overrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const tx = await signer.getDepositTx({ + token: tokenL1, + amount: "10000000", +}); +``` + +### `estimateGasDeposit` + +Estimates the amount of gas required for a deposit transaction on L1 network. Gas of approving ERC20 token is not included in estimation. + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.amount` | `BigNumberish` | The amount of the token to withdraw. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides,
this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.l2GasLimit?` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.refundRecipient?` | `Address` | 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 (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async estimateGasDeposit(transaction: + token: Address; + amount: BigNumberish; + to?: Address; + operatorTip?: BigNumberish; + bridgeAddress?: Address; + customBridgeData?: BytesLike; + l2GasLimit?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.Overrides; + }): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x5C221E77624690fff6dd741493D735a17716c26B"; +const gas = await signer.estimateGasDeposit({ + token: tokenL1, + amount: "10000000", +}); +console.log(`Gas: ${gas}`); +``` + +### `getFullRequiredDepositFee` + +Retrieves the full needed ETH fee for the deposit. Returns the L1 fee and the L2 fee [`FullDepositFee`](./types.md#fulldepositfee). + +#### Inputs + +| Parameter | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.token` | `Address` | The address of the token to deposit. `ETH` by default. | +| `transaction.to?` | `Address` | The address that will receive the deposited tokens on L2 (optional). | +| `transaction.bridgeAddress?` | `Address` | The address of the bridge contract to be used. Defaults to the default zkSync bridge (either `L1EthBridge` or `L1Erc20Bridge`) (optional). | +| `transaction.customBridgeData?` | `BytesLike` | Additional data that can be sent to a bridge (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | Whether or not should the token approval be performed under the hood. Set this flag to `true` if you bridge an ERC20 token and didn't call the `approveERC20` function beforehand (optional). | +| `transaction.overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getFullRequiredDepositFee(transaction: { + token: Address; + to?: Address; + bridgeAddress?: Address; + customBridgeData?: BytesLike; + gasPerPubdataByte?: BigNumberish; + overrides?: ethers.Overrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const tokenL1 = "0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be"; +const fee = await signer.getFullRequiredDepositFee({ + token: tokenL1, + to: await wallet.getAddress(), +}); +console.log(`Fee: ${fee}`); +``` + +### `claimFailedDeposit` + +The `claimFailedDeposit` method 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, otherwise throws the error. + +#### Inputs + +| Parameter | Type | Description | +| ----------- | --------- | ---------------------------------------------- | +| depositHash | `bytes32` | The L2 transaction hash of the failed deposit. | + +```ts +async claimFailedDeposit(depositHash: BytesLike): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const FAILED_DEPOSIT_HASH = ""; +const claimFailedDepositHandle = await signer.claimFailedDeposit(FAILED_DEPOSIT_HASH); +``` + +### `finalizeWithdrawal` + +Proves the inclusion of the L2 -> L1 withdrawal message. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0) (optional). | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async finalizeWithdrawal(withdrawalHash: BytesLike, index: number = 0, overrides?: ethers.Overrides): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const WITHDRAWAL_HASH = ""; +const finalizeWithdrawHandle = await signer.finalizeWithdrawal(WITHDRAWAL_HASH); +``` + +### `isWithdrawalFinalized` + +Returns whether the withdrawal transaction is finalized on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0) (optional). | + +```ts +async isWithdrawalFinalized(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const WITHDRAWAL_HASH = ""; +const isFinalized = await signer.isWithdrawalFinalized(WITHDRAWAL_HASH); +``` + +### `finalizeWithdrawalParams` + +Returns the [parameters](./types.md#finalizewithdrawalparams) required for finalizing a withdrawal from the withdrawal transaction's log on the L1 network. + +#### Inputs + +| Name | Type | Description | +| ---------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `withdrawalHash` | `BytesLike` | Hash of the L2 transaction where the withdrawal was initiated. | +| `index?` | `number` | In case there were multiple withdrawals in one transaction, you may pass an index of the withdrawal you want to finalize. Defaults to 0) (optional). | + +```ts +async finalizeWithdrawalParams(withdrawalHash: BytesLike, index: number = 0): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const WITHDRAWAL_HASH = ""; +const params = await signer.finalizeWithdrawalParams(WITHDRAWAL_HASH); +``` + +### `requestExecute` + +Request execution of L2 transaction from L1. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async requestExecute(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.Overrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const CONTRACT_ADDRESS = ""; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const gasPrice = await signer.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = 1000n; + +const txCostPrice = await signer.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await signer.requestExecute({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); + +await executeTx.wait(); +``` + +### `getRequestExecuteTx` + +Returns populated deposit transaction. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async getRequestExecuteTx(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit?: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.Overrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const CONTRACT_ADDRESS = ""; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const gasPrice = await signer.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = 1000n; + +const txCostPrice = await signer.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await signer.getRequestExecuteTx({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); +``` + +### `estimateGasRequestExecute` + +Estimates the amount of gas required for a request execute transaction. + +#### Inputs + +| Name | Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `transaction.contractAddress` | `Address` | The L2 contract to be called (optional). | +| `transaction.calldata` | `BytesLike` | The input of the L2 transaction (optional). | +| `transaction.l2GasLimit` | `BigNumberish` | Maximum amount of L2 gas that transaction can consume during execution on L2 (optional). | +| `transaction.l2Value?` | `BigNumberish` | `msg.value` of L2 transaction (optional). | +| `transaction.factoryDeps?` | `ethers.BytesLike[]` | An array of L2 bytecodes that will be marked as known on L2 (optional). | +| `transaction.operatorTip?` | `BigNumberish` | (_currently is not used_) If the ETH value passed with the transaction is not explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the base cost of the transaction (optional). | +| `transaction.gasPerPubdataByte?` | `BigNumberish` | The L2 gas price for each published L1 calldata byte (optional). | +| `transaction.refundRecipient?` | `Address` | 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` (optional). | +| `transaction.overrides` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | + +```ts +async estimateGasRequestExecute(transaction: { + contractAddress: Address; + calldata: string; + l2GasLimit?: BigNumberish; + l2Value?: BigNumberish; + factoryDeps?: ethers.BytesLike[]; + operatorTip?: BigNumberish; + gasPerPubdataByte?: BigNumberish; + refundRecipient?: Address; + overrides?: ethers.Overrides; +}): Promise +``` + +#### Example + +```ts +import { Provider, L1Signer, types } from "zksync-ethers"; +import { ethers } from "ethers"; + +const CONTRACT_ADDRESS = ""; + +const provider = new ethers.BrowserProvider(window.ethereum); +const zksyncProvider = Provider.getDefaultProvider(types.Network.Sepolia); +const signer = L1Signer.from(provider.getSigner(), zksyncProvider); + +const gasPrice = await signer.providerL1.getGasPrice(); + +// The calldata can be encoded the same way as for Ethereum. +// Here is an example of how to get the calldata from an ABI: +const abi = [ + { + inputs: [], + name: "increment", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +const contractInterface = new ethers.utils.Interface(abi); +const calldata = contractInterface.encodeFunctionData("increment", []); +const l2GasLimit = 1000n; + +const txCostPrice = await signer.getBaseCost({ + gasPrice, + calldataLength: ethers.utils.arrayify(calldata).length, + l2GasLimit, +}); + +console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`); + +const executeTx = await signer.getRequestExecuteTx({ + contractAddress: CONTRACT_ADDRESS, + calldata, + l2Value: 1, + l2GasLimit, + overrides: { + gasPrice, + value: txCostPrice, + }, +}); +``` diff --git a/docs/build/sdks/js/zksync-ethers/getting-started.md b/docs/build/sdks/js/zksync-ethers/getting-started.md index cb111bb234..180a959fc9 100644 --- a/docs/build/sdks/js/zksync-ethers/getting-started.md +++ b/docs/build/sdks/js/zksync-ethers/getting-started.md @@ -87,8 +87,7 @@ Also, the following examples demonstrate how to: 1. [Deposit ETH and tokens from Ethereum into zkSync Era](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/01_deposit.ts) 2. [Transfer ETH and tokens on zkSync Era](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/02_transfer.ts) 3. [Withdraw ETH and tokens from zkSync Era to Ethereum](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/03_withdraw.ts) -4. [Get confirmed tokens on zkSync Era](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/08_get_confirmed_tokens.ts) -5. [Use paymaster to pay fee with token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/19_use_paymaster.ts) +4. [Use paymaster to pay fee with token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/19_use_paymaster.ts) Full code for all examples is available [here](https://github.com/zksync-sdk/zksync2-examples/tree/main/js/src). Examples are configured to -interact with `zkSync Era`, `Sepolia` and `Goerli` test networks. +interact with `zkSync Era` and `Sepolia` test networks. diff --git a/docs/build/sdks/js/zksync-ethers/paymaster-utils.md b/docs/build/sdks/js/zksync-ethers/paymaster-utils.md index f0caaa1a3d..3ed0ff44e3 100644 --- a/docs/build/sdks/js/zksync-ethers/paymaster-utils.md +++ b/docs/build/sdks/js/zksync-ethers/paymaster-utils.md @@ -11,18 +11,11 @@ The [paymaster utilities library](https://github.com/zksync-sdk/zksync-ethers/bl ## Contract interfaces -### `IPaymasterFlow` Deprecated - -::: warning Deprecated - -- This method is deprecated in favor of [utils.PAYMASTER_FLOW_ABI](./utils.md#paymasterflow). - ::: - Constant ABI definition for -the [Paymaster Flow Interface](https://github.com/matter-labs/era-contracts/blob/87cd8d7b0f8c02e9672c0603a821641a566b5dd8/l2-contracts/contracts/interfaces/IPaymasterFlow.sol). +the [Paymaster Flow Interface](https://github.com/matter-labs/era-contracts/blob/583cb674a2b942dda34e9f46edb5a9f5b696b90a/l2-contracts/contracts/interfaces/IPaymasterFlow.sol). ```typescript -export const IPaymasterFlow = new ethers.utils.Interface(require("../abi/IPaymasterFlow.json").abi); +export const PAYMASTER_FLOW_ABI = new ethers.Interface(require("../abi/IPaymasterFlow.json")); ``` ## Functions @@ -71,3 +64,26 @@ export function getPaymasterParams(paymasterAddress: Address, paymasterInput: Pa ``` Find out more about the [`PaymasterInput` type](./types.md). + +#### Examples + +Creating `General` paymaster parameters. + +```ts +const paymasterAddress = "0x0a67078A35745947A37A552174aFe724D8180c25"; +const paymasterParams = utils.getPaymasterParams(paymasterAddress, { + type: "General", + innerInput: new Uint8Array(), +}); +``` + +Creating `ApprovalBased` paymaster parameters. + +```ts +const result = utils.getPaymasterParams("0x0a67078A35745947A37A552174aFe724D8180c25", { + type: "ApprovalBased", + token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964", + minimalAllowance: BigInt(1), + innerInput: new Uint8Array(), +}); +``` diff --git a/docs/build/sdks/js/zksync-ethers/providers.md b/docs/build/sdks/js/zksync-ethers/providers.md index 592d909eed..dbeeb04770 100644 --- a/docs/build/sdks/js/zksync-ethers/providers.md +++ b/docs/build/sdks/js/zksync-ethers/providers.md @@ -9,7 +9,7 @@ head: A Web3 Provider object provides application-layer access to underlying blockchain networks. -The `zksync-ethers` library supports provider methods from the [`ethers.js`](https://docs.ethers.io/v6/api/providers) library and +The [`zksync-ethers`](https://www.npmjs.com/package/zksync-ethers)) library supports provider methods from the [`ethers.js`](https://docs.ethers.io/v6/api/providers) library and supplies additional functionality. Two providers are available: @@ -75,8 +75,8 @@ override async broadcastTransaction(signedTx: string): Promise +async estimateFee([`TransactionRequest`](https://docs.ethers.org/v6/api/providers/#TransactionRequest)): Promise ``` Helper function: [toJSON](#tojson). @@ -146,9 +146,11 @@ console.log(`Gas for token approval tx: ${gasTokenApprove}`); ```ts import { Provider, types } from "zksync-ethers"; +const ADDRESS = "
"; +const RECEIVER = ""; const provider = Provider.getDefaultProvider(types.Network.Sepolia); -const tokenAddress = "0x765F5AF819D818a8e8ee6ff63D8d0e8056DBE150"; // Crown token which can be minted for free -const paymasterAddress = "0x3cb2b87d10ac01736a65688f3e0fb1b070b3eea3"; // Paymaster for Crown token +const tokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free +const paymasterAddress = "0x13D0D8550769f59aa241a41897D4859c87f7Dd46"; // Paymaster for Crown token const paymasterParams = utils.getPaymasterParams(paymasterAddress, { type: "ApprovalBased", @@ -158,7 +160,7 @@ const paymasterParams = utils.getPaymasterParams(paymasterAddress, { }); const tokenApprove = await provider.estimateGas({ - from: PUBLIC_KEY, + from: ADDRESS, to: tokenAddress, data: utils.IERC20.encodeFunctionData("approve", [RECEIVER, 1]), customData: { @@ -252,14 +254,14 @@ withdrawal transaction and sends it to the [`estimateGas`](#estimategas) method. #### Inputs -| Parameter | Type | Description | -| ---------------- | ------------------------------------------------------------------------ | ---------------------------------------- | -| `token` | `Address` | Token address. | -| `amount` | `BigNumberish` | Amount of token. | -| `from?` | `Address` | From address (optional). | -| `to?` | `Address` | To address (optional). | -| `bridgeAddress?` | `Address` | Bridge address (optional). | -| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Ethers call overrides object (optional). | +| Parameter | Type | Description | +| ---------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `bridgeAddress?` | `Address` | Bridge address (optional). | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). . | ```ts async estimateGasWithdraw(transaction: { @@ -292,15 +294,15 @@ Returns gas estimation for an L1 to L2 execute operation. #### Inputs -| Parameter | Type | Description | -| -------------------- | ------------------ | ---------------------------------------------------------------- | -| `contractAddress` | `Address` | Address of contract. | -| `calldata` | `BytesLike` | The transaction call data. | -| `caller?` | `Address` | Caller address (optional). | -| `l2Value?` | `BigNumberish` | Current L2 gas value (optional). | -| `factoryDeps?` | `BytesLike[]` | Byte array containing contract bytecode. | -| `gasPerPubdataByte?` | `BigNumberish` | Constant representing current amount of gas per byte (optional). | -| `overrides?` | `ethers.Overrides` | Ethers overrides object (optional). | +| Parameter | Type | Description | +| -------------------- | ------------------ | ----------------------------------------------------------------------------------------------------- | +| `contractAddress` | `Address` | Address of contract. | +| `calldata` | `string` | The transaction call data. | +| `caller?` | `Address` | Caller address (optional). | +| `l2Value?` | `BigNumberish` | Current L2 gas value (optional). | +| `factoryDeps?` | `BytesLike[]` | Byte array containing contract bytecode. | +| `gasPerPubdataByte?` | `BigNumberish` | Constant representing current amount of gas per byte (optional). | +| `overrides?` | `ethers.Overrides` | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async estimateL1ToL2Execute(transaction: { @@ -369,7 +371,7 @@ When block and token are not supplied, `committed` and `ETH` are the default val | --------------- | ---------- | ------------------------------------------------------------------------------------- | | `address` | `Address` | Account address. | | `blockTag?` | `BlockTag` | Block tag for getting the balance on. Latest `committed` block is default (optional). | -| `tokenAddress?` | `Address` | he address of the token. ETH is default (optional). | +| `tokenAddress?` | `Address` | Token address. ETH is default (optional). | ```ts async getBalance(address: Address, blockTag?: BlockTag, tokenAddress?: Address) @@ -382,7 +384,7 @@ import { Provider, types } from "zksync-ethers"; const provider = Provider.getDefaultProvider(types.Network.Sepolia); const account = "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049"; -const tokenAddress = "0x765F5AF819D818a8e8ee6ff63D8d0e8056DBE150"; // Crown token which can be minted for free +const tokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free console.log(`ETH balance: ${await provider.getBalance(account)}`); console.log(`Token balance: ${await provider.getBalance(account, "latest", tokenAddres)}`); ``` @@ -442,7 +444,7 @@ console.log(`Block details: ${toJSON(await provider.getBlockDetails(90_000))}`); ### `getBytecodeByHash` -Returns bytecode of a transaction given by its hash. +Returns bytecode of a contract given by its hash. Calls the [`zks_getBytecodeByHash`](../../../api.md#zks-getbytecodebyhash) JSON-RPC method. @@ -461,8 +463,14 @@ async getBytecodeByHash(bytecodeHash: BytesLike): Promise ```ts import { Provider, types } from "zksync-ethers"; +// Bytecode hash can be computed by following these steps: +// const testnetPaymasterBytecode = await provider.getCode(await provider.getTestnetPaymasterAddress()); +// const testnetPaymasterBytecodeHash = ethers.hexlify(utils.hashBytecode(testnetPaymasterBytecode)); + +const testnetPaymasterBytecodeHash = "0x010000f16d2b10ddeb1c32f2c9d222eb1aea0f638ec94a81d4e916c627720e30"; + const provider = Provider.getDefaultProvider(types.Network.Goerli); -console.log(`Bytecode: ${await provider.getBytecodeByHash("0x0f9acdb01827403765458b4685de6d9007580d15")}`); +console.log(`Bytecode: ${await provider.getBytecodeByHash(testnetPaymasterBytecodeHash)}`); ``` ### `getContractAccountInfo` @@ -487,7 +495,7 @@ Helper function: [toJSON](#tojson). import { Provider, types } from "zksync-ethers"; const provider = Provider.getDefaultProvider(types.Network.Sepolia); -const tokenAddress = "0x765F5AF819D818a8e8ee6ff63D8d0e8056DBE150"; // Crown token which can be minted for free +const tokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free console.log(`Contract account info: ${toJSON(await provider.getContractAccountInfo(tokenAddress))}`); ``` @@ -496,8 +504,7 @@ console.log(`Contract account info: ${toJSON(await provider.getContractAccountIn Returns the addresses of the default zkSync Era bridge contracts on both L1 and L2. ```ts -async; -getDefaultBridgeAddresses(); +async getDefaultBridgeAddresses(): Promise<{erc20L1: string, erc20L2: string, wethL1: string, wethL2: string}>; ``` #### Example @@ -709,6 +716,8 @@ Helper function: [toJSON](#tojson). import { Provider, types } from "zksync-ethers"; const provider = Provider.getDefaultProvider(types.Network.Sepolia); +// Any L2 -> L1 transaction can be used. +// In this case, withdrawal transaction is used. const tx = "0x2a1c6c74b184965c0cb015aae9ea134fd96215d2e4f4979cfec12563295f610e"; console.log(`Log ${toJSON(await provider.getLogProof(tx, 0))}`); ``` @@ -905,7 +914,7 @@ Returns the transaction receipt from a given hash number. | `txHash` | `string` | Transaction hash. | ```ts -override async getTransactionReceipt(txHash: string): Promise +async getTransactionReceipt(txHash: string): Promise ``` #### Example @@ -953,13 +962,13 @@ Returns the populated transfer transaction. #### Inputs -| Parameter | Type | Description | -| ------------ | ------------------------------------------------------------------------ | ----------------------------------- | -| `token` | `Address` | Token address. | -| `amount` | `BigNumberish` | Amount of token. | -| `from?` | `Address` | From address (optional). | -| `to?` | `Address` | To address (optional). | -| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Ethers overrides object (optional). | +| Parameter | Type | Description | +| ------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async getTransferTx(transaction: { @@ -989,14 +998,14 @@ Returns the populated withdrawal transaction. #### Inputs -| Parameter | Type | Description | -| ---------------- | ------------------------------------------------------------------------ | ----------------------------------- | -| `token` | `Address` | Token address. | -| `amount` | `BigNumberish` | Amount of token. | -| `from?` | `Address` | From address (optional). | -| `to?` | `Address` | To address (optional). | -| `bridgeAddress?` | `Address` | Bridge address (optional). | -| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Ethers overrides object (optional). | +| Parameter | Type | Description | +| ---------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `token` | `Address` | Token address. | +| `amount` | `BigNumberish` | Amount of token. | +| `from?` | `Address` | From address (optional). | +| `to?` | `Address` | To address (optional). | +| `bridgeAddress?` | `Address` | Bridge address (optional). | +| `overrides?` | [`ethers.Overrides`](https://docs.ethers.org/v6/api/contract/#Overrides) | Transaction's overrides which may be used to pass l2 `gasLimit`, `gasPrice`, `value`, etc (optional). | ```ts async getWithdrawTx(transaction: { @@ -1018,7 +1027,7 @@ const tx = await provider.getWithdrawTx({ to: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", }); -console.log(`Gas for transfer tx: ${tx}`); +console.log(`Gas for withdrawal tx: ${tx}`); ``` ### `l1ChainId` @@ -1033,8 +1042,6 @@ async l1ChainId(): Promise #### Example -Helper function: [toJSON](#tojson). - ```ts import { Provider, types } from "zksync-ethers"; @@ -1197,7 +1204,7 @@ Returns gas estimate by overriding the zkSync Era [`estimateGas`](#estimategas) | ------------- | ----------------------------------------------------- | -------------------- | | `transaction` | [`TransactionRequest`](./types.md#transactionrequest) | Transaction request. | -```typescript +```ts override async estimateGas(transaction: TransactionRequest): Promise ``` @@ -1208,7 +1215,7 @@ import { BrowserProvider } from "zksync-ethers"; const provider = new BrowserProvider(window.ethereum); const gas = await provider.estimateGas({ - to: RECEIVER, + to: "", amount: ethers.parseEther("0.01"), }); console.log(`Gas: ${gas}`); @@ -1218,13 +1225,19 @@ console.log(`Gas: ${gas}`); Override of [Ethers implementation](https://docs.ethers.org/v6/api/providers/jsonrpc/#JsonRpcApiProvider-getSigner). +#### Inputs + +| Parameter | Type | Description | +| ---------- | -------------------- | ------------------------------------ | +| `address?` | `number` or `string` | Account address or index (optional). | + ```ts -override async getSigner(address ? : number | string): Promise +async getSigner(address ? : number | string): Promise ``` #### Example -```typescript +```ts import { BrowserProvider } from "zksync-ethers"; const provider = new BrowserProvider(window.ethereum); @@ -1243,7 +1256,7 @@ Returns a provider request object by overriding the [Ethers implementation](http | `params?` | `Array` | Parameters of any type (optional). | ```ts -override async send(method: string, params?: Array): Promise +async send(method: string, params?: Array): Promise ``` ## Appendix diff --git a/docs/build/sdks/js/zksync-ethers/types.md b/docs/build/sdks/js/zksync-ethers/types.md index e4d2cf7e2a..0ba1768b0e 100644 --- a/docs/build/sdks/js/zksync-ethers/types.md +++ b/docs/build/sdks/js/zksync-ethers/types.md @@ -144,6 +144,17 @@ Interface representation of transaction fee. - `maxPriorityFeePerGas`: `BigInt`; - `maxFeePerGas`: `BigInt`; +## `FinalizeWithdrawalParams` + +Interface representation of finalize withdrawal parameters. + +- `l1BatchNumber`: `number | null`; +- `l2MessageIndex`: `number`; +- `l2TxNumberInBlock`: `number | null`; +- `message`: `any`; +- `sender`: `string`; +- `proof`: `string[]`; + ## `FullDepositFee` Interface representation of full deposit fee containing various mandatory and optional fields. @@ -222,6 +233,35 @@ Interface representation of priority op response that extends [`TransactionRespo - `waitL1Commit(confirmation?: number)`: `Promise`; +## `RawBlockTransaction` + +Interface representation of raw block with transactions + +- `common_data`: + - `L2`: + - `nonce`: `number`; + - `fee`: + - `gas_limit`: `BigInt`; + - `max_fee_per_gas`: `BigInt`; + - `max_priority_fee_per_gas`: `BigInt`; + - `gas_per_pubdata_limit`: `BigInt`; + - `initiatorAddress`: `Address`; + - `signature`: `Uint8Array`; + - `transactionType`: `string`; + - `input` + - `hash`: `string`; + - `data`: `Uint8Array`; + - `paymasterParams`: + - `paymaster`: `Address`; + - `paymasterInput`: `Uint8Array`; +- `execute`: + - `calldata`: `string`; + - `contractAddress`: `Address`; + - `factoryDeps`: `BytesLike[]`; + - `value`: `BigInt`; +- `received_timestamp_ms`: `number`; +- `raw_bytes`: `string`; + ## `Signature` 0x-prefixed, hex-encoded, ECDSA signature as string. diff --git a/docs/build/sdks/js/zksync-ethers/utils.md b/docs/build/sdks/js/zksync-ethers/utils.md index f4b3957aab..4a402ee539 100644 --- a/docs/build/sdks/js/zksync-ethers/utils.md +++ b/docs/build/sdks/js/zksync-ethers/utils.md @@ -31,7 +31,7 @@ import { utils } from "zksync-ethers"; #### zkSync Era main contract ```typescript -export const ZKSYNC_MAIN_ABI = new utils.Interface(require("../abi/IZkSync.json").abi); +export const ZKSYNC_MAIN_ABI = new utils.Interface(require("../abi/IZkSync.json")); ``` #### IERC20 @@ -39,13 +39,15 @@ export const ZKSYNC_MAIN_ABI = new utils.Interface(require("../abi/IZkSync.json" For interacting with native tokens. ```typescript -export const IERC20 = new utils.Interface(require("../abi/IERC20.json").abi); +export const IERC20 = new utils.Interface(require("../abi/IERC20.json")); ``` #### IERC1271 +For interacting with contract which implements ERC1271. + ```ts -export const IERC1271 = new utils.Interface(require("../abi/IERC1271.json").abi); +export const IERC1271 = new utils.Interface(require("../abi/IERC1271.json")); ``` #### Contract deployer @@ -53,7 +55,7 @@ export const IERC1271 = new utils.Interface(require("../abi/IERC1271.json").abi) Used for deploying smart contracts. ```ts -export const CONTRACT_DEPLOYER = new utils.Interface(require("../abi/ContractDeployer.json").abi); +export const CONTRACT_DEPLOYER = new utils.Interface(require("../abi/ContractDeployer.json")); ``` #### L1 messenger @@ -61,7 +63,7 @@ export const CONTRACT_DEPLOYER = new utils.Interface(require("../abi/ContractDep Used for sending messages from zkSync Era to Ethereum. ```ts -export const L1_MESSENGER = new utils.Interface(require("../abi/IL1Messenger.json").abi); +export const L1_MESSENGER = new utils.Interface(require("../abi/IL1Messenger.json")); ``` #### L1 and L2 bridges @@ -69,8 +71,8 @@ export const L1_MESSENGER = new utils.Interface(require("../abi/IL1Messenger.jso Bridge interface ABIs for L1 and L2. ```ts -export const L1_BRIDGE_ABI = new utils.Interface(require("../abi/IL1Bridge.json").abi); -export const L2_BRIDGE_ABI = new utils.Interface(require("../abi/IL2Bridge.json").abi); +export const L1_BRIDGE_ABI = new utils.Interface(require("../abi/IL1Bridge.json")); +export const L2_BRIDGE_ABI = new utils.Interface(require("../abi/IL2Bridge.json")); ``` #### NonceHolder @@ -78,15 +80,7 @@ export const L2_BRIDGE_ABI = new utils.Interface(require("../abi/IL2Bridge.json" Used for managing deployment nonce. ```ts -export const NONCE_HOLDER_ABI = new ethers.Interface(require("../abi/INonceHolder.json").abi); -``` - -#### PaymasterFlow - -Used for encoding paymaster flows. - -```ts -export const PAYMASTER_FLOW_ABI = new ethers.Interface(require("../abi/IPaymasterFlow.json").abi); +export const NONCE_HOLDER_ABI = new ethers.Interface(require("../abi/INonceHolder.json")); ``` #### L1 to L2 alias offset @@ -113,6 +107,30 @@ Constant representing an EIP712 transaction type. export const EIP712_TX_TYPE = 0x71; ``` +#### EIP712 structures + +Collection of EIP712 structures with their types. + +```ts +export const EIP712_TYPES = { + Transaction: [ + { name: "txType", type: "uint256" }, + { name: "from", type: "uint256" }, + { name: "to", type: "uint256" }, + { name: "gasLimit", type: "uint256" }, + { name: "gasPerPubdataByteLimit", type: "uint256" }, + { name: "maxFeePerGas", type: "uint256" }, + { name: "maxPriorityFeePerGas", type: "uint256" }, + { name: "paymaster", type: "uint256" }, + { name: "nonce", type: "uint256" }, + { name: "value", type: "uint256" }, + { name: "data", type: "bytes" }, + { name: "factoryDeps", type: "bytes32[]" }, + { name: "paymasterInput", type: "bytes" }, + ], +}; +``` + #### Priority op transaction on L2 Constant representing a priority transaction operation on L2. @@ -197,14 +215,6 @@ export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800; Converts the address that submitted a transaction to the inbox on L1 to the `msg.sender` viewed on L2. -#### Inputs - -| Parameter | Type | Description | -| --------- | ------ | ----------------- | -| `address` | string | Contract address. | - -#### Outputs - Returns the `msg.sender` of the L1->L2 transaction as the `address` of the contract that initiated the transaction. :::tip More info @@ -214,12 +224,53 @@ Returns the `msg.sender` of the L1->L2 transaction as the `address` of the contr 3. During L1->L2 communication, if a contract A calls contract B, the `msg.sender` is `applyL1ToL2Alias(A)`. ::: +#### Inputs + +| Parameter | Type | Description | +| --------- | ------ | ----------------- | +| `address` | string | Contract address. | + ```ts export function applyL1ToL2Alias(address: string): string; ``` +#### Example + +```ts +const l1ContractAddress = "0x702942B8205E5dEdCD3374E5f4419843adA76Eeb"; +const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress); +// l2ContractAddress = "0x813A42B8205E5DedCd3374e5f4419843ADa77FFC" +``` + See also [`undol1tol2alias`](#undol1tol2alias). +### `checkBaseCost` + +Checks if the transaction's base cost is greater than the provided value, which covers the transaction's cost. Throws an error if it is not. + +#### Inputs + +| Parameter | Type | Description | +| ---------- | -------------- | ------------------------------------------ | +| `baseCost` | `BigNumberish` | transaction's base cost. | +| `value` | `BigNumberish` | value which covers the transaction's cost. | + +```ts +async function checkBaseCost(baseCost: ethers.BigNumberish, value: ethers.BigNumberish | Promise): Promise; +``` + +### Example + +```ts +const baseCost = 100; +const value = 99; +try { + await utils.checkBaseCost(baseCost, value); +} catch (e) { + // e.message = `The base cost of performing the priority operation is higher than the provided value parameter for the transaction: baseCost: ${baseCost}, provided value: ${value}`, +} +``` + ### `create2Address` Generates a future-proof contract address using salt plus bytecode which allows determination of an address before deployment. @@ -238,12 +289,15 @@ Generates a future-proof contract address using salt plus bytecode which allows | `salt` | `BytesLike` | Randomization element. | | `input` | `BytesLike` | ABI encoded constructor arguments. | -#### Outputs +```ts +export function create2Address(sender: Address, bytecodeHash: BytesLike, salt: BytesLike, input: BytesLike): string; +``` -- Returns an `Address` object. +#### Example ```ts -export function create2Address(sender: Address, bytecodeHash: BytesLike, salt: BytesLike, input: BytesLike); +const address = utils.create2Address("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", "0x010001cb6a6e8d5f6829522f19fa9568660e0a9cd53b2e8be4deb0a679452e41", "0x01", "0x01"); +// address = "0x29bac3E5E8FFE7415F97C956BFA106D70316ad50" ``` :::tip @@ -261,12 +315,15 @@ Generates a contract address from deployer's account and nonce. | `sender` | `Address` | Sender address. | | `senderNonce` | `BigNumberish` | Sender nonce. | -#### Outputs +```ts +export function createAddress(sender: Address, senderNonce: BigNumberish): string; +``` -- Returns an `Address` object. +#### Example ```ts -export function createAddress(sender: Address, senderNonce: BigNumberish); +const address = utils.createAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", 1); +// address = "0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021" ``` ### `eip712TxHash` @@ -281,7 +338,7 @@ Returns the hash of an EIP712 transaction. | `ethSignature?` | [`EthereumSignature`](./types.md#ethereumsignature) | ECDSA signature of the transaction (optional). | ```ts -function eip712TxHash(transaction: any, ethSignature?: EthereumSignature); +function eip712TxHash(transaction: any, ethSignature?: EthereumSignature): string; ``` ### `estimateDefaultBridgeDepositL2Gas` @@ -377,7 +434,15 @@ Returns a keccak encoded message with a given sender address and block number fr | `txNumberInBlock` | number | Index of the transaction in the block. | ```ts -export function getHashedL2ToL1Msg(sender: Address, msg: BytesLike, txNumberInBlock: number); +export function getHashedL2ToL1Msg(sender: Address, msg: BytesLike, txNumberInBlock: number): string; +``` + +#### Example + +```ts +const withdrawETHMessage = "0x6c0960f936615cf349d7f6344891b1e7ca7c72883f5dc04900000000000000000000000000000000000000000000000000000001a13b8600"; +const withdrawETHMessageHash = utils.getHashedL2ToL1Msg("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", withdrawETHMessage, 0); +// withdrawETHMessageHash = "0xd8c80ecb64619e343f57c3b133c6c6d8dd0572dd3488f1ca3276c5b7fd3a938d" ``` ### `hashBytecode` @@ -394,9 +459,25 @@ Returns the hash of given bytecode. export function hashBytecode(bytecode: ethers.BytesLike): Uint8Array; ``` +#### Examples + +```ts +const bytecode = + "0x000200000000000200010000000103550000006001100270000000130010019d0000008001000039000000400010043f0000000101200190000000290000c13d0000000001000031000000040110008c000000420000413d0000000101000367000000000101043b000000e001100270000000150210009c000000310000613d000000160110009c000000420000c13d0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000200310008c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000420000c13d00000004010000390000000101100367000000000101043b000000000010041b0000000001000019000000490001042e0000000001000416000000000110004c000000420000c13d0000002001000039000001000010044300000120000004430000001401000041000000490001042e0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000000310004c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000440000613d00000000010000190000004a00010430000000000100041a000000800010043f0000001801000041000000490001042e0000004800000432000000490001042e0000004a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000006d4ce63c0000000000000000000000000000000000000000000000000000000060fe47b18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000000000000000000000009c8c8fa789967eb514f3ec9def748480945cc9b10fcbd1a19597d924eb201083"; +const hashedBytecode = utils.hashBytecode(bytecode); +/* +hashedBytecode = new Uint8Array([ + 1, 0, 0, 27, 57, 231, 154, 55, 0, 164, 201, 96, 244, 120, 23, 112, 54, 34, 224, 133, + 160, 122, 88, 164, 112, 80, 0, 134, 48, 138, 74, 16, + ]), +); + */ +``` + ### `isECDSASignatureCorrect` -Like similar functionality in `ethers.js` but with added try/catch facility. The function returns true if the validation process succeeds. +Validates signatures from non-contract account addresses (EOA). Provides similar functionality in `ethers.js` but returns `true` if the +validation process succeeds, otherwise returns `false`. Called from [`isSignatureCorrect`](#isSignatureCorrect) for non-contract account addresses. @@ -404,7 +485,7 @@ Called from [`isSignatureCorrect`](#isSignatureCorrect) for non-contract account | Parameter | Type | Description | | ----------- | --------------- | ------------------------------ | -| `address` | `Address` | Address which signs `msgHash`. | +| `address` | `string` | Address which signs `msgHash`. | | `msgHash` | `Address` | Hash of the message. | | `signature` | `SignatureLike` | Ethers signature. | @@ -412,6 +493,26 @@ Called from [`isSignatureCorrect`](#isSignatureCorrect) for non-contract account function isECDSASignatureCorrect(address: string, msgHash: string, signature: SignatureLike): boolean; ``` +#### Examples + +```ts +import { Wallet, utils } from "zksync-ethers"; + +const ADDRESS = ""; +const PRIVATE_KEY = ""; + +const message = "Hello, world!"; +const signature = await new Wallet(PRIVATE_KEY).signMessage(message); +// ethers.Wallet can be used as well +// const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message); + +const isValidSignature = await utils.isECDSASignatureCorrect(ADDRESS, message, signature); +// isValidSignature = true +``` + +See also [`isMessageSignatureCorrect()`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect()`](#istypeddatasignaturecorrect) +to validate signatures. + ### `isEIP1271SignatureCorrect` Called from [`isSignatureCorrect`](#isSignatureCorrect) for contract account addresses, the function returns true if the validation process results in the `EIP1271_MAGIC_VALUE`. @@ -421,14 +522,17 @@ Called from [`isSignatureCorrect`](#isSignatureCorrect) for contract account add | Parameter | Type | Description | | ----------- | --------------- | ------------------------ | | `provider` | `Provider` | Provider. | -| `address` | `Address` | Sender address. | -| `msgHash` | `Address` | The hash of the message. | +| `address` | `string` | Sender address. | +| `msgHash` | `string` | The hash of the message. | | `signature` | `SignatureLike` | Ethers signature. | ```ts async function isEIP1271SignatureCorrect(provider: Provider, address: string, msgHash: string, signature: SignatureLike): Promise; ``` +See also [`isMessageSignatureCorrect()`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect()`](#istypeddatasignaturecorrect) +to validate signatures. + ### `isETH` Returns true if token represents ETH on L1 or L2. @@ -443,23 +547,48 @@ Returns true if token represents ETH on L1 or L2. export function isETH(token: Address); ``` +#### Example + +```ts +const isL1ETH = utils.isETH(utils.ETH_ADDRESS); // true +const isL2ETH = utils.isETH(utils.L2_ETH_TOKEN_ADDRESS); // true +``` + ### `isMessageSignatureCorrect` -Returns true if account abstraction EIP712 signature is correct. +Returns true if account abstraction signature is correct. #### Inputs | Parameter | Type | Description | | ----------- | --------------- | ------------------------ | | `provider` | `Provider` | Provider. | -| `address` | `Address` | Sender address. | -| `message` | `Address` | The hash of the message. | +| `address` | `string` | Sender address. | +| `message` | `string` | The hash of the message. | | `signature` | `SignatureLike` | Ethers signature. | ```ts export async function isMessageSignatureCorrect(provider: Provider, address: string, message: ethers.Bytes | string, signature: SignatureLike): Promise; ``` +#### Example + +```ts +import { Wallet, utils, Provider } from "zksync-ethers"; + +const ADDRESS = ""; +const PRIVATE_KEY = ""; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); + +const message = "Hello, world!"; +const signature = await new Wallet(PRIVATE_KEY).signMessage(message); +// ethers.Wallet can be used as well +// const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message); + +const isValidSignature = await utils.isMessageSignatureCorrect(provider, ADDRESS, message, signature); +// isValidSignature = true +``` + ### `isSignatureCorrect` Called from [`isMessageSignatureCorrect`](#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect`](#istypeddatasignaturecorrect). Returns true if account abstraction EIP712 @@ -470,8 +599,8 @@ signature is correct. | Parameter | Type | Description | | ----------- | --------------- | ------------------------ | | `provider` | `Provider` | Provider. | -| `address` | `Address` | Sender address. | -| `msgHash` | `Address` | The hash of the message. | +| `address` | `string` | Sender address. | +| `msgHash` | `string` | The hash of the message. | | `signature` | `SignatureLike` | Ethers signature. | ```ts @@ -487,7 +616,7 @@ Returns true if account abstraction EIP712 signature is correct. | Parameter | Type | Description | | ----------- | ----------------------------- | ------------------------------------------------------ | | `provider` | `Provider` | Provider. | -| `address` | `Address` | Sender address. | +| `address` | `string` | Sender address. | | `domain` | `TypedDataDomain` | Domain data. | | `types` | `Map` | Map of records pointing from field name to field type. | | `value` | `Record` | A single record value. | @@ -504,6 +633,126 @@ export async function isTypedDataSignatureCorrect( ): Promise; ``` +#### Example + +```ts +import { Wallet, utils, Provider, EIP712Signer } from "zksync-ethers"; + +const ADDRESS = ""; +const PRIVATE_KEY = ""; +const provider = Provider.getDefaultProvider(types.Network.Sepolia); + +const tx: types.TransactionRequest = { + type: 113, + chainId: 270, + from: ADDRESS, + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: BigInt(7_000_000), +}; + +const eip712Signer = new EIP712Signer( + new Wallet(PRIVATE_KEY), // or new ethers.Wallet(PRIVATE_KEY), + Number((await provider.getNetwork()).chainId) +); + +const signature = await eip712Signer.sign(tx); + +const isValidSignature = await utils.isTypedDataSignatureCorrect(provider, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), signature); +// isValidSignature = true +``` + +### `parseEip712` + +Parses an EIP712 transaction from a payload. + +#### Inputs + +| Parameter | Type | Description | +| --------- | ----------- | ----------- | +| `payload` | `BytesLike` | Payload. | + +```ts +export function parseEip712(payload: ethers.BytesLike): TransactionLike; +``` + +### Example + +```ts +import { types } from "zksync-ethers"; + +const serializedTx = + "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"; +const tx: types.TransactionLike = utils.parseEip712(serializedTx); +/* +tx: types.TransactionLike = { + type: 113, + nonce: 0, + maxPriorityFeePerGas: BigInt(0), + maxFeePerGas: BigInt(0), + gasLimit: BigInt(0), + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: BigInt(1000000), + data: "0x", + chainId: BigInt(270), + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + customData: { + gasPerPubdata: BigInt(50000), + factoryDeps: [], + customSignature: "0x", + paymasterParams: null, + }, + hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee", +}; + */ +``` + +### `serializeEip712` + +Serializes an EIP712 transaction and include a signature if it is provided. Throws an error if: + +- `transaction.customData.customSignature` is an empty `string`. The transaction should be signed and the + `transaction.customData.customSignature` field should be populated with the signature. It should not be specified if the transaction is not signed. +- `transaction.chainId` is not provided. +- `transaction.from` is not provided. + +#### Inputs + +| Parameter | Type | Description | +| ------------- | ----------------- | ---------------------------------------------------------------------- | +| `transaction` | `TransactionLike` | Transaction that needs to be serialized. | +| `signature?` | `SignatureLike` | Ethers signature that needs to be included in transactions (optional). | + +```ts +export function serializeEip712(transaction: TransactionLike, signature?: ethers.SignatureLike): string; +``` + +#### Examples + +Serialize EIP712 transaction without signature. + +```ts +const serializedTx = utils.serializeEip712({ chainId: 270, from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" }, null); + +// serializedTx = "0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0" +``` + +Serialize EIP712 transaction with signature. + +```ts +const signature = ethers.Signature.from("0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a"); + +const serializedTx = utils.serializeEip712( + { + chainId: 270, + from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + value: 1_000_000, + }, + signature +); +// serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0" +``` + ### `sleep` Common sleep function that pauses execution for a number of milliseconds. @@ -532,4 +781,12 @@ Converts and returns the `msg.sender` viewed on L2 to the address that submitted export function undoL1ToL2Alias(address: string): string; ``` +#### Example + +```ts +const l2ContractAddress = "0x813A42B8205E5DedCd3374e5f4419843ADa77FFC"; +const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress); +// const l1ContractAddress = "0x702942B8205E5dEdCD3374E5f4419843adA76Eeb" +``` + See also [`applyl1tol2alias`](#applyl1tol2alias).