Deposit ERC20 tokens to ZKsync
This how-to guide explains how to create a script that deposits any ERC20 token to ZKsync.
What you'll learn:
- How L2 deposits work
- How to deposit ERC20 tokens to ZKsync
Depositing ERC-20 tokens from Layer 1 (L1) to Layer 2 (L2) is a vital step to engage with ZKsync efficiently. This guide outlines the process of depositing ERC-20 tokens using the ZKsync Javascript SDK, ensuring a streamlined transition of assets to L2. In ZKsync, assets deposited from L1 are secured in a smart contract, while corresponding representations are created on L2, paving the way for swift and economical transactions.
Prerequisites
- Node.js: Ensure you have Node.js 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 Alchemy.
Understand L1 - L2 Deposits
Depositing assets from L1 to L2 involves calling the
deposit
method on the L1 bridge contract. Here's a simplified breakdown of the process:
- Locking L1 Tokens: Initially, the specified tokens on L1 are sent to the L1 bridge, where they are locked.
- Initiating L1 to L2 Transaction: The L1 bridge then initiates a transaction to the L2 bridge, marking the start of the deposit process.
- Minting L2 Tokens: Tokens corresponding to the deposit are minted on L2 and sent to the designated L2 address.
- If the token contract isn’t already present on ZKsync Era, a new contract is deployed with a new L2 token address, which is derived from the original L1 address, name, and symbol.
- Confirmation Logging: Each executed L1 to L2 transaction is confirmed with a log message sent from L2 back to L1.
- Finalizing Deposit: Finally, the
finalizeDeposit
method is called on the L2 bridge to complete the deposit process, ensuring the funds are minted on L2.
This structured flow ensures a secure and orderly transfer of assets from L1 to L2, paving the way for further interactions on the Layer 2 network.
Set Up Environment
Set up the node script:
mkdir deposit-erc20-script && cd deposit-erc20-script
npm init -y
npm i typescript ts-node ethers@^5.7.2 zksync-ethers@5 dotenv
Create 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 Deposit Script
Create a new file deposit-erc20.ts
and insert the below code:
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 (DAI) address in L1
const TOKEN_ADDRESS = "0x5C221E77624690fff6dd741493D735a17716c26B";
// Amount of tokens
const AMOUNT = "1";
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 L2`);
// Initialize the wallet.
const l1provider = new 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()}`);
// Deposit token to L2
const depositHandle = await wallet.deposit({
to: wallet.address, // can bridge to a different address in L2
token: TOKEN_ADDRESS,
amount: ethers.utils.parseEther(AMOUNT), // assumes ERC-20 has 18 decimals
// performs the ERC-20 approve action
approveERC20: true,
});
console.log(`Deposit transaction sent ${depositHandle.hash}`);
console.log(`Please wait a few minutes for the deposit to be processed in L2`);
}
main()
.then()
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run the Script
Execute the script using the following command:
npx ts-node deposit-erc20.ts
Verify the Deposit
Upon running the script, you should see output similar to below, indicating the deposit transaction has been sent and is being processed on L2:
Running script to bridge ERC-20 to L2
L1 Balance is 19500035772482145
L2 Balance is 2969807401250000000
Deposit transaction sent 0xffb8e302430b0584e2e0104dd6295a03688c98ba7b6e9279b01dba65188cc444
Conclusion
By adhering to this guide, you have successfully deposited ERC-20 tokens from L1 to L2 using the ZKsync Javascript SDK, making a significant stride towards interacting with the ZKsync Era.