Skip to content

Commit 11e921d

Browse files
authored
feat: add tests for bridge deposit and withdraw (#191)
1 parent f49b995 commit 11e921d

6 files changed

Lines changed: 132 additions & 40 deletions

File tree

.github/workflows/tests.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,28 @@ jobs:
2121
with:
2222
node-version: "18.x"
2323
registry-url: "https://registry.npmjs.org"
24+
2425
- name: Install dependencies
2526
run: npm ci
27+
28+
- name: Build zksync-cli
29+
run: npm run build
30+
31+
- name: Install Foundry
32+
uses: foundry-rs/foundry-toolchain@v1
33+
34+
- name: Start Anvil in background
35+
run: |
36+
nohup anvil --port 8012 --no-request-size-limit > anvil-foundry.log 2>&1 &
37+
echo "⏳ waiting for anvil to come up…"
38+
sleep 5
39+
40+
- name: Start anvil-zksync in background
41+
uses: dutterbutter/anvil-zksync-action@v1.2.0
42+
with:
43+
mode: run
44+
releaseTag: v0.6.1
45+
externalL1: "http://localhost:8012"
46+
2647
- name: Test
2748
run: npm run test

src/commands/bridge/deposit.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,7 @@ export const handler = async (options: DepositOptions) => {
101101
const toChain = chains.find((e) => e.network === options.chain);
102102
const toChainLabel = toChain && !options.rpc ? toChain.name : (options.rpc ?? "Unknown chain");
103103

104-
const l1Provider = getL1Provider(
105-
options.l1Rpc ??
106-
toChain?.rpcUrl ??
107-
(() => {
108-
throw new Error("You must supply either --chain or --l1-rpc");
109-
})(),
110-
toChain?.id,
111-
Boolean(options.l1Rpc)
112-
);
104+
const l1Provider = getL1Provider(options.l1Rpc ?? toChain!.rpcUrl);
113105
const l2Provider = getL2Provider(options.rpc ?? toChain!.rpcUrl);
114106
const senderWallet = getL2Wallet(options.privateKey, l2Provider, l1Provider);
115107
const token = options.token ? await getTokenInfo(options.token!, l2Provider, l1Provider) : ETH_TOKEN;

src/commands/bridge/withdraw-finalize.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,7 @@ export const handler = async (options: WithdrawFinalizeOptions) => {
9191
Logger.info(` Withdrawal transaction (L2): ${options.hash}`);
9292
Logger.info(` Finalizer address (L1): ${getAddressFromPrivateKey(options.privateKey)}`);
9393

94-
const l1Provider = getL1Provider(
95-
options.l1Rpc ??
96-
toChain?.rpcUrl ??
97-
(() => {
98-
throw new Error("You must supply either --chain or --l1-rpc");
99-
})(),
100-
toChain?.id,
101-
Boolean(options.l1Rpc)
102-
);
94+
const l1Provider = getL1Provider(options.l1Rpc ?? toChain!.rpcUrl);
10395
const l2Provider = getL2Provider(options.rpc ?? fromChain!.rpcUrl);
10496
const zkWallet = getL2Wallet(options.privateKey, l2Provider, l1Provider);
10597

src/commands/bridge/withdraw.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,7 @@ export const handler = async (options: WithdrawOptions) => {
100100
const toChain = chains.find((e) => e.network === options.chain)?.l1Chain;
101101
const toChainLabel = toChain && !options.l1Rpc ? toChain.name : (options.l1Rpc ?? "Unknown chain");
102102

103-
const l1Provider = getL1Provider(
104-
options.l1Rpc ??
105-
toChain?.rpcUrl ??
106-
(() => {
107-
throw new Error("You must supply either --chain or --l1-rpc");
108-
})(),
109-
toChain?.id,
110-
Boolean(options.l1Rpc)
111-
);
103+
const l1Provider = getL1Provider(options.l1Rpc ?? toChain!.rpcUrl);
112104
const l2Provider = getL2Provider(options.rpc ?? fromChain!.rpcUrl);
113105
const senderWallet = getL2Wallet(options.privateKey, l2Provider, l1Provider);
114106
const token = options.token ? await getTokenInfo(options.token!, l2Provider, l1Provider) : ETH_TOKEN;

src/utils/helpers.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import chalk from "chalk";
22
import { spawn } from "child_process";
3-
import { computeAddress, JsonRpcProvider, Network, type Networkish } from "ethers";
3+
import { computeAddress, JsonRpcProvider } from "ethers";
44
import { Wallet, Provider } from "zksync-ethers";
55
import { Logger } from "../lib/index.js";
66

@@ -21,18 +21,8 @@ export const getAddressFromPrivateKey = (privateKey: string): string => {
2121
return computeAddress(privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`);
2222
};
2323

24-
export const getL1Provider = (url: string, network?: Networkish, detectNetwork = false) => {
25-
if (detectNetwork) {
26-
return new JsonRpcProvider(url, undefined, { staticNetwork: true });
27-
}
28-
29-
if (network == null) {
30-
throw new Error("No chain metadata available.");
31-
}
32-
33-
const netObj: Network = Network.from(network);
34-
35-
return new JsonRpcProvider(url, netObj, { staticNetwork: netObj });
24+
export const getL1Provider = (url: string) => {
25+
return new JsonRpcProvider(url);
3626
};
3727

3828
export const getL2Provider = (rpc: string) => {

tests/bridge.spec.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// tests/bridge.e2e.test.ts
2+
import { beforeAll, describe, expect, it } from "vitest";
3+
import { execa } from "execa";
4+
import { JsonRpcProvider } from "ethers";
5+
import { Provider, Wallet } from "zksync-ethers";
6+
7+
const PK = process.env.TEST_PK ?? "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
8+
const L1_RPC = process.env.L1_RPC ?? "http://127.0.0.1:8012";
9+
const L2_RPC = process.env.L2_RPC ?? "http://127.0.0.1:8011";
10+
const CHAIN = process.env.CHAIN ?? "in-memory-node";
11+
12+
const DEPOSIT_ETH = "1";
13+
const WITHDRAW_ETH = "0.001";
14+
15+
if (!PK) throw new Error("TEST_PK is not set – provide a funded private key");
16+
17+
// ────────────────────────────────────────────────────────────
18+
// Helpers
19+
// ────────────────────────────────────────────────────────────
20+
async function cli(args: string[], env: Record<string, string> = {}) {
21+
const { stdout } = await execa("node", ["bin/index.js", ...args], { env });
22+
return stdout;
23+
}
24+
25+
function extractHash(out: string) {
26+
const m = out.match(/Transaction hash:\s+(0x[a-f0-9]{64})/i);
27+
if (!m) throw new Error("Cannot find tx hash in CLI output\n" + out);
28+
return m[1];
29+
}
30+
31+
// ────────────────────────────────────────────────────────────
32+
// Tests
33+
// ────────────────────────────────────────────────────────────
34+
describe("bridge CLI", () => {
35+
let wallet: Wallet;
36+
let l1Provider: JsonRpcProvider;
37+
let l2Provider: Provider;
38+
39+
let depositHash: string | undefined;
40+
let withdrawHash: string | undefined;
41+
42+
beforeAll(() => {
43+
l1Provider = new JsonRpcProvider(L1_RPC);
44+
l2Provider = new Provider(L2_RPC);
45+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
46+
wallet = new Wallet(PK, l2Provider, l1Provider as any);
47+
});
48+
49+
// ──────────────────────────────────────────────────────────
50+
// Deposit
51+
// ──────────────────────────────────────────────────────────
52+
it("sends a deposit L1 ➜ L2", { timeout: 90_000 }, async () => {
53+
const out = await cli([
54+
"bridge",
55+
"deposit",
56+
"--chain",
57+
CHAIN,
58+
"--amount",
59+
DEPOSIT_ETH,
60+
"--private-key",
61+
PK,
62+
"--recipient",
63+
wallet.address,
64+
"--l1-rpc",
65+
L1_RPC,
66+
"--rpc",
67+
L2_RPC,
68+
]);
69+
70+
expect(out).toMatch(/Deposit sent:/);
71+
depositHash = extractHash(out);
72+
expect(depositHash).toMatch(/^0x[a-f0-9]{64}$/);
73+
});
74+
75+
// ──────────────────────────────────────────────────────────
76+
// Withdraw
77+
// ──────────────────────────────────────────────────────────
78+
it("sends a withdrawal L2 ➜ L1", { timeout: 90_000 }, async () => {
79+
const out = await cli([
80+
"bridge",
81+
"withdraw",
82+
"--chain",
83+
CHAIN,
84+
"--amount",
85+
WITHDRAW_ETH,
86+
"--private-key",
87+
PK,
88+
"--recipient",
89+
wallet.address,
90+
"--l1-rpc",
91+
L1_RPC,
92+
"--rpc",
93+
L2_RPC,
94+
]);
95+
96+
expect(out).toMatch(/Withdraw sent:/);
97+
withdrawHash = extractHash(out);
98+
expect(withdrawHash).toMatch(/^0x[a-f0-9]{64}$/);
99+
});
100+
101+
// ──────────────────────────────────────────────────────────
102+
// Finalize
103+
// ──────────────────────────────────────────────────────────
104+
// TODO: need to figure out how to wait for the withdrawal to be ready for finalization
105+
});

0 commit comments

Comments
 (0)