> ## Documentation Index
> Fetch the complete documentation index at: https://zksync-sdk.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# ETH Deposit (L1 → L2)

> Get your first ETH deposit from Ethereum (L1) to ZKsync (L2).

## 1. Prerequisites

* You have [Bun](https://bun.sh/) installed.
* A funded **L1 wallet** with ETH for both the deposit amount and L1 gas fees

<Info>Use a test network like **Sepolia** for experimentation.</Info>

## 2. Installation

Choose your adapter and install the SDK + adapter package:

<CodeGroup>
  ```bash title="viem" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  bun install @dutterbutter/zksync-sdk viem dotenv
  ```

  ```bash title="ethers" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  bun install @dutterbutter/zksync-sdk ethers dotenv
  ```
</CodeGroup>

Create a `.env` file in your project root:

```env theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
# Your funded L1 private key (0x + 64 hex)
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE

# RPC endpoints
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_ID
L2_RPC_URL=ZKSYNC-OS-TESTNET-RPC
```

<Warning>Never commit your `.env` file to source control.</Warning>

## 3. Write the deposit script

<CodeGroup>
  ```ts title="deposit/viem.ts" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  import 'dotenv/config';
  import { createPublicClient, createWalletClient, http, parseEther } from 'viem';
  import { privateKeyToAccount } from 'viem/accounts';
  import { createViemClient, createViemSdk } from '@dutterbutter/zksync-sdk/viem';
  import { ETH_ADDRESS } from '@dutterbutter/zksync-sdk/core';

  const PRIVATE_KEY = process.env.PRIVATE_KEY;
  const L1_RPC_URL = process.env.L1_RPC_URL;
  const L2_RPC_URL = process.env.L2_RPC_URL;

  async function main() {
    if (!PRIVATE_KEY || !L1_RPC_URL || !L2_RPC_URL) {
      throw new Error('Please set PRIVATE_KEY, L1_RPC_URL, and L2_RPC_URL in your .env file');
    }

    // 1. Set up clients
    const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`);
    const l1 = createPublicClient({ transport: http(L1_RPC_URL) });
    const l2 = createPublicClient({ transport: http(L2_RPC_URL) });
    const l1Wallet = createWalletClient({ account, transport: http(L1_RPC_URL) });

    // 2. Initialize the SDK
    const client = createViemClient({ l1, l2, l1Wallet });
    const sdk = createViemSdk(client);

    console.log('Wallet balances:');
    console.log('  L1:', await l1.getBalance({ address: account.address }));
    console.log('  L2:', await l2.getBalance({ address: account.address }));

    // 3. Perform the deposit
    console.log('Sending deposit transaction...');
    const depositHandle = await sdk.deposits.create({
      token: ETH_ADDRESS,
      amount: parseEther('0.001'),
      to: account.address,
    });

    console.log(`L1 transaction hash: ${depositHandle.l1TxHash}`);

    console.log('Waiting for confirmation on L1...');
    const l1Receipt = await sdk.deposits.wait(depositHandle, { for: 'l1' });
    console.log(`✔️ Confirmed on L1 at block ${l1Receipt?.blockNumber}`);

    console.log('Waiting for execution on L2...');
    const l2Receipt = await sdk.deposits.wait(depositHandle, { for: 'l2' });
    console.log(`✔️ Executed on L2 at block ${l2Receipt?.blockNumber}`);

    console.log('Deposit complete ✅');

    console.log('Updated balances:');
    console.log('  L1:', await l1.getBalance({ address: account.address }));
    console.log('  L2:', await l2.getBalance({ address: account.address }));
  }

  main().catch((err) => {
    console.error('An error occurred:', err);
    process.exit(1);
  });
  ```

  ```tsx title="deposit/ethers.ts" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  import 'dotenv/config'; // Load environment variables from .env
  import { JsonRpcProvider, Wallet, parseEther } from 'ethers';
  import { createEthersClient, createEthersSdk } from '@dutterbutter/zksync-sdk/ethers';
  import { ETH_ADDRESS } from '@dutterbutter/zksync-sdk/core';

  const PRIVATE_KEY = process.env.PRIVATE_KEY;
  const L1_RPC_URL = process.env.L1_RPC_URL;
  const L2_RPC_URL = process.env.L2_RPC_URL;

  async function main() {
    if (!PRIVATE_KEY || !L1_RPC_URL || !L2_RPC_URL) {
      throw new Error('Please set your PRIVATE_KEY, L1_RPC_URL, and L2_RPC_URL in a .env file');
    }

    // 1. SET UP PROVIDERS AND SIGNER
    // The SDK needs connections to both L1 and L2 to function.
    const l1Provider = new JsonRpcProvider(L1_RPC_URL);
    const l2Provider = new JsonRpcProvider(L2_RPC_URL);
    const signer = new Wallet(PRIVATE_KEY, l1Provider);

    // 2. INITIALIZE THE SDK CLIENT
    // The client is the low-level interface for interacting with the API.
    const client = await createEthersClient({
      l1Provider,
      l2Provider,
      signer,
    });
    const sdk = createEthersSdk(client);

    const L1balance = await l1.getBalance({ address: signer.address });
    const L2balance = await l2.getBalance({ address: signer.address });

    console.log('Wallet balance on L1:', L1balance);
    console.log('Wallet balance on L2:', L2balance);

    // 3. PERFORM THE DEPOSIT
    // The create() method prepares and sends the transaction.
    // The wait() method polls until the transaction is complete.
    console.log('Sending deposit transaction...');
    const depositHandle = await sdk.deposits.create({
      token: ETH_ADDRESS,
      amount: parseEther('0.001'), // 0.001 ETH
      to: account.address,
    });

    console.log(`L1 transaction hash: ${depositHandle.l1TxHash}`);
    console.log('Waiting for the deposit to be confirmed on L1...');

    // Wait for L1 inclusion
    const l1Receipt = await sdk.deposits.wait(depositHandle, { for: 'l1' });
    console.log(`Deposit confirmed on L1 in block ${l1Receipt?.blockNumber}`);

    console.log('Waiting for the deposit to be executed on L2...');

    // Wait for L2 execution
    const l2Receipt = await sdk.deposits.wait(depositHandle, { for: 'l2' });
    console.log(`Deposit executed on L2 in block ${l2Receipt?.blockNumber}`);
    console.log('Deposit complete! ✅');

    const L1balanceAfter = await l1.getBalance({ address: signer.address });
    const L2balanceAfter = await l2.getBalance({ address: signer.address });

    console.log('Wallet balance on L1 after:', L1balanceAfter);
    console.log('Wallet balance on L2 after:', L2balanceAfter);
  }

  main().catch((error) => {
    console.error('An error occurred:', error);
    process.exit(1);
  });
  ```
</CodeGroup>

## 4. Run it

Execute the script using `bun`.

<CodeGroup>
  ```bash title="viem" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  bun run deposit/viem.ts
  ```

  ```bash title="ethers" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  bun run deposit/ethers.ts
  ```
</CodeGroup>

You’ll see logs for the L1 transaction, then L2 execution, followed by updated balances.

## 5. Troubleshooting

* **Insufficient funds on L1:** Ensure enough ETH for the deposit **and** L1 gas.
* **Invalid PRIVATE\_KEY:** Must be 0x + 64 hex chars.
* **Stuck at wait(..., `{ for: 'l2' }`):** Verify L2\_RPC\_URL and network health; check sdk.deposits.status(handle) to see the current phase.
* **ERC-20 deposits:** Make sure you have an L1 token deployed and a sufficient balance.
