@alchemy/cli 0.9.3 → 0.11.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/README.md +26 -26
- package/dist/{auth-KS6VPHHU.js → auth-5XFZB2BU.js} +2 -2
- package/dist/auth-6X2DBBYQ.js +16 -0
- package/dist/{chunk-NUSUQI7X.js → chunk-4QSVWWSK.js} +3 -3
- package/dist/{chunk-CTTW4PA4.js → chunk-5BEJA752.js} +21 -27
- package/dist/{chunk-ANONMDDZ.js → chunk-B5KVL3ZR.js} +61 -87
- package/dist/{chunk-VN5JUWHO.js → chunk-CSBYGYG6.js} +7 -12
- package/dist/{chunk-PMNRIXJI.js → chunk-DXQAGBW6.js} +1 -1
- package/dist/{chunk-AMGGO36F.js → chunk-I6YQX7PF.js} +1 -1
- package/dist/{chunk-D2RUM2DD.js → chunk-KIYIW6SX.js} +4 -4
- package/dist/{chunk-GLKB4JM7.js → chunk-LANOFNO6.js} +1 -11
- package/dist/{chunk-3GBDYROJ.js → chunk-RPSHRYCZ.js} +1 -1
- package/dist/{chunk-L5E7GEUU.js → chunk-XZS6KZHN.js} +6 -6
- package/dist/{errors-YPNK3AVF.js → errors-6BEPCY5N.js} +5 -5
- package/dist/index.js +717 -249
- package/dist/{interactive-KLVEYTQM.js → interactive-EXQM6HWQ.js} +7 -9
- package/dist/{onboarding-IJQ72DK7.js → onboarding-2FLKQYLY.js} +6 -6
- package/dist/{policy-prompt-V6W7CPAO.js → policy-prompt-3EVE7DW3.js} +5 -5
- package/dist/{resolve-REZCFZZ7.js → resolve-FRMIY357.js} +9 -5
- package/package.json +1 -1
- package/dist/auth-23OYLRWN.js +0 -16
package/dist/index.js
CHANGED
|
@@ -5,26 +5,27 @@ import {
|
|
|
5
5
|
errNotLoggedInForPolicyLookup,
|
|
6
6
|
errSponsorshipNeedsPolicy,
|
|
7
7
|
selectOrCreatePolicy
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KIYIW6SX.js";
|
|
9
9
|
import {
|
|
10
10
|
registerAuth
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-XZS6KZHN.js";
|
|
12
12
|
import {
|
|
13
13
|
openBrowser
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-I6YQX7PF.js";
|
|
15
15
|
import {
|
|
16
16
|
SETUP_CAPABILITY_LABELS,
|
|
17
17
|
SETUP_CAPABILITY_ORDER,
|
|
18
18
|
getSetupStatus,
|
|
19
19
|
isSetupComplete,
|
|
20
20
|
shouldRunOnboarding
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-CSBYGYG6.js";
|
|
22
22
|
import {
|
|
23
23
|
isInteractiveAllowed
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-RPSHRYCZ.js";
|
|
25
25
|
import {
|
|
26
26
|
RpcApiError,
|
|
27
27
|
adminClientFromFlags,
|
|
28
|
+
apiKeyClientFromFlags,
|
|
28
29
|
clearSession,
|
|
29
30
|
clientFromFlags,
|
|
30
31
|
createPendingSession,
|
|
@@ -47,6 +48,8 @@ import {
|
|
|
47
48
|
resolveGasPolicyId,
|
|
48
49
|
resolveGasSponsored,
|
|
49
50
|
resolveNetwork,
|
|
51
|
+
resolveOptionalNetwork,
|
|
52
|
+
resolveRequiredNetwork,
|
|
50
53
|
resolveSolanaFeePolicyId,
|
|
51
54
|
resolveSolanaFeeSponsored,
|
|
52
55
|
resolveSolanaNetwork,
|
|
@@ -60,12 +63,12 @@ import {
|
|
|
60
63
|
updateSession,
|
|
61
64
|
validateNetwork,
|
|
62
65
|
walletNetworkToChain
|
|
63
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-B5KVL3ZR.js";
|
|
64
67
|
import {
|
|
65
68
|
getAvailableUpdate,
|
|
66
69
|
getUpdateStatus,
|
|
67
70
|
printUpdateNotice
|
|
68
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-4QSVWWSK.js";
|
|
69
72
|
import {
|
|
70
73
|
bold,
|
|
71
74
|
brand,
|
|
@@ -89,7 +92,7 @@ import {
|
|
|
89
92
|
weiToEth,
|
|
90
93
|
withSpinner,
|
|
91
94
|
yellow
|
|
92
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-DXQAGBW6.js";
|
|
93
96
|
import {
|
|
94
97
|
KEY_MAP,
|
|
95
98
|
configDir,
|
|
@@ -100,7 +103,7 @@ import {
|
|
|
100
103
|
save,
|
|
101
104
|
toMap,
|
|
102
105
|
validKeys
|
|
103
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-LANOFNO6.js";
|
|
104
107
|
import {
|
|
105
108
|
CLIError,
|
|
106
109
|
EXIT_CODES,
|
|
@@ -120,6 +123,7 @@ import {
|
|
|
120
123
|
errSolanaTransactionFailed,
|
|
121
124
|
errSolanaWalletKeyRequired,
|
|
122
125
|
errWalletKeyRequired,
|
|
126
|
+
errWalletRequired,
|
|
123
127
|
esc,
|
|
124
128
|
exitWithError,
|
|
125
129
|
fetchWithTimeout,
|
|
@@ -138,7 +142,7 @@ import {
|
|
|
138
142
|
setNoColor,
|
|
139
143
|
timeout,
|
|
140
144
|
verbose
|
|
141
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-5BEJA752.js";
|
|
142
146
|
|
|
143
147
|
// src/index.ts
|
|
144
148
|
import { Command, Help } from "commander";
|
|
@@ -222,11 +226,11 @@ async function resolveENS(name, client) {
|
|
|
222
226
|
if (dataHex.length < 64) {
|
|
223
227
|
throw errInvalidArgs(`ENS name "${name}" could not be resolved.`);
|
|
224
228
|
}
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
229
|
+
const address3 = "0x" + dataHex.slice(24, 64);
|
|
230
|
+
if (address3 === "0x0000000000000000000000000000000000000000") {
|
|
227
231
|
throw errInvalidArgs(`ENS name "${name}" is not registered or has no address set.`);
|
|
228
232
|
}
|
|
229
|
-
return
|
|
233
|
+
return address3;
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
// src/lib/validators.ts
|
|
@@ -267,10 +271,10 @@ async function readStdinLines(name) {
|
|
|
267
271
|
}
|
|
268
272
|
return lines;
|
|
269
273
|
}
|
|
270
|
-
function validateAddress(
|
|
271
|
-
if (!ADDRESS_RE.test(
|
|
274
|
+
function validateAddress(address3) {
|
|
275
|
+
if (!ADDRESS_RE.test(address3)) {
|
|
272
276
|
throw errInvalidArgs(
|
|
273
|
-
`Invalid address "${
|
|
277
|
+
`Invalid address "${address3}". Expected 0x-prefixed 40-hex-character address.`
|
|
274
278
|
);
|
|
275
279
|
}
|
|
276
280
|
}
|
|
@@ -497,16 +501,6 @@ function registerConfig(program2) {
|
|
|
497
501
|
exitWithError(err);
|
|
498
502
|
}
|
|
499
503
|
});
|
|
500
|
-
setCmd.command("network <network>").description("Set the default network (e.g. eth-mainnet, polygon-mainnet)").action((network) => {
|
|
501
|
-
try {
|
|
502
|
-
const cfg = load();
|
|
503
|
-
save({ ...cfg, network });
|
|
504
|
-
printHuman(`${green("\u2713")} Set network to ${network}
|
|
505
|
-
`, { key: "network", value: network, status: "set" });
|
|
506
|
-
} catch (err) {
|
|
507
|
-
exitWithError(err);
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
504
|
setCmd.command("verbose <enabled>").description("Set default verbose output (true|false)").action((enabled) => {
|
|
511
505
|
try {
|
|
512
506
|
const normalized = enabled.trim().toLowerCase();
|
|
@@ -585,8 +579,8 @@ function registerConfig(program2) {
|
|
|
585
579
|
"Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set evm-gas-policy-id <id>`."
|
|
586
580
|
);
|
|
587
581
|
}
|
|
588
|
-
const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-
|
|
589
|
-
const { resolveNetwork: resolveNetwork2 } = await import("./resolve-
|
|
582
|
+
const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-3EVE7DW3.js");
|
|
583
|
+
const { resolveNetwork: resolveNetwork2 } = await import("./resolve-FRMIY357.js");
|
|
590
584
|
const network = resolveNetwork2(program2);
|
|
591
585
|
await selectOrCreatePolicy2({
|
|
592
586
|
flavor: "sponsorship",
|
|
@@ -640,8 +634,8 @@ function registerConfig(program2) {
|
|
|
640
634
|
"Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set solana-fee-policy-id <id>`."
|
|
641
635
|
);
|
|
642
636
|
}
|
|
643
|
-
const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-
|
|
644
|
-
const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-
|
|
637
|
+
const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-3EVE7DW3.js");
|
|
638
|
+
const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-FRMIY357.js");
|
|
645
639
|
const network = resolveSolanaNetwork2(program2);
|
|
646
640
|
await selectOrCreatePolicy2({
|
|
647
641
|
flavor: "solana",
|
|
@@ -663,13 +657,12 @@ function registerConfig(program2) {
|
|
|
663
657
|
exitWithError(err);
|
|
664
658
|
}
|
|
665
659
|
});
|
|
666
|
-
cmd.command("get <key>").description("Get a config value (api-key, app,
|
|
660
|
+
cmd.command("get <key>").description("Get a config value (api-key, app, verbose, wallet-key-file, x402)").action((key) => {
|
|
667
661
|
const cfg = load();
|
|
668
662
|
let value = get(cfg, key);
|
|
669
663
|
let isDefault = false;
|
|
670
664
|
if (value === void 0) {
|
|
671
665
|
const defaults = {
|
|
672
|
-
network: "eth-mainnet",
|
|
673
666
|
verbose: "false",
|
|
674
667
|
x402: "false",
|
|
675
668
|
evm_gas_sponsored: "false",
|
|
@@ -699,7 +692,7 @@ function registerConfig(program2) {
|
|
|
699
692
|
printJSON(toMap(cfg));
|
|
700
693
|
return;
|
|
701
694
|
}
|
|
702
|
-
const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-
|
|
695
|
+
const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-FRMIY357.js");
|
|
703
696
|
const validToken = resolveAuthToken2(cfg);
|
|
704
697
|
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");
|
|
705
698
|
const pairs = [
|
|
@@ -713,7 +706,6 @@ function registerConfig(program2) {
|
|
|
713
706
|
"app",
|
|
714
707
|
cfg.app ? `${cfg.app.name} ${dim(`(${cfg.app.id})`)}` : dim("(not set) \u2014 set automatically via 'alchemy auth' or 'config set app'")
|
|
715
708
|
],
|
|
716
|
-
["network", cfg.network || dim("(not set, defaults to eth-mainnet)")],
|
|
717
709
|
[
|
|
718
710
|
"verbose",
|
|
719
711
|
cfg.verbose !== void 0 ? String(cfg.verbose) : dim("(not set, defaults to false)")
|
|
@@ -2034,14 +2026,14 @@ function formatWalletStatus(value) {
|
|
|
2034
2026
|
function walletKeysDirPath() {
|
|
2035
2027
|
return join(configDir(), WALLET_KEYS_DIR);
|
|
2036
2028
|
}
|
|
2037
|
-
function walletKeyPath(prefix,
|
|
2038
|
-
const addr =
|
|
2029
|
+
function walletKeyPath(prefix, address3) {
|
|
2030
|
+
const addr = address3.trim().toLowerCase().replace(/^0x/, "").replace(/[^a-z0-9]/g, "").slice(0, ADDRESS_SLICE_LEN);
|
|
2039
2031
|
const addressTag = addr || "unknown";
|
|
2040
2032
|
const fileName = `${prefix}-${addressTag}-${Date.now()}-${randomUUID().slice(0, UUID_SLICE_LEN)}.txt`;
|
|
2041
2033
|
return join(walletKeysDirPath(), fileName);
|
|
2042
2034
|
}
|
|
2043
|
-
function persistWalletKey(prefix, privateKey,
|
|
2044
|
-
const keyPath = walletKeyPath(prefix,
|
|
2035
|
+
function persistWalletKey(prefix, privateKey, address3) {
|
|
2036
|
+
const keyPath = walletKeyPath(prefix, address3);
|
|
2045
2037
|
mkdirSync(dirname(keyPath), { recursive: true, mode: 493 });
|
|
2046
2038
|
writeFileSync(keyPath, privateKey + "\n", { mode: 384, flag: "wx" });
|
|
2047
2039
|
return keyPath;
|
|
@@ -2152,11 +2144,11 @@ function importAndPersistWallet(path) {
|
|
|
2152
2144
|
} catch {
|
|
2153
2145
|
throw errInvalidArgs(`Could not read key file: ${path}`);
|
|
2154
2146
|
}
|
|
2155
|
-
const
|
|
2156
|
-
const keyPath = persistWalletKey("wallet-key", key,
|
|
2147
|
+
const address3 = getEvmWalletAddress(key);
|
|
2148
|
+
const keyPath = persistWalletKey("wallet-key", key, address3);
|
|
2157
2149
|
const cfg = load();
|
|
2158
|
-
save({ ...cfg, wallet_key_file: keyPath, wallet_address:
|
|
2159
|
-
return { address:
|
|
2150
|
+
save({ ...cfg, wallet_key_file: keyPath, wallet_address: address3 });
|
|
2151
|
+
return { address: address3, keyFile: keyPath };
|
|
2160
2152
|
}
|
|
2161
2153
|
function hasValidSessionWithEvm() {
|
|
2162
2154
|
const session = resolveWalletSession();
|
|
@@ -2596,21 +2588,20 @@ async function runConnectFlow(program2, opts) {
|
|
|
2596
2588
|
}
|
|
2597
2589
|
if (!mode) {
|
|
2598
2590
|
if (!isInteractiveAllowed(program2)) {
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2591
|
+
mode = "session";
|
|
2592
|
+
} else {
|
|
2593
|
+
const choice = await promptSelect({
|
|
2594
|
+
message: "Choose a wallet to connect",
|
|
2595
|
+
options: [
|
|
2596
|
+
{ value: "session", label: "Session wallet", hint: "Recommended \u2014 Alchemy-managed, more secure" },
|
|
2597
|
+
{ value: "local", label: "Local wallet", hint: "Private key stored on this machine" }
|
|
2598
|
+
],
|
|
2599
|
+
initialValue: "session",
|
|
2600
|
+
cancelMessage: "Wallet connect cancelled."
|
|
2601
|
+
});
|
|
2602
|
+
if (choice === null) throw new WalletConnectInterruptedError();
|
|
2603
|
+
mode = choice;
|
|
2602
2604
|
}
|
|
2603
|
-
const choice = await promptSelect({
|
|
2604
|
-
message: "Choose a wallet to connect",
|
|
2605
|
-
options: [
|
|
2606
|
-
{ value: "session", label: "Session wallet", hint: "Recommended \u2014 Alchemy-managed, more secure" },
|
|
2607
|
-
{ value: "local", label: "Local wallet", hint: "Private key stored on this machine" }
|
|
2608
|
-
],
|
|
2609
|
-
initialValue: "session",
|
|
2610
|
-
cancelMessage: "Wallet connect cancelled."
|
|
2611
|
-
});
|
|
2612
|
-
if (choice === null) throw new WalletConnectInterruptedError();
|
|
2613
|
-
mode = choice;
|
|
2614
2605
|
}
|
|
2615
2606
|
if (mode === "session") {
|
|
2616
2607
|
await runSessionConnect({
|
|
@@ -3312,7 +3303,7 @@ function registerNetwork(program2) {
|
|
|
3312
3303
|
cmd.command("list").description("List RPC network IDs for use with --network (e.g. eth-mainnet)").option("--mainnet-only", "Show only mainnet networks").option("--testnet-only", "Show only testnet networks").option("--search <term>", "Filter networks by name or ID").action(async (opts) => {
|
|
3313
3304
|
try {
|
|
3314
3305
|
let display = getRPCNetworks();
|
|
3315
|
-
const current =
|
|
3306
|
+
const current = resolveOptionalNetwork(program2);
|
|
3316
3307
|
if (opts.mainnetOnly) {
|
|
3317
3308
|
display = display.filter((n) => !n.isTestnet);
|
|
3318
3309
|
} else if (opts.testnetOnly) {
|
|
@@ -3329,15 +3320,17 @@ function registerNetwork(program2) {
|
|
|
3329
3320
|
return;
|
|
3330
3321
|
}
|
|
3331
3322
|
const rows = display.map((network) => {
|
|
3332
|
-
const isCurrent = network.id === current;
|
|
3323
|
+
const isCurrent = current !== void 0 && network.id === current;
|
|
3333
3324
|
const idCell = isCurrent ? green(network.id) : network.id;
|
|
3334
3325
|
const nameCell = isCurrent ? green(network.name) : network.name;
|
|
3335
3326
|
const testnetCell = network.isTestnet ? dim("yes") : "no";
|
|
3336
3327
|
return [idCell, nameCell, network.family, testnetCell];
|
|
3337
3328
|
});
|
|
3338
3329
|
printTable(["Network ID", "Name", "Family", "Testnet"], rows);
|
|
3339
|
-
|
|
3330
|
+
if (current) {
|
|
3331
|
+
console.log(`
|
|
3340
3332
|
Current: ${green(current)}`);
|
|
3333
|
+
}
|
|
3341
3334
|
console.log(
|
|
3342
3335
|
` ${dim("Need Admin API chain identifiers (e.g. ETH_MAINNET)? See: app chains")}`
|
|
3343
3336
|
);
|
|
@@ -3349,7 +3342,7 @@ function registerNetwork(program2) {
|
|
|
3349
3342
|
printJSON({
|
|
3350
3343
|
mode: "all",
|
|
3351
3344
|
networks: display,
|
|
3352
|
-
currentNetwork: current
|
|
3345
|
+
currentNetwork: current ?? null
|
|
3353
3346
|
});
|
|
3354
3347
|
}
|
|
3355
3348
|
} catch (err) {
|
|
@@ -3401,7 +3394,7 @@ function formatTokenAmount(rawAmount, decimals) {
|
|
|
3401
3394
|
return frac ? `${whole}.${frac}` : whole;
|
|
3402
3395
|
}
|
|
3403
3396
|
async function fetchTokenDecimals(program2, tokenAddress, opts) {
|
|
3404
|
-
const client =
|
|
3397
|
+
const client = apiKeyClientFromFlags(
|
|
3405
3398
|
program2,
|
|
3406
3399
|
opts?.network ? { forceNetwork: opts.network } : void 0
|
|
3407
3400
|
);
|
|
@@ -4127,15 +4120,176 @@ function printAccountSummary(args) {
|
|
|
4127
4120
|
|
|
4128
4121
|
// src/commands/send-solana.ts
|
|
4129
4122
|
import { address as solAddress2 } from "@solana/kit";
|
|
4123
|
+
|
|
4124
|
+
// src/lib/solana-token.ts
|
|
4125
|
+
import {
|
|
4126
|
+
AccountRole as AccountRole2,
|
|
4127
|
+
address as address2,
|
|
4128
|
+
getAddressEncoder,
|
|
4129
|
+
getProgramDerivedAddress
|
|
4130
|
+
} from "@solana/kit";
|
|
4131
|
+
var SPL_TOKEN_PROGRAM_ADDRESS2 = address2("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
4132
|
+
var SPL_TOKEN_2022_PROGRAM_ADDRESS = address2("TokenzQdBNbLqP5VEhdkAS6EPYdWnYARjfkM8GoyZ2G");
|
|
4133
|
+
var ASSOCIATED_TOKEN_PROGRAM_ADDRESS = address2("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
|
4134
|
+
var SYSTEM_PROGRAM_ADDRESS2 = address2("11111111111111111111111111111111");
|
|
4135
|
+
var SUPPORTED_TOKEN_PROGRAMS = /* @__PURE__ */ new Set([
|
|
4136
|
+
SPL_TOKEN_PROGRAM_ADDRESS2,
|
|
4137
|
+
SPL_TOKEN_2022_PROGRAM_ADDRESS
|
|
4138
|
+
]);
|
|
4139
|
+
async function deriveAssociatedTokenAccount(args) {
|
|
4140
|
+
const addressEncoder = getAddressEncoder();
|
|
4141
|
+
const [associatedTokenAccount] = await getProgramDerivedAddress({
|
|
4142
|
+
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
|
|
4143
|
+
seeds: [
|
|
4144
|
+
addressEncoder.encode(args.owner),
|
|
4145
|
+
addressEncoder.encode(args.tokenProgramAddress),
|
|
4146
|
+
addressEncoder.encode(args.mint)
|
|
4147
|
+
]
|
|
4148
|
+
});
|
|
4149
|
+
return associatedTokenAccount;
|
|
4150
|
+
}
|
|
4151
|
+
async function fetchSplMintInfo(client, mint) {
|
|
4152
|
+
const account = await fetchParsedAccount(client, mint);
|
|
4153
|
+
if (!account) {
|
|
4154
|
+
throw errInvalidArgs(`SPL token mint ${mint} was not found on this network.`);
|
|
4155
|
+
}
|
|
4156
|
+
const tokenProgramAddress = address2(account.owner);
|
|
4157
|
+
assertSupportedTokenProgram(tokenProgramAddress);
|
|
4158
|
+
const data = parseAccountData(account.data);
|
|
4159
|
+
const decimals = data?.parsed?.info?.decimals;
|
|
4160
|
+
if (typeof decimals !== "number" || !Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
|
|
4161
|
+
throw errInvalidArgs(`Could not read decimals for SPL token mint ${mint}.`);
|
|
4162
|
+
}
|
|
4163
|
+
return {
|
|
4164
|
+
mint,
|
|
4165
|
+
decimals,
|
|
4166
|
+
tokenProgramAddress
|
|
4167
|
+
};
|
|
4168
|
+
}
|
|
4169
|
+
async function fetchSplTokenAccountInfo(client, tokenAccount) {
|
|
4170
|
+
const account = await fetchParsedAccount(client, tokenAccount);
|
|
4171
|
+
if (!account) return null;
|
|
4172
|
+
const tokenProgramAddress = address2(account.owner);
|
|
4173
|
+
assertSupportedTokenProgram(tokenProgramAddress);
|
|
4174
|
+
const data = parseAccountData(account.data);
|
|
4175
|
+
const info = data?.parsed?.info;
|
|
4176
|
+
if (!info || data?.parsed?.type !== "account") {
|
|
4177
|
+
throw errInvalidArgs(`Account ${tokenAccount} is not an SPL token account.`);
|
|
4178
|
+
}
|
|
4179
|
+
const mint = readAddressField(info, "mint", `Account ${tokenAccount} is missing token mint metadata.`);
|
|
4180
|
+
const owner = readAddressField(info, "owner", `Account ${tokenAccount} is missing token owner metadata.`);
|
|
4181
|
+
return {
|
|
4182
|
+
address: tokenAccount,
|
|
4183
|
+
mint,
|
|
4184
|
+
owner,
|
|
4185
|
+
tokenProgramAddress
|
|
4186
|
+
};
|
|
4187
|
+
}
|
|
4188
|
+
function buildCreateAssociatedTokenAccountIdempotentInstruction(args) {
|
|
4189
|
+
return {
|
|
4190
|
+
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
|
|
4191
|
+
accounts: [
|
|
4192
|
+
{ address: args.payer.address, role: AccountRole2.WRITABLE_SIGNER },
|
|
4193
|
+
{ address: args.associatedTokenAccount, role: AccountRole2.WRITABLE },
|
|
4194
|
+
{ address: args.owner, role: AccountRole2.READONLY },
|
|
4195
|
+
{ address: args.mint, role: AccountRole2.READONLY },
|
|
4196
|
+
{ address: SYSTEM_PROGRAM_ADDRESS2, role: AccountRole2.READONLY },
|
|
4197
|
+
{ address: args.tokenProgramAddress, role: AccountRole2.READONLY }
|
|
4198
|
+
],
|
|
4199
|
+
data: new Uint8Array([1])
|
|
4200
|
+
};
|
|
4201
|
+
}
|
|
4202
|
+
function buildSplTokenTransferCheckedInstruction(args) {
|
|
4203
|
+
if (!Number.isInteger(args.decimals) || args.decimals < 0 || args.decimals > 255) {
|
|
4204
|
+
throw errInvalidArgs("Token decimals must be an integer between 0 and 255.");
|
|
4205
|
+
}
|
|
4206
|
+
return {
|
|
4207
|
+
programAddress: args.tokenProgramAddress,
|
|
4208
|
+
accounts: [
|
|
4209
|
+
{ address: args.sourceTokenAccount, role: AccountRole2.WRITABLE },
|
|
4210
|
+
{ address: args.mint, role: AccountRole2.READONLY },
|
|
4211
|
+
{ address: args.destinationTokenAccount, role: AccountRole2.WRITABLE },
|
|
4212
|
+
{ address: args.owner.address, role: AccountRole2.READONLY_SIGNER }
|
|
4213
|
+
],
|
|
4214
|
+
data: new Uint8Array([
|
|
4215
|
+
12,
|
|
4216
|
+
...encodeU64LE2(args.amount),
|
|
4217
|
+
args.decimals
|
|
4218
|
+
])
|
|
4219
|
+
};
|
|
4220
|
+
}
|
|
4221
|
+
function assertTokenAccountMatches(args) {
|
|
4222
|
+
if (args.account.mint !== args.expectedMint) {
|
|
4223
|
+
throw errInvalidArgs(
|
|
4224
|
+
`${args.label} ${args.account.address} is for mint ${args.account.mint}, expected ${args.expectedMint}.`
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
4227
|
+
if (args.expectedOwner && args.account.owner !== args.expectedOwner) {
|
|
4228
|
+
throw errInvalidArgs(
|
|
4229
|
+
`${args.label} ${args.account.address} is owned by ${args.account.owner}, expected ${args.expectedOwner}.`
|
|
4230
|
+
);
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
function assertSupportedTokenProgram(tokenProgramAddress) {
|
|
4234
|
+
if (!SUPPORTED_TOKEN_PROGRAMS.has(tokenProgramAddress)) {
|
|
4235
|
+
throw errInvalidArgs(
|
|
4236
|
+
`Unsupported Solana token program ${tokenProgramAddress}. Only SPL Token and Token-2022 mints are supported.`
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
async function fetchParsedAccount(client, accountAddress) {
|
|
4241
|
+
const result = await client.call("getAccountInfo", [
|
|
4242
|
+
accountAddress,
|
|
4243
|
+
{ encoding: "jsonParsed" }
|
|
4244
|
+
]);
|
|
4245
|
+
return result.value;
|
|
4246
|
+
}
|
|
4247
|
+
function parseAccountData(data) {
|
|
4248
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return null;
|
|
4249
|
+
return data;
|
|
4250
|
+
}
|
|
4251
|
+
function readAddressField(info, field, errorMessage) {
|
|
4252
|
+
const value = info[field];
|
|
4253
|
+
if (typeof value !== "string") {
|
|
4254
|
+
throw errInvalidArgs(errorMessage);
|
|
4255
|
+
}
|
|
4256
|
+
return address2(value);
|
|
4257
|
+
}
|
|
4258
|
+
function encodeU64LE2(value) {
|
|
4259
|
+
if (value < 0n || value > 0xffffffffffffffffn) {
|
|
4260
|
+
throw errInvalidArgs("Amount must fit in an unsigned 64-bit integer.");
|
|
4261
|
+
}
|
|
4262
|
+
const bytes = new Uint8Array(8);
|
|
4263
|
+
let remaining = value;
|
|
4264
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
4265
|
+
bytes[i] = Number(remaining & 0xffn);
|
|
4266
|
+
remaining >>= 8n;
|
|
4267
|
+
}
|
|
4268
|
+
return bytes;
|
|
4269
|
+
}
|
|
4270
|
+
|
|
4271
|
+
// src/commands/send-solana.ts
|
|
4130
4272
|
function registerSolanaSend(program2) {
|
|
4131
|
-
const sendCmd = program2.command("send <to> <amount>").description("Send SOL to an address").option("--token <address>", "SPL token
|
|
4273
|
+
const sendCmd = program2.command("send <to> <amount>").description("Send SOL or SPL tokens to an address").option("--token <mint>", "SPL token mint address (omit for native SOL)").option("--dry-run", "Preview transaction without signing or sending").option("--fail-if-associated-token-account-missing", "Fail if the recipient associated token account is missing").option("--from-token-account <address>", "Source SPL token account (defaults to sender associated token account)").option("--recipient-token-account <address>", "Destination SPL token account (defaults to recipient associated token account)").option("--fee-sponsored", "Enable Solana fee sponsorship").option("--fee-policy-id <id>", "Solana fee policy ID for sponsorship").addHelpText(
|
|
4274
|
+
"after",
|
|
4275
|
+
`
|
|
4276
|
+
Examples:
|
|
4277
|
+
alchemy solana send <pubkey> 0.1
|
|
4278
|
+
alchemy solana send <pubkey> 10 --token <mint>
|
|
4279
|
+
alchemy solana send <pubkey> 10 --token <mint> --dry-run
|
|
4280
|
+
alchemy solana send <pubkey> 10 --token <mint> --fail-if-associated-token-account-missing`
|
|
4281
|
+
);
|
|
4132
4282
|
addSignerOption(sendCmd);
|
|
4133
4283
|
sendCmd.action(
|
|
4134
4284
|
async (toArg, amountArg, _opts, actionCommand) => {
|
|
4135
4285
|
try {
|
|
4136
4286
|
const opts = actionCommand.opts();
|
|
4137
4287
|
await performSolanaSend(actionCommand, toArg, amountArg, opts.token, {
|
|
4138
|
-
signer: parseSignerOpt(opts.signer)
|
|
4288
|
+
signer: parseSignerOpt(opts.signer),
|
|
4289
|
+
dryRun: opts.dryRun,
|
|
4290
|
+
failIfAssociatedTokenAccountMissing: opts.failIfAssociatedTokenAccountMissing,
|
|
4291
|
+
fromTokenAccount: opts.fromTokenAccount,
|
|
4292
|
+
recipientTokenAccount: opts.recipientTokenAccount
|
|
4139
4293
|
});
|
|
4140
4294
|
} catch (err) {
|
|
4141
4295
|
exitWithError(err);
|
|
@@ -4144,75 +4298,378 @@ function registerSolanaSend(program2) {
|
|
|
4144
4298
|
);
|
|
4145
4299
|
}
|
|
4146
4300
|
async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts = {}) {
|
|
4147
|
-
|
|
4148
|
-
throw errInvalidArgs("SPL token transfers are not yet supported. Omit --token for native SOL transfers.");
|
|
4149
|
-
}
|
|
4301
|
+
validateSolanaTokenOptions(tokenAddress, opts);
|
|
4150
4302
|
const signer = await resolveSolanaSigner(program2, opts.signer);
|
|
4151
4303
|
validateSolanaAddress(toArg);
|
|
4152
4304
|
const to = solAddress2(toArg);
|
|
4153
4305
|
const network = resolveSolanaNetwork(program2);
|
|
4154
|
-
const
|
|
4155
|
-
const
|
|
4156
|
-
|
|
4157
|
-
|
|
4306
|
+
const { sponsored, feePolicyId } = await resolveSolanaFeeSponsorship(program2);
|
|
4307
|
+
const client = clientFromFlags(program2, { forceNetwork: network });
|
|
4308
|
+
if (tokenAddress) {
|
|
4309
|
+
await performSplTokenSend({
|
|
4310
|
+
client,
|
|
4311
|
+
signer,
|
|
4312
|
+
to,
|
|
4313
|
+
toArg,
|
|
4314
|
+
amountArg,
|
|
4315
|
+
tokenAddress,
|
|
4316
|
+
network,
|
|
4317
|
+
sponsored,
|
|
4318
|
+
feePolicyId,
|
|
4319
|
+
opts
|
|
4320
|
+
});
|
|
4321
|
+
return;
|
|
4322
|
+
}
|
|
4323
|
+
await performNativeSolSend({
|
|
4324
|
+
client,
|
|
4325
|
+
signer,
|
|
4158
4326
|
to,
|
|
4327
|
+
toArg,
|
|
4328
|
+
amountArg,
|
|
4329
|
+
network,
|
|
4330
|
+
sponsored,
|
|
4331
|
+
feePolicyId,
|
|
4332
|
+
dryRun: opts.dryRun
|
|
4333
|
+
});
|
|
4334
|
+
}
|
|
4335
|
+
async function performNativeSolSend(args) {
|
|
4336
|
+
const symbol = nativeTokenSymbol(args.network);
|
|
4337
|
+
const lamports = parseAmount(args.amountArg, SOL_DECIMALS);
|
|
4338
|
+
const instruction = buildSolTransferInstruction(
|
|
4339
|
+
{ address: solAddress2(args.signer.address) },
|
|
4340
|
+
args.to,
|
|
4159
4341
|
lamports
|
|
4160
4342
|
);
|
|
4161
|
-
|
|
4162
|
-
|
|
4343
|
+
if (args.dryRun) {
|
|
4344
|
+
printNativeDryRun({
|
|
4345
|
+
from: args.signer.address,
|
|
4346
|
+
to: args.toArg,
|
|
4347
|
+
amount: args.amountArg,
|
|
4348
|
+
symbol,
|
|
4349
|
+
network: args.network,
|
|
4350
|
+
sponsored: args.sponsored
|
|
4351
|
+
});
|
|
4352
|
+
return;
|
|
4353
|
+
}
|
|
4163
4354
|
const result = await withSpinner(
|
|
4164
4355
|
"Sending transaction\u2026",
|
|
4165
4356
|
"Transaction submitted",
|
|
4166
4357
|
async () => {
|
|
4167
|
-
if (signer.type === "session") {
|
|
4358
|
+
if (args.signer.type === "session") {
|
|
4168
4359
|
return await buildAndSendSolanaTransactionWithSession({
|
|
4169
|
-
client,
|
|
4360
|
+
client: args.client,
|
|
4170
4361
|
instructions: [instruction],
|
|
4171
|
-
session: signer.session,
|
|
4172
|
-
authToken: signer.authToken,
|
|
4173
|
-
sponsored,
|
|
4174
|
-
gasPolicyId: feePolicyId
|
|
4362
|
+
session: args.signer.session,
|
|
4363
|
+
authToken: args.signer.authToken,
|
|
4364
|
+
sponsored: args.sponsored,
|
|
4365
|
+
gasPolicyId: args.feePolicyId
|
|
4175
4366
|
});
|
|
4176
4367
|
}
|
|
4177
4368
|
return await buildAndSendSolanaTransaction({
|
|
4178
|
-
client,
|
|
4369
|
+
client: args.client,
|
|
4179
4370
|
instructions: [instruction],
|
|
4180
|
-
senderKeyBytes: signer.keyBytes,
|
|
4181
|
-
sponsored,
|
|
4182
|
-
gasPolicyId: feePolicyId
|
|
4371
|
+
senderKeyBytes: args.signer.keyBytes,
|
|
4372
|
+
sponsored: args.sponsored,
|
|
4373
|
+
gasPolicyId: args.feePolicyId
|
|
4183
4374
|
});
|
|
4184
4375
|
}
|
|
4185
4376
|
);
|
|
4186
4377
|
const confirmed = await withSpinner(
|
|
4187
4378
|
"Waiting for confirmation\u2026",
|
|
4188
4379
|
"Confirmation status received",
|
|
4189
|
-
() => waitForSolanaConfirmation(client, result.signature)
|
|
4380
|
+
() => waitForSolanaConfirmation(args.client, result.signature)
|
|
4381
|
+
);
|
|
4382
|
+
printNativeResult({
|
|
4383
|
+
result,
|
|
4384
|
+
to: args.toArg,
|
|
4385
|
+
amount: args.amountArg,
|
|
4386
|
+
symbol,
|
|
4387
|
+
network: args.network,
|
|
4388
|
+
sponsored: args.sponsored,
|
|
4389
|
+
status: confirmed ? "confirmed" : "pending"
|
|
4390
|
+
});
|
|
4391
|
+
}
|
|
4392
|
+
function validateSolanaTokenOptions(tokenAddress, opts) {
|
|
4393
|
+
if (tokenAddress) return;
|
|
4394
|
+
const splOnlyFlags = [
|
|
4395
|
+
opts.fromTokenAccount ? "--from-token-account" : void 0,
|
|
4396
|
+
opts.recipientTokenAccount ? "--recipient-token-account" : void 0,
|
|
4397
|
+
opts.failIfAssociatedTokenAccountMissing ? "--fail-if-associated-token-account-missing" : void 0
|
|
4398
|
+
].filter((flag) => flag !== void 0);
|
|
4399
|
+
if (splOnlyFlags.length === 0) return;
|
|
4400
|
+
throw errInvalidArgs(
|
|
4401
|
+
`${splOnlyFlags.join(", ")} ${splOnlyFlags.length === 1 ? "requires" : "require"} --token <mint>.`
|
|
4402
|
+
);
|
|
4403
|
+
}
|
|
4404
|
+
async function performSplTokenSend(args) {
|
|
4405
|
+
validateSolanaAddress(args.tokenAddress);
|
|
4406
|
+
if (args.opts.fromTokenAccount) validateSolanaAddress(args.opts.fromTokenAccount);
|
|
4407
|
+
if (args.opts.recipientTokenAccount) validateSolanaAddress(args.opts.recipientTokenAccount);
|
|
4408
|
+
const mint = solAddress2(args.tokenAddress);
|
|
4409
|
+
const owner = solAddress2(args.signer.address);
|
|
4410
|
+
const mintInfo = await fetchSplMintInfo(args.client, mint);
|
|
4411
|
+
const amount = parseAmount(args.amountArg, mintInfo.decimals);
|
|
4412
|
+
const plan = await buildSplSendPlan({
|
|
4413
|
+
client: args.client,
|
|
4414
|
+
owner,
|
|
4415
|
+
recipient: args.to,
|
|
4416
|
+
mint,
|
|
4417
|
+
amount,
|
|
4418
|
+
decimals: mintInfo.decimals,
|
|
4419
|
+
tokenProgramAddress: mintInfo.tokenProgramAddress,
|
|
4420
|
+
failIfAssociatedTokenAccountMissing: Boolean(args.opts.failIfAssociatedTokenAccountMissing),
|
|
4421
|
+
fromTokenAccount: args.opts.fromTokenAccount ? solAddress2(args.opts.fromTokenAccount) : void 0,
|
|
4422
|
+
recipientTokenAccount: args.opts.recipientTokenAccount ? solAddress2(args.opts.recipientTokenAccount) : void 0
|
|
4423
|
+
});
|
|
4424
|
+
if (args.opts.dryRun) {
|
|
4425
|
+
printSplDryRun({
|
|
4426
|
+
from: args.signer.address,
|
|
4427
|
+
to: args.toArg,
|
|
4428
|
+
amount: args.amountArg,
|
|
4429
|
+
network: args.network,
|
|
4430
|
+
sponsored: args.sponsored,
|
|
4431
|
+
plan
|
|
4432
|
+
});
|
|
4433
|
+
return;
|
|
4434
|
+
}
|
|
4435
|
+
const result = await submitSolanaInstructions({
|
|
4436
|
+
client: args.client,
|
|
4437
|
+
signer: args.signer,
|
|
4438
|
+
instructions: plan.instructions,
|
|
4439
|
+
sponsored: args.sponsored,
|
|
4440
|
+
feePolicyId: args.feePolicyId
|
|
4441
|
+
});
|
|
4442
|
+
const confirmed = await withSpinner(
|
|
4443
|
+
"Waiting for confirmation\u2026",
|
|
4444
|
+
"Confirmation status received",
|
|
4445
|
+
() => waitForSolanaConfirmation(args.client, result.signature)
|
|
4190
4446
|
);
|
|
4447
|
+
printSplResult({
|
|
4448
|
+
result,
|
|
4449
|
+
to: args.toArg,
|
|
4450
|
+
amount: args.amountArg,
|
|
4451
|
+
network: args.network,
|
|
4452
|
+
sponsored: args.sponsored,
|
|
4453
|
+
plan,
|
|
4454
|
+
status: confirmed ? "confirmed" : "pending"
|
|
4455
|
+
});
|
|
4456
|
+
}
|
|
4457
|
+
async function buildSplSendPlan(args) {
|
|
4458
|
+
const sourceTokenAccount = args.fromTokenAccount ?? await deriveAssociatedTokenAccount({
|
|
4459
|
+
owner: args.owner,
|
|
4460
|
+
mint: args.mint,
|
|
4461
|
+
tokenProgramAddress: args.tokenProgramAddress
|
|
4462
|
+
});
|
|
4463
|
+
const sourceAccount = await fetchSplTokenAccountInfo(args.client, sourceTokenAccount);
|
|
4464
|
+
if (!sourceAccount) {
|
|
4465
|
+
throw errInvalidArgs(
|
|
4466
|
+
`Source token account ${sourceTokenAccount} was not found. Fund the sender's associated token account for mint ${args.mint} and retry.`
|
|
4467
|
+
);
|
|
4468
|
+
}
|
|
4469
|
+
assertTokenAccountMatches({
|
|
4470
|
+
account: sourceAccount,
|
|
4471
|
+
expectedMint: args.mint,
|
|
4472
|
+
expectedOwner: args.owner,
|
|
4473
|
+
label: "Source token account"
|
|
4474
|
+
});
|
|
4475
|
+
const destinationTokenAccount = args.recipientTokenAccount ?? await deriveAssociatedTokenAccount({
|
|
4476
|
+
owner: args.recipient,
|
|
4477
|
+
mint: args.mint,
|
|
4478
|
+
tokenProgramAddress: args.tokenProgramAddress
|
|
4479
|
+
});
|
|
4480
|
+
const destinationAccount = await fetchSplTokenAccountInfo(args.client, destinationTokenAccount);
|
|
4481
|
+
const createDestinationTokenAccount = !destinationAccount && !args.recipientTokenAccount && !args.failIfAssociatedTokenAccountMissing;
|
|
4482
|
+
if (!destinationAccount && args.recipientTokenAccount) {
|
|
4483
|
+
throw errInvalidArgs(`Recipient token account ${destinationTokenAccount} was not found.`);
|
|
4484
|
+
}
|
|
4485
|
+
if (!destinationAccount && args.failIfAssociatedTokenAccountMissing) {
|
|
4486
|
+
throw errInvalidArgs(
|
|
4487
|
+
`Recipient associated token account ${destinationTokenAccount} does not exist. Omit --fail-if-associated-token-account-missing to create it automatically.`
|
|
4488
|
+
);
|
|
4489
|
+
}
|
|
4490
|
+
if (destinationAccount) {
|
|
4491
|
+
assertTokenAccountMatches({
|
|
4492
|
+
account: destinationAccount,
|
|
4493
|
+
expectedMint: args.mint,
|
|
4494
|
+
...args.recipientTokenAccount ? {} : { expectedOwner: args.recipient },
|
|
4495
|
+
label: "Recipient token account"
|
|
4496
|
+
});
|
|
4497
|
+
}
|
|
4498
|
+
const instructions = [];
|
|
4499
|
+
if (createDestinationTokenAccount) {
|
|
4500
|
+
instructions.push(buildCreateAssociatedTokenAccountIdempotentInstruction({
|
|
4501
|
+
payer: { address: args.owner },
|
|
4502
|
+
associatedTokenAccount: destinationTokenAccount,
|
|
4503
|
+
owner: args.recipient,
|
|
4504
|
+
mint: args.mint,
|
|
4505
|
+
tokenProgramAddress: args.tokenProgramAddress
|
|
4506
|
+
}));
|
|
4507
|
+
}
|
|
4508
|
+
instructions.push(buildSplTokenTransferCheckedInstruction({
|
|
4509
|
+
sourceTokenAccount,
|
|
4510
|
+
mint: args.mint,
|
|
4511
|
+
destinationTokenAccount,
|
|
4512
|
+
owner: { address: args.owner },
|
|
4513
|
+
amount: args.amount,
|
|
4514
|
+
decimals: args.decimals,
|
|
4515
|
+
tokenProgramAddress: args.tokenProgramAddress
|
|
4516
|
+
}));
|
|
4517
|
+
return {
|
|
4518
|
+
instructions,
|
|
4519
|
+
mint: args.mint,
|
|
4520
|
+
tokenProgramAddress: args.tokenProgramAddress,
|
|
4521
|
+
sourceTokenAccount,
|
|
4522
|
+
destinationTokenAccount,
|
|
4523
|
+
createdDestinationTokenAccount: createDestinationTokenAccount,
|
|
4524
|
+
decimals: args.decimals
|
|
4525
|
+
};
|
|
4526
|
+
}
|
|
4527
|
+
async function submitSolanaInstructions(args) {
|
|
4528
|
+
const result = await withSpinner(
|
|
4529
|
+
"Sending transaction\u2026",
|
|
4530
|
+
"Transaction submitted",
|
|
4531
|
+
async () => {
|
|
4532
|
+
if (args.signer.type === "session") {
|
|
4533
|
+
return await buildAndSendSolanaTransactionWithSession({
|
|
4534
|
+
client: args.client,
|
|
4535
|
+
instructions: args.instructions,
|
|
4536
|
+
session: args.signer.session,
|
|
4537
|
+
authToken: args.signer.authToken,
|
|
4538
|
+
sponsored: args.sponsored,
|
|
4539
|
+
gasPolicyId: args.feePolicyId
|
|
4540
|
+
});
|
|
4541
|
+
}
|
|
4542
|
+
return await buildAndSendSolanaTransaction({
|
|
4543
|
+
client: args.client,
|
|
4544
|
+
instructions: args.instructions,
|
|
4545
|
+
senderKeyBytes: args.signer.keyBytes,
|
|
4546
|
+
sponsored: args.sponsored,
|
|
4547
|
+
gasPolicyId: args.feePolicyId
|
|
4548
|
+
});
|
|
4549
|
+
}
|
|
4550
|
+
);
|
|
4551
|
+
return result;
|
|
4552
|
+
}
|
|
4553
|
+
function printNativeDryRun(args) {
|
|
4191
4554
|
if (isJSONMode()) {
|
|
4192
4555
|
printJSON({
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4556
|
+
dryRun: true,
|
|
4557
|
+
action: "solana-send",
|
|
4558
|
+
from: args.from,
|
|
4559
|
+
to: args.to,
|
|
4560
|
+
amount: args.amount,
|
|
4561
|
+
token: args.symbol,
|
|
4562
|
+
network: args.network,
|
|
4563
|
+
sponsored: args.sponsored,
|
|
4564
|
+
instructions: ["transferSol"]
|
|
4201
4565
|
});
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
]
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4568
|
+
const pairs = [
|
|
4569
|
+
["Dry Run", "yes"],
|
|
4570
|
+
["From", args.from],
|
|
4571
|
+
["To", args.to],
|
|
4572
|
+
["Amount", green(`${args.amount} ${args.symbol}`)],
|
|
4573
|
+
["Network", args.network],
|
|
4574
|
+
["Instructions", "transferSol"]
|
|
4575
|
+
];
|
|
4576
|
+
if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
|
|
4577
|
+
printKeyValue(pairs);
|
|
4578
|
+
}
|
|
4579
|
+
function printNativeResult(args) {
|
|
4580
|
+
if (isJSONMode()) {
|
|
4581
|
+
printJSON({
|
|
4582
|
+
from: args.result.fromAddress,
|
|
4583
|
+
to: args.to,
|
|
4584
|
+
amount: args.amount,
|
|
4585
|
+
token: args.symbol,
|
|
4586
|
+
network: args.network,
|
|
4587
|
+
sponsored: args.sponsored,
|
|
4588
|
+
signature: args.result.signature,
|
|
4589
|
+
status: args.status
|
|
4590
|
+
});
|
|
4591
|
+
return;
|
|
4215
4592
|
}
|
|
4593
|
+
const pairs = [
|
|
4594
|
+
["From", args.result.fromAddress],
|
|
4595
|
+
["To", args.to],
|
|
4596
|
+
["Amount", green(`${args.amount} ${args.symbol}`)],
|
|
4597
|
+
["Network", args.network]
|
|
4598
|
+
];
|
|
4599
|
+
if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
|
|
4600
|
+
pairs.push(["Signature", args.result.signature]);
|
|
4601
|
+
pairs.push(["Status", args.status === "confirmed" ? `${successBadge()} ${green("Confirmed")}` : "Pending"]);
|
|
4602
|
+
printKeyValue(pairs);
|
|
4603
|
+
}
|
|
4604
|
+
function printSplDryRun(args) {
|
|
4605
|
+
const instructions = args.plan.createdDestinationTokenAccount ? ["createAssociatedTokenAccountIdempotent", "transferChecked"] : ["transferChecked"];
|
|
4606
|
+
if (isJSONMode()) {
|
|
4607
|
+
printJSON({
|
|
4608
|
+
dryRun: true,
|
|
4609
|
+
action: "solana-send",
|
|
4610
|
+
from: args.from,
|
|
4611
|
+
to: args.to,
|
|
4612
|
+
amount: args.amount,
|
|
4613
|
+
token: args.plan.mint,
|
|
4614
|
+
tokenAddress: args.plan.mint,
|
|
4615
|
+
network: args.network,
|
|
4616
|
+
sponsored: args.sponsored,
|
|
4617
|
+
tokenProgramAddress: args.plan.tokenProgramAddress,
|
|
4618
|
+
sourceTokenAccount: args.plan.sourceTokenAccount,
|
|
4619
|
+
destinationTokenAccount: args.plan.destinationTokenAccount,
|
|
4620
|
+
createdDestinationTokenAccount: args.plan.createdDestinationTokenAccount,
|
|
4621
|
+
instructions
|
|
4622
|
+
});
|
|
4623
|
+
return;
|
|
4624
|
+
}
|
|
4625
|
+
const pairs = [
|
|
4626
|
+
["Dry Run", "yes"],
|
|
4627
|
+
["From", args.from],
|
|
4628
|
+
["To", args.to],
|
|
4629
|
+
["Amount", green(`${args.amount} ${args.plan.mint}`)],
|
|
4630
|
+
["Mint", args.plan.mint],
|
|
4631
|
+
["Source Token Account", args.plan.sourceTokenAccount],
|
|
4632
|
+
["Destination Token Account", args.plan.destinationTokenAccount],
|
|
4633
|
+
["Created Destination Token Account", args.plan.createdDestinationTokenAccount ? "yes" : "no"],
|
|
4634
|
+
["Network", args.network],
|
|
4635
|
+
["Instructions", instructions.join(", ")]
|
|
4636
|
+
];
|
|
4637
|
+
if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
|
|
4638
|
+
printKeyValue(pairs);
|
|
4639
|
+
}
|
|
4640
|
+
function printSplResult(args) {
|
|
4641
|
+
if (isJSONMode()) {
|
|
4642
|
+
printJSON({
|
|
4643
|
+
from: args.result.fromAddress,
|
|
4644
|
+
to: args.to,
|
|
4645
|
+
amount: args.amount,
|
|
4646
|
+
token: args.plan.mint,
|
|
4647
|
+
tokenAddress: args.plan.mint,
|
|
4648
|
+
network: args.network,
|
|
4649
|
+
sponsored: args.sponsored,
|
|
4650
|
+
tokenProgramAddress: args.plan.tokenProgramAddress,
|
|
4651
|
+
sourceTokenAccount: args.plan.sourceTokenAccount,
|
|
4652
|
+
destinationTokenAccount: args.plan.destinationTokenAccount,
|
|
4653
|
+
createdDestinationTokenAccount: args.plan.createdDestinationTokenAccount,
|
|
4654
|
+
signature: args.result.signature,
|
|
4655
|
+
status: args.status
|
|
4656
|
+
});
|
|
4657
|
+
return;
|
|
4658
|
+
}
|
|
4659
|
+
const pairs = [
|
|
4660
|
+
["From", args.result.fromAddress],
|
|
4661
|
+
["To", args.to],
|
|
4662
|
+
["Amount", green(`${args.amount} ${args.plan.mint}`)],
|
|
4663
|
+
["Mint", args.plan.mint],
|
|
4664
|
+
["Source Token Account", args.plan.sourceTokenAccount],
|
|
4665
|
+
["Destination Token Account", args.plan.destinationTokenAccount],
|
|
4666
|
+
["Created Destination Token Account", args.plan.createdDestinationTokenAccount ? "yes" : "no"],
|
|
4667
|
+
["Network", args.network]
|
|
4668
|
+
];
|
|
4669
|
+
if (args.sponsored) pairs.push(["Fee", green("Sponsored")]);
|
|
4670
|
+
pairs.push(["Signature", args.result.signature]);
|
|
4671
|
+
pairs.push(["Status", args.status === "confirmed" ? `${successBadge()} ${green("Confirmed")}` : "Pending"]);
|
|
4672
|
+
printKeyValue(pairs);
|
|
4216
4673
|
}
|
|
4217
4674
|
async function resolveSolanaSigner(program2, signer) {
|
|
4218
4675
|
if (signer === "session") {
|
|
@@ -4466,9 +4923,9 @@ async function debugRecoveredTypedDataAddress(args) {
|
|
|
4466
4923
|
}
|
|
4467
4924
|
function createDelegatedAccount(args) {
|
|
4468
4925
|
const sessionBinding = buildSessionBindingInput(args.session);
|
|
4469
|
-
const
|
|
4926
|
+
const address3 = args.session.evmAddress;
|
|
4470
4927
|
return toAccount({
|
|
4471
|
-
address:
|
|
4928
|
+
address: address3,
|
|
4472
4929
|
async signTransaction() {
|
|
4473
4930
|
throw errInvalidArgs(
|
|
4474
4931
|
"Delegated signer does not support direct transaction signing. Use wallet commands that execute through Alchemy smart wallets."
|
|
@@ -4499,7 +4956,7 @@ function createDelegatedAccount(args) {
|
|
|
4499
4956
|
message: remoteMessage.message,
|
|
4500
4957
|
encoding: remoteMessage.encoding,
|
|
4501
4958
|
signature: signedMessage,
|
|
4502
|
-
expectedAddress:
|
|
4959
|
+
expectedAddress: address3
|
|
4503
4960
|
});
|
|
4504
4961
|
return signedMessage;
|
|
4505
4962
|
},
|
|
@@ -4535,7 +4992,7 @@ function createDelegatedAccount(args) {
|
|
|
4535
4992
|
await debugRecoveredTypedDataAddress({
|
|
4536
4993
|
typedData: serializedTypedData,
|
|
4537
4994
|
signature: typedDataSignature,
|
|
4538
|
-
expectedAddress:
|
|
4995
|
+
expectedAddress: address3
|
|
4539
4996
|
});
|
|
4540
4997
|
return typedDataSignature;
|
|
4541
4998
|
},
|
|
@@ -4581,7 +5038,7 @@ function createAlchemyWalletTransport(apiKey) {
|
|
|
4581
5038
|
}
|
|
4582
5039
|
|
|
4583
5040
|
// src/lib/smart-wallet.ts
|
|
4584
|
-
async function ensureGasPolicyResolved(program2) {
|
|
5041
|
+
async function ensureGasPolicyResolved(program2, options = {}) {
|
|
4585
5042
|
const cfg = load();
|
|
4586
5043
|
if (!resolveGasSponsored(program2, cfg)) return void 0;
|
|
4587
5044
|
const existing = resolveGasPolicyId(program2, cfg);
|
|
@@ -4592,7 +5049,7 @@ async function ensureGasPolicyResolved(program2) {
|
|
|
4592
5049
|
if (!hasAuthLoginToken(cfg)) {
|
|
4593
5050
|
throw errNotLoggedInForPolicyLookup();
|
|
4594
5051
|
}
|
|
4595
|
-
const network = resolveNetwork(program2
|
|
5052
|
+
const network = options.networkOverride ?? resolveNetwork(program2);
|
|
4596
5053
|
const policyId = await selectOrCreatePolicy({
|
|
4597
5054
|
flavor: "sponsorship",
|
|
4598
5055
|
network,
|
|
@@ -4647,7 +5104,7 @@ function buildWalletClient(program2, options = {}) {
|
|
|
4647
5104
|
const apiKey = resolveAPIKey(program2);
|
|
4648
5105
|
if (!apiKey) throw errAuthRequired();
|
|
4649
5106
|
const cfg = load();
|
|
4650
|
-
const network = resolveNetwork(program2
|
|
5107
|
+
const network = options.networkOverride ?? resolveNetwork(program2);
|
|
4651
5108
|
const chain = networkToChain(network);
|
|
4652
5109
|
const gasSponsored = resolveGasSponsored(program2, cfg);
|
|
4653
5110
|
const gasPolicyId = options.gasPolicyIdOverride ?? resolveGasPolicyId(program2, cfg);
|
|
@@ -4689,7 +5146,9 @@ function buildWalletClient(program2, options = {}) {
|
|
|
4689
5146
|
address: evmSession.evmAddress
|
|
4690
5147
|
};
|
|
4691
5148
|
}
|
|
4692
|
-
if (!localKey)
|
|
5149
|
+
if (!localKey) {
|
|
5150
|
+
throw pref === "local" ? errWalletKeyRequired() : errWalletRequired();
|
|
5151
|
+
}
|
|
4693
5152
|
const account = privateKeyToAccount2(normalizeKey(localKey));
|
|
4694
5153
|
return {
|
|
4695
5154
|
signer: account,
|
|
@@ -4782,10 +5241,10 @@ function registerStatus(program2, options = {}) {
|
|
|
4782
5241
|
"after",
|
|
4783
5242
|
`
|
|
4784
5243
|
Examples:
|
|
4785
|
-
alchemy evm status call-123
|
|
5244
|
+
alchemy evm status call-123 -n eth-mainnet EVM smart wallet operation
|
|
4786
5245
|
alchemy evm status 0xTxHash... -n eth-mainnet Raw EVM transaction
|
|
4787
5246
|
alchemy solana status 5wHu1qwD7q... -n solana-devnet Solana transaction
|
|
4788
|
-
echo "call-123" | alchemy evm status
|
|
5247
|
+
echo "call-123" | alchemy evm status -n eth-mainnet
|
|
4789
5248
|
|
|
4790
5249
|
Tip: use an EVM network for operation IDs and tx hashes, or a Solana network for signatures.`
|
|
4791
5250
|
).action(async (idArg) => {
|
|
@@ -5773,10 +6232,8 @@ var ERROR_RECOVERY = {
|
|
|
5773
6232
|
AUTH_REQUIRED: "Set ALCHEMY_API_KEY env var or run: alchemy config set app",
|
|
5774
6233
|
INVALID_API_KEY: "Check your API key and select a valid app: alchemy config set app",
|
|
5775
6234
|
NETWORK_NOT_ENABLED: "Enable the target network for your app at dashboard.alchemy.com",
|
|
5776
|
-
INVALID_ACCESS_KEY: "Check your access key: https://dashboard.alchemy.com/",
|
|
5777
|
-
ACCESS_KEY_REQUIRED: "Set ALCHEMY_ACCESS_KEY env var or run: alchemy config set access-key <key>",
|
|
5778
6235
|
APP_REQUIRED: "Select an app: alchemy config set app <app-id>",
|
|
5779
|
-
ADMIN_API_ERROR: "Check the error message for details; verify
|
|
6236
|
+
ADMIN_API_ERROR: "Check the error message for details; verify account permissions",
|
|
5780
6237
|
NETWORK_ERROR: "Check internet connection and retry",
|
|
5781
6238
|
RPC_ERROR: "Check RPC method, params, and network; verify API key has access",
|
|
5782
6239
|
INVALID_ARGS: "Check command usage via: alchemy --json help <command>",
|
|
@@ -5892,11 +6349,10 @@ function buildAgentPrompt(program2) {
|
|
|
5892
6349
|
]
|
|
5893
6350
|
},
|
|
5894
6351
|
{
|
|
5895
|
-
method: "
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
notes: "No command-line flag exists for access-key auth; use the env var or saved config."
|
|
6352
|
+
method: "Alchemy login",
|
|
6353
|
+
setup: "alchemy auth login",
|
|
6354
|
+
commandFamilies: ["app", "evm network", "gas-manager"],
|
|
6355
|
+
notes: "Admin surfaces use the browser login session stored by `alchemy auth login`."
|
|
5900
6356
|
},
|
|
5901
6357
|
{
|
|
5902
6358
|
method: "Webhook API key",
|
|
@@ -5953,15 +6409,15 @@ function buildAgentPrompt(program2) {
|
|
|
5953
6409
|
examples: [
|
|
5954
6410
|
"alchemy --json --no-interactive config status",
|
|
5955
6411
|
"alchemy --json --no-interactive update-check",
|
|
5956
|
-
"alchemy --json --no-interactive evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY",
|
|
5957
|
-
"
|
|
5958
|
-
"alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY",
|
|
6412
|
+
"alchemy --json --no-interactive evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY -n eth-mainnet",
|
|
6413
|
+
"alchemy --json --no-interactive app list",
|
|
6414
|
+
"alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY -n eth-mainnet",
|
|
5959
6415
|
"alchemy --json --no-interactive evm network list",
|
|
5960
6416
|
"alchemy --json --no-interactive evm send 0xRecipient 0.001 --dry-run -n eth-sepolia",
|
|
5961
6417
|
`alchemy --json --no-interactive evm contract read 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 "balanceOf(address)(uint256)" --args '["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]' -n eth-mainnet`,
|
|
5962
6418
|
"alchemy --json --no-interactive evm swap quote --from 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --amount 1.0 --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet",
|
|
5963
|
-
"alchemy --json --no-interactive evm logs --from-block latest --limit 25",
|
|
5964
|
-
"alchemy --json --no-interactive evm block latest --summary",
|
|
6419
|
+
"alchemy --json --no-interactive evm logs --from-block latest --limit 25 -n eth-mainnet",
|
|
6420
|
+
"alchemy --json --no-interactive evm block latest --summary -n eth-mainnet",
|
|
5965
6421
|
"alchemy --json --no-interactive evm status 0xCallId -n eth-mainnet"
|
|
5966
6422
|
],
|
|
5967
6423
|
docs: "https://www.alchemy.com/docs"
|
|
@@ -5997,7 +6453,9 @@ function formatAsSystemPrompt(payload) {
|
|
|
5997
6453
|
lines.push("Auth methods:");
|
|
5998
6454
|
for (const auth of payload.auth) {
|
|
5999
6455
|
lines.push(` ${auth.method}:`);
|
|
6000
|
-
|
|
6456
|
+
if (auth.envVar) {
|
|
6457
|
+
lines.push(` env: ${auth.envVar}`);
|
|
6458
|
+
}
|
|
6001
6459
|
if (auth.flag) {
|
|
6002
6460
|
lines.push(` flag: ${auth.flag}`);
|
|
6003
6461
|
}
|
|
@@ -6174,8 +6632,8 @@ function registrySymbolSuggestions(network) {
|
|
|
6174
6632
|
}
|
|
6175
6633
|
|
|
6176
6634
|
// src/commands/approve.ts
|
|
6177
|
-
function isNativeToken(
|
|
6178
|
-
return
|
|
6635
|
+
function isNativeToken(address3) {
|
|
6636
|
+
return address3.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
|
|
6179
6637
|
}
|
|
6180
6638
|
function buildApprovalRequest(opts, tokenMeta) {
|
|
6181
6639
|
validateApprovalMode(opts);
|
|
@@ -6281,11 +6739,11 @@ function registerApprove(program2) {
|
|
|
6281
6739
|
"after",
|
|
6282
6740
|
`
|
|
6283
6741
|
Examples:
|
|
6284
|
-
alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100
|
|
6285
|
-
alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100 --reset-first
|
|
6286
|
-
alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited
|
|
6287
|
-
alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited --yes
|
|
6288
|
-
alchemy evm approve 0xRouter --token-address 0xUSDC --revoke`
|
|
6742
|
+
alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100 -n eth-mainnet
|
|
6743
|
+
alchemy evm approve 0xRouter --token-address 0xUSDC --amount 100 --reset-first -n eth-mainnet
|
|
6744
|
+
alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited -n eth-mainnet
|
|
6745
|
+
alchemy evm approve 0xRouter --token-address 0xUSDC --unlimited --yes -n eth-mainnet
|
|
6746
|
+
alchemy evm approve 0xRouter --token-address 0xUSDC --revoke -n eth-mainnet`
|
|
6289
6747
|
).action(async (spenderArg, _opts, cmd) => {
|
|
6290
6748
|
try {
|
|
6291
6749
|
await performApprove(cmd, spenderArg, cmd.opts());
|
|
@@ -6307,8 +6765,8 @@ async function performApprove(program2, spenderArg, opts) {
|
|
|
6307
6765
|
signer,
|
|
6308
6766
|
...gasPolicyIdOverride && { gasPolicyIdOverride }
|
|
6309
6767
|
});
|
|
6310
|
-
const rpcClient =
|
|
6311
|
-
const tokenMeta = await fetchTokenDecimals(program2, opts.tokenAddress);
|
|
6768
|
+
const rpcClient = apiKeyClientFromFlags(program2, { forceNetwork: network });
|
|
6769
|
+
const tokenMeta = await fetchTokenDecimals(program2, opts.tokenAddress, { network });
|
|
6312
6770
|
const approval = buildApprovalRequest(opts, tokenMeta);
|
|
6313
6771
|
if (!await confirmUnlimitedApproval(program2, tokenMeta.symbol, spenderArg, opts)) {
|
|
6314
6772
|
return;
|
|
@@ -6424,9 +6882,9 @@ function registerBlock(program2) {
|
|
|
6424
6882
|
"after",
|
|
6425
6883
|
`
|
|
6426
6884
|
Examples:
|
|
6427
|
-
alchemy evm block latest
|
|
6428
|
-
alchemy evm block 17000000
|
|
6429
|
-
alchemy evm block 0x1`
|
|
6885
|
+
alchemy evm block latest -n eth-mainnet
|
|
6886
|
+
alchemy evm block 17000000 -n eth-mainnet
|
|
6887
|
+
alchemy evm block 0x1 -n eth-mainnet`
|
|
6430
6888
|
).action(async (blockId, opts) => {
|
|
6431
6889
|
try {
|
|
6432
6890
|
let blockParam;
|
|
@@ -6703,11 +7161,11 @@ function registerContract(program2) {
|
|
|
6703
7161
|
"after",
|
|
6704
7162
|
`
|
|
6705
7163
|
Examples:
|
|
6706
|
-
alchemy evm contract read 0xA0b8...USDC "balanceOf(address)(uint256)" --args '["0xHolder"]'
|
|
6707
|
-
alchemy evm contract read 0xA0b8...USDC "name()(string)"
|
|
6708
|
-
alchemy evm contract read 0xA0b8...USDC "decimals()(uint8)"
|
|
6709
|
-
alchemy evm contract read 0xContract balanceOf --abi-file ./erc20.json --args '["0xHolder"]'
|
|
6710
|
-
alchemy evm contract read 0xPool "quote((address,uint256))(uint256)" --args '[["0xToken", "1000000"]]' --block 12345678`
|
|
7164
|
+
alchemy evm contract read 0xA0b8...USDC "balanceOf(address)(uint256)" --args '["0xHolder"]' -n eth-mainnet
|
|
7165
|
+
alchemy evm contract read 0xA0b8...USDC "name()(string)" -n eth-mainnet
|
|
7166
|
+
alchemy evm contract read 0xA0b8...USDC "decimals()(uint8)" -n eth-mainnet
|
|
7167
|
+
alchemy evm contract read 0xContract balanceOf --abi-file ./erc20.json --args '["0xHolder"]' -n eth-mainnet
|
|
7168
|
+
alchemy evm contract read 0xPool "quote((address,uint256))(uint256)" --args '[["0xToken", "1000000"]]' --block 12345678 -n eth-mainnet`
|
|
6711
7169
|
).action(async (addressArg, functionArg, opts) => {
|
|
6712
7170
|
try {
|
|
6713
7171
|
await performContractRead(program2, addressArg, functionArg, opts);
|
|
@@ -6721,9 +7179,9 @@ Examples:
|
|
|
6721
7179
|
"after",
|
|
6722
7180
|
`
|
|
6723
7181
|
Examples:
|
|
6724
|
-
alchemy evm contract call 0xToken "approve(address,uint256)" --args '["0xSpender", "1000000"]'
|
|
6725
|
-
alchemy evm contract call 0xToken "transfer(address,uint256)" --args '["0xTo", "1000000"]'
|
|
6726
|
-
alchemy evm contract call 0xContract deposit --abi-file ./contract.json --value 0.1`
|
|
7182
|
+
alchemy evm contract call 0xToken "approve(address,uint256)" --args '["0xSpender", "1000000"]' -n eth-mainnet
|
|
7183
|
+
alchemy evm contract call 0xToken "transfer(address,uint256)" --args '["0xTo", "1000000"]' -n eth-mainnet
|
|
7184
|
+
alchemy evm contract call 0xContract deposit --abi-file ./contract.json --value 0.1 -n eth-mainnet`
|
|
6727
7185
|
).action(async (addressArg, functionArg, _opts, cmd2) => {
|
|
6728
7186
|
try {
|
|
6729
7187
|
const opts = cmd2.opts();
|
|
@@ -6787,7 +7245,7 @@ async function performContractCall(program2, addressArg, functionArg, opts) {
|
|
|
6787
7245
|
signer,
|
|
6788
7246
|
...gasPolicyIdOverride && { gasPolicyIdOverride }
|
|
6789
7247
|
});
|
|
6790
|
-
const rpcClient =
|
|
7248
|
+
const rpcClient = apiKeyClientFromFlags(program2, { forceNetwork: network });
|
|
6791
7249
|
const contractAddress = await resolveAddress(addressArg, rpcClient);
|
|
6792
7250
|
const data = encodeFunctionData2({ abi, functionName, args });
|
|
6793
7251
|
const value = opts.value !== void 0 ? parseEthValue(opts.value) : void 0;
|
|
@@ -6847,18 +7305,18 @@ async function performContractCall(program2, addressArg, functionArg, opts) {
|
|
|
6847
7305
|
// src/commands/balance.ts
|
|
6848
7306
|
async function fetchBalance(program2, addressInput, blockParam) {
|
|
6849
7307
|
const client = clientFromFlags(program2);
|
|
6850
|
-
const
|
|
7308
|
+
const address3 = await resolveAddress(addressInput, client);
|
|
6851
7309
|
const result = await withSpinner(
|
|
6852
7310
|
"Fetching balance\u2026",
|
|
6853
7311
|
"Balance fetched",
|
|
6854
|
-
() => client.call("eth_getBalance", [
|
|
7312
|
+
() => client.call("eth_getBalance", [address3, blockParam])
|
|
6855
7313
|
);
|
|
6856
7314
|
const wei = BigInt(result);
|
|
6857
7315
|
const network = resolveNetwork(program2);
|
|
6858
7316
|
const symbol = nativeTokenSymbol(network);
|
|
6859
7317
|
if (isJSONMode()) {
|
|
6860
7318
|
printJSON({
|
|
6861
|
-
address:
|
|
7319
|
+
address: address3,
|
|
6862
7320
|
wei: wei.toString(),
|
|
6863
7321
|
balance: weiToEth(wei),
|
|
6864
7322
|
symbol,
|
|
@@ -6866,7 +7324,7 @@ async function fetchBalance(program2, addressInput, blockParam) {
|
|
|
6866
7324
|
});
|
|
6867
7325
|
} else {
|
|
6868
7326
|
printKeyValue([
|
|
6869
|
-
["Address",
|
|
7327
|
+
["Address", address3],
|
|
6870
7328
|
["Balance", green(`${weiToEth(wei)} ${symbol}`)],
|
|
6871
7329
|
["Network", network]
|
|
6872
7330
|
]);
|
|
@@ -6874,7 +7332,7 @@ async function fetchBalance(program2, addressInput, blockParam) {
|
|
|
6874
7332
|
console.log("");
|
|
6875
7333
|
printJSON({
|
|
6876
7334
|
rpcMethod: "eth_getBalance",
|
|
6877
|
-
rpcParams: [
|
|
7335
|
+
rpcParams: [address3, blockParam],
|
|
6878
7336
|
rpcResult: result
|
|
6879
7337
|
});
|
|
6880
7338
|
}
|
|
@@ -6898,12 +7356,12 @@ function registerBalance(program2) {
|
|
|
6898
7356
|
"after",
|
|
6899
7357
|
`
|
|
6900
7358
|
Examples:
|
|
6901
|
-
alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
7359
|
+
alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
|
|
6902
7360
|
alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n polygon-mainnet
|
|
6903
|
-
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data balance
|
|
6904
|
-
alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --block 15537393
|
|
6905
|
-
alchemy evm data balance vitalik.eth
|
|
6906
|
-
cat addresses.txt | alchemy evm data balance`
|
|
7361
|
+
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data balance -n eth-mainnet
|
|
7362
|
+
alchemy evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --block 15537393 -n eth-mainnet
|
|
7363
|
+
alchemy evm data balance vitalik.eth -n eth-mainnet
|
|
7364
|
+
cat addresses.txt | alchemy evm data balance -n eth-mainnet`
|
|
6907
7365
|
).option("--block <block>", "Block number, hex, or tag (default: latest)").action(async (addressArg, opts) => {
|
|
6908
7366
|
try {
|
|
6909
7367
|
const blockParam = resolveBlockParam2(opts?.block);
|
|
@@ -6951,17 +7409,17 @@ function registerNFTs(program2) {
|
|
|
6951
7409
|
"after",
|
|
6952
7410
|
`
|
|
6953
7411
|
Examples:
|
|
6954
|
-
alchemy evm data nfts 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
6955
|
-
alchemy evm data nfts metadata --contract 0x... --token-id 1
|
|
6956
|
-
alchemy evm data nfts contract 0x...
|
|
6957
|
-
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data nfts`
|
|
7412
|
+
alchemy evm data nfts 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
|
|
7413
|
+
alchemy evm data nfts metadata --contract 0x... --token-id 1 -n eth-mainnet
|
|
7414
|
+
alchemy evm data nfts contract 0x... -n eth-mainnet
|
|
7415
|
+
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data nfts -n eth-mainnet`
|
|
6958
7416
|
).action(async (addressArg, opts) => {
|
|
6959
7417
|
try {
|
|
6960
7418
|
const addressInput = addressArg ?? await readStdinArg("address");
|
|
6961
7419
|
const client = clientFromFlags(program2);
|
|
6962
|
-
const
|
|
7420
|
+
const address3 = await resolveAddress(addressInput, client);
|
|
6963
7421
|
const params = {
|
|
6964
|
-
owner:
|
|
7422
|
+
owner: address3,
|
|
6965
7423
|
withMetadata: "true"
|
|
6966
7424
|
};
|
|
6967
7425
|
if (opts.limit) params.pageSize = String(opts.limit);
|
|
@@ -6995,7 +7453,7 @@ Examples:
|
|
|
6995
7453
|
break;
|
|
6996
7454
|
}
|
|
6997
7455
|
const nextParams = {
|
|
6998
|
-
owner:
|
|
7456
|
+
owner: address3,
|
|
6999
7457
|
withMetadata: "true",
|
|
7000
7458
|
pageKey
|
|
7001
7459
|
};
|
|
@@ -7037,15 +7495,15 @@ Examples:
|
|
|
7037
7495
|
exitWithError(err);
|
|
7038
7496
|
}
|
|
7039
7497
|
});
|
|
7040
|
-
cmd.command("contract <address>").description("Get NFT contract metadata").action(async (
|
|
7498
|
+
cmd.command("contract <address>").description("Get NFT contract metadata").action(async (address3) => {
|
|
7041
7499
|
try {
|
|
7042
|
-
validateAddress(
|
|
7500
|
+
validateAddress(address3);
|
|
7043
7501
|
const client = clientFromFlags(program2);
|
|
7044
7502
|
const result = await withSpinner(
|
|
7045
7503
|
"Fetching contract metadata\u2026",
|
|
7046
7504
|
"Contract metadata fetched",
|
|
7047
7505
|
() => client.callEnhanced("getContractMetadata", {
|
|
7048
|
-
contractAddress:
|
|
7506
|
+
contractAddress: address3
|
|
7049
7507
|
})
|
|
7050
7508
|
);
|
|
7051
7509
|
if (isJSONMode()) printJSON(result);
|
|
@@ -7175,15 +7633,15 @@ function registerTokens(program2) {
|
|
|
7175
7633
|
"after",
|
|
7176
7634
|
`
|
|
7177
7635
|
Examples:
|
|
7178
|
-
alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
7179
|
-
alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --metadata
|
|
7180
|
-
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data tokens balances`
|
|
7636
|
+
alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
|
|
7637
|
+
alchemy evm data tokens balances 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --metadata -n eth-mainnet
|
|
7638
|
+
echo 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 | alchemy evm data tokens balances -n eth-mainnet`
|
|
7181
7639
|
).action(async (addressArg, opts) => {
|
|
7182
7640
|
try {
|
|
7183
7641
|
const addressInput = addressArg ?? await readStdinArg("address");
|
|
7184
7642
|
const client = clientFromFlags(program2);
|
|
7185
|
-
const
|
|
7186
|
-
const params = [
|
|
7643
|
+
const address3 = await resolveAddress(addressInput, client);
|
|
7644
|
+
const params = [address3];
|
|
7187
7645
|
if (opts.pageKey) {
|
|
7188
7646
|
params.push("erc20", { pageKey: opts.pageKey });
|
|
7189
7647
|
}
|
|
@@ -7220,7 +7678,7 @@ Examples:
|
|
|
7220
7678
|
}
|
|
7221
7679
|
let totalShown = nonZero.length;
|
|
7222
7680
|
printKeyValue([
|
|
7223
|
-
["Address",
|
|
7681
|
+
["Address", address3],
|
|
7224
7682
|
["Network", client.network],
|
|
7225
7683
|
["Tokens", String(totalShown)]
|
|
7226
7684
|
]);
|
|
@@ -7246,7 +7704,7 @@ Examples:
|
|
|
7246
7704
|
const nextResult = await withSpinner(
|
|
7247
7705
|
"Fetching next page\u2026",
|
|
7248
7706
|
"Page fetched",
|
|
7249
|
-
() => client.call("alchemy_getTokenBalances", [
|
|
7707
|
+
() => client.call("alchemy_getTokenBalances", [address3, "erc20", { pageKey }])
|
|
7250
7708
|
);
|
|
7251
7709
|
if (isJSONMode()) {
|
|
7252
7710
|
printJSON(nextResult);
|
|
@@ -7282,7 +7740,7 @@ Examples:
|
|
|
7282
7740
|
"after",
|
|
7283
7741
|
`
|
|
7284
7742
|
Examples:
|
|
7285
|
-
alchemy evm data tokens metadata 0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eB48`
|
|
7743
|
+
alchemy evm data tokens metadata 0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eB48 -n eth-mainnet`
|
|
7286
7744
|
).action(async (contract) => {
|
|
7287
7745
|
try {
|
|
7288
7746
|
validateAddress(contract);
|
|
@@ -7353,20 +7811,20 @@ function registerTransfers(program2) {
|
|
|
7353
7811
|
`
|
|
7354
7812
|
Examples:
|
|
7355
7813
|
# Outgoing transfers from an address
|
|
7356
|
-
alchemy evm data history 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
7814
|
+
alchemy evm data history 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
|
|
7357
7815
|
|
|
7358
7816
|
# Incoming transfers to an address
|
|
7359
|
-
alchemy evm data history --to-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
7817
|
+
alchemy evm data history --to-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet
|
|
7360
7818
|
|
|
7361
7819
|
# Outgoing ERC-20 transfers only
|
|
7362
|
-
alchemy evm data history --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --category erc20
|
|
7820
|
+
alchemy evm data history --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --category erc20 -n eth-mainnet
|
|
7363
7821
|
|
|
7364
7822
|
# Transfers within a block range
|
|
7365
|
-
alchemy evm data history 0xd8dA... --from-block 0x100000 --to-block latest`
|
|
7823
|
+
alchemy evm data history 0xd8dA... --from-block 0x100000 --to-block latest -n eth-mainnet`
|
|
7366
7824
|
).action(async (addressArg, opts) => {
|
|
7367
7825
|
try {
|
|
7368
7826
|
const client = clientFromFlags(program2);
|
|
7369
|
-
const
|
|
7827
|
+
const address3 = addressArg ? await resolveAddress(addressArg, client) : void 0;
|
|
7370
7828
|
if (opts.fromAddress) opts.fromAddress = await resolveAddress(opts.fromAddress, client);
|
|
7371
7829
|
if (opts.toAddress) opts.toAddress = await resolveAddress(opts.toAddress, client);
|
|
7372
7830
|
const baseFilter = {
|
|
@@ -7382,8 +7840,8 @@ Examples:
|
|
|
7382
7840
|
}
|
|
7383
7841
|
if (opts.pageKey) baseFilter.pageKey = opts.pageKey;
|
|
7384
7842
|
const filter = { ...baseFilter };
|
|
7385
|
-
if (
|
|
7386
|
-
filter.fromAddress =
|
|
7843
|
+
if (address3 && !opts.fromAddress && !opts.toAddress) {
|
|
7844
|
+
filter.fromAddress = address3;
|
|
7387
7845
|
} else {
|
|
7388
7846
|
if (opts.fromAddress) filter.fromAddress = opts.fromAddress;
|
|
7389
7847
|
if (opts.toAddress) filter.toAddress = opts.toAddress;
|
|
@@ -7620,9 +8078,9 @@ function registerGas(program2) {
|
|
|
7620
8078
|
"after",
|
|
7621
8079
|
`
|
|
7622
8080
|
Examples:
|
|
7623
|
-
alchemy evm gas
|
|
8081
|
+
alchemy evm gas -n eth-mainnet
|
|
7624
8082
|
alchemy evm gas -n polygon-mainnet
|
|
7625
|
-
alchemy evm gas --json`
|
|
8083
|
+
alchemy evm gas --json -n eth-mainnet`
|
|
7626
8084
|
).action(async () => {
|
|
7627
8085
|
try {
|
|
7628
8086
|
const client = clientFromFlags(program2);
|
|
@@ -7734,10 +8192,10 @@ function registerLogs(program2) {
|
|
|
7734
8192
|
"after",
|
|
7735
8193
|
`
|
|
7736
8194
|
Examples:
|
|
7737
|
-
alchemy evm logs --from-block 18000000 --to-block 18000010
|
|
7738
|
-
alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --from-block 18000000 --to-block 18000010
|
|
7739
|
-
alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --topic 0xddf252ad...
|
|
7740
|
-
alchemy evm logs --from-block latest --json`
|
|
8195
|
+
alchemy evm logs --from-block 18000000 --to-block 18000010 -n eth-mainnet
|
|
8196
|
+
alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --from-block 18000000 --to-block 18000010 -n eth-mainnet
|
|
8197
|
+
alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --topic 0xddf252ad... -n eth-mainnet
|
|
8198
|
+
alchemy evm logs --from-block latest --json -n eth-mainnet`
|
|
7741
8199
|
).option("--address <address>", "Contract address to filter logs").option("--topic <topic...>", "Event topic(s) to filter (topic0, topic1, ...)").option("--from-block <block>", "Start block (number, hex, or tag)", "latest").option("--to-block <block>", "End block (number, hex, or tag)", "latest").option("--limit <n>", "Limit returned logs in output").action(async (opts) => {
|
|
7742
8200
|
try {
|
|
7743
8201
|
const outputLimit = parseOptionalInt(opts.limit, "--limit");
|
|
@@ -7826,10 +8284,10 @@ function registerReceipt(program2) {
|
|
|
7826
8284
|
"after",
|
|
7827
8285
|
`
|
|
7828
8286
|
Examples:
|
|
7829
|
-
alchemy evm receipt 0xabc123...
|
|
7830
|
-
echo 0xabc123... | alchemy evm receipt
|
|
8287
|
+
alchemy evm receipt 0xabc123... -n eth-mainnet
|
|
8288
|
+
echo 0xabc123... | alchemy evm receipt -n eth-mainnet
|
|
7831
8289
|
|
|
7832
|
-
Tip: use 'alchemy evm tx <hash>' for transaction details (value, block, nonce). Receipt provides execution results (status, gas used, logs).`
|
|
8290
|
+
Tip: use 'alchemy evm tx <hash> -n <net>' for transaction details (value, block, nonce). Receipt provides execution results (status, gas used, logs).`
|
|
7833
8291
|
).action(async (hashArg) => {
|
|
7834
8292
|
try {
|
|
7835
8293
|
const hash = hashArg ?? await readStdinArg("hash");
|
|
@@ -7893,9 +8351,9 @@ function registerRPC(program2) {
|
|
|
7893
8351
|
"after",
|
|
7894
8352
|
`
|
|
7895
8353
|
Examples:
|
|
7896
|
-
alchemy evm rpc eth_blockNumber
|
|
7897
|
-
alchemy evm rpc eth_getBalance "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" "latest"
|
|
7898
|
-
alchemy evm rpc eth_getBlockByNumber "0x1" true`
|
|
8354
|
+
alchemy evm rpc eth_blockNumber -n eth-mainnet
|
|
8355
|
+
alchemy evm rpc eth_getBalance "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" "latest" -n eth-mainnet
|
|
8356
|
+
alchemy evm rpc eth_getBlockByNumber "0x1" true -n eth-mainnet`
|
|
7899
8357
|
).action(async (method, params) => {
|
|
7900
8358
|
try {
|
|
7901
8359
|
if (outputRpcHelp(cmd, method, params)) {
|
|
@@ -7956,13 +8414,17 @@ function registerEvmSend(program2) {
|
|
|
7956
8414
|
sendCmd.option("--gas-sponsored", "Enable gas sponsorship (env: ALCHEMY_EVM_GAS_SPONSORED)").option("--gas-policy-id <id>", "Gas policy ID for sponsorship (env: ALCHEMY_EVM_GAS_POLICY_ID)").addHelpText(
|
|
7957
8415
|
"after",
|
|
7958
8416
|
`
|
|
8417
|
+
Omit --token to send the native token for the selected network.
|
|
8418
|
+
Network is required via -n/--network.
|
|
8419
|
+
|
|
7959
8420
|
Examples:
|
|
7960
|
-
alchemy evm send 0xAbC...123 1.5
|
|
7961
|
-
alchemy evm send vitalik.eth 0.1 -n base-mainnet
|
|
7962
|
-
alchemy evm send 0xAbC...123
|
|
7963
|
-
alchemy evm send 0xAbC...123
|
|
7964
|
-
alchemy evm send 0xAbC...123 1 --
|
|
7965
|
-
alchemy evm send 0xAbC...123 1
|
|
8421
|
+
alchemy evm send 0xAbC...123 1.5 -n eth-mainnet Send 1.5 ETH
|
|
8422
|
+
alchemy evm send vitalik.eth 0.1 -n base-mainnet Send 0.1 ETH on Base
|
|
8423
|
+
alchemy evm send 0xAbC...123 1 -n monad-testnet Send 1 MON
|
|
8424
|
+
alchemy evm send 0xAbC...123 100 --token 0xA0b8...USDC -n eth-mainnet Send 100 USDC
|
|
8425
|
+
alchemy evm send 0xAbC...123 1.5 --dry-run -n eth-mainnet Preview without signing or sending
|
|
8426
|
+
alchemy evm send 0xAbC...123 1 -n base-mainnet --gas-sponsored --gas-policy-id <id>
|
|
8427
|
+
alchemy evm send 0xAbC...123 1.5 --signer local -n eth-mainnet Force the local wallet`
|
|
7966
8428
|
).action(async (toArg, amountArg, _opts, cmd) => {
|
|
7967
8429
|
try {
|
|
7968
8430
|
const opts = cmd.opts();
|
|
@@ -7971,7 +8433,7 @@ Examples:
|
|
|
7971
8433
|
dryRun: opts.dryRun
|
|
7972
8434
|
});
|
|
7973
8435
|
} catch (err) {
|
|
7974
|
-
const { exitWithError: exitWithError2 } = await import("./errors-
|
|
8436
|
+
const { exitWithError: exitWithError2 } = await import("./errors-6BEPCY5N.js");
|
|
7975
8437
|
exitWithError2(err);
|
|
7976
8438
|
}
|
|
7977
8439
|
});
|
|
@@ -7980,17 +8442,25 @@ async function performEvmSend(program2, toArg, amountArg, tokenAddress, opts = {
|
|
|
7980
8442
|
if (tokenAddress) {
|
|
7981
8443
|
validateAddress(tokenAddress);
|
|
7982
8444
|
}
|
|
7983
|
-
const
|
|
7984
|
-
const
|
|
8445
|
+
const network = resolveRequiredNetwork(program2);
|
|
8446
|
+
const gasPolicyIdOverride = await ensureGasPolicyResolved(program2, {
|
|
8447
|
+
networkOverride: network
|
|
8448
|
+
});
|
|
8449
|
+
const { client, address: from, paymaster } = buildWalletClient(program2, {
|
|
7985
8450
|
signer: opts.signer,
|
|
8451
|
+
networkOverride: network,
|
|
7986
8452
|
...gasPolicyIdOverride && { gasPolicyIdOverride }
|
|
7987
8453
|
});
|
|
7988
|
-
const rpcClient =
|
|
8454
|
+
const rpcClient = apiKeyClientFromFlags(program2, {
|
|
8455
|
+
forceNetwork: network
|
|
8456
|
+
});
|
|
7989
8457
|
const to = await resolveAddress(toArg, rpcClient);
|
|
7990
8458
|
let decimals;
|
|
7991
8459
|
let symbol;
|
|
7992
8460
|
if (tokenAddress) {
|
|
7993
|
-
const meta = await fetchTokenDecimals(program2, tokenAddress
|
|
8461
|
+
const meta = await fetchTokenDecimals(program2, tokenAddress, {
|
|
8462
|
+
network
|
|
8463
|
+
});
|
|
7994
8464
|
decimals = meta.decimals;
|
|
7995
8465
|
symbol = meta.symbol;
|
|
7996
8466
|
} else {
|
|
@@ -8145,14 +8615,13 @@ import {
|
|
|
8145
8615
|
// src/lib/wallet-quote-client.ts
|
|
8146
8616
|
import { createClient } from "viem";
|
|
8147
8617
|
import { parseAccount } from "viem/accounts";
|
|
8148
|
-
function buildWalletQuoteClient(program2,
|
|
8618
|
+
function buildWalletQuoteClient(program2, address3) {
|
|
8149
8619
|
const apiKey = resolveAPIKey(program2);
|
|
8150
8620
|
if (!apiKey) throw errAuthRequired();
|
|
8151
|
-
const
|
|
8152
|
-
const network = resolveNetwork(program2, cfg);
|
|
8621
|
+
const network = resolveNetwork(program2);
|
|
8153
8622
|
const chain = networkToChain(network);
|
|
8154
8623
|
const client = createClient({
|
|
8155
|
-
account: parseAccount(
|
|
8624
|
+
account: parseAccount(address3),
|
|
8156
8625
|
transport: createAlchemyWalletTransport(apiKey),
|
|
8157
8626
|
chain,
|
|
8158
8627
|
name: "alchemyQuoteClient"
|
|
@@ -8164,7 +8633,7 @@ function buildWalletQuoteClient(program2, address2) {
|
|
|
8164
8633
|
client,
|
|
8165
8634
|
network,
|
|
8166
8635
|
chain,
|
|
8167
|
-
address:
|
|
8636
|
+
address: address3,
|
|
8168
8637
|
paymaster: void 0
|
|
8169
8638
|
};
|
|
8170
8639
|
}
|
|
@@ -8241,8 +8710,8 @@ function normalizeQuoteError(err, flow) {
|
|
|
8241
8710
|
|
|
8242
8711
|
// src/commands/swap.ts
|
|
8243
8712
|
var NATIVE_DECIMALS = 18;
|
|
8244
|
-
function isNativeToken2(
|
|
8245
|
-
return
|
|
8713
|
+
function isNativeToken2(address3) {
|
|
8714
|
+
return address3.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
|
|
8246
8715
|
}
|
|
8247
8716
|
function slippagePercentToBasisPoints(percent) {
|
|
8248
8717
|
return BigInt(Math.round(percent * 100));
|
|
@@ -8252,7 +8721,7 @@ async function resolveTokenInfo(network, program2, tokenAddress) {
|
|
|
8252
8721
|
return { decimals: NATIVE_DECIMALS, symbol: nativeTokenSymbol(network) };
|
|
8253
8722
|
}
|
|
8254
8723
|
try {
|
|
8255
|
-
return await fetchTokenDecimals(program2, tokenAddress);
|
|
8724
|
+
return await fetchTokenDecimals(program2, tokenAddress, { network });
|
|
8256
8725
|
} catch (err) {
|
|
8257
8726
|
if (err instanceof CLIError && err.code === "INVALID_ARGS") {
|
|
8258
8727
|
throw err;
|
|
@@ -8318,14 +8787,14 @@ function registerSwap(program2) {
|
|
|
8318
8787
|
quoteCmd.addHelpText(
|
|
8319
8788
|
"after",
|
|
8320
8789
|
`
|
|
8321
|
-
Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
|
|
8790
|
+
Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
|
|
8322
8791
|
(ETH, USDC, WETH, USDT, DAI, \u2026). Example:
|
|
8323
|
-
--from $(alchemy evm token ETH --address-only) \\
|
|
8792
|
+
--from $(alchemy evm token ETH --address-only -n eth-mainnet) \\
|
|
8324
8793
|
--to $(alchemy evm token USDC --address-only -n eth-mainnet)
|
|
8325
8794
|
|
|
8326
8795
|
Examples:
|
|
8327
8796
|
alchemy evm swap quote --from 0xEeee...EEeE --to 0xA0b8...USDC --amount 1.0 -n eth-mainnet
|
|
8328
|
-
alchemy evm swap quote --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0`
|
|
8797
|
+
alchemy evm swap quote --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0 -n eth-mainnet`
|
|
8329
8798
|
).action(async (opts) => {
|
|
8330
8799
|
try {
|
|
8331
8800
|
await performSwapQuote(program2, opts);
|
|
@@ -8338,14 +8807,14 @@ Examples:
|
|
|
8338
8807
|
executeCmd.option("--gas-sponsored", "Enable gas sponsorship (env: ALCHEMY_EVM_GAS_SPONSORED)").option("--gas-policy-id <id>", "Gas policy ID for sponsorship (env: ALCHEMY_EVM_GAS_POLICY_ID)").addHelpText(
|
|
8339
8808
|
"after",
|
|
8340
8809
|
`
|
|
8341
|
-
Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
|
|
8342
|
-
(ETH, USDC, WETH, USDT, DAI, \u2026). Run 'alchemy evm token list' to discover
|
|
8343
|
-
known symbols on
|
|
8810
|
+
Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
|
|
8811
|
+
(ETH, USDC, WETH, USDT, DAI, \u2026). Run 'alchemy evm token list -n <net>' to discover
|
|
8812
|
+
known symbols on that network.
|
|
8344
8813
|
|
|
8345
8814
|
Examples:
|
|
8346
8815
|
alchemy evm swap execute --from 0xEeee...EEeE --to 0xA0b8...USDC --amount 1.0 -n eth-mainnet
|
|
8347
|
-
alchemy evm swap execute --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0
|
|
8348
|
-
alchemy evm swap execute --from 0xEeee...EEeE --to 0xUSDC --amount 0.1 --gas-sponsored --gas-policy-id <id
|
|
8816
|
+
alchemy evm swap execute --from 0xUSDC --to 0xDAI --amount 100 --slippage 1.0 -n eth-mainnet
|
|
8817
|
+
alchemy evm swap execute --from 0xEeee...EEeE --to 0xUSDC --amount 0.1 --gas-sponsored --gas-policy-id <id> -n eth-mainnet`
|
|
8349
8818
|
).action(async (_opts, cmd2) => {
|
|
8350
8819
|
try {
|
|
8351
8820
|
const opts = cmd2.opts();
|
|
@@ -8528,20 +8997,20 @@ applicable. The native gas token (ETH, POL, BNB, AVAX, \u2026) resolves to the
|
|
|
8528
8997
|
EIP-7528 native sentinel 0xEeee\u2026EEeE.
|
|
8529
8998
|
|
|
8530
8999
|
Examples:
|
|
8531
|
-
alchemy evm token ETH
|
|
9000
|
+
alchemy evm token ETH -n eth-mainnet
|
|
8532
9001
|
alchemy evm token USDC -n base-mainnet
|
|
8533
9002
|
alchemy evm token USDC.e -n arb-mainnet
|
|
8534
|
-
alchemy evm token USDC --address-only
|
|
8535
|
-
alchemy --json evm token USDC | jq -r .address
|
|
9003
|
+
alchemy evm token USDC --address-only -n base-mainnet
|
|
9004
|
+
alchemy --json evm token USDC -n base-mainnet | jq -r .address
|
|
8536
9005
|
|
|
8537
|
-
alchemy evm token list
|
|
9006
|
+
alchemy evm token list -n eth-mainnet
|
|
8538
9007
|
alchemy evm token list --all # list tokens across all networks`
|
|
8539
9008
|
).action(async (symbol, opts) => {
|
|
8540
9009
|
try {
|
|
8541
9010
|
if (!symbol) {
|
|
8542
9011
|
if (opts.addressOnly) {
|
|
8543
9012
|
throw errInvalidArgs(
|
|
8544
|
-
"--address-only requires a symbol. Example: alchemy evm token USDC --address-only"
|
|
9013
|
+
"--address-only requires a symbol. Example: alchemy evm token USDC --address-only -n base-mainnet"
|
|
8545
9014
|
);
|
|
8546
9015
|
}
|
|
8547
9016
|
await runList(program2, {});
|
|
@@ -8616,7 +9085,7 @@ async function runList(program2, opts) {
|
|
|
8616
9085
|
}
|
|
8617
9086
|
console.log(
|
|
8618
9087
|
`
|
|
8619
|
-
${dim("Tip: pass -n/--network <net> to scope a lookup. Use 'alchemy evm token <SYMBOL>' to fetch a single address.")}`
|
|
9088
|
+
${dim("Tip: pass -n/--network <net> to scope a lookup. Use 'alchemy evm token <SYMBOL> -n <net>' to fetch a single address.")}`
|
|
8620
9089
|
);
|
|
8621
9090
|
return;
|
|
8622
9091
|
}
|
|
@@ -8637,7 +9106,7 @@ async function runList(program2, opts) {
|
|
|
8637
9106
|
printTokenTable(tokens);
|
|
8638
9107
|
console.log(
|
|
8639
9108
|
`
|
|
8640
|
-
${dim("Tip: 'alchemy evm token <SYMBOL>' resolves one symbol. Add --all to see every network.")}`
|
|
9109
|
+
${dim("Tip: 'alchemy evm token <SYMBOL> -n <net>' resolves one symbol. Add --all to see every network.")}`
|
|
8641
9110
|
);
|
|
8642
9111
|
}
|
|
8643
9112
|
function printTokenTable(tokens) {
|
|
@@ -8683,10 +9152,10 @@ function registerTx(program2) {
|
|
|
8683
9152
|
"after",
|
|
8684
9153
|
`
|
|
8685
9154
|
Examples:
|
|
8686
|
-
alchemy evm tx 0xabc123...
|
|
8687
|
-
echo 0xabc123... | alchemy evm tx
|
|
9155
|
+
alchemy evm tx 0xabc123... -n eth-mainnet
|
|
9156
|
+
echo 0xabc123... | alchemy evm tx -n eth-mainnet
|
|
8688
9157
|
|
|
8689
|
-
Tip: use 'alchemy evm receipt <hash>' to get the transaction receipt (status, gas used, logs).`
|
|
9158
|
+
Tip: use 'alchemy evm receipt <hash> -n <net>' to get the transaction receipt (status, gas used, logs).`
|
|
8690
9159
|
).action(async (hashArg) => {
|
|
8691
9160
|
try {
|
|
8692
9161
|
const hash = hashArg ?? await readStdinArg("hash");
|
|
@@ -8934,8 +9403,8 @@ import {
|
|
|
8934
9403
|
swapActions as swapActions2
|
|
8935
9404
|
} from "@alchemy/wallet-apis/experimental";
|
|
8936
9405
|
var NATIVE_DECIMALS2 = 18;
|
|
8937
|
-
function isNativeToken3(
|
|
8938
|
-
return
|
|
9406
|
+
function isNativeToken3(address3) {
|
|
9407
|
+
return address3.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
|
|
8939
9408
|
}
|
|
8940
9409
|
function slippagePercentToBasisPoints2(percent) {
|
|
8941
9410
|
return BigInt(Math.round(percent * 100));
|
|
@@ -9034,7 +9503,7 @@ function registerBridge(program2) {
|
|
|
9034
9503
|
Source network comes from the global -n/--network flag. Use --to-network for the destination chain.
|
|
9035
9504
|
For same-chain token exchanges, use 'alchemy evm swap'.
|
|
9036
9505
|
|
|
9037
|
-
Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
|
|
9506
|
+
Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
|
|
9038
9507
|
(ETH, USDC, WETH, USDT, DAI, \u2026) per chain.
|
|
9039
9508
|
|
|
9040
9509
|
Examples:
|
|
@@ -9055,7 +9524,7 @@ Examples:
|
|
|
9055
9524
|
Source network comes from the global -n/--network flag. Use --to-network for the destination chain.
|
|
9056
9525
|
For same-chain token exchanges, use 'alchemy evm swap'.
|
|
9057
9526
|
|
|
9058
|
-
Tip: use 'alchemy evm token <SYMBOL>' to resolve common token addresses
|
|
9527
|
+
Tip: use 'alchemy evm token <SYMBOL> -n <net>' to resolve common token addresses
|
|
9059
9528
|
(ETH, USDC, WETH, USDT, DAI, \u2026) per chain.
|
|
9060
9529
|
|
|
9061
9530
|
Examples:
|
|
@@ -9740,11 +10209,10 @@ function doctorSetupStatus(setup) {
|
|
|
9740
10209
|
...setup,
|
|
9741
10210
|
complete: x402OnlySetup ? false : setup.complete,
|
|
9742
10211
|
satisfiedBy: x402OnlySetup ? null : setup.satisfiedBy,
|
|
9743
|
-
missing: x402OnlySetup ? ["Provide one auth path: alchemy auth OR api-key
|
|
10212
|
+
missing: x402OnlySetup ? ["Provide one auth path: alchemy auth OR api-key"] : removeX402SetupMissing(setup.missing),
|
|
9744
10213
|
nextCommands: x402OnlySetup ? [
|
|
9745
10214
|
"alchemy auth",
|
|
9746
|
-
"alchemy config set app"
|
|
9747
|
-
"alchemy config set access-key <key> && alchemy config set app <app-id>"
|
|
10215
|
+
"alchemy config set app"
|
|
9748
10216
|
] : removeX402NextCommands(setup.nextCommands),
|
|
9749
10217
|
capabilities: sanitizedCapabilities
|
|
9750
10218
|
};
|
|
@@ -9888,9 +10356,9 @@ async function flushProcessOutput() {
|
|
|
9888
10356
|
}
|
|
9889
10357
|
program.name("alchemy").description(
|
|
9890
10358
|
"The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
|
|
9891
|
-
).version("0.
|
|
10359
|
+
).version("0.11.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
|
|
9892
10360
|
"-n, --network <network>",
|
|
9893
|
-
"Target network
|
|
10361
|
+
"Target network for networked commands"
|
|
9894
10362
|
).option("--x402", "Use x402 wallet-based gateway auth").option(
|
|
9895
10363
|
"--wallet-key-file <path>",
|
|
9896
10364
|
"Path to wallet private key file for x402"
|
|
@@ -10026,9 +10494,9 @@ ${styledLine}`;
|
|
|
10026
10494
|
`${hBrand("\u25C6")} ${hBold("Quick Start")}`,
|
|
10027
10495
|
` ${hDim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`,
|
|
10028
10496
|
` ${hBrand("alchemy")} ${hDim("Interactive mode with guided setup")}`,
|
|
10029
|
-
` ${hBrand("alchemy evm data balance")} ${hDim("<address>")}
|
|
10030
|
-
` ${hBrand("alchemy evm block latest")}
|
|
10031
|
-
` ${hBrand("alchemy evm rpc eth_chainId")}
|
|
10497
|
+
` ${hBrand("alchemy evm data balance")} ${hDim("<address> -n eth-mainnet")} ${hDim("Get native token balance")}`,
|
|
10498
|
+
` ${hBrand("alchemy evm block latest")} ${hDim("-n eth-mainnet")} ${hDim("Latest block summary")}`,
|
|
10499
|
+
` ${hBrand("alchemy evm rpc eth_chainId")} ${hDim("-n eth-mainnet")} ${hDim("Raw JSON-RPC call")}`,
|
|
10032
10500
|
` ${hBrand("alchemy config list")} ${hDim("View current configuration")}`,
|
|
10033
10501
|
"",
|
|
10034
10502
|
`${hBrand("\u25C6")} ${hBold("Exit Codes")}`,
|
|
@@ -10075,11 +10543,11 @@ ${styledLine}`;
|
|
|
10075
10543
|
"wallet"
|
|
10076
10544
|
];
|
|
10077
10545
|
if (!skipAppPrompt.includes(cmdName) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
|
|
10078
|
-
const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-
|
|
10546
|
+
const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-FRMIY357.js");
|
|
10079
10547
|
const authToken = resolveAuthToken2(cfg);
|
|
10080
10548
|
const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
|
|
10081
10549
|
if (authToken && !hasApiKey) {
|
|
10082
|
-
const { selectAppAfterAuth } = await import("./auth-
|
|
10550
|
+
const { selectAppAfterAuth } = await import("./auth-6X2DBBYQ.js");
|
|
10083
10551
|
console.log("");
|
|
10084
10552
|
console.log(` No app selected. Please select an app to continue.`);
|
|
10085
10553
|
await selectAppAfterAuth(authToken);
|
|
@@ -10114,7 +10582,7 @@ ${styledLine}`;
|
|
|
10114
10582
|
if (isInteractiveAllowed(program)) {
|
|
10115
10583
|
let latestForInteractiveStartup = null;
|
|
10116
10584
|
if (shouldRunOnboarding(program, cfg)) {
|
|
10117
|
-
const { runOnboarding } = await import("./onboarding-
|
|
10585
|
+
const { runOnboarding } = await import("./onboarding-2FLKQYLY.js");
|
|
10118
10586
|
const latest = getAvailableUpdateOnce();
|
|
10119
10587
|
const completed = await runOnboarding(program, latest);
|
|
10120
10588
|
updateShownDuringInteractiveStartup = Boolean(latest);
|
|
@@ -10128,7 +10596,7 @@ ${styledLine}`;
|
|
|
10128
10596
|
latestForInteractiveStartup
|
|
10129
10597
|
);
|
|
10130
10598
|
}
|
|
10131
|
-
const { startREPL } = await import("./interactive-
|
|
10599
|
+
const { startREPL } = await import("./interactive-EXQM6HWQ.js");
|
|
10132
10600
|
program.exitOverride();
|
|
10133
10601
|
program.configureOutput({
|
|
10134
10602
|
writeErr: () => {
|