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,730 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bonk Protocol Instruction Builder
|
|
3
|
+
*
|
|
4
|
+
* Production-grade instruction builder for Bonk AMM protocol.
|
|
5
|
+
* Supports buy and sell operations with WSOL and USD1 pools.
|
|
6
|
+
* 100% port from Rust: src/instruction/bonk.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
|
+
TOKEN_PROGRAM_ID,
|
|
20
|
+
createCloseAccountInstruction,
|
|
21
|
+
NATIVE_MINT,
|
|
22
|
+
createSyncNativeInstruction,
|
|
23
|
+
} from "@solana/spl-token";
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// Program IDs and Constants - from Rust src/instruction/utils/bonk.rs
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
/** Bonk program ID */
|
|
30
|
+
export const BONK_PROGRAM_ID = new PublicKey(
|
|
31
|
+
"LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
/** Bonk Authority */
|
|
35
|
+
export const BONK_AUTHORITY = new PublicKey(
|
|
36
|
+
"WLhv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh"
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** Bonk Global Config */
|
|
40
|
+
export const BONK_GLOBAL_CONFIG = new PublicKey(
|
|
41
|
+
"6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/** Bonk USD1 Global Config */
|
|
45
|
+
export const BONK_USD1_GLOBAL_CONFIG = new PublicKey(
|
|
46
|
+
"EPiZbnrThjyLnoQ6QQzkxeFqyL5uyg9RzNHHAudUPxBz"
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
/** Bonk Event Authority */
|
|
50
|
+
export const BONK_EVENT_AUTHORITY = new PublicKey(
|
|
51
|
+
"2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr"
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
/** WSOL Token Account (mint) */
|
|
55
|
+
export const WSOL_MINT = new PublicKey(
|
|
56
|
+
"So11111111111111111111111111111111111111112"
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/** USD1 Token Account (mint) */
|
|
60
|
+
export const USD1_MINT = new PublicKey(
|
|
61
|
+
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/** USDC Token Account (mint) */
|
|
65
|
+
export const USDC_MINT = new PublicKey(
|
|
66
|
+
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
/** Fee rates - from Rust */
|
|
70
|
+
export const BONK_PLATFORM_FEE_RATE = BigInt(100); // 1%
|
|
71
|
+
export const BONK_PROTOCOL_FEE_RATE = BigInt(25); // 0.25%
|
|
72
|
+
export const BONK_SHARE_FEE_RATE = BigInt(0); // 0%
|
|
73
|
+
|
|
74
|
+
// ============================================
|
|
75
|
+
// Discriminators - from Rust src/instruction/utils/bonk.rs
|
|
76
|
+
// ============================================
|
|
77
|
+
|
|
78
|
+
/** Buy exact in instruction discriminator */
|
|
79
|
+
export const BONK_BUY_EXACT_IN_DISCRIMINATOR: Buffer = Buffer.from([
|
|
80
|
+
250, 234, 13, 123, 213, 156, 19, 236,
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
/** Sell exact in instruction discriminator */
|
|
84
|
+
export const BONK_SELL_EXACT_IN_DISCRIMINATOR: Buffer = Buffer.from([
|
|
85
|
+
149, 39, 222, 155, 211, 124, 152, 26,
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
// ============================================
|
|
89
|
+
// Seeds
|
|
90
|
+
// ============================================
|
|
91
|
+
|
|
92
|
+
export const BONK_POOL_SEED = Buffer.from("pool");
|
|
93
|
+
export const BONK_POOL_VAULT_SEED = Buffer.from("pool_vault");
|
|
94
|
+
|
|
95
|
+
// ============================================
|
|
96
|
+
// PDA Derivation Functions
|
|
97
|
+
// ============================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Derive the pool PDA for given base and quote mints
|
|
101
|
+
*/
|
|
102
|
+
export function getBonkPoolPda(baseMint: PublicKey, quoteMint: PublicKey): PublicKey {
|
|
103
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
104
|
+
[BONK_POOL_SEED, baseMint.toBuffer(), quoteMint.toBuffer()],
|
|
105
|
+
BONK_PROGRAM_ID
|
|
106
|
+
);
|
|
107
|
+
return pda;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Derive the vault PDA for given pool and mint
|
|
112
|
+
*/
|
|
113
|
+
export function getBonkVaultPda(poolState: PublicKey, mint: PublicKey): PublicKey {
|
|
114
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
115
|
+
[BONK_POOL_VAULT_SEED, poolState.toBuffer(), mint.toBuffer()],
|
|
116
|
+
BONK_PROGRAM_ID
|
|
117
|
+
);
|
|
118
|
+
return pda;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get platform associated account PDA
|
|
123
|
+
*/
|
|
124
|
+
export function getBonkPlatformAssociatedAccount(platformConfig: PublicKey): PublicKey {
|
|
125
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
126
|
+
[platformConfig.toBuffer(), WSOL_MINT.toBuffer()],
|
|
127
|
+
BONK_PROGRAM_ID
|
|
128
|
+
);
|
|
129
|
+
return pda;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get creator associated account PDA
|
|
134
|
+
*/
|
|
135
|
+
export function getBonkCreatorAssociatedAccount(creator: PublicKey): PublicKey {
|
|
136
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
137
|
+
[creator.toBuffer(), WSOL_MINT.toBuffer()],
|
|
138
|
+
BONK_PROGRAM_ID
|
|
139
|
+
);
|
|
140
|
+
return pda;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============================================
|
|
144
|
+
// Types
|
|
145
|
+
// ============================================
|
|
146
|
+
|
|
147
|
+
export interface BonkParams {
|
|
148
|
+
poolState: PublicKey;
|
|
149
|
+
baseVault: PublicKey;
|
|
150
|
+
quoteVault: PublicKey;
|
|
151
|
+
virtualBase: bigint;
|
|
152
|
+
virtualQuote: bigint;
|
|
153
|
+
realBase: bigint;
|
|
154
|
+
realQuote: bigint;
|
|
155
|
+
mintTokenProgram: PublicKey;
|
|
156
|
+
platformConfig: PublicKey;
|
|
157
|
+
platformAssociatedAccount: PublicKey;
|
|
158
|
+
creatorAssociatedAccount: PublicKey;
|
|
159
|
+
globalConfig?: PublicKey;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface BonkBuildBuyParams {
|
|
163
|
+
payer: Keypair | PublicKey;
|
|
164
|
+
outputMint: PublicKey;
|
|
165
|
+
inputAmount: bigint;
|
|
166
|
+
slippageBasisPoints?: bigint;
|
|
167
|
+
fixedOutputAmount?: bigint;
|
|
168
|
+
createInputMintAta?: boolean;
|
|
169
|
+
createOutputMintAta?: boolean;
|
|
170
|
+
closeInputMintAta?: boolean;
|
|
171
|
+
protocolParams: BonkParams;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface BonkBuildSellParams {
|
|
175
|
+
payer: Keypair | PublicKey;
|
|
176
|
+
inputMint: PublicKey;
|
|
177
|
+
inputAmount: bigint;
|
|
178
|
+
slippageBasisPoints?: bigint;
|
|
179
|
+
fixedOutputAmount?: bigint;
|
|
180
|
+
createOutputMintAta?: boolean;
|
|
181
|
+
closeOutputMintAta?: boolean;
|
|
182
|
+
closeInputMintAta?: boolean;
|
|
183
|
+
protocolParams: BonkParams;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============================================
|
|
187
|
+
// Helper Functions
|
|
188
|
+
// ============================================
|
|
189
|
+
|
|
190
|
+
function getAmountOut(
|
|
191
|
+
amountIn: bigint,
|
|
192
|
+
protocolFeeRate: bigint,
|
|
193
|
+
platformFeeRate: bigint,
|
|
194
|
+
shareFeeRate: bigint,
|
|
195
|
+
virtualBase: bigint,
|
|
196
|
+
virtualQuote: bigint,
|
|
197
|
+
realBase: bigint,
|
|
198
|
+
realQuote: bigint,
|
|
199
|
+
slippageBps: bigint,
|
|
200
|
+
): bigint {
|
|
201
|
+
const protocolFee = (amountIn * protocolFeeRate) / BigInt(10000);
|
|
202
|
+
const platformFee = (amountIn * platformFeeRate) / BigInt(10000);
|
|
203
|
+
const shareFee = (amountIn * shareFeeRate) / BigInt(10000);
|
|
204
|
+
const amountInNet = amountIn - protocolFee - platformFee - shareFee;
|
|
205
|
+
|
|
206
|
+
const inputReserve = virtualQuote + realQuote;
|
|
207
|
+
const outputReserve = virtualBase - realBase;
|
|
208
|
+
const numerator = amountInNet * outputReserve;
|
|
209
|
+
const denominator = inputReserve + amountInNet;
|
|
210
|
+
let amountOut = numerator / denominator;
|
|
211
|
+
|
|
212
|
+
amountOut = amountOut - (amountOut * slippageBps) / BigInt(10000);
|
|
213
|
+
return amountOut;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ============================================
|
|
217
|
+
// Instruction Builders
|
|
218
|
+
// ============================================
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Build buy instructions for Bonk protocol
|
|
222
|
+
* 100% port from Rust: src/instruction/bonk.rs build_buy_instructions
|
|
223
|
+
*/
|
|
224
|
+
export function buildBonkBuyInstructions(
|
|
225
|
+
params: BonkBuildBuyParams
|
|
226
|
+
): TransactionInstruction[] {
|
|
227
|
+
const {
|
|
228
|
+
payer,
|
|
229
|
+
outputMint,
|
|
230
|
+
inputAmount,
|
|
231
|
+
slippageBasisPoints = BigInt(1000),
|
|
232
|
+
fixedOutputAmount,
|
|
233
|
+
createInputMintAta = true,
|
|
234
|
+
createOutputMintAta = true,
|
|
235
|
+
closeInputMintAta = false,
|
|
236
|
+
protocolParams,
|
|
237
|
+
} = params;
|
|
238
|
+
|
|
239
|
+
if (inputAmount === BigInt(0)) {
|
|
240
|
+
throw new Error("Amount cannot be zero");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
244
|
+
const instructions: TransactionInstruction[] = [];
|
|
245
|
+
|
|
246
|
+
const isUsd1Pool = protocolParams.globalConfig?.equals(BONK_USD1_GLOBAL_CONFIG) ?? false;
|
|
247
|
+
const quoteMint = isUsd1Pool ? USD1_MINT : WSOL_MINT;
|
|
248
|
+
|
|
249
|
+
// Get pool state
|
|
250
|
+
const poolState = protocolParams.poolState.equals(PublicKey.default)
|
|
251
|
+
? getBonkPoolPda(outputMint, quoteMint)
|
|
252
|
+
: protocolParams.poolState;
|
|
253
|
+
|
|
254
|
+
const globalConfig = isUsd1Pool ? BONK_USD1_GLOBAL_CONFIG : BONK_GLOBAL_CONFIG;
|
|
255
|
+
|
|
256
|
+
// Calculate minimum output
|
|
257
|
+
const minimumAmountOut = fixedOutputAmount ?? getAmountOut(
|
|
258
|
+
inputAmount,
|
|
259
|
+
BONK_PROTOCOL_FEE_RATE,
|
|
260
|
+
BONK_PLATFORM_FEE_RATE,
|
|
261
|
+
BONK_SHARE_FEE_RATE,
|
|
262
|
+
protocolParams.virtualBase,
|
|
263
|
+
protocolParams.virtualQuote,
|
|
264
|
+
protocolParams.realBase,
|
|
265
|
+
protocolParams.realQuote,
|
|
266
|
+
slippageBasisPoints,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Derive token accounts
|
|
270
|
+
const userBaseTokenAccount = getAssociatedTokenAddressSync(
|
|
271
|
+
outputMint,
|
|
272
|
+
payerPubkey,
|
|
273
|
+
true,
|
|
274
|
+
protocolParams.mintTokenProgram,
|
|
275
|
+
);
|
|
276
|
+
const userQuoteTokenAccount = getAssociatedTokenAddressSync(
|
|
277
|
+
quoteMint,
|
|
278
|
+
payerPubkey,
|
|
279
|
+
true,
|
|
280
|
+
TOKEN_PROGRAM_ID,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// Derive vaults
|
|
284
|
+
const baseVault = protocolParams.baseVault.equals(PublicKey.default)
|
|
285
|
+
? getBonkVaultPda(poolState, outputMint)
|
|
286
|
+
: protocolParams.baseVault;
|
|
287
|
+
const quoteVault = protocolParams.quoteVault.equals(PublicKey.default)
|
|
288
|
+
? getBonkVaultPda(poolState, quoteMint)
|
|
289
|
+
: protocolParams.quoteVault;
|
|
290
|
+
|
|
291
|
+
// Handle WSOL wrapping
|
|
292
|
+
if (createInputMintAta && !isUsd1Pool) {
|
|
293
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true, TOKEN_PROGRAM_ID);
|
|
294
|
+
instructions.push(
|
|
295
|
+
createAssociatedTokenAccountInstruction(payerPubkey, wsolAta, payerPubkey, NATIVE_MINT)
|
|
296
|
+
);
|
|
297
|
+
// Transfer SOL
|
|
298
|
+
const transferIx = SystemProgram.transfer({
|
|
299
|
+
fromPubkey: payerPubkey,
|
|
300
|
+
toPubkey: wsolAta,
|
|
301
|
+
lamports: Number(inputAmount),
|
|
302
|
+
});
|
|
303
|
+
instructions.push(transferIx);
|
|
304
|
+
// Sync native
|
|
305
|
+
instructions.push(createSyncNativeInstruction(wsolAta));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Create output ATA
|
|
309
|
+
if (createOutputMintAta) {
|
|
310
|
+
instructions.push(
|
|
311
|
+
createAssociatedTokenAccountInstruction(
|
|
312
|
+
payerPubkey,
|
|
313
|
+
userBaseTokenAccount,
|
|
314
|
+
payerPubkey,
|
|
315
|
+
outputMint,
|
|
316
|
+
protocolParams.mintTokenProgram,
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Build instruction data
|
|
322
|
+
const shareFeeRate = BigInt(0);
|
|
323
|
+
const data = Buffer.alloc(32);
|
|
324
|
+
BONK_BUY_EXACT_IN_DISCRIMINATOR.copy(data, 0);
|
|
325
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
326
|
+
data.writeBigUInt64LE(minimumAmountOut, 16);
|
|
327
|
+
data.writeBigUInt64LE(shareFeeRate, 24);
|
|
328
|
+
|
|
329
|
+
// Build accounts
|
|
330
|
+
const keys: AccountMeta[] = [
|
|
331
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
332
|
+
{ pubkey: BONK_AUTHORITY, isSigner: false, isWritable: false },
|
|
333
|
+
{ pubkey: globalConfig, isSigner: false, isWritable: false },
|
|
334
|
+
{ pubkey: protocolParams.platformConfig, isSigner: false, isWritable: false },
|
|
335
|
+
{ pubkey: poolState, isSigner: false, isWritable: true },
|
|
336
|
+
{ pubkey: userBaseTokenAccount, isSigner: false, isWritable: true },
|
|
337
|
+
{ pubkey: userQuoteTokenAccount, isSigner: false, isWritable: true },
|
|
338
|
+
{ pubkey: baseVault, isSigner: false, isWritable: true },
|
|
339
|
+
{ pubkey: quoteVault, isSigner: false, isWritable: true },
|
|
340
|
+
{ pubkey: outputMint, isSigner: false, isWritable: false },
|
|
341
|
+
{ pubkey: quoteMint, isSigner: false, isWritable: false },
|
|
342
|
+
{ pubkey: protocolParams.mintTokenProgram, isSigner: false, isWritable: false },
|
|
343
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
344
|
+
{ pubkey: BONK_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
345
|
+
{ pubkey: BONK_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
346
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
347
|
+
{ pubkey: protocolParams.platformAssociatedAccount, isSigner: false, isWritable: true },
|
|
348
|
+
{ pubkey: protocolParams.creatorAssociatedAccount, isSigner: false, isWritable: true },
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
instructions.push(
|
|
352
|
+
new TransactionInstruction({
|
|
353
|
+
keys,
|
|
354
|
+
programId: BONK_PROGRAM_ID,
|
|
355
|
+
data,
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Close WSOL ATA
|
|
360
|
+
if (closeInputMintAta && !isUsd1Pool) {
|
|
361
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true, TOKEN_PROGRAM_ID);
|
|
362
|
+
instructions.push(
|
|
363
|
+
createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey)
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return instructions;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Build sell instructions for Bonk protocol
|
|
372
|
+
* 100% port from Rust: src/instruction/bonk.rs build_sell_instructions
|
|
373
|
+
*/
|
|
374
|
+
export function buildBonkSellInstructions(
|
|
375
|
+
params: BonkBuildSellParams
|
|
376
|
+
): TransactionInstruction[] {
|
|
377
|
+
const {
|
|
378
|
+
payer,
|
|
379
|
+
inputMint,
|
|
380
|
+
inputAmount,
|
|
381
|
+
slippageBasisPoints = BigInt(1000),
|
|
382
|
+
fixedOutputAmount,
|
|
383
|
+
createOutputMintAta = true,
|
|
384
|
+
closeOutputMintAta = false,
|
|
385
|
+
closeInputMintAta = false,
|
|
386
|
+
protocolParams,
|
|
387
|
+
} = params;
|
|
388
|
+
|
|
389
|
+
if (inputAmount === BigInt(0)) {
|
|
390
|
+
throw new Error("Amount cannot be zero");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
394
|
+
const instructions: TransactionInstruction[] = [];
|
|
395
|
+
|
|
396
|
+
const isUsd1Pool = protocolParams.globalConfig?.equals(BONK_USD1_GLOBAL_CONFIG) ?? false;
|
|
397
|
+
const quoteMint = isUsd1Pool ? USD1_MINT : WSOL_MINT;
|
|
398
|
+
|
|
399
|
+
// Get pool state
|
|
400
|
+
const poolState = protocolParams.poolState.equals(PublicKey.default)
|
|
401
|
+
? getBonkPoolPda(inputMint, quoteMint)
|
|
402
|
+
: protocolParams.poolState;
|
|
403
|
+
|
|
404
|
+
const globalConfig = isUsd1Pool ? BONK_USD1_GLOBAL_CONFIG : BONK_GLOBAL_CONFIG;
|
|
405
|
+
|
|
406
|
+
// Calculate minimum output
|
|
407
|
+
const minimumAmountOut = fixedOutputAmount ?? getAmountOut(
|
|
408
|
+
inputAmount,
|
|
409
|
+
BONK_PROTOCOL_FEE_RATE,
|
|
410
|
+
BONK_PLATFORM_FEE_RATE,
|
|
411
|
+
BONK_SHARE_FEE_RATE,
|
|
412
|
+
protocolParams.virtualBase,
|
|
413
|
+
protocolParams.virtualQuote,
|
|
414
|
+
protocolParams.realBase,
|
|
415
|
+
protocolParams.realQuote,
|
|
416
|
+
slippageBasisPoints,
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// Derive token accounts
|
|
420
|
+
const userBaseTokenAccount = getAssociatedTokenAddressSync(
|
|
421
|
+
inputMint,
|
|
422
|
+
payerPubkey,
|
|
423
|
+
true,
|
|
424
|
+
protocolParams.mintTokenProgram,
|
|
425
|
+
);
|
|
426
|
+
const userQuoteTokenAccount = getAssociatedTokenAddressSync(
|
|
427
|
+
quoteMint,
|
|
428
|
+
payerPubkey,
|
|
429
|
+
true,
|
|
430
|
+
TOKEN_PROGRAM_ID,
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// Derive vaults
|
|
434
|
+
const baseVault = protocolParams.baseVault.equals(PublicKey.default)
|
|
435
|
+
? getBonkVaultPda(poolState, inputMint)
|
|
436
|
+
: protocolParams.baseVault;
|
|
437
|
+
const quoteVault = protocolParams.quoteVault.equals(PublicKey.default)
|
|
438
|
+
? getBonkVaultPda(poolState, quoteMint)
|
|
439
|
+
: protocolParams.quoteVault;
|
|
440
|
+
|
|
441
|
+
// Create output ATA for WSOL
|
|
442
|
+
if (createOutputMintAta && !isUsd1Pool) {
|
|
443
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true, TOKEN_PROGRAM_ID);
|
|
444
|
+
instructions.push(
|
|
445
|
+
createAssociatedTokenAccountInstruction(payerPubkey, wsolAta, payerPubkey, NATIVE_MINT)
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Build instruction data
|
|
450
|
+
const shareFeeRate = BigInt(0);
|
|
451
|
+
const data = Buffer.alloc(32);
|
|
452
|
+
BONK_SELL_EXACT_IN_DISCRIMINATOR.copy(data, 0);
|
|
453
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
454
|
+
data.writeBigUInt64LE(minimumAmountOut, 16);
|
|
455
|
+
data.writeBigUInt64LE(shareFeeRate, 24);
|
|
456
|
+
|
|
457
|
+
// Build accounts
|
|
458
|
+
const keys: AccountMeta[] = [
|
|
459
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
460
|
+
{ pubkey: BONK_AUTHORITY, isSigner: false, isWritable: false },
|
|
461
|
+
{ pubkey: globalConfig, isSigner: false, isWritable: false },
|
|
462
|
+
{ pubkey: protocolParams.platformConfig, isSigner: false, isWritable: false },
|
|
463
|
+
{ pubkey: poolState, isSigner: false, isWritable: true },
|
|
464
|
+
{ pubkey: userBaseTokenAccount, isSigner: false, isWritable: true },
|
|
465
|
+
{ pubkey: userQuoteTokenAccount, isSigner: false, isWritable: true },
|
|
466
|
+
{ pubkey: baseVault, isSigner: false, isWritable: true },
|
|
467
|
+
{ pubkey: quoteVault, isSigner: false, isWritable: true },
|
|
468
|
+
{ pubkey: inputMint, isSigner: false, isWritable: false },
|
|
469
|
+
{ pubkey: quoteMint, isSigner: false, isWritable: false },
|
|
470
|
+
{ pubkey: protocolParams.mintTokenProgram, isSigner: false, isWritable: false },
|
|
471
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
472
|
+
{ pubkey: BONK_EVENT_AUTHORITY, isSigner: false, isWritable: false },
|
|
473
|
+
{ pubkey: BONK_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
474
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
475
|
+
{ pubkey: protocolParams.platformAssociatedAccount, isSigner: false, isWritable: true },
|
|
476
|
+
{ pubkey: protocolParams.creatorAssociatedAccount, isSigner: false, isWritable: true },
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
instructions.push(
|
|
480
|
+
new TransactionInstruction({
|
|
481
|
+
keys,
|
|
482
|
+
programId: BONK_PROGRAM_ID,
|
|
483
|
+
data,
|
|
484
|
+
})
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
// Close WSOL ATA
|
|
488
|
+
if (closeOutputMintAta && !isUsd1Pool) {
|
|
489
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true, TOKEN_PROGRAM_ID);
|
|
490
|
+
instructions.push(
|
|
491
|
+
createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey)
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Close input token account
|
|
496
|
+
if (closeInputMintAta) {
|
|
497
|
+
instructions.push(
|
|
498
|
+
createCloseAccountInstruction(userBaseTokenAccount, payerPubkey, payerPubkey, [], protocolParams.mintTokenProgram)
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return instructions;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// ===== Pool State Decoder - from Rust: src/instruction/utils/bonk_types.rs =====
|
|
506
|
+
|
|
507
|
+
export const BONK_POOL_STATE_SIZE = 421; // 8 + 1*5 + 8*10 + 32*7 + 8*8 + 8*5
|
|
508
|
+
|
|
509
|
+
export interface BonkVestingSchedule {
|
|
510
|
+
totalLockedAmount: bigint;
|
|
511
|
+
cliffPeriod: bigint;
|
|
512
|
+
unlockPeriod: bigint;
|
|
513
|
+
startTime: bigint;
|
|
514
|
+
allocatedShareAmount: bigint;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export interface BonkPoolState {
|
|
518
|
+
epoch: bigint;
|
|
519
|
+
authBump: number;
|
|
520
|
+
status: number;
|
|
521
|
+
baseDecimals: number;
|
|
522
|
+
quoteDecimals: number;
|
|
523
|
+
migrateType: number;
|
|
524
|
+
supply: bigint;
|
|
525
|
+
totalBaseSell: bigint;
|
|
526
|
+
virtualBase: bigint;
|
|
527
|
+
virtualQuote: bigint;
|
|
528
|
+
realBase: bigint;
|
|
529
|
+
realQuote: bigint;
|
|
530
|
+
totalQuoteFundRaising: bigint;
|
|
531
|
+
quoteProtocolFee: bigint;
|
|
532
|
+
platformFee: bigint;
|
|
533
|
+
migrateFee: bigint;
|
|
534
|
+
vestingSchedule: BonkVestingSchedule;
|
|
535
|
+
globalConfig: PublicKey;
|
|
536
|
+
platformConfig: PublicKey;
|
|
537
|
+
baseMint: PublicKey;
|
|
538
|
+
quoteMint: PublicKey;
|
|
539
|
+
baseVault: PublicKey;
|
|
540
|
+
quoteVault: PublicKey;
|
|
541
|
+
creator: PublicKey;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Decode a Bonk pool state from account data
|
|
546
|
+
* 100% from Rust: src/instruction/utils/bonk_types.rs pool_state_decode
|
|
547
|
+
*/
|
|
548
|
+
export function decodeBonkPoolState(data: Buffer): BonkPoolState | null {
|
|
549
|
+
if (data.length < BONK_POOL_STATE_SIZE) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
try {
|
|
554
|
+
let offset = 0;
|
|
555
|
+
|
|
556
|
+
// epoch: u64
|
|
557
|
+
const epoch = data.readBigUInt64LE(offset);
|
|
558
|
+
offset += 8;
|
|
559
|
+
|
|
560
|
+
// auth_bump: u8
|
|
561
|
+
const authBump = data.readUInt8(offset);
|
|
562
|
+
offset += 1;
|
|
563
|
+
|
|
564
|
+
// status: u8
|
|
565
|
+
const status = data.readUInt8(offset);
|
|
566
|
+
offset += 1;
|
|
567
|
+
|
|
568
|
+
// base_decimals: u8
|
|
569
|
+
const baseDecimals = data.readUInt8(offset);
|
|
570
|
+
offset += 1;
|
|
571
|
+
|
|
572
|
+
// quote_decimals: u8
|
|
573
|
+
const quoteDecimals = data.readUInt8(offset);
|
|
574
|
+
offset += 1;
|
|
575
|
+
|
|
576
|
+
// migrate_type: u8
|
|
577
|
+
const migrateType = data.readUInt8(offset);
|
|
578
|
+
offset += 1;
|
|
579
|
+
|
|
580
|
+
// supply: u64
|
|
581
|
+
const supply = data.readBigUInt64LE(offset);
|
|
582
|
+
offset += 8;
|
|
583
|
+
|
|
584
|
+
// total_base_sell: u64
|
|
585
|
+
const totalBaseSell = data.readBigUInt64LE(offset);
|
|
586
|
+
offset += 8;
|
|
587
|
+
|
|
588
|
+
// virtual_base: u64
|
|
589
|
+
const virtualBase = data.readBigUInt64LE(offset);
|
|
590
|
+
offset += 8;
|
|
591
|
+
|
|
592
|
+
// virtual_quote: u64
|
|
593
|
+
const virtualQuote = data.readBigUInt64LE(offset);
|
|
594
|
+
offset += 8;
|
|
595
|
+
|
|
596
|
+
// real_base: u64
|
|
597
|
+
const realBase = data.readBigUInt64LE(offset);
|
|
598
|
+
offset += 8;
|
|
599
|
+
|
|
600
|
+
// real_quote: u64
|
|
601
|
+
const realQuote = data.readBigUInt64LE(offset);
|
|
602
|
+
offset += 8;
|
|
603
|
+
|
|
604
|
+
// total_quote_fund_raising: u64
|
|
605
|
+
const totalQuoteFundRaising = data.readBigUInt64LE(offset);
|
|
606
|
+
offset += 8;
|
|
607
|
+
|
|
608
|
+
// quote_protocol_fee: u64
|
|
609
|
+
const quoteProtocolFee = data.readBigUInt64LE(offset);
|
|
610
|
+
offset += 8;
|
|
611
|
+
|
|
612
|
+
// platform_fee: u64
|
|
613
|
+
const platformFee = data.readBigUInt64LE(offset);
|
|
614
|
+
offset += 8;
|
|
615
|
+
|
|
616
|
+
// migrate_fee: u64
|
|
617
|
+
const migrateFee = data.readBigUInt64LE(offset);
|
|
618
|
+
offset += 8;
|
|
619
|
+
|
|
620
|
+
// vesting_schedule: VestingSchedule (5 * u64)
|
|
621
|
+
const vestingSchedule: BonkVestingSchedule = {
|
|
622
|
+
totalLockedAmount: data.readBigUInt64LE(offset),
|
|
623
|
+
cliffPeriod: data.readBigUInt64LE(offset + 8),
|
|
624
|
+
unlockPeriod: data.readBigUInt64LE(offset + 16),
|
|
625
|
+
startTime: data.readBigUInt64LE(offset + 24),
|
|
626
|
+
allocatedShareAmount: data.readBigUInt64LE(offset + 32),
|
|
627
|
+
};
|
|
628
|
+
offset += 40;
|
|
629
|
+
|
|
630
|
+
// global_config: Pubkey
|
|
631
|
+
const globalConfig = new PublicKey(data.subarray(offset, offset + 32));
|
|
632
|
+
offset += 32;
|
|
633
|
+
|
|
634
|
+
// platform_config: Pubkey
|
|
635
|
+
const platformConfig = new PublicKey(data.subarray(offset, offset + 32));
|
|
636
|
+
offset += 32;
|
|
637
|
+
|
|
638
|
+
// base_mint: Pubkey
|
|
639
|
+
const baseMint = new PublicKey(data.subarray(offset, offset + 32));
|
|
640
|
+
offset += 32;
|
|
641
|
+
|
|
642
|
+
// quote_mint: Pubkey
|
|
643
|
+
const quoteMint = new PublicKey(data.subarray(offset, offset + 32));
|
|
644
|
+
offset += 32;
|
|
645
|
+
|
|
646
|
+
// base_vault: Pubkey
|
|
647
|
+
const baseVault = new PublicKey(data.subarray(offset, offset + 32));
|
|
648
|
+
offset += 32;
|
|
649
|
+
|
|
650
|
+
// quote_vault: Pubkey
|
|
651
|
+
const quoteVault = new PublicKey(data.subarray(offset, offset + 32));
|
|
652
|
+
offset += 32;
|
|
653
|
+
|
|
654
|
+
// creator: Pubkey
|
|
655
|
+
const creator = new PublicKey(data.subarray(offset, offset + 32));
|
|
656
|
+
// offset += 32; // Not needed, last field
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
epoch,
|
|
660
|
+
authBump,
|
|
661
|
+
status,
|
|
662
|
+
baseDecimals,
|
|
663
|
+
quoteDecimals,
|
|
664
|
+
migrateType,
|
|
665
|
+
supply,
|
|
666
|
+
totalBaseSell,
|
|
667
|
+
virtualBase,
|
|
668
|
+
virtualQuote,
|
|
669
|
+
realBase,
|
|
670
|
+
realQuote,
|
|
671
|
+
totalQuoteFundRaising,
|
|
672
|
+
quoteProtocolFee,
|
|
673
|
+
platformFee,
|
|
674
|
+
migrateFee,
|
|
675
|
+
vestingSchedule,
|
|
676
|
+
globalConfig,
|
|
677
|
+
platformConfig,
|
|
678
|
+
baseMint,
|
|
679
|
+
quoteMint,
|
|
680
|
+
baseVault,
|
|
681
|
+
quoteVault,
|
|
682
|
+
creator,
|
|
683
|
+
};
|
|
684
|
+
} catch {
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// ===== Async Fetch Functions - from Rust: src/instruction/utils/bonk.rs =====
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Fetch a Bonk pool state from RPC.
|
|
693
|
+
* 100% from Rust: src/instruction/utils/bonk.rs fetch_pool_state
|
|
694
|
+
*/
|
|
695
|
+
export async function fetchBonkPoolState(
|
|
696
|
+
connection: { getAccountInfo: (pubkey: PublicKey) => Promise<{ value?: { data: Buffer } }> },
|
|
697
|
+
poolAddress: PublicKey
|
|
698
|
+
): Promise<BonkPoolState | null> {
|
|
699
|
+
const account = await connection.getAccountInfo(poolAddress);
|
|
700
|
+
if (!account?.value?.data) {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
return decodeBonkPoolState(account.value.data);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Get pool PDA for Bonk.
|
|
708
|
+
* Seeds: ["pool", base_mint, quote_mint]
|
|
709
|
+
*/
|
|
710
|
+
export function getBonkPoolPDA(baseMint: PublicKey, quoteMint: PublicKey): PublicKey {
|
|
711
|
+
const POOL_SEED = Buffer.from('pool');
|
|
712
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
713
|
+
[POOL_SEED, baseMint.toBuffer(), quoteMint.toBuffer()],
|
|
714
|
+
BONK_PROGRAM_ID
|
|
715
|
+
);
|
|
716
|
+
return pda;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Get vault PDA for Bonk.
|
|
721
|
+
* Seeds: ["pool_vault", pool_state, mint]
|
|
722
|
+
*/
|
|
723
|
+
export function getBonkVaultPDA(poolState: PublicKey, mint: PublicKey): PublicKey {
|
|
724
|
+
const POOL_VAULT_SEED = Buffer.from('pool_vault');
|
|
725
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
726
|
+
[POOL_VAULT_SEED, poolState.toBuffer(), mint.toBuffer()],
|
|
727
|
+
BONK_PROGRAM_ID
|
|
728
|
+
);
|
|
729
|
+
return pda;
|
|
730
|
+
}
|