@semaphore-protocol/cli 3.5.0 → 3.6.0-beta.0
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.
- package/dist/index.js +211 -117
- package/package.json +7 -4
- package/src/getGroupIds.ts +42 -0
- package/src/index.ts +153 -131
- package/src/inquirerPrompts.ts +48 -0
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @module @semaphore-protocol/cli
|
|
5
|
-
* @version 3.
|
|
5
|
+
* @version 3.6.0-beta.0
|
|
6
6
|
* @file A command line tool to set up your Semaphore project and get group data.
|
|
7
7
|
* @copyright Ethereum Foundation 2022
|
|
8
8
|
* @license MIT
|
|
@@ -12,11 +12,10 @@
|
|
|
12
12
|
import { SemaphoreSubgraph, SemaphoreEthers } from '@semaphore-protocol/data';
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { program } from 'commander';
|
|
15
|
-
import download from 'download';
|
|
16
15
|
import figlet from 'figlet';
|
|
17
|
-
import { readFileSync, existsSync
|
|
18
|
-
import inquirer from 'inquirer';
|
|
16
|
+
import { readFileSync, existsSync } from 'fs';
|
|
19
17
|
import logSymbols from 'log-symbols';
|
|
18
|
+
import pacote from 'pacote';
|
|
20
19
|
import { dirname } from 'path';
|
|
21
20
|
import { fileURLToPath } from 'url';
|
|
22
21
|
import axios from 'axios';
|
|
@@ -24,6 +23,7 @@ import boxen from 'boxen';
|
|
|
24
23
|
import { execSync } from 'child_process';
|
|
25
24
|
import { lt } from 'semver';
|
|
26
25
|
import ora from 'ora';
|
|
26
|
+
import inquirer from 'inquirer';
|
|
27
27
|
|
|
28
28
|
const cliRegistryURL = "https://registry.npmjs.org/-/package/@semaphore-protocol/cli/dist-tags";
|
|
29
29
|
/**
|
|
@@ -70,9 +70,98 @@ class Spinner {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Gets all group ids on the specified network
|
|
75
|
+
* @param network The specified network
|
|
76
|
+
*/
|
|
77
|
+
async function getGroupIds(network) {
|
|
78
|
+
let groupIds;
|
|
79
|
+
const spinner = new Spinner("Fetching groups");
|
|
80
|
+
spinner.start();
|
|
81
|
+
try {
|
|
82
|
+
const semaphoreSubgraph = new SemaphoreSubgraph(network);
|
|
83
|
+
groupIds = await semaphoreSubgraph.getGroupIds();
|
|
84
|
+
spinner.stop();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
try {
|
|
88
|
+
const semaphoreEthers = new SemaphoreEthers(network);
|
|
89
|
+
groupIds = await semaphoreEthers.getGroupIds();
|
|
90
|
+
spinner.stop();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
spinner.stop();
|
|
94
|
+
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package");
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (groupIds.length === 0) {
|
|
99
|
+
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n");
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return groupIds;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function getProjectName() {
|
|
106
|
+
const { projectName } = await inquirer.prompt({
|
|
107
|
+
name: "projectName",
|
|
108
|
+
type: "input",
|
|
109
|
+
message: "What is your project name?",
|
|
110
|
+
default: "my-app"
|
|
111
|
+
});
|
|
112
|
+
return projectName;
|
|
113
|
+
}
|
|
114
|
+
async function getSupportedTemplate(supportedTemplates) {
|
|
115
|
+
const { selectedTemplate } = await inquirer.prompt({
|
|
116
|
+
name: "selectedTemplate",
|
|
117
|
+
type: "list",
|
|
118
|
+
message: "Select one of the supported templates:",
|
|
119
|
+
default: 0,
|
|
120
|
+
choices: supportedTemplates.map((template) => ({
|
|
121
|
+
value: template.value,
|
|
122
|
+
name: `${template.value} (${template.name})`
|
|
123
|
+
}))
|
|
124
|
+
});
|
|
125
|
+
return selectedTemplate;
|
|
126
|
+
}
|
|
127
|
+
async function getSupportedNetwork(supportedNetworks) {
|
|
128
|
+
const { selectedNetwork } = await inquirer.prompt({
|
|
129
|
+
name: "selectedNetwork",
|
|
130
|
+
type: "list",
|
|
131
|
+
message: "Select one of the supported networks:",
|
|
132
|
+
default: 0,
|
|
133
|
+
choices: supportedNetworks
|
|
134
|
+
});
|
|
135
|
+
return selectedNetwork;
|
|
136
|
+
}
|
|
137
|
+
async function getGroupId(groupIds) {
|
|
138
|
+
const { selectedGroupId } = await inquirer.prompt({
|
|
139
|
+
name: "selectedGroupId",
|
|
140
|
+
type: "list",
|
|
141
|
+
message: "Select one of the following existing group ids:",
|
|
142
|
+
default: 0,
|
|
143
|
+
choices: groupIds
|
|
144
|
+
});
|
|
145
|
+
return selectedGroupId;
|
|
146
|
+
}
|
|
147
|
+
|
|
73
148
|
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`;
|
|
74
149
|
const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"));
|
|
75
150
|
const supportedNetworks = ["sepolia", "goerli", "mumbai", "optimism-goerli", "arbitrum", "arbitrum-goerli"];
|
|
151
|
+
const supportedTemplates = [
|
|
152
|
+
{
|
|
153
|
+
value: "contracts-hardhat",
|
|
154
|
+
name: "Hardhat"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
value: "monorepo-ethers",
|
|
158
|
+
name: "Hardhat + Next.js + SemaphoreEthers"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
value: "monorepo-subgraph",
|
|
162
|
+
name: "Hardhat + Next.js + SemaphoreSubgraph"
|
|
163
|
+
}
|
|
164
|
+
];
|
|
76
165
|
program
|
|
77
166
|
.name("semaphore")
|
|
78
167
|
.description(description)
|
|
@@ -90,29 +179,28 @@ program
|
|
|
90
179
|
.command("create")
|
|
91
180
|
.description("Create a Semaphore project with a supported template.")
|
|
92
181
|
.argument("[project-directory]", "Directory of the project.")
|
|
93
|
-
|
|
182
|
+
.option("-t, --template <template-name>", "Supported Semaphore template.")
|
|
94
183
|
.allowExcessArguments(false)
|
|
95
|
-
.action(async (projectDirectory) => {
|
|
184
|
+
.action(async (projectDirectory, { template }) => {
|
|
96
185
|
if (!projectDirectory) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
186
|
+
projectDirectory = await getProjectName();
|
|
187
|
+
}
|
|
188
|
+
if (!template) {
|
|
189
|
+
template = await getSupportedTemplate(supportedTemplates);
|
|
190
|
+
}
|
|
191
|
+
if (!supportedTemplates.some((t) => t.value === template)) {
|
|
192
|
+
console.info(`\n ${logSymbols.error}`, `error: the template '${template}' is not supported\n`);
|
|
193
|
+
return;
|
|
104
194
|
}
|
|
105
195
|
const currentDirectory = process.cwd();
|
|
106
196
|
const spinner = new Spinner(`Creating your project in ${chalk.green(`./${projectDirectory}`)}`);
|
|
107
|
-
const templateURL = `https://registry.npmjs.org/@semaphore-protocol/cli-template-hardhat/-/cli-template-hardhat-${version}.tgz`;
|
|
108
197
|
if (existsSync(projectDirectory)) {
|
|
109
198
|
console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`);
|
|
110
199
|
return;
|
|
111
200
|
}
|
|
112
201
|
spinner.start();
|
|
113
202
|
await checkLatestVersion(version);
|
|
114
|
-
await
|
|
115
|
-
renameSync(`${currentDirectory}/package`, `${currentDirectory}/${projectDirectory}`);
|
|
203
|
+
await pacote.extract(`@semaphore-protocol/cli-template-${template}@${version}`, `${currentDirectory}/${projectDirectory}`);
|
|
116
204
|
spinner.stop();
|
|
117
205
|
console.info(`\n ${logSymbols.success}`, `Your project is ready!\n`);
|
|
118
206
|
console.info(` Please, install your dependencies by running:\n`);
|
|
@@ -134,137 +222,153 @@ program
|
|
|
134
222
|
.allowExcessArguments(false)
|
|
135
223
|
.action(async ({ network }) => {
|
|
136
224
|
if (!network) {
|
|
137
|
-
|
|
138
|
-
name: "selectedNetwork",
|
|
139
|
-
type: "list",
|
|
140
|
-
message: "Select one of our supported networks:",
|
|
141
|
-
default: supportedNetworks[0],
|
|
142
|
-
choices: supportedNetworks
|
|
143
|
-
});
|
|
144
|
-
network = selectedNetwork;
|
|
225
|
+
network = await getSupportedNetwork(supportedNetworks);
|
|
145
226
|
}
|
|
146
227
|
if (!supportedNetworks.includes(network)) {
|
|
147
228
|
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
|
|
148
229
|
return;
|
|
149
230
|
}
|
|
150
|
-
|
|
151
|
-
|
|
231
|
+
const groupIds = await getGroupIds(network);
|
|
232
|
+
if (groupIds === null)
|
|
233
|
+
return;
|
|
234
|
+
const content = `${chalk.bold("Groups")} (${groupIds.length}): \n${groupIds
|
|
235
|
+
.map((id) => ` - ${id}`)
|
|
236
|
+
.join("\n")}`;
|
|
237
|
+
console.info(`\n${content}\n`);
|
|
238
|
+
});
|
|
239
|
+
program
|
|
240
|
+
.command("get-group")
|
|
241
|
+
.description("Get the data of a group from a supported network (e.g. goerli or arbitrum).")
|
|
242
|
+
.argument("[group-id]", "Identifier of the group.")
|
|
243
|
+
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
|
244
|
+
.allowExcessArguments(false)
|
|
245
|
+
.action(async (groupId, { network }) => {
|
|
246
|
+
if (!network) {
|
|
247
|
+
network = await getSupportedNetwork(supportedNetworks);
|
|
248
|
+
}
|
|
249
|
+
if (!supportedNetworks.includes(network)) {
|
|
250
|
+
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (!groupId) {
|
|
254
|
+
const groupIds = await getGroupIds(network);
|
|
255
|
+
if (groupIds === null)
|
|
256
|
+
return;
|
|
257
|
+
groupId = await getGroupId(groupIds);
|
|
258
|
+
}
|
|
259
|
+
let group;
|
|
260
|
+
const spinner = new Spinner(`Fetching group ${groupId}`);
|
|
152
261
|
spinner.start();
|
|
153
262
|
try {
|
|
154
263
|
const semaphoreSubgraph = new SemaphoreSubgraph(network);
|
|
155
|
-
|
|
264
|
+
group = await semaphoreSubgraph.getGroup(groupId);
|
|
156
265
|
spinner.stop();
|
|
157
266
|
}
|
|
158
267
|
catch {
|
|
159
268
|
try {
|
|
160
269
|
const semaphoreEthers = new SemaphoreEthers(network);
|
|
161
|
-
|
|
270
|
+
group = await semaphoreEthers.getGroup(groupId);
|
|
271
|
+
group.admin = await semaphoreEthers.getGroupAdmin(groupId);
|
|
162
272
|
spinner.stop();
|
|
163
273
|
}
|
|
164
274
|
catch {
|
|
165
275
|
spinner.stop();
|
|
166
|
-
console.info(`\n ${logSymbols.error}`, "error:
|
|
276
|
+
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
|
|
167
277
|
return;
|
|
168
278
|
}
|
|
169
279
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
280
|
+
let content = ` ${chalk.bold("Id")}: ${group.id}\n`;
|
|
281
|
+
content += ` ${chalk.bold("Admin")}: ${group.admin}\n`;
|
|
282
|
+
content += ` ${chalk.bold("Merkle tree")}:\n`;
|
|
283
|
+
content += ` Root: ${group.merkleTree.root}\n`;
|
|
284
|
+
content += ` Depth: ${group.merkleTree.depth}\n`;
|
|
285
|
+
content += ` Zero value: ${group.merkleTree.zeroValue}\n`;
|
|
286
|
+
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`;
|
|
287
|
+
console.info(`\n${content}\n`);
|
|
176
288
|
});
|
|
177
289
|
program
|
|
178
|
-
.command("get-
|
|
179
|
-
.description("Get the
|
|
290
|
+
.command("get-members")
|
|
291
|
+
.description("Get the members of a group from a supported network (e.g. goerli or arbitrum).")
|
|
180
292
|
.argument("[group-id]", "Identifier of the group.")
|
|
181
293
|
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
|
182
|
-
.option("-m, --members", "Show group members.")
|
|
183
|
-
.option("-s, --signals", "Show group signals.")
|
|
184
294
|
.allowExcessArguments(false)
|
|
185
|
-
.action(async (groupId, { network
|
|
295
|
+
.action(async (groupId, { network }) => {
|
|
186
296
|
if (!network) {
|
|
187
|
-
|
|
188
|
-
name: "selectedNetwork",
|
|
189
|
-
type: "list",
|
|
190
|
-
message: "Select one of our supported networks:",
|
|
191
|
-
default: supportedNetworks[0],
|
|
192
|
-
choices: supportedNetworks
|
|
193
|
-
});
|
|
194
|
-
network = selectedNetwork;
|
|
297
|
+
network = await getSupportedNetwork(supportedNetworks);
|
|
195
298
|
}
|
|
196
299
|
if (!supportedNetworks.includes(network)) {
|
|
197
300
|
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
|
|
198
301
|
return;
|
|
199
302
|
}
|
|
200
303
|
if (!groupId) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
304
|
+
const groupIds = await getGroupIds(network);
|
|
305
|
+
if (groupIds === null)
|
|
306
|
+
return;
|
|
307
|
+
groupId = await getGroupId(groupIds);
|
|
308
|
+
}
|
|
309
|
+
let groupMembers;
|
|
310
|
+
const spinner = new Spinner(`Fetching members of group ${groupId}`);
|
|
311
|
+
spinner.start();
|
|
312
|
+
try {
|
|
313
|
+
const semaphoreSubgraph = new SemaphoreSubgraph(network);
|
|
314
|
+
const group = await semaphoreSubgraph.getGroup(groupId, { members: true });
|
|
315
|
+
groupMembers = group.members;
|
|
316
|
+
spinner.stop();
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
204
319
|
try {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
320
|
+
const semaphoreEthers = new SemaphoreEthers(network);
|
|
321
|
+
groupMembers = await semaphoreEthers.getGroupMembers(groupId);
|
|
322
|
+
spinner.stop();
|
|
208
323
|
}
|
|
209
324
|
catch {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
groupIds = await semaphoreEthersGroups.getGroupIds();
|
|
213
|
-
spinnerGroups.stop();
|
|
214
|
-
}
|
|
215
|
-
catch {
|
|
216
|
-
spinnerGroups.stop();
|
|
217
|
-
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package");
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
if (groupIds.length === 0) {
|
|
222
|
-
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n");
|
|
325
|
+
spinner.stop();
|
|
326
|
+
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n");
|
|
223
327
|
return;
|
|
224
328
|
}
|
|
225
|
-
const { selectedGroupId } = await inquirer.prompt({
|
|
226
|
-
name: "selectedGroupId",
|
|
227
|
-
type: "list",
|
|
228
|
-
message: "Select one of the following existing group ids:",
|
|
229
|
-
choices: groupIds
|
|
230
|
-
});
|
|
231
|
-
groupId = selectedGroupId;
|
|
232
|
-
}
|
|
233
|
-
if (!members && !signals) {
|
|
234
|
-
const { showMembers } = await inquirer.prompt({
|
|
235
|
-
name: "showMembers",
|
|
236
|
-
type: "confirm",
|
|
237
|
-
message: "Do you want to show members?",
|
|
238
|
-
default: false
|
|
239
|
-
});
|
|
240
|
-
members = showMembers;
|
|
241
|
-
const { showSignals } = await inquirer.prompt({
|
|
242
|
-
name: "showSignals",
|
|
243
|
-
type: "confirm",
|
|
244
|
-
message: "Do you want to show signals?",
|
|
245
|
-
default: false
|
|
246
|
-
});
|
|
247
|
-
signals = showSignals;
|
|
248
329
|
}
|
|
249
|
-
|
|
250
|
-
|
|
330
|
+
if (groupMembers.length === 0) {
|
|
331
|
+
console.info(`\n ${logSymbols.info}`, "info: there are no members in this group\n");
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const content = `${chalk.bold("Members")} (${groupMembers.length}): \n${groupMembers
|
|
335
|
+
.map((member, i) => ` ${i}. ${member}`)
|
|
336
|
+
.join("\n")}`;
|
|
337
|
+
console.info(`\n${content}\n`);
|
|
338
|
+
});
|
|
339
|
+
program
|
|
340
|
+
.command("get-proofs")
|
|
341
|
+
.description("Get the proofs from a supported network (e.g. goerli or arbitrum).")
|
|
342
|
+
.argument("[group-id]", "Identifier of the group.")
|
|
343
|
+
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
|
344
|
+
.allowExcessArguments(false)
|
|
345
|
+
.action(async (groupId, { network }) => {
|
|
346
|
+
if (!network) {
|
|
347
|
+
network = await getSupportedNetwork(supportedNetworks);
|
|
348
|
+
}
|
|
349
|
+
if (!supportedNetworks.includes(network)) {
|
|
350
|
+
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (!groupId) {
|
|
354
|
+
const groupIds = await getGroupIds(network);
|
|
355
|
+
if (groupIds === null)
|
|
356
|
+
return;
|
|
357
|
+
groupId = await getGroupId(groupIds);
|
|
358
|
+
}
|
|
359
|
+
let verifiedProofs;
|
|
360
|
+
const spinner = new Spinner(`Fetching proofs of group ${groupId}`);
|
|
251
361
|
spinner.start();
|
|
252
362
|
try {
|
|
253
363
|
const semaphoreSubgraph = new SemaphoreSubgraph(network);
|
|
254
|
-
group = await semaphoreSubgraph.getGroup(groupId, {
|
|
364
|
+
const group = await semaphoreSubgraph.getGroup(groupId, { verifiedProofs: true });
|
|
365
|
+
verifiedProofs = group.verifiedProofs;
|
|
255
366
|
spinner.stop();
|
|
256
367
|
}
|
|
257
368
|
catch {
|
|
258
369
|
try {
|
|
259
370
|
const semaphoreEthers = new SemaphoreEthers(network);
|
|
260
|
-
|
|
261
|
-
if (members) {
|
|
262
|
-
group.members = await semaphoreEthers.getGroupMembers(groupId);
|
|
263
|
-
}
|
|
264
|
-
if (signals) {
|
|
265
|
-
group.verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs(groupId);
|
|
266
|
-
}
|
|
267
|
-
group.admin = await semaphoreEthers.getGroupAdmin(groupId);
|
|
371
|
+
verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs(groupId);
|
|
268
372
|
spinner.stop();
|
|
269
373
|
}
|
|
270
374
|
catch {
|
|
@@ -273,23 +377,13 @@ program
|
|
|
273
377
|
return;
|
|
274
378
|
}
|
|
275
379
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
content += ` Root: ${group.merkleTree.root}\n`;
|
|
280
|
-
content += ` Depth: ${group.merkleTree.depth}\n`;
|
|
281
|
-
content += ` Zero value: ${group.merkleTree.zeroValue}\n`;
|
|
282
|
-
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`;
|
|
283
|
-
if (members) {
|
|
284
|
-
content += `\n\n ${chalk.bold("Members")}: \n${group.members
|
|
285
|
-
.map((member, i) => ` ${i}. ${member}`)
|
|
286
|
-
.join("\n")}`;
|
|
287
|
-
}
|
|
288
|
-
if (signals) {
|
|
289
|
-
content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
|
|
290
|
-
.map(({ signal }) => ` - ${signal}`)
|
|
291
|
-
.join("\n")}`;
|
|
380
|
+
if (verifiedProofs.length === 0) {
|
|
381
|
+
console.info(`\n ${logSymbols.info}`, "info: there are no proofs in this group\n");
|
|
382
|
+
return;
|
|
292
383
|
}
|
|
384
|
+
const content = `${chalk.bold("Proofs")} (${verifiedProofs.length}): \n${verifiedProofs
|
|
385
|
+
.map(({ signal, merkleTreeRoot, externalNullifier, nullifierHash }, i) => ` ${i}. signal: ${signal} \n merkleTreeRoot: ${merkleTreeRoot} \n externalNullifier: ${externalNullifier} \n nullifierHash: ${nullifierHash}`)
|
|
386
|
+
.join("\n")}`;
|
|
293
387
|
console.info(`\n${content}\n`);
|
|
294
388
|
});
|
|
295
389
|
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": "3.
|
|
4
|
+
"version": "3.6.0-beta.0",
|
|
5
5
|
"description": "A command line tool to set up your Semaphore project and get group data.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/clear": "^0.1.2",
|
|
36
36
|
"@types/figlet": "^1.5.5",
|
|
37
|
+
"@types/inquirer": "^9.0.3",
|
|
38
|
+
"@types/pacote": "^11.1.5",
|
|
37
39
|
"@types/semver": "^7.3.13",
|
|
38
40
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
39
41
|
"rollup-plugin-typescript2": "^0.31.2",
|
|
@@ -41,16 +43,17 @@
|
|
|
41
43
|
"typedoc": "^0.22.11"
|
|
42
44
|
},
|
|
43
45
|
"dependencies": {
|
|
44
|
-
"@semaphore-protocol/data": "3.
|
|
46
|
+
"@semaphore-protocol/data": "3.6.0-beta.0",
|
|
45
47
|
"axios": "^1.3.2",
|
|
46
48
|
"boxen": "^7.0.1",
|
|
47
49
|
"chalk": "^5.1.2",
|
|
48
50
|
"commander": "^9.4.1",
|
|
49
|
-
"download": "^8.0.0",
|
|
50
51
|
"figlet": "^1.5.2",
|
|
51
52
|
"inquirer": "^9.1.4",
|
|
52
53
|
"log-symbols": "^5.1.0",
|
|
53
54
|
"ora": "^6.1.2",
|
|
55
|
+
"pacote": "^15.1.1",
|
|
54
56
|
"semver": "^7.3.8"
|
|
55
|
-
}
|
|
57
|
+
},
|
|
58
|
+
"stableVersion": "3.5.0"
|
|
56
59
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SemaphoreSubgraph, SemaphoreEthers } from "@semaphore-protocol/data"
|
|
2
|
+
import logSymbols from "log-symbols"
|
|
3
|
+
import Spinner from "./spinner.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Gets all group ids on the specified network
|
|
7
|
+
* @param network The specified network
|
|
8
|
+
*/
|
|
9
|
+
export default async function getGroupIds(network): Promise<string[]> {
|
|
10
|
+
let groupIds: string[]
|
|
11
|
+
|
|
12
|
+
const spinner = new Spinner("Fetching groups")
|
|
13
|
+
|
|
14
|
+
spinner.start()
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const semaphoreSubgraph = new SemaphoreSubgraph(network)
|
|
18
|
+
|
|
19
|
+
groupIds = await semaphoreSubgraph.getGroupIds()
|
|
20
|
+
|
|
21
|
+
spinner.stop()
|
|
22
|
+
} catch {
|
|
23
|
+
try {
|
|
24
|
+
const semaphoreEthers = new SemaphoreEthers(network)
|
|
25
|
+
|
|
26
|
+
groupIds = await semaphoreEthers.getGroupIds()
|
|
27
|
+
|
|
28
|
+
spinner.stop()
|
|
29
|
+
} catch {
|
|
30
|
+
spinner.stop()
|
|
31
|
+
|
|
32
|
+
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package")
|
|
33
|
+
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (groupIds.length === 0) {
|
|
38
|
+
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
return groupIds
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GroupResponse, SemaphoreEthers, SemaphoreSubgraph } from "@semaphore-protocol/data"
|
|
2
2
|
import chalk from "chalk"
|
|
3
3
|
import { program } from "commander"
|
|
4
|
-
import download from "download"
|
|
5
4
|
import figlet from "figlet"
|
|
6
|
-
import { existsSync, readFileSync
|
|
7
|
-
import inquirer from "inquirer"
|
|
5
|
+
import { existsSync, readFileSync } from "fs"
|
|
8
6
|
import logSymbols from "log-symbols"
|
|
7
|
+
import pacote from "pacote"
|
|
9
8
|
import { dirname } from "path"
|
|
10
9
|
import { fileURLToPath } from "url"
|
|
11
10
|
import checkLatestVersion from "./checkLatestVersion.js"
|
|
11
|
+
import getGroupIds from "./getGroupIds.js"
|
|
12
|
+
import { getGroupId, getProjectName, getSupportedNetwork, getSupportedTemplate } from "./inquirerPrompts.js"
|
|
12
13
|
import Spinner from "./spinner.js"
|
|
13
14
|
|
|
14
15
|
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
|
|
@@ -16,6 +17,21 @@ const { description, version } = JSON.parse(readFileSync(`${packagePath}/package
|
|
|
16
17
|
|
|
17
18
|
const supportedNetworks = ["sepolia", "goerli", "mumbai", "optimism-goerli", "arbitrum", "arbitrum-goerli"]
|
|
18
19
|
|
|
20
|
+
const supportedTemplates = [
|
|
21
|
+
{
|
|
22
|
+
value: "contracts-hardhat",
|
|
23
|
+
name: "Hardhat"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: "monorepo-ethers",
|
|
27
|
+
name: "Hardhat + Next.js + SemaphoreEthers"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: "monorepo-subgraph",
|
|
31
|
+
name: "Hardhat + Next.js + SemaphoreSubgraph"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
|
|
19
35
|
program
|
|
20
36
|
.name("semaphore")
|
|
21
37
|
.description(description)
|
|
@@ -34,26 +50,27 @@ program
|
|
|
34
50
|
.command("create")
|
|
35
51
|
.description("Create a Semaphore project with a supported template.")
|
|
36
52
|
.argument("[project-directory]", "Directory of the project.")
|
|
37
|
-
|
|
53
|
+
.option("-t, --template <template-name>", "Supported Semaphore template.")
|
|
38
54
|
.allowExcessArguments(false)
|
|
39
|
-
.action(async (projectDirectory) => {
|
|
55
|
+
.action(async (projectDirectory, { template }) => {
|
|
40
56
|
if (!projectDirectory) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
projectDirectory = await getProjectName()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!template) {
|
|
61
|
+
template = await getSupportedTemplate(supportedTemplates)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!supportedTemplates.some((t) => t.value === template)) {
|
|
65
|
+
console.info(`\n ${logSymbols.error}`, `error: the template '${template}' is not supported\n`)
|
|
66
|
+
return
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
const currentDirectory = process.cwd()
|
|
51
70
|
const spinner = new Spinner(`Creating your project in ${chalk.green(`./${projectDirectory}`)}`)
|
|
52
|
-
const templateURL = `https://registry.npmjs.org/@semaphore-protocol/cli-template-hardhat/-/cli-template-hardhat-${version}.tgz`
|
|
53
71
|
|
|
54
72
|
if (existsSync(projectDirectory)) {
|
|
55
73
|
console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`)
|
|
56
|
-
|
|
57
74
|
return
|
|
58
75
|
}
|
|
59
76
|
|
|
@@ -61,9 +78,10 @@ program
|
|
|
61
78
|
|
|
62
79
|
await checkLatestVersion(version)
|
|
63
80
|
|
|
64
|
-
await
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
await pacote.extract(
|
|
82
|
+
`@semaphore-protocol/cli-template-${template}@${version}`,
|
|
83
|
+
`${currentDirectory}/${projectDirectory}`
|
|
84
|
+
)
|
|
67
85
|
|
|
68
86
|
spinner.stop()
|
|
69
87
|
|
|
@@ -94,209 +112,213 @@ program
|
|
|
94
112
|
.allowExcessArguments(false)
|
|
95
113
|
.action(async ({ network }) => {
|
|
96
114
|
if (!network) {
|
|
97
|
-
|
|
98
|
-
name: "selectedNetwork",
|
|
99
|
-
type: "list",
|
|
100
|
-
message: "Select one of our supported networks:",
|
|
101
|
-
default: supportedNetworks[0],
|
|
102
|
-
choices: supportedNetworks
|
|
103
|
-
})
|
|
104
|
-
network = selectedNetwork
|
|
115
|
+
network = await getSupportedNetwork(supportedNetworks)
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
if (!supportedNetworks.includes(network)) {
|
|
108
119
|
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const groupIds = await getGroupIds(network)
|
|
109
124
|
|
|
125
|
+
if (groupIds === null) return
|
|
126
|
+
|
|
127
|
+
const content = `${chalk.bold("Groups")} (${groupIds.length}): \n${groupIds
|
|
128
|
+
.map((id: any) => ` - ${id}`)
|
|
129
|
+
.join("\n")}`
|
|
130
|
+
|
|
131
|
+
console.info(`\n${content}\n`)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
program
|
|
135
|
+
.command("get-group")
|
|
136
|
+
.description("Get the data of a group from a supported network (e.g. goerli or arbitrum).")
|
|
137
|
+
.argument("[group-id]", "Identifier of the group.")
|
|
138
|
+
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
|
139
|
+
.allowExcessArguments(false)
|
|
140
|
+
.action(async (groupId, { network }) => {
|
|
141
|
+
if (!network) {
|
|
142
|
+
network = await getSupportedNetwork(supportedNetworks)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!supportedNetworks.includes(network)) {
|
|
146
|
+
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
|
|
110
147
|
return
|
|
111
148
|
}
|
|
112
149
|
|
|
113
|
-
|
|
150
|
+
if (!groupId) {
|
|
151
|
+
const groupIds = await getGroupIds(network)
|
|
152
|
+
|
|
153
|
+
if (groupIds === null) return
|
|
154
|
+
|
|
155
|
+
groupId = await getGroupId(groupIds)
|
|
156
|
+
}
|
|
114
157
|
|
|
115
|
-
|
|
158
|
+
let group: GroupResponse
|
|
159
|
+
|
|
160
|
+
const spinner = new Spinner(`Fetching group ${groupId}`)
|
|
116
161
|
|
|
117
162
|
spinner.start()
|
|
118
163
|
|
|
119
164
|
try {
|
|
120
165
|
const semaphoreSubgraph = new SemaphoreSubgraph(network)
|
|
121
166
|
|
|
122
|
-
|
|
167
|
+
group = await semaphoreSubgraph.getGroup(groupId)
|
|
123
168
|
|
|
124
169
|
spinner.stop()
|
|
125
170
|
} catch {
|
|
126
171
|
try {
|
|
127
172
|
const semaphoreEthers = new SemaphoreEthers(network)
|
|
128
173
|
|
|
129
|
-
|
|
174
|
+
group = await semaphoreEthers.getGroup(groupId)
|
|
175
|
+
|
|
176
|
+
group.admin = await semaphoreEthers.getGroupAdmin(groupId)
|
|
130
177
|
|
|
131
178
|
spinner.stop()
|
|
132
179
|
} catch {
|
|
133
180
|
spinner.stop()
|
|
134
|
-
|
|
135
|
-
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package")
|
|
136
|
-
|
|
181
|
+
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n")
|
|
137
182
|
return
|
|
138
183
|
}
|
|
139
184
|
}
|
|
140
|
-
if (groupIds.length === 0) {
|
|
141
|
-
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
|
|
142
185
|
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
186
|
+
let content = ` ${chalk.bold("Id")}: ${group.id}\n`
|
|
187
|
+
content += ` ${chalk.bold("Admin")}: ${group.admin}\n`
|
|
188
|
+
content += ` ${chalk.bold("Merkle tree")}:\n`
|
|
189
|
+
content += ` Root: ${group.merkleTree.root}\n`
|
|
190
|
+
content += ` Depth: ${group.merkleTree.depth}\n`
|
|
191
|
+
content += ` Zero value: ${group.merkleTree.zeroValue}\n`
|
|
192
|
+
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
|
|
147
193
|
|
|
148
|
-
console.info(
|
|
194
|
+
console.info(`\n${content}\n`)
|
|
149
195
|
})
|
|
150
196
|
|
|
151
197
|
program
|
|
152
|
-
.command("get-
|
|
153
|
-
.description("Get the
|
|
198
|
+
.command("get-members")
|
|
199
|
+
.description("Get the members of a group from a supported network (e.g. goerli or arbitrum).")
|
|
154
200
|
.argument("[group-id]", "Identifier of the group.")
|
|
155
201
|
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
|
156
|
-
.option("-m, --members", "Show group members.")
|
|
157
|
-
.option("-s, --signals", "Show group signals.")
|
|
158
202
|
.allowExcessArguments(false)
|
|
159
|
-
.action(async (groupId, { network
|
|
203
|
+
.action(async (groupId, { network }) => {
|
|
160
204
|
if (!network) {
|
|
161
|
-
|
|
162
|
-
name: "selectedNetwork",
|
|
163
|
-
type: "list",
|
|
164
|
-
message: "Select one of our supported networks:",
|
|
165
|
-
default: supportedNetworks[0],
|
|
166
|
-
choices: supportedNetworks
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
network = selectedNetwork
|
|
205
|
+
network = await getSupportedNetwork(supportedNetworks)
|
|
170
206
|
}
|
|
171
207
|
|
|
172
208
|
if (!supportedNetworks.includes(network)) {
|
|
173
209
|
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
|
|
174
|
-
|
|
175
210
|
return
|
|
176
211
|
}
|
|
177
212
|
|
|
178
213
|
if (!groupId) {
|
|
179
|
-
|
|
214
|
+
const groupIds = await getGroupIds(network)
|
|
180
215
|
|
|
181
|
-
|
|
216
|
+
if (groupIds === null) return
|
|
182
217
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
const semaphoreSubgraphGroups = new SemaphoreSubgraph(network)
|
|
218
|
+
groupId = await getGroupId(groupIds)
|
|
219
|
+
}
|
|
187
220
|
|
|
188
|
-
|
|
221
|
+
let groupMembers: string[]
|
|
189
222
|
|
|
190
|
-
|
|
191
|
-
} catch {
|
|
192
|
-
try {
|
|
193
|
-
const semaphoreEthersGroups = new SemaphoreEthers(network)
|
|
223
|
+
const spinner = new Spinner(`Fetching members of group ${groupId}`)
|
|
194
224
|
|
|
195
|
-
|
|
225
|
+
spinner.start()
|
|
196
226
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
spinnerGroups.stop()
|
|
227
|
+
try {
|
|
228
|
+
const semaphoreSubgraph = new SemaphoreSubgraph(network)
|
|
200
229
|
|
|
201
|
-
|
|
230
|
+
const group = await semaphoreSubgraph.getGroup(groupId, { members: true })
|
|
231
|
+
groupMembers = group.members
|
|
202
232
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
233
|
+
spinner.stop()
|
|
234
|
+
} catch {
|
|
235
|
+
try {
|
|
236
|
+
const semaphoreEthers = new SemaphoreEthers(network)
|
|
206
237
|
|
|
207
|
-
|
|
208
|
-
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
|
|
238
|
+
groupMembers = await semaphoreEthers.getGroupMembers(groupId)
|
|
209
239
|
|
|
240
|
+
spinner.stop()
|
|
241
|
+
} catch {
|
|
242
|
+
spinner.stop()
|
|
243
|
+
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n")
|
|
210
244
|
return
|
|
211
245
|
}
|
|
246
|
+
}
|
|
212
247
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
248
|
+
if (groupMembers.length === 0) {
|
|
249
|
+
console.info(`\n ${logSymbols.info}`, "info: there are no members in this group\n")
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const content = `${chalk.bold("Members")} (${groupMembers.length}): \n${groupMembers
|
|
254
|
+
.map((member: string, i: number) => ` ${i}. ${member}`)
|
|
255
|
+
.join("\n")}`
|
|
219
256
|
|
|
220
|
-
|
|
257
|
+
console.info(`\n${content}\n`)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
program
|
|
261
|
+
.command("get-proofs")
|
|
262
|
+
.description("Get the proofs from a supported network (e.g. goerli or arbitrum).")
|
|
263
|
+
.argument("[group-id]", "Identifier of the group.")
|
|
264
|
+
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
|
265
|
+
.allowExcessArguments(false)
|
|
266
|
+
.action(async (groupId, { network }) => {
|
|
267
|
+
if (!network) {
|
|
268
|
+
network = await getSupportedNetwork(supportedNetworks)
|
|
221
269
|
}
|
|
222
270
|
|
|
223
|
-
if (!
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
message: "Do you want to show members?",
|
|
228
|
-
default: false
|
|
229
|
-
})
|
|
271
|
+
if (!supportedNetworks.includes(network)) {
|
|
272
|
+
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
|
|
273
|
+
return
|
|
274
|
+
}
|
|
230
275
|
|
|
231
|
-
|
|
276
|
+
if (!groupId) {
|
|
277
|
+
const groupIds = await getGroupIds(network)
|
|
232
278
|
|
|
233
|
-
|
|
234
|
-
name: "showSignals",
|
|
235
|
-
type: "confirm",
|
|
236
|
-
message: "Do you want to show signals?",
|
|
237
|
-
default: false
|
|
238
|
-
})
|
|
279
|
+
if (groupIds === null) return
|
|
239
280
|
|
|
240
|
-
|
|
281
|
+
groupId = await getGroupId(groupIds)
|
|
241
282
|
}
|
|
242
283
|
|
|
243
|
-
let
|
|
284
|
+
let verifiedProofs: any[]
|
|
244
285
|
|
|
245
|
-
const spinner = new Spinner(`Fetching group ${groupId}`)
|
|
286
|
+
const spinner = new Spinner(`Fetching proofs of group ${groupId}`)
|
|
246
287
|
|
|
247
288
|
spinner.start()
|
|
248
289
|
|
|
249
290
|
try {
|
|
250
291
|
const semaphoreSubgraph = new SemaphoreSubgraph(network)
|
|
251
292
|
|
|
252
|
-
group = await semaphoreSubgraph.getGroup(groupId, {
|
|
293
|
+
const group = await semaphoreSubgraph.getGroup(groupId, { verifiedProofs: true })
|
|
294
|
+
verifiedProofs = group.verifiedProofs
|
|
253
295
|
|
|
254
296
|
spinner.stop()
|
|
255
297
|
} catch {
|
|
256
298
|
try {
|
|
257
299
|
const semaphoreEthers = new SemaphoreEthers(network)
|
|
258
300
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (members) {
|
|
262
|
-
group.members = await semaphoreEthers.getGroupMembers(groupId)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (signals) {
|
|
266
|
-
group.verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs(groupId)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
group.admin = await semaphoreEthers.getGroupAdmin(groupId)
|
|
301
|
+
verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs(groupId)
|
|
270
302
|
|
|
271
303
|
spinner.stop()
|
|
272
304
|
} catch {
|
|
273
305
|
spinner.stop()
|
|
274
|
-
|
|
275
306
|
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n")
|
|
276
|
-
|
|
277
307
|
return
|
|
278
308
|
}
|
|
279
309
|
}
|
|
280
310
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
content += ` Root: ${group.merkleTree.root}\n`
|
|
285
|
-
content += ` Depth: ${group.merkleTree.depth}\n`
|
|
286
|
-
content += ` Zero value: ${group.merkleTree.zeroValue}\n`
|
|
287
|
-
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
|
|
288
|
-
|
|
289
|
-
if (members) {
|
|
290
|
-
content += `\n\n ${chalk.bold("Members")}: \n${group.members
|
|
291
|
-
.map((member: string, i: number) => ` ${i}. ${member}`)
|
|
292
|
-
.join("\n")}`
|
|
311
|
+
if (verifiedProofs.length === 0) {
|
|
312
|
+
console.info(`\n ${logSymbols.info}`, "info: there are no proofs in this group\n")
|
|
313
|
+
return
|
|
293
314
|
}
|
|
294
315
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
316
|
+
const content = `${chalk.bold("Proofs")} (${verifiedProofs.length}): \n${verifiedProofs
|
|
317
|
+
.map(
|
|
318
|
+
({ signal, merkleTreeRoot, externalNullifier, nullifierHash }: any, i: number) =>
|
|
319
|
+
` ${i}. signal: ${signal} \n merkleTreeRoot: ${merkleTreeRoot} \n externalNullifier: ${externalNullifier} \n nullifierHash: ${nullifierHash}`
|
|
320
|
+
)
|
|
321
|
+
.join("\n")}`
|
|
300
322
|
|
|
301
323
|
console.info(`\n${content}\n`)
|
|
302
324
|
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import inquirer from "inquirer"
|
|
2
|
+
|
|
3
|
+
export async function getProjectName() {
|
|
4
|
+
const { projectName } = await inquirer.prompt({
|
|
5
|
+
name: "projectName",
|
|
6
|
+
type: "input",
|
|
7
|
+
message: "What is your project name?",
|
|
8
|
+
default: "my-app"
|
|
9
|
+
})
|
|
10
|
+
return projectName
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function getSupportedTemplate(supportedTemplates: { value: string; name: string }[]) {
|
|
14
|
+
const { selectedTemplate } = await inquirer.prompt({
|
|
15
|
+
name: "selectedTemplate",
|
|
16
|
+
type: "list",
|
|
17
|
+
message: "Select one of the supported templates:",
|
|
18
|
+
default: 0,
|
|
19
|
+
choices: supportedTemplates.map((template) => ({
|
|
20
|
+
value: template.value,
|
|
21
|
+
name: `${template.value} (${template.name})`
|
|
22
|
+
}))
|
|
23
|
+
})
|
|
24
|
+
return selectedTemplate
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function getSupportedNetwork(supportedNetworks: string[]) {
|
|
28
|
+
const { selectedNetwork } = await inquirer.prompt({
|
|
29
|
+
name: "selectedNetwork",
|
|
30
|
+
type: "list",
|
|
31
|
+
message: "Select one of the supported networks:",
|
|
32
|
+
default: 0,
|
|
33
|
+
choices: supportedNetworks
|
|
34
|
+
})
|
|
35
|
+
return selectedNetwork
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function getGroupId(groupIds: string[]) {
|
|
39
|
+
const { selectedGroupId } = await inquirer.prompt({
|
|
40
|
+
name: "selectedGroupId",
|
|
41
|
+
type: "list",
|
|
42
|
+
message: "Select one of the following existing group ids:",
|
|
43
|
+
default: 0,
|
|
44
|
+
choices: groupIds
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return selectedGroupId
|
|
48
|
+
}
|