@solard/cli 0.2.2 → 0.2.6

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.6",
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.6",
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
@@ -118,7 +118,8 @@ function help(): string {
118
118
  return `${OWL} slrd — multi-wallet Solana CLI + SDK for traders and AI agents
119
119
 
120
120
  Wallets and tokens
121
- slrd wallet create [name] Generate and persist an encrypted Solana wallet
121
+ slrd wallets create [name] Generate and persist an encrypted Solana wallet
122
+ slrd wallet create [name] Alias for wallets create
122
123
  slrd import <private_key> [name]
123
124
  cat key.json | slrd import --stdin [name]
124
125
  slrd wallets [--token <token>] [--addresses-only] Show SOL and registered-token balances
@@ -180,7 +181,7 @@ Transactions and ALTs
180
181
  slrd alt extend <address> --wallet <wallet> <account...>
181
182
 
182
183
  Environment
183
- SLRD_DB_PATH shared SDK/CLI database; default ./slrd.db (SOLARD_DB_PATH alias supported)
184
+ SLRD_DB_PATH shared SDK/CLI database; default ~/.solard/solard.sqlite (SOLARD_DB_PATH alias supported)
184
185
  SLRD_MASTER_KEY required to create/import/decrypt stored wallets
185
186
  RPC_ENDPOINT required only for chain operations
186
187
  HELIUS_SENDER_URL regional/global Helius Sender endpoint used by launch scripts
@@ -249,12 +250,12 @@ async function main() {
249
250
  return;
250
251
  }
251
252
  if (command === "spam-buy" || command === "buy-spam") {
252
- const { runPumpSpamBuyFromArgs } = await import("@solard/sdk");
253
+ const { runPumpSpamBuyFromArgs } = await import("./pump/spam-buy-cli.ts");
253
254
  await runPumpSpamBuyFromArgs(rest);
254
255
  return;
255
256
  }
256
257
  if (command === "buy" && flags.has("spam")) {
257
- const { runPumpSpamBuyFromArgs } = await import("@solard/sdk");
258
+ const { runPumpSpamBuyFromArgs } = await import("./pump/spam-buy-cli.ts");
258
259
  await runPumpSpamBuyFromArgs(rest);
259
260
  return;
260
261
  }
@@ -273,7 +274,8 @@ async function main() {
273
274
  command === "launch" &&
274
275
  (values[0] === "pump" || values[0] === "pump-token")
275
276
  ) {
276
- const { runPumpTokenLaunchFromArgs } = await import("@solard/sdk");
277
+ const { runPumpTokenLaunchFromArgs } =
278
+ await import("./pump/token-launch-cli.ts");
277
279
  await runPumpTokenLaunchFromArgs(rest.slice(1), {
278
280
  defaultSubmitMode: "after-deploy-processed",
279
281
  defaultDeploymentPriorityMicroLamports: 500_000,
@@ -328,7 +330,10 @@ async function main() {
328
330
  ]);
329
331
  const slrd = createTraderSolard();
330
332
  try {
331
- if (command === "wallet" && values[0] === "create") {
333
+ if (
334
+ (command === "wallet" || command === "wallets") &&
335
+ values[0] === "create"
336
+ ) {
332
337
  const wallet = slrd.createWallet(values[1]);
333
338
  emit(`${OWL} created @${wallet.name} ${wallet.address}\n`);
334
339
  return;
@@ -918,4 +923,4 @@ main().catch((error) => {
918
923
  process.exitCode = 1;
919
924
  });
920
925
 
921
- export { formatCliError } from "@solard/sdk";
926
+ 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
+ }