@solard/cli 0.2.2 → 0.2.3
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/bin/solard.ts +1 -1
- package/package.json +7 -3
- package/src/index.ts +5 -4
- package/src/pump/spam-buy-cli.ts +314 -0
- package/src/pump/token-launch-cli.ts +1652 -0
package/bin/solard.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import "../src/index.
|
|
2
|
+
import "../src/index.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solard/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Command-line interface for the Solard SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
"bun": ">=1.1.0"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@solard/sdk": "0.2.
|
|
22
|
-
"bs58": "^6.0.0"
|
|
21
|
+
"@solard/sdk": "^0.2.1",
|
|
22
|
+
"bs58": "^6.0.0",
|
|
23
|
+
"@solana/web3.js": "^1.98.4"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"typecheck": "tsc --noEmit -p ../../tsconfig.json"
|
|
23
27
|
}
|
|
24
28
|
}
|
package/src/index.ts
CHANGED
|
@@ -249,12 +249,12 @@ async function main() {
|
|
|
249
249
|
return;
|
|
250
250
|
}
|
|
251
251
|
if (command === "spam-buy" || command === "buy-spam") {
|
|
252
|
-
const { runPumpSpamBuyFromArgs } = await import("
|
|
252
|
+
const { runPumpSpamBuyFromArgs } = await import("./pump/spam-buy-cli.ts");
|
|
253
253
|
await runPumpSpamBuyFromArgs(rest);
|
|
254
254
|
return;
|
|
255
255
|
}
|
|
256
256
|
if (command === "buy" && flags.has("spam")) {
|
|
257
|
-
const { runPumpSpamBuyFromArgs } = await import("
|
|
257
|
+
const { runPumpSpamBuyFromArgs } = await import("./pump/spam-buy-cli.ts");
|
|
258
258
|
await runPumpSpamBuyFromArgs(rest);
|
|
259
259
|
return;
|
|
260
260
|
}
|
|
@@ -273,7 +273,8 @@ async function main() {
|
|
|
273
273
|
command === "launch" &&
|
|
274
274
|
(values[0] === "pump" || values[0] === "pump-token")
|
|
275
275
|
) {
|
|
276
|
-
const { runPumpTokenLaunchFromArgs } =
|
|
276
|
+
const { runPumpTokenLaunchFromArgs } =
|
|
277
|
+
await import("./pump/token-launch-cli.ts");
|
|
277
278
|
await runPumpTokenLaunchFromArgs(rest.slice(1), {
|
|
278
279
|
defaultSubmitMode: "after-deploy-processed",
|
|
279
280
|
defaultDeploymentPriorityMicroLamports: 500_000,
|
|
@@ -918,4 +919,4 @@ main().catch((error) => {
|
|
|
918
919
|
process.exitCode = 1;
|
|
919
920
|
});
|
|
920
921
|
|
|
921
|
-
export { formatCliError } from "
|
|
922
|
+
export { formatCliError } from "./pump/token-launch-cli.ts";
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createTraderSolard,
|
|
5
|
+
installPumpLaunchSenders,
|
|
6
|
+
loadExplicitBuyerAllocations,
|
|
7
|
+
runPumpSpamBuyers,
|
|
8
|
+
sol,
|
|
9
|
+
validateHeliusTip,
|
|
10
|
+
type ExplicitBuyerAmount,
|
|
11
|
+
type ExplicitBuyerPlanRow,
|
|
12
|
+
type LaunchReporter,
|
|
13
|
+
type PumpSpamBuyRunResult,
|
|
14
|
+
type PumpSpamBuySettings,
|
|
15
|
+
type SenderId,
|
|
16
|
+
} from "@solard/sdk";
|
|
17
|
+
import {
|
|
18
|
+
bigintFlag,
|
|
19
|
+
enabled,
|
|
20
|
+
first,
|
|
21
|
+
json,
|
|
22
|
+
numberFlag,
|
|
23
|
+
parseArgs,
|
|
24
|
+
pumpLaunchEnvironmentFromFlags,
|
|
25
|
+
required,
|
|
26
|
+
} from "./token-launch-cli.ts";
|
|
27
|
+
|
|
28
|
+
export type PumpSpamBuyCliOptions = {
|
|
29
|
+
report?: LaunchReporter;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function defaultReport(label: string, value: unknown): void {
|
|
33
|
+
console.log(`${label}: ${json(value)}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function selectedMint(
|
|
37
|
+
flags: ReturnType<typeof parseArgs>["flags"],
|
|
38
|
+
positionals: string[],
|
|
39
|
+
): string {
|
|
40
|
+
const values = positionals[0] === "pump" ? positionals.slice(1) : positionals;
|
|
41
|
+
const positionalMint = values[0];
|
|
42
|
+
const flagMint = first(flags, "mint");
|
|
43
|
+
const mint = flagMint && flagMint !== "true" ? flagMint : positionalMint;
|
|
44
|
+
if (!mint) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Usage: bun run scripts/spam-pump-buyers.ts --mint <mint> (--wallet <wallet> | --group <group>) --amount-mode <range-bps|exact-sol|exact-lamports> [amount flags] [--live]",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (flagMint && positionalMint && flagMint !== positionalMint) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Conflicting mint values: --mint ${flagMint} and positional ${positionalMint}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return new PublicKey(mint).toBase58();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function amountFromFlags(
|
|
58
|
+
flags: ReturnType<typeof parseArgs>["flags"],
|
|
59
|
+
): ExplicitBuyerAmount {
|
|
60
|
+
const requestedMode = first(flags, "amount-mode")?.trim().toLowerCase();
|
|
61
|
+
const exactSol = first(flags, "sol") ?? first(flags, "exact-sol");
|
|
62
|
+
const exactLamports =
|
|
63
|
+
first(flags, "lamports") ?? first(flags, "exact-lamports");
|
|
64
|
+
const hasRangeFlags =
|
|
65
|
+
flags.has("min-bps") ||
|
|
66
|
+
flags.has("max-bps") ||
|
|
67
|
+
flags.has("buyer-min-bps") ||
|
|
68
|
+
flags.has("buyer-max-bps");
|
|
69
|
+
|
|
70
|
+
const inferredMode =
|
|
71
|
+
exactSol != null
|
|
72
|
+
? "exact-sol"
|
|
73
|
+
: exactLamports != null
|
|
74
|
+
? "exact-lamports"
|
|
75
|
+
: hasRangeFlags
|
|
76
|
+
? "range-bps"
|
|
77
|
+
: null;
|
|
78
|
+
const modeAliases: Record<
|
|
79
|
+
string,
|
|
80
|
+
"range-bps" | "exact-sol" | "exact-lamports"
|
|
81
|
+
> = {
|
|
82
|
+
"range-bps": "range-bps",
|
|
83
|
+
range: "range-bps",
|
|
84
|
+
"balance-bps": "range-bps",
|
|
85
|
+
"exact-sol": "exact-sol",
|
|
86
|
+
sol: "exact-sol",
|
|
87
|
+
"exact-lamports": "exact-lamports",
|
|
88
|
+
lamports: "exact-lamports",
|
|
89
|
+
};
|
|
90
|
+
if (requestedMode && !modeAliases[requestedMode]) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Invalid --amount-mode ${requestedMode}. Use range-bps, exact-sol, or exact-lamports.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const mode = requestedMode ? modeAliases[requestedMode] : inferredMode;
|
|
96
|
+
|
|
97
|
+
if (!mode) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
"Missing amount configuration. Use --amount-mode range-bps with --min-bps/--max-bps, --amount-mode exact-sol with --sol, or --amount-mode exact-lamports with --lamports.",
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (inferredMode && mode !== inferredMode) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`--amount-mode ${requestedMode} conflicts with the supplied amount flags.`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (mode === "exact-sol") {
|
|
109
|
+
if (exactSol == null || exactSol === "true") {
|
|
110
|
+
throw new Error("--amount-mode exact-sol requires --sol <amount>.");
|
|
111
|
+
}
|
|
112
|
+
return { kind: "exact-sol", sol: exactSol };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (mode === "exact-lamports") {
|
|
116
|
+
if (exactLamports == null || exactLamports === "true") {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"--amount-mode exact-lamports requires --lamports <amount>.",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
kind: "exact-lamports",
|
|
123
|
+
lamports: BigInt(exactLamports),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const minKey = flags.has("min-bps") ? "min-bps" : "buyer-min-bps";
|
|
128
|
+
const maxKey = flags.has("max-bps") ? "max-bps" : "buyer-max-bps";
|
|
129
|
+
if (!flags.has(minKey) || !flags.has(maxKey)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"--amount-mode range-bps requires both --min-bps <n> and --max-bps <n>.",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const reserveLamports = flags.has("reserve-lamports")
|
|
136
|
+
? bigintFlag(flags, "reserve-lamports", 0n)
|
|
137
|
+
: flags.has("buyer-reserve-lamports")
|
|
138
|
+
? bigintFlag(flags, "buyer-reserve-lamports", 0n)
|
|
139
|
+
: sol(
|
|
140
|
+
first(flags, "reserve-sol") ??
|
|
141
|
+
first(flags, "buyer-reserve-sol") ??
|
|
142
|
+
"0.01",
|
|
143
|
+
).raw;
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
kind: "balance-bps",
|
|
147
|
+
minBps: numberFlag(flags, minKey, 0),
|
|
148
|
+
maxBps: numberFlag(flags, maxKey, 0),
|
|
149
|
+
reserveLamports,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function buyerRows(args: {
|
|
154
|
+
flags: ReturnType<typeof parseArgs>["flags"];
|
|
155
|
+
amount: ExplicitBuyerAmount;
|
|
156
|
+
}): ExplicitBuyerPlanRow[] {
|
|
157
|
+
const wallet = first(args.flags, "wallet");
|
|
158
|
+
const group = first(args.flags, "group");
|
|
159
|
+
if (Boolean(wallet) === Boolean(group)) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
"Supply exactly one of --wallet <wallet> or --group <group>.",
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (wallet) return [{ wallet, amount: args.amount }];
|
|
166
|
+
|
|
167
|
+
// Group expansion is intentionally deferred until Solard is available below.
|
|
168
|
+
return [{ wallet: `group:${group}`, amount: args.amount }];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function settingsFromFlags(args: {
|
|
172
|
+
flags: ReturnType<typeof parseArgs>["flags"];
|
|
173
|
+
sender: SenderId;
|
|
174
|
+
tipAccount?: string;
|
|
175
|
+
tipLamports?: bigint;
|
|
176
|
+
defaults: ReturnType<typeof pumpLaunchEnvironmentFromFlags>;
|
|
177
|
+
}): PumpSpamBuySettings {
|
|
178
|
+
return {
|
|
179
|
+
sender: args.sender,
|
|
180
|
+
tip:
|
|
181
|
+
String(args.sender) === "helius-fast" &&
|
|
182
|
+
args.tipAccount &&
|
|
183
|
+
args.tipLamports != null
|
|
184
|
+
? { account: args.tipAccount, lamports: args.tipLamports }
|
|
185
|
+
: {},
|
|
186
|
+
cuLimit: numberFlag(args.flags, "cu-limit", args.defaults.cuLimit),
|
|
187
|
+
priorityMicroLamports: numberFlag(
|
|
188
|
+
args.flags,
|
|
189
|
+
"priority-micro-lamports",
|
|
190
|
+
args.defaults.priorityMicroLamports,
|
|
191
|
+
),
|
|
192
|
+
slippageBps: numberFlag(args.flags, "slippage-bps", 1_500),
|
|
193
|
+
discoveryIntervalMs: numberFlag(args.flags, "discovery-interval-ms", 25),
|
|
194
|
+
retryIntervalMs: numberFlag(
|
|
195
|
+
args.flags,
|
|
196
|
+
"retry-interval-ms",
|
|
197
|
+
args.defaults.spam.intervalMs,
|
|
198
|
+
),
|
|
199
|
+
recompileIntervalMs: numberFlag(
|
|
200
|
+
args.flags,
|
|
201
|
+
args.flags.has("recompile-interval-ms")
|
|
202
|
+
? "recompile-interval-ms"
|
|
203
|
+
: "retry-recompile-interval-ms",
|
|
204
|
+
args.defaults.spam.recompileIntervalMs ?? 750,
|
|
205
|
+
),
|
|
206
|
+
freshQuoteIntervalMs: numberFlag(
|
|
207
|
+
args.flags,
|
|
208
|
+
"fresh-quote-delay-ms",
|
|
209
|
+
args.defaults.spam.freshQuoteDelayMs ?? 2_500,
|
|
210
|
+
),
|
|
211
|
+
timeoutMs: numberFlag(
|
|
212
|
+
args.flags,
|
|
213
|
+
args.flags.has("timeout-ms") ? "timeout-ms" : "retry-timeout-ms",
|
|
214
|
+
0,
|
|
215
|
+
),
|
|
216
|
+
maxFailedAttempts: numberFlag(
|
|
217
|
+
args.flags,
|
|
218
|
+
"max-failed-attempts",
|
|
219
|
+
args.defaults.spam.maxFailedAttempts,
|
|
220
|
+
),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export async function runPumpSpamBuyFromArgs(
|
|
225
|
+
argv: string[],
|
|
226
|
+
options: PumpSpamBuyCliOptions = {},
|
|
227
|
+
): Promise<PumpSpamBuyRunResult> {
|
|
228
|
+
const { flags, positionals } = parseArgs(argv);
|
|
229
|
+
const report = options.report ?? defaultReport;
|
|
230
|
+
const mint = selectedMint(flags, positionals);
|
|
231
|
+
const amount = amountFromFlags(flags);
|
|
232
|
+
const requestedRows = buyerRows({ flags, amount });
|
|
233
|
+
const env = pumpLaunchEnvironmentFromFlags(flags);
|
|
234
|
+
const sender = (first(flags, "sender") ??
|
|
235
|
+
first(flags, "buyer-sender") ??
|
|
236
|
+
String(env.policy.rpcTraderSender)) as SenderId;
|
|
237
|
+
const live = enabled(flags, "live", "LIVE");
|
|
238
|
+
|
|
239
|
+
const tipAccount =
|
|
240
|
+
first(flags, "tip-account") ??
|
|
241
|
+
first(flags, "helius-tip-account") ??
|
|
242
|
+
env.policy.fastTip.account;
|
|
243
|
+
const tipLamports = flags.has("tip-lamports")
|
|
244
|
+
? bigintFlag(flags, "tip-lamports", 0n)
|
|
245
|
+
: flags.has("tip-sol")
|
|
246
|
+
? sol(required(flags, "tip-sol")).raw
|
|
247
|
+
: env.policy.fastTip.lamports;
|
|
248
|
+
|
|
249
|
+
if (String(sender) === "helius-fast") {
|
|
250
|
+
if (!env.senderUrl) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
"--sender helius-fast requires HELIUS_SENDER_URL or --helius-sender-url.",
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
validateHeliusTip({
|
|
256
|
+
tip: { account: tipAccount, lamports: tipLamports },
|
|
257
|
+
endpoint: env.senderUrl,
|
|
258
|
+
live,
|
|
259
|
+
label: "Pump spam buyer",
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const slrd = createTraderSolard({ rpcUrl: env.rpcUrl });
|
|
264
|
+
installPumpLaunchSenders(slrd, env);
|
|
265
|
+
const controller = new AbortController();
|
|
266
|
+
const stop = () => controller.abort();
|
|
267
|
+
process.once("SIGINT", stop);
|
|
268
|
+
process.once("SIGTERM", stop);
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const rows = requestedRows[0]!.wallet.toString().startsWith("group:")
|
|
272
|
+
? slrd
|
|
273
|
+
.groupWallets(requestedRows[0]!.wallet.toString().slice(6))
|
|
274
|
+
.map((wallet): ExplicitBuyerPlanRow => ({ wallet, amount }))
|
|
275
|
+
: requestedRows;
|
|
276
|
+
const buyers = await loadExplicitBuyerAllocations({ slrd, rows });
|
|
277
|
+
const settings = settingsFromFlags({
|
|
278
|
+
flags,
|
|
279
|
+
sender,
|
|
280
|
+
tipAccount,
|
|
281
|
+
tipLamports,
|
|
282
|
+
defaults: env,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
report("pump spam-buy plan", {
|
|
286
|
+
mint,
|
|
287
|
+
live,
|
|
288
|
+
buyers: buyers.map((buyer) => ({
|
|
289
|
+
wallet: buyer.address,
|
|
290
|
+
balanceLamports: buyer.balanceLamports,
|
|
291
|
+
reserveLamports: buyer.reserveLamports,
|
|
292
|
+
selectedBps: buyer.selectedBps,
|
|
293
|
+
spendLamports: buyer.spendLamports,
|
|
294
|
+
})),
|
|
295
|
+
settings,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const result = await runPumpSpamBuyers({
|
|
299
|
+
slrd,
|
|
300
|
+
mint,
|
|
301
|
+
buyers,
|
|
302
|
+
settings,
|
|
303
|
+
live,
|
|
304
|
+
signal: controller.signal,
|
|
305
|
+
reporter: report,
|
|
306
|
+
});
|
|
307
|
+
report("pump spam-buy result", result);
|
|
308
|
+
return result;
|
|
309
|
+
} finally {
|
|
310
|
+
process.off("SIGINT", stop);
|
|
311
|
+
process.off("SIGTERM", stop);
|
|
312
|
+
slrd.close();
|
|
313
|
+
}
|
|
314
|
+
}
|