@alchemy/cli 0.8.0 → 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
@@ -5,23 +5,23 @@ import {
5
5
  errNotLoggedInForPolicyLookup,
6
6
  errSponsorshipNeedsPolicy,
7
7
  selectOrCreatePolicy
8
- } from "./chunk-5Y7UQ27V.js";
8
+ } from "./chunk-AJPOUEO6.js";
9
9
  import {
10
10
  registerAuth
11
- } from "./chunk-EKS2THJU.js";
11
+ } from "./chunk-XCKUCXC6.js";
12
12
  import {
13
13
  openBrowser
14
- } from "./chunk-NGF46GZP.js";
14
+ } from "./chunk-RGVM5SNE.js";
15
15
  import {
16
16
  SETUP_CAPABILITY_LABELS,
17
17
  SETUP_CAPABILITY_ORDER,
18
18
  getSetupStatus,
19
19
  isSetupComplete,
20
20
  shouldRunOnboarding
21
- } from "./chunk-GNOTKJF4.js";
21
+ } from "./chunk-NMT7XH3C.js";
22
22
  import {
23
23
  isInteractiveAllowed
24
- } from "./chunk-L7VFXQSF.js";
24
+ } from "./chunk-75ICFV5K.js";
25
25
  import {
26
26
  adminClientFromFlags,
27
27
  clearSession,
@@ -30,6 +30,7 @@ import {
30
30
  fromAdminNetworkId,
31
31
  gasManagerClientFromFlags,
32
32
  getRPCNetworks,
33
+ getWalletSessionByChain,
33
34
  hasAuthLoginToken,
34
35
  isSessionValid,
35
36
  isSolanaNetwork,
@@ -53,12 +54,12 @@ import {
53
54
  saveSession,
54
55
  toAdminNetworkId,
55
56
  updateSession
56
- } from "./chunk-JWLZAO7S.js";
57
+ } from "./chunk-7ZSEELHZ.js";
57
58
  import {
58
59
  getAvailableUpdate,
59
60
  getUpdateStatus,
60
61
  printUpdateNotice
61
- } from "./chunk-76ZO4UJ4.js";
62
+ } from "./chunk-WT4RGQLC.js";
62
63
  import {
63
64
  bold,
64
65
  brand,
@@ -82,7 +83,7 @@ import {
82
83
  weiToEth,
83
84
  withSpinner,
84
85
  yellow
85
- } from "./chunk-DGZYRBXR.js";
86
+ } from "./chunk-MV7O3XBG.js";
86
87
  import {
87
88
  KEY_MAP,
88
89
  configDir,
@@ -93,7 +94,7 @@ import {
93
94
  save,
94
95
  toMap,
95
96
  validKeys
96
- } from "./chunk-ROBA7SR7.js";
97
+ } from "./chunk-GDLPBPG3.js";
97
98
  import {
98
99
  CLIError,
99
100
  EXIT_CODES,
@@ -129,7 +130,7 @@ import {
129
130
  setFlags,
130
131
  setNoColor,
131
132
  verbose
132
- } from "./chunk-46LMXT54.js";
133
+ } from "./chunk-OVLQH6KL.js";
133
134
 
134
135
  // src/index.ts
135
136
  import { Command, Help } from "commander";
@@ -576,8 +577,8 @@ function registerConfig(program2) {
576
577
  "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set evm-gas-policy-id <id>`."
577
578
  );
578
579
  }
579
- const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-7AUZOA7S.js");
580
- const { resolveNetwork: resolveNetwork2 } = await import("./resolve-N3SX252M.js");
580
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-PHVO6VRZ.js");
581
+ const { resolveNetwork: resolveNetwork2 } = await import("./resolve-GBS26K44.js");
581
582
  const network = resolveNetwork2(program2);
582
583
  await selectOrCreatePolicy2({
583
584
  flavor: "sponsorship",
@@ -631,8 +632,8 @@ function registerConfig(program2) {
631
632
  "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set solana-fee-policy-id <id>`."
632
633
  );
633
634
  }
634
- const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-7AUZOA7S.js");
635
- const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-N3SX252M.js");
635
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-PHVO6VRZ.js");
636
+ const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-GBS26K44.js");
636
637
  const network = resolveSolanaNetwork2(program2);
637
638
  await selectOrCreatePolicy2({
638
639
  flavor: "solana",
@@ -690,7 +691,7 @@ function registerConfig(program2) {
690
691
  printJSON(toMap(cfg));
691
692
  return;
692
693
  }
693
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-N3SX252M.js");
694
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-GBS26K44.js");
694
695
  const validToken = resolveAuthToken2(cfg);
695
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");
696
697
  const pairs = [
@@ -1369,7 +1370,19 @@ var walletSessionCreateResponseSchema = z.object({
1369
1370
  approvalUrl: z.string().url(),
1370
1371
  expiresAt: z.string().datetime().optional()
1371
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();
1372
1384
  var rawWalletSessionStatusResponseSchema = z.object({
1385
+ type: z.literal("session").optional(),
1373
1386
  sessionId: z.string().min(1).optional(),
1374
1387
  status: rawWalletSessionStateSchema,
1375
1388
  expiresAt: z.string().datetime().optional(),
@@ -1385,6 +1398,15 @@ var rawWalletSessionStatusResponseSchema = z.object({
1385
1398
  chainType: z.string().min(1).optional(),
1386
1399
  capabilities: z.record(z.string(), z.boolean()).optional()
1387
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();
1388
1410
  var walletSessionDisconnectResponseSchema = z.object({
1389
1411
  sessionId: z.string().min(1).optional(),
1390
1412
  status: rawWalletSessionStateSchema.optional().transform(
@@ -1411,12 +1433,27 @@ var rawEvmSigningChallengeResponseSchema = z.object({
1411
1433
  challenge: z.string().min(1),
1412
1434
  expiresAt: z.string().datetime(),
1413
1435
  requestExpiry: z.string().min(1),
1414
- 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
+ ]),
1415
1441
  walletId: z.string().min(1),
1416
1442
  walletAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
1417
1443
  providerKeyQuorumId: z.string().min(1).optional(),
1418
1444
  providerSignerId: z.string().min(1).optional()
1419
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();
1420
1457
  var completeEvmChallengeInputSchema = z.object({
1421
1458
  challengeId: z.string().uuid(),
1422
1459
  signature: z.string().min(1)
@@ -1439,6 +1476,10 @@ var signAuthorizationCompleteResponseSchema = z.object({
1439
1476
  y_parity: z.number().int()
1440
1477
  }).strict()
1441
1478
  }).strict();
1479
+ var solanaSignTransactionCompleteResponseSchema = z.object({
1480
+ signedTransaction: z.string().min(1),
1481
+ encoding: z.literal("base64")
1482
+ }).strict();
1442
1483
  function baseURLOverride() {
1443
1484
  const options = { allowedHostnames: [STAGING_ADMIN_API_HOST] };
1444
1485
  return parseBaseURLOverride(WALLET_API_BASE_URL_ENV, options) ?? parseBaseURLOverride(ADMIN_API_BASE_URL_ENV, options);
@@ -1532,6 +1573,17 @@ function normalizeWalletSessionStatus(session) {
1532
1573
  privySignerId: session.providerSignerId
1533
1574
  };
1534
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
+ }
1535
1587
  function normalizeEvmSigningChallengeResponse(response) {
1536
1588
  return {
1537
1589
  ...response,
@@ -1539,6 +1591,13 @@ function normalizeEvmSigningChallengeResponse(response) {
1539
1591
  privySignerId: response.providerSignerId
1540
1592
  };
1541
1593
  }
1594
+ function normalizeSolanaSigningChallengeResponse(response) {
1595
+ return {
1596
+ ...response,
1597
+ privyKeyQuorumId: response.providerKeyQuorumId,
1598
+ privySignerId: response.providerSignerId
1599
+ };
1600
+ }
1542
1601
  function toProviderSigningBindingInput(input) {
1543
1602
  const { privyKeyQuorumId, privySignerId, ...rest } = input;
1544
1603
  return {
@@ -1547,7 +1606,15 @@ function toProviderSigningBindingInput(input) {
1547
1606
  ...privySignerId ? { providerSignerId: privySignerId } : {}
1548
1607
  };
1549
1608
  }
1550
- 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) {
1551
1618
  let data;
1552
1619
  let requestInput = input;
1553
1620
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -1558,16 +1625,19 @@ async function createRemoteWalletSession(token, input) {
1558
1625
  "/wallet/sessions",
1559
1626
  requestInput
1560
1627
  );
1561
- return unwrapAdminData(walletSessionCreateResponseSchema, data);
1628
+ return unwrapAdminData(walletSessionRequestCreateResponseSchema, data);
1562
1629
  } catch (err) {
1563
- const retryInput = getClientInstanceCompatibilityRetryInput(requestInput, err);
1630
+ const retryInput = getClientInstanceCompatibilityRetryInput(
1631
+ requestInput,
1632
+ err
1633
+ );
1564
1634
  if (retryInput === void 0) {
1565
1635
  throw err;
1566
1636
  }
1567
1637
  requestInput = retryInput;
1568
1638
  }
1569
1639
  }
1570
- throw new CLIError(ErrorCode.NETWORK_ERROR, "Unable to create wallet session.");
1640
+ throw new CLIError(ErrorCode.NETWORK_ERROR, "Unable to create wallet session request.");
1571
1641
  }
1572
1642
  function getClientInstanceCompatibilityRetryInput(input, err) {
1573
1643
  if (input.environment.clientInstanceName !== void 0 && isUnsupportedClientInstanceMetadataError(err, "clientInstanceName")) {
@@ -1602,6 +1672,16 @@ async function getRemoteWalletSession(token, sessionId) {
1602
1672
  unwrapAdminData(rawWalletSessionStatusResponseSchema, data)
1603
1673
  );
1604
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
+ }
1605
1685
  async function disconnectRemoteWalletSession(token, sessionId) {
1606
1686
  try {
1607
1687
  const data = await request(
@@ -1680,6 +1760,24 @@ var signAuthorizationChallengeInputSchema = evmSigningSessionBindingBaseSchema.e
1680
1760
  executor: z.literal("self").optional()
1681
1761
  }).strict()
1682
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
+ });
1683
1781
  async function createRemoteSignTypedDataChallenge(token, input) {
1684
1782
  const parsedInput = signTypedDataChallengeInputSchema.parse(input);
1685
1783
  return createEvmSigningChallenge(
@@ -1718,6 +1816,25 @@ async function completeRemoteSignAuthorizationChallenge(token, input) {
1718
1816
  adminApiResponseSchema(signAuthorizationCompleteResponseSchema).transform((resp) => resp.data)
1719
1817
  );
1720
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
+ }
1721
1838
 
1722
1839
  // src/commands/wallet.ts
1723
1840
  import QRCode from "qrcode";
@@ -1728,6 +1845,9 @@ var DEFAULT_WALLET_CAPABILITIES = {
1728
1845
  "evm.signTypedData": true,
1729
1846
  "evm.signAuthorization": true
1730
1847
  };
1848
+ var DEFAULT_SOLANA_SESSION_CAPABILITIES = {
1849
+ "solana.signTransaction": true
1850
+ };
1731
1851
  function createEvmWallet() {
1732
1852
  const privateKey = generatePrivateKey();
1733
1853
  const account = privateKeyToAccount(privateKey);
@@ -1851,7 +1971,13 @@ async function withApprovalInterruptHandler(args) {
1851
1971
  return await args.run(controller.signal);
1852
1972
  } catch (err) {
1853
1973
  if (isWalletConnectInterruptedError(err)) {
1854
- 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
+ );
1855
1981
  clearSession();
1856
1982
  }
1857
1983
  throw err;
@@ -1862,23 +1988,9 @@ async function withApprovalInterruptHandler(args) {
1862
1988
  }
1863
1989
  }
1864
1990
  }
1865
- function isFutureIsoDate(value) {
1866
- const date = new Date(value);
1867
- if (Number.isNaN(date.getTime())) {
1868
- return false;
1869
- }
1870
- return date > /* @__PURE__ */ new Date();
1871
- }
1872
- function isActiveWalletSession(session, remoteStatus) {
1873
- if (!session) return false;
1874
- if (session.status !== "approved") return false;
1875
- if (!isFutureIsoDate(session.expiresAt)) return false;
1876
- if (remoteStatus && remoteStatus !== "approved") return false;
1877
- return true;
1878
- }
1879
- function deriveWalletStatus(session, remoteStatus) {
1991
+ function deriveWalletStatus(session) {
1880
1992
  if (!session) return "none";
1881
- if (isActiveWalletSession(session, remoteStatus)) return "active";
1993
+ if (isSessionValid(session)) return "active";
1882
1994
  if (session.status === "pending") return "none";
1883
1995
  return "expired";
1884
1996
  }
@@ -1988,7 +2100,7 @@ function resolveQrAddress(args) {
1988
2100
  function missingQrWalletError(type, source) {
1989
2101
  if (source === "session") {
1990
2102
  return type === "solana" ? errInvalidArgs(
1991
- "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`."
1992
2104
  ) : errNoActiveSession();
1993
2105
  }
1994
2106
  if (source === "local") {
@@ -1999,20 +2111,6 @@ function missingQrWalletError(type, source) {
1999
2111
  function indentBlock(text, prefix = " ") {
2000
2112
  return text.split("\n").map((line) => line ? `${prefix}${line}` : line).join("\n");
2001
2113
  }
2002
- function generateAndPersistWallet() {
2003
- const wallet = createEvmWallet();
2004
- const keyPath = persistWalletKey("wallet-key", wallet.privateKey, wallet.address);
2005
- const cfg = load();
2006
- save({ ...cfg, wallet_key_file: keyPath, wallet_address: wallet.address });
2007
- return { address: wallet.address, keyFile: keyPath };
2008
- }
2009
- async function generateAndPersistSolanaWallet() {
2010
- const wallet = await createSolanaWallet();
2011
- const keyPath = persistWalletKey("solana-wallet-key", wallet.secretKey, wallet.address);
2012
- const cfg = load();
2013
- save({ ...cfg, solana_wallet_key_file: keyPath, solana_wallet_address: wallet.address });
2014
- return { address: wallet.address, keyFile: keyPath };
2015
- }
2016
2114
  async function createAndPersistWallets() {
2017
2115
  const evm = createEvmWallet();
2018
2116
  const solana = await createSolanaWallet();
@@ -2077,14 +2175,12 @@ function getEffectiveLocalWalletState(program2, cfg) {
2077
2175
  }
2078
2176
  async function runLocalCreate(opts) {
2079
2177
  const cfg = load();
2080
- const wantsEvm = opts.chain === "evm" || opts.chain === "both";
2081
- const wantsSolana = opts.chain === "solana" || opts.chain === "both";
2082
- const evmConflict = wantsEvm && Boolean(cfg.wallet_key_file);
2083
- 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);
2084
2180
  if ((evmConflict || solanaConflict) && !opts.force) {
2085
2181
  if (!isInteractiveAllowed(opts.program)) {
2086
2182
  throw errInvalidArgs(
2087
- "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."
2088
2184
  );
2089
2185
  }
2090
2186
  const kinds = [evmConflict ? "EVM" : null, solanaConflict ? "Solana" : null].filter(Boolean).join(" and ");
@@ -2097,16 +2193,7 @@ async function runLocalCreate(opts) {
2097
2193
  throw errInvalidArgs("Aborted. Existing local key preserved.");
2098
2194
  }
2099
2195
  }
2100
- if (opts.chain === "both") {
2101
- const { evm, solana: solana2 } = await createAndPersistWallets();
2102
- return { evm, solana: solana2 };
2103
- }
2104
- if (opts.chain === "evm") {
2105
- const evm = generateAndPersistWallet();
2106
- return { evm };
2107
- }
2108
- const solana = await generateAndPersistSolanaWallet();
2109
- return { solana };
2196
+ return await createAndPersistWallets();
2110
2197
  }
2111
2198
  function runLocalImport(opts) {
2112
2199
  const cfg = load();
@@ -2139,7 +2226,27 @@ async function runLocalImportInteractive(opts) {
2139
2226
  return runLocalImport({ ...opts, force: true });
2140
2227
  }
2141
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
+ }
2142
2248
  async function runSessionConnect(opts) {
2249
+ const requestedChains = ["evm", "solana"];
2143
2250
  const authToken = resolveAuthToken();
2144
2251
  if (!authToken) throw errAuthRequired();
2145
2252
  const existing = loadSession();
@@ -2177,32 +2284,51 @@ async function runSessionConnect(opts) {
2177
2284
  ...clientInstance.name === void 0 ? {} : { clientInstanceName: clientInstance.name }
2178
2285
  };
2179
2286
  updateSession({
2180
- chainType: "evm",
2181
- capabilities: { ...DEFAULT_WALLET_CAPABILITIES },
2287
+ chainType: "both",
2288
+ capabilities: Object.assign(
2289
+ {},
2290
+ ...requestedChains.map((chain) => capabilitiesForChain(chain))
2291
+ ),
2182
2292
  backendBaseUrl: getWalletApiBaseUrl(),
2183
2293
  environment: sessionEnvironment
2184
2294
  });
2185
- const remoteSession = await createRemoteWalletSession(authToken, {
2295
+ const remoteRequest = await createRemoteWalletSessionRequest(authToken, {
2186
2296
  publicKeyJwk: session.publicKeyJwk,
2187
2297
  requestSignerVersion: session.envelopeVersion,
2188
- chainType: "evm",
2189
- capabilities: { ...DEFAULT_WALLET_CAPABILITIES },
2298
+ chains: requestedChains,
2299
+ capabilities: capabilitiesForChains(requestedChains),
2190
2300
  environment: sessionEnvironment
2191
2301
  }).catch((err) => {
2192
2302
  clearSession();
2193
2303
  throw err;
2194
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
+ );
2195
2319
  updateSession({
2196
- sessionId: remoteSession.sessionId,
2197
- expiresAt: remoteSession.expiresAt ?? session.expiresAt
2320
+ connectionRequestId: remoteRequest.sessionId,
2321
+ sessionId: remoteRequest.sessions[0].walletSessionId,
2322
+ expiresAt: remoteRequest.expiresAt ?? session.expiresAt,
2323
+ sessionsByChain: pendingSessionsByChain
2198
2324
  });
2199
2325
  if (!isJSONMode()) {
2200
2326
  const summaryPairs = [
2201
- ["Session ID", remoteSession.sessionId],
2327
+ ["Connection Request ID", remoteRequest.sessionId],
2202
2328
  ["Status", "pending"],
2203
- ["Approval URL", remoteSession.approvalUrl],
2329
+ ["Approval URL", remoteRequest.approvalUrl],
2204
2330
  ["Created", session.createdAt],
2205
- ["Expires", remoteSession.expiresAt ?? session.expiresAt]
2331
+ ["Expires", remoteRequest.expiresAt ?? session.expiresAt]
2206
2332
  ];
2207
2333
  if (clientInstance.name !== void 0) {
2208
2334
  summaryPairs.splice(1, 0, ["Instance", clientInstance.name]);
@@ -2217,7 +2343,14 @@ async function runSessionConnect(opts) {
2217
2343
  });
2218
2344
  if (answer === null) {
2219
2345
  clearSession();
2220
- 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
+ );
2221
2354
  throw new WalletConnectInterruptedError();
2222
2355
  }
2223
2356
  }
@@ -2225,25 +2358,41 @@ async function runSessionConnect(opts) {
2225
2358
  console.log(` Opening browser for approval...`);
2226
2359
  console.log(` ${dim("Waiting for dashboard approval to complete connection.")}`);
2227
2360
  }
2228
- openBrowser(remoteSession.approvalUrl);
2229
- const approvedSession = await withApprovalInterruptHandler({
2361
+ openBrowser(remoteRequest.approvalUrl);
2362
+ const approvedSessions = await withApprovalInterruptHandler({
2230
2363
  authToken,
2231
- sessionId: remoteSession.sessionId,
2364
+ sessionIds: remoteRequest.sessions.map(
2365
+ (remoteSession) => remoteSession.walletSessionId
2366
+ ),
2232
2367
  run: async (signal) => {
2233
2368
  const startedAt = Date.now();
2234
2369
  while (Date.now() - startedAt < CONNECT_TIMEOUT_MS) {
2235
- const current = await waitForInterruptible(
2236
- 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
+ ),
2237
2379
  signal
2238
2380
  );
2239
- if (current.status === "approved") {
2240
- return current;
2381
+ if (requestedChains.every(
2382
+ (chain) => currentSessions.some((current) => {
2383
+ return current.chainType === chain && current.status === "approved";
2384
+ })
2385
+ )) {
2386
+ return currentSessions;
2241
2387
  }
2242
- 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) {
2243
2392
  clearSession();
2244
2393
  throw new CLIError(
2245
2394
  ErrorCode.INTERNAL_ERROR,
2246
- `Wallet session ${current.status}.`,
2395
+ `Wallet session ${terminalSession.status}.`,
2247
2396
  "Run 'alchemy wallet connect' to start a new approval flow."
2248
2397
  );
2249
2398
  }
@@ -2252,58 +2401,123 @@ async function runSessionConnect(opts) {
2252
2401
  return null;
2253
2402
  }
2254
2403
  });
2255
- if (!approvedSession) {
2404
+ if (!approvedSessions) {
2256
2405
  throw new CLIError(
2257
2406
  ErrorCode.NETWORK_ERROR,
2258
2407
  "Timed out waiting for wallet approval.",
2259
2408
  "Finish approval in the dashboard and rerun 'alchemy wallet connect --force' if needed."
2260
2409
  );
2261
2410
  }
2262
- const walletId = approvedSession.walletId ?? approvedSession.evmWalletId;
2263
- const evmAddress = approvedSession.evmAddress ?? approvedSession.address;
2264
- if (!approvedSession.privyAppId || !walletId || !evmAddress) {
2411
+ const missingProviderAppId = approvedSessions.find((approvedSession) => {
2412
+ return !approvedSession.privyAppId;
2413
+ });
2414
+ if (missingProviderAppId) {
2265
2415
  throw new CLIError(
2266
2416
  ErrorCode.INTERNAL_ERROR,
2267
2417
  "Approved wallet session response missing required metadata."
2268
2418
  );
2269
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;
2270
2456
  updateSession({
2271
- sessionId: approvedSession.sessionId ?? remoteSession.sessionId,
2457
+ connectionRequestId: remoteRequest.sessionId,
2458
+ sessionId: firstApprovedSession.sessionId ?? remoteRequest.sessions[0].walletSessionId,
2272
2459
  status: "approved",
2273
- expiresAt: approvedSession.expiresAt ?? remoteSession.expiresAt ?? session.expiresAt,
2274
- privyAppId: approvedSession.privyAppId,
2275
- walletId,
2276
- evmWalletId: approvedSession.evmWalletId ?? walletId,
2277
- evmAddress,
2278
- solanaWalletId: approvedSession.solanaWalletId,
2279
- solanaAddress: approvedSession.solanaAddress,
2280
- privyKeyQuorumId: approvedSession.privyKeyQuorumId,
2281
- privySignerId: approvedSession.privySignerId,
2282
- chainType: approvedSession.chainType ?? "evm",
2283
- 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,
2284
2476
  backendBaseUrl: getWalletApiBaseUrl(),
2285
2477
  environment: sessionEnvironment
2286
2478
  });
2287
2479
  if (isJSONMode()) {
2288
2480
  printJSON({
2289
- 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,
2290
2484
  status: "approved",
2291
- privyAppId: approvedSession.privyAppId,
2292
- walletId,
2293
- evmAddress,
2294
- evmWalletId: approvedSession.evmWalletId ?? walletId,
2295
- chainType: approvedSession.chainType ?? "evm",
2296
- capabilities: approvedSession.capabilities ?? { ...DEFAULT_WALLET_CAPABILITIES },
2297
- 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
2298
2502
  });
2299
2503
  } else {
2300
- printKeyValue([
2301
- ["Session ID", approvedSession.sessionId ?? remoteSession.sessionId],
2302
- ["Status", green("approved")],
2303
- ["Wallet ID", walletId],
2304
- ["EVM Address", green(evmAddress)],
2305
- ["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
2306
2519
  ]);
2520
+ printKeyValue(pairs);
2307
2521
  console.log(` ${green("\u2713")} Wallet connected`);
2308
2522
  }
2309
2523
  }
@@ -2372,17 +2586,6 @@ async function runConnectFlow(program2, opts) {
2372
2586
  }
2373
2587
  mode2 = "local";
2374
2588
  }
2375
- let chain = "both";
2376
- if (opts.chain !== void 0) {
2377
- if (opts.chain !== "evm" && opts.chain !== "solana" && opts.chain !== "both") {
2378
- throw errInvalidArgs("`--chain` must be 'evm', 'solana', or 'both'.");
2379
- }
2380
- chain = opts.chain;
2381
- }
2382
- if (importPath && chain !== "evm" && opts.chain !== void 0) {
2383
- throw errInvalidArgs("`--import` implies `--chain evm`; omit `--chain` or set it to 'evm'.");
2384
- }
2385
- if (importPath) chain = "evm";
2386
2589
  if (!mode2) {
2387
2590
  if (!isInteractiveAllowed(program2)) {
2388
2591
  throw errInvalidArgs(
@@ -2392,7 +2595,7 @@ async function runConnectFlow(program2, opts) {
2392
2595
  const choice = await promptSelect({
2393
2596
  message: "Choose a wallet to connect",
2394
2597
  options: [
2395
- { 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" },
2396
2599
  { value: "local", label: "Local wallet", hint: "Private key stored on this machine" }
2397
2600
  ],
2398
2601
  initialValue: "session",
@@ -2402,7 +2605,11 @@ async function runConnectFlow(program2, opts) {
2402
2605
  mode2 = choice;
2403
2606
  }
2404
2607
  if (mode2 === "session") {
2405
- await runSessionConnect({ program: program2, force, instanceName: opts.instanceName });
2608
+ await runSessionConnect({
2609
+ program: program2,
2610
+ force,
2611
+ instanceName: opts.instanceName
2612
+ });
2406
2613
  printCrossModeHintAfterSessionConnect();
2407
2614
  return;
2408
2615
  }
@@ -2412,9 +2619,55 @@ async function runConnectFlow(program2, opts) {
2412
2619
  printCrossModeHintAfterLocal("evm");
2413
2620
  return;
2414
2621
  }
2415
- const result = await runLocalCreate({ chain, force, program: program2 });
2622
+ const result = await runLocalCreate({ force, program: program2 });
2416
2623
  printPostLocalCreateSummary(result);
2417
- 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;
2418
2671
  }
2419
2672
  async function buildSessionSnapshot(program2, verify) {
2420
2673
  let session = loadStoredSession?.() ?? loadSession();
@@ -2422,25 +2675,51 @@ async function buildSessionSnapshot(program2, verify) {
2422
2675
  if (verify && session) {
2423
2676
  const authToken = resolveAuthToken();
2424
2677
  if (!authToken) throw errAuthRequired();
2425
- const remote = await getRemoteWalletSession(authToken, session.sessionId);
2678
+ const remoteSessions = await getRemoteSessionsForStatus(authToken, session);
2679
+ const remote = selectRemoteSessionForStatus(session, remoteSessions);
2426
2680
  remoteStatus = remote.status;
2427
- 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;
2428
2706
  session = {
2429
2707
  ...session,
2430
2708
  status: normalizeRemoteStatusForStorage(remote.status),
2431
2709
  expiresAt: remote.expiresAt ?? session.expiresAt,
2432
2710
  privyAppId: remote.privyAppId ?? session.privyAppId,
2433
- walletId: walletId2 ?? session.walletId,
2434
- evmWalletId: remote.evmWalletId ?? walletId2 ?? session.evmWalletId,
2435
- evmAddress: remote.evmAddress ?? remote.address ?? session.evmAddress,
2436
- solanaWalletId: remote.solanaWalletId ?? session.solanaWalletId,
2437
- solanaAddress: remote.solanaAddress ?? session.solanaAddress,
2438
- chainType: remote.chainType ?? session.chainType,
2439
- 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
2440
2719
  };
2441
2720
  saveSession(session);
2442
2721
  }
2443
- const status = deriveWalletStatus(session, remoteStatus ?? void 0);
2722
+ const status = deriveWalletStatus(session);
2444
2723
  const walletAddress = resolveSessionAddress(session);
2445
2724
  const environment = resolveSessionEnvironment(session);
2446
2725
  const signerCapabilities = resolveEnabledCapabilities(session);
@@ -2456,14 +2735,16 @@ async function buildSessionSnapshot(program2, verify) {
2456
2735
  walletId,
2457
2736
  expiresAt,
2458
2737
  sessionId: session?.sessionId ?? null,
2738
+ connectionRequestId: session?.connectionRequestId ?? null,
2459
2739
  sessionState: session?.status ?? null,
2460
2740
  chainType: session?.chainType ?? null,
2741
+ sessionsByChain: session?.sessionsByChain ?? null,
2461
2742
  valid: session ? isSessionValid(session) : false
2462
2743
  };
2463
2744
  }
2464
2745
  function registerWallets(program2) {
2465
2746
  const cmd = program2.command("wallet").description("Manage wallets");
2466
- 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) => {
2467
2748
  try {
2468
2749
  await runConnectFlow(program2, opts);
2469
2750
  } catch (err) {
@@ -2493,10 +2774,12 @@ function registerWallets(program2) {
2493
2774
  expiresAt: snap.expiresAt,
2494
2775
  environment: snap.environment,
2495
2776
  signerCapabilities: snap.signerCapabilities,
2777
+ connectionRequestId: snap.connectionRequestId,
2496
2778
  sessionId: snap.sessionId,
2497
2779
  sessionState: snap.sessionState,
2498
2780
  walletId: snap.walletId,
2499
2781
  chainType: snap.chainType,
2782
+ sessionsByChain: snap.sessionsByChain,
2500
2783
  verified: Boolean(opts.verify),
2501
2784
  remoteStatus: snap.remoteStatus,
2502
2785
  valid: snap.valid,
@@ -2507,10 +2790,12 @@ function registerWallets(program2) {
2507
2790
  expiresAt: snap.expiresAt,
2508
2791
  environment: snap.environment,
2509
2792
  signerCapabilities: snap.signerCapabilities,
2793
+ connectionRequestId: snap.connectionRequestId,
2510
2794
  sessionId: snap.sessionId,
2511
2795
  sessionState: snap.sessionState,
2512
2796
  walletId: snap.walletId,
2513
2797
  chainType: snap.chainType,
2798
+ sessionsByChain: snap.sessionsByChain,
2514
2799
  valid: snap.valid
2515
2800
  },
2516
2801
  localEvm: localState.evmAddress ? { address: localState.evmAddress, keyFile: localState.evmKeyFile } : null,
@@ -2528,7 +2813,11 @@ function registerWallets(program2) {
2528
2813
  ["Environment", snap.environment ?? dim("none")],
2529
2814
  ["Signer Capabilities", snap.signerCapabilities.length > 0 ? snap.signerCapabilities.join(", ") : dim("none")]
2530
2815
  ];
2531
- 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
+ }
2532
2821
  if (snap.walletId) pairs.push(["Wallet ID", snap.walletId]);
2533
2822
  if (opts.verify) {
2534
2823
  pairs.push(["Backend Status", snap.remoteStatus ?? dim("not checked")]);
@@ -2683,7 +2972,7 @@ function registerWallets(program2) {
2683
2972
  }
2684
2973
  if (target === "local" && !hasLocalEvmKey()) {
2685
2974
  throw errInvalidArgs(
2686
- "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."
2687
2976
  );
2688
2977
  }
2689
2978
  const cfg = load();
@@ -2717,11 +3006,23 @@ function registerWallets(program2) {
2717
3006
  let revoked = false;
2718
3007
  let alreadyDisconnected = false;
2719
3008
  let remoteStatus = null;
3009
+ let backendRevokeFailed = false;
3010
+ const sessionIdsToDisconnect = uniqueSessionIds(session);
2720
3011
  if (authToken) {
2721
- const remote = await disconnectRemoteWalletSession(authToken, session.sessionId);
2722
- revoked = !remote.alreadyDisconnected;
2723
- alreadyDisconnected = remote.alreadyDisconnected;
2724
- 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;
2725
3026
  }
2726
3027
  const removed = clearSession();
2727
3028
  const cfg = load();
@@ -2735,8 +3036,11 @@ function registerWallets(program2) {
2735
3036
  revoked,
2736
3037
  alreadyDisconnected,
2737
3038
  remoteStatus,
2738
- backendRevokeAttempted: Boolean(authToken)
3039
+ backendRevokeAttempted: Boolean(authToken),
3040
+ backendRevokeFailed
2739
3041
  });
3042
+ } else if (removed && authToken && backendRevokeFailed) {
3043
+ console.log(` ${green("\u2713")} Wallet session disconnected (${dim("backend revoke partially failed")})`);
2740
3044
  } else if (removed && authToken && alreadyDisconnected) {
2741
3045
  console.log(` ${green("\u2713")} Wallet session disconnected (${dim("already revoked or expired")})`);
2742
3046
  } else if (removed && authToken) {
@@ -3120,9 +3424,10 @@ import {
3120
3424
  createKeyPairSignerFromBytes as createKeyPairSignerFromBytes2,
3121
3425
  createKeyPairSignerFromPrivateKeyBytes as createKeyPairSignerFromPrivateKeyBytes2
3122
3426
  } from "@solana/kit";
3123
- import { getTransferSolInstruction } from "@solana-program/system";
3427
+ import { sign as signWithPrivateKey } from "crypto";
3124
3428
  var SOL_DECIMALS = 9;
3125
3429
  var SPONSOR_FEE_PAYER_PLACEHOLDER = address("Amh6quo1FcmL16Qmzdugzjq3Lv1zXzTW7ktswyLDzits");
3430
+ var SYSTEM_PROGRAM_ADDRESS = address("11111111111111111111111111111111");
3126
3431
  var SPL_TOKEN_PROGRAM_ADDRESS = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
3127
3432
  function parseSolanaKeyBytes(secret) {
3128
3433
  const trimmed = secret.trim();
@@ -3153,11 +3458,28 @@ async function createSolanaSignerFromKeyBytes(keyBytes) {
3153
3458
  throw errInvalidArgs("Invalid Solana key: expected 64-byte secret key or 32-byte private key.");
3154
3459
  }
3155
3460
  function buildSolTransferInstruction(from, to, lamports) {
3156
- return getTransferSolInstruction({
3157
- source: from,
3158
- destination: to,
3159
- amount: lamports
3160
- });
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;
3161
3483
  }
3162
3484
  function encodeU64LE(value) {
3163
3485
  if (value < 0n || value > 0xffffffffffffffffn) {
@@ -3238,6 +3560,38 @@ async function buildAndSendSolanaTransaction(opts) {
3238
3560
  const signature = await client.call("sendTransaction", [wireTransaction, { encoding: "base64" }]);
3239
3561
  return { signature, fromAddress: signer.address };
3240
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
+ }
3241
3595
  async function waitForSolanaConfirmation(client, signature, timeoutMs = 6e4, pollIntervalMs = 2e3) {
3242
3596
  const start = Date.now();
3243
3597
  while (Date.now() - start < timeoutMs) {
@@ -3254,6 +3608,76 @@ async function waitForSolanaConfirmation(client, signature, timeoutMs = 6e4, pol
3254
3608
  }
3255
3609
  return false;
3256
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
+ }
3257
3681
 
3258
3682
  // src/lib/solana-fees.ts
3259
3683
  async function resolveSolanaFeeSponsorship(program2) {
@@ -3287,23 +3711,54 @@ function parseDecimals(value) {
3287
3711
  }
3288
3712
  return decimals;
3289
3713
  }
3290
- async function resolveLocalSolanaDelegateSigner(program2, signerOpt) {
3714
+ async function resolveSolanaDelegateSigner(program2, signerOpt) {
3291
3715
  const signer = parseSignerOpt(signerOpt);
3292
3716
  if (signer === "session") {
3293
- throw errInvalidArgs(
3294
- "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`."
3295
- );
3717
+ return resolveSessionSolanaDelegateSigner();
3718
+ }
3719
+ if (signer === void 0 && resolveActiveSigner(program2) === "session") {
3720
+ return resolveSessionSolanaDelegateSigner();
3296
3721
  }
3297
3722
  const solanaKey = resolveSolanaWalletKey(program2);
3298
3723
  if (!solanaKey) {
3724
+ if (signer === void 0) {
3725
+ const sessionSigner = tryResolveSessionSolanaDelegateSigner();
3726
+ if (sessionSigner) {
3727
+ return sessionSigner;
3728
+ }
3729
+ }
3299
3730
  throw errSolanaWalletKeyRequired();
3300
3731
  }
3301
3732
  const keyBytes = parseSolanaKeyBytes(solanaKey);
3302
3733
  return {
3734
+ type: "local",
3303
3735
  keyBytes,
3304
3736
  signer: await createSolanaSignerFromKeyBytes(keyBytes)
3305
3737
  };
3306
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
+ }
3307
3762
  function registerSolanaDelegate(program2) {
3308
3763
  const cmd = program2.command("delegate").description("Delegate Solana token authority");
3309
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");
@@ -3337,7 +3792,7 @@ async function performSolanaDelegateApprove(program2, opts) {
3337
3792
  validateSolanaAddress(opts.delegate);
3338
3793
  const decimals = parseDecimals(opts.decimals);
3339
3794
  const rawAmount = parseAmount(opts.amount, decimals);
3340
- const { keyBytes, signer } = await resolveLocalSolanaDelegateSigner(program2, opts.signer);
3795
+ const signer = await resolveSolanaDelegateSigner(program2, opts.signer);
3341
3796
  const network = resolveSolanaNetwork(program2);
3342
3797
  const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
3343
3798
  const client = clientFromFlags(program2, { forceNetwork: network });
@@ -3345,20 +3800,32 @@ async function performSolanaDelegateApprove(program2, opts) {
3345
3800
  tokenAccount: solAddress(opts.tokenAccount),
3346
3801
  mint: solAddress(opts.mint),
3347
3802
  delegate: solAddress(opts.delegate),
3348
- owner: signer,
3803
+ owner: { address: solAddress(signer.type === "session" ? signer.address : signer.signer.address) },
3349
3804
  amount: rawAmount,
3350
3805
  decimals
3351
3806
  });
3352
3807
  const result = await withSpinner(
3353
3808
  "Approving delegate...",
3354
3809
  "Delegate approved",
3355
- () => buildAndSendSolanaTransaction({
3356
- client,
3357
- instructions: [instruction],
3358
- senderKeyBytes: keyBytes,
3359
- sponsored,
3360
- gasPolicyId: feePolicyId
3361
- })
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
+ }
3362
3829
  );
3363
3830
  const confirmed = await withSpinner(
3364
3831
  "Waiting for confirmation...",
@@ -3399,24 +3866,36 @@ async function performSolanaDelegateApprove(program2, opts) {
3399
3866
  }
3400
3867
  async function performSolanaDelegateRevoke(program2, opts) {
3401
3868
  validateSolanaAddress(opts.tokenAccount);
3402
- const { keyBytes, signer } = await resolveLocalSolanaDelegateSigner(program2, opts.signer);
3869
+ const signer = await resolveSolanaDelegateSigner(program2, opts.signer);
3403
3870
  const network = resolveSolanaNetwork(program2);
3404
3871
  const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
3405
3872
  const client = clientFromFlags(program2, { forceNetwork: network });
3406
3873
  const instruction = buildSplTokenRevokeInstruction({
3407
3874
  tokenAccount: solAddress(opts.tokenAccount),
3408
- owner: signer
3875
+ owner: { address: solAddress(signer.type === "session" ? signer.address : signer.signer.address) }
3409
3876
  });
3410
3877
  const result = await withSpinner(
3411
3878
  "Revoking delegate...",
3412
3879
  "Delegate revoked",
3413
- () => buildAndSendSolanaTransaction({
3414
- client,
3415
- instructions: [instruction],
3416
- senderKeyBytes: keyBytes,
3417
- sponsored,
3418
- gasPolicyId: feePolicyId
3419
- })
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
+ }
3420
3899
  );
3421
3900
  const confirmed = await withSpinner(
3422
3901
  "Waiting for confirmation...",
@@ -3660,35 +4139,41 @@ async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts
3660
4139
  if (tokenAddress) {
3661
4140
  throw errInvalidArgs("SPL token transfers are not yet supported. Omit --token for native SOL transfers.");
3662
4141
  }
3663
- if (opts.signer === "session") {
3664
- throw errInvalidArgs(
3665
- "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`."
3666
- );
3667
- }
3668
- const solanaKey = resolveSolanaWalletKey(program2);
3669
- if (!solanaKey) {
3670
- throw errSolanaWalletKeyRequired();
3671
- }
3672
- const keyBytes = parseSolanaKeyBytes(solanaKey);
4142
+ const signer = await resolveSolanaSigner(program2, opts.signer);
3673
4143
  validateSolanaAddress(toArg);
3674
4144
  const to = solAddress2(toArg);
3675
4145
  const network = resolveSolanaNetwork(program2);
3676
4146
  const symbol = nativeTokenSymbol(network);
3677
4147
  const lamports = parseAmount(amountArg, SOL_DECIMALS);
3678
- const signer = await createSolanaSignerFromKeyBytes(keyBytes);
3679
- const instruction = buildSolTransferInstruction(signer, to, lamports);
4148
+ const instruction = buildSolTransferInstruction(
4149
+ { address: solAddress2(signer.address) },
4150
+ to,
4151
+ lamports
4152
+ );
3680
4153
  const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
3681
4154
  const client = clientFromFlags(program2, { forceNetwork: network });
3682
4155
  const result = await withSpinner(
3683
4156
  "Sending transaction\u2026",
3684
4157
  "Transaction submitted",
3685
- () => buildAndSendSolanaTransaction({
3686
- client,
3687
- instructions: [instruction],
3688
- senderKeyBytes: keyBytes,
3689
- sponsored,
3690
- gasPolicyId: feePolicyId
3691
- })
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
+ }
3692
4177
  );
3693
4178
  const confirmed = await withSpinner(
3694
4179
  "Waiting for confirmation\u2026",
@@ -3721,6 +4206,61 @@ async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts
3721
4206
  printKeyValue(pairs);
3722
4207
  }
3723
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
+ }
3724
4264
 
3725
4265
  // src/lib/smart-wallet.ts
3726
4266
  import { createSmartWalletClient } from "@alchemy/wallet-apis";
@@ -3780,7 +4320,7 @@ function networkToChain(network) {
3780
4320
  }
3781
4321
 
3782
4322
  // src/lib/delegated-signer.ts
3783
- import { sign as signWithPrivateKey } from "crypto";
4323
+ import { sign as signWithPrivateKey2 } from "crypto";
3784
4324
  import { isAddressEqual, recoverMessageAddress, recoverTypedDataAddress } from "viem";
3785
4325
  import { toAccount } from "viem/accounts";
3786
4326
  var messageCapabilities = {
@@ -3794,8 +4334,8 @@ function toHex(bytes) {
3794
4334
  function normalizeSignedChallengeSignature(signature) {
3795
4335
  return signature.toString("base64url");
3796
4336
  }
3797
- function signChallengePayload(args) {
3798
- const signature = signWithPrivateKey(
4337
+ function signChallengePayload2(args) {
4338
+ const signature = signWithPrivateKey2(
3799
4339
  "sha256",
3800
4340
  Buffer.from(args.challengePayload, "utf8"),
3801
4341
  {
@@ -3978,7 +4518,7 @@ function createDelegatedAccount(args) {
3978
4518
  message: remoteMessage.message,
3979
4519
  encoding: remoteMessage.encoding
3980
4520
  });
3981
- const signature = signChallengePayload({
4521
+ const signature = signChallengePayload2({
3982
4522
  challengePayload: challenge.challenge,
3983
4523
  privateKeyPem: args.session.privateKeyPem
3984
4524
  });
@@ -4013,7 +4553,7 @@ function createDelegatedAccount(args) {
4013
4553
  ...sessionBinding,
4014
4554
  typedData: serializedTypedData
4015
4555
  });
4016
- const signature = signChallengePayload({
4556
+ const signature = signChallengePayload2({
4017
4557
  challengePayload: challenge.challenge,
4018
4558
  privateKeyPem: args.session.privateKeyPem
4019
4559
  });
@@ -4042,7 +4582,7 @@ function createDelegatedAccount(args) {
4042
4582
  ...sessionBinding,
4043
4583
  authorization
4044
4584
  });
4045
- const signature = signChallengePayload({
4585
+ const signature = signChallengePayload2({
4046
4586
  challengePayload: challenge.challenge,
4047
4587
  privateKeyPem: args.session.privateKeyPem
4048
4588
  });
@@ -4163,12 +4703,13 @@ function buildWalletClient(program2, options = {}) {
4163
4703
  }
4164
4704
  throw errNoActiveSession();
4165
4705
  }
4166
- assertSessionMetadata(validSession);
4167
- if (validSession.chainType && validSession.chainType !== "evm") {
4706
+ const evmSession = getWalletSessionByChain(validSession, "evm");
4707
+ if (!evmSession) {
4168
4708
  throw errInvalidArgs(
4169
- `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'."
4170
4710
  );
4171
4711
  }
4712
+ assertSessionMetadata(evmSession);
4172
4713
  const authToken = resolveAuthToken(cfg);
4173
4714
  if (!authToken) {
4174
4715
  throw errAuthRequired();
@@ -4176,9 +4717,9 @@ function buildWalletClient(program2, options = {}) {
4176
4717
  return {
4177
4718
  signer: createDelegatedAccount({
4178
4719
  authToken,
4179
- session: validSession
4720
+ session: evmSession
4180
4721
  }),
4181
- address: validSession.evmAddress
4722
+ address: evmSession.evmAddress
4182
4723
  };
4183
4724
  }
4184
4725
  if (!localKey) throw errWalletKeyRequired();
@@ -4917,7 +5458,7 @@ function buildAgentPrompt(program2) {
4917
5458
  method: "Local wallet + API key",
4918
5459
  envVar: "ALCHEMY_WALLET_KEY",
4919
5460
  flag: "--wallet-key-file <path> | --signer local",
4920
- setup: "alchemy wallet connect --mode local --chain evm",
5461
+ setup: "alchemy wallet connect --mode local",
4921
5462
  commandFamilies: [
4922
5463
  "evm send",
4923
5464
  "evm contract call",
@@ -6828,7 +7369,7 @@ Examples:
6828
7369
  dryRun: opts.dryRun
6829
7370
  });
6830
7371
  } catch (err) {
6831
- const { exitWithError: exitWithError2 } = await import("./errors-A53DVJDY.js");
7372
+ const { exitWithError: exitWithError2 } = await import("./errors-T6XE2I2L.js");
6832
7373
  exitWithError2(err);
6833
7374
  }
6834
7375
  });
@@ -8584,7 +9125,7 @@ async function flushProcessOutput() {
8584
9125
  }
8585
9126
  program.name("alchemy").description(
8586
9127
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
8587
- ).version("0.8.0", "-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(
8588
9129
  "-n, --network <network>",
8589
9130
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
8590
9131
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
@@ -8771,11 +9312,11 @@ ${styledLine}`;
8771
9312
  "wallet"
8772
9313
  ];
8773
9314
  if (!skipAppPrompt.includes(cmdName) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
8774
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-N3SX252M.js");
9315
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-GBS26K44.js");
8775
9316
  const authToken = resolveAuthToken2(cfg);
8776
9317
  const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
8777
9318
  if (authToken && !hasApiKey) {
8778
- const { selectAppAfterAuth } = await import("./auth-RINQSQDT.js");
9319
+ const { selectAppAfterAuth } = await import("./auth-65X7EMCF.js");
8779
9320
  console.log("");
8780
9321
  console.log(` No app selected. Please select an app to continue.`);
8781
9322
  await selectAppAfterAuth(authToken);
@@ -8810,7 +9351,7 @@ ${styledLine}`;
8810
9351
  if (isInteractiveAllowed(program)) {
8811
9352
  let latestForInteractiveStartup = null;
8812
9353
  if (shouldRunOnboarding(program, cfg)) {
8813
- const { runOnboarding } = await import("./onboarding-YHYXW4F3.js");
9354
+ const { runOnboarding } = await import("./onboarding-MDHVOUY3.js");
8814
9355
  const latest = getAvailableUpdateOnce();
8815
9356
  const completed = await runOnboarding(program, latest);
8816
9357
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -8824,7 +9365,7 @@ ${styledLine}`;
8824
9365
  latestForInteractiveStartup
8825
9366
  );
8826
9367
  }
8827
- const { startREPL } = await import("./interactive-6R3VKAPQ.js");
9368
+ const { startREPL } = await import("./interactive-GVMPYXNJ.js");
8828
9369
  program.exitOverride();
8829
9370
  program.configureOutput({
8830
9371
  writeErr: () => {