@alchemy/cli 0.7.4 → 0.9.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
@@ -1,27 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
+ import {
4
+ createPolicyInteractive,
5
+ errNotLoggedInForPolicyLookup,
6
+ errSponsorshipNeedsPolicy,
7
+ selectOrCreatePolicy
8
+ } from "./chunk-AJPOUEO6.js";
3
9
  import {
4
10
  registerAuth
5
- } from "./chunk-XSN4XA5Z.js";
11
+ } from "./chunk-XCKUCXC6.js";
6
12
  import {
7
13
  openBrowser
8
- } from "./chunk-C5HNQOLB.js";
14
+ } from "./chunk-RGVM5SNE.js";
9
15
  import {
10
16
  SETUP_CAPABILITY_LABELS,
11
17
  SETUP_CAPABILITY_ORDER,
12
18
  getSetupStatus,
13
19
  isSetupComplete,
14
20
  shouldRunOnboarding
15
- } from "./chunk-R4W44A6E.js";
21
+ } from "./chunk-NMT7XH3C.js";
16
22
  import {
17
23
  isInteractiveAllowed
18
- } from "./chunk-64A5W4M2.js";
24
+ } from "./chunk-75ICFV5K.js";
19
25
  import {
20
26
  adminClientFromFlags,
21
27
  clearSession,
22
28
  clientFromFlags,
23
29
  createPendingSession,
30
+ fromAdminNetworkId,
31
+ gasManagerClientFromFlags,
24
32
  getRPCNetworks,
33
+ getWalletSessionByChain,
34
+ hasAuthLoginToken,
25
35
  isSessionValid,
26
36
  isSolanaNetwork,
27
37
  loadSession,
@@ -42,13 +52,14 @@ import {
42
52
  resolveWalletSession,
43
53
  resolveX402Client,
44
54
  saveSession,
55
+ toAdminNetworkId,
45
56
  updateSession
46
- } from "./chunk-K6V3R7SH.js";
57
+ } from "./chunk-7ZSEELHZ.js";
47
58
  import {
48
59
  getAvailableUpdate,
49
60
  getUpdateStatus,
50
61
  printUpdateNotice
51
- } from "./chunk-N36ZNOVV.js";
62
+ } from "./chunk-WT4RGQLC.js";
52
63
  import {
53
64
  bold,
54
65
  brand,
@@ -72,7 +83,7 @@ import {
72
83
  weiToEth,
73
84
  withSpinner,
74
85
  yellow
75
- } from "./chunk-5IL2PMZ6.js";
86
+ } from "./chunk-MV7O3XBG.js";
76
87
  import {
77
88
  KEY_MAP,
78
89
  configDir,
@@ -83,7 +94,7 @@ import {
83
94
  save,
84
95
  toMap,
85
96
  validKeys
86
- } from "./chunk-JUCUKTP3.js";
97
+ } from "./chunk-GDLPBPG3.js";
87
98
  import {
88
99
  CLIError,
89
100
  EXIT_CODES,
@@ -119,7 +130,7 @@ import {
119
130
  setFlags,
120
131
  setNoColor,
121
132
  verbose
122
- } from "./chunk-2BALTY22.js";
133
+ } from "./chunk-OVLQH6KL.js";
123
134
 
124
135
  // src/index.ts
125
136
  import { Command, Help } from "commander";
@@ -552,12 +563,39 @@ function registerConfig(program2) {
552
563
  exitWithError(err);
553
564
  }
554
565
  });
555
- setCmd.command("evm-gas-policy-id <id>").description("Set the EVM gas policy ID for sponsored transactions").action((id) => {
566
+ setCmd.command("evm-gas-policy-id [id]").description("Set the EVM gas policy ID for sponsored transactions (omit ID for interactive)").action(async (id) => {
556
567
  try {
557
- const cfg = load();
558
- save({ ...cfg, evm_gas_policy_id: id });
559
- printHuman(`${green("\u2713")} Set evm-gas-policy-id
568
+ if (id) {
569
+ const cfg = load();
570
+ save({ ...cfg, evm_gas_policy_id: id });
571
+ printHuman(`${green("\u2713")} Set evm-gas-policy-id
560
572
  `, { key: "evm-gas-policy-id", status: "set" });
573
+ return;
574
+ }
575
+ if (!isInteractiveAllowed(program2)) {
576
+ throw errInvalidArgs(
577
+ "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set evm-gas-policy-id <id>`."
578
+ );
579
+ }
580
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-PHVO6VRZ.js");
581
+ const { resolveNetwork: resolveNetwork2 } = await import("./resolve-GBS26K44.js");
582
+ const network = resolveNetwork2(program2);
583
+ await selectOrCreatePolicy2({
584
+ flavor: "sponsorship",
585
+ network,
586
+ program: program2,
587
+ skipPersistPrompt: true
588
+ }).then((policyId) => {
589
+ if (!policyId) return;
590
+ const cfg = load();
591
+ save({ ...cfg, evm_gas_policy_id: policyId });
592
+ printHuman(`${green("\u2713")} Set evm-gas-policy-id
593
+ `, {
594
+ key: "evm-gas-policy-id",
595
+ value: policyId,
596
+ status: "set"
597
+ });
598
+ });
561
599
  } catch (err) {
562
600
  exitWithError(err);
563
601
  }
@@ -580,12 +618,39 @@ function registerConfig(program2) {
580
618
  exitWithError(err);
581
619
  }
582
620
  });
583
- setCmd.command("solana-fee-policy-id <id>").description("Set the Solana fee policy ID for sponsored transactions").action((id) => {
621
+ setCmd.command("solana-fee-policy-id [id]").description("Set the Solana fee policy ID for sponsored transactions (omit ID for interactive)").action(async (id) => {
584
622
  try {
585
- const cfg = load();
586
- save({ ...cfg, solana_fee_policy_id: id });
587
- printHuman(`${green("\u2713")} Set solana-fee-policy-id
623
+ if (id) {
624
+ const cfg = load();
625
+ save({ ...cfg, solana_fee_policy_id: id });
626
+ printHuman(`${green("\u2713")} Set solana-fee-policy-id
588
627
  `, { key: "solana-fee-policy-id", status: "set" });
628
+ return;
629
+ }
630
+ if (!isInteractiveAllowed(program2)) {
631
+ throw errInvalidArgs(
632
+ "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set solana-fee-policy-id <id>`."
633
+ );
634
+ }
635
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-PHVO6VRZ.js");
636
+ const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-GBS26K44.js");
637
+ const network = resolveSolanaNetwork2(program2);
638
+ await selectOrCreatePolicy2({
639
+ flavor: "solana",
640
+ network,
641
+ program: program2,
642
+ skipPersistPrompt: true
643
+ }).then((policyId) => {
644
+ if (!policyId) return;
645
+ const cfg = load();
646
+ save({ ...cfg, solana_fee_policy_id: policyId });
647
+ printHuman(`${green("\u2713")} Set solana-fee-policy-id
648
+ `, {
649
+ key: "solana-fee-policy-id",
650
+ value: policyId,
651
+ status: "set"
652
+ });
653
+ });
589
654
  } catch (err) {
590
655
  exitWithError(err);
591
656
  }
@@ -626,7 +691,7 @@ function registerConfig(program2) {
626
691
  printJSON(toMap(cfg));
627
692
  return;
628
693
  }
629
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-WXT5ZCUK.js");
694
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-GBS26K44.js");
630
695
  const validToken = resolveAuthToken2(cfg);
631
696
  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");
632
697
  const pairs = [
@@ -1305,7 +1370,19 @@ var walletSessionCreateResponseSchema = z.object({
1305
1370
  approvalUrl: z.string().url(),
1306
1371
  expiresAt: z.string().datetime().optional()
1307
1372
  }).passthrough();
1373
+ var walletSessionRequestSessionCreateResponseSchema = z.object({
1374
+ walletSessionId: z.string().min(1),
1375
+ chainType: z.string().min(1),
1376
+ status: rawWalletSessionStateSchema.transform(normalizeWalletSessionState)
1377
+ }).passthrough();
1378
+ var walletSessionRequestCreateResponseSchema = z.object({
1379
+ sessionId: z.string().min(1),
1380
+ approvalUrl: z.string().url(),
1381
+ expiresAt: z.string().datetime().optional(),
1382
+ sessions: z.array(walletSessionRequestSessionCreateResponseSchema).min(1)
1383
+ }).passthrough();
1308
1384
  var rawWalletSessionStatusResponseSchema = z.object({
1385
+ type: z.literal("session").optional(),
1309
1386
  sessionId: z.string().min(1).optional(),
1310
1387
  status: rawWalletSessionStateSchema,
1311
1388
  expiresAt: z.string().datetime().optional(),
@@ -1321,6 +1398,15 @@ var rawWalletSessionStatusResponseSchema = z.object({
1321
1398
  chainType: z.string().min(1).optional(),
1322
1399
  capabilities: z.record(z.string(), z.boolean()).optional()
1323
1400
  }).passthrough();
1401
+ var walletSessionRequestStatusResponseSchema = z.object({
1402
+ type: z.literal("sessionRequest"),
1403
+ sessionId: z.string().min(1),
1404
+ sessions: z.array(
1405
+ rawWalletSessionStatusResponseSchema.extend({
1406
+ walletSessionId: z.string().min(1)
1407
+ })
1408
+ ).min(1)
1409
+ }).passthrough();
1324
1410
  var walletSessionDisconnectResponseSchema = z.object({
1325
1411
  sessionId: z.string().min(1).optional(),
1326
1412
  status: rawWalletSessionStateSchema.optional().transform(
@@ -1347,12 +1433,27 @@ var rawEvmSigningChallengeResponseSchema = z.object({
1347
1433
  challenge: z.string().min(1),
1348
1434
  expiresAt: z.string().datetime(),
1349
1435
  requestExpiry: z.string().min(1),
1350
- method: z.enum(["personal_sign", "eth_signTypedData_v4", "eth_sign7702Authorization"]),
1436
+ method: z.enum([
1437
+ "personal_sign",
1438
+ "eth_signTypedData_v4",
1439
+ "eth_sign7702Authorization"
1440
+ ]),
1351
1441
  walletId: z.string().min(1),
1352
1442
  walletAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
1353
1443
  providerKeyQuorumId: z.string().min(1).optional(),
1354
1444
  providerSignerId: z.string().min(1).optional()
1355
1445
  }).strict();
1446
+ var rawSolanaSigningChallengeResponseSchema = z.object({
1447
+ challengeId: z.string().uuid(),
1448
+ challenge: z.string().min(1),
1449
+ expiresAt: z.string().datetime(),
1450
+ requestExpiry: z.string().min(1),
1451
+ method: z.literal("signTransaction"),
1452
+ walletId: z.string().min(1),
1453
+ walletAddress: z.string().min(1),
1454
+ providerKeyQuorumId: z.string().min(1).optional(),
1455
+ providerSignerId: z.string().min(1).optional()
1456
+ }).strict();
1356
1457
  var completeEvmChallengeInputSchema = z.object({
1357
1458
  challengeId: z.string().uuid(),
1358
1459
  signature: z.string().min(1)
@@ -1375,6 +1476,10 @@ var signAuthorizationCompleteResponseSchema = z.object({
1375
1476
  y_parity: z.number().int()
1376
1477
  }).strict()
1377
1478
  }).strict();
1479
+ var solanaSignTransactionCompleteResponseSchema = z.object({
1480
+ signedTransaction: z.string().min(1),
1481
+ encoding: z.literal("base64")
1482
+ }).strict();
1378
1483
  function baseURLOverride() {
1379
1484
  const options = { allowedHostnames: [STAGING_ADMIN_API_HOST] };
1380
1485
  return parseBaseURLOverride(WALLET_API_BASE_URL_ENV, options) ?? parseBaseURLOverride(ADMIN_API_BASE_URL_ENV, options);
@@ -1468,6 +1573,17 @@ function normalizeWalletSessionStatus(session) {
1468
1573
  privySignerId: session.providerSignerId
1469
1574
  };
1470
1575
  }
1576
+ function normalizeWalletSessionRequestStatus(request2) {
1577
+ return {
1578
+ sessionId: request2.sessionId,
1579
+ sessions: request2.sessions.map(({ walletSessionId, ...session }) => {
1580
+ return normalizeWalletSessionStatus({
1581
+ ...session,
1582
+ sessionId: walletSessionId
1583
+ });
1584
+ })
1585
+ };
1586
+ }
1471
1587
  function normalizeEvmSigningChallengeResponse(response) {
1472
1588
  return {
1473
1589
  ...response,
@@ -1475,6 +1591,13 @@ function normalizeEvmSigningChallengeResponse(response) {
1475
1591
  privySignerId: response.providerSignerId
1476
1592
  };
1477
1593
  }
1594
+ function normalizeSolanaSigningChallengeResponse(response) {
1595
+ return {
1596
+ ...response,
1597
+ privyKeyQuorumId: response.providerKeyQuorumId,
1598
+ privySignerId: response.providerSignerId
1599
+ };
1600
+ }
1478
1601
  function toProviderSigningBindingInput(input) {
1479
1602
  const { privyKeyQuorumId, privySignerId, ...rest } = input;
1480
1603
  return {
@@ -1483,7 +1606,15 @@ function toProviderSigningBindingInput(input) {
1483
1606
  ...privySignerId ? { providerSignerId: privySignerId } : {}
1484
1607
  };
1485
1608
  }
1486
- async function createRemoteWalletSession(token, input) {
1609
+ function toProviderSolanaSigningBindingInput(input) {
1610
+ const { privyKeyQuorumId, privySignerId, ...rest } = input;
1611
+ return {
1612
+ ...rest,
1613
+ ...privyKeyQuorumId ? { providerKeyQuorumId: privyKeyQuorumId } : {},
1614
+ ...privySignerId ? { providerSignerId: privySignerId } : {}
1615
+ };
1616
+ }
1617
+ async function createRemoteWalletSessionRequest(token, input) {
1487
1618
  let data;
1488
1619
  let requestInput = input;
1489
1620
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -1494,16 +1625,19 @@ async function createRemoteWalletSession(token, input) {
1494
1625
  "/wallet/sessions",
1495
1626
  requestInput
1496
1627
  );
1497
- return unwrapAdminData(walletSessionCreateResponseSchema, data);
1628
+ return unwrapAdminData(walletSessionRequestCreateResponseSchema, data);
1498
1629
  } catch (err) {
1499
- const retryInput = getClientInstanceCompatibilityRetryInput(requestInput, err);
1630
+ const retryInput = getClientInstanceCompatibilityRetryInput(
1631
+ requestInput,
1632
+ err
1633
+ );
1500
1634
  if (retryInput === void 0) {
1501
1635
  throw err;
1502
1636
  }
1503
1637
  requestInput = retryInput;
1504
1638
  }
1505
1639
  }
1506
- throw new CLIError(ErrorCode.NETWORK_ERROR, "Unable to create wallet session.");
1640
+ throw new CLIError(ErrorCode.NETWORK_ERROR, "Unable to create wallet session request.");
1507
1641
  }
1508
1642
  function getClientInstanceCompatibilityRetryInput(input, err) {
1509
1643
  if (input.environment.clientInstanceName !== void 0 && isUnsupportedClientInstanceMetadataError(err, "clientInstanceName")) {
@@ -1538,6 +1672,16 @@ async function getRemoteWalletSession(token, sessionId) {
1538
1672
  unwrapAdminData(rawWalletSessionStatusResponseSchema, data)
1539
1673
  );
1540
1674
  }
1675
+ async function getRemoteWalletSessionRequest(token, sessionId) {
1676
+ const data = await request(
1677
+ token,
1678
+ "GET",
1679
+ `/wallet/sessions/${encodeURIComponent(sessionId)}`
1680
+ );
1681
+ return normalizeWalletSessionRequestStatus(
1682
+ unwrapAdminData(walletSessionRequestStatusResponseSchema, data)
1683
+ );
1684
+ }
1541
1685
  async function disconnectRemoteWalletSession(token, sessionId) {
1542
1686
  try {
1543
1687
  const data = await request(
@@ -1616,6 +1760,24 @@ var signAuthorizationChallengeInputSchema = evmSigningSessionBindingBaseSchema.e
1616
1760
  executor: z.literal("self").optional()
1617
1761
  }).strict()
1618
1762
  }).strict().superRefine(ensureSessionSignerBinding);
1763
+ var solanaSigningSessionBindingBaseSchema = z.object({
1764
+ sessionId: z.string().uuid(),
1765
+ walletId: z.string().min(1),
1766
+ walletAddress: z.string().min(1),
1767
+ privyKeyQuorumId: z.string().min(1).optional(),
1768
+ privySignerId: z.string().min(1).optional()
1769
+ });
1770
+ var solanaSignTransactionChallengeInputSchema = solanaSigningSessionBindingBaseSchema.extend({
1771
+ transaction: z.string().min(1),
1772
+ encoding: z.literal("base64")
1773
+ }).strict().superRefine((input, ctx) => {
1774
+ if (!input.privyKeyQuorumId && !input.privySignerId) {
1775
+ ctx.addIssue({
1776
+ code: z.ZodIssueCode.custom,
1777
+ message: "Provide privyKeyQuorumId or privySignerId."
1778
+ });
1779
+ }
1780
+ });
1619
1781
  async function createRemoteSignTypedDataChallenge(token, input) {
1620
1782
  const parsedInput = signTypedDataChallengeInputSchema.parse(input);
1621
1783
  return createEvmSigningChallenge(
@@ -1654,6 +1816,25 @@ async function completeRemoteSignAuthorizationChallenge(token, input) {
1654
1816
  adminApiResponseSchema(signAuthorizationCompleteResponseSchema).transform((resp) => resp.data)
1655
1817
  );
1656
1818
  }
1819
+ async function createRemoteSolanaSignTransactionChallenge(token, input) {
1820
+ const parsedInput = solanaSignTransactionChallengeInputSchema.parse(input);
1821
+ return createEvmSigningChallenge(
1822
+ token,
1823
+ "/wallet/solana/sign-transaction/challenge",
1824
+ toProviderSolanaSigningBindingInput(parsedInput),
1825
+ adminApiResponseSchema(rawSolanaSigningChallengeResponseSchema).transform(
1826
+ (resp) => normalizeSolanaSigningChallengeResponse(resp.data)
1827
+ )
1828
+ );
1829
+ }
1830
+ async function completeRemoteSolanaSignTransactionChallenge(token, input) {
1831
+ return completeEvmSigningChallenge(
1832
+ token,
1833
+ "/wallet/solana/sign-transaction/complete",
1834
+ input,
1835
+ adminApiResponseSchema(solanaSignTransactionCompleteResponseSchema).transform((resp) => resp.data)
1836
+ );
1837
+ }
1657
1838
 
1658
1839
  // src/commands/wallet.ts
1659
1840
  import QRCode from "qrcode";
@@ -1664,6 +1845,9 @@ var DEFAULT_WALLET_CAPABILITIES = {
1664
1845
  "evm.signTypedData": true,
1665
1846
  "evm.signAuthorization": true
1666
1847
  };
1848
+ var DEFAULT_SOLANA_SESSION_CAPABILITIES = {
1849
+ "solana.signTransaction": true
1850
+ };
1667
1851
  function createEvmWallet() {
1668
1852
  const privateKey = generatePrivateKey();
1669
1853
  const account = privateKeyToAccount(privateKey);
@@ -1787,7 +1971,13 @@ async function withApprovalInterruptHandler(args) {
1787
1971
  return await args.run(controller.signal);
1788
1972
  } catch (err) {
1789
1973
  if (isWalletConnectInterruptedError(err)) {
1790
- await disconnectRemoteWalletSession(args.authToken, args.sessionId).catch(() => void 0);
1974
+ await Promise.all(
1975
+ args.sessionIds.map(
1976
+ (sessionId) => disconnectRemoteWalletSession(args.authToken, sessionId).catch(
1977
+ () => void 0
1978
+ )
1979
+ )
1980
+ );
1791
1981
  clearSession();
1792
1982
  }
1793
1983
  throw err;
@@ -1798,23 +1988,9 @@ async function withApprovalInterruptHandler(args) {
1798
1988
  }
1799
1989
  }
1800
1990
  }
1801
- function isFutureIsoDate(value) {
1802
- const date = new Date(value);
1803
- if (Number.isNaN(date.getTime())) {
1804
- return false;
1805
- }
1806
- return date > /* @__PURE__ */ new Date();
1807
- }
1808
- function isActiveWalletSession(session, remoteStatus) {
1809
- if (!session) return false;
1810
- if (session.status !== "approved") return false;
1811
- if (!isFutureIsoDate(session.expiresAt)) return false;
1812
- if (remoteStatus && remoteStatus !== "approved") return false;
1813
- return true;
1814
- }
1815
- function deriveWalletStatus(session, remoteStatus) {
1991
+ function deriveWalletStatus(session) {
1816
1992
  if (!session) return "none";
1817
- if (isActiveWalletSession(session, remoteStatus)) return "active";
1993
+ if (isSessionValid(session)) return "active";
1818
1994
  if (session.status === "pending") return "none";
1819
1995
  return "expired";
1820
1996
  }
@@ -1924,7 +2100,7 @@ function resolveQrAddress(args) {
1924
2100
  function missingQrWalletError(type, source) {
1925
2101
  if (source === "session") {
1926
2102
  return type === "solana" ? errInvalidArgs(
1927
- "The session wallet does not yet support Solana QR selection. Use `alchemy wallet use local` or configure a local Solana wallet with `alchemy wallet connect --mode local --chain solana`."
2103
+ "No Solana session wallet is configured. Run `alchemy wallet connect --mode session --force`."
1928
2104
  ) : errNoActiveSession();
1929
2105
  }
1930
2106
  if (source === "local") {
@@ -1935,20 +2111,6 @@ function missingQrWalletError(type, source) {
1935
2111
  function indentBlock(text, prefix = " ") {
1936
2112
  return text.split("\n").map((line) => line ? `${prefix}${line}` : line).join("\n");
1937
2113
  }
1938
- function generateAndPersistWallet() {
1939
- const wallet = createEvmWallet();
1940
- const keyPath = persistWalletKey("wallet-key", wallet.privateKey, wallet.address);
1941
- const cfg = load();
1942
- save({ ...cfg, wallet_key_file: keyPath, wallet_address: wallet.address });
1943
- return { address: wallet.address, keyFile: keyPath };
1944
- }
1945
- async function generateAndPersistSolanaWallet() {
1946
- const wallet = await createSolanaWallet();
1947
- const keyPath = persistWalletKey("solana-wallet-key", wallet.secretKey, wallet.address);
1948
- const cfg = load();
1949
- save({ ...cfg, solana_wallet_key_file: keyPath, solana_wallet_address: wallet.address });
1950
- return { address: wallet.address, keyFile: keyPath };
1951
- }
1952
2114
  async function createAndPersistWallets() {
1953
2115
  const evm = createEvmWallet();
1954
2116
  const solana = await createSolanaWallet();
@@ -2013,14 +2175,12 @@ function getEffectiveLocalWalletState(program2, cfg) {
2013
2175
  }
2014
2176
  async function runLocalCreate(opts) {
2015
2177
  const cfg = load();
2016
- const wantsEvm = opts.chain === "evm" || opts.chain === "both";
2017
- const wantsSolana = opts.chain === "solana" || opts.chain === "both";
2018
- const evmConflict = wantsEvm && Boolean(cfg.wallet_key_file);
2019
- const solanaConflict = wantsSolana && Boolean(cfg.solana_wallet_key_file);
2178
+ const evmConflict = Boolean(cfg.wallet_key_file);
2179
+ const solanaConflict = Boolean(cfg.solana_wallet_key_file);
2020
2180
  if ((evmConflict || solanaConflict) && !opts.force) {
2021
2181
  if (!isInteractiveAllowed(opts.program)) {
2022
2182
  throw errInvalidArgs(
2023
- "A local key is already configured for the selected chain. Re-run with --force to replace it."
2183
+ "A local key is already configured. Re-run with --force to replace it."
2024
2184
  );
2025
2185
  }
2026
2186
  const kinds = [evmConflict ? "EVM" : null, solanaConflict ? "Solana" : null].filter(Boolean).join(" and ");
@@ -2033,16 +2193,7 @@ async function runLocalCreate(opts) {
2033
2193
  throw errInvalidArgs("Aborted. Existing local key preserved.");
2034
2194
  }
2035
2195
  }
2036
- if (opts.chain === "both") {
2037
- const { evm, solana: solana2 } = await createAndPersistWallets();
2038
- return { evm, solana: solana2 };
2039
- }
2040
- if (opts.chain === "evm") {
2041
- const evm = generateAndPersistWallet();
2042
- return { evm };
2043
- }
2044
- const solana = await generateAndPersistSolanaWallet();
2045
- return { solana };
2196
+ return await createAndPersistWallets();
2046
2197
  }
2047
2198
  function runLocalImport(opts) {
2048
2199
  const cfg = load();
@@ -2075,7 +2226,27 @@ async function runLocalImportInteractive(opts) {
2075
2226
  return runLocalImport({ ...opts, force: true });
2076
2227
  }
2077
2228
  }
2229
+ function capabilitiesForChain(chain) {
2230
+ return chain === "evm" ? { ...DEFAULT_WALLET_CAPABILITIES } : { ...DEFAULT_SOLANA_SESSION_CAPABILITIES };
2231
+ }
2232
+ function capabilitiesForChains(chains) {
2233
+ return Object.fromEntries(
2234
+ chains.map((chain) => [chain, capabilitiesForChain(chain)])
2235
+ );
2236
+ }
2237
+ function getApprovedSessionWalletMetadata(chain, session) {
2238
+ const walletId = chain === "evm" ? session.walletId ?? session.evmWalletId : session.solanaWalletId ?? session.walletId;
2239
+ const walletAddress = chain === "evm" ? session.evmAddress ?? session.address : session.solanaAddress ?? session.address;
2240
+ if (!walletId || !walletAddress) {
2241
+ throw new CLIError(
2242
+ ErrorCode.INTERNAL_ERROR,
2243
+ "Approved wallet session response missing required metadata."
2244
+ );
2245
+ }
2246
+ return { walletId, walletAddress };
2247
+ }
2078
2248
  async function runSessionConnect(opts) {
2249
+ const requestedChains = ["evm", "solana"];
2079
2250
  const authToken = resolveAuthToken();
2080
2251
  if (!authToken) throw errAuthRequired();
2081
2252
  const existing = loadSession();
@@ -2113,32 +2284,51 @@ async function runSessionConnect(opts) {
2113
2284
  ...clientInstance.name === void 0 ? {} : { clientInstanceName: clientInstance.name }
2114
2285
  };
2115
2286
  updateSession({
2116
- chainType: "evm",
2117
- capabilities: { ...DEFAULT_WALLET_CAPABILITIES },
2287
+ chainType: "both",
2288
+ capabilities: Object.assign(
2289
+ {},
2290
+ ...requestedChains.map((chain) => capabilitiesForChain(chain))
2291
+ ),
2118
2292
  backendBaseUrl: getWalletApiBaseUrl(),
2119
2293
  environment: sessionEnvironment
2120
2294
  });
2121
- const remoteSession = await createRemoteWalletSession(authToken, {
2295
+ const remoteRequest = await createRemoteWalletSessionRequest(authToken, {
2122
2296
  publicKeyJwk: session.publicKeyJwk,
2123
2297
  requestSignerVersion: session.envelopeVersion,
2124
- chainType: "evm",
2125
- capabilities: { ...DEFAULT_WALLET_CAPABILITIES },
2298
+ chains: requestedChains,
2299
+ capabilities: capabilitiesForChains(requestedChains),
2126
2300
  environment: sessionEnvironment
2127
2301
  }).catch((err) => {
2128
2302
  clearSession();
2129
2303
  throw err;
2130
2304
  });
2305
+ const pendingSessionsByChain = Object.fromEntries(
2306
+ remoteRequest.sessions.map((remoteSession) => [
2307
+ remoteSession.chainType,
2308
+ {
2309
+ sessionId: remoteSession.walletSessionId,
2310
+ status: "pending",
2311
+ expiresAt: remoteRequest.expiresAt ?? session.expiresAt,
2312
+ chainType: remoteSession.chainType,
2313
+ capabilities: capabilitiesForChain(
2314
+ remoteSession.chainType
2315
+ )
2316
+ }
2317
+ ])
2318
+ );
2131
2319
  updateSession({
2132
- sessionId: remoteSession.sessionId,
2133
- expiresAt: remoteSession.expiresAt ?? session.expiresAt
2320
+ connectionRequestId: remoteRequest.sessionId,
2321
+ sessionId: remoteRequest.sessions[0].walletSessionId,
2322
+ expiresAt: remoteRequest.expiresAt ?? session.expiresAt,
2323
+ sessionsByChain: pendingSessionsByChain
2134
2324
  });
2135
2325
  if (!isJSONMode()) {
2136
2326
  const summaryPairs = [
2137
- ["Session ID", remoteSession.sessionId],
2327
+ ["Connection Request ID", remoteRequest.sessionId],
2138
2328
  ["Status", "pending"],
2139
- ["Approval URL", remoteSession.approvalUrl],
2329
+ ["Approval URL", remoteRequest.approvalUrl],
2140
2330
  ["Created", session.createdAt],
2141
- ["Expires", remoteSession.expiresAt ?? session.expiresAt]
2331
+ ["Expires", remoteRequest.expiresAt ?? session.expiresAt]
2142
2332
  ];
2143
2333
  if (clientInstance.name !== void 0) {
2144
2334
  summaryPairs.splice(1, 0, ["Instance", clientInstance.name]);
@@ -2153,7 +2343,14 @@ async function runSessionConnect(opts) {
2153
2343
  });
2154
2344
  if (answer === null) {
2155
2345
  clearSession();
2156
- await disconnectRemoteWalletSession(authToken, remoteSession.sessionId).catch(() => void 0);
2346
+ await Promise.all(
2347
+ remoteRequest.sessions.map(
2348
+ (remoteSession) => disconnectRemoteWalletSession(
2349
+ authToken,
2350
+ remoteSession.walletSessionId
2351
+ ).catch(() => void 0)
2352
+ )
2353
+ );
2157
2354
  throw new WalletConnectInterruptedError();
2158
2355
  }
2159
2356
  }
@@ -2161,25 +2358,41 @@ async function runSessionConnect(opts) {
2161
2358
  console.log(` Opening browser for approval...`);
2162
2359
  console.log(` ${dim("Waiting for dashboard approval to complete connection.")}`);
2163
2360
  }
2164
- openBrowser(remoteSession.approvalUrl);
2165
- const approvedSession = await withApprovalInterruptHandler({
2361
+ openBrowser(remoteRequest.approvalUrl);
2362
+ const approvedSessions = await withApprovalInterruptHandler({
2166
2363
  authToken,
2167
- sessionId: remoteSession.sessionId,
2364
+ sessionIds: remoteRequest.sessions.map(
2365
+ (remoteSession) => remoteSession.walletSessionId
2366
+ ),
2168
2367
  run: async (signal) => {
2169
2368
  const startedAt = Date.now();
2170
2369
  while (Date.now() - startedAt < CONNECT_TIMEOUT_MS) {
2171
- const current = await waitForInterruptible(
2172
- getRemoteWalletSession(authToken, remoteSession.sessionId),
2370
+ const currentSessions = await waitForInterruptible(
2371
+ remoteRequest.sessionId ? getRemoteWalletSessionRequest(
2372
+ authToken,
2373
+ remoteRequest.sessionId
2374
+ ).then((request2) => request2.sessions) : Promise.all(
2375
+ remoteRequest.sessions.map(
2376
+ (remoteSession) => getRemoteWalletSession(authToken, remoteSession.walletSessionId)
2377
+ )
2378
+ ),
2173
2379
  signal
2174
2380
  );
2175
- if (current.status === "approved") {
2176
- return current;
2381
+ if (requestedChains.every(
2382
+ (chain) => currentSessions.some((current) => {
2383
+ return current.chainType === chain && current.status === "approved";
2384
+ })
2385
+ )) {
2386
+ return currentSessions;
2177
2387
  }
2178
- if (current.status === "denied" || current.status === "revoked" || current.status === "expired") {
2388
+ const terminalSession = currentSessions.find((current) => {
2389
+ return current.status === "denied" || current.status === "revoked" || current.status === "expired";
2390
+ });
2391
+ if (terminalSession) {
2179
2392
  clearSession();
2180
2393
  throw new CLIError(
2181
2394
  ErrorCode.INTERNAL_ERROR,
2182
- `Wallet session ${current.status}.`,
2395
+ `Wallet session ${terminalSession.status}.`,
2183
2396
  "Run 'alchemy wallet connect' to start a new approval flow."
2184
2397
  );
2185
2398
  }
@@ -2188,58 +2401,123 @@ async function runSessionConnect(opts) {
2188
2401
  return null;
2189
2402
  }
2190
2403
  });
2191
- if (!approvedSession) {
2404
+ if (!approvedSessions) {
2192
2405
  throw new CLIError(
2193
2406
  ErrorCode.NETWORK_ERROR,
2194
2407
  "Timed out waiting for wallet approval.",
2195
2408
  "Finish approval in the dashboard and rerun 'alchemy wallet connect --force' if needed."
2196
2409
  );
2197
2410
  }
2198
- const walletId = approvedSession.walletId ?? approvedSession.evmWalletId;
2199
- const evmAddress = approvedSession.evmAddress ?? approvedSession.address;
2200
- if (!approvedSession.privyAppId || !walletId || !evmAddress) {
2411
+ const missingProviderAppId = approvedSessions.find((approvedSession) => {
2412
+ return !approvedSession.privyAppId;
2413
+ });
2414
+ if (missingProviderAppId) {
2201
2415
  throw new CLIError(
2202
2416
  ErrorCode.INTERNAL_ERROR,
2203
2417
  "Approved wallet session response missing required metadata."
2204
2418
  );
2205
2419
  }
2420
+ const approvedSessionsByChain = Object.fromEntries(
2421
+ requestedChains.map((chain) => {
2422
+ const approvedSession = approvedSessions.find((current) => {
2423
+ return current.chainType === chain;
2424
+ });
2425
+ if (!approvedSession) {
2426
+ throw new CLIError(
2427
+ ErrorCode.INTERNAL_ERROR,
2428
+ "Approved wallet session response missing requested chain."
2429
+ );
2430
+ }
2431
+ const { walletId, walletAddress } = getApprovedSessionWalletMetadata(
2432
+ chain,
2433
+ approvedSession
2434
+ );
2435
+ return [
2436
+ chain,
2437
+ {
2438
+ sessionId: approvedSession.sessionId ?? remoteRequest.sessions.find((remoteSession) => remoteSession.chainType === chain)?.walletSessionId ?? session.sessionId,
2439
+ status: "approved",
2440
+ expiresAt: approvedSession.expiresAt ?? remoteRequest.expiresAt ?? session.expiresAt,
2441
+ chainType: chain,
2442
+ walletId,
2443
+ walletAddress,
2444
+ providerKeyQuorumId: approvedSession.privyKeyQuorumId,
2445
+ providerSignerId: approvedSession.privySignerId,
2446
+ capabilities: approvedSession.capabilities ?? capabilitiesForChain(chain)
2447
+ }
2448
+ ];
2449
+ })
2450
+ );
2451
+ const firstApprovedSession = approvedSessions.find((approvedSession) => {
2452
+ return approvedSession.chainType === requestedChains[0];
2453
+ }) ?? approvedSessions[0];
2454
+ const evmSession = approvedSessionsByChain.evm;
2455
+ const solanaSession = approvedSessionsByChain.solana;
2206
2456
  updateSession({
2207
- sessionId: approvedSession.sessionId ?? remoteSession.sessionId,
2457
+ connectionRequestId: remoteRequest.sessionId,
2458
+ sessionId: firstApprovedSession.sessionId ?? remoteRequest.sessions[0].walletSessionId,
2208
2459
  status: "approved",
2209
- expiresAt: approvedSession.expiresAt ?? remoteSession.expiresAt ?? session.expiresAt,
2210
- privyAppId: approvedSession.privyAppId,
2211
- walletId,
2212
- evmWalletId: approvedSession.evmWalletId ?? walletId,
2213
- evmAddress,
2214
- solanaWalletId: approvedSession.solanaWalletId,
2215
- solanaAddress: approvedSession.solanaAddress,
2216
- privyKeyQuorumId: approvedSession.privyKeyQuorumId,
2217
- privySignerId: approvedSession.privySignerId,
2218
- chainType: approvedSession.chainType ?? "evm",
2219
- capabilities: approvedSession.capabilities ?? { ...DEFAULT_WALLET_CAPABILITIES },
2460
+ expiresAt: firstApprovedSession.expiresAt ?? remoteRequest.expiresAt ?? session.expiresAt,
2461
+ privyAppId: firstApprovedSession.privyAppId,
2462
+ walletId: evmSession?.walletId ?? solanaSession?.walletId,
2463
+ evmWalletId: evmSession?.walletId,
2464
+ evmAddress: evmSession?.walletAddress,
2465
+ solanaWalletId: solanaSession?.walletId,
2466
+ solanaAddress: solanaSession?.walletAddress,
2467
+ privyKeyQuorumId: evmSession?.providerKeyQuorumId ?? solanaSession?.providerKeyQuorumId,
2468
+ privySignerId: evmSession?.providerSignerId ?? solanaSession?.providerSignerId,
2469
+ chainType: "both",
2470
+ capabilities: Object.assign(
2471
+ {},
2472
+ ...requestedChains.map((chain) => capabilitiesForChain(chain)),
2473
+ ...approvedSessions.map((approvedSession) => approvedSession.capabilities ?? {})
2474
+ ),
2475
+ sessionsByChain: approvedSessionsByChain,
2220
2476
  backendBaseUrl: getWalletApiBaseUrl(),
2221
2477
  environment: sessionEnvironment
2222
2478
  });
2223
2479
  if (isJSONMode()) {
2224
2480
  printJSON({
2225
- sessionId: approvedSession.sessionId ?? remoteSession.sessionId,
2481
+ connectionRequestId: remoteRequest.sessionId,
2482
+ sessionId: firstApprovedSession.sessionId ?? remoteRequest.sessions[0].walletSessionId,
2483
+ walletSessionId: firstApprovedSession.sessionId ?? remoteRequest.sessions[0].walletSessionId,
2226
2484
  status: "approved",
2227
- privyAppId: approvedSession.privyAppId,
2228
- walletId,
2229
- evmAddress,
2230
- evmWalletId: approvedSession.evmWalletId ?? walletId,
2231
- chainType: approvedSession.chainType ?? "evm",
2232
- capabilities: approvedSession.capabilities ?? { ...DEFAULT_WALLET_CAPABILITIES },
2233
- expiresAt: approvedSession.expiresAt ?? remoteSession.expiresAt ?? session.expiresAt
2485
+ privyAppId: firstApprovedSession.privyAppId,
2486
+ walletId: evmSession?.walletId ?? solanaSession?.walletId,
2487
+ ...evmSession ? {
2488
+ evmAddress: evmSession.walletAddress,
2489
+ evmWalletId: evmSession.walletId
2490
+ } : {},
2491
+ ...solanaSession ? {
2492
+ solanaAddress: solanaSession.walletAddress,
2493
+ solanaWalletId: solanaSession.walletId
2494
+ } : {},
2495
+ chainType: "both",
2496
+ capabilities: Object.assign(
2497
+ {},
2498
+ ...requestedChains.map((chain) => capabilitiesForChain(chain))
2499
+ ),
2500
+ expiresAt: firstApprovedSession.expiresAt ?? remoteRequest.expiresAt ?? session.expiresAt,
2501
+ sessionsByChain: approvedSessionsByChain
2234
2502
  });
2235
2503
  } else {
2236
- printKeyValue([
2237
- ["Session ID", approvedSession.sessionId ?? remoteSession.sessionId],
2238
- ["Status", green("approved")],
2239
- ["Wallet ID", walletId],
2240
- ["EVM Address", green(evmAddress)],
2241
- ["Expires", approvedSession.expiresAt ?? remoteSession.expiresAt ?? session.expiresAt]
2504
+ const pairs = [
2505
+ ["Connection Request ID", remoteRequest.sessionId],
2506
+ ["Status", green("approved")]
2507
+ ];
2508
+ for (const chain of requestedChains) {
2509
+ const chainSession = approvedSessionsByChain[chain];
2510
+ if (!chainSession) continue;
2511
+ pairs.push(
2512
+ [`${chain.toUpperCase()} Wallet ID`, chainSession.walletId ?? ""],
2513
+ [`${chain.toUpperCase()} Address`, green(chainSession.walletAddress ?? "")]
2514
+ );
2515
+ }
2516
+ pairs.push([
2517
+ "Expires",
2518
+ firstApprovedSession.expiresAt ?? remoteRequest.expiresAt ?? session.expiresAt
2242
2519
  ]);
2520
+ printKeyValue(pairs);
2243
2521
  console.log(` ${green("\u2713")} Wallet connected`);
2244
2522
  }
2245
2523
  }
@@ -2308,17 +2586,6 @@ async function runConnectFlow(program2, opts) {
2308
2586
  }
2309
2587
  mode2 = "local";
2310
2588
  }
2311
- let chain = "both";
2312
- if (opts.chain !== void 0) {
2313
- if (opts.chain !== "evm" && opts.chain !== "solana" && opts.chain !== "both") {
2314
- throw errInvalidArgs("`--chain` must be 'evm', 'solana', or 'both'.");
2315
- }
2316
- chain = opts.chain;
2317
- }
2318
- if (importPath && chain !== "evm" && opts.chain !== void 0) {
2319
- throw errInvalidArgs("`--import` implies `--chain evm`; omit `--chain` or set it to 'evm'.");
2320
- }
2321
- if (importPath) chain = "evm";
2322
2589
  if (!mode2) {
2323
2590
  if (!isInteractiveAllowed(program2)) {
2324
2591
  throw errInvalidArgs(
@@ -2328,7 +2595,7 @@ async function runConnectFlow(program2, opts) {
2328
2595
  const choice = await promptSelect({
2329
2596
  message: "Choose a wallet to connect",
2330
2597
  options: [
2331
- { value: "session", label: "Session wallet", hint: "Recommended \u2014 Alchemy/Privy-managed, more secure" },
2598
+ { value: "session", label: "Session wallet", hint: "Recommended \u2014 Alchemy-managed, more secure" },
2332
2599
  { value: "local", label: "Local wallet", hint: "Private key stored on this machine" }
2333
2600
  ],
2334
2601
  initialValue: "session",
@@ -2338,7 +2605,11 @@ async function runConnectFlow(program2, opts) {
2338
2605
  mode2 = choice;
2339
2606
  }
2340
2607
  if (mode2 === "session") {
2341
- await runSessionConnect({ program: program2, force, instanceName: opts.instanceName });
2608
+ await runSessionConnect({
2609
+ program: program2,
2610
+ force,
2611
+ instanceName: opts.instanceName
2612
+ });
2342
2613
  printCrossModeHintAfterSessionConnect();
2343
2614
  return;
2344
2615
  }
@@ -2348,9 +2619,55 @@ async function runConnectFlow(program2, opts) {
2348
2619
  printCrossModeHintAfterLocal("evm");
2349
2620
  return;
2350
2621
  }
2351
- const result = await runLocalCreate({ chain, force, program: program2 });
2622
+ const result = await runLocalCreate({ force, program: program2 });
2352
2623
  printPostLocalCreateSummary(result);
2353
- printCrossModeHintAfterLocal(chain);
2624
+ printCrossModeHintAfterLocal("both");
2625
+ }
2626
+ function uniqueSessionIds(session) {
2627
+ const ids = Object.values(session.sessionsByChain ?? {}).map((chainSession) => chainSession?.sessionId).filter((sessionId) => Boolean(sessionId));
2628
+ if (ids.length === 0) {
2629
+ ids.push(session.sessionId);
2630
+ }
2631
+ return Array.from(new Set(ids));
2632
+ }
2633
+ function selectRemoteSessionForStatus(session, remoteSessions) {
2634
+ return remoteSessions.find((remoteSession) => {
2635
+ return remoteSession.status !== "approved";
2636
+ }) ?? remoteSessions.find((remoteSession) => {
2637
+ return remoteSession.sessionId === session.sessionId;
2638
+ }) ?? remoteSessions[0];
2639
+ }
2640
+ function mergeRemoteCapabilities(session, remoteSessions) {
2641
+ return Object.assign(
2642
+ {},
2643
+ session.capabilities ?? {},
2644
+ ...remoteSessions.map((remoteSession) => remoteSession.capabilities ?? {})
2645
+ );
2646
+ }
2647
+ async function getRemoteSessionsForStatus(authToken, session) {
2648
+ const sessionIds = uniqueSessionIds(session);
2649
+ if (session.connectionRequestId) {
2650
+ try {
2651
+ return await getRemoteWalletSessionRequest(
2652
+ authToken,
2653
+ session.connectionRequestId
2654
+ ).then((request2) => request2.sessions);
2655
+ } catch {
2656
+ }
2657
+ }
2658
+ const results = await Promise.allSettled(
2659
+ sessionIds.map((sessionId) => getRemoteWalletSession(authToken, sessionId))
2660
+ );
2661
+ const remoteSessions = results.flatMap((result) => {
2662
+ return result.status === "fulfilled" ? [result.value] : [];
2663
+ });
2664
+ if (remoteSessions.length === 0) {
2665
+ const firstRejection = results.find((result) => {
2666
+ return result.status === "rejected";
2667
+ });
2668
+ throw firstRejection?.reason ?? new Error("Unable to verify wallet session.");
2669
+ }
2670
+ return remoteSessions;
2354
2671
  }
2355
2672
  async function buildSessionSnapshot(program2, verify) {
2356
2673
  let session = loadStoredSession?.() ?? loadSession();
@@ -2358,25 +2675,51 @@ async function buildSessionSnapshot(program2, verify) {
2358
2675
  if (verify && session) {
2359
2676
  const authToken = resolveAuthToken();
2360
2677
  if (!authToken) throw errAuthRequired();
2361
- const remote = await getRemoteWalletSession(authToken, session.sessionId);
2678
+ const remoteSessions = await getRemoteSessionsForStatus(authToken, session);
2679
+ const remote = selectRemoteSessionForStatus(session, remoteSessions);
2362
2680
  remoteStatus = remote.status;
2363
- const walletId2 = remote.walletId ?? remote.evmWalletId;
2681
+ const nextSessionsByChain = { ...session.sessionsByChain ?? {} };
2682
+ for (const remoteSession of remoteSessions) {
2683
+ const chain = remoteSession.chainType === "solana" ? "solana" : "evm";
2684
+ const walletIdForChain = chain === "solana" ? remoteSession.solanaWalletId ?? remoteSession.walletId : remoteSession.evmWalletId ?? remoteSession.walletId;
2685
+ const walletAddressForChain = chain === "solana" ? remoteSession.solanaAddress ?? remoteSession.address : remoteSession.evmAddress ?? remoteSession.address;
2686
+ nextSessionsByChain[chain] = {
2687
+ ...nextSessionsByChain[chain] ?? {},
2688
+ sessionId: remoteSession.sessionId ?? nextSessionsByChain[chain]?.sessionId ?? session.sessionId,
2689
+ status: normalizeRemoteStatusForStorage(remoteSession.status),
2690
+ expiresAt: remoteSession.expiresAt ?? nextSessionsByChain[chain]?.expiresAt ?? session.expiresAt,
2691
+ chainType: chain,
2692
+ ...walletIdForChain ? { walletId: walletIdForChain } : {},
2693
+ ...walletAddressForChain ? { walletAddress: walletAddressForChain } : {},
2694
+ ...remoteSession.privyKeyQuorumId ? { providerKeyQuorumId: remoteSession.privyKeyQuorumId } : {},
2695
+ ...remoteSession.privySignerId ? { providerSignerId: remoteSession.privySignerId } : {},
2696
+ ...remoteSession.capabilities ? { capabilities: remoteSession.capabilities } : {}
2697
+ };
2698
+ }
2699
+ const remoteChain = remote.chainType === "solana" ? "solana" : "evm";
2700
+ const remoteWalletId = remoteChain === "solana" ? remote.solanaWalletId ?? remote.walletId : remote.evmWalletId ?? remote.walletId;
2701
+ const evmWalletId = remoteChain === "evm" ? remoteWalletId ?? session.evmWalletId : session.evmWalletId;
2702
+ const evmAddress = remoteChain === "evm" ? remote.evmAddress ?? remote.address ?? session.evmAddress : session.evmAddress;
2703
+ const solanaWalletId = remoteChain === "solana" ? remoteWalletId ?? session.solanaWalletId : session.solanaWalletId;
2704
+ const solanaAddress = remoteChain === "solana" ? remote.solanaAddress ?? remote.address ?? session.solanaAddress : session.solanaAddress;
2705
+ const chainType = nextSessionsByChain.evm && nextSessionsByChain.solana ? "both" : remote.chainType ?? session.chainType;
2364
2706
  session = {
2365
2707
  ...session,
2366
2708
  status: normalizeRemoteStatusForStorage(remote.status),
2367
2709
  expiresAt: remote.expiresAt ?? session.expiresAt,
2368
2710
  privyAppId: remote.privyAppId ?? session.privyAppId,
2369
- walletId: walletId2 ?? session.walletId,
2370
- evmWalletId: remote.evmWalletId ?? walletId2 ?? session.evmWalletId,
2371
- evmAddress: remote.evmAddress ?? remote.address ?? session.evmAddress,
2372
- solanaWalletId: remote.solanaWalletId ?? session.solanaWalletId,
2373
- solanaAddress: remote.solanaAddress ?? session.solanaAddress,
2374
- chainType: remote.chainType ?? session.chainType,
2375
- capabilities: remote.capabilities ?? session.capabilities
2711
+ walletId: evmWalletId ?? solanaWalletId ?? session.walletId,
2712
+ evmWalletId,
2713
+ evmAddress,
2714
+ solanaWalletId,
2715
+ solanaAddress,
2716
+ chainType,
2717
+ capabilities: mergeRemoteCapabilities(session, remoteSessions),
2718
+ sessionsByChain: nextSessionsByChain
2376
2719
  };
2377
2720
  saveSession(session);
2378
2721
  }
2379
- const status = deriveWalletStatus(session, remoteStatus ?? void 0);
2722
+ const status = deriveWalletStatus(session);
2380
2723
  const walletAddress = resolveSessionAddress(session);
2381
2724
  const environment = resolveSessionEnvironment(session);
2382
2725
  const signerCapabilities = resolveEnabledCapabilities(session);
@@ -2392,14 +2735,16 @@ async function buildSessionSnapshot(program2, verify) {
2392
2735
  walletId,
2393
2736
  expiresAt,
2394
2737
  sessionId: session?.sessionId ?? null,
2738
+ connectionRequestId: session?.connectionRequestId ?? null,
2395
2739
  sessionState: session?.status ?? null,
2396
2740
  chainType: session?.chainType ?? null,
2741
+ sessionsByChain: session?.sessionsByChain ?? null,
2397
2742
  valid: session ? isSessionValid(session) : false
2398
2743
  };
2399
2744
  }
2400
2745
  function registerWallets(program2) {
2401
2746
  const cmd = program2.command("wallet").description("Manage wallets");
2402
- cmd.command("connect").description("Connect a wallet for onchain actions (session or local)").option("--mode <mode>", "session | local").option("--chain <chain>", "For --mode local: evm | solana | both (default: both)").option("--import <path>", "For --mode local: import EVM key from file (implies --chain evm)").option("--instance-name <name>", "For --mode session: name this CLI instance").option("--force", "Replace the existing signer").action(async (opts) => {
2747
+ cmd.command("connect").description("Connect a wallet for onchain actions (session or local)").option("--mode <mode>", "session | local").option("--import <path>", "For --mode local: import an EVM key from file instead of creating both wallets").option("--instance-name <name>", "For --mode session: name this CLI instance").option("--force", "Replace the existing signer").action(async (opts) => {
2403
2748
  try {
2404
2749
  await runConnectFlow(program2, opts);
2405
2750
  } catch (err) {
@@ -2429,10 +2774,12 @@ function registerWallets(program2) {
2429
2774
  expiresAt: snap.expiresAt,
2430
2775
  environment: snap.environment,
2431
2776
  signerCapabilities: snap.signerCapabilities,
2777
+ connectionRequestId: snap.connectionRequestId,
2432
2778
  sessionId: snap.sessionId,
2433
2779
  sessionState: snap.sessionState,
2434
2780
  walletId: snap.walletId,
2435
2781
  chainType: snap.chainType,
2782
+ sessionsByChain: snap.sessionsByChain,
2436
2783
  verified: Boolean(opts.verify),
2437
2784
  remoteStatus: snap.remoteStatus,
2438
2785
  valid: snap.valid,
@@ -2443,10 +2790,12 @@ function registerWallets(program2) {
2443
2790
  expiresAt: snap.expiresAt,
2444
2791
  environment: snap.environment,
2445
2792
  signerCapabilities: snap.signerCapabilities,
2793
+ connectionRequestId: snap.connectionRequestId,
2446
2794
  sessionId: snap.sessionId,
2447
2795
  sessionState: snap.sessionState,
2448
2796
  walletId: snap.walletId,
2449
2797
  chainType: snap.chainType,
2798
+ sessionsByChain: snap.sessionsByChain,
2450
2799
  valid: snap.valid
2451
2800
  },
2452
2801
  localEvm: localState.evmAddress ? { address: localState.evmAddress, keyFile: localState.evmKeyFile } : null,
@@ -2464,7 +2813,11 @@ function registerWallets(program2) {
2464
2813
  ["Environment", snap.environment ?? dim("none")],
2465
2814
  ["Signer Capabilities", snap.signerCapabilities.length > 0 ? snap.signerCapabilities.join(", ") : dim("none")]
2466
2815
  ];
2467
- if (snap.sessionId) pairs.push(["Session ID", snap.sessionId]);
2816
+ if (snap.connectionRequestId) {
2817
+ pairs.push(["Connection Request ID", snap.connectionRequestId]);
2818
+ } else if (snap.sessionId) {
2819
+ pairs.push(["Session ID", snap.sessionId]);
2820
+ }
2468
2821
  if (snap.walletId) pairs.push(["Wallet ID", snap.walletId]);
2469
2822
  if (opts.verify) {
2470
2823
  pairs.push(["Backend Status", snap.remoteStatus ?? dim("not checked")]);
@@ -2619,7 +2972,7 @@ function registerWallets(program2) {
2619
2972
  }
2620
2973
  if (target === "local" && !hasLocalEvmKey()) {
2621
2974
  throw errInvalidArgs(
2622
- "No local EVM key configured. Run `alchemy wallet connect --mode local --chain evm` first."
2975
+ "No local EVM key configured. Run `alchemy wallet connect --mode local` first."
2623
2976
  );
2624
2977
  }
2625
2978
  const cfg = load();
@@ -2653,11 +3006,23 @@ function registerWallets(program2) {
2653
3006
  let revoked = false;
2654
3007
  let alreadyDisconnected = false;
2655
3008
  let remoteStatus = null;
3009
+ let backendRevokeFailed = false;
3010
+ const sessionIdsToDisconnect = uniqueSessionIds(session);
2656
3011
  if (authToken) {
2657
- const remote = await disconnectRemoteWalletSession(authToken, session.sessionId);
2658
- revoked = !remote.alreadyDisconnected;
2659
- alreadyDisconnected = remote.alreadyDisconnected;
2660
- remoteStatus = remote.status;
3012
+ const results = await Promise.allSettled(
3013
+ sessionIdsToDisconnect.map(
3014
+ (sessionId) => disconnectRemoteWalletSession(authToken, sessionId)
3015
+ )
3016
+ );
3017
+ const successfulResults = results.flatMap((result) => {
3018
+ return result.status === "fulfilled" ? [result.value] : [];
3019
+ });
3020
+ backendRevokeFailed = results.some((result) => {
3021
+ return result.status === "rejected";
3022
+ });
3023
+ revoked = successfulResults.some((remote) => !remote.alreadyDisconnected);
3024
+ alreadyDisconnected = successfulResults.length > 0 && successfulResults.every((remote) => remote.alreadyDisconnected);
3025
+ remoteStatus = successfulResults[0]?.status ?? null;
2661
3026
  }
2662
3027
  const removed = clearSession();
2663
3028
  const cfg = load();
@@ -2671,8 +3036,11 @@ function registerWallets(program2) {
2671
3036
  revoked,
2672
3037
  alreadyDisconnected,
2673
3038
  remoteStatus,
2674
- backendRevokeAttempted: Boolean(authToken)
3039
+ backendRevokeAttempted: Boolean(authToken),
3040
+ backendRevokeFailed
2675
3041
  });
3042
+ } else if (removed && authToken && backendRevokeFailed) {
3043
+ console.log(` ${green("\u2713")} Wallet session disconnected (${dim("backend revoke partially failed")})`);
2676
3044
  } else if (removed && authToken && alreadyDisconnected) {
2677
3045
  console.log(` ${green("\u2713")} Wallet session disconnected (${dim("already revoked or expired")})`);
2678
3046
  } else if (removed && authToken) {
@@ -3056,9 +3424,10 @@ import {
3056
3424
  createKeyPairSignerFromBytes as createKeyPairSignerFromBytes2,
3057
3425
  createKeyPairSignerFromPrivateKeyBytes as createKeyPairSignerFromPrivateKeyBytes2
3058
3426
  } from "@solana/kit";
3059
- import { getTransferSolInstruction } from "@solana-program/system";
3427
+ import { sign as signWithPrivateKey } from "crypto";
3060
3428
  var SOL_DECIMALS = 9;
3061
3429
  var SPONSOR_FEE_PAYER_PLACEHOLDER = address("Amh6quo1FcmL16Qmzdugzjq3Lv1zXzTW7ktswyLDzits");
3430
+ var SYSTEM_PROGRAM_ADDRESS = address("11111111111111111111111111111111");
3062
3431
  var SPL_TOKEN_PROGRAM_ADDRESS = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
3063
3432
  function parseSolanaKeyBytes(secret) {
3064
3433
  const trimmed = secret.trim();
@@ -3089,11 +3458,28 @@ async function createSolanaSignerFromKeyBytes(keyBytes) {
3089
3458
  throw errInvalidArgs("Invalid Solana key: expected 64-byte secret key or 32-byte private key.");
3090
3459
  }
3091
3460
  function buildSolTransferInstruction(from, to, lamports) {
3092
- return getTransferSolInstruction({
3093
- source: from,
3094
- destination: to,
3095
- amount: lamports
3096
- });
3461
+ return {
3462
+ programAddress: SYSTEM_PROGRAM_ADDRESS,
3463
+ accounts: [
3464
+ { address: from.address, role: AccountRole.WRITABLE_SIGNER },
3465
+ { address: to, role: AccountRole.WRITABLE }
3466
+ ],
3467
+ data: new Uint8Array([
3468
+ ...encodeU32LE(2),
3469
+ ...encodeU64LE(lamports)
3470
+ ])
3471
+ };
3472
+ }
3473
+ function encodeU32LE(value) {
3474
+ if (!Number.isSafeInteger(value) || value < 0 || value > 4294967295) {
3475
+ throw errInvalidArgs("Instruction discriminator must fit in an unsigned 32-bit integer.");
3476
+ }
3477
+ const bytes = new Uint8Array(4);
3478
+ bytes[0] = value & 255;
3479
+ bytes[1] = value >> 8 & 255;
3480
+ bytes[2] = value >> 16 & 255;
3481
+ bytes[3] = value >> 24 & 255;
3482
+ return bytes;
3097
3483
  }
3098
3484
  function encodeU64LE(value) {
3099
3485
  if (value < 0n || value > 0xffffffffffffffffn) {
@@ -3174,6 +3560,38 @@ async function buildAndSendSolanaTransaction(opts) {
3174
3560
  const signature = await client.call("sendTransaction", [wireTransaction, { encoding: "base64" }]);
3175
3561
  return { signature, fromAddress: signer.address };
3176
3562
  }
3563
+ async function buildAndSendSolanaTransactionWithSession(opts) {
3564
+ const { client, instructions, session, authToken, sponsored, gasPolicyId } = opts;
3565
+ const fromAddress = getSolanaSessionAddress(session);
3566
+ const blockhashResult = await client.call("getLatestBlockhash", [{ commitment: "finalized" }]);
3567
+ const { blockhash, lastValidBlockHeight } = blockhashResult.value;
3568
+ const feePayer = sponsored ? SPONSOR_FEE_PAYER_PLACEHOLDER : address(fromAddress);
3569
+ const txMessage = pipe(
3570
+ createTransactionMessage({ version: 0 }),
3571
+ (msg) => setTransactionMessageFeePayer(feePayer, msg),
3572
+ (msg) => setTransactionMessageLifetimeUsingBlockhash(
3573
+ { blockhash, lastValidBlockHeight },
3574
+ msg
3575
+ ),
3576
+ (msg) => appendTransactionMessageInstructions(instructions, msg)
3577
+ );
3578
+ const compiledTx = compileTransaction(txMessage);
3579
+ const transactionToSign = sponsored ? await sponsorSolanaTransaction({
3580
+ client,
3581
+ compiledTransactionBase64: getBase64EncodedWireTransaction(compiledTx),
3582
+ gasPolicyId
3583
+ }) : getBase64EncodedWireTransaction(compiledTx);
3584
+ const signedTransaction = await signSolanaTransactionWithSession({
3585
+ authToken,
3586
+ session,
3587
+ transactionBase64: transactionToSign
3588
+ });
3589
+ const signature = await client.call("sendTransaction", [
3590
+ signedTransaction,
3591
+ { encoding: "base64" }
3592
+ ]);
3593
+ return { signature, fromAddress };
3594
+ }
3177
3595
  async function waitForSolanaConfirmation(client, signature, timeoutMs = 6e4, pollIntervalMs = 2e3) {
3178
3596
  const start = Date.now();
3179
3597
  while (Date.now() - start < timeoutMs) {
@@ -3190,17 +3608,99 @@ async function waitForSolanaConfirmation(client, signature, timeoutMs = 6e4, pol
3190
3608
  }
3191
3609
  return false;
3192
3610
  }
3611
+ async function sponsorSolanaTransaction(args) {
3612
+ if (!args.gasPolicyId) {
3613
+ throw errInvalidArgs("Fee sponsorship requires a fee policy ID.");
3614
+ }
3615
+ const feePayerResponse = await args.client.call("alchemy_requestFeePayer", [{
3616
+ policyId: args.gasPolicyId,
3617
+ serializedTransaction: args.compiledTransactionBase64
3618
+ }]);
3619
+ return feePayerResponse.serializedTransaction;
3620
+ }
3621
+ async function signSolanaTransactionWithSession(args) {
3622
+ const binding = getSolanaSessionBinding(args.session);
3623
+ if (args.session.capabilities?.["solana.signTransaction"] === false) {
3624
+ throw errInvalidArgs(
3625
+ "Delegated wallet session does not allow 'solana.signTransaction'. Reconnect the wallet session to refresh capabilities."
3626
+ );
3627
+ }
3628
+ const challenge = await createRemoteSolanaSignTransactionChallenge(args.authToken, {
3629
+ ...binding,
3630
+ transaction: args.transactionBase64,
3631
+ encoding: "base64"
3632
+ });
3633
+ const completion = await completeRemoteSolanaSignTransactionChallenge(args.authToken, {
3634
+ challengeId: challenge.challengeId,
3635
+ signature: signChallengePayload({
3636
+ challengePayload: challenge.challenge,
3637
+ privateKeyPem: args.session.privateKeyPem
3638
+ })
3639
+ });
3640
+ return completion.signedTransaction;
3641
+ }
3642
+ function getSolanaSessionBinding(session) {
3643
+ const walletId = session.solanaWalletId ?? session.walletId;
3644
+ const walletAddress = getSolanaSessionAddress(session);
3645
+ if (!walletId) {
3646
+ throw errInvalidArgs(
3647
+ "Delegated wallet session is missing Solana wallet ID metadata. Run 'alchemy wallet connect --mode session --force'."
3648
+ );
3649
+ }
3650
+ if (!session.privyKeyQuorumId && !session.privySignerId) {
3651
+ throw errInvalidArgs(
3652
+ "Delegated wallet session is missing signer binding metadata. Run 'alchemy wallet connect --mode session --force'."
3653
+ );
3654
+ }
3655
+ return {
3656
+ sessionId: session.sessionId,
3657
+ walletId,
3658
+ walletAddress,
3659
+ privyKeyQuorumId: session.privyKeyQuorumId,
3660
+ privySignerId: session.privySignerId
3661
+ };
3662
+ }
3663
+ function getSolanaSessionAddress(session) {
3664
+ if (!session.solanaAddress) {
3665
+ throw errInvalidArgs(
3666
+ "Delegated wallet session is missing Solana address metadata. Run 'alchemy wallet connect --mode session --force'."
3667
+ );
3668
+ }
3669
+ return session.solanaAddress;
3670
+ }
3671
+ function signChallengePayload(args) {
3672
+ return signWithPrivateKey(
3673
+ "sha256",
3674
+ Buffer.from(args.challengePayload, "utf8"),
3675
+ {
3676
+ key: args.privateKeyPem,
3677
+ dsaEncoding: "der"
3678
+ }
3679
+ ).toString("base64url");
3680
+ }
3193
3681
 
3194
3682
  // src/lib/solana-fees.ts
3195
- function resolveSolanaFeeSponsorship(program2) {
3683
+ async function resolveSolanaFeeSponsorship(program2) {
3196
3684
  const sponsored = resolveSolanaFeeSponsored(program2);
3197
- const feePolicyId = resolveSolanaFeePolicyId(program2);
3198
- if (sponsored && !feePolicyId) {
3199
- throw errInvalidArgs(
3200
- "Fee sponsorship requires a fee policy ID. Set one with --fee-policy-id or configure a Solana fee policy."
3201
- );
3685
+ const existing = resolveSolanaFeePolicyId(program2);
3686
+ if (!sponsored) return { sponsored, feePolicyId: existing };
3687
+ if (existing) return { sponsored, feePolicyId: existing };
3688
+ if (!isInteractiveAllowed(program2)) {
3689
+ throw errSponsorshipNeedsPolicy("solana");
3690
+ }
3691
+ if (!hasAuthLoginToken()) {
3692
+ throw errNotLoggedInForPolicyLookup();
3202
3693
  }
3203
- return { sponsored, feePolicyId };
3694
+ const network = resolveSolanaNetwork(program2);
3695
+ const selected = await selectOrCreatePolicy({
3696
+ flavor: "solana",
3697
+ network,
3698
+ program: program2
3699
+ });
3700
+ if (!selected) {
3701
+ throw errSponsorshipNeedsPolicy("solana");
3702
+ }
3703
+ return { sponsored, feePolicyId: selected };
3204
3704
  }
3205
3705
 
3206
3706
  // src/commands/solana-delegate.ts
@@ -3211,23 +3711,54 @@ function parseDecimals(value) {
3211
3711
  }
3212
3712
  return decimals;
3213
3713
  }
3214
- async function resolveLocalSolanaDelegateSigner(program2, signerOpt) {
3714
+ async function resolveSolanaDelegateSigner(program2, signerOpt) {
3215
3715
  const signer = parseSignerOpt(signerOpt);
3216
3716
  if (signer === "session") {
3217
- throw errInvalidArgs(
3218
- "The session wallet does not yet support Solana. Use `--signer local` or configure a local Solana wallet with `alchemy wallet connect --mode local --chain solana`."
3219
- );
3717
+ return resolveSessionSolanaDelegateSigner();
3718
+ }
3719
+ if (signer === void 0 && resolveActiveSigner(program2) === "session") {
3720
+ return resolveSessionSolanaDelegateSigner();
3220
3721
  }
3221
3722
  const solanaKey = resolveSolanaWalletKey(program2);
3222
3723
  if (!solanaKey) {
3724
+ if (signer === void 0) {
3725
+ const sessionSigner = tryResolveSessionSolanaDelegateSigner();
3726
+ if (sessionSigner) {
3727
+ return sessionSigner;
3728
+ }
3729
+ }
3223
3730
  throw errSolanaWalletKeyRequired();
3224
3731
  }
3225
3732
  const keyBytes = parseSolanaKeyBytes(solanaKey);
3226
3733
  return {
3734
+ type: "local",
3227
3735
  keyBytes,
3228
3736
  signer: await createSolanaSignerFromKeyBytes(keyBytes)
3229
3737
  };
3230
3738
  }
3739
+ function resolveSessionSolanaDelegateSigner() {
3740
+ const sessionSigner = tryResolveSessionSolanaDelegateSigner();
3741
+ if (!sessionSigner) {
3742
+ throw errInvalidArgs(
3743
+ "No active Solana wallet session. Run `alchemy wallet connect --mode session` first."
3744
+ );
3745
+ }
3746
+ return sessionSigner;
3747
+ }
3748
+ function tryResolveSessionSolanaDelegateSigner() {
3749
+ const authToken = resolveAuthToken();
3750
+ const session = resolveWalletSession();
3751
+ const solanaSession = session ? getWalletSessionByChain(session, "solana") : null;
3752
+ if (!authToken || !solanaSession?.solanaAddress) {
3753
+ return void 0;
3754
+ }
3755
+ return {
3756
+ type: "session",
3757
+ address: solanaSession.solanaAddress,
3758
+ session: solanaSession,
3759
+ authToken
3760
+ };
3761
+ }
3231
3762
  function registerSolanaDelegate(program2) {
3232
3763
  const cmd = program2.command("delegate").description("Delegate Solana token authority");
3233
3764
  const approveCmd = cmd.command("approve").description("Approve a delegate for an SPL token account").requiredOption("--token-account <address>", "SPL token account to delegate from").requiredOption("--mint <address>", "SPL token mint").requiredOption("--delegate <address>", "Delegate address").requiredOption("--amount <number>", "Amount to delegate in decimal token units").requiredOption("--decimals <n>", "Mint decimals").option("--fee-sponsored", "Enable Solana fee sponsorship").option("--fee-policy-id <id>", "Solana fee policy ID for sponsorship");
@@ -3261,28 +3792,40 @@ async function performSolanaDelegateApprove(program2, opts) {
3261
3792
  validateSolanaAddress(opts.delegate);
3262
3793
  const decimals = parseDecimals(opts.decimals);
3263
3794
  const rawAmount = parseAmount(opts.amount, decimals);
3264
- const { keyBytes, signer } = await resolveLocalSolanaDelegateSigner(program2, opts.signer);
3795
+ const signer = await resolveSolanaDelegateSigner(program2, opts.signer);
3265
3796
  const network = resolveSolanaNetwork(program2);
3266
- const { sponsored, feePolicyId } = resolveSolanaFeeSponsorship(program2);
3797
+ const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
3267
3798
  const client = clientFromFlags(program2, { forceNetwork: network });
3268
3799
  const instruction = buildSplTokenApproveCheckedInstruction({
3269
3800
  tokenAccount: solAddress(opts.tokenAccount),
3270
3801
  mint: solAddress(opts.mint),
3271
3802
  delegate: solAddress(opts.delegate),
3272
- owner: signer,
3803
+ owner: { address: solAddress(signer.type === "session" ? signer.address : signer.signer.address) },
3273
3804
  amount: rawAmount,
3274
3805
  decimals
3275
3806
  });
3276
3807
  const result = await withSpinner(
3277
3808
  "Approving delegate...",
3278
3809
  "Delegate approved",
3279
- () => buildAndSendSolanaTransaction({
3280
- client,
3281
- instructions: [instruction],
3282
- senderKeyBytes: keyBytes,
3283
- sponsored,
3284
- gasPolicyId: feePolicyId
3285
- })
3810
+ async () => {
3811
+ if (signer.type === "session") {
3812
+ return await buildAndSendSolanaTransactionWithSession({
3813
+ client,
3814
+ instructions: [instruction],
3815
+ session: signer.session,
3816
+ authToken: signer.authToken,
3817
+ sponsored,
3818
+ gasPolicyId: feePolicyId
3819
+ });
3820
+ }
3821
+ return await buildAndSendSolanaTransaction({
3822
+ client,
3823
+ instructions: [instruction],
3824
+ senderKeyBytes: signer.keyBytes,
3825
+ sponsored,
3826
+ gasPolicyId: feePolicyId
3827
+ });
3828
+ }
3286
3829
  );
3287
3830
  const confirmed = await withSpinner(
3288
3831
  "Waiting for confirmation...",
@@ -3323,24 +3866,36 @@ async function performSolanaDelegateApprove(program2, opts) {
3323
3866
  }
3324
3867
  async function performSolanaDelegateRevoke(program2, opts) {
3325
3868
  validateSolanaAddress(opts.tokenAccount);
3326
- const { keyBytes, signer } = await resolveLocalSolanaDelegateSigner(program2, opts.signer);
3869
+ const signer = await resolveSolanaDelegateSigner(program2, opts.signer);
3327
3870
  const network = resolveSolanaNetwork(program2);
3328
- const { sponsored, feePolicyId } = resolveSolanaFeeSponsorship(program2);
3871
+ const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
3329
3872
  const client = clientFromFlags(program2, { forceNetwork: network });
3330
3873
  const instruction = buildSplTokenRevokeInstruction({
3331
3874
  tokenAccount: solAddress(opts.tokenAccount),
3332
- owner: signer
3875
+ owner: { address: solAddress(signer.type === "session" ? signer.address : signer.signer.address) }
3333
3876
  });
3334
3877
  const result = await withSpinner(
3335
3878
  "Revoking delegate...",
3336
3879
  "Delegate revoked",
3337
- () => buildAndSendSolanaTransaction({
3338
- client,
3339
- instructions: [instruction],
3340
- senderKeyBytes: keyBytes,
3341
- sponsored,
3342
- gasPolicyId: feePolicyId
3343
- })
3880
+ async () => {
3881
+ if (signer.type === "session") {
3882
+ return await buildAndSendSolanaTransactionWithSession({
3883
+ client,
3884
+ instructions: [instruction],
3885
+ session: signer.session,
3886
+ authToken: signer.authToken,
3887
+ sponsored,
3888
+ gasPolicyId: feePolicyId
3889
+ });
3890
+ }
3891
+ return await buildAndSendSolanaTransaction({
3892
+ client,
3893
+ instructions: [instruction],
3894
+ senderKeyBytes: signer.keyBytes,
3895
+ sponsored,
3896
+ gasPolicyId: feePolicyId
3897
+ });
3898
+ }
3344
3899
  );
3345
3900
  const confirmed = await withSpinner(
3346
3901
  "Waiting for confirmation...",
@@ -3584,35 +4139,41 @@ async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts
3584
4139
  if (tokenAddress) {
3585
4140
  throw errInvalidArgs("SPL token transfers are not yet supported. Omit --token for native SOL transfers.");
3586
4141
  }
3587
- if (opts.signer === "session") {
3588
- throw errInvalidArgs(
3589
- "The session wallet does not yet support Solana. Use `--signer local` or configure a local Solana wallet with `alchemy wallet connect --mode local --chain solana`."
3590
- );
3591
- }
3592
- const solanaKey = resolveSolanaWalletKey(program2);
3593
- if (!solanaKey) {
3594
- throw errSolanaWalletKeyRequired();
3595
- }
3596
- const keyBytes = parseSolanaKeyBytes(solanaKey);
4142
+ const signer = await resolveSolanaSigner(program2, opts.signer);
3597
4143
  validateSolanaAddress(toArg);
3598
4144
  const to = solAddress2(toArg);
3599
4145
  const network = resolveSolanaNetwork(program2);
3600
4146
  const symbol = nativeTokenSymbol(network);
3601
4147
  const lamports = parseAmount(amountArg, SOL_DECIMALS);
3602
- const signer = await createSolanaSignerFromKeyBytes(keyBytes);
3603
- const instruction = buildSolTransferInstruction(signer, to, lamports);
3604
- const { sponsored, feePolicyId } = resolveSolanaFeeSponsorship(program2);
4148
+ const instruction = buildSolTransferInstruction(
4149
+ { address: solAddress2(signer.address) },
4150
+ to,
4151
+ lamports
4152
+ );
4153
+ const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
3605
4154
  const client = clientFromFlags(program2, { forceNetwork: network });
3606
4155
  const result = await withSpinner(
3607
4156
  "Sending transaction\u2026",
3608
4157
  "Transaction submitted",
3609
- () => buildAndSendSolanaTransaction({
3610
- client,
3611
- instructions: [instruction],
3612
- senderKeyBytes: keyBytes,
3613
- sponsored,
3614
- gasPolicyId: feePolicyId
3615
- })
4158
+ async () => {
4159
+ if (signer.type === "session") {
4160
+ return await buildAndSendSolanaTransactionWithSession({
4161
+ client,
4162
+ instructions: [instruction],
4163
+ session: signer.session,
4164
+ authToken: signer.authToken,
4165
+ sponsored,
4166
+ gasPolicyId: feePolicyId
4167
+ });
4168
+ }
4169
+ return await buildAndSendSolanaTransaction({
4170
+ client,
4171
+ instructions: [instruction],
4172
+ senderKeyBytes: signer.keyBytes,
4173
+ sponsored,
4174
+ gasPolicyId: feePolicyId
4175
+ });
4176
+ }
3616
4177
  );
3617
4178
  const confirmed = await withSpinner(
3618
4179
  "Waiting for confirmation\u2026",
@@ -3645,6 +4206,61 @@ async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts
3645
4206
  printKeyValue(pairs);
3646
4207
  }
3647
4208
  }
4209
+ async function resolveSolanaSigner(program2, signer) {
4210
+ if (signer === "session") {
4211
+ return resolveSessionSolanaSigner();
4212
+ }
4213
+ if (signer === void 0 && resolveActiveSigner(program2) === "session") {
4214
+ return resolveSessionSolanaSigner();
4215
+ }
4216
+ const solanaKey = resolveSolanaWalletKey(program2);
4217
+ if (!solanaKey) {
4218
+ if (signer === void 0) {
4219
+ const authToken = resolveAuthToken();
4220
+ const session = resolveWalletSession();
4221
+ const solanaSession = session ? getWalletSessionByChain(session, "solana") : null;
4222
+ if (authToken && solanaSession?.solanaAddress) {
4223
+ return {
4224
+ type: "session",
4225
+ address: solanaSession.solanaAddress,
4226
+ session: solanaSession,
4227
+ authToken
4228
+ };
4229
+ }
4230
+ }
4231
+ throw errSolanaWalletKeyRequired();
4232
+ }
4233
+ const keyBytes = parseSolanaKeyBytes(solanaKey);
4234
+ const localSigner = await createSolanaSignerFromKeyBytes(keyBytes);
4235
+ return {
4236
+ type: "local",
4237
+ address: localSigner.address,
4238
+ keyBytes
4239
+ };
4240
+ }
4241
+ function resolveSessionSolanaSigner() {
4242
+ const sessionSigner = tryResolveSessionSolanaSigner();
4243
+ if (!sessionSigner) {
4244
+ throw errInvalidArgs(
4245
+ "No active Solana wallet session. Run `alchemy wallet connect --mode session` first."
4246
+ );
4247
+ }
4248
+ return sessionSigner;
4249
+ }
4250
+ function tryResolveSessionSolanaSigner() {
4251
+ const authToken = resolveAuthToken();
4252
+ const session = resolveWalletSession();
4253
+ const solanaSession = session ? getWalletSessionByChain(session, "solana") : null;
4254
+ if (!authToken || !solanaSession?.solanaAddress) {
4255
+ return void 0;
4256
+ }
4257
+ return {
4258
+ type: "session",
4259
+ address: solanaSession.solanaAddress,
4260
+ session: solanaSession,
4261
+ authToken
4262
+ };
4263
+ }
3648
4264
 
3649
4265
  // src/lib/smart-wallet.ts
3650
4266
  import { createSmartWalletClient } from "@alchemy/wallet-apis";
@@ -3704,7 +4320,7 @@ function networkToChain(network) {
3704
4320
  }
3705
4321
 
3706
4322
  // src/lib/delegated-signer.ts
3707
- import { sign as signWithPrivateKey } from "crypto";
4323
+ import { sign as signWithPrivateKey2 } from "crypto";
3708
4324
  import { isAddressEqual, recoverMessageAddress, recoverTypedDataAddress } from "viem";
3709
4325
  import { toAccount } from "viem/accounts";
3710
4326
  var messageCapabilities = {
@@ -3718,8 +4334,8 @@ function toHex(bytes) {
3718
4334
  function normalizeSignedChallengeSignature(signature) {
3719
4335
  return signature.toString("base64url");
3720
4336
  }
3721
- function signChallengePayload(args) {
3722
- const signature = signWithPrivateKey(
4337
+ function signChallengePayload2(args) {
4338
+ const signature = signWithPrivateKey2(
3723
4339
  "sha256",
3724
4340
  Buffer.from(args.challengePayload, "utf8"),
3725
4341
  {
@@ -3902,7 +4518,7 @@ function createDelegatedAccount(args) {
3902
4518
  message: remoteMessage.message,
3903
4519
  encoding: remoteMessage.encoding
3904
4520
  });
3905
- const signature = signChallengePayload({
4521
+ const signature = signChallengePayload2({
3906
4522
  challengePayload: challenge.challenge,
3907
4523
  privateKeyPem: args.session.privateKeyPem
3908
4524
  });
@@ -3937,7 +4553,7 @@ function createDelegatedAccount(args) {
3937
4553
  ...sessionBinding,
3938
4554
  typedData: serializedTypedData
3939
4555
  });
3940
- const signature = signChallengePayload({
4556
+ const signature = signChallengePayload2({
3941
4557
  challengePayload: challenge.challenge,
3942
4558
  privateKeyPem: args.session.privateKeyPem
3943
4559
  });
@@ -3966,7 +4582,7 @@ function createDelegatedAccount(args) {
3966
4582
  ...sessionBinding,
3967
4583
  authorization
3968
4584
  });
3969
- const signature = signChallengePayload({
4585
+ const signature = signChallengePayload2({
3970
4586
  challengePayload: challenge.challenge,
3971
4587
  privateKeyPem: args.session.privateKeyPem
3972
4588
  });
@@ -3998,6 +4614,28 @@ function createAlchemyWalletTransport(apiKey) {
3998
4614
  }
3999
4615
 
4000
4616
  // src/lib/smart-wallet.ts
4617
+ async function ensureGasPolicyResolved(program2) {
4618
+ const cfg = load();
4619
+ if (!resolveGasSponsored(program2, cfg)) return void 0;
4620
+ const existing = resolveGasPolicyId(program2, cfg);
4621
+ if (existing) return existing;
4622
+ if (!isInteractiveAllowed(program2)) {
4623
+ throw errSponsorshipNeedsPolicy("sponsorship");
4624
+ }
4625
+ if (!hasAuthLoginToken(cfg)) {
4626
+ throw errNotLoggedInForPolicyLookup();
4627
+ }
4628
+ const network = resolveNetwork(program2, cfg);
4629
+ const policyId = await selectOrCreatePolicy({
4630
+ flavor: "sponsorship",
4631
+ network,
4632
+ program: program2
4633
+ });
4634
+ if (!policyId) {
4635
+ throw errSponsorshipNeedsPolicy("sponsorship");
4636
+ }
4637
+ return policyId;
4638
+ }
4001
4639
  function normalizeKey(key) {
4002
4640
  const trimmed = key.trim();
4003
4641
  return trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
@@ -4045,7 +4683,7 @@ function buildWalletClient(program2, options = {}) {
4045
4683
  const network = resolveNetwork(program2, cfg);
4046
4684
  const chain = networkToChain(network);
4047
4685
  const gasSponsored = resolveGasSponsored(program2, cfg);
4048
- const gasPolicyId = resolveGasPolicyId(program2, cfg);
4686
+ const gasPolicyId = options.gasPolicyIdOverride ?? resolveGasPolicyId(program2, cfg);
4049
4687
  if (gasSponsored && !gasPolicyId) {
4050
4688
  throw errInvalidArgs(
4051
4689
  "Gas sponsorship requires a gas policy ID. Set one with --gas-policy-id or `alchemy config set evm-gas-policy-id <id>`."
@@ -4065,12 +4703,13 @@ function buildWalletClient(program2, options = {}) {
4065
4703
  }
4066
4704
  throw errNoActiveSession();
4067
4705
  }
4068
- assertSessionMetadata(validSession);
4069
- if (validSession.chainType && validSession.chainType !== "evm") {
4706
+ const evmSession = getWalletSessionByChain(validSession, "evm");
4707
+ if (!evmSession) {
4070
4708
  throw errInvalidArgs(
4071
- `Wallet session is configured for '${validSession.chainType}', not EVM. Run 'alchemy wallet connect --force'.`
4709
+ "Wallet session is missing EVM metadata. Run 'alchemy wallet connect --mode session --force'."
4072
4710
  );
4073
4711
  }
4712
+ assertSessionMetadata(evmSession);
4074
4713
  const authToken = resolveAuthToken(cfg);
4075
4714
  if (!authToken) {
4076
4715
  throw errAuthRequired();
@@ -4078,9 +4717,9 @@ function buildWalletClient(program2, options = {}) {
4078
4717
  return {
4079
4718
  signer: createDelegatedAccount({
4080
4719
  authToken,
4081
- session: validSession
4720
+ session: evmSession
4082
4721
  }),
4083
- address: validSession.evmAddress
4722
+ address: evmSession.evmAddress
4084
4723
  };
4085
4724
  }
4086
4725
  if (!localKey) throw errWalletKeyRequired();
@@ -4819,7 +5458,7 @@ function buildAgentPrompt(program2) {
4819
5458
  method: "Local wallet + API key",
4820
5459
  envVar: "ALCHEMY_WALLET_KEY",
4821
5460
  flag: "--wallet-key-file <path> | --signer local",
4822
- setup: "alchemy wallet connect --mode local --chain evm",
5461
+ setup: "alchemy wallet connect --mode local",
4823
5462
  commandFamilies: [
4824
5463
  "evm send",
4825
5464
  "evm contract call",
@@ -5083,7 +5722,11 @@ async function performApprove(program2, spenderArg, opts) {
5083
5722
  }
5084
5723
  validateApprovalMode(opts);
5085
5724
  const signer = parseSignerOpt(opts.signer);
5086
- const { client, network, address: from, paymaster } = buildWalletClient(program2, { signer });
5725
+ const gasPolicyIdOverride = await ensureGasPolicyResolved(program2);
5726
+ const { client, network, address: from, paymaster } = buildWalletClient(program2, {
5727
+ signer,
5728
+ ...gasPolicyIdOverride && { gasPolicyIdOverride }
5729
+ });
5087
5730
  const rpcClient = clientFromFlags(program2);
5088
5731
  const tokenMeta = await fetchTokenDecimals(program2, opts.tokenAddress);
5089
5732
  const approval = buildApprovalRequest(opts, tokenMeta);
@@ -5559,7 +6202,11 @@ async function performContractCall(program2, addressArg, functionArg, opts) {
5559
6202
  const { abi, functionName } = resolveAbi(functionArg, opts);
5560
6203
  const args = parseArgs(opts.args);
5561
6204
  const signer = parseSignerOpt(opts.signer);
5562
- const { client, network, address: from, paymaster } = buildWalletClient(program2, { signer });
6205
+ const gasPolicyIdOverride = await ensureGasPolicyResolved(program2);
6206
+ const { client, network, address: from, paymaster } = buildWalletClient(program2, {
6207
+ signer,
6208
+ ...gasPolicyIdOverride && { gasPolicyIdOverride }
6209
+ });
5563
6210
  const rpcClient = clientFromFlags(program2);
5564
6211
  const contractAddress = await resolveAddress(addressArg, rpcClient);
5565
6212
  const data = encodeFunctionData2({ abi, functionName, args });
@@ -6722,7 +7369,7 @@ Examples:
6722
7369
  dryRun: opts.dryRun
6723
7370
  });
6724
7371
  } catch (err) {
6725
- const { exitWithError: exitWithError2 } = await import("./errors-E2P6WHTX.js");
7372
+ const { exitWithError: exitWithError2 } = await import("./errors-T6XE2I2L.js");
6726
7373
  exitWithError2(err);
6727
7374
  }
6728
7375
  });
@@ -6731,7 +7378,11 @@ async function performEvmSend(program2, toArg, amountArg, tokenAddress, opts = {
6731
7378
  if (tokenAddress) {
6732
7379
  validateAddress(tokenAddress);
6733
7380
  }
6734
- const { client, network, address: from, paymaster } = buildWalletClient(program2, { signer: opts.signer });
7381
+ const gasPolicyIdOverride = await ensureGasPolicyResolved(program2);
7382
+ const { client, network, address: from, paymaster } = buildWalletClient(program2, {
7383
+ signer: opts.signer,
7384
+ ...gasPolicyIdOverride && { gasPolicyIdOverride }
7385
+ });
6735
7386
  const rpcClient = clientFromFlags(program2);
6736
7387
  const to = await resolveAddress(toArg, rpcClient);
6737
7388
  let decimals;
@@ -7155,7 +7806,11 @@ async function performSwapExecute(program2, opts) {
7155
7806
  validateAddress(opts.from);
7156
7807
  validateAddress(opts.to);
7157
7808
  const signer = parseSignerOpt(opts.signer);
7158
- const { client, network, address: from, paymaster } = buildWalletClient(program2, { signer });
7809
+ const gasPolicyIdOverride = await ensureGasPolicyResolved(program2);
7810
+ const { client, network, address: from, paymaster } = buildWalletClient(program2, {
7811
+ signer,
7812
+ ...gasPolicyIdOverride && { gasPolicyIdOverride }
7813
+ });
7159
7814
  const swapClient = client.extend(swapActions);
7160
7815
  const fromInfo = await resolveTokenInfo(network, program2, opts.from);
7161
7816
  const rawAmount = parseAmount(opts.amount, fromInfo.decimals);
@@ -7348,6 +8003,174 @@ function registerEvm(program2) {
7348
8003
  registerSimulate(cmd);
7349
8004
  }
7350
8005
 
8006
+ // src/commands/gas-manager.ts
8007
+ function summariseNetworks(p) {
8008
+ return p.networks.map(fromAdminNetworkId).join(", ");
8009
+ }
8010
+ function policyToRow(p) {
8011
+ return [
8012
+ p.policyId,
8013
+ p.policyName,
8014
+ p.policyType,
8015
+ p.status === "active" ? green(p.status) : dim(p.status),
8016
+ summariseNetworks(p) || dim("(none)")
8017
+ ];
8018
+ }
8019
+ function policyToJSON(p) {
8020
+ return {
8021
+ policyId: p.policyId,
8022
+ policyName: p.policyName,
8023
+ policyType: p.policyType,
8024
+ status: p.status,
8025
+ appId: p.appId,
8026
+ networks: p.networks,
8027
+ networkSlugs: p.networks.map(fromAdminNetworkId),
8028
+ policyState: p.policyState ?? null,
8029
+ lastUpdatedUnix: p.lastUpdatedUnix ?? null,
8030
+ policyVersion: p.policyVersion ?? null
8031
+ };
8032
+ }
8033
+ function requireAppId(program2, opts) {
8034
+ if (opts.appId) return opts.appId;
8035
+ const resolved = resolveAppId(program2);
8036
+ if (!resolved) throw errAppRequired();
8037
+ return resolved;
8038
+ }
8039
+ function parseTypeFilter(value) {
8040
+ if (!value) return null;
8041
+ if (value === "sponsorship" || value === "erc20" || value === "solana") return value;
8042
+ throw errInvalidArgs(`Unknown --type ${value}. Expected one of: sponsorship, erc20, solana.`);
8043
+ }
8044
+ function parseStatusFilter(value) {
8045
+ if (!value) return null;
8046
+ if (value === "active" || value === "inactive") return value;
8047
+ throw errInvalidArgs(`Unknown --status ${value}. Expected one of: active, inactive.`);
8048
+ }
8049
+ function registerGasManager(program2) {
8050
+ const cmd = program2.command("gas-manager").description("Manage Alchemy Gas Manager policies");
8051
+ const policyCmd = cmd.command("policy").description("Manage gas sponsorship policies");
8052
+ policyCmd.command("list").description("List gas policies for the default app").option("--app-id <id>", "Override the default app ID").option("--type <type>", "Filter by policy type (sponsorship | erc20 | solana)").option("--network <slug>", "Filter by network slug (e.g. eth-mainnet)").option("--status <status>", "Filter by status (active | inactive)").action(async (opts) => {
8053
+ try {
8054
+ const client = gasManagerClientFromFlags(program2);
8055
+ const appId = requireAppId(program2, opts);
8056
+ const typeFilter = parseTypeFilter(opts.type);
8057
+ const statusFilter = parseStatusFilter(opts.status);
8058
+ const networkFilter = opts.network ? toAdminNetworkId(opts.network) : null;
8059
+ const policies = await withSpinner(
8060
+ "Fetching policies\u2026",
8061
+ "Policies fetched",
8062
+ () => client.listAllPolicies({ appId })
8063
+ );
8064
+ const filtered = policies.filter((p) => {
8065
+ if (typeFilter && p.policyType !== typeFilter) return false;
8066
+ if (statusFilter && p.status !== statusFilter) return false;
8067
+ if (networkFilter && !p.networks.includes(networkFilter)) return false;
8068
+ return true;
8069
+ });
8070
+ if (isJSONMode()) {
8071
+ printJSON({ appId, policies: filtered.map(policyToJSON) });
8072
+ return;
8073
+ }
8074
+ if (filtered.length === 0) {
8075
+ console.log(
8076
+ `
8077
+ ${dim(`No policies for app ${appId}${networkFilter ? ` on ${opts.network}` : ""}.`)}`
8078
+ );
8079
+ console.log(
8080
+ ` ${dim("Create one with: alchemy gas-manager policy create")}`
8081
+ );
8082
+ return;
8083
+ }
8084
+ printTable(
8085
+ ["Policy ID", "Name", "Type", "Status", "Networks"],
8086
+ filtered.map(policyToRow)
8087
+ );
8088
+ console.log(`
8089
+ ${dim(`${filtered.length} policies (app ${appId}).`)}`);
8090
+ } catch (err) {
8091
+ exitWithError(err);
8092
+ }
8093
+ });
8094
+ policyCmd.command("get <policy-id>").description("Show details for a single policy").action(async (policyId) => {
8095
+ try {
8096
+ const client = gasManagerClientFromFlags(program2);
8097
+ const policy = await withSpinner(
8098
+ "Fetching policy\u2026",
8099
+ "Policy fetched",
8100
+ () => client.getPolicy(policyId)
8101
+ );
8102
+ if (isJSONMode()) {
8103
+ printJSON(policyToJSON(policy));
8104
+ return;
8105
+ }
8106
+ printKeyValue([
8107
+ ["Policy ID", policy.policyId],
8108
+ ["Name", policy.policyName],
8109
+ ["Type", policy.policyType],
8110
+ ["Status", policy.status === "active" ? green("active") : dim(policy.status)],
8111
+ ["App", policy.appId],
8112
+ ["Networks", summariseNetworks(policy) || dim("(none)")],
8113
+ ...policy.lastUpdatedUnix ? [["Updated", policy.lastUpdatedUnix]] : []
8114
+ ]);
8115
+ } catch (err) {
8116
+ exitWithError(err);
8117
+ }
8118
+ });
8119
+ policyCmd.command("create").description("Create a new gas policy interactively").option("--type <type>", "Policy type (sponsorship | solana)", "sponsorship").action(async (opts) => {
8120
+ try {
8121
+ const type = parseTypeFilter(opts.type ?? "sponsorship");
8122
+ if (type === "erc20") {
8123
+ throw errInvalidArgs(
8124
+ "ERC-20 policies are not supported in `gas-manager policy create` yet. Create them in the Alchemy dashboard."
8125
+ );
8126
+ }
8127
+ if (!isInteractiveAllowed(program2)) {
8128
+ throw errInvalidArgs(
8129
+ "Policy creation requires an interactive terminal. Create policies in the Alchemy dashboard for non-interactive environments."
8130
+ );
8131
+ }
8132
+ const flavor = type === "solana" ? "solana" : "sponsorship";
8133
+ const network = flavor === "solana" ? resolveSolanaNetwork(program2) : resolveNetwork(program2);
8134
+ const result = await createPolicyInteractive({
8135
+ flavor,
8136
+ network,
8137
+ program: program2
8138
+ });
8139
+ if (!result) return;
8140
+ if (isJSONMode()) {
8141
+ printJSON({
8142
+ policyId: result.policyId,
8143
+ status: result.status,
8144
+ activated: result.activated
8145
+ });
8146
+ }
8147
+ } catch (err) {
8148
+ exitWithError(err);
8149
+ }
8150
+ });
8151
+ policyCmd.command("activate <policy-id>").description("Activate a policy so it can be used for sponsorship").action(async (policyId) => {
8152
+ try {
8153
+ const client = gasManagerClientFromFlags(program2);
8154
+ const policy = await withSpinner(
8155
+ "Activating policy\u2026",
8156
+ "Policy active",
8157
+ () => client.setPolicyStatus(policyId, "active")
8158
+ );
8159
+ if (isJSONMode()) {
8160
+ printJSON(policyToJSON(policy));
8161
+ return;
8162
+ }
8163
+ printKeyValue([
8164
+ ["Policy ID", policy.policyId],
8165
+ ["Name", policy.policyName],
8166
+ ["Status", green(policy.status)]
8167
+ ]);
8168
+ } catch (err) {
8169
+ exitWithError(err);
8170
+ }
8171
+ });
8172
+ }
8173
+
7351
8174
  // src/commands/bridge.ts
7352
8175
  import {
7353
8176
  swapActions as swapActions2
@@ -7548,7 +8371,11 @@ async function performBridgeExecute(program2, opts) {
7548
8371
  const toChainId = bridgeDestinationChainId(opts.toNetwork);
7549
8372
  validateAddress(opts.to);
7550
8373
  const signer = parseSignerOpt(opts.signer);
7551
- const { client, network, address: from, paymaster } = buildWalletClient(program2, { signer });
8374
+ const gasPolicyIdOverride = await ensureGasPolicyResolved(program2);
8375
+ const { client, network, address: from, paymaster } = buildWalletClient(program2, {
8376
+ signer,
8377
+ ...gasPolicyIdOverride && { gasPolicyIdOverride }
8378
+ });
7552
8379
  validateBridgeNetworks(network, opts.toNetwork);
7553
8380
  const swapClient = client.extend(swapActions2);
7554
8381
  const fromInfo = await resolveTokenInfo2(network, program2, opts.from);
@@ -8298,7 +9125,7 @@ async function flushProcessOutput() {
8298
9125
  }
8299
9126
  program.name("alchemy").description(
8300
9127
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
8301
- ).version("0.7.4", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
9128
+ ).version("0.9.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
8302
9129
  "-n, --network <network>",
8303
9130
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
8304
9131
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
@@ -8485,11 +9312,11 @@ ${styledLine}`;
8485
9312
  "wallet"
8486
9313
  ];
8487
9314
  if (!skipAppPrompt.includes(cmdName) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
8488
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-WXT5ZCUK.js");
9315
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-GBS26K44.js");
8489
9316
  const authToken = resolveAuthToken2(cfg);
8490
9317
  const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
8491
9318
  if (authToken && !hasApiKey) {
8492
- const { selectAppAfterAuth } = await import("./auth-F2IXC6CM.js");
9319
+ const { selectAppAfterAuth } = await import("./auth-65X7EMCF.js");
8493
9320
  console.log("");
8494
9321
  console.log(` No app selected. Please select an app to continue.`);
8495
9322
  await selectAppAfterAuth(authToken);
@@ -8524,7 +9351,7 @@ ${styledLine}`;
8524
9351
  if (isInteractiveAllowed(program)) {
8525
9352
  let latestForInteractiveStartup = null;
8526
9353
  if (shouldRunOnboarding(program, cfg)) {
8527
- const { runOnboarding } = await import("./onboarding-CT4RRH6O.js");
9354
+ const { runOnboarding } = await import("./onboarding-MDHVOUY3.js");
8528
9355
  const latest = getAvailableUpdateOnce();
8529
9356
  const completed = await runOnboarding(program, latest);
8530
9357
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -8538,7 +9365,7 @@ ${styledLine}`;
8538
9365
  latestForInteractiveStartup
8539
9366
  );
8540
9367
  }
8541
- const { startREPL } = await import("./interactive-MHAC5WQI.js");
9368
+ const { startREPL } = await import("./interactive-GVMPYXNJ.js");
8542
9369
  program.exitOverride();
8543
9370
  program.configureOutput({
8544
9371
  writeErr: () => {
@@ -8553,6 +9380,7 @@ registerEvm(program);
8553
9380
  registerSolana(program);
8554
9381
  registerXchain(program);
8555
9382
  registerWallets(program);
9383
+ registerGasManager(program);
8556
9384
  registerApps(program);
8557
9385
  registerWebhooks(program);
8558
9386
  registerAuth(program);