Skip to content

Commit 44ce06e

Browse files
mationoratootani88jpcentenoMexicanAce
authored
feat: Add localnet command (#16)
Co-authored-by: Maksym <[email protected]> Co-authored-by: Joaquín P. Centeno <[email protected]> Co-authored-by: Nicolas Villanueva <[email protected]>
1 parent 94a2dc9 commit 44ce06e

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ You can install this program globally with `npm i -g zksync-cli` or run the comm
3737

3838
- `zksync-cli --version`: Returns the current version
3939

40+
- `zksync-cli localnet`: Manages a local zkSync Era and Ethereum L1 testnet (it requires docker running on your system). It supports a set of sub-subcommands:
41+
- `zksync-cli localnet up`: Bootstrap L1 and L2 localnets.
42+
- `zksync-cli localnet down`: clear L1 and L2 localnets.
43+
- `zksync-cli localnet start`: start L1 and L2 localnets.
44+
- `zksync-cli localnet stop`: stop L1 and L2 localnets.
45+
- `zksync-cli localnet logs`: Display logs.
46+
- `zksync-cli localnet help`: Display this message and quit.
47+
- `zksync-cli localnet wallets`: Display seeded wallet keys.
4048

4149
### 🔗 Supported chains
4250

src/commands/localnet.ts

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { program } from "../setup";
2+
import { execSync, ExecSyncOptions } from 'child_process';
3+
import * as path from 'path';
4+
import * as fs from 'fs';
5+
import * as os from 'os';
6+
7+
const REPO_URL: string = "https://github.com/matter-labs/local-setup.git";
8+
const REPO_BRANCH: string = "main"
9+
10+
// ---------------------------------------------------------------------------------------
11+
// Utilities
12+
// ---------------------------------------------------------------------------------------
13+
14+
function runSystemCommand(command: string, options?: ExecSyncOptions): string {
15+
const defaultOptions: ExecSyncOptions = { cwd: repoDirectory(), encoding: 'utf-8' };
16+
const unifiedOptions: ExecSyncOptions = {...defaultOptions, ...options};
17+
return execSync(command, unifiedOptions).toString();
18+
}
19+
20+
/**
21+
* Returns the path where the `zksync-cli/local-setup` repository, used
22+
* internally to manage localnet deployments, should be located.
23+
*
24+
* **Why follow the XDG Base Directory Specification?**
25+
*
26+
* This function follows the XDG Base Directory Specification
27+
* (https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
28+
* to determine the parent directory location:
29+
*
30+
* The XDG Base Directory Specification is widely accepted as a standard.
31+
* The decision to place the files under `$XDG_STATE_HOME` is based on considering the
32+
* presence or absence of this repository as part of the CLI tool's state.
33+
*
34+
* Alternative locations within the XDG Base Directory Specification were
35+
* considered and ruled out for the following reasons:
36+
*
37+
* - `$XDG_DATA_HOME` was not chosen because these files aren't user-specific
38+
* data files.
39+
*
40+
* - `$XDG_CACHE_HOME` was not chosen because these files aren't considered
41+
* non-essential cached data files.
42+
*
43+
* @returns {string} The path where the `zksync-cli/local-setup` repository should be
44+
* placed.
45+
*/
46+
function repoDirectory(): string {
47+
// From the XDG Base Directory Specification:
48+
// `$XDG_STATE_HOME` defines the base directory relative to which user-specific state files should be stored. If `$XDG_STATE_HOME` is either not set or empty, a default equal to `$HOME/.local/state` should be used.
49+
const xdgStateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local/state");
50+
return path.join(xdgStateHome, "zksync-cli/local-setup");
51+
}
52+
53+
function isRepoCloned(): boolean {
54+
return fs.existsSync(repoDirectory());
55+
}
56+
57+
function cloneRepo() {
58+
const parentDirectory = path.join(repoDirectory(), "..");
59+
runSystemCommand(`mkdir -p '${parentDirectory}'`, { cwd: "/" });
60+
const options: ExecSyncOptions = { cwd: parentDirectory };
61+
runSystemCommand(`git clone --branch '${REPO_BRANCH}' '${REPO_URL}'`, options);
62+
}
63+
64+
function setUp() {
65+
cloneRepo();
66+
}
67+
68+
// ---------------------------------------------------------------------------------------
69+
// Command handling
70+
// ---------------------------------------------------------------------------------------
71+
72+
const localnet = program
73+
.command("localnet")
74+
.description("Manage local L1 and L2 chains");
75+
76+
localnet
77+
.command("up")
78+
.description("Startup L1 and L2 localnets")
79+
.action(() => {
80+
if (! isRepoCloned()) {
81+
setUp();
82+
}
83+
runSystemCommand("docker compose up --detach");
84+
});
85+
86+
localnet
87+
.command("down")
88+
.description("clear L1 and L2 localnets")
89+
.action(() => {
90+
runSystemCommand("docker compose down --volumes");
91+
});
92+
93+
localnet
94+
.command("start")
95+
.description("start L1 and L2 localnets")
96+
.action(() => {
97+
runSystemCommand("docker compose start");
98+
});
99+
100+
localnet
101+
.command("stop")
102+
.description("stop L1 and L2 localnets")
103+
.action(() => {
104+
runSystemCommand("docker compose stop");
105+
});
106+
107+
localnet
108+
.command("logs")
109+
.description("Display logs")
110+
.action(() => {
111+
const options: ExecSyncOptions = { stdio: 'inherit' };
112+
runSystemCommand("docker-compose logs --follow", options);
113+
});
114+
115+
localnet
116+
.command("wallets")
117+
.description("Display rich wallet keys and addresses")
118+
.action(() => {
119+
const rawJSON = fs.readFileSync(path.join(repoDirectory(), "rich-wallets.json")).toString();
120+
const wallets = JSON.parse(rawJSON);
121+
console.log(wallets);
122+
});

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ import "./commands/deposit";
66
import "./commands/withdraw";
77
import "./commands/withdraw-finalize";
88
import "./commands/create-project";
9+
import "./commands/localnet";
910

1011
program.parse();

0 commit comments

Comments
 (0)