@alchemy/cli 0.7.2 → 0.7.4-alpha.37
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/{auth-TA3SL4BL.js → auth-D6CT363I.js} +2 -2
- package/dist/auth-R5QHPFMA.js +16 -0
- package/dist/{chunk-LQXLZLCY.js → chunk-2OUAYCVA.js} +6 -6
- package/dist/{chunk-5LCA2B6S.js → chunk-5HYOZ773.js} +610 -483
- package/dist/{chunk-MB6REYQL.js → chunk-6UHKZ5EN.js} +3 -3
- package/dist/{chunk-RE5HSYJJ.js → chunk-AUGBYMHT.js} +1 -1
- package/dist/{chunk-DGKUBK7G.js → chunk-HR2UZ6ZU.js} +1 -1
- package/dist/{chunk-KLPWJFWP.js → chunk-MYHXAACL.js} +6 -23
- package/dist/{chunk-BAZ4NGOD.js → chunk-PX2YJ7XC.js} +1 -1
- package/dist/chunk-UYZH6GSY.js +134 -0
- package/dist/{chunk-INVT5BV6.js → chunk-WCZIVY4O.js} +1 -1
- package/dist/{errors-J6HNGXVA.js → errors-UL3W4ECQ.js} +1 -1
- package/dist/index.js +1114 -279
- package/dist/{interactive-JNTFVCUJ.js → interactive-Z2YHE6ME.js} +13 -10
- package/dist/{onboarding-WN3IMTGS.js → onboarding-Q5PBXH3M.js} +6 -6
- package/dist/{resolve-ZCR3YCHJ.js → resolve-PAQKIAX3.js} +3 -3
- package/package.json +18 -24
- package/scripts/postinstall.cjs +2 -0
- package/dist/auth-7QNYRHIK.js +0 -16
- package/dist/chunk-EZ2ZD7YO.js +0 -64
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
configDir,
|
|
5
5
|
load,
|
|
6
6
|
save
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PX2YJ7XC.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-
|
|
32
|
+
} from "./chunk-MYHXAACL.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;
|
|
@@ -376,486 +645,222 @@ var X402Client = class _X402Client {
|
|
|
376
645
|
} catch {
|
|
377
646
|
return new CLIError(
|
|
378
647
|
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;
|
|
648
|
+
`x402 payment failed: ${text}`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
587
651
|
}
|
|
588
|
-
async
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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;
|
|
593
679
|
}
|
|
594
680
|
};
|
|
595
681
|
|
|
596
|
-
// src/lib/
|
|
597
|
-
var
|
|
598
|
-
var
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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}`;
|
|
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";
|
|
@@ -879,7 +884,20 @@ var walletSessionEnvironmentSchema = z.object({
|
|
|
879
884
|
clientInstanceId: z.string().uuid().optional(),
|
|
880
885
|
clientInstanceName: z.string().min(1).max(64).optional()
|
|
881
886
|
}).strict();
|
|
887
|
+
var chainWalletSessionSchema = z.object({
|
|
888
|
+
sessionId: z.string().uuid(),
|
|
889
|
+
status: z.enum(["pending", "approved", "revoked", "expired"]),
|
|
890
|
+
expiresAt: z.string().datetime(),
|
|
891
|
+
chainType: z.enum(["evm", "solana"]),
|
|
892
|
+
walletId: z.string().min(1).optional(),
|
|
893
|
+
walletAddress: z.string().min(1).optional(),
|
|
894
|
+
providerKeyQuorumId: z.string().min(1).optional(),
|
|
895
|
+
providerSignerId: z.string().min(1).optional(),
|
|
896
|
+
capabilities: walletSessionCapabilitiesSchema.optional()
|
|
897
|
+
}).strict();
|
|
882
898
|
var walletSessionSchema = z.object({
|
|
899
|
+
version: z.number().int().positive().optional(),
|
|
900
|
+
connectionRequestId: z.string().min(1).optional(),
|
|
883
901
|
sessionId: z.string().uuid(),
|
|
884
902
|
status: z.enum(["pending", "approved", "revoked", "expired"]),
|
|
885
903
|
createdAt: z.string().datetime(),
|
|
@@ -898,7 +916,11 @@ var walletSessionSchema = z.object({
|
|
|
898
916
|
chainType: z.string().min(1).optional(),
|
|
899
917
|
backendBaseUrl: z.string().min(1).optional(),
|
|
900
918
|
environment: walletSessionEnvironmentSchema.optional(),
|
|
901
|
-
capabilities: walletSessionCapabilitiesSchema.optional()
|
|
919
|
+
capabilities: walletSessionCapabilitiesSchema.optional(),
|
|
920
|
+
sessionsByChain: z.object({
|
|
921
|
+
evm: chainWalletSessionSchema.optional(),
|
|
922
|
+
solana: chainWalletSessionSchema.optional()
|
|
923
|
+
}).strict().optional()
|
|
902
924
|
}).strict();
|
|
903
925
|
function sessionPath() {
|
|
904
926
|
return join(configDir(), SESSION_FILE);
|
|
@@ -906,7 +928,59 @@ function sessionPath() {
|
|
|
906
928
|
function parseStoredSession(value) {
|
|
907
929
|
const parsed = walletSessionSchema.safeParse(value);
|
|
908
930
|
if (!parsed.success) return null;
|
|
909
|
-
return parsed.data;
|
|
931
|
+
return withLegacyChainSessions(parsed.data);
|
|
932
|
+
}
|
|
933
|
+
function withLegacyChainSessions(session) {
|
|
934
|
+
if (session.sessionsByChain) {
|
|
935
|
+
return withCompatibilityAliases(session);
|
|
936
|
+
}
|
|
937
|
+
const chainType = session.chainType === "solana" ? "solana" : "evm";
|
|
938
|
+
const walletId = chainType === "solana" ? session.solanaWalletId ?? session.walletId : session.evmWalletId ?? session.walletId;
|
|
939
|
+
const walletAddress = chainType === "solana" ? session.solanaAddress : session.evmAddress;
|
|
940
|
+
return withCompatibilityAliases({
|
|
941
|
+
...session,
|
|
942
|
+
version: session.version ?? 1,
|
|
943
|
+
sessionsByChain: {
|
|
944
|
+
[chainType]: {
|
|
945
|
+
sessionId: session.sessionId,
|
|
946
|
+
status: session.status,
|
|
947
|
+
expiresAt: session.expiresAt,
|
|
948
|
+
chainType,
|
|
949
|
+
...walletId ? { walletId } : {},
|
|
950
|
+
...walletAddress ? { walletAddress } : {},
|
|
951
|
+
...session.privyKeyQuorumId ? { providerKeyQuorumId: session.privyKeyQuorumId } : {},
|
|
952
|
+
...session.privySignerId ? { providerSignerId: session.privySignerId } : {},
|
|
953
|
+
...session.capabilities ? { capabilities: session.capabilities } : {}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
function withCompatibilityAliases(session) {
|
|
959
|
+
const evm = session.sessionsByChain?.evm;
|
|
960
|
+
const solana = session.sessionsByChain?.solana;
|
|
961
|
+
const preferred = evm ?? solana;
|
|
962
|
+
if (!preferred) {
|
|
963
|
+
return session;
|
|
964
|
+
}
|
|
965
|
+
return {
|
|
966
|
+
...session,
|
|
967
|
+
sessionId: preferred.sessionId,
|
|
968
|
+
status: preferred.status,
|
|
969
|
+
expiresAt: preferred.expiresAt,
|
|
970
|
+
walletId: evm?.walletId ?? solana?.walletId ?? session.walletId,
|
|
971
|
+
evmWalletId: evm?.walletId ?? session.evmWalletId,
|
|
972
|
+
evmAddress: evm?.walletAddress ?? session.evmAddress,
|
|
973
|
+
solanaWalletId: solana?.walletId ?? session.solanaWalletId,
|
|
974
|
+
solanaAddress: solana?.walletAddress ?? session.solanaAddress,
|
|
975
|
+
privyKeyQuorumId: evm?.providerKeyQuorumId ?? solana?.providerKeyQuorumId ?? session.privyKeyQuorumId,
|
|
976
|
+
privySignerId: evm?.providerSignerId ?? solana?.providerSignerId ?? session.privySignerId,
|
|
977
|
+
chainType: evm && solana ? "both" : preferred.chainType,
|
|
978
|
+
capabilities: {
|
|
979
|
+
...evm?.capabilities ?? {},
|
|
980
|
+
...solana?.capabilities ?? {},
|
|
981
|
+
...session.capabilities ?? {}
|
|
982
|
+
}
|
|
983
|
+
};
|
|
910
984
|
}
|
|
911
985
|
function loadStoredSessionFromPath(path) {
|
|
912
986
|
if (!existsSync(path)) return null;
|
|
@@ -920,13 +994,23 @@ function isExpired(session) {
|
|
|
920
994
|
const expiresAt = new Date(session.expiresAt);
|
|
921
995
|
return !Number.isNaN(expiresAt.getTime()) && expiresAt <= /* @__PURE__ */ new Date();
|
|
922
996
|
}
|
|
997
|
+
function isSessionLoadable(session) {
|
|
998
|
+
if (session.sessionsByChain) {
|
|
999
|
+
return Object.values(session.sessionsByChain).some((chainSession) => {
|
|
1000
|
+
return Boolean(
|
|
1001
|
+
chainSession && chainSession.status !== "revoked" && !isExpired(chainSession)
|
|
1002
|
+
);
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
if (session.status === "revoked") return false;
|
|
1006
|
+
return !isExpired(session);
|
|
1007
|
+
}
|
|
923
1008
|
function createFileWalletSessionStore(path = sessionPath()) {
|
|
924
1009
|
return {
|
|
925
1010
|
load() {
|
|
926
1011
|
const session = loadStoredSessionFromPath(path);
|
|
927
1012
|
if (!session) return null;
|
|
928
|
-
if (session
|
|
929
|
-
if (isExpired(session)) return null;
|
|
1013
|
+
if (!isSessionLoadable(session)) return null;
|
|
930
1014
|
return session;
|
|
931
1015
|
},
|
|
932
1016
|
loadRaw() {
|
|
@@ -970,6 +1054,7 @@ function createPendingSession() {
|
|
|
970
1054
|
const now = /* @__PURE__ */ new Date();
|
|
971
1055
|
const expiresAt = new Date(now.getTime() + SESSION_TTL_DAYS * 24 * 60 * 60 * 1e3);
|
|
972
1056
|
const session = {
|
|
1057
|
+
version: 2,
|
|
973
1058
|
sessionId: randomUUID(),
|
|
974
1059
|
status: "pending",
|
|
975
1060
|
createdAt: now.toISOString(),
|
|
@@ -992,12 +1077,12 @@ function loadStoredSession() {
|
|
|
992
1077
|
return store.load();
|
|
993
1078
|
}
|
|
994
1079
|
function saveSession(session) {
|
|
995
|
-
getWalletSessionStore().save(session);
|
|
1080
|
+
getWalletSessionStore().save(withCompatibilityAliases(session));
|
|
996
1081
|
}
|
|
997
1082
|
function updateSession(updates) {
|
|
998
1083
|
const session = loadSession();
|
|
999
1084
|
if (!session) return null;
|
|
1000
|
-
const updated = { ...session, ...updates };
|
|
1085
|
+
const updated = withCompatibilityAliases({ ...session, ...updates });
|
|
1001
1086
|
saveSession(updated);
|
|
1002
1087
|
return updated;
|
|
1003
1088
|
}
|
|
@@ -1005,11 +1090,52 @@ function clearSession() {
|
|
|
1005
1090
|
return getWalletSessionStore().clear();
|
|
1006
1091
|
}
|
|
1007
1092
|
function isSessionValid(session) {
|
|
1093
|
+
if (session.sessionsByChain) {
|
|
1094
|
+
return Object.values(session.sessionsByChain).some((chainSession) => {
|
|
1095
|
+
return Boolean(chainSession && isChainSessionValid(chainSession));
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
if (session.status !== "approved") return false;
|
|
1099
|
+
const expiresAt = new Date(session.expiresAt);
|
|
1100
|
+
if (Number.isNaN(expiresAt.getTime())) return false;
|
|
1101
|
+
return expiresAt > /* @__PURE__ */ new Date();
|
|
1102
|
+
}
|
|
1103
|
+
function isChainSessionValid(session) {
|
|
1008
1104
|
if (session.status !== "approved") return false;
|
|
1009
1105
|
const expiresAt = new Date(session.expiresAt);
|
|
1010
1106
|
if (Number.isNaN(expiresAt.getTime())) return false;
|
|
1011
1107
|
return expiresAt > /* @__PURE__ */ new Date();
|
|
1012
1108
|
}
|
|
1109
|
+
function getWalletSessionByChain(session, chainType) {
|
|
1110
|
+
const chainSession = session.sessionsByChain?.[chainType];
|
|
1111
|
+
if (!chainSession) {
|
|
1112
|
+
if (!isSessionValid(session)) return null;
|
|
1113
|
+
if (chainType === "evm" && session.evmAddress) return session;
|
|
1114
|
+
if (chainType === "solana" && session.solanaAddress) return session;
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
if (!isChainSessionValid(chainSession)) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
return withCompatibilityAliases({
|
|
1121
|
+
...session,
|
|
1122
|
+
sessionId: chainSession.sessionId,
|
|
1123
|
+
status: chainSession.status,
|
|
1124
|
+
expiresAt: chainSession.expiresAt,
|
|
1125
|
+
walletId: chainSession.walletId,
|
|
1126
|
+
evmWalletId: chainType === "evm" ? chainSession.walletId : session.evmWalletId,
|
|
1127
|
+
evmAddress: chainType === "evm" ? chainSession.walletAddress : session.evmAddress,
|
|
1128
|
+
solanaWalletId: chainType === "solana" ? chainSession.walletId : session.solanaWalletId,
|
|
1129
|
+
solanaAddress: chainType === "solana" ? chainSession.walletAddress : session.solanaAddress,
|
|
1130
|
+
privyKeyQuorumId: chainSession.providerKeyQuorumId,
|
|
1131
|
+
privySignerId: chainSession.providerSignerId,
|
|
1132
|
+
chainType,
|
|
1133
|
+
capabilities: chainSession.capabilities ?? session.capabilities,
|
|
1134
|
+
sessionsByChain: {
|
|
1135
|
+
[chainType]: chainSession
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1013
1139
|
|
|
1014
1140
|
// src/lib/resolve.ts
|
|
1015
1141
|
function getCommandOptions(program) {
|
|
@@ -1232,11 +1358,11 @@ function resolveWalletSession() {
|
|
|
1232
1358
|
var resolveDelegatedSession = resolveWalletSession;
|
|
1233
1359
|
|
|
1234
1360
|
export {
|
|
1235
|
-
AdminClient,
|
|
1236
1361
|
getRPCNetworks,
|
|
1237
1362
|
getRPCNetworkIds,
|
|
1238
1363
|
isSolanaNetwork,
|
|
1239
1364
|
nativeTokenSymbol,
|
|
1365
|
+
AdminClient,
|
|
1240
1366
|
createPendingSession,
|
|
1241
1367
|
loadSession,
|
|
1242
1368
|
loadStoredSession,
|
|
@@ -1244,6 +1370,7 @@ export {
|
|
|
1244
1370
|
updateSession,
|
|
1245
1371
|
clearSession,
|
|
1246
1372
|
isSessionValid,
|
|
1373
|
+
getWalletSessionByChain,
|
|
1247
1374
|
resolveAPIKey,
|
|
1248
1375
|
resolveAccessKey,
|
|
1249
1376
|
resolveNetwork,
|