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,795 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raydium CPMM (Concentrated Pool Market Maker) Protocol Instruction Builder
|
|
3
|
+
*
|
|
4
|
+
* Production-grade instruction builder for Raydium CPMM protocol.
|
|
5
|
+
* 100% port of Rust implementation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
PublicKey,
|
|
10
|
+
Keypair,
|
|
11
|
+
AccountMeta,
|
|
12
|
+
TransactionInstruction,
|
|
13
|
+
SystemProgram,
|
|
14
|
+
} from "@solana/web3.js";
|
|
15
|
+
import {
|
|
16
|
+
getAssociatedTokenAddressSync,
|
|
17
|
+
createAssociatedTokenAccountInstruction,
|
|
18
|
+
TOKEN_PROGRAM_ID,
|
|
19
|
+
createCloseAccountInstruction,
|
|
20
|
+
NATIVE_MINT,
|
|
21
|
+
createSyncNativeInstruction,
|
|
22
|
+
} from "@solana/spl-token";
|
|
23
|
+
|
|
24
|
+
// ============================================
|
|
25
|
+
// Program IDs and Constants
|
|
26
|
+
// ============================================
|
|
27
|
+
|
|
28
|
+
/** Raydium CPMM program ID */
|
|
29
|
+
export const RAYDIUM_CPMM_PROGRAM_ID = new PublicKey(
|
|
30
|
+
"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
/** Authority */
|
|
34
|
+
export const RAYDIUM_CPMM_AUTHORITY = new PublicKey(
|
|
35
|
+
"GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL"
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/** Fee rates */
|
|
39
|
+
export const RAYDIUM_CPMM_FEE_RATE_DENOMINATOR_VALUE = BigInt(1_000_000);
|
|
40
|
+
export const RAYDIUM_CPMM_TRADE_FEE_RATE = BigInt(2500);
|
|
41
|
+
export const RAYDIUM_CPMM_CREATOR_FEE_RATE = BigInt(0);
|
|
42
|
+
export const RAYDIUM_CPMM_PROTOCOL_FEE_RATE = BigInt(120000);
|
|
43
|
+
export const RAYDIUM_CPMM_FUND_FEE_RATE = BigInt(40000);
|
|
44
|
+
|
|
45
|
+
// ============================================
|
|
46
|
+
// Discriminators
|
|
47
|
+
// ============================================
|
|
48
|
+
|
|
49
|
+
/** Swap base in instruction discriminator */
|
|
50
|
+
export const RAYDIUM_CPMM_SWAP_BASE_IN_DISCRIMINATOR: Buffer = Buffer.from([
|
|
51
|
+
143, 190, 90, 218, 196, 30, 51, 222,
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
/** Swap base out instruction discriminator */
|
|
55
|
+
export const RAYDIUM_CPMM_SWAP_BASE_OUT_DISCRIMINATOR: Buffer = Buffer.from([
|
|
56
|
+
55, 217, 98, 86, 163, 74, 180, 173,
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
// ============================================
|
|
60
|
+
// Seeds
|
|
61
|
+
// ============================================
|
|
62
|
+
|
|
63
|
+
export const RAYDIUM_CPMM_POOL_SEED = Buffer.from("pool");
|
|
64
|
+
export const RAYDIUM_CPMM_POOL_VAULT_SEED = Buffer.from("pool_vault");
|
|
65
|
+
export const RAYDIUM_CPMM_OBSERVATION_STATE_SEED = Buffer.from("observation");
|
|
66
|
+
|
|
67
|
+
// ============================================
|
|
68
|
+
// PDA Derivation Functions
|
|
69
|
+
// ============================================
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Derive the pool PDA for given config and mints
|
|
73
|
+
*/
|
|
74
|
+
export function getRaydiumCpmmPoolPda(
|
|
75
|
+
ammConfig: PublicKey,
|
|
76
|
+
mint1: PublicKey,
|
|
77
|
+
mint2: PublicKey
|
|
78
|
+
): PublicKey {
|
|
79
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
80
|
+
[RAYDIUM_CPMM_POOL_SEED, ammConfig.toBuffer(), mint1.toBuffer(), mint2.toBuffer()],
|
|
81
|
+
RAYDIUM_CPMM_PROGRAM_ID
|
|
82
|
+
);
|
|
83
|
+
return pda;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Derive the vault PDA for a pool and mint
|
|
88
|
+
*/
|
|
89
|
+
export function getRaydiumCpmmVaultPda(poolState: PublicKey, mint: PublicKey): PublicKey {
|
|
90
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
91
|
+
[RAYDIUM_CPMM_POOL_VAULT_SEED, poolState.toBuffer(), mint.toBuffer()],
|
|
92
|
+
RAYDIUM_CPMM_PROGRAM_ID
|
|
93
|
+
);
|
|
94
|
+
return pda;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Derive the observation state PDA for a pool
|
|
99
|
+
*/
|
|
100
|
+
export function getRaydiumCpmmObservationStatePda(poolState: PublicKey): PublicKey {
|
|
101
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
102
|
+
[RAYDIUM_CPMM_OBSERVATION_STATE_SEED, poolState.toBuffer()],
|
|
103
|
+
RAYDIUM_CPMM_PROGRAM_ID
|
|
104
|
+
);
|
|
105
|
+
return pda;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================
|
|
109
|
+
// Helper Functions
|
|
110
|
+
// ============================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Compute swap amount for CPMM
|
|
114
|
+
*/
|
|
115
|
+
export function computeRaydiumCpmmSwapAmount(
|
|
116
|
+
baseReserve: bigint,
|
|
117
|
+
quoteReserve: bigint,
|
|
118
|
+
isBaseIn: boolean,
|
|
119
|
+
amountIn: bigint,
|
|
120
|
+
slippageBasisPoints: bigint
|
|
121
|
+
): { amountOut: bigint; minAmountOut: bigint } {
|
|
122
|
+
// Apply trade fee (0.25%)
|
|
123
|
+
const feeRate = RAYDIUM_CPMM_TRADE_FEE_RATE;
|
|
124
|
+
const feeDenominator = RAYDIUM_CPMM_FEE_RATE_DENOMINATOR_VALUE;
|
|
125
|
+
const amountInAfterFee = amountIn - (amountIn * feeRate) / feeDenominator;
|
|
126
|
+
|
|
127
|
+
// Calculate output using constant product formula
|
|
128
|
+
let amountOut: bigint;
|
|
129
|
+
if (isBaseIn) {
|
|
130
|
+
// Selling base for quote: output = (quoteReserve * amountIn) / (baseReserve + amountIn)
|
|
131
|
+
const denominator = baseReserve + amountInAfterFee;
|
|
132
|
+
amountOut = (quoteReserve * amountInAfterFee) / denominator;
|
|
133
|
+
} else {
|
|
134
|
+
// Selling quote for base: output = (baseReserve * amountIn) / (quoteReserve + amountIn)
|
|
135
|
+
const denominator = quoteReserve + amountInAfterFee;
|
|
136
|
+
amountOut = (baseReserve * amountInAfterFee) / denominator;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Apply slippage
|
|
140
|
+
const minAmountOut = amountOut - (amountOut * slippageBasisPoints) / BigInt(10000);
|
|
141
|
+
|
|
142
|
+
return { amountOut, minAmountOut };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ============================================
|
|
146
|
+
// Types
|
|
147
|
+
// ============================================
|
|
148
|
+
|
|
149
|
+
export interface RaydiumCpmmParams {
|
|
150
|
+
poolState?: PublicKey;
|
|
151
|
+
ammConfig: PublicKey;
|
|
152
|
+
baseMint: PublicKey;
|
|
153
|
+
quoteMint: PublicKey;
|
|
154
|
+
baseTokenProgram: PublicKey;
|
|
155
|
+
quoteTokenProgram: PublicKey;
|
|
156
|
+
baseVault?: PublicKey;
|
|
157
|
+
quoteVault?: PublicKey;
|
|
158
|
+
baseReserve: bigint;
|
|
159
|
+
quoteReserve: bigint;
|
|
160
|
+
observationState?: PublicKey;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface BuildRaydiumCpmmBuyInstructionsParams {
|
|
164
|
+
payer: Keypair | PublicKey;
|
|
165
|
+
outputMint: PublicKey;
|
|
166
|
+
inputAmount: bigint;
|
|
167
|
+
slippageBasisPoints?: bigint;
|
|
168
|
+
fixedOutputAmount?: bigint;
|
|
169
|
+
createInputMintAta?: boolean;
|
|
170
|
+
createOutputMintAta?: boolean;
|
|
171
|
+
closeInputMintAta?: boolean;
|
|
172
|
+
protocolParams: RaydiumCpmmParams;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface BuildRaydiumCpmmSellInstructionsParams {
|
|
176
|
+
payer: Keypair | PublicKey;
|
|
177
|
+
inputMint: PublicKey;
|
|
178
|
+
inputAmount: bigint;
|
|
179
|
+
slippageBasisPoints?: bigint;
|
|
180
|
+
fixedOutputAmount?: bigint;
|
|
181
|
+
createOutputMintAta?: boolean;
|
|
182
|
+
closeOutputMintAta?: boolean;
|
|
183
|
+
closeInputMintAta?: boolean;
|
|
184
|
+
protocolParams: RaydiumCpmmParams;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ============================================
|
|
188
|
+
// Instruction Builders
|
|
189
|
+
// ============================================
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Build buy instructions for Raydium CPMM protocol
|
|
193
|
+
*/
|
|
194
|
+
export function buildRaydiumCpmmBuyInstructions(
|
|
195
|
+
params: BuildRaydiumCpmmBuyInstructionsParams
|
|
196
|
+
): TransactionInstruction[] {
|
|
197
|
+
const {
|
|
198
|
+
payer,
|
|
199
|
+
outputMint,
|
|
200
|
+
inputAmount,
|
|
201
|
+
slippageBasisPoints = BigInt(1000),
|
|
202
|
+
fixedOutputAmount,
|
|
203
|
+
createInputMintAta = true,
|
|
204
|
+
createOutputMintAta = true,
|
|
205
|
+
closeInputMintAta = false,
|
|
206
|
+
protocolParams,
|
|
207
|
+
} = params;
|
|
208
|
+
|
|
209
|
+
if (inputAmount === BigInt(0)) {
|
|
210
|
+
throw new Error("Amount cannot be zero");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
214
|
+
const instructions: TransactionInstruction[] = [];
|
|
215
|
+
|
|
216
|
+
const WSOL_TOKEN_ACCOUNT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
217
|
+
const USDC_TOKEN_ACCOUNT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
218
|
+
|
|
219
|
+
const {
|
|
220
|
+
ammConfig,
|
|
221
|
+
baseMint,
|
|
222
|
+
quoteMint,
|
|
223
|
+
baseTokenProgram,
|
|
224
|
+
quoteTokenProgram,
|
|
225
|
+
baseVault,
|
|
226
|
+
quoteVault,
|
|
227
|
+
baseReserve,
|
|
228
|
+
quoteReserve,
|
|
229
|
+
observationState,
|
|
230
|
+
} = protocolParams;
|
|
231
|
+
|
|
232
|
+
// Check pool type
|
|
233
|
+
const isWsol = baseMint.equals(WSOL_TOKEN_ACCOUNT) || quoteMint.equals(WSOL_TOKEN_ACCOUNT);
|
|
234
|
+
const isUsdc = baseMint.equals(USDC_TOKEN_ACCOUNT) || quoteMint.equals(USDC_TOKEN_ACCOUNT);
|
|
235
|
+
|
|
236
|
+
if (!isWsol && !isUsdc) {
|
|
237
|
+
throw new Error("Pool must contain WSOL or USDC");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Determine swap direction
|
|
241
|
+
const isBaseIn = baseMint.equals(WSOL_TOKEN_ACCOUNT) || baseMint.equals(USDC_TOKEN_ACCOUNT);
|
|
242
|
+
const mintTokenProgram = isBaseIn ? quoteTokenProgram : baseTokenProgram;
|
|
243
|
+
|
|
244
|
+
// Derive pool state
|
|
245
|
+
const poolState = protocolParams.poolState && !protocolParams.poolState.equals(PublicKey.default)
|
|
246
|
+
? protocolParams.poolState
|
|
247
|
+
: getRaydiumCpmmPoolPda(ammConfig, baseMint, quoteMint);
|
|
248
|
+
|
|
249
|
+
// Calculate output
|
|
250
|
+
const swapResult = computeRaydiumCpmmSwapAmount(
|
|
251
|
+
baseReserve,
|
|
252
|
+
quoteReserve,
|
|
253
|
+
isBaseIn,
|
|
254
|
+
inputAmount,
|
|
255
|
+
slippageBasisPoints
|
|
256
|
+
);
|
|
257
|
+
const minimumAmountOut = fixedOutputAmount || swapResult.minAmountOut;
|
|
258
|
+
|
|
259
|
+
// Determine input/output mints
|
|
260
|
+
const inputMint = isWsol ? WSOL_TOKEN_ACCOUNT : USDC_TOKEN_ACCOUNT;
|
|
261
|
+
|
|
262
|
+
// Derive user token accounts
|
|
263
|
+
const inputTokenAccount = getAssociatedTokenAddressSync(
|
|
264
|
+
inputMint,
|
|
265
|
+
payerPubkey,
|
|
266
|
+
true,
|
|
267
|
+
TOKEN_PROGRAM_ID
|
|
268
|
+
);
|
|
269
|
+
const outputTokenAccount = getAssociatedTokenAddressSync(
|
|
270
|
+
outputMint,
|
|
271
|
+
payerPubkey,
|
|
272
|
+
true,
|
|
273
|
+
mintTokenProgram
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Derive vault accounts
|
|
277
|
+
const inputVaultAccount = ((): PublicKey => {
|
|
278
|
+
if (isWsol && baseMint.equals(inputMint) && baseVault && !baseVault.equals(PublicKey.default)) {
|
|
279
|
+
return baseVault;
|
|
280
|
+
}
|
|
281
|
+
if (!isWsol && quoteMint.equals(inputMint) && quoteVault && !quoteVault.equals(PublicKey.default)) {
|
|
282
|
+
return quoteVault;
|
|
283
|
+
}
|
|
284
|
+
return getRaydiumCpmmVaultPda(poolState, inputMint);
|
|
285
|
+
})();
|
|
286
|
+
|
|
287
|
+
const outputVaultAccount = ((): PublicKey => {
|
|
288
|
+
if (baseMint.equals(outputMint) && baseVault && !baseVault.equals(PublicKey.default)) {
|
|
289
|
+
return baseVault;
|
|
290
|
+
}
|
|
291
|
+
if (quoteMint.equals(outputMint) && quoteVault && !quoteVault.equals(PublicKey.default)) {
|
|
292
|
+
return quoteVault;
|
|
293
|
+
}
|
|
294
|
+
return getRaydiumCpmmVaultPda(poolState, outputMint);
|
|
295
|
+
})();
|
|
296
|
+
|
|
297
|
+
// Derive observation state
|
|
298
|
+
const observationStateAccount = observationState && !observationState.equals(PublicKey.default)
|
|
299
|
+
? observationState
|
|
300
|
+
: getRaydiumCpmmObservationStatePda(poolState);
|
|
301
|
+
|
|
302
|
+
// Handle WSOL wrapping
|
|
303
|
+
if (createInputMintAta && isWsol) {
|
|
304
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
305
|
+
instructions.push(
|
|
306
|
+
createAssociatedTokenAccountInstruction(
|
|
307
|
+
payerPubkey,
|
|
308
|
+
wsolAta,
|
|
309
|
+
payerPubkey,
|
|
310
|
+
NATIVE_MINT,
|
|
311
|
+
TOKEN_PROGRAM_ID
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
instructions.push(createSyncNativeInstruction(wsolAta));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Create output mint ATA if needed
|
|
318
|
+
if (createOutputMintAta) {
|
|
319
|
+
instructions.push(
|
|
320
|
+
createAssociatedTokenAccountInstruction(
|
|
321
|
+
payerPubkey,
|
|
322
|
+
outputTokenAccount,
|
|
323
|
+
payerPubkey,
|
|
324
|
+
outputMint,
|
|
325
|
+
mintTokenProgram
|
|
326
|
+
)
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Build instruction data
|
|
331
|
+
const data = Buffer.alloc(24);
|
|
332
|
+
RAYDIUM_CPMM_SWAP_BASE_IN_DISCRIMINATOR.copy(data, 0);
|
|
333
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
334
|
+
data.writeBigUInt64LE(minimumAmountOut, 16);
|
|
335
|
+
|
|
336
|
+
// Build accounts
|
|
337
|
+
const accounts: AccountMeta[] = [
|
|
338
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
339
|
+
{ pubkey: RAYDIUM_CPMM_AUTHORITY, isSigner: false, isWritable: false },
|
|
340
|
+
{ pubkey: ammConfig, isSigner: false, isWritable: false },
|
|
341
|
+
{ pubkey: poolState, isSigner: false, isWritable: true },
|
|
342
|
+
{ pubkey: inputTokenAccount, isSigner: false, isWritable: true },
|
|
343
|
+
{ pubkey: outputTokenAccount, isSigner: false, isWritable: true },
|
|
344
|
+
{ pubkey: inputVaultAccount, isSigner: false, isWritable: true },
|
|
345
|
+
{ pubkey: outputVaultAccount, isSigner: false, isWritable: true },
|
|
346
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
347
|
+
{ pubkey: mintTokenProgram, isSigner: false, isWritable: false },
|
|
348
|
+
{ pubkey: inputMint, isSigner: false, isWritable: false },
|
|
349
|
+
{ pubkey: outputMint, isSigner: false, isWritable: false },
|
|
350
|
+
{ pubkey: observationStateAccount, isSigner: false, isWritable: true },
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
instructions.push(
|
|
354
|
+
new TransactionInstruction({
|
|
355
|
+
keys: accounts,
|
|
356
|
+
programId: RAYDIUM_CPMM_PROGRAM_ID,
|
|
357
|
+
data,
|
|
358
|
+
})
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
// Close WSOL ATA if requested
|
|
362
|
+
if (closeInputMintAta && isWsol) {
|
|
363
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
364
|
+
instructions.push(
|
|
365
|
+
createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID)
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return instructions;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Build sell instructions for Raydium CPMM protocol
|
|
374
|
+
*/
|
|
375
|
+
export function buildRaydiumCpmmSellInstructions(
|
|
376
|
+
params: BuildRaydiumCpmmSellInstructionsParams
|
|
377
|
+
): TransactionInstruction[] {
|
|
378
|
+
const {
|
|
379
|
+
payer,
|
|
380
|
+
inputMint,
|
|
381
|
+
inputAmount,
|
|
382
|
+
slippageBasisPoints = BigInt(1000),
|
|
383
|
+
fixedOutputAmount,
|
|
384
|
+
createOutputMintAta = true,
|
|
385
|
+
closeOutputMintAta = false,
|
|
386
|
+
closeInputMintAta = false,
|
|
387
|
+
protocolParams,
|
|
388
|
+
} = params;
|
|
389
|
+
|
|
390
|
+
if (inputAmount === BigInt(0)) {
|
|
391
|
+
throw new Error("Amount cannot be zero");
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer;
|
|
395
|
+
const instructions: TransactionInstruction[] = [];
|
|
396
|
+
|
|
397
|
+
const WSOL_TOKEN_ACCOUNT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
398
|
+
const USDC_TOKEN_ACCOUNT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
399
|
+
|
|
400
|
+
const {
|
|
401
|
+
ammConfig,
|
|
402
|
+
baseMint,
|
|
403
|
+
quoteMint,
|
|
404
|
+
baseTokenProgram,
|
|
405
|
+
quoteTokenProgram,
|
|
406
|
+
baseVault,
|
|
407
|
+
quoteVault,
|
|
408
|
+
baseReserve,
|
|
409
|
+
quoteReserve,
|
|
410
|
+
observationState,
|
|
411
|
+
} = protocolParams;
|
|
412
|
+
|
|
413
|
+
// Check pool type
|
|
414
|
+
const isWsol = baseMint.equals(WSOL_TOKEN_ACCOUNT) || quoteMint.equals(WSOL_TOKEN_ACCOUNT);
|
|
415
|
+
const isUsdc = baseMint.equals(USDC_TOKEN_ACCOUNT) || quoteMint.equals(USDC_TOKEN_ACCOUNT);
|
|
416
|
+
|
|
417
|
+
if (!isWsol && !isUsdc) {
|
|
418
|
+
throw new Error("Pool must contain WSOL or USDC");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Determine swap direction
|
|
422
|
+
const isQuoteOut = quoteMint.equals(WSOL_TOKEN_ACCOUNT) || quoteMint.equals(USDC_TOKEN_ACCOUNT);
|
|
423
|
+
const mintTokenProgram = isQuoteOut ? baseTokenProgram : quoteTokenProgram;
|
|
424
|
+
|
|
425
|
+
// Derive pool state
|
|
426
|
+
const poolState = protocolParams.poolState && !protocolParams.poolState.equals(PublicKey.default)
|
|
427
|
+
? protocolParams.poolState
|
|
428
|
+
: getRaydiumCpmmPoolPda(ammConfig, baseMint, quoteMint);
|
|
429
|
+
|
|
430
|
+
// Calculate output
|
|
431
|
+
const swapResult = computeRaydiumCpmmSwapAmount(
|
|
432
|
+
baseReserve,
|
|
433
|
+
quoteReserve,
|
|
434
|
+
isQuoteOut,
|
|
435
|
+
inputAmount,
|
|
436
|
+
slippageBasisPoints
|
|
437
|
+
);
|
|
438
|
+
const minimumAmountOut = fixedOutputAmount || swapResult.minAmountOut;
|
|
439
|
+
|
|
440
|
+
// Determine output mint
|
|
441
|
+
const outputMint = isWsol ? WSOL_TOKEN_ACCOUNT : USDC_TOKEN_ACCOUNT;
|
|
442
|
+
|
|
443
|
+
// Derive user token accounts
|
|
444
|
+
const inputTokenAccount = getAssociatedTokenAddressSync(
|
|
445
|
+
inputMint,
|
|
446
|
+
payerPubkey,
|
|
447
|
+
true,
|
|
448
|
+
mintTokenProgram
|
|
449
|
+
);
|
|
450
|
+
const outputTokenAccount = getAssociatedTokenAddressSync(
|
|
451
|
+
outputMint,
|
|
452
|
+
payerPubkey,
|
|
453
|
+
true,
|
|
454
|
+
TOKEN_PROGRAM_ID
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
// Derive vault accounts
|
|
458
|
+
const inputVaultAccount = ((): PublicKey => {
|
|
459
|
+
if (baseMint.equals(inputMint) && baseVault && !baseVault.equals(PublicKey.default)) {
|
|
460
|
+
return baseVault;
|
|
461
|
+
}
|
|
462
|
+
if (quoteMint.equals(inputMint) && quoteVault && !quoteVault.equals(PublicKey.default)) {
|
|
463
|
+
return quoteVault;
|
|
464
|
+
}
|
|
465
|
+
return getRaydiumCpmmVaultPda(poolState, inputMint);
|
|
466
|
+
})();
|
|
467
|
+
|
|
468
|
+
const outputVaultAccount = ((): PublicKey => {
|
|
469
|
+
if (isWsol && baseMint.equals(outputMint) && baseVault && !baseVault.equals(PublicKey.default)) {
|
|
470
|
+
return baseVault;
|
|
471
|
+
}
|
|
472
|
+
if (!isWsol && quoteMint.equals(outputMint) && quoteVault && !quoteVault.equals(PublicKey.default)) {
|
|
473
|
+
return quoteVault;
|
|
474
|
+
}
|
|
475
|
+
return getRaydiumCpmmVaultPda(poolState, outputMint);
|
|
476
|
+
})();
|
|
477
|
+
|
|
478
|
+
// Derive observation state
|
|
479
|
+
const observationStateAccount = observationState && !observationState.equals(PublicKey.default)
|
|
480
|
+
? observationState
|
|
481
|
+
: getRaydiumCpmmObservationStatePda(poolState);
|
|
482
|
+
|
|
483
|
+
// Create WSOL ATA for receiving if needed
|
|
484
|
+
if (createOutputMintAta && isWsol) {
|
|
485
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
486
|
+
instructions.push(
|
|
487
|
+
createAssociatedTokenAccountInstruction(
|
|
488
|
+
payerPubkey,
|
|
489
|
+
wsolAta,
|
|
490
|
+
payerPubkey,
|
|
491
|
+
NATIVE_MINT,
|
|
492
|
+
TOKEN_PROGRAM_ID
|
|
493
|
+
)
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Build instruction data
|
|
498
|
+
const data = Buffer.alloc(24);
|
|
499
|
+
RAYDIUM_CPMM_SWAP_BASE_IN_DISCRIMINATOR.copy(data, 0);
|
|
500
|
+
data.writeBigUInt64LE(inputAmount, 8);
|
|
501
|
+
data.writeBigUInt64LE(minimumAmountOut, 16);
|
|
502
|
+
|
|
503
|
+
// Build accounts
|
|
504
|
+
const accounts: AccountMeta[] = [
|
|
505
|
+
{ pubkey: payerPubkey, isSigner: true, isWritable: true },
|
|
506
|
+
{ pubkey: RAYDIUM_CPMM_AUTHORITY, isSigner: false, isWritable: false },
|
|
507
|
+
{ pubkey: ammConfig, isSigner: false, isWritable: false },
|
|
508
|
+
{ pubkey: poolState, isSigner: false, isWritable: true },
|
|
509
|
+
{ pubkey: inputTokenAccount, isSigner: false, isWritable: true },
|
|
510
|
+
{ pubkey: outputTokenAccount, isSigner: false, isWritable: true },
|
|
511
|
+
{ pubkey: inputVaultAccount, isSigner: false, isWritable: true },
|
|
512
|
+
{ pubkey: outputVaultAccount, isSigner: false, isWritable: true },
|
|
513
|
+
{ pubkey: mintTokenProgram, isSigner: false, isWritable: false },
|
|
514
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
515
|
+
{ pubkey: inputMint, isSigner: false, isWritable: false },
|
|
516
|
+
{ pubkey: outputMint, isSigner: false, isWritable: false },
|
|
517
|
+
{ pubkey: observationStateAccount, isSigner: false, isWritable: true },
|
|
518
|
+
];
|
|
519
|
+
|
|
520
|
+
instructions.push(
|
|
521
|
+
new TransactionInstruction({
|
|
522
|
+
keys: accounts,
|
|
523
|
+
programId: RAYDIUM_CPMM_PROGRAM_ID,
|
|
524
|
+
data,
|
|
525
|
+
})
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
// Close WSOL ATA if requested
|
|
529
|
+
if (closeOutputMintAta && isWsol) {
|
|
530
|
+
const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true);
|
|
531
|
+
instructions.push(
|
|
532
|
+
createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Close input token ATA if requested
|
|
537
|
+
if (closeInputMintAta) {
|
|
538
|
+
instructions.push(
|
|
539
|
+
createCloseAccountInstruction(
|
|
540
|
+
inputTokenAccount,
|
|
541
|
+
payerPubkey,
|
|
542
|
+
payerPubkey,
|
|
543
|
+
[],
|
|
544
|
+
mintTokenProgram
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return instructions;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ===== Pool State Decoder - from Rust: src/instruction/utils/raydium_cpmm_types.rs =====
|
|
553
|
+
|
|
554
|
+
export const RAYDIUM_CPMM_POOL_STATE_SIZE = 629;
|
|
555
|
+
|
|
556
|
+
export interface RaydiumCPMMpoolState {
|
|
557
|
+
ammConfig: PublicKey;
|
|
558
|
+
poolCreator: PublicKey;
|
|
559
|
+
token0Vault: PublicKey;
|
|
560
|
+
token1Vault: PublicKey;
|
|
561
|
+
lpMint: PublicKey;
|
|
562
|
+
token0Mint: PublicKey;
|
|
563
|
+
token1Mint: PublicKey;
|
|
564
|
+
token0Program: PublicKey;
|
|
565
|
+
token1Program: PublicKey;
|
|
566
|
+
observationKey: PublicKey;
|
|
567
|
+
authBump: number;
|
|
568
|
+
status: number;
|
|
569
|
+
lpMintDecimals: number;
|
|
570
|
+
mint0Decimals: number;
|
|
571
|
+
mint1Decimals: number;
|
|
572
|
+
lpSupply: bigint;
|
|
573
|
+
protocolFeesToken0: bigint;
|
|
574
|
+
protocolFeesToken1: bigint;
|
|
575
|
+
fundFeesToken0: bigint;
|
|
576
|
+
fundFeesToken1: bigint;
|
|
577
|
+
openTime: bigint;
|
|
578
|
+
recentEpoch: bigint;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Decode a Raydium CPMM pool state from account data
|
|
583
|
+
* 100% from Rust: src/instruction/utils/raydium_cpmm_types.rs pool_state_decode
|
|
584
|
+
*/
|
|
585
|
+
export function decodeRaydiumCPMMpoolState(data: Buffer): RaydiumCPMMpoolState | null {
|
|
586
|
+
if (data.length < RAYDIUM_CPMM_POOL_STATE_SIZE) {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
try {
|
|
591
|
+
let offset = 0;
|
|
592
|
+
|
|
593
|
+
// amm_config: Pubkey
|
|
594
|
+
const ammConfig = new PublicKey(data.subarray(offset, offset + 32));
|
|
595
|
+
offset += 32;
|
|
596
|
+
|
|
597
|
+
// pool_creator: Pubkey
|
|
598
|
+
const poolCreator = new PublicKey(data.subarray(offset, offset + 32));
|
|
599
|
+
offset += 32;
|
|
600
|
+
|
|
601
|
+
// token0_vault: Pubkey
|
|
602
|
+
const token0Vault = new PublicKey(data.subarray(offset, offset + 32));
|
|
603
|
+
offset += 32;
|
|
604
|
+
|
|
605
|
+
// token1_vault: Pubkey
|
|
606
|
+
const token1Vault = new PublicKey(data.subarray(offset, offset + 32));
|
|
607
|
+
offset += 32;
|
|
608
|
+
|
|
609
|
+
// lp_mint: Pubkey
|
|
610
|
+
const lpMint = new PublicKey(data.subarray(offset, offset + 32));
|
|
611
|
+
offset += 32;
|
|
612
|
+
|
|
613
|
+
// token0_mint: Pubkey
|
|
614
|
+
const token0Mint = new PublicKey(data.subarray(offset, offset + 32));
|
|
615
|
+
offset += 32;
|
|
616
|
+
|
|
617
|
+
// token1_mint: Pubkey
|
|
618
|
+
const token1Mint = new PublicKey(data.subarray(offset, offset + 32));
|
|
619
|
+
offset += 32;
|
|
620
|
+
|
|
621
|
+
// token0_program: Pubkey
|
|
622
|
+
const token0Program = new PublicKey(data.subarray(offset, offset + 32));
|
|
623
|
+
offset += 32;
|
|
624
|
+
|
|
625
|
+
// token1_program: Pubkey
|
|
626
|
+
const token1Program = new PublicKey(data.subarray(offset, offset + 32));
|
|
627
|
+
offset += 32;
|
|
628
|
+
|
|
629
|
+
// observation_key: Pubkey
|
|
630
|
+
const observationKey = new PublicKey(data.subarray(offset, offset + 32));
|
|
631
|
+
offset += 32;
|
|
632
|
+
|
|
633
|
+
// auth_bump: u8
|
|
634
|
+
const authBump = data.readUInt8(offset);
|
|
635
|
+
offset += 1;
|
|
636
|
+
|
|
637
|
+
// status: u8
|
|
638
|
+
const status = data.readUInt8(offset);
|
|
639
|
+
offset += 1;
|
|
640
|
+
|
|
641
|
+
// lp_mint_decimals: u8
|
|
642
|
+
const lpMintDecimals = data.readUInt8(offset);
|
|
643
|
+
offset += 1;
|
|
644
|
+
|
|
645
|
+
// mint0_decimals: u8
|
|
646
|
+
const mint0Decimals = data.readUInt8(offset);
|
|
647
|
+
offset += 1;
|
|
648
|
+
|
|
649
|
+
// mint1_decimals: u8
|
|
650
|
+
const mint1Decimals = data.readUInt8(offset);
|
|
651
|
+
offset += 1;
|
|
652
|
+
|
|
653
|
+
// lp_supply: u64
|
|
654
|
+
const lpSupply = data.readBigUInt64LE(offset);
|
|
655
|
+
offset += 8;
|
|
656
|
+
|
|
657
|
+
// protocol_fees_token0: u64
|
|
658
|
+
const protocolFeesToken0 = data.readBigUInt64LE(offset);
|
|
659
|
+
offset += 8;
|
|
660
|
+
|
|
661
|
+
// protocol_fees_token1: u64
|
|
662
|
+
const protocolFeesToken1 = data.readBigUInt64LE(offset);
|
|
663
|
+
offset += 8;
|
|
664
|
+
|
|
665
|
+
// fund_fees_token0: u64
|
|
666
|
+
const fundFeesToken0 = data.readBigUInt64LE(offset);
|
|
667
|
+
offset += 8;
|
|
668
|
+
|
|
669
|
+
// fund_fees_token1: u64
|
|
670
|
+
const fundFeesToken1 = data.readBigUInt64LE(offset);
|
|
671
|
+
offset += 8;
|
|
672
|
+
|
|
673
|
+
// open_time: u64
|
|
674
|
+
const openTime = data.readBigUInt64LE(offset);
|
|
675
|
+
offset += 8;
|
|
676
|
+
|
|
677
|
+
// recent_epoch: u64
|
|
678
|
+
const recentEpoch = data.readBigUInt64LE(offset);
|
|
679
|
+
|
|
680
|
+
return {
|
|
681
|
+
ammConfig,
|
|
682
|
+
poolCreator,
|
|
683
|
+
token0Vault,
|
|
684
|
+
token1Vault,
|
|
685
|
+
lpMint,
|
|
686
|
+
token0Mint,
|
|
687
|
+
token1Mint,
|
|
688
|
+
token0Program,
|
|
689
|
+
token1Program,
|
|
690
|
+
observationKey,
|
|
691
|
+
authBump,
|
|
692
|
+
status,
|
|
693
|
+
lpMintDecimals,
|
|
694
|
+
mint0Decimals,
|
|
695
|
+
mint1Decimals,
|
|
696
|
+
lpSupply,
|
|
697
|
+
protocolFeesToken0,
|
|
698
|
+
protocolFeesToken1,
|
|
699
|
+
fundFeesToken0,
|
|
700
|
+
fundFeesToken1,
|
|
701
|
+
openTime,
|
|
702
|
+
recentEpoch,
|
|
703
|
+
};
|
|
704
|
+
} catch {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ===== Async Fetch Functions - from Rust: src/instruction/utils/raydium_cpmm.rs =====
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Fetch a Raydium CPMM pool state from RPC.
|
|
713
|
+
* 100% from Rust: src/instruction/utils/raydium_cpmm.rs fetch_pool_state
|
|
714
|
+
*/
|
|
715
|
+
export async function fetchRaydiumCPMMpoolState(
|
|
716
|
+
connection: { getAccountInfo: (pubkey: PublicKey) => Promise<{ value?: { data: Buffer } }> },
|
|
717
|
+
poolAddress: PublicKey
|
|
718
|
+
): Promise<RaydiumCPMMpoolState | null> {
|
|
719
|
+
const account = await connection.getAccountInfo(poolAddress);
|
|
720
|
+
if (!account?.value?.data) {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
return decodeRaydiumCPMMpoolState(account.value.data);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Get pool PDA for Raydium CPMM.
|
|
728
|
+
* Seeds: ["pool", amm_config, mint1, mint2]
|
|
729
|
+
*/
|
|
730
|
+
export function getRaydiumCPMMpoolPDA(
|
|
731
|
+
ammConfig: PublicKey,
|
|
732
|
+
mint1: PublicKey,
|
|
733
|
+
mint2: PublicKey
|
|
734
|
+
): PublicKey {
|
|
735
|
+
const POOL_SEED = Buffer.from('pool');
|
|
736
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
737
|
+
[POOL_SEED, ammConfig.toBuffer(), mint1.toBuffer(), mint2.toBuffer()],
|
|
738
|
+
RAYDIUM_CPMM_PROGRAM_ID
|
|
739
|
+
);
|
|
740
|
+
return pda;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Get vault PDA for Raydium CPMM.
|
|
745
|
+
* Seeds: ["pool_vault", pool_state, mint]
|
|
746
|
+
*/
|
|
747
|
+
export function getRaydiumCPMMvaultPDA(poolState: PublicKey, mint: PublicKey): PublicKey {
|
|
748
|
+
const POOL_VAULT_SEED = Buffer.from('pool_vault');
|
|
749
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
750
|
+
[POOL_VAULT_SEED, poolState.toBuffer(), mint.toBuffer()],
|
|
751
|
+
RAYDIUM_CPMM_PROGRAM_ID
|
|
752
|
+
);
|
|
753
|
+
return pda;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Get observation state PDA for Raydium CPMM.
|
|
758
|
+
* Seeds: ["observation", pool_state]
|
|
759
|
+
*/
|
|
760
|
+
export function getRaydiumCPMMobservationStatePDA(poolState: PublicKey): PublicKey {
|
|
761
|
+
const OBSERVATION_STATE_SEED = Buffer.from('observation');
|
|
762
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
763
|
+
[OBSERVATION_STATE_SEED, poolState.toBuffer()],
|
|
764
|
+
RAYDIUM_CPMM_PROGRAM_ID
|
|
765
|
+
);
|
|
766
|
+
return pda;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Get token balances for a Raydium CPMM pool.
|
|
771
|
+
* 100% from Rust: src/instruction/utils/raydium_cpmm.rs get_pool_token_balances
|
|
772
|
+
*/
|
|
773
|
+
export async function getRaydiumCPMMpoolTokenBalances(
|
|
774
|
+
connection: {
|
|
775
|
+
getTokenAccountBalance: (pubkey: PublicKey) => Promise<{ value?: { amount: string } }>
|
|
776
|
+
},
|
|
777
|
+
poolState: PublicKey,
|
|
778
|
+
token0Mint: PublicKey,
|
|
779
|
+
token1Mint: PublicKey
|
|
780
|
+
): Promise<{ token0Balance: bigint; token1Balance: bigint } | null> {
|
|
781
|
+
try {
|
|
782
|
+
const token0Vault = getRaydiumCPMMvaultPDA(poolState, token0Mint);
|
|
783
|
+
const token1Vault = getRaydiumCPMMvaultPDA(poolState, token1Mint);
|
|
784
|
+
|
|
785
|
+
const token0Result = await connection.getTokenAccountBalance(token0Vault);
|
|
786
|
+
const token1Result = await connection.getTokenAccountBalance(token1Vault);
|
|
787
|
+
|
|
788
|
+
const token0Balance = BigInt(token0Result?.value?.amount ?? '0');
|
|
789
|
+
const token1Balance = BigInt(token1Result?.value?.amount ?? '0');
|
|
790
|
+
|
|
791
|
+
return { token0Balance, token1Balance };
|
|
792
|
+
} catch {
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
}
|