@solard/cli 0.2.37 → 0.2.40
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 +3 -3
- package/src/commands/buy.ts +21 -1
- package/src/commands/sell.ts +10 -1
- package/src/commands/trade-minimum.test.ts +50 -0
- package/src/commands/trade-minimum.ts +23 -0
- package/src/index.ts +3421 -3327
- package/src/liquidation-wallets.test.ts +27 -0
- package/src/liquidation-wallets.ts +33 -0
- package/src/pump/token-launch-cli.ts +1 -1
- package/src/rpc-management.test.ts +108 -0
- package/src/rpc-management.ts +109 -0
- package/src/wallet-group-filter.test.ts +10 -0
- package/src/wallet-group-filter.ts +12 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solard/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
4
4
|
"description": "Command-line interface for the Solard SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@solana/web3.js": "^1.98.4",
|
|
22
|
-
"@solard/core": "0.2.
|
|
23
|
-
"@solard/sdk": "0.2.
|
|
22
|
+
"@solard/core": "0.2.40",
|
|
23
|
+
"@solard/sdk": "0.2.40",
|
|
24
24
|
"bs58": "^6.0.0",
|
|
25
25
|
"measure-fn": "5.2.0"
|
|
26
26
|
},
|
package/src/commands/buy.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { hasOutputMinimum, parseOutputMinimum } from "./trade-minimum.ts";
|
|
1
2
|
import {
|
|
2
3
|
executeJupiterSwap,
|
|
4
|
+
minimumBuyOutputForPrice,
|
|
3
5
|
NATIVE_SOL_MINT,
|
|
4
6
|
nativePumpTradeAvailable,
|
|
5
7
|
quoteJupiterSwap,
|
|
@@ -74,7 +76,7 @@ export async function runSmartBuyCommand({
|
|
|
74
76
|
}: RunSmartBuyCommandArgs): Promise<SmartBuyCommandResult> {
|
|
75
77
|
const tokenRef = values[0];
|
|
76
78
|
const usage =
|
|
77
|
-
"Usage: slrd buy <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>) --sol <amount> [--venue auto|native|jupiter]";
|
|
79
|
+
"Usage: slrd buy <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>) --sol <amount> [--venue auto|native|jupiter] [--max-price-sol <SOL/token>] [--min-out <amount> | --min-out-raw <units> | --slippage-bps N]";
|
|
78
80
|
if (!tokenRef) throw new Error(usage);
|
|
79
81
|
|
|
80
82
|
const amountSol = flags.get("sol");
|
|
@@ -98,6 +100,20 @@ export async function runSmartBuyCommand({
|
|
|
98
100
|
);
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
const maxPriceSol = flags.get("max-price-sol");
|
|
104
|
+
if (flags.has("max-price-sol") && (!maxPriceSol || maxPriceSol === "true"))
|
|
105
|
+
throw new Error("--max-price-sol requires a positive SOL price per token");
|
|
106
|
+
if (maxPriceSol != null && flags.has("slippage-bps"))
|
|
107
|
+
throw new Error("Use --max-price-sol instead of --slippage-bps");
|
|
108
|
+
let outputMinimumRaw = parseOutputMinimum(flags, resolution.asset.decimals);
|
|
109
|
+
if (maxPriceSol != null) {
|
|
110
|
+
const priceMinimum = minimumBuyOutputForPrice(amount.raw, resolution.asset.decimals, maxPriceSol);
|
|
111
|
+
if (outputMinimumRaw == null || priceMinimum > outputMinimumRaw) outputMinimumRaw = priceMinimum;
|
|
112
|
+
}
|
|
113
|
+
if ((hasOutputMinimum(flags) || maxPriceSol != null) && resolution.route !== "native") {
|
|
114
|
+
throw new Error("Absolute minimum output is currently supported on native Pump/PumpSwap routes; Jupiter orders do not expose a verified on-chain minimum");
|
|
115
|
+
}
|
|
116
|
+
|
|
101
117
|
if (resolution.route === "native") {
|
|
102
118
|
if (!nativePumpTradeAvailable(resolution.token)) {
|
|
103
119
|
throw new Error(
|
|
@@ -107,6 +123,8 @@ export async function runSmartBuyCommand({
|
|
|
107
123
|
|
|
108
124
|
const options = {
|
|
109
125
|
...tradeFeeOptions(flags),
|
|
126
|
+
outputMinimumRaw,
|
|
127
|
+
maxPriceSol,
|
|
110
128
|
slippageBps: intFlag(flags, "slippage-bps", 1_500),
|
|
111
129
|
via: (flags.get("sender") ?? "rpc") as SenderId,
|
|
112
130
|
skipSimulation: flags.has("skip-simulation"),
|
|
@@ -140,6 +158,8 @@ export async function runSmartBuyCommand({
|
|
|
140
158
|
...(await slrd.simulatePlan(prepared.plan)),
|
|
141
159
|
priorityMicroLamports: prepared.priorityMicroLamports,
|
|
142
160
|
cuLimit: prepared.plan.draft.cuLimit ?? 600_000,
|
|
161
|
+
requestedMinimumOutputRaw: outputMinimumRaw?.toString(),
|
|
162
|
+
guaranteedOutputRaw: prepared.plan.draft.actions.find(action => action.kind === "buy")?.meta?.minOutputRaw,
|
|
143
163
|
};
|
|
144
164
|
}),
|
|
145
165
|
);
|
package/src/commands/sell.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hasOutputMinimum, parseOutputMinimum } from "./trade-minimum.ts";
|
|
1
2
|
import {
|
|
2
3
|
executeJupiterSwap,
|
|
3
4
|
NATIVE_SOL_MINT,
|
|
@@ -118,7 +119,7 @@ export async function runSmartSellCommand({
|
|
|
118
119
|
}: RunSmartSellCommandArgs): Promise<SmartSellCommandResult> {
|
|
119
120
|
const tokenRef = values[0];
|
|
120
121
|
const usage =
|
|
121
|
-
"Usage: slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>) [--bps 10000] [--venue auto|native|jupiter]";
|
|
122
|
+
"Usage: slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <group>) [--bps 10000] [--venue auto|native|jupiter] [--min-out <amount> | --min-out-raw <units> | --slippage-bps N]";
|
|
122
123
|
if (!tokenRef) throw new Error(usage);
|
|
123
124
|
|
|
124
125
|
const bps = intFlag(flags, "bps", 10_000);
|
|
@@ -135,6 +136,11 @@ export async function runSmartSellCommand({
|
|
|
135
136
|
throw new Error("slrd sell expects an SPL token");
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
const outputMinimumRaw = parseOutputMinimum(flags, 9);
|
|
140
|
+
if (hasOutputMinimum(flags) && resolution.route !== "native") {
|
|
141
|
+
throw new Error("Absolute minimum output is currently supported on native Pump/PumpSwap routes; Jupiter orders do not expose a verified on-chain minimum");
|
|
142
|
+
}
|
|
143
|
+
|
|
138
144
|
if (resolution.route === "native") {
|
|
139
145
|
if (!nativePumpTradeAvailable(resolution.token)) {
|
|
140
146
|
throw new Error(
|
|
@@ -144,6 +150,7 @@ export async function runSmartSellCommand({
|
|
|
144
150
|
|
|
145
151
|
const options = {
|
|
146
152
|
...tradeFeeOptions(flags),
|
|
153
|
+
outputMinimumRaw,
|
|
147
154
|
bps,
|
|
148
155
|
slippageBps: intFlag(flags, "slippage-bps", 1_500),
|
|
149
156
|
via: (flags.get("sender") ?? "rpc") as SenderId,
|
|
@@ -178,6 +185,8 @@ export async function runSmartSellCommand({
|
|
|
178
185
|
...(await slrd.simulatePlan(prepared.plan)),
|
|
179
186
|
priorityMicroLamports: prepared.priorityMicroLamports,
|
|
180
187
|
cuLimit: prepared.plan.draft.cuLimit ?? 600_000,
|
|
188
|
+
requestedMinimumOutputRaw: outputMinimumRaw?.toString(),
|
|
189
|
+
guaranteedOutputRaw: prepared.plan.draft.actions.find(action => action.kind === "sell")?.meta?.minOutputRaw,
|
|
181
190
|
};
|
|
182
191
|
}),
|
|
183
192
|
);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { parseOutputMinimum } from "./trade-minimum.ts";
|
|
3
|
+
import { runSmartBuyCommand } from "./buy.ts";
|
|
4
|
+
import { runSmartSellCommand } from "./sell.ts";
|
|
5
|
+
import type { Solard } from "@solard/core";
|
|
6
|
+
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
7
|
+
const mint = "E4R6KvcnxvoHYBe872byyCeoyExGHja8n5Vo3JNWodus";
|
|
8
|
+
test("absolute output parsing preserves precision and rejects ambiguous or unsafe input", () => {
|
|
9
|
+
expect(parseOutputMinimum(new Map([["min-out", "123.000001"]]), 6)).toBe(123000001n);
|
|
10
|
+
expect(parseOutputMinimum(new Map([["min-out", "0.001"]]), 9)).toBe(1000000n);
|
|
11
|
+
expect(parseOutputMinimum(new Map([["min-out-raw", "9007199254740993"]]), 6)).toBe(9007199254740993n);
|
|
12
|
+
for (const value of ["true", "0", "-1", "1e3", "0.0000001", "18446744073709551616"]) {
|
|
13
|
+
expect(() => parseOutputMinimum(new Map([["min-out", value]]), 6)).toThrow();
|
|
14
|
+
}
|
|
15
|
+
expect(() => parseOutputMinimum(new Map([["min-out", "1"], ["slippage-bps", "100"]]), 6)).toThrow();
|
|
16
|
+
expect(() => parseOutputMinimum(new Map([["min-out", "1"], ["min-out-raw", "1"]]), 6)).toThrow();
|
|
17
|
+
});
|
|
18
|
+
test("CLI buy/sell forward per-wallet absolute output floors in correct units", async () => {
|
|
19
|
+
const seen: unknown[] = [];
|
|
20
|
+
const host = {
|
|
21
|
+
tokens: { list: () => [{ mint, decimals: 6, baseTokenProgram: TOKEN_PROGRAM_ID.toBase58(), venueHint: "pumpswap" }] },
|
|
22
|
+
buy: async (_mint: unknown, _wallet: unknown, _amount: unknown, options: unknown) => { seen.push(options); },
|
|
23
|
+
sell: async (_mint: unknown, _wallet: unknown, options: unknown) => { seen.push(options); },
|
|
24
|
+
} as unknown as Solard;
|
|
25
|
+
await runSmartBuyCommand({ slrd: host, values: [mint], flags: new Map([["wallet", "test"], ["sol", "0.001"], ["min-out", "123.5"]]) });
|
|
26
|
+
await runSmartSellCommand({ slrd: host, values: [mint], flags: new Map([["wallet", "test"], ["min-out", "0.001"]]) });
|
|
27
|
+
expect(seen[0]).toMatchObject({ outputMinimumRaw: 123500000n });
|
|
28
|
+
expect(seen[1]).toMatchObject({ outputMinimumRaw: 1000000n });
|
|
29
|
+
});
|
|
30
|
+
test("Jupiter absolute-floor requests fail explicitly without signing", async () => {
|
|
31
|
+
const host = { tokens: { list: () => [] }, signer: () => { throw new Error("SIGNER_ACCESSED"); } } as unknown as Solard;
|
|
32
|
+
await expect(runSmartBuyCommand({ slrd: host, values: ["USDC"],
|
|
33
|
+
flags: new Map([["wallet", "test"], ["sol", "0.001"], ["min-out", "1"]]),
|
|
34
|
+
})).rejects.toThrow("verified on-chain minimum");
|
|
35
|
+
});
|
|
36
|
+
test("maximum buy price derives the on-chain floor and combines with stricter token minima", async () => {
|
|
37
|
+
const seen: unknown[] = [];
|
|
38
|
+
const host = {
|
|
39
|
+
tokens: { list: () => [{ mint, decimals: 6, baseTokenProgram: TOKEN_PROGRAM_ID.toBase58(), venueHint: "pumpswap" }] },
|
|
40
|
+
buy: async (_mint: unknown, _wallet: unknown, _amount: unknown, options: unknown) => { seen.push(options); },
|
|
41
|
+
} as unknown as Solard;
|
|
42
|
+
const flags = new Map([["wallet", "test"], ["sol", "0.1"], ["max-price-sol", "0.0001"]]);
|
|
43
|
+
await runSmartBuyCommand({ slrd: host, values: [mint], flags });
|
|
44
|
+
expect(seen[0]).toMatchObject({ maxPriceSol: "0.0001", outputMinimumRaw: 1000000000n });
|
|
45
|
+
flags.set("min-out", "1100");
|
|
46
|
+
await runSmartBuyCommand({ slrd: host, values: [mint], flags });
|
|
47
|
+
expect(seen[1]).toMatchObject({ outputMinimumRaw: 1100000000n });
|
|
48
|
+
flags.delete("min-out"); flags.set("slippage-bps", "100");
|
|
49
|
+
await expect(runSmartBuyCommand({ slrd: host, values: [mint], flags })).rejects.toThrow("instead of");
|
|
50
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TradeCommandFlags } from "./trade-venue.ts";
|
|
2
|
+
|
|
3
|
+
export function hasOutputMinimum(flags: TradeCommandFlags): boolean {
|
|
4
|
+
return flags.has("min-out") || flags.has("min-out-raw");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function parseOutputMinimum(flags: TradeCommandFlags, decimals: number): bigint | undefined {
|
|
8
|
+
if (!hasOutputMinimum(flags)) return undefined;
|
|
9
|
+
if (flags.has("slippage-bps")) throw new Error("Use --min-out or --min-out-raw instead of --slippage-bps");
|
|
10
|
+
if (flags.has("min-out") && flags.has("min-out-raw")) throw new Error("Choose --min-out or --min-out-raw");
|
|
11
|
+
const raw = flags.has("min-out-raw");
|
|
12
|
+
const value = flags.get(raw ? "min-out-raw" : "min-out") ?? "";
|
|
13
|
+
if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > 255)
|
|
14
|
+
throw new Error("Output mint decimals unavailable");
|
|
15
|
+
if (!(raw ? /^\d+$/ : /^\d+(?:\.\d+)?$/).test(value))
|
|
16
|
+
throw new Error("Minimum output must be a positive decimal amount (integer for --min-out-raw)");
|
|
17
|
+
const [whole, fraction = ""] = value.split(".");
|
|
18
|
+
if (!raw && fraction.length > decimals) throw new Error(`Minimum output supports at most ${decimals} decimal places`);
|
|
19
|
+
const amount = raw ? BigInt(value)
|
|
20
|
+
: BigInt(whole!) * 10n ** BigInt(decimals) + BigInt(fraction.padEnd(decimals, "0") || "0");
|
|
21
|
+
if (amount <= 0n || amount > 0xffffffffffffffffn) throw new Error("Minimum output must be a positive u64");
|
|
22
|
+
return amount;
|
|
23
|
+
}
|