@solard/cli 0.2.13 → 0.2.15
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/package.json +2 -2
- package/src/backtest-batch-command.ts +262 -0
- package/src/backtest-command.ts +533 -0
- package/src/commands/buy.ts +207 -0
- package/src/commands/jupiter-swap.test.ts +78 -0
- package/src/commands/jupiter-swap.ts +269 -0
- package/src/commands/sell.ts +239 -0
- package/src/commands/trade-result.ts +19 -0
- package/src/commands/trade-targets.ts +43 -0
- package/src/commands/trade-venue.test.ts +32 -0
- package/src/commands/trade-venue.ts +19 -0
- package/src/commands/transfer.ts +200 -0
- package/src/fairfun-commands.ts +441 -0
- package/src/index.ts +387 -290
- package/src/meteora-commands.ts +148 -6
- package/src/pump/external-deployment-cli.ts +136 -0
- package/src/pump/token-launch-cli.ts +82 -6
- package/src/raydium-commands.ts +398 -0
- package/src/refs.ts +56 -0
- package/src/token-history-command.ts +559 -0
- package/src/vault-onboarding.ts +242 -0
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
planRegistryTokenLiquidation,
|
|
14
14
|
removeExternalContact,
|
|
15
15
|
resetSolardRpcStats,
|
|
16
|
+
resolvePumpQuoteAsset,
|
|
16
17
|
resolveTokenMintForPolicy,
|
|
17
18
|
runScript,
|
|
18
19
|
listScripts,
|
|
@@ -23,6 +24,13 @@ import {
|
|
|
23
24
|
} from "@solard/sdk";
|
|
24
25
|
|
|
25
26
|
import { handleMeteoraCommand } from "./meteora-commands.ts";
|
|
27
|
+
import { handleFairfunCommand } from "./fairfun-commands.ts";
|
|
28
|
+
import { resolveDestinationRef } from "./refs.ts";
|
|
29
|
+
import {
|
|
30
|
+
VaultOnboardingError,
|
|
31
|
+
cliVaultStatus,
|
|
32
|
+
ensureCliVaultReady,
|
|
33
|
+
} from "./vault-onboarding.ts";
|
|
26
34
|
|
|
27
35
|
function emit(value: string): void {
|
|
28
36
|
process.stdout.write(value);
|
|
@@ -32,35 +40,6 @@ const OWL = "🦉";
|
|
|
32
40
|
const NATIVE_SOL_MINT = "So11111111111111111111111111111111111111112";
|
|
33
41
|
const CANONICAL_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
34
42
|
|
|
35
|
-
async function quoteJupiterSwap(
|
|
36
|
-
args: Record<string, unknown>,
|
|
37
|
-
): Promise<unknown> {
|
|
38
|
-
const sdk = (await import("@solard/sdk")) as Record<string, unknown>;
|
|
39
|
-
const fn = sdk.quoteJupiterSwap;
|
|
40
|
-
if (typeof fn !== "function") {
|
|
41
|
-
throw new Error(
|
|
42
|
-
"@solard/sdk does not export quoteJupiterSwap required by `slrd swap`.",
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
return await (fn as (input: Record<string, unknown>) => Promise<unknown>)(
|
|
46
|
-
args,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function executeJupiterSwap(
|
|
51
|
-
args: Record<string, unknown>,
|
|
52
|
-
): Promise<unknown> {
|
|
53
|
-
const sdk = (await import("@solard/sdk")) as Record<string, unknown>;
|
|
54
|
-
const fn = sdk.executeJupiterSwap;
|
|
55
|
-
if (typeof fn !== "function") {
|
|
56
|
-
throw new Error(
|
|
57
|
-
"@solard/sdk does not export executeJupiterSwap required by `slrd swap`.",
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
return await (fn as (input: Record<string, unknown>) => Promise<unknown>)(
|
|
61
|
-
args,
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
43
|
type Flags = Map<string, string>;
|
|
65
44
|
function args(input: string[]): { values: string[]; flags: Flags } {
|
|
66
45
|
const values: string[] = [],
|
|
@@ -122,6 +101,24 @@ function formatError(error: unknown): string {
|
|
|
122
101
|
return String(error);
|
|
123
102
|
}
|
|
124
103
|
}
|
|
104
|
+
|
|
105
|
+
function friendlyCliError(error: unknown): string | null {
|
|
106
|
+
const debug =
|
|
107
|
+
process.argv.includes("--debug") ||
|
|
108
|
+
process.env.SLRD_DEBUG === "1" ||
|
|
109
|
+
process.env.SOLARD_DEBUG === "1";
|
|
110
|
+
if (debug || !(error instanceof Error)) return null;
|
|
111
|
+
const code =
|
|
112
|
+
"code" in error &&
|
|
113
|
+
typeof (error as Error & { code?: unknown }).code === "string"
|
|
114
|
+
? String((error as Error & { code?: unknown }).code)
|
|
115
|
+
: null;
|
|
116
|
+
if (error instanceof VaultOnboardingError) return error.message;
|
|
117
|
+
if (code === "MISSING_CONFIG" && error.message.includes("SLRD_MASTER_KEY")) {
|
|
118
|
+
return `${error.message}\n\nEnter the wallet password interactively, or set SLRD_MASTER_KEY for automation/CI.`;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
125
122
|
function duration(value: string | undefined, fallbackMs: number): number {
|
|
126
123
|
if (!value) return fallbackMs;
|
|
127
124
|
const match = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/i.exec(value.trim());
|
|
@@ -202,10 +199,22 @@ function txWalletTokenBalances(
|
|
|
202
199
|
return out;
|
|
203
200
|
}
|
|
204
201
|
|
|
202
|
+
function formatRawLocal(value: bigint, decimals: number): string {
|
|
203
|
+
const unit = 10n ** BigInt(Math.max(0, decimals));
|
|
204
|
+
const whole = value / unit;
|
|
205
|
+
const fraction = value % unit;
|
|
206
|
+
if (decimals <= 0 || fraction === 0n) return whole.toString();
|
|
207
|
+
const fractionText = fraction
|
|
208
|
+
.toString()
|
|
209
|
+
.padStart(decimals, "0")
|
|
210
|
+
.replace(/0+$/, "");
|
|
211
|
+
return `${whole.toString()}.${fractionText}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
205
214
|
function formatSignedRaw(value: bigint, decimals: number): string {
|
|
206
215
|
const sign = value > 0n ? "+" : value < 0n ? "-" : "";
|
|
207
216
|
const abs = value < 0n ? -value : value;
|
|
208
|
-
return `${sign}${
|
|
217
|
+
return `${sign}${formatRawLocal(abs, decimals)}`;
|
|
209
218
|
}
|
|
210
219
|
function formatDurationMs(value: number): string {
|
|
211
220
|
if (!Number.isFinite(value) || value < 0) return "0ms";
|
|
@@ -250,55 +259,80 @@ function targetWallets(
|
|
|
250
259
|
group,
|
|
251
260
|
};
|
|
252
261
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
walletAddress = slrd.resolveWallet(input).address.toBase58();
|
|
268
|
-
} catch {
|
|
269
|
-
walletAddress = null;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (contact && walletAddress && contact.address !== walletAddress) {
|
|
273
|
-
throw new Error(
|
|
274
|
-
`Ambiguous destination ${input}: external contact @${contact.name} points to ${contact.address}, ` +
|
|
275
|
-
`but a stored signing wallet resolves to ${walletAddress}. Rename one of them.`,
|
|
276
|
-
);
|
|
262
|
+
function commandNeedsSigningVault(
|
|
263
|
+
command: string | undefined,
|
|
264
|
+
values: string[],
|
|
265
|
+
flags: Flags,
|
|
266
|
+
): boolean {
|
|
267
|
+
if (!command) return false;
|
|
268
|
+
if (command === "wallet" && values[0] === "create") return true;
|
|
269
|
+
if (command === "rewards" && values[0] === "claim") return true;
|
|
270
|
+
if (command === "rewards" && values[0] === "distribute")
|
|
271
|
+
return flags.has("live");
|
|
272
|
+
if (command === "transfer-many") {
|
|
273
|
+
if (values[0] === "status") return false;
|
|
274
|
+
if (values[0] === "resume") return true;
|
|
275
|
+
return flags.has("live");
|
|
277
276
|
}
|
|
277
|
+
if (
|
|
278
|
+
[
|
|
279
|
+
"import",
|
|
280
|
+
"export",
|
|
281
|
+
"transfer",
|
|
282
|
+
"send-sol",
|
|
283
|
+
"buy",
|
|
284
|
+
"sell",
|
|
285
|
+
"unwrap-wsol",
|
|
286
|
+
"claim",
|
|
287
|
+
].includes(command)
|
|
288
|
+
)
|
|
289
|
+
return true;
|
|
290
|
+
if (command === "alt" && (values[0] === "create" || values[0] === "extend"))
|
|
291
|
+
return true;
|
|
292
|
+
if (command === "vanity" && values[0] === "pool" && values[1] === "generate")
|
|
293
|
+
return true;
|
|
294
|
+
|
|
295
|
+
// Local Pump launch planning/building resolves a real signing wallet even in
|
|
296
|
+
// dry-run mode. External-wallet preparation is deliberately public-key-only.
|
|
297
|
+
if (
|
|
298
|
+
command === "launch" &&
|
|
299
|
+
(values[0] === "pump" || values[0] === "pump-token")
|
|
300
|
+
)
|
|
301
|
+
return !flags.has("prepare");
|
|
302
|
+
if (command === "deploy" || command === "vamp") return true;
|
|
278
303
|
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
304
|
+
if (
|
|
305
|
+
flags.has("live") &&
|
|
306
|
+
[
|
|
307
|
+
"swap",
|
|
308
|
+
"sweep",
|
|
309
|
+
"liquidate",
|
|
310
|
+
"meteora",
|
|
311
|
+
"raydium",
|
|
312
|
+
"spam-buy",
|
|
313
|
+
"buy-spam",
|
|
314
|
+
"run",
|
|
315
|
+
].includes(command)
|
|
316
|
+
)
|
|
317
|
+
return true;
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
286
320
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
321
|
+
async function storedWalletCount(): Promise<number> {
|
|
322
|
+
const { createTraderSolard } = await import("@solard/sdk");
|
|
323
|
+
const probe = createTraderSolard();
|
|
324
|
+
try {
|
|
325
|
+
return probe.wallets.list().length;
|
|
326
|
+
} finally {
|
|
327
|
+
probe.close();
|
|
293
328
|
}
|
|
294
|
-
|
|
295
|
-
return { input, address: input };
|
|
296
329
|
}
|
|
297
330
|
|
|
298
331
|
function help(): string {
|
|
299
332
|
return `${OWL} slrd — multi-wallet Solana CLI + SDK for traders and AI agents
|
|
300
333
|
|
|
301
334
|
Wallets and tokens
|
|
335
|
+
slrd setup [--status] Check ephemeral wallet-password mode; no password is persisted
|
|
302
336
|
slrd wallet create [name] Generate and persist an encrypted Solana wallet
|
|
303
337
|
slrd import <private_key> [name]
|
|
304
338
|
cat key.json | slrd import --stdin [name]
|
|
@@ -316,7 +350,13 @@ External contacts (public addresses only; never signing wallets or group members
|
|
|
316
350
|
slrd token <token_ca> [name] [--metadata-json <json>]
|
|
317
351
|
slrd token set <token|ca> [--pool <address>] [--quote-mint <mint>] [--quote-program <program>] [--metadata-json <json>]
|
|
318
352
|
slrd token refresh <token|ca>
|
|
353
|
+
slrd token backfill <ca> [--replace] [--confirmed] [--batch-size 100] [--rpc-concurrency 3] [--max-signatures N] [--json]
|
|
354
|
+
Durable exact trades + sparse 1s candle materialization from creation
|
|
355
|
+
slrd token trades <ca> [--from-start] [--min-sol N] [--owner <wallet>] [--side buy|sell] [--limit N] [--json]
|
|
356
|
+
slrd token analyze <ca> [--top N] [--json]
|
|
319
357
|
slrd tokens
|
|
358
|
+
slrd holders <token|ca> [--exclude <a,b>] [--min-raw N] [--json] Complete on-chain holder snapshot
|
|
359
|
+
slrd events <token|ca> [--swaps-only|--transfers-only] [--jsonl] Live typed Pump/PumpSwap + transfer stream
|
|
320
360
|
|
|
321
361
|
Vanity mints
|
|
322
362
|
slrd vanity --suffix pump --out .\\mint.json [--count <n>]
|
|
@@ -326,14 +366,16 @@ Vanity mints
|
|
|
326
366
|
launch/vamp: add --mint-pool pump [--mint-pool-address <address>] to consume a pooled mint
|
|
327
367
|
|
|
328
368
|
Metadata and launching
|
|
329
|
-
slrd launch pump --creator <wallet> (--uri <metadata_uri> | --metadata <json> | --image <path> --description <text>) [--alias <name>] [--live] [--skip-simulation]
|
|
369
|
+
slrd launch pump --creator <wallet> (--uri <metadata_uri> | --metadata <json> | --image <path> --description <text>) [--pair SOL|USDC|<custom-quote-mint>] [--beneficiary <wallet|address>] [--alias <name>] [--live] [--skip-simulation]
|
|
330
370
|
[--deployment-sender helius-rpc|helius-fast] [--helius-tip-sol 0.01]
|
|
371
|
+
slrd prepare pump --payer <phantom-address> --name <name> --symbol <symbol> --uri <metadata_uri> [--pair SOL|USDC|<custom-quote-mint>] [--beneficiary <address>] [--out prepared.json]
|
|
372
|
+
Build + mint-partial-sign only; payer remains unsigned and nothing is broadcast
|
|
331
373
|
slrd metadata upload --image <local_path> --name <name> --symbol <symbol> --description <text> [--provider pump-frontend|pinata]
|
|
332
374
|
[--twitter <url>] [--telegram <url>] [--website <url>] [--video <url>] [--hide-name]
|
|
333
375
|
slrd vamp <source-mint> --creator <wallet> [--name <name>] [--symbol <symbol>] [--image <path-or-url>] [--description <text>]
|
|
334
376
|
[--website <url>] [--twitter <url>] [--telegram <url>] [--video <url>] [--uri <metadata-uri>]
|
|
335
377
|
[--buy-plan <file>] [--mint-pool pump] [--submit-mode jito-bundle] [--live] [--skip-simulation]
|
|
336
|
-
slrd deploy pump --wallet <wallet> --name <name> --symbol <symbol> (--uri <metadata_uri> | --image <local_path> --description <text>) [--alias <name>] [--live]
|
|
378
|
+
slrd deploy pump --wallet <wallet> --name <name> --symbol <symbol> (--uri <metadata_uri> | --image <local_path> --description <text>) [--pair SOL|USDC|<custom-quote-mint>] [--beneficiary <wallet|address>] [--alias <name>] [--live]
|
|
337
379
|
[--twitter <url>] [--telegram <url>] [--website <url>] [--video <url>] [--hide-name]
|
|
338
380
|
|
|
339
381
|
Prices
|
|
@@ -342,18 +384,24 @@ Prices
|
|
|
342
384
|
slrd price average <token|ca> --period 15m Average stored samples in a period
|
|
343
385
|
slrd price watch <token|ca...> [--interval 1s] [--period 1m]
|
|
344
386
|
|
|
387
|
+
Backtesting
|
|
388
|
+
slrd backtest <mint> --dip-pct 20 --profit-pct 40 --buy-sol 0.1 [--from creation|migration|<time>] [--to <time>] [--capital-sol 5] [--slippage-bps N] [--venue-fee-bps N] [--network-fee-sol N] [--latency-ms N] [--require-from-start] [--exact-trades] [--ledger[=<csv>]] [--json]
|
|
389
|
+
Replays sparse 1s candles by default; --exact-trades uses durable market fills
|
|
390
|
+
|
|
345
391
|
Transfers and consolidation
|
|
346
392
|
slrd transfer <contact|wallet|address> --wallet <source-wallet> --sol <amount> [--simulate-only]
|
|
347
|
-
slrd
|
|
393
|
+
slrd transfer <contact|wallet|address> --wallet <source-wallet> --token <USDC|mint> --amount <ui> [--simulate-only]
|
|
394
|
+
slrd sweep sol --to <contact|wallet|address> [--wallets <a,b,...>] [--max-balance-sol <SOL>] [--exclude-group <group>] [--exclude-prefix <prefix>] [--keep <wallet=SOL,...>] [--keep-if-tokens <SOL> | --keep-if-token <token>=<SOL>] [--simulate | --live] [--json]
|
|
348
395
|
Without --wallets, sweep considers all stored signing wallets
|
|
349
396
|
|
|
350
397
|
Token liquidation
|
|
351
|
-
slrd liquidate tokens --except <token|mint> [--wallets <a,b,...>] [--slippage-bps 1500] [--no-jupiter] [--simulate | --live]
|
|
398
|
+
slrd liquidate tokens --except <token|mint> [--wallets <a,b,...>] [--slippage-bps 1500] [--no-jupiter] [--burn-unsellable] [--simulate | --live]
|
|
352
399
|
Sell supported tokens except protected mint(s); unwrap WSOL; close unprotected zero-balance token accounts and reclaim rent
|
|
353
400
|
|
|
354
401
|
RPC
|
|
355
402
|
All Solard JSON-RPC traffic is globally rate-limited to 5 req/s by default.
|
|
356
403
|
Override only when your provider allows it: SLRD_RPC_MAX_RPS=<n>
|
|
404
|
+
Fetch-level network failures (ECONNRESET, socket close, etc.) retry 4 times by default; SLRD_RPC_NETWORK_RETRIES=<n>.
|
|
357
405
|
Jupiter fallback: keyless 0.5 req/s, API-key default 1 req/s; override SLRD_JUPITER_MAX_RPS=<n>
|
|
358
406
|
|
|
359
407
|
Diagnostics
|
|
@@ -362,13 +410,22 @@ Diagnostics
|
|
|
362
410
|
--measure-stream Restore raw live measure-fn output for low-level debugging
|
|
363
411
|
|
|
364
412
|
Trading
|
|
365
|
-
slrd swap <token|mint> --
|
|
366
|
-
slrd
|
|
413
|
+
slrd swap --from <SOL|token|mint> --to <SOL|token|mint> --amount <ui> --wallet <wallet> [--live] Jupiter exact-input any -> any; quote-only unless --live
|
|
414
|
+
slrd swap <token|mint> --wallet <wallet> --sol <amount> [--live] Compatibility: SOL -> token
|
|
415
|
+
slrd buy <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <name>) --sol <amount> [--venue auto|native|jupiter] [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only]
|
|
367
416
|
slrd buy <future-mint> (--wallet <wallet> | --group <name>) (--sol <amount> | --lamports <amount> | --min-bps <n> --max-bps <n>) --spam [--live]
|
|
368
417
|
slrd spam-buy [pump] <future-mint> (--wallet <wallet> | --group <name>) (--sol <amount> | --lamports <amount> | --min-bps <n> --max-bps <n>) [--sender <id>] [--live]
|
|
369
|
-
slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <name>) [--bps 10000] [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only]
|
|
418
|
+
slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <name>) [--bps 10000] [--venue auto|native|jupiter] [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only]
|
|
370
419
|
slrd unwrap-wsol (--wallet <wallet> | --wallets <w1,w2> | --group <name>) [--sender rpc|helius|jito] [--ignore-missing] [--continue-on-error] [--simulate-only]
|
|
371
420
|
slrd claim <token|ca> --wallet <wallet> [--sender rpc|helius|jito]
|
|
421
|
+
slrd transfer-many --wallet <wallet> (--token <mint>|--sol) --file <allocations.json> [--id <stable-id> --live]
|
|
422
|
+
slrd transfer-many status <stable-id>
|
|
423
|
+
slrd transfer-many resume <stable-id> [--sender rpc|helius]
|
|
424
|
+
slrd rewards distribute <token|ca> --wallet <beneficiary> --id <ledger-id> --snapshot <snapshot.json> [--reward-mint <mint>] [--live]
|
|
425
|
+
Snapshot contains cumulative totalClaimedRaw + recipients[].entitledRaw; Solard tracks confirmed payouts
|
|
426
|
+
slrd rewards status <distribution-id>
|
|
427
|
+
slrd rewards inspect <token|ca> Show claim source, quote asset, estimated accrued creator reward, and payout address
|
|
428
|
+
slrd rewards claim <token|ca> --wallet <fee-payer> Claim creator rewards; on-chain beneficiary receives the reward
|
|
372
429
|
|
|
373
430
|
Scripts (strategies stay outside the kernel)
|
|
374
431
|
slrd scripts List scripts registered in slrd.config.ts
|
|
@@ -418,8 +475,8 @@ Transactions and ALTs
|
|
|
418
475
|
slrd alt extend <address> --wallet <wallet> <account...>
|
|
419
476
|
|
|
420
477
|
Environment
|
|
421
|
-
SLRD_DB_PATH shared SDK/CLI database; default
|
|
422
|
-
SLRD_MASTER_KEY
|
|
478
|
+
SLRD_DB_PATH shared SDK/CLI database; default ~/.solard/solard.sqlite (SOLARD_DB_PATH alias supported)
|
|
479
|
+
SLRD_MASTER_KEY optional explicit override for CI/automation; otherwise signing commands prompt each invocation and never persist the password
|
|
423
480
|
RPC_ENDPOINT required only for chain operations
|
|
424
481
|
HELIUS_SENDER_URL regional/global Helius Sender endpoint used by launch scripts
|
|
425
482
|
HELIUS_RPC_URL RPC endpoint used for ordinary Helius-RPC buyer lane
|
|
@@ -450,10 +507,71 @@ async function main() {
|
|
|
450
507
|
process.env.SLRD_MEASURE_STREAM === "true";
|
|
451
508
|
configureSolardMeasure(
|
|
452
509
|
streamMeasures
|
|
453
|
-
? {
|
|
454
|
-
|
|
510
|
+
? {
|
|
511
|
+
silent: false,
|
|
512
|
+
// Middleware mode: accumulate the structured event and then delegate
|
|
513
|
+
// to measure-fn's built-in logger. The collector itself does not own
|
|
514
|
+
// formatting or streaming.
|
|
515
|
+
logger: measureCollector.middleware,
|
|
516
|
+
}
|
|
517
|
+
: {
|
|
518
|
+
silent: false,
|
|
519
|
+
// Replacement mode: accumulate without printing every span.
|
|
520
|
+
logger: measureCollector.logger,
|
|
521
|
+
},
|
|
455
522
|
);
|
|
456
523
|
|
|
524
|
+
const vaultCommand = command === "setup";
|
|
525
|
+
const needsVault = commandNeedsSigningVault(command, values, flags);
|
|
526
|
+
if (vaultCommand && flags.has("status")) {
|
|
527
|
+
const status = cliVaultStatus();
|
|
528
|
+
if (flags.has("json")) {
|
|
529
|
+
emit(json(status) + "\n");
|
|
530
|
+
} else {
|
|
531
|
+
emit(
|
|
532
|
+
`${OWL} wallet password mode\n` +
|
|
533
|
+
` mode ephemeral per command\n` +
|
|
534
|
+
` password persisted no\n` +
|
|
535
|
+
` env override ${status.environmentOverride ? "yes" : "no"}\n`,
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (vaultCommand) {
|
|
542
|
+
const count = await storedWalletCount();
|
|
543
|
+
if (count === 0 && !process.env.SLRD_MASTER_KEY?.trim()) {
|
|
544
|
+
emit(
|
|
545
|
+
`${OWL} no wallet password is stored by Solard.\n` +
|
|
546
|
+
` No signing wallets exist yet. Run: slrd wallet create [name]\n` +
|
|
547
|
+
` That command will ask for a password twice and use it only for that process.\n`,
|
|
548
|
+
);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const result = await ensureCliVaultReady({
|
|
552
|
+
existingWalletCount: count,
|
|
553
|
+
allowCreate: false,
|
|
554
|
+
});
|
|
555
|
+
emit(
|
|
556
|
+
`${OWL} wallet password accepted source=${result.source}\n` +
|
|
557
|
+
` Nothing was written to disk; a future invocation will prompt again unless SLRD_MASTER_KEY is set.\n`,
|
|
558
|
+
);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (needsVault && !process.env.SLRD_MASTER_KEY?.trim()) {
|
|
563
|
+
const allowCreate =
|
|
564
|
+
(command === "wallet" && values[0] === "create") ||
|
|
565
|
+
command === "import" ||
|
|
566
|
+
(command === "vanity" &&
|
|
567
|
+
values[0] === "pool" &&
|
|
568
|
+
values[1] === "generate");
|
|
569
|
+
await ensureCliVaultReady({
|
|
570
|
+
existingWalletCount: await storedWalletCount(),
|
|
571
|
+
allowCreate,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
457
575
|
let perfPrinted = false;
|
|
458
576
|
const printPerfSummary = () => {
|
|
459
577
|
if (perfPrinted) return;
|
|
@@ -475,7 +593,10 @@ async function main() {
|
|
|
475
593
|
`PERF wall=${formatDurationMs(wallMs)} ` +
|
|
476
594
|
`measure=${measures.completed} (${measures.errors} err) ` +
|
|
477
595
|
`rpc=${rpc.requestStarts} 429=${rpc.rateLimited429} ` +
|
|
478
|
-
`
|
|
596
|
+
`neterr=${rpc.networkErrors} ` +
|
|
597
|
+
`retries=${rpc.retries429 + rpc.retriesNetwork} ` +
|
|
598
|
+
`(429=${rpc.retries429},net=${rpc.retriesNetwork}) ` +
|
|
599
|
+
`limit=${rpc.maxRps}/s\n`,
|
|
479
600
|
);
|
|
480
601
|
|
|
481
602
|
if (flags.has("measure") && !streamMeasures) {
|
|
@@ -630,6 +751,11 @@ async function main() {
|
|
|
630
751
|
await runPumpSpamBuyFromArgs(rest);
|
|
631
752
|
return;
|
|
632
753
|
}
|
|
754
|
+
if (command === "backtest") {
|
|
755
|
+
const { runBacktestCommand } = await import("./backtest-command.ts");
|
|
756
|
+
await runBacktestCommand({ values, flags, emit });
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
633
759
|
if (command === "scripts") {
|
|
634
760
|
emit(json(await listScripts()) + "\n");
|
|
635
761
|
return;
|
|
@@ -641,6 +767,18 @@ async function main() {
|
|
|
641
767
|
process.exitCode = await runScript(script, scriptArgs);
|
|
642
768
|
return;
|
|
643
769
|
}
|
|
770
|
+
if (
|
|
771
|
+
(command === "prepare" && values[0] === "pump") ||
|
|
772
|
+
(command === "launch" &&
|
|
773
|
+
(values[0] === "pump" || values[0] === "pump-token") &&
|
|
774
|
+
flags.has("prepare"))
|
|
775
|
+
) {
|
|
776
|
+
const { runPumpExternalDeploymentFromArgs } =
|
|
777
|
+
await import("./pump/external-deployment-cli.ts");
|
|
778
|
+
const externalArgs = command === "prepare" ? rest.slice(1) : rest.slice(1);
|
|
779
|
+
await runPumpExternalDeploymentFromArgs(externalArgs);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
644
782
|
if (
|
|
645
783
|
command === "launch" &&
|
|
646
784
|
(values[0] === "pump" || values[0] === "pump-token")
|
|
@@ -713,69 +851,19 @@ async function main() {
|
|
|
713
851
|
]);
|
|
714
852
|
const slrd = createTraderSolard();
|
|
715
853
|
try {
|
|
854
|
+
if (await handleFairfunCommand({ command, values, flags, slrd, emit })) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
716
858
|
if (command === "meteora") {
|
|
717
859
|
await handleMeteoraCommand({ slrd, values, flags, emit });
|
|
718
860
|
return;
|
|
719
861
|
}
|
|
720
862
|
|
|
721
863
|
if (command === "swap") {
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
"Usage: slrd swap <token|mint> --wallet <wallet> --sol <amount> [--live]",
|
|
726
|
-
);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const wallet = need(flags, "wallet");
|
|
730
|
-
const amountSol = need(flags, "sol");
|
|
731
|
-
const amountRaw = sol(amountSol).raw;
|
|
732
|
-
if (amountRaw <= 0n) throw new Error("--sol must be greater than zero");
|
|
733
|
-
|
|
734
|
-
const normalizedOutput = outputRef.replace(/^\$/, "").trim();
|
|
735
|
-
const outputMint =
|
|
736
|
-
normalizedOutput.toUpperCase() === "USDC"
|
|
737
|
-
? CANONICAL_USDC_MINT
|
|
738
|
-
: resolveTokenMintForPolicy(slrd, normalizedOutput);
|
|
739
|
-
|
|
740
|
-
if (outputMint === NATIVE_SOL_MINT) {
|
|
741
|
-
throw new Error("Swap output is SOL; choose a different token mint.");
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
if (!flags.has("live")) {
|
|
745
|
-
const quote = await quoteJupiterSwap({
|
|
746
|
-
inputMint: NATIVE_SOL_MINT,
|
|
747
|
-
outputMint,
|
|
748
|
-
amountRaw,
|
|
749
|
-
});
|
|
750
|
-
emit(
|
|
751
|
-
json({
|
|
752
|
-
mode: "quote",
|
|
753
|
-
wallet,
|
|
754
|
-
input: { symbol: "SOL", mint: NATIVE_SOL_MINT, amountSol },
|
|
755
|
-
output: { ref: outputRef, mint: outputMint },
|
|
756
|
-
quote,
|
|
757
|
-
hint: "Re-run with --live to execute this swap.",
|
|
758
|
-
}) + "\n",
|
|
759
|
-
);
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
const signer = slrd.signer(wallet);
|
|
764
|
-
const result = await executeJupiterSwap({
|
|
765
|
-
inputMint: NATIVE_SOL_MINT,
|
|
766
|
-
outputMint,
|
|
767
|
-
amountRaw,
|
|
768
|
-
signer,
|
|
769
|
-
});
|
|
770
|
-
emit(
|
|
771
|
-
json({
|
|
772
|
-
mode: "live",
|
|
773
|
-
wallet,
|
|
774
|
-
input: { symbol: "SOL", mint: NATIVE_SOL_MINT, amountSol },
|
|
775
|
-
output: { ref: outputRef, mint: outputMint },
|
|
776
|
-
result,
|
|
777
|
-
}) + "\n",
|
|
778
|
-
);
|
|
864
|
+
const { runJupiterSwapCommand } =
|
|
865
|
+
await import("./commands/jupiter-swap.ts");
|
|
866
|
+
emit(json(await runJupiterSwapCommand({ slrd, values, flags })) + "\n");
|
|
779
867
|
return;
|
|
780
868
|
}
|
|
781
869
|
if (command === "contact" || command === "contacts") {
|
|
@@ -840,8 +928,26 @@ async function main() {
|
|
|
840
928
|
}
|
|
841
929
|
|
|
842
930
|
if (command === "wallet" && values[0] === "create") {
|
|
843
|
-
const
|
|
844
|
-
|
|
931
|
+
const firstWallet = slrd.wallets.list().length === 0;
|
|
932
|
+
const wallet = slrd.createWallet(
|
|
933
|
+
values[1] ?? (firstWallet ? "main" : undefined),
|
|
934
|
+
);
|
|
935
|
+
if (flags.has("json")) {
|
|
936
|
+
emit(json(wallet) + "\n");
|
|
937
|
+
} else if (process.stdout.isTTY) {
|
|
938
|
+
emit(
|
|
939
|
+
`\n╭─ ${OWL} wallet ready ──────────────────────────────────╮\n` +
|
|
940
|
+
` name @${wallet.name}\n` +
|
|
941
|
+
` address ${wallet.address}\n` +
|
|
942
|
+
` storage encrypted in Solard's local wallet database\n` +
|
|
943
|
+
` password NOT saved; you will enter it again next invocation\n` +
|
|
944
|
+
`╰──────────────────────────────────────────────────────╯\n` +
|
|
945
|
+
`\nNext: slrd balances --wallet ${wallet.name}\n` +
|
|
946
|
+
`Backup: slrd export ${wallet.name} --out .\\${wallet.name}-key.txt\n`,
|
|
947
|
+
);
|
|
948
|
+
} else {
|
|
949
|
+
emit(`${OWL} created @${wallet.name} ${wallet.address}\n`);
|
|
950
|
+
}
|
|
845
951
|
return;
|
|
846
952
|
}
|
|
847
953
|
if (command === "export") {
|
|
@@ -1482,14 +1588,39 @@ async function main() {
|
|
|
1482
1588
|
);
|
|
1483
1589
|
uri = (uploadedMetadata as { metadataUri: string }).metadataUri;
|
|
1484
1590
|
}
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1591
|
+
const payer = slrd.signer(wallet).publicKey;
|
|
1592
|
+
const beneficiaryInput = flags.get("beneficiary") ?? flags.get("creator");
|
|
1593
|
+
let creator = payer;
|
|
1594
|
+
if (beneficiaryInput && beneficiaryInput !== "true") {
|
|
1595
|
+
try {
|
|
1596
|
+
creator = slrd.resolveWallet(beneficiaryInput).address;
|
|
1597
|
+
} catch {
|
|
1598
|
+
const contact = findExternalContact(beneficiaryInput);
|
|
1599
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
1600
|
+
creator = new PublicKey(contact?.address ?? beneficiaryInput);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
const quoteAsset =
|
|
1604
|
+
launchpad === "pump"
|
|
1605
|
+
? await resolvePumpQuoteAsset(
|
|
1606
|
+
slrd.connection(),
|
|
1607
|
+
flags.get("pair") ?? flags.get("quote-mint") ?? "SOL",
|
|
1608
|
+
)
|
|
1609
|
+
: undefined;
|
|
1610
|
+
if (
|
|
1611
|
+
launchpad !== "pump" &&
|
|
1612
|
+
(flags.has("pair") || flags.has("beneficiary"))
|
|
1613
|
+
) {
|
|
1614
|
+
throw new Error(
|
|
1615
|
+
"--pair and --beneficiary are currently defined for Pump create_v2 deployment only.",
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1488
1618
|
const deployment = await slrd.prepareTokenDeployment(launchpad, wallet, {
|
|
1489
1619
|
name,
|
|
1490
1620
|
symbol,
|
|
1491
1621
|
uri,
|
|
1492
1622
|
creator,
|
|
1623
|
+
quoteAsset,
|
|
1493
1624
|
mayhemMode: flags.has("mayhem"),
|
|
1494
1625
|
cashback: flags.has("cashback"),
|
|
1495
1626
|
});
|
|
@@ -1508,6 +1639,12 @@ async function main() {
|
|
|
1508
1639
|
name,
|
|
1509
1640
|
symbol,
|
|
1510
1641
|
uri,
|
|
1642
|
+
beneficiary: creator.toBase58(),
|
|
1643
|
+
pair:
|
|
1644
|
+
quoteAsset?.kind === "native-sol"
|
|
1645
|
+
? "SOL"
|
|
1646
|
+
: (quoteAsset?.mint.toBase58() ?? null),
|
|
1647
|
+
quoteMint: quoteAsset?.mint.toBase58() ?? null,
|
|
1511
1648
|
uploadedMetadata,
|
|
1512
1649
|
live: flags.has("live"),
|
|
1513
1650
|
};
|
|
@@ -1532,7 +1669,31 @@ async function main() {
|
|
|
1532
1669
|
emit(json({ ...header, receipt, token }) + "\n");
|
|
1533
1670
|
return;
|
|
1534
1671
|
}
|
|
1535
|
-
if (
|
|
1672
|
+
if (
|
|
1673
|
+
command === "token" &&
|
|
1674
|
+
(values[0] === "backfill" ||
|
|
1675
|
+
values[0] === "trades" ||
|
|
1676
|
+
values[0] === "analyze")
|
|
1677
|
+
) {
|
|
1678
|
+
const { runTokenHistoryCommand } =
|
|
1679
|
+
await import("./token-history-command.ts");
|
|
1680
|
+
await runTokenHistoryCommand({
|
|
1681
|
+
slrd,
|
|
1682
|
+
action: values[0]!,
|
|
1683
|
+
values: values.slice(1),
|
|
1684
|
+
flags,
|
|
1685
|
+
emit,
|
|
1686
|
+
});
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
if (
|
|
1690
|
+
command === "token" &&
|
|
1691
|
+
values[0] !== "set" &&
|
|
1692
|
+
values[0] !== "refresh" &&
|
|
1693
|
+
values[0] !== "backfill" &&
|
|
1694
|
+
values[0] !== "trades" &&
|
|
1695
|
+
values[0] !== "analyze"
|
|
1696
|
+
) {
|
|
1536
1697
|
const [mint, name] = values;
|
|
1537
1698
|
if (!mint) throw new Error("Usage: slrd token <token_ca> [name]");
|
|
1538
1699
|
const metadata = flags.get("metadata-json");
|
|
@@ -1658,7 +1819,7 @@ async function main() {
|
|
|
1658
1819
|
const except = csv(flags.get("except"));
|
|
1659
1820
|
if (except.length === 0) {
|
|
1660
1821
|
throw new Error(
|
|
1661
|
-
"Usage: slrd liquidate tokens --except <token|mint>[,<token|mint>...] [--wallets <a,b,...>] [--simulate | --live]",
|
|
1822
|
+
"Usage: slrd liquidate tokens --except <token|mint>[,<token|mint>...] [--wallets <a,b,...>] [--burn-unsellable] [--simulate | --live]",
|
|
1662
1823
|
);
|
|
1663
1824
|
}
|
|
1664
1825
|
if (flags.has("simulate") && flags.has("live")) {
|
|
@@ -1711,6 +1872,7 @@ async function main() {
|
|
|
1711
1872
|
portfolioConcurrency: 1,
|
|
1712
1873
|
portfolioDelayMs: 0,
|
|
1713
1874
|
jupiterFallback: !flags.has("no-jupiter"),
|
|
1875
|
+
burnUnsellable: flags.has("burn-unsellable"),
|
|
1714
1876
|
onProgress: (event: any) => {
|
|
1715
1877
|
if (event.stage === "portfolio-start") {
|
|
1716
1878
|
emit("SCAN wallet token accounts...\n");
|
|
@@ -1833,7 +1995,7 @@ async function main() {
|
|
|
1833
1995
|
const destinationInput = flags.get("to") ?? values[1];
|
|
1834
1996
|
if (!destinationInput || destinationInput === "true") {
|
|
1835
1997
|
throw new Error(
|
|
1836
|
-
"Usage: slrd sweep sol --to <contact|wallet|address> [--wallets <a,b,...>] [--exclude-group <group>] [--exclude-prefix <prefix>] [--keep <wallet=SOL,...>] [--keep-if-tokens <SOL> | --keep-if-token <token>=<SOL>] [--simulate | --live] [--json]",
|
|
1998
|
+
"Usage: slrd sweep sol --to <contact|wallet|address> [--wallets <a,b,...>] [--max-balance-sol <SOL>] [--exclude-group <group>] [--exclude-prefix <prefix>] [--keep <wallet=SOL,...>] [--keep-if-tokens <SOL> | --keep-if-token <token>=<SOL>] [--simulate | --live] [--json]",
|
|
1837
1999
|
);
|
|
1838
2000
|
}
|
|
1839
2001
|
|
|
@@ -1894,6 +2056,7 @@ async function main() {
|
|
|
1894
2056
|
excludeGroups,
|
|
1895
2057
|
excludePrefixes,
|
|
1896
2058
|
includeWallets: includeWallets.length ? includeWallets : undefined,
|
|
2059
|
+
maxBalanceSol: flags.get("max-balance-sol"),
|
|
1897
2060
|
keepSolByWallet,
|
|
1898
2061
|
defaultKeepSol: flags.get("default-keep-sol") ?? "0",
|
|
1899
2062
|
keepSolIfTokens: flags.get("keep-if-tokens"),
|
|
@@ -2046,156 +2209,19 @@ async function main() {
|
|
|
2046
2209
|
}
|
|
2047
2210
|
|
|
2048
2211
|
if (command === "transfer" || command === "send-sol") {
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
throw new Error(
|
|
2052
|
-
"Usage: slrd transfer <contact|wallet|address> --wallet <wallet> --sol <amount> [--sender rpc|helius|jito] [--simulate-only]",
|
|
2053
|
-
);
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
const recipient = resolveDestinationRef(slrd, recipientInput);
|
|
2057
|
-
const wallet = need(flags, "wallet");
|
|
2058
|
-
const amount = need(flags, "sol");
|
|
2059
|
-
const via = flags.get("sender") ?? "rpc";
|
|
2060
|
-
|
|
2061
|
-
const cuLimit = Number(flags.get("cu-limit") ?? "10000");
|
|
2062
|
-
const priorityMicroLamports = Number(
|
|
2063
|
-
flags.get("priority-micro-lamports") ?? "0",
|
|
2064
|
-
);
|
|
2065
|
-
if (!Number.isInteger(cuLimit) || cuLimit <= 0)
|
|
2066
|
-
throw new Error("--cu-limit must be a positive integer");
|
|
2067
|
-
if (
|
|
2068
|
-
!Number.isInteger(priorityMicroLamports) ||
|
|
2069
|
-
priorityMicroLamports < 0
|
|
2070
|
-
) {
|
|
2071
|
-
throw new Error(
|
|
2072
|
-
"--priority-micro-lamports must be a non-negative integer",
|
|
2073
|
-
);
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
|
-
const composer = slrd
|
|
2077
|
-
.tx(wallet)
|
|
2078
|
-
.transferSol(recipient.address, sol(amount))
|
|
2079
|
-
.priorityFee({
|
|
2080
|
-
cuLimit,
|
|
2081
|
-
microLamports: priorityMicroLamports,
|
|
2082
|
-
});
|
|
2083
|
-
|
|
2084
|
-
if (flags.has("simulate-only")) {
|
|
2085
|
-
const plan = await composer.build();
|
|
2086
|
-
const result = await slrd.simulatePlan(plan);
|
|
2087
|
-
emit(
|
|
2088
|
-
json({
|
|
2089
|
-
mode: "simulation",
|
|
2090
|
-
wallet,
|
|
2091
|
-
recipient,
|
|
2092
|
-
sol: amount,
|
|
2093
|
-
result,
|
|
2094
|
-
}) + "\n",
|
|
2095
|
-
);
|
|
2096
|
-
return;
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
const receipt = await composer.send({
|
|
2100
|
-
via,
|
|
2101
|
-
kind: "transfer-sol",
|
|
2102
|
-
skipSimulation: flags.has("skip-simulation"),
|
|
2103
|
-
skipPreflight:
|
|
2104
|
-
flags.has("skip-preflight") || flags.has("skip-simulation"),
|
|
2105
|
-
});
|
|
2106
|
-
emit(
|
|
2107
|
-
json({
|
|
2108
|
-
...receipt,
|
|
2109
|
-
recipient,
|
|
2110
|
-
}) + "\n",
|
|
2111
|
-
);
|
|
2212
|
+
const { runTransferCommand } = await import("./commands/transfer.ts");
|
|
2213
|
+
emit(json(await runTransferCommand({ slrd, values, flags })) + "\n");
|
|
2112
2214
|
return;
|
|
2113
2215
|
}
|
|
2114
2216
|
|
|
2115
2217
|
if (command === "buy") {
|
|
2116
|
-
const
|
|
2117
|
-
|
|
2118
|
-
throw new Error(
|
|
2119
|
-
"Usage: slrd buy <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>) --sol <amount>",
|
|
2120
|
-
);
|
|
2121
|
-
const amount = flags.get("sol");
|
|
2122
|
-
if (!amount) throw new Error("Buy requires explicit --sol <amount>");
|
|
2123
|
-
const targets = targetWallets(
|
|
2124
|
-
slrd,
|
|
2125
|
-
flags,
|
|
2126
|
-
"Usage: slrd buy <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>) --sol <amount>.",
|
|
2127
|
-
);
|
|
2128
|
-
const options = {
|
|
2129
|
-
slippageBps: int(flags, "slippage-bps", 1500),
|
|
2130
|
-
via: flags.get("sender") ?? "rpc",
|
|
2131
|
-
skipSimulation: flags.has("skip-simulation"),
|
|
2132
|
-
skipPreflight:
|
|
2133
|
-
flags.has("skip-preflight") || flags.has("skip-simulation"),
|
|
2134
|
-
};
|
|
2135
|
-
if (flags.has("simulate-only")) {
|
|
2136
|
-
const plans =
|
|
2137
|
-
targets.refs.length === 1
|
|
2138
|
-
? [
|
|
2139
|
-
await slrd
|
|
2140
|
-
.tx(targets.refs[0]!)
|
|
2141
|
-
.buy(token, sol(amount), options)
|
|
2142
|
-
.build(),
|
|
2143
|
-
]
|
|
2144
|
-
: await slrd
|
|
2145
|
-
.composeMany(targets.refs)
|
|
2146
|
-
.buy(token, sol(amount), options)
|
|
2147
|
-
.build();
|
|
2148
|
-
const results = await Promise.all(
|
|
2149
|
-
plans.map((plan) => slrd.simulatePlan(plan)),
|
|
2150
|
-
);
|
|
2151
|
-
emit(json({ mode: "simulation", target: targets, results }) + "\n");
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
const receipts =
|
|
2155
|
-
targets.refs.length === 1
|
|
2156
|
-
? await slrd.buy(token, targets.refs[0]!, sol(amount), options)
|
|
2157
|
-
: await slrd.buyMany(token, targets.refs, sol(amount), options);
|
|
2158
|
-
emit(json(receipts) + "\n");
|
|
2218
|
+
const { runSmartBuyCommand } = await import("./commands/buy.ts");
|
|
2219
|
+
emit(json(await runSmartBuyCommand({ slrd, values, flags })) + "\n");
|
|
2159
2220
|
return;
|
|
2160
2221
|
}
|
|
2161
2222
|
if (command === "sell") {
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2164
|
-
throw new Error(
|
|
2165
|
-
"Usage: slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>)",
|
|
2166
|
-
);
|
|
2167
|
-
const targets = targetWallets(
|
|
2168
|
-
slrd,
|
|
2169
|
-
flags,
|
|
2170
|
-
"Usage: slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>).",
|
|
2171
|
-
);
|
|
2172
|
-
const sellOptions = {
|
|
2173
|
-
bps: int(flags, "bps", 10000),
|
|
2174
|
-
slippageBps: int(flags, "slippage-bps", 1500),
|
|
2175
|
-
via: flags.get("sender") ?? "rpc",
|
|
2176
|
-
skipSimulation: flags.has("skip-simulation"),
|
|
2177
|
-
skipPreflight:
|
|
2178
|
-
flags.has("skip-preflight") || flags.has("skip-simulation"),
|
|
2179
|
-
};
|
|
2180
|
-
if (flags.has("simulate-only")) {
|
|
2181
|
-
const plans =
|
|
2182
|
-
targets.refs.length === 1
|
|
2183
|
-
? [await slrd.tx(targets.refs[0]!).sell(token, sellOptions).build()]
|
|
2184
|
-
: await slrd
|
|
2185
|
-
.composeMany(targets.refs)
|
|
2186
|
-
.sell(token, sellOptions)
|
|
2187
|
-
.build();
|
|
2188
|
-
const results = await Promise.all(
|
|
2189
|
-
plans.map((plan) => slrd.simulatePlan(plan)),
|
|
2190
|
-
);
|
|
2191
|
-
emit(json({ mode: "simulation", target: targets, results }) + "\n");
|
|
2192
|
-
return;
|
|
2193
|
-
}
|
|
2194
|
-
const receipts =
|
|
2195
|
-
targets.refs.length === 1
|
|
2196
|
-
? await slrd.sell(token, targets.refs[0]!, sellOptions)
|
|
2197
|
-
: await slrd.sellMany(token, targets.refs, sellOptions);
|
|
2198
|
-
emit(json(receipts) + "\n");
|
|
2223
|
+
const { runSmartSellCommand } = await import("./commands/sell.ts");
|
|
2224
|
+
emit(json(await runSmartSellCommand({ slrd, values, flags })) + "\n");
|
|
2199
2225
|
return;
|
|
2200
2226
|
}
|
|
2201
2227
|
if (
|
|
@@ -2348,6 +2374,74 @@ async function main() {
|
|
|
2348
2374
|
emit(json(receipts) + "\n");
|
|
2349
2375
|
return;
|
|
2350
2376
|
}
|
|
2377
|
+
if (command === "rewards") {
|
|
2378
|
+
const action = values[0] ?? "inspect";
|
|
2379
|
+
const tokenRef = values[1];
|
|
2380
|
+
if (!tokenRef)
|
|
2381
|
+
throw new Error(
|
|
2382
|
+
"Usage: slrd rewards <inspect|claim> <token|ca> [--wallet <fee-payer>]",
|
|
2383
|
+
);
|
|
2384
|
+
|
|
2385
|
+
if (action === "claim") {
|
|
2386
|
+
const wallet = need(flags, "wallet");
|
|
2387
|
+
const receipt = await slrd.claim(tokenRef, wallet, {
|
|
2388
|
+
via: flags.get("sender") ?? "rpc",
|
|
2389
|
+
});
|
|
2390
|
+
emit(
|
|
2391
|
+
json({
|
|
2392
|
+
action: "claim",
|
|
2393
|
+
token: tokenRef,
|
|
2394
|
+
feePayer: wallet,
|
|
2395
|
+
receipt,
|
|
2396
|
+
}) + "\n",
|
|
2397
|
+
);
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
if (action === "inspect") {
|
|
2402
|
+
let token = slrd.resolveToken(tokenRef);
|
|
2403
|
+
try {
|
|
2404
|
+
token = await slrd.refreshToken(token);
|
|
2405
|
+
} catch {
|
|
2406
|
+
// A registered token can still expose a useful claim plan when one
|
|
2407
|
+
// enrichment source is temporarily unavailable.
|
|
2408
|
+
}
|
|
2409
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
2410
|
+
const probeUser = token.creator
|
|
2411
|
+
? new PublicKey(token.creator)
|
|
2412
|
+
: new PublicKey(token.mint);
|
|
2413
|
+
const plan = await slrd.resolveClaim(token, probeUser);
|
|
2414
|
+
const payoutAddress =
|
|
2415
|
+
typeof plan.meta?.payoutAddress === "string"
|
|
2416
|
+
? plan.meta.payoutAddress
|
|
2417
|
+
: token.creator;
|
|
2418
|
+
const output = {
|
|
2419
|
+
action: "inspect",
|
|
2420
|
+
token: token.mint,
|
|
2421
|
+
source: plan.source,
|
|
2422
|
+
payoutAddress: payoutAddress ?? null,
|
|
2423
|
+
quote: {
|
|
2424
|
+
kind: plan.quoteAsset.kind,
|
|
2425
|
+
mint: plan.quoteAsset.mint.toBase58(),
|
|
2426
|
+
decimals: plan.quoteAsset.decimals,
|
|
2427
|
+
tokenProgram: plan.quoteAsset.tokenProgram.toBase58(),
|
|
2428
|
+
},
|
|
2429
|
+
estimatedClaimRaw: plan.estimatedClaimRaw.toString(),
|
|
2430
|
+
estimatedClaimUi: formatRaw(
|
|
2431
|
+
plan.estimatedClaimRaw,
|
|
2432
|
+
plan.quoteAsset.decimals,
|
|
2433
|
+
),
|
|
2434
|
+
spendableByProbeRaw: plan.spendableByUserRaw.toString(),
|
|
2435
|
+
sharingConfig: plan.meta?.sharingConfig ?? null,
|
|
2436
|
+
note: "Claim is permissionless where supported; --wallet is the transaction fee payer. The configured on-chain beneficiary receives the reward.",
|
|
2437
|
+
};
|
|
2438
|
+
emit(json(output) + "\n");
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
throw new Error("Usage: slrd rewards <inspect|claim> <token|ca>");
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2351
2445
|
if (command === "claim") {
|
|
2352
2446
|
const token = values[0];
|
|
2353
2447
|
if (!token)
|
|
@@ -2508,7 +2602,10 @@ async function main() {
|
|
|
2508
2602
|
}
|
|
2509
2603
|
}
|
|
2510
2604
|
main().catch((error) => {
|
|
2511
|
-
|
|
2605
|
+
const friendly = friendlyCliError(error);
|
|
2606
|
+
emit(
|
|
2607
|
+
friendly ? `${OWL} ${friendly}\n` : `${OWL} error: ${formatError(error)}\n`,
|
|
2608
|
+
);
|
|
2512
2609
|
process.exitCode = 1;
|
|
2513
2610
|
});
|
|
2514
2611
|
|