sol-trade-sdk 0.1.0 → 0.1.2
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 +12 -5
- package/dist/cache/index.d.mts +70 -0
- package/dist/cache/index.d.ts +70 -0
- package/dist/cache/index.js +259 -0
- package/dist/cache/index.mjs +19 -0
- package/dist/chunk-64JY4YH6.mjs +227 -0
- package/dist/chunk-6BWS3CLP.mjs +16 -0
- package/dist/{chunk-NEZDFAYA.mjs → chunk-H4ZUMTTP.mjs} +864 -336
- package/dist/{chunk-MMQAMIKR.mjs → chunk-L6RZIXXN.mjs} +4 -11
- package/dist/{clients-VITWK7B6.mjs → chunk-V7QDWUXJ.mjs} +39 -39
- package/dist/clients-PSG35HN4.mjs +107 -0
- package/dist/index-hZh5hrGj.d.mts +3089 -0
- package/dist/index-yJ5AsNfm.d.ts +3089 -0
- package/dist/index.d.mts +2 -2658
- package/dist/index.d.ts +2 -2658
- package/dist/index.js +794 -278
- package/dist/index.mjs +21 -4
- package/dist/perf/index.mjs +2 -1
- package/dist/pool/index.d.mts +88 -0
- package/dist/pool/index.d.ts +88 -0
- package/dist/pool/index.js +262 -0
- package/dist/pool/index.mjs +233 -0
- package/dist/rpc/index.d.mts +77 -0
- package/dist/rpc/index.d.ts +77 -0
- package/dist/rpc/index.js +447 -0
- package/dist/rpc/index.mjs +223 -0
- package/dist/swqos/index.d.mts +416 -0
- package/dist/swqos/index.d.ts +416 -0
- package/dist/swqos/index.js +3450 -0
- package/dist/swqos/index.mjs +1375 -0
- package/dist/trading/index.d.mts +998 -0
- package/dist/trading/index.d.ts +998 -0
- package/dist/trading/index.js +4302 -0
- package/dist/trading/index.mjs +2097 -0
- package/package.json +5 -4
- package/src/__tests__/hotpath.test.ts +33 -0
- package/src/__tests__/parser-compat.test.ts +82 -0
- package/src/__tests__/protocol-parity.test.ts +242 -0
- package/src/__tests__/sdk.test.ts +16 -0
- package/src/cache/index.ts +1 -0
- package/src/hotpath/executor.ts +25 -18
- package/src/index.ts +330 -92
- package/src/instruction/meteora_damm_v2_builder.ts +88 -20
- package/src/instruction/pumpfun_builder.ts +133 -43
- package/src/instruction/raydium_amm_v4_builder.ts +244 -35
- package/src/instruction/raydium_cpmm_builder.ts +87 -69
- package/src/params/index.ts +38 -5
- package/src/pool/index.ts +1 -0
- package/src/rpc/index.ts +1 -0
- package/src/trading/factory.ts +10 -0
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
// Program IDs and Constants
|
|
26
26
|
// ============================================
|
|
27
27
|
|
|
28
|
+
const SOL_TOKEN_ACCOUNT = new PublicKey("So11111111111111111111111111111111111111111");
|
|
29
|
+
|
|
28
30
|
/** Meteora DAMM V2 program ID */
|
|
29
31
|
export const METEORA_DAMM_V2_PROGRAM_ID = new PublicKey(
|
|
30
32
|
"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"
|
|
@@ -43,6 +45,10 @@ export const METEORA_DAMM_V2_AUTHORITY = new PublicKey(
|
|
|
43
45
|
export const METEORA_DAMM_V2_SWAP_DISCRIMINATOR: Buffer = Buffer.from([
|
|
44
46
|
248, 198, 158, 145, 225, 117, 135, 200,
|
|
45
47
|
]);
|
|
48
|
+
export const METEORA_DAMM_V2_SWAP2_DISCRIMINATOR: Buffer = Buffer.from([
|
|
49
|
+
65, 75, 63, 76, 235, 91, 91, 136,
|
|
50
|
+
]);
|
|
51
|
+
export const METEORA_DAMM_V2_SWAP_MODE_PARTIAL_FILL = 1;
|
|
46
52
|
|
|
47
53
|
// ============================================
|
|
48
54
|
// Seeds
|
|
@@ -109,6 +115,25 @@ export interface BuildMeteoraDammV2SellInstructionsParams {
|
|
|
109
115
|
// Instruction Builders
|
|
110
116
|
// ============================================
|
|
111
117
|
|
|
118
|
+
function isDefaultPublicKey(pubkey: PublicKey): boolean {
|
|
119
|
+
return pubkey.equals(PublicKey.default);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isMintMatch(requested: PublicKey, expected: PublicKey): boolean {
|
|
123
|
+
return (
|
|
124
|
+
requested.equals(expected) ||
|
|
125
|
+
(expected.equals(NATIVE_MINT) && requested.equals(SOL_TOKEN_ACCOUNT))
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function ensureExpectedMint(label: string, requested: PublicKey, expected: PublicKey): void {
|
|
130
|
+
if (!isDefaultPublicKey(requested) && !isMintMatch(requested, expected)) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`${label} must match the Meteora DAMM v2 pool side (${expected.toBase58()}), got ${requested.toBase58()}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
112
137
|
/**
|
|
113
138
|
* Build buy instructions for Meteora DAMM V2 protocol
|
|
114
139
|
*/
|
|
@@ -117,8 +142,8 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
117
142
|
): TransactionInstruction[] {
|
|
118
143
|
const {
|
|
119
144
|
payer,
|
|
120
|
-
inputMint,
|
|
121
|
-
outputMint,
|
|
145
|
+
inputMint: requestedInputMint,
|
|
146
|
+
outputMint: requestedOutputMint,
|
|
122
147
|
inputAmount,
|
|
123
148
|
fixedOutputAmount,
|
|
124
149
|
createInputMintAta = true,
|
|
@@ -162,6 +187,11 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
162
187
|
// Determine swap direction
|
|
163
188
|
const isAIn = tokenAMint.equals(WSOL_TOKEN_ACCOUNT) || tokenAMint.equals(USDC_TOKEN_ACCOUNT);
|
|
164
189
|
|
|
190
|
+
const inputMint = isAIn ? tokenAMint : tokenBMint;
|
|
191
|
+
const outputMint = isAIn ? tokenBMint : tokenAMint;
|
|
192
|
+
ensureExpectedMint("inputMint", requestedInputMint, inputMint);
|
|
193
|
+
ensureExpectedMint("outputMint", requestedOutputMint, outputMint);
|
|
194
|
+
|
|
165
195
|
// Derive user token accounts
|
|
166
196
|
const inputTokenAccount = getAssociatedTokenAddressSync(
|
|
167
197
|
inputMint,
|
|
@@ -179,8 +209,11 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
179
209
|
// Derive event authority
|
|
180
210
|
const eventAuthority = getMeteoraDammV2EventAuthorityPda();
|
|
181
211
|
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
const inputTokenProgram = isAIn ? tokenAProgram : tokenBProgram;
|
|
213
|
+
const outputTokenProgram = isAIn ? tokenBProgram : tokenAProgram;
|
|
214
|
+
|
|
215
|
+
// Handle input account creation/wrapping
|
|
216
|
+
if (createInputMintAta && inputMint.equals(WSOL_TOKEN_ACCOUNT)) {
|
|
184
217
|
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
185
218
|
instructions.push(
|
|
186
219
|
createAssociatedTokenAccountInstruction(
|
|
@@ -191,7 +224,24 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
191
224
|
TOKEN_PROGRAM_ID
|
|
192
225
|
)
|
|
193
226
|
);
|
|
227
|
+
instructions.push(
|
|
228
|
+
SystemProgram.transfer({
|
|
229
|
+
fromPubkey: payerPubkey,
|
|
230
|
+
toPubkey: wsolAta,
|
|
231
|
+
lamports: Number(inputAmount),
|
|
232
|
+
})
|
|
233
|
+
);
|
|
194
234
|
instructions.push(createSyncNativeInstruction(wsolAta));
|
|
235
|
+
} else if (createInputMintAta) {
|
|
236
|
+
instructions.push(
|
|
237
|
+
createAssociatedTokenAccountInstruction(
|
|
238
|
+
payerPubkey,
|
|
239
|
+
inputTokenAccount,
|
|
240
|
+
payerPubkey,
|
|
241
|
+
inputMint,
|
|
242
|
+
inputTokenProgram
|
|
243
|
+
)
|
|
244
|
+
);
|
|
195
245
|
}
|
|
196
246
|
|
|
197
247
|
// Create output mint ATA if needed
|
|
@@ -202,18 +252,19 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
202
252
|
outputTokenAccount,
|
|
203
253
|
payerPubkey,
|
|
204
254
|
outputMint,
|
|
205
|
-
|
|
255
|
+
outputTokenProgram
|
|
206
256
|
)
|
|
207
257
|
);
|
|
208
258
|
}
|
|
209
259
|
|
|
210
|
-
// Build instruction data
|
|
211
|
-
const data = Buffer.alloc(
|
|
212
|
-
|
|
260
|
+
// Build swap2 instruction data
|
|
261
|
+
const data = Buffer.alloc(25);
|
|
262
|
+
METEORA_DAMM_V2_SWAP2_DISCRIMINATOR.copy(data, 0);
|
|
213
263
|
data.writeBigUInt64LE(inputAmount, 8);
|
|
214
264
|
data.writeBigUInt64LE(fixedOutputAmount, 16);
|
|
265
|
+
data.writeUInt8(METEORA_DAMM_V2_SWAP_MODE_PARTIAL_FILL, 24);
|
|
215
266
|
|
|
216
|
-
// Build accounts (
|
|
267
|
+
// Build accounts (13 accounts)
|
|
217
268
|
const accounts: AccountMeta[] = [
|
|
218
269
|
{ pubkey: METEORA_DAMM_V2_AUTHORITY, isSigner: false, isWritable: false },
|
|
219
270
|
{ pubkey: pool, isSigner: false, isWritable: true },
|
|
@@ -226,7 +277,6 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
226
277
|
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
227
278
|
{ pubkey: tokenAProgram, isSigner: false, isWritable: false },
|
|
228
279
|
{ pubkey: tokenBProgram, isSigner: false, isWritable: false },
|
|
229
|
-
{ pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false }, // Referral Token Account (placeholder)
|
|
230
280
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
231
281
|
{ pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
232
282
|
];
|
|
@@ -240,7 +290,7 @@ export function buildMeteoraDammV2BuyInstructions(
|
|
|
240
290
|
);
|
|
241
291
|
|
|
242
292
|
// Close WSOL ATA if requested
|
|
243
|
-
if (closeInputMintAta &&
|
|
293
|
+
if (closeInputMintAta && inputMint.equals(WSOL_TOKEN_ACCOUNT)) {
|
|
244
294
|
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
245
295
|
instructions.push(
|
|
246
296
|
createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID)
|
|
@@ -258,8 +308,8 @@ export function buildMeteoraDammV2SellInstructions(
|
|
|
258
308
|
): TransactionInstruction[] {
|
|
259
309
|
const {
|
|
260
310
|
payer,
|
|
261
|
-
inputMint,
|
|
262
|
-
outputMint,
|
|
311
|
+
inputMint: requestedInputMint,
|
|
312
|
+
outputMint: requestedOutputMint,
|
|
263
313
|
inputAmount,
|
|
264
314
|
fixedOutputAmount,
|
|
265
315
|
createOutputMintAta = true,
|
|
@@ -303,6 +353,11 @@ export function buildMeteoraDammV2SellInstructions(
|
|
|
303
353
|
// Determine swap direction (selling token for WSOL/USDC)
|
|
304
354
|
const isAIn = tokenBMint.equals(WSOL_TOKEN_ACCOUNT) || tokenBMint.equals(USDC_TOKEN_ACCOUNT);
|
|
305
355
|
|
|
356
|
+
const inputMint = isAIn ? tokenAMint : tokenBMint;
|
|
357
|
+
const outputMint = isAIn ? tokenBMint : tokenAMint;
|
|
358
|
+
ensureExpectedMint("inputMint", requestedInputMint, inputMint);
|
|
359
|
+
ensureExpectedMint("outputMint", requestedOutputMint, outputMint);
|
|
360
|
+
|
|
306
361
|
// Derive user token accounts
|
|
307
362
|
const inputTokenAccount = getAssociatedTokenAddressSync(
|
|
308
363
|
inputMint,
|
|
@@ -320,8 +375,11 @@ export function buildMeteoraDammV2SellInstructions(
|
|
|
320
375
|
// Derive event authority
|
|
321
376
|
const eventAuthority = getMeteoraDammV2EventAuthorityPda();
|
|
322
377
|
|
|
323
|
-
|
|
324
|
-
|
|
378
|
+
const inputTokenProgram = isAIn ? tokenAProgram : tokenBProgram;
|
|
379
|
+
const outputTokenProgram = isAIn ? tokenBProgram : tokenAProgram;
|
|
380
|
+
|
|
381
|
+
// Create output ATA for receiving if needed
|
|
382
|
+
if (createOutputMintAta && outputMint.equals(WSOL_TOKEN_ACCOUNT)) {
|
|
325
383
|
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
326
384
|
instructions.push(
|
|
327
385
|
createAssociatedTokenAccountInstruction(
|
|
@@ -332,13 +390,24 @@ export function buildMeteoraDammV2SellInstructions(
|
|
|
332
390
|
TOKEN_PROGRAM_ID
|
|
333
391
|
)
|
|
334
392
|
);
|
|
393
|
+
} else if (createOutputMintAta) {
|
|
394
|
+
instructions.push(
|
|
395
|
+
createAssociatedTokenAccountInstruction(
|
|
396
|
+
payerPubkey,
|
|
397
|
+
outputTokenAccount,
|
|
398
|
+
payerPubkey,
|
|
399
|
+
outputMint,
|
|
400
|
+
outputTokenProgram
|
|
401
|
+
)
|
|
402
|
+
);
|
|
335
403
|
}
|
|
336
404
|
|
|
337
|
-
// Build instruction data
|
|
338
|
-
const data = Buffer.alloc(
|
|
339
|
-
|
|
405
|
+
// Build swap2 instruction data
|
|
406
|
+
const data = Buffer.alloc(25);
|
|
407
|
+
METEORA_DAMM_V2_SWAP2_DISCRIMINATOR.copy(data, 0);
|
|
340
408
|
data.writeBigUInt64LE(inputAmount, 8);
|
|
341
409
|
data.writeBigUInt64LE(fixedOutputAmount, 16);
|
|
410
|
+
data.writeUInt8(METEORA_DAMM_V2_SWAP_MODE_PARTIAL_FILL, 24);
|
|
342
411
|
|
|
343
412
|
// Build accounts
|
|
344
413
|
const accounts: AccountMeta[] = [
|
|
@@ -353,7 +422,6 @@ export function buildMeteoraDammV2SellInstructions(
|
|
|
353
422
|
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
354
423
|
{ pubkey: tokenAProgram, isSigner: false, isWritable: false },
|
|
355
424
|
{ pubkey: tokenBProgram, isSigner: false, isWritable: false },
|
|
356
|
-
{ pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false }, // Referral Token Account
|
|
357
425
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
358
426
|
{ pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
359
427
|
];
|
|
@@ -367,7 +435,7 @@ export function buildMeteoraDammV2SellInstructions(
|
|
|
367
435
|
);
|
|
368
436
|
|
|
369
437
|
// Close WSOL ATA if requested
|
|
370
|
-
if (closeOutputMintAta &&
|
|
438
|
+
if (closeOutputMintAta && outputMint.equals(WSOL_TOKEN_ACCOUNT)) {
|
|
371
439
|
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
372
440
|
instructions.push(
|
|
373
441
|
createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID)
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
} from "@solana/web3.js";
|
|
16
16
|
import {
|
|
17
17
|
getAssociatedTokenAddressSync,
|
|
18
|
-
createAssociatedTokenAccountInstruction,
|
|
19
18
|
createAssociatedTokenAccountIdempotentInstruction,
|
|
20
19
|
TOKEN_PROGRAM_ID,
|
|
21
20
|
TOKEN_2022_PROGRAM_ID,
|
|
@@ -25,6 +24,8 @@ import {
|
|
|
25
24
|
NATIVE_MINT,
|
|
26
25
|
} from "@solana/spl-token";
|
|
27
26
|
|
|
27
|
+
const SOL_TOKEN_ACCOUNT = new PublicKey("So11111111111111111111111111111111111111111");
|
|
28
|
+
|
|
28
29
|
// ============================================
|
|
29
30
|
// Program IDs and Constants
|
|
30
31
|
// ============================================
|
|
@@ -281,35 +282,34 @@ export interface PumpFunParams {
|
|
|
281
282
|
closeTokenAccountWhenSell?: boolean;
|
|
282
283
|
/** From parser/gRPC (`tradeEvent.feeRecipient`); default pubkey → random pool */
|
|
283
284
|
feeRecipient?: PublicKey;
|
|
284
|
-
/**
|
|
285
|
+
/** Layout selector: default/Solscan SOL sentinel keeps legacy SOL; WSOL/USDC selects V2. */
|
|
285
286
|
quoteMint?: PublicKey;
|
|
286
|
-
/** Per-params V2 toggle; global `TradeConfig.usePumpfunV2` also maps here. */
|
|
287
|
-
useV2Ix?: boolean;
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
export interface PumpFunBuildBuyParams {
|
|
291
290
|
payer: Keypair | PublicKey;
|
|
291
|
+
inputMint?: PublicKey;
|
|
292
292
|
outputMint: PublicKey;
|
|
293
293
|
inputAmount: bigint;
|
|
294
294
|
slippageBasisPoints?: bigint;
|
|
295
295
|
fixedOutputAmount?: bigint;
|
|
296
296
|
createOutputMintAta?: boolean;
|
|
297
297
|
createInputMintAta?: boolean;
|
|
298
|
+
closeInputMintAta?: boolean;
|
|
298
299
|
protocolParams: PumpFunParams;
|
|
299
300
|
useExactSolAmount?: boolean;
|
|
300
|
-
usePumpFunV2?: boolean;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
export interface PumpFunBuildSellParams {
|
|
304
304
|
payer: Keypair | PublicKey;
|
|
305
305
|
inputMint: PublicKey;
|
|
306
|
+
outputMint?: PublicKey;
|
|
306
307
|
inputAmount: bigint;
|
|
307
308
|
slippageBasisPoints?: bigint;
|
|
308
309
|
fixedOutputAmount?: bigint;
|
|
309
310
|
createOutputMintAta?: boolean;
|
|
310
311
|
closeInputMintAta?: boolean;
|
|
311
312
|
protocolParams: PumpFunParams;
|
|
312
|
-
usePumpFunV2?: boolean;
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
// ============================================
|
|
@@ -391,7 +391,40 @@ function effectivePumpMintTokenProgram(mint: PublicKey, protocolParams: PumpFunP
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
function effectiveQuoteMint(protocolParams: PumpFunParams): PublicKey {
|
|
394
|
-
|
|
394
|
+
if (!isUsablePubkey(protocolParams.quoteMint) || protocolParams.quoteMint.equals(SOL_TOKEN_ACCOUNT)) {
|
|
395
|
+
return NATIVE_MINT;
|
|
396
|
+
}
|
|
397
|
+
return protocolParams.quoteMint;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function usesPumpFunV2Layout(protocolParams: PumpFunParams): boolean {
|
|
401
|
+
return isUsablePubkey(protocolParams.quoteMint) && !protocolParams.quoteMint.equals(SOL_TOKEN_ACCOUNT);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function isSolQuoteMint(mint: PublicKey): boolean {
|
|
405
|
+
return mint.equals(SOL_TOKEN_ACCOUNT) || mint.equals(NATIVE_MINT);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function validateV2BuyQuoteMint(inputMint: PublicKey, quoteMint: PublicKey): void {
|
|
409
|
+
if (isSolQuoteMint(quoteMint)) {
|
|
410
|
+
if (inputMint.equals(SOL_TOKEN_ACCOUNT) || inputMint.equals(NATIVE_MINT)) return;
|
|
411
|
+
} else if (inputMint.equals(quoteMint)) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
throw new Error(
|
|
415
|
+
`PumpFun V2 buy input_mint ${inputMint.toBase58()} does not match quote_mint ${quoteMint.toBase58()}; USDC quote pools must be bought with USDC, not SOL`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function validateV2SellQuoteMint(outputMint: PublicKey, quoteMint: PublicKey): void {
|
|
420
|
+
if (isSolQuoteMint(quoteMint)) {
|
|
421
|
+
if (outputMint.equals(SOL_TOKEN_ACCOUNT) || outputMint.equals(NATIVE_MINT)) return;
|
|
422
|
+
} else if (outputMint.equals(quoteMint)) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
throw new Error(
|
|
426
|
+
`PumpFun V2 sell output_mint ${outputMint.toBase58()} does not match quote_mint ${quoteMint.toBase58()}; USDC quote pools settle to USDC, not SOL`
|
|
427
|
+
);
|
|
395
428
|
}
|
|
396
429
|
|
|
397
430
|
function associatedTokenAddress(mint: PublicKey, owner: PublicKey, tokenProgram: PublicKey): PublicKey {
|
|
@@ -404,6 +437,36 @@ function associatedTokenAddress(mint: PublicKey, owner: PublicKey, tokenProgram:
|
|
|
404
437
|
);
|
|
405
438
|
}
|
|
406
439
|
|
|
440
|
+
function pushCreateOrWrapUserTokenAccount(
|
|
441
|
+
instructions: TransactionInstruction[],
|
|
442
|
+
payer: PublicKey,
|
|
443
|
+
ata: PublicKey,
|
|
444
|
+
mint: PublicKey,
|
|
445
|
+
tokenProgram: PublicKey,
|
|
446
|
+
amount: bigint
|
|
447
|
+
): void {
|
|
448
|
+
instructions.push(
|
|
449
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
450
|
+
payer,
|
|
451
|
+
ata,
|
|
452
|
+
payer,
|
|
453
|
+
mint,
|
|
454
|
+
tokenProgram,
|
|
455
|
+
SPL_ASSOCIATED_TOKEN_PROGRAM_ID
|
|
456
|
+
)
|
|
457
|
+
);
|
|
458
|
+
if (mint.equals(NATIVE_MINT)) {
|
|
459
|
+
instructions.push(
|
|
460
|
+
SystemProgram.transfer({
|
|
461
|
+
fromPubkey: payer,
|
|
462
|
+
toPubkey: ata,
|
|
463
|
+
lamports: amount,
|
|
464
|
+
})
|
|
465
|
+
);
|
|
466
|
+
instructions.push(createSyncNativeInstruction(ata));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
407
470
|
function getBuyTokenAmountFromSolAmount(
|
|
408
471
|
amount: bigint,
|
|
409
472
|
bondingCurve: PumpFunBondingCurve,
|
|
@@ -457,22 +520,24 @@ export function buildPumpFunBuyInstructions(
|
|
|
457
520
|
): TransactionInstruction[] {
|
|
458
521
|
const {
|
|
459
522
|
payer,
|
|
523
|
+
inputMint = SOL_TOKEN_ACCOUNT,
|
|
460
524
|
outputMint,
|
|
461
525
|
inputAmount,
|
|
462
526
|
slippageBasisPoints = BigInt(1000),
|
|
463
527
|
fixedOutputAmount,
|
|
464
528
|
createOutputMintAta = true,
|
|
465
529
|
createInputMintAta = false,
|
|
530
|
+
closeInputMintAta = false,
|
|
466
531
|
protocolParams,
|
|
467
532
|
useExactSolAmount = true,
|
|
468
|
-
usePumpFunV2 = false,
|
|
469
533
|
} = params;
|
|
470
534
|
|
|
471
|
-
if (
|
|
535
|
+
if (usesPumpFunV2Layout(protocolParams)) {
|
|
472
536
|
return buildPumpFunBuyV2Instructions({
|
|
473
537
|
...params,
|
|
538
|
+
inputMint,
|
|
474
539
|
createInputMintAta,
|
|
475
|
-
|
|
540
|
+
closeInputMintAta,
|
|
476
541
|
});
|
|
477
542
|
}
|
|
478
543
|
|
|
@@ -518,12 +583,13 @@ export function buildPumpFunBuyInstructions(
|
|
|
518
583
|
// Create ATA if needed
|
|
519
584
|
if (createOutputMintAta) {
|
|
520
585
|
instructions.push(
|
|
521
|
-
|
|
586
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
522
587
|
payerPubkey,
|
|
523
588
|
userTokenAccount,
|
|
524
589
|
payerPubkey,
|
|
525
590
|
outputMint,
|
|
526
|
-
tokenProgramId
|
|
591
|
+
tokenProgramId,
|
|
592
|
+
SPL_ASSOCIATED_TOKEN_PROGRAM_ID
|
|
527
593
|
)
|
|
528
594
|
);
|
|
529
595
|
}
|
|
@@ -534,9 +600,7 @@ export function buildPumpFunBuyInstructions(
|
|
|
534
600
|
const bondingCurveV2 = getBondingCurveV2Pda(outputMint);
|
|
535
601
|
|
|
536
602
|
// Track volume for cashback coins
|
|
537
|
-
const trackVolume = bondingCurve.isCashbackCoin
|
|
538
|
-
? Buffer.from([1, 1])
|
|
539
|
-
: Buffer.from([1, 0]);
|
|
603
|
+
const trackVolume = bondingCurve.isCashbackCoin ? 1 : 0;
|
|
540
604
|
|
|
541
605
|
const buyTokenAmount = fixedOutputAmount
|
|
542
606
|
? fixedOutputAmount
|
|
@@ -545,23 +609,27 @@ export function buildPumpFunBuyInstructions(
|
|
|
545
609
|
|
|
546
610
|
// Build instruction data
|
|
547
611
|
let data: Buffer;
|
|
548
|
-
if (
|
|
612
|
+
if (fixedOutputAmount !== undefined) {
|
|
613
|
+
data = Buffer.alloc(25);
|
|
614
|
+
PUMPFUN_BUY_DISCRIMINATOR.copy(data, 0);
|
|
615
|
+
data.writeBigUInt64LE(fixedOutputAmount, 8);
|
|
616
|
+
data.writeBigUInt64LE(inputAmount, 16);
|
|
617
|
+
data[24] = trackVolume;
|
|
618
|
+
} else if (useExactSolAmount) {
|
|
549
619
|
// buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64, track_volume)
|
|
550
|
-
const minTokensOut =
|
|
551
|
-
|
|
552
|
-
: calculateWithSlippageSell(buyTokenAmount, slippageBasisPoints);
|
|
553
|
-
data = Buffer.alloc(26);
|
|
620
|
+
const minTokensOut = calculateWithSlippageSell(buyTokenAmount, slippageBasisPoints);
|
|
621
|
+
data = Buffer.alloc(25);
|
|
554
622
|
PUMPFUN_BUY_EXACT_SOL_IN_DISCRIMINATOR.copy(data, 0);
|
|
555
623
|
data.writeBigUInt64LE(inputAmount, 8);
|
|
556
624
|
data.writeBigUInt64LE(minTokensOut, 16);
|
|
557
|
-
|
|
625
|
+
data[24] = trackVolume;
|
|
558
626
|
} else {
|
|
559
627
|
// buy(token_amount: u64, max_sol_cost: u64, track_volume)
|
|
560
|
-
data = Buffer.alloc(
|
|
628
|
+
data = Buffer.alloc(25);
|
|
561
629
|
PUMPFUN_BUY_DISCRIMINATOR.copy(data, 0);
|
|
562
630
|
data.writeBigUInt64LE(buyTokenAmount, 8);
|
|
563
631
|
data.writeBigUInt64LE(maxSolCost, 16);
|
|
564
|
-
|
|
632
|
+
data[24] = trackVolume;
|
|
565
633
|
}
|
|
566
634
|
|
|
567
635
|
// Build accounts
|
|
@@ -607,20 +675,20 @@ export function buildPumpFunSellInstructions(
|
|
|
607
675
|
const {
|
|
608
676
|
payer,
|
|
609
677
|
inputMint,
|
|
678
|
+
outputMint = SOL_TOKEN_ACCOUNT,
|
|
610
679
|
inputAmount,
|
|
611
680
|
slippageBasisPoints = BigInt(1000),
|
|
612
681
|
fixedOutputAmount,
|
|
613
682
|
createOutputMintAta = false,
|
|
614
683
|
closeInputMintAta = false,
|
|
615
684
|
protocolParams,
|
|
616
|
-
usePumpFunV2 = false,
|
|
617
685
|
} = params;
|
|
618
686
|
|
|
619
|
-
if (
|
|
687
|
+
if (usesPumpFunV2Layout(protocolParams)) {
|
|
620
688
|
return buildPumpFunSellV2Instructions({
|
|
621
689
|
...params,
|
|
690
|
+
outputMint,
|
|
622
691
|
createOutputMintAta,
|
|
623
|
-
usePumpFunV2: true,
|
|
624
692
|
});
|
|
625
693
|
}
|
|
626
694
|
|
|
@@ -740,12 +808,14 @@ export function buildPumpFunBuyV2Instructions(
|
|
|
740
808
|
): TransactionInstruction[] {
|
|
741
809
|
const {
|
|
742
810
|
payer,
|
|
811
|
+
inputMint = SOL_TOKEN_ACCOUNT,
|
|
743
812
|
outputMint,
|
|
744
813
|
inputAmount,
|
|
745
814
|
slippageBasisPoints = BigInt(1000),
|
|
746
815
|
fixedOutputAmount,
|
|
747
816
|
createOutputMintAta = true,
|
|
748
817
|
createInputMintAta = false,
|
|
818
|
+
closeInputMintAta = false,
|
|
749
819
|
protocolParams,
|
|
750
820
|
useExactSolAmount = true,
|
|
751
821
|
} = params;
|
|
@@ -767,6 +837,7 @@ export function buildPumpFunBuyV2Instructions(
|
|
|
767
837
|
|
|
768
838
|
const baseTokenProgram = effectivePumpMintTokenProgram(outputMint, protocolParams);
|
|
769
839
|
const quoteMint = effectiveQuoteMint(protocolParams);
|
|
840
|
+
validateV2BuyQuoteMint(inputMint, quoteMint);
|
|
770
841
|
const quoteTokenProgram = TOKEN_PROGRAM_ID;
|
|
771
842
|
|
|
772
843
|
const associatedBaseBondingCurve = associatedTokenAddress(
|
|
@@ -824,37 +895,42 @@ export function buildPumpFunBuyV2Instructions(
|
|
|
824
895
|
);
|
|
825
896
|
}
|
|
826
897
|
|
|
827
|
-
if (createInputMintAta) {
|
|
828
|
-
instructions.push(
|
|
829
|
-
createAssociatedTokenAccountIdempotentInstruction(
|
|
830
|
-
payerPubkey,
|
|
831
|
-
associatedQuoteUser,
|
|
832
|
-
payerPubkey,
|
|
833
|
-
quoteMint,
|
|
834
|
-
quoteTokenProgram,
|
|
835
|
-
SPL_ASSOCIATED_TOKEN_PROGRAM_ID
|
|
836
|
-
)
|
|
837
|
-
);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
898
|
const buyTokenAmount = fixedOutputAmount
|
|
841
899
|
? fixedOutputAmount
|
|
842
900
|
: getBuyTokenAmountFromSolAmount(inputAmount, bondingCurve, creator);
|
|
843
901
|
const maxSolCost = calculateWithSlippageBuy(inputAmount, slippageBasisPoints);
|
|
844
902
|
let data: Buffer;
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
903
|
+
let quoteAmountToFund: bigint;
|
|
904
|
+
if (fixedOutputAmount !== undefined) {
|
|
905
|
+
data = Buffer.alloc(24);
|
|
906
|
+
PUMPFUN_BUY_V2_DISCRIMINATOR.copy(data, 0);
|
|
907
|
+
data.writeBigUInt64LE(fixedOutputAmount, 8);
|
|
908
|
+
data.writeBigUInt64LE(inputAmount, 16);
|
|
909
|
+
quoteAmountToFund = inputAmount;
|
|
910
|
+
} else if (useExactSolAmount) {
|
|
911
|
+
const minTokensOut = calculateWithSlippageSell(buyTokenAmount, slippageBasisPoints);
|
|
849
912
|
data = Buffer.alloc(24);
|
|
850
913
|
PUMPFUN_BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR.copy(data, 0);
|
|
851
914
|
data.writeBigUInt64LE(inputAmount, 8);
|
|
852
915
|
data.writeBigUInt64LE(minTokensOut, 16);
|
|
916
|
+
quoteAmountToFund = inputAmount;
|
|
853
917
|
} else {
|
|
854
918
|
data = Buffer.alloc(24);
|
|
855
919
|
PUMPFUN_BUY_V2_DISCRIMINATOR.copy(data, 0);
|
|
856
920
|
data.writeBigUInt64LE(buyTokenAmount, 8);
|
|
857
921
|
data.writeBigUInt64LE(maxSolCost, 16);
|
|
922
|
+
quoteAmountToFund = maxSolCost;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (createInputMintAta) {
|
|
926
|
+
pushCreateOrWrapUserTokenAccount(
|
|
927
|
+
instructions,
|
|
928
|
+
payerPubkey,
|
|
929
|
+
associatedQuoteUser,
|
|
930
|
+
quoteMint,
|
|
931
|
+
quoteTokenProgram,
|
|
932
|
+
quoteAmountToFund
|
|
933
|
+
);
|
|
858
934
|
}
|
|
859
935
|
|
|
860
936
|
const keys: AccountMeta[] = [
|
|
@@ -895,6 +971,18 @@ export function buildPumpFunBuyV2Instructions(
|
|
|
895
971
|
})
|
|
896
972
|
);
|
|
897
973
|
|
|
974
|
+
if (closeInputMintAta && quoteMint.equals(NATIVE_MINT)) {
|
|
975
|
+
instructions.push(
|
|
976
|
+
createCloseAccountInstruction(
|
|
977
|
+
associatedQuoteUser,
|
|
978
|
+
payerPubkey,
|
|
979
|
+
payerPubkey,
|
|
980
|
+
[],
|
|
981
|
+
quoteTokenProgram
|
|
982
|
+
)
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
|
|
898
986
|
return instructions;
|
|
899
987
|
}
|
|
900
988
|
|
|
@@ -907,6 +995,7 @@ export function buildPumpFunSellV2Instructions(
|
|
|
907
995
|
const {
|
|
908
996
|
payer,
|
|
909
997
|
inputMint,
|
|
998
|
+
outputMint = SOL_TOKEN_ACCOUNT,
|
|
910
999
|
inputAmount,
|
|
911
1000
|
slippageBasisPoints = BigInt(1000),
|
|
912
1001
|
fixedOutputAmount,
|
|
@@ -932,6 +1021,7 @@ export function buildPumpFunSellV2Instructions(
|
|
|
932
1021
|
|
|
933
1022
|
const baseTokenProgram = effectivePumpMintTokenProgram(inputMint, protocolParams);
|
|
934
1023
|
const quoteMint = effectiveQuoteMint(protocolParams);
|
|
1024
|
+
validateV2SellQuoteMint(outputMint, quoteMint);
|
|
935
1025
|
const quoteTokenProgram = TOKEN_PROGRAM_ID;
|
|
936
1026
|
|
|
937
1027
|
const associatedBaseBondingCurve = associatedTokenAddress(
|