@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/auth-65X7EMCF.js +16 -0
- package/dist/{auth-GD7BJOMK.js → auth-IAM4AMBK.js} +2 -2
- package/dist/{chunk-64A5W4M2.js → chunk-75ICFV5K.js} +1 -1
- package/dist/{chunk-K6V3R7SH.js → chunk-7ZSEELHZ.js} +312 -8
- package/dist/chunk-AJPOUEO6.js +186 -0
- package/dist/{chunk-JUCUKTP3.js → chunk-GDLPBPG3.js} +1 -1
- package/dist/{chunk-5IL2PMZ6.js → chunk-MV7O3XBG.js} +1 -1
- package/dist/{chunk-R4W44A6E.js → chunk-NMT7XH3C.js} +5 -5
- package/dist/{chunk-2BALTY22.js → chunk-OVLQH6KL.js} +12 -4
- package/dist/{chunk-C5HNQOLB.js → chunk-RGVM5SNE.js} +1 -1
- package/dist/{chunk-N36ZNOVV.js → chunk-WT4RGQLC.js} +3 -3
- package/dist/{chunk-XSN4XA5Z.js → chunk-XCKUCXC6.js} +6 -6
- package/dist/{errors-E2P6WHTX.js → errors-T6XE2I2L.js} +3 -1
- package/dist/index.js +1062 -234
- package/dist/{interactive-MHAC5WQI.js → interactive-GVMPYXNJ.js} +7 -7
- package/dist/{onboarding-CT4RRH6O.js → onboarding-MDHVOUY3.js} +6 -6
- package/dist/policy-prompt-PHVO6VRZ.js +18 -0
- package/dist/{resolve-WXT5ZCUK.js → resolve-GBS26K44.js} +7 -3
- package/package.json +1 -1
- package/dist/auth-F2IXC6CM.js +0 -16
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-
|
|
11
|
+
} from "./chunk-XCKUCXC6.js";
|
|
6
12
|
import {
|
|
7
13
|
openBrowser
|
|
8
|
-
} from "./chunk-
|
|
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-
|
|
21
|
+
} from "./chunk-NMT7XH3C.js";
|
|
16
22
|
import {
|
|
17
23
|
isInteractiveAllowed
|
|
18
|
-
} from "./chunk-
|
|
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-
|
|
57
|
+
} from "./chunk-7ZSEELHZ.js";
|
|
47
58
|
import {
|
|
48
59
|
getAvailableUpdate,
|
|
49
60
|
getUpdateStatus,
|
|
50
61
|
printUpdateNotice
|
|
51
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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-
|
|
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([
|
|
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
|
-
|
|
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(
|
|
1628
|
+
return unwrapAdminData(walletSessionRequestCreateResponseSchema, data);
|
|
1498
1629
|
} catch (err) {
|
|
1499
|
-
const retryInput = getClientInstanceCompatibilityRetryInput(
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
"
|
|
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
|
|
2017
|
-
const
|
|
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
|
|
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
|
-
|
|
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: "
|
|
2117
|
-
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
|
|
2295
|
+
const remoteRequest = await createRemoteWalletSessionRequest(authToken, {
|
|
2122
2296
|
publicKeyJwk: session.publicKeyJwk,
|
|
2123
2297
|
requestSignerVersion: session.envelopeVersion,
|
|
2124
|
-
|
|
2125
|
-
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
|
-
|
|
2133
|
-
|
|
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
|
-
["
|
|
2327
|
+
["Connection Request ID", remoteRequest.sessionId],
|
|
2138
2328
|
["Status", "pending"],
|
|
2139
|
-
["Approval URL",
|
|
2329
|
+
["Approval URL", remoteRequest.approvalUrl],
|
|
2140
2330
|
["Created", session.createdAt],
|
|
2141
|
-
["Expires",
|
|
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
|
|
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(
|
|
2165
|
-
const
|
|
2361
|
+
openBrowser(remoteRequest.approvalUrl);
|
|
2362
|
+
const approvedSessions = await withApprovalInterruptHandler({
|
|
2166
2363
|
authToken,
|
|
2167
|
-
|
|
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
|
|
2172
|
-
|
|
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 (
|
|
2176
|
-
|
|
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
|
-
|
|
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 ${
|
|
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 (!
|
|
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
|
|
2199
|
-
|
|
2200
|
-
|
|
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
|
-
|
|
2457
|
+
connectionRequestId: remoteRequest.sessionId,
|
|
2458
|
+
sessionId: firstApprovedSession.sessionId ?? remoteRequest.sessions[0].walletSessionId,
|
|
2208
2459
|
status: "approved",
|
|
2209
|
-
expiresAt:
|
|
2210
|
-
privyAppId:
|
|
2211
|
-
walletId,
|
|
2212
|
-
evmWalletId:
|
|
2213
|
-
evmAddress,
|
|
2214
|
-
solanaWalletId:
|
|
2215
|
-
solanaAddress:
|
|
2216
|
-
privyKeyQuorumId:
|
|
2217
|
-
privySignerId:
|
|
2218
|
-
chainType:
|
|
2219
|
-
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
|
-
|
|
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:
|
|
2228
|
-
walletId,
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
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
|
-
|
|
2237
|
-
["
|
|
2238
|
-
["Status", green("approved")]
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
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
|
|
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({
|
|
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({
|
|
2622
|
+
const result = await runLocalCreate({ force, program: program2 });
|
|
2352
2623
|
printPostLocalCreateSummary(result);
|
|
2353
|
-
printCrossModeHintAfterLocal(
|
|
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
|
|
2678
|
+
const remoteSessions = await getRemoteSessionsForStatus(authToken, session);
|
|
2679
|
+
const remote = selectRemoteSessionForStatus(session, remoteSessions);
|
|
2362
2680
|
remoteStatus = remote.status;
|
|
2363
|
-
const
|
|
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:
|
|
2370
|
-
evmWalletId
|
|
2371
|
-
evmAddress
|
|
2372
|
-
solanaWalletId
|
|
2373
|
-
solanaAddress
|
|
2374
|
-
chainType
|
|
2375
|
-
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
|
|
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("--
|
|
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.
|
|
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
|
|
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
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
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 {
|
|
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
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
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
|
|
3198
|
-
if (sponsored
|
|
3199
|
-
|
|
3200
|
-
|
|
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
|
-
|
|
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
|
|
3714
|
+
async function resolveSolanaDelegateSigner(program2, signerOpt) {
|
|
3215
3715
|
const signer = parseSignerOpt(signerOpt);
|
|
3216
3716
|
if (signer === "session") {
|
|
3217
|
-
|
|
3218
|
-
|
|
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
|
|
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
|
-
() =>
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
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
|
|
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
|
-
() =>
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
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
|
-
|
|
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
|
|
3603
|
-
|
|
3604
|
-
|
|
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
|
-
() =>
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
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
|
|
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
|
|
3722
|
-
const signature =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
4069
|
-
if (
|
|
4706
|
+
const evmSession = getWalletSessionByChain(validSession, "evm");
|
|
4707
|
+
if (!evmSession) {
|
|
4070
4708
|
throw errInvalidArgs(
|
|
4071
|
-
|
|
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:
|
|
4720
|
+
session: evmSession
|
|
4082
4721
|
}),
|
|
4083
|
-
address:
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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);
|