@semaphore-protocol/cli 4.0.0-alpha.4 → 4.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +367 -367
  2. package/package.json +4 -5
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @module @semaphore-protocol/cli
5
- * @version 4.0.0-alpha.4
5
+ * @version 4.0.0-alpha.5
6
6
  * @file A command line tool to set up your Semaphore project and get group data.
7
7
  * @copyright Ethereum Foundation 2024
8
8
  * @license MIT
@@ -26,378 +26,378 @@ import { lt } from 'semver';
26
26
  import ora from 'ora';
27
27
  import inquirer from 'inquirer';
28
28
 
29
- const cliRegistryURL = "https://registry.npmjs.org/-/package/@semaphore-protocol/cli/dist-tags";
30
- /**
31
- * Checks the registry directly via the API, if that fails, tries the slower `npm view [package] version` command.
32
- * This is important for users in environments where direct access to npm is blocked by a firewall, and packages are
33
- * provided exclusively via a private registry.
34
- * @param currentVersion The current version of the CLI.
35
- */
36
- async function checkLatestVersion(currentVersion) {
37
- let latestVersion;
38
- try {
39
- const { data } = await axios.get(cliRegistryURL);
40
- latestVersion = data.latest;
41
- }
42
- catch {
43
- try {
44
- latestVersion = execSync("npm view @semaphore-protocol/cli version").toString().trim();
45
- }
46
- catch {
47
- latestVersion = null;
48
- }
49
- }
50
- if (latestVersion && lt(currentVersion, latestVersion)) {
51
- console.info("\n");
52
- console.info(boxen(chalk.white(`Update available ${chalk.gray(currentVersion)} -> ${chalk.green(latestVersion)} \n\n You are currently using @semaphore-protocol/cli ${chalk.gray(currentVersion)} which is behind the latest release ${chalk.green(latestVersion)} \n\n Run ${chalk.cyan("npm install -g @semaphore-protocol/cli@latest")} to get the latest version`), { padding: 1, borderColor: "yellow", textAlignment: "center" }));
53
- console.info("");
54
- }
29
+ const cliRegistryURL = "https://registry.npmjs.org/-/package/@semaphore-protocol/cli/dist-tags";
30
+ /**
31
+ * Checks the registry directly via the API, if that fails, tries the slower `npm view [package] version` command.
32
+ * This is important for users in environments where direct access to npm is blocked by a firewall, and packages are
33
+ * provided exclusively via a private registry.
34
+ * @param currentVersion The current version of the CLI.
35
+ */
36
+ async function checkLatestVersion(currentVersion) {
37
+ let latestVersion;
38
+ try {
39
+ const { data } = await axios.get(cliRegistryURL);
40
+ latestVersion = data.latest;
41
+ }
42
+ catch {
43
+ try {
44
+ latestVersion = execSync("npm view @semaphore-protocol/cli version").toString().trim();
45
+ }
46
+ catch {
47
+ latestVersion = null;
48
+ }
49
+ }
50
+ if (latestVersion && lt(currentVersion, latestVersion)) {
51
+ console.info("\n");
52
+ console.info(boxen(chalk.white(`Update available ${chalk.gray(currentVersion)} -> ${chalk.green(latestVersion)} \n\n You are currently using @semaphore-protocol/cli ${chalk.gray(currentVersion)} which is behind the latest release ${chalk.green(latestVersion)} \n\n Run ${chalk.cyan("npm install -g @semaphore-protocol/cli@latest")} to get the latest version`), { padding: 1, borderColor: "yellow", textAlignment: "center" }));
53
+ console.info("");
54
+ }
55
55
  }
56
56
 
57
- class Spinner {
58
- constructor(text) {
59
- this.text = text;
60
- }
61
- start() {
62
- console.info("");
63
- this.ora = ora({
64
- text: this.text,
65
- indent: 1
66
- }).start();
67
- }
68
- stop() {
69
- this.ora.stop();
70
- process.stdout.moveCursor(0, -1);
71
- }
57
+ class Spinner {
58
+ constructor(text) {
59
+ this.text = text;
60
+ }
61
+ start() {
62
+ console.info("");
63
+ this.ora = ora({
64
+ text: this.text,
65
+ indent: 1
66
+ }).start();
67
+ }
68
+ stop() {
69
+ this.ora.stop();
70
+ process.stdout.moveCursor(0, -1);
71
+ }
72
72
  }
73
73
 
74
- /**
75
- * Gets all group ids on the specified network
76
- * @param network The specified network
77
- */
78
- async function getGroupIds(network) {
79
- let groupIds;
80
- const spinner = new Spinner("Fetching groups");
81
- spinner.start();
82
- try {
83
- const semaphoreSubgraph = new SemaphoreSubgraph(network);
84
- groupIds = await semaphoreSubgraph.getGroupIds();
85
- spinner.stop();
86
- }
87
- catch {
88
- try {
89
- const semaphoreEthers = new SemaphoreEthers(network);
90
- groupIds = await semaphoreEthers.getGroupIds();
91
- spinner.stop();
92
- }
93
- catch {
94
- spinner.stop();
95
- console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package");
96
- return null;
97
- }
98
- }
99
- if (groupIds.length === 0) {
100
- console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n");
101
- return null;
102
- }
103
- return groupIds;
74
+ /**
75
+ * Gets all group ids on the specified network
76
+ * @param network The specified network
77
+ */
78
+ async function getGroupIds(network) {
79
+ let groupIds;
80
+ const spinner = new Spinner("Fetching groups");
81
+ spinner.start();
82
+ try {
83
+ const semaphoreSubgraph = new SemaphoreSubgraph(network);
84
+ groupIds = await semaphoreSubgraph.getGroupIds();
85
+ spinner.stop();
86
+ }
87
+ catch {
88
+ try {
89
+ const semaphoreEthers = new SemaphoreEthers(network);
90
+ groupIds = await semaphoreEthers.getGroupIds();
91
+ spinner.stop();
92
+ }
93
+ catch {
94
+ spinner.stop();
95
+ console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package");
96
+ return null;
97
+ }
98
+ }
99
+ if (groupIds.length === 0) {
100
+ console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n");
101
+ return null;
102
+ }
103
+ return groupIds;
104
104
  }
105
105
 
106
- async function getProjectName() {
107
- const { projectName } = await inquirer.prompt({
108
- name: "projectName",
109
- type: "input",
110
- message: "What is your project name?",
111
- default: "my-app"
112
- });
113
- return projectName;
114
- }
115
- async function getSupportedTemplate(supportedTemplates) {
116
- const { selectedTemplate } = await inquirer.prompt({
117
- name: "selectedTemplate",
118
- type: "list",
119
- message: "Select one of the supported templates:",
120
- default: 0,
121
- choices: supportedTemplates.map((template) => ({
122
- value: template.value,
123
- name: `${template.value} (${template.name})`
124
- }))
125
- });
126
- return selectedTemplate;
127
- }
128
- async function getSupportedNetwork(supportedNetworks) {
129
- const { selectedNetwork } = await inquirer.prompt({
130
- name: "selectedNetwork",
131
- type: "list",
132
- message: "Select one of the supported networks:",
133
- default: 0,
134
- choices: supportedNetworks
135
- });
136
- return selectedNetwork;
137
- }
138
- async function getGroupId(groupIds) {
139
- const { selectedGroupId } = await inquirer.prompt({
140
- name: "selectedGroupId",
141
- type: "list",
142
- message: "Select one of the following existing group ids:",
143
- default: 0,
144
- choices: groupIds
145
- });
146
- return selectedGroupId;
106
+ async function getProjectName() {
107
+ const { projectName } = await inquirer.prompt({
108
+ name: "projectName",
109
+ type: "input",
110
+ message: "What is your project name?",
111
+ default: "my-app"
112
+ });
113
+ return projectName;
114
+ }
115
+ async function getSupportedTemplate(supportedTemplates) {
116
+ const { selectedTemplate } = await inquirer.prompt({
117
+ name: "selectedTemplate",
118
+ type: "list",
119
+ message: "Select one of the supported templates:",
120
+ default: 0,
121
+ choices: supportedTemplates.map((template) => ({
122
+ value: template.value,
123
+ name: `${template.value} (${template.name})`
124
+ }))
125
+ });
126
+ return selectedTemplate;
127
+ }
128
+ async function getSupportedNetwork(supportedNetworks) {
129
+ const { selectedNetwork } = await inquirer.prompt({
130
+ name: "selectedNetwork",
131
+ type: "list",
132
+ message: "Select one of the supported networks:",
133
+ default: 0,
134
+ choices: supportedNetworks
135
+ });
136
+ return selectedNetwork;
137
+ }
138
+ async function getGroupId(groupIds) {
139
+ const { selectedGroupId } = await inquirer.prompt({
140
+ name: "selectedGroupId",
141
+ type: "list",
142
+ message: "Select one of the following existing group ids:",
143
+ default: 0,
144
+ choices: groupIds
145
+ });
146
+ return selectedGroupId;
147
147
  }
148
148
 
149
- const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`;
150
- const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"));
151
- const supportedNetworks = getSupportedNetworks();
152
- const supportedTemplates = [
153
- {
154
- value: "monorepo-ethers",
155
- name: "Hardhat + Next.js + SemaphoreEthers"
156
- },
157
- {
158
- value: "monorepo-subgraph",
159
- name: "Hardhat + Next.js + SemaphoreSubgraph"
160
- },
161
- {
162
- value: "contracts-hardhat",
163
- name: "Hardhat"
164
- }
165
- ];
166
- program
167
- .name("semaphore")
168
- .description(description)
169
- .version(version, "-v, --version", "Show Semaphore CLI version.")
170
- .addHelpText("before", `${figlet.textSync("Semaphore")}\n`)
171
- .addHelpText("after", "\r")
172
- .helpOption(undefined, "Display this help.")
173
- .addHelpCommand("help [command]", "Display help for a specific command.")
174
- .configureOutput({
175
- outputError: (message) => {
176
- console.info(`\n ${logSymbols.error}`, message);
177
- }
178
- });
179
- program
180
- .command("create")
181
- .description("Create a Semaphore project with a supported template.")
182
- .argument("[project-directory]", "Directory of the project.")
183
- .option("-t, --template <template-name>", "Supported Semaphore template.")
184
- .allowExcessArguments(false)
185
- .action(async (projectDirectory, { template }) => {
186
- if (!projectDirectory) {
187
- projectDirectory = await getProjectName();
188
- }
189
- if (!template) {
190
- template = await getSupportedTemplate(supportedTemplates);
191
- }
192
- if (!supportedTemplates.some((t) => t.value === template)) {
193
- console.info(`\n ${logSymbols.error}`, `error: the template '${template}' is not supported\n`);
194
- return;
195
- }
196
- const currentDirectory = process.cwd();
197
- const spinner = new Spinner(`Creating your project in ${chalk.green(`./${projectDirectory}`)}`);
198
- if (existsSync(projectDirectory)) {
199
- console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`);
200
- return;
201
- }
202
- spinner.start();
203
- await checkLatestVersion(version);
204
- await pacote.extract(`@semaphore-protocol/cli-template-${template}@${version}`, `${currentDirectory}/${projectDirectory}`);
205
- await decompress(`${currentDirectory}/${projectDirectory}/files.tgz`, `${currentDirectory}/${projectDirectory}`);
206
- unlinkSync(`${currentDirectory}/${projectDirectory}/files.tgz`);
207
- copyFileSync(`${currentDirectory}/${projectDirectory}/.env.example`, `${currentDirectory}/${projectDirectory}/.env`);
208
- spinner.stop();
209
- console.info(`\n ${logSymbols.success}`, `Your project is ready!\n`);
210
- console.info(` Please, install your dependencies by running:\n`);
211
- console.info(` ${chalk.cyan("cd")} ${projectDirectory}`);
212
- console.info(` ${chalk.cyan("yarn install")}\n`);
213
- const { scripts } = JSON.parse(readFileSync(`${currentDirectory}/${projectDirectory}/package.json`, "utf8"));
214
- if (scripts) {
215
- console.info(` Available scripts:\n`);
216
- console.info(`${Object.keys(scripts)
217
- .map((s) => ` ${chalk.cyan(`yarn ${s}`)}`)
218
- .join("\n")}\n`);
219
- console.info(` See the README.md file to understand how to use them!\n`);
220
- }
221
- });
222
- program
223
- .command("get-groups")
224
- .description("Get the list of groups from a supported network (e.g. sepolia or arbitrum).")
225
- .option("-n, --network <network-name>", "Supported Ethereum network.")
226
- .allowExcessArguments(false)
227
- .action(async ({ network }) => {
228
- if (!network) {
229
- network = await getSupportedNetwork(supportedNetworks);
230
- }
231
- if (!supportedNetworks.includes(network)) {
232
- console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
233
- return;
234
- }
235
- const groupIds = await getGroupIds(network);
236
- if (groupIds === null) {
237
- console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
238
- return;
239
- }
240
- const content = `${chalk.bold("Groups")} (${groupIds.length}): \n${groupIds
241
- .map((id) => ` - ${id}`)
242
- .join("\n")}`;
243
- console.info(`\n${content}\n`);
244
- });
245
- program
246
- .command("get-group")
247
- .description("Get the data of a group from a supported network (e.g. sepolia or arbitrum).")
248
- .argument("[group-id]", "Identifier of the group.")
249
- .option("-n, --network <network-name>", "Supported Ethereum network.")
250
- .allowExcessArguments(false)
251
- .action(async (groupId, { network }) => {
252
- if (!network) {
253
- network = await getSupportedNetwork(supportedNetworks);
254
- }
255
- if (!supportedNetworks.includes(network)) {
256
- console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
257
- return;
258
- }
259
- if (!groupId) {
260
- const groupIds = await getGroupIds(network);
261
- if (groupIds === null) {
262
- console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
263
- return;
264
- }
265
- groupId = await getGroupId(groupIds);
266
- }
267
- let group;
268
- const spinner = new Spinner(`Fetching group ${groupId}`);
269
- spinner.start();
270
- try {
271
- const semaphoreSubgraph = new SemaphoreSubgraph(network);
272
- group = await semaphoreSubgraph.getGroup(groupId);
273
- spinner.stop();
274
- }
275
- catch {
276
- try {
277
- const semaphoreEthers = new SemaphoreEthers(network);
278
- group = await semaphoreEthers.getGroup(groupId);
279
- spinner.stop();
280
- }
281
- catch {
282
- spinner.stop();
283
- console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
284
- return;
285
- }
286
- }
287
- if (!group) {
288
- console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
289
- return;
290
- }
291
- let content = ` ${chalk.bold("Id")}: ${group.id}\n`;
292
- content += ` ${chalk.bold("Admin")}: ${group.admin}\n`;
293
- content += ` ${chalk.bold("Merkle tree")}:\n`;
294
- content += ` Root: ${group.merkleTree.root}\n`;
295
- content += ` Depth: ${group.merkleTree.depth}\n`;
296
- content += ` Size: ${group.merkleTree.size}`;
297
- console.info(`\n${content}\n`);
298
- });
299
- program
300
- .command("get-members")
301
- .description("Get the members of a group from a supported network (e.g. sepolia or arbitrum).")
302
- .argument("[group-id]", "Identifier of the group.")
303
- .option("-n, --network <network-name>", "Supported Ethereum network.")
304
- .allowExcessArguments(false)
305
- .action(async (groupId, { network }) => {
306
- if (!network) {
307
- network = await getSupportedNetwork(supportedNetworks);
308
- }
309
- if (!supportedNetworks.includes(network)) {
310
- console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
311
- return;
312
- }
313
- if (!groupId) {
314
- const groupIds = await getGroupIds(network);
315
- if (groupIds === null) {
316
- console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
317
- return;
318
- }
319
- groupId = await getGroupId(groupIds);
320
- }
321
- let groupMembers;
322
- const spinner = new Spinner(`Fetching members of group ${groupId}`);
323
- spinner.start();
324
- try {
325
- const semaphoreSubgraph = new SemaphoreSubgraph(network);
326
- const group = await semaphoreSubgraph.getGroup(groupId, { members: true });
327
- groupMembers = group.members;
328
- spinner.stop();
329
- }
330
- catch {
331
- try {
332
- const semaphoreEthers = new SemaphoreEthers(network);
333
- groupMembers = await semaphoreEthers.getGroupMembers(groupId);
334
- spinner.stop();
335
- }
336
- catch {
337
- spinner.stop();
338
- console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
339
- return;
340
- }
341
- }
342
- if (groupMembers.length === 0) {
343
- console.info(`\n ${logSymbols.info}`, "info: there are no members in this group\n");
344
- return;
345
- }
346
- const content = `${chalk.bold("Members")} (${groupMembers.length}): \n${groupMembers
347
- .map((member, i) => ` ${i}. ${member}`)
348
- .join("\n")}`;
349
- console.info(`\n${content}\n`);
350
- });
351
- program
352
- .command("get-proofs")
353
- .description("Get the proofs of a group from a supported network (e.g. sepolia or arbitrum).")
354
- .argument("[group-id]", "Identifier of the group.")
355
- .option("-n, --network <network-name>", "Supported Ethereum network.")
356
- .allowExcessArguments(false)
357
- .action(async (groupId, { network }) => {
358
- if (!network) {
359
- network = await getSupportedNetwork(supportedNetworks);
360
- }
361
- if (!supportedNetworks.includes(network)) {
362
- console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
363
- return;
364
- }
365
- if (!groupId) {
366
- const groupIds = await getGroupIds(network);
367
- if (groupIds === null) {
368
- console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
369
- return;
370
- }
371
- groupId = await getGroupId(groupIds);
372
- }
373
- let validatedProofs;
374
- const spinner = new Spinner(`Fetching proofs of group ${groupId}`);
375
- spinner.start();
376
- try {
377
- const semaphoreSubgraph = new SemaphoreSubgraph(network);
378
- const group = await semaphoreSubgraph.getGroup(groupId, { validatedProofs: true });
379
- validatedProofs = group.validatedProofs;
380
- spinner.stop();
381
- }
382
- catch {
383
- try {
384
- const semaphoreEthers = new SemaphoreEthers(network);
385
- validatedProofs = await semaphoreEthers.getGroupValidatedProofs(groupId);
386
- spinner.stop();
387
- }
388
- catch {
389
- spinner.stop();
390
- console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
391
- return;
392
- }
393
- }
394
- if (validatedProofs.length === 0) {
395
- console.info(`\n ${logSymbols.info}`, "info: there are no proofs in this group\n");
396
- return;
397
- }
398
- const content = `${chalk.bold("Proofs")} (${validatedProofs.length}): \n${validatedProofs
399
- .map(({ message, merkleTreeRoot, merkleTreeDepth, scope, nullifier, points }, i) => ` ${i}. message: ${message} \n merkleTreeRoot: ${merkleTreeRoot} \n merkleTreeDepth: ${merkleTreeDepth} \n scope: ${scope} \n nullifier: ${nullifier} \n points: [${points.join(", ")}]`)
400
- .join("\n")}`;
401
- console.info(`\n${content}\n`);
402
- });
149
+ const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`;
150
+ const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"));
151
+ const supportedNetworks = getSupportedNetworks();
152
+ const supportedTemplates = [
153
+ {
154
+ value: "monorepo-ethers",
155
+ name: "Hardhat + Next.js + SemaphoreEthers"
156
+ },
157
+ {
158
+ value: "monorepo-subgraph",
159
+ name: "Hardhat + Next.js + SemaphoreSubgraph"
160
+ },
161
+ {
162
+ value: "contracts-hardhat",
163
+ name: "Hardhat"
164
+ }
165
+ ];
166
+ program
167
+ .name("semaphore")
168
+ .description(description)
169
+ .version(version, "-v, --version", "Show Semaphore CLI version.")
170
+ .addHelpText("before", `${figlet.textSync("Semaphore")}\n`)
171
+ .addHelpText("after", "\r")
172
+ .helpOption(undefined, "Display this help.")
173
+ .addHelpCommand("help [command]", "Display help for a specific command.")
174
+ .configureOutput({
175
+ outputError: (message) => {
176
+ console.info(`\n ${logSymbols.error}`, message);
177
+ }
178
+ });
179
+ program
180
+ .command("create")
181
+ .description("Create a Semaphore project with a supported template.")
182
+ .argument("[project-directory]", "Directory of the project.")
183
+ .option("-t, --template <template-name>", "Supported Semaphore template.")
184
+ .allowExcessArguments(false)
185
+ .action(async (projectDirectory, { template }) => {
186
+ if (!projectDirectory) {
187
+ projectDirectory = await getProjectName();
188
+ }
189
+ if (!template) {
190
+ template = await getSupportedTemplate(supportedTemplates);
191
+ }
192
+ if (!supportedTemplates.some((t) => t.value === template)) {
193
+ console.info(`\n ${logSymbols.error}`, `error: the template '${template}' is not supported\n`);
194
+ return;
195
+ }
196
+ const currentDirectory = process.cwd();
197
+ const spinner = new Spinner(`Creating your project in ${chalk.green(`./${projectDirectory}`)}`);
198
+ if (existsSync(projectDirectory)) {
199
+ console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`);
200
+ return;
201
+ }
202
+ spinner.start();
203
+ await checkLatestVersion(version);
204
+ await pacote.extract(`@semaphore-protocol/cli-template-${template}@${version}`, `${currentDirectory}/${projectDirectory}`);
205
+ await decompress(`${currentDirectory}/${projectDirectory}/files.tgz`, `${currentDirectory}/${projectDirectory}`);
206
+ unlinkSync(`${currentDirectory}/${projectDirectory}/files.tgz`);
207
+ copyFileSync(`${currentDirectory}/${projectDirectory}/.env.example`, `${currentDirectory}/${projectDirectory}/.env`);
208
+ spinner.stop();
209
+ console.info(`\n ${logSymbols.success}`, `Your project is ready!\n`);
210
+ console.info(` Please, install your dependencies by running:\n`);
211
+ console.info(` ${chalk.cyan("cd")} ${projectDirectory}`);
212
+ console.info(` ${chalk.cyan("yarn install")}\n`);
213
+ const { scripts } = JSON.parse(readFileSync(`${currentDirectory}/${projectDirectory}/package.json`, "utf8"));
214
+ if (scripts) {
215
+ console.info(` Available scripts:\n`);
216
+ console.info(`${Object.keys(scripts)
217
+ .map((s) => ` ${chalk.cyan(`yarn ${s}`)}`)
218
+ .join("\n")}\n`);
219
+ console.info(` See the README.md file to understand how to use them!\n`);
220
+ }
221
+ });
222
+ program
223
+ .command("get-groups")
224
+ .description("Get the list of groups from a supported network (e.g. sepolia or arbitrum).")
225
+ .option("-n, --network <network-name>", "Supported Ethereum network.")
226
+ .allowExcessArguments(false)
227
+ .action(async ({ network }) => {
228
+ if (!network) {
229
+ network = await getSupportedNetwork(supportedNetworks);
230
+ }
231
+ if (!supportedNetworks.includes(network)) {
232
+ console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
233
+ return;
234
+ }
235
+ const groupIds = await getGroupIds(network);
236
+ if (groupIds === null) {
237
+ console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
238
+ return;
239
+ }
240
+ const content = `${chalk.bold("Groups")} (${groupIds.length}): \n${groupIds
241
+ .map((id) => ` - ${id}`)
242
+ .join("\n")}`;
243
+ console.info(`\n${content}\n`);
244
+ });
245
+ program
246
+ .command("get-group")
247
+ .description("Get the data of a group from a supported network (e.g. sepolia or arbitrum).")
248
+ .argument("[group-id]", "Identifier of the group.")
249
+ .option("-n, --network <network-name>", "Supported Ethereum network.")
250
+ .allowExcessArguments(false)
251
+ .action(async (groupId, { network }) => {
252
+ if (!network) {
253
+ network = await getSupportedNetwork(supportedNetworks);
254
+ }
255
+ if (!supportedNetworks.includes(network)) {
256
+ console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
257
+ return;
258
+ }
259
+ if (!groupId) {
260
+ const groupIds = await getGroupIds(network);
261
+ if (groupIds === null) {
262
+ console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
263
+ return;
264
+ }
265
+ groupId = await getGroupId(groupIds);
266
+ }
267
+ let group;
268
+ const spinner = new Spinner(`Fetching group ${groupId}`);
269
+ spinner.start();
270
+ try {
271
+ const semaphoreSubgraph = new SemaphoreSubgraph(network);
272
+ group = await semaphoreSubgraph.getGroup(groupId);
273
+ spinner.stop();
274
+ }
275
+ catch {
276
+ try {
277
+ const semaphoreEthers = new SemaphoreEthers(network);
278
+ group = await semaphoreEthers.getGroup(groupId);
279
+ spinner.stop();
280
+ }
281
+ catch {
282
+ spinner.stop();
283
+ console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
284
+ return;
285
+ }
286
+ }
287
+ if (!group) {
288
+ console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
289
+ return;
290
+ }
291
+ let content = ` ${chalk.bold("Id")}: ${group.id}\n`;
292
+ content += ` ${chalk.bold("Admin")}: ${group.admin}\n`;
293
+ content += ` ${chalk.bold("Merkle tree")}:\n`;
294
+ content += ` Root: ${group.merkleTree.root}\n`;
295
+ content += ` Depth: ${group.merkleTree.depth}\n`;
296
+ content += ` Size: ${group.merkleTree.size}`;
297
+ console.info(`\n${content}\n`);
298
+ });
299
+ program
300
+ .command("get-members")
301
+ .description("Get the members of a group from a supported network (e.g. sepolia or arbitrum).")
302
+ .argument("[group-id]", "Identifier of the group.")
303
+ .option("-n, --network <network-name>", "Supported Ethereum network.")
304
+ .allowExcessArguments(false)
305
+ .action(async (groupId, { network }) => {
306
+ if (!network) {
307
+ network = await getSupportedNetwork(supportedNetworks);
308
+ }
309
+ if (!supportedNetworks.includes(network)) {
310
+ console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
311
+ return;
312
+ }
313
+ if (!groupId) {
314
+ const groupIds = await getGroupIds(network);
315
+ if (groupIds === null) {
316
+ console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
317
+ return;
318
+ }
319
+ groupId = await getGroupId(groupIds);
320
+ }
321
+ let groupMembers;
322
+ const spinner = new Spinner(`Fetching members of group ${groupId}`);
323
+ spinner.start();
324
+ try {
325
+ const semaphoreSubgraph = new SemaphoreSubgraph(network);
326
+ const group = await semaphoreSubgraph.getGroup(groupId, { members: true });
327
+ groupMembers = group.members;
328
+ spinner.stop();
329
+ }
330
+ catch {
331
+ try {
332
+ const semaphoreEthers = new SemaphoreEthers(network);
333
+ groupMembers = await semaphoreEthers.getGroupMembers(groupId);
334
+ spinner.stop();
335
+ }
336
+ catch {
337
+ spinner.stop();
338
+ console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
339
+ return;
340
+ }
341
+ }
342
+ if (groupMembers.length === 0) {
343
+ console.info(`\n ${logSymbols.info}`, "info: there are no members in this group\n");
344
+ return;
345
+ }
346
+ const content = `${chalk.bold("Members")} (${groupMembers.length}): \n${groupMembers
347
+ .map((member, i) => ` ${i}. ${member}`)
348
+ .join("\n")}`;
349
+ console.info(`\n${content}\n`);
350
+ });
351
+ program
352
+ .command("get-proofs")
353
+ .description("Get the proofs of a group from a supported network (e.g. sepolia or arbitrum).")
354
+ .argument("[group-id]", "Identifier of the group.")
355
+ .option("-n, --network <network-name>", "Supported Ethereum network.")
356
+ .allowExcessArguments(false)
357
+ .action(async (groupId, { network }) => {
358
+ if (!network) {
359
+ network = await getSupportedNetwork(supportedNetworks);
360
+ }
361
+ if (!supportedNetworks.includes(network)) {
362
+ console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
363
+ return;
364
+ }
365
+ if (!groupId) {
366
+ const groupIds = await getGroupIds(network);
367
+ if (groupIds === null) {
368
+ console.info(`\n ${logSymbols.info}`, `info: there are no groups on the '${network}' network\n`);
369
+ return;
370
+ }
371
+ groupId = await getGroupId(groupIds);
372
+ }
373
+ let validatedProofs;
374
+ const spinner = new Spinner(`Fetching proofs of group ${groupId}`);
375
+ spinner.start();
376
+ try {
377
+ const semaphoreSubgraph = new SemaphoreSubgraph(network);
378
+ const group = await semaphoreSubgraph.getGroup(groupId, { validatedProofs: true });
379
+ validatedProofs = group.validatedProofs;
380
+ spinner.stop();
381
+ }
382
+ catch {
383
+ try {
384
+ const semaphoreEthers = new SemaphoreEthers(network);
385
+ validatedProofs = await semaphoreEthers.getGroupValidatedProofs(groupId);
386
+ spinner.stop();
387
+ }
388
+ catch {
389
+ spinner.stop();
390
+ console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
391
+ return;
392
+ }
393
+ }
394
+ if (validatedProofs.length === 0) {
395
+ console.info(`\n ${logSymbols.info}`, "info: there are no proofs in this group\n");
396
+ return;
397
+ }
398
+ const content = `${chalk.bold("Proofs")} (${validatedProofs.length}): \n${validatedProofs
399
+ .map(({ message, merkleTreeRoot, merkleTreeDepth, scope, nullifier, points }, i) => ` ${i}. message: ${message} \n merkleTreeRoot: ${merkleTreeRoot} \n merkleTreeDepth: ${merkleTreeDepth} \n scope: ${scope} \n nullifier: ${nullifier} \n points: [${points.join(", ")}]`)
400
+ .join("\n")}`;
401
+ console.info(`\n${content}\n`);
402
+ });
403
403
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@semaphore-protocol/cli",
3
3
  "type": "module",
4
- "version": "4.0.0-alpha.4",
4
+ "version": "4.0.0-alpha.5",
5
5
  "description": "A command line tool to set up your Semaphore project and get group data.",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "node": ">=14.16"
24
24
  },
25
25
  "scripts": {
26
- "start": "node --loader ts-node/esm --no-warnings src/index.ts",
26
+ "start": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' ./src/index.ts",
27
27
  "build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
28
28
  "prepublishOnly": "yarn build"
29
29
  },
@@ -37,11 +37,10 @@
37
37
  "@types/pacote": "^11.1.5",
38
38
  "@types/semver": "^7.3.13",
39
39
  "rollup-plugin-cleanup": "^3.2.1",
40
- "rollup-plugin-typescript2": "^0.31.2",
41
- "ts-node": "^10.9.1"
40
+ "rollup-plugin-typescript2": "^0.36.0"
42
41
  },
43
42
  "dependencies": {
44
- "@semaphore-protocol/data": "4.0.0-alpha.4",
43
+ "@semaphore-protocol/data": "4.0.0-alpha.5",
45
44
  "axios": "^1.3.2",
46
45
  "boxen": "^7.0.1",
47
46
  "chalk": "^5.1.2",