Withdraw ERC20 tokens from ZKsync
This how-to guide explains how to create a script that withdraw any ERC20 token from ZKsync to Ethereum.
What you'll learn:
- How L2 withdraw work
- How to withdraw ERC20 tokens to 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-erc20-script && cd withdraw-erc20-script
npm init -y
npm i typescript ts-node ethers@^5.7.2 zksync-ethers@5 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-erc20.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
and TOKEN_ADDRESS
variable if necessary.
withdraw-erc20.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
// ERC-20 Token address in L2
const TOKEN_ADDRESS = "<TOKEN_ADDRESS>";
// Amount of tokens
const AMOUNT = "5";
const WALLET_PRIV_KEY = process.env.WALLET_PRIV_KEY || "";
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");
}
if (!TOKEN_ADDRESS) {
throw new Error("Missing address of the ERC-20 token in L1");
}
async function main() {
console.log(`Running script to bridge ERC-20 to L1`);
// Initialize the wallet.
const l1provider = new ethers.providers.JsonRpcProvider(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()}`);
// withdraw ERC-20 token to L2
const withdrawErc20Handle = await wallet.withdraw({
to: wallet.address, // can bridge to a different address in L1
token: TOKEN_ADDRESS,
amount: ethers.utils.parseEther(AMOUNT), // assumes ERC-20 has 18 decimals
});
console.log(`Withdraw ERC-20 transaction sent ${withdrawErc20Handle.hash}`);
}
main()
.then()
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run the Script
Execute the script using the following command:
npx ts-node withdraw-erc20.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 bridge ERC-20 to L1
L1 Balance is 19421054769191270
L2 Balance is 2969626077250000000
Withdraw ERC-20 transaction sent 0x280a2168f464c93e8c56df3291076bbb6cff78ebdc30fdaad22bc275d56aa3ed
Conclusion
By following this guide, you have successfully withdrawn ERC-20 token from L2 to L1 using the ZKsync Javascript SDK. This is a significant step towards managing your assets on the ZKsync Era.