@alchemy/cli 0.7.2-alpha.21 → 0.7.2-alpha.29

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.
@@ -4,7 +4,7 @@ import {
4
4
  configDir,
5
5
  load,
6
6
  save
7
- } from "./chunk-BAZ4NGOD.js";
7
+ } from "./chunk-JUCUKTP3.js";
8
8
  import {
9
9
  CLIError,
10
10
  ErrorCode,
@@ -29,11 +29,275 @@ import {
29
29
  parseBaseURLOverride,
30
30
  redactSensitiveText,
31
31
  verbose
32
- } from "./chunk-KLPWJFWP.js";
32
+ } from "./chunk-2BALTY22.js";
33
33
 
34
34
  // src/lib/resolve.ts
35
35
  import { readFileSync as readFileSync2 } from "fs";
36
36
 
37
+ // src/lib/networks.ts
38
+ 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;
39
+ var FAMILY_ALIASES = {
40
+ arb: "Arbitrum",
41
+ arbnova: "Arbitrum Nova",
42
+ avax: "Avalanche",
43
+ bnb: "BNB Smart Chain",
44
+ eth: "Ethereum",
45
+ opt: "OP Mainnet",
46
+ polygonzkevm: "Polygon zkEVM"
47
+ };
48
+ var NAME_ALIASES = {
49
+ arb: "Arbitrum",
50
+ avax: "Avalanche",
51
+ bnb: "BNB",
52
+ eth: "Ethereum",
53
+ opbnb: "opBNB",
54
+ opt: "OP Mainnet",
55
+ sui: "SUI",
56
+ xmtp: "XMTP",
57
+ zksync: "ZKsync"
58
+ };
59
+ var RPC_NETWORK_IDS = [
60
+ "abstract-mainnet",
61
+ "abstract-testnet",
62
+ "adi-mainnet",
63
+ "adi-testnet",
64
+ "alchemy-internal",
65
+ "alchemy-sepolia",
66
+ "alchemyarb-fam",
67
+ "alchemyarb-sepolia",
68
+ "alterscope-mainnet",
69
+ "anime-mainnet",
70
+ "anime-sepolia",
71
+ "apechain-curtis",
72
+ "apechain-mainnet",
73
+ "aptos-mainnet",
74
+ "aptos-testnet",
75
+ "arb-mainnet",
76
+ "arb-sepolia",
77
+ "arbnova-mainnet",
78
+ "arc-testnet",
79
+ "astar-mainnet",
80
+ "avax-fuji",
81
+ "avax-mainnet",
82
+ "base-mainnet",
83
+ "base-sepolia",
84
+ "berachain-bepolia",
85
+ "berachain-mainnet",
86
+ "bitcoin-mainnet",
87
+ "bitcoin-signet",
88
+ "bitcoin-testnet",
89
+ "blast-mainnet",
90
+ "blast-sepolia",
91
+ "bnb-mainnet",
92
+ "bnb-testnet",
93
+ "bob-mainnet",
94
+ "bob-sepolia",
95
+ "boba-mainnet",
96
+ "boba-sepolia",
97
+ "botanix-mainnet",
98
+ "botanix-testnet",
99
+ "celestiabridge-mainnet",
100
+ "celestiabridge-mocha",
101
+ "celo-mainnet",
102
+ "celo-sepolia",
103
+ "citrea-mainnet",
104
+ "citrea-testnet",
105
+ "clankermon-mainnet",
106
+ "commons-mainnet",
107
+ "crossfi-mainnet",
108
+ "crossfi-testnet",
109
+ "degen-mainnet",
110
+ "degen-sepolia",
111
+ "earnm-mainnet",
112
+ "earnm-sepolia",
113
+ "edge-mainnet",
114
+ "edge-testnet",
115
+ "eth-holesky",
116
+ "eth-holeskybeacon",
117
+ "eth-hoodi",
118
+ "eth-hoodibeacon",
119
+ "eth-mainnet",
120
+ "eth-mainnetbeacon",
121
+ "eth-sepolia",
122
+ "eth-sepoliabeacon",
123
+ "flow-mainnet",
124
+ "flow-testnet",
125
+ "frax-hoodi",
126
+ "frax-mainnet",
127
+ "galactica-cassiopeia",
128
+ "galactica-mainnet",
129
+ "gensyn-mainnet",
130
+ "gensyn-testnet",
131
+ "gnosis-chiado",
132
+ "gnosis-mainnet",
133
+ "humanity-mainnet",
134
+ "humanity-testnet",
135
+ "hyperliquid-mainnet",
136
+ "hyperliquid-testnet",
137
+ "ink-mainnet",
138
+ "ink-sepolia",
139
+ "lens-mainnet",
140
+ "lens-sepolia",
141
+ "linea-mainnet",
142
+ "linea-sepolia",
143
+ "mantle-mainnet",
144
+ "mantle-sepolia",
145
+ "megaeth-mainnet",
146
+ "megaeth-testnet",
147
+ "metis-mainnet",
148
+ "mode-mainnet",
149
+ "mode-sepolia",
150
+ "monad-mainnet",
151
+ "monad-testnet",
152
+ "moonbeam-mainnet",
153
+ "mythos-mainnet",
154
+ "opbnb-mainnet",
155
+ "opbnb-testnet",
156
+ "openloot-sepolia",
157
+ "opt-mainnet",
158
+ "opt-sepolia",
159
+ "plasma-mainnet",
160
+ "plasma-testnet",
161
+ "polygon-amoy",
162
+ "polygon-mainnet",
163
+ "polygonzkevm-cardona",
164
+ "polygonzkevm-mainnet",
165
+ "polynomial-mainnet",
166
+ "polynomial-sepolia",
167
+ "race-mainnet",
168
+ "race-sepolia",
169
+ "risa-testnet",
170
+ "rise-testnet",
171
+ "ronin-mainnet",
172
+ "ronin-saigon",
173
+ "rootstock-mainnet",
174
+ "rootstock-testnet",
175
+ "scroll-mainnet",
176
+ "scroll-sepolia",
177
+ "sei-mainnet",
178
+ "sei-testnet",
179
+ "settlus-mainnet",
180
+ "settlus-septestnet",
181
+ "shape-mainnet",
182
+ "shape-sepolia",
183
+ "solana-devnet",
184
+ "solana-mainnet",
185
+ "soneium-mainnet",
186
+ "soneium-minato",
187
+ "sonic-blaze",
188
+ "sonic-mainnet",
189
+ "sonic-testnet",
190
+ "stable-mainnet",
191
+ "stable-testnet",
192
+ "standard-mainnet",
193
+ "starknet-mainnet",
194
+ "starknet-sepolia",
195
+ "story-aeneid",
196
+ "story-mainnet",
197
+ "sui-mainnet",
198
+ "sui-testnet",
199
+ "superseed-mainnet",
200
+ "superseed-sepolia",
201
+ "synd-mainnet",
202
+ "syndicate-manchego",
203
+ "tea-sepolia",
204
+ "tempo-testnet",
205
+ "tron-mainnet",
206
+ "tron-testnet",
207
+ "unichain-mainnet",
208
+ "unichain-sepolia",
209
+ "unite-mainnet",
210
+ "unite-testnet",
211
+ "worldchain-mainnet",
212
+ "worldchain-sepolia",
213
+ "worldl3-devnet",
214
+ "worldmobile-devnet",
215
+ "worldmobile-testnet",
216
+ "worldmobilechain-mainnet",
217
+ "xmtp-mainnet",
218
+ "xmtp-ropsten",
219
+ "xprotocol-mainnet",
220
+ "zetachain-mainnet",
221
+ "zetachain-testnet",
222
+ "zksync-mainnet",
223
+ "zksync-sepolia",
224
+ "zora-mainnet",
225
+ "zora-sepolia"
226
+ ];
227
+ function isTestnetNetwork(id) {
228
+ return TESTNET_TOKEN_RE.test(id);
229
+ }
230
+ function tokenToName(token) {
231
+ const alias = NAME_ALIASES[token];
232
+ if (alias) return alias;
233
+ return token.charAt(0).toUpperCase() + token.slice(1);
234
+ }
235
+ function toFamily(id) {
236
+ const [head] = id.split("-");
237
+ return FAMILY_ALIASES[head] ?? tokenToName(head);
238
+ }
239
+ function toDisplayName(id) {
240
+ return id.split("-").map((part) => tokenToName(part)).join(" ");
241
+ }
242
+ function toHttpsUrlTemplate(id) {
243
+ const domain = getBaseDomain();
244
+ if (id === "starknet-mainnet" || id === "starknet-sepolia") {
245
+ return `https://${id}.g.${domain}/starknet/version/rpc/v0_10/{apiKey}`;
246
+ }
247
+ return `https://${id}.g.${domain}/v2/{apiKey}`;
248
+ }
249
+ function getRPCNetworks() {
250
+ return RPC_NETWORK_IDS.map((id) => ({
251
+ id,
252
+ name: toDisplayName(id),
253
+ family: toFamily(id),
254
+ isTestnet: isTestnetNetwork(id),
255
+ httpsUrlTemplate: toHttpsUrlTemplate(id)
256
+ }));
257
+ }
258
+ function getRPCNetworkIds() {
259
+ return [...RPC_NETWORK_IDS];
260
+ }
261
+ var NATIVE_TOKEN_SYMBOLS = {
262
+ eth: "ETH",
263
+ arb: "ETH",
264
+ arbnova: "ETH",
265
+ opt: "ETH",
266
+ base: "ETH",
267
+ zksync: "ETH",
268
+ scroll: "ETH",
269
+ blast: "ETH",
270
+ linea: "ETH",
271
+ zora: "ETH",
272
+ shape: "ETH",
273
+ polygon: "POL",
274
+ polygonzkevm: "ETH",
275
+ bnb: "BNB",
276
+ opbnb: "BNB",
277
+ avax: "AVAX",
278
+ solana: "SOL",
279
+ starknet: "ETH",
280
+ fantom: "FTM",
281
+ metis: "METIS",
282
+ mantle: "MNT",
283
+ celo: "CELO",
284
+ gnosis: "xDAI",
285
+ frax: "frxETH",
286
+ worldchain: "ETH",
287
+ berachain: "BERA",
288
+ flow: "FLOW",
289
+ rootstock: "RBTC",
290
+ zetachain: "ZETA",
291
+ sui: "SUI"
292
+ };
293
+ function isSolanaNetwork(networkId) {
294
+ return networkId.startsWith("solana-");
295
+ }
296
+ function nativeTokenSymbol(networkId) {
297
+ const prefix = networkId.replace(/-(mainnet|testnet|sepolia|holesky|hoodi|devnet|amoy|fuji|cardona|saigon|chiado|signet|mocha|blaze|curtis|bepolia).*$/, "");
298
+ return NATIVE_TOKEN_SYMBOLS[prefix] ?? "ETH";
299
+ }
300
+
37
301
  // src/lib/client.ts
38
302
  var Client = class _Client {
39
303
  apiKey;
@@ -49,6 +313,11 @@ var Client = class _Client {
49
313
  if (this.rpcBaseURLOverride()) {
50
314
  return;
51
315
  }
316
+ if (!getRPCNetworkIds().includes(network)) {
317
+ throw errInvalidArgs(
318
+ `Unknown network '${network}'. Run 'alchemy evm network list' to see available networks.`
319
+ );
320
+ }
52
321
  const domain = getBaseDomain();
53
322
  const hostname = `${network}.g.${domain}`;
54
323
  let parsed;
@@ -346,516 +615,252 @@ var X402Client = class _X402Client {
346
615
  if (!resp.ok) {
347
616
  const text = await resp.text().catch(() => "");
348
617
  throw errNetwork(`HTTP ${resp.status}: ${text}`);
349
- }
350
- return resp.json();
351
- }
352
- async doFetch(url, init) {
353
- return fetchWithTimeout(url, init);
354
- }
355
- async parsePaymentError(resp) {
356
- const text = await resp.text().catch(() => "");
357
- try {
358
- const body = JSON.parse(text);
359
- const reason = body?.extensions?.paymentError?.info?.reason;
360
- const message = body?.extensions?.paymentError?.info?.message;
361
- const payer = body?.extensions?.paymentError?.info?.payer;
362
- if (reason === "insufficient_funds") {
363
- const network = body?.accepts?.[0]?.network;
364
- const asset = body?.accepts?.[0]?.extra?.name ?? "USDC";
365
- const networkLabel = network === "eip155:8453" ? "Base" : network ?? "the payment network";
366
- return new CLIError(
367
- ErrorCode.PAYMENT_REQUIRED,
368
- `Insufficient ${asset} balance on ${networkLabel}. ${message ?? ""}`.trim(),
369
- `Fund wallet ${payer ?? ""} with ${asset} on ${networkLabel} to use x402.`.trim()
370
- );
371
- }
372
- return new CLIError(
373
- ErrorCode.PAYMENT_REQUIRED,
374
- `x402 payment failed: ${message || body?.error || text}`
375
- );
376
- } catch {
377
- return new CLIError(
378
- ErrorCode.PAYMENT_REQUIRED,
379
- `x402 payment failed: ${text}`
380
- );
381
- }
382
- }
383
- async handleAuthAndPayment(resp, retries) {
384
- if (resp.status === 401) {
385
- const detail = await resp.text().catch(() => "");
386
- if (detail.includes("MESSAGE_EXPIRED")) {
387
- return retries.authRetry();
388
- }
389
- throw new CLIError(
390
- ErrorCode.AUTH_REQUIRED,
391
- `x402 authentication failed: ${detail || "unauthorized"}`,
392
- "Check your wallet key and try again."
393
- );
394
- }
395
- if (resp.status === 402) {
396
- const paymentRequiredHeader = resp.headers.get("payment-required");
397
- if (!paymentRequiredHeader) {
398
- throw new CLIError(
399
- ErrorCode.PAYMENT_REQUIRED,
400
- "x402 payment required but no Payment-Required header received."
401
- );
402
- }
403
- const paymentSignature = await createPayment({
404
- privateKey: this.privateKey,
405
- paymentRequiredHeader
406
- });
407
- return retries.paymentRetry(paymentSignature);
408
- }
409
- return resp;
410
- }
411
- };
412
-
413
- // src/lib/admin-client.ts
414
- var STAGING_ADMIN_API_HOST = "admin-api.alchemypreview.com";
415
- var AdminClient = class _AdminClient {
416
- static get ADMIN_API_HOST() {
417
- return `admin-api.${getBaseDomain()}`;
418
- }
419
- // Test/debug only: used by mock E2E to route admin requests locally.
420
- static ADMIN_API_BASE_URL_ENV = "ALCHEMY_ADMIN_API_BASE_URL";
421
- credential;
422
- constructor(credential) {
423
- if (typeof credential === "string") {
424
- this.validateAccessKey(credential);
425
- this.credential = { type: "access_key", key: credential };
426
- } else {
427
- if (credential.type === "access_key") {
428
- this.validateAccessKey(credential.key);
429
- } else if (!credential.token.trim()) {
430
- throw errAuthRequired();
431
- }
432
- this.credential = credential;
433
- }
434
- }
435
- baseURL() {
436
- const override = this.baseURLOverride();
437
- if (override) return override.toString().replace(/\/$/, "");
438
- return `https://admin-api.${getBaseDomain()}`;
439
- }
440
- allowedHosts() {
441
- const hosts = /* @__PURE__ */ new Set([_AdminClient.ADMIN_API_HOST]);
442
- const override = this.baseURLOverride();
443
- if (override) hosts.add(override.hostname);
444
- return hosts;
445
- }
446
- allowInsecureTransport(hostname) {
447
- return isLocalhost(hostname);
448
- }
449
- baseURLOverride() {
450
- return parseBaseURLOverride(_AdminClient.ADMIN_API_BASE_URL_ENV, {
451
- allowedHostnames: [STAGING_ADMIN_API_HOST]
452
- });
453
- }
454
- validateAccessKey(accessKey) {
455
- if (!accessKey.trim() || /\s/.test(accessKey)) {
456
- throw errInvalidAccessKey();
457
- }
458
- }
459
- assertSafeRequestTarget(url) {
460
- let parsed;
461
- try {
462
- parsed = new URL(url);
463
- } catch {
464
- throw errInvalidArgs("Invalid admin API URL.");
465
- }
466
- if (!this.allowedHosts().has(parsed.hostname)) {
467
- throw errInvalidArgs(`Refusing to send credentials to unexpected host: ${parsed.hostname}`);
468
- }
469
- if (parsed.protocol !== "https:" && !this.allowInsecureTransport(parsed.hostname)) {
470
- throw errInvalidArgs("Refusing to send credentials over non-HTTPS connection.");
471
- }
472
- }
473
- async request(method, path, body) {
474
- const url = `${this.baseURL()}${path}`;
475
- debug(`${method} ${url}`);
476
- this.assertSafeRequestTarget(url);
477
- const resp = await fetchWithTimeout(url, {
478
- method,
479
- redirect: "error",
480
- headers: {
481
- Authorization: `Bearer ${this.credential.type === "access_key" ? this.credential.key : this.credential.token}`,
482
- "Content-Type": "application/json",
483
- Accept: "application/json"
484
- },
485
- ...body !== void 0 && { body: JSON.stringify(body) }
486
- });
487
- if (resp.status === 401) {
488
- debug(`401 Unauthorized from ${url}`);
489
- throw errInvalidAccessKey();
490
- }
491
- if (resp.status === 403) {
492
- const detail = await resp.text().catch(() => "");
493
- let reason;
494
- try {
495
- const parsed = JSON.parse(detail);
496
- reason = parsed?.message || parsed?.error?.message || parsed?.error || void 0;
497
- } catch {
498
- reason = detail || void 0;
499
- }
500
- throw errAccessDenied(typeof reason === "string" ? reason : void 0);
501
- }
502
- if (resp.status === 404) {
503
- const text = await resp.text().catch(() => "");
504
- throw errNotFound(text || path);
505
- }
506
- if (resp.status === 429) throw errRateLimited();
507
- if (!resp.ok) {
508
- const text = await resp.text().catch(() => "");
509
- throw errAdminAPI(resp.status, text);
510
- }
511
- return resp.json();
512
- }
513
- async listChains() {
514
- const result = await this.request("GET", "/v1/chains");
515
- const chains = (Array.isArray(result.data) ? result.data : void 0) ?? (!Array.isArray(result.data) ? result.data?.networks : void 0) ?? (!Array.isArray(result.data) ? result.data?.chains : void 0) ?? result.networks ?? result.chains;
516
- if (!Array.isArray(chains)) {
517
- throw errAdminAPI(200, "Unexpected response shape for /v1/chains.");
518
- }
519
- return chains;
520
- }
521
- async listApps(opts) {
522
- const params = new URLSearchParams();
523
- if (opts?.cursor) params.set("cursor", opts.cursor);
524
- if (opts?.limit) params.set("limit", String(opts.limit));
525
- const qs = params.toString();
526
- const resp = await this.request(
527
- "GET",
528
- `/v1/apps${qs ? `?${qs}` : ""}`
529
- );
530
- return resp.data;
531
- }
532
- async listAllApps(opts) {
533
- const apps = [];
534
- const seenCursors = /* @__PURE__ */ new Set();
535
- let cursor;
536
- let pages = 0;
537
- do {
538
- const page = await this.listApps({
539
- ...cursor && { cursor },
540
- ...opts?.limit !== void 0 && { limit: opts.limit }
541
- });
542
- pages += 1;
543
- apps.push(...page.apps);
544
- cursor = page.cursor;
545
- if (cursor && seenCursors.has(cursor)) break;
546
- if (cursor) seenCursors.add(cursor);
547
- } while (cursor);
548
- return { apps, pages };
549
- }
550
- async getApp(id) {
551
- const resp = await this.request("GET", `/v1/apps/${id}`);
552
- return resp.data;
553
- }
554
- async createApp(opts) {
555
- const resp = await this.request("POST", "/v1/apps", {
556
- name: opts.name,
557
- chainNetworks: opts.networks,
558
- ...opts.description && { description: opts.description },
559
- ...opts.products && { products: opts.products }
560
- });
561
- return resp.data;
562
- }
563
- async deleteApp(id) {
564
- await this.request("DELETE", `/v1/apps/${id}`);
565
- }
566
- async updateApp(id, opts) {
567
- const resp = await this.request("PATCH", `/v1/apps/${id}`, opts);
568
- return resp.data;
569
- }
570
- async updateNetworkAllowlist(id, networks) {
571
- const resp = await this.request("PUT", `/v1/apps/${id}/networks`, {
572
- chainNetworks: networks
573
- });
574
- return resp.data;
575
- }
576
- async updateAddressAllowlist(id, addresses) {
577
- const resp = await this.request("PUT", `/v1/apps/${id}/address-allowlist`, {
578
- addressAllowlist: addresses
579
- });
580
- return resp.data;
581
- }
582
- async updateOriginAllowlist(id, origins) {
583
- const resp = await this.request("PUT", `/v1/apps/${id}/origin-allowlist`, {
584
- originAllowlist: origins
585
- });
586
- return resp.data;
618
+ }
619
+ return resp.json();
587
620
  }
588
- async updateIpAllowlist(id, ips) {
589
- const resp = await this.request("PUT", `/v1/apps/${id}/ip-allowlist`, {
590
- ipAllowlist: ips
591
- });
592
- return resp.data;
621
+ async doFetch(url, init) {
622
+ return fetchWithTimeout(url, init);
593
623
  }
594
- };
595
-
596
- // src/lib/networks.ts
597
- 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;
598
- var FAMILY_ALIASES = {
599
- arb: "Arbitrum",
600
- arbnova: "Arbitrum Nova",
601
- avax: "Avalanche",
602
- bnb: "BNB Smart Chain",
603
- eth: "Ethereum",
604
- opt: "OP Mainnet",
605
- polygonzkevm: "Polygon zkEVM"
606
- };
607
- var NAME_ALIASES = {
608
- arb: "Arbitrum",
609
- avax: "Avalanche",
610
- bnb: "BNB",
611
- eth: "Ethereum",
612
- opbnb: "opBNB",
613
- opt: "OP Mainnet",
614
- sui: "SUI",
615
- xmtp: "XMTP",
616
- zksync: "ZKsync"
617
- };
618
- var RPC_NETWORK_IDS = [
619
- "abstract-mainnet",
620
- "abstract-testnet",
621
- "adi-mainnet",
622
- "adi-testnet",
623
- "alchemy-internal",
624
- "alchemy-sepolia",
625
- "alchemyarb-fam",
626
- "alchemyarb-sepolia",
627
- "alterscope-mainnet",
628
- "anime-mainnet",
629
- "anime-sepolia",
630
- "apechain-curtis",
631
- "apechain-mainnet",
632
- "aptos-mainnet",
633
- "aptos-testnet",
634
- "arb-mainnet",
635
- "arb-sepolia",
636
- "arbnova-mainnet",
637
- "arc-testnet",
638
- "astar-mainnet",
639
- "avax-fuji",
640
- "avax-mainnet",
641
- "base-mainnet",
642
- "base-sepolia",
643
- "berachain-bepolia",
644
- "berachain-mainnet",
645
- "bitcoin-mainnet",
646
- "bitcoin-signet",
647
- "bitcoin-testnet",
648
- "blast-mainnet",
649
- "blast-sepolia",
650
- "bnb-mainnet",
651
- "bnb-testnet",
652
- "bob-mainnet",
653
- "bob-sepolia",
654
- "boba-mainnet",
655
- "boba-sepolia",
656
- "botanix-mainnet",
657
- "botanix-testnet",
658
- "celestiabridge-mainnet",
659
- "celestiabridge-mocha",
660
- "celo-mainnet",
661
- "celo-sepolia",
662
- "citrea-mainnet",
663
- "citrea-testnet",
664
- "clankermon-mainnet",
665
- "commons-mainnet",
666
- "crossfi-mainnet",
667
- "crossfi-testnet",
668
- "degen-mainnet",
669
- "degen-sepolia",
670
- "earnm-mainnet",
671
- "earnm-sepolia",
672
- "edge-mainnet",
673
- "edge-testnet",
674
- "eth-holesky",
675
- "eth-holeskybeacon",
676
- "eth-hoodi",
677
- "eth-hoodibeacon",
678
- "eth-mainnet",
679
- "eth-mainnetbeacon",
680
- "eth-sepolia",
681
- "eth-sepoliabeacon",
682
- "flow-mainnet",
683
- "flow-testnet",
684
- "frax-hoodi",
685
- "frax-mainnet",
686
- "galactica-cassiopeia",
687
- "galactica-mainnet",
688
- "gensyn-mainnet",
689
- "gensyn-testnet",
690
- "gnosis-chiado",
691
- "gnosis-mainnet",
692
- "humanity-mainnet",
693
- "humanity-testnet",
694
- "hyperliquid-mainnet",
695
- "hyperliquid-testnet",
696
- "ink-mainnet",
697
- "ink-sepolia",
698
- "lens-mainnet",
699
- "lens-sepolia",
700
- "linea-mainnet",
701
- "linea-sepolia",
702
- "mantle-mainnet",
703
- "mantle-sepolia",
704
- "megaeth-mainnet",
705
- "megaeth-testnet",
706
- "metis-mainnet",
707
- "mode-mainnet",
708
- "mode-sepolia",
709
- "monad-mainnet",
710
- "monad-testnet",
711
- "moonbeam-mainnet",
712
- "mythos-mainnet",
713
- "opbnb-mainnet",
714
- "opbnb-testnet",
715
- "openloot-sepolia",
716
- "opt-mainnet",
717
- "opt-sepolia",
718
- "plasma-mainnet",
719
- "plasma-testnet",
720
- "polygon-amoy",
721
- "polygon-mainnet",
722
- "polygonzkevm-cardona",
723
- "polygonzkevm-mainnet",
724
- "polynomial-mainnet",
725
- "polynomial-sepolia",
726
- "race-mainnet",
727
- "race-sepolia",
728
- "risa-testnet",
729
- "rise-testnet",
730
- "ronin-mainnet",
731
- "ronin-saigon",
732
- "rootstock-mainnet",
733
- "rootstock-testnet",
734
- "scroll-mainnet",
735
- "scroll-sepolia",
736
- "sei-mainnet",
737
- "sei-testnet",
738
- "settlus-mainnet",
739
- "settlus-septestnet",
740
- "shape-mainnet",
741
- "shape-sepolia",
742
- "solana-devnet",
743
- "solana-mainnet",
744
- "soneium-mainnet",
745
- "soneium-minato",
746
- "sonic-blaze",
747
- "sonic-mainnet",
748
- "sonic-testnet",
749
- "stable-mainnet",
750
- "stable-testnet",
751
- "standard-mainnet",
752
- "starknet-mainnet",
753
- "starknet-sepolia",
754
- "story-aeneid",
755
- "story-mainnet",
756
- "sui-mainnet",
757
- "sui-testnet",
758
- "superseed-mainnet",
759
- "superseed-sepolia",
760
- "synd-mainnet",
761
- "syndicate-manchego",
762
- "tea-sepolia",
763
- "tempo-testnet",
764
- "tron-mainnet",
765
- "tron-testnet",
766
- "unichain-mainnet",
767
- "unichain-sepolia",
768
- "unite-mainnet",
769
- "unite-testnet",
770
- "worldchain-mainnet",
771
- "worldchain-sepolia",
772
- "worldl3-devnet",
773
- "worldmobile-devnet",
774
- "worldmobile-testnet",
775
- "worldmobilechain-mainnet",
776
- "xmtp-mainnet",
777
- "xmtp-ropsten",
778
- "xprotocol-mainnet",
779
- "zetachain-mainnet",
780
- "zetachain-testnet",
781
- "zksync-mainnet",
782
- "zksync-sepolia",
783
- "zora-mainnet",
784
- "zora-sepolia"
785
- ];
786
- function isTestnetNetwork(id) {
787
- return TESTNET_TOKEN_RE.test(id);
788
- }
789
- function tokenToName(token) {
790
- const alias = NAME_ALIASES[token];
791
- if (alias) return alias;
792
- return token.charAt(0).toUpperCase() + token.slice(1);
793
- }
794
- function toFamily(id) {
795
- const [head] = id.split("-");
796
- return FAMILY_ALIASES[head] ?? tokenToName(head);
797
- }
798
- function toDisplayName(id) {
799
- return id.split("-").map((part) => tokenToName(part)).join(" ");
800
- }
801
- function toHttpsUrlTemplate(id) {
802
- const domain = getBaseDomain();
803
- if (id === "starknet-mainnet" || id === "starknet-sepolia") {
804
- return `https://${id}.g.${domain}/starknet/version/rpc/v0_10/{apiKey}`;
624
+ async parsePaymentError(resp) {
625
+ const text = await resp.text().catch(() => "");
626
+ try {
627
+ const body = JSON.parse(text);
628
+ const reason = body?.extensions?.paymentError?.info?.reason;
629
+ const message = body?.extensions?.paymentError?.info?.message;
630
+ const payer = body?.extensions?.paymentError?.info?.payer;
631
+ if (reason === "insufficient_funds") {
632
+ const network = body?.accepts?.[0]?.network;
633
+ const asset = body?.accepts?.[0]?.extra?.name ?? "USDC";
634
+ const networkLabel = network === "eip155:8453" ? "Base" : network ?? "the payment network";
635
+ return new CLIError(
636
+ ErrorCode.PAYMENT_REQUIRED,
637
+ `Insufficient ${asset} balance on ${networkLabel}. ${message ?? ""}`.trim(),
638
+ `Fund wallet ${payer ?? ""} with ${asset} on ${networkLabel} to use x402.`.trim()
639
+ );
640
+ }
641
+ return new CLIError(
642
+ ErrorCode.PAYMENT_REQUIRED,
643
+ `x402 payment failed: ${message || body?.error || text}`
644
+ );
645
+ } catch {
646
+ return new CLIError(
647
+ ErrorCode.PAYMENT_REQUIRED,
648
+ `x402 payment failed: ${text}`
649
+ );
650
+ }
651
+ }
652
+ async handleAuthAndPayment(resp, retries) {
653
+ if (resp.status === 401) {
654
+ const detail = await resp.text().catch(() => "");
655
+ if (detail.includes("MESSAGE_EXPIRED")) {
656
+ return retries.authRetry();
657
+ }
658
+ throw new CLIError(
659
+ ErrorCode.AUTH_REQUIRED,
660
+ `x402 authentication failed: ${detail || "unauthorized"}`,
661
+ "Check your wallet key and try again."
662
+ );
663
+ }
664
+ if (resp.status === 402) {
665
+ const paymentRequiredHeader = resp.headers.get("payment-required");
666
+ if (!paymentRequiredHeader) {
667
+ throw new CLIError(
668
+ ErrorCode.PAYMENT_REQUIRED,
669
+ "x402 payment required but no Payment-Required header received."
670
+ );
671
+ }
672
+ const paymentSignature = await createPayment({
673
+ privateKey: this.privateKey,
674
+ paymentRequiredHeader
675
+ });
676
+ return retries.paymentRetry(paymentSignature);
677
+ }
678
+ return resp;
679
+ }
680
+ };
681
+
682
+ // src/lib/admin-client.ts
683
+ var STAGING_ADMIN_API_HOST = "admin-api.alchemypreview.com";
684
+ var AdminClient = class _AdminClient {
685
+ static get ADMIN_API_HOST() {
686
+ return `admin-api.${getBaseDomain()}`;
687
+ }
688
+ // Test/debug only: used by mock E2E to route admin requests locally.
689
+ static ADMIN_API_BASE_URL_ENV = "ALCHEMY_ADMIN_API_BASE_URL";
690
+ credential;
691
+ constructor(credential) {
692
+ if (typeof credential === "string") {
693
+ this.validateAccessKey(credential);
694
+ this.credential = { type: "access_key", key: credential };
695
+ } else {
696
+ if (credential.type === "access_key") {
697
+ this.validateAccessKey(credential.key);
698
+ } else if (!credential.token.trim()) {
699
+ throw errAuthRequired();
700
+ }
701
+ this.credential = credential;
702
+ }
703
+ }
704
+ baseURL() {
705
+ const override = this.baseURLOverride();
706
+ if (override) return override.toString().replace(/\/$/, "");
707
+ return `https://admin-api.${getBaseDomain()}`;
708
+ }
709
+ allowedHosts() {
710
+ const hosts = /* @__PURE__ */ new Set([_AdminClient.ADMIN_API_HOST]);
711
+ const override = this.baseURLOverride();
712
+ if (override) hosts.add(override.hostname);
713
+ return hosts;
714
+ }
715
+ allowInsecureTransport(hostname) {
716
+ return isLocalhost(hostname);
717
+ }
718
+ baseURLOverride() {
719
+ return parseBaseURLOverride(_AdminClient.ADMIN_API_BASE_URL_ENV, {
720
+ allowedHostnames: [STAGING_ADMIN_API_HOST]
721
+ });
722
+ }
723
+ validateAccessKey(accessKey) {
724
+ if (!accessKey.trim() || /\s/.test(accessKey)) {
725
+ throw errInvalidAccessKey();
726
+ }
727
+ }
728
+ assertSafeRequestTarget(url) {
729
+ let parsed;
730
+ try {
731
+ parsed = new URL(url);
732
+ } catch {
733
+ throw errInvalidArgs("Invalid admin API URL.");
734
+ }
735
+ if (!this.allowedHosts().has(parsed.hostname)) {
736
+ throw errInvalidArgs(`Refusing to send credentials to unexpected host: ${parsed.hostname}`);
737
+ }
738
+ if (parsed.protocol !== "https:" && !this.allowInsecureTransport(parsed.hostname)) {
739
+ throw errInvalidArgs("Refusing to send credentials over non-HTTPS connection.");
740
+ }
741
+ }
742
+ async request(method, path, body) {
743
+ const url = `${this.baseURL()}${path}`;
744
+ debug(`${method} ${url}`);
745
+ this.assertSafeRequestTarget(url);
746
+ const resp = await fetchWithTimeout(url, {
747
+ method,
748
+ redirect: "error",
749
+ headers: {
750
+ Authorization: `Bearer ${this.credential.type === "access_key" ? this.credential.key : this.credential.token}`,
751
+ "Content-Type": "application/json",
752
+ Accept: "application/json"
753
+ },
754
+ ...body !== void 0 && { body: JSON.stringify(body) }
755
+ });
756
+ if (resp.status === 401) {
757
+ debug(`401 Unauthorized from ${url}`);
758
+ throw errInvalidAccessKey();
759
+ }
760
+ if (resp.status === 403) {
761
+ const detail = await resp.text().catch(() => "");
762
+ let reason;
763
+ try {
764
+ const parsed = JSON.parse(detail);
765
+ reason = parsed?.message || parsed?.error?.message || parsed?.error || void 0;
766
+ } catch {
767
+ reason = detail || void 0;
768
+ }
769
+ throw errAccessDenied(typeof reason === "string" ? reason : void 0);
770
+ }
771
+ if (resp.status === 404) {
772
+ const text = await resp.text().catch(() => "");
773
+ throw errNotFound(text || path);
774
+ }
775
+ if (resp.status === 429) throw errRateLimited();
776
+ if (!resp.ok) {
777
+ const text = await resp.text().catch(() => "");
778
+ throw errAdminAPI(resp.status, text);
779
+ }
780
+ return resp.json();
781
+ }
782
+ async listChains() {
783
+ const result = await this.request("GET", "/v1/chains");
784
+ const chains = (Array.isArray(result.data) ? result.data : void 0) ?? (!Array.isArray(result.data) ? result.data?.networks : void 0) ?? (!Array.isArray(result.data) ? result.data?.chains : void 0) ?? result.networks ?? result.chains;
785
+ if (!Array.isArray(chains)) {
786
+ throw errAdminAPI(200, "Unexpected response shape for /v1/chains.");
787
+ }
788
+ return chains;
789
+ }
790
+ async listApps(opts) {
791
+ const params = new URLSearchParams();
792
+ if (opts?.cursor) params.set("cursor", opts.cursor);
793
+ if (opts?.limit) params.set("limit", String(opts.limit));
794
+ const qs = params.toString();
795
+ const resp = await this.request(
796
+ "GET",
797
+ `/v1/apps${qs ? `?${qs}` : ""}`
798
+ );
799
+ return resp.data;
800
+ }
801
+ async listAllApps(opts) {
802
+ const apps = [];
803
+ const seenCursors = /* @__PURE__ */ new Set();
804
+ let cursor;
805
+ let pages = 0;
806
+ do {
807
+ const page = await this.listApps({
808
+ ...cursor && { cursor },
809
+ ...opts?.limit !== void 0 && { limit: opts.limit }
810
+ });
811
+ pages += 1;
812
+ apps.push(...page.apps);
813
+ cursor = page.cursor;
814
+ if (cursor && seenCursors.has(cursor)) break;
815
+ if (cursor) seenCursors.add(cursor);
816
+ } while (cursor);
817
+ return { apps, pages };
818
+ }
819
+ async getApp(id) {
820
+ const resp = await this.request("GET", `/v1/apps/${id}`);
821
+ return resp.data;
822
+ }
823
+ async createApp(opts) {
824
+ const resp = await this.request("POST", "/v1/apps", {
825
+ name: opts.name,
826
+ chainNetworks: opts.networks,
827
+ ...opts.description && { description: opts.description },
828
+ ...opts.products && { products: opts.products }
829
+ });
830
+ return resp.data;
831
+ }
832
+ async deleteApp(id) {
833
+ await this.request("DELETE", `/v1/apps/${id}`);
834
+ }
835
+ async updateApp(id, opts) {
836
+ const resp = await this.request("PATCH", `/v1/apps/${id}`, opts);
837
+ return resp.data;
838
+ }
839
+ async updateNetworkAllowlist(id, networks) {
840
+ const resp = await this.request("PUT", `/v1/apps/${id}/networks`, {
841
+ chainNetworks: networks
842
+ });
843
+ return resp.data;
844
+ }
845
+ async updateAddressAllowlist(id, addresses) {
846
+ const resp = await this.request("PUT", `/v1/apps/${id}/address-allowlist`, {
847
+ addressAllowlist: addresses
848
+ });
849
+ return resp.data;
850
+ }
851
+ async updateOriginAllowlist(id, origins) {
852
+ const resp = await this.request("PUT", `/v1/apps/${id}/origin-allowlist`, {
853
+ originAllowlist: origins
854
+ });
855
+ return resp.data;
856
+ }
857
+ async updateIpAllowlist(id, ips) {
858
+ const resp = await this.request("PUT", `/v1/apps/${id}/ip-allowlist`, {
859
+ ipAllowlist: ips
860
+ });
861
+ return resp.data;
805
862
  }
806
- return `https://${id}.g.${domain}/v2/{apiKey}`;
807
- }
808
- function getRPCNetworks() {
809
- return RPC_NETWORK_IDS.map((id) => ({
810
- id,
811
- name: toDisplayName(id),
812
- family: toFamily(id),
813
- isTestnet: isTestnetNetwork(id),
814
- httpsUrlTemplate: toHttpsUrlTemplate(id)
815
- }));
816
- }
817
- function getRPCNetworkIds() {
818
- return [...RPC_NETWORK_IDS];
819
- }
820
- var NATIVE_TOKEN_SYMBOLS = {
821
- eth: "ETH",
822
- arb: "ETH",
823
- arbnova: "ETH",
824
- opt: "ETH",
825
- base: "ETH",
826
- zksync: "ETH",
827
- scroll: "ETH",
828
- blast: "ETH",
829
- linea: "ETH",
830
- zora: "ETH",
831
- shape: "ETH",
832
- polygon: "POL",
833
- polygonzkevm: "ETH",
834
- bnb: "BNB",
835
- opbnb: "BNB",
836
- avax: "AVAX",
837
- solana: "SOL",
838
- starknet: "ETH",
839
- fantom: "FTM",
840
- metis: "METIS",
841
- mantle: "MNT",
842
- celo: "CELO",
843
- gnosis: "xDAI",
844
- frax: "frxETH",
845
- worldchain: "ETH",
846
- berachain: "BERA",
847
- flow: "FLOW",
848
- rootstock: "RBTC",
849
- zetachain: "ZETA",
850
- sui: "SUI"
851
863
  };
852
- function isSolanaNetwork(networkId) {
853
- return networkId.startsWith("solana-");
854
- }
855
- function nativeTokenSymbol(networkId) {
856
- const prefix = networkId.replace(/-(mainnet|testnet|sepolia|holesky|hoodi|devnet|amoy|fuji|cardona|saigon|chiado|signet|mocha|blaze|curtis|bepolia).*$/, "");
857
- return NATIVE_TOKEN_SYMBOLS[prefix] ?? "ETH";
858
- }
859
864
 
860
865
  // src/lib/wallet-session.ts
861
866
  import { generateKeyPairSync, randomUUID } from "crypto";
@@ -1232,11 +1237,11 @@ function resolveWalletSession() {
1232
1237
  var resolveDelegatedSession = resolveWalletSession;
1233
1238
 
1234
1239
  export {
1235
- AdminClient,
1236
1240
  getRPCNetworks,
1237
1241
  getRPCNetworkIds,
1238
1242
  isSolanaNetwork,
1239
1243
  nativeTokenSymbol,
1244
+ AdminClient,
1240
1245
  createPendingSession,
1241
1246
  loadSession,
1242
1247
  loadStoredSession,