@alchemy/cli 0.2.2 → 0.3.1
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/README.md +29 -9
- package/dist/{chunk-2WI4JODY.js → chunk-6XTLILDF.js} +15 -7
- package/dist/{chunk-6KOJKH6N.js → chunk-J6RZM4CJ.js} +153 -12
- package/dist/{chunk-NA7MQB7X.js → chunk-QDDJ3OYO.js} +38 -1
- package/dist/index.js +801 -178
- package/dist/{interactive-EAUEVR63.js → interactive-CLPT5QDZ.js} +44 -10
- package/dist/{onboarding-SJ2BRK5I.js → onboarding-XNAWN5BR.js} +2 -2
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -8,19 +8,21 @@ import {
|
|
|
8
8
|
registerConfig,
|
|
9
9
|
registerWallet,
|
|
10
10
|
resolveAPIKey,
|
|
11
|
+
resolveAddress,
|
|
11
12
|
resolveAppId,
|
|
12
13
|
resolveConfiguredNetworkSlugs,
|
|
13
14
|
resolveNetwork,
|
|
14
15
|
splitCommaList,
|
|
15
16
|
validateAddress,
|
|
16
17
|
validateTxHash
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-J6RZM4CJ.js";
|
|
18
19
|
import {
|
|
19
20
|
getRPCNetworks,
|
|
20
21
|
getSetupStatus,
|
|
21
22
|
isSetupComplete,
|
|
23
|
+
nativeTokenSymbol,
|
|
22
24
|
shouldRunOnboarding
|
|
23
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-QDDJ3OYO.js";
|
|
24
26
|
import {
|
|
25
27
|
EXIT_CODES,
|
|
26
28
|
ErrorCode,
|
|
@@ -57,22 +59,25 @@ import {
|
|
|
57
59
|
printSyntaxJSON,
|
|
58
60
|
printTable,
|
|
59
61
|
printUpdateNotice,
|
|
62
|
+
promptConfirm,
|
|
60
63
|
promptSelect,
|
|
61
64
|
quiet,
|
|
65
|
+
red,
|
|
62
66
|
setFlags,
|
|
67
|
+
setNoColor,
|
|
63
68
|
successBadge,
|
|
64
69
|
timeAgo,
|
|
65
70
|
verbose,
|
|
66
71
|
weiToEth,
|
|
67
72
|
withSpinner
|
|
68
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-6XTLILDF.js";
|
|
69
74
|
|
|
70
75
|
// src/index.ts
|
|
71
76
|
import { Command, Help } from "commander";
|
|
72
77
|
|
|
73
78
|
// src/commands/rpc.ts
|
|
74
79
|
function registerRPC(program2) {
|
|
75
|
-
program2.command("rpc
|
|
80
|
+
program2.command("rpc").argument("<method>", "JSON-RPC method name (e.g. eth_blockNumber)").argument("[params...]", "Method parameters as JSON values").description("Make a raw JSON-RPC call").addHelpText(
|
|
76
81
|
"after",
|
|
77
82
|
`
|
|
78
83
|
Examples:
|
|
@@ -104,36 +109,50 @@ Examples:
|
|
|
104
109
|
|
|
105
110
|
// src/commands/balance.ts
|
|
106
111
|
function registerBalance(program2) {
|
|
107
|
-
program2.command("balance
|
|
112
|
+
program2.command("balance").argument("[address]", "Wallet address (0x...) or ENS name, or pipe via stdin").alias("bal").description("Get the native token balance of an address").addHelpText(
|
|
108
113
|
"after",
|
|
109
114
|
`
|
|
110
115
|
Examples:
|
|
111
116
|
alchemy balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
112
117
|
alchemy balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n polygon-mainnet
|
|
113
|
-
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy balance
|
|
114
|
-
|
|
118
|
+
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy balance
|
|
119
|
+
alchemy balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --block 15537393
|
|
120
|
+
alchemy balance vitalik.eth`
|
|
121
|
+
).option("--block <block>", "Block number, hex, or tag (default: latest)").action(async (addressArg, opts) => {
|
|
115
122
|
try {
|
|
116
|
-
const
|
|
117
|
-
validateAddress(address);
|
|
123
|
+
const addressInput = addressArg ?? await readStdinArg("address");
|
|
118
124
|
const client = clientFromFlags(program2);
|
|
125
|
+
const address = await resolveAddress(addressInput, client);
|
|
126
|
+
let blockParam = opts?.block ?? "latest";
|
|
127
|
+
if (blockParam !== "latest" && blockParam !== "earliest" && blockParam !== "pending") {
|
|
128
|
+
if (!blockParam.startsWith("0x")) {
|
|
129
|
+
const num = parseInt(blockParam, 10);
|
|
130
|
+
if (isNaN(num) || num < 0) {
|
|
131
|
+
throw errInvalidArgs("Block must be a number, hex, or tag (latest, earliest, pending).");
|
|
132
|
+
}
|
|
133
|
+
blockParam = `0x${num.toString(16)}`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
119
136
|
const result = await withSpinner(
|
|
120
137
|
"Fetching balance\u2026",
|
|
121
138
|
"Balance fetched",
|
|
122
|
-
() => client.call("eth_getBalance", [address,
|
|
139
|
+
() => client.call("eth_getBalance", [address, blockParam])
|
|
123
140
|
);
|
|
124
141
|
const wei = BigInt(result);
|
|
125
142
|
const network = resolveNetwork(program2);
|
|
143
|
+
const symbol = nativeTokenSymbol(network);
|
|
126
144
|
if (isJSONMode()) {
|
|
127
145
|
printJSON({
|
|
128
146
|
address,
|
|
129
147
|
wei: wei.toString(),
|
|
130
|
-
|
|
148
|
+
balance: weiToEth(wei),
|
|
149
|
+
symbol,
|
|
131
150
|
network
|
|
132
151
|
});
|
|
133
152
|
} else {
|
|
134
153
|
printKeyValueBox([
|
|
135
154
|
["Address", address],
|
|
136
|
-
["Balance", green(weiToEth(wei)
|
|
155
|
+
["Balance", green(`${weiToEth(wei)} ${symbol}`)],
|
|
137
156
|
["Network", network]
|
|
138
157
|
]);
|
|
139
158
|
if (verbose) {
|
|
@@ -151,9 +170,76 @@ Examples:
|
|
|
151
170
|
});
|
|
152
171
|
}
|
|
153
172
|
|
|
173
|
+
// src/lib/block-format.ts
|
|
174
|
+
function parseHexQuantity(value) {
|
|
175
|
+
if (typeof value !== "string" || !/^0x[0-9a-f]+$/i.test(value)) {
|
|
176
|
+
return void 0;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
return BigInt(value);
|
|
180
|
+
} catch {
|
|
181
|
+
return void 0;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function formatWithCommas(value) {
|
|
185
|
+
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
186
|
+
}
|
|
187
|
+
function formatHexQuantity(value) {
|
|
188
|
+
const parsed = parseHexQuantity(value);
|
|
189
|
+
if (parsed === void 0) return void 0;
|
|
190
|
+
return formatWithCommas(parsed);
|
|
191
|
+
}
|
|
192
|
+
function formatBlockTimestamp(value) {
|
|
193
|
+
if (typeof value !== "string") return void 0;
|
|
194
|
+
const parsed = parseHexQuantity(value);
|
|
195
|
+
if (parsed === void 0) return void 0;
|
|
196
|
+
const millis = Number(parsed) * 1e3;
|
|
197
|
+
if (!Number.isFinite(millis)) return void 0;
|
|
198
|
+
const d = new Date(millis);
|
|
199
|
+
if (Number.isNaN(d.getTime())) return void 0;
|
|
200
|
+
const iso = d.toISOString().replace(".000Z", "Z");
|
|
201
|
+
return `${iso} ${dim("(" + timeAgo(value) + ")")}`;
|
|
202
|
+
}
|
|
203
|
+
function formatHexWithRaw(value) {
|
|
204
|
+
if (typeof value !== "string") return void 0;
|
|
205
|
+
const parsed = parseHexQuantity(value);
|
|
206
|
+
if (parsed === void 0) return void 0;
|
|
207
|
+
return `${formatWithCommas(parsed)} ${dim(`(${value})`)}`;
|
|
208
|
+
}
|
|
209
|
+
function formatWeiWithRaw(value, symbol = "ETH") {
|
|
210
|
+
if (typeof value !== "string") return void 0;
|
|
211
|
+
const parsed = parseHexQuantity(value);
|
|
212
|
+
if (parsed === void 0) return void 0;
|
|
213
|
+
return `${weiToEth(parsed)} ${symbol} ${dim(`(${value})`)}`;
|
|
214
|
+
}
|
|
215
|
+
function formatGwei(gwei) {
|
|
216
|
+
const fixed = gwei.toFixed(9);
|
|
217
|
+
return fixed.replace(/\.?0+$/, "") || "0";
|
|
218
|
+
}
|
|
219
|
+
function formatGweiWithRaw(value) {
|
|
220
|
+
if (typeof value !== "string") return void 0;
|
|
221
|
+
const parsed = parseHexQuantity(value);
|
|
222
|
+
if (parsed === void 0) return void 0;
|
|
223
|
+
const gwei = Number(parsed) / 1e9;
|
|
224
|
+
return `${formatGwei(gwei)} gwei ${dim(`(${value})`)}`;
|
|
225
|
+
}
|
|
226
|
+
function formatGasSummary(gasUsed, gasLimit, options) {
|
|
227
|
+
const used = parseHexQuantity(gasUsed);
|
|
228
|
+
const limit = parseHexQuantity(gasLimit);
|
|
229
|
+
if (used === void 0 || limit === void 0) return void 0;
|
|
230
|
+
const usedFormatted = formatWithCommas(used);
|
|
231
|
+
const limitFormatted = formatWithCommas(limit);
|
|
232
|
+
if (limit === 0n) return `${usedFormatted} / ${limitFormatted}`;
|
|
233
|
+
const bps = used * 10000n / limit;
|
|
234
|
+
const percent = Number(bps) / 100;
|
|
235
|
+
const percentText = `${percent.toFixed(2)}%`;
|
|
236
|
+
const percentDisplay = options?.colored ? dim(percentText) : percentText;
|
|
237
|
+
return `${usedFormatted} / ${limitFormatted} (${percentDisplay})`;
|
|
238
|
+
}
|
|
239
|
+
|
|
154
240
|
// src/commands/tx.ts
|
|
155
241
|
function registerTx(program2) {
|
|
156
|
-
program2.command("tx
|
|
242
|
+
program2.command("tx").argument("[hash]", "Transaction hash (0x...) or pipe via stdin").description("Get transaction details by hash").addHelpText(
|
|
157
243
|
"after",
|
|
158
244
|
`
|
|
159
245
|
Examples:
|
|
@@ -178,23 +264,34 @@ Examples:
|
|
|
178
264
|
printJSON({ transaction: tx, receipt });
|
|
179
265
|
return;
|
|
180
266
|
}
|
|
267
|
+
const network = resolveNetwork(program2);
|
|
268
|
+
const symbol = nativeTokenSymbol(network);
|
|
181
269
|
const pairs = [["Hash", hash]];
|
|
182
270
|
if (tx.from) pairs.push(["From", String(tx.from)]);
|
|
183
271
|
if (tx.to) pairs.push(["To", String(tx.to)]);
|
|
184
272
|
if (tx.value) {
|
|
185
|
-
const
|
|
186
|
-
pairs.push(["Value", green(
|
|
273
|
+
const formatted = formatWeiWithRaw(tx.value, symbol);
|
|
274
|
+
pairs.push(["Value", formatted ? green(formatted) : String(tx.value)]);
|
|
275
|
+
}
|
|
276
|
+
if (tx.blockNumber) {
|
|
277
|
+
const formatted = formatHexWithRaw(tx.blockNumber);
|
|
278
|
+
pairs.push(["Block", formatted ?? String(tx.blockNumber)]);
|
|
187
279
|
}
|
|
188
|
-
if (tx.blockNumber) pairs.push(["Block", String(tx.blockNumber)]);
|
|
189
280
|
if (receipt) {
|
|
190
281
|
if (receipt.status === "0x1") {
|
|
191
282
|
pairs.push(["Status", `${successBadge()} Success`]);
|
|
192
283
|
} else if (receipt.status) {
|
|
193
284
|
pairs.push(["Status", `${failBadge()} Failed`]);
|
|
194
285
|
}
|
|
195
|
-
if (receipt.gasUsed)
|
|
286
|
+
if (receipt.gasUsed) {
|
|
287
|
+
const formatted = formatHexWithRaw(receipt.gasUsed);
|
|
288
|
+
pairs.push(["Gas Used", formatted ?? String(receipt.gasUsed)]);
|
|
289
|
+
}
|
|
290
|
+
if (receipt.effectiveGasPrice) {
|
|
291
|
+
const formatted = formatGweiWithRaw(receipt.effectiveGasPrice);
|
|
292
|
+
pairs.push(["Gas Price", formatted ?? String(receipt.effectiveGasPrice)]);
|
|
293
|
+
}
|
|
196
294
|
}
|
|
197
|
-
const network = resolveNetwork(program2);
|
|
198
295
|
const explorerURL = etherscanTxURL(hash, network);
|
|
199
296
|
if (explorerURL) {
|
|
200
297
|
pairs.push(["Explorer", explorerURL]);
|
|
@@ -210,53 +307,74 @@ Examples:
|
|
|
210
307
|
});
|
|
211
308
|
}
|
|
212
309
|
|
|
213
|
-
// src/
|
|
214
|
-
function
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
310
|
+
// src/commands/receipt.ts
|
|
311
|
+
function registerReceipt(program2) {
|
|
312
|
+
program2.command("receipt").argument("[hash]", "Transaction hash (0x...) or pipe via stdin").description("Get transaction receipt (status, gas used, logs)").addHelpText(
|
|
313
|
+
"after",
|
|
314
|
+
`
|
|
315
|
+
Examples:
|
|
316
|
+
alchemy receipt 0xabc123...
|
|
317
|
+
echo 0xabc123... | alchemy receipt`
|
|
318
|
+
).action(async (hashArg) => {
|
|
319
|
+
try {
|
|
320
|
+
const hash = hashArg ?? await readStdinArg("hash");
|
|
321
|
+
validateTxHash(hash);
|
|
322
|
+
const client = clientFromFlags(program2);
|
|
323
|
+
const receipt = await withSpinner(
|
|
324
|
+
"Fetching receipt\u2026",
|
|
325
|
+
"Receipt fetched",
|
|
326
|
+
() => client.call("eth_getTransactionReceipt", [hash])
|
|
327
|
+
);
|
|
328
|
+
if (!receipt) throw errNotFound(`receipt for ${hash}`);
|
|
329
|
+
if (isJSONMode()) {
|
|
330
|
+
printJSON(receipt);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const pairs = [["Hash", hash]];
|
|
334
|
+
if (receipt.status === "0x1") {
|
|
335
|
+
pairs.push(["Status", `${successBadge()} ${green("Success")}`]);
|
|
336
|
+
} else if (receipt.status) {
|
|
337
|
+
pairs.push(["Status", `${failBadge()} ${red("Failed")}`]);
|
|
338
|
+
}
|
|
339
|
+
if (receipt.from) pairs.push(["From", String(receipt.from)]);
|
|
340
|
+
if (receipt.to) pairs.push(["To", String(receipt.to)]);
|
|
341
|
+
if (receipt.contractAddress) {
|
|
342
|
+
pairs.push(["Contract Created", String(receipt.contractAddress)]);
|
|
343
|
+
}
|
|
344
|
+
if (receipt.blockNumber) {
|
|
345
|
+
const hex = String(receipt.blockNumber);
|
|
346
|
+
const decoded = formatHexQuantity(hex);
|
|
347
|
+
pairs.push(["Block", decoded ? `${decoded} ${dim(`(${hex})`)}` : hex]);
|
|
348
|
+
}
|
|
349
|
+
if (receipt.gasUsed) {
|
|
350
|
+
const hex = String(receipt.gasUsed);
|
|
351
|
+
const decoded = formatHexQuantity(hex);
|
|
352
|
+
pairs.push(["Gas Used", decoded ? `${decoded} ${dim(`(${hex})`)}` : hex]);
|
|
353
|
+
}
|
|
354
|
+
if (receipt.effectiveGasPrice) {
|
|
355
|
+
const hex = String(receipt.effectiveGasPrice);
|
|
356
|
+
const wei = BigInt(hex);
|
|
357
|
+
const gwei = Number(wei) / 1e9;
|
|
358
|
+
pairs.push(["Gas Price", `${formatGwei(gwei)} gwei ${dim(`(${hex})`)}`]);
|
|
359
|
+
}
|
|
360
|
+
if (Array.isArray(receipt.logs)) {
|
|
361
|
+
pairs.push(["Logs", `${receipt.logs.length} event${receipt.logs.length === 1 ? "" : "s"}`]);
|
|
362
|
+
}
|
|
363
|
+
const network = resolveNetwork(program2);
|
|
364
|
+
const explorerURL = etherscanTxURL(hash, network);
|
|
365
|
+
if (explorerURL) {
|
|
366
|
+
pairs.push(["Explorer", explorerURL]);
|
|
367
|
+
}
|
|
368
|
+
printKeyValueBox(pairs);
|
|
369
|
+
} catch (err) {
|
|
370
|
+
exitWithError(err);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
255
373
|
}
|
|
256
374
|
|
|
257
375
|
// src/commands/block.ts
|
|
258
376
|
function registerBlock(program2) {
|
|
259
|
-
program2.command("block
|
|
377
|
+
program2.command("block").argument("<number>", "Block number, hex (0x...), or tag (latest, earliest, pending)").description("Get block details by number").addHelpText(
|
|
260
378
|
"after",
|
|
261
379
|
`
|
|
262
380
|
Examples:
|
|
@@ -295,7 +413,7 @@ Examples:
|
|
|
295
413
|
}
|
|
296
414
|
const pairs = [];
|
|
297
415
|
if (block.number) {
|
|
298
|
-
const formatted =
|
|
416
|
+
const formatted = formatHexWithRaw(block.number);
|
|
299
417
|
pairs.push(["Block", bold(formatted ?? String(block.number))]);
|
|
300
418
|
}
|
|
301
419
|
if (block.hash) pairs.push(["Hash", String(block.hash)]);
|
|
@@ -313,11 +431,11 @@ Examples:
|
|
|
313
431
|
pairs.push(["Gas", gasSummary]);
|
|
314
432
|
} else {
|
|
315
433
|
if (block.gasUsed) {
|
|
316
|
-
const formatted =
|
|
434
|
+
const formatted = formatHexWithRaw(block.gasUsed);
|
|
317
435
|
pairs.push(["Gas Used", formatted ?? String(block.gasUsed)]);
|
|
318
436
|
}
|
|
319
437
|
if (block.gasLimit) {
|
|
320
|
-
const formatted =
|
|
438
|
+
const formatted = formatHexWithRaw(block.gasLimit);
|
|
321
439
|
pairs.push(["Gas Limit", formatted ?? String(block.gasLimit)]);
|
|
322
440
|
}
|
|
323
441
|
}
|
|
@@ -336,8 +454,28 @@ Examples:
|
|
|
336
454
|
}
|
|
337
455
|
|
|
338
456
|
// src/commands/nfts.ts
|
|
457
|
+
async function promptNFTPagination(shown, total) {
|
|
458
|
+
const action = await promptSelect({
|
|
459
|
+
message: `Showing ${shown} of ${total} NFTs`,
|
|
460
|
+
options: [
|
|
461
|
+
{ label: "Load next page", value: "next" },
|
|
462
|
+
{ label: "Stop here", value: "stop" }
|
|
463
|
+
],
|
|
464
|
+
initialValue: "next",
|
|
465
|
+
cancelMessage: "Stopped pagination."
|
|
466
|
+
});
|
|
467
|
+
if (action === null) return "stop";
|
|
468
|
+
return action;
|
|
469
|
+
}
|
|
470
|
+
function formatNFTRows(nfts) {
|
|
471
|
+
return nfts.map((nft) => [
|
|
472
|
+
nft.contract.name || dim("unnamed"),
|
|
473
|
+
nft.name || `#${nft.tokenId}`,
|
|
474
|
+
nft.contract.address
|
|
475
|
+
]);
|
|
476
|
+
}
|
|
339
477
|
function registerNFTs(program2) {
|
|
340
|
-
const cmd = program2.command("nfts").description("NFT API wrappers").argument("[address]", "Wallet address (default action: list owned NFTs)").option("--limit <n>", "Maximum number of NFTs to return", parseInt).option("--page-key <key>", "Pagination key from a previous response").addHelpText(
|
|
478
|
+
const cmd = program2.command("nfts").description("NFT API wrappers").argument("[address]", "Wallet address or ENS name (default action: list owned NFTs)").option("--limit <n>", "Maximum number of NFTs to return per page", parseInt).option("--page-key <key>", "Pagination key from a previous response").addHelpText(
|
|
341
479
|
"after",
|
|
342
480
|
`
|
|
343
481
|
Examples:
|
|
@@ -347,15 +485,15 @@ Examples:
|
|
|
347
485
|
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy nfts`
|
|
348
486
|
).action(async (addressArg, opts) => {
|
|
349
487
|
try {
|
|
350
|
-
const
|
|
351
|
-
|
|
488
|
+
const addressInput = addressArg ?? await readStdinArg("address");
|
|
489
|
+
const client = clientFromFlags(program2);
|
|
490
|
+
const address = await resolveAddress(addressInput, client);
|
|
352
491
|
const params = {
|
|
353
492
|
owner: address,
|
|
354
493
|
withMetadata: "true"
|
|
355
494
|
};
|
|
356
495
|
if (opts.limit) params.pageSize = String(opts.limit);
|
|
357
496
|
if (opts.pageKey) params.pageKey = opts.pageKey;
|
|
358
|
-
const client = clientFromFlags(program2);
|
|
359
497
|
const result = await withSpinner(
|
|
360
498
|
"Fetching NFTs\u2026",
|
|
361
499
|
"NFTs fetched",
|
|
@@ -369,19 +507,41 @@ Examples:
|
|
|
369
507
|
emptyState("No NFTs found.");
|
|
370
508
|
return;
|
|
371
509
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
nft.name || `#${nft.tokenId}`,
|
|
375
|
-
nft.contract.address
|
|
376
|
-
]);
|
|
377
|
-
printTable(["Collection", "Name", "Contract"], rows);
|
|
510
|
+
let shown = result.ownedNfts.length;
|
|
511
|
+
printTable(["Collection", "Name", "Contract"], formatNFTRows(result.ownedNfts));
|
|
378
512
|
if (verbose) {
|
|
379
513
|
console.log("");
|
|
380
514
|
printJSON(result);
|
|
381
515
|
}
|
|
382
|
-
|
|
516
|
+
const interactive = isInteractiveAllowed(program2);
|
|
517
|
+
let pageKey = result.pageKey;
|
|
518
|
+
while (pageKey && interactive) {
|
|
519
|
+
const action = await promptNFTPagination(shown, result.totalCount);
|
|
520
|
+
if (action === "stop") {
|
|
521
|
+
console.log(`
|
|
522
|
+
${dim(`Next page key: ${pageKey}`)}`);
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
const nextParams = {
|
|
526
|
+
owner: address,
|
|
527
|
+
withMetadata: "true",
|
|
528
|
+
pageKey
|
|
529
|
+
};
|
|
530
|
+
if (opts.limit) nextParams.pageSize = String(opts.limit);
|
|
531
|
+
const nextResult = await withSpinner(
|
|
532
|
+
"Fetching next page\u2026",
|
|
533
|
+
"Page fetched",
|
|
534
|
+
() => client.callEnhanced("getNFTsForOwner", nextParams)
|
|
535
|
+
);
|
|
536
|
+
if (nextResult.ownedNfts.length > 0) {
|
|
537
|
+
printTable(["Collection", "Name", "Contract"], formatNFTRows(nextResult.ownedNfts));
|
|
538
|
+
shown += nextResult.ownedNfts.length;
|
|
539
|
+
}
|
|
540
|
+
pageKey = nextResult.pageKey;
|
|
541
|
+
}
|
|
542
|
+
if (pageKey && !interactive) {
|
|
383
543
|
console.log(`
|
|
384
|
-
${dim(`More results available. Use --page-key ${
|
|
544
|
+
${dim(`More results available. Use --page-key ${pageKey} to see the next page.`)}`);
|
|
385
545
|
}
|
|
386
546
|
} catch (err) {
|
|
387
547
|
exitWithError(err);
|
|
@@ -425,8 +585,34 @@ Examples:
|
|
|
425
585
|
}
|
|
426
586
|
|
|
427
587
|
// src/commands/tokens.ts
|
|
588
|
+
async function promptTokensPagination() {
|
|
589
|
+
const action = await promptSelect({
|
|
590
|
+
message: "More token balances available",
|
|
591
|
+
options: [
|
|
592
|
+
{ label: "Load next page", value: "next" },
|
|
593
|
+
{ label: "Stop here", value: "stop" }
|
|
594
|
+
],
|
|
595
|
+
initialValue: "next",
|
|
596
|
+
cancelMessage: "Stopped pagination."
|
|
597
|
+
});
|
|
598
|
+
if (action === null) return "stop";
|
|
599
|
+
return action;
|
|
600
|
+
}
|
|
601
|
+
function formatTokenRows(balances) {
|
|
602
|
+
const nonZero = balances.filter(
|
|
603
|
+
(tb) => tb.tokenBalance !== "0x0" && tb.tokenBalance !== "0x0000000000000000000000000000000000000000000000000000000000000000"
|
|
604
|
+
);
|
|
605
|
+
return nonZero.map((tb) => {
|
|
606
|
+
let decimalBalance = dim("unparseable");
|
|
607
|
+
try {
|
|
608
|
+
decimalBalance = BigInt(tb.tokenBalance).toString();
|
|
609
|
+
} catch {
|
|
610
|
+
}
|
|
611
|
+
return [tb.contractAddress, decimalBalance, tb.tokenBalance];
|
|
612
|
+
});
|
|
613
|
+
}
|
|
428
614
|
function registerTokens(program2) {
|
|
429
|
-
const cmd = program2.command("tokens").description("Token API wrappers").argument("[address]", "Wallet address (default action: list balances)").option("--page-key <key>", "Pagination key from a previous response").addHelpText(
|
|
615
|
+
const cmd = program2.command("tokens").description("Token API wrappers").argument("[address]", "Wallet address or ENS name (default action: list balances)").option("--page-key <key>", "Pagination key from a previous response").addHelpText(
|
|
430
616
|
"after",
|
|
431
617
|
`
|
|
432
618
|
Examples:
|
|
@@ -436,13 +622,13 @@ Examples:
|
|
|
436
622
|
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy tokens`
|
|
437
623
|
).action(async (addressArg, opts) => {
|
|
438
624
|
try {
|
|
439
|
-
const
|
|
440
|
-
|
|
625
|
+
const addressInput = addressArg ?? await readStdinArg("address");
|
|
626
|
+
const client = clientFromFlags(program2);
|
|
627
|
+
const address = await resolveAddress(addressInput, client);
|
|
441
628
|
const params = [address];
|
|
442
629
|
if (opts.pageKey) {
|
|
443
630
|
params.push("erc20", { pageKey: opts.pageKey });
|
|
444
631
|
}
|
|
445
|
-
const client = clientFromFlags(program2);
|
|
446
632
|
const result = await withSpinner(
|
|
447
633
|
"Fetching token balances\u2026",
|
|
448
634
|
"Token balances fetched",
|
|
@@ -452,36 +638,54 @@ Examples:
|
|
|
452
638
|
printJSON(result);
|
|
453
639
|
return;
|
|
454
640
|
}
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
);
|
|
458
|
-
if (nonZero.length === 0) {
|
|
641
|
+
const rows = formatTokenRows(result.tokenBalances);
|
|
642
|
+
if (rows.length === 0) {
|
|
459
643
|
emptyState("No token balances found.");
|
|
460
644
|
return;
|
|
461
645
|
}
|
|
462
|
-
|
|
463
|
-
let decimalBalance = dim("unparseable");
|
|
464
|
-
try {
|
|
465
|
-
decimalBalance = BigInt(tb.tokenBalance).toString();
|
|
466
|
-
} catch {
|
|
467
|
-
}
|
|
468
|
-
return [tb.contractAddress, decimalBalance, tb.tokenBalance];
|
|
469
|
-
});
|
|
646
|
+
let totalShown = rows.length;
|
|
470
647
|
printKeyValueBox([
|
|
471
648
|
["Address", address],
|
|
472
649
|
["Network", client.network],
|
|
473
|
-
["
|
|
650
|
+
["Tokens", String(totalShown)]
|
|
474
651
|
]);
|
|
475
652
|
printTable(["Contract", "Balance (base units)", "Raw (hex)"], rows);
|
|
476
653
|
console.log(`
|
|
477
|
-
${dim(
|
|
654
|
+
${dim(`${totalShown} tokens (zero balances hidden).`)}`);
|
|
478
655
|
if (verbose) {
|
|
479
656
|
console.log("");
|
|
480
657
|
printJSON(result);
|
|
481
658
|
}
|
|
482
|
-
|
|
659
|
+
const interactive = isInteractiveAllowed(program2);
|
|
660
|
+
let pageKey = result.pageKey;
|
|
661
|
+
while (pageKey && interactive) {
|
|
662
|
+
const action = await promptTokensPagination();
|
|
663
|
+
if (action === "stop") {
|
|
664
|
+
console.log(`
|
|
665
|
+
${dim(`Next page key: ${pageKey}`)}`);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
const nextResult = await withSpinner(
|
|
669
|
+
"Fetching next page\u2026",
|
|
670
|
+
"Page fetched",
|
|
671
|
+
() => client.call("alchemy_getTokenBalances", [address, "erc20", { pageKey }])
|
|
672
|
+
);
|
|
673
|
+
if (isJSONMode()) {
|
|
674
|
+
printJSON(nextResult);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const nextRows = formatTokenRows(nextResult.tokenBalances);
|
|
678
|
+
totalShown += nextRows.length;
|
|
679
|
+
if (nextRows.length > 0) {
|
|
680
|
+
printTable(["Contract", "Balance (base units)", "Raw (hex)"], nextRows);
|
|
681
|
+
}
|
|
682
|
+
console.log(`
|
|
683
|
+
${dim(`${totalShown} tokens total (zero balances hidden).`)}`);
|
|
684
|
+
pageKey = nextResult.pageKey;
|
|
685
|
+
}
|
|
686
|
+
if (pageKey && !interactive) {
|
|
483
687
|
console.log(`
|
|
484
|
-
${dim(`More results available. Use --page-key ${
|
|
688
|
+
${dim(`More results available. Use --page-key ${pageKey} to see the next page.`)}`);
|
|
485
689
|
}
|
|
486
690
|
} catch (err) {
|
|
487
691
|
exitWithError(err);
|
|
@@ -528,7 +732,7 @@ Examples:
|
|
|
528
732
|
// src/commands/network.ts
|
|
529
733
|
function registerNetwork(program2) {
|
|
530
734
|
const cmd = program2.command("network").description("Manage networks");
|
|
531
|
-
cmd.command("list").description("List
|
|
735
|
+
cmd.command("list").description("List RPC network IDs for use with --network (e.g. eth-mainnet)").option(
|
|
532
736
|
"--configured",
|
|
533
737
|
"List only configured app RPC networks (requires access key and app context)"
|
|
534
738
|
).option(
|
|
@@ -576,7 +780,7 @@ function registerNetwork(program2) {
|
|
|
576
780
|
console.log(`
|
|
577
781
|
Current: ${green(current)}`);
|
|
578
782
|
console.log(
|
|
579
|
-
` ${dim("Need Admin API chain
|
|
783
|
+
` ${dim("Need Admin API chain identifiers (e.g. ETH_MAINNET)? See: apps chains")}`
|
|
580
784
|
);
|
|
581
785
|
if (verbose) {
|
|
582
786
|
console.log("");
|
|
@@ -603,43 +807,6 @@ function registerVersion(program2) {
|
|
|
603
807
|
});
|
|
604
808
|
}
|
|
605
809
|
|
|
606
|
-
// src/commands/chains.ts
|
|
607
|
-
function registerChains(program2) {
|
|
608
|
-
const cmd = program2.command("chains").description("Manage Admin API chain enums");
|
|
609
|
-
cmd.command("list").description("List available Admin API chain enums").action(async () => {
|
|
610
|
-
try {
|
|
611
|
-
const admin = adminClientFromFlags(program2);
|
|
612
|
-
const chains = await withSpinner(
|
|
613
|
-
"Fetching chains\u2026",
|
|
614
|
-
"Chains fetched",
|
|
615
|
-
() => admin.listChains()
|
|
616
|
-
);
|
|
617
|
-
if (isJSONMode()) {
|
|
618
|
-
printJSON(chains);
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
if (chains.length === 0) {
|
|
622
|
-
emptyState("No chain networks were returned.");
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
const rows = chains.map((c) => [
|
|
626
|
-
c.id,
|
|
627
|
-
c.name,
|
|
628
|
-
c.isTestnet ? dim("yes") : "no",
|
|
629
|
-
c.availability === "public" ? green(c.availability) : dim(c.availability),
|
|
630
|
-
c.currency
|
|
631
|
-
]);
|
|
632
|
-
printTable(["ID", "Name", "Testnet", "Availability", "Currency"], rows);
|
|
633
|
-
if (verbose) {
|
|
634
|
-
console.log("");
|
|
635
|
-
printJSON(chains);
|
|
636
|
-
}
|
|
637
|
-
} catch (err) {
|
|
638
|
-
exitWithError(err);
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
|
|
643
810
|
// src/commands/apps.ts
|
|
644
811
|
function maskAppSecrets(app) {
|
|
645
812
|
return {
|
|
@@ -658,19 +825,14 @@ async function promptPaginationAction() {
|
|
|
658
825
|
message: "More apps available",
|
|
659
826
|
options: [
|
|
660
827
|
{ label: "Load next page", value: "next" },
|
|
661
|
-
{ label: "Load
|
|
828
|
+
{ label: "Load next 5 pages", value: "next5" },
|
|
662
829
|
{ label: "Stop here", value: "stop" }
|
|
663
830
|
],
|
|
664
831
|
initialValue: "next",
|
|
665
832
|
cancelMessage: "Stopped pagination."
|
|
666
833
|
});
|
|
667
|
-
if (action === null)
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
if (action === "next" || action === "all" || action === "stop") {
|
|
671
|
-
return action;
|
|
672
|
-
}
|
|
673
|
-
return "stop";
|
|
834
|
+
if (action === null) return "stop";
|
|
835
|
+
return action;
|
|
674
836
|
}
|
|
675
837
|
function matchesSearch(app, query) {
|
|
676
838
|
const q = query.toLowerCase();
|
|
@@ -763,7 +925,7 @@ function registerApps(program2) {
|
|
|
763
925
|
const interactivePagination = isInteractiveAllowed(program2) && !opts.all;
|
|
764
926
|
if (interactivePagination) {
|
|
765
927
|
let page = result;
|
|
766
|
-
let
|
|
928
|
+
let batchRemaining = 0;
|
|
767
929
|
let pagesFetched = 0;
|
|
768
930
|
let appsFetched = 0;
|
|
769
931
|
while (true) {
|
|
@@ -780,7 +942,7 @@ function registerApps(program2) {
|
|
|
780
942
|
printFetchSummary(appsFetched, pagesFetched);
|
|
781
943
|
return;
|
|
782
944
|
}
|
|
783
|
-
if (
|
|
945
|
+
if (batchRemaining <= 0) {
|
|
784
946
|
printFetchSummary(appsFetched, pagesFetched, { suffix: "so far" });
|
|
785
947
|
const action = await promptPaginationAction();
|
|
786
948
|
if (action === "stop") {
|
|
@@ -789,9 +951,11 @@ function registerApps(program2) {
|
|
|
789
951
|
printFetchSummary(appsFetched, pagesFetched);
|
|
790
952
|
return;
|
|
791
953
|
}
|
|
792
|
-
if (action === "
|
|
793
|
-
|
|
954
|
+
if (action === "next5") {
|
|
955
|
+
batchRemaining = 4;
|
|
794
956
|
}
|
|
957
|
+
} else {
|
|
958
|
+
batchRemaining -= 1;
|
|
795
959
|
}
|
|
796
960
|
page = await withSpinner(
|
|
797
961
|
"Fetching next page\u2026",
|
|
@@ -875,9 +1039,21 @@ function registerApps(program2) {
|
|
|
875
1039
|
exitWithError(err);
|
|
876
1040
|
}
|
|
877
1041
|
});
|
|
878
|
-
cmd.command("delete <id>").description("Delete an app").option("--dry-run", "Preview without executing").action(async (id, opts) => {
|
|
1042
|
+
cmd.command("delete <id>").description("Delete an app").option("--dry-run", "Preview without executing").option("-y, --yes", "Skip confirmation prompt").action(async (id, opts) => {
|
|
879
1043
|
try {
|
|
880
1044
|
if (handleDryRun(opts, "delete", { id }, `Would delete app ${id}`)) return;
|
|
1045
|
+
if (!opts.yes && isInteractiveAllowed(program2)) {
|
|
1046
|
+
const proceed = await promptConfirm({
|
|
1047
|
+
message: `Delete app ${id}?`,
|
|
1048
|
+
initialValue: false,
|
|
1049
|
+
cancelMessage: "Cancelled app deletion."
|
|
1050
|
+
});
|
|
1051
|
+
if (proceed === null) return;
|
|
1052
|
+
if (!proceed) {
|
|
1053
|
+
console.log(` ${dim("Skipped app deletion.")}`);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
881
1057
|
const admin = adminClientFromFlags(program2);
|
|
882
1058
|
await withSpinner(
|
|
883
1059
|
"Deleting app\u2026",
|
|
@@ -1006,6 +1182,45 @@ function registerApps(program2) {
|
|
|
1006
1182
|
exitWithError(err);
|
|
1007
1183
|
}
|
|
1008
1184
|
});
|
|
1185
|
+
cmd.command("chains").description("List Admin API chain identifiers for app configuration (e.g. ETH_MAINNET)").action(async () => {
|
|
1186
|
+
try {
|
|
1187
|
+
const admin = adminClientFromFlags(program2);
|
|
1188
|
+
const chains = await withSpinner(
|
|
1189
|
+
"Fetching chains\u2026",
|
|
1190
|
+
"Chains fetched",
|
|
1191
|
+
() => admin.listChains()
|
|
1192
|
+
);
|
|
1193
|
+
if (isJSONMode()) {
|
|
1194
|
+
printJSON(chains);
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
if (chains.length === 0) {
|
|
1198
|
+
emptyState("No chain networks were returned.");
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
const formatChainId = (value) => {
|
|
1202
|
+
if (!value) return dim("\u2014");
|
|
1203
|
+
const num = parseInt(value, 10);
|
|
1204
|
+
if (isNaN(num)) return value;
|
|
1205
|
+
return `${num} (0x${num.toString(16)})`;
|
|
1206
|
+
};
|
|
1207
|
+
const rows = chains.map((c) => [
|
|
1208
|
+
c.id,
|
|
1209
|
+
c.name,
|
|
1210
|
+
formatChainId(c.networkChainId),
|
|
1211
|
+
c.isTestnet ? dim("yes") : "no",
|
|
1212
|
+
c.availability === "public" ? green(c.availability) : dim(c.availability),
|
|
1213
|
+
c.currency
|
|
1214
|
+
]);
|
|
1215
|
+
printTable(["ID", "Name", "Chain ID", "Testnet", "Availability", "Currency"], rows);
|
|
1216
|
+
if (verbose) {
|
|
1217
|
+
console.log("");
|
|
1218
|
+
printJSON(chains);
|
|
1219
|
+
}
|
|
1220
|
+
} catch (err) {
|
|
1221
|
+
exitWithError(err);
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1009
1224
|
}
|
|
1010
1225
|
|
|
1011
1226
|
// src/commands/setup.ts
|
|
@@ -1066,7 +1281,7 @@ function normalizeTraceMethod(method) {
|
|
|
1066
1281
|
return `trace_${normalized}`;
|
|
1067
1282
|
}
|
|
1068
1283
|
function registerTrace(program2) {
|
|
1069
|
-
program2.command("trace
|
|
1284
|
+
program2.command("trace").argument("<method>", "Trace method name (e.g. trace_transaction)").argument("[params...]", "Method parameters as JSON values").description("Call a trace_* method").action(async (method, params) => {
|
|
1070
1285
|
try {
|
|
1071
1286
|
const client = clientFromFlags(program2);
|
|
1072
1287
|
const rpcMethod = normalizeTraceMethod(method);
|
|
@@ -1088,7 +1303,7 @@ function normalizeDebugMethod(method) {
|
|
|
1088
1303
|
return `debug_${method.replace(/-/g, "_")}`;
|
|
1089
1304
|
}
|
|
1090
1305
|
function registerDebug(program2) {
|
|
1091
|
-
program2.command("debug
|
|
1306
|
+
program2.command("debug").argument("<method>", "Debug method name (e.g. debug_traceTransaction)").argument("[params...]", "Method parameters as JSON values").description("Call a debug_* method").action(async (method, params) => {
|
|
1092
1307
|
try {
|
|
1093
1308
|
const client = clientFromFlags(program2);
|
|
1094
1309
|
const rpcMethod = normalizeDebugMethod(method);
|
|
@@ -1105,29 +1320,72 @@ function registerDebug(program2) {
|
|
|
1105
1320
|
}
|
|
1106
1321
|
|
|
1107
1322
|
// src/commands/transfers.ts
|
|
1323
|
+
async function promptTransfersPagination(shown) {
|
|
1324
|
+
const action = await promptSelect({
|
|
1325
|
+
message: `${shown} transfers loaded \u2014 more available`,
|
|
1326
|
+
options: [
|
|
1327
|
+
{ label: "Load next page", value: "next" },
|
|
1328
|
+
{ label: "Stop here", value: "stop" }
|
|
1329
|
+
],
|
|
1330
|
+
initialValue: "next",
|
|
1331
|
+
cancelMessage: "Stopped pagination."
|
|
1332
|
+
});
|
|
1333
|
+
if (action === null) return "stop";
|
|
1334
|
+
return action;
|
|
1335
|
+
}
|
|
1336
|
+
var TABLE_HEADERS = ["Block", "From", "To", "Value", "Asset", "Category"];
|
|
1337
|
+
function formatTransferRows(transfers) {
|
|
1338
|
+
return transfers.map((t) => {
|
|
1339
|
+
const block = t.blockNum ? String(parseInt(t.blockNum, 16)) : dim("\u2014");
|
|
1340
|
+
const from = t.from ? `${t.from.slice(0, 8)}\u2026${t.from.slice(-4)}` : dim("\u2014");
|
|
1341
|
+
const to = t.to ? `${t.to.slice(0, 8)}\u2026${t.to.slice(-4)}` : dim("contract creation");
|
|
1342
|
+
const value = t.value !== null && t.value !== void 0 ? String(t.value) : dim("\u2014");
|
|
1343
|
+
const asset = t.asset ?? dim("\u2014");
|
|
1344
|
+
const category = t.category;
|
|
1345
|
+
return [block, from, to, value, asset, category];
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1108
1348
|
function registerTransfers(program2) {
|
|
1109
|
-
program2.command("transfers
|
|
1349
|
+
program2.command("transfers").argument("[address]", "Wallet address or ENS name \u2014 queries outgoing transfers (use --to-address for incoming)").description("Get transfer history (alchemy_getAssetTransfers)").option("--from-address <address>", "Filter sender address").option("--to-address <address>", "Filter recipient address").option("--from-block <block>", "Start block (default: 0x0)").option("--to-block <block>", "End block (default: latest)").option("--category <list>", "Comma-separated categories (erc20,erc721,erc1155,external,internal,specialnft)").option("--max-count <hexOrDecimal>", "Max records to return per page").option("--page-key <key>", "Pagination key").addHelpText(
|
|
1350
|
+
"after",
|
|
1351
|
+
`
|
|
1352
|
+
Examples:
|
|
1353
|
+
# Outgoing transfers from an address
|
|
1354
|
+
alchemy transfers 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
1355
|
+
|
|
1356
|
+
# Incoming transfers to an address
|
|
1357
|
+
alchemy transfers --to-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
1358
|
+
|
|
1359
|
+
# Outgoing ERC-20 transfers only
|
|
1360
|
+
alchemy transfers --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --category erc20
|
|
1361
|
+
|
|
1362
|
+
# Transfers within a block range
|
|
1363
|
+
alchemy transfers 0xd8dA... --from-block 0x100000 --to-block latest`
|
|
1364
|
+
).action(async (addressArg, opts) => {
|
|
1110
1365
|
try {
|
|
1111
1366
|
const client = clientFromFlags(program2);
|
|
1112
|
-
const address = addressArg
|
|
1113
|
-
if (
|
|
1114
|
-
if (opts.
|
|
1115
|
-
|
|
1116
|
-
const filter = {
|
|
1367
|
+
const address = addressArg ? await resolveAddress(addressArg, client) : void 0;
|
|
1368
|
+
if (opts.fromAddress) opts.fromAddress = await resolveAddress(opts.fromAddress, client);
|
|
1369
|
+
if (opts.toAddress) opts.toAddress = await resolveAddress(opts.toAddress, client);
|
|
1370
|
+
const baseFilter = {
|
|
1117
1371
|
fromBlock: opts.fromBlock ?? "0x0",
|
|
1118
1372
|
toBlock: opts.toBlock ?? "latest",
|
|
1119
|
-
withMetadata: true
|
|
1373
|
+
withMetadata: true,
|
|
1374
|
+
category: opts.category ? splitCommaList(opts.category) : ["external", "internal", "erc20", "erc721", "erc1155", "specialnft"]
|
|
1120
1375
|
};
|
|
1376
|
+
if (opts.maxCount) {
|
|
1377
|
+
baseFilter.maxCount = opts.maxCount.startsWith("0x") ? opts.maxCount : `0x${Number.parseInt(opts.maxCount, 10).toString(16)}`;
|
|
1378
|
+
} else if (!isJSONMode()) {
|
|
1379
|
+
baseFilter.maxCount = "0x19";
|
|
1380
|
+
}
|
|
1381
|
+
if (opts.pageKey) baseFilter.pageKey = opts.pageKey;
|
|
1382
|
+
const filter = { ...baseFilter };
|
|
1121
1383
|
if (address && !opts.fromAddress && !opts.toAddress) {
|
|
1122
1384
|
filter.fromAddress = address;
|
|
1123
|
-
filter.toAddress = address;
|
|
1124
1385
|
} else {
|
|
1125
1386
|
if (opts.fromAddress) filter.fromAddress = opts.fromAddress;
|
|
1126
1387
|
if (opts.toAddress) filter.toAddress = opts.toAddress;
|
|
1127
1388
|
}
|
|
1128
|
-
if (opts.category) filter.category = splitCommaList(opts.category);
|
|
1129
|
-
if (opts.maxCount) filter.maxCount = opts.maxCount.startsWith("0x") ? opts.maxCount : `0x${Number.parseInt(opts.maxCount, 10).toString(16)}`;
|
|
1130
|
-
if (opts.pageKey) filter.pageKey = opts.pageKey;
|
|
1131
1389
|
const result = await withSpinner(
|
|
1132
1390
|
"Fetching transfers\u2026",
|
|
1133
1391
|
"Transfers fetched",
|
|
@@ -1135,8 +1393,43 @@ function registerTransfers(program2) {
|
|
|
1135
1393
|
);
|
|
1136
1394
|
if (isJSONMode()) {
|
|
1137
1395
|
printJSON(result);
|
|
1138
|
-
|
|
1139
|
-
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
let totalShown = result.transfers.length;
|
|
1399
|
+
if (totalShown === 0) {
|
|
1400
|
+
console.log(dim("No transfers found."));
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
console.log(`${totalShown} transfer${totalShown === 1 ? "" : "s"}
|
|
1404
|
+
`);
|
|
1405
|
+
printTable(TABLE_HEADERS, formatTransferRows(result.transfers));
|
|
1406
|
+
const interactive = isInteractiveAllowed(program2);
|
|
1407
|
+
let pageKey = result.pageKey;
|
|
1408
|
+
while (pageKey && interactive) {
|
|
1409
|
+
const action = await promptTransfersPagination(totalShown);
|
|
1410
|
+
if (action === "stop") {
|
|
1411
|
+
console.log(`
|
|
1412
|
+
${dim(`Next page key: ${pageKey}`)}`);
|
|
1413
|
+
break;
|
|
1414
|
+
}
|
|
1415
|
+
filter.pageKey = pageKey;
|
|
1416
|
+
const nextResult = await withSpinner(
|
|
1417
|
+
"Fetching next page\u2026",
|
|
1418
|
+
"Page fetched",
|
|
1419
|
+
() => client.call("alchemy_getAssetTransfers", [filter])
|
|
1420
|
+
);
|
|
1421
|
+
if (nextResult.transfers.length > 0) {
|
|
1422
|
+
totalShown += nextResult.transfers.length;
|
|
1423
|
+
console.log(`
|
|
1424
|
+
${totalShown} transfers total
|
|
1425
|
+
`);
|
|
1426
|
+
printTable(TABLE_HEADERS, formatTransferRows(nextResult.transfers));
|
|
1427
|
+
}
|
|
1428
|
+
pageKey = nextResult.pageKey;
|
|
1429
|
+
}
|
|
1430
|
+
if (pageKey && !interactive) {
|
|
1431
|
+
console.log(`
|
|
1432
|
+
${dim(`More results available. Use --page-key ${pageKey} to see the next page.`)}`);
|
|
1140
1433
|
}
|
|
1141
1434
|
} catch (err) {
|
|
1142
1435
|
exitWithError(err);
|
|
@@ -1455,8 +1748,20 @@ function registerWebhooks(program2) {
|
|
|
1455
1748
|
exitWithError(err);
|
|
1456
1749
|
}
|
|
1457
1750
|
});
|
|
1458
|
-
cmd.command("delete <webhookId>").description("Delete webhook").action(async (webhookId) => {
|
|
1751
|
+
cmd.command("delete <webhookId>").description("Delete webhook").option("-y, --yes", "Skip confirmation prompt").action(async (webhookId, opts) => {
|
|
1459
1752
|
try {
|
|
1753
|
+
if (!opts.yes && isInteractiveAllowed(program2)) {
|
|
1754
|
+
const proceed = await promptConfirm({
|
|
1755
|
+
message: `Delete webhook ${webhookId}?`,
|
|
1756
|
+
initialValue: false,
|
|
1757
|
+
cancelMessage: "Cancelled webhook deletion."
|
|
1758
|
+
});
|
|
1759
|
+
if (proceed === null) return;
|
|
1760
|
+
if (!proceed) {
|
|
1761
|
+
console.log(` ${dim("Skipped webhook deletion.")}`);
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1460
1765
|
const token = resolveWebhookApiKey(cmd.opts());
|
|
1461
1766
|
const result = await withSpinner(
|
|
1462
1767
|
"Deleting webhook\u2026",
|
|
@@ -1590,7 +1895,7 @@ function registerSolana(program2) {
|
|
|
1590
1895
|
const cmd = program2.command("solana").description("Solana RPC and DAS wrappers");
|
|
1591
1896
|
cmd.command("rpc <method> [params...]").description("Call a Solana JSON-RPC method").action(async (method, params) => {
|
|
1592
1897
|
try {
|
|
1593
|
-
const client = clientFromFlags(program2);
|
|
1898
|
+
const client = clientFromFlags(program2, { defaultNetwork: "solana-mainnet" });
|
|
1594
1899
|
const result = await withSpinner(
|
|
1595
1900
|
`Calling ${method}\u2026`,
|
|
1596
1901
|
`Called ${method}`,
|
|
@@ -1601,14 +1906,15 @@ function registerSolana(program2) {
|
|
|
1601
1906
|
exitWithError(err);
|
|
1602
1907
|
}
|
|
1603
1908
|
});
|
|
1604
|
-
|
|
1605
|
-
das.command("<method> [params...]").description("Call DAS method (e.g. getAssetsByOwner)").action(async (method, params) => {
|
|
1909
|
+
cmd.command("das <method> [params...]").description("Call a Solana DAS method (e.g. getAssetsByOwner)").action(async (method, params) => {
|
|
1606
1910
|
try {
|
|
1607
|
-
const client = clientFromFlags(program2);
|
|
1911
|
+
const client = clientFromFlags(program2, { defaultNetwork: "solana-mainnet" });
|
|
1912
|
+
const parsed = parseCLIParams(params);
|
|
1913
|
+
const rpcParams = parsed.length === 1 && typeof parsed[0] === "object" && parsed[0] !== null && !Array.isArray(parsed[0]) ? parsed[0] : parsed;
|
|
1608
1914
|
const result = await withSpinner(
|
|
1609
1915
|
`Calling ${method}\u2026`,
|
|
1610
1916
|
`Called ${method}`,
|
|
1611
|
-
() => client.call(method,
|
|
1917
|
+
() => client.call(method, rpcParams)
|
|
1612
1918
|
);
|
|
1613
1919
|
printSyntaxJSON(result);
|
|
1614
1920
|
} catch (err) {
|
|
@@ -1617,6 +1923,311 @@ function registerSolana(program2) {
|
|
|
1617
1923
|
});
|
|
1618
1924
|
}
|
|
1619
1925
|
|
|
1926
|
+
// src/commands/gas.ts
|
|
1927
|
+
function registerGas(program2) {
|
|
1928
|
+
program2.command("gas").description("Get current gas prices (base fee + priority fee)").addHelpText(
|
|
1929
|
+
"after",
|
|
1930
|
+
`
|
|
1931
|
+
Examples:
|
|
1932
|
+
alchemy gas
|
|
1933
|
+
alchemy gas -n polygon-mainnet
|
|
1934
|
+
alchemy gas --json`
|
|
1935
|
+
).action(async () => {
|
|
1936
|
+
try {
|
|
1937
|
+
const client = clientFromFlags(program2);
|
|
1938
|
+
const [gasPrice, maxPriorityFee] = await withSpinner(
|
|
1939
|
+
"Fetching gas prices\u2026",
|
|
1940
|
+
"Gas prices fetched",
|
|
1941
|
+
async () => {
|
|
1942
|
+
const [gp, mpf] = await Promise.all([
|
|
1943
|
+
client.call("eth_gasPrice", []),
|
|
1944
|
+
client.call("eth_maxPriorityFeePerGas", []).catch(() => null)
|
|
1945
|
+
]);
|
|
1946
|
+
return [gp, mpf];
|
|
1947
|
+
}
|
|
1948
|
+
);
|
|
1949
|
+
const network = resolveNetwork(program2);
|
|
1950
|
+
const gasPriceWei = BigInt(gasPrice);
|
|
1951
|
+
const gasPriceGwei = Number(gasPriceWei) / 1e9;
|
|
1952
|
+
let priorityFeeGwei = null;
|
|
1953
|
+
let priorityFeeWei = null;
|
|
1954
|
+
if (maxPriorityFee) {
|
|
1955
|
+
priorityFeeWei = BigInt(maxPriorityFee);
|
|
1956
|
+
priorityFeeGwei = Number(priorityFeeWei) / 1e9;
|
|
1957
|
+
}
|
|
1958
|
+
if (isJSONMode()) {
|
|
1959
|
+
const json = {
|
|
1960
|
+
gasPrice,
|
|
1961
|
+
gasPriceGwei: formatGwei(gasPriceGwei),
|
|
1962
|
+
network
|
|
1963
|
+
};
|
|
1964
|
+
if (maxPriorityFee) {
|
|
1965
|
+
json.maxPriorityFeePerGas = maxPriorityFee;
|
|
1966
|
+
json.maxPriorityFeePerGasGwei = formatGwei(priorityFeeGwei);
|
|
1967
|
+
}
|
|
1968
|
+
printJSON(json);
|
|
1969
|
+
} else {
|
|
1970
|
+
const pairs = [];
|
|
1971
|
+
const gasPriceFormatted = formatGweiWithRaw(gasPrice);
|
|
1972
|
+
pairs.push(["Gas Price", gasPriceFormatted ?? `${formatGwei(gasPriceGwei)} gwei`]);
|
|
1973
|
+
if (maxPriorityFee && priorityFeeGwei !== null) {
|
|
1974
|
+
const priorityFormatted = formatGweiWithRaw(maxPriorityFee);
|
|
1975
|
+
pairs.push(["Priority Fee", priorityFormatted ?? `${formatGwei(priorityFeeGwei)} gwei`]);
|
|
1976
|
+
}
|
|
1977
|
+
pairs.push(["Network", network]);
|
|
1978
|
+
printKeyValueBox(pairs);
|
|
1979
|
+
if (verbose) {
|
|
1980
|
+
console.log("");
|
|
1981
|
+
const verboseData = {
|
|
1982
|
+
rpcMethod: "eth_gasPrice",
|
|
1983
|
+
rpcResult: gasPrice
|
|
1984
|
+
};
|
|
1985
|
+
if (maxPriorityFee) {
|
|
1986
|
+
verboseData.rpcMethod2 = "eth_maxPriorityFeePerGas";
|
|
1987
|
+
verboseData.rpcResult2 = maxPriorityFee;
|
|
1988
|
+
}
|
|
1989
|
+
printJSON(verboseData);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
} catch (err) {
|
|
1993
|
+
exitWithError(err);
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// src/commands/logs.ts
|
|
1999
|
+
var PAGE_SIZE = 25;
|
|
2000
|
+
var LARGE_PAGE_SIZE = 100;
|
|
2001
|
+
function normalizeBlockParam(value) {
|
|
2002
|
+
if (value === "latest" || value === "earliest" || value === "pending") {
|
|
2003
|
+
return value;
|
|
2004
|
+
}
|
|
2005
|
+
if (value.startsWith("0x")) return value;
|
|
2006
|
+
const num = parseInt(value, 10);
|
|
2007
|
+
if (isNaN(num) || num < 0) {
|
|
2008
|
+
throw errInvalidArgs("Block must be a number, hex, or tag (latest, earliest, pending).");
|
|
2009
|
+
}
|
|
2010
|
+
return `0x${num.toString(16)}`;
|
|
2011
|
+
}
|
|
2012
|
+
function formatLogRows(logs) {
|
|
2013
|
+
return logs.map((log) => {
|
|
2014
|
+
const block = formatHexQuantity(log.blockNumber) ?? log.blockNumber;
|
|
2015
|
+
const idx = formatHexQuantity(log.logIndex) ?? log.logIndex;
|
|
2016
|
+
const topic0 = log.topics[0] ? `${log.topics[0].slice(0, 10)}\u2026` : dim("none");
|
|
2017
|
+
const addr = log.address;
|
|
2018
|
+
const txHash = `${log.transactionHash.slice(0, 10)}\u2026`;
|
|
2019
|
+
return [block, idx, addr, topic0, txHash];
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
var TABLE_HEADERS2 = ["Block", "Index", "Address", "Topic0", "Tx Hash"];
|
|
2023
|
+
async function promptPaginationAction2(shown, total) {
|
|
2024
|
+
const remaining = total - shown;
|
|
2025
|
+
const options = [
|
|
2026
|
+
{ label: `Show next ${Math.min(PAGE_SIZE, remaining)}`, value: "next" }
|
|
2027
|
+
];
|
|
2028
|
+
if (remaining > PAGE_SIZE) {
|
|
2029
|
+
options.push({ label: `Show next ${Math.min(LARGE_PAGE_SIZE, remaining)}`, value: "next-large" });
|
|
2030
|
+
}
|
|
2031
|
+
options.push({ label: "Stop here", value: "stop" });
|
|
2032
|
+
const action = await promptSelect({
|
|
2033
|
+
message: `Showing ${shown} of ${total} logs (${remaining} remaining)`,
|
|
2034
|
+
options,
|
|
2035
|
+
initialValue: "next",
|
|
2036
|
+
cancelMessage: "Stopped pagination."
|
|
2037
|
+
});
|
|
2038
|
+
if (action === null) return "stop";
|
|
2039
|
+
return action;
|
|
2040
|
+
}
|
|
2041
|
+
function registerLogs(program2) {
|
|
2042
|
+
program2.command("logs").description("Query event logs (eth_getLogs)").addHelpText(
|
|
2043
|
+
"after",
|
|
2044
|
+
`
|
|
2045
|
+
Examples:
|
|
2046
|
+
alchemy logs --from-block 18000000 --to-block 18000010
|
|
2047
|
+
alchemy logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --from-block 18000000 --to-block 18000010
|
|
2048
|
+
alchemy logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --topic 0xddf252ad...
|
|
2049
|
+
alchemy logs --from-block latest --json`
|
|
2050
|
+
).option("--address <address>", "Contract address to filter logs").option("--topic <topic...>", "Event topic(s) to filter (topic0, topic1, ...)").option("--from-block <block>", "Start block (number, hex, or tag)", "latest").option("--to-block <block>", "End block (number, hex, or tag)", "latest").action(async (opts) => {
|
|
2051
|
+
try {
|
|
2052
|
+
const filter = {
|
|
2053
|
+
fromBlock: normalizeBlockParam(opts.fromBlock),
|
|
2054
|
+
toBlock: normalizeBlockParam(opts.toBlock)
|
|
2055
|
+
};
|
|
2056
|
+
if (opts.address) {
|
|
2057
|
+
filter.address = opts.address;
|
|
2058
|
+
}
|
|
2059
|
+
if (opts.topic && opts.topic.length > 0) {
|
|
2060
|
+
filter.topics = opts.topic;
|
|
2061
|
+
}
|
|
2062
|
+
const client = clientFromFlags(program2);
|
|
2063
|
+
const logs = await withSpinner(
|
|
2064
|
+
"Fetching logs\u2026",
|
|
2065
|
+
"Logs fetched",
|
|
2066
|
+
() => client.call("eth_getLogs", [filter])
|
|
2067
|
+
);
|
|
2068
|
+
const network = resolveNetwork(program2);
|
|
2069
|
+
if (isJSONMode()) {
|
|
2070
|
+
printJSON({ logs, count: logs.length, network });
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
if (logs.length === 0) {
|
|
2074
|
+
console.log(dim("No logs found for the given filter."));
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
const total = logs.length;
|
|
2078
|
+
const interactive = isInteractiveAllowed(program2);
|
|
2079
|
+
if (!interactive) {
|
|
2080
|
+
console.log(`Found ${total} log${total === 1 ? "" : "s"} on ${network}
|
|
2081
|
+
`);
|
|
2082
|
+
printTable(TABLE_HEADERS2, formatLogRows(logs));
|
|
2083
|
+
} else {
|
|
2084
|
+
let offset = 0;
|
|
2085
|
+
const firstPage = logs.slice(0, PAGE_SIZE);
|
|
2086
|
+
console.log(`Found ${total} log${total === 1 ? "" : "s"} on ${network}
|
|
2087
|
+
`);
|
|
2088
|
+
printTable(TABLE_HEADERS2, formatLogRows(firstPage));
|
|
2089
|
+
offset = firstPage.length;
|
|
2090
|
+
while (offset < total) {
|
|
2091
|
+
const action = await promptPaginationAction2(offset, total);
|
|
2092
|
+
if (action === "stop") break;
|
|
2093
|
+
const size = action === "next-large" ? LARGE_PAGE_SIZE : PAGE_SIZE;
|
|
2094
|
+
const page = logs.slice(offset, offset + size);
|
|
2095
|
+
console.log("");
|
|
2096
|
+
printTable(TABLE_HEADERS2, formatLogRows(page));
|
|
2097
|
+
offset += page.length;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
if (total >= 1e4) {
|
|
2101
|
+
console.log("");
|
|
2102
|
+
console.log(dim("\u26A0 RPC result limit reached (10,000). Narrow your block range for complete results."));
|
|
2103
|
+
}
|
|
2104
|
+
} catch (err) {
|
|
2105
|
+
exitWithError(err);
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// src/commands/completions.ts
|
|
2111
|
+
function getDescription(cmd) {
|
|
2112
|
+
try {
|
|
2113
|
+
return typeof cmd.description === "function" ? cmd.description() : String(cmd.description || "");
|
|
2114
|
+
} catch {
|
|
2115
|
+
return "";
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
function collectCommands(cmd, prefix = []) {
|
|
2119
|
+
const results = [];
|
|
2120
|
+
for (const sub of cmd.commands) {
|
|
2121
|
+
const path = [...prefix, sub.name()];
|
|
2122
|
+
results.push({ path, description: getDescription(sub) });
|
|
2123
|
+
results.push(...collectCommands(sub, path));
|
|
2124
|
+
}
|
|
2125
|
+
return results;
|
|
2126
|
+
}
|
|
2127
|
+
function generateBash(program2) {
|
|
2128
|
+
const commands = collectCommands(program2);
|
|
2129
|
+
const topLevel = program2.commands.map((c) => c.name()).join(" ");
|
|
2130
|
+
const cases = commands.filter((c) => c.path.length === 1).map((c) => {
|
|
2131
|
+
const subs = commands.filter((s) => s.path.length === 2 && s.path[0] === c.path[0]).map((s) => s.path[1]);
|
|
2132
|
+
if (subs.length === 0) return "";
|
|
2133
|
+
return ` ${c.path[0]}) COMPREPLY=($(compgen -W "${subs.join(" ")}" -- "$cur")) ;;`;
|
|
2134
|
+
}).filter(Boolean).join("\n");
|
|
2135
|
+
return `# bash completion for alchemy CLI
|
|
2136
|
+
# Add to ~/.bashrc: eval "$(alchemy completions bash)"
|
|
2137
|
+
_alchemy_completions() {
|
|
2138
|
+
local cur prev
|
|
2139
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
2140
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
2141
|
+
|
|
2142
|
+
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
2143
|
+
COMPREPLY=($(compgen -W "${topLevel}" -- "$cur"))
|
|
2144
|
+
return
|
|
2145
|
+
fi
|
|
2146
|
+
|
|
2147
|
+
case "$prev" in
|
|
2148
|
+
${cases}
|
|
2149
|
+
-n|--network) COMPREPLY=() ;;
|
|
2150
|
+
*) COMPREPLY=() ;;
|
|
2151
|
+
esac
|
|
2152
|
+
}
|
|
2153
|
+
complete -F _alchemy_completions alchemy
|
|
2154
|
+
`;
|
|
2155
|
+
}
|
|
2156
|
+
function generateZsh(program2) {
|
|
2157
|
+
const commands = collectCommands(program2);
|
|
2158
|
+
const topLevel = program2.commands.map((c) => `'${c.name()}:${getDescription(c).replace(/'/g, "'\\''")}'`).join("\n ");
|
|
2159
|
+
const subcommandCases = commands.filter((c) => c.path.length === 1).map((c) => {
|
|
2160
|
+
const subs = commands.filter((s) => s.path.length === 2 && s.path[0] === c.path[0]).map((s) => `'${s.path[1]}:${s.description.replace(/'/g, "'\\''")}'`);
|
|
2161
|
+
if (subs.length === 0) return "";
|
|
2162
|
+
return ` ${c.path[0]})
|
|
2163
|
+
_values 'subcommand' \\
|
|
2164
|
+
${subs.join(" \\\n ")}
|
|
2165
|
+
;;`;
|
|
2166
|
+
}).filter(Boolean).join("\n");
|
|
2167
|
+
return `#compdef alchemy
|
|
2168
|
+
# zsh completion for alchemy CLI
|
|
2169
|
+
# Add to ~/.zshrc: eval "$(alchemy completions zsh)"
|
|
2170
|
+
_alchemy() {
|
|
2171
|
+
local -a commands
|
|
2172
|
+
if (( CURRENT == 2 )); then
|
|
2173
|
+
commands=(
|
|
2174
|
+
${topLevel}
|
|
2175
|
+
)
|
|
2176
|
+
_describe 'command' commands
|
|
2177
|
+
return
|
|
2178
|
+
fi
|
|
2179
|
+
|
|
2180
|
+
case "$words[2]" in
|
|
2181
|
+
${subcommandCases}
|
|
2182
|
+
esac
|
|
2183
|
+
}
|
|
2184
|
+
_alchemy "$@"
|
|
2185
|
+
compdef _alchemy alchemy
|
|
2186
|
+
`;
|
|
2187
|
+
}
|
|
2188
|
+
function generateFish(program2) {
|
|
2189
|
+
const commands = collectCommands(program2);
|
|
2190
|
+
const lines = commands.map((c) => {
|
|
2191
|
+
const desc = c.description.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
2192
|
+
if (c.path.length === 1) {
|
|
2193
|
+
return `complete -c alchemy -n '__fish_use_subcommand' -a '${c.path[0]}' -d '${desc}'`;
|
|
2194
|
+
}
|
|
2195
|
+
if (c.path.length === 2) {
|
|
2196
|
+
return `complete -c alchemy -n '__fish_seen_subcommand_from ${c.path[0]}' -a '${c.path[1]}' -d '${desc}'`;
|
|
2197
|
+
}
|
|
2198
|
+
return "";
|
|
2199
|
+
}).filter(Boolean);
|
|
2200
|
+
return `# fish completion for alchemy CLI
|
|
2201
|
+
# Add to ~/.config/fish/completions/alchemy.fish
|
|
2202
|
+
${lines.join("\n")}
|
|
2203
|
+
`;
|
|
2204
|
+
}
|
|
2205
|
+
function registerCompletions(program2) {
|
|
2206
|
+
program2.command("completions").argument("<shell>", "Shell type: bash, zsh, or fish").description("Generate shell completion scripts").addHelpText(
|
|
2207
|
+
"after",
|
|
2208
|
+
`
|
|
2209
|
+
Examples:
|
|
2210
|
+
alchemy completions bash >> ~/.bashrc
|
|
2211
|
+
eval "$(alchemy completions zsh)"
|
|
2212
|
+
alchemy completions fish > ~/.config/fish/completions/alchemy.fish`
|
|
2213
|
+
).action((shell) => {
|
|
2214
|
+
switch (shell.toLowerCase()) {
|
|
2215
|
+
case "bash":
|
|
2216
|
+
process.stdout.write(generateBash(program2));
|
|
2217
|
+
break;
|
|
2218
|
+
case "zsh":
|
|
2219
|
+
process.stdout.write(generateZsh(program2));
|
|
2220
|
+
break;
|
|
2221
|
+
case "fish":
|
|
2222
|
+
process.stdout.write(generateFish(program2));
|
|
2223
|
+
break;
|
|
2224
|
+
default:
|
|
2225
|
+
console.error(`Unknown shell: ${shell}. Supported: bash, zsh, fish`);
|
|
2226
|
+
process.exit(2);
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
}
|
|
2230
|
+
|
|
1620
2231
|
// src/commands/agent-prompt.ts
|
|
1621
2232
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
1622
2233
|
ErrorCode.RATE_LIMITED,
|
|
@@ -1718,7 +2329,7 @@ function buildAgentPrompt(program2) {
|
|
|
1718
2329
|
envVar: "ALCHEMY_ACCESS_KEY",
|
|
1719
2330
|
flag: "--access-key <key>",
|
|
1720
2331
|
configKey: "access-key",
|
|
1721
|
-
commandFamilies: ["apps", "
|
|
2332
|
+
commandFamilies: ["apps", "network list --configured"]
|
|
1722
2333
|
},
|
|
1723
2334
|
{
|
|
1724
2335
|
method: "Webhook API key",
|
|
@@ -1850,7 +2461,7 @@ var ROOT_OPTION_GROUPS = [
|
|
|
1850
2461
|
var ROOT_COMMAND_PILLARS = [
|
|
1851
2462
|
{
|
|
1852
2463
|
label: "Node",
|
|
1853
|
-
commands: ["balance", "tx", "block", "rpc", "trace", "debug"]
|
|
2464
|
+
commands: ["balance", "tx", "block", "rpc", "trace", "debug", "gas", "logs"]
|
|
1854
2465
|
},
|
|
1855
2466
|
{
|
|
1856
2467
|
label: "Data",
|
|
@@ -1862,11 +2473,11 @@ var ROOT_COMMAND_PILLARS = [
|
|
|
1862
2473
|
},
|
|
1863
2474
|
{
|
|
1864
2475
|
label: "Chains",
|
|
1865
|
-
commands: ["network", "
|
|
2476
|
+
commands: ["network", "solana"]
|
|
1866
2477
|
},
|
|
1867
2478
|
{
|
|
1868
2479
|
label: "Admin",
|
|
1869
|
-
commands: ["apps", "config", "setup", "agent-prompt", "update-check", "version", "help"]
|
|
2480
|
+
commands: ["apps", "config", "setup", "completions", "agent-prompt", "update-check", "version", "help"]
|
|
1870
2481
|
}
|
|
1871
2482
|
];
|
|
1872
2483
|
function formatCommandSignature(sub) {
|
|
@@ -1915,10 +2526,10 @@ function resetUpdateNoticeState() {
|
|
|
1915
2526
|
}
|
|
1916
2527
|
program.name("alchemy").description(
|
|
1917
2528
|
"The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
|
|
1918
|
-
).version("0.
|
|
2529
|
+
).version("0.3.1", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option("--access-key <key>", "Alchemy access key (env: ALCHEMY_ACCESS_KEY)").option(
|
|
1919
2530
|
"-n, --network <network>",
|
|
1920
2531
|
"Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
|
|
1921
|
-
).option("--x402", "Use x402 wallet-based gateway auth").option("--wallet-key-file <path>", "Path to wallet private key file for x402").option("--json", "Force JSON output").option("-q, --quiet", "Suppress non-essential output").option("--verbose", "Enable verbose output").option("--no-color", "Disable color output").option("--reveal", "Show secrets in plain text (TTY only)").option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--debug", "Enable debug diagnostics").option("--no-interactive", "Disable REPL and prompt-driven interactions").addHelpCommand(false).allowExcessArguments(true).exitOverride((err) => {
|
|
2532
|
+
).option("--x402", "Use x402 wallet-based gateway auth").option("--wallet-key-file <path>", "Path to wallet private key file for x402").option("--json", "Force JSON output (auto-enabled when piped)").option("-q, --quiet", "Suppress non-essential output").option("--verbose", "Enable verbose output").option("--no-color", "Disable color output").option("--reveal", "Show secrets in plain text (TTY only)").option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--debug", "Enable debug diagnostics").option("--no-interactive", "Disable REPL and prompt-driven interactions").addHelpCommand(false).allowExcessArguments(true).exitOverride((err) => {
|
|
1922
2533
|
if (err.code === "commander.help" || err.code === "commander.helpDisplayed" || err.code === "commander.version") {
|
|
1923
2534
|
process.exit(0);
|
|
1924
2535
|
}
|
|
@@ -2031,6 +2642,14 @@ ${styledLine}`;
|
|
|
2031
2642
|
}).addHelpText("beforeAll", () => isHelpInvocation ? brandedHelp() : "").addHelpText("after", () => {
|
|
2032
2643
|
if (isJSONMode()) return "";
|
|
2033
2644
|
return [
|
|
2645
|
+
"",
|
|
2646
|
+
`${hBrand("\u25C6")} ${hBold("Quick Start")}`,
|
|
2647
|
+
` ${hDim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`,
|
|
2648
|
+
` ${hBrand("alchemy")} ${hDim("Interactive mode with guided setup")}`,
|
|
2649
|
+
` ${hBrand("alchemy balance")} ${hDim("<address>")} ${hDim("Get native token balance")}`,
|
|
2650
|
+
` ${hBrand("alchemy block latest")} ${hDim("Latest block summary")}`,
|
|
2651
|
+
` ${hBrand("alchemy rpc eth_chainId")} ${hDim("Raw JSON-RPC call")}`,
|
|
2652
|
+
` ${hBrand("alchemy config list")} ${hDim("View current configuration")}`,
|
|
2034
2653
|
"",
|
|
2035
2654
|
`${hBrand("\u25C6")} ${hBold("Exit Codes")}`,
|
|
2036
2655
|
` ${hDim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`,
|
|
@@ -2052,6 +2671,7 @@ ${styledLine}`;
|
|
|
2052
2671
|
].join("\n");
|
|
2053
2672
|
}).hook("preAction", () => {
|
|
2054
2673
|
const opts = program.opts();
|
|
2674
|
+
if (opts.color === false) setNoColor(true);
|
|
2055
2675
|
const cfg = load();
|
|
2056
2676
|
setFlags({
|
|
2057
2677
|
json: opts.json,
|
|
@@ -2086,7 +2706,7 @@ ${styledLine}`;
|
|
|
2086
2706
|
if (isInteractiveAllowed(program)) {
|
|
2087
2707
|
let latestForInteractiveStartup = null;
|
|
2088
2708
|
if (shouldRunOnboarding(program, cfg)) {
|
|
2089
|
-
const { runOnboarding } = await import("./onboarding-
|
|
2709
|
+
const { runOnboarding } = await import("./onboarding-XNAWN5BR.js");
|
|
2090
2710
|
const latest = getAvailableUpdateOnce();
|
|
2091
2711
|
const completed = await runOnboarding(program, latest);
|
|
2092
2712
|
updateShownDuringInteractiveStartup = Boolean(latest);
|
|
@@ -2098,7 +2718,7 @@ ${styledLine}`;
|
|
|
2098
2718
|
latestForInteractiveStartup = getAvailableUpdateOnce();
|
|
2099
2719
|
updateShownDuringInteractiveStartup = Boolean(latestForInteractiveStartup);
|
|
2100
2720
|
}
|
|
2101
|
-
const { startREPL } = await import("./interactive-
|
|
2721
|
+
const { startREPL } = await import("./interactive-CLPT5QDZ.js");
|
|
2102
2722
|
program.exitOverride();
|
|
2103
2723
|
program.configureOutput({
|
|
2104
2724
|
writeErr: () => {
|
|
@@ -2112,9 +2732,12 @@ ${styledLine}`;
|
|
|
2112
2732
|
registerRPC(program);
|
|
2113
2733
|
registerBalance(program);
|
|
2114
2734
|
registerTx(program);
|
|
2735
|
+
registerReceipt(program);
|
|
2115
2736
|
registerBlock(program);
|
|
2116
2737
|
registerTrace(program);
|
|
2117
2738
|
registerDebug(program);
|
|
2739
|
+
registerGas(program);
|
|
2740
|
+
registerLogs(program);
|
|
2118
2741
|
registerTokens(program);
|
|
2119
2742
|
registerNFTs(program);
|
|
2120
2743
|
registerTransfers(program);
|
|
@@ -2126,12 +2749,12 @@ registerBundler(program);
|
|
|
2126
2749
|
registerGasManager(program);
|
|
2127
2750
|
registerWebhooks(program);
|
|
2128
2751
|
registerNetwork(program);
|
|
2129
|
-
registerChains(program);
|
|
2130
2752
|
registerApps(program);
|
|
2131
2753
|
registerSetup(program);
|
|
2132
2754
|
registerConfig(program);
|
|
2133
2755
|
registerSolana(program);
|
|
2134
2756
|
registerAgentPrompt(program);
|
|
2757
|
+
registerCompletions(program);
|
|
2135
2758
|
registerUpdateCheck(program);
|
|
2136
2759
|
registerVersion(program);
|
|
2137
2760
|
program.command("help [command...]").description("display help for command").action((commandPath) => {
|