midnight-wallet-cli 0.1.6 → 0.1.8
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/mcp-server.js +217 -2689
- package/dist/wallet.js +263 -3498
- package/package.json +2 -2
package/dist/wallet.js
CHANGED
|
@@ -1,988 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
subcommand: positionals[1],
|
|
50
|
-
positionals: positionals.slice(2),
|
|
51
|
-
flags
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function getFlag(args, name) {
|
|
55
|
-
const value = args.flags[name];
|
|
56
|
-
if (value === undefined || value === true)
|
|
57
|
-
return;
|
|
58
|
-
return value;
|
|
59
|
-
}
|
|
60
|
-
function hasFlag(args, name) {
|
|
61
|
-
return name in args.flags;
|
|
62
|
-
}
|
|
63
|
-
function requireFlag(args, name, description) {
|
|
64
|
-
const value = getFlag(args, name);
|
|
65
|
-
if (value === undefined) {
|
|
66
|
-
throw new Error(`Missing required flag: --${name} <${description}>`);
|
|
67
|
-
}
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// src/lib/constants.ts
|
|
72
|
-
var GENESIS_SEED = "0000000000000000000000000000000000000000000000000000000000000001", NATIVE_TOKEN_TYPE = "0000000000000000000000000000000000000000000000000000000000000000", TOKEN_DECIMALS = 6, TOKEN_MULTIPLIER = 1e6, DUST_COST_OVERHEAD = 1000000000000n, DUST_FEE_BLOCKS_MARGIN = 5, SYNC_TIMEOUT_MS = 300000, PRE_SEND_SYNC_TIMEOUT_MS = 1e4, DUST_TIMEOUT_MS = 120000, PROOF_TIMEOUT_MS = 300000, BALANCE_CHECK_TIMEOUT_MS = 60000, TX_TTL_MINUTES = 10, MAX_RETRY_ATTEMPTS = 3, STALE_UTXO_ERROR_CODE = 115, MIDNIGHT_DIR = ".midnight", DEFAULT_WALLET_FILENAME = "wallet.json", DEFAULT_CONFIG_FILENAME = "config.json", DIR_MODE = 448, FILE_MODE = 384, LOCALNET_DIR_NAME = "localnet";
|
|
73
|
-
|
|
74
|
-
// src/ui/colors.ts
|
|
75
|
-
function isColorEnabled() {
|
|
76
|
-
return !("NO_COLOR" in process.env);
|
|
77
|
-
}
|
|
78
|
-
function fg(text, code) {
|
|
79
|
-
if (!isColorEnabled())
|
|
80
|
-
return text;
|
|
81
|
-
return `${ESC}38;5;${code}m${text}${RESET}`;
|
|
82
|
-
}
|
|
83
|
-
function bold(text) {
|
|
84
|
-
if (!isColorEnabled())
|
|
85
|
-
return text;
|
|
86
|
-
return `${ESC}1m${text}${RESET}`;
|
|
87
|
-
}
|
|
88
|
-
function dim(text) {
|
|
89
|
-
if (!isColorEnabled())
|
|
90
|
-
return text;
|
|
91
|
-
return `${ESC}2m${text}${RESET}`;
|
|
92
|
-
}
|
|
93
|
-
function teal(text) {
|
|
94
|
-
return fg(text, TEAL);
|
|
95
|
-
}
|
|
96
|
-
function red(text) {
|
|
97
|
-
return fg(text, RED_CODE);
|
|
98
|
-
}
|
|
99
|
-
function green(text) {
|
|
100
|
-
return fg(text, GREEN_CODE);
|
|
101
|
-
}
|
|
102
|
-
function yellow(text) {
|
|
103
|
-
return fg(text, YELLOW_CODE);
|
|
104
|
-
}
|
|
105
|
-
function white(text) {
|
|
106
|
-
return fg(text, WHITE_CODE);
|
|
107
|
-
}
|
|
108
|
-
function gray(text) {
|
|
109
|
-
return fg(text, DIM_GRAY);
|
|
110
|
-
}
|
|
111
|
-
var ESC = "\x1B[", RESET = "\x1B[0m", TEAL = 38, WHITE_CODE = 15, DIM_GRAY = 245, RED_CODE = 196, GREEN_CODE = 40, YELLOW_CODE = 226;
|
|
112
|
-
|
|
113
|
-
// src/ui/format.ts
|
|
114
|
-
function header(title, width = DEFAULT_WIDTH) {
|
|
115
|
-
const paddedTitle = ` ${title} `;
|
|
116
|
-
const remaining = width - paddedTitle.length;
|
|
117
|
-
if (remaining <= 0)
|
|
118
|
-
return bold(paddedTitle);
|
|
119
|
-
const left = Math.floor(remaining / 2);
|
|
120
|
-
const right = remaining - left;
|
|
121
|
-
return bold("═".repeat(left) + paddedTitle + "═".repeat(right));
|
|
122
|
-
}
|
|
123
|
-
function divider(width = DEFAULT_WIDTH) {
|
|
124
|
-
return dim("─".repeat(width));
|
|
125
|
-
}
|
|
126
|
-
function keyValue(key, value, padWidth = 16) {
|
|
127
|
-
const paddedKey = (key + ":").padEnd(padWidth);
|
|
128
|
-
return ` ${gray(paddedKey)}${value}`;
|
|
129
|
-
}
|
|
130
|
-
function toNight(microNight) {
|
|
131
|
-
const isNegative = microNight < 0n;
|
|
132
|
-
const abs = isNegative ? -microNight : microNight;
|
|
133
|
-
const multiplier = BigInt(10 ** TOKEN_DECIMALS);
|
|
134
|
-
const whole = abs / multiplier;
|
|
135
|
-
const frac = abs % multiplier;
|
|
136
|
-
const fracStr = frac.toString().padStart(TOKEN_DECIMALS, "0");
|
|
137
|
-
const sign = isNegative ? "-" : "";
|
|
138
|
-
return `${sign}${whole}.${fracStr}`;
|
|
139
|
-
}
|
|
140
|
-
function formatNight(microNight) {
|
|
141
|
-
return `${toNight(microNight)} NIGHT`;
|
|
142
|
-
}
|
|
143
|
-
function toDust(specks) {
|
|
144
|
-
const isNegative = specks < 0n;
|
|
145
|
-
const abs = isNegative ? -specks : specks;
|
|
146
|
-
const multiplier = 10n ** BigInt(DUST_DECIMALS);
|
|
147
|
-
const whole = abs / multiplier;
|
|
148
|
-
const frac = abs % multiplier;
|
|
149
|
-
const fracStr = frac.toString().padStart(DUST_DECIMALS, "0");
|
|
150
|
-
const trimmed = fracStr.replace(/0+$/, "").padEnd(6, "0");
|
|
151
|
-
const sign = isNegative ? "-" : "";
|
|
152
|
-
return `${sign}${whole}.${trimmed}`;
|
|
153
|
-
}
|
|
154
|
-
function formatDust(specks) {
|
|
155
|
-
return `${toDust(specks)} DUST`;
|
|
156
|
-
}
|
|
157
|
-
function formatAddress(address, truncate = false) {
|
|
158
|
-
const display = truncate && address.length > 20 ? address.slice(0, 10) + "…" + address.slice(-8) : address;
|
|
159
|
-
return teal(display);
|
|
160
|
-
}
|
|
161
|
-
function wrapLine(line, maxWidth) {
|
|
162
|
-
const visible = stripAnsi(line);
|
|
163
|
-
if (visible.length <= maxWidth)
|
|
164
|
-
return [line];
|
|
165
|
-
const words = line.split(/(\s+)/);
|
|
166
|
-
const result = [];
|
|
167
|
-
let currentLine = "";
|
|
168
|
-
let currentLen = 0;
|
|
169
|
-
for (const word of words) {
|
|
170
|
-
const wordLen = stripAnsi(word).length;
|
|
171
|
-
if (currentLen + wordLen > maxWidth && currentLen > 0) {
|
|
172
|
-
result.push(currentLine);
|
|
173
|
-
currentLine = word.trimStart();
|
|
174
|
-
currentLen = stripAnsi(currentLine).length;
|
|
175
|
-
} else {
|
|
176
|
-
currentLine += word;
|
|
177
|
-
currentLen += wordLen;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (currentLine.length > 0)
|
|
181
|
-
result.push(currentLine);
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
function box(lines, style = "light", maxWidth = 70) {
|
|
185
|
-
const chars = style === "heavy" ? { tl: "╔", tr: "╗", bl: "╚", br: "╝", h: "═", v: "║" } : { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│" };
|
|
186
|
-
const contentMaxWidth = maxWidth - 4;
|
|
187
|
-
const expanded = [];
|
|
188
|
-
for (const line of lines) {
|
|
189
|
-
const subLines = line.split(`
|
|
190
|
-
`);
|
|
191
|
-
for (const sub of subLines) {
|
|
192
|
-
expanded.push(...wrapLine(sub, contentMaxWidth));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
const maxLen = Math.max(...expanded.map((l) => stripAnsi(l).length));
|
|
196
|
-
const innerWidth = Math.max(maxLen + 2, 20);
|
|
197
|
-
const top = chars.tl + chars.h.repeat(innerWidth) + chars.tr;
|
|
198
|
-
const bottom = chars.bl + chars.h.repeat(innerWidth) + chars.br;
|
|
199
|
-
const body = expanded.map((line) => {
|
|
200
|
-
const visibleLen = stripAnsi(line).length;
|
|
201
|
-
const padding = innerWidth - visibleLen - 2;
|
|
202
|
-
return `${chars.v} ${line}${" ".repeat(Math.max(0, padding))} ${chars.v}`;
|
|
203
|
-
});
|
|
204
|
-
return [top, ...body, bottom].join(`
|
|
205
|
-
`);
|
|
206
|
-
}
|
|
207
|
-
function errorBox(error, suggestion) {
|
|
208
|
-
const errorLines = error.split(`
|
|
209
|
-
`);
|
|
210
|
-
const lines = errorLines.map((line, i) => i === 0 ? red(bold("Error: ")) + red(line) : red(line));
|
|
211
|
-
if (suggestion) {
|
|
212
|
-
lines.push("");
|
|
213
|
-
lines.push(dim("Suggestion: ") + suggestion);
|
|
214
|
-
}
|
|
215
|
-
const output = box(lines, "heavy");
|
|
216
|
-
if (isColorEnabled()) {
|
|
217
|
-
return output.replace(/[╔╗╚╝═║]/g, (match) => red(match));
|
|
218
|
-
}
|
|
219
|
-
return output;
|
|
220
|
-
}
|
|
221
|
-
function successMessage(msg, txHash) {
|
|
222
|
-
const check = green("✓");
|
|
223
|
-
const parts = [`${check} ${msg}`];
|
|
224
|
-
if (txHash) {
|
|
225
|
-
parts.push(keyValue("Transaction", teal(txHash)));
|
|
226
|
-
}
|
|
227
|
-
return parts.join(`
|
|
228
|
-
`);
|
|
229
|
-
}
|
|
230
|
-
var DEFAULT_WIDTH = 60, DUST_DECIMALS = 15, stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
231
|
-
var init_format = () => {
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// src/lib/exit-codes.ts
|
|
235
|
-
function classifyError(err) {
|
|
236
|
-
const msg = (err.message ?? "").toLowerCase();
|
|
237
|
-
if (msg.includes("operation cancelled") || msg.includes("operation aborted") || msg === "cancelled" || msg === "aborted") {
|
|
238
|
-
return { exitCode: EXIT_CANCELLED, errorCode: ERROR_CODES.CANCELLED };
|
|
239
|
-
}
|
|
240
|
-
if (msg.includes("wallet file not found") || msg.includes("wallet") && msg.includes("not found")) {
|
|
241
|
-
return { exitCode: EXIT_WALLET_NOT_FOUND, errorCode: ERROR_CODES.WALLET_NOT_FOUND };
|
|
242
|
-
}
|
|
243
|
-
if (msg.includes("missing required flag") || msg.includes("missing amount") || msg.includes("missing recipient") || msg.includes("missing config key") || msg.includes("missing or invalid subcommand") || msg.includes("unknown command") || msg.includes("cannot specify both") || msg.includes("invalid bip-39") || msg.includes("seed must be") || msg.includes("key index must be") || msg.includes("usage:")) {
|
|
244
|
-
return { exitCode: EXIT_INVALID_ARGS, errorCode: ERROR_CODES.INVALID_ARGS };
|
|
245
|
-
}
|
|
246
|
-
if (msg.includes("proof") && msg.includes("timeout")) {
|
|
247
|
-
return { exitCode: EXIT_TX_REJECTED, errorCode: ERROR_CODES.PROOF_TIMEOUT };
|
|
248
|
-
}
|
|
249
|
-
if (msg.includes("stale utxo") || msg.includes("error code 115") || msg.includes("errorcode: 115")) {
|
|
250
|
-
return { exitCode: EXIT_TX_REJECTED, errorCode: ERROR_CODES.STALE_UTXO };
|
|
251
|
-
}
|
|
252
|
-
if (msg.includes("no dust") || msg.includes("dust") && (msg.includes("required") || msg.includes("available") || msg.includes("insufficient"))) {
|
|
253
|
-
return { exitCode: EXIT_INSUFFICIENT_BALANCE, errorCode: ERROR_CODES.DUST_REQUIRED };
|
|
254
|
-
}
|
|
255
|
-
if (msg.includes("insufficient") || msg.includes("not enough")) {
|
|
256
|
-
return { exitCode: EXIT_INSUFFICIENT_BALANCE, errorCode: ERROR_CODES.INSUFFICIENT_BALANCE };
|
|
257
|
-
}
|
|
258
|
-
if (msg.includes("rejected") || msg.includes("transaction failed")) {
|
|
259
|
-
return { exitCode: EXIT_TX_REJECTED, errorCode: ERROR_CODES.TX_REJECTED };
|
|
260
|
-
}
|
|
261
|
-
if (msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("etimedout") || msg.includes("websocket") || msg.includes("connection refused") || msg.includes("network") && msg.includes("error")) {
|
|
262
|
-
return { exitCode: EXIT_NETWORK_ERROR, errorCode: ERROR_CODES.NETWORK_ERROR };
|
|
263
|
-
}
|
|
264
|
-
return { exitCode: EXIT_GENERAL_ERROR, errorCode: ERROR_CODES.UNKNOWN };
|
|
265
|
-
}
|
|
266
|
-
var EXIT_GENERAL_ERROR = 1, EXIT_INVALID_ARGS = 2, EXIT_WALLET_NOT_FOUND = 3, EXIT_NETWORK_ERROR = 4, EXIT_INSUFFICIENT_BALANCE = 5, EXIT_TX_REJECTED = 6, EXIT_CANCELLED = 7, ERROR_CODES;
|
|
267
|
-
var init_exit_codes = __esm(() => {
|
|
268
|
-
ERROR_CODES = {
|
|
269
|
-
INVALID_ARGS: "INVALID_ARGS",
|
|
270
|
-
WALLET_NOT_FOUND: "WALLET_NOT_FOUND",
|
|
271
|
-
NETWORK_ERROR: "NETWORK_ERROR",
|
|
272
|
-
INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
|
|
273
|
-
TX_REJECTED: "TX_REJECTED",
|
|
274
|
-
STALE_UTXO: "STALE_UTXO",
|
|
275
|
-
PROOF_TIMEOUT: "PROOF_TIMEOUT",
|
|
276
|
-
DUST_REQUIRED: "DUST_REQUIRED",
|
|
277
|
-
CANCELLED: "CANCELLED",
|
|
278
|
-
UNKNOWN: "UNKNOWN"
|
|
279
|
-
};
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// src/lib/json-output.ts
|
|
283
|
-
function suppressStderr() {
|
|
284
|
-
const original = process.stderr.write;
|
|
285
|
-
process.stderr.write = () => true;
|
|
286
|
-
return () => {
|
|
287
|
-
process.stderr.write = original;
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
function setCaptureTarget(fn) {
|
|
291
|
-
captureTarget = fn;
|
|
292
|
-
}
|
|
293
|
-
function writeJsonResult(data) {
|
|
294
|
-
const json = JSON.stringify(data) + `
|
|
295
|
-
`;
|
|
296
|
-
if (captureTarget) {
|
|
297
|
-
captureTarget(json);
|
|
298
|
-
} else {
|
|
299
|
-
process.stdout.write(json);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
function writeJsonError(err, errorCode, exitCode) {
|
|
303
|
-
process.stdout.write(JSON.stringify({
|
|
304
|
-
error: true,
|
|
305
|
-
code: errorCode,
|
|
306
|
-
message: err.message,
|
|
307
|
-
exitCode
|
|
308
|
-
}) + `
|
|
309
|
-
`);
|
|
310
|
-
}
|
|
311
|
-
var captureTarget = null;
|
|
312
|
-
|
|
313
|
-
// package.json
|
|
314
|
-
var package_default;
|
|
315
|
-
var init_package = __esm(() => {
|
|
316
|
-
package_default = {
|
|
317
|
-
name: "midnight-wallet-cli",
|
|
318
|
-
version: "0.1.6",
|
|
319
|
-
type: "module",
|
|
320
|
-
description: "Git-style CLI wallet for the Midnight blockchain",
|
|
321
|
-
license: "Apache-2.0",
|
|
322
|
-
bin: {
|
|
323
|
-
midnight: "dist/wallet.js",
|
|
324
|
-
mn: "dist/wallet.js",
|
|
325
|
-
"midnight-wallet-cli": "dist/wallet.js",
|
|
326
|
-
"midnight-wallet-mcp": "dist/mcp-server.js"
|
|
327
|
-
},
|
|
328
|
-
files: [
|
|
329
|
-
"dist"
|
|
330
|
-
],
|
|
331
|
-
scripts: {
|
|
332
|
-
wallet: "tsx src/wallet.ts",
|
|
333
|
-
build: 'bun build src/wallet.ts --outfile dist/wallet.js --target node --format esm --packages external --banner "#!/usr/bin/env node" && bun build src/mcp-server.ts --outfile dist/mcp-server.js --target node --format esm --packages external --banner "#!/usr/bin/env node"',
|
|
334
|
-
mcp: "tsx src/mcp-server.ts",
|
|
335
|
-
prepublishOnly: "npm run build && npm run test",
|
|
336
|
-
test: "vitest run",
|
|
337
|
-
"test:watch": "vitest",
|
|
338
|
-
typecheck: "tsc --noEmit"
|
|
339
|
-
},
|
|
340
|
-
dependencies: {
|
|
341
|
-
"@midnight-ntwrk/ledger-v7": "7.0.0",
|
|
342
|
-
"@midnight-ntwrk/midnight-js-network-id": "3.0.0",
|
|
343
|
-
"@midnight-ntwrk/midnight-js-types": "3.0.0",
|
|
344
|
-
"@midnight-ntwrk/wallet-sdk-abstractions": "1.0.0",
|
|
345
|
-
"@midnight-ntwrk/wallet-sdk-address-format": "3.0.0",
|
|
346
|
-
"@midnight-ntwrk/wallet-sdk-dust-wallet": "1.0.0",
|
|
347
|
-
"@midnight-ntwrk/wallet-sdk-facade": "1.0.0",
|
|
348
|
-
"@midnight-ntwrk/wallet-sdk-hd": "3.0.0",
|
|
349
|
-
"@midnight-ntwrk/wallet-sdk-shielded": "1.0.0",
|
|
350
|
-
"@midnight-ntwrk/wallet-sdk-unshielded-wallet": "1.0.0",
|
|
351
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
352
|
-
"@scure/bip39": "^2.0.1",
|
|
353
|
-
rxjs: "^7.8.1",
|
|
354
|
-
ws: "^8.19.0"
|
|
355
|
-
},
|
|
356
|
-
devDependencies: {
|
|
357
|
-
"@types/node": "^22.19.13",
|
|
358
|
-
"@types/ws": "^8.18.1",
|
|
359
|
-
tsx: "^4.21.0",
|
|
360
|
-
typescript: "^5.9.3",
|
|
361
|
-
vitest: "^3.2.4"
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// src/lib/pkg.ts
|
|
367
|
-
var PKG_NAME, PKG_VERSION, PKG_DESCRIPTION;
|
|
368
|
-
var init_pkg = __esm(() => {
|
|
369
|
-
init_package();
|
|
370
|
-
PKG_NAME = package_default.name;
|
|
371
|
-
PKG_VERSION = package_default.version;
|
|
372
|
-
PKG_DESCRIPTION = package_default.description;
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// src/lib/run-command.ts
|
|
376
|
-
async function captureCommand(handler, args, signal) {
|
|
377
|
-
const chunks = [];
|
|
378
|
-
setCaptureTarget((json) => chunks.push(json));
|
|
379
|
-
const originalStderr = process.stderr.write;
|
|
380
|
-
process.stderr.write = () => true;
|
|
381
|
-
try {
|
|
382
|
-
args.flags.json = true;
|
|
383
|
-
await handler(args, signal);
|
|
384
|
-
const raw = chunks.join("").trim();
|
|
385
|
-
if (!raw)
|
|
386
|
-
return {};
|
|
387
|
-
return JSON.parse(raw);
|
|
388
|
-
} finally {
|
|
389
|
-
setCaptureTarget(null);
|
|
390
|
-
process.stderr.write = originalStderr;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
var init_run_command = () => {
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// src/lib/derive-address.ts
|
|
397
|
-
import { HDWallet, Roles } from "@midnight-ntwrk/wallet-sdk-hd";
|
|
398
|
-
import { createKeystore, PublicKey } from "@midnight-ntwrk/wallet-sdk-unshielded-wallet";
|
|
399
|
-
import { NetworkId } from "@midnight-ntwrk/wallet-sdk-abstractions";
|
|
400
|
-
function deriveUnshieldedAddress(seedBuffer, networkName, keyIndex = 0) {
|
|
401
|
-
const networkId = NETWORK_ID_MAP[networkName];
|
|
402
|
-
const hdResult = HDWallet.fromSeed(seedBuffer);
|
|
403
|
-
if (hdResult.type !== "seedOk") {
|
|
404
|
-
throw new Error("Invalid seed for HD wallet");
|
|
405
|
-
}
|
|
406
|
-
const derivation = hdResult.hdWallet.selectAccount(0).selectRole(Roles.NightExternal).deriveKeyAt(keyIndex);
|
|
407
|
-
if (derivation.type === "keyOutOfBounds") {
|
|
408
|
-
throw new Error(`Key index ${keyIndex} out of bounds`);
|
|
409
|
-
}
|
|
410
|
-
const keystore = createKeystore(derivation.key, networkId);
|
|
411
|
-
const publicKey = PublicKey.fromKeyStore(keystore);
|
|
412
|
-
return publicKey.address;
|
|
413
|
-
}
|
|
414
|
-
var NETWORK_ID_MAP;
|
|
415
|
-
var init_derive_address = __esm(() => {
|
|
416
|
-
NETWORK_ID_MAP = {
|
|
417
|
-
preprod: NetworkId.NetworkId.PreProd,
|
|
418
|
-
preview: NetworkId.NetworkId.Preview,
|
|
419
|
-
undeployed: NetworkId.NetworkId.Undeployed
|
|
420
|
-
};
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// src/lib/network.ts
|
|
424
|
-
import { execSync } from "child_process";
|
|
425
|
-
function isValidNetworkName(name) {
|
|
426
|
-
return VALID_NETWORK_NAMES.includes(name);
|
|
427
|
-
}
|
|
428
|
-
function getNetworkConfig(name) {
|
|
429
|
-
return { ...NETWORK_CONFIGS[name] };
|
|
430
|
-
}
|
|
431
|
-
function getValidNetworkNames() {
|
|
432
|
-
return VALID_NETWORK_NAMES;
|
|
433
|
-
}
|
|
434
|
-
function detectNetworkFromAddress(address) {
|
|
435
|
-
if (address.startsWith("mn_addr_preprod1"))
|
|
436
|
-
return "preprod";
|
|
437
|
-
if (address.startsWith("mn_addr_preview1"))
|
|
438
|
-
return "preview";
|
|
439
|
-
if (address.startsWith("mn_addr_undeployed1"))
|
|
440
|
-
return "undeployed";
|
|
441
|
-
return null;
|
|
442
|
-
}
|
|
443
|
-
function detectTestcontainerPorts() {
|
|
444
|
-
try {
|
|
445
|
-
const output = execSync('docker ps --format "{{.Image}}|{{.Ports}}"', { encoding: "utf-8", timeout: 5000 });
|
|
446
|
-
const result = {};
|
|
447
|
-
for (const line of output.trim().split(`
|
|
448
|
-
`)) {
|
|
449
|
-
if (!line)
|
|
450
|
-
continue;
|
|
451
|
-
const [image, ports] = line.split("|");
|
|
452
|
-
const extractHostPort = (containerPort) => {
|
|
453
|
-
const regex = new RegExp(`0\\.0\\.0\\.0:(\\d+)->${containerPort}/tcp`);
|
|
454
|
-
const match = ports?.match(regex);
|
|
455
|
-
return match ? parseInt(match[1], 10) : undefined;
|
|
456
|
-
};
|
|
457
|
-
if (image.includes("indexer-standalone") || image.includes("indexer")) {
|
|
458
|
-
const port = extractHostPort(8088);
|
|
459
|
-
if (port)
|
|
460
|
-
result.indexerPort = port;
|
|
461
|
-
}
|
|
462
|
-
if (image.includes("midnight-node")) {
|
|
463
|
-
const port = extractHostPort(9944);
|
|
464
|
-
if (port)
|
|
465
|
-
result.nodePort = port;
|
|
466
|
-
}
|
|
467
|
-
if (image.includes("proof-server")) {
|
|
468
|
-
const port = extractHostPort(6300);
|
|
469
|
-
if (port)
|
|
470
|
-
result.proofServerPort = port;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
return result;
|
|
474
|
-
} catch {
|
|
475
|
-
return {};
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
function resolveNetworkConfig(name) {
|
|
479
|
-
const config = getNetworkConfig(name);
|
|
480
|
-
if (name === "undeployed") {
|
|
481
|
-
const detected = detectTestcontainerPorts();
|
|
482
|
-
if (detected.indexerPort) {
|
|
483
|
-
config.indexer = `http://localhost:${detected.indexerPort}/api/v3/graphql`;
|
|
484
|
-
config.indexerWS = `ws://localhost:${detected.indexerPort}/api/v3/graphql/ws`;
|
|
485
|
-
}
|
|
486
|
-
if (detected.nodePort) {
|
|
487
|
-
config.node = `ws://localhost:${detected.nodePort}`;
|
|
488
|
-
}
|
|
489
|
-
if (detected.proofServerPort) {
|
|
490
|
-
config.proofServer = `http://localhost:${detected.proofServerPort}`;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return config;
|
|
494
|
-
}
|
|
495
|
-
var NETWORK_CONFIGS, VALID_NETWORK_NAMES;
|
|
496
|
-
var init_network = __esm(() => {
|
|
497
|
-
NETWORK_CONFIGS = {
|
|
498
|
-
preprod: {
|
|
499
|
-
indexer: "https://indexer.preprod.midnight.network/api/v3/graphql",
|
|
500
|
-
indexerWS: "wss://indexer.preprod.midnight.network/api/v3/graphql/ws",
|
|
501
|
-
node: "wss://rpc.preprod.midnight.network",
|
|
502
|
-
proofServer: "http://localhost:6300",
|
|
503
|
-
networkId: "PreProd"
|
|
504
|
-
},
|
|
505
|
-
preview: {
|
|
506
|
-
indexer: "https://indexer.preview.midnight.network/api/v3/graphql",
|
|
507
|
-
indexerWS: "wss://indexer.preview.midnight.network/api/v3/graphql/ws",
|
|
508
|
-
node: "wss://rpc.preview.midnight.network",
|
|
509
|
-
proofServer: "http://localhost:6300",
|
|
510
|
-
networkId: "Preview"
|
|
511
|
-
},
|
|
512
|
-
undeployed: {
|
|
513
|
-
indexer: "http://localhost:8088/api/v3/graphql",
|
|
514
|
-
indexerWS: "ws://localhost:8088/api/v3/graphql/ws",
|
|
515
|
-
node: "ws://localhost:9944",
|
|
516
|
-
proofServer: "http://localhost:6300",
|
|
517
|
-
networkId: "Undeployed"
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
VALID_NETWORK_NAMES = ["preprod", "preview", "undeployed"];
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
// src/lib/cli-config.ts
|
|
524
|
-
import * as fs from "fs";
|
|
525
|
-
import * as path from "path";
|
|
526
|
-
import { homedir } from "os";
|
|
527
|
-
function getConfigDir(configDir) {
|
|
528
|
-
return configDir ?? path.join(homedir(), MIDNIGHT_DIR);
|
|
529
|
-
}
|
|
530
|
-
function getConfigPath(configDir) {
|
|
531
|
-
return path.join(getConfigDir(configDir), DEFAULT_CONFIG_FILENAME);
|
|
532
|
-
}
|
|
533
|
-
function ensureConfigDir(configDir) {
|
|
534
|
-
const dir = getConfigDir(configDir);
|
|
535
|
-
if (!fs.existsSync(dir)) {
|
|
536
|
-
fs.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
function loadCliConfig(configDir) {
|
|
540
|
-
const configPath = getConfigPath(configDir);
|
|
541
|
-
if (!fs.existsSync(configPath)) {
|
|
542
|
-
return { ...DEFAULT_CLI_CONFIG };
|
|
543
|
-
}
|
|
544
|
-
let content;
|
|
545
|
-
try {
|
|
546
|
-
content = fs.readFileSync(configPath, "utf-8");
|
|
547
|
-
} catch {
|
|
548
|
-
return { ...DEFAULT_CLI_CONFIG };
|
|
549
|
-
}
|
|
550
|
-
let parsed;
|
|
551
|
-
try {
|
|
552
|
-
parsed = JSON.parse(content);
|
|
553
|
-
} catch {
|
|
554
|
-
return { ...DEFAULT_CLI_CONFIG };
|
|
555
|
-
}
|
|
556
|
-
return {
|
|
557
|
-
network: parsed.network && isValidNetworkName(parsed.network) ? parsed.network : DEFAULT_CLI_CONFIG.network
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
function saveCliConfig(config, configDir) {
|
|
561
|
-
ensureConfigDir(configDir);
|
|
562
|
-
const configPath = getConfigPath(configDir);
|
|
563
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + `
|
|
564
|
-
`, { mode: FILE_MODE });
|
|
565
|
-
}
|
|
566
|
-
function getConfigValue(key, configDir) {
|
|
567
|
-
const config = loadCliConfig(configDir);
|
|
568
|
-
if (key === "network")
|
|
569
|
-
return config.network;
|
|
570
|
-
throw new Error(`Unknown config key: "${key}"
|
|
571
|
-
Valid keys: ${VALID_CONFIG_KEYS.join(", ")}`);
|
|
572
|
-
}
|
|
573
|
-
function setConfigValue(key, value, configDir) {
|
|
574
|
-
const config = loadCliConfig(configDir);
|
|
575
|
-
if (key === "network") {
|
|
576
|
-
if (!isValidNetworkName(value)) {
|
|
577
|
-
throw new Error(`Invalid network: "${value}"
|
|
578
|
-
Valid networks: preprod, preview, undeployed`);
|
|
579
|
-
}
|
|
580
|
-
config.network = value;
|
|
581
|
-
} else {
|
|
582
|
-
throw new Error(`Unknown config key: "${key}"
|
|
583
|
-
Valid keys: ${VALID_CONFIG_KEYS.join(", ")}`);
|
|
584
|
-
}
|
|
585
|
-
saveCliConfig(config, configDir);
|
|
586
|
-
}
|
|
587
|
-
function getValidConfigKeys() {
|
|
588
|
-
return VALID_CONFIG_KEYS;
|
|
589
|
-
}
|
|
590
|
-
var DEFAULT_CLI_CONFIG, VALID_CONFIG_KEYS;
|
|
591
|
-
var init_cli_config = __esm(() => {
|
|
592
|
-
init_network();
|
|
593
|
-
DEFAULT_CLI_CONFIG = {
|
|
594
|
-
network: "undeployed"
|
|
595
|
-
};
|
|
596
|
-
VALID_CONFIG_KEYS = ["network"];
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
// src/lib/resolve-network.ts
|
|
600
|
-
function resolveNetworkName(ctx) {
|
|
601
|
-
const flagValue = getFlag(ctx.args, "network");
|
|
602
|
-
if (flagValue !== undefined) {
|
|
603
|
-
if (!isValidNetworkName(flagValue)) {
|
|
604
|
-
throw new Error(`Invalid network: "${flagValue}"
|
|
605
|
-
` + `Valid networks: ${getValidNetworkNames().join(", ")}`);
|
|
606
|
-
}
|
|
607
|
-
return flagValue;
|
|
608
|
-
}
|
|
609
|
-
if (ctx.walletNetwork && isValidNetworkName(ctx.walletNetwork)) {
|
|
610
|
-
return ctx.walletNetwork;
|
|
611
|
-
}
|
|
612
|
-
if (ctx.address) {
|
|
613
|
-
const detected = detectNetworkFromAddress(ctx.address);
|
|
614
|
-
if (detected)
|
|
615
|
-
return detected;
|
|
616
|
-
}
|
|
617
|
-
const cliConfig = loadCliConfig(ctx.configDir);
|
|
618
|
-
if (cliConfig.network && isValidNetworkName(cliConfig.network)) {
|
|
619
|
-
return cliConfig.network;
|
|
620
|
-
}
|
|
621
|
-
return "undeployed";
|
|
622
|
-
}
|
|
623
|
-
function resolveNetwork(ctx) {
|
|
624
|
-
const name = resolveNetworkName(ctx);
|
|
625
|
-
const config = resolveNetworkConfig(name);
|
|
626
|
-
return { name, config };
|
|
627
|
-
}
|
|
628
|
-
var init_resolve_network = __esm(() => {
|
|
629
|
-
init_network();
|
|
630
|
-
init_cli_config();
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
// src/lib/wallet-config.ts
|
|
634
|
-
import * as fs2 from "fs";
|
|
635
|
-
import * as path2 from "path";
|
|
636
|
-
import { homedir as homedir2 } from "os";
|
|
637
|
-
function getMidnightDir() {
|
|
638
|
-
return path2.join(homedir2(), MIDNIGHT_DIR);
|
|
639
|
-
}
|
|
640
|
-
function getDefaultWalletPath() {
|
|
641
|
-
return path2.join(getMidnightDir(), DEFAULT_WALLET_FILENAME);
|
|
642
|
-
}
|
|
643
|
-
function ensureMidnightDir() {
|
|
644
|
-
const dir = getMidnightDir();
|
|
645
|
-
if (!fs2.existsSync(dir)) {
|
|
646
|
-
fs2.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
function loadWalletConfig(walletPath) {
|
|
650
|
-
const resolvedPath = walletPath ? path2.resolve(walletPath) : getDefaultWalletPath();
|
|
651
|
-
if (!fs2.existsSync(resolvedPath)) {
|
|
652
|
-
throw new Error(`Wallet file not found: ${resolvedPath}
|
|
653
|
-
` + `Generate a wallet first: midnight generate --network <name>`);
|
|
654
|
-
}
|
|
655
|
-
let content;
|
|
656
|
-
try {
|
|
657
|
-
content = fs2.readFileSync(resolvedPath, "utf-8");
|
|
658
|
-
} catch (err) {
|
|
659
|
-
throw new Error(`Failed to read wallet file: ${resolvedPath}
|
|
660
|
-
${err.message}`);
|
|
661
|
-
}
|
|
662
|
-
let config;
|
|
663
|
-
try {
|
|
664
|
-
config = JSON.parse(content);
|
|
665
|
-
} catch {
|
|
666
|
-
throw new Error(`Invalid JSON in wallet file: ${resolvedPath}`);
|
|
667
|
-
}
|
|
668
|
-
if (!config.seed || !config.network || !config.address || !config.createdAt) {
|
|
669
|
-
const missing = ["seed", "network", "address", "createdAt"].filter((f) => !config[f]);
|
|
670
|
-
throw new Error(`Wallet file is missing required fields (${missing.join(", ")}): ${resolvedPath}`);
|
|
671
|
-
}
|
|
672
|
-
if (!/^[0-9a-fA-F]+$/.test(config.seed)) {
|
|
673
|
-
throw new Error(`Invalid seed format in wallet file (expected hex string): ${resolvedPath}`);
|
|
674
|
-
}
|
|
675
|
-
if (!isValidNetworkName(config.network)) {
|
|
676
|
-
throw new Error(`Invalid network "${config.network}" in wallet file: ${resolvedPath}
|
|
677
|
-
` + `Valid networks: preprod, preview, undeployed`);
|
|
678
|
-
}
|
|
679
|
-
return config;
|
|
680
|
-
}
|
|
681
|
-
function saveWalletConfig(config, walletPath) {
|
|
682
|
-
const resolvedPath = walletPath ? path2.resolve(walletPath) : getDefaultWalletPath();
|
|
683
|
-
if (!walletPath) {
|
|
684
|
-
ensureMidnightDir();
|
|
685
|
-
} else {
|
|
686
|
-
const dir = path2.dirname(resolvedPath);
|
|
687
|
-
if (!fs2.existsSync(dir)) {
|
|
688
|
-
fs2.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
fs2.writeFileSync(resolvedPath, JSON.stringify(config, null, 2) + `
|
|
692
|
-
`, { mode: FILE_MODE });
|
|
693
|
-
return resolvedPath;
|
|
694
|
-
}
|
|
695
|
-
var init_wallet_config = __esm(() => {
|
|
696
|
-
init_network();
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
// src/commands/generate.ts
|
|
700
|
-
var exports_generate = {};
|
|
701
|
-
__export(exports_generate, {
|
|
702
|
-
default: () => generateCommand
|
|
703
|
-
});
|
|
704
|
-
import * as fs3 from "fs";
|
|
705
|
-
import * as path3 from "path";
|
|
706
|
-
import { homedir as homedir3 } from "os";
|
|
707
|
-
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
|
|
708
|
-
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
709
|
-
async function generateCommand(args) {
|
|
710
|
-
const networkName = resolveNetworkName({ args });
|
|
711
|
-
const outputPath = getFlag(args, "output");
|
|
712
|
-
const seedHex = getFlag(args, "seed");
|
|
713
|
-
const mnemonicStr = getFlag(args, "mnemonic");
|
|
714
|
-
if (seedHex !== undefined && mnemonicStr !== undefined) {
|
|
715
|
-
throw new Error("Cannot specify both --seed and --mnemonic. Use one or the other.");
|
|
716
|
-
}
|
|
717
|
-
const targetPath = outputPath ? path3.resolve(outputPath) : path3.join(homedir3(), MIDNIGHT_DIR, DEFAULT_WALLET_FILENAME);
|
|
718
|
-
if (fs3.existsSync(targetPath) && !hasFlag(args, "force")) {
|
|
719
|
-
throw new Error(`Wallet file already exists: ${targetPath}
|
|
720
|
-
` + `Use --force to overwrite, or --output <file> to save to a different path.`);
|
|
721
|
-
}
|
|
722
|
-
let seedBuffer;
|
|
723
|
-
let mnemonic;
|
|
724
|
-
if (seedHex !== undefined) {
|
|
725
|
-
const cleaned = seedHex.replace(/^0x/, "");
|
|
726
|
-
if (cleaned.length !== 64 || !/^[0-9a-fA-F]+$/.test(cleaned)) {
|
|
727
|
-
throw new Error("Seed must be a 64-character hex string (32 bytes)");
|
|
728
|
-
}
|
|
729
|
-
seedBuffer = Buffer.from(cleaned, "hex");
|
|
730
|
-
} else if (mnemonicStr !== undefined) {
|
|
731
|
-
if (!validateMnemonic(mnemonicStr, wordlist)) {
|
|
732
|
-
throw new Error("Invalid BIP-39 mnemonic. Expected 12 or 24 words from the English wordlist.");
|
|
733
|
-
}
|
|
734
|
-
mnemonic = mnemonicStr;
|
|
735
|
-
seedBuffer = Buffer.from(mnemonicToSeedSync(mnemonic).slice(0, 32));
|
|
736
|
-
} else {
|
|
737
|
-
mnemonic = generateMnemonic(wordlist, 256);
|
|
738
|
-
seedBuffer = Buffer.from(mnemonicToSeedSync(mnemonic).slice(0, 32));
|
|
739
|
-
}
|
|
740
|
-
const address = deriveUnshieldedAddress(seedBuffer, networkName);
|
|
741
|
-
const config = {
|
|
742
|
-
seed: seedBuffer.toString("hex"),
|
|
743
|
-
network: networkName,
|
|
744
|
-
address,
|
|
745
|
-
createdAt: new Date().toISOString()
|
|
746
|
-
};
|
|
747
|
-
if (mnemonic) {
|
|
748
|
-
config.mnemonic = mnemonic;
|
|
749
|
-
}
|
|
750
|
-
const savedPath = saveWalletConfig(config, outputPath);
|
|
751
|
-
if (hasFlag(args, "json")) {
|
|
752
|
-
const result = {
|
|
753
|
-
address,
|
|
754
|
-
network: networkName,
|
|
755
|
-
seed: seedBuffer.toString("hex"),
|
|
756
|
-
file: savedPath,
|
|
757
|
-
createdAt: config.createdAt
|
|
758
|
-
};
|
|
759
|
-
if (mnemonic)
|
|
760
|
-
result.mnemonic = mnemonic;
|
|
761
|
-
writeJsonResult(result);
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
process.stdout.write(address + `
|
|
765
|
-
`);
|
|
766
|
-
process.stderr.write(`
|
|
767
|
-
` + header("Wallet Generated") + `
|
|
768
|
-
|
|
769
|
-
`);
|
|
770
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
771
|
-
`);
|
|
772
|
-
process.stderr.write(keyValue("Address", formatAddress(address)) + `
|
|
773
|
-
`);
|
|
774
|
-
process.stderr.write(keyValue("File", savedPath) + `
|
|
775
|
-
`);
|
|
776
|
-
process.stderr.write(`
|
|
777
|
-
`);
|
|
778
|
-
if (mnemonic) {
|
|
779
|
-
process.stderr.write(yellow(bold(" MNEMONIC (save securely!):")) + `
|
|
780
|
-
`);
|
|
781
|
-
process.stderr.write(` ${mnemonic}
|
|
782
|
-
|
|
783
|
-
`);
|
|
784
|
-
}
|
|
785
|
-
process.stderr.write(yellow(bold(" SEED (hex):")) + `
|
|
786
|
-
`);
|
|
787
|
-
process.stderr.write(` ${seedBuffer.toString("hex")}
|
|
788
|
-
|
|
789
|
-
`);
|
|
790
|
-
process.stderr.write(divider() + `
|
|
791
|
-
`);
|
|
792
|
-
process.stderr.write(dim(" Next: midnight info | midnight balance") + `
|
|
793
|
-
|
|
794
|
-
`);
|
|
795
|
-
process.stderr.write(green("✓") + ` Wallet saved
|
|
796
|
-
`);
|
|
797
|
-
}
|
|
798
|
-
var init_generate = __esm(() => {
|
|
799
|
-
init_derive_address();
|
|
800
|
-
init_resolve_network();
|
|
801
|
-
init_wallet_config();
|
|
802
|
-
init_format();
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
// src/commands/info.ts
|
|
806
|
-
var exports_info = {};
|
|
807
|
-
__export(exports_info, {
|
|
808
|
-
default: () => infoCommand
|
|
809
|
-
});
|
|
810
|
-
import * as path4 from "path";
|
|
811
|
-
import { homedir as homedir4 } from "os";
|
|
812
|
-
async function infoCommand(args) {
|
|
813
|
-
const walletPath = getFlag(args, "wallet");
|
|
814
|
-
const config = loadWalletConfig(walletPath);
|
|
815
|
-
const resolvedPath = walletPath ? path4.resolve(walletPath) : path4.join(homedir4(), MIDNIGHT_DIR, DEFAULT_WALLET_FILENAME);
|
|
816
|
-
if (hasFlag(args, "json")) {
|
|
817
|
-
writeJsonResult({
|
|
818
|
-
address: config.address,
|
|
819
|
-
network: config.network,
|
|
820
|
-
createdAt: config.createdAt,
|
|
821
|
-
file: resolvedPath
|
|
822
|
-
});
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
process.stdout.write(config.address + `
|
|
826
|
-
`);
|
|
827
|
-
process.stderr.write(`
|
|
828
|
-
` + header("Wallet Info") + `
|
|
829
|
-
|
|
830
|
-
`);
|
|
831
|
-
process.stderr.write(keyValue("Address", formatAddress(config.address)) + `
|
|
832
|
-
`);
|
|
833
|
-
process.stderr.write(keyValue("Network", config.network) + `
|
|
834
|
-
`);
|
|
835
|
-
process.stderr.write(keyValue("Created", config.createdAt) + `
|
|
836
|
-
`);
|
|
837
|
-
process.stderr.write(keyValue("File", resolvedPath) + `
|
|
838
|
-
`);
|
|
839
|
-
process.stderr.write(`
|
|
840
|
-
` + divider() + `
|
|
841
|
-
|
|
842
|
-
`);
|
|
843
|
-
}
|
|
844
|
-
var init_info = __esm(() => {
|
|
845
|
-
init_wallet_config();
|
|
846
|
-
init_format();
|
|
847
|
-
init_format();
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
// src/lib/balance-subscription.ts
|
|
851
|
-
import WebSocket from "ws";
|
|
852
|
-
function checkBalance(address, indexerWS, onProgress) {
|
|
853
|
-
return new Promise((resolve4, reject) => {
|
|
854
|
-
const ws = new WebSocket(indexerWS, ["graphql-transport-ws"]);
|
|
855
|
-
const utxos = new Map;
|
|
856
|
-
let txCount = 0;
|
|
857
|
-
let highestTxId = 0;
|
|
858
|
-
let lastSeenTxId = 0;
|
|
859
|
-
let progressReceived = false;
|
|
860
|
-
let settled = false;
|
|
861
|
-
let timeoutId;
|
|
862
|
-
const buildResult = () => {
|
|
863
|
-
const balances = new Map;
|
|
864
|
-
let utxoCount = 0;
|
|
865
|
-
for (const utxo of utxos.values()) {
|
|
866
|
-
if (!utxo.spent) {
|
|
867
|
-
utxoCount++;
|
|
868
|
-
const current = balances.get(utxo.tokenType) ?? 0n;
|
|
869
|
-
balances.set(utxo.tokenType, current + utxo.value);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
return { balances, utxoCount, txCount, highestTxId };
|
|
873
|
-
};
|
|
874
|
-
const settle = () => {
|
|
875
|
-
clearTimeout(timeoutId);
|
|
876
|
-
};
|
|
877
|
-
const checkComplete = () => {
|
|
878
|
-
if (!settled && progressReceived && (highestTxId === 0 || lastSeenTxId >= highestTxId)) {
|
|
879
|
-
settled = true;
|
|
880
|
-
settle();
|
|
881
|
-
ws.send(JSON.stringify({ id: "1", type: "complete" }));
|
|
882
|
-
ws.close();
|
|
883
|
-
resolve4(buildResult());
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
ws.on("open", () => {
|
|
887
|
-
ws.send(JSON.stringify({ type: "connection_init" }));
|
|
888
|
-
});
|
|
889
|
-
ws.on("message", (data) => {
|
|
890
|
-
const message = JSON.parse(data.toString());
|
|
891
|
-
switch (message.type) {
|
|
892
|
-
case "connection_ack":
|
|
893
|
-
ws.send(JSON.stringify({
|
|
894
|
-
id: "1",
|
|
895
|
-
type: "subscribe",
|
|
896
|
-
payload: {
|
|
897
|
-
query: SUBSCRIPTION_QUERY,
|
|
898
|
-
variables: { address }
|
|
899
|
-
}
|
|
900
|
-
}));
|
|
901
|
-
break;
|
|
902
|
-
case "next": {
|
|
903
|
-
if (message.payload?.errors) {
|
|
904
|
-
const errMsg = message.payload.errors[0]?.message || "Unknown GraphQL error";
|
|
905
|
-
if (!settled) {
|
|
906
|
-
settled = true;
|
|
907
|
-
settle();
|
|
908
|
-
ws.close();
|
|
909
|
-
reject(new Error(`GraphQL error: ${errMsg}`));
|
|
910
|
-
}
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
const event = message.payload?.data?.unshieldedTransactions;
|
|
914
|
-
if (!event)
|
|
915
|
-
return;
|
|
916
|
-
if (event.__typename === "UnshieldedTransaction") {
|
|
917
|
-
txCount++;
|
|
918
|
-
const tx = event;
|
|
919
|
-
lastSeenTxId = Math.max(lastSeenTxId, tx.transaction.id);
|
|
920
|
-
for (const utxo of tx.createdUtxos) {
|
|
921
|
-
const key = `${utxo.intentHash}:${utxo.outputIndex}`;
|
|
922
|
-
utxos.set(key, {
|
|
923
|
-
value: BigInt(utxo.value),
|
|
924
|
-
tokenType: utxo.tokenType,
|
|
925
|
-
spent: false
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
for (const utxo of tx.spentUtxos) {
|
|
929
|
-
const key = `${utxo.intentHash}:${utxo.outputIndex}`;
|
|
930
|
-
const existing = utxos.get(key);
|
|
931
|
-
if (existing) {
|
|
932
|
-
existing.spent = true;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
if (onProgress) {
|
|
936
|
-
onProgress(lastSeenTxId, highestTxId);
|
|
937
|
-
}
|
|
938
|
-
checkComplete();
|
|
939
|
-
} else if (event.__typename === "UnshieldedTransactionsProgress") {
|
|
940
|
-
const progress = event;
|
|
941
|
-
highestTxId = progress.highestTransactionId;
|
|
942
|
-
progressReceived = true;
|
|
943
|
-
checkComplete();
|
|
944
|
-
}
|
|
945
|
-
break;
|
|
946
|
-
}
|
|
947
|
-
case "error":
|
|
948
|
-
if (!settled) {
|
|
949
|
-
settled = true;
|
|
950
|
-
settle();
|
|
951
|
-
ws.close();
|
|
952
|
-
reject(new Error(`GraphQL subscription error: ${JSON.stringify(message.payload)}`));
|
|
953
|
-
}
|
|
954
|
-
break;
|
|
955
|
-
case "complete":
|
|
956
|
-
break;
|
|
957
|
-
}
|
|
958
|
-
});
|
|
959
|
-
ws.on("error", (error) => {
|
|
960
|
-
if (!settled) {
|
|
961
|
-
settled = true;
|
|
962
|
-
settle();
|
|
963
|
-
reject(new Error(`WebSocket connection failed: ${error.message}`));
|
|
964
|
-
}
|
|
965
|
-
});
|
|
966
|
-
ws.on("close", () => {
|
|
967
|
-
if (!settled) {
|
|
968
|
-
settled = true;
|
|
969
|
-
settle();
|
|
970
|
-
reject(new Error(`Indexer closed the connection before balance sync completed. ` + `Indexer: ${indexerWS}`));
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
|
-
timeoutId = setTimeout(() => {
|
|
974
|
-
if (!settled) {
|
|
975
|
-
settled = true;
|
|
976
|
-
ws.close();
|
|
977
|
-
reject(new Error(`Balance check timed out after ${BALANCE_CHECK_TIMEOUT_MS / 1000}s. ` + `Indexer: ${indexerWS}`));
|
|
978
|
-
}
|
|
979
|
-
}, BALANCE_CHECK_TIMEOUT_MS);
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
function isNativeToken(tokenType) {
|
|
983
|
-
return tokenType === NATIVE_TOKEN_TYPE;
|
|
984
|
-
}
|
|
985
|
-
var SUBSCRIPTION_QUERY = `
|
|
2
|
+
var P2=Object.defineProperty;var w=($,Z)=>{for(var Q in Z)P2($,Q,{get:Z[Q],enumerable:!0,configurable:!0,set:(X)=>Z[Q]=()=>X})};var O=($,Z)=>()=>($&&(Z=$($=0)),Z);function o1($){let Z=$??process.argv.slice(2),Q=[],X={},q=0;while(q<Z.length){let z=Z[q];if(z.startsWith("--")){let Y=z.slice(2),K=Z[q+1];if(K!==void 0&&!K.startsWith("-"))X[Y]=K,q+=2;else X[Y]=!0,q+=1}else if(z.startsWith("-")&&z.length===2){let Y=z.slice(1),K=Z[q+1];if(K!==void 0&&!K.startsWith("-"))X[Y]=K,q+=2;else X[Y]=!0,q+=1}else Q.push(z),q+=1}return{command:Q[0],subcommand:Q[1],positionals:Q.slice(2),flags:X}}function A($,Z){let Q=$.flags[Z];if(Q===void 0||Q===!0)return;return Q}function P($,Z){return Z in $.flags}function n1($,Z,Q){let X=A($,Z);if(X===void 0)throw new Error(`Missing required flag: --${Z} <${Q}>`);return X}var _0="0000000000000000000000000000000000000000000000000000000000000001",a1="0000000000000000000000000000000000000000000000000000000000000000",r0=6,t1=1e6,r1=1000000000000n,s1=5,e1=300000,$3=1e4,A0=120000,Z3=300000,s0=60000,e0=10,D0=3,Q3=600000,R0=15000,X3=115,p=".midnight",z0="wallet.json",q3="config.json",$0=448,x0=384,z3="localnet";function l(){return!("NO_COLOR"in process.env)}function Y0($,Z){if(!l())return $;return`\x1B[38;5;${Z}m${$}\x1B[0m`}function y($){if(!l())return $;return`\x1B[1m${$}\x1B[0m`}function j($){if(!l())return $;return`\x1B[2m${$}\x1B[0m`}function a($){return Y0($,38)}function S($){return Y0($,196)}function i($){return Y0($,40)}function V0($){return Y0($,226)}function H0($){return Y0($,15)}function J0($){return Y0($,245)}function _($,Z=J3){let Q=` ${$} `,X=Z-Q.length;if(X<=0)return y(Q);let q=Math.floor(X/2),z=X-q;return y("═".repeat(q)+Q+"═".repeat(z))}function D($=J3){return j("─".repeat($))}function H($,Z,Q=16){let X=($+":").padEnd(Q);return` ${J0(X)}${Z}`}function F0($){let Z=$<0n,Q=Z?-$:$,X=BigInt(10**r0),q=Q/X,Y=(Q%X).toString().padStart(r0,"0");return`${Z?"-":""}${q}.${Y}`}function E0($){return`${F0($)} NIGHT`}function P0($){let Z=$<0n,Q=Z?-$:$,X=10n**BigInt(Y3),q=Q/X,K=(Q%X).toString().padStart(Y3,"0").replace(/0+$/,"").padEnd(6,"0");return`${Z?"-":""}${q}.${K}`}function N0($){return`${P0($)} DUST`}function E($,Z=!1){let Q=Z&&$.length>20?$.slice(0,10)+"…"+$.slice(-8):$;return a(Q)}function O2($,Z){if(j0($).length<=Z)return[$];let X=$.split(/(\s+)/),q=[],z="",Y=0;for(let K of X){let G=j0(K).length;if(Y+G>Z&&Y>0)q.push(z),z=K.trimStart(),Y=j0(z).length;else z+=K,Y+=G}if(z.length>0)q.push(z);return q}function L2($,Z="light",Q=70){let X=Z==="heavy"?{tl:"╔",tr:"╗",bl:"╚",br:"╝",h:"═",v:"║"}:{tl:"┌",tr:"┐",bl:"└",br:"┘",h:"─",v:"│"},q=Q-4,z=[];for(let J of $){let V=J.split(`
|
|
3
|
+
`);for(let B of V)z.push(...O2(B,q))}let Y=Math.max(...z.map((J)=>j0(J).length)),K=Math.max(Y+2,20),G=X.tl+X.h.repeat(K)+X.tr,U=X.bl+X.h.repeat(K)+X.br,W=z.map((J)=>{let V=j0(J).length,B=K-V-2;return`${X.v} ${J}${" ".repeat(Math.max(0,B))} ${X.v}`});return[G,...W,U].join(`
|
|
4
|
+
`)}function K3($,Z){let X=$.split(`
|
|
5
|
+
`).map((z,Y)=>Y===0?S(y("Error: "))+S(z):S(z));if(Z)X.push(""),X.push(j("Suggestion: ")+Z);let q=L2(X,"heavy");if(l())return q.replace(/[╔╗╚╝═║]/g,(z)=>S(z));return q}function Z0($,Z){let X=[`${i("✓")} ${$}`];if(Z)X.push(H("Transaction",a(Z)));return X.join(`
|
|
6
|
+
`)}var J3=60,Y3=15,j0=($)=>$.replace(/\x1b\[[0-9;]*m/g,"");var N=()=>{};function O0($){let Z=($.message??"").toLowerCase();if(Z.includes("operation cancelled")||Z.includes("operation aborted")||Z==="cancelled"||Z==="aborted")return{exitCode:7,errorCode:u.CANCELLED};if(Z.includes("wallet file not found")||Z.includes("wallet")&&Z.includes("not found"))return{exitCode:3,errorCode:u.WALLET_NOT_FOUND};if(Z.includes("missing required flag")||Z.includes("missing amount")||Z.includes("missing recipient")||Z.includes("missing config key")||Z.includes("missing or invalid subcommand")||Z.includes("unknown command")||Z.includes("cannot specify both")||Z.includes("invalid bip-39")||Z.includes("seed must be")||Z.includes("key index must be")||Z.includes("usage:"))return{exitCode:2,errorCode:u.INVALID_ARGS};if(Z.includes("proof")&&Z.includes("timeout"))return{exitCode:6,errorCode:u.PROOF_TIMEOUT};if(Z.includes("stale utxo")||Z.includes("error code 115")||Z.includes("errorcode: 115"))return{exitCode:6,errorCode:u.STALE_UTXO};if(Z.includes("no dust")||Z.includes("dust")&&(Z.includes("required")||Z.includes("available")||Z.includes("insufficient")))return{exitCode:5,errorCode:u.DUST_REQUIRED};if(Z.includes("insufficient")||Z.includes("not enough"))return{exitCode:5,errorCode:u.INSUFFICIENT_BALANCE};if(Z.includes("rejected")||Z.includes("transaction failed"))return{exitCode:6,errorCode:u.TX_REJECTED};if(Z.includes("econnrefused")||Z.includes("enotfound")||Z.includes("etimedout")||Z.includes("websocket")||Z.includes("connection refused")||Z.includes("network")&&Z.includes("error"))return{exitCode:4,errorCode:u.NETWORK_ERROR};return{exitCode:1,errorCode:u.UNKNOWN}}var u;var $1=O(()=>{u={INVALID_ARGS:"INVALID_ARGS",WALLET_NOT_FOUND:"WALLET_NOT_FOUND",NETWORK_ERROR:"NETWORK_ERROR",INSUFFICIENT_BALANCE:"INSUFFICIENT_BALANCE",TX_REJECTED:"TX_REJECTED",STALE_UTXO:"STALE_UTXO",PROOF_TIMEOUT:"PROOF_TIMEOUT",DUST_REQUIRED:"DUST_REQUIRED",CANCELLED:"CANCELLED",UNKNOWN:"UNKNOWN"}});function G3(){let $=process.stderr.write;return process.stderr.write=()=>!0,()=>{process.stderr.write=$}}function Q1($){Z1=$}function L($){let Z=JSON.stringify($)+`
|
|
7
|
+
`;if(Z1)Z1(Z);else process.stdout.write(Z)}function U3($,Z,Q){process.stdout.write(JSON.stringify({error:!0,code:Z,message:$.message,exitCode:Q})+`
|
|
8
|
+
`)}var Z1=null;var v0;var B3=O(()=>{v0={name:"midnight-wallet-cli",version:"0.1.8",type:"module",description:"Git-style CLI wallet for the Midnight blockchain",license:"Apache-2.0",bin:{midnight:"dist/wallet.js",mn:"dist/wallet.js","midnight-wallet-cli":"dist/wallet.js","midnight-wallet-mcp":"dist/mcp-server.js"},files:["dist"],scripts:{wallet:"tsx src/wallet.ts",build:'bun build src/wallet.ts --outfile dist/wallet.js --target node --format esm --packages external --minify --banner "#!/usr/bin/env node" && bun build src/mcp-server.ts --outfile dist/mcp-server.js --target node --format esm --packages external --minify --banner "#!/usr/bin/env node"',mcp:"tsx src/mcp-server.ts",prepublishOnly:"npm run build && npm run test",test:"vitest run","test:watch":"vitest",typecheck:"tsc --noEmit"},dependencies:{"@midnight-ntwrk/ledger-v7":"7.0.0","@midnight-ntwrk/midnight-js-network-id":"3.0.0","@midnight-ntwrk/midnight-js-types":"3.0.0","@midnight-ntwrk/wallet-sdk-abstractions":"1.0.0","@midnight-ntwrk/wallet-sdk-address-format":"3.0.0","@midnight-ntwrk/wallet-sdk-dust-wallet":"1.0.0","@midnight-ntwrk/wallet-sdk-facade":"1.0.0","@midnight-ntwrk/wallet-sdk-hd":"3.0.0","@midnight-ntwrk/wallet-sdk-shielded":"1.0.0","@midnight-ntwrk/wallet-sdk-unshielded-wallet":"1.0.0","@modelcontextprotocol/sdk":"^1.27.1","@scure/bip39":"^2.0.1",rxjs:"^7.8.1",ws:"^8.19.0"},devDependencies:{"@types/node":"^22.19.13","@types/ws":"^8.18.1",tsx:"^4.21.0",typescript:"^5.9.3",vitest:"^3.2.4"}}});var W3,Q0,V3;var k0=O(()=>{B3();W3=v0.name,Q0=v0.version,V3=v0.description});async function R($,Z,Q){let X=[];Q1((z)=>X.push(z));let q=process.stderr.write;process.stderr.write=()=>!0;try{Z.flags.json=!0,await $(Z,Q);let z=X.join("").trim();if(!z)return{};return JSON.parse(z)}finally{Q1(null),process.stderr.write=q}}var H3=()=>{};import{HDWallet as T2,Roles as M2}from"@midnight-ntwrk/wallet-sdk-hd";import{createKeystore as I2,PublicKey as _2}from"@midnight-ntwrk/wallet-sdk-unshielded-wallet";import{NetworkId as X1}from"@midnight-ntwrk/wallet-sdk-abstractions";function K0($,Z,Q=0){let X=A2[Z],q=T2.fromSeed($);if(q.type!=="seedOk")throw new Error("Invalid seed for HD wallet");let z=q.hdWallet.selectAccount(0).selectRole(M2.NightExternal).deriveKeyAt(Q);if(z.type==="keyOutOfBounds")throw new Error(`Key index ${Q} out of bounds`);let Y=I2(z.key,X);return _2.fromKeyStore(Y).address}var A2;var b0=O(()=>{A2={preprod:X1.NetworkId.PreProd,preview:X1.NetworkId.Preview,undeployed:X1.NetworkId.Undeployed}});import{execSync as D2}from"child_process";function o($){return j3.includes($)}function x2($){return{...R2[$]}}function F3(){return j3}function P3($){if($.startsWith("mn_addr_preprod1"))return"preprod";if($.startsWith("mn_addr_preview1"))return"preview";if($.startsWith("mn_addr_undeployed1"))return"undeployed";return null}function E2(){try{let $=D2('docker ps --format "{{.Image}}|{{.Ports}}"',{encoding:"utf-8",timeout:5000}),Z={};for(let Q of $.trim().split(`
|
|
9
|
+
`)){if(!Q)continue;let[X,q]=Q.split("|"),z=(Y)=>{let K=new RegExp(`0\\.0\\.0\\.0:(\\d+)->${Y}/tcp`),G=q?.match(K);return G?parseInt(G[1],10):void 0};if(X.includes("indexer-standalone")||X.includes("indexer")){let Y=z(8088);if(Y)Z.indexerPort=Y}if(X.includes("midnight-node")){let Y=z(9944);if(Y)Z.nodePort=Y}if(X.includes("proof-server")){let Y=z(6300);if(Y)Z.proofServerPort=Y}}return Z}catch{return{}}}function O3($){let Z=x2($);if($==="undeployed"){let Q=E2();if(Q.indexerPort)Z.indexer=`http://localhost:${Q.indexerPort}/api/v3/graphql`,Z.indexerWS=`ws://localhost:${Q.indexerPort}/api/v3/graphql/ws`;if(Q.nodePort)Z.node=`ws://localhost:${Q.nodePort}`;if(Q.proofServerPort)Z.proofServer=`http://localhost:${Q.proofServerPort}`}return Z}var R2,j3;var w0=O(()=>{R2={preprod:{indexer:"https://indexer.preprod.midnight.network/api/v3/graphql",indexerWS:"wss://indexer.preprod.midnight.network/api/v3/graphql/ws",node:"wss://rpc.preprod.midnight.network",proofServer:"http://localhost:6300",networkId:"PreProd"},preview:{indexer:"https://indexer.preview.midnight.network/api/v3/graphql",indexerWS:"wss://indexer.preview.midnight.network/api/v3/graphql/ws",node:"wss://rpc.preview.midnight.network",proofServer:"http://localhost:6300",networkId:"Preview"},undeployed:{indexer:"http://localhost:8088/api/v3/graphql",indexerWS:"ws://localhost:8088/api/v3/graphql/ws",node:"ws://localhost:9944",proofServer:"http://localhost:6300",networkId:"Undeployed"}},j3=["preprod","preview","undeployed"]});import*as n from"fs";import*as q1 from"path";import{homedir as N2}from"os";function L3($){return $??q1.join(N2(),p)}function y3($){return q1.join(L3($),q3)}function v2($){let Z=L3($);if(!n.existsSync(Z))n.mkdirSync(Z,{recursive:!0,mode:$0})}function C0($){let Z=y3($);if(!n.existsSync(Z))return{...S0};let Q;try{Q=n.readFileSync(Z,"utf-8")}catch{return{...S0}}let X;try{X=JSON.parse(Q)}catch{return{...S0}}return{network:X.network&&o(X.network)?X.network:S0.network}}function k2($,Z){v2(Z);let Q=y3(Z);n.writeFileSync(Q,JSON.stringify($,null,2)+`
|
|
10
|
+
`,{mode:x0})}function T3($,Z){let Q=C0(Z);if($==="network")return Q.network;throw new Error(`Unknown config key: "${$}"
|
|
11
|
+
Valid keys: ${z1.join(", ")}`)}function M3($,Z,Q){let X=C0(Q);if($==="network"){if(!o(Z))throw new Error(`Invalid network: "${Z}"
|
|
12
|
+
Valid networks: preprod, preview, undeployed`);X.network=Z}else throw new Error(`Unknown config key: "${$}"
|
|
13
|
+
Valid keys: ${z1.join(", ")}`);k2(X,Q)}function Y1(){return z1}var S0,z1;var J1=O(()=>{w0();S0={network:"undeployed"},z1=["network"]});function X0($){let Z=A($.args,"network");if(Z!==void 0){if(!o(Z))throw new Error(`Invalid network: "${Z}"
|
|
14
|
+
Valid networks: ${F3().join(", ")}`);return Z}if($.walletNetwork&&o($.walletNetwork))return $.walletNetwork;if($.address){let X=P3($.address);if(X)return X}let Q=C0($.configDir);if(Q.network&&o(Q.network))return Q.network;return"undeployed"}function t($){let Z=X0($),Q=O3(Z);return{name:Z,config:Q}}var r=O(()=>{w0();J1()});import*as C from"fs";import*as s from"path";import{homedir as b2}from"os";function I3(){return s.join(b2(),p)}function _3(){return s.join(I3(),z0)}function w2(){let $=I3();if(!C.existsSync($))C.mkdirSync($,{recursive:!0,mode:$0})}function c($){let Z=$?s.resolve($):_3();if(!C.existsSync(Z))throw new Error(`Wallet file not found: ${Z}
|
|
15
|
+
Generate a wallet first: midnight generate --network <name>`);let Q;try{Q=C.readFileSync(Z,"utf-8")}catch(q){throw new Error(`Failed to read wallet file: ${Z}
|
|
16
|
+
${q.message}`)}let X;try{X=JSON.parse(Q)}catch{throw new Error(`Invalid JSON in wallet file: ${Z}`)}if(!X.seed||!X.network||!X.address||!X.createdAt){let q=["seed","network","address","createdAt"].filter((z)=>!X[z]);throw new Error(`Wallet file is missing required fields (${q.join(", ")}): ${Z}`)}if(!/^[0-9a-fA-F]+$/.test(X.seed))throw new Error(`Invalid seed format in wallet file (expected hex string): ${Z}`);if(!o(X.network))throw new Error(`Invalid network "${X.network}" in wallet file: ${Z}
|
|
17
|
+
Valid networks: preprod, preview, undeployed`);return X}function A3($,Z){let Q=Z?s.resolve(Z):_3();if(!Z)w2();else{let X=s.dirname(Q);if(!C.existsSync(X))C.mkdirSync(X,{recursive:!0,mode:$0})}return C.writeFileSync(Q,JSON.stringify($,null,2)+`
|
|
18
|
+
`,{mode:x0}),Q}var q0=O(()=>{w0()});var K1={};w(K1,{default:()=>E3});import*as x3 from"fs";import*as f0 from"path";import{homedir as S2}from"os";import{generateMnemonic as C2,mnemonicToSeedSync as D3,validateMnemonic as f2}from"@scure/bip39";import{wordlist as R3}from"@scure/bip39/wordlists/english.js";async function E3($){let Z=X0({args:$}),Q=A($,"output"),X=A($,"seed"),q=A($,"mnemonic");if(X!==void 0&&q!==void 0)throw new Error("Cannot specify both --seed and --mnemonic. Use one or the other.");let z=Q?f0.resolve(Q):f0.join(S2(),p,z0);if(x3.existsSync(z)&&!P($,"force"))throw new Error(`Wallet file already exists: ${z}
|
|
19
|
+
Use --force to overwrite, or --output <file> to save to a different path.`);let Y,K;if(X!==void 0){let J=X.replace(/^0x/,"");if(J.length!==64||!/^[0-9a-fA-F]+$/.test(J))throw new Error("Seed must be a 64-character hex string (32 bytes)");Y=Buffer.from(J,"hex")}else if(q!==void 0){if(!f2(q,R3))throw new Error("Invalid BIP-39 mnemonic. Expected 12 or 24 words from the English wordlist.");K=q,Y=Buffer.from(D3(K).slice(0,32))}else K=C2(R3,256),Y=Buffer.from(D3(K).slice(0,32));let G=K0(Y,Z),U={seed:Y.toString("hex"),network:Z,address:G,createdAt:new Date().toISOString()};if(K)U.mnemonic=K;let W=A3(U,Q);if(P($,"json")){let J={address:G,network:Z,seed:Y.toString("hex"),file:W,createdAt:U.createdAt};if(K)J.mnemonic=K;L(J);return}if(process.stdout.write(G+`
|
|
20
|
+
`),process.stderr.write(`
|
|
21
|
+
`+_("Wallet Generated")+`
|
|
22
|
+
|
|
23
|
+
`),process.stderr.write(H("Network",Z)+`
|
|
24
|
+
`),process.stderr.write(H("Address",E(G))+`
|
|
25
|
+
`),process.stderr.write(H("File",W)+`
|
|
26
|
+
`),process.stderr.write(`
|
|
27
|
+
`),K)process.stderr.write(V0(y(" MNEMONIC (save securely!):"))+`
|
|
28
|
+
`),process.stderr.write(` ${K}
|
|
29
|
+
|
|
30
|
+
`);process.stderr.write(V0(y(" SEED (hex):"))+`
|
|
31
|
+
`),process.stderr.write(` ${Y.toString("hex")}
|
|
32
|
+
|
|
33
|
+
`),process.stderr.write(D()+`
|
|
34
|
+
`),process.stderr.write(j(" Next: midnight info | midnight balance")+`
|
|
35
|
+
|
|
36
|
+
`),process.stderr.write(i("✓")+` Wallet saved
|
|
37
|
+
`)}var G1=O(()=>{b0();r();q0();N()});var U1={};w(U1,{default:()=>N3});import*as h0 from"path";import{homedir as h2}from"os";async function N3($){let Z=A($,"wallet"),Q=c(Z),X=Z?h0.resolve(Z):h0.join(h2(),p,z0);if(P($,"json")){L({address:Q.address,network:Q.network,createdAt:Q.createdAt,file:X});return}process.stdout.write(Q.address+`
|
|
38
|
+
`),process.stderr.write(`
|
|
39
|
+
`+_("Wallet Info")+`
|
|
40
|
+
|
|
41
|
+
`),process.stderr.write(H("Address",E(Q.address))+`
|
|
42
|
+
`),process.stderr.write(H("Network",Q.network)+`
|
|
43
|
+
`),process.stderr.write(H("Created",Q.createdAt)+`
|
|
44
|
+
`),process.stderr.write(H("File",X)+`
|
|
45
|
+
`),process.stderr.write(`
|
|
46
|
+
`+D()+`
|
|
47
|
+
|
|
48
|
+
`)}var B1=O(()=>{q0();N();N()});import p2 from"ws";function v3($,Z,Q){return new Promise((X,q)=>{let z=new p2(Z,["graphql-transport-ws"]),Y=new Map,K=0,G=0,U=0,W=!1,J=!1,V,B=()=>{let I=new Map,M=0;for(let k of Y.values())if(!k.spent){M++;let g=I.get(k.tokenType)??0n;I.set(k.tokenType,g+k.value)}return{balances:I,utxoCount:M,txCount:K,highestTxId:G}},F=()=>{clearTimeout(V)},T=()=>{if(!J&&W&&(G===0||U>=G))J=!0,F(),z.send(JSON.stringify({id:"1",type:"complete"})),z.close(),X(B())};z.on("open",()=>{z.send(JSON.stringify({type:"connection_init"}))}),z.on("message",(I)=>{let M=JSON.parse(I.toString());switch(M.type){case"connection_ack":z.send(JSON.stringify({id:"1",type:"subscribe",payload:{query:u2,variables:{address:$}}}));break;case"next":{if(M.payload?.errors){let g=M.payload.errors[0]?.message||"Unknown GraphQL error";if(!J)J=!0,F(),z.close(),q(new Error(`GraphQL error: ${g}`));return}let k=M.payload?.data?.unshieldedTransactions;if(!k)return;if(k.__typename==="UnshieldedTransaction"){K++;let g=k;U=Math.max(U,g.transaction.id);for(let d of g.createdUtxos){let t0=`${d.intentHash}:${d.outputIndex}`;Y.set(t0,{value:BigInt(d.value),tokenType:d.tokenType,spent:!1})}for(let d of g.spentUtxos){let t0=`${d.intentHash}:${d.outputIndex}`,i1=Y.get(t0);if(i1)i1.spent=!0}if(Q)Q(U,G);T()}else if(k.__typename==="UnshieldedTransactionsProgress")G=k.highestTransactionId,W=!0,T();break}case"error":if(!J)J=!0,F(),z.close(),q(new Error(`GraphQL subscription error: ${JSON.stringify(M.payload)}`));break;case"complete":break}}),z.on("error",(I)=>{if(!J)J=!0,F(),q(new Error(`WebSocket connection failed: ${I.message}`))}),z.on("close",()=>{if(!J)J=!0,F(),q(new Error(`Indexer closed the connection before balance sync completed. Indexer: ${Z}`))}),V=setTimeout(()=>{if(!J)J=!0,z.close(),q(new Error(`Balance check timed out after ${s0/1000}s. Indexer: ${Z}`))},s0)})}function L0($){return $===a1}var u2=`
|
|
986
49
|
subscription UnshieldedTransactions($address: UnshieldedAddress!) {
|
|
987
50
|
unshieldedTransactions(address: $address) {
|
|
988
51
|
__typename
|
|
@@ -996,1193 +59,138 @@ var SUBSCRIPTION_QUERY = `
|
|
|
996
59
|
}
|
|
997
60
|
}
|
|
998
61
|
}
|
|
999
|
-
`;
|
|
1000
|
-
|
|
1001
|
-
};
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
`);
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
var
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
`)
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
`)
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
`
|
|
1110
|
-
|
|
1111
|
-
`);
|
|
1112
|
-
|
|
1113
|
-
`)
|
|
1114
|
-
|
|
1115
|
-
`)
|
|
1116
|
-
|
|
1117
|
-
`)
|
|
1118
|
-
|
|
1119
|
-
`)
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
`);
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
`);
|
|
1130
|
-
|
|
1131
|
-
const shortType = tokenType.slice(0, 8) + "…" + tokenType.slice(-8);
|
|
1132
|
-
process.stderr.write(keyValue(`Token ${shortType}`, bold(amount.toString())) + `
|
|
1133
|
-
`);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
process.stderr.write(`
|
|
1138
|
-
` + divider() + `
|
|
1139
|
-
|
|
1140
|
-
`);
|
|
1141
|
-
} catch (err) {
|
|
1142
|
-
spinner.stop("Failed");
|
|
1143
|
-
throw err;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
var init_balance = __esm(() => {
|
|
1147
|
-
init_wallet_config();
|
|
1148
|
-
init_resolve_network();
|
|
1149
|
-
init_balance_subscription();
|
|
1150
|
-
init_format();
|
|
1151
|
-
init_spinner();
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
// src/commands/address.ts
|
|
1155
|
-
var exports_address = {};
|
|
1156
|
-
__export(exports_address, {
|
|
1157
|
-
default: () => addressCommand
|
|
1158
|
-
});
|
|
1159
|
-
async function addressCommand(args) {
|
|
1160
|
-
const seedHex = requireFlag(args, "seed", "hex").replace(/^0x/, "");
|
|
1161
|
-
if (seedHex.length !== 64 || !/^[0-9a-fA-F]+$/.test(seedHex)) {
|
|
1162
|
-
throw new Error("Seed must be a 64-character hex string (32 bytes)");
|
|
1163
|
-
}
|
|
1164
|
-
const indexStr = getFlag(args, "index");
|
|
1165
|
-
const keyIndex = indexStr !== undefined ? parseInt(indexStr, 10) : 0;
|
|
1166
|
-
if (isNaN(keyIndex) || keyIndex < 0 || !Number.isInteger(Number(indexStr ?? "0"))) {
|
|
1167
|
-
throw new Error("Key index must be a non-negative integer");
|
|
1168
|
-
}
|
|
1169
|
-
const seedBuffer = Buffer.from(seedHex, "hex");
|
|
1170
|
-
const networkName = resolveNetworkName({ args });
|
|
1171
|
-
const address = deriveUnshieldedAddress(seedBuffer, networkName, keyIndex);
|
|
1172
|
-
const derivationPath = `m/44'/2400'/0'/NightExternal/${keyIndex}`;
|
|
1173
|
-
if (hasFlag(args, "json")) {
|
|
1174
|
-
writeJsonResult({
|
|
1175
|
-
address,
|
|
1176
|
-
network: networkName,
|
|
1177
|
-
index: keyIndex,
|
|
1178
|
-
path: derivationPath
|
|
1179
|
-
});
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
|
-
process.stdout.write(address + `
|
|
1183
|
-
`);
|
|
1184
|
-
process.stderr.write(`
|
|
1185
|
-
`);
|
|
1186
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
1187
|
-
`);
|
|
1188
|
-
process.stderr.write(keyValue("Index", keyIndex.toString()) + `
|
|
1189
|
-
`);
|
|
1190
|
-
process.stderr.write(keyValue("Address", formatAddress(address)) + `
|
|
1191
|
-
`);
|
|
1192
|
-
process.stderr.write(keyValue("Path", dim(derivationPath)) + `
|
|
1193
|
-
`);
|
|
1194
|
-
process.stderr.write(divider() + `
|
|
1195
|
-
|
|
1196
|
-
`);
|
|
1197
|
-
}
|
|
1198
|
-
var init_address = __esm(() => {
|
|
1199
|
-
init_derive_address();
|
|
1200
|
-
init_resolve_network();
|
|
1201
|
-
init_format();
|
|
1202
|
-
});
|
|
1203
|
-
|
|
1204
|
-
// src/commands/genesis-address.ts
|
|
1205
|
-
var exports_genesis_address = {};
|
|
1206
|
-
__export(exports_genesis_address, {
|
|
1207
|
-
default: () => genesisAddressCommand
|
|
1208
|
-
});
|
|
1209
|
-
async function genesisAddressCommand(args) {
|
|
1210
|
-
const networkName = resolveNetworkName({ args });
|
|
1211
|
-
const seedBuffer = Buffer.from(GENESIS_SEED, "hex");
|
|
1212
|
-
const address = deriveUnshieldedAddress(seedBuffer, networkName);
|
|
1213
|
-
if (hasFlag(args, "json")) {
|
|
1214
|
-
writeJsonResult({ address, network: networkName });
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1217
|
-
process.stdout.write(address + `
|
|
1218
|
-
`);
|
|
1219
|
-
process.stderr.write(`
|
|
1220
|
-
`);
|
|
1221
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
1222
|
-
`);
|
|
1223
|
-
process.stderr.write(keyValue("Address", formatAddress(address)) + `
|
|
1224
|
-
`);
|
|
1225
|
-
process.stderr.write(keyValue("Seed", dim("0x01 (genesis)")) + `
|
|
1226
|
-
`);
|
|
1227
|
-
process.stderr.write(divider() + `
|
|
1228
|
-
|
|
1229
|
-
`);
|
|
1230
|
-
}
|
|
1231
|
-
var init_genesis_address = __esm(() => {
|
|
1232
|
-
init_derive_address();
|
|
1233
|
-
init_resolve_network();
|
|
1234
|
-
init_format();
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
// src/commands/inspect-cost.ts
|
|
1238
|
-
var exports_inspect_cost = {};
|
|
1239
|
-
__export(exports_inspect_cost, {
|
|
1240
|
-
default: () => inspectCostCommand
|
|
1241
|
-
});
|
|
1242
|
-
import * as ledger from "@midnight-ntwrk/ledger-v7";
|
|
1243
|
-
function probeDimension(params, dimension, probeValue) {
|
|
1244
|
-
const cost = {
|
|
1245
|
-
readTime: 0n,
|
|
1246
|
-
computeTime: 0n,
|
|
1247
|
-
blockUsage: 0n,
|
|
1248
|
-
bytesWritten: 0n,
|
|
1249
|
-
bytesChurned: 0n
|
|
1250
|
-
};
|
|
1251
|
-
cost[dimension] = probeValue;
|
|
1252
|
-
const normalized = params.normalizeFullness(cost);
|
|
1253
|
-
const normalizedValue = normalized[dimension];
|
|
1254
|
-
return Math.round(Number(probeValue) / normalizedValue);
|
|
1255
|
-
}
|
|
1256
|
-
function deriveBlockLimits(params) {
|
|
1257
|
-
return {
|
|
1258
|
-
readTime: probeDimension(params, "readTime", 1000000000n),
|
|
1259
|
-
computeTime: probeDimension(params, "computeTime", 1000000000n),
|
|
1260
|
-
blockUsage: probeDimension(params, "blockUsage", 10000n),
|
|
1261
|
-
bytesWritten: probeDimension(params, "bytesWritten", 10000n),
|
|
1262
|
-
bytesChurned: probeDimension(params, "bytesChurned", 1000000n)
|
|
1263
|
-
};
|
|
1264
|
-
}
|
|
1265
|
-
async function inspectCostCommand(args) {
|
|
1266
|
-
const params = ledger.LedgerParameters.initialParameters();
|
|
1267
|
-
const limits = deriveBlockLimits(params);
|
|
1268
|
-
if (hasFlag(args, "json")) {
|
|
1269
|
-
writeJsonResult(limits);
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
for (const [dimension, value] of Object.entries(limits)) {
|
|
1273
|
-
process.stdout.write(`${dimension}=${value}
|
|
1274
|
-
`);
|
|
1275
|
-
}
|
|
1276
|
-
process.stderr.write(`
|
|
1277
|
-
` + header("Block Limits") + `
|
|
1278
|
-
|
|
1279
|
-
`);
|
|
1280
|
-
process.stderr.write(dim(" Derived from LedgerParameters.initialParameters()") + `
|
|
1281
|
-
|
|
1282
|
-
`);
|
|
1283
|
-
for (const [dimension, value] of Object.entries(limits)) {
|
|
1284
|
-
const unit = UNITS[dimension] ?? "";
|
|
1285
|
-
process.stderr.write(keyValue(dimension, `${bold(value.toLocaleString())} ${dim(unit)}`) + `
|
|
1286
|
-
`);
|
|
1287
|
-
}
|
|
1288
|
-
process.stderr.write(`
|
|
1289
|
-
` + divider() + `
|
|
1290
|
-
`);
|
|
1291
|
-
process.stderr.write(dim(" bytesWritten is typically the tightest constraint") + `
|
|
1292
|
-
`);
|
|
1293
|
-
process.stderr.write(dim(" for large contract deployments.") + `
|
|
1294
|
-
|
|
1295
|
-
`);
|
|
1296
|
-
}
|
|
1297
|
-
var UNITS;
|
|
1298
|
-
var init_inspect_cost = __esm(() => {
|
|
1299
|
-
init_format();
|
|
1300
|
-
UNITS = {
|
|
1301
|
-
readTime: "picoseconds",
|
|
1302
|
-
computeTime: "picoseconds",
|
|
1303
|
-
blockUsage: "bytes",
|
|
1304
|
-
bytesWritten: "bytes",
|
|
1305
|
-
bytesChurned: "bytes"
|
|
1306
|
-
};
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
// src/lib/derivation.ts
|
|
1310
|
-
import { HDWallet as HDWallet2, Roles as Roles2 } from "@midnight-ntwrk/wallet-sdk-hd";
|
|
1311
|
-
function deriveKey(seedBuffer, role) {
|
|
1312
|
-
const hdResult = HDWallet2.fromSeed(seedBuffer);
|
|
1313
|
-
if (hdResult.type !== "seedOk") {
|
|
1314
|
-
throw new Error("Invalid seed for HD wallet");
|
|
1315
|
-
}
|
|
1316
|
-
const derivation = hdResult.hdWallet.selectAccount(0).selectRole(role).deriveKeyAt(0);
|
|
1317
|
-
if (derivation.type === "keyOutOfBounds") {
|
|
1318
|
-
throw new Error("Key derivation out of bounds");
|
|
1319
|
-
}
|
|
1320
|
-
return derivation.key;
|
|
1321
|
-
}
|
|
1322
|
-
function deriveShieldedSeed(seedBuffer) {
|
|
1323
|
-
return deriveKey(seedBuffer, Roles2.Zswap);
|
|
1324
|
-
}
|
|
1325
|
-
function deriveUnshieldedSeed(seedBuffer) {
|
|
1326
|
-
return deriveKey(seedBuffer, Roles2.NightExternal);
|
|
1327
|
-
}
|
|
1328
|
-
function deriveDustSeed(seedBuffer) {
|
|
1329
|
-
return deriveKey(seedBuffer, Roles2.Dust);
|
|
1330
|
-
}
|
|
1331
|
-
var init_derivation = () => {
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
// src/lib/facade.ts
|
|
1335
|
-
import { ShieldedWallet } from "@midnight-ntwrk/wallet-sdk-shielded";
|
|
1336
|
-
import {
|
|
1337
|
-
UnshieldedWallet,
|
|
1338
|
-
createKeystore as createKeystore2,
|
|
1339
|
-
PublicKey as PublicKey2,
|
|
1340
|
-
InMemoryTransactionHistoryStorage
|
|
1341
|
-
} from "@midnight-ntwrk/wallet-sdk-unshielded-wallet";
|
|
1342
|
-
import { DustWallet } from "@midnight-ntwrk/wallet-sdk-dust-wallet";
|
|
1343
|
-
import { WalletFacade } from "@midnight-ntwrk/wallet-sdk-facade";
|
|
1344
|
-
import * as ledger2 from "@midnight-ntwrk/ledger-v7";
|
|
1345
|
-
import { NetworkId as NetworkId2 } from "@midnight-ntwrk/wallet-sdk-abstractions";
|
|
1346
|
-
import * as rx from "rxjs";
|
|
1347
|
-
function buildFacade(seedBuffer, networkConfig) {
|
|
1348
|
-
const networkId = NETWORK_ID_MAP2[networkConfig.networkId];
|
|
1349
|
-
if (networkId === undefined) {
|
|
1350
|
-
throw new Error(`Unknown networkId: ${networkConfig.networkId}`);
|
|
1351
|
-
}
|
|
1352
|
-
const shieldedSeed = deriveShieldedSeed(seedBuffer);
|
|
1353
|
-
const unshieldedSeed = deriveUnshieldedSeed(seedBuffer);
|
|
1354
|
-
const dustSeed = deriveDustSeed(seedBuffer);
|
|
1355
|
-
const zswapSecretKeys = ledger2.ZswapSecretKeys.fromSeed(shieldedSeed);
|
|
1356
|
-
const dustSecretKey = ledger2.DustSecretKey.fromSeed(dustSeed);
|
|
1357
|
-
const keystore = createKeystore2(unshieldedSeed, networkId);
|
|
1358
|
-
const shieldedConfig = {
|
|
1359
|
-
networkId,
|
|
1360
|
-
indexerClientConnection: {
|
|
1361
|
-
indexerHttpUrl: networkConfig.indexer,
|
|
1362
|
-
indexerWsUrl: networkConfig.indexerWS
|
|
1363
|
-
},
|
|
1364
|
-
provingServerUrl: new URL(networkConfig.proofServer),
|
|
1365
|
-
relayURL: new URL(networkConfig.node)
|
|
1366
|
-
};
|
|
1367
|
-
const unshieldedConfig = {
|
|
1368
|
-
networkId,
|
|
1369
|
-
indexerClientConnection: {
|
|
1370
|
-
indexerHttpUrl: networkConfig.indexer,
|
|
1371
|
-
indexerWsUrl: networkConfig.indexerWS
|
|
1372
|
-
},
|
|
1373
|
-
txHistoryStorage: new InMemoryTransactionHistoryStorage
|
|
1374
|
-
};
|
|
1375
|
-
const dustConfig = {
|
|
1376
|
-
networkId,
|
|
1377
|
-
costParameters: {
|
|
1378
|
-
additionalFeeOverhead: DUST_COST_OVERHEAD,
|
|
1379
|
-
feeBlocksMargin: DUST_FEE_BLOCKS_MARGIN
|
|
1380
|
-
},
|
|
1381
|
-
indexerClientConnection: {
|
|
1382
|
-
indexerHttpUrl: networkConfig.indexer,
|
|
1383
|
-
indexerWsUrl: networkConfig.indexerWS
|
|
1384
|
-
},
|
|
1385
|
-
provingServerUrl: new URL(networkConfig.proofServer),
|
|
1386
|
-
relayURL: new URL(networkConfig.node)
|
|
1387
|
-
};
|
|
1388
|
-
const shieldedWallet = ShieldedWallet(shieldedConfig).startWithSecretKeys(zswapSecretKeys);
|
|
1389
|
-
const unshieldedWallet = UnshieldedWallet(unshieldedConfig).startWithPublicKey(PublicKey2.fromKeyStore(keystore));
|
|
1390
|
-
const dustWallet = DustWallet(dustConfig).startWithSecretKey(dustSecretKey, ledger2.LedgerParameters.initialParameters().dust);
|
|
1391
|
-
const facade = new WalletFacade(shieldedWallet, unshieldedWallet, dustWallet);
|
|
1392
|
-
return { facade, keystore, zswapSecretKeys, dustSecretKey };
|
|
1393
|
-
}
|
|
1394
|
-
async function startAndSyncFacade(bundle, onProgress) {
|
|
1395
|
-
const { facade, zswapSecretKeys, dustSecretKey } = bundle;
|
|
1396
|
-
await facade.start(zswapSecretKeys, dustSecretKey);
|
|
1397
|
-
return rx.firstValueFrom(facade.state().pipe(rx.tap((state) => {
|
|
1398
|
-
if (onProgress) {
|
|
1399
|
-
const progress = state.unshielded.progress;
|
|
1400
|
-
if (progress) {
|
|
1401
|
-
onProgress(Number(progress.appliedId), Number(progress.highestTransactionId));
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}), rx.filter((state) => state.isSynced), rx.timeout(SYNC_TIMEOUT_MS)));
|
|
1405
|
-
}
|
|
1406
|
-
async function quickSync(bundle) {
|
|
1407
|
-
return rx.firstValueFrom(bundle.facade.state().pipe(rx.filter((state) => state.isSynced), rx.timeout(PRE_SEND_SYNC_TIMEOUT_MS)));
|
|
1408
|
-
}
|
|
1409
|
-
async function stopFacade(bundle) {
|
|
1410
|
-
await bundle.facade.stop();
|
|
1411
|
-
}
|
|
1412
|
-
function suppressSdkTransientErrors(onWarning) {
|
|
1413
|
-
const rejectionHandler = (reason) => {
|
|
1414
|
-
const tag = reason?._tag;
|
|
1415
|
-
if (typeof tag === "string" && tag.startsWith("Wallet.")) {
|
|
1416
|
-
const msg = reason?.message ?? "transient error";
|
|
1417
|
-
onWarning?.(tag, msg);
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
originalConsoleError("Unhandled rejection:", reason);
|
|
1421
|
-
process.exit(1);
|
|
1422
|
-
};
|
|
1423
|
-
const originalConsoleError = console.error;
|
|
1424
|
-
console.error = (...args) => {
|
|
1425
|
-
const firstArg = args[0];
|
|
1426
|
-
if (typeof firstArg === "object" && firstArg?._tag?.startsWith("Wallet.")) {
|
|
1427
|
-
onWarning?.(firstArg._tag, firstArg?.message ?? "transient error");
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
if (typeof firstArg === "string" && firstArg.startsWith("Wallet.")) {
|
|
1431
|
-
onWarning?.("Wallet.Sync", "transient error");
|
|
1432
|
-
return;
|
|
1433
|
-
}
|
|
1434
|
-
originalConsoleError(...args);
|
|
1435
|
-
};
|
|
1436
|
-
process.on("unhandledRejection", rejectionHandler);
|
|
1437
|
-
return () => {
|
|
1438
|
-
process.removeListener("unhandledRejection", rejectionHandler);
|
|
1439
|
-
console.error = originalConsoleError;
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
var NETWORK_ID_MAP2;
|
|
1443
|
-
var init_facade = __esm(() => {
|
|
1444
|
-
init_derivation();
|
|
1445
|
-
NETWORK_ID_MAP2 = {
|
|
1446
|
-
PreProd: NetworkId2.NetworkId.PreProd,
|
|
1447
|
-
Preview: NetworkId2.NetworkId.Preview,
|
|
1448
|
-
Undeployed: NetworkId2.NetworkId.Undeployed
|
|
1449
|
-
};
|
|
1450
|
-
});
|
|
1451
|
-
|
|
1452
|
-
// src/lib/transfer.ts
|
|
1453
|
-
import * as ledger3 from "@midnight-ntwrk/ledger-v7";
|
|
1454
|
-
import { MidnightBech32m, UnshieldedAddress } from "@midnight-ntwrk/wallet-sdk-address-format";
|
|
1455
|
-
import { NetworkId as NetworkId3 } from "@midnight-ntwrk/wallet-sdk-abstractions";
|
|
1456
|
-
import * as rx2 from "rxjs";
|
|
1457
|
-
function nightToMicro(amountNight) {
|
|
1458
|
-
if (amountNight <= 0) {
|
|
1459
|
-
throw new Error("Amount must be greater than 0");
|
|
1460
|
-
}
|
|
1461
|
-
if (!Number.isFinite(amountNight)) {
|
|
1462
|
-
throw new Error("Amount must be a finite number");
|
|
1463
|
-
}
|
|
1464
|
-
const str = amountNight.toFixed(6);
|
|
1465
|
-
const [whole, frac] = str.split(".");
|
|
1466
|
-
const microStr = whole + (frac ?? "").padEnd(6, "0");
|
|
1467
|
-
const micro = BigInt(microStr);
|
|
1468
|
-
if (micro <= 0n) {
|
|
1469
|
-
throw new Error("Amount too small — minimum is 0.000001 NIGHT");
|
|
1470
|
-
}
|
|
1471
|
-
return micro;
|
|
1472
|
-
}
|
|
1473
|
-
function parseAmount(amountStr) {
|
|
1474
|
-
const amount = Number(amountStr);
|
|
1475
|
-
if (Number.isNaN(amount) || !Number.isFinite(amount)) {
|
|
1476
|
-
throw new Error(`Invalid amount: "${amountStr}" — must be a positive number`);
|
|
1477
|
-
}
|
|
1478
|
-
if (amount <= 0) {
|
|
1479
|
-
throw new Error(`Invalid amount: "${amountStr}" — must be greater than 0`);
|
|
1480
|
-
}
|
|
1481
|
-
return amount;
|
|
1482
|
-
}
|
|
1483
|
-
function validateRecipientAddress(address, networkConfig) {
|
|
1484
|
-
const networkId = NETWORK_ID_MAP3[networkConfig.networkId];
|
|
1485
|
-
if (networkId === undefined) {
|
|
1486
|
-
throw new Error(`Unknown networkId: ${networkConfig.networkId}`);
|
|
1487
|
-
}
|
|
1488
|
-
try {
|
|
1489
|
-
MidnightBech32m.parse(address).decode(UnshieldedAddress, networkId);
|
|
1490
|
-
} catch (err) {
|
|
1491
|
-
throw new Error(`Invalid recipient address: ${err.message}
|
|
1492
|
-
` + `Expected a bech32m address for network "${networkConfig.networkId}"`);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
async function ensureDust(bundle, onDust) {
|
|
1496
|
-
const state = await rx2.firstValueFrom(bundle.facade.state().pipe(rx2.filter((s) => s.isSynced)));
|
|
1497
|
-
const nightUtxos = state.unshielded.availableCoins.filter((coin) => coin.meta?.registeredForDustGeneration !== true);
|
|
1498
|
-
if (nightUtxos.length > 0) {
|
|
1499
|
-
onDust?.(`Registering ${nightUtxos.length} UTXO(s) for dust generation...`);
|
|
1500
|
-
const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
|
|
1501
|
-
const dustReceiverAddress = state.dust.dustAddress;
|
|
1502
|
-
const dustUtxos = nightUtxos.map((coin) => ({
|
|
1503
|
-
...coin.utxo,
|
|
1504
|
-
ctime: new Date(coin.meta.ctime)
|
|
1505
|
-
}));
|
|
1506
|
-
await bundle.facade.dust.waitForSyncedState();
|
|
1507
|
-
const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
|
|
1508
|
-
const intent = unprovenTx.intents?.get(1);
|
|
1509
|
-
if (!intent) {
|
|
1510
|
-
throw new Error("Dust generation intent not found on transaction");
|
|
1511
|
-
}
|
|
1512
|
-
const signature = bundle.keystore.signData(intent.signatureData(1));
|
|
1513
|
-
const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
|
|
1514
|
-
const finalized = await bundle.facade.finalizeTransaction(signedTx);
|
|
1515
|
-
await bundle.facade.submitTransaction(finalized);
|
|
1516
|
-
} else if (state.dust.availableCoins.length > 0) {
|
|
1517
|
-
onDust?.("Dust available");
|
|
1518
|
-
return;
|
|
1519
|
-
} else {
|
|
1520
|
-
onDust?.("UTXOs already registered, waiting for dust generation...");
|
|
1521
|
-
}
|
|
1522
|
-
onDust?.("Waiting for dust tokens...");
|
|
1523
|
-
await rx2.firstValueFrom(bundle.facade.state().pipe(rx2.throttleTime(5000), rx2.filter((s) => s.isSynced), rx2.filter((s) => s.dust.walletBalance(new Date) > 0n), rx2.timeout(DUST_TIMEOUT_MS)));
|
|
1524
|
-
onDust?.("Dust available");
|
|
1525
|
-
}
|
|
1526
|
-
async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting) {
|
|
1527
|
-
let lastError;
|
|
1528
|
-
for (let attempt = 1;attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
|
|
1529
|
-
try {
|
|
1530
|
-
if (attempt > 1) {
|
|
1531
|
-
await quickSync(bundle);
|
|
1532
|
-
}
|
|
1533
|
-
const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
|
|
1534
|
-
const unprovenRecipe = await bundle.facade.transferTransaction([
|
|
1535
|
-
{
|
|
1536
|
-
type: "unshielded",
|
|
1537
|
-
outputs: [
|
|
1538
|
-
{
|
|
1539
|
-
amount,
|
|
1540
|
-
receiverAddress: recipientAddress,
|
|
1541
|
-
type: ledger3.unshieldedToken().raw
|
|
1542
|
-
}
|
|
1543
|
-
]
|
|
1544
|
-
}
|
|
1545
|
-
], { shieldedSecretKeys: bundle.zswapSecretKeys, dustSecretKey: bundle.dustSecretKey }, { ttl, payFees: true });
|
|
1546
|
-
const signedRecipe = await bundle.facade.signRecipe(unprovenRecipe, (payload) => bundle.keystore.signData(payload));
|
|
1547
|
-
onProving?.();
|
|
1548
|
-
const finalizedTx = await Promise.race([
|
|
1549
|
-
bundle.facade.finalizeRecipe(signedRecipe),
|
|
1550
|
-
new Promise((_, reject) => {
|
|
1551
|
-
setTimeout(() => reject(new Error("ZK proof generation timed out")), PROOF_TIMEOUT_MS);
|
|
1552
|
-
})
|
|
1553
|
-
]);
|
|
1554
|
-
onSubmitting?.();
|
|
1555
|
-
const txHash = await bundle.facade.submitTransaction(finalizedTx);
|
|
1556
|
-
return txHash;
|
|
1557
|
-
} catch (err) {
|
|
1558
|
-
lastError = err;
|
|
1559
|
-
const isStaleUtxo = err?.code === STALE_UTXO_ERROR_CODE || err?.message?.includes("115") || err?.message?.toLowerCase().includes("stale");
|
|
1560
|
-
if (isStaleUtxo && attempt < MAX_RETRY_ATTEMPTS) {
|
|
1561
|
-
continue;
|
|
1562
|
-
}
|
|
1563
|
-
throw err;
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
throw lastError ?? new Error("Transfer failed after retries");
|
|
1567
|
-
}
|
|
1568
|
-
async function executeTransfer(params) {
|
|
1569
|
-
const {
|
|
1570
|
-
seedBuffer,
|
|
1571
|
-
networkConfig,
|
|
1572
|
-
recipientAddress,
|
|
1573
|
-
amountNight,
|
|
1574
|
-
signal,
|
|
1575
|
-
onSync,
|
|
1576
|
-
onDust,
|
|
1577
|
-
onProving,
|
|
1578
|
-
onSubmitting,
|
|
1579
|
-
onSyncWarning
|
|
1580
|
-
} = params;
|
|
1581
|
-
const amount = nightToMicro(amountNight);
|
|
1582
|
-
validateRecipientAddress(recipientAddress, networkConfig);
|
|
1583
|
-
const unsuppress = suppressSdkTransientErrors(onSyncWarning);
|
|
1584
|
-
const bundle = buildFacade(seedBuffer, networkConfig);
|
|
1585
|
-
let shutdownComplete = false;
|
|
1586
|
-
const cleanup = async () => {
|
|
1587
|
-
if (!shutdownComplete) {
|
|
1588
|
-
shutdownComplete = true;
|
|
1589
|
-
try {
|
|
1590
|
-
await stopFacade(bundle);
|
|
1591
|
-
} catch {
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
};
|
|
1595
|
-
const onAbort = () => {
|
|
1596
|
-
cleanup();
|
|
1597
|
-
};
|
|
1598
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1599
|
-
try {
|
|
1600
|
-
const syncedState = await startAndSyncFacade(bundle, onSync);
|
|
1601
|
-
if (signal?.aborted)
|
|
1602
|
-
throw new Error("Operation cancelled");
|
|
1603
|
-
const unshieldedBalance = syncedState.unshielded.balances[ledger3.unshieldedToken().raw] ?? 0n;
|
|
1604
|
-
if (unshieldedBalance < amount) {
|
|
1605
|
-
const haveNight = Number(unshieldedBalance) / TOKEN_MULTIPLIER;
|
|
1606
|
-
throw new Error(`Insufficient balance: ${haveNight.toFixed(6)} NIGHT available, ` + `${amountNight} NIGHT requested`);
|
|
1607
|
-
}
|
|
1608
|
-
if (signal?.aborted)
|
|
1609
|
-
throw new Error("Operation cancelled");
|
|
1610
|
-
await ensureDust(bundle, onDust);
|
|
1611
|
-
if (signal?.aborted)
|
|
1612
|
-
throw new Error("Operation cancelled");
|
|
1613
|
-
const txHash = await buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting);
|
|
1614
|
-
return { txHash, amountMicroNight: amount };
|
|
1615
|
-
} finally {
|
|
1616
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1617
|
-
unsuppress();
|
|
1618
|
-
await cleanup();
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
var NETWORK_ID_MAP3;
|
|
1622
|
-
var init_transfer = __esm(() => {
|
|
1623
|
-
init_facade();
|
|
1624
|
-
NETWORK_ID_MAP3 = {
|
|
1625
|
-
PreProd: NetworkId3.NetworkId.PreProd,
|
|
1626
|
-
Preview: NetworkId3.NetworkId.Preview,
|
|
1627
|
-
Undeployed: NetworkId3.NetworkId.Undeployed
|
|
1628
|
-
};
|
|
1629
|
-
});
|
|
1630
|
-
|
|
1631
|
-
// src/commands/airdrop.ts
|
|
1632
|
-
var exports_airdrop = {};
|
|
1633
|
-
__export(exports_airdrop, {
|
|
1634
|
-
default: () => airdropCommand
|
|
1635
|
-
});
|
|
1636
|
-
async function airdropCommand(args, signal) {
|
|
1637
|
-
const amountStr = args.subcommand;
|
|
1638
|
-
if (!amountStr) {
|
|
1639
|
-
throw new Error(`Missing amount.
|
|
1640
|
-
` + `Usage: midnight airdrop <amount>
|
|
1641
|
-
` + "Example: midnight airdrop 1000");
|
|
1642
|
-
}
|
|
1643
|
-
const amountNight = parseAmount(amountStr);
|
|
1644
|
-
const walletPath = getFlag(args, "wallet");
|
|
1645
|
-
const config = loadWalletConfig(walletPath);
|
|
1646
|
-
const { name: networkName, config: networkConfig } = resolveNetwork({
|
|
1647
|
-
args,
|
|
1648
|
-
walletNetwork: config.network,
|
|
1649
|
-
address: config.address
|
|
1650
|
-
});
|
|
1651
|
-
if (networkName !== "undeployed") {
|
|
1652
|
-
throw new Error(`Airdrop is only available on the "undeployed" network (local devnet).
|
|
1653
|
-
` + `Current network: "${networkName}"
|
|
1654
|
-
` + `On preprod/preview, use a faucet or transfer from another wallet.`);
|
|
1655
|
-
}
|
|
1656
|
-
const recipientAddress = config.address;
|
|
1657
|
-
const genesisSeedBuffer = Buffer.from(GENESIS_SEED, "hex");
|
|
1658
|
-
process.stderr.write(`
|
|
1659
|
-
` + header("Airdrop") + `
|
|
1660
|
-
|
|
1661
|
-
`);
|
|
1662
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
1663
|
-
`);
|
|
1664
|
-
process.stderr.write(keyValue("From", dim("genesis (seed 0x01)")) + `
|
|
1665
|
-
`);
|
|
1666
|
-
process.stderr.write(keyValue("To", formatAddress(recipientAddress, true)) + `
|
|
1667
|
-
`);
|
|
1668
|
-
process.stderr.write(keyValue("Amount", bold(amountNight + " NIGHT")) + `
|
|
1669
|
-
`);
|
|
1670
|
-
process.stderr.write(`
|
|
1671
|
-
`);
|
|
1672
|
-
const spinner = start("Starting genesis wallet...");
|
|
1673
|
-
try {
|
|
1674
|
-
const result = await executeTransfer({
|
|
1675
|
-
seedBuffer: genesisSeedBuffer,
|
|
1676
|
-
networkConfig,
|
|
1677
|
-
recipientAddress,
|
|
1678
|
-
amountNight,
|
|
1679
|
-
signal,
|
|
1680
|
-
onSync(applied, highest) {
|
|
1681
|
-
if (highest > 0) {
|
|
1682
|
-
const pct = Math.round(applied / highest * 100);
|
|
1683
|
-
spinner.update(`Syncing genesis wallet... ${pct}%`);
|
|
1684
|
-
}
|
|
1685
|
-
},
|
|
1686
|
-
onDust(status) {
|
|
1687
|
-
spinner.update(`Dust: ${status}`);
|
|
1688
|
-
},
|
|
1689
|
-
onProving() {
|
|
1690
|
-
spinner.update("Generating ZK proof (this may take a few minutes)...");
|
|
1691
|
-
},
|
|
1692
|
-
onSubmitting() {
|
|
1693
|
-
spinner.update("Submitting transaction...");
|
|
1694
|
-
},
|
|
1695
|
-
onSyncWarning(_tag, msg) {
|
|
1696
|
-
spinner.update(`Syncing genesis wallet... (${msg}, retrying)`);
|
|
1697
|
-
}
|
|
1698
|
-
});
|
|
1699
|
-
spinner.stop("Transaction submitted");
|
|
1700
|
-
if (hasFlag(args, "json")) {
|
|
1701
|
-
writeJsonResult({
|
|
1702
|
-
txHash: result.txHash,
|
|
1703
|
-
amount: amountNight,
|
|
1704
|
-
recipient: recipientAddress,
|
|
1705
|
-
network: networkName
|
|
1706
|
-
});
|
|
1707
|
-
return;
|
|
1708
|
-
}
|
|
1709
|
-
process.stdout.write(result.txHash + `
|
|
1710
|
-
`);
|
|
1711
|
-
process.stderr.write(`
|
|
1712
|
-
` + successMessage(`Airdropped ${amountNight} NIGHT to your wallet`, result.txHash) + `
|
|
1713
|
-
`);
|
|
1714
|
-
process.stderr.write(`
|
|
1715
|
-
` + divider() + `
|
|
1716
|
-
`);
|
|
1717
|
-
process.stderr.write(dim(" Verify: midnight balance") + `
|
|
1718
|
-
|
|
1719
|
-
`);
|
|
1720
|
-
} catch (err) {
|
|
1721
|
-
spinner.stop("Failed");
|
|
1722
|
-
if (err instanceof Error && err.message.toLowerCase().includes("dust")) {
|
|
1723
|
-
throw new Error(`${err.message}
|
|
1724
|
-
|
|
1725
|
-
` + `On a fresh localnet, the minimum airdrop is ~1 NIGHT.
|
|
1726
|
-
` + `Try: midnight airdrop 1`);
|
|
1727
|
-
}
|
|
1728
|
-
throw err;
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
var init_airdrop = __esm(() => {
|
|
1732
|
-
init_wallet_config();
|
|
1733
|
-
init_resolve_network();
|
|
1734
|
-
init_transfer();
|
|
1735
|
-
init_format();
|
|
1736
|
-
init_spinner();
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
|
-
// src/commands/transfer.ts
|
|
1740
|
-
var exports_transfer = {};
|
|
1741
|
-
__export(exports_transfer, {
|
|
1742
|
-
default: () => transferCommand
|
|
1743
|
-
});
|
|
1744
|
-
async function transferCommand(args, signal) {
|
|
1745
|
-
const recipientAddress = args.subcommand;
|
|
1746
|
-
const amountStr = args.positionals[0];
|
|
1747
|
-
if (!recipientAddress) {
|
|
1748
|
-
throw new Error(`Missing recipient address.
|
|
1749
|
-
` + `Usage: midnight transfer <to> <amount>
|
|
1750
|
-
` + "Example: midnight transfer mn_addr_undeployed1... 100");
|
|
1751
|
-
}
|
|
1752
|
-
if (!amountStr) {
|
|
1753
|
-
throw new Error(`Missing amount.
|
|
1754
|
-
` + `Usage: midnight transfer <to> <amount>
|
|
1755
|
-
` + "Example: midnight transfer mn_addr_undeployed1... 100");
|
|
1756
|
-
}
|
|
1757
|
-
const amountNight = parseAmount(amountStr);
|
|
1758
|
-
const walletPath = getFlag(args, "wallet");
|
|
1759
|
-
const config = loadWalletConfig(walletPath);
|
|
1760
|
-
const seedBuffer = Buffer.from(config.seed, "hex");
|
|
1761
|
-
const { name: networkName, config: networkConfig } = resolveNetwork({
|
|
1762
|
-
args,
|
|
1763
|
-
walletNetwork: config.network,
|
|
1764
|
-
address: config.address
|
|
1765
|
-
});
|
|
1766
|
-
process.stderr.write(`
|
|
1767
|
-
` + header("Transfer") + `
|
|
1768
|
-
|
|
1769
|
-
`);
|
|
1770
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
1771
|
-
`);
|
|
1772
|
-
process.stderr.write(keyValue("From", formatAddress(config.address, true)) + `
|
|
1773
|
-
`);
|
|
1774
|
-
process.stderr.write(keyValue("To", formatAddress(recipientAddress, true)) + `
|
|
1775
|
-
`);
|
|
1776
|
-
process.stderr.write(keyValue("Amount", bold(amountNight + " NIGHT")) + `
|
|
1777
|
-
`);
|
|
1778
|
-
process.stderr.write(`
|
|
1779
|
-
`);
|
|
1780
|
-
const spinner = start("Starting wallet...");
|
|
1781
|
-
try {
|
|
1782
|
-
const result = await executeTransfer({
|
|
1783
|
-
seedBuffer,
|
|
1784
|
-
networkConfig,
|
|
1785
|
-
recipientAddress,
|
|
1786
|
-
amountNight,
|
|
1787
|
-
signal,
|
|
1788
|
-
onSync(applied, highest) {
|
|
1789
|
-
if (highest > 0) {
|
|
1790
|
-
const pct = Math.round(applied / highest * 100);
|
|
1791
|
-
spinner.update(`Syncing wallet... ${pct}%`);
|
|
1792
|
-
}
|
|
1793
|
-
},
|
|
1794
|
-
onDust(status) {
|
|
1795
|
-
spinner.update(`Dust: ${status}`);
|
|
1796
|
-
},
|
|
1797
|
-
onProving() {
|
|
1798
|
-
spinner.update("Generating ZK proof (this may take a few minutes)...");
|
|
1799
|
-
},
|
|
1800
|
-
onSubmitting() {
|
|
1801
|
-
spinner.update("Submitting transaction...");
|
|
1802
|
-
},
|
|
1803
|
-
onSyncWarning(_tag, msg) {
|
|
1804
|
-
spinner.update(`Syncing wallet... (${msg}, retrying)`);
|
|
1805
|
-
}
|
|
1806
|
-
});
|
|
1807
|
-
spinner.stop("Transaction submitted");
|
|
1808
|
-
if (hasFlag(args, "json")) {
|
|
1809
|
-
writeJsonResult({
|
|
1810
|
-
txHash: result.txHash,
|
|
1811
|
-
amount: amountNight,
|
|
1812
|
-
recipient: recipientAddress,
|
|
1813
|
-
network: networkName
|
|
1814
|
-
});
|
|
1815
|
-
return;
|
|
1816
|
-
}
|
|
1817
|
-
process.stdout.write(result.txHash + `
|
|
1818
|
-
`);
|
|
1819
|
-
process.stderr.write(`
|
|
1820
|
-
` + successMessage(`Transferred ${amountNight} NIGHT`, result.txHash) + `
|
|
1821
|
-
`);
|
|
1822
|
-
process.stderr.write(`
|
|
1823
|
-
` + divider() + `
|
|
1824
|
-
`);
|
|
1825
|
-
process.stderr.write(dim(" Verify: midnight balance") + `
|
|
1826
|
-
|
|
1827
|
-
`);
|
|
1828
|
-
} catch (err) {
|
|
1829
|
-
spinner.stop("Failed");
|
|
1830
|
-
throw err;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
var init_transfer2 = __esm(() => {
|
|
1834
|
-
init_wallet_config();
|
|
1835
|
-
init_resolve_network();
|
|
1836
|
-
init_transfer();
|
|
1837
|
-
init_format();
|
|
1838
|
-
init_spinner();
|
|
1839
|
-
});
|
|
1840
|
-
|
|
1841
|
-
// src/commands/dust.ts
|
|
1842
|
-
var exports_dust = {};
|
|
1843
|
-
__export(exports_dust, {
|
|
1844
|
-
default: () => dustCommand
|
|
1845
|
-
});
|
|
1846
|
-
import * as ledger4 from "@midnight-ntwrk/ledger-v7";
|
|
1847
|
-
import * as rx3 from "rxjs";
|
|
1848
|
-
async function dustCommand(args, signal) {
|
|
1849
|
-
const subcommand = args.subcommand;
|
|
1850
|
-
if (!subcommand || subcommand !== "register" && subcommand !== "status") {
|
|
1851
|
-
throw new Error(`Missing or invalid subcommand.
|
|
1852
|
-
` + `Usage:
|
|
1853
|
-
` + ` midnight dust register Register NIGHT UTXOs for dust generation
|
|
1854
|
-
` + " midnight dust status Check dust registration status");
|
|
1855
|
-
}
|
|
1856
|
-
const walletPath = getFlag(args, "wallet");
|
|
1857
|
-
const config = loadWalletConfig(walletPath);
|
|
1858
|
-
const seedBuffer = Buffer.from(config.seed, "hex");
|
|
1859
|
-
const { name: networkName, config: networkConfig } = resolveNetwork({
|
|
1860
|
-
args,
|
|
1861
|
-
walletNetwork: config.network,
|
|
1862
|
-
address: config.address
|
|
1863
|
-
});
|
|
1864
|
-
const bundle = buildFacade(seedBuffer, networkConfig);
|
|
1865
|
-
const cleanup = async () => {
|
|
1866
|
-
try {
|
|
1867
|
-
await stopFacade(bundle);
|
|
1868
|
-
} catch {
|
|
1869
|
-
}
|
|
1870
|
-
};
|
|
1871
|
-
const onAbort = () => {
|
|
1872
|
-
cleanup();
|
|
1873
|
-
};
|
|
1874
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1875
|
-
const warningRef = {};
|
|
1876
|
-
const unsuppress = suppressSdkTransientErrors((tag, msg) => {
|
|
1877
|
-
warningRef.current?.(tag, msg);
|
|
1878
|
-
});
|
|
1879
|
-
const isJson = hasFlag(args, "json");
|
|
1880
|
-
try {
|
|
1881
|
-
if (subcommand === "register") {
|
|
1882
|
-
await dustRegister(bundle, networkName, isJson, signal, warningRef);
|
|
1883
|
-
} else {
|
|
1884
|
-
await dustStatus(bundle, networkName, isJson, signal, warningRef);
|
|
1885
|
-
}
|
|
1886
|
-
} finally {
|
|
1887
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1888
|
-
unsuppress();
|
|
1889
|
-
await cleanup();
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
async function dustRegister(bundle, networkName, jsonMode, signal, warningRef) {
|
|
1893
|
-
process.stderr.write(`
|
|
1894
|
-
` + header("Dust Register") + `
|
|
1895
|
-
|
|
1896
|
-
`);
|
|
1897
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
1898
|
-
|
|
1899
|
-
`);
|
|
1900
|
-
const spinner = start("Syncing wallet...");
|
|
1901
|
-
if (warningRef) {
|
|
1902
|
-
warningRef.current = (_tag, msg) => spinner.update(`Syncing wallet... (${msg}, retrying)`);
|
|
1903
|
-
}
|
|
1904
|
-
try {
|
|
1905
|
-
await startAndSyncFacade(bundle, (applied, highest) => {
|
|
1906
|
-
if (highest > 0) {
|
|
1907
|
-
const pct = Math.round(applied / highest * 100);
|
|
1908
|
-
spinner.update(`Syncing wallet... ${pct}%`);
|
|
1909
|
-
}
|
|
1910
|
-
});
|
|
1911
|
-
if (signal?.aborted)
|
|
1912
|
-
throw new Error("Operation cancelled");
|
|
1913
|
-
spinner.update("Checking dust status...");
|
|
1914
|
-
const state = await rx3.firstValueFrom(bundle.facade.state().pipe(rx3.filter((s) => s.isSynced)));
|
|
1915
|
-
if (state.dust.availableCoins.length > 0) {
|
|
1916
|
-
const dustBal2 = state.dust.walletBalance(new Date);
|
|
1917
|
-
spinner.stop("Dust already available");
|
|
1918
|
-
if (jsonMode) {
|
|
1919
|
-
writeJsonResult({ subcommand: "register", dustBalance: toDust(dustBal2) });
|
|
1920
|
-
return;
|
|
1921
|
-
}
|
|
1922
|
-
process.stdout.write(dustBal2.toString() + `
|
|
1923
|
-
`);
|
|
1924
|
-
process.stderr.write(`
|
|
1925
|
-
` + successMessage(`Dust tokens already available: ${formatDust(dustBal2)}`) + `
|
|
1926
|
-
|
|
1927
|
-
`);
|
|
1928
|
-
return;
|
|
1929
|
-
}
|
|
1930
|
-
const nightUtxos = state.unshielded.availableCoins.filter((coin) => coin.meta?.registeredForDustGeneration !== true);
|
|
1931
|
-
let txHash;
|
|
1932
|
-
if (nightUtxos.length === 0) {
|
|
1933
|
-
spinner.update("All UTXOs already registered, waiting for dust generation...");
|
|
1934
|
-
} else {
|
|
1935
|
-
spinner.update(`Registering ${nightUtxos.length} UTXO(s) for dust generation...`);
|
|
1936
|
-
const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
|
|
1937
|
-
const dustReceiverAddress = state.dust.dustAddress;
|
|
1938
|
-
const dustUtxos = nightUtxos.map((coin) => ({
|
|
1939
|
-
...coin.utxo,
|
|
1940
|
-
ctime: new Date(coin.meta.ctime)
|
|
1941
|
-
}));
|
|
1942
|
-
await bundle.facade.dust.waitForSyncedState();
|
|
1943
|
-
const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
|
|
1944
|
-
const intent = unprovenTx.intents?.get(1);
|
|
1945
|
-
if (!intent) {
|
|
1946
|
-
throw new Error("Dust generation intent not found on transaction");
|
|
1947
|
-
}
|
|
1948
|
-
const signature = bundle.keystore.signData(intent.signatureData(1));
|
|
1949
|
-
const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
|
|
1950
|
-
const finalized = await bundle.facade.finalizeTransaction(signedTx);
|
|
1951
|
-
txHash = await bundle.facade.submitTransaction(finalized);
|
|
1952
|
-
spinner.update(`Registration submitted (${txHash.slice(0, 12)}...), waiting for dust...`);
|
|
1953
|
-
}
|
|
1954
|
-
if (signal?.aborted)
|
|
1955
|
-
throw new Error("Operation cancelled");
|
|
1956
|
-
const dustState = await rx3.firstValueFrom(bundle.facade.state().pipe(rx3.throttleTime(5000), rx3.filter((s) => s.isSynced), rx3.filter((s) => s.dust.walletBalance(new Date) > 0n), rx3.timeout(DUST_TIMEOUT_MS)));
|
|
1957
|
-
const dustBal = dustState.dust.walletBalance(new Date);
|
|
1958
|
-
spinner.stop("Dust registration complete");
|
|
1959
|
-
if (jsonMode) {
|
|
1960
|
-
const result = { subcommand: "register", dustBalance: toDust(dustBal) };
|
|
1961
|
-
if (txHash)
|
|
1962
|
-
result.txHash = txHash;
|
|
1963
|
-
writeJsonResult(result);
|
|
1964
|
-
return;
|
|
1965
|
-
}
|
|
1966
|
-
process.stdout.write(dustBal.toString() + `
|
|
1967
|
-
`);
|
|
1968
|
-
process.stderr.write(`
|
|
1969
|
-
` + successMessage(`Dust tokens available: ${formatDust(dustBal)}`) + `
|
|
1970
|
-
|
|
1971
|
-
`);
|
|
1972
|
-
} catch (err) {
|
|
1973
|
-
spinner.stop("Failed");
|
|
1974
|
-
throw err;
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
async function dustStatus(bundle, networkName, jsonMode, signal, warningRef) {
|
|
1978
|
-
process.stderr.write(`
|
|
1979
|
-
` + header("Dust Status") + `
|
|
1980
|
-
|
|
1981
|
-
`);
|
|
1982
|
-
process.stderr.write(keyValue("Network", networkName) + `
|
|
1983
|
-
|
|
1984
|
-
`);
|
|
1985
|
-
const spinner = start("Syncing wallet...");
|
|
1986
|
-
if (warningRef) {
|
|
1987
|
-
warningRef.current = (_tag, msg) => spinner.update(`Syncing wallet... (${msg}, retrying)`);
|
|
1988
|
-
}
|
|
1989
|
-
try {
|
|
1990
|
-
await startAndSyncFacade(bundle, (applied, highest) => {
|
|
1991
|
-
if (highest > 0) {
|
|
1992
|
-
const pct = Math.round(applied / highest * 100);
|
|
1993
|
-
spinner.update(`Syncing wallet... ${pct}%`);
|
|
1994
|
-
}
|
|
1995
|
-
});
|
|
1996
|
-
if (signal?.aborted)
|
|
1997
|
-
throw new Error("Operation cancelled");
|
|
1998
|
-
spinner.update("Checking dust status...");
|
|
1999
|
-
const state = await rx3.firstValueFrom(bundle.facade.state().pipe(rx3.filter((s) => s.isSynced)));
|
|
2000
|
-
const dustBalance = state.dust.walletBalance(new Date);
|
|
2001
|
-
const hasAvailableDust = state.dust.availableCoins.length > 0;
|
|
2002
|
-
const allUtxos = state.unshielded.availableCoins;
|
|
2003
|
-
const unregisteredUtxos = allUtxos.filter((coin) => coin.meta?.registeredForDustGeneration !== true);
|
|
2004
|
-
const registeredCount = allUtxos.length - unregisteredUtxos.length;
|
|
2005
|
-
const unshieldedBalance = state.unshielded.balances[ledger4.unshieldedToken().raw] ?? 0n;
|
|
2006
|
-
spinner.stop("Done");
|
|
2007
|
-
if (jsonMode) {
|
|
2008
|
-
writeJsonResult({
|
|
2009
|
-
subcommand: "status",
|
|
2010
|
-
dustBalance: toDust(dustBalance),
|
|
2011
|
-
registered: registeredCount,
|
|
2012
|
-
unregistered: unregisteredUtxos.length,
|
|
2013
|
-
nightBalance: toNight(unshieldedBalance),
|
|
2014
|
-
dustAvailable: hasAvailableDust
|
|
2015
|
-
});
|
|
2016
|
-
return;
|
|
2017
|
-
}
|
|
2018
|
-
process.stdout.write(`dust=${dustBalance}
|
|
2019
|
-
`);
|
|
2020
|
-
process.stdout.write(`registered=${registeredCount}
|
|
2021
|
-
`);
|
|
2022
|
-
process.stdout.write(`unregistered=${unregisteredUtxos.length}
|
|
2023
|
-
`);
|
|
2024
|
-
process.stderr.write(keyValue("NIGHT Balance", bold(formatNight(unshieldedBalance))) + `
|
|
2025
|
-
`);
|
|
2026
|
-
process.stderr.write(keyValue("Dust Balance", bold(formatDust(dustBalance))) + `
|
|
2027
|
-
`);
|
|
2028
|
-
process.stderr.write(keyValue("Dust Available", hasAvailableDust ? "yes" : "no") + `
|
|
2029
|
-
`);
|
|
2030
|
-
process.stderr.write(keyValue("Registered", registeredCount.toString() + " UTXO(s)") + `
|
|
2031
|
-
`);
|
|
2032
|
-
process.stderr.write(keyValue("Unregistered", unregisteredUtxos.length.toString() + " UTXO(s)") + `
|
|
2033
|
-
`);
|
|
2034
|
-
process.stderr.write(`
|
|
2035
|
-
` + divider() + `
|
|
2036
|
-
|
|
2037
|
-
`);
|
|
2038
|
-
} catch (err) {
|
|
2039
|
-
spinner.stop("Failed");
|
|
2040
|
-
throw err;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
var init_dust = __esm(() => {
|
|
2044
|
-
init_wallet_config();
|
|
2045
|
-
init_resolve_network();
|
|
2046
|
-
init_facade();
|
|
2047
|
-
init_format();
|
|
2048
|
-
init_spinner();
|
|
2049
|
-
});
|
|
2050
|
-
|
|
2051
|
-
// src/commands/config.ts
|
|
2052
|
-
var exports_config = {};
|
|
2053
|
-
__export(exports_config, {
|
|
2054
|
-
default: () => configCommand
|
|
2055
|
-
});
|
|
2056
|
-
async function configCommand(args) {
|
|
2057
|
-
const action = args.subcommand;
|
|
2058
|
-
if (!action || action !== "get" && action !== "set") {
|
|
2059
|
-
throw new Error(`Usage: midnight config <get|set> <key> [value]
|
|
2060
|
-
` + `Valid keys: ${getValidConfigKeys().join(", ")}`);
|
|
2061
|
-
}
|
|
2062
|
-
const key = args.positionals[0];
|
|
2063
|
-
if (!key) {
|
|
2064
|
-
throw new Error(`Missing config key.
|
|
2065
|
-
` + `Valid keys: ${getValidConfigKeys().join(", ")}`);
|
|
2066
|
-
}
|
|
2067
|
-
if (action === "get") {
|
|
2068
|
-
const value = getConfigValue(key);
|
|
2069
|
-
if (hasFlag(args, "json")) {
|
|
2070
|
-
writeJsonResult({ action: "get", key, value });
|
|
2071
|
-
return;
|
|
2072
|
-
}
|
|
2073
|
-
process.stdout.write(value + `
|
|
2074
|
-
`);
|
|
2075
|
-
} else {
|
|
2076
|
-
const value = args.positionals[1];
|
|
2077
|
-
if (value === undefined) {
|
|
2078
|
-
throw new Error(`Missing value for config set.
|
|
2079
|
-
Usage: midnight config set ${key} <value>`);
|
|
2080
|
-
}
|
|
2081
|
-
setConfigValue(key, value);
|
|
2082
|
-
if (hasFlag(args, "json")) {
|
|
2083
|
-
writeJsonResult({ action: "set", key, value });
|
|
2084
|
-
return;
|
|
2085
|
-
}
|
|
2086
|
-
process.stderr.write(green("✓") + ` ${key} = ${value}
|
|
2087
|
-
`);
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
var init_config = __esm(() => {
|
|
2091
|
-
init_cli_config();
|
|
2092
|
-
});
|
|
2093
|
-
|
|
2094
|
-
// src/lib/localnet.ts
|
|
2095
|
-
import { execSync as execSync2 } from "child_process";
|
|
2096
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2097
|
-
import { homedir as homedir5 } from "os";
|
|
2098
|
-
import { join as join5 } from "path";
|
|
2099
|
-
function checkDockerAvailable() {
|
|
2100
|
-
try {
|
|
2101
|
-
const output = execSync2("docker compose version", { ...EXEC_OPTIONS, timeout: 1e4 });
|
|
2102
|
-
return output.trim();
|
|
2103
|
-
} catch {
|
|
2104
|
-
try {
|
|
2105
|
-
execSync2("docker --version", { ...EXEC_OPTIONS, timeout: 5000 });
|
|
2106
|
-
throw new Error(`Docker Compose v2 is required.
|
|
2107
|
-
` + "Install it from https://docs.docker.com/compose/install/");
|
|
2108
|
-
} catch (innerErr) {
|
|
2109
|
-
if (innerErr instanceof Error && innerErr.message.includes("Docker Compose v2")) {
|
|
2110
|
-
throw innerErr;
|
|
2111
|
-
}
|
|
2112
|
-
throw new Error(`Docker is required but was not found.
|
|
2113
|
-
` + "Install Docker from https://docs.docker.com/get-docker/");
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
function ensureComposeFile() {
|
|
2118
|
-
const versionMatches = existsSync4(VERSION_PATH) && existsSync4(COMPOSE_PATH) && readFileSync3(VERSION_PATH, "utf-8").trim() === COMPOSE_VERSION;
|
|
2119
|
-
if (versionMatches)
|
|
2120
|
-
return false;
|
|
2121
|
-
mkdirSync3(LOCALNET_DIR, { recursive: true, mode: DIR_MODE });
|
|
2122
|
-
writeFileSync3(COMPOSE_PATH, COMPOSE_YAML, "utf-8");
|
|
2123
|
-
writeFileSync3(VERSION_PATH, COMPOSE_VERSION, "utf-8");
|
|
2124
|
-
return true;
|
|
2125
|
-
}
|
|
2126
|
-
function dockerCompose(args) {
|
|
2127
|
-
return execSync2(`docker compose -f "${COMPOSE_PATH}" ${args}`, EXEC_OPTIONS);
|
|
2128
|
-
}
|
|
2129
|
-
function getServiceStatus() {
|
|
2130
|
-
try {
|
|
2131
|
-
const output = dockerCompose("ps --format json");
|
|
2132
|
-
if (!output.trim())
|
|
2133
|
-
return [];
|
|
2134
|
-
const lines = output.trim().split(`
|
|
2135
|
-
`);
|
|
2136
|
-
const services = [];
|
|
2137
|
-
for (const line of lines) {
|
|
2138
|
-
if (!line.trim())
|
|
2139
|
-
continue;
|
|
2140
|
-
try {
|
|
2141
|
-
const obj = JSON.parse(line);
|
|
2142
|
-
const name = obj.Service ?? "unknown";
|
|
2143
|
-
services.push({
|
|
2144
|
-
name,
|
|
2145
|
-
state: obj.State ?? "unknown",
|
|
2146
|
-
health: obj.Health ?? "",
|
|
2147
|
-
port: PORT_MAP[name] ?? ""
|
|
2148
|
-
});
|
|
2149
|
-
} catch {
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
return services;
|
|
2153
|
-
} catch {
|
|
2154
|
-
return [];
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
|
-
function waitForHealthy(timeoutMs = 120000, intervalMs = 3000) {
|
|
2158
|
-
const deadline = Date.now() + timeoutMs;
|
|
2159
|
-
while (Date.now() < deadline) {
|
|
2160
|
-
const services = getServiceStatus();
|
|
2161
|
-
const allRunning = services.length === 3 && services.every((s) => s.state === "running");
|
|
2162
|
-
if (allRunning) {
|
|
2163
|
-
const healthyOrNoCheck = services.every((s) => s.health === "healthy" || s.health === "");
|
|
2164
|
-
if (healthyOrNoCheck)
|
|
2165
|
-
return true;
|
|
2166
|
-
}
|
|
2167
|
-
execSync2(`sleep ${intervalMs / 1000}`, { timeout: intervalMs + 1000 });
|
|
2168
|
-
}
|
|
2169
|
-
return false;
|
|
2170
|
-
}
|
|
2171
|
-
function getComposePath() {
|
|
2172
|
-
return COMPOSE_PATH;
|
|
2173
|
-
}
|
|
2174
|
-
function removeConflictingContainers() {
|
|
2175
|
-
const removed = [];
|
|
2176
|
-
for (const name of CONTAINER_NAMES) {
|
|
2177
|
-
try {
|
|
2178
|
-
execSync2(`docker rm -f "${name}"`, { ...EXEC_OPTIONS, timeout: 1e4 });
|
|
2179
|
-
removed.push(name);
|
|
2180
|
-
} catch {
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
return removed;
|
|
2184
|
-
}
|
|
2185
|
-
var COMPOSE_VERSION = "1.4.0", LOCALNET_DIR, COMPOSE_PATH, VERSION_PATH, COMPOSE_YAML = `services:
|
|
62
|
+
`;var k3=()=>{};function v($){if(!l())return process.stderr.write(`⠋ ${$}`),{update(z){process.stderr.write(`\r⠋ ${z}`)},stop(z){let Y=z??$;process.stderr.write(`\r✓ ${Y}
|
|
63
|
+
`)}};let Z=0,Q=$,X=()=>{let z=a(b3[Z]);process.stderr.write(`\r${z} ${Q}\x1B[K`),Z=(Z+1)%b3.length};X();let q=setInterval(X,c2);return{update(z){Q=z},stop(z){clearInterval(q);let Y=z??Q;process.stderr.write(`\r\x1B[32m✓\x1B[0m ${Y}\x1B[K
|
|
64
|
+
`)}}}var b3,c2=80;var G0=O(()=>{b3=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"]});var W1={};w(W1,{default:()=>w3});async function w3($){let Z,Q;if($.subcommand)Z=$.subcommand;else{let G=A($,"wallet"),U=c(G);Z=U.address,Q=U.network}if(!Z)throw new Error("No address provided and no wallet file found.");let{name:X,config:q}=t({args:$,walletNetwork:Q,address:Z}),Y=A($,"indexer-ws")??q.indexerWS,K=v(`Checking balance on ${X}...`);try{let G=await v3(Z,Y,(U,W)=>{if(W>0){let J=Math.round(U/W*100);K.update(`Syncing transactions... ${J}%`)}});if(K.stop(`Synced ${G.txCount} transactions`),P($,"json")){let U={};for(let[W,J]of G.balances){let V=L0(W)?"NIGHT":W;U[V]=L0(W)?F0(J):J.toString()}L({address:Z,network:X,balances:U,utxoCount:G.utxoCount,txCount:G.txCount});return}if(G.balances.size===0)process.stdout.write(`0
|
|
65
|
+
`);else for(let[U,W]of G.balances)if(L0(U))process.stdout.write(`NIGHT=${W}
|
|
66
|
+
`);else process.stdout.write(`${U}=${W}
|
|
67
|
+
`);if(process.stderr.write(`
|
|
68
|
+
`+_("Balance")+`
|
|
69
|
+
|
|
70
|
+
`),process.stderr.write(H("Address",E(Z))+`
|
|
71
|
+
`),process.stderr.write(H("Network",X)+`
|
|
72
|
+
`),process.stderr.write(H("UTXOs",G.utxoCount.toString())+`
|
|
73
|
+
`),process.stderr.write(H("Transactions",G.txCount.toString())+`
|
|
74
|
+
`),process.stderr.write(`
|
|
75
|
+
`),G.balances.size===0)process.stderr.write(` ${j("No balance found")}
|
|
76
|
+
`);else for(let[U,W]of G.balances)if(L0(U))process.stderr.write(H("NIGHT",y(E0(W)))+`
|
|
77
|
+
`);else{let J=U.slice(0,8)+"…"+U.slice(-8);process.stderr.write(H(`Token ${J}`,y(W.toString()))+`
|
|
78
|
+
`)}process.stderr.write(`
|
|
79
|
+
`+D()+`
|
|
80
|
+
|
|
81
|
+
`)}catch(G){throw K.stop("Failed"),G}}var V1=O(()=>{q0();r();k3();N();G0()});var H1={};w(H1,{default:()=>S3});async function S3($){let Z=n1($,"seed","hex").replace(/^0x/,"");if(Z.length!==64||!/^[0-9a-fA-F]+$/.test(Z))throw new Error("Seed must be a 64-character hex string (32 bytes)");let Q=A($,"index"),X=Q!==void 0?parseInt(Q,10):0;if(isNaN(X)||X<0||!Number.isInteger(Number(Q??"0")))throw new Error("Key index must be a non-negative integer");let q=Buffer.from(Z,"hex"),z=X0({args:$}),Y=K0(q,z,X),K=`m/44'/2400'/0'/NightExternal/${X}`;if(P($,"json")){L({address:Y,network:z,index:X,path:K});return}process.stdout.write(Y+`
|
|
82
|
+
`),process.stderr.write(`
|
|
83
|
+
`),process.stderr.write(H("Network",z)+`
|
|
84
|
+
`),process.stderr.write(H("Index",X.toString())+`
|
|
85
|
+
`),process.stderr.write(H("Address",E(Y))+`
|
|
86
|
+
`),process.stderr.write(H("Path",j(K))+`
|
|
87
|
+
`),process.stderr.write(D()+`
|
|
88
|
+
|
|
89
|
+
`)}var j1=O(()=>{b0();r();N()});var F1={};w(F1,{default:()=>C3});async function C3($){let Z=X0({args:$}),Q=Buffer.from(_0,"hex"),X=K0(Q,Z);if(P($,"json")){L({address:X,network:Z});return}process.stdout.write(X+`
|
|
90
|
+
`),process.stderr.write(`
|
|
91
|
+
`),process.stderr.write(H("Network",Z)+`
|
|
92
|
+
`),process.stderr.write(H("Address",E(X))+`
|
|
93
|
+
`),process.stderr.write(H("Seed",j("0x01 (genesis)"))+`
|
|
94
|
+
`),process.stderr.write(D()+`
|
|
95
|
+
|
|
96
|
+
`)}var P1=O(()=>{b0();r();N()});var O1={};w(O1,{default:()=>h3});import*as f3 from"@midnight-ntwrk/ledger-v7";function y0($,Z,Q){let X={readTime:0n,computeTime:0n,blockUsage:0n,bytesWritten:0n,bytesChurned:0n};X[Z]=Q;let z=$.normalizeFullness(X)[Z];return Math.round(Number(Q)/z)}function m2($){return{readTime:y0($,"readTime",1000000000n),computeTime:y0($,"computeTime",1000000000n),blockUsage:y0($,"blockUsage",10000n),bytesWritten:y0($,"bytesWritten",10000n),bytesChurned:y0($,"bytesChurned",1000000n)}}async function h3($){let Z=f3.LedgerParameters.initialParameters(),Q=m2(Z);if(P($,"json")){L(Q);return}for(let[X,q]of Object.entries(Q))process.stdout.write(`${X}=${q}
|
|
97
|
+
`);process.stderr.write(`
|
|
98
|
+
`+_("Block Limits")+`
|
|
99
|
+
|
|
100
|
+
`),process.stderr.write(j(" Derived from LedgerParameters.initialParameters()")+`
|
|
101
|
+
|
|
102
|
+
`);for(let[X,q]of Object.entries(Q)){let z=g2[X]??"";process.stderr.write(H(X,`${y(q.toLocaleString())} ${j(z)}`)+`
|
|
103
|
+
`)}process.stderr.write(`
|
|
104
|
+
`+D()+`
|
|
105
|
+
`),process.stderr.write(j(" bytesWritten is typically the tightest constraint")+`
|
|
106
|
+
`),process.stderr.write(j(" for large contract deployments.")+`
|
|
107
|
+
|
|
108
|
+
`)}var g2;var L1=O(()=>{N();g2={readTime:"picoseconds",computeTime:"picoseconds",blockUsage:"bytes",bytesWritten:"bytes",bytesChurned:"bytes"}});import{HDWallet as d2,Roles as y1}from"@midnight-ntwrk/wallet-sdk-hd";function T1($,Z){let Q=d2.fromSeed($);if(Q.type!=="seedOk")throw new Error("Invalid seed for HD wallet");let X=Q.hdWallet.selectAccount(0).selectRole(Z).deriveKeyAt(0);if(X.type==="keyOutOfBounds")throw new Error("Key derivation out of bounds");return X.key}function p3($){return T1($,y1.Zswap)}function u3($){return T1($,y1.NightExternal)}function c3($){return T1($,y1.Dust)}var m3=()=>{};import{ShieldedWallet as l2}from"@midnight-ntwrk/wallet-sdk-shielded";import{UnshieldedWallet as i2,createKeystore as o2,PublicKey as n2,InMemoryTransactionHistoryStorage as a2}from"@midnight-ntwrk/wallet-sdk-unshielded-wallet";import{DustWallet as t2}from"@midnight-ntwrk/wallet-sdk-dust-wallet";import{WalletFacade as r2}from"@midnight-ntwrk/wallet-sdk-facade";import*as U0 from"@midnight-ntwrk/ledger-v7";import{NetworkId as M1}from"@midnight-ntwrk/wallet-sdk-abstractions";import*as f from"rxjs";function p0($,Z){let Q=s2[Z.networkId];if(Q===void 0)throw new Error(`Unknown networkId: ${Z.networkId}`);let X=p3($),q=u3($),z=c3($),Y=U0.ZswapSecretKeys.fromSeed(X),K=U0.DustSecretKey.fromSeed(z),G=o2(q,Q),U={networkId:Q,indexerClientConnection:{indexerHttpUrl:Z.indexer,indexerWsUrl:Z.indexerWS},provingServerUrl:new URL(Z.proofServer),relayURL:new URL(Z.node)},W={networkId:Q,indexerClientConnection:{indexerHttpUrl:Z.indexer,indexerWsUrl:Z.indexerWS},txHistoryStorage:new a2},J={networkId:Q,costParameters:{additionalFeeOverhead:r1,feeBlocksMargin:s1},indexerClientConnection:{indexerHttpUrl:Z.indexer,indexerWsUrl:Z.indexerWS},provingServerUrl:new URL(Z.proofServer),relayURL:new URL(Z.node)},V=l2(U).startWithSecretKeys(Y),B=i2(W).startWithPublicKey(n2.fromKeyStore(G)),F=t2(J).startWithSecretKey(K,U0.LedgerParameters.initialParameters().dust);return{facade:new r2(V,B,F),keystore:G,zswapSecretKeys:Y,dustSecretKey:K}}async function T0($,Z){let{facade:Q,zswapSecretKeys:X,dustSecretKey:q}=$;return await Q.start(X,q),f.firstValueFrom(Q.state().pipe(f.tap((z)=>{if(Z){let Y=z.unshielded.progress;if(Y)Z(Number(Y.appliedId),Number(Y.highestTransactionId))}}),f.filter((z)=>z.isSynced),f.timeout(e1)))}async function g3($){return f.firstValueFrom($.facade.state().pipe(f.filter((Z)=>Z.isSynced),f.timeout($3)))}async function u0($){await $.facade.stop()}function c0($){let Z=(X)=>{let q=X?._tag;if(typeof q==="string"&&q.startsWith("Wallet.")){let z=X?.message??"transient error";$?.(q,z);return}Q("Unhandled rejection:",X),process.exit(1)},Q=console.error;return console.error=(...X)=>{let q=X[0];if(typeof q==="object"&&q?._tag?.startsWith("Wallet.")){$?.(q._tag,q?.message??"transient error");return}if(typeof q==="string"&&q.startsWith("Wallet.")){$?.("Wallet.Sync","transient error");return}Q(...X)},process.on("unhandledRejection",Z),()=>{process.removeListener("unhandledRejection",Z),console.error=Q}}var s2;var I1=O(()=>{m3();s2={PreProd:M1.NetworkId.PreProd,Preview:M1.NetworkId.Preview,Undeployed:M1.NetworkId.Undeployed}});import*as A1 from"@midnight-ntwrk/ledger-v7";import{MidnightBech32m as e2,UnshieldedAddress as $$}from"@midnight-ntwrk/wallet-sdk-address-format";import{NetworkId as _1}from"@midnight-ntwrk/wallet-sdk-abstractions";import*as h from"rxjs";function Q$($){if($<=0)throw new Error("Amount must be greater than 0");if(!Number.isFinite($))throw new Error("Amount must be a finite number");let Z=$.toFixed(6),[Q,X]=Z.split("."),q=Q+(X??"").padEnd(6,"0"),z=BigInt(q);if(z<=0n)throw new Error("Amount too small — minimum is 0.000001 NIGHT");return z}function m0($){let Z=Number($);if(Number.isNaN(Z)||!Number.isFinite(Z))throw new Error(`Invalid amount: "${$}" — must be a positive number`);if(Z<=0)throw new Error(`Invalid amount: "${$}" — must be greater than 0`);return Z}function X$($,Z){let Q=Z$[Z.networkId];if(Q===void 0)throw new Error(`Unknown networkId: ${Z.networkId}`);try{e2.parse($).decode($$,Q)}catch(X){throw new Error(`Invalid recipient address: ${X.message}
|
|
109
|
+
Expected a bech32m address for network "${Z.networkId}"`)}}function q$($){let Z=$;while(Z){let Q=String(Z?.message??"").toLowerCase();if(Q.includes("submission error"))return!0;if(Q.includes("transaction")&&Q.includes("invalid"))return!0;if(Q.includes("138"))return!0;let X=Z?._tag;if(X==="TransactionInvalidError"||X==="SubmissionError")return!0;Z=Z.cause}return!1}function z$($){let Z=Math.round($/1000);if(Z<60)return`${Z}s`;let Q=Math.floor(Z/60),X=Z%60;return`${Q}m ${X}s`}async function Y$($,Z,Q){let X=new Date(Date.now()+e0*60*1000);await $.facade.dust.waitForSyncedState();let q=await $.facade.dust.createDustGenerationTransaction(new Date,X,Z,$.keystore.getPublicKey(),Q),z=q.intents?.get(1);if(!z)throw new Error("Dust generation intent not found on transaction");let Y=$.keystore.signData(z.signatureData(1)),K=await $.facade.dust.addDustGenerationSignature(q,Y),G=await $.facade.finalizeTransaction(K);return await $.facade.submitTransaction(G)}async function D1($,Z,Q,X){let q=Date.now(),z=q+Q3,Y,K=console.warn,G=console.error,U=(V)=>V.some((B)=>String(B).includes("RPC-CORE")),W=()=>{console.warn=(...V)=>{if(U(V))return;K(...V)},console.error=(...V)=>{if(U(V))return;G(...V)}},J=()=>{console.warn=K,console.error=G};W();try{while(Date.now()<z)try{return await Y$($,Z,Q)}catch(V){if(Y=V,q$(V)&&Date.now()+R0<z){let B=z$(Date.now()-q);X?.(`Waiting for dust generation capacity (${B} elapsed, ~5 min on fresh wallets)...`),await new Promise((F)=>setTimeout(F,R0));continue}throw V}throw Y??new Error("Dust registration timed out")}finally{J()}}async function J$($,Z){let Q=await h.firstValueFrom($.facade.state().pipe(h.filter((q)=>q.isSynced))),X=Q.unshielded.availableCoins.filter((q)=>q.meta?.registeredForDustGeneration!==!0);if(X.length>0){Z?.(`Registering ${X.length} UTXO(s) for dust generation...`);let q=X.map((z)=>({...z.utxo,ctime:new Date(z.meta.ctime)}));await D1($,q,Q.dust.dustAddress,Z)}else if(Q.dust.availableCoins.length>0){Z?.("Dust available");return}else Z?.("UTXOs already registered, waiting for dust generation...");Z?.("Waiting for dust tokens..."),await h.firstValueFrom($.facade.state().pipe(h.throttleTime(5000),h.filter((q)=>q.isSynced),h.filter((q)=>q.dust.walletBalance(new Date)>0n),h.timeout(A0))),Z?.("Dust available")}async function K$($,Z,Q,X,q,z){let Y;for(let K=1;K<=D0;K++)try{if(K>1)await g3($);let G=new Date(Date.now()+e0*60*1000),U=await $.facade.transferTransaction([{type:"unshielded",outputs:[{amount:Q,receiverAddress:Z,type:A1.unshieldedToken().raw}]}],{shieldedSecretKeys:$.zswapSecretKeys,dustSecretKey:$.dustSecretKey},{ttl:G,payFees:!0}),W=await $.facade.signRecipe(U,(B)=>$.keystore.signData(B));X?.();let J=await Promise.race([$.facade.finalizeRecipe(W),new Promise((B,F)=>{setTimeout(()=>F(new Error("ZK proof generation timed out")),Z3)})]);return q?.(),await $.facade.submitTransaction(J)}catch(G){if(Y=G,(G?.code===X3||G?.message?.includes("115")||G?.message?.toLowerCase().includes("stale"))&&K<D0)continue;if((G?.message?.toLowerCase().includes("not enough dust")||G?.message?.toLowerCase().includes("dust generated"))&&K<D0){z?.("Waiting for more dust to accumulate..."),await new Promise((J)=>setTimeout(J,R0));continue}throw G}throw Y??new Error("Transfer failed after retries")}async function g0($){let{seedBuffer:Z,networkConfig:Q,recipientAddress:X,amountNight:q,signal:z,onSync:Y,onDust:K,onProving:G,onSubmitting:U,onSyncWarning:W}=$,J=Q$(q);X$(X,Q);let V=c0(W),B=p0(Z,Q),F=!1,T=async()=>{if(!F){F=!0;try{await u0(B)}catch{}}},I=()=>{T()};z?.addEventListener("abort",I,{once:!0});try{let M=await T0(B,Y);if(z?.aborted)throw new Error("Operation cancelled");let k=M.unshielded.balances[A1.unshieldedToken().raw]??0n;if(k<J){let d=Number(k)/t1;throw new Error(`Insufficient balance: ${d.toFixed(6)} NIGHT available, ${q} NIGHT requested`)}if(z?.aborted)throw new Error("Operation cancelled");if(await J$(B,K),z?.aborted)throw new Error("Operation cancelled");return{txHash:await K$(B,X,J,G,U,K),amountMicroNight:J}}finally{z?.removeEventListener("abort",I),V(),await T()}}var Z$;var d0=O(()=>{I1();Z$={PreProd:_1.NetworkId.PreProd,Preview:_1.NetworkId.Preview,Undeployed:_1.NetworkId.Undeployed}});var R1={};w(R1,{default:()=>d3});async function d3($,Z){let Q=$.subcommand;if(!Q)throw new Error(`Missing amount.
|
|
110
|
+
Usage: midnight airdrop <amount>
|
|
111
|
+
Example: midnight airdrop 1000`);let X=m0(Q),q=A($,"wallet"),z=c(q),{name:Y,config:K}=t({args:$,walletNetwork:z.network,address:z.address});if(Y!=="undeployed")throw new Error(`Airdrop is only available on the "undeployed" network (local devnet).
|
|
112
|
+
Current network: "${Y}"
|
|
113
|
+
On preprod/preview, use a faucet or transfer from another wallet.`);let G=z.address,U=Buffer.from(_0,"hex");process.stderr.write(`
|
|
114
|
+
`+_("Airdrop")+`
|
|
115
|
+
|
|
116
|
+
`),process.stderr.write(H("Network",Y)+`
|
|
117
|
+
`),process.stderr.write(H("From",j("genesis (seed 0x01)"))+`
|
|
118
|
+
`),process.stderr.write(H("To",E(G,!0))+`
|
|
119
|
+
`),process.stderr.write(H("Amount",y(X+" NIGHT"))+`
|
|
120
|
+
`),process.stderr.write(`
|
|
121
|
+
`);let W=v("Starting genesis wallet...");try{let J=await g0({seedBuffer:U,networkConfig:K,recipientAddress:G,amountNight:X,signal:Z,onSync(V,B){if(B>0){let F=Math.round(V/B*100);W.update(`Syncing genesis wallet... ${F}%`)}},onDust(V){W.update(`Dust: ${V}`)},onProving(){W.update("Generating ZK proof (this may take a few minutes)...")},onSubmitting(){W.update("Submitting transaction...")},onSyncWarning(V,B){W.update(`Syncing genesis wallet... (${B}, retrying)`)}});if(W.stop("Transaction submitted"),P($,"json")){L({txHash:J.txHash,amount:X,recipient:G,network:Y});return}process.stdout.write(J.txHash+`
|
|
122
|
+
`),process.stderr.write(`
|
|
123
|
+
`+Z0(`Airdropped ${X} NIGHT to your wallet`,J.txHash)+`
|
|
124
|
+
`),process.stderr.write(`
|
|
125
|
+
`+D()+`
|
|
126
|
+
`),process.stderr.write(j(" Verify: midnight balance")+`
|
|
127
|
+
`),process.stderr.write(j(" Register dust: midnight dust register")+`
|
|
128
|
+
`),process.stderr.write(j(" Note: Dust generation takes a few minutes on a fresh wallet.")+`
|
|
129
|
+
`),process.stderr.write(j(" It will happen automatically on your first transfer.")+`
|
|
130
|
+
|
|
131
|
+
`)}catch(J){if(W.stop("Failed"),J instanceof Error&&J.message.toLowerCase().includes("dust"))throw new Error(`${J.message}
|
|
132
|
+
|
|
133
|
+
On a fresh localnet, the minimum airdrop is ~1 NIGHT.
|
|
134
|
+
Try: midnight airdrop 1`);throw J}}var x1=O(()=>{q0();r();d0();N();G0()});var E1={};w(E1,{default:()=>l3});async function l3($,Z){let Q=$.subcommand,X=$.positionals[0];if(!Q)throw new Error(`Missing recipient address.
|
|
135
|
+
Usage: midnight transfer <to> <amount>
|
|
136
|
+
Example: midnight transfer mn_addr_undeployed1... 100`);if(!X)throw new Error(`Missing amount.
|
|
137
|
+
Usage: midnight transfer <to> <amount>
|
|
138
|
+
Example: midnight transfer mn_addr_undeployed1... 100`);let q=m0(X),z=A($,"wallet"),Y=c(z),K=Buffer.from(Y.seed,"hex"),{name:G,config:U}=t({args:$,walletNetwork:Y.network,address:Y.address});process.stderr.write(`
|
|
139
|
+
`+_("Transfer")+`
|
|
140
|
+
|
|
141
|
+
`),process.stderr.write(H("Network",G)+`
|
|
142
|
+
`),process.stderr.write(H("From",E(Y.address,!0))+`
|
|
143
|
+
`),process.stderr.write(H("To",E(Q,!0))+`
|
|
144
|
+
`),process.stderr.write(H("Amount",y(q+" NIGHT"))+`
|
|
145
|
+
`),process.stderr.write(`
|
|
146
|
+
`);let W=v("Starting wallet...");try{let J=await g0({seedBuffer:K,networkConfig:U,recipientAddress:Q,amountNight:q,signal:Z,onSync(V,B){if(B>0){let F=Math.round(V/B*100);W.update(`Syncing wallet... ${F}%`)}},onDust(V){W.update(`Dust: ${V}`)},onProving(){W.update("Generating ZK proof (this may take a few minutes)...")},onSubmitting(){W.update("Submitting transaction...")},onSyncWarning(V,B){W.update(`Syncing wallet... (${B}, retrying)`)}});if(W.stop("Transaction submitted"),P($,"json")){L({txHash:J.txHash,amount:q,recipient:Q,network:G});return}process.stdout.write(J.txHash+`
|
|
147
|
+
`),process.stderr.write(`
|
|
148
|
+
`+Z0(`Transferred ${q} NIGHT`,J.txHash)+`
|
|
149
|
+
`),process.stderr.write(`
|
|
150
|
+
`+D()+`
|
|
151
|
+
`),process.stderr.write(j(" Verify: midnight balance")+`
|
|
152
|
+
|
|
153
|
+
`)}catch(J){throw W.stop("Failed"),J}}var N1=O(()=>{q0();r();d0();N();G0()});var v1={};w(v1,{default:()=>o3});import*as i3 from"@midnight-ntwrk/ledger-v7";import*as b from"rxjs";async function o3($,Z){let Q=$.subcommand;if(!Q||Q!=="register"&&Q!=="status")throw new Error(`Missing or invalid subcommand.
|
|
154
|
+
Usage:
|
|
155
|
+
midnight dust register Register NIGHT UTXOs for dust generation
|
|
156
|
+
midnight dust status Check dust registration status`);let X=A($,"wallet"),q=c(X),z=Buffer.from(q.seed,"hex"),{name:Y,config:K}=t({args:$,walletNetwork:q.network,address:q.address}),G=p0(z,K),U=async()=>{try{await u0(G)}catch{}},W=()=>{U()};Z?.addEventListener("abort",W,{once:!0});let J={},V=c0((F,T)=>{J.current?.(F,T)}),B=P($,"json");try{if(Q==="register")await G$(G,Y,B,Z,J);else await U$(G,Y,B,Z,J)}finally{Z?.removeEventListener("abort",W),V(),await U()}}async function G$($,Z,Q,X,q){process.stderr.write(`
|
|
157
|
+
`+_("Dust Register")+`
|
|
158
|
+
|
|
159
|
+
`),process.stderr.write(H("Network",Z)+`
|
|
160
|
+
|
|
161
|
+
`);let z=v("Syncing wallet...");if(q)q.current=(Y,K)=>z.update(`Syncing wallet... (${K}, retrying)`);try{if(await T0($,(J,V)=>{if(V>0){let B=Math.round(J/V*100);z.update(`Syncing wallet... ${B}%`)}}),X?.aborted)throw new Error("Operation cancelled");z.update("Checking dust status...");let Y=await b.firstValueFrom($.facade.state().pipe(b.filter((J)=>J.isSynced)));if(Y.dust.availableCoins.length>0){let J=Y.dust.walletBalance(new Date);if(z.stop("Dust already available"),Q){L({subcommand:"register",dustBalance:P0(J)});return}process.stdout.write(J.toString()+`
|
|
162
|
+
`),process.stderr.write(`
|
|
163
|
+
`+Z0(`Dust tokens already available: ${N0(J)}`)+`
|
|
164
|
+
|
|
165
|
+
`);return}let K=Y.unshielded.availableCoins.filter((J)=>J.meta?.registeredForDustGeneration!==!0),G;if(K.length===0)z.update("All UTXOs already registered, waiting for dust generation...");else{z.update(`Registering ${K.length} UTXO(s) for dust generation...`);let J=K.map((V)=>({...V.utxo,ctime:new Date(V.meta.ctime)}));G=await D1($,J,Y.dust.dustAddress,(V)=>{z.update(V)}),z.update(`Registration submitted (${G.slice(0,12)}...), waiting for dust...`)}if(X?.aborted)throw new Error("Operation cancelled");let W=(await b.firstValueFrom($.facade.state().pipe(b.throttleTime(5000),b.filter((J)=>J.isSynced),b.filter((J)=>J.dust.walletBalance(new Date)>0n),b.timeout(A0)))).dust.walletBalance(new Date);if(z.stop("Dust registration complete"),Q){let J={subcommand:"register",dustBalance:P0(W)};if(G)J.txHash=G;L(J);return}process.stdout.write(W.toString()+`
|
|
166
|
+
`),process.stderr.write(`
|
|
167
|
+
`+Z0(`Dust tokens available: ${N0(W)}`)+`
|
|
168
|
+
|
|
169
|
+
`)}catch(Y){throw z.stop("Failed"),Y}}async function U$($,Z,Q,X,q){process.stderr.write(`
|
|
170
|
+
`+_("Dust Status")+`
|
|
171
|
+
|
|
172
|
+
`),process.stderr.write(H("Network",Z)+`
|
|
173
|
+
|
|
174
|
+
`);let z=v("Syncing wallet...");if(q)q.current=(Y,K)=>z.update(`Syncing wallet... (${K}, retrying)`);try{if(await T0($,(B,F)=>{if(F>0){let T=Math.round(B/F*100);z.update(`Syncing wallet... ${T}%`)}}),X?.aborted)throw new Error("Operation cancelled");z.update("Checking dust status...");let Y=await b.firstValueFrom($.facade.state().pipe(b.filter((B)=>B.isSynced))),K=Y.dust.walletBalance(new Date),G=Y.dust.availableCoins.length>0,U=Y.unshielded.availableCoins,W=U.filter((B)=>B.meta?.registeredForDustGeneration!==!0),J=U.length-W.length,V=Y.unshielded.balances[i3.unshieldedToken().raw]??0n;if(z.stop("Done"),Q){L({subcommand:"status",dustBalance:P0(K),registered:J,unregistered:W.length,nightBalance:F0(V),dustAvailable:G});return}process.stdout.write(`dust=${K}
|
|
175
|
+
`),process.stdout.write(`registered=${J}
|
|
176
|
+
`),process.stdout.write(`unregistered=${W.length}
|
|
177
|
+
`),process.stderr.write(H("NIGHT Balance",y(E0(V)))+`
|
|
178
|
+
`),process.stderr.write(H("Dust Balance",y(N0(K)))+`
|
|
179
|
+
`),process.stderr.write(H("Dust Available",G?"yes":"no")+`
|
|
180
|
+
`),process.stderr.write(H("Registered",J.toString()+" UTXO(s)")+`
|
|
181
|
+
`),process.stderr.write(H("Unregistered",W.length.toString()+" UTXO(s)")+`
|
|
182
|
+
`),process.stderr.write(`
|
|
183
|
+
`+D()+`
|
|
184
|
+
|
|
185
|
+
`)}catch(Y){throw z.stop("Failed"),Y}}var k1=O(()=>{q0();r();I1();d0();N();G0()});var b1={};w(b1,{default:()=>n3});async function n3($){let Z=$.subcommand;if(!Z||Z!=="get"&&Z!=="set")throw new Error(`Usage: midnight config <get|set> <key> [value]
|
|
186
|
+
Valid keys: ${Y1().join(", ")}`);let Q=$.positionals[0];if(!Q)throw new Error(`Missing config key.
|
|
187
|
+
Valid keys: ${Y1().join(", ")}`);if(Z==="get"){let X=T3(Q);if(P($,"json")){L({action:"get",key:Q,value:X});return}process.stdout.write(X+`
|
|
188
|
+
`)}else{let X=$.positionals[1];if(X===void 0)throw new Error(`Missing value for config set.
|
|
189
|
+
Usage: midnight config set ${Q} <value>`);if(M3(Q,X),P($,"json")){L({action:"set",key:Q,value:X});return}process.stderr.write(i("✓")+` ${Q} = ${X}
|
|
190
|
+
`)}}var w1=O(()=>{J1()});import{execSync as M0}from"child_process";import{existsSync as a3,mkdirSync as B$,readFileSync as W$,writeFileSync as t3}from"fs";import{homedir as V$}from"os";import{join as C1}from"path";function s3(){try{return M0("docker compose version",{...i0,timeout:1e4}).trim()}catch{try{throw M0("docker --version",{...i0,timeout:5000}),new Error(`Docker Compose v2 is required.
|
|
191
|
+
Install it from https://docs.docker.com/compose/install/`)}catch($){if($ instanceof Error&&$.message.includes("Docker Compose v2"))throw $;throw new Error(`Docker is required but was not found.
|
|
192
|
+
Install Docker from https://docs.docker.com/get-docker/`)}}}function e3(){if(a3(S1)&&a3(l0)&&W$(S1,"utf-8").trim()===r3)return!1;return B$(f1,{recursive:!0,mode:$0}),t3(l0,H$,"utf-8"),t3(S1,r3,"utf-8"),!0}function B0($){return M0(`docker compose -f "${l0}" ${$}`,i0)}function o0(){try{let $=B0("ps --format json");if(!$.trim())return[];let Z=$.trim().split(`
|
|
193
|
+
`),Q=[];for(let X of Z){if(!X.trim())continue;try{let q=JSON.parse(X),z=q.Service??"unknown";Q.push({name:z,state:q.State??"unknown",health:q.Health??"",port:j$[z]??""})}catch{}}return Q}catch{return[]}}function $2($=120000,Z=3000){let Q=Date.now()+$;while(Date.now()<Q){let X=o0();if(X.length===3&&X.every((z)=>z.state==="running")){if(X.every((Y)=>Y.health==="healthy"||Y.health===""))return!0}M0(`sleep ${Z/1000}`,{timeout:Z+1000})}return!1}function h1(){return l0}function Z2(){let $=[];for(let Z of F$)try{M0(`docker rm -f "${Z}"`,{...i0,timeout:1e4}),$.push(Z)}catch{}return $}var r3="1.4.0",f1,l0,S1,H$=`services:
|
|
2186
194
|
proof-server:
|
|
2187
195
|
image: 'nel349/proof-server:7.0.0'
|
|
2188
196
|
container_name: "proof-server"
|
|
@@ -2224,950 +232,84 @@ var COMPOSE_VERSION = "1.4.0", LOCALNET_DIR, COMPOSE_PATH, VERSION_PATH, COMPOSE
|
|
|
2224
232
|
start_period: 5s
|
|
2225
233
|
environment:
|
|
2226
234
|
CFG_PRESET: "dev"
|
|
2227
|
-
`,
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
`);
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
` + dim(" Tip: run ") + bold("midnight localnet logs") + dim(" to check for errors") + `
|
|
2279
|
-
`);
|
|
2280
|
-
} else {
|
|
2281
|
-
spinner.stop("Local network is running");
|
|
2282
|
-
}
|
|
2283
|
-
} catch (err) {
|
|
2284
|
-
spinner.stop(red("Failed to start local network"));
|
|
2285
|
-
if (err instanceof Error) {
|
|
2286
|
-
if (err.message.includes("is already in use by container")) {
|
|
2287
|
-
throw new Error(`Container name conflict — containers with the same names already exist
|
|
2288
|
-
` + `(likely from a previous midnight-local-network setup).
|
|
2289
|
-
|
|
2290
|
-
` + 'Run "midnight localnet clean" to remove them, then try again.');
|
|
2291
|
-
}
|
|
2292
|
-
if (err.message.includes("address already in use")) {
|
|
2293
|
-
throw new Error(`Port conflict detected — another process is using a required port.
|
|
2294
|
-
` + "Check ports 9944, 8088, and 6300, then try again.");
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
throw err;
|
|
2298
|
-
}
|
|
2299
|
-
const services = getServiceStatus();
|
|
2300
|
-
if (jsonMode) {
|
|
2301
|
-
writeJsonResult({
|
|
2302
|
-
subcommand: "up",
|
|
2303
|
-
services: services.map((s) => ({ name: s.name, state: s.state, port: s.port, health: s.health }))
|
|
2304
|
-
});
|
|
2305
|
-
return;
|
|
2306
|
-
}
|
|
2307
|
-
if (services.length > 0) {
|
|
2308
|
-
process.stderr.write(`
|
|
2309
|
-
` + formatServiceTable(services) + `
|
|
2310
|
-
`);
|
|
2311
|
-
}
|
|
2312
|
-
for (const svc of services) {
|
|
2313
|
-
process.stdout.write(`${svc.name}=${svc.state}:${svc.port}
|
|
2314
|
-
`);
|
|
2315
|
-
}
|
|
2316
|
-
process.stderr.write(`
|
|
2317
|
-
` + dim(" Next: ") + bold("midnight generate --network undeployed") + `
|
|
2318
|
-
`);
|
|
2319
|
-
}
|
|
2320
|
-
async function handleStop(jsonMode) {
|
|
2321
|
-
const spinner = start("Stopping local network...");
|
|
2322
|
-
try {
|
|
2323
|
-
dockerCompose("stop");
|
|
2324
|
-
spinner.stop("Local network stopped (containers preserved)");
|
|
2325
|
-
if (jsonMode) {
|
|
2326
|
-
writeJsonResult({ subcommand: "stop", status: "stopped" });
|
|
2327
|
-
return;
|
|
2328
|
-
}
|
|
2329
|
-
} catch (err) {
|
|
2330
|
-
spinner.stop(red("Failed to stop local network"));
|
|
2331
|
-
throw err;
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
async function handleDown(jsonMode) {
|
|
2335
|
-
const spinner = start("Tearing down local network...");
|
|
2336
|
-
try {
|
|
2337
|
-
dockerCompose("down --volumes");
|
|
2338
|
-
spinner.stop("Local network removed (containers, networks, volumes)");
|
|
2339
|
-
if (jsonMode) {
|
|
2340
|
-
writeJsonResult({ subcommand: "down", status: "removed" });
|
|
2341
|
-
return;
|
|
2342
|
-
}
|
|
2343
|
-
} catch (err) {
|
|
2344
|
-
spinner.stop(red("Failed to tear down local network"));
|
|
2345
|
-
throw err;
|
|
2346
|
-
}
|
|
2347
|
-
}
|
|
2348
|
-
async function handleStatus(jsonMode) {
|
|
2349
|
-
const services = getServiceStatus();
|
|
2350
|
-
if (jsonMode) {
|
|
2351
|
-
writeJsonResult({
|
|
2352
|
-
subcommand: "status",
|
|
2353
|
-
services: services.map((s) => ({ name: s.name, state: s.state, port: s.port, health: s.health }))
|
|
2354
|
-
});
|
|
2355
|
-
return;
|
|
2356
|
-
}
|
|
2357
|
-
if (services.length === 0) {
|
|
2358
|
-
process.stderr.write(`
|
|
2359
|
-
` + header("Localnet Status") + `
|
|
2360
|
-
|
|
2361
|
-
`);
|
|
2362
|
-
process.stderr.write(dim(" No services running.") + `
|
|
2363
|
-
`);
|
|
2364
|
-
process.stderr.write(dim(" Run ") + bold("midnight localnet up") + dim(" to start.") + `
|
|
2365
|
-
|
|
2366
|
-
`);
|
|
2367
|
-
return;
|
|
2368
|
-
}
|
|
2369
|
-
process.stderr.write(`
|
|
2370
|
-
` + header("Localnet Status") + `
|
|
2371
|
-
|
|
2372
|
-
`);
|
|
2373
|
-
process.stderr.write(formatServiceTable(services) + `
|
|
2374
|
-
`);
|
|
2375
|
-
process.stderr.write(`
|
|
2376
|
-
` + divider() + `
|
|
2377
|
-
|
|
2378
|
-
`);
|
|
2379
|
-
for (const svc of services) {
|
|
2380
|
-
process.stdout.write(`${svc.name}=${svc.state}:${svc.port}
|
|
2381
|
-
`);
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
async function handleClean(jsonMode) {
|
|
2385
|
-
const spinner = start("Removing conflicting containers...");
|
|
2386
|
-
try {
|
|
2387
|
-
try {
|
|
2388
|
-
dockerCompose("down");
|
|
2389
|
-
} catch {
|
|
2390
|
-
}
|
|
2391
|
-
const removed = removeConflictingContainers();
|
|
2392
|
-
if (removed.length > 0) {
|
|
2393
|
-
spinner.stop(`Removed ${removed.length} container${removed.length > 1 ? "s" : ""}: ${removed.join(", ")}`);
|
|
2394
|
-
} else {
|
|
2395
|
-
spinner.stop("No conflicting containers found");
|
|
2396
|
-
}
|
|
2397
|
-
if (jsonMode) {
|
|
2398
|
-
writeJsonResult({ subcommand: "clean", status: "cleaned", removed });
|
|
2399
|
-
return;
|
|
2400
|
-
}
|
|
2401
|
-
} catch (err) {
|
|
2402
|
-
spinner.stop(red("Failed to clean up"));
|
|
2403
|
-
throw err;
|
|
2404
|
-
}
|
|
2405
|
-
}
|
|
2406
|
-
async function handleLogs() {
|
|
2407
|
-
const composePath = getComposePath();
|
|
2408
|
-
const child = spawn("docker", ["compose", "-f", composePath, "logs", "-f"], {
|
|
2409
|
-
stdio: "inherit"
|
|
2410
|
-
});
|
|
2411
|
-
return new Promise((resolve4, reject) => {
|
|
2412
|
-
child.on("close", (code) => {
|
|
2413
|
-
if (code === 0 || code === 130 || code === null) {
|
|
2414
|
-
resolve4();
|
|
2415
|
-
} else {
|
|
2416
|
-
reject(new Error(`docker compose logs exited with code ${code}`));
|
|
2417
|
-
}
|
|
2418
|
-
});
|
|
2419
|
-
child.on("error", reject);
|
|
2420
|
-
});
|
|
2421
|
-
}
|
|
2422
|
-
async function localnetCommand(args) {
|
|
2423
|
-
const subcommand = args.subcommand;
|
|
2424
|
-
if (!subcommand || !isValidSubcommand(subcommand)) {
|
|
2425
|
-
throw new Error(`Usage: midnight localnet <${VALID_SUBCOMMANDS.join("|")}>
|
|
2426
|
-
|
|
2427
|
-
` + `Subcommands:
|
|
2428
|
-
` + ` up Start the local network
|
|
2429
|
-
` + ` stop Stop containers (preserves state)
|
|
2430
|
-
` + ` down Remove containers, networks, volumes
|
|
2431
|
-
` + ` status Show service status
|
|
2432
|
-
` + ` logs Stream service logs
|
|
2433
|
-
` + ` clean Remove conflicting containers
|
|
2434
|
-
|
|
2435
|
-
` + `Example: midnight localnet up`);
|
|
2436
|
-
}
|
|
2437
|
-
checkDockerAvailable();
|
|
2438
|
-
const jsonMode = hasFlag(args, "json");
|
|
2439
|
-
process.stderr.write(`
|
|
2440
|
-
` + header("Localnet") + `
|
|
2441
|
-
|
|
2442
|
-
`);
|
|
2443
|
-
switch (subcommand) {
|
|
2444
|
-
case "up":
|
|
2445
|
-
return handleUp(jsonMode);
|
|
2446
|
-
case "stop":
|
|
2447
|
-
return handleStop(jsonMode);
|
|
2448
|
-
case "down":
|
|
2449
|
-
return handleDown(jsonMode);
|
|
2450
|
-
case "status":
|
|
2451
|
-
return handleStatus(jsonMode);
|
|
2452
|
-
case "logs":
|
|
2453
|
-
return handleLogs();
|
|
2454
|
-
case "clean":
|
|
2455
|
-
return handleClean(jsonMode);
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
var VALID_SUBCOMMANDS;
|
|
2459
|
-
var init_localnet2 = __esm(() => {
|
|
2460
|
-
init_localnet();
|
|
2461
|
-
init_format();
|
|
2462
|
-
init_spinner();
|
|
2463
|
-
VALID_SUBCOMMANDS = ["up", "stop", "down", "status", "logs", "clean"];
|
|
2464
|
-
});
|
|
2465
|
-
|
|
2466
|
-
// src/mcp-server.ts
|
|
2467
|
-
var exports_mcp_server = {};
|
|
2468
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2469
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2470
|
-
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
2471
|
-
function buildArgs(command, params, subcommand) {
|
|
2472
|
-
const flags = { json: true };
|
|
2473
|
-
const positionals = [];
|
|
2474
|
-
for (const [key, value] of Object.entries(params)) {
|
|
2475
|
-
if (value === undefined || value === null)
|
|
2476
|
-
continue;
|
|
2477
|
-
if (typeof value === "boolean") {
|
|
2478
|
-
if (value)
|
|
2479
|
-
flags[key] = true;
|
|
2480
|
-
} else {
|
|
2481
|
-
flags[key] = String(value);
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
return {
|
|
2485
|
-
command,
|
|
2486
|
-
subcommand,
|
|
2487
|
-
positionals,
|
|
2488
|
-
flags
|
|
2489
|
-
};
|
|
2490
|
-
}
|
|
2491
|
-
async function importHandler(name) {
|
|
2492
|
-
const loader = handlerLoaders[name];
|
|
2493
|
-
if (!loader)
|
|
2494
|
-
throw new Error(`Unknown command handler: ${name}`);
|
|
2495
|
-
const mod = await loader();
|
|
2496
|
-
return mod.default;
|
|
2497
|
-
}
|
|
2498
|
-
async function main() {
|
|
2499
|
-
const transport = new StdioServerTransport;
|
|
2500
|
-
await server.connect(transport);
|
|
2501
|
-
}
|
|
2502
|
-
var handlerLoaders, TOOLS, server;
|
|
2503
|
-
var init_mcp_server = __esm(() => {
|
|
2504
|
-
init_run_command();
|
|
2505
|
-
init_exit_codes();
|
|
2506
|
-
init_pkg();
|
|
2507
|
-
handlerLoaders = {
|
|
2508
|
-
generate: () => Promise.resolve().then(() => (init_generate(), exports_generate)),
|
|
2509
|
-
info: () => Promise.resolve().then(() => (init_info(), exports_info)),
|
|
2510
|
-
balance: () => Promise.resolve().then(() => (init_balance(), exports_balance)),
|
|
2511
|
-
address: () => Promise.resolve().then(() => (init_address(), exports_address)),
|
|
2512
|
-
"genesis-address": () => Promise.resolve().then(() => (init_genesis_address(), exports_genesis_address)),
|
|
2513
|
-
"inspect-cost": () => Promise.resolve().then(() => (init_inspect_cost(), exports_inspect_cost)),
|
|
2514
|
-
airdrop: () => Promise.resolve().then(() => (init_airdrop(), exports_airdrop)),
|
|
2515
|
-
transfer: () => Promise.resolve().then(() => (init_transfer2(), exports_transfer)),
|
|
2516
|
-
dust: () => Promise.resolve().then(() => (init_dust(), exports_dust)),
|
|
2517
|
-
config: () => Promise.resolve().then(() => (init_config(), exports_config)),
|
|
2518
|
-
localnet: () => Promise.resolve().then(() => (init_localnet2(), exports_localnet))
|
|
2519
|
-
};
|
|
2520
|
-
TOOLS = [
|
|
2521
|
-
{
|
|
2522
|
-
name: "midnight_generate",
|
|
2523
|
-
description: "Generate a new wallet (random mnemonic, or restore from seed/mnemonic)",
|
|
2524
|
-
inputSchema: {
|
|
2525
|
-
type: "object",
|
|
2526
|
-
properties: {
|
|
2527
|
-
network: { type: "string", description: "Network: preprod, preview, undeployed", enum: ["preprod", "preview", "undeployed"] },
|
|
2528
|
-
seed: { type: "string", description: "Restore from existing seed (64-char hex)" },
|
|
2529
|
-
mnemonic: { type: "string", description: "Restore from BIP-39 mnemonic (24 words)" },
|
|
2530
|
-
output: { type: "string", description: "Custom output path (default: ~/.midnight/wallet.json)" },
|
|
2531
|
-
force: { type: "string", description: 'Set to "true" to overwrite existing wallet file' }
|
|
2532
|
-
}
|
|
2533
|
-
},
|
|
2534
|
-
async handler(params) {
|
|
2535
|
-
const args = buildArgs("generate", params);
|
|
2536
|
-
if (params.force === "true" || params.force === true)
|
|
2537
|
-
args.flags.force = true;
|
|
2538
|
-
const handler = await importHandler("generate");
|
|
2539
|
-
return captureCommand(handler, args);
|
|
2540
|
-
}
|
|
2541
|
-
},
|
|
2542
|
-
{
|
|
2543
|
-
name: "midnight_info",
|
|
2544
|
-
description: "Display wallet address, network, creation date (no secrets shown)",
|
|
2545
|
-
inputSchema: {
|
|
2546
|
-
type: "object",
|
|
2547
|
-
properties: {
|
|
2548
|
-
wallet: { type: "string", description: "Custom wallet file path" }
|
|
2549
|
-
}
|
|
2550
|
-
},
|
|
2551
|
-
async handler(params) {
|
|
2552
|
-
const args = buildArgs("info", params);
|
|
2553
|
-
const handler = await importHandler("info");
|
|
2554
|
-
return captureCommand(handler, args);
|
|
2555
|
-
}
|
|
2556
|
-
},
|
|
2557
|
-
{
|
|
2558
|
-
name: "midnight_balance",
|
|
2559
|
-
description: "Check unshielded balance via indexer subscription",
|
|
2560
|
-
inputSchema: {
|
|
2561
|
-
type: "object",
|
|
2562
|
-
properties: {
|
|
2563
|
-
address: { type: "string", description: "Address to check (or reads from wallet file)" },
|
|
2564
|
-
wallet: { type: "string", description: "Custom wallet file path" },
|
|
2565
|
-
network: { type: "string", description: "Override network detection", enum: ["preprod", "preview", "undeployed"] },
|
|
2566
|
-
"indexer-ws": { type: "string", description: "Custom indexer WebSocket URL" }
|
|
2567
|
-
}
|
|
2568
|
-
},
|
|
2569
|
-
async handler(params) {
|
|
2570
|
-
const address = params.address;
|
|
2571
|
-
const args = buildArgs("balance", params, address);
|
|
2572
|
-
delete args.flags.address;
|
|
2573
|
-
const handler = await importHandler("balance");
|
|
2574
|
-
return captureCommand(handler, args);
|
|
2575
|
-
}
|
|
2576
|
-
},
|
|
2577
|
-
{
|
|
2578
|
-
name: "midnight_address",
|
|
2579
|
-
description: "Derive and display an unshielded address from a seed",
|
|
2580
|
-
inputSchema: {
|
|
2581
|
-
type: "object",
|
|
2582
|
-
properties: {
|
|
2583
|
-
seed: { type: "string", description: "Seed to derive from (required, 64-char hex)" },
|
|
2584
|
-
network: { type: "string", description: "Network for address prefix", enum: ["preprod", "preview", "undeployed"] },
|
|
2585
|
-
index: { type: "string", description: "Key derivation index (default: 0)" }
|
|
2586
|
-
},
|
|
2587
|
-
required: ["seed"]
|
|
2588
|
-
},
|
|
2589
|
-
async handler(params) {
|
|
2590
|
-
const args = buildArgs("address", params);
|
|
2591
|
-
const handler = await importHandler("address");
|
|
2592
|
-
return captureCommand(handler, args);
|
|
2593
|
-
}
|
|
2594
|
-
},
|
|
2595
|
-
{
|
|
2596
|
-
name: "midnight_genesis_address",
|
|
2597
|
-
description: "Display the genesis wallet address (seed 0x01) for a network",
|
|
2598
|
-
inputSchema: {
|
|
2599
|
-
type: "object",
|
|
2600
|
-
properties: {
|
|
2601
|
-
network: { type: "string", description: "Network for address prefix", enum: ["preprod", "preview", "undeployed"] }
|
|
2602
|
-
}
|
|
2603
|
-
},
|
|
2604
|
-
async handler(params) {
|
|
2605
|
-
const args = buildArgs("genesis-address", params);
|
|
2606
|
-
const handler = await importHandler("genesis-address");
|
|
2607
|
-
return captureCommand(handler, args);
|
|
2608
|
-
}
|
|
2609
|
-
},
|
|
2610
|
-
{
|
|
2611
|
-
name: "midnight_inspect_cost",
|
|
2612
|
-
description: "Display current block limits derived from LedgerParameters",
|
|
2613
|
-
inputSchema: {
|
|
2614
|
-
type: "object",
|
|
2615
|
-
properties: {}
|
|
2616
|
-
},
|
|
2617
|
-
async handler() {
|
|
2618
|
-
const args = buildArgs("inspect-cost", {});
|
|
2619
|
-
const handler = await importHandler("inspect-cost");
|
|
2620
|
-
return captureCommand(handler, args);
|
|
2621
|
-
}
|
|
2622
|
-
},
|
|
2623
|
-
{
|
|
2624
|
-
name: "midnight_airdrop",
|
|
2625
|
-
description: "Fund your wallet from the genesis wallet (undeployed network only)",
|
|
2626
|
-
inputSchema: {
|
|
2627
|
-
type: "object",
|
|
2628
|
-
properties: {
|
|
2629
|
-
amount: { type: "string", description: "Amount in NIGHT to airdrop" },
|
|
2630
|
-
wallet: { type: "string", description: "Custom wallet file path" }
|
|
2631
|
-
},
|
|
2632
|
-
required: ["amount"]
|
|
2633
|
-
},
|
|
2634
|
-
async handler(params) {
|
|
2635
|
-
const amount = params.amount;
|
|
2636
|
-
const args = buildArgs("airdrop", params, amount);
|
|
2637
|
-
delete args.flags.amount;
|
|
2638
|
-
const handler = await importHandler("airdrop");
|
|
2639
|
-
return captureCommand(handler, args);
|
|
2640
|
-
}
|
|
2641
|
-
},
|
|
2642
|
-
{
|
|
2643
|
-
name: "midnight_transfer",
|
|
2644
|
-
description: "Send NIGHT tokens to another address",
|
|
2645
|
-
inputSchema: {
|
|
2646
|
-
type: "object",
|
|
2647
|
-
properties: {
|
|
2648
|
-
to: { type: "string", description: "Recipient bech32m address" },
|
|
2649
|
-
amount: { type: "string", description: "Amount in NIGHT to send" },
|
|
2650
|
-
wallet: { type: "string", description: "Custom wallet file path" }
|
|
2651
|
-
},
|
|
2652
|
-
required: ["to", "amount"]
|
|
2653
|
-
},
|
|
2654
|
-
async handler(params) {
|
|
2655
|
-
const to = params.to;
|
|
2656
|
-
const amount = params.amount;
|
|
2657
|
-
const args = buildArgs("transfer", params, to);
|
|
2658
|
-
args.positionals = [amount];
|
|
2659
|
-
delete args.flags.to;
|
|
2660
|
-
delete args.flags.amount;
|
|
2661
|
-
const handler = await importHandler("transfer");
|
|
2662
|
-
return captureCommand(handler, args);
|
|
2663
|
-
}
|
|
2664
|
-
},
|
|
2665
|
-
{
|
|
2666
|
-
name: "midnight_dust_register",
|
|
2667
|
-
description: "Register NIGHT UTXOs for dust (fee token) generation",
|
|
2668
|
-
inputSchema: {
|
|
2669
|
-
type: "object",
|
|
2670
|
-
properties: {
|
|
2671
|
-
wallet: { type: "string", description: "Custom wallet file path" }
|
|
2672
|
-
}
|
|
2673
|
-
},
|
|
2674
|
-
async handler(params) {
|
|
2675
|
-
const args = buildArgs("dust", params, "register");
|
|
2676
|
-
const handler = await importHandler("dust");
|
|
2677
|
-
return captureCommand(handler, args);
|
|
2678
|
-
}
|
|
2679
|
-
},
|
|
2680
|
-
{
|
|
2681
|
-
name: "midnight_dust_status",
|
|
2682
|
-
description: "Check dust registration status and balance",
|
|
2683
|
-
inputSchema: {
|
|
2684
|
-
type: "object",
|
|
2685
|
-
properties: {
|
|
2686
|
-
wallet: { type: "string", description: "Custom wallet file path" }
|
|
2687
|
-
}
|
|
2688
|
-
},
|
|
2689
|
-
async handler(params) {
|
|
2690
|
-
const args = buildArgs("dust", params, "status");
|
|
2691
|
-
const handler = await importHandler("dust");
|
|
2692
|
-
return captureCommand(handler, args);
|
|
2693
|
-
}
|
|
2694
|
-
},
|
|
2695
|
-
{
|
|
2696
|
-
name: "midnight_config_get",
|
|
2697
|
-
description: "Read a persistent config value",
|
|
2698
|
-
inputSchema: {
|
|
2699
|
-
type: "object",
|
|
2700
|
-
properties: {
|
|
2701
|
-
key: { type: "string", description: 'Config key to read (e.g. "network")' }
|
|
2702
|
-
},
|
|
2703
|
-
required: ["key"]
|
|
2704
|
-
},
|
|
2705
|
-
async handler(params) {
|
|
2706
|
-
const key = params.key;
|
|
2707
|
-
const args = {
|
|
2708
|
-
command: "config",
|
|
2709
|
-
subcommand: "get",
|
|
2710
|
-
positionals: [key],
|
|
2711
|
-
flags: { json: true }
|
|
2712
|
-
};
|
|
2713
|
-
const handler = await importHandler("config");
|
|
2714
|
-
return captureCommand(handler, args);
|
|
2715
|
-
}
|
|
2716
|
-
},
|
|
2717
|
-
{
|
|
2718
|
-
name: "midnight_config_set",
|
|
2719
|
-
description: "Write a persistent config value",
|
|
2720
|
-
inputSchema: {
|
|
2721
|
-
type: "object",
|
|
2722
|
-
properties: {
|
|
2723
|
-
key: { type: "string", description: 'Config key to set (e.g. "network")' },
|
|
2724
|
-
value: { type: "string", description: "Config value to set" }
|
|
2725
|
-
},
|
|
2726
|
-
required: ["key", "value"]
|
|
2727
|
-
},
|
|
2728
|
-
async handler(params) {
|
|
2729
|
-
const key = params.key;
|
|
2730
|
-
const value = params.value;
|
|
2731
|
-
const args = {
|
|
2732
|
-
command: "config",
|
|
2733
|
-
subcommand: "set",
|
|
2734
|
-
positionals: [key, value],
|
|
2735
|
-
flags: { json: true }
|
|
2736
|
-
};
|
|
2737
|
-
const handler = await importHandler("config");
|
|
2738
|
-
return captureCommand(handler, args);
|
|
2739
|
-
}
|
|
2740
|
-
},
|
|
2741
|
-
{
|
|
2742
|
-
name: "midnight_localnet_up",
|
|
2743
|
-
description: "Start a local Midnight network via Docker Compose",
|
|
2744
|
-
inputSchema: {
|
|
2745
|
-
type: "object",
|
|
2746
|
-
properties: {}
|
|
2747
|
-
},
|
|
2748
|
-
async handler() {
|
|
2749
|
-
const args = {
|
|
2750
|
-
command: "localnet",
|
|
2751
|
-
subcommand: "up",
|
|
2752
|
-
positionals: [],
|
|
2753
|
-
flags: { json: true }
|
|
2754
|
-
};
|
|
2755
|
-
const handler = await importHandler("localnet");
|
|
2756
|
-
return captureCommand(handler, args);
|
|
2757
|
-
}
|
|
2758
|
-
},
|
|
2759
|
-
{
|
|
2760
|
-
name: "midnight_localnet_stop",
|
|
2761
|
-
description: "Stop local network containers (preserves state for fast restart)",
|
|
2762
|
-
inputSchema: {
|
|
2763
|
-
type: "object",
|
|
2764
|
-
properties: {}
|
|
2765
|
-
},
|
|
2766
|
-
async handler() {
|
|
2767
|
-
const args = {
|
|
2768
|
-
command: "localnet",
|
|
2769
|
-
subcommand: "stop",
|
|
2770
|
-
positionals: [],
|
|
2771
|
-
flags: { json: true }
|
|
2772
|
-
};
|
|
2773
|
-
const handler = await importHandler("localnet");
|
|
2774
|
-
return captureCommand(handler, args);
|
|
2775
|
-
}
|
|
2776
|
-
},
|
|
2777
|
-
{
|
|
2778
|
-
name: "midnight_localnet_down",
|
|
2779
|
-
description: "Remove local network containers, networks, and volumes (full teardown)",
|
|
2780
|
-
inputSchema: {
|
|
2781
|
-
type: "object",
|
|
2782
|
-
properties: {}
|
|
2783
|
-
},
|
|
2784
|
-
async handler() {
|
|
2785
|
-
const args = {
|
|
2786
|
-
command: "localnet",
|
|
2787
|
-
subcommand: "down",
|
|
2788
|
-
positionals: [],
|
|
2789
|
-
flags: { json: true }
|
|
2790
|
-
};
|
|
2791
|
-
const handler = await importHandler("localnet");
|
|
2792
|
-
return captureCommand(handler, args);
|
|
2793
|
-
}
|
|
2794
|
-
},
|
|
2795
|
-
{
|
|
2796
|
-
name: "midnight_localnet_status",
|
|
2797
|
-
description: "Show local network service status and ports",
|
|
2798
|
-
inputSchema: {
|
|
2799
|
-
type: "object",
|
|
2800
|
-
properties: {}
|
|
2801
|
-
},
|
|
2802
|
-
async handler() {
|
|
2803
|
-
const args = {
|
|
2804
|
-
command: "localnet",
|
|
2805
|
-
subcommand: "status",
|
|
2806
|
-
positionals: [],
|
|
2807
|
-
flags: { json: true }
|
|
2808
|
-
};
|
|
2809
|
-
const handler = await importHandler("localnet");
|
|
2810
|
-
return captureCommand(handler, args);
|
|
2811
|
-
}
|
|
2812
|
-
},
|
|
2813
|
-
{
|
|
2814
|
-
name: "midnight_localnet_clean",
|
|
2815
|
-
description: "Remove conflicting containers from other setups",
|
|
2816
|
-
inputSchema: {
|
|
2817
|
-
type: "object",
|
|
2818
|
-
properties: {}
|
|
2819
|
-
},
|
|
2820
|
-
async handler() {
|
|
2821
|
-
const args = {
|
|
2822
|
-
command: "localnet",
|
|
2823
|
-
subcommand: "clean",
|
|
2824
|
-
positionals: [],
|
|
2825
|
-
flags: { json: true }
|
|
2826
|
-
};
|
|
2827
|
-
const handler = await importHandler("localnet");
|
|
2828
|
-
return captureCommand(handler, args);
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
];
|
|
2832
|
-
server = new Server({ name: "midnight-wallet-cli", version: PKG_VERSION }, { capabilities: { tools: {} } });
|
|
2833
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
2834
|
-
return {
|
|
2835
|
-
tools: TOOLS.map((t) => ({
|
|
2836
|
-
name: t.name,
|
|
2837
|
-
description: t.description,
|
|
2838
|
-
inputSchema: t.inputSchema
|
|
2839
|
-
}))
|
|
2840
|
-
};
|
|
2841
|
-
});
|
|
2842
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2843
|
-
const { name, arguments: params } = request.params;
|
|
2844
|
-
const tool = TOOLS.find((t) => t.name === name);
|
|
2845
|
-
if (!tool) {
|
|
2846
|
-
return {
|
|
2847
|
-
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
2848
|
-
isError: true
|
|
2849
|
-
};
|
|
2850
|
-
}
|
|
2851
|
-
try {
|
|
2852
|
-
const result = await tool.handler(params ?? {});
|
|
2853
|
-
return {
|
|
2854
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
2855
|
-
};
|
|
2856
|
-
} catch (err) {
|
|
2857
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
2858
|
-
const { errorCode } = classifyError(error);
|
|
2859
|
-
return {
|
|
2860
|
-
content: [{
|
|
2861
|
-
type: "text",
|
|
2862
|
-
text: JSON.stringify({ error: true, code: errorCode, message: error.message })
|
|
2863
|
-
}],
|
|
2864
|
-
isError: true
|
|
2865
|
-
};
|
|
2866
|
-
}
|
|
2867
|
-
});
|
|
2868
|
-
main().catch((err) => {
|
|
2869
|
-
process.stderr.write(`MCP server error: ${err.message}
|
|
2870
|
-
`);
|
|
2871
|
-
process.exit(1);
|
|
2872
|
-
});
|
|
2873
|
-
});
|
|
2874
|
-
|
|
2875
|
-
// src/ui/art.ts
|
|
2876
|
-
function getWordmarkTypingFrame(progress) {
|
|
2877
|
-
const clamped = Math.max(0, Math.min(1, progress));
|
|
2878
|
-
const width = WORDMARK_BIG[0].length;
|
|
2879
|
-
const visibleCols = Math.floor(clamped * width);
|
|
2880
|
-
return WORDMARK_BIG.map((line) => line.slice(0, visibleCols).padEnd(width));
|
|
2881
|
-
}
|
|
2882
|
-
function getWordmarkMaterializeFrame(progress) {
|
|
2883
|
-
const clamped = Math.max(0, Math.min(1, progress));
|
|
2884
|
-
const seed = Math.floor(progress * 100);
|
|
2885
|
-
if (clamped >= 1)
|
|
2886
|
-
return WORDMARK_BIG;
|
|
2887
|
-
return WORDMARK_BIG.map((line, row) => Array.from(line).map((ch, col) => {
|
|
2888
|
-
if (ch === " ")
|
|
2889
|
-
return " ";
|
|
2890
|
-
if (clamped <= 0)
|
|
2891
|
-
return noiseChar(row + 20, col, 0);
|
|
2892
|
-
const threshold = ((row + 20) * 131 + col * 997) % 100 / 100;
|
|
2893
|
-
return clamped >= threshold ? ch : noiseChar(row + 20, col, seed);
|
|
2894
|
-
}).join(""));
|
|
2895
|
-
}
|
|
2896
|
-
function noiseChar(row, col, seed) {
|
|
2897
|
-
const hash = (row * 131 + col * 997 + seed * 7919) % 65537 / 65537;
|
|
2898
|
-
return NOISE_CHARS[Math.floor(hash * NOISE_CHARS.length)];
|
|
2899
|
-
}
|
|
2900
|
-
function getMaterializeFrame(progress) {
|
|
2901
|
-
const clamped = Math.max(0, Math.min(1, progress));
|
|
2902
|
-
const lines = MIDNIGHT_LOGO.split(`
|
|
2903
|
-
`);
|
|
2904
|
-
const seed = Math.floor(progress * 100);
|
|
2905
|
-
if (clamped >= 1)
|
|
2906
|
-
return MIDNIGHT_LOGO;
|
|
2907
|
-
if (clamped <= 0) {
|
|
2908
|
-
return lines.map((line, row) => Array.from(line).map((_, col) => noiseChar(row, col, 0)).join("")).join(`
|
|
2909
|
-
`);
|
|
2910
|
-
}
|
|
2911
|
-
return lines.map((line, row) => Array.from(line).map((ch, col) => {
|
|
2912
|
-
if (ch === " ")
|
|
2913
|
-
return " ";
|
|
2914
|
-
const threshold = (row * 131 + col * 997) % 100 / 100;
|
|
2915
|
-
return clamped >= threshold ? ch : noiseChar(row, col, seed);
|
|
2916
|
-
}).join("")).join(`
|
|
2917
|
-
`);
|
|
2918
|
-
}
|
|
2919
|
-
var MIDNIGHT_LOGO, WORDMARK_BIG, COMMAND_BRIEFS, NOISE_CHARS;
|
|
2920
|
-
var init_art = __esm(() => {
|
|
2921
|
-
MIDNIGHT_LOGO = [
|
|
2922
|
-
" ████████████ ",
|
|
2923
|
-
" ███ ███ ",
|
|
2924
|
-
" ███ ██ ███ ",
|
|
2925
|
-
" ██ ██ ",
|
|
2926
|
-
" ██ ██ ██ ",
|
|
2927
|
-
" ██ ██ ",
|
|
2928
|
-
" ██ ██ ██ ",
|
|
2929
|
-
" ██ ██ ",
|
|
2930
|
-
" ██ ██ ",
|
|
2931
|
-
" ██ ██ ",
|
|
2932
|
-
" ██ ██ ",
|
|
2933
|
-
" ███ ███ ",
|
|
2934
|
-
" ███ ███ ",
|
|
2935
|
-
" ████████████ "
|
|
2936
|
-
].join(`
|
|
2937
|
-
`);
|
|
2938
|
-
WORDMARK_BIG = [
|
|
2939
|
-
"█▄ ▄█ █ █▀▄ █▄ █ █ █▀▀ █ █ ▀█▀",
|
|
2940
|
-
"█ █ █ █ █ █ █ ██ █ █ █ █▀█ █ ",
|
|
2941
|
-
"▀ ▀ ▀ ▀▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ "
|
|
2942
|
-
];
|
|
2943
|
-
COMMAND_BRIEFS = [
|
|
2944
|
-
["generate", "Generate or restore a wallet"],
|
|
2945
|
-
["info", "Display wallet metadata"],
|
|
2946
|
-
["balance", "Check unshielded balance"],
|
|
2947
|
-
["address", "Derive address from seed"],
|
|
2948
|
-
["genesis-address", "Show genesis address"],
|
|
2949
|
-
["inspect-cost", "Display block limits"],
|
|
2950
|
-
["airdrop", "Fund from genesis wallet"],
|
|
2951
|
-
["transfer", "Send NIGHT tokens"],
|
|
2952
|
-
["dust", "Manage dust (fee tokens)"],
|
|
2953
|
-
["config", "Manage CLI config"],
|
|
2954
|
-
["localnet", "Manage local network"],
|
|
2955
|
-
["help", "Show command usage"]
|
|
2956
|
-
];
|
|
2957
|
-
NOISE_CHARS = ["░", "▒", "▓", "█", "·", " "];
|
|
2958
|
-
});
|
|
2959
|
-
|
|
2960
|
-
// src/ui/animate.ts
|
|
2961
|
-
function sleep(ms, signal) {
|
|
2962
|
-
return new Promise((resolve4, reject) => {
|
|
2963
|
-
if (signal?.aborted) {
|
|
2964
|
-
resolve4();
|
|
2965
|
-
return;
|
|
2966
|
-
}
|
|
2967
|
-
const timer = setTimeout(resolve4, ms);
|
|
2968
|
-
signal?.addEventListener("abort", () => {
|
|
2969
|
-
clearTimeout(timer);
|
|
2970
|
-
resolve4();
|
|
2971
|
-
}, { once: true });
|
|
2972
|
-
});
|
|
2973
|
-
}
|
|
2974
|
-
async function animateMaterialize(signal, sideContent) {
|
|
2975
|
-
const totalFrames = 20;
|
|
2976
|
-
const logoLines = MIDNIGHT_LOGO.split(`
|
|
2977
|
-
`);
|
|
2978
|
-
const logoLineCount = logoLines.length;
|
|
2979
|
-
const rightCol = 36;
|
|
2980
|
-
const totalHeight = Math.max(logoLineCount, sideContent?.length ?? 0);
|
|
2981
|
-
const wordmarkLineCount = 3;
|
|
2982
|
-
if (!isColorEnabled()) {
|
|
2983
|
-
for (let j = 0;j < totalHeight; j++) {
|
|
2984
|
-
const left = (j < logoLineCount ? logoLines[j] : "").padEnd(rightCol - 1);
|
|
2985
|
-
const right = sideContent?.[j] ?? "";
|
|
2986
|
-
process.stderr.write(left + right + `
|
|
2987
|
-
`);
|
|
2988
|
-
}
|
|
2989
|
-
return;
|
|
2990
|
-
}
|
|
2991
|
-
function renderFrame(frameLogoLines, rightLines) {
|
|
2992
|
-
for (let j = 0;j < totalHeight; j++) {
|
|
2993
|
-
const left = j < frameLogoLines.length ? white(frameLogoLines[j]) : "";
|
|
2994
|
-
const right = rightLines[j] ?? "";
|
|
2995
|
-
if (right) {
|
|
2996
|
-
process.stderr.write(left + `\x1B[${rightCol}G` + right + `\x1B[K
|
|
2997
|
-
`);
|
|
2998
|
-
} else {
|
|
2999
|
-
process.stderr.write(left + `\x1B[K
|
|
3000
|
-
`);
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
function moveUp() {
|
|
3005
|
-
process.stderr.write(`\x1B[${totalHeight}A`);
|
|
3006
|
-
}
|
|
3007
|
-
for (let i = 0;i <= totalFrames; i++) {
|
|
3008
|
-
if (signal?.aborted)
|
|
3009
|
-
break;
|
|
3010
|
-
const progress = i / totalFrames;
|
|
3011
|
-
const frame = getMaterializeFrame(progress);
|
|
3012
|
-
const frameLines = frame.split(`
|
|
3013
|
-
`);
|
|
3014
|
-
if (i > 0)
|
|
3015
|
-
moveUp();
|
|
3016
|
-
renderFrame(frameLines, []);
|
|
3017
|
-
await sleep(FRAME_MS, signal);
|
|
3018
|
-
}
|
|
3019
|
-
const typingFrames = 20;
|
|
3020
|
-
for (let i = 0;i <= typingFrames; i++) {
|
|
3021
|
-
if (signal?.aborted)
|
|
3022
|
-
break;
|
|
3023
|
-
const progress = i / typingFrames;
|
|
3024
|
-
const typedLines = getWordmarkTypingFrame(progress);
|
|
3025
|
-
moveUp();
|
|
3026
|
-
const rightLines = new Array(totalHeight).fill(null);
|
|
3027
|
-
for (let j = 0;j < typedLines.length; j++) {
|
|
3028
|
-
rightLines[j] = bold(white(typedLines[j]));
|
|
3029
|
-
}
|
|
3030
|
-
renderFrame(logoLines, rightLines);
|
|
3031
|
-
await sleep(FRAME_MS, signal);
|
|
3032
|
-
}
|
|
3033
|
-
const flashFrames = 12;
|
|
3034
|
-
for (let i = 0;i <= flashFrames; i++) {
|
|
3035
|
-
if (signal?.aborted)
|
|
3036
|
-
break;
|
|
3037
|
-
const progress = i / flashFrames;
|
|
3038
|
-
const flashedLines = getWordmarkMaterializeFrame(progress);
|
|
3039
|
-
moveUp();
|
|
3040
|
-
const rightLines = new Array(totalHeight).fill(null);
|
|
3041
|
-
for (let j = 0;j < flashedLines.length; j++) {
|
|
3042
|
-
rightLines[j] = bold(white(flashedLines[j]));
|
|
3043
|
-
}
|
|
3044
|
-
renderFrame(logoLines, rightLines);
|
|
3045
|
-
await sleep(FRAME_MS, signal);
|
|
3046
|
-
}
|
|
3047
|
-
moveUp();
|
|
3048
|
-
const fullRight = new Array(totalHeight).fill(null);
|
|
3049
|
-
if (sideContent) {
|
|
3050
|
-
for (let j = 0;j < sideContent.length; j++) {
|
|
3051
|
-
if (j < wordmarkLineCount) {
|
|
3052
|
-
fullRight[j] = bold(white(sideContent[j]));
|
|
3053
|
-
} else {
|
|
3054
|
-
fullRight[j] = sideContent[j];
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
|
-
}
|
|
3058
|
-
renderFrame(logoLines, fullRight);
|
|
3059
|
-
}
|
|
3060
|
-
var FRAME_MS = 80;
|
|
3061
|
-
var init_animate = __esm(() => {
|
|
3062
|
-
init_art();
|
|
3063
|
-
});
|
|
3064
|
-
|
|
3065
|
-
// src/commands/help.ts
|
|
3066
|
-
var exports_help = {};
|
|
3067
|
-
__export(exports_help, {
|
|
3068
|
-
default: () => helpCommand,
|
|
3069
|
-
COMMAND_SPECS: () => COMMAND_SPECS
|
|
3070
|
-
});
|
|
3071
|
-
function buildRightColumn() {
|
|
3072
|
-
const lines = [];
|
|
3073
|
-
for (const wl of WORDMARK_BIG) {
|
|
3074
|
-
lines.push(wl);
|
|
3075
|
-
}
|
|
3076
|
-
lines.push("");
|
|
3077
|
-
lines.push(bold("Commands"));
|
|
3078
|
-
for (const [name, brief] of COMMAND_BRIEFS) {
|
|
3079
|
-
lines.push(`${teal(name.padEnd(18))}${brief}`);
|
|
3080
|
-
}
|
|
3081
|
-
lines.push("");
|
|
3082
|
-
lines.push(dim(`midnight (or mn) help <command>`));
|
|
3083
|
-
lines.push(dim(`--json flag available on all commands`));
|
|
3084
|
-
lines.push(dim(`midnight help --agent`) + dim(` AI & MCP reference`));
|
|
3085
|
-
return lines;
|
|
3086
|
-
}
|
|
3087
|
-
function printPlainHelp() {
|
|
3088
|
-
process.stderr.write(`
|
|
235
|
+
`,i0,j$,F$;var Q2=O(()=>{f1=C1(V$(),p,z3),l0=C1(f1,"compose.yml"),S1=C1(f1,".version"),i0={encoding:"utf-8",timeout:30000};j$={node:"9944",indexer:"8088","proof-server":"6300"};F$=["node","indexer","proof-server"]});var p1={};w(p1,{default:()=>z2});import{spawn as P$}from"child_process";function O$($){return X2.includes($)}function q2($){let Z=[];for(let Q of $){let X=Q.state==="running"?i:S,q=Q.health?` (${Q.health})`:"",z=Q.port?`:${Q.port}`:"";Z.push(` ${Q.name.padEnd(16)}${X(Q.state)}${j(q)}${j(z)}`)}return Z.join(`
|
|
236
|
+
`)}async function L$($){if(e3())process.stderr.write(j(` Wrote compose.yml to ${h1()}`)+`
|
|
237
|
+
`);let Q=v("Starting local network...");try{if(B0("up -d"),Q.update("Waiting for services to be healthy..."),!$2(120000))Q.stop(V0("Services started but not all healthy yet")),process.stderr.write(`
|
|
238
|
+
`+j(" Tip: run ")+y("midnight localnet logs")+j(" to check for errors")+`
|
|
239
|
+
`);else Q.stop("Local network is running")}catch(q){if(Q.stop(S("Failed to start local network")),q instanceof Error){if(q.message.includes("is already in use by container"))throw new Error(`Container name conflict — containers with the same names already exist
|
|
240
|
+
`+`(likely from a previous midnight-local-network setup).
|
|
241
|
+
|
|
242
|
+
Run "midnight localnet clean" to remove them, then try again.`);if(q.message.includes("address already in use"))throw new Error(`Port conflict detected — another process is using a required port.
|
|
243
|
+
`+"Check ports 9944, 8088, and 6300, then try again.")}throw q}let X=o0();if($){L({subcommand:"up",services:X.map((q)=>({name:q.name,state:q.state,port:q.port,health:q.health}))});return}if(X.length>0)process.stderr.write(`
|
|
244
|
+
`+q2(X)+`
|
|
245
|
+
`);for(let q of X)process.stdout.write(`${q.name}=${q.state}:${q.port}
|
|
246
|
+
`);process.stderr.write(`
|
|
247
|
+
`+j(" Next: ")+y("midnight generate --network undeployed")+`
|
|
248
|
+
`)}async function y$($){let Z=v("Stopping local network...");try{if(B0("stop"),Z.stop("Local network stopped (containers preserved)"),$){L({subcommand:"stop",status:"stopped"});return}}catch(Q){throw Z.stop(S("Failed to stop local network")),Q}}async function T$($){let Z=v("Tearing down local network...");try{if(B0("down --volumes"),Z.stop("Local network removed (containers, networks, volumes)"),$){L({subcommand:"down",status:"removed"});return}}catch(Q){throw Z.stop(S("Failed to tear down local network")),Q}}async function M$($){let Z=o0();if($){L({subcommand:"status",services:Z.map((Q)=>({name:Q.name,state:Q.state,port:Q.port,health:Q.health}))});return}if(Z.length===0){process.stderr.write(`
|
|
249
|
+
`+_("Localnet Status")+`
|
|
250
|
+
|
|
251
|
+
`),process.stderr.write(j(" No services running.")+`
|
|
252
|
+
`),process.stderr.write(j(" Run ")+y("midnight localnet up")+j(" to start.")+`
|
|
253
|
+
|
|
254
|
+
`);return}process.stderr.write(`
|
|
255
|
+
`+_("Localnet Status")+`
|
|
256
|
+
|
|
257
|
+
`),process.stderr.write(q2(Z)+`
|
|
258
|
+
`),process.stderr.write(`
|
|
259
|
+
`+D()+`
|
|
260
|
+
|
|
261
|
+
`);for(let Q of Z)process.stdout.write(`${Q.name}=${Q.state}:${Q.port}
|
|
262
|
+
`)}async function I$($){let Z=v("Removing conflicting containers...");try{try{B0("down")}catch{}let Q=Z2();if(Q.length>0)Z.stop(`Removed ${Q.length} container${Q.length>1?"s":""}: ${Q.join(", ")}`);else Z.stop("No conflicting containers found");if($){L({subcommand:"clean",status:"cleaned",removed:Q});return}}catch(Q){throw Z.stop(S("Failed to clean up")),Q}}async function _$(){let $=h1(),Z=P$("docker",["compose","-f",$,"logs","-f"],{stdio:"inherit"});return new Promise((Q,X)=>{Z.on("close",(q)=>{if(q===0||q===130||q===null)Q();else X(new Error(`docker compose logs exited with code ${q}`))}),Z.on("error",X)})}async function z2($){let Z=$.subcommand;if(!Z||!O$(Z))throw new Error(`Usage: midnight localnet <${X2.join("|")}>
|
|
263
|
+
|
|
264
|
+
Subcommands:
|
|
265
|
+
up Start the local network
|
|
266
|
+
stop Stop containers (preserves state)
|
|
267
|
+
down Remove containers, networks, volumes
|
|
268
|
+
status Show service status
|
|
269
|
+
logs Stream service logs
|
|
270
|
+
clean Remove conflicting containers
|
|
271
|
+
|
|
272
|
+
Example: midnight localnet up`);s3();let Q=P($,"json");switch(process.stderr.write(`
|
|
273
|
+
`+_("Localnet")+`
|
|
274
|
+
|
|
275
|
+
`),Z){case"up":return L$(Q);case"stop":return y$(Q);case"down":return T$(Q);case"status":return M$(Q);case"logs":return _$();case"clean":return I$(Q)}}var X2;var u1=O(()=>{Q2();N();G0();X2=["up","stop","down","status","logs","clean"]});var v$={};import{Server as A$}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as D$}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as R$,CallToolRequestSchema as x$}from"@modelcontextprotocol/sdk/types.js";function m($,Z,Q){let X={json:!0},q=[];for(let[z,Y]of Object.entries(Z)){if(Y===void 0||Y===null)continue;if(typeof Y==="boolean"){if(Y)X[z]=!0}else X[z]=String(Y)}return{command:$,subcommand:Q,positionals:q,flags:X}}async function x($){let Z=E$[$];if(!Z)throw new Error(`Unknown command handler: ${$}`);return(await Z()).default}async function N$(){let $=new D$;await c1.connect($)}var E$,Y2,c1;var J2=O(()=>{H3();$1();k0();E$={generate:()=>Promise.resolve().then(() => (G1(),K1)),info:()=>Promise.resolve().then(() => (B1(),U1)),balance:()=>Promise.resolve().then(() => (V1(),W1)),address:()=>Promise.resolve().then(() => (j1(),H1)),"genesis-address":()=>Promise.resolve().then(() => (P1(),F1)),"inspect-cost":()=>Promise.resolve().then(() => (L1(),O1)),airdrop:()=>Promise.resolve().then(() => (x1(),R1)),transfer:()=>Promise.resolve().then(() => (N1(),E1)),dust:()=>Promise.resolve().then(() => (k1(),v1)),config:()=>Promise.resolve().then(() => (w1(),b1)),localnet:()=>Promise.resolve().then(() => (u1(),p1))};Y2=[{name:"midnight_generate",description:"Generate a new wallet (random mnemonic, or restore from seed/mnemonic)",inputSchema:{type:"object",properties:{network:{type:"string",description:"Network: preprod, preview, undeployed",enum:["preprod","preview","undeployed"]},seed:{type:"string",description:"Restore from existing seed (64-char hex)"},mnemonic:{type:"string",description:"Restore from BIP-39 mnemonic (24 words)"},output:{type:"string",description:"Custom output path (default: ~/.midnight/wallet.json)"},force:{type:"string",description:'Set to "true" to overwrite existing wallet file'}}},async handler($){let Z=m("generate",$);if($.force==="true"||$.force===!0)Z.flags.force=!0;let Q=await x("generate");return R(Q,Z)}},{name:"midnight_info",description:"Display wallet address, network, creation date (no secrets shown)",inputSchema:{type:"object",properties:{wallet:{type:"string",description:"Custom wallet file path"}}},async handler($){let Z=m("info",$),Q=await x("info");return R(Q,Z)}},{name:"midnight_balance",description:"Check unshielded balance via indexer subscription",inputSchema:{type:"object",properties:{address:{type:"string",description:"Address to check (or reads from wallet file)"},wallet:{type:"string",description:"Custom wallet file path"},network:{type:"string",description:"Override network detection",enum:["preprod","preview","undeployed"]},"indexer-ws":{type:"string",description:"Custom indexer WebSocket URL"}}},async handler($){let Z=$.address,Q=m("balance",$,Z);delete Q.flags.address;let X=await x("balance");return R(X,Q)}},{name:"midnight_address",description:"Derive and display an unshielded address from a seed",inputSchema:{type:"object",properties:{seed:{type:"string",description:"Seed to derive from (required, 64-char hex)"},network:{type:"string",description:"Network for address prefix",enum:["preprod","preview","undeployed"]},index:{type:"string",description:"Key derivation index (default: 0)"}},required:["seed"]},async handler($){let Z=m("address",$),Q=await x("address");return R(Q,Z)}},{name:"midnight_genesis_address",description:"Display the genesis wallet address (seed 0x01) for a network",inputSchema:{type:"object",properties:{network:{type:"string",description:"Network for address prefix",enum:["preprod","preview","undeployed"]}}},async handler($){let Z=m("genesis-address",$),Q=await x("genesis-address");return R(Q,Z)}},{name:"midnight_inspect_cost",description:"Display current block limits derived from LedgerParameters",inputSchema:{type:"object",properties:{}},async handler(){let $=m("inspect-cost",{}),Z=await x("inspect-cost");return R(Z,$)}},{name:"midnight_airdrop",description:"Fund your wallet from the genesis wallet (undeployed network only)",inputSchema:{type:"object",properties:{amount:{type:"string",description:"Amount in NIGHT to airdrop"},wallet:{type:"string",description:"Custom wallet file path"}},required:["amount"]},async handler($){let Z=$.amount,Q=m("airdrop",$,Z);delete Q.flags.amount;let X=await x("airdrop");return R(X,Q)}},{name:"midnight_transfer",description:"Send NIGHT tokens to another address",inputSchema:{type:"object",properties:{to:{type:"string",description:"Recipient bech32m address"},amount:{type:"string",description:"Amount in NIGHT to send"},wallet:{type:"string",description:"Custom wallet file path"}},required:["to","amount"]},async handler($){let{to:Z,amount:Q}=$,X=m("transfer",$,Z);X.positionals=[Q],delete X.flags.to,delete X.flags.amount;let q=await x("transfer");return R(q,X)}},{name:"midnight_dust_register",description:"Register NIGHT UTXOs for dust (fee token) generation",inputSchema:{type:"object",properties:{wallet:{type:"string",description:"Custom wallet file path"}}},async handler($){let Z=m("dust",$,"register"),Q=await x("dust");return R(Q,Z)}},{name:"midnight_dust_status",description:"Check dust registration status and balance",inputSchema:{type:"object",properties:{wallet:{type:"string",description:"Custom wallet file path"}}},async handler($){let Z=m("dust",$,"status"),Q=await x("dust");return R(Q,Z)}},{name:"midnight_config_get",description:"Read a persistent config value",inputSchema:{type:"object",properties:{key:{type:"string",description:'Config key to read (e.g. "network")'}},required:["key"]},async handler($){let Q={command:"config",subcommand:"get",positionals:[$.key],flags:{json:!0}},X=await x("config");return R(X,Q)}},{name:"midnight_config_set",description:"Write a persistent config value",inputSchema:{type:"object",properties:{key:{type:"string",description:'Config key to set (e.g. "network")'},value:{type:"string",description:"Config value to set"}},required:["key","value"]},async handler($){let{key:Z,value:Q}=$,X={command:"config",subcommand:"set",positionals:[Z,Q],flags:{json:!0}},q=await x("config");return R(q,X)}},{name:"midnight_localnet_up",description:"Start a local Midnight network via Docker Compose",inputSchema:{type:"object",properties:{}},async handler(){let $={command:"localnet",subcommand:"up",positionals:[],flags:{json:!0}},Z=await x("localnet");return R(Z,$)}},{name:"midnight_localnet_stop",description:"Stop local network containers (preserves state for fast restart)",inputSchema:{type:"object",properties:{}},async handler(){let $={command:"localnet",subcommand:"stop",positionals:[],flags:{json:!0}},Z=await x("localnet");return R(Z,$)}},{name:"midnight_localnet_down",description:"Remove local network containers, networks, and volumes (full teardown)",inputSchema:{type:"object",properties:{}},async handler(){let $={command:"localnet",subcommand:"down",positionals:[],flags:{json:!0}},Z=await x("localnet");return R(Z,$)}},{name:"midnight_localnet_status",description:"Show local network service status and ports",inputSchema:{type:"object",properties:{}},async handler(){let $={command:"localnet",subcommand:"status",positionals:[],flags:{json:!0}},Z=await x("localnet");return R(Z,$)}},{name:"midnight_localnet_clean",description:"Remove conflicting containers from other setups",inputSchema:{type:"object",properties:{}},async handler(){let $={command:"localnet",subcommand:"clean",positionals:[],flags:{json:!0}},Z=await x("localnet");return R(Z,$)}}],c1=new A$({name:"midnight-wallet-cli",version:Q0},{capabilities:{tools:{}}});c1.setRequestHandler(R$,async()=>{return{tools:Y2.map(($)=>({name:$.name,description:$.description,inputSchema:$.inputSchema}))}});c1.setRequestHandler(x$,async($)=>{let{name:Z,arguments:Q}=$.params,X=Y2.find((q)=>q.name===Z);if(!X)return{content:[{type:"text",text:`Unknown tool: ${Z}`}],isError:!0};try{let q=await X.handler(Q??{});return{content:[{type:"text",text:JSON.stringify(q,null,2)}]}}catch(q){let z=q instanceof Error?q:new Error(String(q)),{errorCode:Y}=O0(z);return{content:[{type:"text",text:JSON.stringify({error:!0,code:Y,message:z.message})}],isError:!0}}});N$().catch(($)=>{process.stderr.write(`MCP server error: ${$.message}
|
|
276
|
+
`),process.exit(1)})});function G2($){let Z=Math.max(0,Math.min(1,$)),Q=W0[0].length,X=Math.floor(Z*Q);return W0.map((q)=>q.slice(0,X).padEnd(Q))}function U2($){let Z=Math.max(0,Math.min(1,$)),Q=Math.floor($*100);if(Z>=1)return W0;return W0.map((X,q)=>Array.from(X).map((z,Y)=>{if(z===" ")return" ";if(Z<=0)return a0(q+20,Y,0);let K=((q+20)*131+Y*997)%100/100;return Z>=K?z:a0(q+20,Y,Q)}).join(""))}function a0($,Z,Q){let X=($*131+Z*997+Q*7919)%65537/65537;return K2[Math.floor(X*K2.length)]}function B2($){let Z=Math.max(0,Math.min(1,$)),Q=n0.split(`
|
|
277
|
+
`),X=Math.floor($*100);if(Z>=1)return n0;if(Z<=0)return Q.map((q,z)=>Array.from(q).map((Y,K)=>a0(z,K,0)).join("")).join(`
|
|
278
|
+
`);return Q.map((q,z)=>Array.from(q).map((Y,K)=>{if(Y===" ")return" ";let G=(z*131+K*997)%100/100;return Z>=G?Y:a0(z,K,X)}).join("")).join(`
|
|
279
|
+
`)}var n0,W0,m1,K2;var g1=O(()=>{n0=[" ████████████ "," ███ ███ "," ███ ██ ███ "," ██ ██ "," ██ ██ ██ "," ██ ██ "," ██ ██ ██ "," ██ ██ "," ██ ██ "," ██ ██ "," ██ ██ "," ███ ███ "," ███ ███ "," ████████████ "].join(`
|
|
280
|
+
`),W0=["█▄ ▄█ █ █▀▄ █▄ █ █ █▀▀ █ █ ▀█▀","█ █ █ █ █ █ █ ██ █ █ █ █▀█ █ ","▀ ▀ ▀ ▀▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ "];m1=[["generate","Generate or restore a wallet"],["info","Display wallet metadata"],["balance","Check unshielded balance"],["address","Derive address from seed"],["genesis-address","Show genesis address"],["inspect-cost","Display block limits"],["airdrop","Fund from genesis wallet"],["transfer","Send NIGHT tokens"],["dust","Manage dust (fee tokens)"],["config","Manage CLI config"],["localnet","Manage local network"],["help","Show command usage"]],K2=["░","▒","▓","█","·"," "]});function l1($,Z){return new Promise((Q,X)=>{if(Z?.aborted){Q();return}let q=setTimeout(Q,$);Z?.addEventListener("abort",()=>{clearTimeout(q),Q()},{once:!0})})}async function W2($,Z){let X=n0.split(`
|
|
281
|
+
`),q=X.length,z=36,Y=Math.max(q,Z?.length??0),K=3;if(!l()){for(let B=0;B<Y;B++){let F=(B<q?X[B]:"").padEnd(35),T=Z?.[B]??"";process.stderr.write(F+T+`
|
|
282
|
+
`)}return}function G(B,F){for(let T=0;T<Y;T++){let I=T<B.length?H0(B[T]):"",M=F[T]??"";if(M)process.stderr.write(I+"\x1B[36G"+M+`\x1B[K
|
|
283
|
+
`);else process.stderr.write(I+`\x1B[K
|
|
284
|
+
`)}}function U(){process.stderr.write(`\x1B[${Y}A`)}for(let B=0;B<=20;B++){if($?.aborted)break;let F=B/20,I=B2(F).split(`
|
|
285
|
+
`);if(B>0)U();G(I,[]),await l1(d1,$)}let W=20;for(let B=0;B<=W;B++){if($?.aborted)break;let F=B/W,T=G2(F);U();let I=new Array(Y).fill(null);for(let M=0;M<T.length;M++)I[M]=y(H0(T[M]));G(X,I),await l1(d1,$)}let J=12;for(let B=0;B<=J;B++){if($?.aborted)break;let F=B/J,T=U2(F);U();let I=new Array(Y).fill(null);for(let M=0;M<T.length;M++)I[M]=y(H0(T[M]));G(X,I),await l1(d1,$)}U();let V=new Array(Y).fill(null);if(Z)for(let B=0;B<Z.length;B++)if(B<3)V[B]=y(H0(Z[B]));else V[B]=Z[B];G(X,V)}var d1=80;var V2=O(()=>{g1()});var j2={};w(j2,{default:()=>H2,COMMAND_SPECS:()=>I0});function k$(){let $=[];for(let Z of W0)$.push(Z);$.push(""),$.push(y("Commands"));for(let[Z,Q]of m1)$.push(`${a(Z.padEnd(18))}${Q}`);return $.push(""),$.push(j("midnight (or mn) help <command>")),$.push(j("--json flag available on all commands")),$.push(j("midnight help --agent")+j(" AI & MCP reference")),$}function b$(){process.stderr.write(`
|
|
3089
286
|
Commands:
|
|
3090
287
|
|
|
3091
|
-
`);
|
|
3092
|
-
|
|
3093
|
-
process.stderr.write(` ${name.padEnd(18)}${brief}
|
|
3094
|
-
`);
|
|
3095
|
-
}
|
|
3096
|
-
process.stderr.write(`
|
|
288
|
+
`);for(let[$,Z]of m1)process.stderr.write(` ${$.padEnd(18)}${Z}
|
|
289
|
+
`);process.stderr.write(`
|
|
3097
290
|
Usage: midnight <command> (or: mn <command>)
|
|
3098
|
-
`)
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
`
|
|
3108
|
-
|
|
3109
|
-
`)
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
`)
|
|
3113
|
-
|
|
3114
|
-
`);
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
`);
|
|
3118
|
-
if (spec.flags && spec.flags.length > 0) {
|
|
3119
|
-
process.stdout.write(gray("Flags:") + `
|
|
3120
|
-
`);
|
|
3121
|
-
for (const flag of spec.flags) {
|
|
3122
|
-
process.stdout.write(` ${flag}
|
|
3123
|
-
`);
|
|
3124
|
-
}
|
|
3125
|
-
process.stdout.write(`
|
|
3126
|
-
`);
|
|
3127
|
-
}
|
|
3128
|
-
if (spec.examples && spec.examples.length > 0) {
|
|
3129
|
-
process.stdout.write(gray("Examples:") + `
|
|
3130
|
-
`);
|
|
3131
|
-
for (const example of spec.examples) {
|
|
3132
|
-
process.stdout.write(` ${dim("$")} ${example}
|
|
3133
|
-
`);
|
|
3134
|
-
}
|
|
3135
|
-
process.stdout.write(`
|
|
3136
|
-
`);
|
|
3137
|
-
}
|
|
3138
|
-
}
|
|
3139
|
-
function outputJsonManifest() {
|
|
3140
|
-
const manifest = {
|
|
3141
|
-
cli: {
|
|
3142
|
-
name: PKG_NAME,
|
|
3143
|
-
version: PKG_VERSION,
|
|
3144
|
-
description: PKG_DESCRIPTION,
|
|
3145
|
-
bin: ["midnight", "mn"]
|
|
3146
|
-
},
|
|
3147
|
-
globalFlags: [
|
|
3148
|
-
{ name: "--json", description: "Output structured JSON to stdout (suppresses all stderr)" },
|
|
3149
|
-
{ name: "--wallet <file>", description: "Custom wallet file path" },
|
|
3150
|
-
{ name: "--network <name>", description: "Override network (preprod, preview, undeployed)" },
|
|
3151
|
-
{ name: "--version, -v", description: "Print CLI version" },
|
|
3152
|
-
{ name: "--help, -h", description: "Show help" }
|
|
3153
|
-
],
|
|
3154
|
-
commands: COMMAND_SPECS.map((spec) => ({
|
|
3155
|
-
name: spec.name,
|
|
3156
|
-
description: spec.description,
|
|
3157
|
-
usage: spec.usage,
|
|
3158
|
-
flags: spec.flags,
|
|
3159
|
-
examples: spec.examples,
|
|
3160
|
-
jsonFields: spec.jsonFields
|
|
3161
|
-
}))
|
|
3162
|
-
};
|
|
3163
|
-
writeJsonResult(manifest);
|
|
3164
|
-
}
|
|
3165
|
-
function printAgentManual() {
|
|
3166
|
-
const manual = `
|
|
291
|
+
`),process.stderr.write(` --json flag available on all commands
|
|
292
|
+
`),process.stderr.write(` midnight help --agent AI & MCP reference
|
|
293
|
+
|
|
294
|
+
`)}function w$($){if(process.stdout.write(`
|
|
295
|
+
`+_($.name)+`
|
|
296
|
+
|
|
297
|
+
`),process.stdout.write(` ${$.description}
|
|
298
|
+
|
|
299
|
+
`),process.stdout.write(J0("Usage:")+`
|
|
300
|
+
`),process.stdout.write(` ${$.usage}
|
|
301
|
+
|
|
302
|
+
`),$.flags&&$.flags.length>0){process.stdout.write(J0("Flags:")+`
|
|
303
|
+
`);for(let Z of $.flags)process.stdout.write(` ${Z}
|
|
304
|
+
`);process.stdout.write(`
|
|
305
|
+
`)}if($.examples&&$.examples.length>0){process.stdout.write(J0("Examples:")+`
|
|
306
|
+
`);for(let Z of $.examples)process.stdout.write(` ${j("$")} ${Z}
|
|
307
|
+
`);process.stdout.write(`
|
|
308
|
+
`)}}function S$(){let $={cli:{name:W3,version:Q0,description:V3,bin:["midnight","mn"]},globalFlags:[{name:"--json",description:"Output structured JSON to stdout (suppresses all stderr)"},{name:"--wallet <file>",description:"Custom wallet file path"},{name:"--network <name>",description:"Override network (preprod, preview, undeployed)"},{name:"--version, -v",description:"Print CLI version"},{name:"--help, -h",description:"Show help"}],commands:I0.map((Z)=>({name:Z.name,description:Z.description,usage:Z.usage,flags:Z.flags,examples:Z.examples,jsonFields:Z.jsonFields}))};L($)}function C$(){let $=`
|
|
3167
309
|
MIDNIGHT CLI — AI Agent & MCP Reference
|
|
3168
310
|
========================================
|
|
3169
311
|
|
|
3170
|
-
Version: ${
|
|
312
|
+
Version: ${Q0}
|
|
3171
313
|
|
|
3172
314
|
STRUCTURED JSON OUTPUT
|
|
3173
315
|
──────────────────────
|
|
@@ -3190,15 +332,12 @@ for programmatic discovery of CLI capabilities.
|
|
|
3190
332
|
|
|
3191
333
|
COMMANDS & JSON SCHEMAS
|
|
3192
334
|
───────────────────────
|
|
3193
|
-
${
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
${spec.name}
|
|
3198
|
-
${spec.usage}
|
|
335
|
+
${I0.filter((Z)=>Z.jsonFields).map((Z)=>{let Q=Object.entries(Z.jsonFields).map(([X,q])=>` ${X.padEnd(20)}${q}`).join(`
|
|
336
|
+
`);return`
|
|
337
|
+
${Z.name}
|
|
338
|
+
${Z.usage}
|
|
3199
339
|
JSON fields:
|
|
3200
|
-
${
|
|
3201
|
-
}).join(`
|
|
340
|
+
${Q}`}).join(`
|
|
3202
341
|
`)}
|
|
3203
342
|
|
|
3204
343
|
ERROR CODES
|
|
@@ -3272,384 +411,10 @@ EXAMPLE WORKFLOW
|
|
|
3272
411
|
# 4. Transfer tokens
|
|
3273
412
|
midnight transfer mn_addr_... 100 --json
|
|
3274
413
|
# → {"txHash":"...","amount":"100","recipient":"mn_addr_...","network":"undeployed"}
|
|
3275
|
-
`;
|
|
3276
|
-
process.stdout.write(
|
|
3277
|
-
}
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
}
|
|
3283
|
-
if (hasFlag(args, "json")) {
|
|
3284
|
-
outputJsonManifest();
|
|
3285
|
-
return;
|
|
3286
|
-
}
|
|
3287
|
-
const targetCommand = args.subcommand;
|
|
3288
|
-
if (targetCommand) {
|
|
3289
|
-
const spec = COMMAND_SPECS.find((s) => s.name === targetCommand);
|
|
3290
|
-
if (!spec) {
|
|
3291
|
-
throw new Error(`Unknown command: "${targetCommand}"
|
|
3292
|
-
` + `Available commands: ${COMMAND_SPECS.map((s) => s.name).join(", ")}`);
|
|
3293
|
-
}
|
|
3294
|
-
printCommandHelp(spec);
|
|
3295
|
-
return;
|
|
3296
|
-
}
|
|
3297
|
-
if (!process.stderr.isTTY) {
|
|
3298
|
-
printPlainHelp();
|
|
3299
|
-
return;
|
|
3300
|
-
}
|
|
3301
|
-
const rightColumn = buildRightColumn();
|
|
3302
|
-
await animateMaterialize(undefined, rightColumn);
|
|
3303
|
-
}
|
|
3304
|
-
var COMMAND_SPECS;
|
|
3305
|
-
var init_help = __esm(() => {
|
|
3306
|
-
init_format();
|
|
3307
|
-
init_animate();
|
|
3308
|
-
init_art();
|
|
3309
|
-
init_pkg();
|
|
3310
|
-
COMMAND_SPECS = [
|
|
3311
|
-
{
|
|
3312
|
-
name: "generate",
|
|
3313
|
-
description: "Generate a new wallet (random mnemonic, or restore from seed/mnemonic)",
|
|
3314
|
-
usage: 'midnight generate [--network <name>] [--seed <hex>] [--mnemonic "..."] [--output <file>] [--force]',
|
|
3315
|
-
flags: [
|
|
3316
|
-
"--network <name> Network: preprod, preview, undeployed",
|
|
3317
|
-
"--seed <hex> Restore from existing seed (64-char hex)",
|
|
3318
|
-
'--mnemonic "..." Restore from BIP-39 mnemonic (24 words)',
|
|
3319
|
-
"--output <file> Custom output path (default: ~/.midnight/wallet.json)",
|
|
3320
|
-
"--force Overwrite existing wallet file"
|
|
3321
|
-
],
|
|
3322
|
-
examples: [
|
|
3323
|
-
"midnight generate --network preprod",
|
|
3324
|
-
"midnight generate --network preprod --output my-wallet.json",
|
|
3325
|
-
"midnight generate --seed 0123456789abcdef..."
|
|
3326
|
-
],
|
|
3327
|
-
jsonFields: {
|
|
3328
|
-
address: "Generated wallet address (bech32m)",
|
|
3329
|
-
network: "Network name",
|
|
3330
|
-
seed: "Hex-encoded 32-byte seed",
|
|
3331
|
-
mnemonic: "BIP-39 mnemonic (24 words, only if generated or provided)",
|
|
3332
|
-
file: "Path where wallet file was saved",
|
|
3333
|
-
createdAt: "ISO 8601 creation timestamp"
|
|
3334
|
-
}
|
|
3335
|
-
},
|
|
3336
|
-
{
|
|
3337
|
-
name: "info",
|
|
3338
|
-
description: "Display wallet address, network, creation date (no secrets shown)",
|
|
3339
|
-
usage: "midnight info [--wallet <file>]",
|
|
3340
|
-
flags: [
|
|
3341
|
-
"--wallet <file> Custom wallet file path"
|
|
3342
|
-
],
|
|
3343
|
-
examples: [
|
|
3344
|
-
"midnight info",
|
|
3345
|
-
"midnight info --wallet my-wallet.json"
|
|
3346
|
-
],
|
|
3347
|
-
jsonFields: {
|
|
3348
|
-
address: "Wallet address (bech32m)",
|
|
3349
|
-
network: "Network name",
|
|
3350
|
-
createdAt: "ISO 8601 creation timestamp",
|
|
3351
|
-
file: "Wallet file path"
|
|
3352
|
-
}
|
|
3353
|
-
},
|
|
3354
|
-
{
|
|
3355
|
-
name: "balance",
|
|
3356
|
-
description: "Check unshielded balance via GraphQL subscription",
|
|
3357
|
-
usage: "midnight balance [address] [--network <name>] [--indexer-ws <url>]",
|
|
3358
|
-
flags: [
|
|
3359
|
-
"<address> Address to check (or reads from wallet file)",
|
|
3360
|
-
"--network <name> Override network detection",
|
|
3361
|
-
"--indexer-ws <url> Custom indexer WebSocket URL"
|
|
3362
|
-
],
|
|
3363
|
-
examples: [
|
|
3364
|
-
"midnight balance",
|
|
3365
|
-
"midnight balance mn_addr_preprod1...",
|
|
3366
|
-
"midnight balance --network preprod"
|
|
3367
|
-
],
|
|
3368
|
-
jsonFields: {
|
|
3369
|
-
address: "Checked address (bech32m)",
|
|
3370
|
-
network: "Network name",
|
|
3371
|
-
balances: "Object mapping token type to balance string",
|
|
3372
|
-
utxoCount: "Number of UTXOs",
|
|
3373
|
-
txCount: "Number of transactions synced"
|
|
3374
|
-
}
|
|
3375
|
-
},
|
|
3376
|
-
{
|
|
3377
|
-
name: "address",
|
|
3378
|
-
description: "Derive and display an unshielded address from a seed",
|
|
3379
|
-
usage: "midnight address --seed <hex> [--network <name>] [--index <n>]",
|
|
3380
|
-
flags: [
|
|
3381
|
-
"--seed <hex> Seed to derive from (required, 64-char hex)",
|
|
3382
|
-
"--network <name> Network for address prefix (default: resolved)",
|
|
3383
|
-
"--index <n> Key derivation index (default: 0)"
|
|
3384
|
-
],
|
|
3385
|
-
examples: [
|
|
3386
|
-
"midnight address --seed 0123456789abcdef... --network preprod",
|
|
3387
|
-
"midnight address --seed 0123456789abcdef... --index 1"
|
|
3388
|
-
],
|
|
3389
|
-
jsonFields: {
|
|
3390
|
-
address: "Derived address (bech32m)",
|
|
3391
|
-
network: "Network name",
|
|
3392
|
-
index: "Key derivation index",
|
|
3393
|
-
path: "BIP-44 derivation path"
|
|
3394
|
-
}
|
|
3395
|
-
},
|
|
3396
|
-
{
|
|
3397
|
-
name: "genesis-address",
|
|
3398
|
-
description: "Display the genesis wallet address (seed 0x01) for a network",
|
|
3399
|
-
usage: "midnight genesis-address [--network <name>]",
|
|
3400
|
-
flags: [
|
|
3401
|
-
"--network <name> Network for address prefix (default: resolved)"
|
|
3402
|
-
],
|
|
3403
|
-
examples: [
|
|
3404
|
-
"midnight genesis-address --network undeployed",
|
|
3405
|
-
"midnight genesis-address --network preprod"
|
|
3406
|
-
],
|
|
3407
|
-
jsonFields: {
|
|
3408
|
-
address: "Genesis wallet address (bech32m)",
|
|
3409
|
-
network: "Network name"
|
|
3410
|
-
}
|
|
3411
|
-
},
|
|
3412
|
-
{
|
|
3413
|
-
name: "inspect-cost",
|
|
3414
|
-
description: "Display current block limits derived from LedgerParameters",
|
|
3415
|
-
usage: "midnight inspect-cost",
|
|
3416
|
-
examples: [
|
|
3417
|
-
"midnight inspect-cost"
|
|
3418
|
-
],
|
|
3419
|
-
jsonFields: {
|
|
3420
|
-
readTime: "Read time limit (picoseconds)",
|
|
3421
|
-
computeTime: "Compute time limit (picoseconds)",
|
|
3422
|
-
blockUsage: "Block usage limit (bytes)",
|
|
3423
|
-
bytesWritten: "Bytes written limit (bytes)",
|
|
3424
|
-
bytesChurned: "Bytes churned limit (bytes)"
|
|
3425
|
-
}
|
|
3426
|
-
},
|
|
3427
|
-
{
|
|
3428
|
-
name: "airdrop",
|
|
3429
|
-
description: "Fund your wallet from the genesis wallet (undeployed network only)",
|
|
3430
|
-
usage: "midnight airdrop <amount> [--wallet <file>]",
|
|
3431
|
-
flags: [
|
|
3432
|
-
"<amount> Amount in NIGHT to airdrop",
|
|
3433
|
-
"--wallet <file> Custom wallet file path"
|
|
3434
|
-
],
|
|
3435
|
-
examples: [
|
|
3436
|
-
"midnight airdrop 1000",
|
|
3437
|
-
"midnight airdrop 0.5 --wallet my-wallet.json"
|
|
3438
|
-
],
|
|
3439
|
-
jsonFields: {
|
|
3440
|
-
txHash: "Transaction hash",
|
|
3441
|
-
amount: "Amount airdropped (NIGHT string)",
|
|
3442
|
-
recipient: "Recipient address (bech32m)",
|
|
3443
|
-
network: "Network name"
|
|
3444
|
-
}
|
|
3445
|
-
},
|
|
3446
|
-
{
|
|
3447
|
-
name: "transfer",
|
|
3448
|
-
description: "Send NIGHT tokens to another address",
|
|
3449
|
-
usage: "midnight transfer <to> <amount> [--wallet <file>]",
|
|
3450
|
-
flags: [
|
|
3451
|
-
"<to> Recipient bech32m address",
|
|
3452
|
-
"<amount> Amount in NIGHT to send",
|
|
3453
|
-
"--wallet <file> Custom wallet file path"
|
|
3454
|
-
],
|
|
3455
|
-
examples: [
|
|
3456
|
-
"midnight transfer mn_addr_undeployed1... 100",
|
|
3457
|
-
"midnight transfer mn_addr_preprod1... 50 --wallet my-wallet.json"
|
|
3458
|
-
],
|
|
3459
|
-
jsonFields: {
|
|
3460
|
-
txHash: "Transaction hash",
|
|
3461
|
-
amount: "Amount transferred (NIGHT string)",
|
|
3462
|
-
recipient: "Recipient address (bech32m)",
|
|
3463
|
-
network: "Network name"
|
|
3464
|
-
}
|
|
3465
|
-
},
|
|
3466
|
-
{
|
|
3467
|
-
name: "dust",
|
|
3468
|
-
description: "Register UTXOs for dust (fee token) generation or check status",
|
|
3469
|
-
usage: "midnight dust <register|status> [--wallet <file>]",
|
|
3470
|
-
flags: [
|
|
3471
|
-
"register Register NIGHT UTXOs for dust generation",
|
|
3472
|
-
"status Check dust registration status and balance",
|
|
3473
|
-
"--wallet <file> Custom wallet file path"
|
|
3474
|
-
],
|
|
3475
|
-
examples: [
|
|
3476
|
-
"midnight dust register",
|
|
3477
|
-
"midnight dust status"
|
|
3478
|
-
],
|
|
3479
|
-
jsonFields: {
|
|
3480
|
-
subcommand: "register or status",
|
|
3481
|
-
dustBalance: "Dust balance (raw bigint string)",
|
|
3482
|
-
registered: "Number of registered UTXOs (status only)",
|
|
3483
|
-
unregistered: "Number of unregistered UTXOs (status only)",
|
|
3484
|
-
nightBalance: "NIGHT balance (raw bigint string, status only)",
|
|
3485
|
-
dustAvailable: "Whether dust tokens are available (status only)",
|
|
3486
|
-
txHash: "Registration transaction hash (register only, if submitted)"
|
|
3487
|
-
}
|
|
3488
|
-
},
|
|
3489
|
-
{
|
|
3490
|
-
name: "config",
|
|
3491
|
-
description: "Manage persistent config (default network, etc.)",
|
|
3492
|
-
usage: "midnight config <get|set> <key> [value]",
|
|
3493
|
-
flags: [
|
|
3494
|
-
"get <key> Read a config value",
|
|
3495
|
-
"set <key> <value> Write a config value"
|
|
3496
|
-
],
|
|
3497
|
-
examples: [
|
|
3498
|
-
"midnight config get network",
|
|
3499
|
-
"midnight config set network preprod"
|
|
3500
|
-
],
|
|
3501
|
-
jsonFields: {
|
|
3502
|
-
action: "get or set",
|
|
3503
|
-
key: "Config key name",
|
|
3504
|
-
value: "Config value"
|
|
3505
|
-
}
|
|
3506
|
-
},
|
|
3507
|
-
{
|
|
3508
|
-
name: "localnet",
|
|
3509
|
-
description: "Manage a local Midnight network via Docker Compose",
|
|
3510
|
-
usage: "midnight localnet <up|stop|down|status|logs|clean>",
|
|
3511
|
-
flags: [
|
|
3512
|
-
"up Start the local network (node, indexer, proof server)",
|
|
3513
|
-
"stop Stop containers (preserves state for fast restart)",
|
|
3514
|
-
"down Remove containers, networks, and volumes (full teardown)",
|
|
3515
|
-
"status Show service status and ports",
|
|
3516
|
-
"logs Stream service logs (Ctrl+C to stop)",
|
|
3517
|
-
"clean Remove conflicting containers from other setups"
|
|
3518
|
-
],
|
|
3519
|
-
examples: [
|
|
3520
|
-
"midnight localnet up",
|
|
3521
|
-
"midnight localnet stop",
|
|
3522
|
-
"midnight localnet status",
|
|
3523
|
-
"midnight localnet down",
|
|
3524
|
-
"midnight localnet clean"
|
|
3525
|
-
],
|
|
3526
|
-
jsonFields: {
|
|
3527
|
-
subcommand: "up, stop, down, status, or clean",
|
|
3528
|
-
services: "Array of { name, state, port, health? } (up/status only)",
|
|
3529
|
-
status: "Operation result message (stop/down/clean)",
|
|
3530
|
-
removed: "Array of removed container names (clean only)"
|
|
3531
|
-
}
|
|
3532
|
-
},
|
|
3533
|
-
{
|
|
3534
|
-
name: "help",
|
|
3535
|
-
description: "Show usage for all commands or a specific command",
|
|
3536
|
-
usage: "midnight help [command]",
|
|
3537
|
-
examples: [
|
|
3538
|
-
"midnight help",
|
|
3539
|
-
"midnight help balance"
|
|
3540
|
-
],
|
|
3541
|
-
jsonFields: {
|
|
3542
|
-
cli: "CLI metadata (name, version, description)",
|
|
3543
|
-
globalFlags: "Array of global flag descriptions",
|
|
3544
|
-
commands: "Array of command specs with jsonFields"
|
|
3545
|
-
}
|
|
3546
|
-
}
|
|
3547
|
-
];
|
|
3548
|
-
});
|
|
3549
|
-
|
|
3550
|
-
// src/wallet.ts
|
|
3551
|
-
init_format();
|
|
3552
|
-
init_exit_codes();
|
|
3553
|
-
init_pkg();
|
|
3554
|
-
if (process.argv.includes("--mcp")) {
|
|
3555
|
-
await Promise.resolve().then(() => (init_mcp_server(), exports_mcp_server));
|
|
3556
|
-
} else {
|
|
3557
|
-
let handleShutdown = function() {
|
|
3558
|
-
abortController.abort();
|
|
3559
|
-
setTimeout(() => process.exit(130), 5000).unref();
|
|
3560
|
-
};
|
|
3561
|
-
const args = parseArgs();
|
|
3562
|
-
const jsonMode = hasFlag(args, "json");
|
|
3563
|
-
if (hasFlag(args, "version") || hasFlag(args, "v")) {
|
|
3564
|
-
process.stdout.write(PKG_VERSION + `
|
|
3565
|
-
`);
|
|
3566
|
-
process.exit(0);
|
|
3567
|
-
}
|
|
3568
|
-
if (hasFlag(args, "help") || hasFlag(args, "h")) {
|
|
3569
|
-
args.command = "help";
|
|
3570
|
-
}
|
|
3571
|
-
const command = args.command ?? "help";
|
|
3572
|
-
let restoreStderr;
|
|
3573
|
-
if (jsonMode) {
|
|
3574
|
-
restoreStderr = suppressStderr();
|
|
3575
|
-
}
|
|
3576
|
-
const abortController = new AbortController;
|
|
3577
|
-
const { signal } = abortController;
|
|
3578
|
-
process.on("SIGINT", handleShutdown);
|
|
3579
|
-
process.on("SIGTERM", handleShutdown);
|
|
3580
|
-
async function run() {
|
|
3581
|
-
switch (command) {
|
|
3582
|
-
case "help": {
|
|
3583
|
-
const { default: handler } = await Promise.resolve().then(() => (init_help(), exports_help));
|
|
3584
|
-
return handler(args);
|
|
3585
|
-
}
|
|
3586
|
-
case "generate": {
|
|
3587
|
-
const { default: handler } = await Promise.resolve().then(() => (init_generate(), exports_generate));
|
|
3588
|
-
return handler(args);
|
|
3589
|
-
}
|
|
3590
|
-
case "info": {
|
|
3591
|
-
const { default: handler } = await Promise.resolve().then(() => (init_info(), exports_info));
|
|
3592
|
-
return handler(args);
|
|
3593
|
-
}
|
|
3594
|
-
case "balance": {
|
|
3595
|
-
const { default: handler } = await Promise.resolve().then(() => (init_balance(), exports_balance));
|
|
3596
|
-
return handler(args);
|
|
3597
|
-
}
|
|
3598
|
-
case "address": {
|
|
3599
|
-
const { default: handler } = await Promise.resolve().then(() => (init_address(), exports_address));
|
|
3600
|
-
return handler(args);
|
|
3601
|
-
}
|
|
3602
|
-
case "genesis-address": {
|
|
3603
|
-
const { default: handler } = await Promise.resolve().then(() => (init_genesis_address(), exports_genesis_address));
|
|
3604
|
-
return handler(args);
|
|
3605
|
-
}
|
|
3606
|
-
case "inspect-cost": {
|
|
3607
|
-
const { default: handler } = await Promise.resolve().then(() => (init_inspect_cost(), exports_inspect_cost));
|
|
3608
|
-
return handler(args);
|
|
3609
|
-
}
|
|
3610
|
-
case "config": {
|
|
3611
|
-
const { default: handler } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
3612
|
-
return handler(args);
|
|
3613
|
-
}
|
|
3614
|
-
case "airdrop": {
|
|
3615
|
-
const { default: handler } = await Promise.resolve().then(() => (init_airdrop(), exports_airdrop));
|
|
3616
|
-
return handler(args, signal);
|
|
3617
|
-
}
|
|
3618
|
-
case "transfer": {
|
|
3619
|
-
const { default: handler } = await Promise.resolve().then(() => (init_transfer2(), exports_transfer));
|
|
3620
|
-
return handler(args, signal);
|
|
3621
|
-
}
|
|
3622
|
-
case "dust": {
|
|
3623
|
-
const { default: handler } = await Promise.resolve().then(() => (init_dust(), exports_dust));
|
|
3624
|
-
return handler(args, signal);
|
|
3625
|
-
}
|
|
3626
|
-
case "localnet": {
|
|
3627
|
-
const { default: handler } = await Promise.resolve().then(() => (init_localnet2(), exports_localnet));
|
|
3628
|
-
return handler(args);
|
|
3629
|
-
}
|
|
3630
|
-
default:
|
|
3631
|
-
throw new Error(`Unknown command: "${command}"
|
|
3632
|
-
Run "midnight help" to see available commands.`);
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
const FACADE_COMMANDS = new Set(["airdrop", "transfer", "dust"]);
|
|
3636
|
-
run().then(() => {
|
|
3637
|
-
if (FACADE_COMMANDS.has(command)) {
|
|
3638
|
-
process.exit(0);
|
|
3639
|
-
}
|
|
3640
|
-
}).catch((err) => {
|
|
3641
|
-
if (jsonMode) {
|
|
3642
|
-
restoreStderr?.();
|
|
3643
|
-
const { exitCode, errorCode } = classifyError(err);
|
|
3644
|
-
writeJsonError(err, errorCode, exitCode);
|
|
3645
|
-
process.exit(exitCode);
|
|
3646
|
-
} else {
|
|
3647
|
-
process.stderr.write(`
|
|
3648
|
-
` + errorBox(err.message, 'Run "midnight help" for usage information.') + `
|
|
3649
|
-
|
|
3650
|
-
`);
|
|
3651
|
-
const { exitCode } = classifyError(err);
|
|
3652
|
-
process.exit(exitCode);
|
|
3653
|
-
}
|
|
3654
|
-
});
|
|
3655
|
-
}
|
|
414
|
+
`;process.stdout.write($)}async function H2($){if(P($,"agent")){C$();return}if(P($,"json")){S$();return}let Z=$.subcommand;if(Z){let X=I0.find((q)=>q.name===Z);if(!X)throw new Error(`Unknown command: "${Z}"
|
|
415
|
+
Available commands: ${I0.map((q)=>q.name).join(", ")}`);w$(X);return}if(!process.stderr.isTTY){b$();return}let Q=k$();await W2(void 0,Q)}var I0;var F2=O(()=>{N();V2();g1();k0();I0=[{name:"generate",description:"Generate a new wallet (random mnemonic, or restore from seed/mnemonic)",usage:'midnight generate [--network <name>] [--seed <hex>] [--mnemonic "..."] [--output <file>] [--force]',flags:["--network <name> Network: preprod, preview, undeployed","--seed <hex> Restore from existing seed (64-char hex)",'--mnemonic "..." Restore from BIP-39 mnemonic (24 words)',"--output <file> Custom output path (default: ~/.midnight/wallet.json)","--force Overwrite existing wallet file"],examples:["midnight generate --network preprod","midnight generate --network preprod --output my-wallet.json","midnight generate --seed 0123456789abcdef..."],jsonFields:{address:"Generated wallet address (bech32m)",network:"Network name",seed:"Hex-encoded 32-byte seed",mnemonic:"BIP-39 mnemonic (24 words, only if generated or provided)",file:"Path where wallet file was saved",createdAt:"ISO 8601 creation timestamp"}},{name:"info",description:"Display wallet address, network, creation date (no secrets shown)",usage:"midnight info [--wallet <file>]",flags:["--wallet <file> Custom wallet file path"],examples:["midnight info","midnight info --wallet my-wallet.json"],jsonFields:{address:"Wallet address (bech32m)",network:"Network name",createdAt:"ISO 8601 creation timestamp",file:"Wallet file path"}},{name:"balance",description:"Check unshielded balance via GraphQL subscription",usage:"midnight balance [address] [--network <name>] [--indexer-ws <url>]",flags:["<address> Address to check (or reads from wallet file)","--network <name> Override network detection","--indexer-ws <url> Custom indexer WebSocket URL"],examples:["midnight balance","midnight balance mn_addr_preprod1...","midnight balance --network preprod"],jsonFields:{address:"Checked address (bech32m)",network:"Network name",balances:"Object mapping token type to balance string",utxoCount:"Number of UTXOs",txCount:"Number of transactions synced"}},{name:"address",description:"Derive and display an unshielded address from a seed",usage:"midnight address --seed <hex> [--network <name>] [--index <n>]",flags:["--seed <hex> Seed to derive from (required, 64-char hex)","--network <name> Network for address prefix (default: resolved)","--index <n> Key derivation index (default: 0)"],examples:["midnight address --seed 0123456789abcdef... --network preprod","midnight address --seed 0123456789abcdef... --index 1"],jsonFields:{address:"Derived address (bech32m)",network:"Network name",index:"Key derivation index",path:"BIP-44 derivation path"}},{name:"genesis-address",description:"Display the genesis wallet address (seed 0x01) for a network",usage:"midnight genesis-address [--network <name>]",flags:["--network <name> Network for address prefix (default: resolved)"],examples:["midnight genesis-address --network undeployed","midnight genesis-address --network preprod"],jsonFields:{address:"Genesis wallet address (bech32m)",network:"Network name"}},{name:"inspect-cost",description:"Display current block limits derived from LedgerParameters",usage:"midnight inspect-cost",examples:["midnight inspect-cost"],jsonFields:{readTime:"Read time limit (picoseconds)",computeTime:"Compute time limit (picoseconds)",blockUsage:"Block usage limit (bytes)",bytesWritten:"Bytes written limit (bytes)",bytesChurned:"Bytes churned limit (bytes)"}},{name:"airdrop",description:"Fund your wallet from the genesis wallet (undeployed network only)",usage:"midnight airdrop <amount> [--wallet <file>]",flags:["<amount> Amount in NIGHT to airdrop","--wallet <file> Custom wallet file path"],examples:["midnight airdrop 1000","midnight airdrop 0.5 --wallet my-wallet.json"],jsonFields:{txHash:"Transaction hash",amount:"Amount airdropped (NIGHT string)",recipient:"Recipient address (bech32m)",network:"Network name"}},{name:"transfer",description:"Send NIGHT tokens to another address",usage:"midnight transfer <to> <amount> [--wallet <file>]",flags:["<to> Recipient bech32m address","<amount> Amount in NIGHT to send","--wallet <file> Custom wallet file path"],examples:["midnight transfer mn_addr_undeployed1... 100","midnight transfer mn_addr_preprod1... 50 --wallet my-wallet.json"],jsonFields:{txHash:"Transaction hash",amount:"Amount transferred (NIGHT string)",recipient:"Recipient address (bech32m)",network:"Network name"}},{name:"dust",description:"Register UTXOs for dust (fee token) generation or check status",usage:"midnight dust <register|status> [--wallet <file>]",flags:["register Register NIGHT UTXOs for dust generation","status Check dust registration status and balance","--wallet <file> Custom wallet file path"],examples:["midnight dust register","midnight dust status"],jsonFields:{subcommand:"register or status",dustBalance:"Dust balance (raw bigint string)",registered:"Number of registered UTXOs (status only)",unregistered:"Number of unregistered UTXOs (status only)",nightBalance:"NIGHT balance (raw bigint string, status only)",dustAvailable:"Whether dust tokens are available (status only)",txHash:"Registration transaction hash (register only, if submitted)"}},{name:"config",description:"Manage persistent config (default network, etc.)",usage:"midnight config <get|set> <key> [value]",flags:["get <key> Read a config value","set <key> <value> Write a config value"],examples:["midnight config get network","midnight config set network preprod"],jsonFields:{action:"get or set",key:"Config key name",value:"Config value"}},{name:"localnet",description:"Manage a local Midnight network via Docker Compose",usage:"midnight localnet <up|stop|down|status|logs|clean>",flags:["up Start the local network (node, indexer, proof server)","stop Stop containers (preserves state for fast restart)","down Remove containers, networks, and volumes (full teardown)","status Show service status and ports","logs Stream service logs (Ctrl+C to stop)","clean Remove conflicting containers from other setups"],examples:["midnight localnet up","midnight localnet stop","midnight localnet status","midnight localnet down","midnight localnet clean"],jsonFields:{subcommand:"up, stop, down, status, or clean",services:"Array of { name, state, port, health? } (up/status only)",status:"Operation result message (stop/down/clean)",removed:"Array of removed container names (clean only)"}},{name:"help",description:"Show usage for all commands or a specific command",usage:"midnight help [command]",examples:["midnight help","midnight help balance"],jsonFields:{cli:"CLI metadata (name, version, description)",globalFlags:"Array of global flag descriptions",commands:"Array of command specs with jsonFields"}}]});N();$1();k0();if(process.argv.includes("--mcp"))await Promise.resolve().then(() => (J2(),v$));else{let Y=function(){q.abort(),setTimeout(()=>process.exit(130),5000).unref()},$=o1(),Z=P($,"json");if(P($,"version")||P($,"v"))process.stdout.write(Q0+`
|
|
416
|
+
`),process.exit(0);if(P($,"help")||P($,"h"))$.command="help";let Q=$.command??"help",X;if(Z)X=G3();let q=new AbortController,{signal:z}=q;process.on("SIGINT",Y),process.on("SIGTERM",Y);async function K(){switch(Q){case"help":{let{default:U}=await Promise.resolve().then(() => (F2(),j2));return U($)}case"generate":{let{default:U}=await Promise.resolve().then(() => (G1(),K1));return U($)}case"info":{let{default:U}=await Promise.resolve().then(() => (B1(),U1));return U($)}case"balance":{let{default:U}=await Promise.resolve().then(() => (V1(),W1));return U($)}case"address":{let{default:U}=await Promise.resolve().then(() => (j1(),H1));return U($)}case"genesis-address":{let{default:U}=await Promise.resolve().then(() => (P1(),F1));return U($)}case"inspect-cost":{let{default:U}=await Promise.resolve().then(() => (L1(),O1));return U($)}case"config":{let{default:U}=await Promise.resolve().then(() => (w1(),b1));return U($)}case"airdrop":{let{default:U}=await Promise.resolve().then(() => (x1(),R1));return U($,z)}case"transfer":{let{default:U}=await Promise.resolve().then(() => (N1(),E1));return U($,z)}case"dust":{let{default:U}=await Promise.resolve().then(() => (k1(),v1));return U($,z)}case"localnet":{let{default:U}=await Promise.resolve().then(() => (u1(),p1));return U($)}default:throw new Error(`Unknown command: "${Q}"
|
|
417
|
+
Run "midnight help" to see available commands.`)}}let G=new Set(["airdrop","transfer","dust"]);K().then(()=>{if(G.has(Q))process.exit(0)}).catch((U)=>{if(Z){X?.();let{exitCode:W,errorCode:J}=O0(U);U3(U,J,W),process.exit(W)}else{process.stderr.write(`
|
|
418
|
+
`+K3(U.message,'Run "midnight help" for usage information.')+`
|
|
419
|
+
|
|
420
|
+
`);let{exitCode:W}=O0(U);process.exit(W)}})}
|