@solard/cli 0.2.16 → 0.2.19
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 +4 -3
- package/src/accounting-commands.ts +621 -0
- package/src/backtest-batch-command.ts +1 -1
- package/src/backtest-command.ts +1 -1
- package/src/commands/buy.ts +1 -1
- package/src/commands/jupiter-swap.ts +1 -1
- package/src/commands/sell.ts +1 -1
- package/src/commands/trade-result.ts +5 -1
- package/src/commands/trade-targets.ts +1 -1
- package/src/commands/trade-venue.ts +1 -1
- package/src/commands/transfer.ts +1 -1
- package/src/fairfun-commands.ts +213 -61
- package/src/index.ts +246 -116
- package/src/meteora-commands.ts +1 -1
- package/src/pump/external-deployment-cli.ts +1 -1
- package/src/pump/spam-buy-cli.ts +1 -1
- package/src/pump/token-launch-cli.ts +3 -3
- package/src/pump/vamp-cli.ts +1 -1
- package/src/raydium-commands.ts +1 -1
- package/src/refs.ts +1 -1
- package/src/token-history-command.ts +11 -33
- package/src/vault-onboarding.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { configure } from "measure-fn";
|
|
2
3
|
import {
|
|
3
4
|
addExternalContact,
|
|
4
|
-
configureSolardMeasure,
|
|
5
5
|
createSolardMeasureCollector,
|
|
6
6
|
executeRegistrySolSweep,
|
|
7
7
|
executeRegistryTokenLiquidation,
|
|
@@ -21,10 +21,10 @@ import {
|
|
|
21
21
|
simulateRegistryTokenLiquidation,
|
|
22
22
|
wrappedSolAta,
|
|
23
23
|
type TokenRow,
|
|
24
|
-
} from "@solard/
|
|
24
|
+
} from "@solard/core";
|
|
25
25
|
|
|
26
26
|
import { handleMeteoraCommand } from "./meteora-commands.ts";
|
|
27
|
-
import {
|
|
27
|
+
import { handleAccountingCommand } from "./accounting-commands.ts";
|
|
28
28
|
import { resolveDestinationRef } from "./refs.ts";
|
|
29
29
|
import {
|
|
30
30
|
VaultOnboardingError,
|
|
@@ -77,7 +77,12 @@ function int(flags: Flags, key: string, fallback?: number): number | undefined {
|
|
|
77
77
|
function json(value: unknown): string {
|
|
78
78
|
return JSON.stringify(
|
|
79
79
|
value,
|
|
80
|
-
(_, item) =>
|
|
80
|
+
(_, item) =>
|
|
81
|
+
typeof item === "bigint"
|
|
82
|
+
? item.toString()
|
|
83
|
+
: item instanceof Map
|
|
84
|
+
? Object.fromEntries(item)
|
|
85
|
+
: item,
|
|
81
86
|
2,
|
|
82
87
|
);
|
|
83
88
|
}
|
|
@@ -117,7 +122,7 @@ function friendlyCliError(error: unknown): string | null {
|
|
|
117
122
|
if (code === "MISSING_CONFIG" && error.message.includes("SLRD_MASTER_KEY")) {
|
|
118
123
|
return `${error.message}\n\nEnter the wallet password interactively, or set SLRD_MASTER_KEY for automation/CI.`;
|
|
119
124
|
}
|
|
120
|
-
return
|
|
125
|
+
return error.message.trim() || error.name;
|
|
121
126
|
}
|
|
122
127
|
function duration(value: string | undefined, fallbackMs: number): number {
|
|
123
128
|
if (!value) return fallbackMs;
|
|
@@ -318,8 +323,16 @@ function commandNeedsSigningVault(
|
|
|
318
323
|
return false;
|
|
319
324
|
}
|
|
320
325
|
|
|
326
|
+
function sweepUsage(): string {
|
|
327
|
+
return (
|
|
328
|
+
"Usage: slrd sweep --to <contact|wallet|address> --below <SOL> " +
|
|
329
|
+
"[--wallets <a,b,...>] [--keep <wallet=SOL,...>] [--simulate | --live] [--json]\n" +
|
|
330
|
+
"Compatibility: slrd sweep sol --to <destination> --max-balance-sol <SOL>"
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
321
334
|
async function storedWalletCount(): Promise<number> {
|
|
322
|
-
const { createTraderSolard } = await import("@solard/
|
|
335
|
+
const { createTraderSolard } = await import("@solard/core");
|
|
323
336
|
const probe = createTraderSolard();
|
|
324
337
|
try {
|
|
325
338
|
return (probe.db.wallets.select().all() as unknown[]).length;
|
|
@@ -357,6 +370,8 @@ External contacts (public addresses only; never signing wallets or group members
|
|
|
357
370
|
slrd tokens
|
|
358
371
|
slrd holders <token|ca> [--exclude <a,b>] [--min-raw N] [--json] Complete on-chain holder snapshot
|
|
359
372
|
slrd events <token|ca> [--all|--swaps|--transfers|--creates] [--jsonl] Live typed Pump/PumpSwap + transfer/create stream
|
|
373
|
+
slrd events history <token|ca> [--provider auto|solscan|rpc] [--from-slot N] [--to-slot N] [--slot-order] [--json]
|
|
374
|
+
Historical holder-balance movements with explicit completeness verification
|
|
360
375
|
|
|
361
376
|
Vanity mints
|
|
362
377
|
slrd vanity --suffix pump --out .\\mint.json [--count <n>]
|
|
@@ -394,12 +409,13 @@ Backtesting
|
|
|
394
409
|
Transfers and consolidation
|
|
395
410
|
slrd transfer <contact|wallet|address> --wallet <source-wallet> --sol <amount> [--simulate-only]
|
|
396
411
|
slrd transfer <contact|wallet|address> --wallet <source-wallet> --token <USDC|mint> --amount <ui> [--simulate-only]
|
|
397
|
-
slrd sweep
|
|
398
|
-
|
|
412
|
+
slrd sweep --to <contact|wallet|address> --below <SOL> [--wallets <a,b,...>] [--keep <wallet=SOL,...>] [--simulate | --live] [--json]
|
|
413
|
+
Sweeps stored wallets strictly below the threshold; preview is the default
|
|
414
|
+
slrd sweep sol ... Compatibility alias; --max-balance-sol remains accepted
|
|
399
415
|
|
|
400
416
|
Token liquidation
|
|
401
|
-
slrd liquidate tokens --except <token|mint> [--wallets <a,b,...>] [--slippage-bps 1500] [--no-jupiter] [--burn-unsellable] [--simulate | --live]
|
|
402
|
-
Sell supported tokens
|
|
417
|
+
slrd liquidate tokens [--except <token|mint>] [--wallets <a,b,...>] [--except-wallet <wallet>] [--except-wallets <a,b,...>] [--slippage-bps 1500] [--no-jupiter] [--burn-unsellable] [--simulate | --live]
|
|
418
|
+
Sell all supported tokens unless protected with --except; excluded wallets are not scanned or touched
|
|
403
419
|
|
|
404
420
|
RPC
|
|
405
421
|
All Solard JSON-RPC traffic is globally rate-limited to 5 req/s by default.
|
|
@@ -424,11 +440,15 @@ Trading
|
|
|
424
440
|
slrd transfer-many --wallet <wallet> (--token <mint>|--sol) --file <allocations.json> [--id <stable-id> --live]
|
|
425
441
|
slrd transfer-many status <stable-id>
|
|
426
442
|
slrd transfer-many resume <stable-id> [--sender rpc|helius]
|
|
427
|
-
slrd rewards
|
|
428
|
-
|
|
429
|
-
|
|
443
|
+
slrd rewards history <token|ca> [--wallet <reward-recipient>] [--provider auto|solscan|rpc] [--json]
|
|
444
|
+
slrd rewards distribute <token|ca> --wallet <beneficiary> --snapshot <snapshot.json> [--reward-mint <mint>] [--live]
|
|
445
|
+
Pay every outstanding cumulative holder entitlement and exit when complete
|
|
446
|
+
slrd rewards stop <token|ca> Ask the currently running distribution for this token to stop before its next transfer
|
|
447
|
+
slrd rewards status <token|ca> Show remembered cumulative entitlement/payment state
|
|
448
|
+
slrd rewards audit <token|ca> [--holder <wallet>] Show entitlement and confirmed-payment history
|
|
430
449
|
slrd rewards inspect <token|ca> Show claim source, quote asset, estimated accrued creator reward, and payout address
|
|
431
|
-
slrd rewards claim <token|ca> --wallet <fee-payer>
|
|
450
|
+
slrd rewards claim <token|ca> --wallet <fee-payer> [--id <stable-claim-id> --basis <reward-basis.json>]
|
|
451
|
+
slrd rewards claim-status <stable-claim-id>
|
|
432
452
|
|
|
433
453
|
Scripts (strategies stay outside the kernel)
|
|
434
454
|
slrd scripts List scripts registered in slrd.config.ts
|
|
@@ -483,6 +503,7 @@ Environment
|
|
|
483
503
|
RPC_ENDPOINT required only for chain operations
|
|
484
504
|
HELIUS_SENDER_URL regional/global Helius Sender endpoint used by launch scripts
|
|
485
505
|
HELIUS_RPC_URL RPC endpoint used for ordinary Helius-RPC buyer lane
|
|
506
|
+
SOLSCAN_API_KEY optional indexed token-history provider; enables authoritative historical transfer reconstruction
|
|
486
507
|
HELIUS_TIP_ACCOUNT, HELIUS_TIP_LAMPORTS, HELIUS_PRIORITY_MICRO_LAMPORTS launch transport policy
|
|
487
508
|
SLRD_LAUNCH_* launch resend/timeout policy; shared environment, not per-token metadata
|
|
488
509
|
JITO_BLOCK_ENGINE_URL only when explicitly selecting the separate Jito sender
|
|
@@ -508,7 +529,7 @@ async function main() {
|
|
|
508
529
|
flags.has("measure-stream") ||
|
|
509
530
|
process.env.SLRD_MEASURE_STREAM === "1" ||
|
|
510
531
|
process.env.SLRD_MEASURE_STREAM === "true";
|
|
511
|
-
|
|
532
|
+
configure(
|
|
512
533
|
streamMeasures
|
|
513
534
|
? {
|
|
514
535
|
silent: false,
|
|
@@ -638,7 +659,7 @@ async function main() {
|
|
|
638
659
|
listVanityMintPool,
|
|
639
660
|
releaseVanityMintReservation,
|
|
640
661
|
saveMintKeypairFile,
|
|
641
|
-
} = await import("@solard/
|
|
662
|
+
} = await import("@solard/core");
|
|
642
663
|
|
|
643
664
|
if (values[0] === "pool") {
|
|
644
665
|
const action = values[1] ?? "list";
|
|
@@ -816,13 +837,13 @@ async function main() {
|
|
|
816
837
|
process.env.JITO_BLOCK_ENGINE_URL ??
|
|
817
838
|
"https://mainnet.block-engine.jito.wtf"
|
|
818
839
|
).replace(/\/$/, "");
|
|
819
|
-
const { getJitoTipAccounts } = await import("@solard/
|
|
840
|
+
const { getJitoTipAccounts } = await import("@solard/core");
|
|
820
841
|
const tipAccounts = await getJitoTipAccounts(endpoint);
|
|
821
842
|
emit(json({ endpoint, tipAccounts }) + "\n");
|
|
822
843
|
return;
|
|
823
844
|
}
|
|
824
845
|
if (command === "metadata" && values[0] === "upload") {
|
|
825
|
-
const { uploadPumpMetadata } = await import("@solard/
|
|
846
|
+
const { uploadPumpMetadata } = await import("@solard/core");
|
|
826
847
|
const uploaded = await uploadPumpMetadata(
|
|
827
848
|
{
|
|
828
849
|
imagePath: need(flags, "image"),
|
|
@@ -848,13 +869,13 @@ async function main() {
|
|
|
848
869
|
}
|
|
849
870
|
const [{ sol, formatRaw }, { shortKey }, { createTraderSolard }] =
|
|
850
871
|
await Promise.all([
|
|
851
|
-
import("@solard/
|
|
852
|
-
import("@solard/
|
|
853
|
-
import("@solard/
|
|
872
|
+
import("@solard/core"),
|
|
873
|
+
import("@solard/core"),
|
|
874
|
+
import("@solard/core"),
|
|
854
875
|
]);
|
|
855
876
|
const slrd = createTraderSolard();
|
|
856
877
|
try {
|
|
857
|
-
if (await
|
|
878
|
+
if (await handleAccountingCommand({ command, values, flags, slrd, emit })) {
|
|
858
879
|
return;
|
|
859
880
|
}
|
|
860
881
|
|
|
@@ -986,11 +1007,8 @@ async function main() {
|
|
|
986
1007
|
(vanityResult
|
|
987
1008
|
? ` vanity suffix=${vanityResult.suffix} attempts=${vanityResult.attempts}\n`
|
|
988
1009
|
: "") +
|
|
989
|
-
` storage encrypted in Solard's local wallet database\n` +
|
|
990
|
-
` password NOT saved; you will enter it again next invocation\n` +
|
|
991
1010
|
`╰──────────────────────────────────────────────────────╯\n` +
|
|
992
|
-
`\nNext: slrd balances --wallet ${wallet.name}\n
|
|
993
|
-
`Backup: slrd export ${wallet.name} --out .\\${wallet.name}-key.txt\n`,
|
|
1011
|
+
`\nNext: slrd balances --wallet ${wallet.name}\n`,
|
|
994
1012
|
);
|
|
995
1013
|
} else {
|
|
996
1014
|
emit(`${OWL} created @${wallet.name} ${wallet.address}\n`);
|
|
@@ -1612,7 +1630,7 @@ async function main() {
|
|
|
1612
1630
|
throw new Error(
|
|
1613
1631
|
"Provide --uri <metadata_uri> or --image <local_path> --description <text>",
|
|
1614
1632
|
);
|
|
1615
|
-
const { uploadPumpMetadata } = await import("@solard/
|
|
1633
|
+
const { uploadPumpMetadata } = await import("@solard/core");
|
|
1616
1634
|
uploadedMetadata = await uploadPumpMetadata(
|
|
1617
1635
|
{
|
|
1618
1636
|
imagePath: need(flags, "image"),
|
|
@@ -1864,11 +1882,18 @@ async function main() {
|
|
|
1864
1882
|
}
|
|
1865
1883
|
if (command === "liquidate" && (values[0] ?? "tokens") === "tokens") {
|
|
1866
1884
|
const except = csv(flags.get("except"));
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
);
|
|
1871
|
-
|
|
1885
|
+
const exceptWalletValue =
|
|
1886
|
+
flags.get("except-wallet") ?? flags.get("exceptWallet");
|
|
1887
|
+
const exceptWalletsValue =
|
|
1888
|
+
flags.get("except-wallets") ?? flags.get("exceptWallets");
|
|
1889
|
+
if (exceptWalletValue === "true")
|
|
1890
|
+
throw new Error("Missing --except-wallet <wallet>");
|
|
1891
|
+
if (exceptWalletsValue === "true")
|
|
1892
|
+
throw new Error("Missing --except-wallets <a,b,...>");
|
|
1893
|
+
const exceptWalletRefs = [
|
|
1894
|
+
...(exceptWalletValue ? [exceptWalletValue] : []),
|
|
1895
|
+
...csv(exceptWalletsValue),
|
|
1896
|
+
].filter((ref, index, refs) => refs.indexOf(ref) === index);
|
|
1872
1897
|
if (flags.has("simulate") && flags.has("live")) {
|
|
1873
1898
|
throw new Error("Use either --simulate or --live, not both");
|
|
1874
1899
|
}
|
|
@@ -1884,6 +1909,10 @@ async function main() {
|
|
|
1884
1909
|
const mint = resolveTokenMintForPolicy(slrd, ref);
|
|
1885
1910
|
emit(`PROTECT ${ref} -> ${mint}\n`);
|
|
1886
1911
|
}
|
|
1912
|
+
for (const ref of exceptWalletRefs) {
|
|
1913
|
+
const wallet = slrd.resolveWallet(ref);
|
|
1914
|
+
emit(`EXCLUDE ${ref} -> ${wallet.address.toBase58()}\n`);
|
|
1915
|
+
}
|
|
1887
1916
|
emit(
|
|
1888
1917
|
`RPC hard limit ${process.env.SLRD_RPC_MAX_RPS ?? "5"} req/s\n`,
|
|
1889
1918
|
);
|
|
@@ -1899,19 +1928,27 @@ async function main() {
|
|
|
1899
1928
|
name: string | null;
|
|
1900
1929
|
amountUi: string;
|
|
1901
1930
|
kind: string;
|
|
1931
|
+
tokenAccount?: string;
|
|
1902
1932
|
},
|
|
1903
1933
|
) => {
|
|
1904
1934
|
const label = action.symbol ?? action.name ?? shortKey(action.mint);
|
|
1935
|
+
const account =
|
|
1936
|
+
action.kind === "close-empty" && action.tokenAccount
|
|
1937
|
+
? flags.has("verbose")
|
|
1938
|
+
? action.tokenAccount
|
|
1939
|
+
: shortKey(action.tokenAccount)
|
|
1940
|
+
: null;
|
|
1905
1941
|
emit(
|
|
1906
1942
|
`${prefix} ${index}/${total} @${action.walletName} ` +
|
|
1907
1943
|
`${action.kind === "unwrap-wsol" ? "WSOL" : label} ${action.amountUi}` +
|
|
1908
|
-
`${action.kind === "close-empty" ?
|
|
1944
|
+
`${action.kind === "close-empty" ? ` [close empty account${account ? ` ${account}` : ""}]` : ""}\n`,
|
|
1909
1945
|
);
|
|
1910
1946
|
};
|
|
1911
1947
|
|
|
1912
1948
|
const options = {
|
|
1913
1949
|
except,
|
|
1914
1950
|
walletRefs: csv(flags.get("wallets")),
|
|
1951
|
+
exceptWalletRefs,
|
|
1915
1952
|
slippageBps: int(flags, "slippage-bps", 1500) ?? 1500,
|
|
1916
1953
|
via: flags.get("sender") ?? "rpc",
|
|
1917
1954
|
delayMs: int(flags, "delay-ms", 0) ?? 0,
|
|
@@ -1941,6 +1978,13 @@ async function main() {
|
|
|
1941
1978
|
);
|
|
1942
1979
|
} else if (event.stage === "action-done") {
|
|
1943
1980
|
showAction("OK ", event.index, event.total, event.action);
|
|
1981
|
+
} else if (event.stage === "action-pending") {
|
|
1982
|
+
showAction("WAIT", event.index, event.total, event.action);
|
|
1983
|
+
if (flags.has("verbose")) {
|
|
1984
|
+
emit(
|
|
1985
|
+
` attempt=${event.attempt}/${event.maxAttempts} signature=${event.signature ?? "-"} ${event.reason}\n`,
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1944
1988
|
} else if (event.stage === "action-error") {
|
|
1945
1989
|
showAction("FAIL", event.index, event.total, event.action);
|
|
1946
1990
|
emit(` ${event.error}\n`);
|
|
@@ -1981,7 +2025,7 @@ async function main() {
|
|
|
1981
2025
|
}
|
|
1982
2026
|
|
|
1983
2027
|
emit(
|
|
1984
|
-
`KEEP ${plan.totals.keepProtected} protected
|
|
2028
|
+
`KEEP ${plan.totals.keepProtected} protected token holding(s)\n` +
|
|
1985
2029
|
`SKIP ${plan.totals.skipUnsupported} unsupported/no-liquidity holding(s)`,
|
|
1986
2030
|
);
|
|
1987
2031
|
if (plan.totals.skipUnsupported > 0) {
|
|
@@ -2038,13 +2082,43 @@ async function main() {
|
|
|
2038
2082
|
return;
|
|
2039
2083
|
}
|
|
2040
2084
|
|
|
2041
|
-
if (command === "sweep"
|
|
2042
|
-
const
|
|
2085
|
+
if (command === "sweep") {
|
|
2086
|
+
const legacySolSyntax = values[0]?.toLowerCase() === "sol";
|
|
2087
|
+
const positionalDestination = legacySolSyntax ? values[1] : values[0];
|
|
2088
|
+
const extraValues = legacySolSyntax ? values.slice(2) : values.slice(1);
|
|
2089
|
+
if (extraValues.length > 0) {
|
|
2090
|
+
throw new Error(
|
|
2091
|
+
`${sweepUsage()}\nUnexpected argument: ${extraValues[0]}`,
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
const destinationInput = flags.get("to") ?? positionalDestination;
|
|
2043
2096
|
if (!destinationInput || destinationInput === "true") {
|
|
2097
|
+
throw new Error(sweepUsage());
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
const below = flags.get("below");
|
|
2101
|
+
const maxBalance = flags.get("max-balance-sol");
|
|
2102
|
+
if (below === "true") {
|
|
2103
|
+
throw new Error(`--below requires a SOL amount.\n${sweepUsage()}`);
|
|
2104
|
+
}
|
|
2105
|
+
if (maxBalance === "true") {
|
|
2044
2106
|
throw new Error(
|
|
2045
|
-
|
|
2107
|
+
`--max-balance-sol requires a SOL amount.\n${sweepUsage()}`,
|
|
2046
2108
|
);
|
|
2047
2109
|
}
|
|
2110
|
+
if (
|
|
2111
|
+
below != null &&
|
|
2112
|
+
maxBalance != null &&
|
|
2113
|
+
maxBalance !== "true" &&
|
|
2114
|
+
below !== maxBalance
|
|
2115
|
+
) {
|
|
2116
|
+
throw new Error("Use either --below or --max-balance-sol, not both.");
|
|
2117
|
+
}
|
|
2118
|
+
const maxBalanceSol = below ?? maxBalance;
|
|
2119
|
+
if (!legacySolSyntax && maxBalanceSol == null) {
|
|
2120
|
+
throw new Error(`--below is required for slrd sweep.\n${sweepUsage()}`);
|
|
2121
|
+
}
|
|
2048
2122
|
|
|
2049
2123
|
const destination = resolveDestinationRef(slrd, destinationInput);
|
|
2050
2124
|
|
|
@@ -2085,17 +2159,8 @@ async function main() {
|
|
|
2085
2159
|
"Use either --keep-if-tokens or --keep-if-token, not both",
|
|
2086
2160
|
);
|
|
2087
2161
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
? "LIVE"
|
|
2091
|
-
: flags.has("simulate")
|
|
2092
|
-
? "SIMULATE"
|
|
2093
|
-
: "PLAN";
|
|
2094
|
-
if (!flags.has("json")) {
|
|
2095
|
-
emit(`SWEEP ${sweepMode} to=${destination.address}\n`);
|
|
2096
|
-
emit(
|
|
2097
|
-
`RPC hard limit ${process.env.SLRD_RPC_MAX_RPS ?? "5"} req/s\n`,
|
|
2098
|
-
);
|
|
2162
|
+
if (flags.has("simulate") && flags.has("live")) {
|
|
2163
|
+
throw new Error("Use either --simulate or --live, not both");
|
|
2099
2164
|
}
|
|
2100
2165
|
|
|
2101
2166
|
const options = {
|
|
@@ -2103,7 +2168,7 @@ async function main() {
|
|
|
2103
2168
|
excludeGroups,
|
|
2104
2169
|
excludePrefixes,
|
|
2105
2170
|
includeWallets: includeWallets.length ? includeWallets : undefined,
|
|
2106
|
-
maxBalanceSol
|
|
2171
|
+
maxBalanceSol,
|
|
2107
2172
|
keepSolByWallet,
|
|
2108
2173
|
defaultKeepSol: flags.get("default-keep-sol") ?? "0",
|
|
2109
2174
|
keepSolIfTokens: flags.get("keep-if-tokens"),
|
|
@@ -2117,34 +2182,34 @@ async function main() {
|
|
|
2117
2182
|
Math.trunc(int(flags, "rpc-delay-ms", 75) ?? 75),
|
|
2118
2183
|
),
|
|
2119
2184
|
delayMs: int(flags, "delay-ms", 0) ?? 0,
|
|
2120
|
-
onProgress:
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
);
|
|
2139
|
-
} else if (event.stage === "wallet-error") {
|
|
2140
|
-
emit(
|
|
2141
|
-
`FAIL ${event.index}/${event.total} @${event.row.walletName} ` +
|
|
2142
|
-
`${event.error}\n`,
|
|
2143
|
-
);
|
|
2185
|
+
onProgress:
|
|
2186
|
+
!flags.has("json") && flags.has("live")
|
|
2187
|
+
? (event: any) => {
|
|
2188
|
+
if (event.stage === "wallet-start") {
|
|
2189
|
+
emit(
|
|
2190
|
+
`SEND ${event.index}/${event.total} @${event.row.walletName} ` +
|
|
2191
|
+
`${formatRaw(event.row.sendLamports, 9)} SOL\n`,
|
|
2192
|
+
);
|
|
2193
|
+
} else if (event.stage === "wallet-done") {
|
|
2194
|
+
emit(
|
|
2195
|
+
`OK ${event.index}/${event.total} @${event.row.walletName}\n`,
|
|
2196
|
+
);
|
|
2197
|
+
} else if (event.stage === "wallet-error") {
|
|
2198
|
+
emit(
|
|
2199
|
+
`FAIL ${event.index}/${event.total} @${event.row.walletName} ` +
|
|
2200
|
+
`${event.error}\n`,
|
|
2201
|
+
);
|
|
2202
|
+
}
|
|
2144
2203
|
}
|
|
2145
|
-
|
|
2204
|
+
: undefined,
|
|
2146
2205
|
};
|
|
2147
2206
|
const plan = await planRegistrySolSweep(slrd, options);
|
|
2207
|
+
const sendRows = plan.rows.filter((row) => row.sendLamports > 0n);
|
|
2208
|
+
const destinationLabel = destination.contactName
|
|
2209
|
+
? `${destination.contactName} (${plan.destination})`
|
|
2210
|
+
: destination.walletName
|
|
2211
|
+
? `${destination.walletName} (${plan.destination})`
|
|
2212
|
+
: plan.destination;
|
|
2148
2213
|
|
|
2149
2214
|
const printablePlan = {
|
|
2150
2215
|
mode: flags.has("live")
|
|
@@ -2158,6 +2223,7 @@ async function main() {
|
|
|
2158
2223
|
contact: destination.contactName ?? null,
|
|
2159
2224
|
wallet: destination.walletName ?? null,
|
|
2160
2225
|
},
|
|
2226
|
+
belowSol: maxBalanceSol ?? null,
|
|
2161
2227
|
totalSendSol: formatRaw(plan.totalSendLamports, 9),
|
|
2162
2228
|
rows: plan.rows.map((row) => ({
|
|
2163
2229
|
wallet: row.walletName,
|
|
@@ -2181,36 +2247,33 @@ async function main() {
|
|
|
2181
2247
|
return;
|
|
2182
2248
|
}
|
|
2183
2249
|
|
|
2250
|
+
emit(`SWEEP SOL -> ${destinationLabel}\n`);
|
|
2251
|
+
if (maxBalanceSol != null) emit(`Threshold: < ${maxBalanceSol} SOL\n`);
|
|
2184
2252
|
emit(
|
|
2185
|
-
`
|
|
2186
|
-
`
|
|
2187
|
-
`
|
|
2253
|
+
`Scanned: ${plan.rows.length} Send: ${sendRows.length} ` +
|
|
2254
|
+
`Skipped: ${plan.rows.length - sendRows.length} ` +
|
|
2255
|
+
`Total: ${formatRaw(plan.totalSendLamports, 9)} SOL\n`,
|
|
2188
2256
|
);
|
|
2189
2257
|
|
|
2190
2258
|
for (const row of plan.rows) {
|
|
2191
2259
|
if (row.sendLamports <= 0n && !flags.has("show-skipped")) continue;
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2260
|
+
if (row.sendLamports > 0n) {
|
|
2261
|
+
emit(
|
|
2262
|
+
` @${row.walletName} ${formatRaw(row.balanceLamports, 9)} -> ` +
|
|
2263
|
+
`${formatRaw(row.sendLamports, 9)} SOL\n`,
|
|
2264
|
+
);
|
|
2265
|
+
} else {
|
|
2266
|
+
emit(
|
|
2267
|
+
` @${row.walletName} ${formatRaw(row.balanceLamports, 9)} ` +
|
|
2268
|
+
`SKIP ${row.skippedReason ?? "not-sendable"}\n`,
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2201
2271
|
}
|
|
2202
2272
|
|
|
2203
|
-
emit(
|
|
2204
|
-
`\n${OWL} plan only; no signer was decrypted and nothing was submitted. ` +
|
|
2205
|
-
`Use --json for full details, then --simulate or --live.\n`,
|
|
2206
|
-
);
|
|
2273
|
+
emit(`\nPreview only. Add --simulate to verify or --live to send.\n`);
|
|
2207
2274
|
return;
|
|
2208
2275
|
}
|
|
2209
2276
|
|
|
2210
|
-
if (flags.has("simulate") && flags.has("live")) {
|
|
2211
|
-
throw new Error("Use either --simulate or --live, not both");
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
2277
|
if (flags.has("simulate")) {
|
|
2215
2278
|
const results = await simulateRegistrySolSweep(slrd, plan, options);
|
|
2216
2279
|
if (flags.has("json")) {
|
|
@@ -2219,18 +2282,19 @@ async function main() {
|
|
|
2219
2282
|
const failed = results.filter(
|
|
2220
2283
|
(result) => result.error || result.simulation?.success === false,
|
|
2221
2284
|
).length;
|
|
2285
|
+
emit(`SWEEP SIMULATION -> ${destinationLabel}\n`);
|
|
2222
2286
|
emit(
|
|
2223
|
-
`
|
|
2224
|
-
`
|
|
2225
|
-
`to=${plan.destination}\n`,
|
|
2287
|
+
`Attempted: ${results.length} OK: ${results.length - failed} ` +
|
|
2288
|
+
`Failed: ${failed} Total: ${formatRaw(plan.totalSendLamports, 9)} SOL\n`,
|
|
2226
2289
|
);
|
|
2227
2290
|
for (const result of results) {
|
|
2228
2291
|
if (!result.error && result.simulation?.success !== false) continue;
|
|
2229
2292
|
emit(
|
|
2230
|
-
`FAIL
|
|
2293
|
+
` FAIL @${result.row.walletName} ` +
|
|
2231
2294
|
`${result.error ?? "simulation failed"}\n`,
|
|
2232
2295
|
);
|
|
2233
2296
|
}
|
|
2297
|
+
emit("No transactions submitted.\n");
|
|
2234
2298
|
}
|
|
2235
2299
|
return;
|
|
2236
2300
|
}
|
|
@@ -2242,14 +2306,14 @@ async function main() {
|
|
|
2242
2306
|
const failed = receipts.filter((result) =>
|
|
2243
2307
|
Boolean(result.error),
|
|
2244
2308
|
).length;
|
|
2309
|
+
emit(`SWEEP COMPLETE -> ${destinationLabel}\n`);
|
|
2245
2310
|
emit(
|
|
2246
|
-
`
|
|
2247
|
-
`
|
|
2248
|
-
`to=${plan.destination}\n`,
|
|
2311
|
+
`Attempted: ${receipts.length} OK: ${receipts.length - failed} ` +
|
|
2312
|
+
`Failed: ${failed} Planned total: ${formatRaw(plan.totalSendLamports, 9)} SOL\n`,
|
|
2249
2313
|
);
|
|
2250
2314
|
for (const result of receipts) {
|
|
2251
2315
|
if (!result.error) continue;
|
|
2252
|
-
emit(`FAIL
|
|
2316
|
+
emit(` FAIL @${result.row.walletName} ${result.error}\n`);
|
|
2253
2317
|
}
|
|
2254
2318
|
}
|
|
2255
2319
|
return;
|
|
@@ -2423,23 +2487,84 @@ async function main() {
|
|
|
2423
2487
|
}
|
|
2424
2488
|
if (command === "rewards") {
|
|
2425
2489
|
const action = values[0] ?? "inspect";
|
|
2490
|
+
|
|
2491
|
+
if (action === "claim-status") {
|
|
2492
|
+
const id = values[1];
|
|
2493
|
+
if (!id)
|
|
2494
|
+
throw new Error("Usage: slrd rewards claim-status <stable-claim-id>");
|
|
2495
|
+
emit(json(slrd.claims.creatorFees.status(id)) + "\n");
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2426
2499
|
const tokenRef = values[1];
|
|
2427
2500
|
if (!tokenRef)
|
|
2428
2501
|
throw new Error(
|
|
2429
|
-
"Usage: slrd rewards <inspect|claim> <token|ca> [--wallet <fee-payer>]",
|
|
2502
|
+
"Usage: slrd rewards <inspect|claim|claim-status> <token|ca> [--wallet <fee-payer>]",
|
|
2430
2503
|
);
|
|
2431
2504
|
|
|
2432
2505
|
if (action === "claim") {
|
|
2433
2506
|
const wallet = need(flags, "wallet");
|
|
2434
|
-
const
|
|
2507
|
+
const basisPath = flags.get("basis");
|
|
2508
|
+
let basis:
|
|
2509
|
+
| {
|
|
2510
|
+
id: string;
|
|
2511
|
+
slot: number;
|
|
2512
|
+
hash: string;
|
|
2513
|
+
observedAtMs?: number | null;
|
|
2514
|
+
}
|
|
2515
|
+
| undefined;
|
|
2516
|
+
if (basisPath && basisPath !== "true") {
|
|
2517
|
+
const { readFileSync } = await import("node:fs");
|
|
2518
|
+
const { resolve } = await import("node:path");
|
|
2519
|
+
const parsed = JSON.parse(
|
|
2520
|
+
readFileSync(resolve(basisPath), "utf8"),
|
|
2521
|
+
) as Record<string, unknown>;
|
|
2522
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2523
|
+
throw new Error("Reward basis file must contain a JSON object");
|
|
2524
|
+
basis = {
|
|
2525
|
+
id: String(parsed.id ?? "").trim(),
|
|
2526
|
+
slot: Number(parsed.slot),
|
|
2527
|
+
hash: String(parsed.hash ?? "").trim(),
|
|
2528
|
+
observedAtMs:
|
|
2529
|
+
parsed.observedAtMs == null ? null : Number(parsed.observedAtMs),
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
const result = await slrd.claims.creatorFees.claim(tokenRef, wallet, {
|
|
2533
|
+
id: flags.get("id"),
|
|
2534
|
+
basis,
|
|
2435
2535
|
via: flags.get("sender") ?? "rpc",
|
|
2536
|
+
skipSimulation: flags.has("skip-simulation"),
|
|
2537
|
+
skipPreflight: flags.has("skip-preflight"),
|
|
2436
2538
|
});
|
|
2437
2539
|
emit(
|
|
2438
2540
|
json({
|
|
2439
2541
|
action: "claim",
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2542
|
+
claimId: result.claimId,
|
|
2543
|
+
token: result.tokenMint,
|
|
2544
|
+
feePayer: result.feePayer,
|
|
2545
|
+
source: result.source,
|
|
2546
|
+
quote: result.quoteAsset,
|
|
2547
|
+
estimatedClaimRaw: result.estimatedClaimRaw,
|
|
2548
|
+
claimedRaw: result.claimedRaw,
|
|
2549
|
+
claimedUi:
|
|
2550
|
+
result.claimedRaw == null
|
|
2551
|
+
? null
|
|
2552
|
+
: formatRaw(result.claimedRaw, result.quoteAsset.decimals),
|
|
2553
|
+
payouts: result.payouts,
|
|
2554
|
+
receipt: result.receipt,
|
|
2555
|
+
basis: result.basis,
|
|
2556
|
+
checkpoint:
|
|
2557
|
+
result.claimedRaw == null
|
|
2558
|
+
? null
|
|
2559
|
+
: {
|
|
2560
|
+
id: result.claimId ?? result.claimSignature,
|
|
2561
|
+
claimSignature: result.claimSignature,
|
|
2562
|
+
slot: result.claimSlot,
|
|
2563
|
+
claimedRaw: result.claimedRaw,
|
|
2564
|
+
quoteMint: result.quoteAsset.mint,
|
|
2565
|
+
observedAtMs: result.observedAtMs,
|
|
2566
|
+
basis: result.basis,
|
|
2567
|
+
},
|
|
2443
2568
|
}) + "\n",
|
|
2444
2569
|
);
|
|
2445
2570
|
return;
|
|
@@ -2449,24 +2574,27 @@ async function main() {
|
|
|
2449
2574
|
let token = slrd.resolveToken(tokenRef);
|
|
2450
2575
|
try {
|
|
2451
2576
|
token = await slrd.refreshToken(token);
|
|
2452
|
-
} catch {
|
|
2453
|
-
// A registered token can still expose a useful claim plan when one
|
|
2454
|
-
// enrichment source is temporarily unavailable.
|
|
2455
|
-
}
|
|
2577
|
+
} catch {}
|
|
2456
2578
|
const { PublicKey } = await import("@solana/web3.js");
|
|
2457
2579
|
const probeUser = token.creator
|
|
2458
2580
|
? new PublicKey(token.creator)
|
|
2459
2581
|
: new PublicKey(token.mint);
|
|
2460
2582
|
const plan = await slrd.resolveClaim(token, probeUser);
|
|
2583
|
+
const payoutAddresses = plan.payouts?.length
|
|
2584
|
+
? plan.payouts.map((row) => row.address.toBase58())
|
|
2585
|
+
: typeof plan.meta?.payoutAddress === "string"
|
|
2586
|
+
? [plan.meta.payoutAddress]
|
|
2587
|
+
: token.creator
|
|
2588
|
+
? [token.creator]
|
|
2589
|
+
: [];
|
|
2461
2590
|
const payoutAddress =
|
|
2462
|
-
|
|
2463
|
-
? plan.meta.payoutAddress
|
|
2464
|
-
: token.creator;
|
|
2591
|
+
payoutAddresses.length === 1 ? payoutAddresses[0] : null;
|
|
2465
2592
|
const output = {
|
|
2466
2593
|
action: "inspect",
|
|
2467
2594
|
token: token.mint,
|
|
2468
2595
|
source: plan.source,
|
|
2469
2596
|
payoutAddress: payoutAddress ?? null,
|
|
2597
|
+
payoutAddresses,
|
|
2470
2598
|
quote: {
|
|
2471
2599
|
kind: plan.quoteAsset.kind,
|
|
2472
2600
|
mint: plan.quoteAsset.mint.toBase58(),
|
|
@@ -2486,7 +2614,7 @@ async function main() {
|
|
|
2486
2614
|
return;
|
|
2487
2615
|
}
|
|
2488
2616
|
|
|
2489
|
-
throw new Error("Usage: slrd rewards <inspect|claim>
|
|
2617
|
+
throw new Error("Usage: slrd rewards <inspect|claim|claim-status> ...");
|
|
2490
2618
|
}
|
|
2491
2619
|
|
|
2492
2620
|
if (command === "claim") {
|
|
@@ -2643,7 +2771,9 @@ async function main() {
|
|
|
2643
2771
|
);
|
|
2644
2772
|
return;
|
|
2645
2773
|
}
|
|
2646
|
-
throw new Error(
|
|
2774
|
+
throw new Error(
|
|
2775
|
+
`Unknown command: ${command}. Run "slrd help" for available commands.`,
|
|
2776
|
+
);
|
|
2647
2777
|
} finally {
|
|
2648
2778
|
slrd.close();
|
|
2649
2779
|
}
|