@alchemy/cli 0.9.3 → 0.11.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/dist/index.js CHANGED
@@ -5,26 +5,27 @@ import {
5
5
  errNotLoggedInForPolicyLookup,
6
6
  errSponsorshipNeedsPolicy,
7
7
  selectOrCreatePolicy
8
- } from "./chunk-D2RUM2DD.js";
8
+ } from "./chunk-KIYIW6SX.js";
9
9
  import {
10
10
  registerAuth
11
- } from "./chunk-L5E7GEUU.js";
11
+ } from "./chunk-XZS6KZHN.js";
12
12
  import {
13
13
  openBrowser
14
- } from "./chunk-AMGGO36F.js";
14
+ } from "./chunk-I6YQX7PF.js";
15
15
  import {
16
16
  SETUP_CAPABILITY_LABELS,
17
17
  SETUP_CAPABILITY_ORDER,
18
18
  getSetupStatus,
19
19
  isSetupComplete,
20
20
  shouldRunOnboarding
21
- } from "./chunk-VN5JUWHO.js";
21
+ } from "./chunk-CSBYGYG6.js";
22
22
  import {
23
23
  isInteractiveAllowed
24
- } from "./chunk-3GBDYROJ.js";
24
+ } from "./chunk-RPSHRYCZ.js";
25
25
  import {
26
26
  RpcApiError,
27
27
  adminClientFromFlags,
28
+ apiKeyClientFromFlags,
28
29
  clearSession,
29
30
  clientFromFlags,
30
31
  createPendingSession,
@@ -47,6 +48,8 @@ import {
47
48
  resolveGasPolicyId,
48
49
  resolveGasSponsored,
49
50
  resolveNetwork,
51
+ resolveOptionalNetwork,
52
+ resolveRequiredNetwork,
50
53
  resolveSolanaFeePolicyId,
51
54
  resolveSolanaFeeSponsored,
52
55
  resolveSolanaNetwork,
@@ -60,12 +63,12 @@ import {
60
63
  updateSession,
61
64
  validateNetwork,
62
65
  walletNetworkToChain
63
- } from "./chunk-ANONMDDZ.js";
66
+ } from "./chunk-B5KVL3ZR.js";
64
67
  import {
65
68
  getAvailableUpdate,
66
69
  getUpdateStatus,
67
70
  printUpdateNotice
68
- } from "./chunk-NUSUQI7X.js";
71
+ } from "./chunk-4QSVWWSK.js";
69
72
  import {
70
73
  bold,
71
74
  brand,
@@ -89,7 +92,7 @@ import {
89
92
  weiToEth,
90
93
  withSpinner,
91
94
  yellow
92
- } from "./chunk-PMNRIXJI.js";
95
+ } from "./chunk-DXQAGBW6.js";
93
96
  import {
94
97
  KEY_MAP,
95
98
  configDir,
@@ -100,7 +103,7 @@ import {
100
103
  save,
101
104
  toMap,
102
105
  validKeys
103
- } from "./chunk-GLKB4JM7.js";
106
+ } from "./chunk-LANOFNO6.js";
104
107
  import {
105
108
  CLIError,
106
109
  EXIT_CODES,
@@ -120,6 +123,7 @@ import {
120
123
  errSolanaTransactionFailed,
121
124
  errSolanaWalletKeyRequired,
122
125
  errWalletKeyRequired,
126
+ errWalletRequired,
123
127
  esc,
124
128
  exitWithError,
125
129
  fetchWithTimeout,
@@ -138,7 +142,7 @@ import {
138
142
  setNoColor,
139
143
  timeout,
140
144
  verbose
141
- } from "./chunk-CTTW4PA4.js";
145
+ } from "./chunk-5BEJA752.js";
142
146
 
143
147
  // src/index.ts
144
148
  import { Command, Help } from "commander";
@@ -222,11 +226,11 @@ async function resolveENS(name, client) {
222
226
  if (dataHex.length < 64) {
223
227
  throw errInvalidArgs(`ENS name "${name}" could not be resolved.`);
224
228
  }
225
- const address2 = "0x" + dataHex.slice(24, 64);
226
- if (address2 === "0x0000000000000000000000000000000000000000") {
229
+ const address3 = "0x" + dataHex.slice(24, 64);
230
+ if (address3 === "0x0000000000000000000000000000000000000000") {
227
231
  throw errInvalidArgs(`ENS name "${name}" is not registered or has no address set.`);
228
232
  }
229
- return address2;
233
+ return address3;
230
234
  }
231
235
 
232
236
  // src/lib/validators.ts
@@ -267,10 +271,10 @@ async function readStdinLines(name) {
267
271
  }
268
272
  return lines;
269
273
  }
270
- function validateAddress(address2) {
271
- if (!ADDRESS_RE.test(address2)) {
274
+ function validateAddress(address3) {
275
+ if (!ADDRESS_RE.test(address3)) {
272
276
  throw errInvalidArgs(
273
- `Invalid address "${address2}". Expected 0x-prefixed 40-hex-character address.`
277
+ `Invalid address "${address3}". Expected 0x-prefixed 40-hex-character address.`
274
278
  );
275
279
  }
276
280
  }
@@ -497,16 +501,6 @@ function registerConfig(program2) {
497
501
  exitWithError(err);
498
502
  }
499
503
  });
500
- setCmd.command("network <network>").description("Set the default network (e.g. eth-mainnet, polygon-mainnet)").action((network) => {
501
- try {
502
- const cfg = load();
503
- save({ ...cfg, network });
504
- printHuman(`${green("\u2713")} Set network to ${network}
505
- `, { key: "network", value: network, status: "set" });
506
- } catch (err) {
507
- exitWithError(err);
508
- }
509
- });
510
504
  setCmd.command("verbose <enabled>").description("Set default verbose output (true|false)").action((enabled) => {
511
505
  try {
512
506
  const normalized = enabled.trim().toLowerCase();
@@ -585,8 +579,8 @@ function registerConfig(program2) {
585
579
  "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set evm-gas-policy-id <id>`."
586
580
  );
587
581
  }
588
- const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-V6W7CPAO.js");
589
- const { resolveNetwork: resolveNetwork2 } = await import("./resolve-REZCFZZ7.js");
582
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-3EVE7DW3.js");
583
+ const { resolveNetwork: resolveNetwork2 } = await import("./resolve-FRMIY357.js");
590
584
  const network = resolveNetwork2(program2);
591
585
  await selectOrCreatePolicy2({
592
586
  flavor: "sponsorship",
@@ -640,8 +634,8 @@ function registerConfig(program2) {
640
634
  "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set solana-fee-policy-id <id>`."
641
635
  );
642
636
  }
643
- const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-V6W7CPAO.js");
644
- const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-REZCFZZ7.js");
637
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-3EVE7DW3.js");
638
+ const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-FRMIY357.js");
645
639
  const network = resolveSolanaNetwork2(program2);
646
640
  await selectOrCreatePolicy2({
647
641
  flavor: "solana",
@@ -663,13 +657,12 @@ function registerConfig(program2) {
663
657
  exitWithError(err);
664
658
  }
665
659
  });
666
- cmd.command("get <key>").description("Get a config value (api-key, app, network, verbose, wallet-key-file, x402)").action((key) => {
660
+ cmd.command("get <key>").description("Get a config value (api-key, app, verbose, wallet-key-file, x402)").action((key) => {
667
661
  const cfg = load();
668
662
  let value = get(cfg, key);
669
663
  let isDefault = false;
670
664
  if (value === void 0) {
671
665
  const defaults = {
672
- network: "eth-mainnet",
673
666
  verbose: "false",
674
667
  x402: "false",
675
668
  evm_gas_sponsored: "false",
@@ -699,7 +692,7 @@ function registerConfig(program2) {
699
692
  printJSON(toMap(cfg));
700
693
  return;
701
694
  }
702
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-REZCFZZ7.js");
695
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-FRMIY357.js");
703
696
  const validToken = resolveAuthToken2(cfg);
704
697
  const authStatus = cfg.auth_token ? validToken ? `${green("\u2713")} authenticated${cfg.auth_token_expires_at ? ` ${dim(`(expires ${cfg.auth_token_expires_at})`)}` : ""}` : `${yellow("\u25C6")} expired${cfg.auth_token_expires_at ? ` ${dim(`(${cfg.auth_token_expires_at})`)}` : ""}` : dim("(not set) \u2014 run 'alchemy auth' to log in");
705
698
  const pairs = [
@@ -713,7 +706,6 @@ function registerConfig(program2) {
713
706
  "app",
714
707
  cfg.app ? `${cfg.app.name} ${dim(`(${cfg.app.id})`)}` : dim("(not set) \u2014 set automatically via 'alchemy auth' or 'config set app'")
715
708
  ],
716
- ["network", cfg.network || dim("(not set, defaults to eth-mainnet)")],
717
709
  [
718
710
  "verbose",
719
711
  cfg.verbose !== void 0 ? String(cfg.verbose) : dim("(not set, defaults to false)")
@@ -2034,14 +2026,14 @@ function formatWalletStatus(value) {
2034
2026
  function walletKeysDirPath() {
2035
2027
  return join(configDir(), WALLET_KEYS_DIR);
2036
2028
  }
2037
- function walletKeyPath(prefix, address2) {
2038
- const addr = address2.trim().toLowerCase().replace(/^0x/, "").replace(/[^a-z0-9]/g, "").slice(0, ADDRESS_SLICE_LEN);
2029
+ function walletKeyPath(prefix, address3) {
2030
+ const addr = address3.trim().toLowerCase().replace(/^0x/, "").replace(/[^a-z0-9]/g, "").slice(0, ADDRESS_SLICE_LEN);
2039
2031
  const addressTag = addr || "unknown";
2040
2032
  const fileName = `${prefix}-${addressTag}-${Date.now()}-${randomUUID().slice(0, UUID_SLICE_LEN)}.txt`;
2041
2033
  return join(walletKeysDirPath(), fileName);
2042
2034
  }
2043
- function persistWalletKey(prefix, privateKey, address2) {
2044
- const keyPath = walletKeyPath(prefix, address2);
2035
+ function persistWalletKey(prefix, privateKey, address3) {
2036
+ const keyPath = walletKeyPath(prefix, address3);
2045
2037
  mkdirSync(dirname(keyPath), { recursive: true, mode: 493 });
2046
2038
  writeFileSync(keyPath, privateKey + "\n", { mode: 384, flag: "wx" });
2047
2039
  return keyPath;
@@ -2152,11 +2144,11 @@ function importAndPersistWallet(path) {
2152
2144
  } catch {
2153
2145
  throw errInvalidArgs(`Could not read key file: ${path}`);
2154
2146
  }
2155
- const address2 = getEvmWalletAddress(key);
2156
- const keyPath = persistWalletKey("wallet-key", key, address2);
2147
+ const address3 = getEvmWalletAddress(key);
2148
+ const keyPath = persistWalletKey("wallet-key", key, address3);
2157
2149
  const cfg = load();
2158
- save({ ...cfg, wallet_key_file: keyPath, wallet_address: address2 });
2159
- return { address: address2, keyFile: keyPath };
2150
+ save({ ...cfg, wallet_key_file: keyPath, wallet_address: address3 });
2151
+ return { address: address3, keyFile: keyPath };
2160
2152
  }
2161
2153
  function hasValidSessionWithEvm() {
2162
2154
  const session = resolveWalletSession();
@@ -2596,21 +2588,20 @@ async function runConnectFlow(program2, opts) {
2596
2588
  }
2597
2589
  if (!mode) {
2598
2590
  if (!isInteractiveAllowed(program2)) {
2599
- throw errInvalidArgs(
2600
- "Specify `--mode <session|local>` in non-interactive mode."
2601
- );
2591
+ mode = "session";
2592
+ } else {
2593
+ const choice = await promptSelect({
2594
+ message: "Choose a wallet to connect",
2595
+ options: [
2596
+ { value: "session", label: "Session wallet", hint: "Recommended \u2014 Alchemy-managed, more secure" },
2597
+ { value: "local", label: "Local wallet", hint: "Private key stored on this machine" }
2598
+ ],
2599
+ initialValue: "session",
2600
+ cancelMessage: "Wallet connect cancelled."
2601
+ });
2602
+ if (choice === null) throw new WalletConnectInterruptedError();
2603
+ mode = choice;
2602
2604
  }
2603
- const choice = await promptSelect({
2604
- message: "Choose a wallet to connect",
2605
- options: [
2606
- { value: "session", label: "Session wallet", hint: "Recommended \u2014 Alchemy-managed, more secure" },
2607
- { value: "local", label: "Local wallet", hint: "Private key stored on this machine" }
2608
- ],
2609
- initialValue: "session",
2610
- cancelMessage: "Wallet connect cancelled."
2611
- });
2612
- if (choice === null) throw new WalletConnectInterruptedError();
2613
- mode = choice;
2614
2605
  }
2615
2606
  if (mode === "session") {
2616
2607
  await runSessionConnect({
@@ -3312,7 +3303,7 @@ function registerNetwork(program2) {
3312
3303
  cmd.command("list").description("List RPC network IDs for use with --network (e.g. eth-mainnet)").option("--mainnet-only", "Show only mainnet networks").option("--testnet-only", "Show only testnet networks").option("--search <term>", "Filter networks by name or ID").action(async (opts) => {
3313
3304
  try {
3314
3305
  let display = getRPCNetworks();
3315
- const current = resolveNetwork(program2);
3306
+ const current = resolveOptionalNetwork(program2);
3316
3307
  if (opts.mainnetOnly) {
3317
3308
  display = display.filter((n) => !n.isTestnet);
3318
3309
  } else if (opts.testnetOnly) {
@@ -3329,15 +3320,17 @@ function registerNetwork(program2) {
3329
3320
  return;
3330
3321
  }
3331
3322
  const rows = display.map((network) => {
3332
- const isCurrent = network.id === current;
3323
+ const isCurrent = current !== void 0 && network.id === current;
3333
3324
  const idCell = isCurrent ? green(network.id) : network.id;
3334
3325
  const nameCell = isCurrent ? green(network.name) : network.name;
3335
3326
  const testnetCell = network.isTestnet ? dim("yes") : "no";
3336
3327
  return [idCell, nameCell, network.family, testnetCell];
3337
3328
  });
3338
3329
  printTable(["Network ID", "Name", "Family", "Testnet"], rows);
3339
- console.log(`
3330
+ if (current) {
3331
+ console.log(`
3340
3332
  Current: ${green(current)}`);
3333
+ }
3341
3334
  console.log(
3342
3335
  ` ${dim("Need Admin API chain identifiers (e.g. ETH_MAINNET)? See: app chains")}`
3343
3336
  );
@@ -3349,7 +3342,7 @@ function registerNetwork(program2) {
3349
3342
  printJSON({
3350
3343
  mode: "all",
3351
3344
  networks: display,
3352
- currentNetwork: current
3345
+ currentNetwork: current ?? null
3353
3346
  });
3354
3347
  }
3355
3348
  } catch (err) {
@@ -3401,7 +3394,7 @@ function formatTokenAmount(rawAmount, decimals) {
3401
3394
  return frac ? `${whole}.${frac}` : whole;
3402
3395
  }
3403
3396
  async function fetchTokenDecimals(program2, tokenAddress, opts) {
3404
- const client = clientFromFlags(
3397
+ const client = apiKeyClientFromFlags(
3405
3398
  program2,
3406
3399
  opts?.network ? { forceNetwork: opts.network } : void 0
3407
3400
  );
@@ -4127,15 +4120,176 @@ function printAccountSummary(args) {
4127
4120
 
4128
4121
  // src/commands/send-solana.ts
4129
4122
  import { address as solAddress2 } from "@solana/kit";
4123
+
4124
+ // src/lib/solana-token.ts
4125
+ import {
4126
+ AccountRole as AccountRole2,
4127
+ address as address2,
4128
+ getAddressEncoder,
4129
+ getProgramDerivedAddress
4130
+ } from "@solana/kit";
4131
+ var SPL_TOKEN_PROGRAM_ADDRESS2 = address2("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
4132
+ var SPL_TOKEN_2022_PROGRAM_ADDRESS = address2("TokenzQdBNbLqP5VEhdkAS6EPYdWnYARjfkM8GoyZ2G");
4133
+ var ASSOCIATED_TOKEN_PROGRAM_ADDRESS = address2("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
4134
+ var SYSTEM_PROGRAM_ADDRESS2 = address2("11111111111111111111111111111111");
4135
+ var SUPPORTED_TOKEN_PROGRAMS = /* @__PURE__ */ new Set([
4136
+ SPL_TOKEN_PROGRAM_ADDRESS2,
4137
+ SPL_TOKEN_2022_PROGRAM_ADDRESS
4138
+ ]);
4139
+ async function deriveAssociatedTokenAccount(args) {
4140
+ const addressEncoder = getAddressEncoder();
4141
+ const [associatedTokenAccount] = await getProgramDerivedAddress({
4142
+ programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
4143
+ seeds: [
4144
+ addressEncoder.encode(args.owner),
4145
+ addressEncoder.encode(args.tokenProgramAddress),
4146
+ addressEncoder.encode(args.mint)
4147
+ ]
4148
+ });
4149
+ return associatedTokenAccount;
4150
+ }
4151
+ async function fetchSplMintInfo(client, mint) {
4152
+ const account = await fetchParsedAccount(client, mint);
4153
+ if (!account) {
4154
+ throw errInvalidArgs(`SPL token mint ${mint} was not found on this network.`);
4155
+ }
4156
+ const tokenProgramAddress = address2(account.owner);
4157
+ assertSupportedTokenProgram(tokenProgramAddress);
4158
+ const data = parseAccountData(account.data);
4159
+ const decimals = data?.parsed?.info?.decimals;
4160
+ if (typeof decimals !== "number" || !Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
4161
+ throw errInvalidArgs(`Could not read decimals for SPL token mint ${mint}.`);
4162
+ }
4163
+ return {
4164
+ mint,
4165
+ decimals,
4166
+ tokenProgramAddress
4167
+ };
4168
+ }
4169
+ async function fetchSplTokenAccountInfo(client, tokenAccount) {
4170
+ const account = await fetchParsedAccount(client, tokenAccount);
4171
+ if (!account) return null;
4172
+ const tokenProgramAddress = address2(account.owner);
4173
+ assertSupportedTokenProgram(tokenProgramAddress);
4174
+ const data = parseAccountData(account.data);
4175
+ const info = data?.parsed?.info;
4176
+ if (!info || data?.parsed?.type !== "account") {
4177
+ throw errInvalidArgs(`Account ${tokenAccount} is not an SPL token account.`);
4178
+ }
4179
+ const mint = readAddressField(info, "mint", `Account ${tokenAccount} is missing token mint metadata.`);
4180
+ const owner = readAddressField(info, "owner", `Account ${tokenAccount} is missing token owner metadata.`);
4181
+ return {
4182
+ address: tokenAccount,
4183
+ mint,
4184
+ owner,
4185
+ tokenProgramAddress
4186
+ };
4187
+ }
4188
+ function buildCreateAssociatedTokenAccountIdempotentInstruction(args) {
4189
+ return {
4190
+ programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
4191
+ accounts: [
4192
+ { address: args.payer.address, role: AccountRole2.WRITABLE_SIGNER },
4193
+ { address: args.associatedTokenAccount, role: AccountRole2.WRITABLE },
4194
+ { address: args.owner, role: AccountRole2.READONLY },
4195
+ { address: args.mint, role: AccountRole2.READONLY },
4196
+ { address: SYSTEM_PROGRAM_ADDRESS2, role: AccountRole2.READONLY },
4197
+ { address: args.tokenProgramAddress, role: AccountRole2.READONLY }
4198
+ ],
4199
+ data: new Uint8Array([1])
4200
+ };
4201
+ }
4202
+ function buildSplTokenTransferCheckedInstruction(args) {
4203
+ if (!Number.isInteger(args.decimals) || args.decimals < 0 || args.decimals > 255) {
4204
+ throw errInvalidArgs("Token decimals must be an integer between 0 and 255.");
4205
+ }
4206
+ return {
4207
+ programAddress: args.tokenProgramAddress,
4208
+ accounts: [
4209
+ { address: args.sourceTokenAccount, role: AccountRole2.WRITABLE },
4210
+ { address: args.mint, role: AccountRole2.READONLY },
4211
+ { address: args.destinationTokenAccount, role: AccountRole2.WRITABLE },
4212
+ { address: args.owner.address, role: AccountRole2.READONLY_SIGNER }
4213
+ ],
4214
+ data: new Uint8Array([
4215
+ 12,
4216
+ ...encodeU64LE2(args.amount),
4217
+ args.decimals
4218
+ ])
4219
+ };
4220
+ }
4221
+ function assertTokenAccountMatches(args) {
4222
+ if (args.account.mint !== args.expectedMint) {
4223
+ throw errInvalidArgs(
4224
+ `${args.label} ${args.account.address} is for mint ${args.account.mint}, expected ${args.expectedMint}.`
4225
+ );
4226
+ }
4227
+ if (args.expectedOwner && args.account.owner !== args.expectedOwner) {
4228
+ throw errInvalidArgs(
4229
+ `${args.label} ${args.account.address} is owned by ${args.account.owner}, expected ${args.expectedOwner}.`
4230
+ );
4231
+ }
4232
+ }
4233
+ function assertSupportedTokenProgram(tokenProgramAddress) {
4234
+ if (!SUPPORTED_TOKEN_PROGRAMS.has(tokenProgramAddress)) {
4235
+ throw errInvalidArgs(
4236
+ `Unsupported Solana token program ${tokenProgramAddress}. Only SPL Token and Token-2022 mints are supported.`
4237
+ );
4238
+ }
4239
+ }
4240
+ async function fetchParsedAccount(client, accountAddress) {
4241
+ const result = await client.call("getAccountInfo", [
4242
+ accountAddress,
4243
+ { encoding: "jsonParsed" }
4244
+ ]);
4245
+ return result.value;
4246
+ }
4247
+ function parseAccountData(data) {
4248
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
4249
+ return data;
4250
+ }
4251
+ function readAddressField(info, field, errorMessage) {
4252
+ const value = info[field];
4253
+ if (typeof value !== "string") {
4254
+ throw errInvalidArgs(errorMessage);
4255
+ }
4256
+ return address2(value);
4257
+ }
4258
+ function encodeU64LE2(value) {
4259
+ if (value < 0n || value > 0xffffffffffffffffn) {
4260
+ throw errInvalidArgs("Amount must fit in an unsigned 64-bit integer.");
4261
+ }
4262
+ const bytes = new Uint8Array(8);
4263
+ let remaining = value;
4264
+ for (let i = 0; i < bytes.length; i++) {
4265
+ bytes[i] = Number(remaining & 0xffn);
4266
+ remaining >>= 8n;
4267
+ }
4268
+ return bytes;
4269
+ }
4270
+
4271
+ // src/commands/send-solana.ts
4130
4272
  function registerSolanaSend(program2) {
4131
- const sendCmd = program2.command("send <to> <amount>").description("Send SOL to an address").option("--token <address>", "SPL token contract address (not yet supported)").option("--fee-sponsored", "Enable Solana fee sponsorship").option("--fee-policy-id <id>", "Solana fee policy ID for sponsorship");
4273
+ const sendCmd = program2.command("send <to> <amount>").description("Send SOL or SPL tokens to an address").option("--token <mint>", "SPL token mint address (omit for native SOL)").option("--dry-run", "Preview transaction without signing or sending").option("--fail-if-associated-token-account-missing", "Fail if the recipient associated token account is missing").option("--from-token-account <address>", "Source SPL token account (defaults to sender associated token account)").option("--recipient-token-account <address>", "Destination SPL token account (defaults to recipient associated token account)").option("--fee-sponsored", "Enable Solana fee sponsorship").option("--fee-policy-id <id>", "Solana fee policy ID for sponsorship").addHelpText(
4274
+ "after",
4275
+ `
4276
+ Examples:
4277
+ alchemy solana send <pubkey> 0.1
4278
+ alchemy solana send <pubkey> 10 --token <mint>
4279
+ alchemy solana send <pubkey> 10 --token <mint> --dry-run
4280
+ alchemy solana send <pubkey> 10 --token <mint> --fail-if-associated-token-account-missing`
4281
+ );
4132
4282
  addSignerOption(sendCmd);
4133
4283
  sendCmd.action(
4134
4284
  async (toArg, amountArg, _opts, actionCommand) => {
4135
4285
  try {
4136
4286
  const opts = actionCommand.opts();
4137
4287
  await performSolanaSend(actionCommand, toArg, amountArg, opts.token, {
4138
- signer: parseSignerOpt(opts.signer)
4288
+ signer: parseSignerOpt(opts.signer),
4289
+ dryRun: opts.dryRun,
4290
+ failIfAssociatedTokenAccountMissing: opts.failIfAssociatedTokenAccountMissing,
4291
+ fromTokenAccount: opts.fromTokenAccount,
4292
+ recipientTokenAccount: opts.recipientTokenAccount
4139
4293
  });
4140
4294
  } catch (err) {
4141
4295
  exitWithError(err);
@@ -4144,75 +4298,378 @@ function registerSolanaSend(program2) {
4144
4298
  );
4145
4299
  }
4146
4300
  async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts = {}) {
4147
- if (tokenAddress) {
4148
- throw errInvalidArgs("SPL token transfers are not yet supported. Omit --token for native SOL transfers.");
4149
- }
4301
+ validateSolanaTokenOptions(tokenAddress, opts);
4150
4302
  const signer = await resolveSolanaSigner(program2, opts.signer);
4151
4303
  validateSolanaAddress(toArg);
4152
4304
  const to = solAddress2(toArg);
4153
4305
  const network = resolveSolanaNetwork(program2);
4154
- const symbol = nativeTokenSymbol(network);
4155
- const lamports = parseAmount(amountArg, SOL_DECIMALS);
4156
- const instruction = buildSolTransferInstruction(
4157
- { address: solAddress2(signer.address) },
4306
+ const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
4307
+ const client = clientFromFlags(program2, { forceNetwork: network });
4308
+ if (tokenAddress) {
4309
+ await performSplTokenSend({
4310
+ client,
4311
+ signer,
4312
+ to,
4313
+ toArg,
4314
+ amountArg,
4315
+ tokenAddress,
4316
+ network,
4317
+ sponsored,
4318
+ feePolicyId,
4319
+ opts
4320
+ });
4321
+ return;
4322
+ }
4323
+ await performNativeSolSend({
4324
+ client,
4325
+ signer,
4158
4326
  to,
4327
+ toArg,
4328
+ amountArg,
4329
+ network,
4330
+ sponsored,
4331
+ feePolicyId,
4332
+ dryRun: opts.dryRun
4333
+ });
4334
+ }
4335
+ async function performNativeSolSend(args) {
4336
+ const symbol = nativeTokenSymbol(args.network);
4337
+ const lamports = parseAmount(args.amountArg, SOL_DECIMALS);
4338
+ const instruction = buildSolTransferInstruction(
4339
+ { address: solAddress2(args.signer.address) },
4340
+ args.to,
4159
4341
  lamports
4160
4342
  );
4161
- const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
4162
- const client = clientFromFlags(program2, { forceNetwork: network });
4343
+ if (args.dryRun) {
4344
+ printNativeDryRun({
4345
+ from: args.signer.address,
4346
+ to: args.toArg,
4347
+ amount: args.amountArg,
4348
+ symbol,
4349
+ network: args.network,
4350
+ sponsored: args.sponsored
4351
+ });
4352
+ return;
4353
+ }
4163
4354
  const result = await withSpinner(
4164
4355
  "Sending transaction\u2026",
4165
4356
  "Transaction submitted",
4166
4357
  async () => {
4167
- if (signer.type === "session") {
4358
+ if (args.signer.type === "session") {
4168
4359
  return await buildAndSendSolanaTransactionWithSession({
4169
- client,
4360
+ client: args.client,
4170
4361
  instructions: [instruction],
4171
- session: signer.session,
4172
- authToken: signer.authToken,
4173
- sponsored,
4174
- gasPolicyId: feePolicyId
4362
+ session: args.signer.session,
4363
+ authToken: args.signer.authToken,
4364
+ sponsored: args.sponsored,
4365
+ gasPolicyId: args.feePolicyId
4175
4366
  });
4176
4367
  }
4177
4368
  return await buildAndSendSolanaTransaction({
4178
- client,
4369
+ client: args.client,
4179
4370
  instructions: [instruction],
4180
- senderKeyBytes: signer.keyBytes,
4181
- sponsored,
4182
- gasPolicyId: feePolicyId
4371
+ senderKeyBytes: args.signer.keyBytes,
4372
+ sponsored: args.sponsored,
4373
+ gasPolicyId: args.feePolicyId
4183
4374
  });
4184
4375
  }
4185
4376
  );
4186
4377
  const confirmed = await withSpinner(
4187
4378
  "Waiting for confirmation\u2026",
4188
4379
  "Confirmation status received",
4189
- () => waitForSolanaConfirmation(client, result.signature)
4380
+ () => waitForSolanaConfirmation(args.client, result.signature)
4381
+ );
4382
+ printNativeResult({
4383
+ result,
4384
+ to: args.toArg,
4385
+ amount: args.amountArg,
4386
+ symbol,
4387
+ network: args.network,
4388
+ sponsored: args.sponsored,
4389
+ status: confirmed ? "confirmed" : "pending"
4390
+ });
4391
+ }
4392
+ function validateSolanaTokenOptions(tokenAddress, opts) {
4393
+ if (tokenAddress) return;
4394
+ const splOnlyFlags = [
4395
+ opts.fromTokenAccount ? "--from-token-account" : void 0,
4396
+ opts.recipientTokenAccount ? "--recipient-token-account" : void 0,
4397
+ opts.failIfAssociatedTokenAccountMissing ? "--fail-if-associated-token-account-missing" : void 0
4398
+ ].filter((flag) => flag !== void 0);
4399
+ if (splOnlyFlags.length === 0) return;
4400
+ throw errInvalidArgs(
4401
+ `${splOnlyFlags.join(", ")} ${splOnlyFlags.length === 1 ? "requires" : "require"} --token <mint>.`
4402
+ );
4403
+ }
4404
+ async function performSplTokenSend(args) {
4405
+ validateSolanaAddress(args.tokenAddress);
4406
+ if (args.opts.fromTokenAccount) validateSolanaAddress(args.opts.fromTokenAccount);
4407
+ if (args.opts.recipientTokenAccount) validateSolanaAddress(args.opts.recipientTokenAccount);
4408
+ const mint = solAddress2(args.tokenAddress);
4409
+ const owner = solAddress2(args.signer.address);
4410
+ const mintInfo = await fetchSplMintInfo(args.client, mint);
4411
+ const amount = parseAmount(args.amountArg, mintInfo.decimals);
4412
+ const plan = await buildSplSendPlan({
4413
+ client: args.client,
4414
+ owner,
4415
+ recipient: args.to,
4416
+ mint,
4417
+ amount,
4418
+ decimals: mintInfo.decimals,
4419
+ tokenProgramAddress: mintInfo.tokenProgramAddress,
4420
+ failIfAssociatedTokenAccountMissing: Boolean(args.opts.failIfAssociatedTokenAccountMissing),
4421
+ fromTokenAccount: args.opts.fromTokenAccount ? solAddress2(args.opts.fromTokenAccount) : void 0,
4422
+ recipientTokenAccount: args.opts.recipientTokenAccount ? solAddress2(args.opts.recipientTokenAccount) : void 0
4423
+ });
4424
+ if (args.opts.dryRun) {
4425
+ printSplDryRun({
4426
+ from: args.signer.address,
4427
+ to: args.toArg,
4428
+ amount: args.amountArg,
4429
+ network: args.network,
4430
+ sponsored: args.sponsored,
4431
+ plan
4432
+ });
4433
+ return;
4434
+ }
4435
+ const result = await submitSolanaInstructions({
4436
+ client: args.client,
4437
+ signer: args.signer,
4438
+ instructions: plan.instructions,
4439
+ sponsored: args.sponsored,
4440
+ feePolicyId: args.feePolicyId
4441
+ });
4442
+ const confirmed = await withSpinner(
4443
+ "Waiting for confirmation\u2026",
4444
+ "Confirmation status received",
4445
+ () => waitForSolanaConfirmation(args.client, result.signature)
4190
4446
  );
4447
+ printSplResult({
4448
+ result,
4449
+ to: args.toArg,
4450
+ amount: args.amountArg,
4451
+ network: args.network,
4452
+ sponsored: args.sponsored,
4453
+ plan,
4454
+ status: confirmed ? "confirmed" : "pending"
4455
+ });
4456
+ }
4457
+ async function buildSplSendPlan(args) {
4458
+ const sourceTokenAccount = args.fromTokenAccount ?? await deriveAssociatedTokenAccount({
4459
+ owner: args.owner,
4460
+ mint: args.mint,
4461
+ tokenProgramAddress: args.tokenProgramAddress
4462
+ });
4463
+ const sourceAccount = await fetchSplTokenAccountInfo(args.client, sourceTokenAccount);
4464
+ if (!sourceAccount) {
4465
+ throw errInvalidArgs(
4466
+ `Source token account ${sourceTokenAccount} was not found. Fund the sender's associated token account for mint ${args.mint} and retry.`
4467
+ );
4468
+ }
4469
+ assertTokenAccountMatches({
4470
+ account: sourceAccount,
4471
+ expectedMint: args.mint,
4472
+ expectedOwner: args.owner,
4473
+ label: "Source token account"
4474
+ });
4475
+ const destinationTokenAccount = args.recipientTokenAccount ?? await deriveAssociatedTokenAccount({
4476
+ owner: args.recipient,
4477
+ mint: args.mint,
4478
+ tokenProgramAddress: args.tokenProgramAddress
4479
+ });
4480
+ const destinationAccount = await fetchSplTokenAccountInfo(args.client, destinationTokenAccount);
4481
+ const createDestinationTokenAccount = !destinationAccount && !args.recipientTokenAccount && !args.failIfAssociatedTokenAccountMissing;
4482
+ if (!destinationAccount && args.recipientTokenAccount) {
4483
+ throw errInvalidArgs(`Recipient token account ${destinationTokenAccount} was not found.`);
4484
+ }
4485
+ if (!destinationAccount && args.failIfAssociatedTokenAccountMissing) {
4486
+ throw errInvalidArgs(
4487
+ `Recipient associated token account ${destinationTokenAccount} does not exist. Omit --fail-if-associated-token-account-missing to create it automatically.`
4488
+ );
4489
+ }
4490
+ if (destinationAccount) {
4491
+ assertTokenAccountMatches({
4492
+ account: destinationAccount,
4493
+ expectedMint: args.mint,
4494
+ ...args.recipientTokenAccount ? {} : { expectedOwner: args.recipient },
4495
+ label: "Recipient token account"
4496
+ });
4497
+ }
4498
+ const instructions = [];
4499
+ if (createDestinationTokenAccount) {
4500
+ instructions.push(buildCreateAssociatedTokenAccountIdempotentInstruction({
4501
+ payer: { address: args.owner },
4502
+ associatedTokenAccount: destinationTokenAccount,
4503
+ owner: args.recipient,
4504
+ mint: args.mint,
4505
+ tokenProgramAddress: args.tokenProgramAddress
4506
+ }));
4507
+ }
4508
+ instructions.push(buildSplTokenTransferCheckedInstruction({
4509
+ sourceTokenAccount,
4510
+ mint: args.mint,
4511
+ destinationTokenAccount,
4512
+ owner: { address: args.owner },
4513
+ amount: args.amount,
4514
+ decimals: args.decimals,
4515
+ tokenProgramAddress: args.tokenProgramAddress
4516
+ }));
4517
+ return {
4518
+ instructions,
4519
+ mint: args.mint,
4520
+ tokenProgramAddress: args.tokenProgramAddress,
4521
+ sourceTokenAccount,
4522
+ destinationTokenAccount,
4523
+ createdDestinationTokenAccount: createDestinationTokenAccount,
4524
+ decimals: args.decimals
4525
+ };
4526
+ }
4527
+ async function submitSolanaInstructions(args) {
4528
+ const result = await withSpinner(
4529
+ "Sending transaction\u2026",
4530
+ "Transaction submitted",
4531
+ async () => {
4532
+ if (args.signer.type === "session") {
4533
+ return await buildAndSendSolanaTransactionWithSession({
4534
+ client: args.client,
4535
+ instructions: args.instructions,
4536
+ session: args.signer.session,
4537
+ authToken: args.signer.authToken,
4538
+ sponsored: args.sponsored,
4539
+ gasPolicyId: args.feePolicyId
4540
+ });
4541
+ }
4542
+ return await buildAndSendSolanaTransaction({
4543
+ client: args.client,
4544
+ instructions: args.instructions,
4545
+ senderKeyBytes: args.signer.keyBytes,
4546
+ sponsored: args.sponsored,
4547
+ gasPolicyId: args.feePolicyId
4548
+ });
4549
+ }
4550
+ );
4551
+ return result;
4552
+ }
4553
+ function printNativeDryRun(args) {
4191
4554
  if (isJSONMode()) {
4192
4555
  printJSON({
4193
- from: result.fromAddress,
4194
- to: toArg,
4195
- amount: amountArg,
4196
- token: symbol,
4197
- network,
4198
- sponsored,
4199
- signature: result.signature,
4200
- status: confirmed ? "confirmed" : "pending"
4556
+ dryRun: true,
4557
+ action: "solana-send",
4558
+ from: args.from,
4559
+ to: args.to,
4560
+ amount: args.amount,
4561
+ token: args.symbol,
4562
+ network: args.network,
4563
+ sponsored: args.sponsored,
4564
+ instructions: ["transferSol"]
4201
4565
  });
4202
- } else {
4203
- const pairs = [
4204
- ["From", result.fromAddress],
4205
- ["To", toArg],
4206
- ["Amount", green(`${amountArg} ${symbol}`)],
4207
- ["Network", network]
4208
- ];
4209
- if (sponsored) {
4210
- pairs.push(["Fee", green("Sponsored")]);
4211
- }
4212
- pairs.push(["Signature", result.signature]);
4213
- pairs.push(["Status", confirmed ? `${successBadge()} ${green("Confirmed")}` : "Pending"]);
4214
- printKeyValue(pairs);
4566
+ return;
4567
+ }
4568
+ const pairs = [
4569
+ ["Dry Run", "yes"],
4570
+ ["From", args.from],
4571
+ ["To", args.to],
4572
+ ["Amount", green(`${args.amount} ${args.symbol}`)],
4573
+ ["Network", args.network],
4574
+ ["Instructions", "transferSol"]
4575
+ ];
4576
+ if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
4577
+ printKeyValue(pairs);
4578
+ }
4579
+ function printNativeResult(args) {
4580
+ if (isJSONMode()) {
4581
+ printJSON({
4582
+ from: args.result.fromAddress,
4583
+ to: args.to,
4584
+ amount: args.amount,
4585
+ token: args.symbol,
4586
+ network: args.network,
4587
+ sponsored: args.sponsored,
4588
+ signature: args.result.signature,
4589
+ status: args.status
4590
+ });
4591
+ return;
4215
4592
  }
4593
+ const pairs = [
4594
+ ["From", args.result.fromAddress],
4595
+ ["To", args.to],
4596
+ ["Amount", green(`${args.amount} ${args.symbol}`)],
4597
+ ["Network", args.network]
4598
+ ];
4599
+ if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
4600
+ pairs.push(["Signature", args.result.signature]);
4601
+ pairs.push(["Status", args.status === "confirmed" ? `${successBadge()} ${green("Confirmed")}` : "Pending"]);
4602
+ printKeyValue(pairs);
4603
+ }
4604
+ function printSplDryRun(args) {
4605
+ const instructions = args.plan.createdDestinationTokenAccount ? ["createAssociatedTokenAccountIdempotent", "transferChecked"] : ["transferChecked"];
4606
+ if (isJSONMode()) {
4607
+ printJSON({
4608
+ dryRun: true,
4609
+ action: "solana-send",
4610
+ from: args.from,
4611
+ to: args.to,
4612
+ amount: args.amount,
4613
+ token: args.plan.mint,
4614
+ tokenAddress: args.plan.mint,
4615
+ network: args.network,
4616
+ sponsored: args.sponsored,
4617
+ tokenProgramAddress: args.plan.tokenProgramAddress,
4618
+ sourceTokenAccount: args.plan.sourceTokenAccount,
4619
+ destinationTokenAccount: args.plan.destinationTokenAccount,
4620
+ createdDestinationTokenAccount: args.plan.createdDestinationTokenAccount,
4621
+ instructions
4622
+ });
4623
+ return;
4624
+ }
4625
+ const pairs = [
4626
+ ["Dry Run", "yes"],
4627
+ ["From", args.from],
4628
+ ["To", args.to],
4629
+ ["Amount", green(`${args.amount} ${args.plan.mint}`)],
4630
+ ["Mint", args.plan.mint],
4631
+ ["Source Token Account", args.plan.sourceTokenAccount],
4632
+ ["Destination Token Account", args.plan.destinationTokenAccount],
4633
+ ["Created Destination Token Account", args.plan.createdDestinationTokenAccount ? "yes" : "no"],
4634
+ ["Network", args.network],
4635
+ ["Instructions", instructions.join(", ")]
4636
+ ];
4637
+ if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
4638
+ printKeyValue(pairs);
4639
+ }
4640
+ function printSplResult(args) {
4641
+ if (isJSONMode()) {
4642
+ printJSON({
4643
+ from: args.result.fromAddress,
4644
+ to: args.to,
4645
+ amount: args.amount,
4646
+ token: args.plan.mint,
4647
+ tokenAddress: args.plan.mint,
4648
+ network: args.network,
4649
+ sponsored: args.sponsored,
4650
+ tokenProgramAddress: args.plan.tokenProgramAddress,
4651
+ sourceTokenAccount: args.plan.sourceTokenAccount,
4652
+ destinationTokenAccount: args.plan.destinationTokenAccount,
4653
+ createdDestinationTokenAccount: args.plan.createdDestinationTokenAccount,
4654
+ signature: args.result.signature,
4655
+ status: args.status
4656
+ });
4657
+ return;
4658
+ }
4659
+ const pairs = [
4660
+ ["From", args.result.fromAddress],
4661
+ ["To", args.to],
4662
+ ["Amount", green(`${args.amount} ${args.plan.mint}`)],
4663
+ ["Mint", args.plan.mint],
4664
+ ["Source Token Account", args.plan.sourceTokenAccount],
4665
+ ["Destination Token Account", args.plan.destinationTokenAccount],
4666
+ ["Created Destination Token Account", args.plan.createdDestinationTokenAccount ? "yes" : "no"],
4667
+ ["Network", args.network]
4668
+ ];
4669
+ if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
4670
+ pairs.push(["Signature", args.result.signature]);
4671
+ pairs.push(["Status", args.status === "confirmed" ? `${successBadge()} ${green("Confirmed")}` : "Pending"]);
4672
+ printKeyValue(pairs);
4216
4673
  }
4217
4674
  async function resolveSolanaSigner(program2, signer) {
4218
4675
  if (signer === "session") {
@@ -4466,9 +4923,9 @@ async function debugRecoveredTypedDataAddress(args) {
4466
4923
  }
4467
4924
  function createDelegatedAccount(args) {
4468
4925
  const sessionBinding = buildSessionBindingInput(args.session);
4469
- const address2 = args.session.evmAddress;
4926
+ const address3 = args.session.evmAddress;
4470
4927
  return toAccount({
4471
- address: address2,
4928
+ address: address3,
4472
4929
  async signTransaction() {
4473
4930
  throw errInvalidArgs(
4474
4931
  "Delegated signer does not support direct transaction signing. Use wallet commands that execute through Alchemy smart wallets."
@@ -4499,7 +4956,7 @@ function createDelegatedAccount(args) {
4499
4956
  message: remoteMessage.message,
4500
4957
  encoding: remoteMessage.encoding,
4501
4958
  signature: signedMessage,
4502
- expectedAddress: address2
4959
+ expectedAddress: address3
4503
4960
  });
4504
4961
  return signedMessage;
4505
4962
  },
@@ -4535,7 +4992,7 @@ function createDelegatedAccount(args) {
4535
4992
  await debugRecoveredTypedDataAddress({
4536
4993
  typedData: serializedTypedData,
4537
4994
  signature: typedDataSignature,
4538
- expectedAddress: address2
4995
+ expectedAddress: address3
4539
4996
  });
4540
4997
  return typedDataSignature;
4541
4998
  },
@@ -4581,7 +5038,7 @@ function createAlchemyWalletTransport(apiKey) {
4581
5038
  }
4582
5039
 
4583
5040
  // src/lib/smart-wallet.ts
4584
- async function ensureGasPolicyResolved(program2) {
5041
+ async function ensureGasPolicyResolved(program2, options = {}) {
4585
5042
  const cfg = load();
4586
5043
  if (!resolveGasSponsored(program2, cfg)) return void 0;
4587
5044
  const existing = resolveGasPolicyId(program2, cfg);
@@ -4592,7 +5049,7 @@ async function ensureGasPolicyResolved(program2) {
4592
5049
  if (!hasAuthLoginToken(cfg)) {
4593
5050
  throw errNotLoggedInForPolicyLookup();
4594
5051
  }
4595
- const network = resolveNetwork(program2, cfg);
5052
+ const network = options.networkOverride ?? resolveNetwork(program2);
4596
5053
  const policyId = await selectOrCreatePolicy({
4597
5054
  flavor: "sponsorship",
4598
5055
  network,
@@ -4647,7 +5104,7 @@ function buildWalletClient(program2, options = {}) {
4647
5104
  const apiKey = resolveAPIKey(program2);
4648
5105
  if (!apiKey) throw errAuthRequired();
4649
5106
  const cfg = load();
4650
- const network = resolveNetwork(program2, cfg);
5107
+ const network = options.networkOverride ?? resolveNetwork(program2);
4651
5108
  const chain = networkToChain(network);
4652
5109
  const gasSponsored = resolveGasSponsored(program2, cfg);
4653
5110
  const gasPolicyId = options.gasPolicyIdOverride ?? resolveGasPolicyId(program2, cfg);
@@ -4689,7 +5146,9 @@ function buildWalletClient(program2, options = {}) {
4689
5146
  address: evmSession.evmAddress
4690
5147
  };
4691
5148
  }
4692
- if (!localKey) throw errWalletKeyRequired();
5149
+ if (!localKey) {
5150
+ throw pref === "local" ? errWalletKeyRequired() : errWalletRequired();
5151
+ }
4693
5152
  const account = privateKeyToAccount2(normalizeKey(localKey));
4694
5153
  return {
4695
5154
  signer: account,
@@ -4782,10 +5241,10 @@ function registerStatus(program2, options = {}) {
4782
5241
  "after",
4783
5242
  `
4784
5243
  Examples:
4785
- alchemy evm status call-123 EVM smart wallet operation
5244
+ alchemy evm status call-123 -n eth-mainnet EVM smart wallet operation
4786
5245
  alchemy evm status 0xTxHash... -n eth-mainnet Raw EVM transaction
4787
5246
  alchemy solana status 5wHu1qwD7q... -n solana-devnet Solana transaction
4788
- echo "call-123" | alchemy evm status
5247
+ echo "call-123" | alchemy evm status -n eth-mainnet
4789
5248
 
4790
5249
  Tip: use an EVM network for operation IDs and tx hashes, or a Solana network for signatures.`
4791
5250
  ).action(async (idArg) => {
@@ -5773,10 +6232,8 @@ var ERROR_RECOVERY = {
5773
6232
  AUTH_REQUIRED: "Set ALCHEMY_API_KEY env var or run: alchemy config set app",
5774
6233
  INVALID_API_KEY: "Check your API key and select a valid app: alchemy config set app",
5775
6234
  NETWORK_NOT_ENABLED: "Enable the target network for your app at dashboard.alchemy.com",
5776
- INVALID_ACCESS_KEY: "Check your access key: https://dashboard.alchemy.com/",
5777
- ACCESS_KEY_REQUIRED: "Set ALCHEMY_ACCESS_KEY env var or run: alchemy config set access-key <key>",
5778
6235
  APP_REQUIRED: "Select an app: alchemy config set app <app-id>",
5779
- ADMIN_API_ERROR: "Check the error message for details; verify access key permissions",
6236
+ ADMIN_API_ERROR: "Check the error message for details; verify account permissions",
5780
6237
  NETWORK_ERROR: "Check internet connection and retry",
5781
6238
  RPC_ERROR: "Check RPC method, params, and network; verify API key has access",
5782
6239
  INVALID_ARGS: "Check command usage via: alchemy --json help <command>",
@@ -5892,11 +6349,10 @@ function buildAgentPrompt(program2) {
5892
6349
  ]
5893
6350
  },
5894
6351
  {
5895
- method: "Access key",
5896
- envVar: "ALCHEMY_ACCESS_KEY",
5897
- configKey: "access-key",
5898
- commandFamilies: ["app", "evm network"],
5899
- notes: "No command-line flag exists for access-key auth; use the env var or saved config."
6352
+ method: "Alchemy login",
6353
+ setup: "alchemy auth login",
6354
+ commandFamilies: ["app", "evm network", "gas-manager"],
6355
+ notes: "Admin surfaces use the browser login session stored by `alchemy auth login`."
5900
6356
  },
5901
6357
  {
5902
6358
  method: "Webhook API key",
@@ -5953,15 +6409,15 @@ function buildAgentPrompt(program2) {
5953
6409
  examples: [
5954
6410
  "alchemy --json --no-interactive config status",
5955
6411
  "alchemy --json --no-interactive update-check",
5956
- "alchemy --json --no-interactive evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY",
5957
- "ALCHEMY_ACCESS_KEY=ak_xxx alchemy --json --no-interactive app list",
5958
- "alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY",
6412
+ "alchemy --json --no-interactive evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY -n eth-mainnet",
6413
+ "alchemy --json --no-interactive app list",
6414
+ "alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY -n eth-mainnet",
5959
6415
  "alchemy --json --no-interactive evm network list",
5960
6416
  "alchemy --json --no-interactive evm send 0xRecipient 0.001 --dry-run -n eth-sepolia",
5961
6417
  `alchemy --json --no-interactive evm contract read 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 "balanceOf(address)(uint256)" --args '["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]' -n eth-mainnet`,
5962
6418
  "alchemy --json --no-interactive evm swap quote --from 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --amount 1.0 --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet",
5963
- "alchemy --json --no-interactive evm logs --from-block latest --limit 25",
5964
- "alchemy --json --no-interactive evm block latest --summary",
6419
+ "alchemy --json --no-interactive evm logs --from-block latest --limit 25 -n eth-mainnet",
6420
+ "alchemy --json --no-interactive evm block latest --summary -n eth-mainnet",
5965
6421
  "alchemy --json --no-interactive evm status 0xCallId -n eth-mainnet"
5966
6422
  ],
5967
6423
  docs: "https://www.alchemy.com/docs"
@@ -5997,7 +6453,9 @@ function formatAsSystemPrompt(payload) {
5997
6453
  lines.push("Auth methods:");
5998
6454
  for (const auth of payload.auth) {
5999
6455
  lines.push(` ${auth.method}:`);
6000
- lines.push(` env: ${auth.envVar}`);
6456
+ if (auth.envVar) {
6457
+ lines.push(` env: ${auth.envVar}`);
6458
+ }
6001
6459
  if (auth.flag) {
6002
6460
  lines.push(` flag: ${auth.flag}`);
6003
6461
  }
@@ -6174,8 +6632,8 @@ function registrySymbolSuggestions(network) {
6174
6632
  }
6175
6633
 
6176
6634
  // src/commands/approve.ts
6177
- function isNativeToken(address2) {
6178
- return address2.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
6635
+ function isNativeToken(address3) {
6636
+ return address3.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
6179
6637
  }
6180
6638
  function buildApprovalRequest(opts, tokenMeta) {
6181
6639
  validateApprovalMode(opts);
@@ -6281,11 +6739,11 @@ function registerApprove(program2) {
6281
6739
  "after",
6282
6740
  `
6283
6741
  Examples:
6284
- alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100
6285
- alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100 --reset-first
6286
- alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited
6287
- alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited --yes
6288
- alchemy evm approve 0xRouter --token-address 0xUSDC --revoke`
6742
+ alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100 -n eth-mainnet
6743
+ alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100 --reset-first -n eth-mainnet
6744
+ alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited -n eth-mainnet
6745
+ alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited --yes -n eth-mainnet
6746
+ alchemy evm approve 0xRouter --token-address 0xUSDC --revoke -n eth-mainnet`
6289
6747
  ).action(async (spenderArg, _opts, cmd) => {
6290
6748
  try {
6291
6749
  await performApprove(cmd, spenderArg, cmd.opts());
@@ -6307,8 +6765,8 @@ async function performApprove(program2, spenderArg, opts) {
6307
6765
  signer,
6308
6766
  ...gasPolicyIdOverride && { gasPolicyIdOverride }
6309
6767
  });
6310
- const rpcClient = clientFromFlags(program2);
6311
- const tokenMeta = await fetchTokenDecimals(program2, opts.tokenAddress);
6768
+ const rpcClient = apiKeyClientFromFlags(program2, { forceNetwork: network });
6769
+ const tokenMeta = await fetchTokenDecimals(program2, opts.tokenAddress, { network });
6312
6770
  const approval = buildApprovalRequest(opts, tokenMeta);
6313
6771
  if (!await confirmUnlimitedApproval(program2, tokenMeta.symbol, spenderArg, opts)) {
6314
6772
  return;
@@ -6424,9 +6882,9 @@ function registerBlock(program2) {
6424
6882
  "after",
6425
6883
  `
6426
6884
  Examples:
6427
- alchemy evm block latest
6428
- alchemy evm block 17000000
6429
- alchemy evm block 0x1`
6885
+ alchemy evm block latest -n eth-mainnet
6886
+ alchemy evm block 17000000 -n eth-mainnet
6887
+ alchemy evm block 0x1 -n eth-mainnet`
6430
6888
  ).action(async (blockId, opts) => {
6431
6889
  try {
6432
6890
  let blockParam;
@@ -6703,11 +7161,11 @@ function registerContract(program2) {
6703
7161
  "after",
6704
7162
  `
6705
7163
  Examples:
6706
- alchemy evm contract read 0xA0b8...USDC "balanceOf(address)(uint256)" --args '["0xHolder"]'
6707
- alchemy evm contract read 0xA0b8...USDC "name()(string)"
6708
- alchemy evm contract read 0xA0b8...USDC "decimals()(uint8)"
6709
- alchemy evm contract read 0xContract balanceOf --abi-file ./erc20.json --args '["0xHolder"]'
6710
- alchemy evm contract read 0xPool "quote((address,uint256))(uint256)" --args '[["0xToken", "1000000"]]' --block 12345678`
7164
+ alchemy evm contract read 0xA0b8...USDC "balanceOf(address)(uint256)" --args '["0xHolder"]' -n eth-mainnet
7165
+ alchemy evm contract read 0xA0b8...USDC "name()(string)" -n eth-mainnet
7166
+ alchemy evm contract read 0xA0b8...USDC "decimals()(uint8)" -n eth-mainnet
7167
+ alchemy evm contract read 0xContract balanceOf --abi-file ./erc20.json --args '["0xHolder"]' -n eth-mainnet
7168
+ alchemy evm contract read 0xPool "quote((address,uint256))(uint256)" --args '[["0xToken", "1000000"]]' --block 12345678 -n eth-mainnet`
6711
7169
  ).action(async (addressArg, functionArg, opts) => {
6712
7170
  try {
6713
7171
  await performContractRead(program2, addressArg, functionArg, opts);
@@ -6721,9 +7179,9 @@ Examples:
6721
7179
  "after",
6722
7180
  `
6723
7181
  Examples:
6724
- alchemy evm contract call 0xToken "approve(address,uint256)" --args '["0xSpender", "1000000"]'
6725
- alchemy evm contract call 0xToken "transfer(address,uint256)" --args '["0xTo", "1000000"]'
6726
- alchemy evm contract call 0xContract deposit --abi-file ./contract.json --value 0.1`
7182
+ alchemy evm contract call 0xToken "approve(address,uint256)" --args '["0xSpender", "1000000"]' -n eth-mainnet
7183
+ alchemy evm contract call 0xToken "transfer(address,uint256)" --args '["0xTo", "1000000"]' -n eth-mainnet
7184
+ alchemy evm contract call 0xContract deposit --abi-file ./contract.json --value 0.1 -n eth-mainnet`
6727
7185
  ).action(async (addressArg, functionArg, _opts, cmd2) => {
6728
7186
  try {
6729
7187
  const opts = cmd2.opts();
@@ -6787,7 +7245,7 @@ async function performContractCall(program2, addressArg, functionArg, opts) {
6787
7245
  signer,
6788
7246
  ...gasPolicyIdOverride && { gasPolicyIdOverride }
6789
7247
  });
6790
- const rpcClient = clientFromFlags(program2);
7248
+ const rpcClient = apiKeyClientFromFlags(program2, { forceNetwork: network });
6791
7249
  const contractAddress = await resolveAddress(addressArg, rpcClient);
6792
7250
  const data = encodeFunctionData2({ abi, functionName, args });
6793
7251
  const value = opts.value !== void 0 ? parseEthValue(opts.value) : void 0;
@@ -6847,18 +7305,18 @@ async function performContractCall(program2, addressArg, functionArg, opts) {
6847
7305
  // src/commands/balance.ts
6848
7306
  async function fetchBalance(program2, addressInput, blockParam) {
6849
7307
  const client = clientFromFlags(program2);
6850
- const address2 = await resolveAddress(addressInput, client);
7308
+ const address3 = await resolveAddress(addressInput, client);
6851
7309
  const result = await withSpinner(
6852
7310
  "Fetching balance\u2026",
6853
7311
  "Balance fetched",
6854
- () => client.call("eth_getBalance", [address2, blockParam])
7312
+ () => client.call("eth_getBalance", [address3, blockParam])
6855
7313
  );
6856
7314
  const wei = BigInt(result);
6857
7315
  const network = resolveNetwork(program2);
6858
7316
  const symbol = nativeTokenSymbol(network);
6859
7317
  if (isJSONMode()) {
6860
7318
  printJSON({
6861
- address: address2,
7319
+ address: address3,
6862
7320
  wei: wei.toString(),
6863
7321
  balance: weiToEth(wei),
6864
7322
  symbol,
@@ -6866,7 +7324,7 @@ async function fetchBalance(program2, addressInput, blockParam) {
6866
7324
  });
6867
7325
  } else {
6868
7326
  printKeyValue([
6869
- ["Address", address2],
7327
+ ["Address", address3],
6870
7328
  ["Balance", green(`${weiToEth(wei)} ${symbol}`)],
6871
7329
  ["Network", network]
6872
7330
  ]);
@@ -6874,7 +7332,7 @@ async function fetchBalance(program2, addressInput, blockParam) {
6874
7332
  console.log("");
6875
7333
  printJSON({
6876
7334
  rpcMethod: "eth_getBalance",
6877
- rpcParams: [address2, blockParam],
7335
+ rpcParams: [address3, blockParam],
6878
7336
  rpcResult: result
6879
7337
  });
6880
7338
  }
@@ -6898,12 +7356,12 @@ function registerBalance(program2) {
6898
7356
  "after",
6899
7357
  `
6900
7358
  Examples:
6901
- alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
7359
+ alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
6902
7360
  alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n polygon-mainnet
6903
- echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data balance
6904
- alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --block 15537393
6905
- alchemy evm data balance vitalik.eth
6906
- cat addresses.txt | alchemy evm data balance`
7361
+ echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data balance -n eth-mainnet
7362
+ alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --block 15537393 -n eth-mainnet
7363
+ alchemy evm data balance vitalik.eth -n eth-mainnet
7364
+ cat addresses.txt | alchemy evm data balance -n eth-mainnet`
6907
7365
  ).option("--block <block>", "Block number, hex, or tag (default: latest)").action(async (addressArg, opts) => {
6908
7366
  try {
6909
7367
  const blockParam = resolveBlockParam2(opts?.block);
@@ -6951,17 +7409,17 @@ function registerNFTs(program2) {
6951
7409
  "after",
6952
7410
  `
6953
7411
  Examples:
6954
- alchemy evm data nfts 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
6955
- alchemy evm data nfts metadata --contract 0x... --token-id 1
6956
- alchemy evm data nfts contract 0x...
6957
- echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data nfts`
7412
+ alchemy evm data nfts 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
7413
+ alchemy evm data nfts metadata --contract 0x... --token-id 1 -n eth-mainnet
7414
+ alchemy evm data nfts contract 0x... -n eth-mainnet
7415
+ echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data nfts -n eth-mainnet`
6958
7416
  ).action(async (addressArg, opts) => {
6959
7417
  try {
6960
7418
  const addressInput = addressArg ?? await readStdinArg("address");
6961
7419
  const client = clientFromFlags(program2);
6962
- const address2 = await resolveAddress(addressInput, client);
7420
+ const address3 = await resolveAddress(addressInput, client);
6963
7421
  const params = {
6964
- owner: address2,
7422
+ owner: address3,
6965
7423
  withMetadata: "true"
6966
7424
  };
6967
7425
  if (opts.limit) params.pageSize = String(opts.limit);
@@ -6995,7 +7453,7 @@ Examples:
6995
7453
  break;
6996
7454
  }
6997
7455
  const nextParams = {
6998
- owner: address2,
7456
+ owner: address3,
6999
7457
  withMetadata: "true",
7000
7458
  pageKey
7001
7459
  };
@@ -7037,15 +7495,15 @@ Examples:
7037
7495
  exitWithError(err);
7038
7496
  }
7039
7497
  });
7040
- cmd.command("contract <address>").description("Get NFT contract metadata").action(async (address2) => {
7498
+ cmd.command("contract <address>").description("Get NFT contract metadata").action(async (address3) => {
7041
7499
  try {
7042
- validateAddress(address2);
7500
+ validateAddress(address3);
7043
7501
  const client = clientFromFlags(program2);
7044
7502
  const result = await withSpinner(
7045
7503
  "Fetching contract metadata\u2026",
7046
7504
  "Contract metadata fetched",
7047
7505
  () => client.callEnhanced("getContractMetadata", {
7048
- contractAddress: address2
7506
+ contractAddress: address3
7049
7507
  })
7050
7508
  );
7051
7509
  if (isJSONMode()) printJSON(result);
@@ -7175,15 +7633,15 @@ function registerTokens(program2) {
7175
7633
  "after",
7176
7634
  `
7177
7635
  Examples:
7178
- alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
7179
- alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --metadata
7180
- echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data tokens balances`
7636
+ alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
7637
+ alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --metadata -n eth-mainnet
7638
+ echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data tokens balances -n eth-mainnet`
7181
7639
  ).action(async (addressArg, opts) => {
7182
7640
  try {
7183
7641
  const addressInput = addressArg ?? await readStdinArg("address");
7184
7642
  const client = clientFromFlags(program2);
7185
- const address2 = await resolveAddress(addressInput, client);
7186
- const params = [address2];
7643
+ const address3 = await resolveAddress(addressInput, client);
7644
+ const params = [address3];
7187
7645
  if (opts.pageKey) {
7188
7646
  params.push("erc20", { pageKey: opts.pageKey });
7189
7647
  }
@@ -7220,7 +7678,7 @@ Examples:
7220
7678
  }
7221
7679
  let totalShown = nonZero.length;
7222
7680
  printKeyValue([
7223
- ["Address", address2],
7681
+ ["Address", address3],
7224
7682
  ["Network", client.network],
7225
7683
  ["Tokens", String(totalShown)]
7226
7684
  ]);
@@ -7246,7 +7704,7 @@ Examples:
7246
7704
  const nextResult = await withSpinner(
7247
7705
  "Fetching next page\u2026",
7248
7706
  "Page fetched",
7249
- () => client.call("alchemy_getTokenBalances", [address2, "erc20", { pageKey }])
7707
+ () => client.call("alchemy_getTokenBalances", [address3, "erc20", { pageKey }])
7250
7708
  );
7251
7709
  if (isJSONMode()) {
7252
7710
  printJSON(nextResult);
@@ -7282,7 +7740,7 @@ Examples:
7282
7740
  "after",
7283
7741
  `
7284
7742
  Examples:
7285
- alchemy evm data tokens metadata 0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eB48`
7743
+ alchemy evm data tokens metadata 0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eB48 -n eth-mainnet`
7286
7744
  ).action(async (contract) => {
7287
7745
  try {
7288
7746
  validateAddress(contract);
@@ -7353,20 +7811,20 @@ function registerTransfers(program2) {
7353
7811
  `
7354
7812
  Examples:
7355
7813
  # Outgoing transfers from an address
7356
- alchemy evm data history 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
7814
+ alchemy evm data history 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
7357
7815
 
7358
7816
  # Incoming transfers to an address
7359
- alchemy evm data history --to-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
7817
+ alchemy evm data history --to-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
7360
7818
 
7361
7819
  # Outgoing ERC-20 transfers only
7362
- alchemy evm data history --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --category erc20
7820
+ alchemy evm data history --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --category erc20 -n eth-mainnet
7363
7821
 
7364
7822
  # Transfers within a block range
7365
- alchemy evm data history 0xd8dA... --from-block 0x100000 --to-block latest`
7823
+ alchemy evm data history 0xd8dA... --from-block 0x100000 --to-block latest -n eth-mainnet`
7366
7824
  ).action(async (addressArg, opts) => {
7367
7825
  try {
7368
7826
  const client = clientFromFlags(program2);
7369
- const address2 = addressArg ? await resolveAddress(addressArg, client) : void 0;
7827
+ const address3 = addressArg ? await resolveAddress(addressArg, client) : void 0;
7370
7828
  if (opts.fromAddress) opts.fromAddress = await resolveAddress(opts.fromAddress, client);
7371
7829
  if (opts.toAddress) opts.toAddress = await resolveAddress(opts.toAddress, client);
7372
7830
  const baseFilter = {
@@ -7382,8 +7840,8 @@ Examples:
7382
7840
  }
7383
7841
  if (opts.pageKey) baseFilter.pageKey = opts.pageKey;
7384
7842
  const filter = { ...baseFilter };
7385
- if (address2 && !opts.fromAddress && !opts.toAddress) {
7386
- filter.fromAddress = address2;
7843
+ if (address3 && !opts.fromAddress && !opts.toAddress) {
7844
+ filter.fromAddress = address3;
7387
7845
  } else {
7388
7846
  if (opts.fromAddress) filter.fromAddress = opts.fromAddress;
7389
7847
  if (opts.toAddress) filter.toAddress = opts.toAddress;
@@ -7620,9 +8078,9 @@ function registerGas(program2) {
7620
8078
  "after",
7621
8079
  `
7622
8080
  Examples:
7623
- alchemy evm gas
8081
+ alchemy evm gas -n eth-mainnet
7624
8082
  alchemy evm gas -n polygon-mainnet
7625
- alchemy evm gas --json`
8083
+ alchemy evm gas --json -n eth-mainnet`
7626
8084
  ).action(async () => {
7627
8085
  try {
7628
8086
  const client = clientFromFlags(program2);
@@ -7734,10 +8192,10 @@ function registerLogs(program2) {
7734
8192
  "after",
7735
8193
  `
7736
8194
  Examples:
7737
- alchemy evm logs --from-block 18000000 --to-block 18000010
7738
- alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --from-block 18000000 --to-block 18000010
7739
- alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --topic 0xddf252ad...
7740
- alchemy evm logs --from-block latest --json`
8195
+ alchemy evm logs --from-block 18000000 --to-block 18000010 -n eth-mainnet
8196
+ alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --from-block 18000000 --to-block 18000010 -n eth-mainnet
8197
+ alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --topic 0xddf252ad... -n eth-mainnet
8198
+ alchemy evm logs --from-block latest --json -n eth-mainnet`
7741
8199
  ).option("--address <address>", "Contract address to filter logs").option("--topic <topic...>", "Event topic(s) to filter (topic0, topic1, ...)").option("--from-block <block>", "Start block (number, hex, or tag)", "latest").option("--to-block <block>", "End block (number, hex, or tag)", "latest").option("--limit <n>", "Limit returned logs in output").action(async (opts) => {
7742
8200
  try {
7743
8201
  const outputLimit = parseOptionalInt(opts.limit, "--limit");
@@ -7826,10 +8284,10 @@ function registerReceipt(program2) {
7826
8284
  "after",
7827
8285
  `
7828
8286
  Examples:
7829
- alchemy evm receipt 0xabc123...
7830
- echo 0xabc123... | alchemy evm receipt
8287
+ alchemy evm receipt 0xabc123... -n eth-mainnet
8288
+ echo 0xabc123... | alchemy evm receipt -n eth-mainnet
7831
8289
 
7832
- Tip: use 'alchemy evm tx <hash>' for transaction details (value, block, nonce). Receipt provides execution results (status, gas used, logs).`
8290
+ Tip: use 'alchemy evm tx <hash> -n <net>' for transaction details (value, block, nonce). Receipt provides execution results (status, gas used, logs).`
7833
8291
  ).action(async (hashArg) => {
7834
8292
  try {
7835
8293
  const hash = hashArg ?? await readStdinArg("hash");
@@ -7893,9 +8351,9 @@ function registerRPC(program2) {
7893
8351
  "after",
7894
8352
  `
7895
8353
  Examples:
7896
- alchemy evm rpc eth_blockNumber
7897
- alchemy evm rpc eth_getBalance "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" "latest"
7898
- alchemy evm rpc eth_getBlockByNumber "0x1" true`
8354
+ alchemy evm rpc eth_blockNumber -n eth-mainnet
8355
+ alchemy evm rpc eth_getBalance "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" "latest" -n eth-mainnet
8356
+ alchemy evm rpc eth_getBlockByNumber "0x1" true -n eth-mainnet`
7899
8357
  ).action(async (method, params) => {
7900
8358
  try {
7901
8359
  if (outputRpcHelp(cmd, method, params)) {
@@ -7956,13 +8414,17 @@ function registerEvmSend(program2) {
7956
8414
  sendCmd.option("--gas-sponsored", "Enable gas sponsorship (env: ALCHEMY_EVM_GAS_SPONSORED)").option("--gas-policy-id <id>", "Gas policy ID for sponsorship (env: ALCHEMY_EVM_GAS_POLICY_ID)").addHelpText(
7957
8415
  "after",
7958
8416
  `
8417
+ Omit --token to send the native token for the selected network.
8418
+ Network is required via -n/--network.
8419
+
7959
8420
  Examples:
7960
- alchemy evm send 0xAbC...123 1.5 Send 1.5 ETH
7961
- alchemy evm send vitalik.eth 0.1 -n base-mainnet Send 0.1 ETH on Base
7962
- alchemy evm send 0xAbC...123 100 --token 0xA0b8...USDC Send 100 USDC
7963
- alchemy evm send 0xAbC...123 1.5 --dry-run Preview without signing or sending
7964
- alchemy evm send 0xAbC...123 1 --gas-sponsored --gas-policy-id <id>
7965
- alchemy evm send 0xAbC...123 1.5 --signer local Force the local wallet`
8421
+ alchemy evm send 0xAbC...123 1.5 -n eth-mainnet Send 1.5 ETH
8422
+ alchemy evm send vitalik.eth 0.1 -n base-mainnet Send 0.1 ETH on Base
8423
+ alchemy evm send 0xAbC...123 1 -n monad-testnet Send 1 MON
8424
+ alchemy evm send 0xAbC...123 100 --token 0xA0b8...USDC -n eth-mainnet Send 100 USDC
8425
+ alchemy evm send 0xAbC...123 1.5 --dry-run -n eth-mainnet Preview without signing or sending
8426
+ alchemy evm send 0xAbC...123 1 -n base-mainnet --gas-sponsored --gas-policy-id <id>
8427
+ alchemy evm send 0xAbC...123 1.5 --signer local -n eth-mainnet Force the local wallet`
7966
8428
  ).action(async (toArg, amountArg, _opts, cmd) => {
7967
8429
  try {
7968
8430
  const opts = cmd.opts();
@@ -7971,7 +8433,7 @@ Examples:
7971
8433
  dryRun: opts.dryRun
7972
8434
  });
7973
8435
  } catch (err) {
7974
- const { exitWithError: exitWithError2 } = await import("./errors-YPNK3AVF.js");
8436
+ const { exitWithError: exitWithError2 } = await import("./errors-6BEPCY5N.js");
7975
8437
  exitWithError2(err);
7976
8438
  }
7977
8439
  });
@@ -7980,17 +8442,25 @@ async function performEvmSend(program2, toArg, amountArg, tokenAddress, opts = {
7980
8442
  if (tokenAddress) {
7981
8443
  validateAddress(tokenAddress);
7982
8444
  }
7983
- const gasPolicyIdOverride = await ensureGasPolicyResolved(program2);
7984
- const { client, network, address: from, paymaster } = buildWalletClient(program2, {
8445
+ const network = resolveRequiredNetwork(program2);
8446
+ const gasPolicyIdOverride = await ensureGasPolicyResolved(program2, {
8447
+ networkOverride: network
8448
+ });
8449
+ const { client, address: from, paymaster } = buildWalletClient(program2, {
7985
8450
  signer: opts.signer,
8451
+ networkOverride: network,
7986
8452
  ...gasPolicyIdOverride && { gasPolicyIdOverride }
7987
8453
  });
7988
- const rpcClient = clientFromFlags(program2);
8454
+ const rpcClient = apiKeyClientFromFlags(program2, {
8455
+ forceNetwork: network
8456
+ });
7989
8457
  const to = await resolveAddress(toArg, rpcClient);
7990
8458
  let decimals;
7991
8459
  let symbol;
7992
8460
  if (tokenAddress) {
7993
- const meta = await fetchTokenDecimals(program2, tokenAddress);
8461
+ const meta = await fetchTokenDecimals(program2, tokenAddress, {
8462
+ network
8463
+ });
7994
8464
  decimals = meta.decimals;
7995
8465
  symbol = meta.symbol;
7996
8466
  } else {
@@ -8145,14 +8615,13 @@ import {
8145
8615
  // src/lib/wallet-quote-client.ts
8146
8616
  import { createClient } from "viem";
8147
8617
  import { parseAccount } from "viem/accounts";
8148
- function buildWalletQuoteClient(program2, address2) {
8618
+ function buildWalletQuoteClient(program2, address3) {
8149
8619
  const apiKey = resolveAPIKey(program2);
8150
8620
  if (!apiKey) throw errAuthRequired();
8151
- const cfg = load();
8152
- const network = resolveNetwork(program2, cfg);
8621
+ const network = resolveNetwork(program2);
8153
8622
  const chain = networkToChain(network);
8154
8623
  const client = createClient({
8155
- account: parseAccount(address2),
8624
+ account: parseAccount(address3),
8156
8625
  transport: createAlchemyWalletTransport(apiKey),
8157
8626
  chain,
8158
8627
  name: "alchemyQuoteClient"
@@ -8164,7 +8633,7 @@ function buildWalletQuoteClient(program2, address2) {
8164
8633
  client,
8165
8634
  network,
8166
8635
  chain,
8167
- address: address2,
8636
+ address: address3,
8168
8637
  paymaster: void 0
8169
8638
  };
8170
8639
  }
@@ -8241,8 +8710,8 @@ function normalizeQuoteError(err, flow) {
8241
8710
 
8242
8711
  // src/commands/swap.ts
8243
8712
  var NATIVE_DECIMALS = 18;
8244
- function isNativeToken2(address2) {
8245
- return address2.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
8713
+ function isNativeToken2(address3) {
8714
+ return address3.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
8246
8715
  }
8247
8716
  function slippagePercentToBasisPoints(percent) {
8248
8717
  return BigInt(Math.round(percent * 100));
@@ -8252,7 +8721,7 @@ async function resolveTokenInfo(network, program2, tokenAddress) {
8252
8721
  return { decimals: NATIVE_DECIMALS, symbol: nativeTokenSymbol(network) };
8253
8722
  }
8254
8723
  try {
8255
- return await fetchTokenDecimals(program2, tokenAddress);
8724
+ return await fetchTokenDecimals(program2, tokenAddress, { network });
8256
8725
  } catch (err) {
8257
8726
  if (err instanceof CLIError && err.code === "INVALID_ARGS") {
8258
8727
  throw err;
@@ -8318,14 +8787,14 @@ function registerSwap(program2) {
8318
8787
  quoteCmd.addHelpText(
8319
8788
  "after",
8320
8789
  `
8321
- Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
8790
+ Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
8322
8791
  (ETH, USDC, WETH, USDT, DAI, \u2026). Example:
8323
- --from $(alchemy evm token ETH --address-only) \\
8792
+ --from $(alchemy evm token ETH --address-only -n eth-mainnet) \\
8324
8793
  --to $(alchemy evm token USDC --address-only -n eth-mainnet)
8325
8794
 
8326
8795
  Examples:
8327
8796
  alchemy evm swap quote --from 0xEeee...EEeE --to 0xA0b8...USDC --amount 1.0 -n eth-mainnet
8328
- alchemy evm swap quote --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0`
8797
+ alchemy evm swap quote --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0 -n eth-mainnet`
8329
8798
  ).action(async (opts) => {
8330
8799
  try {
8331
8800
  await performSwapQuote(program2, opts);
@@ -8338,14 +8807,14 @@ Examples:
8338
8807
  executeCmd.option("--gas-sponsored", "Enable gas sponsorship (env: ALCHEMY_EVM_GAS_SPONSORED)").option("--gas-policy-id <id>", "Gas policy ID for sponsorship (env: ALCHEMY_EVM_GAS_POLICY_ID)").addHelpText(
8339
8808
  "after",
8340
8809
  `
8341
- Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
8342
- (ETH, USDC, WETH, USDT, DAI, \u2026). Run 'alchemy evm token list' to discover
8343
- known symbols on the current network.
8810
+ Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
8811
+ (ETH, USDC, WETH, USDT, DAI, \u2026). Run 'alchemy evm token list -n <net>' to discover
8812
+ known symbols on that network.
8344
8813
 
8345
8814
  Examples:
8346
8815
  alchemy evm swap execute --from 0xEeee...EEeE --to 0xA0b8...USDC --amount 1.0 -n eth-mainnet
8347
- alchemy evm swap execute --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0
8348
- alchemy evm swap execute --from 0xEeee...EEeE --to 0xUSDC --amount 0.1 --gas-sponsored --gas-policy-id <id>`
8816
+ alchemy evm swap execute --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0 -n eth-mainnet
8817
+ alchemy evm swap execute --from 0xEeee...EEeE --to 0xUSDC --amount 0.1 --gas-sponsored --gas-policy-id <id> -n eth-mainnet`
8349
8818
  ).action(async (_opts, cmd2) => {
8350
8819
  try {
8351
8820
  const opts = cmd2.opts();
@@ -8528,20 +8997,20 @@ applicable. The native gas token (ETH, POL, BNB, AVAX, \u2026) resolves to the
8528
8997
  EIP-7528 native sentinel 0xEeee\u2026EEeE.
8529
8998
 
8530
8999
  Examples:
8531
- alchemy evm token ETH # uses current network
9000
+ alchemy evm token ETH -n eth-mainnet
8532
9001
  alchemy evm token USDC -n base-mainnet
8533
9002
  alchemy evm token USDC.e -n arb-mainnet
8534
- alchemy evm token USDC --address-only # prints just the 0x address
8535
- alchemy --json evm token USDC | jq -r .address
9003
+ alchemy evm token USDC --address-only -n base-mainnet
9004
+ alchemy --json evm token USDC -n base-mainnet | jq -r .address
8536
9005
 
8537
- alchemy evm token list # list tokens on current network
9006
+ alchemy evm token list -n eth-mainnet
8538
9007
  alchemy evm token list --all # list tokens across all networks`
8539
9008
  ).action(async (symbol, opts) => {
8540
9009
  try {
8541
9010
  if (!symbol) {
8542
9011
  if (opts.addressOnly) {
8543
9012
  throw errInvalidArgs(
8544
- "--address-only requires a symbol. Example: alchemy evm token USDC --address-only"
9013
+ "--address-only requires a symbol. Example: alchemy evm token USDC --address-only -n base-mainnet"
8545
9014
  );
8546
9015
  }
8547
9016
  await runList(program2, {});
@@ -8616,7 +9085,7 @@ async function runList(program2, opts) {
8616
9085
  }
8617
9086
  console.log(
8618
9087
  `
8619
- ${dim("Tip: pass -n/--network <net> to scope a lookup. Use 'alchemy evm token <SYMBOL>' to fetch a single address.")}`
9088
+ ${dim("Tip: pass -n/--network <net> to scope a lookup. Use 'alchemy evm token <SYMBOL> -n <net>' to fetch a single address.")}`
8620
9089
  );
8621
9090
  return;
8622
9091
  }
@@ -8637,7 +9106,7 @@ async function runList(program2, opts) {
8637
9106
  printTokenTable(tokens);
8638
9107
  console.log(
8639
9108
  `
8640
- ${dim("Tip: 'alchemy evm token <SYMBOL>' resolves one symbol. Add --all to see every network.")}`
9109
+ ${dim("Tip: 'alchemy evm token <SYMBOL> -n <net>' resolves one symbol. Add --all to see every network.")}`
8641
9110
  );
8642
9111
  }
8643
9112
  function printTokenTable(tokens) {
@@ -8683,10 +9152,10 @@ function registerTx(program2) {
8683
9152
  "after",
8684
9153
  `
8685
9154
  Examples:
8686
- alchemy evm tx 0xabc123...
8687
- echo 0xabc123... | alchemy evm tx
9155
+ alchemy evm tx 0xabc123... -n eth-mainnet
9156
+ echo 0xabc123... | alchemy evm tx -n eth-mainnet
8688
9157
 
8689
- Tip: use 'alchemy evm receipt <hash>' to get the transaction receipt (status, gas used, logs).`
9158
+ Tip: use 'alchemy evm receipt <hash> -n <net>' to get the transaction receipt (status, gas used, logs).`
8690
9159
  ).action(async (hashArg) => {
8691
9160
  try {
8692
9161
  const hash = hashArg ?? await readStdinArg("hash");
@@ -8934,8 +9403,8 @@ import {
8934
9403
  swapActions as swapActions2
8935
9404
  } from "@alchemy/wallet-apis/experimental";
8936
9405
  var NATIVE_DECIMALS2 = 18;
8937
- function isNativeToken3(address2) {
8938
- return address2.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
9406
+ function isNativeToken3(address3) {
9407
+ return address3.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
8939
9408
  }
8940
9409
  function slippagePercentToBasisPoints2(percent) {
8941
9410
  return BigInt(Math.round(percent * 100));
@@ -9034,7 +9503,7 @@ function registerBridge(program2) {
9034
9503
  Source network comes from the global -n/--network flag. Use --to-network for the destination chain.
9035
9504
  For same-chain token exchanges, use 'alchemy evm swap'.
9036
9505
 
9037
- Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
9506
+ Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
9038
9507
  (ETH, USDC, WETH, USDT, DAI, \u2026) per chain.
9039
9508
 
9040
9509
  Examples:
@@ -9055,7 +9524,7 @@ Examples:
9055
9524
  Source network comes from the global -n/--network flag. Use --to-network for the destination chain.
9056
9525
  For same-chain token exchanges, use 'alchemy evm swap'.
9057
9526
 
9058
- Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
9527
+ Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
9059
9528
  (ETH, USDC, WETH, USDT, DAI, \u2026) per chain.
9060
9529
 
9061
9530
  Examples:
@@ -9740,11 +10209,10 @@ function doctorSetupStatus(setup) {
9740
10209
  ...setup,
9741
10210
  complete: x402OnlySetup ? false : setup.complete,
9742
10211
  satisfiedBy: x402OnlySetup ? null : setup.satisfiedBy,
9743
- missing: x402OnlySetup ? ["Provide one auth path: alchemy auth OR api-key OR ALCHEMY_ACCESS_KEY+app"] : removeX402SetupMissing(setup.missing),
10212
+ missing: x402OnlySetup ? ["Provide one auth path: alchemy auth OR api-key"] : removeX402SetupMissing(setup.missing),
9744
10213
  nextCommands: x402OnlySetup ? [
9745
10214
  "alchemy auth",
9746
- "alchemy config set app",
9747
- "alchemy config set access-key <key> && alchemy config set app <app-id>"
10215
+ "alchemy config set app"
9748
10216
  ] : removeX402NextCommands(setup.nextCommands),
9749
10217
  capabilities: sanitizedCapabilities
9750
10218
  };
@@ -9888,9 +10356,9 @@ async function flushProcessOutput() {
9888
10356
  }
9889
10357
  program.name("alchemy").description(
9890
10358
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
9891
- ).version("0.9.3", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
10359
+ ).version("0.11.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
9892
10360
  "-n, --network <network>",
9893
- "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
10361
+ "Target network for networked commands"
9894
10362
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
9895
10363
  "--wallet-key-file <path>",
9896
10364
  "Path to wallet private key file for x402"
@@ -10026,9 +10494,9 @@ ${styledLine}`;
10026
10494
  `${hBrand("\u25C6")} ${hBold("Quick Start")}`,
10027
10495
  ` ${hDim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`,
10028
10496
  ` ${hBrand("alchemy")} ${hDim("Interactive mode with guided setup")}`,
10029
- ` ${hBrand("alchemy evm data balance")} ${hDim("<address>")} ${hDim("Get native token balance")}`,
10030
- ` ${hBrand("alchemy evm block latest")} ${hDim("Latest block summary")}`,
10031
- ` ${hBrand("alchemy evm rpc eth_chainId")} ${hDim("Raw JSON-RPC call")}`,
10497
+ ` ${hBrand("alchemy evm data balance")} ${hDim("<address> -n eth-mainnet")} ${hDim("Get native token balance")}`,
10498
+ ` ${hBrand("alchemy evm block latest")} ${hDim("-n eth-mainnet")} ${hDim("Latest block summary")}`,
10499
+ ` ${hBrand("alchemy evm rpc eth_chainId")} ${hDim("-n eth-mainnet")} ${hDim("Raw JSON-RPC call")}`,
10032
10500
  ` ${hBrand("alchemy config list")} ${hDim("View current configuration")}`,
10033
10501
  "",
10034
10502
  `${hBrand("\u25C6")} ${hBold("Exit Codes")}`,
@@ -10075,11 +10543,11 @@ ${styledLine}`;
10075
10543
  "wallet"
10076
10544
  ];
10077
10545
  if (!skipAppPrompt.includes(cmdName) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
10078
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-REZCFZZ7.js");
10546
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-FRMIY357.js");
10079
10547
  const authToken = resolveAuthToken2(cfg);
10080
10548
  const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
10081
10549
  if (authToken && !hasApiKey) {
10082
- const { selectAppAfterAuth } = await import("./auth-23OYLRWN.js");
10550
+ const { selectAppAfterAuth } = await import("./auth-6X2DBBYQ.js");
10083
10551
  console.log("");
10084
10552
  console.log(` No app selected. Please select an app to continue.`);
10085
10553
  await selectAppAfterAuth(authToken);
@@ -10114,7 +10582,7 @@ ${styledLine}`;
10114
10582
  if (isInteractiveAllowed(program)) {
10115
10583
  let latestForInteractiveStartup = null;
10116
10584
  if (shouldRunOnboarding(program, cfg)) {
10117
- const { runOnboarding } = await import("./onboarding-IJQ72DK7.js");
10585
+ const { runOnboarding } = await import("./onboarding-2FLKQYLY.js");
10118
10586
  const latest = getAvailableUpdateOnce();
10119
10587
  const completed = await runOnboarding(program, latest);
10120
10588
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -10128,7 +10596,7 @@ ${styledLine}`;
10128
10596
  latestForInteractiveStartup
10129
10597
  );
10130
10598
  }
10131
- const { startREPL } = await import("./interactive-KLVEYTQM.js");
10599
+ const { startREPL } = await import("./interactive-EXQM6HWQ.js");
10132
10600
  program.exitOverride();
10133
10601
  program.configureOutput({
10134
10602
  writeErr: () => {