@solard/cli 0.2.13 → 0.2.14
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 +381 -0
- package/src/index.ts +386 -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 +214 -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,21 @@ 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> [--claim-first|--amount-raw N] [--id <epoch>] [--live]
|
|
425
|
+
slrd rewards status <distribution-id>
|
|
426
|
+
slrd rewards inspect <token|ca> Show claim source, quote asset, estimated accrued creator reward, and payout address
|
|
427
|
+
slrd rewards claim <token|ca> --wallet <fee-payer> Claim creator rewards; on-chain beneficiary receives the reward
|
|
372
428
|
|
|
373
429
|
Scripts (strategies stay outside the kernel)
|
|
374
430
|
slrd scripts List scripts registered in slrd.config.ts
|
|
@@ -418,8 +474,8 @@ Transactions and ALTs
|
|
|
418
474
|
slrd alt extend <address> --wallet <wallet> <account...>
|
|
419
475
|
|
|
420
476
|
Environment
|
|
421
|
-
SLRD_DB_PATH shared SDK/CLI database; default
|
|
422
|
-
SLRD_MASTER_KEY
|
|
477
|
+
SLRD_DB_PATH shared SDK/CLI database; default ~/.solard/solard.sqlite (SOLARD_DB_PATH alias supported)
|
|
478
|
+
SLRD_MASTER_KEY optional explicit override for CI/automation; otherwise signing commands prompt each invocation and never persist the password
|
|
423
479
|
RPC_ENDPOINT required only for chain operations
|
|
424
480
|
HELIUS_SENDER_URL regional/global Helius Sender endpoint used by launch scripts
|
|
425
481
|
HELIUS_RPC_URL RPC endpoint used for ordinary Helius-RPC buyer lane
|
|
@@ -450,10 +506,71 @@ async function main() {
|
|
|
450
506
|
process.env.SLRD_MEASURE_STREAM === "true";
|
|
451
507
|
configureSolardMeasure(
|
|
452
508
|
streamMeasures
|
|
453
|
-
? {
|
|
454
|
-
|
|
509
|
+
? {
|
|
510
|
+
silent: false,
|
|
511
|
+
// Middleware mode: accumulate the structured event and then delegate
|
|
512
|
+
// to measure-fn's built-in logger. The collector itself does not own
|
|
513
|
+
// formatting or streaming.
|
|
514
|
+
logger: measureCollector.middleware,
|
|
515
|
+
}
|
|
516
|
+
: {
|
|
517
|
+
silent: false,
|
|
518
|
+
// Replacement mode: accumulate without printing every span.
|
|
519
|
+
logger: measureCollector.logger,
|
|
520
|
+
},
|
|
455
521
|
);
|
|
456
522
|
|
|
523
|
+
const vaultCommand = command === "setup";
|
|
524
|
+
const needsVault = commandNeedsSigningVault(command, values, flags);
|
|
525
|
+
if (vaultCommand && flags.has("status")) {
|
|
526
|
+
const status = cliVaultStatus();
|
|
527
|
+
if (flags.has("json")) {
|
|
528
|
+
emit(json(status) + "\n");
|
|
529
|
+
} else {
|
|
530
|
+
emit(
|
|
531
|
+
`${OWL} wallet password mode\n` +
|
|
532
|
+
` mode ephemeral per command\n` +
|
|
533
|
+
` password persisted no\n` +
|
|
534
|
+
` env override ${status.environmentOverride ? "yes" : "no"}\n`,
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (vaultCommand) {
|
|
541
|
+
const count = await storedWalletCount();
|
|
542
|
+
if (count === 0 && !process.env.SLRD_MASTER_KEY?.trim()) {
|
|
543
|
+
emit(
|
|
544
|
+
`${OWL} no wallet password is stored by Solard.\n` +
|
|
545
|
+
` No signing wallets exist yet. Run: slrd wallet create [name]\n` +
|
|
546
|
+
` That command will ask for a password twice and use it only for that process.\n`,
|
|
547
|
+
);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const result = await ensureCliVaultReady({
|
|
551
|
+
existingWalletCount: count,
|
|
552
|
+
allowCreate: false,
|
|
553
|
+
});
|
|
554
|
+
emit(
|
|
555
|
+
`${OWL} wallet password accepted source=${result.source}\n` +
|
|
556
|
+
` Nothing was written to disk; a future invocation will prompt again unless SLRD_MASTER_KEY is set.\n`,
|
|
557
|
+
);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (needsVault && !process.env.SLRD_MASTER_KEY?.trim()) {
|
|
562
|
+
const allowCreate =
|
|
563
|
+
(command === "wallet" && values[0] === "create") ||
|
|
564
|
+
command === "import" ||
|
|
565
|
+
(command === "vanity" &&
|
|
566
|
+
values[0] === "pool" &&
|
|
567
|
+
values[1] === "generate");
|
|
568
|
+
await ensureCliVaultReady({
|
|
569
|
+
existingWalletCount: await storedWalletCount(),
|
|
570
|
+
allowCreate,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
457
574
|
let perfPrinted = false;
|
|
458
575
|
const printPerfSummary = () => {
|
|
459
576
|
if (perfPrinted) return;
|
|
@@ -475,7 +592,10 @@ async function main() {
|
|
|
475
592
|
`PERF wall=${formatDurationMs(wallMs)} ` +
|
|
476
593
|
`measure=${measures.completed} (${measures.errors} err) ` +
|
|
477
594
|
`rpc=${rpc.requestStarts} 429=${rpc.rateLimited429} ` +
|
|
478
|
-
`
|
|
595
|
+
`neterr=${rpc.networkErrors} ` +
|
|
596
|
+
`retries=${rpc.retries429 + rpc.retriesNetwork} ` +
|
|
597
|
+
`(429=${rpc.retries429},net=${rpc.retriesNetwork}) ` +
|
|
598
|
+
`limit=${rpc.maxRps}/s\n`,
|
|
479
599
|
);
|
|
480
600
|
|
|
481
601
|
if (flags.has("measure") && !streamMeasures) {
|
|
@@ -630,6 +750,11 @@ async function main() {
|
|
|
630
750
|
await runPumpSpamBuyFromArgs(rest);
|
|
631
751
|
return;
|
|
632
752
|
}
|
|
753
|
+
if (command === "backtest") {
|
|
754
|
+
const { runBacktestCommand } = await import("./backtest-command.ts");
|
|
755
|
+
await runBacktestCommand({ values, flags, emit });
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
633
758
|
if (command === "scripts") {
|
|
634
759
|
emit(json(await listScripts()) + "\n");
|
|
635
760
|
return;
|
|
@@ -641,6 +766,18 @@ async function main() {
|
|
|
641
766
|
process.exitCode = await runScript(script, scriptArgs);
|
|
642
767
|
return;
|
|
643
768
|
}
|
|
769
|
+
if (
|
|
770
|
+
(command === "prepare" && values[0] === "pump") ||
|
|
771
|
+
(command === "launch" &&
|
|
772
|
+
(values[0] === "pump" || values[0] === "pump-token") &&
|
|
773
|
+
flags.has("prepare"))
|
|
774
|
+
) {
|
|
775
|
+
const { runPumpExternalDeploymentFromArgs } =
|
|
776
|
+
await import("./pump/external-deployment-cli.ts");
|
|
777
|
+
const externalArgs = command === "prepare" ? rest.slice(1) : rest.slice(1);
|
|
778
|
+
await runPumpExternalDeploymentFromArgs(externalArgs);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
644
781
|
if (
|
|
645
782
|
command === "launch" &&
|
|
646
783
|
(values[0] === "pump" || values[0] === "pump-token")
|
|
@@ -713,69 +850,19 @@ async function main() {
|
|
|
713
850
|
]);
|
|
714
851
|
const slrd = createTraderSolard();
|
|
715
852
|
try {
|
|
853
|
+
if (await handleFairfunCommand({ command, values, flags, slrd, emit })) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
|
|
716
857
|
if (command === "meteora") {
|
|
717
858
|
await handleMeteoraCommand({ slrd, values, flags, emit });
|
|
718
859
|
return;
|
|
719
860
|
}
|
|
720
861
|
|
|
721
862
|
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
|
-
);
|
|
863
|
+
const { runJupiterSwapCommand } =
|
|
864
|
+
await import("./commands/jupiter-swap.ts");
|
|
865
|
+
emit(json(await runJupiterSwapCommand({ slrd, values, flags })) + "\n");
|
|
779
866
|
return;
|
|
780
867
|
}
|
|
781
868
|
if (command === "contact" || command === "contacts") {
|
|
@@ -840,8 +927,26 @@ async function main() {
|
|
|
840
927
|
}
|
|
841
928
|
|
|
842
929
|
if (command === "wallet" && values[0] === "create") {
|
|
843
|
-
const
|
|
844
|
-
|
|
930
|
+
const firstWallet = slrd.wallets.list().length === 0;
|
|
931
|
+
const wallet = slrd.createWallet(
|
|
932
|
+
values[1] ?? (firstWallet ? "main" : undefined),
|
|
933
|
+
);
|
|
934
|
+
if (flags.has("json")) {
|
|
935
|
+
emit(json(wallet) + "\n");
|
|
936
|
+
} else if (process.stdout.isTTY) {
|
|
937
|
+
emit(
|
|
938
|
+
`\n╭─ ${OWL} wallet ready ──────────────────────────────────╮\n` +
|
|
939
|
+
` name @${wallet.name}\n` +
|
|
940
|
+
` address ${wallet.address}\n` +
|
|
941
|
+
` storage encrypted in Solard's local wallet database\n` +
|
|
942
|
+
` password NOT saved; you will enter it again next invocation\n` +
|
|
943
|
+
`╰──────────────────────────────────────────────────────╯\n` +
|
|
944
|
+
`\nNext: slrd balances --wallet ${wallet.name}\n` +
|
|
945
|
+
`Backup: slrd export ${wallet.name} --out .\\${wallet.name}-key.txt\n`,
|
|
946
|
+
);
|
|
947
|
+
} else {
|
|
948
|
+
emit(`${OWL} created @${wallet.name} ${wallet.address}\n`);
|
|
949
|
+
}
|
|
845
950
|
return;
|
|
846
951
|
}
|
|
847
952
|
if (command === "export") {
|
|
@@ -1482,14 +1587,39 @@ async function main() {
|
|
|
1482
1587
|
);
|
|
1483
1588
|
uri = (uploadedMetadata as { metadataUri: string }).metadataUri;
|
|
1484
1589
|
}
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1590
|
+
const payer = slrd.signer(wallet).publicKey;
|
|
1591
|
+
const beneficiaryInput = flags.get("beneficiary") ?? flags.get("creator");
|
|
1592
|
+
let creator = payer;
|
|
1593
|
+
if (beneficiaryInput && beneficiaryInput !== "true") {
|
|
1594
|
+
try {
|
|
1595
|
+
creator = slrd.resolveWallet(beneficiaryInput).address;
|
|
1596
|
+
} catch {
|
|
1597
|
+
const contact = findExternalContact(beneficiaryInput);
|
|
1598
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
1599
|
+
creator = new PublicKey(contact?.address ?? beneficiaryInput);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
const quoteAsset =
|
|
1603
|
+
launchpad === "pump"
|
|
1604
|
+
? await resolvePumpQuoteAsset(
|
|
1605
|
+
slrd.connection(),
|
|
1606
|
+
flags.get("pair") ?? flags.get("quote-mint") ?? "SOL",
|
|
1607
|
+
)
|
|
1608
|
+
: undefined;
|
|
1609
|
+
if (
|
|
1610
|
+
launchpad !== "pump" &&
|
|
1611
|
+
(flags.has("pair") || flags.has("beneficiary"))
|
|
1612
|
+
) {
|
|
1613
|
+
throw new Error(
|
|
1614
|
+
"--pair and --beneficiary are currently defined for Pump create_v2 deployment only.",
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1488
1617
|
const deployment = await slrd.prepareTokenDeployment(launchpad, wallet, {
|
|
1489
1618
|
name,
|
|
1490
1619
|
symbol,
|
|
1491
1620
|
uri,
|
|
1492
1621
|
creator,
|
|
1622
|
+
quoteAsset,
|
|
1493
1623
|
mayhemMode: flags.has("mayhem"),
|
|
1494
1624
|
cashback: flags.has("cashback"),
|
|
1495
1625
|
});
|
|
@@ -1508,6 +1638,12 @@ async function main() {
|
|
|
1508
1638
|
name,
|
|
1509
1639
|
symbol,
|
|
1510
1640
|
uri,
|
|
1641
|
+
beneficiary: creator.toBase58(),
|
|
1642
|
+
pair:
|
|
1643
|
+
quoteAsset?.kind === "native-sol"
|
|
1644
|
+
? "SOL"
|
|
1645
|
+
: (quoteAsset?.mint.toBase58() ?? null),
|
|
1646
|
+
quoteMint: quoteAsset?.mint.toBase58() ?? null,
|
|
1511
1647
|
uploadedMetadata,
|
|
1512
1648
|
live: flags.has("live"),
|
|
1513
1649
|
};
|
|
@@ -1532,7 +1668,31 @@ async function main() {
|
|
|
1532
1668
|
emit(json({ ...header, receipt, token }) + "\n");
|
|
1533
1669
|
return;
|
|
1534
1670
|
}
|
|
1535
|
-
if (
|
|
1671
|
+
if (
|
|
1672
|
+
command === "token" &&
|
|
1673
|
+
(values[0] === "backfill" ||
|
|
1674
|
+
values[0] === "trades" ||
|
|
1675
|
+
values[0] === "analyze")
|
|
1676
|
+
) {
|
|
1677
|
+
const { runTokenHistoryCommand } =
|
|
1678
|
+
await import("./token-history-command.ts");
|
|
1679
|
+
await runTokenHistoryCommand({
|
|
1680
|
+
slrd,
|
|
1681
|
+
action: values[0]!,
|
|
1682
|
+
values: values.slice(1),
|
|
1683
|
+
flags,
|
|
1684
|
+
emit,
|
|
1685
|
+
});
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
if (
|
|
1689
|
+
command === "token" &&
|
|
1690
|
+
values[0] !== "set" &&
|
|
1691
|
+
values[0] !== "refresh" &&
|
|
1692
|
+
values[0] !== "backfill" &&
|
|
1693
|
+
values[0] !== "trades" &&
|
|
1694
|
+
values[0] !== "analyze"
|
|
1695
|
+
) {
|
|
1536
1696
|
const [mint, name] = values;
|
|
1537
1697
|
if (!mint) throw new Error("Usage: slrd token <token_ca> [name]");
|
|
1538
1698
|
const metadata = flags.get("metadata-json");
|
|
@@ -1658,7 +1818,7 @@ async function main() {
|
|
|
1658
1818
|
const except = csv(flags.get("except"));
|
|
1659
1819
|
if (except.length === 0) {
|
|
1660
1820
|
throw new Error(
|
|
1661
|
-
"Usage: slrd liquidate tokens --except <token|mint>[,<token|mint>...] [--wallets <a,b,...>] [--simulate | --live]",
|
|
1821
|
+
"Usage: slrd liquidate tokens --except <token|mint>[,<token|mint>...] [--wallets <a,b,...>] [--burn-unsellable] [--simulate | --live]",
|
|
1662
1822
|
);
|
|
1663
1823
|
}
|
|
1664
1824
|
if (flags.has("simulate") && flags.has("live")) {
|
|
@@ -1711,6 +1871,7 @@ async function main() {
|
|
|
1711
1871
|
portfolioConcurrency: 1,
|
|
1712
1872
|
portfolioDelayMs: 0,
|
|
1713
1873
|
jupiterFallback: !flags.has("no-jupiter"),
|
|
1874
|
+
burnUnsellable: flags.has("burn-unsellable"),
|
|
1714
1875
|
onProgress: (event: any) => {
|
|
1715
1876
|
if (event.stage === "portfolio-start") {
|
|
1716
1877
|
emit("SCAN wallet token accounts...\n");
|
|
@@ -1833,7 +1994,7 @@ async function main() {
|
|
|
1833
1994
|
const destinationInput = flags.get("to") ?? values[1];
|
|
1834
1995
|
if (!destinationInput || destinationInput === "true") {
|
|
1835
1996
|
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]",
|
|
1997
|
+
"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
1998
|
);
|
|
1838
1999
|
}
|
|
1839
2000
|
|
|
@@ -1894,6 +2055,7 @@ async function main() {
|
|
|
1894
2055
|
excludeGroups,
|
|
1895
2056
|
excludePrefixes,
|
|
1896
2057
|
includeWallets: includeWallets.length ? includeWallets : undefined,
|
|
2058
|
+
maxBalanceSol: flags.get("max-balance-sol"),
|
|
1897
2059
|
keepSolByWallet,
|
|
1898
2060
|
defaultKeepSol: flags.get("default-keep-sol") ?? "0",
|
|
1899
2061
|
keepSolIfTokens: flags.get("keep-if-tokens"),
|
|
@@ -2046,156 +2208,19 @@ async function main() {
|
|
|
2046
2208
|
}
|
|
2047
2209
|
|
|
2048
2210
|
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
|
-
);
|
|
2211
|
+
const { runTransferCommand } = await import("./commands/transfer.ts");
|
|
2212
|
+
emit(json(await runTransferCommand({ slrd, values, flags })) + "\n");
|
|
2112
2213
|
return;
|
|
2113
2214
|
}
|
|
2114
2215
|
|
|
2115
2216
|
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");
|
|
2217
|
+
const { runSmartBuyCommand } = await import("./commands/buy.ts");
|
|
2218
|
+
emit(json(await runSmartBuyCommand({ slrd, values, flags })) + "\n");
|
|
2159
2219
|
return;
|
|
2160
2220
|
}
|
|
2161
2221
|
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");
|
|
2222
|
+
const { runSmartSellCommand } = await import("./commands/sell.ts");
|
|
2223
|
+
emit(json(await runSmartSellCommand({ slrd, values, flags })) + "\n");
|
|
2199
2224
|
return;
|
|
2200
2225
|
}
|
|
2201
2226
|
if (
|
|
@@ -2348,6 +2373,74 @@ async function main() {
|
|
|
2348
2373
|
emit(json(receipts) + "\n");
|
|
2349
2374
|
return;
|
|
2350
2375
|
}
|
|
2376
|
+
if (command === "rewards") {
|
|
2377
|
+
const action = values[0] ?? "inspect";
|
|
2378
|
+
const tokenRef = values[1];
|
|
2379
|
+
if (!tokenRef)
|
|
2380
|
+
throw new Error(
|
|
2381
|
+
"Usage: slrd rewards <inspect|claim> <token|ca> [--wallet <fee-payer>]",
|
|
2382
|
+
);
|
|
2383
|
+
|
|
2384
|
+
if (action === "claim") {
|
|
2385
|
+
const wallet = need(flags, "wallet");
|
|
2386
|
+
const receipt = await slrd.claim(tokenRef, wallet, {
|
|
2387
|
+
via: flags.get("sender") ?? "rpc",
|
|
2388
|
+
});
|
|
2389
|
+
emit(
|
|
2390
|
+
json({
|
|
2391
|
+
action: "claim",
|
|
2392
|
+
token: tokenRef,
|
|
2393
|
+
feePayer: wallet,
|
|
2394
|
+
receipt,
|
|
2395
|
+
}) + "\n",
|
|
2396
|
+
);
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
if (action === "inspect") {
|
|
2401
|
+
let token = slrd.resolveToken(tokenRef);
|
|
2402
|
+
try {
|
|
2403
|
+
token = await slrd.refreshToken(token);
|
|
2404
|
+
} catch {
|
|
2405
|
+
// A registered token can still expose a useful claim plan when one
|
|
2406
|
+
// enrichment source is temporarily unavailable.
|
|
2407
|
+
}
|
|
2408
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
2409
|
+
const probeUser = token.creator
|
|
2410
|
+
? new PublicKey(token.creator)
|
|
2411
|
+
: new PublicKey(token.mint);
|
|
2412
|
+
const plan = await slrd.resolveClaim(token, probeUser);
|
|
2413
|
+
const payoutAddress =
|
|
2414
|
+
typeof plan.meta?.payoutAddress === "string"
|
|
2415
|
+
? plan.meta.payoutAddress
|
|
2416
|
+
: token.creator;
|
|
2417
|
+
const output = {
|
|
2418
|
+
action: "inspect",
|
|
2419
|
+
token: token.mint,
|
|
2420
|
+
source: plan.source,
|
|
2421
|
+
payoutAddress: payoutAddress ?? null,
|
|
2422
|
+
quote: {
|
|
2423
|
+
kind: plan.quoteAsset.kind,
|
|
2424
|
+
mint: plan.quoteAsset.mint.toBase58(),
|
|
2425
|
+
decimals: plan.quoteAsset.decimals,
|
|
2426
|
+
tokenProgram: plan.quoteAsset.tokenProgram.toBase58(),
|
|
2427
|
+
},
|
|
2428
|
+
estimatedClaimRaw: plan.estimatedClaimRaw.toString(),
|
|
2429
|
+
estimatedClaimUi: formatRaw(
|
|
2430
|
+
plan.estimatedClaimRaw,
|
|
2431
|
+
plan.quoteAsset.decimals,
|
|
2432
|
+
),
|
|
2433
|
+
spendableByProbeRaw: plan.spendableByUserRaw.toString(),
|
|
2434
|
+
sharingConfig: plan.meta?.sharingConfig ?? null,
|
|
2435
|
+
note: "Claim is permissionless where supported; --wallet is the transaction fee payer. The configured on-chain beneficiary receives the reward.",
|
|
2436
|
+
};
|
|
2437
|
+
emit(json(output) + "\n");
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
throw new Error("Usage: slrd rewards <inspect|claim> <token|ca>");
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2351
2444
|
if (command === "claim") {
|
|
2352
2445
|
const token = values[0];
|
|
2353
2446
|
if (!token)
|
|
@@ -2508,7 +2601,10 @@ async function main() {
|
|
|
2508
2601
|
}
|
|
2509
2602
|
}
|
|
2510
2603
|
main().catch((error) => {
|
|
2511
|
-
|
|
2604
|
+
const friendly = friendlyCliError(error);
|
|
2605
|
+
emit(
|
|
2606
|
+
friendly ? `${OWL} ${friendly}\n` : `${OWL} error: ${formatError(error)}\n`,
|
|
2607
|
+
);
|
|
2512
2608
|
process.exitCode = 1;
|
|
2513
2609
|
});
|
|
2514
2610
|
|