@zoidz123/raydium-cli 0.1.0
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/commands/clmm/index.js +2118 -0
- package/dist/commands/config/index.js +113 -0
- package/dist/commands/cpmm/index.js +480 -0
- package/dist/commands/launchpad/index.js +1677 -0
- package/dist/commands/pools/index.js +81 -0
- package/dist/commands/swap/index.js +490 -0
- package/dist/commands/tokens/index.js +93 -0
- package/dist/commands/wallet/index.js +267 -0
- package/dist/index.js +43 -0
- package/dist/lib/clmm-utils.js +296 -0
- package/dist/lib/codex-sdk.js +17 -0
- package/dist/lib/config-manager.js +67 -0
- package/dist/lib/connection.js +9 -0
- package/dist/lib/ipfs.js +117 -0
- package/dist/lib/output.js +59 -0
- package/dist/lib/paths.js +11 -0
- package/dist/lib/prompt.js +58 -0
- package/dist/lib/raydium-client.js +23 -0
- package/dist/lib/token-price.js +45 -0
- package/dist/lib/wallet-manager.js +173 -0
- package/dist/types/config.js +11 -0
- package/package.json +56 -0
|
@@ -0,0 +1,1677 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerLaunchpadCommands = registerLaunchpadCommands;
|
|
7
|
+
const node_util_1 = require("node:util");
|
|
8
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
9
|
+
const raydium_sdk_v2_1 = require("@raydium-io/raydium-sdk-v2");
|
|
10
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
11
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
12
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
13
|
+
const raydium_client_1 = require("../../lib/raydium-client");
|
|
14
|
+
const config_manager_1 = require("../../lib/config-manager");
|
|
15
|
+
const wallet_manager_1 = require("../../lib/wallet-manager");
|
|
16
|
+
const prompt_1 = require("../../lib/prompt");
|
|
17
|
+
const output_1 = require("../../lib/output");
|
|
18
|
+
const ipfs_1 = require("../../lib/ipfs");
|
|
19
|
+
const NATIVE_MINT = new web3_js_1.PublicKey("So11111111111111111111111111111111111111112");
|
|
20
|
+
// Known Token-2022 quote tokens (use TOKEN_2022_PROGRAM_ID instead of TOKEN_PROGRAM_ID)
|
|
21
|
+
// Note: USD1 uses regular Token Program, not Token-2022
|
|
22
|
+
const TOKEN_2022_MINTS = new Set([
|
|
23
|
+
// Add Token-2022 mints here as needed
|
|
24
|
+
]);
|
|
25
|
+
const CURVE_TYPE_NAMES = {
|
|
26
|
+
0: "Constant Product",
|
|
27
|
+
1: "Fixed Price",
|
|
28
|
+
2: "Linear Price"
|
|
29
|
+
};
|
|
30
|
+
const FEE_DENOMINATOR = 1000000;
|
|
31
|
+
const SLIPPAGE_DENOMINATOR = 10000; // 10000 = 100%
|
|
32
|
+
const DEFAULT_COMPUTE_UNITS = 600000;
|
|
33
|
+
function formatFeeRate(rate) {
|
|
34
|
+
const numRate = typeof rate === "string" ? Number(rate) : rate;
|
|
35
|
+
const percent = (numRate / FEE_DENOMINATOR) * 100;
|
|
36
|
+
return `${percent}%`;
|
|
37
|
+
}
|
|
38
|
+
function formatAmount(raw, decimals) {
|
|
39
|
+
const value = new decimal_js_1.default(String(raw)).div(new decimal_js_1.default(10).pow(decimals));
|
|
40
|
+
return value.toFixed();
|
|
41
|
+
}
|
|
42
|
+
function registerLaunchpadCommands(program) {
|
|
43
|
+
const launchpad = program.command("launchpad").description("Launchpad commands");
|
|
44
|
+
launchpad
|
|
45
|
+
.command("configs")
|
|
46
|
+
.description("List available launchpad configurations")
|
|
47
|
+
.action(async () => {
|
|
48
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ disableLoadToken: true }));
|
|
49
|
+
const configs = await (0, output_1.withSpinner)("Fetching launchpad configs", () => raydium.api.fetchLaunchConfigs());
|
|
50
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
51
|
+
(0, output_1.logJson)({ configs });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (configs.length === 0) {
|
|
55
|
+
(0, output_1.logInfo)("No launchpad configurations found");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
configs.forEach((config) => {
|
|
59
|
+
const curveTypeName = CURVE_TYPE_NAMES[config.key.curveType] ?? `Unknown (${config.key.curveType})`;
|
|
60
|
+
const quoteSymbol = config.mintInfoB?.symbol ?? "Unknown";
|
|
61
|
+
const quoteDecimals = config.mintInfoB?.decimals ?? 9;
|
|
62
|
+
// Default params (tokens have 6 decimals by default)
|
|
63
|
+
const defaultSupply = config.defaultParams?.supplyInit
|
|
64
|
+
? formatAmount(config.defaultParams.supplyInit, 6)
|
|
65
|
+
: "N/A";
|
|
66
|
+
const defaultSellPercent = config.defaultParams?.totalSellA && config.defaultParams?.supplyInit
|
|
67
|
+
? ((Number(config.defaultParams.totalSellA) / Number(config.defaultParams.supplyInit)) * 100).toFixed(2)
|
|
68
|
+
: "N/A";
|
|
69
|
+
const defaultRaise = config.defaultParams?.totalFundRaisingB
|
|
70
|
+
? formatAmount(config.defaultParams.totalFundRaisingB, quoteDecimals)
|
|
71
|
+
: "N/A";
|
|
72
|
+
(0, output_1.logInfo)(`${quoteSymbol} - ${config.key.name}`);
|
|
73
|
+
(0, output_1.logInfo)(` Config: ${config.key.pubKey}`);
|
|
74
|
+
(0, output_1.logInfo)(` Quote: ${quoteSymbol} (${config.key.mintB})`);
|
|
75
|
+
(0, output_1.logInfo)(` Trade Fee: ${formatFeeRate(config.key.tradeFeeRate)}`);
|
|
76
|
+
(0, output_1.logInfo)(` Min Raise: ${formatAmount(config.key.minFundRaisingB, quoteDecimals)} ${quoteSymbol}`);
|
|
77
|
+
(0, output_1.logInfo)(` Defaults:`);
|
|
78
|
+
(0, output_1.logInfo)(` Supply: ${defaultSupply} tokens`);
|
|
79
|
+
(0, output_1.logInfo)(` On Curve: ${defaultSellPercent}%`);
|
|
80
|
+
(0, output_1.logInfo)(` Target Raise: ${defaultRaise} ${quoteSymbol}`);
|
|
81
|
+
(0, output_1.logInfo)(` Protocol Wallets:`);
|
|
82
|
+
(0, output_1.logInfo)(` Protocol Fee Owner: ${config.key.protocolFeeOwner}`);
|
|
83
|
+
(0, output_1.logInfo)(` Migrate Fee Owner: ${config.key.migrateFeeOwner}`);
|
|
84
|
+
(0, output_1.logInfo)(` Migrate to AMM: ${config.key.migrateToAmmWallet}`);
|
|
85
|
+
(0, output_1.logInfo)(` Migrate to CPMM: ${config.key.migrateToCpmmWallet}`);
|
|
86
|
+
(0, output_1.logInfo)("");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
launchpad
|
|
90
|
+
.command("platforms")
|
|
91
|
+
.description("List LaunchLab platforms")
|
|
92
|
+
.option("--limit <number>", "Max results", "20")
|
|
93
|
+
.option("--page <number>", "Page number", "1")
|
|
94
|
+
.action(async (options) => {
|
|
95
|
+
const limit = Number(options.limit);
|
|
96
|
+
const page = Number(options.page);
|
|
97
|
+
let platforms = [];
|
|
98
|
+
try {
|
|
99
|
+
platforms = await (0, output_1.withSpinner)("Fetching platforms", async () => {
|
|
100
|
+
const response = await fetch(`https://launch-mint-v1.raydium.io/main/platforms?page=${page}&pageSize=${limit}`);
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(`HTTP ${response.status}`);
|
|
103
|
+
}
|
|
104
|
+
const json = (await response.json());
|
|
105
|
+
return json.data?.data ?? [];
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
(0, output_1.logError)("Failed to fetch platforms", error.message);
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
114
|
+
(0, output_1.logJson)({ platforms, page, count: platforms.length });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (platforms.length === 0) {
|
|
118
|
+
(0, output_1.logInfo)("No platforms found");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
(0, output_1.logInfo)(`Found ${platforms.length} platforms (page ${page})\n`);
|
|
122
|
+
platforms.forEach((platform) => {
|
|
123
|
+
const feePercent = (Number(platform.feeRate) / 10000).toFixed(2);
|
|
124
|
+
const creatorFeePercent = (Number(platform.creatorFeeRate) / 10000).toFixed(2);
|
|
125
|
+
const platformLpPercent = (Number(platform.platformScale) / 10000).toFixed(1);
|
|
126
|
+
const creatorLpPercent = (Number(platform.creatorScale) / 10000).toFixed(1);
|
|
127
|
+
const burnLpPercent = (Number(platform.burnScale) / 10000).toFixed(1);
|
|
128
|
+
(0, output_1.logInfo)(`${platform.name}`);
|
|
129
|
+
(0, output_1.logInfo)(` ID: ${platform.pubKey}`);
|
|
130
|
+
if (platform.web)
|
|
131
|
+
(0, output_1.logInfo)(` Web: ${platform.web}`);
|
|
132
|
+
(0, output_1.logInfo)(` Fees: ${feePercent}% platform, ${creatorFeePercent}% creator`);
|
|
133
|
+
(0, output_1.logInfo)(` LP Split: ${platformLpPercent}% platform / ${creatorLpPercent}% creator / ${burnLpPercent}% burn`);
|
|
134
|
+
(0, output_1.logInfo)("");
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
const USD1_MINT = new web3_js_1.PublicKey("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
|
|
138
|
+
launchpad
|
|
139
|
+
.command("info")
|
|
140
|
+
.description("Get launchpad pool info")
|
|
141
|
+
.option("--mint <address>", "Token mint address (derives pool from mint + quote)")
|
|
142
|
+
.option("--pool <address>", "Direct pool address")
|
|
143
|
+
.option("--usd1", "Use USD1 as quote token instead of SOL (only with --mint)")
|
|
144
|
+
.action(async (options) => {
|
|
145
|
+
if (!options.mint && !options.pool) {
|
|
146
|
+
(0, output_1.logError)("Must specify either --mint or --pool");
|
|
147
|
+
process.exitCode = 1;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (options.mint && options.pool) {
|
|
151
|
+
(0, output_1.logError)("Cannot specify both --mint and --pool");
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
let poolId;
|
|
156
|
+
if (options.pool) {
|
|
157
|
+
try {
|
|
158
|
+
poolId = new web3_js_1.PublicKey(options.pool);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
(0, output_1.logError)("Invalid pool address");
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Derive pool ID from mint + quote token
|
|
168
|
+
let mintA;
|
|
169
|
+
try {
|
|
170
|
+
mintA = new web3_js_1.PublicKey(options.mint);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
(0, output_1.logError)("Invalid mint address");
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const mintB = options.usd1 ? USD1_MINT : NATIVE_MINT;
|
|
178
|
+
const { publicKey } = (0, raydium_sdk_v2_1.getPdaLaunchpadPoolId)(raydium_sdk_v2_1.LAUNCHPAD_PROGRAM, mintA, mintB);
|
|
179
|
+
poolId = publicKey;
|
|
180
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
181
|
+
(0, output_1.logInfo)(`Derived pool: ${poolId.toBase58()}`);
|
|
182
|
+
(0, output_1.logInfo)(`Quote token: ${options.usd1 ? "USD1" : "SOL"}`);
|
|
183
|
+
(0, output_1.logInfo)("");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ disableLoadToken: true }));
|
|
187
|
+
let poolInfo;
|
|
188
|
+
try {
|
|
189
|
+
poolInfo = await (0, output_1.withSpinner)("Fetching pool info", () => raydium.launchpad.getRpcPoolInfo({ poolId }));
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
193
|
+
(0, output_1.logError)("Failed to fetch pool info", message);
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const curveType = poolInfo.configInfo.curveType;
|
|
198
|
+
const decimalA = poolInfo.mintDecimalsA;
|
|
199
|
+
const decimalB = poolInfo.mintDecimalsB;
|
|
200
|
+
// Calculate current price
|
|
201
|
+
const currentPrice = raydium_sdk_v2_1.Curve.getPrice({
|
|
202
|
+
poolInfo,
|
|
203
|
+
curveType,
|
|
204
|
+
decimalA,
|
|
205
|
+
decimalB
|
|
206
|
+
});
|
|
207
|
+
// Calculate progress
|
|
208
|
+
const soldAmount = new decimal_js_1.default(poolInfo.realA.toString());
|
|
209
|
+
const totalSellAmount = new decimal_js_1.default(poolInfo.totalSellA.toString());
|
|
210
|
+
const progressPercent = totalSellAmount.isZero()
|
|
211
|
+
? new decimal_js_1.default(0)
|
|
212
|
+
: soldAmount.div(totalSellAmount).mul(100);
|
|
213
|
+
// Calculate raised amount
|
|
214
|
+
const raisedAmount = new decimal_js_1.default(poolInfo.realB.toString());
|
|
215
|
+
const targetAmount = new decimal_js_1.default(poolInfo.totalFundRaisingB.toString());
|
|
216
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
217
|
+
(0, output_1.logJson)({
|
|
218
|
+
poolId: poolId.toBase58(),
|
|
219
|
+
status: poolInfo.status,
|
|
220
|
+
mintA: poolInfo.mintA.toBase58(),
|
|
221
|
+
mintB: poolInfo.mintB.toBase58(),
|
|
222
|
+
mintDecimalsA: decimalA,
|
|
223
|
+
mintDecimalsB: decimalB,
|
|
224
|
+
curveType,
|
|
225
|
+
currentPrice: currentPrice.toFixed(),
|
|
226
|
+
progress: {
|
|
227
|
+
sold: poolInfo.realA.toString(),
|
|
228
|
+
total: poolInfo.totalSellA.toString(),
|
|
229
|
+
percent: progressPercent.toFixed(2)
|
|
230
|
+
},
|
|
231
|
+
raised: {
|
|
232
|
+
current: poolInfo.realB.toString(),
|
|
233
|
+
target: poolInfo.totalFundRaisingB.toString()
|
|
234
|
+
},
|
|
235
|
+
vestingSchedule: {
|
|
236
|
+
totalLockedAmount: poolInfo.vestingSchedule.totalLockedAmount.toString(),
|
|
237
|
+
cliffPeriod: poolInfo.vestingSchedule.cliffPeriod.toString(),
|
|
238
|
+
unlockPeriod: poolInfo.vestingSchedule.unlockPeriod.toString()
|
|
239
|
+
},
|
|
240
|
+
creator: poolInfo.creator.toBase58(),
|
|
241
|
+
configId: poolInfo.configId.toBase58(),
|
|
242
|
+
platformId: poolInfo.platformId.toBase58()
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const curveTypeName = CURVE_TYPE_NAMES[curveType] ?? `Unknown (${curveType})`;
|
|
247
|
+
const soldFormatted = formatAmount(poolInfo.realA.toString(), decimalA);
|
|
248
|
+
const totalSellFormatted = formatAmount(poolInfo.totalSellA.toString(), decimalA);
|
|
249
|
+
const raisedFormatted = formatAmount(poolInfo.realB.toString(), decimalB);
|
|
250
|
+
const targetFormatted = formatAmount(poolInfo.totalFundRaisingB.toString(), decimalB);
|
|
251
|
+
(0, output_1.logInfo)(`Pool: ${poolId.toBase58()}`);
|
|
252
|
+
(0, output_1.logInfo)(`Status: ${poolInfo.status}`);
|
|
253
|
+
(0, output_1.logInfo)(`Token (mintA): ${poolInfo.mintA.toBase58()}`);
|
|
254
|
+
(0, output_1.logInfo)(`Quote (mintB): ${poolInfo.mintB.toBase58()}`);
|
|
255
|
+
(0, output_1.logInfo)("");
|
|
256
|
+
(0, output_1.logInfo)(`Curve: ${curveTypeName}`);
|
|
257
|
+
(0, output_1.logInfo)(`Current Price: ${currentPrice.toFixed()} (mintB per mintA)`);
|
|
258
|
+
(0, output_1.logInfo)("");
|
|
259
|
+
(0, output_1.logInfo)(`Progress: ${progressPercent.toFixed(2)}%`);
|
|
260
|
+
(0, output_1.logInfo)(` Sold: ${soldFormatted} / ${totalSellFormatted}`);
|
|
261
|
+
(0, output_1.logInfo)(` Raised: ${raisedFormatted} / ${targetFormatted}`);
|
|
262
|
+
(0, output_1.logInfo)("");
|
|
263
|
+
(0, output_1.logInfo)(`Creator: ${poolInfo.creator.toBase58()}`);
|
|
264
|
+
if (!poolInfo.vestingSchedule.totalLockedAmount.isZero()) {
|
|
265
|
+
const lockedFormatted = formatAmount(poolInfo.vestingSchedule.totalLockedAmount.toString(), decimalA);
|
|
266
|
+
(0, output_1.logInfo)("");
|
|
267
|
+
(0, output_1.logInfo)("Vesting Schedule:");
|
|
268
|
+
(0, output_1.logInfo)(` Locked: ${lockedFormatted}`);
|
|
269
|
+
(0, output_1.logInfo)(` Cliff: ${poolInfo.vestingSchedule.cliffPeriod.toString()} seconds`);
|
|
270
|
+
(0, output_1.logInfo)(` Unlock: ${poolInfo.vestingSchedule.unlockPeriod.toString()} seconds`);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
// Buy command - spend mintB (SOL/USDC/USD1) to get mintA (token)
|
|
274
|
+
launchpad
|
|
275
|
+
.command("buy")
|
|
276
|
+
.description("Buy tokens from a launchpad pool")
|
|
277
|
+
.requiredOption("--mint <address>", "Token mint address (mintA)")
|
|
278
|
+
.requiredOption("--amount <number>", "Amount of quote token to spend")
|
|
279
|
+
.option("--slippage <percent>", "Slippage tolerance in percent")
|
|
280
|
+
.option("--priority-fee <sol>", "Priority fee in SOL")
|
|
281
|
+
.option("--debug", "Print full error on failure")
|
|
282
|
+
.action(async (options) => {
|
|
283
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
284
|
+
// Validate slippage
|
|
285
|
+
const slippagePercent = options.slippage ? Number(options.slippage) : config["default-slippage"];
|
|
286
|
+
if (!Number.isFinite(slippagePercent) || slippagePercent < 0) {
|
|
287
|
+
(0, output_1.logError)("Invalid slippage percent");
|
|
288
|
+
process.exitCode = 1;
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const slippageBps = new bn_js_1.default(Math.round(slippagePercent * (SLIPPAGE_DENOMINATOR / 100)));
|
|
292
|
+
// Validate priority fee
|
|
293
|
+
const priorityFeeSol = options.priorityFee ? Number(options.priorityFee) : config["priority-fee"];
|
|
294
|
+
if (!Number.isFinite(priorityFeeSol) || priorityFeeSol < 0) {
|
|
295
|
+
(0, output_1.logError)("Invalid priority fee");
|
|
296
|
+
process.exitCode = 1;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const priorityFeeLamports = priorityFeeSol * 1e9;
|
|
300
|
+
const priorityFeeMicroLamports = Math.round((priorityFeeLamports * 1e6) / DEFAULT_COMPUTE_UNITS);
|
|
301
|
+
// Check wallet
|
|
302
|
+
const walletName = config.activeWallet;
|
|
303
|
+
if (!walletName) {
|
|
304
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
305
|
+
process.exitCode = 1;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// Validate mint address
|
|
309
|
+
let mintA;
|
|
310
|
+
try {
|
|
311
|
+
mintA = new web3_js_1.PublicKey(options.mint);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
(0, output_1.logError)("Invalid mint address");
|
|
315
|
+
process.exitCode = 1;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
// Validate amount
|
|
319
|
+
const amountNum = Number(options.amount);
|
|
320
|
+
if (!Number.isFinite(amountNum) || amountNum <= 0) {
|
|
321
|
+
(0, output_1.logError)("Amount must be greater than zero");
|
|
322
|
+
process.exitCode = 1;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// Prompt for password and decrypt wallet
|
|
326
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
327
|
+
let owner;
|
|
328
|
+
try {
|
|
329
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
333
|
+
process.exitCode = 1;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// Load Raydium with owner
|
|
337
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ owner, disableLoadToken: true }));
|
|
338
|
+
// First fetch pool info to get the quote token (mintB) and its decimals
|
|
339
|
+
// Try different quote tokens (SOL, USD1, USDC) to find the pool
|
|
340
|
+
const KNOWN_QUOTE_TOKENS = [
|
|
341
|
+
{ mint: new web3_js_1.PublicKey("So11111111111111111111111111111111111111112"), symbol: "SOL" },
|
|
342
|
+
{ mint: new web3_js_1.PublicKey("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"), symbol: "USD1" },
|
|
343
|
+
{ mint: new web3_js_1.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), symbol: "USDC" }
|
|
344
|
+
];
|
|
345
|
+
let poolInfo;
|
|
346
|
+
let mintB;
|
|
347
|
+
try {
|
|
348
|
+
poolInfo = await (0, output_1.withSpinner)("Fetching pool info", async () => {
|
|
349
|
+
for (const quote of KNOWN_QUOTE_TOKENS) {
|
|
350
|
+
try {
|
|
351
|
+
const { publicKey: poolId } = (0, raydium_sdk_v2_1.getPdaLaunchpadPoolId)(raydium_sdk_v2_1.LAUNCHPAD_PROGRAM, mintA, quote.mint);
|
|
352
|
+
const info = await raydium.launchpad.getRpcPoolInfo({ poolId });
|
|
353
|
+
mintB = quote.mint;
|
|
354
|
+
return info;
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
// Try next quote token
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
throw new Error("No launchpad pool found for this token with SOL, USD1, or USDC");
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
365
|
+
(0, output_1.logError)("Failed to find launchpad pool for this token", message);
|
|
366
|
+
process.exitCode = 1;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const mintBDecimals = poolInfo.mintDecimalsB;
|
|
370
|
+
const mintBStr = mintB.toBase58();
|
|
371
|
+
// Identify quote token for display
|
|
372
|
+
const KNOWN_QUOTES = {
|
|
373
|
+
"So11111111111111111111111111111111111111112": "SOL",
|
|
374
|
+
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB": "USD1",
|
|
375
|
+
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC"
|
|
376
|
+
};
|
|
377
|
+
const quoteSymbol = KNOWN_QUOTES[mintBStr] ?? mintBStr.slice(0, 8) + "...";
|
|
378
|
+
(0, output_1.logInfo)(`Pool quote token: ${quoteSymbol} (${mintBStr})`);
|
|
379
|
+
(0, output_1.logInfo)(`You need ${quoteSymbol} to buy from this pool.`);
|
|
380
|
+
// Build buy transaction
|
|
381
|
+
let txData;
|
|
382
|
+
let extInfo;
|
|
383
|
+
try {
|
|
384
|
+
const result = await (0, output_1.withSpinner)("Building buy transaction", async () => {
|
|
385
|
+
// Convert amount using the correct decimals for the quote token
|
|
386
|
+
const buyAmountRaw = new bn_js_1.default(new decimal_js_1.default(options.amount).mul(new decimal_js_1.default(10).pow(mintBDecimals)).toFixed(0));
|
|
387
|
+
return raydium.launchpad.buyToken({
|
|
388
|
+
mintA,
|
|
389
|
+
mintB: mintB,
|
|
390
|
+
poolInfo,
|
|
391
|
+
buyAmount: buyAmountRaw,
|
|
392
|
+
slippage: slippageBps,
|
|
393
|
+
txVersion: raydium_sdk_v2_1.TxVersion.V0,
|
|
394
|
+
computeBudgetConfig: priorityFeeMicroLamports > 0
|
|
395
|
+
? { units: DEFAULT_COMPUTE_UNITS, microLamports: priorityFeeMicroLamports }
|
|
396
|
+
: undefined
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
txData = result;
|
|
400
|
+
extInfo = result.extInfo;
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
404
|
+
if (options.debug) {
|
|
405
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
406
|
+
(0, output_1.logError)("Failed to build transaction", detail);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
(0, output_1.logError)("Failed to build transaction", message);
|
|
410
|
+
}
|
|
411
|
+
process.exitCode = 1;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
// Show preview
|
|
415
|
+
const outAmount = extInfo.decimalOutAmount;
|
|
416
|
+
const minOutAmount = extInfo.minDecimalOutAmount;
|
|
417
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
418
|
+
(0, output_1.logJson)({
|
|
419
|
+
action: "buy",
|
|
420
|
+
input: { amount: options.amount, token: quoteSymbol, mint: mintBStr },
|
|
421
|
+
output: {
|
|
422
|
+
estimated: outAmount.toFixed(),
|
|
423
|
+
minimum: minOutAmount.toFixed()
|
|
424
|
+
},
|
|
425
|
+
slippage: slippagePercent,
|
|
426
|
+
fees: {
|
|
427
|
+
platform: extInfo.splitFee.platformFee.toString(),
|
|
428
|
+
protocol: extInfo.splitFee.protocolFee.toString(),
|
|
429
|
+
creator: extInfo.splitFee.creatorFee.toString()
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
(0, output_1.logInfo)("");
|
|
435
|
+
(0, output_1.logInfo)(`Buying tokens from launchpad`);
|
|
436
|
+
(0, output_1.logInfo)(`Input: ${options.amount} ${quoteSymbol}`);
|
|
437
|
+
(0, output_1.logInfo)(`Estimated output: ${outAmount.toFixed()} tokens`);
|
|
438
|
+
(0, output_1.logInfo)(`Minimum output: ${minOutAmount.toFixed()} tokens`);
|
|
439
|
+
(0, output_1.logInfo)(`Slippage: ${slippagePercent}%`);
|
|
440
|
+
}
|
|
441
|
+
// Confirm
|
|
442
|
+
const ok = await (0, prompt_1.promptConfirm)("Proceed with buy?", false);
|
|
443
|
+
if (!ok) {
|
|
444
|
+
(0, output_1.logInfo)("Cancelled");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
// Execute transaction
|
|
448
|
+
let result;
|
|
449
|
+
try {
|
|
450
|
+
result = await (0, output_1.withSpinner)("Sending transaction", () => txData.execute({ sendAndConfirm: true }));
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
const message = error instanceof Error ? error.message : String(error ?? "Buy failed");
|
|
454
|
+
if (options.debug) {
|
|
455
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
456
|
+
(0, output_1.logError)("Buy failed", detail);
|
|
457
|
+
const logs = error?.logs;
|
|
458
|
+
if (logs?.length) {
|
|
459
|
+
console.error(logs.join("\n"));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
(0, output_1.logError)("Buy failed", message);
|
|
464
|
+
}
|
|
465
|
+
process.exitCode = 1;
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
469
|
+
(0, output_1.logJson)({ txId: result.txId });
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
(0, output_1.logSuccess)(`Buy submitted: ${result.txId}`);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
// Sell command - sell mintA (token) to get mintB (SOL/USDC/USD1)
|
|
476
|
+
launchpad
|
|
477
|
+
.command("sell")
|
|
478
|
+
.description("Sell tokens back to a launchpad pool")
|
|
479
|
+
.requiredOption("--mint <address>", "Token mint address (mintA)")
|
|
480
|
+
.requiredOption("--amount <number>", "Amount of tokens to sell")
|
|
481
|
+
.option("--slippage <percent>", "Slippage tolerance in percent")
|
|
482
|
+
.option("--priority-fee <sol>", "Priority fee in SOL")
|
|
483
|
+
.option("--debug", "Print full error on failure")
|
|
484
|
+
.action(async (options) => {
|
|
485
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
486
|
+
// Validate slippage
|
|
487
|
+
const slippagePercent = options.slippage ? Number(options.slippage) : config["default-slippage"];
|
|
488
|
+
if (!Number.isFinite(slippagePercent) || slippagePercent < 0) {
|
|
489
|
+
(0, output_1.logError)("Invalid slippage percent");
|
|
490
|
+
process.exitCode = 1;
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const slippageBps = new bn_js_1.default(Math.round(slippagePercent * (SLIPPAGE_DENOMINATOR / 100)));
|
|
494
|
+
// Validate priority fee
|
|
495
|
+
const priorityFeeSol = options.priorityFee ? Number(options.priorityFee) : config["priority-fee"];
|
|
496
|
+
if (!Number.isFinite(priorityFeeSol) || priorityFeeSol < 0) {
|
|
497
|
+
(0, output_1.logError)("Invalid priority fee");
|
|
498
|
+
process.exitCode = 1;
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const priorityFeeLamports = priorityFeeSol * 1e9;
|
|
502
|
+
const priorityFeeMicroLamports = Math.round((priorityFeeLamports * 1e6) / DEFAULT_COMPUTE_UNITS);
|
|
503
|
+
// Check wallet
|
|
504
|
+
const walletName = config.activeWallet;
|
|
505
|
+
if (!walletName) {
|
|
506
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
507
|
+
process.exitCode = 1;
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
// Validate mint address
|
|
511
|
+
let mintA;
|
|
512
|
+
try {
|
|
513
|
+
mintA = new web3_js_1.PublicKey(options.mint);
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
(0, output_1.logError)("Invalid mint address");
|
|
517
|
+
process.exitCode = 1;
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
// Validate amount
|
|
521
|
+
const amountNum = Number(options.amount);
|
|
522
|
+
if (!Number.isFinite(amountNum) || amountNum <= 0) {
|
|
523
|
+
(0, output_1.logError)("Amount must be greater than zero");
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
// Prompt for password and decrypt wallet
|
|
528
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
529
|
+
let owner;
|
|
530
|
+
try {
|
|
531
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
535
|
+
process.exitCode = 1;
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
// Load Raydium with owner
|
|
539
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ owner, disableLoadToken: true }));
|
|
540
|
+
// First fetch pool info to get the quote token (mintB) and its decimals
|
|
541
|
+
// Try different quote tokens (SOL, USD1, USDC) to find the pool
|
|
542
|
+
const KNOWN_QUOTE_TOKENS = [
|
|
543
|
+
{ mint: new web3_js_1.PublicKey("So11111111111111111111111111111111111111112"), symbol: "SOL" },
|
|
544
|
+
{ mint: new web3_js_1.PublicKey("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"), symbol: "USD1" },
|
|
545
|
+
{ mint: new web3_js_1.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), symbol: "USDC" }
|
|
546
|
+
];
|
|
547
|
+
let poolInfo;
|
|
548
|
+
let mintB;
|
|
549
|
+
try {
|
|
550
|
+
poolInfo = await (0, output_1.withSpinner)("Fetching pool info", async () => {
|
|
551
|
+
for (const quote of KNOWN_QUOTE_TOKENS) {
|
|
552
|
+
try {
|
|
553
|
+
const { publicKey: poolId } = (0, raydium_sdk_v2_1.getPdaLaunchpadPoolId)(raydium_sdk_v2_1.LAUNCHPAD_PROGRAM, mintA, quote.mint);
|
|
554
|
+
const info = await raydium.launchpad.getRpcPoolInfo({ poolId });
|
|
555
|
+
mintB = quote.mint;
|
|
556
|
+
return info;
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
// Try next quote token
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
throw new Error("No launchpad pool found for this token with SOL, USD1, or USDC");
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
567
|
+
(0, output_1.logError)("Failed to find launchpad pool for this token", message);
|
|
568
|
+
process.exitCode = 1;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const mintADecimals = poolInfo.mintDecimalsA;
|
|
572
|
+
const mintBDecimals = poolInfo.mintDecimalsB;
|
|
573
|
+
const mintBStr = mintB.toBase58();
|
|
574
|
+
// Identify quote token for display
|
|
575
|
+
const KNOWN_QUOTES = {
|
|
576
|
+
"So11111111111111111111111111111111111111112": "SOL",
|
|
577
|
+
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB": "USD1",
|
|
578
|
+
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC"
|
|
579
|
+
};
|
|
580
|
+
const quoteSymbol = KNOWN_QUOTES[mintBStr] ?? mintBStr.slice(0, 8) + "...";
|
|
581
|
+
(0, output_1.logInfo)(`Pool quote token: ${quoteSymbol} (${mintBStr})`);
|
|
582
|
+
(0, output_1.logInfo)(`You will receive ${quoteSymbol} from this sale.`);
|
|
583
|
+
// Build sell transaction to get quote
|
|
584
|
+
let txData;
|
|
585
|
+
let extInfo;
|
|
586
|
+
try {
|
|
587
|
+
const result = await (0, output_1.withSpinner)("Building sell transaction", async () => {
|
|
588
|
+
// Convert amount using the correct decimals for mintA
|
|
589
|
+
const sellAmountRaw = new bn_js_1.default(new decimal_js_1.default(options.amount).mul(new decimal_js_1.default(10).pow(mintADecimals)).toFixed(0));
|
|
590
|
+
return raydium.launchpad.sellToken({
|
|
591
|
+
mintA,
|
|
592
|
+
mintB: mintB,
|
|
593
|
+
poolInfo,
|
|
594
|
+
sellAmount: sellAmountRaw,
|
|
595
|
+
slippage: slippageBps,
|
|
596
|
+
txVersion: raydium_sdk_v2_1.TxVersion.V0,
|
|
597
|
+
computeBudgetConfig: priorityFeeMicroLamports > 0
|
|
598
|
+
? { units: DEFAULT_COMPUTE_UNITS, microLamports: priorityFeeMicroLamports }
|
|
599
|
+
: undefined
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
txData = result;
|
|
603
|
+
extInfo = result.extInfo;
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
607
|
+
if (options.debug) {
|
|
608
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
609
|
+
(0, output_1.logError)("Failed to build transaction", detail);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
(0, output_1.logError)("Failed to build transaction", message);
|
|
613
|
+
}
|
|
614
|
+
process.exitCode = 1;
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
// Show preview
|
|
618
|
+
const outAmountRaw = extInfo.outAmount;
|
|
619
|
+
const outAmountFormatted = new decimal_js_1.default(outAmountRaw.toString())
|
|
620
|
+
.div(new decimal_js_1.default(10).pow(mintBDecimals))
|
|
621
|
+
.toFixed();
|
|
622
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
623
|
+
(0, output_1.logJson)({
|
|
624
|
+
action: "sell",
|
|
625
|
+
input: { amount: options.amount, token: "mintA" },
|
|
626
|
+
output: {
|
|
627
|
+
amount: outAmountRaw.toString(),
|
|
628
|
+
formatted: outAmountFormatted,
|
|
629
|
+
token: quoteSymbol,
|
|
630
|
+
mint: mintBStr
|
|
631
|
+
},
|
|
632
|
+
slippage: slippagePercent
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
(0, output_1.logInfo)("");
|
|
637
|
+
(0, output_1.logInfo)(`Selling tokens to launchpad`);
|
|
638
|
+
(0, output_1.logInfo)(`Input: ${options.amount} tokens`);
|
|
639
|
+
(0, output_1.logInfo)(`Minimum output: ${outAmountFormatted} ${quoteSymbol}`);
|
|
640
|
+
(0, output_1.logInfo)(`Slippage: ${slippagePercent}%`);
|
|
641
|
+
}
|
|
642
|
+
// Confirm
|
|
643
|
+
const ok = await (0, prompt_1.promptConfirm)("Proceed with sell?", false);
|
|
644
|
+
if (!ok) {
|
|
645
|
+
(0, output_1.logInfo)("Cancelled");
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
// Execute transaction
|
|
649
|
+
let result;
|
|
650
|
+
try {
|
|
651
|
+
result = await (0, output_1.withSpinner)("Sending transaction", () => txData.execute({ sendAndConfirm: true }));
|
|
652
|
+
}
|
|
653
|
+
catch (error) {
|
|
654
|
+
const message = error instanceof Error ? error.message : String(error ?? "Sell failed");
|
|
655
|
+
if (options.debug) {
|
|
656
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
657
|
+
(0, output_1.logError)("Sell failed", detail);
|
|
658
|
+
const logs = error?.logs;
|
|
659
|
+
if (logs?.length) {
|
|
660
|
+
console.error(logs.join("\n"));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
(0, output_1.logError)("Sell failed", message);
|
|
665
|
+
}
|
|
666
|
+
process.exitCode = 1;
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
670
|
+
(0, output_1.logJson)({ txId: result.txId });
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
(0, output_1.logSuccess)(`Sell submitted: ${result.txId}`);
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
// Create platform config command
|
|
677
|
+
launchpad
|
|
678
|
+
.command("create-platform")
|
|
679
|
+
.description("Create a new launchpad platform configuration")
|
|
680
|
+
.requiredOption("--name <string>", "Platform name")
|
|
681
|
+
.option("--fee-rate <bps>", "Platform fee in basis points (default: 100 = 1%)", "100")
|
|
682
|
+
.option("--creator-fee-rate <bps>", "Creator fee in basis points (default: 50 = 0.5%)", "50")
|
|
683
|
+
.option("--platform-scale <percent>", "Platform LP % on migration (default: 50)", "50")
|
|
684
|
+
.option("--creator-scale <percent>", "Creator LP % on migration (default: 50)", "50")
|
|
685
|
+
.option("--burn-scale <percent>", "Burn LP % on migration (default: 0)", "0")
|
|
686
|
+
.option("--web <url>", "Platform website URL")
|
|
687
|
+
.option("--img <url>", "Platform logo image URL")
|
|
688
|
+
.option("--priority-fee <sol>", "Priority fee in SOL")
|
|
689
|
+
.option("--debug", "Print full error on failure")
|
|
690
|
+
.action(async (options) => {
|
|
691
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
692
|
+
// Validate fee rates
|
|
693
|
+
const feeRateBps = Number(options.feeRate);
|
|
694
|
+
const creatorFeeRateBps = Number(options.creatorFeeRate);
|
|
695
|
+
if (!Number.isFinite(feeRateBps) || feeRateBps < 0 || feeRateBps > 1000000) {
|
|
696
|
+
(0, output_1.logError)("Invalid fee rate (must be 0-1000000 bps)");
|
|
697
|
+
process.exitCode = 1;
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (!Number.isFinite(creatorFeeRateBps) || creatorFeeRateBps < 0 || creatorFeeRateBps > 1000000) {
|
|
701
|
+
(0, output_1.logError)("Invalid creator fee rate (must be 0-1000000 bps)");
|
|
702
|
+
process.exitCode = 1;
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
// Validate LP scales (must sum to 100)
|
|
706
|
+
const platformScale = Number(options.platformScale);
|
|
707
|
+
const creatorScale = Number(options.creatorScale);
|
|
708
|
+
const burnScale = Number(options.burnScale);
|
|
709
|
+
if (platformScale + creatorScale + burnScale !== 100) {
|
|
710
|
+
(0, output_1.logError)("LP scales must sum to 100 (platform + creator + burn)");
|
|
711
|
+
process.exitCode = 1;
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
// Validate priority fee
|
|
715
|
+
const priorityFeeSol = options.priorityFee ? Number(options.priorityFee) : config["priority-fee"];
|
|
716
|
+
if (!Number.isFinite(priorityFeeSol) || priorityFeeSol < 0) {
|
|
717
|
+
(0, output_1.logError)("Invalid priority fee");
|
|
718
|
+
process.exitCode = 1;
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const priorityFeeLamports = priorityFeeSol * 1e9;
|
|
722
|
+
const priorityFeeMicroLamports = Math.round((priorityFeeLamports * 1e6) / DEFAULT_COMPUTE_UNITS);
|
|
723
|
+
// Check wallet
|
|
724
|
+
const walletName = config.activeWallet;
|
|
725
|
+
if (!walletName) {
|
|
726
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
727
|
+
process.exitCode = 1;
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
// Prompt for password and decrypt wallet
|
|
731
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
732
|
+
let owner;
|
|
733
|
+
try {
|
|
734
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
738
|
+
process.exitCode = 1;
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
// Load Raydium with owner
|
|
742
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ owner, disableLoadToken: true }));
|
|
743
|
+
// Fetch CPMM configs to get a valid cpConfigId for pool migration
|
|
744
|
+
const cpmmConfigs = await (0, output_1.withSpinner)("Fetching CPMM configs", () => raydium.api.getCpmmConfigs());
|
|
745
|
+
if (cpmmConfigs.length === 0) {
|
|
746
|
+
(0, output_1.logError)("No CPMM configs available from API");
|
|
747
|
+
process.exitCode = 1;
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const selectedCpmmConfig = cpmmConfigs[0];
|
|
751
|
+
// Convert percentages to scale (1,000,000 denominator)
|
|
752
|
+
const platformScaleBN = new bn_js_1.default(platformScale * 10000);
|
|
753
|
+
const creatorScaleBN = new bn_js_1.default(creatorScale * 10000);
|
|
754
|
+
const burnScaleBN = new bn_js_1.default(burnScale * 10000);
|
|
755
|
+
// Convert fee rates (bps to 1,000,000 denominator: 100 bps = 1% = 10000)
|
|
756
|
+
const feeRateBN = new bn_js_1.default(feeRateBps * 100);
|
|
757
|
+
const creatorFeeRateBN = new bn_js_1.default(creatorFeeRateBps * 100);
|
|
758
|
+
// Show preview
|
|
759
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
760
|
+
(0, output_1.logJson)({
|
|
761
|
+
action: "create-platform",
|
|
762
|
+
name: options.name,
|
|
763
|
+
feeRate: `${feeRateBps / 100}%`,
|
|
764
|
+
creatorFeeRate: `${creatorFeeRateBps / 100}%`,
|
|
765
|
+
lpSplit: {
|
|
766
|
+
platform: `${platformScale}%`,
|
|
767
|
+
creator: `${creatorScale}%`,
|
|
768
|
+
burn: `${burnScale}%`
|
|
769
|
+
},
|
|
770
|
+
cpmmConfig: selectedCpmmConfig.id
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
(0, output_1.logInfo)("");
|
|
775
|
+
(0, output_1.logInfo)(`Creating Platform Config`);
|
|
776
|
+
(0, output_1.logInfo)(` Name: ${options.name}`);
|
|
777
|
+
(0, output_1.logInfo)(` Platform Fee: ${feeRateBps / 100}%`);
|
|
778
|
+
(0, output_1.logInfo)(` Creator Fee: ${creatorFeeRateBps / 100}%`);
|
|
779
|
+
(0, output_1.logInfo)(` LP Split: ${platformScale}% platform / ${creatorScale}% creator / ${burnScale}% burn`);
|
|
780
|
+
(0, output_1.logInfo)(` CPMM Config: ${selectedCpmmConfig.id}`);
|
|
781
|
+
if (options.web)
|
|
782
|
+
(0, output_1.logInfo)(` Website: ${options.web}`);
|
|
783
|
+
if (options.img)
|
|
784
|
+
(0, output_1.logInfo)(` Image: ${options.img}`);
|
|
785
|
+
}
|
|
786
|
+
// Confirm
|
|
787
|
+
const ok = await (0, prompt_1.promptConfirm)("Proceed with creating platform?", false);
|
|
788
|
+
if (!ok) {
|
|
789
|
+
(0, output_1.logInfo)("Cancelled");
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
// Build transaction
|
|
793
|
+
let txData;
|
|
794
|
+
let extInfo;
|
|
795
|
+
try {
|
|
796
|
+
const result = await (0, output_1.withSpinner)("Building transaction", async () => {
|
|
797
|
+
return raydium.launchpad.createPlatformConfig({
|
|
798
|
+
platformAdmin: owner.publicKey,
|
|
799
|
+
platformClaimFeeWallet: owner.publicKey,
|
|
800
|
+
platformLockNftWallet: owner.publicKey,
|
|
801
|
+
platformVestingWallet: owner.publicKey,
|
|
802
|
+
cpConfigId: new web3_js_1.PublicKey(selectedCpmmConfig.id),
|
|
803
|
+
migrateCpLockNftScale: {
|
|
804
|
+
platformScale: platformScaleBN,
|
|
805
|
+
creatorScale: creatorScaleBN,
|
|
806
|
+
burnScale: burnScaleBN
|
|
807
|
+
},
|
|
808
|
+
transferFeeExtensionAuth: owner.publicKey,
|
|
809
|
+
feeRate: feeRateBN,
|
|
810
|
+
creatorFeeRate: creatorFeeRateBN,
|
|
811
|
+
name: options.name,
|
|
812
|
+
web: options.web ?? "",
|
|
813
|
+
img: options.img ?? "",
|
|
814
|
+
platformVestingScale: new bn_js_1.default(0),
|
|
815
|
+
txVersion: raydium_sdk_v2_1.TxVersion.V0,
|
|
816
|
+
computeBudgetConfig: priorityFeeMicroLamports > 0
|
|
817
|
+
? { units: DEFAULT_COMPUTE_UNITS, microLamports: priorityFeeMicroLamports }
|
|
818
|
+
: undefined
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
txData = result;
|
|
822
|
+
extInfo = result.extInfo;
|
|
823
|
+
}
|
|
824
|
+
catch (error) {
|
|
825
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
826
|
+
if (options.debug) {
|
|
827
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
828
|
+
(0, output_1.logError)("Failed to build transaction", detail);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
(0, output_1.logError)("Failed to build transaction", message);
|
|
832
|
+
}
|
|
833
|
+
process.exitCode = 1;
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
// Execute transaction
|
|
837
|
+
let result;
|
|
838
|
+
try {
|
|
839
|
+
result = await (0, output_1.withSpinner)("Sending transaction", () => txData.execute({ sendAndConfirm: true }));
|
|
840
|
+
}
|
|
841
|
+
catch (error) {
|
|
842
|
+
if (options.debug) {
|
|
843
|
+
console.error("\n=== Full error object ===");
|
|
844
|
+
console.error((0, node_util_1.inspect)(error, { depth: 10, colors: true }));
|
|
845
|
+
console.error("=========================\n");
|
|
846
|
+
// Try to extract any useful info
|
|
847
|
+
const err = error;
|
|
848
|
+
if (err.signature)
|
|
849
|
+
console.error(`Signature: ${err.signature}`);
|
|
850
|
+
if (err.txId)
|
|
851
|
+
console.error(`TxId: ${err.txId}`);
|
|
852
|
+
if (err.logs)
|
|
853
|
+
console.error(`Logs:\n${err.logs.join("\n")}`);
|
|
854
|
+
}
|
|
855
|
+
const message = error instanceof Error ? error.message : String(error ?? "Unknown error");
|
|
856
|
+
(0, output_1.logError)("Create platform failed", message || "Transaction failed (no error message from SDK)");
|
|
857
|
+
process.exitCode = 1;
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
861
|
+
(0, output_1.logJson)({
|
|
862
|
+
txId: result.txId,
|
|
863
|
+
platformId: extInfo.platformId.toBase58()
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
(0, output_1.logSuccess)(`Platform created: ${result.txId}`);
|
|
868
|
+
(0, output_1.logInfo)(`Platform ID: ${extInfo.platformId.toBase58()}`);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
// Create launchpad (launch a new token) command
|
|
872
|
+
launchpad
|
|
873
|
+
.command("create")
|
|
874
|
+
.description("Launch a new token with a bonding curve")
|
|
875
|
+
.requiredOption("--platform-id <address>", "Platform config address")
|
|
876
|
+
.requiredOption("--name <string>", "Token name")
|
|
877
|
+
.requiredOption("--symbol <string>", "Token symbol")
|
|
878
|
+
.option("--image <path>", "Path to token image (uploads to IPFS)")
|
|
879
|
+
.option("--uri <string>", "Token metadata URI (use instead of --image if you have a URI)")
|
|
880
|
+
.option("--description <string>", "Token description")
|
|
881
|
+
.option("--twitter <url>", "Twitter URL")
|
|
882
|
+
.option("--telegram <url>", "Telegram URL")
|
|
883
|
+
.option("--website <url>", "Website URL")
|
|
884
|
+
.option("--config-id <address>", "Launchpad config ID (auto-detected if not specified)")
|
|
885
|
+
.option("--decimals <number>", "Token decimals (default: 6)", "6")
|
|
886
|
+
.option("--buy-amount <sol>", "Initial SOL to buy (optional dev buy)")
|
|
887
|
+
.option("--slippage <percent>", "Slippage tolerance for initial buy (default: 1)", "1")
|
|
888
|
+
.option("--priority-fee <sol>", "Priority fee in SOL")
|
|
889
|
+
.option("--debug", "Print full error on failure")
|
|
890
|
+
.action(async (options) => {
|
|
891
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
892
|
+
// Validate that either --image or --uri is provided
|
|
893
|
+
if (!options.image && !options.uri) {
|
|
894
|
+
(0, output_1.logError)("Either --image or --uri is required");
|
|
895
|
+
(0, output_1.logInfo)(" --image <path> Path to local image file (auto-uploads to IPFS)");
|
|
896
|
+
(0, output_1.logInfo)(" --uri <url> Pre-hosted metadata URI");
|
|
897
|
+
process.exitCode = 1;
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
// Validate platform ID
|
|
901
|
+
let platformId;
|
|
902
|
+
try {
|
|
903
|
+
platformId = new web3_js_1.PublicKey(options.platformId);
|
|
904
|
+
}
|
|
905
|
+
catch {
|
|
906
|
+
(0, output_1.logError)("Invalid platform ID address");
|
|
907
|
+
process.exitCode = 1;
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
// Validate decimals
|
|
911
|
+
const decimals = Number(options.decimals);
|
|
912
|
+
if (!Number.isFinite(decimals) || decimals < 0 || decimals > 9) {
|
|
913
|
+
(0, output_1.logError)("Invalid decimals (must be 0-9)");
|
|
914
|
+
process.exitCode = 1;
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
// Validate slippage
|
|
918
|
+
const slippagePercent = Number(options.slippage);
|
|
919
|
+
if (!Number.isFinite(slippagePercent) || slippagePercent < 0) {
|
|
920
|
+
(0, output_1.logError)("Invalid slippage percent");
|
|
921
|
+
process.exitCode = 1;
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const slippageBps = new bn_js_1.default(Math.round(slippagePercent * 100));
|
|
925
|
+
// Validate buy amount
|
|
926
|
+
let buyAmountLamports = new bn_js_1.default(0);
|
|
927
|
+
if (options.buyAmount) {
|
|
928
|
+
const buyAmountSol = Number(options.buyAmount);
|
|
929
|
+
if (!Number.isFinite(buyAmountSol) || buyAmountSol < 0) {
|
|
930
|
+
(0, output_1.logError)("Invalid buy amount");
|
|
931
|
+
process.exitCode = 1;
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
buyAmountLamports = new bn_js_1.default(Math.round(buyAmountSol * 1e9));
|
|
935
|
+
}
|
|
936
|
+
// Validate priority fee
|
|
937
|
+
const priorityFeeSol = options.priorityFee ? Number(options.priorityFee) : config["priority-fee"];
|
|
938
|
+
if (!Number.isFinite(priorityFeeSol) || priorityFeeSol < 0) {
|
|
939
|
+
(0, output_1.logError)("Invalid priority fee");
|
|
940
|
+
process.exitCode = 1;
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const priorityFeeLamports = priorityFeeSol * 1e9;
|
|
944
|
+
const priorityFeeMicroLamports = Math.round((priorityFeeLamports * 1e6) / DEFAULT_COMPUTE_UNITS);
|
|
945
|
+
// Check wallet
|
|
946
|
+
const walletName = config.activeWallet;
|
|
947
|
+
if (!walletName) {
|
|
948
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
949
|
+
process.exitCode = 1;
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
// Handle image upload to IPFS if --image is provided
|
|
953
|
+
let metadataUri;
|
|
954
|
+
let imageUrl;
|
|
955
|
+
if (options.image) {
|
|
956
|
+
// Check if Pinata JWT is configured
|
|
957
|
+
const pinataJwt = config["pinata-jwt"];
|
|
958
|
+
if (!pinataJwt) {
|
|
959
|
+
(0, output_1.logError)("Pinata JWT not configured");
|
|
960
|
+
(0, output_1.logInfo)("Get a free JWT at https://pinata.cloud and run:");
|
|
961
|
+
(0, output_1.logInfo)(" raydium config set pinata-jwt <your-jwt>");
|
|
962
|
+
process.exitCode = 1;
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
try {
|
|
966
|
+
const result = await (0, output_1.withSpinner)("Uploading to IPFS", () => (0, ipfs_1.uploadTokenMetadata)({
|
|
967
|
+
imagePath: options.image,
|
|
968
|
+
name: options.name,
|
|
969
|
+
symbol: options.symbol,
|
|
970
|
+
description: options.description,
|
|
971
|
+
twitter: options.twitter,
|
|
972
|
+
telegram: options.telegram,
|
|
973
|
+
website: options.website,
|
|
974
|
+
apiKey: pinataJwt,
|
|
975
|
+
}));
|
|
976
|
+
metadataUri = result.uri;
|
|
977
|
+
imageUrl = result.imageUrl;
|
|
978
|
+
(0, output_1.logInfo)(`Image: ${imageUrl}`);
|
|
979
|
+
(0, output_1.logInfo)(`Metadata: ${metadataUri}`);
|
|
980
|
+
}
|
|
981
|
+
catch (error) {
|
|
982
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
983
|
+
(0, output_1.logError)("Failed to upload to IPFS", message);
|
|
984
|
+
process.exitCode = 1;
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
metadataUri = options.uri;
|
|
990
|
+
}
|
|
991
|
+
// Prompt for password and decrypt wallet
|
|
992
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
993
|
+
let owner;
|
|
994
|
+
try {
|
|
995
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
996
|
+
}
|
|
997
|
+
catch (error) {
|
|
998
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
999
|
+
process.exitCode = 1;
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
// Load Raydium with owner
|
|
1003
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ owner, disableLoadToken: true }));
|
|
1004
|
+
let platformInfo;
|
|
1005
|
+
try {
|
|
1006
|
+
platformInfo = await (0, output_1.withSpinner)("Fetching platform info", async () => {
|
|
1007
|
+
const response = await fetch(`https://launch-mint-v1.raydium.io/main/platforms?pageSize=100`);
|
|
1008
|
+
if (!response.ok) {
|
|
1009
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1010
|
+
}
|
|
1011
|
+
const json = (await response.json());
|
|
1012
|
+
const platforms = json.data?.data ?? [];
|
|
1013
|
+
return platforms.find((p) => p.pubKey === platformId.toBase58());
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
catch (error) {
|
|
1017
|
+
(0, output_1.logError)("Failed to fetch platform info", error.message);
|
|
1018
|
+
process.exitCode = 1;
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (!platformInfo) {
|
|
1022
|
+
(0, output_1.logError)(`Platform not found: ${platformId.toBase58()}`);
|
|
1023
|
+
(0, output_1.logInfo)("Use 'raydium launchpad platforms' to list available platforms");
|
|
1024
|
+
process.exitCode = 1;
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
// Fetch available launchpad configs from API
|
|
1028
|
+
const launchConfigs = await (0, output_1.withSpinner)("Fetching launchpad configs", () => raydium.api.fetchLaunchConfigs());
|
|
1029
|
+
if (launchConfigs.length === 0) {
|
|
1030
|
+
(0, output_1.logError)("No launchpad configs available from API");
|
|
1031
|
+
process.exitCode = 1;
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
// Select config: use provided config-id, or find SOL config
|
|
1035
|
+
let selectedConfig;
|
|
1036
|
+
if (options.configId) {
|
|
1037
|
+
selectedConfig = launchConfigs.find((c) => c.key.pubKey === options.configId);
|
|
1038
|
+
if (!selectedConfig) {
|
|
1039
|
+
(0, output_1.logError)(`Config not found: ${options.configId}`);
|
|
1040
|
+
(0, output_1.logInfo)("Available configs:");
|
|
1041
|
+
launchConfigs.forEach((c) => {
|
|
1042
|
+
(0, output_1.logInfo)(` ${c.key.pubKey} (${c.mintInfoB.symbol})`);
|
|
1043
|
+
});
|
|
1044
|
+
process.exitCode = 1;
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
else {
|
|
1049
|
+
// Find a config that uses SOL as the quote token
|
|
1050
|
+
const solConfig = launchConfigs.find((c) => c.mintInfoB.symbol === "SOL" || c.mintInfoB.symbol === "WSOL");
|
|
1051
|
+
selectedConfig = solConfig || launchConfigs[0];
|
|
1052
|
+
}
|
|
1053
|
+
// Generate a new keypair for the token mint
|
|
1054
|
+
const mintKeypair = web3_js_1.Keypair.generate();
|
|
1055
|
+
// Show preview
|
|
1056
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1057
|
+
(0, output_1.logJson)({
|
|
1058
|
+
action: "create",
|
|
1059
|
+
token: {
|
|
1060
|
+
name: options.name,
|
|
1061
|
+
symbol: options.symbol,
|
|
1062
|
+
uri: metadataUri,
|
|
1063
|
+
image: imageUrl,
|
|
1064
|
+
decimals,
|
|
1065
|
+
mint: mintKeypair.publicKey.toBase58()
|
|
1066
|
+
},
|
|
1067
|
+
platformId: platformId.toBase58(),
|
|
1068
|
+
configId: selectedConfig.key.pubKey,
|
|
1069
|
+
quoteToken: selectedConfig.mintInfoB.symbol,
|
|
1070
|
+
initialBuy: options.buyAmount ? `${options.buyAmount} SOL` : "none",
|
|
1071
|
+
slippage: `${slippagePercent}%`
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
(0, output_1.logInfo)("");
|
|
1076
|
+
(0, output_1.logInfo)(`Launching Token`);
|
|
1077
|
+
(0, output_1.logInfo)(` Name: ${options.name}`);
|
|
1078
|
+
(0, output_1.logInfo)(` Symbol: ${options.symbol}`);
|
|
1079
|
+
(0, output_1.logInfo)(` Decimals: ${decimals}`);
|
|
1080
|
+
(0, output_1.logInfo)(` URI: ${metadataUri}`);
|
|
1081
|
+
(0, output_1.logInfo)(` Mint: ${mintKeypair.publicKey.toBase58()}`);
|
|
1082
|
+
(0, output_1.logInfo)(` Platform: ${platformInfo.name} (${platformId.toBase58()})`);
|
|
1083
|
+
(0, output_1.logInfo)(` Config: ${selectedConfig.key.pubKey}`);
|
|
1084
|
+
(0, output_1.logInfo)(` Quote: ${selectedConfig.mintInfoB.symbol}`);
|
|
1085
|
+
if (options.buyAmount) {
|
|
1086
|
+
(0, output_1.logInfo)(` Initial Buy: ${options.buyAmount} SOL`);
|
|
1087
|
+
(0, output_1.logInfo)(` Slippage: ${slippagePercent}%`);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
// Confirm
|
|
1091
|
+
const ok = await (0, prompt_1.promptConfirm)("Proceed with launching token?", false);
|
|
1092
|
+
if (!ok) {
|
|
1093
|
+
(0, output_1.logInfo)("Cancelled");
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
// Build transaction
|
|
1097
|
+
let txData;
|
|
1098
|
+
let extInfo;
|
|
1099
|
+
try {
|
|
1100
|
+
const result = await (0, output_1.withSpinner)("Building transaction", async () => {
|
|
1101
|
+
return raydium.launchpad.createLaunchpad({
|
|
1102
|
+
mintA: mintKeypair.publicKey,
|
|
1103
|
+
name: options.name,
|
|
1104
|
+
symbol: options.symbol,
|
|
1105
|
+
uri: metadataUri,
|
|
1106
|
+
decimals,
|
|
1107
|
+
platformId,
|
|
1108
|
+
configId: new web3_js_1.PublicKey(selectedConfig.key.pubKey),
|
|
1109
|
+
migrateType: "cpmm",
|
|
1110
|
+
buyAmount: buyAmountLamports,
|
|
1111
|
+
slippage: slippageBps,
|
|
1112
|
+
createOnly: buyAmountLamports.isZero(), // Skip buy if no amount specified
|
|
1113
|
+
extraSigners: [mintKeypair],
|
|
1114
|
+
txVersion: raydium_sdk_v2_1.TxVersion.V0,
|
|
1115
|
+
computeBudgetConfig: priorityFeeMicroLamports > 0
|
|
1116
|
+
? { units: DEFAULT_COMPUTE_UNITS, microLamports: priorityFeeMicroLamports }
|
|
1117
|
+
: undefined
|
|
1118
|
+
});
|
|
1119
|
+
});
|
|
1120
|
+
txData = result;
|
|
1121
|
+
extInfo = result.extInfo;
|
|
1122
|
+
}
|
|
1123
|
+
catch (error) {
|
|
1124
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1125
|
+
if (options.debug) {
|
|
1126
|
+
console.error("\n[DEBUG] Full error object:");
|
|
1127
|
+
console.error((0, node_util_1.inspect)(error, { depth: 10, colors: true }));
|
|
1128
|
+
// Check for nested errors or causes
|
|
1129
|
+
const err = error;
|
|
1130
|
+
if (err?.cause) {
|
|
1131
|
+
console.error("\n[DEBUG] Error cause:");
|
|
1132
|
+
console.error((0, node_util_1.inspect)(err.cause, { depth: 10, colors: true }));
|
|
1133
|
+
}
|
|
1134
|
+
if (err?.logs) {
|
|
1135
|
+
console.error("\n[DEBUG] Logs:");
|
|
1136
|
+
console.error(err.logs.join("\n"));
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
(0, output_1.logError)("Failed to build transaction", message || "Unknown error - run with --debug for details");
|
|
1140
|
+
process.exitCode = 1;
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
// Execute transaction
|
|
1144
|
+
let result;
|
|
1145
|
+
try {
|
|
1146
|
+
result = await (0, output_1.withSpinner)("Sending transaction", () => txData.execute({ sendAndConfirm: true, sequentially: true }));
|
|
1147
|
+
}
|
|
1148
|
+
catch (error) {
|
|
1149
|
+
const message = error instanceof Error ? error.message : String(error ?? "Create launchpad failed");
|
|
1150
|
+
if (options.debug) {
|
|
1151
|
+
console.error("\n[DEBUG] Full error object:");
|
|
1152
|
+
console.error((0, node_util_1.inspect)(error, { depth: 10, colors: true }));
|
|
1153
|
+
const err = error;
|
|
1154
|
+
if (err?.cause) {
|
|
1155
|
+
console.error("\n[DEBUG] Error cause:");
|
|
1156
|
+
console.error((0, node_util_1.inspect)(err.cause, { depth: 10, colors: true }));
|
|
1157
|
+
}
|
|
1158
|
+
if (err?.logs) {
|
|
1159
|
+
console.error("\n[DEBUG] Transaction logs:");
|
|
1160
|
+
console.error(err.logs.join("\n"));
|
|
1161
|
+
}
|
|
1162
|
+
if (err?.simulationLogs) {
|
|
1163
|
+
console.error("\n[DEBUG] Simulation logs:");
|
|
1164
|
+
console.error(err.simulationLogs.join("\n"));
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
(0, output_1.logError)("Create launchpad failed", message || "Unknown error - run with --debug for details");
|
|
1168
|
+
process.exitCode = 1;
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1172
|
+
(0, output_1.logJson)({
|
|
1173
|
+
txId: result.txIds[0],
|
|
1174
|
+
mintAddress: mintKeypair.publicKey.toBase58(),
|
|
1175
|
+
poolId: extInfo.address.poolId.toBase58()
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
(0, output_1.logSuccess)(`Token launched: ${result.txIds[0]}`);
|
|
1180
|
+
(0, output_1.logInfo)(`Mint Address: ${mintKeypair.publicKey.toBase58()}`);
|
|
1181
|
+
(0, output_1.logInfo)(`Pool ID: ${extInfo.address.poolId.toBase58()}`);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
// Claim platform fees command
|
|
1185
|
+
launchpad
|
|
1186
|
+
.command("claim-fees")
|
|
1187
|
+
.description("Claim platform fees from launchpad")
|
|
1188
|
+
.requiredOption("--platform-id <address>", "Platform config address")
|
|
1189
|
+
.option("--mint-b <address>", "Quote token mint (default: SOL)")
|
|
1190
|
+
.option("--priority-fee <sol>", "Priority fee in SOL")
|
|
1191
|
+
.option("--debug", "Print full error on failure")
|
|
1192
|
+
.action(async (options) => {
|
|
1193
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
1194
|
+
// Validate platform ID
|
|
1195
|
+
let platformId;
|
|
1196
|
+
try {
|
|
1197
|
+
platformId = new web3_js_1.PublicKey(options.platformId);
|
|
1198
|
+
}
|
|
1199
|
+
catch {
|
|
1200
|
+
(0, output_1.logError)("Invalid platform ID address");
|
|
1201
|
+
process.exitCode = 1;
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
// Validate mint B
|
|
1205
|
+
let mintB = NATIVE_MINT;
|
|
1206
|
+
if (options.mintB) {
|
|
1207
|
+
try {
|
|
1208
|
+
mintB = new web3_js_1.PublicKey(options.mintB);
|
|
1209
|
+
}
|
|
1210
|
+
catch {
|
|
1211
|
+
(0, output_1.logError)("Invalid mint B address");
|
|
1212
|
+
process.exitCode = 1;
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
// Validate priority fee
|
|
1217
|
+
const priorityFeeSol = options.priorityFee ? Number(options.priorityFee) : config["priority-fee"];
|
|
1218
|
+
if (!Number.isFinite(priorityFeeSol) || priorityFeeSol < 0) {
|
|
1219
|
+
(0, output_1.logError)("Invalid priority fee");
|
|
1220
|
+
process.exitCode = 1;
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
const priorityFeeLamports = priorityFeeSol * 1e9;
|
|
1224
|
+
const priorityFeeMicroLamports = Math.round((priorityFeeLamports * 1e6) / DEFAULT_COMPUTE_UNITS);
|
|
1225
|
+
// Check wallet
|
|
1226
|
+
const walletName = config.activeWallet;
|
|
1227
|
+
if (!walletName) {
|
|
1228
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
1229
|
+
process.exitCode = 1;
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
// Prompt for password and decrypt wallet
|
|
1233
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
1234
|
+
let owner;
|
|
1235
|
+
try {
|
|
1236
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
1237
|
+
}
|
|
1238
|
+
catch (error) {
|
|
1239
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
1240
|
+
process.exitCode = 1;
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
// Load Raydium with owner
|
|
1244
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ owner, disableLoadToken: true }));
|
|
1245
|
+
// Identify quote token for display
|
|
1246
|
+
const KNOWN_QUOTES = {
|
|
1247
|
+
"So11111111111111111111111111111111111111112": "SOL",
|
|
1248
|
+
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB": "USD1",
|
|
1249
|
+
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC"
|
|
1250
|
+
};
|
|
1251
|
+
const mintBStr = mintB.toBase58();
|
|
1252
|
+
const quoteSymbol = KNOWN_QUOTES[mintBStr] ?? mintBStr.slice(0, 8) + "...";
|
|
1253
|
+
// Show preview
|
|
1254
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1255
|
+
(0, output_1.logJson)({
|
|
1256
|
+
action: "claim-fees",
|
|
1257
|
+
platformId: platformId.toBase58(),
|
|
1258
|
+
quoteToken: quoteSymbol,
|
|
1259
|
+
claimWallet: owner.publicKey.toBase58()
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
(0, output_1.logInfo)("");
|
|
1264
|
+
(0, output_1.logInfo)(`Claiming Platform Fees`);
|
|
1265
|
+
(0, output_1.logInfo)(` Platform: ${platformId.toBase58()}`);
|
|
1266
|
+
(0, output_1.logInfo)(` Quote Token: ${quoteSymbol}`);
|
|
1267
|
+
(0, output_1.logInfo)(` Claim Wallet: ${owner.publicKey.toBase58()}`);
|
|
1268
|
+
}
|
|
1269
|
+
// Confirm
|
|
1270
|
+
const ok = await (0, prompt_1.promptConfirm)("Proceed with claiming fees?", false);
|
|
1271
|
+
if (!ok) {
|
|
1272
|
+
(0, output_1.logInfo)("Cancelled");
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
// Determine the token program for mintB
|
|
1276
|
+
const isToken2022 = TOKEN_2022_MINTS.has(mintBStr);
|
|
1277
|
+
const mintBProgram = isToken2022 ? spl_token_1.TOKEN_2022_PROGRAM_ID : spl_token_1.TOKEN_PROGRAM_ID;
|
|
1278
|
+
if (!(0, output_1.isJsonOutput)() && isToken2022) {
|
|
1279
|
+
(0, output_1.logInfo)(` Token Program: Token-2022`);
|
|
1280
|
+
}
|
|
1281
|
+
// Build transaction
|
|
1282
|
+
let txData;
|
|
1283
|
+
try {
|
|
1284
|
+
txData = await (0, output_1.withSpinner)("Building transaction", async () => {
|
|
1285
|
+
return raydium.launchpad.claimVaultPlatformFee({
|
|
1286
|
+
platformId,
|
|
1287
|
+
mintB,
|
|
1288
|
+
mintBProgram,
|
|
1289
|
+
claimFeeWallet: owner.publicKey,
|
|
1290
|
+
txVersion: raydium_sdk_v2_1.TxVersion.V0,
|
|
1291
|
+
computeBudgetConfig: priorityFeeMicroLamports > 0
|
|
1292
|
+
? { units: DEFAULT_COMPUTE_UNITS, microLamports: priorityFeeMicroLamports }
|
|
1293
|
+
: undefined
|
|
1294
|
+
});
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
catch (error) {
|
|
1298
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1299
|
+
if (options.debug) {
|
|
1300
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
1301
|
+
(0, output_1.logError)("Failed to build transaction", detail);
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
(0, output_1.logError)("Failed to build transaction", message);
|
|
1305
|
+
}
|
|
1306
|
+
process.exitCode = 1;
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
// Execute transaction
|
|
1310
|
+
let result;
|
|
1311
|
+
try {
|
|
1312
|
+
result = await (0, output_1.withSpinner)("Sending transaction", () => txData.execute({ sendAndConfirm: true }));
|
|
1313
|
+
}
|
|
1314
|
+
catch (error) {
|
|
1315
|
+
const message = error instanceof Error ? error.message : String(error ?? "Claim fees failed");
|
|
1316
|
+
if (options.debug) {
|
|
1317
|
+
// Show full error object for debugging
|
|
1318
|
+
console.error("\n[DEBUG] Full error object:");
|
|
1319
|
+
console.error((0, node_util_1.inspect)(error, { depth: 10, colors: true }));
|
|
1320
|
+
const logs = error?.logs;
|
|
1321
|
+
if (logs?.length) {
|
|
1322
|
+
console.error("\n[DEBUG] Transaction logs:");
|
|
1323
|
+
console.error(logs.join("\n"));
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
(0, output_1.logError)("Claim fees failed", message);
|
|
1327
|
+
// Check if it's a "no fees to claim" scenario
|
|
1328
|
+
if (message.includes("insufficient") || message.includes("0x1") || message.includes("0x0")) {
|
|
1329
|
+
(0, output_1.logInfo)("Note: This error may mean there are no fees to claim yet.");
|
|
1330
|
+
}
|
|
1331
|
+
process.exitCode = 1;
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1335
|
+
(0, output_1.logJson)({ txId: result.txId });
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
(0, output_1.logSuccess)(`Fees claimed: ${result.txId}`);
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
// Check platform fee balance command
|
|
1342
|
+
launchpad
|
|
1343
|
+
.command("fee-balance")
|
|
1344
|
+
.description("Check platform fee balances available to claim")
|
|
1345
|
+
.requiredOption("--platform-id <address>", "Platform config address")
|
|
1346
|
+
.option("--mint-b <address>", "Quote token mint (default: checks SOL, USD1, USDC)")
|
|
1347
|
+
.action(async (options) => {
|
|
1348
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
1349
|
+
// Validate platform ID
|
|
1350
|
+
let platformId;
|
|
1351
|
+
try {
|
|
1352
|
+
platformId = new web3_js_1.PublicKey(options.platformId);
|
|
1353
|
+
}
|
|
1354
|
+
catch {
|
|
1355
|
+
(0, output_1.logError)("Invalid platform ID address");
|
|
1356
|
+
process.exitCode = 1;
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
// Known quote tokens to check
|
|
1360
|
+
const KNOWN_QUOTE_TOKENS = [
|
|
1361
|
+
{ symbol: "SOL", mint: "So11111111111111111111111111111111111111112", decimals: 9, isToken2022: false },
|
|
1362
|
+
{ symbol: "USD1", mint: "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB", decimals: 6, isToken2022: false },
|
|
1363
|
+
{ symbol: "USDC", mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decimals: 6, isToken2022: false },
|
|
1364
|
+
];
|
|
1365
|
+
// If specific mint provided, only check that one
|
|
1366
|
+
let tokensToCheck = KNOWN_QUOTE_TOKENS;
|
|
1367
|
+
if (options.mintB) {
|
|
1368
|
+
let mintB;
|
|
1369
|
+
try {
|
|
1370
|
+
mintB = new web3_js_1.PublicKey(options.mintB);
|
|
1371
|
+
}
|
|
1372
|
+
catch {
|
|
1373
|
+
(0, output_1.logError)("Invalid mint B address");
|
|
1374
|
+
process.exitCode = 1;
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
const known = KNOWN_QUOTE_TOKENS.find(t => t.mint === options.mintB);
|
|
1378
|
+
tokensToCheck = known
|
|
1379
|
+
? [known]
|
|
1380
|
+
: [{ symbol: options.mintB.slice(0, 8) + "...", mint: options.mintB, decimals: 9, isToken2022: TOKEN_2022_MINTS.has(options.mintB) }];
|
|
1381
|
+
}
|
|
1382
|
+
// Load Raydium (no owner needed, just connection)
|
|
1383
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ disableLoadToken: true }));
|
|
1384
|
+
(0, output_1.logInfo)("");
|
|
1385
|
+
(0, output_1.logInfo)(`Platform Fee Balances for ${platformId.toBase58()}`);
|
|
1386
|
+
(0, output_1.logInfo)("");
|
|
1387
|
+
const results = [];
|
|
1388
|
+
for (const token of tokensToCheck) {
|
|
1389
|
+
const mintB = new web3_js_1.PublicKey(token.mint);
|
|
1390
|
+
// Derive platform fee vault PDA using SDK's method
|
|
1391
|
+
// PDA: [platformId, mintB] -> LAUNCHPAD_PROGRAM
|
|
1392
|
+
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([platformId.toBuffer(), mintB.toBuffer()], raydium_sdk_v2_1.LAUNCHPAD_PROGRAM);
|
|
1393
|
+
try {
|
|
1394
|
+
// Fetch token account info
|
|
1395
|
+
const accountInfo = await raydium.connection.getAccountInfo(vaultPda);
|
|
1396
|
+
if (!accountInfo) {
|
|
1397
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1398
|
+
(0, output_1.logInfo)(` ${token.symbol}: No vault (0)`);
|
|
1399
|
+
}
|
|
1400
|
+
results.push({ symbol: token.symbol, mint: token.mint, balance: "0", vaultAddress: vaultPda.toBase58() });
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
// Parse token account data to get balance
|
|
1404
|
+
// Token account layout: first 64 bytes contain mint (32) + owner (32), then amount at offset 64 (8 bytes)
|
|
1405
|
+
const data = accountInfo.data;
|
|
1406
|
+
if (data.length < 72) {
|
|
1407
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1408
|
+
(0, output_1.logInfo)(` ${token.symbol}: Invalid account data`);
|
|
1409
|
+
}
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1412
|
+
// Read amount (u64 at offset 64)
|
|
1413
|
+
const amountBuffer = data.slice(64, 72);
|
|
1414
|
+
const amount = amountBuffer.readBigUInt64LE(0);
|
|
1415
|
+
const displayAmount = Number(amount) / Math.pow(10, token.decimals);
|
|
1416
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1417
|
+
(0, output_1.logInfo)(` ${token.symbol}: ${displayAmount.toLocaleString()} (${amount.toString()} raw)`);
|
|
1418
|
+
(0, output_1.logInfo)(` Vault: ${vaultPda.toBase58()}`);
|
|
1419
|
+
}
|
|
1420
|
+
results.push({ symbol: token.symbol, mint: token.mint, balance: displayAmount.toString(), vaultAddress: vaultPda.toBase58() });
|
|
1421
|
+
}
|
|
1422
|
+
catch (error) {
|
|
1423
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1424
|
+
(0, output_1.logInfo)(` ${token.symbol}: Error fetching (${error.message})`);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1429
|
+
(0, output_1.logJson)({ platformId: platformId.toBase58(), balances: results });
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
// Check creator fee balance command
|
|
1433
|
+
launchpad
|
|
1434
|
+
.command("creator-fee-balance")
|
|
1435
|
+
.description("Check your creator fee balances available to claim")
|
|
1436
|
+
.option("--mint-b <address>", "Quote token mint (default: checks SOL, USD1, USDC)")
|
|
1437
|
+
.action(async (options) => {
|
|
1438
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
1439
|
+
// Check wallet
|
|
1440
|
+
const walletName = config.activeWallet;
|
|
1441
|
+
if (!walletName) {
|
|
1442
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
1443
|
+
process.exitCode = 1;
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
// Prompt for password and decrypt wallet
|
|
1447
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
1448
|
+
let owner;
|
|
1449
|
+
try {
|
|
1450
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
1451
|
+
}
|
|
1452
|
+
catch (error) {
|
|
1453
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
1454
|
+
process.exitCode = 1;
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
// Known quote tokens to check
|
|
1458
|
+
const KNOWN_QUOTE_TOKENS = [
|
|
1459
|
+
{ symbol: "SOL", mint: "So11111111111111111111111111111111111111112", decimals: 9, isToken2022: false },
|
|
1460
|
+
{ symbol: "USD1", mint: "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB", decimals: 6, isToken2022: false },
|
|
1461
|
+
{ symbol: "USDC", mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decimals: 6, isToken2022: false },
|
|
1462
|
+
];
|
|
1463
|
+
// If specific mint provided, only check that one
|
|
1464
|
+
let tokensToCheck = KNOWN_QUOTE_TOKENS;
|
|
1465
|
+
if (options.mintB) {
|
|
1466
|
+
let mintB;
|
|
1467
|
+
try {
|
|
1468
|
+
mintB = new web3_js_1.PublicKey(options.mintB);
|
|
1469
|
+
}
|
|
1470
|
+
catch {
|
|
1471
|
+
(0, output_1.logError)("Invalid mint B address");
|
|
1472
|
+
process.exitCode = 1;
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const known = KNOWN_QUOTE_TOKENS.find(t => t.mint === options.mintB);
|
|
1476
|
+
tokensToCheck = known
|
|
1477
|
+
? [known]
|
|
1478
|
+
: [{ symbol: options.mintB.slice(0, 8) + "...", mint: options.mintB, decimals: 9, isToken2022: TOKEN_2022_MINTS.has(options.mintB) }];
|
|
1479
|
+
}
|
|
1480
|
+
// Load Raydium (no owner needed for balance check, just connection)
|
|
1481
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ disableLoadToken: true }));
|
|
1482
|
+
(0, output_1.logInfo)("");
|
|
1483
|
+
(0, output_1.logInfo)(`Creator Fee Balances for ${owner.publicKey.toBase58()}`);
|
|
1484
|
+
(0, output_1.logInfo)("");
|
|
1485
|
+
const results = [];
|
|
1486
|
+
for (const token of tokensToCheck) {
|
|
1487
|
+
const mintB = new web3_js_1.PublicKey(token.mint);
|
|
1488
|
+
// Derive creator fee vault PDA: [creator, mintB] -> LAUNCHPAD_PROGRAM
|
|
1489
|
+
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([owner.publicKey.toBuffer(), mintB.toBuffer()], raydium_sdk_v2_1.LAUNCHPAD_PROGRAM);
|
|
1490
|
+
try {
|
|
1491
|
+
// Fetch token account info
|
|
1492
|
+
const accountInfo = await raydium.connection.getAccountInfo(vaultPda);
|
|
1493
|
+
if (!accountInfo) {
|
|
1494
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1495
|
+
(0, output_1.logInfo)(` ${token.symbol}: No vault (0)`);
|
|
1496
|
+
}
|
|
1497
|
+
results.push({ symbol: token.symbol, mint: token.mint, balance: "0", vaultAddress: vaultPda.toBase58() });
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
// Parse token account data to get balance
|
|
1501
|
+
// Token account layout: first 64 bytes contain mint (32) + owner (32), then amount at offset 64 (8 bytes)
|
|
1502
|
+
const data = accountInfo.data;
|
|
1503
|
+
if (data.length < 72) {
|
|
1504
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1505
|
+
(0, output_1.logInfo)(` ${token.symbol}: Invalid account data`);
|
|
1506
|
+
}
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
// Read amount (u64 at offset 64)
|
|
1510
|
+
const amountBuffer = data.slice(64, 72);
|
|
1511
|
+
const amount = amountBuffer.readBigUInt64LE(0);
|
|
1512
|
+
const displayAmount = Number(amount) / Math.pow(10, token.decimals);
|
|
1513
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1514
|
+
(0, output_1.logInfo)(` ${token.symbol}: ${displayAmount.toLocaleString()} (${amount.toString()} raw)`);
|
|
1515
|
+
(0, output_1.logInfo)(` Vault: ${vaultPda.toBase58()}`);
|
|
1516
|
+
}
|
|
1517
|
+
results.push({ symbol: token.symbol, mint: token.mint, balance: displayAmount.toString(), vaultAddress: vaultPda.toBase58() });
|
|
1518
|
+
}
|
|
1519
|
+
catch (error) {
|
|
1520
|
+
if (!(0, output_1.isJsonOutput)()) {
|
|
1521
|
+
(0, output_1.logInfo)(` ${token.symbol}: Error fetching (${error.message})`);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1526
|
+
(0, output_1.logJson)({ creator: owner.publicKey.toBase58(), balances: results });
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
// Claim creator fees command
|
|
1530
|
+
// Note: Creator fees are accumulated per quote token (mintB), not per pool.
|
|
1531
|
+
// The SDK claims from a creator vault derived from (owner, mintB).
|
|
1532
|
+
launchpad
|
|
1533
|
+
.command("claim-creator-fees")
|
|
1534
|
+
.description("Claim creator fees accumulated from your launchpad tokens")
|
|
1535
|
+
.option("--mint-b <address>", "Quote token mint (default: SOL)")
|
|
1536
|
+
.option("--priority-fee <sol>", "Priority fee in SOL")
|
|
1537
|
+
.option("--debug", "Print full error on failure")
|
|
1538
|
+
.action(async (options) => {
|
|
1539
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
1540
|
+
// Validate mint B (default to SOL)
|
|
1541
|
+
let mintB = NATIVE_MINT;
|
|
1542
|
+
if (options.mintB) {
|
|
1543
|
+
try {
|
|
1544
|
+
mintB = new web3_js_1.PublicKey(options.mintB);
|
|
1545
|
+
}
|
|
1546
|
+
catch {
|
|
1547
|
+
(0, output_1.logError)("Invalid mint B address");
|
|
1548
|
+
process.exitCode = 1;
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
// Validate priority fee
|
|
1553
|
+
const priorityFeeSol = options.priorityFee ? Number(options.priorityFee) : config["priority-fee"];
|
|
1554
|
+
if (!Number.isFinite(priorityFeeSol) || priorityFeeSol < 0) {
|
|
1555
|
+
(0, output_1.logError)("Invalid priority fee");
|
|
1556
|
+
process.exitCode = 1;
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const priorityFeeLamports = priorityFeeSol * 1e9;
|
|
1560
|
+
const priorityFeeMicroLamports = Math.round((priorityFeeLamports * 1e6) / DEFAULT_COMPUTE_UNITS);
|
|
1561
|
+
// Check wallet
|
|
1562
|
+
const walletName = config.activeWallet;
|
|
1563
|
+
if (!walletName) {
|
|
1564
|
+
(0, output_1.logError)("No active wallet set. Use 'raydium wallet use <name>' to set one.");
|
|
1565
|
+
process.exitCode = 1;
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
// Prompt for password and decrypt wallet
|
|
1569
|
+
const password = await (0, prompt_1.promptPassword)("Enter wallet password");
|
|
1570
|
+
let owner;
|
|
1571
|
+
try {
|
|
1572
|
+
owner = await (0, wallet_manager_1.decryptWallet)(walletName, password);
|
|
1573
|
+
}
|
|
1574
|
+
catch (error) {
|
|
1575
|
+
(0, output_1.logError)("Failed to decrypt wallet", error.message);
|
|
1576
|
+
process.exitCode = 1;
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
// Load Raydium with owner
|
|
1580
|
+
const raydium = await (0, output_1.withSpinner)("Loading Raydium", () => (0, raydium_client_1.loadRaydium)({ owner, disableLoadToken: true }));
|
|
1581
|
+
const mintBStr = mintB.toBase58();
|
|
1582
|
+
// Identify quote token for display
|
|
1583
|
+
const KNOWN_QUOTES = {
|
|
1584
|
+
"So11111111111111111111111111111111111111112": "SOL",
|
|
1585
|
+
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB": "USD1",
|
|
1586
|
+
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC"
|
|
1587
|
+
};
|
|
1588
|
+
const quoteSymbol = KNOWN_QUOTES[mintBStr] ?? mintBStr.slice(0, 8) + "...";
|
|
1589
|
+
// Show preview
|
|
1590
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1591
|
+
(0, output_1.logJson)({
|
|
1592
|
+
action: "claim-creator-fees",
|
|
1593
|
+
quoteToken: quoteSymbol,
|
|
1594
|
+
mintB: mintBStr,
|
|
1595
|
+
creator: owner.publicKey.toBase58()
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
(0, output_1.logInfo)("");
|
|
1600
|
+
(0, output_1.logInfo)(`Claiming Creator Fees`);
|
|
1601
|
+
(0, output_1.logInfo)(` Quote Token: ${quoteSymbol} (${mintBStr})`);
|
|
1602
|
+
(0, output_1.logInfo)(` Creator: ${owner.publicKey.toBase58()}`);
|
|
1603
|
+
(0, output_1.logInfo)("");
|
|
1604
|
+
(0, output_1.logInfo)("Note: This claims all accumulated creator fees for this quote token.");
|
|
1605
|
+
}
|
|
1606
|
+
// Determine the token program for mintB
|
|
1607
|
+
const isToken2022 = TOKEN_2022_MINTS.has(mintBStr);
|
|
1608
|
+
const mintBProgram = isToken2022 ? spl_token_1.TOKEN_2022_PROGRAM_ID : spl_token_1.TOKEN_PROGRAM_ID;
|
|
1609
|
+
if (!(0, output_1.isJsonOutput)() && isToken2022) {
|
|
1610
|
+
(0, output_1.logInfo)(` Token Program: Token-2022`);
|
|
1611
|
+
}
|
|
1612
|
+
// Confirm
|
|
1613
|
+
const ok = await (0, prompt_1.promptConfirm)("Proceed with claiming creator fees?", false);
|
|
1614
|
+
if (!ok) {
|
|
1615
|
+
(0, output_1.logInfo)("Cancelled");
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
// Build transaction
|
|
1619
|
+
let txData;
|
|
1620
|
+
try {
|
|
1621
|
+
txData = await (0, output_1.withSpinner)("Building transaction", async () => {
|
|
1622
|
+
return raydium.launchpad.claimCreatorFee({
|
|
1623
|
+
mintB,
|
|
1624
|
+
mintBProgram,
|
|
1625
|
+
txVersion: raydium_sdk_v2_1.TxVersion.V0,
|
|
1626
|
+
computeBudgetConfig: priorityFeeMicroLamports > 0
|
|
1627
|
+
? { units: DEFAULT_COMPUTE_UNITS, microLamports: priorityFeeMicroLamports }
|
|
1628
|
+
: undefined
|
|
1629
|
+
});
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
catch (error) {
|
|
1633
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1634
|
+
if (options.debug) {
|
|
1635
|
+
const detail = error instanceof Error ? error.stack ?? message : (0, node_util_1.inspect)(error, { depth: 6 });
|
|
1636
|
+
(0, output_1.logError)("Failed to build transaction", detail);
|
|
1637
|
+
}
|
|
1638
|
+
else {
|
|
1639
|
+
(0, output_1.logError)("Failed to build transaction", message);
|
|
1640
|
+
}
|
|
1641
|
+
process.exitCode = 1;
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
// Execute transaction
|
|
1645
|
+
let result;
|
|
1646
|
+
try {
|
|
1647
|
+
result = await (0, output_1.withSpinner)("Sending transaction", () => txData.execute({ sendAndConfirm: true }));
|
|
1648
|
+
}
|
|
1649
|
+
catch (error) {
|
|
1650
|
+
if (options.debug) {
|
|
1651
|
+
console.error("\n[DEBUG] Full error object:");
|
|
1652
|
+
console.error((0, node_util_1.inspect)(error, { depth: 10, colors: true }));
|
|
1653
|
+
// Try to extract logs from various error formats
|
|
1654
|
+
const err = error;
|
|
1655
|
+
const logs = err?.logs ?? err?.simulationLogs ?? err?.cause?.logs;
|
|
1656
|
+
if (Array.isArray(logs) && logs.length) {
|
|
1657
|
+
console.error("\n[DEBUG] Transaction logs:");
|
|
1658
|
+
console.error(logs.join("\n"));
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
const message = error instanceof Error ? error.message : String(error ?? "Claim creator fees failed");
|
|
1662
|
+
(0, output_1.logError)("Claim creator fees failed", message);
|
|
1663
|
+
// Check if it's a "no fees to claim" scenario
|
|
1664
|
+
if (message.includes("insufficient") || message.includes("0x1")) {
|
|
1665
|
+
(0, output_1.logInfo)("Note: This error may mean there are no creator fees to claim yet.");
|
|
1666
|
+
}
|
|
1667
|
+
process.exitCode = 1;
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
if ((0, output_1.isJsonOutput)()) {
|
|
1671
|
+
(0, output_1.logJson)({ txId: result.txId });
|
|
1672
|
+
}
|
|
1673
|
+
else {
|
|
1674
|
+
(0, output_1.logSuccess)(`Creator fees claimed: ${result.txId}`);
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
}
|