@spectratools/etherscan-cli 0.1.2 → 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.
- package/dist/cli.js +119 -31
- 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
|
-
{
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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: "
|
|
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.
|
|
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);
|