@solard/cli 0.1.8 → 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/README.md +13 -0
- package/bin/solard.ts +1 -1
- package/package.json +12 -4
- package/src/index.ts +41 -50
- package/src/pump/spam-buy-cli.ts +314 -0
- package/src/pump/token-launch-cli.ts +1652 -0
- package/src/worker-commands.ts +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @solard/cli
|
|
2
|
+
|
|
3
|
+
CLI adapter for `@solard/sdk`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
slrd wallet create trader-1
|
|
7
|
+
slrd wallets
|
|
8
|
+
slrd token <mint> my-token
|
|
9
|
+
slrd buy my-token --wallet trader-1 --sol 0.05
|
|
10
|
+
slrd sell my-token --wallet trader-1
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Set `SLRD_MASTER_KEY` for persisted wallets and `RPC_ENDPOINT` for chain operations.
|
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solard/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "Command-line interface for the Solard SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"bin": {
|
|
@@ -13,8 +13,16 @@
|
|
|
13
13
|
"src",
|
|
14
14
|
"README.md"
|
|
15
15
|
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"engines": {
|
|
18
|
+
"bun": ">=1.1.0"
|
|
19
|
+
},
|
|
16
20
|
"dependencies": {
|
|
17
|
-
"@solard/
|
|
21
|
+
"@solard/sdk": "^0.2.1",
|
|
22
|
+
"bs58": "^6.0.0",
|
|
23
|
+
"@solana/web3.js": "^1.98.4"
|
|
18
24
|
},
|
|
19
|
-
"
|
|
25
|
+
"scripts": {
|
|
26
|
+
"typecheck": "tsc --noEmit -p ../../tsconfig.json"
|
|
27
|
+
}
|
|
20
28
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import type
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { listScripts, runScript, type TokenRow } from "@solard/sdk";
|
|
3
|
+
|
|
4
|
+
function emit(value: string): void {
|
|
5
|
+
process.stdout.write(value);
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
const OWL = "🦉";
|
|
7
9
|
type Flags = Map<string, string>;
|
|
@@ -116,6 +118,7 @@ function help(): string {
|
|
|
116
118
|
return `${OWL} slrd — multi-wallet Solana CLI + SDK for traders and AI agents
|
|
117
119
|
|
|
118
120
|
Wallets and tokens
|
|
121
|
+
slrd wallet create [name] Generate and persist an encrypted Solana wallet
|
|
119
122
|
slrd import <private_key> [name]
|
|
120
123
|
cat key.json | slrd import --stdin [name]
|
|
121
124
|
slrd wallets [--token <token>] [--addresses-only] Show SOL and registered-token balances
|
|
@@ -125,13 +128,12 @@ Wallets and tokens
|
|
|
125
128
|
slrd tokens
|
|
126
129
|
|
|
127
130
|
Metadata and launching
|
|
128
|
-
|
|
131
|
+
slrd launch pump --creator <wallet> (--uri <metadata_uri> | --metadata <json> | --image <path> --description <text>) [--alias <name>] [--live] [--skip-simulation]
|
|
129
132
|
[--deployment-sender helius-rpc|helius-fast] [--helius-tip-sol 0.01]
|
|
130
133
|
slrd metadata upload --image <local_path> --name <name> --symbol <symbol> --description <text> [--provider pump-frontend|pinata]
|
|
131
134
|
[--twitter <url>] [--telegram <url>] [--website <url>] [--video <url>] [--hide-name]
|
|
132
135
|
slrd deploy pump --wallet <wallet> --name <name> --symbol <symbol> (--uri <metadata_uri> | --image <local_path> --description <text>) [--alias <name>] [--live]
|
|
133
136
|
[--twitter <url>] [--telegram <url>] [--website <url>] [--video <url>] [--hide-name]
|
|
134
|
-
slrd run launch-pump-token --creator <wallet> --metadata <token.json> [--deploy-only] [--live]
|
|
135
137
|
|
|
136
138
|
Prices
|
|
137
139
|
slrd quote buy <token|ca> --sol <amount> Inspect buy quote without submitting
|
|
@@ -143,7 +145,6 @@ Trading
|
|
|
143
145
|
slrd buy <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <name>) --sol <amount> [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only]
|
|
144
146
|
slrd buy <future-mint> (--wallet <wallet> | --group <name>) (--sol <amount> | --lamports <amount> | --min-bps <n> --max-bps <n>) --spam [--live]
|
|
145
147
|
slrd spam-buy [pump] <future-mint> (--wallet <wallet> | --group <name>) (--sol <amount> | --lamports <amount> | --min-bps <n> --max-bps <n>) [--sender <id>] [--live]
|
|
146
|
-
bun run scripts/spam-pump-buyers.ts --mint <future-mint> (--wallet <wallet> | --group <name>) --amount-mode range-bps --min-bps <n> --max-bps <n> [--live]
|
|
147
148
|
slrd sell <token|ca> (--wallet <wallet> | --wallets <w1,w2> | --group <name>) [--bps 10000] [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only]
|
|
148
149
|
slrd unwrap-wsol (--wallet <wallet> | --wallets <w1,w2> | --group <name>) [--sender rpc|helius|jito] [--ignore-missing] [--simulate-only]
|
|
149
150
|
slrd claim <token|ca> --wallet <wallet> [--sender rpc|helius|jito]
|
|
@@ -152,7 +153,6 @@ Scripts (strategies stay outside the kernel)
|
|
|
152
153
|
slrd scripts List scripts registered in slrd.config.ts
|
|
153
154
|
slrd run <name-or-path> [script flags...] Execute a script that imports slrd
|
|
154
155
|
slrd run snipe --name <exact_name> --group <group> --sol 0.05 --sender jito
|
|
155
|
-
slrd run claim-trade-send --claim <token> --buy <token> --wallet <wallet> --recipient <address>
|
|
156
156
|
|
|
157
157
|
Groups and agents
|
|
158
158
|
slrd group create <name> [description]
|
|
@@ -180,8 +180,8 @@ Transactions and ALTs
|
|
|
180
180
|
slrd alt extend <address> --wallet <wallet> <account...>
|
|
181
181
|
|
|
182
182
|
Environment
|
|
183
|
-
SLRD_DB_PATH
|
|
184
|
-
SLRD_MASTER_KEY required to import/decrypt stored wallets
|
|
183
|
+
SLRD_DB_PATH shared SDK/CLI database; default ./slrd.db (SOLARD_DB_PATH alias supported)
|
|
184
|
+
SLRD_MASTER_KEY required to create/import/decrypt stored wallets
|
|
185
185
|
RPC_ENDPOINT required only for chain operations
|
|
186
186
|
HELIUS_SENDER_URL regional/global Helius Sender endpoint used by launch scripts
|
|
187
187
|
HELIUS_RPC_URL RPC endpoint used for ordinary Helius-RPC buyer lane
|
|
@@ -212,8 +212,11 @@ async function main() {
|
|
|
212
212
|
return;
|
|
213
213
|
}
|
|
214
214
|
if (command === "vanity") {
|
|
215
|
-
const {
|
|
216
|
-
|
|
215
|
+
const {
|
|
216
|
+
generateMintKeypairWithSuffix,
|
|
217
|
+
saveMintKeypairFile,
|
|
218
|
+
cleanVanitySuffix,
|
|
219
|
+
} = await import("@solard/sdk");
|
|
217
220
|
const suffix = cleanVanitySuffix(need(flags, "suffix"));
|
|
218
221
|
const out = need(flags, "out");
|
|
219
222
|
const count = int(flags, "count", 1)!;
|
|
@@ -226,10 +229,14 @@ async function main() {
|
|
|
226
229
|
timeoutMs: int(flags, "timeout-ms", 0)!,
|
|
227
230
|
reportEvery: int(flags, "report-every", 1_000_000)!,
|
|
228
231
|
onProgress: (p) =>
|
|
229
|
-
emit(
|
|
232
|
+
emit(
|
|
233
|
+
`${OWL} grinding ${p.suffix}: ${p.attempts} attempts, ${p.ratePerSecond}/s\n`,
|
|
234
|
+
),
|
|
230
235
|
});
|
|
231
236
|
const path = count === 1 ? out : out.replace(/(\.json)?$/, `-${i + 1}$1`);
|
|
232
|
-
const saved = saveMintKeypairFile(path, found.mint, {
|
|
237
|
+
const saved = saveMintKeypairFile(path, found.mint, {
|
|
238
|
+
force: flags.has("force"),
|
|
239
|
+
});
|
|
233
240
|
results.push({
|
|
234
241
|
address: found.mint.publicKey.toBase58(),
|
|
235
242
|
keypairPath: saved,
|
|
@@ -242,21 +249,15 @@ async function main() {
|
|
|
242
249
|
return;
|
|
243
250
|
}
|
|
244
251
|
if (command === "spam-buy" || command === "buy-spam") {
|
|
245
|
-
const { runPumpSpamBuyFromArgs } =
|
|
246
|
-
await import("@solard/core/launches/pump/spam-buy-cli.ts");
|
|
252
|
+
const { runPumpSpamBuyFromArgs } = await import("./pump/spam-buy-cli.ts");
|
|
247
253
|
await runPumpSpamBuyFromArgs(rest);
|
|
248
254
|
return;
|
|
249
255
|
}
|
|
250
256
|
if (command === "buy" && flags.has("spam")) {
|
|
251
|
-
const { runPumpSpamBuyFromArgs } =
|
|
252
|
-
await import("@solard/core/launches/pump/spam-buy-cli.ts");
|
|
257
|
+
const { runPumpSpamBuyFromArgs } = await import("./pump/spam-buy-cli.ts");
|
|
253
258
|
await runPumpSpamBuyFromArgs(rest);
|
|
254
259
|
return;
|
|
255
260
|
}
|
|
256
|
-
// const { maybeRunWorkerCliCommand } =
|
|
257
|
-
// await import("./worker-commands.js");
|
|
258
|
-
// if (await maybeRunWorkerCliCommand({ values: [command, ...values], flags }))
|
|
259
|
-
// return;
|
|
260
261
|
if (command === "scripts") {
|
|
261
262
|
emit(json(await listScripts()) + "\n");
|
|
262
263
|
return;
|
|
@@ -273,7 +274,7 @@ async function main() {
|
|
|
273
274
|
(values[0] === "pump" || values[0] === "pump-token")
|
|
274
275
|
) {
|
|
275
276
|
const { runPumpTokenLaunchFromArgs } =
|
|
276
|
-
await import("
|
|
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,
|
|
@@ -290,29 +291,13 @@ async function main() {
|
|
|
290
291
|
process.env.JITO_BLOCK_ENGINE_URL ??
|
|
291
292
|
"https://mainnet.block-engine.jito.wtf"
|
|
292
293
|
).replace(/\/$/, "");
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
body: JSON.stringify({
|
|
297
|
-
jsonrpc: "2.0",
|
|
298
|
-
id: Date.now(),
|
|
299
|
-
method: "getTipAccounts",
|
|
300
|
-
params: [],
|
|
301
|
-
}),
|
|
302
|
-
});
|
|
303
|
-
const payload = (await response.json()) as {
|
|
304
|
-
result?: string[];
|
|
305
|
-
error?: unknown;
|
|
306
|
-
};
|
|
307
|
-
if (!response.ok || payload.error || !Array.isArray(payload.result))
|
|
308
|
-
throw new Error(
|
|
309
|
-
`Jito getTipAccounts failed: ${JSON.stringify(payload.error ?? response.status)}`,
|
|
310
|
-
);
|
|
311
|
-
emit(json({ endpoint, tipAccounts: payload.result }) + "\n");
|
|
294
|
+
const { getJitoTipAccounts } = await import("@solard/sdk");
|
|
295
|
+
const tipAccounts = await getJitoTipAccounts(endpoint);
|
|
296
|
+
emit(json({ endpoint, tipAccounts }) + "\n");
|
|
312
297
|
return;
|
|
313
298
|
}
|
|
314
299
|
if (command === "metadata" && values[0] === "upload") {
|
|
315
|
-
const { uploadPumpMetadata } = await import("@solard/
|
|
300
|
+
const { uploadPumpMetadata } = await import("@solard/sdk");
|
|
316
301
|
const uploaded = await uploadPumpMetadata(
|
|
317
302
|
{
|
|
318
303
|
imagePath: need(flags, "image"),
|
|
@@ -338,12 +323,17 @@ async function main() {
|
|
|
338
323
|
}
|
|
339
324
|
const [{ sol, formatRaw }, { shortKey }, { createTraderSolard }] =
|
|
340
325
|
await Promise.all([
|
|
341
|
-
import("@solard/
|
|
342
|
-
import("@solard/
|
|
343
|
-
import("@solard/
|
|
326
|
+
import("@solard/sdk"),
|
|
327
|
+
import("@solard/sdk"),
|
|
328
|
+
import("@solard/sdk"),
|
|
344
329
|
]);
|
|
345
330
|
const slrd = createTraderSolard();
|
|
346
331
|
try {
|
|
332
|
+
if (command === "wallet" && values[0] === "create") {
|
|
333
|
+
const wallet = slrd.createWallet(values[1]);
|
|
334
|
+
emit(`${OWL} created @${wallet.name} ${wallet.address}\n`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
347
337
|
if (command === "export") {
|
|
348
338
|
const ref = values[0];
|
|
349
339
|
if (!ref)
|
|
@@ -382,10 +372,12 @@ async function main() {
|
|
|
382
372
|
: values[0];
|
|
383
373
|
if (!input)
|
|
384
374
|
throw new Error(
|
|
385
|
-
"Usage: slrd import <private_key> [name] or cat key.json | slrd import --stdin [name]",
|
|
375
|
+
"Usage: slrd import <private_key> [name] [--force] or cat key.json | slrd import --stdin [name] [--force]",
|
|
386
376
|
);
|
|
387
377
|
const name = flags.has("stdin") ? values[0] : values[1];
|
|
388
|
-
const wallet = slrd.importWallet(input, name
|
|
378
|
+
const wallet = slrd.importWallet(input, name, {
|
|
379
|
+
overwrite: flags.has("force") || flags.has("overwrite"),
|
|
380
|
+
});
|
|
389
381
|
emit(`${OWL} imported @${wallet.name} ${wallet.address}\n`);
|
|
390
382
|
return;
|
|
391
383
|
}
|
|
@@ -431,8 +423,7 @@ async function main() {
|
|
|
431
423
|
throw new Error(
|
|
432
424
|
"Provide --uri <metadata_uri> or --image <local_path> --description <text>",
|
|
433
425
|
);
|
|
434
|
-
const { uploadPumpMetadata } =
|
|
435
|
-
await import("@solard/core/metadata/pump-metadata.ts");
|
|
426
|
+
const { uploadPumpMetadata } = await import("@solard/sdk");
|
|
436
427
|
uploadedMetadata = await uploadPumpMetadata(
|
|
437
428
|
{
|
|
438
429
|
imagePath: need(flags, "image"),
|
|
@@ -928,4 +919,4 @@ main().catch((error) => {
|
|
|
928
919
|
process.exitCode = 1;
|
|
929
920
|
});
|
|
930
921
|
|
|
931
|
-
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
|
+
}
|