@solard/cli 0.2.33 → 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 +30 -4
- package/src/commands/sell.ts +19 -4
- package/src/commands/trade-fees.test.ts +18 -6
- package/src/commands/trade-fees.ts +8 -2
- package/src/commands/trade-minimum.test.ts +50 -0
- package/src/commands/trade-minimum.ts +23 -0
- package/src/commands/transfer.test.ts +75 -18
- package/src/commands/transfer.ts +34 -6
- package/src/index.ts +128 -34
- package/src/launch-watch-command.ts +301 -301
- package/src/liquidation-wallets.test.ts +27 -0
- package/src/liquidation-wallets.ts +33 -0
- package/src/price-feed-client.ts +174 -174
- package/src/price-feed-protocol.ts +45 -45
- package/src/price-feed-server.architecture.test.ts +20 -20
- package/src/price-feed-server.ts +315 -315
- package/src/pump/token-launch-cli.ts +1 -1
- package/src/refs.ts +3 -1
- package/src/rpc-management.test.ts +108 -0
- package/src/rpc-management.ts +109 -0
- package/src/trade-strategy-command.ts +1219 -1219
- 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"),
|
|
@@ -131,10 +149,18 @@ export async function runSmartBuyCommand({
|
|
|
131
149
|
.build();
|
|
132
150
|
const results = await Promise.all(
|
|
133
151
|
plans.map(async (plan, index) => {
|
|
134
|
-
const prepared = await slrd.prepareTradePlan(
|
|
135
|
-
|
|
152
|
+
const prepared = await slrd.prepareTradePlan(
|
|
153
|
+
target.refs[index]!,
|
|
154
|
+
plan,
|
|
155
|
+
options,
|
|
156
|
+
);
|
|
157
|
+
return {
|
|
158
|
+
...(await slrd.simulatePlan(prepared.plan)),
|
|
136
159
|
priorityMicroLamports: prepared.priorityMicroLamports,
|
|
137
|
-
cuLimit: prepared.plan.draft.cuLimit ?? 600_000
|
|
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,
|
|
163
|
+
};
|
|
138
164
|
}),
|
|
139
165
|
);
|
|
140
166
|
return {
|
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,
|
|
@@ -169,10 +176,18 @@ export async function runSmartSellCommand({
|
|
|
169
176
|
.build();
|
|
170
177
|
const results = await Promise.all(
|
|
171
178
|
plans.map(async (plan, index) => {
|
|
172
|
-
const prepared = await slrd.prepareTradePlan(
|
|
173
|
-
|
|
179
|
+
const prepared = await slrd.prepareTradePlan(
|
|
180
|
+
target.refs[index]!,
|
|
181
|
+
plan,
|
|
182
|
+
options,
|
|
183
|
+
);
|
|
184
|
+
return {
|
|
185
|
+
...(await slrd.simulatePlan(prepared.plan)),
|
|
174
186
|
priorityMicroLamports: prepared.priorityMicroLamports,
|
|
175
|
-
cuLimit: prepared.plan.draft.cuLimit ?? 600_000
|
|
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,
|
|
190
|
+
};
|
|
176
191
|
}),
|
|
177
192
|
);
|
|
178
193
|
return {
|
|
@@ -2,14 +2,26 @@ import { expect, test } from "bun:test";
|
|
|
2
2
|
import { tradeFeeOptions } from "./trade-fees.ts";
|
|
3
3
|
|
|
4
4
|
test("native buy/sell fee flags preserve custom compute limit and price", () => {
|
|
5
|
-
expect(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
expect(
|
|
6
|
+
tradeFeeOptions(
|
|
7
|
+
new Map([
|
|
8
|
+
["cu-limit", "150000"],
|
|
9
|
+
["priority-micro-lamports", "1000000"],
|
|
10
|
+
["trade-attempts", "4"],
|
|
11
|
+
["max-priority-fee-lamports", "500000"],
|
|
12
|
+
]),
|
|
13
|
+
),
|
|
14
|
+
).toMatchObject({
|
|
15
|
+
priorityFee: { cuLimit: 150_000, microLamports: 1_000_000 },
|
|
16
|
+
landing: { maxAttempts: 4, maxPriorityFeeLamports: 500_000 },
|
|
17
|
+
});
|
|
10
18
|
});
|
|
11
19
|
|
|
12
20
|
test("invalid fee flags reject instead of being ignored", () => {
|
|
13
|
-
for (const [name, value] of [
|
|
21
|
+
for (const [name, value] of [
|
|
22
|
+
["cu-limit", "0"],
|
|
23
|
+
["priority-micro-lamports", "-1"],
|
|
24
|
+
["priority-micro-lamports", "true"],
|
|
25
|
+
])
|
|
14
26
|
expect(() => tradeFeeOptions(new Map([[name!, value!]]))).toThrow();
|
|
15
27
|
});
|
|
@@ -10,9 +10,15 @@ export function tradeFeeOptions(flags: ReadonlyMap<string, string>) {
|
|
|
10
10
|
};
|
|
11
11
|
const cuLimit = number("cu-limit");
|
|
12
12
|
const microLamports = number("priority-micro-lamports");
|
|
13
|
-
if (
|
|
13
|
+
if (
|
|
14
|
+
cuLimit != null &&
|
|
15
|
+
(!Number.isInteger(cuLimit) || cuLimit < 1 || cuLimit > 1_400_000)
|
|
16
|
+
)
|
|
14
17
|
throw new Error("--cu-limit must be an integer between 1 and 1400000");
|
|
15
|
-
if (
|
|
18
|
+
if (
|
|
19
|
+
microLamports != null &&
|
|
20
|
+
(!Number.isSafeInteger(microLamports) || microLamports < 0)
|
|
21
|
+
)
|
|
16
22
|
throw new Error("--priority-micro-lamports must be a non-negative integer");
|
|
17
23
|
const landing: TradeLandingPolicy = {
|
|
18
24
|
maxAttempts: number("trade-attempts"),
|
|
@@ -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
|
+
}
|
|
@@ -3,31 +3,88 @@ import { Keypair } from "@solana/web3.js";
|
|
|
3
3
|
import { Solard, estimatePlanFee, type PlannedTransaction } from "@solard/core";
|
|
4
4
|
import { runTransferCommand } from "./transfer.ts";
|
|
5
5
|
function fixture(status: "confirmed" | "submitted" | "failed" = "confirmed") {
|
|
6
|
-
const core: Solard = Object.create(Solard.prototype);
|
|
7
|
-
const
|
|
6
|
+
const core: Solard = Object.create(Solard.prototype);
|
|
7
|
+
const payer = Keypair.generate();
|
|
8
|
+
const prices: number[] = [];
|
|
9
|
+
let samples = 0;
|
|
8
10
|
core.signer = () => payer;
|
|
9
|
-
core.connection = () =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
core.connection = () =>
|
|
12
|
+
({
|
|
13
|
+
getRecentPrioritizationFees: async () => {
|
|
14
|
+
samples++;
|
|
15
|
+
return [200_000, 400_000, 800_000, 1_000_000].map(
|
|
16
|
+
(prioritizationFee) => ({ prioritizationFee }),
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
getFeeForMessage: async (message: { price: number }) => ({
|
|
20
|
+
value: 5000 + Math.ceil((10_000 * message.price) / 1_000_000),
|
|
21
|
+
}),
|
|
22
|
+
}) as any;
|
|
23
|
+
core.compile = async (_payer, draft) =>
|
|
24
|
+
({
|
|
25
|
+
draft,
|
|
26
|
+
payer: payer.publicKey,
|
|
27
|
+
transaction: {
|
|
28
|
+
message: { price: draft.cuPriceMicroLamports ?? 100_000 },
|
|
29
|
+
},
|
|
30
|
+
}) as unknown as PlannedTransaction;
|
|
31
|
+
core.submitPlan = async (plan, sender) => {
|
|
32
|
+
prices.push(plan.draft.cuPriceMicroLamports!);
|
|
33
|
+
return {
|
|
34
|
+
signature: "test-signature",
|
|
35
|
+
executionId: 1,
|
|
36
|
+
sender,
|
|
37
|
+
plan,
|
|
38
|
+
feeEstimate: await estimatePlanFee(core.connection(), plan),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
core.settleSubmission = async () => ({
|
|
42
|
+
signature: "test-signature",
|
|
43
|
+
sender: "rpc",
|
|
44
|
+
status,
|
|
45
|
+
slot: status === "confirmed" ? 1 : null,
|
|
46
|
+
...(status === "confirmed" ? { feeLamports: 13000 } : {}),
|
|
47
|
+
});
|
|
48
|
+
Object.defineProperty(core, "executions", {
|
|
49
|
+
value: { get: () => ({ metaJson: null }) },
|
|
50
|
+
});
|
|
17
51
|
return { core, prices, samples: () => samples };
|
|
18
52
|
}
|
|
19
|
-
const flags = (extra: Array<[string, string]> = []) =>
|
|
53
|
+
const flags = (extra: Array<[string, string]> = []) =>
|
|
54
|
+
new Map([["wallet", "bags"], ["sol", "2"], ["live", "true"], ...extra]);
|
|
20
55
|
test("SOL transfer uses writable market fees and reports selected versus actual network fees", async () => {
|
|
21
56
|
const ctx = fixture();
|
|
22
|
-
const result = await runTransferCommand({
|
|
23
|
-
|
|
24
|
-
|
|
57
|
+
const result = await runTransferCommand({
|
|
58
|
+
slrd: ctx.core,
|
|
59
|
+
values: [Keypair.generate().publicKey.toBase58()],
|
|
60
|
+
flags: flags(),
|
|
61
|
+
});
|
|
62
|
+
expect(ctx.prices).toEqual([800_000]);
|
|
63
|
+
expect(ctx.samples()).toBe(1);
|
|
64
|
+
expect(result.status).toBe("confirmed");
|
|
65
|
+
expect(result.feeEstimate).toEqual({
|
|
66
|
+
cuLimit: 10_000,
|
|
67
|
+
priorityMicroLamports: 800_000,
|
|
68
|
+
priorityFeeLamports: 8000,
|
|
69
|
+
estimatedBaseFeeLamports: 5000,
|
|
70
|
+
estimatedNetworkFeeLamports: 13000,
|
|
71
|
+
});
|
|
25
72
|
expect(result.feeLamports).toBe(13000);
|
|
26
73
|
});
|
|
27
74
|
test("explicit zero disables transfer fee estimation and pending outcome is never repeated blindly", async () => {
|
|
28
75
|
const ctx = fixture("submitted");
|
|
29
|
-
const result = await runTransferCommand({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
76
|
+
const result = await runTransferCommand({
|
|
77
|
+
slrd: ctx.core,
|
|
78
|
+
values: [Keypair.generate().publicKey.toBase58()],
|
|
79
|
+
flags: flags([["priority-micro-lamports", "0"]]),
|
|
80
|
+
});
|
|
81
|
+
expect(ctx.prices).toEqual([0]);
|
|
82
|
+
expect(ctx.samples()).toBe(0);
|
|
83
|
+
expect(result.status).toBe("unresolved");
|
|
84
|
+
expect(result.code).toBe("UNRESOLVED");
|
|
85
|
+
expect(result.feeLamports).toBeUndefined();
|
|
86
|
+
expect(result.feeEstimate).toMatchObject({
|
|
87
|
+
priorityFeeLamports: 0,
|
|
88
|
+
estimatedNetworkFeeLamports: 5000,
|
|
89
|
+
});
|
|
33
90
|
});
|
package/src/commands/transfer.ts
CHANGED
|
@@ -144,7 +144,11 @@ export async function runTransferCommand({
|
|
|
144
144
|
.priorityFee({ cuLimit, microLamports: priorityMicroLamports });
|
|
145
145
|
|
|
146
146
|
if (flags.has("simulate-only")) {
|
|
147
|
-
const prepared = await slrd.prepareTradePlan(
|
|
147
|
+
const prepared = await slrd.prepareTradePlan(
|
|
148
|
+
wallet,
|
|
149
|
+
await composer.build(),
|
|
150
|
+
executionOptions,
|
|
151
|
+
);
|
|
148
152
|
const result = await slrd.simulatePlan(prepared.plan);
|
|
149
153
|
return {
|
|
150
154
|
mode: "simulation",
|
|
@@ -162,8 +166,18 @@ export async function runTransferCommand({
|
|
|
162
166
|
};
|
|
163
167
|
}
|
|
164
168
|
|
|
165
|
-
const execution = await slrd.executeTradePlan(
|
|
166
|
-
|
|
169
|
+
const execution = await slrd.executeTradePlan(
|
|
170
|
+
wallet,
|
|
171
|
+
() => composer.build(),
|
|
172
|
+
via,
|
|
173
|
+
"transfer-token",
|
|
174
|
+
executionOptions,
|
|
175
|
+
);
|
|
176
|
+
const receipt = tradeResult(
|
|
177
|
+
execution.receipt,
|
|
178
|
+
execution.attempts,
|
|
179
|
+
execution.submission.executionId,
|
|
180
|
+
);
|
|
167
181
|
|
|
168
182
|
return {
|
|
169
183
|
...receipt,
|
|
@@ -184,7 +198,11 @@ export async function runTransferCommand({
|
|
|
184
198
|
.priorityFee({ cuLimit, microLamports: priorityMicroLamports });
|
|
185
199
|
|
|
186
200
|
if (flags.has("simulate-only")) {
|
|
187
|
-
const prepared = await slrd.prepareTradePlan(
|
|
201
|
+
const prepared = await slrd.prepareTradePlan(
|
|
202
|
+
wallet,
|
|
203
|
+
await composer.build(),
|
|
204
|
+
executionOptions,
|
|
205
|
+
);
|
|
188
206
|
const result = await slrd.simulatePlan(prepared.plan);
|
|
189
207
|
return {
|
|
190
208
|
mode: "simulation",
|
|
@@ -198,8 +216,18 @@ export async function runTransferCommand({
|
|
|
198
216
|
};
|
|
199
217
|
}
|
|
200
218
|
|
|
201
|
-
const execution = await slrd.executeTradePlan(
|
|
202
|
-
|
|
219
|
+
const execution = await slrd.executeTradePlan(
|
|
220
|
+
wallet,
|
|
221
|
+
() => composer.build(),
|
|
222
|
+
via,
|
|
223
|
+
"transfer-sol",
|
|
224
|
+
executionOptions,
|
|
225
|
+
);
|
|
226
|
+
const receipt = tradeResult(
|
|
227
|
+
execution.receipt,
|
|
228
|
+
execution.attempts,
|
|
229
|
+
execution.submission.executionId,
|
|
230
|
+
);
|
|
203
231
|
|
|
204
232
|
return { ...receipt, recipient, wallet, sol: amount };
|
|
205
233
|
}
|