@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 CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bun
2
- import "../src/index.js";
2
+ import "../src/index.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solard/cli",
3
- "version": "0.2.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.0",
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("@solard/sdk");
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("@solard/sdk");
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 } = await import("@solard/sdk");
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 "@solard/sdk";
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
+ }