@alchemy/cli 0.2.1 → 0.3.0

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 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):** always use `--json` and prefer non-interactive execution (`--no-interactive`)
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 supported RPC networks | `alchemy network list --configured` |
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: formatted human output
325
- - Non-TTY: JSON output (script-friendly)
326
- - `-v`, `--version`: prints the CLI version
327
- - `--json`: forces JSON output in any context
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
 
@@ -326,8 +326,16 @@ function errNetwork(detail) {
326
326
  "Check your internet connection and try again."
327
327
  );
328
328
  }
329
+ var RPC_ERROR_HINTS = {
330
+ [-32700]: "Parse error. The request JSON is malformed.",
331
+ [-32600]: "Invalid request. Check the JSON-RPC request format.",
332
+ [-32601]: "Method not supported. Check the method name and ensure your plan supports it.",
333
+ [-32602]: "Invalid parameters. Check argument types and format.",
334
+ [-32603]: "Internal JSON-RPC error."
335
+ };
329
336
  function errRPC(code, message) {
330
- return new CLIError(ErrorCode.RPC_ERROR, `RPC error ${code}: ${message}`);
337
+ const hint = RPC_ERROR_HINTS[code];
338
+ return new CLIError(ErrorCode.RPC_ERROR, `RPC error ${code}: ${message}`, hint);
331
339
  }
332
340
  function errInvalidArgs(detail) {
333
341
  return new CLIError(ErrorCode.INVALID_ARGS, detail);
@@ -349,6 +357,14 @@ function errInvalidAccessKey() {
349
357
  "Get an access key: https://www.alchemy.com/docs/reference/admin-api/overview"
350
358
  );
351
359
  }
360
+ function errAccessDenied(detail) {
361
+ const message = detail ? `Access denied: ${detail}` : "Access denied. Your access key may not have permission for this operation.";
362
+ return new CLIError(
363
+ ErrorCode.INVALID_ACCESS_KEY,
364
+ message,
365
+ "Check your account tier and feature access at https://dashboard.alchemy.com/"
366
+ );
367
+ }
352
368
  function errAppRequired() {
353
369
  return new CLIError(
354
370
  ErrorCode.APP_REQUIRED,
@@ -631,8 +647,9 @@ async function runListPrompt(opts) {
631
647
  stdin.setRawMode(previousRawMode);
632
648
  stdin.removeListener("keypress", onKeypress);
633
649
  restoreKeypressListeners();
634
- stdin.pause();
635
- stdin.unref?.();
650
+ if (!previousRawMode) {
651
+ stdin.pause();
652
+ }
636
653
  };
637
654
  const commitSingleLine = (text) => {
638
655
  if (opts.commitLabel === null) return;
@@ -1093,7 +1110,7 @@ function semverLT(a, b) {
1093
1110
  return false;
1094
1111
  }
1095
1112
  function currentVersion() {
1096
- return true ? "0.2.1" : "0.0.0";
1113
+ return true ? "0.3.0" : "0.0.0";
1097
1114
  }
1098
1115
  function toUpdateStatus(latestVersion, checkedAt) {
1099
1116
  const current = currentVersion();
@@ -1156,6 +1173,7 @@ export {
1156
1173
  esc,
1157
1174
  rgb,
1158
1175
  bgRgb,
1176
+ redactSensitiveText,
1159
1177
  quiet,
1160
1178
  verbose,
1161
1179
  timeout,
@@ -1178,6 +1196,7 @@ export {
1178
1196
  errNotFound,
1179
1197
  errRateLimited,
1180
1198
  errInvalidAccessKey,
1199
+ errAccessDenied,
1181
1200
  errAppRequired,
1182
1201
  errWalletKeyRequired,
1183
1202
  errAdminAPI,
@@ -1197,6 +1216,7 @@ export {
1197
1216
  promptAutocomplete,
1198
1217
  promptMultiselect,
1199
1218
  green,
1219
+ red,
1200
1220
  dim,
1201
1221
  bold,
1202
1222
  yellow,
@@ -7,6 +7,7 @@ import {
7
7
  configDir,
8
8
  debug,
9
9
  dim,
10
+ errAccessDenied,
10
11
  errAccessKeyRequired,
11
12
  errAdminAPI,
12
13
  errAppRequired,
@@ -35,12 +36,14 @@ import {
35
36
  promptMultiselect,
36
37
  promptSelect,
37
38
  promptText,
39
+ redactSensitiveText,
38
40
  save,
39
41
  timeout,
40
42
  toMap,
43
+ verbose,
41
44
  withSpinner,
42
45
  yellow
43
- } from "./chunk-SDUCDSCZ.js";
46
+ } from "./chunk-MF6DXNO7.js";
44
47
 
45
48
  // src/lib/client-utils.ts
46
49
  function isLocalhost(hostname) {
@@ -72,17 +75,39 @@ function parseBaseURLOverride(envVarName) {
72
75
  }
73
76
  return parsed;
74
77
  }
78
+ var BREADCRUMB_HEADER = "alchemy-cli";
75
79
  async function fetchWithTimeout(url, init) {
76
80
  try {
77
81
  return await fetch(url, {
78
82
  ...init,
83
+ headers: {
84
+ ...init.headers,
85
+ "x-alchemy-client-breadcrumb": BREADCRUMB_HEADER
86
+ },
79
87
  ...timeout && { signal: AbortSignal.timeout(timeout) }
80
88
  });
81
89
  } catch (err) {
82
90
  if (err instanceof DOMException && err.name === "TimeoutError") {
83
91
  throw errNetwork(`Request timed out after ${timeout}ms`);
84
92
  }
85
- throw errNetwork(err.message);
93
+ const message = err.message ?? String(err);
94
+ const causeMessage = err.cause?.message ?? "";
95
+ const causeCode = err.cause?.code ?? "";
96
+ const fullErrorText = `${message} ${causeMessage} ${causeCode}`;
97
+ if (/ENOTFOUND|EAI_AGAIN|getaddrinfo/i.test(fullErrorText)) {
98
+ try {
99
+ const hostname = new URL(url).hostname;
100
+ const networkSlug = hostname.replace(/\.g\.alchemy\.com$/, "");
101
+ if (networkSlug !== hostname) {
102
+ throw errInvalidArgs(
103
+ `Unknown network '${networkSlug}'. Run 'alchemy network list' to see available networks.`
104
+ );
105
+ }
106
+ } catch (innerErr) {
107
+ if (innerErr instanceof CLIError) throw innerErr;
108
+ }
109
+ }
110
+ throw errNetwork(message);
86
111
  }
87
112
  }
88
113
 
@@ -145,7 +170,18 @@ var AdminClient = class _AdminClient {
145
170
  },
146
171
  ...body !== void 0 && { body: JSON.stringify(body) }
147
172
  });
148
- if (resp.status === 401 || resp.status === 403) throw errInvalidAccessKey();
173
+ if (resp.status === 401) throw errInvalidAccessKey();
174
+ if (resp.status === 403) {
175
+ const detail = await resp.text().catch(() => "");
176
+ let reason;
177
+ try {
178
+ const parsed = JSON.parse(detail);
179
+ reason = parsed?.message || parsed?.error?.message || parsed?.error || void 0;
180
+ } catch {
181
+ reason = detail || void 0;
182
+ }
183
+ throw errAccessDenied(typeof reason === "string" ? reason : void 0);
184
+ }
149
185
  if (resp.status === 404) {
150
186
  const text = await resp.text().catch(() => "");
151
187
  throw errNotFound(text || path);
@@ -240,6 +276,92 @@ var AdminClient = class _AdminClient {
240
276
  }
241
277
  };
242
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
+
243
365
  // src/lib/validators.ts
244
366
  function splitCommaList(input) {
245
367
  return input.split(",").map((s) => s.trim()).filter(Boolean);
@@ -268,6 +390,13 @@ function validateAddress(address) {
268
390
  );
269
391
  }
270
392
  }
393
+ async function resolveAddress(input, client) {
394
+ if (isENSName(input)) {
395
+ return resolveENS(input, client);
396
+ }
397
+ validateAddress(input);
398
+ return input;
399
+ }
271
400
  function validateTxHash(hash) {
272
401
  if (!TX_HASH_RE.test(hash)) {
273
402
  throw errInvalidArgs(
@@ -522,13 +651,13 @@ function registerConfig(program) {
522
651
  if (normalized !== "true" && normalized !== "false") {
523
652
  throw errInvalidArgs("verbose must be 'true' or 'false'");
524
653
  }
525
- const verbose = normalized === "true";
654
+ const verbose2 = normalized === "true";
526
655
  const cfg = load();
527
- save({ ...cfg, verbose });
656
+ save({ ...cfg, verbose: verbose2 });
528
657
  printHuman(
529
- `${green("\u2713")} Set verbose default to ${verbose}
658
+ `${green("\u2713")} Set verbose default to ${verbose2}
530
659
  `,
531
- { key: "verbose", value: String(verbose), status: "set" }
660
+ { key: "verbose", value: String(verbose2), status: "set" }
532
661
  );
533
662
  } catch (err) {
534
663
  exitWithError(err);
@@ -564,13 +693,28 @@ function registerConfig(program) {
564
693
  });
565
694
  cmd.command("get <key>").description("Get a config value (api-key, access-key, app, network, verbose, wallet-key-file, x402)").action((key) => {
566
695
  const cfg = load();
567
- const value = get(cfg, key);
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
+ }
568
711
  if (value === void 0) {
569
712
  exitWithError(errNotFound(`config key '${key}'`));
570
713
  }
571
714
  const isSecret = key === "api-key" || key === "api_key" || key === "access-key" || key === "access_key";
572
715
  const display = isSecret ? maskIf(value) : value;
573
- printHuman(display + "\n", { key, value: display });
716
+ const humanDisplay = isDefault ? `${display} ${dim("(default)")}` : display;
717
+ printHuman(humanDisplay + "\n", { key, value: display, ...isDefault && { default: true } });
574
718
  });
575
719
  cmd.command("list").description("List all config values").action(() => {
576
720
  const cfg = load();
@@ -686,10 +830,14 @@ var Client = class _Client {
686
830
  try {
687
831
  parsed = new URL(`https://${hostname}`);
688
832
  } catch {
689
- throw errInvalidArgs(`Invalid network: ${network}`);
833
+ throw errInvalidArgs(
834
+ `Unknown network '${network}'. Run 'alchemy network list' to see available networks.`
835
+ );
690
836
  }
691
837
  if (!parsed.hostname.endsWith(".g.alchemy.com")) {
692
- throw errInvalidArgs(`Invalid network: ${network} \u2014 hostname must end with .g.alchemy.com`);
838
+ throw errInvalidArgs(
839
+ `Unknown network '${network}'. Run 'alchemy network list' to see available networks.`
840
+ );
693
841
  }
694
842
  }
695
843
  rpcBaseURLOverride() {
@@ -718,6 +866,22 @@ var Client = class _Client {
718
866
  if (networkNotEnabled) return networkNotEnabled;
719
867
  return errInvalidAPIKey(detail || void 0);
720
868
  }
869
+ tryParseRPCError(text) {
870
+ try {
871
+ const parsed = JSON.parse(text);
872
+ if (parsed?.error?.code !== void 0 && parsed?.error?.message !== void 0) {
873
+ return errRPC(parsed.error.code, parsed.error.message);
874
+ }
875
+ } catch {
876
+ }
877
+ return null;
878
+ }
879
+ verboseLog(message) {
880
+ if (verbose) {
881
+ process.stderr.write(`[verbose] ${message}
882
+ `);
883
+ }
884
+ }
721
885
  async doFetch(url, init) {
722
886
  return fetchWithTimeout(url, init);
723
887
  }
@@ -728,6 +892,14 @@ var Client = class _Client {
728
892
  params,
729
893
  id: 1
730
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();
731
903
  const resp = await this.doFetch(this.rpcURL(), {
732
904
  method: "POST",
733
905
  headers: {
@@ -736,6 +908,7 @@ var Client = class _Client {
736
908
  },
737
909
  body: JSON.stringify(body)
738
910
  });
911
+ this.verboseLog(`\u2190 ${resp.status} ${resp.statusText} (${Date.now() - startTime}ms)`);
739
912
  if (resp.status === 429) throw errRateLimited();
740
913
  if (resp.status === 401 || resp.status === 403) {
741
914
  const detail = await resp.text().catch(() => "");
@@ -743,6 +916,8 @@ var Client = class _Client {
743
916
  }
744
917
  if (!resp.ok) {
745
918
  const text = await resp.text().catch(() => "");
919
+ const rpcError = this.tryParseRPCError(text);
920
+ if (rpcError) throw rpcError;
746
921
  throw errNetwork(`HTTP ${resp.status}: ${text}`);
747
922
  }
748
923
  const rpcResp = await resp.json();
@@ -756,9 +931,13 @@ var Client = class _Client {
756
931
  for (const [k, v] of Object.entries(params)) {
757
932
  url.searchParams.set(k, v);
758
933
  }
934
+ const redactedURL = redactSensitiveText(url.toString());
935
+ this.verboseLog(`\u2192 GET ${redactedURL}`);
936
+ const startTime = Date.now();
759
937
  const resp = await this.doFetch(url.toString(), {
760
938
  headers: { Accept: "application/json" }
761
939
  });
940
+ this.verboseLog(`\u2190 ${resp.status} ${resp.statusText} (${Date.now() - startTime}ms)`);
762
941
  if (resp.status === 429) throw errRateLimited();
763
942
  if (resp.status === 401 || resp.status === 403) {
764
943
  const detail = await resp.text().catch(() => "");
@@ -766,6 +945,8 @@ var Client = class _Client {
766
945
  }
767
946
  if (!resp.ok) {
768
947
  const text = await resp.text().catch(() => "");
948
+ const rpcError = this.tryParseRPCError(text);
949
+ if (rpcError) throw rpcError;
769
950
  throw errNetwork(`HTTP ${resp.status}: ${text}`);
770
951
  }
771
952
  return resp.json();
@@ -961,13 +1142,13 @@ function resolveAccessKey(program, cfg) {
961
1142
  if (config.access_key) return config.access_key;
962
1143
  return void 0;
963
1144
  }
964
- function resolveNetwork(program, cfg) {
1145
+ function resolveNetwork(program, cfg, defaultNetwork) {
965
1146
  const opts = program.opts();
966
1147
  if (opts.network) return opts.network;
967
1148
  if (process.env.ALCHEMY_NETWORK) return process.env.ALCHEMY_NETWORK;
968
1149
  const config = cfg ?? load();
969
1150
  if (config.network) return config.network;
970
- return "eth-mainnet";
1151
+ return defaultNetwork ?? "eth-mainnet";
971
1152
  }
972
1153
  function resolveAppId(program, cfg) {
973
1154
  const opts = program.opts();
@@ -1001,10 +1182,16 @@ function resolveWalletKey(program, cfg) {
1001
1182
  }
1002
1183
  return void 0;
1003
1184
  }
1004
- function clientFromFlags(program) {
1185
+ function clientFromFlags(program, opts) {
1005
1186
  const cfg = load();
1006
- const network = resolveNetwork(program, cfg);
1187
+ const network = resolveNetwork(program, cfg, opts?.defaultNetwork);
1007
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
+ }
1008
1195
  if (resolveX402(program, cfg)) {
1009
1196
  const walletKey = resolveWalletKey(program, cfg);
1010
1197
  if (!walletKey) throw errWalletKeyRequired();
@@ -1092,7 +1279,7 @@ function registerWallet(program) {
1092
1279
  exitWithError(err);
1093
1280
  }
1094
1281
  });
1095
- cmd.command("import <path>").description("Import a wallet from a private key file").action((path) => {
1282
+ cmd.command("import").argument("<path>", "Path to private key file").description("Import a wallet from a private key file").action((path) => {
1096
1283
  try {
1097
1284
  const wallet = importAndPersistWallet(path);
1098
1285
  if (isJSONMode()) {
@@ -1130,6 +1317,7 @@ export {
1130
1317
  splitCommaList,
1131
1318
  readStdinArg,
1132
1319
  validateAddress,
1320
+ resolveAddress,
1133
1321
  validateTxHash,
1134
1322
  selectOrCreateApp,
1135
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-SDUCDSCZ.js";
5
+ } from "./chunk-MF6DXNO7.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,