@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 +1 -1
- package/package.json +7 -3
- package/src/index.ts +12 -7
- 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.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.
|
|
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
|
|
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
|
|
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("
|
|
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("
|
|
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 } =
|
|
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 (
|
|
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 "
|
|
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
|
+
}
|