Withdraw Ether from ZKsync
This how-to guide explains how to create a script that withdraws Ether from ZKsync.
What you'll learn:
- How L2 withdrawals work
- How to withdraw Ether from ZKsync
Withdrawing assets from Layer 2 (L2) back to Layer 1 (L1) is an essential procedure to retrieve your assets in ZKsync. This guide outlines how to withdraw ETH using the ZKsync Javascript SDK, ensuring a successful transfer of assets to L1. In ZKsync, withdrawing involves burning tokens on L2 and unlocking the corresponding funds on the L1 bridge.
Prerequisites
- Node.js: Ensure Node.js is installed. If not, download it from here.
- Private Key: Have access to a private key for the account you'll be using.
- L1 RPC Endpoint: A URL to an Ethereum node to interact with. You can find RPC endpoints for Sepolia and Ethereum mainnet on Chainlist or use a node provider like Infura.
Understanding L2 to L1 Withdrawals
Withdrawing assets from L2 to L1 involves the following steps:
- Burning L2 Tokens: Initially, the specified tokens on L2 are burned.
- Sending L2 to L1 Message: A message detailing the withdrawal is sent from L2 to L1.
- Finalizing Withdrawal: The
finalizeWithdrawal
method is called on the L1 bridge to complete the withdrawal process, unlocking the funds from the L1 bridge and sending them to the recipient.
Setup Environment
Create a new directory for your withdrawal scripts and navigate into it:
mkdir withdraw-scripts && cd withdraw-scripts
npm init -y
npm i typescript ts-node zksync-ethers dotenv
Set up environment variables by creating a .env
file in the project root containing your private key and the L1 RPC endpoint:
WALLET_PRIV_KEY=<YOUR_PRIVATE_KEY>
L1_RPC_ENDPOINT=<RPC_URL>
Create the Withdrawal Script
Create a new file withdraw.ts
and insert the following code.
This script utilizes the withdraw
method from the Wallet
class of the ZKsync Javascript SDK to withdraw ETH.
Adjust the AMOUNT
variable if necessary.
withdraw.ts script
import { Wallet, Provider, utils } from "zksync-ethers";
import * as ethers from "ethers";
// load env file
import dotenv from "dotenv";
dotenv.config();
// HTTP RPC endpoints
const L1_RPC_ENDPOINT = process.env.L1_RPC_ENDPOINT || ""; // or an RPC endpoint from Infura/Chainstack/QuickNode/etc.
const L2_RPC_ENDPOINT = process.env.L2_RPC_ENDPOINT || "https://sepolia.era.zksync.dev"; // or the ZKsync Era mainnet
// Wallet
const WALLET_PRIV_KEY = process.env.WALLET_PRIV_KEY || "";
// Amount in ETH
const AMOUNT = "0.00001";
if (!WALLET_PRIV_KEY) {
throw new Error("Wallet private key is not configured in env file");
}
if (!L1_RPC_ENDPOINT) {
throw new Error("Missing L1 RPC endpoint. Check chainlist.org or an RPC node provider");
}
async function main() {
console.log(`Running script to withdraw ETH to L1`);
// Initialize the wallet.
const l1provider = new ethers.Provider(L1_RPC_ENDPOINT);
const l2provider = new Provider(L2_RPC_ENDPOINT);
const wallet = new Wallet(WALLET_PRIV_KEY, l2provider, l1provider);
console.log(`L1 Balance is ${await wallet.getBalanceL1()}`);
console.log(`L2 Balance is ${await wallet.getBalance()}`);
try {
// withdraw ETH to L1
const withdrawHandle = await wallet.withdraw({
to: wallet.address, // can bridge to a different address in L1
token: utils.ETH_ADDRESS,
amount: ethers.parseEther(AMOUNT),
});
console.log(`Withdraw transaction sent ${withdrawHandle.hash}`);
} catch (error: any) {
console.error(`Error withdrawing: ${error.message}`);
process.exitCode = 1;
}
}
main()
.then()
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run the Script
Execute the script using the following command:
npx ts-node withdraw.ts
Verify the Output
Upon running the script, you should see an output similar to below, indicating the withdrawal transaction has been sent and is being processed on L1:
Running script to withdraw ETH to L1
L1 Balance is 6539874840163375070
L2 Balance is 5712612651486983637
Withdraw transaction sent 0x4905176d42b4c3b4ab10f611e688b2d849e761493f4583119b7c7731b4254cf4
Conclusion
By following this guide, you have successfully withdrawn ETH from L2 to L1 using the ZKsync Javascript SDK. This is a significant step towards managing your assets on the ZKsync Era.