sol-trade-sdk 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/README.md +390 -0
- package/dist/chunk-MMQAMIKR.mjs +3735 -0
- package/dist/chunk-NEZDFAYA.mjs +7744 -0
- package/dist/clients-VITWK7B6.mjs +1370 -0
- package/dist/index-1BK_FXsW.d.mts +2327 -0
- package/dist/index-1BK_FXsW.d.ts +2327 -0
- package/dist/index.d.mts +2659 -0
- package/dist/index.d.ts +2659 -0
- package/dist/index.js +13265 -0
- package/dist/index.mjs +562 -0
- package/dist/perf/index.d.mts +2 -0
- package/dist/perf/index.d.ts +2 -0
- package/dist/perf/index.js +3742 -0
- package/dist/perf/index.mjs +214 -0
- package/package.json +101 -0
- package/src/__tests__/complete_sdk.test.ts +354 -0
- package/src/__tests__/hotpath.test.ts +486 -0
- package/src/__tests__/nonce.test.ts +45 -0
- package/src/__tests__/sdk.test.ts +425 -0
- package/src/address-lookup/index.ts +197 -0
- package/src/cache/cache.ts +308 -0
- package/src/calc/index.ts +1058 -0
- package/src/calc/pumpfun.ts +124 -0
- package/src/common/bonding_curve.ts +272 -0
- package/src/common/compute-budget.ts +148 -0
- package/src/common/confirm-any-signature.ts +184 -0
- package/src/common/fast-timing.ts +481 -0
- package/src/common/fast_fn.ts +150 -0
- package/src/common/gas-fee-strategy.ts +253 -0
- package/src/common/map-pool.ts +23 -0
- package/src/common/nonce.ts +40 -0
- package/src/common/sdk-log.ts +460 -0
- package/src/common/seed.ts +381 -0
- package/src/common/spl-token.ts +578 -0
- package/src/common/subscription-handle.ts +644 -0
- package/src/common/trading-utils.ts +239 -0
- package/src/common/wsol-manager.ts +325 -0
- package/src/compute/compute_budget_manager.ts +187 -0
- package/src/compute/index.ts +21 -0
- package/src/constants/index.ts +96 -0
- package/src/execution/execution.ts +532 -0
- package/src/execution/index.ts +42 -0
- package/src/hotpath/executor.ts +464 -0
- package/src/hotpath/index.ts +64 -0
- package/src/hotpath/state.ts +435 -0
- package/src/index.ts +2117 -0
- package/src/instruction/bonk_builder.ts +730 -0
- package/src/instruction/index.ts +24 -0
- package/src/instruction/meteora_damm_v2_builder.ts +509 -0
- package/src/instruction/pumpfun_builder.ts +1183 -0
- package/src/instruction/pumpswap.ts +1123 -0
- package/src/instruction/raydium_amm_v4_builder.ts +692 -0
- package/src/instruction/raydium_cpmm_builder.ts +795 -0
- package/src/middleware/traits.ts +407 -0
- package/src/params/index.ts +483 -0
- package/src/perf/compiler-optimization.ts +529 -0
- package/src/perf/hardware.ts +631 -0
- package/src/perf/index.ts +9 -0
- package/src/perf/kernel-bypass.ts +656 -0
- package/src/perf/protocol.ts +682 -0
- package/src/perf/realtime.ts +592 -0
- package/src/perf/simd.ts +668 -0
- package/src/perf/syscall-bypass.ts +331 -0
- package/src/perf/ultra-low-latency.ts +505 -0
- package/src/perf/zero-copy.ts +589 -0
- package/src/pool/pool.ts +294 -0
- package/src/rpc/client.ts +345 -0
- package/src/sdk-errors.ts +13 -0
- package/src/security/index.ts +26 -0
- package/src/security/secure-key.ts +303 -0
- package/src/security/validators.ts +281 -0
- package/src/seed/pda.ts +262 -0
- package/src/serialization/index.ts +28 -0
- package/src/serialization/serialization.ts +288 -0
- package/src/swqos/clients.ts +1754 -0
- package/src/swqos/index.ts +50 -0
- package/src/swqos/providers.ts +1707 -0
- package/src/trading/core/async-executor.ts +702 -0
- package/src/trading/core/confirmation-monitor.ts +711 -0
- package/src/trading/core/index.ts +82 -0
- package/src/trading/core/retry-handler.ts +683 -0
- package/src/trading/core/transaction-pool.ts +780 -0
- package/src/trading/executor.ts +385 -0
- package/src/trading/factory.ts +282 -0
- package/src/trading/index.ts +30 -0
- package/src/types.ts +8 -0
- package/src/utils/index.ts +155 -0
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PumpFun Protocol Instruction Builder
|
|
3
|
+
*
|
|
4
|
+
* Production-grade instruction builder for PumpFun bonding curve protocol.
|
|
5
|
+
* Supports buy, sell, and cashback claim operations.
|
|
6
|
+
* 100% port from Rust: src/instruction/pumpfun.rs
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
PublicKey,
|
|
11
|
+
Keypair,
|
|
12
|
+
AccountMeta,
|
|
13
|
+
TransactionInstruction,
|
|
14
|
+
SystemProgram,
|
|
15
|
+
} from "@solana/web3.js";
|
|
16
|
+
import {
|
|
17
|
+
getAssociatedTokenAddressSync,
|
|
18
|
+
createAssociatedTokenAccountInstruction,
|
|
19
|
+
createAssociatedTokenAccountIdempotentInstruction,
|
|
20
|
+
TOKEN_PROGRAM_ID,
|
|
21
|
+
TOKEN_2022_PROGRAM_ID,
|
|
22
|
+
ASSOCIATED_TOKEN_PROGRAM_ID as SPL_ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
23
|
+
createCloseAccountInstruction,
|
|
24
|
+
createSyncNativeInstruction,
|
|
25
|
+
NATIVE_MINT,
|
|
26
|
+
} from "@solana/spl-token";
|
|
27
|
+
|
|
28
|
+
// ============================================
|
|
29
|
+
// Program IDs and Constants
|
|
30
|
+
// ============================================
|
|
31
|
+
|
|
32
|
+
/** PumpFun program ID */
|
|
33
|
+
export const PUMPFUN_PROGRAM_ID = new PublicKey(
|
|
34
|
+
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/** Event Authority for PumpFun */
|
|
38
|
+
export const PUMPFUN_EVENT_AUTHORITY = new PublicKey(
|
|
39
|
+
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/** Fee Program */
|
|
43
|
+
export const PUMPFUN_FEE_PROGRAM = new PublicKey(
|
|
44
|
+
"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
/** Global Volume Accumulator */
|
|
48
|
+
export const PUMPFUN_GLOBAL_VOLUME_ACCUMULATOR = new PublicKey(
|
|
49
|
+
"Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y"
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
/** Fee Config */
|
|
53
|
+
export const PUMPFUN_FEE_CONFIG = new PublicKey(
|
|
54
|
+
"8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt"
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/** Global Account */
|
|
58
|
+
export const PUMPFUN_GLOBAL_ACCOUNT = new PublicKey(
|
|
59
|
+
"4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
/** Fee Recipient */
|
|
63
|
+
export const PUMPFUN_FEE_RECIPIENT = new PublicKey(
|
|
64
|
+
"62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
/** Non-mayhem: random among primary + Pump.fun AMM protocol fee recipients (Rust `get_standard_fee_recipient_meta_random`). */
|
|
68
|
+
export const PUMPFUN_STANDARD_FEE_RECIPIENTS: PublicKey[] = [
|
|
69
|
+
PUMPFUN_FEE_RECIPIENT,
|
|
70
|
+
new PublicKey("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"),
|
|
71
|
+
new PublicKey("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"),
|
|
72
|
+
new PublicKey("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"),
|
|
73
|
+
new PublicKey("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"),
|
|
74
|
+
new PublicKey("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"),
|
|
75
|
+
new PublicKey("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"),
|
|
76
|
+
new PublicKey("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"),
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Protocol extra fee recipients (Apr 2026 breaking upgrade).
|
|
81
|
+
* One pubkey is appended after bonding-curve-v2 on buy/sell; account must be writable.
|
|
82
|
+
* @see https://github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
|
|
83
|
+
*/
|
|
84
|
+
export const PUMPFUN_PROTOCOL_EXTRA_FEE_RECIPIENTS: PublicKey[] = [
|
|
85
|
+
new PublicKey("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
|
|
86
|
+
new PublicKey("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
|
|
87
|
+
new PublicKey("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"),
|
|
88
|
+
new PublicKey("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"),
|
|
89
|
+
new PublicKey("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"),
|
|
90
|
+
new PublicKey("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"),
|
|
91
|
+
new PublicKey("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"),
|
|
92
|
+
new PublicKey("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
/** V2 buyback fee recipients (same static pool as Rust `get_buyback_fee_recipient_random`). */
|
|
96
|
+
export const PUMPFUN_BUYBACK_FEE_RECIPIENTS: PublicKey[] = PUMPFUN_PROTOCOL_EXTRA_FEE_RECIPIENTS;
|
|
97
|
+
|
|
98
|
+
/** Mayhem Fee Recipients */
|
|
99
|
+
export const PUMPFUN_MAYHEM_FEE_RECIPIENTS: PublicKey[] = [
|
|
100
|
+
new PublicKey("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"),
|
|
101
|
+
new PublicKey("4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6"),
|
|
102
|
+
new PublicKey("8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR"),
|
|
103
|
+
new PublicKey("4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH"),
|
|
104
|
+
new PublicKey("8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6"),
|
|
105
|
+
new PublicKey("Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk"),
|
|
106
|
+
new PublicKey("463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq"),
|
|
107
|
+
new PublicKey("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"),
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// ============================================
|
|
111
|
+
// Discriminators - from Rust src/instruction/utils/pumpfun.rs
|
|
112
|
+
// ============================================
|
|
113
|
+
|
|
114
|
+
/** Buy instruction discriminator */
|
|
115
|
+
export const PUMPFUN_BUY_DISCRIMINATOR: Buffer = Buffer.from([
|
|
116
|
+
102, 6, 61, 18, 1, 218, 235, 234,
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
/** Buy exact SOL in discriminator */
|
|
120
|
+
export const PUMPFUN_BUY_EXACT_SOL_IN_DISCRIMINATOR: Buffer = Buffer.from([
|
|
121
|
+
56, 252, 116, 8, 158, 223, 205, 95,
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
/** Sell instruction discriminator */
|
|
125
|
+
export const PUMPFUN_SELL_DISCRIMINATOR: Buffer = Buffer.from([
|
|
126
|
+
51, 230, 133, 164, 1, 127, 131, 173,
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
/** PumpFun V2 buy instruction discriminator */
|
|
130
|
+
export const PUMPFUN_BUY_V2_DISCRIMINATOR: Buffer = Buffer.from([
|
|
131
|
+
184, 23, 238, 97, 103, 197, 211, 61,
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
/** PumpFun V2 sell instruction discriminator */
|
|
135
|
+
export const PUMPFUN_SELL_V2_DISCRIMINATOR: Buffer = Buffer.from([
|
|
136
|
+
93, 246, 130, 60, 231, 233, 64, 178,
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
/** PumpFun V2 exact quote-in buy discriminator */
|
|
140
|
+
export const PUMPFUN_BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR: Buffer = Buffer.from([
|
|
141
|
+
194, 171, 28, 70, 104, 77, 91, 47,
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
/** Claim cashback discriminator */
|
|
145
|
+
export const PUMPFUN_CLAIM_CASHBACK_DISCRIMINATOR: Buffer = Buffer.from([
|
|
146
|
+
37, 58, 35, 126, 190, 53, 228, 197,
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
// ============================================
|
|
150
|
+
// Seeds
|
|
151
|
+
// ============================================
|
|
152
|
+
|
|
153
|
+
export const PUMPFUN_BONDING_CURVE_SEED = Buffer.from("bonding-curve");
|
|
154
|
+
export const PUMPFUN_BONDING_CURVE_V2_SEED = Buffer.from("bonding-curve-v2");
|
|
155
|
+
export const PUMPFUN_CREATOR_VAULT_SEED = Buffer.from("creator-vault");
|
|
156
|
+
export const PUMPFUN_USER_VOLUME_ACCUMULATOR_SEED = Buffer.from("user_volume_accumulator");
|
|
157
|
+
export const PUMPFUN_SHARING_CONFIG_SEED = Buffer.from("sharing-config");
|
|
158
|
+
|
|
159
|
+
// ============================================
|
|
160
|
+
// PDA Derivation Functions
|
|
161
|
+
// ============================================
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Derive the bonding curve PDA for a given mint
|
|
165
|
+
*/
|
|
166
|
+
export function getBondingCurvePda(mint: PublicKey): PublicKey {
|
|
167
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
168
|
+
[PUMPFUN_BONDING_CURVE_SEED, mint.toBuffer()],
|
|
169
|
+
PUMPFUN_PROGRAM_ID
|
|
170
|
+
);
|
|
171
|
+
return pda;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Derive the bonding curve v2 PDA for a given mint
|
|
176
|
+
*/
|
|
177
|
+
export function getBondingCurveV2Pda(mint: PublicKey): PublicKey {
|
|
178
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
179
|
+
[PUMPFUN_BONDING_CURVE_V2_SEED, mint.toBuffer()],
|
|
180
|
+
PUMPFUN_PROGRAM_ID
|
|
181
|
+
);
|
|
182
|
+
return pda;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Derive the creator vault PDA for a given creator
|
|
187
|
+
*/
|
|
188
|
+
export function getCreatorVaultPda(creator: PublicKey): PublicKey {
|
|
189
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
190
|
+
[PUMPFUN_CREATOR_VAULT_SEED, creator.toBuffer()],
|
|
191
|
+
PUMPFUN_PROGRAM_ID
|
|
192
|
+
);
|
|
193
|
+
return pda;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Derive the user volume accumulator PDA for a given user
|
|
198
|
+
*/
|
|
199
|
+
export function getPumpFunUserVolumeAccumulatorPda(user: PublicKey): PublicKey {
|
|
200
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
201
|
+
[PUMPFUN_USER_VOLUME_ACCUMULATOR_SEED, user.toBuffer()],
|
|
202
|
+
PUMPFUN_PROGRAM_ID
|
|
203
|
+
);
|
|
204
|
+
return pda;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Derive the fee sharing config PDA for a PumpFun mint.
|
|
209
|
+
*/
|
|
210
|
+
export function getPumpFunFeeSharingConfigPda(mint: PublicKey): PublicKey {
|
|
211
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
212
|
+
[PUMPFUN_SHARING_CONFIG_SEED, mint.toBuffer()],
|
|
213
|
+
PUMPFUN_FEE_PROGRAM
|
|
214
|
+
);
|
|
215
|
+
return pda;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get a random Mayhem fee recipient
|
|
220
|
+
*/
|
|
221
|
+
export function getRandomMayhemFeeRecipient(): PublicKey {
|
|
222
|
+
const index = Math.floor(Math.random() * PUMPFUN_MAYHEM_FEE_RECIPIENTS.length);
|
|
223
|
+
const recipient = PUMPFUN_MAYHEM_FEE_RECIPIENTS[index];
|
|
224
|
+
if (!recipient) {
|
|
225
|
+
return PUMPFUN_MAYHEM_FEE_RECIPIENTS[0]!;
|
|
226
|
+
}
|
|
227
|
+
return recipient;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function getStandardFeeRecipientRandom(): PublicKey {
|
|
231
|
+
const index = Math.floor(Math.random() * PUMPFUN_STANDARD_FEE_RECIPIENTS.length);
|
|
232
|
+
return PUMPFUN_STANDARD_FEE_RECIPIENTS[index] ?? PUMPFUN_FEE_RECIPIENT;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Random protocol extra fee recipient (after bonding-curve-v2, mutable). */
|
|
236
|
+
export function getPumpFunProtocolExtraFeeRecipientRandom(): PublicKey {
|
|
237
|
+
const index = Math.floor(Math.random() * PUMPFUN_PROTOCOL_EXTRA_FEE_RECIPIENTS.length);
|
|
238
|
+
return PUMPFUN_PROTOCOL_EXTRA_FEE_RECIPIENTS[index] ?? PUMPFUN_PROTOCOL_EXTRA_FEE_RECIPIENTS[0]!;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Random PumpFun V2 buyback fee recipient. */
|
|
242
|
+
export function getPumpFunBuybackFeeRecipientRandom(): PublicKey {
|
|
243
|
+
const index = Math.floor(Math.random() * PUMPFUN_BUYBACK_FEE_RECIPIENTS.length);
|
|
244
|
+
return PUMPFUN_BUYBACK_FEE_RECIPIENTS[index] ?? PUMPFUN_BUYBACK_FEE_RECIPIENTS[0]!;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Account #2 fee recipient: prefer gRPC/event `feeRecipient`; if `default` pubkey, random from mayhem or standard pool (Rust `pump_fun_fee_recipient_meta`).
|
|
249
|
+
*/
|
|
250
|
+
export function pumpFunFeeRecipientMeta(
|
|
251
|
+
fromStream: PublicKey | undefined,
|
|
252
|
+
isMayhemMode: boolean
|
|
253
|
+
): PublicKey {
|
|
254
|
+
if (fromStream && !fromStream.equals(PublicKey.default)) {
|
|
255
|
+
return fromStream;
|
|
256
|
+
}
|
|
257
|
+
return isMayhemMode ? getRandomMayhemFeeRecipient() : getStandardFeeRecipientRandom();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ============================================
|
|
261
|
+
// Types
|
|
262
|
+
// ============================================
|
|
263
|
+
|
|
264
|
+
export interface PumpFunBondingCurve {
|
|
265
|
+
account: PublicKey;
|
|
266
|
+
virtualTokenReserves: bigint;
|
|
267
|
+
virtualSolReserves: bigint;
|
|
268
|
+
realTokenReserves: bigint;
|
|
269
|
+
creator?: PublicKey;
|
|
270
|
+
isMayhemMode: boolean;
|
|
271
|
+
isCashbackCoin: boolean;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface PumpFunParams {
|
|
275
|
+
bondingCurve: PumpFunBondingCurve;
|
|
276
|
+
creatorVault: PublicKey;
|
|
277
|
+
tokenProgram: PublicKey;
|
|
278
|
+
associatedBondingCurve?: PublicKey;
|
|
279
|
+
observedTradeCreator?: PublicKey;
|
|
280
|
+
feeSharingCreatorVaultIfActive?: PublicKey;
|
|
281
|
+
closeTokenAccountWhenSell?: boolean;
|
|
282
|
+
/** From parser/gRPC (`tradeEvent.feeRecipient`); default pubkey → random pool */
|
|
283
|
+
feeRecipient?: PublicKey;
|
|
284
|
+
/** Quote mint for V2 instructions; default means WSOL. */
|
|
285
|
+
quoteMint?: PublicKey;
|
|
286
|
+
/** Per-params V2 toggle; global `TradeConfig.usePumpfunV2` also maps here. */
|
|
287
|
+
useV2Ix?: boolean;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface PumpFunBuildBuyParams {
|
|
291
|
+
payer: Keypair | PublicKey;
|
|
292
|
+
outputMint: PublicKey;
|
|
293
|
+
inputAmount: bigint;
|
|
294
|
+
slippageBasisPoints?: bigint;
|
|
295
|
+
fixedOutputAmount?: bigint;
|
|
296
|
+
createOutputMintAta?: boolean;
|
|
297
|
+
createInputMintAta?: boolean;
|
|
298
|
+
protocolParams: PumpFunParams;
|
|
299
|
+
useExactSolAmount?: boolean;
|
|
300
|
+
usePumpFunV2?: boolean;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface PumpFunBuildSellParams {
|
|
304
|
+
payer: Keypair | PublicKey;
|
|
305
|
+
inputMint: PublicKey;
|
|
306
|
+
inputAmount: bigint;
|
|
307
|
+
slippageBasisPoints?: bigint;
|
|
308
|
+
fixedOutputAmount?: bigint;
|
|
309
|
+
createOutputMintAta?: boolean;
|
|
310
|
+
closeInputMintAta?: boolean;
|
|
311
|
+
protocolParams: PumpFunParams;
|
|
312
|
+
usePumpFunV2?: boolean;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ============================================
|
|
316
|
+
// Helper Functions
|
|
317
|
+
// ============================================
|
|
318
|
+
|
|
319
|
+
const MAX_SLIPPAGE_BPS = BigInt(9999);
|
|
320
|
+
const PUMPFUN_FEE_BASIS_POINTS = 95n;
|
|
321
|
+
const PUMPFUN_CREATOR_FEE_BASIS_POINTS = 30n;
|
|
322
|
+
const PHANTOM_DEFAULT_CREATOR_VAULT = new PublicKey(
|
|
323
|
+
"2DR3iqRPVThyRLVJnwjPW1qiGWrp8RUFfHVjMbZyhdNc"
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
function calculateWithSlippageBuy(amount: bigint, basisPoints: bigint): bigint {
|
|
327
|
+
const bps = basisPoints > MAX_SLIPPAGE_BPS ? MAX_SLIPPAGE_BPS : basisPoints;
|
|
328
|
+
return amount + (amount * bps) / BigInt(10000);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function calculateWithSlippageSell(amount: bigint, basisPoints: bigint): bigint {
|
|
332
|
+
const bps = basisPoints > MAX_SLIPPAGE_BPS ? MAX_SLIPPAGE_BPS : basisPoints;
|
|
333
|
+
const result = amount - (amount * bps) / BigInt(10000);
|
|
334
|
+
return result > BigInt(0) ? result : BigInt(1);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isUsablePubkey(value: PublicKey | undefined): value is PublicKey {
|
|
338
|
+
return (
|
|
339
|
+
value !== undefined &&
|
|
340
|
+
!value.equals(PublicKey.default) &&
|
|
341
|
+
!value.equals(PHANTOM_DEFAULT_CREATOR_VAULT)
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function effectiveCreatorForTrade(protocolParams: PumpFunParams): PublicKey {
|
|
346
|
+
if (isUsablePubkey(protocolParams.observedTradeCreator)) {
|
|
347
|
+
return protocolParams.observedTradeCreator;
|
|
348
|
+
}
|
|
349
|
+
if (isUsablePubkey(protocolParams.bondingCurve.creator)) {
|
|
350
|
+
return protocolParams.bondingCurve.creator;
|
|
351
|
+
}
|
|
352
|
+
return PublicKey.default;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function resolveCreatorVaultForIx(protocolParams: PumpFunParams, mint: PublicKey): PublicKey {
|
|
356
|
+
if (isUsablePubkey(protocolParams.creatorVault)) {
|
|
357
|
+
return protocolParams.creatorVault;
|
|
358
|
+
}
|
|
359
|
+
if (isUsablePubkey(protocolParams.feeSharingCreatorVaultIfActive)) {
|
|
360
|
+
return protocolParams.feeSharingCreatorVaultIfActive;
|
|
361
|
+
}
|
|
362
|
+
const creator = effectiveCreatorForTrade(protocolParams);
|
|
363
|
+
if (isUsablePubkey(creator)) {
|
|
364
|
+
return getCreatorVaultPda(creator);
|
|
365
|
+
}
|
|
366
|
+
throw new Error(`creator_vault PDA derivation failed for mint ${mint.toBase58()}`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function resolveCreatorVaultForSellV2(protocolParams: PumpFunParams, mint: PublicKey): PublicKey {
|
|
370
|
+
if (isUsablePubkey(protocolParams.creatorVault)) {
|
|
371
|
+
return protocolParams.creatorVault;
|
|
372
|
+
}
|
|
373
|
+
if (isUsablePubkey(protocolParams.feeSharingCreatorVaultIfActive)) {
|
|
374
|
+
return protocolParams.feeSharingCreatorVaultIfActive;
|
|
375
|
+
}
|
|
376
|
+
const curveCreator = protocolParams.bondingCurve.creator;
|
|
377
|
+
if (isUsablePubkey(curveCreator)) {
|
|
378
|
+
return getCreatorVaultPda(curveCreator);
|
|
379
|
+
}
|
|
380
|
+
throw new Error(`creator_vault PDA derivation failed (curve_creator=${String(curveCreator)}, mint=${mint.toBase58()})`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function effectivePumpMintTokenProgram(mint: PublicKey, protocolParams: PumpFunParams): PublicKey {
|
|
384
|
+
if (mint.toBase58().endsWith("pump")) {
|
|
385
|
+
return TOKEN_2022_PROGRAM_ID;
|
|
386
|
+
}
|
|
387
|
+
if (isUsablePubkey(protocolParams.tokenProgram)) {
|
|
388
|
+
return protocolParams.tokenProgram;
|
|
389
|
+
}
|
|
390
|
+
return TOKEN_2022_PROGRAM_ID;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function effectiveQuoteMint(protocolParams: PumpFunParams): PublicKey {
|
|
394
|
+
return isUsablePubkey(protocolParams.quoteMint) ? protocolParams.quoteMint : NATIVE_MINT;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function associatedTokenAddress(mint: PublicKey, owner: PublicKey, tokenProgram: PublicKey): PublicKey {
|
|
398
|
+
return getAssociatedTokenAddressSync(
|
|
399
|
+
mint,
|
|
400
|
+
owner,
|
|
401
|
+
true,
|
|
402
|
+
tokenProgram,
|
|
403
|
+
SPL_ASSOCIATED_TOKEN_PROGRAM_ID
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function getBuyTokenAmountFromSolAmount(
|
|
408
|
+
amount: bigint,
|
|
409
|
+
bondingCurve: PumpFunBondingCurve,
|
|
410
|
+
creator: PublicKey
|
|
411
|
+
): bigint {
|
|
412
|
+
if (amount === 0n || bondingCurve.virtualTokenReserves === 0n) {
|
|
413
|
+
return 0n;
|
|
414
|
+
}
|
|
415
|
+
const totalFeeBps =
|
|
416
|
+
PUMPFUN_FEE_BASIS_POINTS + (isUsablePubkey(creator) ? PUMPFUN_CREATOR_FEE_BASIS_POINTS : 0n);
|
|
417
|
+
const inputAmount = (amount * 10_000n) / (totalFeeBps + 10_000n);
|
|
418
|
+
const denominator = bondingCurve.virtualSolReserves + inputAmount;
|
|
419
|
+
if (denominator === 0n) {
|
|
420
|
+
return 0n;
|
|
421
|
+
}
|
|
422
|
+
let tokensReceived = (inputAmount * bondingCurve.virtualTokenReserves) / denominator;
|
|
423
|
+
tokensReceived =
|
|
424
|
+
tokensReceived < bondingCurve.realTokenReserves ? tokensReceived : bondingCurve.realTokenReserves;
|
|
425
|
+
if (tokensReceived <= 100n * 1_000_000n) {
|
|
426
|
+
tokensReceived = amount > 10_000_000n ? 25_547_619n * 1_000_000n : 255_476n * 1_000_000n;
|
|
427
|
+
}
|
|
428
|
+
return tokensReceived;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function getSellSolAmountFromTokenAmount(
|
|
432
|
+
amount: bigint,
|
|
433
|
+
bondingCurve: PumpFunBondingCurve,
|
|
434
|
+
creator: PublicKey
|
|
435
|
+
): bigint {
|
|
436
|
+
if (amount === 0n || bondingCurve.virtualTokenReserves === 0n) {
|
|
437
|
+
return 0n;
|
|
438
|
+
}
|
|
439
|
+
const solCost =
|
|
440
|
+
(amount * bondingCurve.virtualSolReserves) / (bondingCurve.virtualTokenReserves + amount);
|
|
441
|
+
const totalFeeBps =
|
|
442
|
+
PUMPFUN_FEE_BASIS_POINTS + (isUsablePubkey(creator) ? PUMPFUN_CREATOR_FEE_BASIS_POINTS : 0n);
|
|
443
|
+
const fee = (solCost * totalFeeBps + 9_999n) / 10_000n;
|
|
444
|
+
return solCost > fee ? solCost - fee : 0n;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ============================================
|
|
448
|
+
// Instruction Builders
|
|
449
|
+
// ============================================
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Build buy instructions for PumpFun protocol
|
|
453
|
+
* 100% port from Rust: src/instruction/pumpfun.rs build_buy_instructions
|
|
454
|
+
*/
|
|
455
|
+
export function buildPumpFunBuyInstructions(
|
|
456
|
+
params: PumpFunBuildBuyParams
|
|
457
|
+
): TransactionInstruction[] {
|
|
458
|
+
const {
|
|
459
|
+
payer,
|
|
460
|
+
outputMint,
|
|
461
|
+
inputAmount,
|
|
462
|
+
slippageBasisPoints = BigInt(1000),
|
|
463
|
+
fixedOutputAmount,
|
|
464
|
+
createOutputMintAta = true,
|
|
465
|
+
createInputMintAta = false,
|
|
466
|
+
protocolParams,
|
|
467
|
+
useExactSolAmount = true,
|
|
468
|
+
usePumpFunV2 = false,
|
|
469
|
+
} = params;
|
|
470
|
+
|
|
471
|
+
if (usePumpFunV2 || protocolParams.useV2Ix || isUsablePubkey(protocolParams.quoteMint)) {
|
|
472
|
+
return buildPumpFunBuyV2Instructions({
|
|
473
|
+
...params,
|
|
474
|
+
createInputMintAta,
|
|
475
|
+
usePumpFunV2: true,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (inputAmount === BigInt(0)) {
|
|
480
|
+
throw new Error("Amount cannot be zero");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
484
|
+
const instructions: TransactionInstruction[] = [];
|
|
485
|
+
|
|
486
|
+
const { bondingCurve, creatorVault, associatedBondingCurve, feeRecipient } =
|
|
487
|
+
protocolParams;
|
|
488
|
+
const creator = effectiveCreatorForTrade(protocolParams);
|
|
489
|
+
const creatorVaultAccount = (() => {
|
|
490
|
+
try {
|
|
491
|
+
return resolveCreatorVaultForIx(protocolParams, outputMint);
|
|
492
|
+
} catch {
|
|
493
|
+
return creatorVault;
|
|
494
|
+
}
|
|
495
|
+
})();
|
|
496
|
+
|
|
497
|
+
// Derive bonding curve address
|
|
498
|
+
const bondingCurveAddr =
|
|
499
|
+
bondingCurve.account.equals(PublicKey.default) || !bondingCurve.account
|
|
500
|
+
? getBondingCurvePda(outputMint)
|
|
501
|
+
: bondingCurve.account;
|
|
502
|
+
|
|
503
|
+
// Get token program
|
|
504
|
+
const tokenProgramId = effectivePumpMintTokenProgram(outputMint, protocolParams);
|
|
505
|
+
|
|
506
|
+
// Derive associated bonding curve
|
|
507
|
+
const associatedBondingCurveAddr =
|
|
508
|
+
associatedBondingCurve && !associatedBondingCurve.equals(PublicKey.default)
|
|
509
|
+
? associatedBondingCurve
|
|
510
|
+
: associatedTokenAddress(outputMint, bondingCurveAddr, tokenProgramId);
|
|
511
|
+
|
|
512
|
+
// Derive user token account
|
|
513
|
+
const userTokenAccount = associatedTokenAddress(outputMint, payerPubkey, tokenProgramId);
|
|
514
|
+
|
|
515
|
+
// Derive user volume accumulator
|
|
516
|
+
const userVolumeAccumulator = getPumpFunUserVolumeAccumulatorPda(payerPubkey);
|
|
517
|
+
|
|
518
|
+
// Create ATA if needed
|
|
519
|
+
if (createOutputMintAta) {
|
|
520
|
+
instructions.push(
|
|
521
|
+
createAssociatedTokenAccountInstruction(
|
|
522
|
+
payerPubkey,
|
|
523
|
+
userTokenAccount,
|
|
524
|
+
payerPubkey,
|
|
525
|
+
outputMint,
|
|
526
|
+
tokenProgramId
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const feeRecipientPk = pumpFunFeeRecipientMeta(feeRecipient, bondingCurve.isMayhemMode);
|
|
532
|
+
|
|
533
|
+
// Derive bonding curve v2
|
|
534
|
+
const bondingCurveV2 = getBondingCurveV2Pda(outputMint);
|
|
535
|
+
|
|
536
|
+
// Track volume for cashback coins
|
|
537
|
+
const trackVolume = bondingCurve.isCashbackCoin
|
|
538
|
+
? Buffer.from([1, 1])
|
|
539
|
+
: Buffer.from([1, 0]);
|
|
540
|
+
|
|
541
|
+
const buyTokenAmount = fixedOutputAmount
|
|
542
|
+
? fixedOutputAmount
|
|
543
|
+
: getBuyTokenAmountFromSolAmount(inputAmount, bondingCurve, creator);
|
|
544
|
+
const maxSolCost = calculateWithSlippageBuy(inputAmount, slippageBasisPoints);
|
|
545
|
+
|
|
546
|
+
// Build instruction data
|
|
547
|
+
let data: Buffer;
|
|
548
|
+
if (useExactSolAmount) {
|
|
549
|
+
// buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64, track_volume)
|
|
550
|
+
const minTokensOut = fixedOutputAmount
|
|
551
|
+
? fixedOutputAmount
|
|
552
|
+
: calculateWithSlippageSell(buyTokenAmount, slippageBasisPoints);
|
|
553
|
+
data = Buffer.alloc(26);
|
|
554
|
+
PUMPFUN_BUY_EXACT_SOL_IN_DISCRIMINATOR.copy(data, 0);
|
|
555
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
556
|
+
data.writeBigUInt64LE(minTokensOut, 16);
|
|
557
|
+
trackVolume.copy(data, 24);
|
|
558
|
+
} else {
|
|
559
|
+
// buy(token_amount: u64, max_sol_cost: u64, track_volume)
|
|
560
|
+
data = Buffer.alloc(26);
|
|
561
|
+
PUMPFUN_BUY_DISCRIMINATOR.copy(data, 0);
|
|
562
|
+
data.writeBigUInt64LE(buyTokenAmount, 8);
|
|
563
|
+
data.writeBigUInt64LE(maxSolCost, 16);
|
|
564
|
+
trackVolume.copy(data, 24);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Build accounts
|
|
568
|
+
const keys: AccountMeta[] = [
|
|
569
|
+
{ pubkey: PUMPFUN_GLOBAL_ACCOUNT, isSigner: false, isWritable: false },
|
|
570
|
+
{ pubkey: feeRecipientPk, isSigner: false, isWritable: true },
|
|
571
|
+
{ pubkey: outputMint, isSigner: false, isWritable: false },
|
|
572
|
+
{ pubkey: bondingCurveAddr, isSigner: false, isWritable: true },
|
|
573
|
+
{ pubkey: associatedBondingCurveAddr, isSigner: false, isWritable: true },
|
|
574
|
+
{ pubkey: userTokenAccount, isSigner: false, isWritable: true },
|
|
575
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
576
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
577
|
+
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
578
|
+
{ pubkey: creatorVaultAccount, isSigner: false, isWritable: true },
|
|
579
|
+
{ pubkey: PUMPFUN_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
580
|
+
{ pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
581
|
+
{ pubkey: PUMPFUN_GLOBAL_VOLUME_ACCUMULATOR, isSigner: false, isWritable: true },
|
|
582
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
583
|
+
{ pubkey: PUMPFUN_FEE_CONFIG, isSigner: false, isWritable: false },
|
|
584
|
+
{ pubkey: PUMPFUN_FEE_PROGRAM, isSigner: false, isWritable: false },
|
|
585
|
+
{ pubkey: bondingCurveV2, isSigner: false, isWritable: false },
|
|
586
|
+
{ pubkey: getPumpFunProtocolExtraFeeRecipientRandom(), isSigner: false, isWritable: true },
|
|
587
|
+
];
|
|
588
|
+
|
|
589
|
+
instructions.push(
|
|
590
|
+
new TransactionInstruction({
|
|
591
|
+
keys,
|
|
592
|
+
programId: PUMPFUN_PROGRAM_ID,
|
|
593
|
+
data,
|
|
594
|
+
})
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
return instructions;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Build sell instructions for PumpFun protocol
|
|
602
|
+
* 100% port from Rust: src/instruction/pumpfun.rs build_sell_instructions
|
|
603
|
+
*/
|
|
604
|
+
export function buildPumpFunSellInstructions(
|
|
605
|
+
params: PumpFunBuildSellParams
|
|
606
|
+
): TransactionInstruction[] {
|
|
607
|
+
const {
|
|
608
|
+
payer,
|
|
609
|
+
inputMint,
|
|
610
|
+
inputAmount,
|
|
611
|
+
slippageBasisPoints = BigInt(1000),
|
|
612
|
+
fixedOutputAmount,
|
|
613
|
+
createOutputMintAta = false,
|
|
614
|
+
closeInputMintAta = false,
|
|
615
|
+
protocolParams,
|
|
616
|
+
usePumpFunV2 = false,
|
|
617
|
+
} = params;
|
|
618
|
+
|
|
619
|
+
if (usePumpFunV2 || protocolParams.useV2Ix || isUsablePubkey(protocolParams.quoteMint)) {
|
|
620
|
+
return buildPumpFunSellV2Instructions({
|
|
621
|
+
...params,
|
|
622
|
+
createOutputMintAta,
|
|
623
|
+
usePumpFunV2: true,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (inputAmount === BigInt(0)) {
|
|
628
|
+
throw new Error("Amount cannot be zero");
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
632
|
+
const instructions: TransactionInstruction[] = [];
|
|
633
|
+
|
|
634
|
+
const {
|
|
635
|
+
bondingCurve,
|
|
636
|
+
creatorVault,
|
|
637
|
+
associatedBondingCurve,
|
|
638
|
+
closeTokenAccountWhenSell,
|
|
639
|
+
feeRecipient,
|
|
640
|
+
} = protocolParams;
|
|
641
|
+
const creator = effectiveCreatorForTrade(protocolParams);
|
|
642
|
+
const creatorVaultAccount = (() => {
|
|
643
|
+
try {
|
|
644
|
+
return resolveCreatorVaultForIx(protocolParams, inputMint);
|
|
645
|
+
} catch {
|
|
646
|
+
return creatorVault;
|
|
647
|
+
}
|
|
648
|
+
})();
|
|
649
|
+
|
|
650
|
+
// Derive bonding curve address
|
|
651
|
+
const bondingCurveAddr =
|
|
652
|
+
bondingCurve.account.equals(PublicKey.default) || !bondingCurve.account
|
|
653
|
+
? getBondingCurvePda(inputMint)
|
|
654
|
+
: bondingCurve.account;
|
|
655
|
+
|
|
656
|
+
// Get token program
|
|
657
|
+
const tokenProgramId = effectivePumpMintTokenProgram(inputMint, protocolParams);
|
|
658
|
+
|
|
659
|
+
// Derive associated bonding curve
|
|
660
|
+
const associatedBondingCurveAddr =
|
|
661
|
+
associatedBondingCurve && !associatedBondingCurve.equals(PublicKey.default)
|
|
662
|
+
? associatedBondingCurve
|
|
663
|
+
: associatedTokenAddress(inputMint, bondingCurveAddr, tokenProgramId);
|
|
664
|
+
|
|
665
|
+
// Derive user token account
|
|
666
|
+
const userTokenAccount = associatedTokenAddress(inputMint, payerPubkey, tokenProgramId);
|
|
667
|
+
|
|
668
|
+
const feeRecipientPk = pumpFunFeeRecipientMeta(feeRecipient, bondingCurve.isMayhemMode);
|
|
669
|
+
|
|
670
|
+
// Derive bonding curve v2
|
|
671
|
+
const bondingCurveV2 = getBondingCurveV2Pda(inputMint);
|
|
672
|
+
|
|
673
|
+
// Build instruction data (sell: token_amount, min_sol_output)
|
|
674
|
+
const expectedSolOutput = getSellSolAmountFromTokenAmount(inputAmount, bondingCurve, creator);
|
|
675
|
+
const minSolOutput = fixedOutputAmount
|
|
676
|
+
? fixedOutputAmount
|
|
677
|
+
: calculateWithSlippageSell(expectedSolOutput, slippageBasisPoints);
|
|
678
|
+
const data = Buffer.alloc(24);
|
|
679
|
+
PUMPFUN_SELL_DISCRIMINATOR.copy(data, 0);
|
|
680
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
681
|
+
data.writeBigUInt64LE(minSolOutput, 16);
|
|
682
|
+
|
|
683
|
+
// Build accounts
|
|
684
|
+
const keys: AccountMeta[] = [
|
|
685
|
+
{ pubkey: PUMPFUN_GLOBAL_ACCOUNT, isSigner: false, isWritable: false },
|
|
686
|
+
{ pubkey: feeRecipientPk, isSigner: false, isWritable: true },
|
|
687
|
+
{ pubkey: inputMint, isSigner: false, isWritable: false },
|
|
688
|
+
{ pubkey: bondingCurveAddr, isSigner: false, isWritable: true },
|
|
689
|
+
{ pubkey: associatedBondingCurveAddr, isSigner: false, isWritable: true },
|
|
690
|
+
{ pubkey: userTokenAccount, isSigner: false, isWritable: true },
|
|
691
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
692
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
693
|
+
{ pubkey: creatorVaultAccount, isSigner: false, isWritable: true },
|
|
694
|
+
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
695
|
+
{ pubkey: PUMPFUN_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
696
|
+
{ pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
697
|
+
{ pubkey: PUMPFUN_FEE_CONFIG, isSigner: false, isWritable: false },
|
|
698
|
+
{ pubkey: PUMPFUN_FEE_PROGRAM, isSigner: false, isWritable: false },
|
|
699
|
+
];
|
|
700
|
+
|
|
701
|
+
// Add user volume accumulator for cashback coins
|
|
702
|
+
if (bondingCurve.isCashbackCoin) {
|
|
703
|
+
const userVolumeAccumulator = getPumpFunUserVolumeAccumulatorPda(payerPubkey);
|
|
704
|
+
keys.push({ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Add bonding curve v2
|
|
708
|
+
keys.push({ pubkey: bondingCurveV2, isSigner: false, isWritable: false });
|
|
709
|
+
keys.push({ pubkey: getPumpFunProtocolExtraFeeRecipientRandom(), isSigner: false, isWritable: true });
|
|
710
|
+
|
|
711
|
+
instructions.push(
|
|
712
|
+
new TransactionInstruction({
|
|
713
|
+
keys,
|
|
714
|
+
programId: PUMPFUN_PROGRAM_ID,
|
|
715
|
+
data,
|
|
716
|
+
})
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
// Close token account if requested
|
|
720
|
+
if (closeInputMintAta || closeTokenAccountWhenSell) {
|
|
721
|
+
instructions.push(
|
|
722
|
+
createCloseAccountInstruction(
|
|
723
|
+
userTokenAccount,
|
|
724
|
+
payerPubkey,
|
|
725
|
+
payerPubkey,
|
|
726
|
+
[],
|
|
727
|
+
tokenProgramId
|
|
728
|
+
)
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
return instructions;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Build PumpFun V2 buy instructions (`buy_v2` / `buy_exact_quote_in_v2`).
|
|
737
|
+
*/
|
|
738
|
+
export function buildPumpFunBuyV2Instructions(
|
|
739
|
+
params: PumpFunBuildBuyParams
|
|
740
|
+
): TransactionInstruction[] {
|
|
741
|
+
const {
|
|
742
|
+
payer,
|
|
743
|
+
outputMint,
|
|
744
|
+
inputAmount,
|
|
745
|
+
slippageBasisPoints = BigInt(1000),
|
|
746
|
+
fixedOutputAmount,
|
|
747
|
+
createOutputMintAta = true,
|
|
748
|
+
createInputMintAta = false,
|
|
749
|
+
protocolParams,
|
|
750
|
+
useExactSolAmount = true,
|
|
751
|
+
} = params;
|
|
752
|
+
|
|
753
|
+
if (inputAmount === 0n) {
|
|
754
|
+
throw new Error("Amount cannot be zero");
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
758
|
+
const instructions: TransactionInstruction[] = [];
|
|
759
|
+
const bondingCurve = protocolParams.bondingCurve;
|
|
760
|
+
const creator = effectiveCreatorForTrade(protocolParams);
|
|
761
|
+
const creatorVaultAccount = resolveCreatorVaultForIx(protocolParams, outputMint);
|
|
762
|
+
|
|
763
|
+
const bondingCurveAddr =
|
|
764
|
+
bondingCurve.account.equals(PublicKey.default) || !bondingCurve.account
|
|
765
|
+
? getBondingCurvePda(outputMint)
|
|
766
|
+
: bondingCurve.account;
|
|
767
|
+
|
|
768
|
+
const baseTokenProgram = effectivePumpMintTokenProgram(outputMint, protocolParams);
|
|
769
|
+
const quoteMint = effectiveQuoteMint(protocolParams);
|
|
770
|
+
const quoteTokenProgram = TOKEN_PROGRAM_ID;
|
|
771
|
+
|
|
772
|
+
const associatedBaseBondingCurve = associatedTokenAddress(
|
|
773
|
+
outputMint,
|
|
774
|
+
bondingCurveAddr,
|
|
775
|
+
baseTokenProgram
|
|
776
|
+
);
|
|
777
|
+
const associatedBaseUser = associatedTokenAddress(outputMint, payerPubkey, baseTokenProgram);
|
|
778
|
+
|
|
779
|
+
const feeRecipientPk = pumpFunFeeRecipientMeta(
|
|
780
|
+
protocolParams.feeRecipient,
|
|
781
|
+
bondingCurve.isMayhemMode
|
|
782
|
+
);
|
|
783
|
+
const buybackFeeRecipient = getPumpFunBuybackFeeRecipientRandom();
|
|
784
|
+
|
|
785
|
+
const associatedQuoteFeeRecipient = associatedTokenAddress(
|
|
786
|
+
quoteMint,
|
|
787
|
+
feeRecipientPk,
|
|
788
|
+
quoteTokenProgram
|
|
789
|
+
);
|
|
790
|
+
const associatedQuoteBuybackFeeRecipient = associatedTokenAddress(
|
|
791
|
+
quoteMint,
|
|
792
|
+
buybackFeeRecipient,
|
|
793
|
+
quoteTokenProgram
|
|
794
|
+
);
|
|
795
|
+
const associatedQuoteBondingCurve = associatedTokenAddress(
|
|
796
|
+
quoteMint,
|
|
797
|
+
bondingCurveAddr,
|
|
798
|
+
quoteTokenProgram
|
|
799
|
+
);
|
|
800
|
+
const associatedQuoteUser = associatedTokenAddress(quoteMint, payerPubkey, quoteTokenProgram);
|
|
801
|
+
const associatedCreatorVault = associatedTokenAddress(
|
|
802
|
+
quoteMint,
|
|
803
|
+
creatorVaultAccount,
|
|
804
|
+
quoteTokenProgram
|
|
805
|
+
);
|
|
806
|
+
const sharingConfig = getPumpFunFeeSharingConfigPda(outputMint);
|
|
807
|
+
const userVolumeAccumulator = getPumpFunUserVolumeAccumulatorPda(payerPubkey);
|
|
808
|
+
const associatedUserVolumeAccumulator = associatedTokenAddress(
|
|
809
|
+
quoteMint,
|
|
810
|
+
userVolumeAccumulator,
|
|
811
|
+
quoteTokenProgram
|
|
812
|
+
);
|
|
813
|
+
|
|
814
|
+
if (createOutputMintAta) {
|
|
815
|
+
instructions.push(
|
|
816
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
817
|
+
payerPubkey,
|
|
818
|
+
associatedBaseUser,
|
|
819
|
+
payerPubkey,
|
|
820
|
+
outputMint,
|
|
821
|
+
baseTokenProgram,
|
|
822
|
+
SPL_ASSOCIATED_TOKEN_PROGRAM_ID
|
|
823
|
+
)
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
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
|
+
const buyTokenAmount = fixedOutputAmount
|
|
841
|
+
? fixedOutputAmount
|
|
842
|
+
: getBuyTokenAmountFromSolAmount(inputAmount, bondingCurve, creator);
|
|
843
|
+
const maxSolCost = calculateWithSlippageBuy(inputAmount, slippageBasisPoints);
|
|
844
|
+
let data: Buffer;
|
|
845
|
+
if (useExactSolAmount) {
|
|
846
|
+
const minTokensOut = fixedOutputAmount
|
|
847
|
+
? fixedOutputAmount
|
|
848
|
+
: calculateWithSlippageSell(buyTokenAmount, slippageBasisPoints);
|
|
849
|
+
data = Buffer.alloc(24);
|
|
850
|
+
PUMPFUN_BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR.copy(data, 0);
|
|
851
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
852
|
+
data.writeBigUInt64LE(minTokensOut, 16);
|
|
853
|
+
} else {
|
|
854
|
+
data = Buffer.alloc(24);
|
|
855
|
+
PUMPFUN_BUY_V2_DISCRIMINATOR.copy(data, 0);
|
|
856
|
+
data.writeBigUInt64LE(buyTokenAmount, 8);
|
|
857
|
+
data.writeBigUInt64LE(maxSolCost, 16);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const keys: AccountMeta[] = [
|
|
861
|
+
{ pubkey: PUMPFUN_GLOBAL_ACCOUNT, isSigner: false, isWritable: false },
|
|
862
|
+
{ pubkey: outputMint, isSigner: false, isWritable: false },
|
|
863
|
+
{ pubkey: quoteMint, isSigner: false, isWritable: false },
|
|
864
|
+
{ pubkey: baseTokenProgram, isSigner: false, isWritable: false },
|
|
865
|
+
{ pubkey: quoteTokenProgram, isSigner: false, isWritable: false },
|
|
866
|
+
{ pubkey: SPL_ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
867
|
+
{ pubkey: feeRecipientPk, isSigner: false, isWritable: true },
|
|
868
|
+
{ pubkey: associatedQuoteFeeRecipient, isSigner: false, isWritable: true },
|
|
869
|
+
{ pubkey: buybackFeeRecipient, isSigner: false, isWritable: false },
|
|
870
|
+
{ pubkey: associatedQuoteBuybackFeeRecipient, isSigner: false, isWritable: true },
|
|
871
|
+
{ pubkey: bondingCurveAddr, isSigner: false, isWritable: true },
|
|
872
|
+
{ pubkey: associatedBaseBondingCurve, isSigner: false, isWritable: true },
|
|
873
|
+
{ pubkey: associatedQuoteBondingCurve, isSigner: false, isWritable: true },
|
|
874
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
875
|
+
{ pubkey: associatedBaseUser, isSigner: false, isWritable: true },
|
|
876
|
+
{ pubkey: associatedQuoteUser, isSigner: false, isWritable: true },
|
|
877
|
+
{ pubkey: creatorVaultAccount, isSigner: false, isWritable: true },
|
|
878
|
+
{ pubkey: associatedCreatorVault, isSigner: false, isWritable: true },
|
|
879
|
+
{ pubkey: sharingConfig, isSigner: false, isWritable: false },
|
|
880
|
+
{ pubkey: PUMPFUN_GLOBAL_VOLUME_ACCUMULATOR, isSigner: false, isWritable: true },
|
|
881
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
882
|
+
{ pubkey: associatedUserVolumeAccumulator, isSigner: false, isWritable: true },
|
|
883
|
+
{ pubkey: PUMPFUN_FEE_CONFIG, isSigner: false, isWritable: false },
|
|
884
|
+
{ pubkey: PUMPFUN_FEE_PROGRAM, isSigner: false, isWritable: false },
|
|
885
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
886
|
+
{ pubkey: PUMPFUN_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
887
|
+
{ pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
888
|
+
];
|
|
889
|
+
|
|
890
|
+
instructions.push(
|
|
891
|
+
new TransactionInstruction({
|
|
892
|
+
keys,
|
|
893
|
+
programId: PUMPFUN_PROGRAM_ID,
|
|
894
|
+
data,
|
|
895
|
+
})
|
|
896
|
+
);
|
|
897
|
+
|
|
898
|
+
return instructions;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Build PumpFun V2 sell instructions (`sell_v2`).
|
|
903
|
+
*/
|
|
904
|
+
export function buildPumpFunSellV2Instructions(
|
|
905
|
+
params: PumpFunBuildSellParams
|
|
906
|
+
): TransactionInstruction[] {
|
|
907
|
+
const {
|
|
908
|
+
payer,
|
|
909
|
+
inputMint,
|
|
910
|
+
inputAmount,
|
|
911
|
+
slippageBasisPoints = BigInt(1000),
|
|
912
|
+
fixedOutputAmount,
|
|
913
|
+
createOutputMintAta = false,
|
|
914
|
+
closeInputMintAta = false,
|
|
915
|
+
protocolParams,
|
|
916
|
+
} = params;
|
|
917
|
+
|
|
918
|
+
if (inputAmount === 0n) {
|
|
919
|
+
throw new Error("Amount cannot be zero");
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
923
|
+
const instructions: TransactionInstruction[] = [];
|
|
924
|
+
const bondingCurve = protocolParams.bondingCurve;
|
|
925
|
+
const creator = effectiveCreatorForTrade(protocolParams);
|
|
926
|
+
const creatorVaultAccount = resolveCreatorVaultForSellV2(protocolParams, inputMint);
|
|
927
|
+
|
|
928
|
+
const bondingCurveAddr =
|
|
929
|
+
bondingCurve.account.equals(PublicKey.default) || !bondingCurve.account
|
|
930
|
+
? getBondingCurvePda(inputMint)
|
|
931
|
+
: bondingCurve.account;
|
|
932
|
+
|
|
933
|
+
const baseTokenProgram = effectivePumpMintTokenProgram(inputMint, protocolParams);
|
|
934
|
+
const quoteMint = effectiveQuoteMint(protocolParams);
|
|
935
|
+
const quoteTokenProgram = TOKEN_PROGRAM_ID;
|
|
936
|
+
|
|
937
|
+
const associatedBaseBondingCurve = associatedTokenAddress(
|
|
938
|
+
inputMint,
|
|
939
|
+
bondingCurveAddr,
|
|
940
|
+
baseTokenProgram
|
|
941
|
+
);
|
|
942
|
+
const associatedBaseUser = associatedTokenAddress(inputMint, payerPubkey, baseTokenProgram);
|
|
943
|
+
|
|
944
|
+
const feeRecipientPk = pumpFunFeeRecipientMeta(
|
|
945
|
+
protocolParams.feeRecipient,
|
|
946
|
+
bondingCurve.isMayhemMode
|
|
947
|
+
);
|
|
948
|
+
const buybackFeeRecipient = getPumpFunBuybackFeeRecipientRandom();
|
|
949
|
+
|
|
950
|
+
const associatedQuoteFeeRecipient = associatedTokenAddress(
|
|
951
|
+
quoteMint,
|
|
952
|
+
feeRecipientPk,
|
|
953
|
+
quoteTokenProgram
|
|
954
|
+
);
|
|
955
|
+
const associatedQuoteBuybackFeeRecipient = associatedTokenAddress(
|
|
956
|
+
quoteMint,
|
|
957
|
+
buybackFeeRecipient,
|
|
958
|
+
quoteTokenProgram
|
|
959
|
+
);
|
|
960
|
+
const associatedQuoteBondingCurve = associatedTokenAddress(
|
|
961
|
+
quoteMint,
|
|
962
|
+
bondingCurveAddr,
|
|
963
|
+
quoteTokenProgram
|
|
964
|
+
);
|
|
965
|
+
const associatedQuoteUser = associatedTokenAddress(quoteMint, payerPubkey, quoteTokenProgram);
|
|
966
|
+
const associatedCreatorVault = associatedTokenAddress(
|
|
967
|
+
quoteMint,
|
|
968
|
+
creatorVaultAccount,
|
|
969
|
+
quoteTokenProgram
|
|
970
|
+
);
|
|
971
|
+
const sharingConfig = getPumpFunFeeSharingConfigPda(inputMint);
|
|
972
|
+
const userVolumeAccumulator = getPumpFunUserVolumeAccumulatorPda(payerPubkey);
|
|
973
|
+
const associatedUserVolumeAccumulator = associatedTokenAddress(
|
|
974
|
+
quoteMint,
|
|
975
|
+
userVolumeAccumulator,
|
|
976
|
+
quoteTokenProgram
|
|
977
|
+
);
|
|
978
|
+
|
|
979
|
+
if (createOutputMintAta) {
|
|
980
|
+
instructions.push(
|
|
981
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
982
|
+
payerPubkey,
|
|
983
|
+
associatedQuoteUser,
|
|
984
|
+
payerPubkey,
|
|
985
|
+
quoteMint,
|
|
986
|
+
quoteTokenProgram,
|
|
987
|
+
SPL_ASSOCIATED_TOKEN_PROGRAM_ID
|
|
988
|
+
)
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
const expectedSolOutput = getSellSolAmountFromTokenAmount(inputAmount, bondingCurve, creator);
|
|
993
|
+
const minSolOutput = fixedOutputAmount
|
|
994
|
+
? fixedOutputAmount
|
|
995
|
+
: calculateWithSlippageSell(expectedSolOutput, slippageBasisPoints);
|
|
996
|
+
const data = Buffer.alloc(24);
|
|
997
|
+
PUMPFUN_SELL_V2_DISCRIMINATOR.copy(data, 0);
|
|
998
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
999
|
+
data.writeBigUInt64LE(minSolOutput, 16);
|
|
1000
|
+
|
|
1001
|
+
const keys: AccountMeta[] = [
|
|
1002
|
+
{ pubkey: PUMPFUN_GLOBAL_ACCOUNT, isSigner: false, isWritable: false },
|
|
1003
|
+
{ pubkey: inputMint, isSigner: false, isWritable: false },
|
|
1004
|
+
{ pubkey: quoteMint, isSigner: false, isWritable: false },
|
|
1005
|
+
{ pubkey: baseTokenProgram, isSigner: false, isWritable: false },
|
|
1006
|
+
{ pubkey: quoteTokenProgram, isSigner: false, isWritable: false },
|
|
1007
|
+
{ pubkey: SPL_ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1008
|
+
{ pubkey: feeRecipientPk, isSigner: false, isWritable: true },
|
|
1009
|
+
{ pubkey: associatedQuoteFeeRecipient, isSigner: false, isWritable: true },
|
|
1010
|
+
{ pubkey: buybackFeeRecipient, isSigner: false, isWritable: false },
|
|
1011
|
+
{ pubkey: associatedQuoteBuybackFeeRecipient, isSigner: false, isWritable: true },
|
|
1012
|
+
{ pubkey: bondingCurveAddr, isSigner: false, isWritable: true },
|
|
1013
|
+
{ pubkey: associatedBaseBondingCurve, isSigner: false, isWritable: true },
|
|
1014
|
+
{ pubkey: associatedQuoteBondingCurve, isSigner: false, isWritable: true },
|
|
1015
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
1016
|
+
{ pubkey: associatedBaseUser, isSigner: false, isWritable: true },
|
|
1017
|
+
{ pubkey: associatedQuoteUser, isSigner: false, isWritable: true },
|
|
1018
|
+
{ pubkey: creatorVaultAccount, isSigner: false, isWritable: true },
|
|
1019
|
+
{ pubkey: associatedCreatorVault, isSigner: false, isWritable: true },
|
|
1020
|
+
{ pubkey: sharingConfig, isSigner: false, isWritable: false },
|
|
1021
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1022
|
+
{ pubkey: associatedUserVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1023
|
+
{ pubkey: PUMPFUN_FEE_CONFIG, isSigner: false, isWritable: false },
|
|
1024
|
+
{ pubkey: PUMPFUN_FEE_PROGRAM, isSigner: false, isWritable: false },
|
|
1025
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1026
|
+
{ pubkey: PUMPFUN_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
1027
|
+
{ pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1028
|
+
];
|
|
1029
|
+
|
|
1030
|
+
instructions.push(
|
|
1031
|
+
new TransactionInstruction({
|
|
1032
|
+
keys,
|
|
1033
|
+
programId: PUMPFUN_PROGRAM_ID,
|
|
1034
|
+
data,
|
|
1035
|
+
})
|
|
1036
|
+
);
|
|
1037
|
+
|
|
1038
|
+
if (closeInputMintAta || protocolParams.closeTokenAccountWhenSell) {
|
|
1039
|
+
instructions.push(
|
|
1040
|
+
createCloseAccountInstruction(
|
|
1041
|
+
associatedBaseUser,
|
|
1042
|
+
payerPubkey,
|
|
1043
|
+
payerPubkey,
|
|
1044
|
+
[],
|
|
1045
|
+
baseTokenProgram
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
return instructions;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Build claim cashback instruction for PumpFun
|
|
1055
|
+
*/
|
|
1056
|
+
export function buildPumpFunClaimCashbackInstruction(payer: PublicKey): TransactionInstruction {
|
|
1057
|
+
const userVolumeAccumulator = getPumpFunUserVolumeAccumulatorPda(payer);
|
|
1058
|
+
|
|
1059
|
+
const keys: AccountMeta[] = [
|
|
1060
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
1061
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1062
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1063
|
+
{ pubkey: PUMPFUN_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
1064
|
+
{ pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1065
|
+
];
|
|
1066
|
+
|
|
1067
|
+
return new TransactionInstruction({
|
|
1068
|
+
keys,
|
|
1069
|
+
programId: PUMPFUN_PROGRAM_ID,
|
|
1070
|
+
data: PUMPFUN_CLAIM_CASHBACK_DISCRIMINATOR,
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// ===== Async Fetch Functions - from Rust: src/instruction/utils/pumpfun.rs =====
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Fetch bonding curve account from RPC.
|
|
1078
|
+
* 100% from Rust: src/instruction/utils/pumpfun.rs fetch_bonding_curve_account
|
|
1079
|
+
*/
|
|
1080
|
+
export async function fetchBondingCurveAccount(
|
|
1081
|
+
connection: { getAccountInfo: (pubkey: PublicKey) => Promise<{ value?: { data: Buffer } }> },
|
|
1082
|
+
mint: PublicKey
|
|
1083
|
+
): Promise<{ bondingCurve: PumpFunBondingCurve; bondingCurvePda: PublicKey } | null> {
|
|
1084
|
+
const bondingCurvePda = getBondingCurvePda(mint);
|
|
1085
|
+
const account = await connection.getAccountInfo(bondingCurvePda);
|
|
1086
|
+
|
|
1087
|
+
if (!account?.value?.data || account.value.data.length === 0) {
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const data = account.value.data;
|
|
1092
|
+
// Bonding curve data starts after 8-byte discriminator
|
|
1093
|
+
let offset = 8;
|
|
1094
|
+
|
|
1095
|
+
// virtual_token_reserves: u64
|
|
1096
|
+
const virtualTokenReserves = data.readBigUInt64LE(offset);
|
|
1097
|
+
offset += 8;
|
|
1098
|
+
|
|
1099
|
+
// virtual_sol_reserves: u64
|
|
1100
|
+
const virtualSolReserves = data.readBigUInt64LE(offset);
|
|
1101
|
+
offset += 8;
|
|
1102
|
+
|
|
1103
|
+
// real_token_reserves: u64
|
|
1104
|
+
const realTokenReserves = data.readBigUInt64LE(offset);
|
|
1105
|
+
offset += 8;
|
|
1106
|
+
|
|
1107
|
+
// real_sol_reserves: u64
|
|
1108
|
+
const realSolReserves = data.readBigUInt64LE(offset);
|
|
1109
|
+
offset += 8;
|
|
1110
|
+
|
|
1111
|
+
// token_total_supply: u64
|
|
1112
|
+
offset += 8; // skip
|
|
1113
|
+
|
|
1114
|
+
// complete: bool
|
|
1115
|
+
const complete = data.readUInt8(offset) === 1;
|
|
1116
|
+
offset += 1;
|
|
1117
|
+
|
|
1118
|
+
// creator: Pubkey (32 bytes)
|
|
1119
|
+
const creator = new PublicKey(data.subarray(offset, offset + 32));
|
|
1120
|
+
offset += 32;
|
|
1121
|
+
|
|
1122
|
+
// is_mayhem_mode: bool
|
|
1123
|
+
const isMayhemMode = data.readUInt8(offset) === 1;
|
|
1124
|
+
offset += 1;
|
|
1125
|
+
|
|
1126
|
+
// is_cashback_coin: bool
|
|
1127
|
+
const isCashbackCoin = data.readUInt8(offset) === 1;
|
|
1128
|
+
|
|
1129
|
+
return {
|
|
1130
|
+
bondingCurve: {
|
|
1131
|
+
account: bondingCurvePda,
|
|
1132
|
+
virtualTokenReserves,
|
|
1133
|
+
virtualSolReserves,
|
|
1134
|
+
realTokenReserves,
|
|
1135
|
+
creator,
|
|
1136
|
+
isMayhemMode,
|
|
1137
|
+
isCashbackCoin,
|
|
1138
|
+
},
|
|
1139
|
+
bondingCurvePda,
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Get creator from creator vault PDA.
|
|
1145
|
+
* 100% from Rust: src/instruction/utils/pumpfun.rs get_creator
|
|
1146
|
+
*/
|
|
1147
|
+
export function getCreator(creatorVaultPda: PublicKey): PublicKey {
|
|
1148
|
+
// Check if creator_vault_pda is default
|
|
1149
|
+
const defaultBytes = Buffer.alloc(32);
|
|
1150
|
+
if (creatorVaultPda.equals(new PublicKey(defaultBytes))) {
|
|
1151
|
+
return new PublicKey(defaultBytes);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// Check against default creator vault
|
|
1155
|
+
const defaultCreatorVault = getCreatorVaultPda(new PublicKey(defaultBytes));
|
|
1156
|
+
if (creatorVaultPda.equals(defaultCreatorVault)) {
|
|
1157
|
+
return new PublicKey(defaultBytes);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return creatorVaultPda;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Get buy price (tokens received for SOL).
|
|
1165
|
+
* 100% from Rust: src/instruction/utils/pumpfun.rs get_buy_price
|
|
1166
|
+
*/
|
|
1167
|
+
export function getBuyPrice(
|
|
1168
|
+
amount: bigint,
|
|
1169
|
+
virtualSolReserves: bigint,
|
|
1170
|
+
virtualTokenReserves: bigint,
|
|
1171
|
+
realTokenReserves: bigint
|
|
1172
|
+
): bigint {
|
|
1173
|
+
if (amount === 0n) {
|
|
1174
|
+
return 0n;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const n = virtualSolReserves * virtualTokenReserves;
|
|
1178
|
+
const i = virtualSolReserves + amount;
|
|
1179
|
+
const r = n / i + 1n;
|
|
1180
|
+
const s = virtualTokenReserves - r;
|
|
1181
|
+
|
|
1182
|
+
return s < realTokenReserves ? s : realTokenReserves;
|
|
1183
|
+
}
|