@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/README.md
CHANGED
|
@@ -18,6 +18,23 @@ Or run without installing globally:
|
|
|
18
18
|
npx @alchemy/cli <command>
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Shell Completions
|
|
22
|
+
|
|
23
|
+
Enable Tab completion for all commands and subcommands:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# zsh (add to ~/.zshrc)
|
|
27
|
+
echo 'eval "$(alchemy completions zsh)"' >> ~/.zshrc
|
|
28
|
+
source ~/.zshrc
|
|
29
|
+
|
|
30
|
+
# bash (add to ~/.bashrc)
|
|
31
|
+
echo 'eval "$(alchemy completions bash)"' >> ~/.bashrc
|
|
32
|
+
source ~/.bashrc
|
|
33
|
+
|
|
34
|
+
# fish
|
|
35
|
+
alchemy completions fish > ~/.config/fish/completions/alchemy.fish
|
|
36
|
+
```
|
|
37
|
+
|
|
21
38
|
## Getting Started
|
|
22
39
|
|
|
23
40
|
### Authentication Quick Start
|
|
@@ -47,7 +64,7 @@ For setup commands, env vars, and resolution order, see [Authentication Referenc
|
|
|
47
64
|
After auth is configured, use the CLI differently depending on who is driving it:
|
|
48
65
|
|
|
49
66
|
- **Humans (interactive terminal):** start with `alchemy` and use the terminal UI/setup flow; this is the recommended path for human usage
|
|
50
|
-
- **Agents/scripts (automation):**
|
|
67
|
+
- **Agents/scripts (automation):** use `--json --no-interactive` to guarantee JSON output and disable prompts (JSON is auto-enabled when piped, but `--json` is a safe default)
|
|
51
68
|
|
|
52
69
|
Quick usage examples:
|
|
53
70
|
|
|
@@ -86,7 +103,10 @@ Use `alchemy help` or `alchemy help <command>` for generated command help.
|
|
|
86
103
|
|---|---|---|
|
|
87
104
|
| `balance [address]` (`bal [address]`) | Gets ETH balance for an address | `alchemy bal 0x...` |
|
|
88
105
|
| `tx [hash]` | Gets transaction + receipt by hash | `alchemy tx 0x...` |
|
|
106
|
+
| `receipt [hash]` | Gets transaction receipt (status, gas, logs) | `alchemy receipt 0x...` |
|
|
89
107
|
| `block <number>` | Gets block details (`latest`, decimal, or hex) | `alchemy block latest` |
|
|
108
|
+
| `gas` | Gets current gas prices (base fee + priority fee) | `alchemy gas -n polygon-mainnet` |
|
|
109
|
+
| `logs` | Queries event logs (`eth_getLogs`) | `alchemy logs --address 0x... --from-block 18000000 --to-block 18000010` |
|
|
90
110
|
| `rpc <method> [params...]` | Makes raw JSON-RPC call | `alchemy rpc eth_blockNumber` |
|
|
91
111
|
| `trace <method> [params...]` | Calls Trace API methods | `alchemy trace call '{"to":"0x..."}' '["trace"]' latest` |
|
|
92
112
|
| `debug <method> [params...]` | Calls Debug API methods | `alchemy debug traceTransaction "0x..."` |
|
|
@@ -136,8 +156,7 @@ Use `alchemy help` or `alchemy help <command>` for generated command help.
|
|
|
136
156
|
|
|
137
157
|
| Command | What it does | Example |
|
|
138
158
|
|---|---|---|
|
|
139
|
-
| `network list` | Lists
|
|
140
|
-
| `chains list` | Lists Admin API chain enums | `alchemy chains list` |
|
|
159
|
+
| `network list` | Lists RPC network IDs for use with `--network` (e.g. `eth-mainnet`) | `alchemy network list --configured` |
|
|
141
160
|
| `solana rpc <method> [params...]` | Calls Solana JSON-RPC methods | `alchemy solana rpc getBalance '"<pubkey>"'` |
|
|
142
161
|
| `solana das <method> [params...]` | Calls Solana DAS methods | `alchemy solana das getAssetsByOwner '{"ownerAddress":"<pubkey>"}'` |
|
|
143
162
|
|
|
@@ -147,6 +166,7 @@ Use `alchemy help` or `alchemy help <command>` for generated command help.
|
|
|
147
166
|
|---|---|---|
|
|
148
167
|
| `(no command)` | Starts interactive REPL mode (TTY only) | `alchemy` |
|
|
149
168
|
| `apps list` | Lists apps (supports pagination/filtering) | `alchemy apps list --all` |
|
|
169
|
+
| `apps chains` | Lists Admin API chain identifiers (e.g. `ETH_MAINNET`) | `alchemy apps chains` |
|
|
150
170
|
| `apps get <id>` | Gets app details | `alchemy apps get <app-id>` |
|
|
151
171
|
| `apps create` | Creates app | `alchemy apps create --name "My App" --networks eth-mainnet` |
|
|
152
172
|
| `apps update <id>` | Updates app name/description | `alchemy apps update <app-id> --name "New Name"` |
|
|
@@ -161,6 +181,7 @@ Use `alchemy help` or `alchemy help <command>` for generated command help.
|
|
|
161
181
|
| `config get <key>` | Gets one config value | `alchemy config get network` |
|
|
162
182
|
| `config list` | Lists all config values | `alchemy config list` |
|
|
163
183
|
| `config reset [key]` | Resets one or all config values | `alchemy config reset --yes` |
|
|
184
|
+
| `completions <shell>` | Generates shell completion scripts (bash/zsh/fish) | `eval "$(alchemy completions zsh)"` |
|
|
164
185
|
| `agent-prompt` | Emits complete agent/automation usage instructions | `alchemy --json agent-prompt` |
|
|
165
186
|
| `version` | Prints CLI version | `alchemy version` |
|
|
166
187
|
|
|
@@ -184,7 +205,7 @@ These apply to all commands.
|
|
|
184
205
|
|
|
185
206
|
| Flag | Env var | Description |
|
|
186
207
|
|---|---|---|
|
|
187
|
-
| `--json` | — | Force JSON output |
|
|
208
|
+
| `--json` | — | Force JSON output (auto-enabled when piped) |
|
|
188
209
|
| `-q, --quiet` | — | Suppress non-essential output |
|
|
189
210
|
| `--verbose` | — | Enable verbose output |
|
|
190
211
|
| `--no-color` | `NO_COLOR` | Disable color output |
|
|
@@ -321,11 +342,10 @@ Use `--no-interactive` to disable REPL/prompts in automation.
|
|
|
321
342
|
|
|
322
343
|
## Output Modes
|
|
323
344
|
|
|
324
|
-
- TTY
|
|
325
|
-
- Non-TTY
|
|
326
|
-
-
|
|
327
|
-
- `--
|
|
328
|
-
- `--verbose` or `alchemy config set verbose true`: includes richer payload output on supported commands
|
|
345
|
+
- **TTY (terminal):** formatted human output (tables, colors, spinners)
|
|
346
|
+
- **Non-TTY (piped/redirected):** JSON output automatically — no flag needed
|
|
347
|
+
- `--json`: forces JSON output even in a terminal
|
|
348
|
+
- `--verbose` or `alchemy config set verbose true`: logs request/response details (method, URL, status, timing) to stderr
|
|
329
349
|
|
|
330
350
|
## Error Format
|
|
331
351
|
|
|
@@ -4,10 +4,13 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
|
4
4
|
// src/lib/colors.ts
|
|
5
5
|
var forceColor = "FORCE_COLOR" in process.env && process.env.FORCE_COLOR !== "0";
|
|
6
6
|
var noColor = !forceColor && ("NO_COLOR" in process.env || process.env.TERM === "dumb");
|
|
7
|
+
function setNoColor(value) {
|
|
8
|
+
noColor = value;
|
|
9
|
+
}
|
|
7
10
|
var identity = (s) => s;
|
|
8
|
-
var esc = (code) => noColor ?
|
|
9
|
-
var rgb = (r, g, b) => noColor ?
|
|
10
|
-
var bgRgb = (r, g, b) => noColor ?
|
|
11
|
+
var esc = (code) => (s) => noColor ? s : `\x1B[${code}m${s}\x1B[0m`;
|
|
12
|
+
var rgb = (r, g, b) => (s) => noColor ? s : `\x1B[38;2;${r};${g};${b}m${s}\x1B[39m`;
|
|
13
|
+
var bgRgb = (r, g, b) => (s) => noColor ? s : `\x1B[48;2;${r};${g};${b}m${s}\x1B[49m`;
|
|
11
14
|
|
|
12
15
|
// src/lib/redact.ts
|
|
13
16
|
var SENSITIVE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
@@ -475,7 +478,8 @@ function getHome() {
|
|
|
475
478
|
}
|
|
476
479
|
function configPath() {
|
|
477
480
|
if (process.env.ALCHEMY_CONFIG) return process.env.ALCHEMY_CONFIG;
|
|
478
|
-
|
|
481
|
+
const configHome = process.env.XDG_CONFIG_HOME || join(getHome(), ".config");
|
|
482
|
+
return join(configHome, "alchemy", "config.json");
|
|
479
483
|
}
|
|
480
484
|
function configDir() {
|
|
481
485
|
return dirname(configPath());
|
|
@@ -647,8 +651,9 @@ async function runListPrompt(opts) {
|
|
|
647
651
|
stdin.setRawMode(previousRawMode);
|
|
648
652
|
stdin.removeListener("keypress", onKeypress);
|
|
649
653
|
restoreKeypressListeners();
|
|
650
|
-
|
|
651
|
-
|
|
654
|
+
if (!previousRawMode) {
|
|
655
|
+
stdin.pause();
|
|
656
|
+
}
|
|
652
657
|
};
|
|
653
658
|
const commitSingleLine = (text) => {
|
|
654
659
|
if (opts.commitLabel === null) return;
|
|
@@ -1109,7 +1114,7 @@ function semverLT(a, b) {
|
|
|
1109
1114
|
return false;
|
|
1110
1115
|
}
|
|
1111
1116
|
function currentVersion() {
|
|
1112
|
-
return true ? "0.
|
|
1117
|
+
return true ? "0.3.1" : "0.0.0";
|
|
1113
1118
|
}
|
|
1114
1119
|
function toUpdateStatus(latestVersion, checkedAt) {
|
|
1115
1120
|
const current = currentVersion();
|
|
@@ -1168,10 +1173,12 @@ ${getUpdateNoticeLines(latest).join("\n")}
|
|
|
1168
1173
|
|
|
1169
1174
|
export {
|
|
1170
1175
|
noColor,
|
|
1176
|
+
setNoColor,
|
|
1171
1177
|
identity,
|
|
1172
1178
|
esc,
|
|
1173
1179
|
rgb,
|
|
1174
1180
|
bgRgb,
|
|
1181
|
+
redactSensitiveText,
|
|
1175
1182
|
quiet,
|
|
1176
1183
|
verbose,
|
|
1177
1184
|
timeout,
|
|
@@ -1214,6 +1221,7 @@ export {
|
|
|
1214
1221
|
promptAutocomplete,
|
|
1215
1222
|
promptMultiselect,
|
|
1216
1223
|
green,
|
|
1224
|
+
red,
|
|
1217
1225
|
dim,
|
|
1218
1226
|
bold,
|
|
1219
1227
|
yellow,
|
|
@@ -36,12 +36,14 @@ import {
|
|
|
36
36
|
promptMultiselect,
|
|
37
37
|
promptSelect,
|
|
38
38
|
promptText,
|
|
39
|
+
redactSensitiveText,
|
|
39
40
|
save,
|
|
40
41
|
timeout,
|
|
41
42
|
toMap,
|
|
43
|
+
verbose,
|
|
42
44
|
withSpinner,
|
|
43
45
|
yellow
|
|
44
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-6XTLILDF.js";
|
|
45
47
|
|
|
46
48
|
// src/lib/client-utils.ts
|
|
47
49
|
function isLocalhost(hostname) {
|
|
@@ -73,10 +75,15 @@ function parseBaseURLOverride(envVarName) {
|
|
|
73
75
|
}
|
|
74
76
|
return parsed;
|
|
75
77
|
}
|
|
78
|
+
var BREADCRUMB_HEADER = "alchemy-cli";
|
|
76
79
|
async function fetchWithTimeout(url, init) {
|
|
77
80
|
try {
|
|
78
81
|
return await fetch(url, {
|
|
79
82
|
...init,
|
|
83
|
+
headers: {
|
|
84
|
+
...init.headers,
|
|
85
|
+
"x-alchemy-client-breadcrumb": BREADCRUMB_HEADER
|
|
86
|
+
},
|
|
80
87
|
...timeout && { signal: AbortSignal.timeout(timeout) }
|
|
81
88
|
});
|
|
82
89
|
} catch (err) {
|
|
@@ -269,6 +276,92 @@ var AdminClient = class _AdminClient {
|
|
|
269
276
|
}
|
|
270
277
|
};
|
|
271
278
|
|
|
279
|
+
// src/lib/ens.ts
|
|
280
|
+
import { keccak_256 } from "@noble/hashes/sha3.js";
|
|
281
|
+
var UNIVERSAL_RESOLVER = "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe";
|
|
282
|
+
var RESOLVE_SELECTOR = "9061b923";
|
|
283
|
+
var ADDR_SELECTOR = "3b3b57de";
|
|
284
|
+
function bytesToHex(bytes) {
|
|
285
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
286
|
+
}
|
|
287
|
+
function pad32(hex) {
|
|
288
|
+
return hex.padStart(64, "0");
|
|
289
|
+
}
|
|
290
|
+
function namehash(name) {
|
|
291
|
+
let node = new Uint8Array(32);
|
|
292
|
+
if (!name) return node;
|
|
293
|
+
const labels = name.split(".");
|
|
294
|
+
for (let i = labels.length - 1; i >= 0; i--) {
|
|
295
|
+
const labelHash = keccak_256(new TextEncoder().encode(labels[i]));
|
|
296
|
+
const combined = new Uint8Array(64);
|
|
297
|
+
combined.set(node, 0);
|
|
298
|
+
combined.set(labelHash, 32);
|
|
299
|
+
node = keccak_256(combined);
|
|
300
|
+
}
|
|
301
|
+
return node;
|
|
302
|
+
}
|
|
303
|
+
function dnsEncode(name) {
|
|
304
|
+
const labels = name.split(".");
|
|
305
|
+
const parts = [];
|
|
306
|
+
for (const label of labels) {
|
|
307
|
+
const encoded = new TextEncoder().encode(label);
|
|
308
|
+
parts.push(encoded.length);
|
|
309
|
+
parts.push(...encoded);
|
|
310
|
+
}
|
|
311
|
+
parts.push(0);
|
|
312
|
+
return new Uint8Array(parts);
|
|
313
|
+
}
|
|
314
|
+
function buildResolveCalldata(name) {
|
|
315
|
+
const dnsName = dnsEncode(name);
|
|
316
|
+
const node = namehash(name);
|
|
317
|
+
const innerHex = ADDR_SELECTOR + bytesToHex(node);
|
|
318
|
+
const innerLen = 36;
|
|
319
|
+
const dnsHex = bytesToHex(dnsName);
|
|
320
|
+
const nameLen = dnsName.length;
|
|
321
|
+
const namePad = Math.ceil(nameLen / 32) * 32;
|
|
322
|
+
const innerPad = Math.ceil(innerLen / 32) * 32;
|
|
323
|
+
const nameOffset = 64;
|
|
324
|
+
const dataOffset = nameOffset + 32 + namePad;
|
|
325
|
+
let hex = RESOLVE_SELECTOR;
|
|
326
|
+
hex += pad32(nameOffset.toString(16));
|
|
327
|
+
hex += pad32(dataOffset.toString(16));
|
|
328
|
+
hex += pad32(nameLen.toString(16));
|
|
329
|
+
hex += dnsHex.padEnd(namePad * 2, "0");
|
|
330
|
+
hex += pad32(innerLen.toString(16));
|
|
331
|
+
hex += innerHex.padEnd(innerPad * 2, "0");
|
|
332
|
+
return "0x" + hex;
|
|
333
|
+
}
|
|
334
|
+
function isENSName(value) {
|
|
335
|
+
return value.endsWith(".eth") && value.length > 4 && !value.startsWith("0x");
|
|
336
|
+
}
|
|
337
|
+
async function resolveENS(name, client) {
|
|
338
|
+
if (!client.network.startsWith("eth-")) {
|
|
339
|
+
throw errInvalidArgs(
|
|
340
|
+
`ENS resolution is only supported on Ethereum networks. Current network: ${client.network}`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
const calldata = buildResolveCalldata(name.toLowerCase());
|
|
344
|
+
const result = await client.call("eth_call", [
|
|
345
|
+
{ to: UNIVERSAL_RESOLVER, data: calldata },
|
|
346
|
+
"latest"
|
|
347
|
+
]);
|
|
348
|
+
if (!result || result === "0x" || result.length < 130) {
|
|
349
|
+
throw errInvalidArgs(`ENS name "${name}" could not be resolved.`);
|
|
350
|
+
}
|
|
351
|
+
const raw = result.slice(2);
|
|
352
|
+
const dataOffset = parseInt(raw.slice(0, 64), 16) * 2;
|
|
353
|
+
const dataLen = parseInt(raw.slice(dataOffset, dataOffset + 64), 16);
|
|
354
|
+
const dataHex = raw.slice(dataOffset + 64, dataOffset + 64 + dataLen * 2);
|
|
355
|
+
if (dataHex.length < 64) {
|
|
356
|
+
throw errInvalidArgs(`ENS name "${name}" could not be resolved.`);
|
|
357
|
+
}
|
|
358
|
+
const address = "0x" + dataHex.slice(24, 64);
|
|
359
|
+
if (address === "0x0000000000000000000000000000000000000000") {
|
|
360
|
+
throw errInvalidArgs(`ENS name "${name}" is not registered or has no address set.`);
|
|
361
|
+
}
|
|
362
|
+
return address;
|
|
363
|
+
}
|
|
364
|
+
|
|
272
365
|
// src/lib/validators.ts
|
|
273
366
|
function splitCommaList(input) {
|
|
274
367
|
return input.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -297,6 +390,13 @@ function validateAddress(address) {
|
|
|
297
390
|
);
|
|
298
391
|
}
|
|
299
392
|
}
|
|
393
|
+
async function resolveAddress(input, client) {
|
|
394
|
+
if (isENSName(input)) {
|
|
395
|
+
return resolveENS(input, client);
|
|
396
|
+
}
|
|
397
|
+
validateAddress(input);
|
|
398
|
+
return input;
|
|
399
|
+
}
|
|
300
400
|
function validateTxHash(hash) {
|
|
301
401
|
if (!TX_HASH_RE.test(hash)) {
|
|
302
402
|
throw errInvalidArgs(
|
|
@@ -551,13 +651,13 @@ function registerConfig(program) {
|
|
|
551
651
|
if (normalized !== "true" && normalized !== "false") {
|
|
552
652
|
throw errInvalidArgs("verbose must be 'true' or 'false'");
|
|
553
653
|
}
|
|
554
|
-
const
|
|
654
|
+
const verbose2 = normalized === "true";
|
|
555
655
|
const cfg = load();
|
|
556
|
-
save({ ...cfg, verbose });
|
|
656
|
+
save({ ...cfg, verbose: verbose2 });
|
|
557
657
|
printHuman(
|
|
558
|
-
`${green("\u2713")} Set verbose default to ${
|
|
658
|
+
`${green("\u2713")} Set verbose default to ${verbose2}
|
|
559
659
|
`,
|
|
560
|
-
{ key: "verbose", value: String(
|
|
660
|
+
{ key: "verbose", value: String(verbose2), status: "set" }
|
|
561
661
|
);
|
|
562
662
|
} catch (err) {
|
|
563
663
|
exitWithError(err);
|
|
@@ -593,13 +693,28 @@ function registerConfig(program) {
|
|
|
593
693
|
});
|
|
594
694
|
cmd.command("get <key>").description("Get a config value (api-key, access-key, app, network, verbose, wallet-key-file, x402)").action((key) => {
|
|
595
695
|
const cfg = load();
|
|
596
|
-
|
|
696
|
+
let value = get(cfg, key);
|
|
697
|
+
let isDefault = false;
|
|
698
|
+
if (value === void 0) {
|
|
699
|
+
const defaults = {
|
|
700
|
+
network: "eth-mainnet",
|
|
701
|
+
verbose: "false",
|
|
702
|
+
x402: "false"
|
|
703
|
+
};
|
|
704
|
+
const normalizedKey = KEY_MAP[key] ?? key;
|
|
705
|
+
const defaultValue = defaults[normalizedKey] ?? defaults[key];
|
|
706
|
+
if (defaultValue !== void 0) {
|
|
707
|
+
value = defaultValue;
|
|
708
|
+
isDefault = true;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
597
711
|
if (value === void 0) {
|
|
598
712
|
exitWithError(errNotFound(`config key '${key}'`));
|
|
599
713
|
}
|
|
600
714
|
const isSecret = key === "api-key" || key === "api_key" || key === "access-key" || key === "access_key";
|
|
601
715
|
const display = isSecret ? maskIf(value) : value;
|
|
602
|
-
|
|
716
|
+
const humanDisplay = isDefault ? `${display} ${dim("(default)")}` : display;
|
|
717
|
+
printHuman(humanDisplay + "\n", { key, value: display, ...isDefault && { default: true } });
|
|
603
718
|
});
|
|
604
719
|
cmd.command("list").description("List all config values").action(() => {
|
|
605
720
|
const cfg = load();
|
|
@@ -761,6 +876,12 @@ var Client = class _Client {
|
|
|
761
876
|
}
|
|
762
877
|
return null;
|
|
763
878
|
}
|
|
879
|
+
verboseLog(message) {
|
|
880
|
+
if (verbose) {
|
|
881
|
+
process.stderr.write(`[verbose] ${message}
|
|
882
|
+
`);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
764
885
|
async doFetch(url, init) {
|
|
765
886
|
return fetchWithTimeout(url, init);
|
|
766
887
|
}
|
|
@@ -771,6 +892,14 @@ var Client = class _Client {
|
|
|
771
892
|
params,
|
|
772
893
|
id: 1
|
|
773
894
|
};
|
|
895
|
+
const redactedURL = redactSensitiveText(this.rpcURL());
|
|
896
|
+
this.verboseLog(`\u2192 POST ${redactedURL}`);
|
|
897
|
+
this.verboseLog(` method: ${method}`);
|
|
898
|
+
const hasParams = Array.isArray(params) ? params.length > 0 : Object.keys(params).length > 0;
|
|
899
|
+
if (hasParams) {
|
|
900
|
+
this.verboseLog(` params: ${JSON.stringify(params)}`);
|
|
901
|
+
}
|
|
902
|
+
const startTime = Date.now();
|
|
774
903
|
const resp = await this.doFetch(this.rpcURL(), {
|
|
775
904
|
method: "POST",
|
|
776
905
|
headers: {
|
|
@@ -779,6 +908,7 @@ var Client = class _Client {
|
|
|
779
908
|
},
|
|
780
909
|
body: JSON.stringify(body)
|
|
781
910
|
});
|
|
911
|
+
this.verboseLog(`\u2190 ${resp.status} ${resp.statusText} (${Date.now() - startTime}ms)`);
|
|
782
912
|
if (resp.status === 429) throw errRateLimited();
|
|
783
913
|
if (resp.status === 401 || resp.status === 403) {
|
|
784
914
|
const detail = await resp.text().catch(() => "");
|
|
@@ -801,9 +931,13 @@ var Client = class _Client {
|
|
|
801
931
|
for (const [k, v] of Object.entries(params)) {
|
|
802
932
|
url.searchParams.set(k, v);
|
|
803
933
|
}
|
|
934
|
+
const redactedURL = redactSensitiveText(url.toString());
|
|
935
|
+
this.verboseLog(`\u2192 GET ${redactedURL}`);
|
|
936
|
+
const startTime = Date.now();
|
|
804
937
|
const resp = await this.doFetch(url.toString(), {
|
|
805
938
|
headers: { Accept: "application/json" }
|
|
806
939
|
});
|
|
940
|
+
this.verboseLog(`\u2190 ${resp.status} ${resp.statusText} (${Date.now() - startTime}ms)`);
|
|
807
941
|
if (resp.status === 429) throw errRateLimited();
|
|
808
942
|
if (resp.status === 401 || resp.status === 403) {
|
|
809
943
|
const detail = await resp.text().catch(() => "");
|
|
@@ -1008,13 +1142,13 @@ function resolveAccessKey(program, cfg) {
|
|
|
1008
1142
|
if (config.access_key) return config.access_key;
|
|
1009
1143
|
return void 0;
|
|
1010
1144
|
}
|
|
1011
|
-
function resolveNetwork(program, cfg) {
|
|
1145
|
+
function resolveNetwork(program, cfg, defaultNetwork) {
|
|
1012
1146
|
const opts = program.opts();
|
|
1013
1147
|
if (opts.network) return opts.network;
|
|
1014
1148
|
if (process.env.ALCHEMY_NETWORK) return process.env.ALCHEMY_NETWORK;
|
|
1015
1149
|
const config = cfg ?? load();
|
|
1016
1150
|
if (config.network) return config.network;
|
|
1017
|
-
return "eth-mainnet";
|
|
1151
|
+
return defaultNetwork ?? "eth-mainnet";
|
|
1018
1152
|
}
|
|
1019
1153
|
function resolveAppId(program, cfg) {
|
|
1020
1154
|
const opts = program.opts();
|
|
@@ -1048,10 +1182,16 @@ function resolveWalletKey(program, cfg) {
|
|
|
1048
1182
|
}
|
|
1049
1183
|
return void 0;
|
|
1050
1184
|
}
|
|
1051
|
-
function clientFromFlags(program) {
|
|
1185
|
+
function clientFromFlags(program, opts) {
|
|
1052
1186
|
const cfg = load();
|
|
1053
|
-
const network = resolveNetwork(program, cfg);
|
|
1187
|
+
const network = resolveNetwork(program, cfg, opts?.defaultNetwork);
|
|
1054
1188
|
debug(`using network=${network}`);
|
|
1189
|
+
const programOpts = program.opts();
|
|
1190
|
+
if (programOpts.accessKey) {
|
|
1191
|
+
throw errInvalidArgs(
|
|
1192
|
+
"--access-key is for admin commands (apps, chains, webhooks). Use --api-key for RPC commands."
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1055
1195
|
if (resolveX402(program, cfg)) {
|
|
1056
1196
|
const walletKey = resolveWalletKey(program, cfg);
|
|
1057
1197
|
if (!walletKey) throw errWalletKeyRequired();
|
|
@@ -1139,7 +1279,7 @@ function registerWallet(program) {
|
|
|
1139
1279
|
exitWithError(err);
|
|
1140
1280
|
}
|
|
1141
1281
|
});
|
|
1142
|
-
cmd.command("import
|
|
1282
|
+
cmd.command("import").argument("<path>", "Path to private key file").description("Import a wallet from a private key file").action((path) => {
|
|
1143
1283
|
try {
|
|
1144
1284
|
const wallet = importAndPersistWallet(path);
|
|
1145
1285
|
if (isJSONMode()) {
|
|
@@ -1177,6 +1317,7 @@ export {
|
|
|
1177
1317
|
splitCommaList,
|
|
1178
1318
|
readStdinArg,
|
|
1179
1319
|
validateAddress,
|
|
1320
|
+
resolveAddress,
|
|
1180
1321
|
validateTxHash,
|
|
1181
1322
|
selectOrCreateApp,
|
|
1182
1323
|
registerConfig,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
3
|
import {
|
|
4
4
|
isInteractiveAllowed
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6XTLILDF.js";
|
|
6
6
|
|
|
7
7
|
// src/lib/networks.ts
|
|
8
8
|
var TESTNET_TOKEN_RE = /(testnet|sepolia|holesky|hoodi|devnet|minato|amoy|fuji|saigon|cardona|aeneid|curtis|chiado|cassiopeia|blaze|ropsten|signet|mocha|fam|bepolia)$/i;
|
|
@@ -227,6 +227,42 @@ function getRPCNetworks() {
|
|
|
227
227
|
function getRPCNetworkIds() {
|
|
228
228
|
return [...RPC_NETWORK_IDS];
|
|
229
229
|
}
|
|
230
|
+
var NATIVE_TOKEN_SYMBOLS = {
|
|
231
|
+
eth: "ETH",
|
|
232
|
+
arb: "ETH",
|
|
233
|
+
arbnova: "ETH",
|
|
234
|
+
opt: "ETH",
|
|
235
|
+
base: "ETH",
|
|
236
|
+
zksync: "ETH",
|
|
237
|
+
scroll: "ETH",
|
|
238
|
+
blast: "ETH",
|
|
239
|
+
linea: "ETH",
|
|
240
|
+
zora: "ETH",
|
|
241
|
+
shape: "ETH",
|
|
242
|
+
polygon: "POL",
|
|
243
|
+
polygonzkevm: "ETH",
|
|
244
|
+
bnb: "BNB",
|
|
245
|
+
opbnb: "BNB",
|
|
246
|
+
avax: "AVAX",
|
|
247
|
+
solana: "SOL",
|
|
248
|
+
starknet: "ETH",
|
|
249
|
+
fantom: "FTM",
|
|
250
|
+
metis: "METIS",
|
|
251
|
+
mantle: "MNT",
|
|
252
|
+
celo: "CELO",
|
|
253
|
+
gnosis: "xDAI",
|
|
254
|
+
frax: "frxETH",
|
|
255
|
+
worldchain: "ETH",
|
|
256
|
+
berachain: "BERA",
|
|
257
|
+
flow: "FLOW",
|
|
258
|
+
rootstock: "RBTC",
|
|
259
|
+
zetachain: "ZETA",
|
|
260
|
+
sui: "SUI"
|
|
261
|
+
};
|
|
262
|
+
function nativeTokenSymbol(networkId) {
|
|
263
|
+
const prefix = networkId.replace(/-(mainnet|testnet|sepolia|holesky|hoodi|devnet|amoy|fuji|cardona|saigon|chiado|signet|mocha|blaze|curtis|bepolia).*$/, "");
|
|
264
|
+
return NATIVE_TOKEN_SYMBOLS[prefix] ?? "ETH";
|
|
265
|
+
}
|
|
230
266
|
|
|
231
267
|
// src/lib/onboarding.ts
|
|
232
268
|
function hasAPIKey(cfg) {
|
|
@@ -275,6 +311,7 @@ function shouldRunOnboarding(program, cfg) {
|
|
|
275
311
|
export {
|
|
276
312
|
getRPCNetworks,
|
|
277
313
|
getRPCNetworkIds,
|
|
314
|
+
nativeTokenSymbol,
|
|
278
315
|
getSetupMethod,
|
|
279
316
|
isSetupComplete,
|
|
280
317
|
getSetupStatus,
|