@spectratools/etherscan-cli 0.2.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/cli.js +119 -31
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { realpathSync } from "fs";
4
+ import { readFileSync, realpathSync } from "fs";
5
+ import { dirname, resolve } from "path";
5
6
  import { fileURLToPath } from "url";
6
7
  import { Cli as Cli7 } from "incur";
7
8
 
@@ -10,6 +11,7 @@ import {
10
11
  checksumAddress,
11
12
  createRateLimiter as createRateLimiter2,
12
13
  formatTimestamp,
14
+ isAddress,
13
15
  weiToEth,
14
16
  withRateLimit as withRateLimit2
15
17
  } from "@spectratools/cli-shared";
@@ -193,6 +195,12 @@ accountCli.command("balance", {
193
195
  }
194
196
  ],
195
197
  async run(c) {
198
+ if (!isAddress(c.args.address)) {
199
+ return c.error({
200
+ code: "INVALID_ADDRESS",
201
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
202
+ });
203
+ }
196
204
  const apiKey = c.env.ETHERSCAN_API_KEY;
197
205
  const chainId = resolveChainId(c.options.chain);
198
206
  const address = normalizeAddress(c.args.address);
@@ -212,10 +220,14 @@ accountCli.command("balance", {
212
220
  );
213
221
  return c.ok(
214
222
  { address, wei, eth: weiToEth(wei), chain: c.options.chain },
215
- {
223
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
216
224
  cta: {
217
225
  commands: [
218
- { command: "account txlist", args: { address }, description: "List transactions" },
226
+ {
227
+ command: "account txlist",
228
+ args: { address },
229
+ description: "List transactions"
230
+ },
219
231
  {
220
232
  command: "account tokentx",
221
233
  args: { address },
@@ -267,6 +279,12 @@ accountCli.command("txlist", {
267
279
  }
268
280
  ],
269
281
  async run(c) {
282
+ if (!isAddress(c.args.address)) {
283
+ return c.error({
284
+ code: "INVALID_ADDRESS",
285
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
286
+ });
287
+ }
270
288
  const apiKey = c.env.ETHERSCAN_API_KEY;
271
289
  const chainId = resolveChainId(c.options.chain);
272
290
  const address = normalizeAddress(c.args.address);
@@ -302,7 +320,7 @@ accountCli.command("txlist", {
302
320
  const firstHash = formatted[0]?.hash;
303
321
  return c.ok(
304
322
  { address, chain: c.options.chain, count: formatted.length, transactions: formatted },
305
- {
323
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
306
324
  cta: {
307
325
  commands: firstHash ? [
308
326
  {
@@ -354,6 +372,12 @@ accountCli.command("tokentx", {
354
372
  }
355
373
  ],
356
374
  async run(c) {
375
+ if (!isAddress(c.args.address)) {
376
+ return c.error({
377
+ code: "INVALID_ADDRESS",
378
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
379
+ });
380
+ }
357
381
  const apiKey = c.env.ETHERSCAN_API_KEY;
358
382
  const chainId = resolveChainId(c.options.chain);
359
383
  const address = normalizeAddress(c.args.address);
@@ -386,7 +410,7 @@ accountCli.command("tokentx", {
386
410
  }));
387
411
  return c.ok(
388
412
  { address, chain: c.options.chain, count: formatted.length, transfers: formatted },
389
- {
413
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
390
414
  cta: {
391
415
  commands: [
392
416
  {
@@ -424,6 +448,18 @@ accountCli.command("tokenbalance", {
424
448
  }
425
449
  ],
426
450
  async run(c) {
451
+ if (!isAddress(c.args.address)) {
452
+ return c.error({
453
+ code: "INVALID_ADDRESS",
454
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
455
+ });
456
+ }
457
+ if (!isAddress(c.options.contractaddress)) {
458
+ return c.error({
459
+ code: "INVALID_ADDRESS",
460
+ message: `Invalid contract address: "${c.options.contractaddress}". Use a valid 0x-prefixed 20-byte hex address.`
461
+ });
462
+ }
427
463
  const apiKey = c.env.ETHERSCAN_API_KEY;
428
464
  const chainId = resolveChainId(c.options.chain);
429
465
  const address = normalizeAddress(c.args.address);
@@ -445,7 +481,7 @@ accountCli.command("tokenbalance", {
445
481
  );
446
482
  return c.ok(
447
483
  { address, contract, balance, chain: c.options.chain },
448
- {
484
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
449
485
  cta: {
450
486
  commands: [
451
487
  {
@@ -461,7 +497,12 @@ accountCli.command("tokenbalance", {
461
497
  });
462
498
 
463
499
  // src/commands/contract.ts
464
- import { checksumAddress as checksumAddress2, createRateLimiter as createRateLimiter3, withRateLimit as withRateLimit3 } from "@spectratools/cli-shared";
500
+ import {
501
+ checksumAddress as checksumAddress2,
502
+ createRateLimiter as createRateLimiter3,
503
+ isAddress as isAddress2,
504
+ withRateLimit as withRateLimit3
505
+ } from "@spectratools/cli-shared";
465
506
  import { Cli as Cli2, z as z4 } from "incur";
466
507
  var rateLimiter2 = createRateLimiter3({ requestsPerSecond: 5 });
467
508
  var chainOption2 = z4.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
@@ -507,6 +548,12 @@ contractCli.command("abi", {
507
548
  }
508
549
  ],
509
550
  async run(c) {
551
+ if (!isAddress2(c.args.address)) {
552
+ return c.error({
553
+ code: "INVALID_ADDRESS",
554
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
555
+ });
556
+ }
510
557
  const apiKey = c.env.ETHERSCAN_API_KEY;
511
558
  const chainId = resolveChainId(c.options.chain);
512
559
  const address = checksumAddress2(c.args.address);
@@ -525,7 +572,7 @@ contractCli.command("abi", {
525
572
  );
526
573
  return c.ok(
527
574
  { address, chain: c.options.chain, abi: JSON.parse(abi) },
528
- {
575
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
529
576
  cta: {
530
577
  commands: [
531
578
  {
@@ -569,6 +616,12 @@ contractCli.command("source", {
569
616
  }
570
617
  ],
571
618
  async run(c) {
619
+ if (!isAddress2(c.args.address)) {
620
+ return c.error({
621
+ code: "INVALID_ADDRESS",
622
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
623
+ });
624
+ }
572
625
  const apiKey = c.env.ETHERSCAN_API_KEY;
573
626
  const chainId = resolveChainId(c.options.chain);
574
627
  const address = checksumAddress2(c.args.address);
@@ -603,7 +656,7 @@ contractCli.command("source", {
603
656
  sourceCode: result.SourceCode,
604
657
  constructorArguments: result.ConstructorArguments
605
658
  },
606
- {
659
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
607
660
  cta: {
608
661
  commands: [
609
662
  {
@@ -640,6 +693,12 @@ contractCli.command("creation", {
640
693
  }
641
694
  ],
642
695
  async run(c) {
696
+ if (!isAddress2(c.args.address)) {
697
+ return c.error({
698
+ code: "INVALID_ADDRESS",
699
+ message: `Invalid address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
700
+ });
701
+ }
643
702
  const apiKey = c.env.ETHERSCAN_API_KEY;
644
703
  const chainId = resolveChainId(c.options.chain);
645
704
  const address = checksumAddress2(c.args.address);
@@ -667,7 +726,7 @@ contractCli.command("creation", {
667
726
  txHash: result.txHash,
668
727
  chain: c.options.chain
669
728
  },
670
- {
729
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
671
730
  cta: {
672
731
  commands: [
673
732
  {
@@ -739,7 +798,7 @@ gasCli.command("oracle", {
739
798
  baseFee: `${oracle.suggestBaseFee} Gwei`,
740
799
  gasUsedRatio: oracle.gasUsedRatio
741
800
  },
742
- {
801
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
743
802
  cta: {
744
803
  commands: [
745
804
  {
@@ -793,7 +852,7 @@ gasCli.command("estimate", {
793
852
  gasprice: c.options.gasprice,
794
853
  estimatedSeconds: estimate
795
854
  },
796
- {
855
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
797
856
  cta: {
798
857
  commands: [
799
858
  {
@@ -858,7 +917,7 @@ statsCli.command("ethprice", {
858
917
  usdTimestamp: new Date(Number(price.ethusd_timestamp) * 1e3).toISOString(),
859
918
  btcTimestamp: new Date(Number(price.ethbtc_timestamp) * 1e3).toISOString()
860
919
  },
861
- {
920
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
862
921
  cta: {
863
922
  commands: [
864
923
  {
@@ -902,7 +961,7 @@ statsCli.command("ethsupply", {
902
961
  chain: c.options.chain,
903
962
  totalSupplyWei: supply
904
963
  },
905
- {
964
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
906
965
  cta: {
907
966
  commands: [
908
967
  {
@@ -917,7 +976,12 @@ statsCli.command("ethsupply", {
917
976
  });
918
977
 
919
978
  // src/commands/token.ts
920
- import { checksumAddress as checksumAddress3, createRateLimiter as createRateLimiter6, withRateLimit as withRateLimit6 } from "@spectratools/cli-shared";
979
+ import {
980
+ checksumAddress as checksumAddress3,
981
+ createRateLimiter as createRateLimiter6,
982
+ isAddress as isAddress3,
983
+ withRateLimit as withRateLimit6
984
+ } from "@spectratools/cli-shared";
921
985
  import { Cli as Cli5, z as z7 } from "incur";
922
986
  var rateLimiter5 = createRateLimiter6({ requestsPerSecond: 5 });
923
987
  var chainOption5 = z7.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
@@ -982,6 +1046,12 @@ tokenCli.command("info", {
982
1046
  }
983
1047
  ],
984
1048
  async run(c) {
1049
+ if (!isAddress3(c.args.contractaddress)) {
1050
+ return c.error({
1051
+ code: "INVALID_ADDRESS",
1052
+ message: `Invalid contract address: "${c.args.contractaddress}". Use a valid 0x-prefixed 20-byte hex address.`
1053
+ });
1054
+ }
985
1055
  const apiKey = c.env.ETHERSCAN_API_KEY;
986
1056
  const chainId = resolveChainId(c.options.chain);
987
1057
  const address = checksumAddress3(c.args.contractaddress);
@@ -1015,7 +1085,7 @@ tokenCli.command("info", {
1015
1085
  website: info.website || void 0,
1016
1086
  description: info.description || void 0
1017
1087
  },
1018
- {
1088
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1019
1089
  cta: {
1020
1090
  commands: [
1021
1091
  {
@@ -1065,6 +1135,12 @@ tokenCli.command("holders", {
1065
1135
  }
1066
1136
  ],
1067
1137
  async run(c) {
1138
+ if (!isAddress3(c.args.contractaddress)) {
1139
+ return c.error({
1140
+ code: "INVALID_ADDRESS",
1141
+ message: `Invalid contract address: "${c.args.contractaddress}". Use a valid 0x-prefixed 20-byte hex address.`
1142
+ });
1143
+ }
1068
1144
  const apiKey = c.env.ETHERSCAN_API_KEY;
1069
1145
  const chainId = resolveChainId(c.options.chain);
1070
1146
  const address = checksumAddress3(c.args.contractaddress);
@@ -1095,7 +1171,7 @@ tokenCli.command("holders", {
1095
1171
  count: formatted.length,
1096
1172
  holders: formatted
1097
1173
  },
1098
- {
1174
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1099
1175
  cta: {
1100
1176
  commands: [
1101
1177
  {
@@ -1131,6 +1207,12 @@ tokenCli.command("supply", {
1131
1207
  }
1132
1208
  ],
1133
1209
  async run(c) {
1210
+ if (!isAddress3(c.args.contractaddress)) {
1211
+ return c.error({
1212
+ code: "INVALID_ADDRESS",
1213
+ message: `Invalid contract address: "${c.args.contractaddress}". Use a valid 0x-prefixed 20-byte hex address.`
1214
+ });
1215
+ }
1134
1216
  const apiKey = c.env.ETHERSCAN_API_KEY;
1135
1217
  const chainId = resolveChainId(c.options.chain);
1136
1218
  const address = checksumAddress3(c.args.contractaddress);
@@ -1149,7 +1231,7 @@ tokenCli.command("supply", {
1149
1231
  );
1150
1232
  return c.ok(
1151
1233
  { contractAddress: address, chain: c.options.chain, totalSupply: supply },
1152
- {
1234
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1153
1235
  cta: {
1154
1236
  commands: [
1155
1237
  {
@@ -1168,6 +1250,9 @@ tokenCli.command("supply", {
1168
1250
  import { createRateLimiter as createRateLimiter7, withRateLimit as withRateLimit7 } from "@spectratools/cli-shared";
1169
1251
  import { Cli as Cli6, z as z8 } from "incur";
1170
1252
  var rateLimiter6 = createRateLimiter7({ requestsPerSecond: 5 });
1253
+ function hexToDecimal(hex) {
1254
+ return BigInt(hex).toString();
1255
+ }
1171
1256
  var chainOption6 = z8.string().default(DEFAULT_CHAIN).describe("Chain name (abstract, ethereum, base, arbitrum, ...)");
1172
1257
  var txCli = Cli6.create("tx", {
1173
1258
  description: "Query transaction details, receipts, and execution status."
@@ -1197,7 +1282,7 @@ var transactionReceiptSchema = z8.object({
1197
1282
  logs: z8.array(z8.unknown())
1198
1283
  });
1199
1284
  var txStatusSchema = z8.object({
1200
- status: z8.string(),
1285
+ isError: z8.string(),
1201
1286
  errDescription: z8.string()
1202
1287
  });
1203
1288
  txCli.command("info", {
@@ -1248,14 +1333,14 @@ txCli.command("info", {
1248
1333
  hash: tx.hash,
1249
1334
  from: tx.from,
1250
1335
  to: tx.to,
1251
- value: tx.value,
1252
- gas: tx.gas,
1253
- gasPrice: tx.gasPrice,
1254
- nonce: tx.nonce,
1255
- block: tx.blockNumber,
1336
+ value: hexToDecimal(tx.value),
1337
+ gas: hexToDecimal(tx.gas),
1338
+ gasPrice: hexToDecimal(tx.gasPrice),
1339
+ nonce: hexToDecimal(tx.nonce),
1340
+ block: hexToDecimal(tx.blockNumber),
1256
1341
  chain: c.options.chain
1257
1342
  },
1258
- {
1343
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1259
1344
  cta: {
1260
1345
  commands: [
1261
1346
  {
@@ -1320,16 +1405,16 @@ txCli.command("receipt", {
1320
1405
  return c.ok(
1321
1406
  {
1322
1407
  hash: receipt.transactionHash,
1323
- block: receipt.blockNumber,
1408
+ block: hexToDecimal(receipt.blockNumber),
1324
1409
  from: receipt.from,
1325
1410
  to: receipt.to,
1326
1411
  status: receipt.status === "0x1" ? "success" : "failed",
1327
- gasUsed: receipt.gasUsed,
1412
+ gasUsed: hexToDecimal(receipt.gasUsed),
1328
1413
  contractAddress: receipt.contractAddress,
1329
1414
  logCount: receipt.logs.length,
1330
1415
  chain: c.options.chain
1331
1416
  },
1332
- {
1417
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1333
1418
  cta: {
1334
1419
  commands: [
1335
1420
  {
@@ -1374,7 +1459,7 @@ txCli.command("status", {
1374
1459
  {
1375
1460
  chainid: chainId,
1376
1461
  module: "transaction",
1377
- action: "gettxreceiptstatus",
1462
+ action: "getstatus",
1378
1463
  txhash: c.args.txhash
1379
1464
  },
1380
1465
  txStatusSchema
@@ -1384,11 +1469,11 @@ txCli.command("status", {
1384
1469
  return c.ok(
1385
1470
  {
1386
1471
  hash: c.args.txhash,
1387
- status: result.status === "1" ? "success" : "failed",
1472
+ status: result.isError === "0" ? "success" : "failed",
1388
1473
  error: result.errDescription || void 0,
1389
1474
  chain: c.options.chain
1390
1475
  },
1391
- {
1476
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1392
1477
  cta: {
1393
1478
  commands: [
1394
1479
  {
@@ -1404,7 +1489,10 @@ txCli.command("status", {
1404
1489
  });
1405
1490
 
1406
1491
  // src/cli.ts
1492
+ var __dirname = dirname(fileURLToPath(import.meta.url));
1493
+ var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8"));
1407
1494
  var cli = Cli7.create("etherscan", {
1495
+ version: pkg.version,
1408
1496
  description: "Query Etherscan API data from the command line."
1409
1497
  });
1410
1498
  cli.command(accountCli);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectratools/etherscan-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Etherscan API CLI for spectra-the-bot",
5
5
  "type": "module",
6
6
  "license": "MIT",