@spectratools/etherscan-cli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +38 -77
  2. package/dist/cli.js +725 -246
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
+ import { realpathSync } from "fs";
4
5
  import { fileURLToPath } from "url";
5
6
  import { Cli as Cli7 } from "incur";
6
7
 
7
8
  // src/commands/account.ts
8
9
  import {
9
- apiKeyAuth,
10
10
  checksumAddress,
11
11
  createRateLimiter as createRateLimiter2,
12
12
  formatTimestamp,
13
13
  weiToEth,
14
14
  withRateLimit as withRateLimit2
15
15
  } from "@spectratools/cli-shared";
16
- import { Cli, z } from "incur";
16
+ import { Cli, z as z3 } from "incur";
17
17
 
18
18
  // src/api.ts
19
19
  import {
@@ -22,14 +22,49 @@ import {
22
22
  withRateLimit,
23
23
  withRetry
24
24
  } from "@spectratools/cli-shared";
25
+ import { z } from "incur";
25
26
  var DEFAULT_BASE_URL = "https://api.etherscan.io/v2/api";
26
27
  var RETRY_OPTIONS = { maxRetries: 3, baseMs: 500, maxMs: 1e4 };
28
+ var etherscanResponseSchema = z.object({
29
+ status: z.string(),
30
+ message: z.string(),
31
+ result: z.unknown()
32
+ });
33
+ var proxyResponseSchema = z.object({
34
+ jsonrpc: z.string(),
35
+ id: z.union([z.number(), z.string()]),
36
+ result: z.unknown()
37
+ });
27
38
  var EtherscanError = class extends Error {
28
- constructor(message) {
39
+ constructor(message, details) {
29
40
  super(message);
41
+ this.details = details;
30
42
  this.name = "EtherscanError";
31
43
  }
32
44
  };
45
+ var EtherscanValidationError = class extends EtherscanError {
46
+ constructor(debug) {
47
+ super("Etherscan response validation failed", {
48
+ code: "INVALID_API_RESPONSE",
49
+ mode: debug.mode,
50
+ params: debug.params,
51
+ issues: debug.issues,
52
+ response: debug.response
53
+ });
54
+ this.debug = debug;
55
+ this.name = "EtherscanValidationError";
56
+ }
57
+ };
58
+ function parseWithSchema(schema, data, mode, params) {
59
+ const parsed = schema.safeParse(data);
60
+ if (parsed.success) return parsed.data;
61
+ throw new EtherscanValidationError({
62
+ mode,
63
+ params,
64
+ issues: parsed.error.issues,
65
+ response: data
66
+ });
67
+ }
33
68
  function createEtherscanClient(apiKey, baseUrl = DEFAULT_BASE_URL) {
34
69
  const http = createHttpClient({ baseUrl });
35
70
  const acquire = createRateLimiter({ requestsPerSecond: 5 });
@@ -44,21 +79,33 @@ function createEtherscanClient(apiKey, baseUrl = DEFAULT_BASE_URL) {
44
79
  RETRY_OPTIONS
45
80
  );
46
81
  }
47
- async function call(params) {
48
- const response = await request(params);
82
+ async function call(params, resultSchema) {
83
+ const rawResponse = await request(params);
84
+ const response = parseWithSchema(etherscanResponseSchema, rawResponse, "rest", params);
49
85
  if (response.status === "0") {
50
86
  const msg = typeof response.result === "string" ? response.result : response.message;
51
- throw new EtherscanError(msg);
87
+ throw new EtherscanError(msg, {
88
+ code: "ETHERSCAN_API_ERROR",
89
+ params,
90
+ response
91
+ });
52
92
  }
53
- return response.result;
93
+ return parseWithSchema(resultSchema, response.result, "rest", params);
54
94
  }
55
- async function callProxy(params) {
56
- const response = await request(params);
57
- return response.result;
95
+ async function callProxy(params, resultSchema) {
96
+ const rawResponse = await request(params);
97
+ const response = parseWithSchema(proxyResponseSchema, rawResponse, "proxy", params);
98
+ return parseWithSchema(resultSchema, response.result, "proxy", params);
58
99
  }
59
100
  return { call, callProxy };
60
101
  }
61
102
 
103
+ // src/auth.ts
104
+ import { z as z2 } from "incur";
105
+ var etherscanEnv = z2.object({
106
+ ETHERSCAN_API_KEY: z2.string().describe("Etherscan V2 API key")
107
+ });
108
+
62
109
  // src/chains.ts
63
110
  var CHAIN_IDS = {
64
111
  abstract: 2741,
@@ -91,7 +138,28 @@ function resolveChainId(chain) {
91
138
 
92
139
  // src/commands/account.ts
93
140
  var rateLimiter = createRateLimiter2({ requestsPerSecond: 5 });
94
- var chainOption = z.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
141
+ var chainOption = z3.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
142
+ var txListItemSchema = z3.object({
143
+ hash: z3.string(),
144
+ from: z3.string(),
145
+ to: z3.string(),
146
+ value: z3.string(),
147
+ timeStamp: z3.string(),
148
+ blockNumber: z3.string(),
149
+ isError: z3.string(),
150
+ gasUsed: z3.string()
151
+ });
152
+ var tokenTxItemSchema = z3.object({
153
+ hash: z3.string(),
154
+ from: z3.string(),
155
+ to: z3.string(),
156
+ value: z3.string(),
157
+ tokenName: z3.string(),
158
+ tokenSymbol: z3.string(),
159
+ tokenDecimal: z3.string(),
160
+ timeStamp: z3.string(),
161
+ contractAddress: z3.string()
162
+ });
95
163
  function normalizeAddress(address) {
96
164
  try {
97
165
  return checksumAddress(address);
@@ -100,29 +168,46 @@ function normalizeAddress(address) {
100
168
  }
101
169
  }
102
170
  var accountCli = Cli.create("account", {
103
- description: "Query account balances, transactions, and token transfers"
171
+ description: "Query account balances, transactions, and token transfers."
104
172
  });
105
173
  accountCli.command("balance", {
106
- description: "Get the ETH balance of an address",
107
- args: z.object({
108
- address: z.string().describe("Ethereum address")
174
+ description: "Get the native-token balance of an address.",
175
+ args: z3.object({
176
+ address: z3.string().describe("Wallet address")
109
177
  }),
110
- options: z.object({
178
+ options: z3.object({
111
179
  chain: chainOption
112
180
  }),
181
+ env: etherscanEnv,
182
+ output: z3.object({
183
+ address: z3.string(),
184
+ wei: z3.string(),
185
+ eth: z3.string(),
186
+ chain: z3.string()
187
+ }),
188
+ examples: [
189
+ {
190
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
191
+ options: { chain: "abstract" },
192
+ description: "Get ETH balance on Abstract"
193
+ }
194
+ ],
113
195
  async run(c) {
114
- const { apiKey } = apiKeyAuth("ETHERSCAN_API_KEY");
196
+ const apiKey = c.env.ETHERSCAN_API_KEY;
115
197
  const chainId = resolveChainId(c.options.chain);
116
198
  const address = normalizeAddress(c.args.address);
117
199
  const client = createEtherscanClient(apiKey);
118
200
  const wei = await withRateLimit2(
119
- () => client.call({
120
- chainid: chainId,
121
- module: "account",
122
- action: "balance",
123
- address,
124
- tag: "latest"
125
- }),
201
+ () => client.call(
202
+ {
203
+ chainid: chainId,
204
+ module: "account",
205
+ action: "balance",
206
+ address,
207
+ tag: "latest"
208
+ },
209
+ z3.string()
210
+ ),
126
211
  rateLimiter
127
212
  );
128
213
  return c.ok(
@@ -143,35 +228,64 @@ accountCli.command("balance", {
143
228
  }
144
229
  });
145
230
  accountCli.command("txlist", {
146
- description: "List normal transactions for an address",
147
- args: z.object({
148
- address: z.string().describe("Ethereum address")
149
- }),
150
- options: z.object({
151
- startblock: z.number().optional().default(0).describe("Start block number"),
152
- endblock: z.string().optional().default("latest").describe("End block number"),
153
- page: z.number().optional().default(1).describe("Page number"),
154
- offset: z.number().optional().default(10).describe("Number of results per page"),
155
- sort: z.string().optional().default("asc").describe("Sort order (asc or desc)"),
231
+ description: "List normal transactions for an address.",
232
+ args: z3.object({
233
+ address: z3.string().describe("Wallet address")
234
+ }),
235
+ options: z3.object({
236
+ startblock: z3.number().optional().default(0).describe("Start block number"),
237
+ endblock: z3.string().optional().default("latest").describe("End block number"),
238
+ page: z3.number().optional().default(1).describe("Page number"),
239
+ offset: z3.number().optional().default(10).describe("Number of results per page"),
240
+ sort: z3.string().optional().default("asc").describe("Sort order (asc or desc)"),
156
241
  chain: chainOption
157
242
  }),
243
+ env: etherscanEnv,
244
+ output: z3.object({
245
+ address: z3.string(),
246
+ chain: z3.string(),
247
+ count: z3.number(),
248
+ transactions: z3.array(
249
+ z3.object({
250
+ hash: z3.string(),
251
+ from: z3.string(),
252
+ to: z3.string(),
253
+ value: z3.string(),
254
+ eth: z3.string(),
255
+ timestamp: z3.string(),
256
+ block: z3.string(),
257
+ status: z3.string(),
258
+ gasUsed: z3.string()
259
+ })
260
+ )
261
+ }),
262
+ examples: [
263
+ {
264
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
265
+ options: { chain: "ethereum", sort: "desc", offset: 5 },
266
+ description: "List most recent transactions for an address"
267
+ }
268
+ ],
158
269
  async run(c) {
159
- const { apiKey } = apiKeyAuth("ETHERSCAN_API_KEY");
270
+ const apiKey = c.env.ETHERSCAN_API_KEY;
160
271
  const chainId = resolveChainId(c.options.chain);
161
272
  const address = normalizeAddress(c.args.address);
162
273
  const client = createEtherscanClient(apiKey);
163
274
  const txs = await withRateLimit2(
164
- () => client.call({
165
- chainid: chainId,
166
- module: "account",
167
- action: "txlist",
168
- address,
169
- startblock: c.options.startblock,
170
- endblock: c.options.endblock,
171
- page: c.options.page,
172
- offset: c.options.offset,
173
- sort: c.options.sort
174
- }),
275
+ () => client.call(
276
+ {
277
+ chainid: chainId,
278
+ module: "account",
279
+ action: "txlist",
280
+ address,
281
+ startblock: c.options.startblock,
282
+ endblock: c.options.endblock,
283
+ page: c.options.page,
284
+ offset: c.options.offset,
285
+ sort: c.options.sort
286
+ },
287
+ z3.array(txListItemSchema)
288
+ ),
175
289
  rateLimiter
176
290
  );
177
291
  const formatted = txs.map((tx) => ({
@@ -203,31 +317,60 @@ accountCli.command("txlist", {
203
317
  }
204
318
  });
205
319
  accountCli.command("tokentx", {
206
- description: "List ERC-20 token transfers for an address",
207
- args: z.object({
208
- address: z.string().describe("Ethereum address")
209
- }),
210
- options: z.object({
211
- contractaddress: z.string().optional().describe("Filter by token contract address"),
212
- page: z.number().optional().default(1).describe("Page number"),
213
- offset: z.number().optional().default(20).describe("Results per page"),
320
+ description: "List ERC-20 token transfers for an address.",
321
+ args: z3.object({
322
+ address: z3.string().describe("Wallet address")
323
+ }),
324
+ options: z3.object({
325
+ contractaddress: z3.string().optional().describe("Filter by token contract address"),
326
+ page: z3.number().optional().default(1).describe("Page number"),
327
+ offset: z3.number().optional().default(20).describe("Results per page"),
214
328
  chain: chainOption
215
329
  }),
330
+ env: etherscanEnv,
331
+ output: z3.object({
332
+ address: z3.string(),
333
+ chain: z3.string(),
334
+ count: z3.number(),
335
+ transfers: z3.array(
336
+ z3.object({
337
+ hash: z3.string(),
338
+ from: z3.string(),
339
+ to: z3.string(),
340
+ value: z3.string(),
341
+ token: z3.string(),
342
+ tokenName: z3.string(),
343
+ decimals: z3.string(),
344
+ timestamp: z3.string(),
345
+ contract: z3.string()
346
+ })
347
+ )
348
+ }),
349
+ examples: [
350
+ {
351
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
352
+ options: { chain: "base", offset: 10 },
353
+ description: "List recent ERC-20 transfers for an address"
354
+ }
355
+ ],
216
356
  async run(c) {
217
- const { apiKey } = apiKeyAuth("ETHERSCAN_API_KEY");
357
+ const apiKey = c.env.ETHERSCAN_API_KEY;
218
358
  const chainId = resolveChainId(c.options.chain);
219
359
  const address = normalizeAddress(c.args.address);
220
360
  const client = createEtherscanClient(apiKey);
221
361
  const transfers = await withRateLimit2(
222
- () => client.call({
223
- chainid: chainId,
224
- module: "account",
225
- action: "tokentx",
226
- address,
227
- contractaddress: c.options.contractaddress,
228
- page: c.options.page,
229
- offset: c.options.offset
230
- }),
362
+ () => client.call(
363
+ {
364
+ chainid: chainId,
365
+ module: "account",
366
+ action: "tokentx",
367
+ address,
368
+ contractaddress: c.options.contractaddress,
369
+ page: c.options.page,
370
+ offset: c.options.offset
371
+ },
372
+ z3.array(tokenTxItemSchema)
373
+ ),
231
374
  rateLimiter
232
375
  );
233
376
  const formatted = transfers.map((tx) => ({
@@ -258,29 +401,46 @@ accountCli.command("tokentx", {
258
401
  }
259
402
  });
260
403
  accountCli.command("tokenbalance", {
261
- description: "Get ERC-20 token balance for an address",
262
- args: z.object({
263
- address: z.string().describe("Ethereum address")
404
+ description: "Get ERC-20 token balance for an address.",
405
+ args: z3.object({
406
+ address: z3.string().describe("Wallet address")
264
407
  }),
265
- options: z.object({
266
- contractaddress: z.string().describe("Token contract address"),
408
+ options: z3.object({
409
+ contractaddress: z3.string().describe("Token contract address"),
267
410
  chain: chainOption
268
411
  }),
412
+ env: etherscanEnv,
413
+ output: z3.object({
414
+ address: z3.string(),
415
+ contract: z3.string(),
416
+ balance: z3.string(),
417
+ chain: z3.string()
418
+ }),
419
+ examples: [
420
+ {
421
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
422
+ options: { contractaddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", chain: "ethereum" },
423
+ description: "Get token balance for a wallet + token pair"
424
+ }
425
+ ],
269
426
  async run(c) {
270
- const { apiKey } = apiKeyAuth("ETHERSCAN_API_KEY");
427
+ const apiKey = c.env.ETHERSCAN_API_KEY;
271
428
  const chainId = resolveChainId(c.options.chain);
272
429
  const address = normalizeAddress(c.args.address);
273
430
  const contract = normalizeAddress(c.options.contractaddress);
274
431
  const client = createEtherscanClient(apiKey);
275
432
  const balance = await withRateLimit2(
276
- () => client.call({
277
- chainid: chainId,
278
- module: "account",
279
- action: "tokenbalance",
280
- address,
281
- contractaddress: contract,
282
- tag: "latest"
283
- }),
433
+ () => client.call(
434
+ {
435
+ chainid: chainId,
436
+ module: "account",
437
+ action: "tokenbalance",
438
+ address,
439
+ contractaddress: contract,
440
+ tag: "latest"
441
+ },
442
+ z3.string()
443
+ ),
284
444
  rateLimiter
285
445
  );
286
446
  return c.ok(
@@ -301,38 +461,66 @@ accountCli.command("tokenbalance", {
301
461
  });
302
462
 
303
463
  // src/commands/contract.ts
304
- import {
305
- apiKeyAuth as apiKeyAuth2,
306
- checksumAddress as checksumAddress2,
307
- createRateLimiter as createRateLimiter3,
308
- withRateLimit as withRateLimit3
309
- } from "@spectratools/cli-shared";
310
- import { Cli as Cli2, z as z2 } from "incur";
464
+ import { checksumAddress as checksumAddress2, createRateLimiter as createRateLimiter3, withRateLimit as withRateLimit3 } from "@spectratools/cli-shared";
465
+ import { Cli as Cli2, z as z4 } from "incur";
311
466
  var rateLimiter2 = createRateLimiter3({ requestsPerSecond: 5 });
312
- var chainOption2 = z2.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
467
+ var chainOption2 = z4.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
313
468
  var contractCli = Cli2.create("contract", {
314
- description: "Query contract ABI, source code, and deployment info"
469
+ description: "Query contract ABI, source code, and deployment metadata."
470
+ });
471
+ var sourceResultSchema = z4.object({
472
+ SourceCode: z4.string(),
473
+ ABI: z4.string(),
474
+ ContractName: z4.string(),
475
+ CompilerVersion: z4.string(),
476
+ OptimizationUsed: z4.string(),
477
+ Runs: z4.string(),
478
+ ConstructorArguments: z4.string(),
479
+ LicenseType: z4.string(),
480
+ Proxy: z4.string(),
481
+ Implementation: z4.string()
482
+ });
483
+ var creationResultSchema = z4.object({
484
+ contractAddress: z4.string(),
485
+ contractCreator: z4.string(),
486
+ txHash: z4.string()
315
487
  });
316
488
  contractCli.command("abi", {
317
- description: "Get the ABI for a verified contract",
318
- args: z2.object({
319
- address: z2.string().describe("Contract address")
489
+ description: "Get the ABI for a verified contract.",
490
+ args: z4.object({
491
+ address: z4.string().describe("Contract address")
320
492
  }),
321
- options: z2.object({
493
+ options: z4.object({
322
494
  chain: chainOption2
323
495
  }),
496
+ env: etherscanEnv,
497
+ output: z4.object({
498
+ address: z4.string(),
499
+ chain: z4.string(),
500
+ abi: z4.array(z4.unknown())
501
+ }),
502
+ examples: [
503
+ {
504
+ args: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
505
+ options: { chain: "ethereum" },
506
+ description: "Fetch ABI for a verified ERC-20 contract"
507
+ }
508
+ ],
324
509
  async run(c) {
325
- const { apiKey } = apiKeyAuth2("ETHERSCAN_API_KEY");
510
+ const apiKey = c.env.ETHERSCAN_API_KEY;
326
511
  const chainId = resolveChainId(c.options.chain);
327
512
  const address = checksumAddress2(c.args.address);
328
513
  const client = createEtherscanClient(apiKey);
329
514
  const abi = await withRateLimit3(
330
- () => client.call({
331
- chainid: chainId,
332
- module: "contract",
333
- action: "getabi",
334
- address
335
- }),
515
+ () => client.call(
516
+ {
517
+ chainid: chainId,
518
+ module: "contract",
519
+ action: "getabi",
520
+ address
521
+ },
522
+ z4.string()
523
+ ),
336
524
  rateLimiter2
337
525
  );
338
526
  return c.ok(
@@ -352,25 +540,49 @@ contractCli.command("abi", {
352
540
  }
353
541
  });
354
542
  contractCli.command("source", {
355
- description: "Get verified source code for a contract",
356
- args: z2.object({
357
- address: z2.string().describe("Contract address")
543
+ description: "Get verified source code for a contract.",
544
+ args: z4.object({
545
+ address: z4.string().describe("Contract address")
358
546
  }),
359
- options: z2.object({
547
+ options: z4.object({
360
548
  chain: chainOption2
361
549
  }),
550
+ env: etherscanEnv,
551
+ output: z4.object({
552
+ address: z4.string(),
553
+ chain: z4.string(),
554
+ name: z4.string(),
555
+ compiler: z4.string(),
556
+ optimized: z4.boolean(),
557
+ runs: z4.string(),
558
+ license: z4.string(),
559
+ proxy: z4.boolean(),
560
+ implementation: z4.string().optional(),
561
+ sourceCode: z4.string(),
562
+ constructorArguments: z4.string()
563
+ }),
564
+ examples: [
565
+ {
566
+ args: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
567
+ options: { chain: "ethereum" },
568
+ description: "Fetch verified source code metadata"
569
+ }
570
+ ],
362
571
  async run(c) {
363
- const { apiKey } = apiKeyAuth2("ETHERSCAN_API_KEY");
572
+ const apiKey = c.env.ETHERSCAN_API_KEY;
364
573
  const chainId = resolveChainId(c.options.chain);
365
574
  const address = checksumAddress2(c.args.address);
366
575
  const client = createEtherscanClient(apiKey);
367
576
  const results = await withRateLimit3(
368
- () => client.call({
369
- chainid: chainId,
370
- module: "contract",
371
- action: "getsourcecode",
372
- address
373
- }),
577
+ () => client.call(
578
+ {
579
+ chainid: chainId,
580
+ module: "contract",
581
+ action: "getsourcecode",
582
+ address
583
+ },
584
+ z4.array(sourceResultSchema)
585
+ ),
374
586
  rateLimiter2
375
587
  );
376
588
  const result = results[0];
@@ -406,25 +618,42 @@ contractCli.command("source", {
406
618
  }
407
619
  });
408
620
  contractCli.command("creation", {
409
- description: "Get the creation transaction for a contract",
410
- args: z2.object({
411
- address: z2.string().describe("Contract address")
621
+ description: "Get the deployment transaction and creator for a contract.",
622
+ args: z4.object({
623
+ address: z4.string().describe("Contract address")
412
624
  }),
413
- options: z2.object({
625
+ options: z4.object({
414
626
  chain: chainOption2
415
627
  }),
628
+ env: etherscanEnv,
629
+ output: z4.object({
630
+ address: z4.string(),
631
+ creator: z4.string(),
632
+ txHash: z4.string(),
633
+ chain: z4.string()
634
+ }),
635
+ examples: [
636
+ {
637
+ args: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
638
+ options: { chain: "ethereum" },
639
+ description: "Find deployment tx for a contract"
640
+ }
641
+ ],
416
642
  async run(c) {
417
- const { apiKey } = apiKeyAuth2("ETHERSCAN_API_KEY");
643
+ const apiKey = c.env.ETHERSCAN_API_KEY;
418
644
  const chainId = resolveChainId(c.options.chain);
419
645
  const address = checksumAddress2(c.args.address);
420
646
  const client = createEtherscanClient(apiKey);
421
647
  const results = await withRateLimit3(
422
- () => client.call({
423
- chainid: chainId,
424
- module: "contract",
425
- action: "getcontractcreation",
426
- contractaddresses: address
427
- }),
648
+ () => client.call(
649
+ {
650
+ chainid: chainId,
651
+ module: "contract",
652
+ action: "getcontractcreation",
653
+ contractaddresses: address
654
+ },
655
+ z4.array(creationResultSchema)
656
+ ),
428
657
  rateLimiter2
429
658
  );
430
659
  const result = results[0];
@@ -454,28 +683,50 @@ contractCli.command("creation", {
454
683
  });
455
684
 
456
685
  // src/commands/gas.ts
457
- import { apiKeyAuth as apiKeyAuth3, createRateLimiter as createRateLimiter4, withRateLimit as withRateLimit4 } from "@spectratools/cli-shared";
458
- import { Cli as Cli3, z as z3 } from "incur";
686
+ import { createRateLimiter as createRateLimiter4, withRateLimit as withRateLimit4 } from "@spectratools/cli-shared";
687
+ import { Cli as Cli3, z as z5 } from "incur";
459
688
  var rateLimiter3 = createRateLimiter4({ requestsPerSecond: 5 });
460
- var chainOption3 = z3.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
689
+ var chainOption3 = z5.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
461
690
  var gasCli = Cli3.create("gas", {
462
- description: "Query gas oracle and estimate gas costs"
691
+ description: "Query gas oracle data and estimate confirmation latency."
692
+ });
693
+ var gasOracleSchema = z5.object({
694
+ LastBlock: z5.string(),
695
+ SafeGasPrice: z5.string(),
696
+ ProposeGasPrice: z5.string(),
697
+ FastGasPrice: z5.string(),
698
+ suggestBaseFee: z5.string(),
699
+ gasUsedRatio: z5.string()
463
700
  });
464
701
  gasCli.command("oracle", {
465
- description: "Get current gas price recommendations",
466
- options: z3.object({
702
+ description: "Get current gas price recommendations.",
703
+ options: z5.object({
467
704
  chain: chainOption3
468
705
  }),
706
+ env: etherscanEnv,
707
+ output: z5.object({
708
+ chain: z5.string(),
709
+ lastBlock: z5.string(),
710
+ slow: z5.string(),
711
+ standard: z5.string(),
712
+ fast: z5.string(),
713
+ baseFee: z5.string(),
714
+ gasUsedRatio: z5.string()
715
+ }),
716
+ examples: [{ options: { chain: "abstract" }, description: "Get gas oracle on Abstract" }],
469
717
  async run(c) {
470
- const { apiKey } = apiKeyAuth3("ETHERSCAN_API_KEY");
718
+ const apiKey = c.env.ETHERSCAN_API_KEY;
471
719
  const chainId = resolveChainId(c.options.chain);
472
720
  const client = createEtherscanClient(apiKey);
473
721
  const oracle = await withRateLimit4(
474
- () => client.call({
475
- chainid: chainId,
476
- module: "gastracker",
477
- action: "gasoracle"
478
- }),
722
+ () => client.call(
723
+ {
724
+ chainid: chainId,
725
+ module: "gastracker",
726
+ action: "gasoracle"
727
+ },
728
+ gasOracleSchema
729
+ ),
479
730
  rateLimiter3
480
731
  );
481
732
  return c.ok(
@@ -503,22 +754,37 @@ gasCli.command("oracle", {
503
754
  }
504
755
  });
505
756
  gasCli.command("estimate", {
506
- description: "Estimate gas cost in wei for a given gas price",
507
- options: z3.object({
508
- gasprice: z3.string().describe("Gas price in wei"),
757
+ description: "Estimate confirmation time in seconds for a gas price (wei).",
758
+ options: z5.object({
759
+ gasprice: z5.string().describe("Gas price in wei"),
509
760
  chain: chainOption3
510
761
  }),
762
+ env: etherscanEnv,
763
+ output: z5.object({
764
+ chain: z5.string(),
765
+ gasprice: z5.string(),
766
+ estimatedSeconds: z5.string()
767
+ }),
768
+ examples: [
769
+ {
770
+ options: { gasprice: "1000000000", chain: "ethereum" },
771
+ description: "Estimate confirmation time at 1 gwei"
772
+ }
773
+ ],
511
774
  async run(c) {
512
- const { apiKey } = apiKeyAuth3("ETHERSCAN_API_KEY");
775
+ const apiKey = c.env.ETHERSCAN_API_KEY;
513
776
  const chainId = resolveChainId(c.options.chain);
514
777
  const client = createEtherscanClient(apiKey);
515
778
  const estimate = await withRateLimit4(
516
- () => client.call({
517
- chainid: chainId,
518
- module: "gastracker",
519
- action: "gasestimate",
520
- gasprice: c.options.gasprice
521
- }),
779
+ () => client.call(
780
+ {
781
+ chainid: chainId,
782
+ module: "gastracker",
783
+ action: "gasestimate",
784
+ gasprice: c.options.gasprice
785
+ },
786
+ z5.string()
787
+ ),
522
788
  rateLimiter3
523
789
  );
524
790
  return c.ok(
@@ -542,28 +808,46 @@ gasCli.command("estimate", {
542
808
  });
543
809
 
544
810
  // src/commands/stats.ts
545
- import { apiKeyAuth as apiKeyAuth4, createRateLimiter as createRateLimiter5, withRateLimit as withRateLimit5 } from "@spectratools/cli-shared";
546
- import { Cli as Cli4, z as z4 } from "incur";
811
+ import { createRateLimiter as createRateLimiter5, withRateLimit as withRateLimit5 } from "@spectratools/cli-shared";
812
+ import { Cli as Cli4, z as z6 } from "incur";
547
813
  var rateLimiter4 = createRateLimiter5({ requestsPerSecond: 5 });
548
- var chainOption4 = z4.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
814
+ var chainOption4 = z6.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
549
815
  var statsCli = Cli4.create("stats", {
550
- description: "Query ETH price and supply statistics"
816
+ description: "Query ETH price and total supply statistics."
817
+ });
818
+ var ethPriceSchema = z6.object({
819
+ ethbtc: z6.string(),
820
+ ethbtc_timestamp: z6.string(),
821
+ ethusd: z6.string(),
822
+ ethusd_timestamp: z6.string()
551
823
  });
552
824
  statsCli.command("ethprice", {
553
- description: "Get the latest ETH price in USD and BTC",
554
- options: z4.object({
825
+ description: "Get latest ETH price in USD and BTC.",
826
+ options: z6.object({
555
827
  chain: chainOption4
556
828
  }),
829
+ env: etherscanEnv,
830
+ output: z6.object({
831
+ chain: z6.string(),
832
+ usd: z6.string(),
833
+ btc: z6.string(),
834
+ usdTimestamp: z6.string(),
835
+ btcTimestamp: z6.string()
836
+ }),
837
+ examples: [{ options: { chain: "ethereum" }, description: "Get ETH spot price on Ethereum" }],
557
838
  async run(c) {
558
- const { apiKey } = apiKeyAuth4("ETHERSCAN_API_KEY");
839
+ const apiKey = c.env.ETHERSCAN_API_KEY;
559
840
  const chainId = resolveChainId(c.options.chain);
560
841
  const client = createEtherscanClient(apiKey);
561
842
  const price = await withRateLimit5(
562
- () => client.call({
563
- chainid: chainId,
564
- module: "stats",
565
- action: "ethprice"
566
- }),
843
+ () => client.call(
844
+ {
845
+ chainid: chainId,
846
+ module: "stats",
847
+ action: "ethprice"
848
+ },
849
+ ethPriceSchema
850
+ ),
567
851
  rateLimiter4
568
852
  );
569
853
  return c.ok(
@@ -588,20 +872,29 @@ statsCli.command("ethprice", {
588
872
  }
589
873
  });
590
874
  statsCli.command("ethsupply", {
591
- description: "Get the total supply of ETH",
592
- options: z4.object({
875
+ description: "Get total ETH supply in wei.",
876
+ options: z6.object({
593
877
  chain: chainOption4
594
878
  }),
879
+ env: etherscanEnv,
880
+ output: z6.object({
881
+ chain: z6.string(),
882
+ totalSupplyWei: z6.string()
883
+ }),
884
+ examples: [{ options: { chain: "ethereum" }, description: "Get total ETH supply" }],
595
885
  async run(c) {
596
- const { apiKey } = apiKeyAuth4("ETHERSCAN_API_KEY");
886
+ const apiKey = c.env.ETHERSCAN_API_KEY;
597
887
  const chainId = resolveChainId(c.options.chain);
598
888
  const client = createEtherscanClient(apiKey);
599
889
  const supply = await withRateLimit5(
600
- () => client.call({
601
- chainid: chainId,
602
- module: "stats",
603
- action: "ethsupply"
604
- }),
890
+ () => client.call(
891
+ {
892
+ chainid: chainId,
893
+ module: "stats",
894
+ action: "ethsupply"
895
+ },
896
+ z6.string()
897
+ ),
605
898
  rateLimiter4
606
899
  );
607
900
  return c.ok(
@@ -624,38 +917,85 @@ statsCli.command("ethsupply", {
624
917
  });
625
918
 
626
919
  // src/commands/token.ts
627
- import {
628
- apiKeyAuth as apiKeyAuth5,
629
- checksumAddress as checksumAddress3,
630
- createRateLimiter as createRateLimiter6,
631
- withRateLimit as withRateLimit6
632
- } from "@spectratools/cli-shared";
633
- import { Cli as Cli5, z as z5 } from "incur";
920
+ import { checksumAddress as checksumAddress3, createRateLimiter as createRateLimiter6, withRateLimit as withRateLimit6 } from "@spectratools/cli-shared";
921
+ import { Cli as Cli5, z as z7 } from "incur";
634
922
  var rateLimiter5 = createRateLimiter6({ requestsPerSecond: 5 });
635
- var chainOption5 = z5.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
923
+ var chainOption5 = z7.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
636
924
  var tokenCli = Cli5.create("token", {
637
- description: "Query token info, holders, and supply"
925
+ description: "Query token metadata, holders, and supply."
926
+ });
927
+ var tokenInfoSchema = z7.object({
928
+ contractAddress: z7.string(),
929
+ tokenName: z7.string(),
930
+ symbol: z7.string(),
931
+ divisor: z7.string(),
932
+ tokenType: z7.string(),
933
+ totalSupply: z7.string(),
934
+ blueCheckmark: z7.string(),
935
+ description: z7.string(),
936
+ website: z7.string(),
937
+ email: z7.string(),
938
+ blog: z7.string(),
939
+ reddit: z7.string(),
940
+ slack: z7.string(),
941
+ facebook: z7.string(),
942
+ twitter: z7.string(),
943
+ bitcointalk: z7.string(),
944
+ github: z7.string(),
945
+ telegram: z7.string(),
946
+ wechat: z7.string(),
947
+ linkedin: z7.string(),
948
+ discord: z7.string(),
949
+ whitepaper: z7.string(),
950
+ tokenPriceUSD: z7.string()
951
+ });
952
+ var holderEntrySchema = z7.object({
953
+ TokenHolderAddress: z7.string(),
954
+ TokenHolderQuantity: z7.string()
638
955
  });
639
956
  tokenCli.command("info", {
640
- description: "Get information about a token contract",
641
- args: z5.object({
642
- contractaddress: z5.string().describe("Token contract address")
957
+ description: "Get metadata for a token contract.",
958
+ args: z7.object({
959
+ contractaddress: z7.string().describe("Token contract address")
643
960
  }),
644
- options: z5.object({
961
+ options: z7.object({
645
962
  chain: chainOption5
646
963
  }),
964
+ env: etherscanEnv,
965
+ output: z7.object({
966
+ address: z7.string(),
967
+ chain: z7.string(),
968
+ name: z7.string(),
969
+ symbol: z7.string(),
970
+ type: z7.string(),
971
+ totalSupply: z7.string(),
972
+ decimals: z7.string(),
973
+ priceUsd: z7.string().optional(),
974
+ website: z7.string().optional(),
975
+ description: z7.string().optional()
976
+ }),
977
+ examples: [
978
+ {
979
+ args: { contractaddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
980
+ options: { chain: "ethereum" },
981
+ description: "Get token metadata for USDC"
982
+ }
983
+ ],
647
984
  async run(c) {
648
- const { apiKey } = apiKeyAuth5("ETHERSCAN_API_KEY");
985
+ const apiKey = c.env.ETHERSCAN_API_KEY;
649
986
  const chainId = resolveChainId(c.options.chain);
650
987
  const address = checksumAddress3(c.args.contractaddress);
651
988
  const client = createEtherscanClient(apiKey);
652
989
  const results = await withRateLimit6(
653
- () => client.call({
654
- chainid: chainId,
655
- module: "token",
656
- action: "tokeninfo",
657
- contractaddress: address
658
- }),
990
+ () => client.call(
991
+ {
992
+ chainid: chainId,
993
+ module: "token",
994
+ action: "tokeninfo",
995
+ contractaddress: address
996
+ },
997
+ z7.array(tokenInfoSchema)
998
+ ),
659
999
  rateLimiter5
660
1000
  );
661
1001
  const info = results[0];
@@ -695,29 +1035,52 @@ tokenCli.command("info", {
695
1035
  }
696
1036
  });
697
1037
  tokenCli.command("holders", {
698
- description: "List top token holders",
699
- args: z5.object({
700
- contractaddress: z5.string().describe("Token contract address")
1038
+ description: "List top token holders.",
1039
+ args: z7.object({
1040
+ contractaddress: z7.string().describe("Token contract address")
701
1041
  }),
702
- options: z5.object({
703
- page: z5.number().optional().default(1).describe("Page number"),
704
- offset: z5.number().optional().default(10).describe("Results per page"),
1042
+ options: z7.object({
1043
+ page: z7.number().optional().default(1).describe("Page number"),
1044
+ offset: z7.number().optional().default(10).describe("Results per page"),
705
1045
  chain: chainOption5
706
1046
  }),
1047
+ env: etherscanEnv,
1048
+ output: z7.object({
1049
+ contractAddress: z7.string(),
1050
+ chain: z7.string(),
1051
+ count: z7.number(),
1052
+ holders: z7.array(
1053
+ z7.object({
1054
+ rank: z7.number(),
1055
+ address: z7.string(),
1056
+ quantity: z7.string()
1057
+ })
1058
+ )
1059
+ }),
1060
+ examples: [
1061
+ {
1062
+ args: { contractaddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
1063
+ options: { page: 1, offset: 20, chain: "ethereum" },
1064
+ description: "List top 20 holders for a token"
1065
+ }
1066
+ ],
707
1067
  async run(c) {
708
- const { apiKey } = apiKeyAuth5("ETHERSCAN_API_KEY");
1068
+ const apiKey = c.env.ETHERSCAN_API_KEY;
709
1069
  const chainId = resolveChainId(c.options.chain);
710
1070
  const address = checksumAddress3(c.args.contractaddress);
711
1071
  const client = createEtherscanClient(apiKey);
712
1072
  const holders = await withRateLimit6(
713
- () => client.call({
714
- chainid: chainId,
715
- module: "token",
716
- action: "tokenholderlist",
717
- contractaddress: address,
718
- page: c.options.page,
719
- offset: c.options.offset
720
- }),
1073
+ () => client.call(
1074
+ {
1075
+ chainid: chainId,
1076
+ module: "token",
1077
+ action: "tokenholderlist",
1078
+ contractaddress: address,
1079
+ page: c.options.page,
1080
+ offset: c.options.offset
1081
+ },
1082
+ z7.array(holderEntrySchema)
1083
+ ),
721
1084
  rateLimiter5
722
1085
  );
723
1086
  const formatted = holders.map((h, i) => ({
@@ -747,25 +1110,41 @@ tokenCli.command("holders", {
747
1110
  }
748
1111
  });
749
1112
  tokenCli.command("supply", {
750
- description: "Get the total supply of a token",
751
- args: z5.object({
752
- contractaddress: z5.string().describe("Token contract address")
1113
+ description: "Get total token supply.",
1114
+ args: z7.object({
1115
+ contractaddress: z7.string().describe("Token contract address")
753
1116
  }),
754
- options: z5.object({
1117
+ options: z7.object({
755
1118
  chain: chainOption5
756
1119
  }),
1120
+ env: etherscanEnv,
1121
+ output: z7.object({
1122
+ contractAddress: z7.string(),
1123
+ chain: z7.string(),
1124
+ totalSupply: z7.string()
1125
+ }),
1126
+ examples: [
1127
+ {
1128
+ args: { contractaddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
1129
+ options: { chain: "ethereum" },
1130
+ description: "Get total supply for a token"
1131
+ }
1132
+ ],
757
1133
  async run(c) {
758
- const { apiKey } = apiKeyAuth5("ETHERSCAN_API_KEY");
1134
+ const apiKey = c.env.ETHERSCAN_API_KEY;
759
1135
  const chainId = resolveChainId(c.options.chain);
760
1136
  const address = checksumAddress3(c.args.contractaddress);
761
1137
  const client = createEtherscanClient(apiKey);
762
1138
  const supply = await withRateLimit6(
763
- () => client.call({
764
- chainid: chainId,
765
- module: "stats",
766
- action: "tokensupply",
767
- contractaddress: address
768
- }),
1139
+ () => client.call(
1140
+ {
1141
+ chainid: chainId,
1142
+ module: "stats",
1143
+ action: "tokensupply",
1144
+ contractaddress: address
1145
+ },
1146
+ z7.string()
1147
+ ),
769
1148
  rateLimiter5
770
1149
  );
771
1150
  return c.ok(
@@ -786,32 +1165,82 @@ tokenCli.command("supply", {
786
1165
  });
787
1166
 
788
1167
  // src/commands/tx.ts
789
- import { apiKeyAuth as apiKeyAuth6, createRateLimiter as createRateLimiter7, withRateLimit as withRateLimit7 } from "@spectratools/cli-shared";
790
- import { Cli as Cli6, z as z6 } from "incur";
1168
+ import { createRateLimiter as createRateLimiter7, withRateLimit as withRateLimit7 } from "@spectratools/cli-shared";
1169
+ import { Cli as Cli6, z as z8 } from "incur";
791
1170
  var rateLimiter6 = createRateLimiter7({ requestsPerSecond: 5 });
792
- var chainOption6 = z6.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
1171
+ var chainOption6 = z8.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
793
1172
  var txCli = Cli6.create("tx", {
794
- description: "Query transaction details, receipts, and status"
1173
+ description: "Query transaction details, receipts, and execution status."
1174
+ });
1175
+ var transactionInfoSchema = z8.object({
1176
+ hash: z8.string(),
1177
+ from: z8.string(),
1178
+ to: z8.string().nullable(),
1179
+ value: z8.string(),
1180
+ gas: z8.string(),
1181
+ gasPrice: z8.string(),
1182
+ nonce: z8.string(),
1183
+ blockNumber: z8.string(),
1184
+ blockHash: z8.string(),
1185
+ input: z8.string()
1186
+ });
1187
+ var transactionReceiptSchema = z8.object({
1188
+ transactionHash: z8.string(),
1189
+ blockNumber: z8.string(),
1190
+ blockHash: z8.string(),
1191
+ from: z8.string(),
1192
+ to: z8.string().nullable(),
1193
+ status: z8.string(),
1194
+ gasUsed: z8.string(),
1195
+ cumulativeGasUsed: z8.string(),
1196
+ contractAddress: z8.string().nullable(),
1197
+ logs: z8.array(z8.unknown())
1198
+ });
1199
+ var txStatusSchema = z8.object({
1200
+ status: z8.string(),
1201
+ errDescription: z8.string()
795
1202
  });
796
1203
  txCli.command("info", {
797
- description: "Get transaction details by hash",
798
- args: z6.object({
799
- txhash: z6.string().describe("Transaction hash")
1204
+ description: "Get transaction details by hash.",
1205
+ args: z8.object({
1206
+ txhash: z8.string().describe("Transaction hash")
800
1207
  }),
801
- options: z6.object({
1208
+ options: z8.object({
802
1209
  chain: chainOption6
803
1210
  }),
1211
+ env: etherscanEnv,
1212
+ output: z8.object({
1213
+ hash: z8.string(),
1214
+ from: z8.string(),
1215
+ to: z8.string().nullable(),
1216
+ value: z8.string(),
1217
+ gas: z8.string(),
1218
+ gasPrice: z8.string(),
1219
+ nonce: z8.string(),
1220
+ block: z8.string(),
1221
+ chain: z8.string()
1222
+ }),
1223
+ examples: [
1224
+ {
1225
+ args: { txhash: "0x1234...abcd" },
1226
+ options: { chain: "abstract" },
1227
+ description: "Inspect one transaction on Abstract"
1228
+ }
1229
+ ],
804
1230
  async run(c) {
805
- const { apiKey } = apiKeyAuth6("ETHERSCAN_API_KEY");
1231
+ const apiKey = c.env.ETHERSCAN_API_KEY;
806
1232
  const chainId = resolveChainId(c.options.chain);
807
1233
  const client = createEtherscanClient(apiKey);
808
1234
  const tx = await withRateLimit7(
809
- () => client.callProxy({
810
- chainid: chainId,
811
- module: "proxy",
812
- action: "eth_getTransactionByHash",
813
- txhash: c.args.txhash
814
- }),
1235
+ () => client.callProxy(
1236
+ {
1237
+ chainid: chainId,
1238
+ module: "proxy",
1239
+ action: "eth_getTransactionByHash",
1240
+ txhash: c.args.txhash
1241
+ },
1242
+ transactionInfoSchema
1243
+ ),
815
1244
  rateLimiter6
816
1245
  );
817
1246
  return c.ok(
@@ -846,24 +1275,46 @@ txCli.command("info", {
846
1275
  }
847
1276
  });
848
1277
  txCli.command("receipt", {
849
- description: "Get the receipt for a transaction",
850
- args: z6.object({
851
- txhash: z6.string().describe("Transaction hash")
1278
+ description: "Get the receipt for a transaction.",
1279
+ args: z8.object({
1280
+ txhash: z8.string().describe("Transaction hash")
852
1281
  }),
853
- options: z6.object({
1282
+ options: z8.object({
854
1283
  chain: chainOption6
855
1284
  }),
1285
+ env: etherscanEnv,
1286
+ output: z8.object({
1287
+ hash: z8.string(),
1288
+ block: z8.string(),
1289
+ from: z8.string(),
1290
+ to: z8.string().nullable(),
1291
+ status: z8.string(),
1292
+ gasUsed: z8.string(),
1293
+ contractAddress: z8.string().nullable(),
1294
+ logCount: z8.number(),
1295
+ chain: z8.string()
1296
+ }),
1297
+ examples: [
1298
+ {
1299
+ args: { txhash: "0x1234...abcd" },
1300
+ options: { chain: "ethereum" },
1301
+ description: "Get receipt details including status and logs"
1302
+ }
1303
+ ],
856
1304
  async run(c) {
857
- const { apiKey } = apiKeyAuth6("ETHERSCAN_API_KEY");
1305
+ const apiKey = c.env.ETHERSCAN_API_KEY;
858
1306
  const chainId = resolveChainId(c.options.chain);
859
1307
  const client = createEtherscanClient(apiKey);
860
1308
  const receipt = await withRateLimit7(
861
- () => client.callProxy({
862
- chainid: chainId,
863
- module: "proxy",
864
- action: "eth_getTransactionReceipt",
865
- txhash: c.args.txhash
866
- }),
1309
+ () => client.callProxy(
1310
+ {
1311
+ chainid: chainId,
1312
+ module: "proxy",
1313
+ action: "eth_getTransactionReceipt",
1314
+ txhash: c.args.txhash
1315
+ },
1316
+ transactionReceiptSchema
1317
+ ),
867
1318
  rateLimiter6
868
1319
  );
869
1320
  return c.ok(
@@ -893,24 +1344,41 @@ txCli.command("receipt", {
893
1344
  }
894
1345
  });
895
1346
  txCli.command("status", {
896
- description: "Check whether a transaction succeeded or failed",
897
- args: z6.object({
898
- txhash: z6.string().describe("Transaction hash")
1347
+ description: "Check whether a transaction succeeded or failed.",
1348
+ args: z8.object({
1349
+ txhash: z8.string().describe("Transaction hash")
899
1350
  }),
900
- options: z6.object({
1351
+ options: z8.object({
901
1352
  chain: chainOption6
902
1353
  }),
1354
+ env: etherscanEnv,
1355
+ output: z8.object({
1356
+ hash: z8.string(),
1357
+ status: z8.string(),
1358
+ error: z8.string().optional(),
1359
+ chain: z8.string()
1360
+ }),
1361
+ examples: [
1362
+ {
1363
+ args: { txhash: "0x1234...abcd" },
1364
+ options: { chain: "base" },
1365
+ description: "Get pass/fail status for a transaction"
1366
+ }
1367
+ ],
903
1368
  async run(c) {
904
- const { apiKey } = apiKeyAuth6("ETHERSCAN_API_KEY");
1369
+ const apiKey = c.env.ETHERSCAN_API_KEY;
905
1370
  const chainId = resolveChainId(c.options.chain);
906
1371
  const client = createEtherscanClient(apiKey);
907
1372
  const result = await withRateLimit7(
908
- () => client.call({
909
- chainid: chainId,
910
- module: "transaction",
911
- action: "gettxreceiptstatus",
912
- txhash: c.args.txhash
913
- }),
1373
+ () => client.call(
1374
+ {
1375
+ chainid: chainId,
1376
+ module: "transaction",
1377
+ action: "gettxreceiptstatus",
1378
+ txhash: c.args.txhash
1379
+ },
1380
+ txStatusSchema
1381
+ ),
914
1382
  rateLimiter6
915
1383
  );
916
1384
  return c.ok(
@@ -945,7 +1413,18 @@ cli.command(txCli);
945
1413
  cli.command(tokenCli);
946
1414
  cli.command(gasCli);
947
1415
  cli.command(statsCli);
948
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
1416
+ var isMain = (() => {
1417
+ const entrypoint = process.argv[1];
1418
+ if (!entrypoint) {
1419
+ return false;
1420
+ }
1421
+ try {
1422
+ return realpathSync(entrypoint) === realpathSync(fileURLToPath(import.meta.url));
1423
+ } catch {
1424
+ return false;
1425
+ }
1426
+ })();
1427
+ if (isMain) {
949
1428
  cli.serve();
950
1429
  }
951
1430
  export {