@shogun-sdk/intents-sdk 1.2.5 → 1.2.6-test
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 +3 -19
- package/dist/esm/chains.js +3 -0
- package/dist/esm/chains.js.map +1 -1
- package/dist/esm/constants.js +9 -0
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/core/evm/chain-provider.js +2 -1
- package/dist/esm/core/evm/chain-provider.js.map +1 -1
- package/dist/esm/core/evm/cross-chain-limit-order.js +1 -1
- package/dist/esm/core/evm/cross-chain-limit-order.js.map +1 -1
- package/dist/esm/core/evm/permit2-signature-transfer.js +15 -0
- package/dist/esm/core/evm/permit2-signature-transfer.js.map +1 -0
- package/dist/esm/core/evm/single-chain-dca-order.js +1 -1
- package/dist/esm/core/evm/single-chain-dca-order.js.map +1 -1
- package/dist/esm/core/evm/single-chain-limit-order.js +1 -1
- package/dist/esm/core/evm/single-chain-limit-order.js.map +1 -1
- package/dist/esm/core/solana/dca/single-chain-dca-order.js +3 -2
- package/dist/esm/core/solana/dca/single-chain-dca-order.js.map +1 -1
- package/dist/esm/core/solana/dca/single-chain-limit-order.js +3 -2
- package/dist/esm/core/solana/dca/single-chain-limit-order.js.map +1 -1
- package/dist/esm/core/solana/utils.js +14 -4
- package/dist/esm/core/solana/utils.js.map +1 -1
- package/dist/esm/core/sui/single-chain-dca-order.js +2 -1
- package/dist/esm/core/sui/single-chain-dca-order.js.map +1 -1
- package/dist/esm/core/sui/single-chain-limit-order.js +2 -1
- package/dist/esm/core/sui/single-chain-limit-order.js.map +1 -1
- package/dist/esm/index.js +6 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/token-list.js +2 -0
- package/dist/esm/types/token-list.js.map +1 -0
- package/dist/esm/utils/defillama.js +1 -0
- package/dist/esm/utils/defillama.js.map +1 -1
- package/dist/esm/utils/quote/aggregator.js +158 -42
- package/dist/esm/utils/quote/aggregator.js.map +1 -1
- package/dist/esm/utils/quote/paraswap.js +1 -0
- package/dist/esm/utils/quote/paraswap.js.map +1 -1
- package/dist/esm/utils/quote/pumpfun/estimations.js +169 -0
- package/dist/esm/utils/quote/pumpfun/estimations.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/estimations_amm.js +130 -0
- package/dist/esm/utils/quote/pumpfun/estimations_amm.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/index.js +198 -0
- package/dist/esm/utils/quote/pumpfun/index.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/models.js +70 -0
- package/dist/esm/utils/quote/pumpfun/models.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/utils.js +269 -0
- package/dist/esm/utils/quote/pumpfun/utils.js.map +1 -0
- package/dist/esm/utils/quote/raydium.js +336 -0
- package/dist/esm/utils/quote/raydium.js.map +1 -0
- package/dist/esm/utils/quote/stablecoins-tokens.js +1 -0
- package/dist/esm/utils/quote/stablecoins-tokens.js.map +1 -1
- package/dist/esm/utils/quote/utils.js +8 -0
- package/dist/esm/utils/quote/utils.js.map +1 -0
- package/dist/esm/utils/tokens/index.js +37 -0
- package/dist/esm/utils/tokens/index.js.map +1 -0
- package/dist/types/chains.d.ts +4 -2
- package/dist/types/chains.d.ts.map +1 -1
- package/dist/types/constants.d.ts +2 -1
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/core/evm/chain-provider.d.ts.map +1 -1
- package/dist/types/core/evm/permit2-signature-transfer.d.ts +3 -0
- package/dist/types/core/evm/permit2-signature-transfer.d.ts.map +1 -0
- package/dist/types/core/solana/dca/single-chain-dca-order.d.ts +1 -2
- package/dist/types/core/solana/dca/single-chain-dca-order.d.ts.map +1 -1
- package/dist/types/core/solana/dca/single-chain-limit-order.d.ts +1 -2
- package/dist/types/core/solana/dca/single-chain-limit-order.d.ts.map +1 -1
- package/dist/types/core/solana/utils.d.ts +2 -0
- package/dist/types/core/solana/utils.d.ts.map +1 -1
- package/dist/types/core/sui/single-chain-dca-order.d.ts.map +1 -1
- package/dist/types/core/sui/single-chain-limit-order.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types/token-list.d.ts +30 -0
- package/dist/types/types/token-list.d.ts.map +1 -0
- package/dist/types/utils/defillama.d.ts +1 -0
- package/dist/types/utils/defillama.d.ts.map +1 -1
- package/dist/types/utils/quote/aggregator.d.ts +42 -5
- package/dist/types/utils/quote/aggregator.d.ts.map +1 -1
- package/dist/types/utils/quote/paraswap.d.ts.map +1 -1
- package/dist/types/utils/quote/pumpfun/estimations.d.ts +30 -0
- package/dist/types/utils/quote/pumpfun/estimations.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/estimations_amm.d.ts +49 -0
- package/dist/types/utils/quote/pumpfun/estimations_amm.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/index.d.ts +10 -0
- package/dist/types/utils/quote/pumpfun/index.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/models.d.ts +94 -0
- package/dist/types/utils/quote/pumpfun/models.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/utils.d.ts +50 -0
- package/dist/types/utils/quote/pumpfun/utils.d.ts.map +1 -0
- package/dist/types/utils/quote/raydium.d.ts +98 -0
- package/dist/types/utils/quote/raydium.d.ts.map +1 -0
- package/dist/types/utils/quote/stablecoins-tokens.d.ts.map +1 -1
- package/dist/types/utils/quote/utils.d.ts +5 -0
- package/dist/types/utils/quote/utils.d.ts.map +1 -0
- package/dist/types/utils/tokens/index.d.ts +20 -0
- package/dist/types/utils/tokens/index.d.ts.map +1 -0
- package/package.json +20 -18
- package/src/chains.ts +3 -0
- package/src/constants.ts +12 -2
- package/src/core/evm/chain-provider.ts +2 -1
- package/src/core/evm/cross-chain-limit-order.ts +1 -1
- package/src/core/evm/permit2-signature-transfer.ts +15 -0
- package/src/core/evm/single-chain-dca-order.ts +1 -1
- package/src/core/evm/single-chain-limit-order.ts +1 -1
- package/src/core/solana/dca/single-chain-dca-order.ts +3 -3
- package/src/core/solana/dca/single-chain-limit-order.ts +3 -2
- package/src/core/solana/utils.ts +24 -12
- package/src/core/sui/single-chain-dca-order.ts +2 -2
- package/src/core/sui/single-chain-limit-order.ts +2 -2
- package/src/index.ts +14 -4
- package/src/types/token-list.ts +35 -0
- package/src/utils/defillama.ts +1 -0
- package/src/utils/quote/aggregator.ts +210 -55
- package/src/utils/quote/paraswap.ts +1 -0
- package/src/utils/quote/pumpfun/estimations.ts +206 -0
- package/src/utils/quote/pumpfun/estimations_amm.ts +165 -0
- package/src/utils/quote/pumpfun/index.ts +266 -0
- package/src/utils/quote/pumpfun/models.ts +163 -0
- package/src/utils/quote/pumpfun/utils.ts +343 -0
- package/src/utils/quote/raydium.ts +506 -0
- package/src/utils/quote/stablecoins-tokens.ts +1 -0
- package/src/utils/quote/utils.ts +8 -0
- package/src/utils/tokens/index.ts +38 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import {
|
|
2
|
+
address,
|
|
3
|
+
fetchEncodedAccount,
|
|
4
|
+
fetchEncodedAccounts,
|
|
5
|
+
getAddressEncoder,
|
|
6
|
+
getProgramDerivedAddress,
|
|
7
|
+
unwrapOption,
|
|
8
|
+
type Address,
|
|
9
|
+
} from '@solana/kit';
|
|
10
|
+
import { getDefaultSolanaRPC } from '../../../core/solana/client.js';
|
|
11
|
+
import {
|
|
12
|
+
getBondingCurveDecoder,
|
|
13
|
+
getGlobalAccountDecoder,
|
|
14
|
+
getGlobalConfigDecoder,
|
|
15
|
+
getPumpPoolDataDecoder,
|
|
16
|
+
type BondingCurve,
|
|
17
|
+
type GlobalAccount,
|
|
18
|
+
type GlobalConfig,
|
|
19
|
+
type PumpPoolData,
|
|
20
|
+
type TokenAccount,
|
|
21
|
+
} from './models.js';
|
|
22
|
+
import { getTokenDecoder } from '@solana-program/token';
|
|
23
|
+
import { ONE_DAY_MS, TEN_MINUTES_MS, TWO_SECONDS_MS } from '../utils.js';
|
|
24
|
+
|
|
25
|
+
export const PUMP_FUN_PROGRAM_ADDRESS = address('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P');
|
|
26
|
+
export const PUMP_FUN_AMM_PROGRAM_ADDRESS = address('pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA');
|
|
27
|
+
export const WRAPPED_NATIVE_TOKEN_PUBKEY = address('So11111111111111111111111111111111111111112');
|
|
28
|
+
export const GLOBAL_CONFIG_AMM = address('ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw');
|
|
29
|
+
export const ANCHOR_DISCRIMINATOR_SIZE = 8;
|
|
30
|
+
|
|
31
|
+
let globalAccountCache: { data: [GlobalAccount, Address]; expiresAt: number } | null = null;
|
|
32
|
+
let globalConfigCache: { data: [GlobalConfig, Address]; expiresAt: number } | null = null;
|
|
33
|
+
const migratedPoolCache = new Map<string, { data: [PumpPoolData, Address]; expiresAt: number }>();
|
|
34
|
+
const poolTokensInfoCache = new Map<string, { data: [TokenAccount, TokenAccount]; expiresAt: number }>();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Finds the PDA for a PumpFun bonding curve
|
|
38
|
+
*/
|
|
39
|
+
async function findBondingCurvePda(tokenMint: Address): Promise<Address> {
|
|
40
|
+
const [pda] = await getProgramDerivedAddress({
|
|
41
|
+
programAddress: PUMP_FUN_PROGRAM_ADDRESS,
|
|
42
|
+
seeds: [new TextEncoder().encode('bonding-curve'), getAddressEncoder().encode(tokenMint)],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return pda;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Finds the PDA for the PumpFun global account
|
|
50
|
+
*/
|
|
51
|
+
async function findGlobalAccountPda(): Promise<Address> {
|
|
52
|
+
const [pda] = await getProgramDerivedAddress({
|
|
53
|
+
programAddress: PUMP_FUN_PROGRAM_ADDRESS,
|
|
54
|
+
seeds: [new TextEncoder().encode('global')],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return pda;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Finds the PDA for pool authority in the AMM program
|
|
62
|
+
*/
|
|
63
|
+
async function findPoolAuthorityPdaAmm(mint: Address): Promise<Address> {
|
|
64
|
+
const [pda] = await getProgramDerivedAddress({
|
|
65
|
+
programAddress: PUMP_FUN_PROGRAM_ADDRESS,
|
|
66
|
+
seeds: [new TextEncoder().encode('pool-authority'), getAddressEncoder().encode(mint)],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return pda;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Finds the PDA for a pool in the AMM program
|
|
74
|
+
*/
|
|
75
|
+
async function findPoolPdaAmm(poolAuthority: Address, baseMint: Address, quoteMint: Address): Promise<Address> {
|
|
76
|
+
const [pda] = await getProgramDerivedAddress({
|
|
77
|
+
programAddress: PUMP_FUN_AMM_PROGRAM_ADDRESS,
|
|
78
|
+
seeds: [
|
|
79
|
+
new TextEncoder().encode('pool'),
|
|
80
|
+
new Uint8Array([0, 0]), // Pool type bytes
|
|
81
|
+
getAddressEncoder().encode(poolAuthority),
|
|
82
|
+
getAddressEncoder().encode(baseMint),
|
|
83
|
+
getAddressEncoder().encode(quoteMint),
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return pda;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fetches and deserializes a PumpFun bonding curve account
|
|
92
|
+
*
|
|
93
|
+
* @param tokenMint - The token mint address
|
|
94
|
+
* @param options - Optional RPC configuration
|
|
95
|
+
* @returns Tuple of [BondingCurve data, PDA address]
|
|
96
|
+
* @throws Error if account doesn't exist or deserialization fails
|
|
97
|
+
*/
|
|
98
|
+
export async function getPumpFunBondingCurve(tokenMint: Address): Promise<[BondingCurve, Address]> {
|
|
99
|
+
const rpc = getDefaultSolanaRPC();
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Derive the PDA for the bonding curve
|
|
103
|
+
const pda = await findBondingCurvePda(tokenMint);
|
|
104
|
+
|
|
105
|
+
// Fetch the account data
|
|
106
|
+
const encodedAccount = await fetchEncodedAccount(rpc, pda);
|
|
107
|
+
|
|
108
|
+
if (!encodedAccount.exists) {
|
|
109
|
+
throw new Error(`Bonding curve account not found for token mint: ${tokenMint}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Anchor accounts have an 8-byte discriminator prefix
|
|
113
|
+
const dataWithoutDiscriminator = encodedAccount.data.slice(8);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const decoder = getBondingCurveDecoder();
|
|
117
|
+
const bondingCurve = decoder.decode(dataWithoutDiscriminator);
|
|
118
|
+
|
|
119
|
+
return [bondingCurve, pda];
|
|
120
|
+
} catch (decodingError) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Failed to deserialize bonding curve: ${decodingError instanceof Error ? decodingError.message : String(decodingError)}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw new Error(`Failed to fetch bonding curve account: ${error instanceof Error ? error.message : String(error)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Fetches and deserializes the PumpFun global account
|
|
132
|
+
*
|
|
133
|
+
* @returns Tuple of [GlobalAccount data, PDA address]
|
|
134
|
+
* @throws {Error} if account doesn't exist
|
|
135
|
+
* @throws {Error} if deserialization fails
|
|
136
|
+
*/
|
|
137
|
+
export async function getPumpFunGlobalAccount(): Promise<[GlobalAccount, Address]> {
|
|
138
|
+
// Return cached value if valid
|
|
139
|
+
if (globalAccountCache && Date.now() < globalAccountCache.expiresAt) {
|
|
140
|
+
return globalAccountCache.data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const rpc = getDefaultSolanaRPC();
|
|
144
|
+
const pda = await findGlobalAccountPda();
|
|
145
|
+
|
|
146
|
+
const encodedAccount = await fetchEncodedAccount(rpc, pda);
|
|
147
|
+
|
|
148
|
+
if (!encodedAccount.exists) {
|
|
149
|
+
throw new Error(`GlobalAccount not found: ${pda}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Validate minimum size
|
|
153
|
+
if (encodedAccount.data.length < ANCHOR_DISCRIMINATOR_SIZE) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`GlobalAccount deserialization failed: Account data too small: ${encodedAccount.data.length} bytes`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Skip Anchor's 8-byte discriminator
|
|
160
|
+
const dataWithoutDiscriminator = encodedAccount.data.slice(ANCHOR_DISCRIMINATOR_SIZE);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const decoder = getGlobalAccountDecoder();
|
|
164
|
+
const globalAccount = decoder.decode(dataWithoutDiscriminator);
|
|
165
|
+
|
|
166
|
+
// Update cache
|
|
167
|
+
globalAccountCache = {
|
|
168
|
+
data: [globalAccount, pda],
|
|
169
|
+
expiresAt: Date.now() + TEN_MINUTES_MS,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return [globalAccount, pda];
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw new Error(`GlobalAccount deserialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Fetches and deserializes the PumpFun global config account
|
|
180
|
+
*
|
|
181
|
+
* @returns Tuple of [GlobalConfig data, PDA address]
|
|
182
|
+
* @throws {Error} if account doesn't exist
|
|
183
|
+
* @throws {Error} if deserialization fails
|
|
184
|
+
*/
|
|
185
|
+
export async function getPumpFunGlobalConfig(): Promise<[GlobalConfig, Address]> {
|
|
186
|
+
// Return cached value if valid
|
|
187
|
+
if (globalConfigCache && Date.now() < globalConfigCache.expiresAt) {
|
|
188
|
+
return globalConfigCache.data;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const rpc = getDefaultSolanaRPC();
|
|
192
|
+
|
|
193
|
+
const encodedAccount = await fetchEncodedAccount(rpc, GLOBAL_CONFIG_AMM);
|
|
194
|
+
|
|
195
|
+
if (!encodedAccount.exists) {
|
|
196
|
+
throw new Error(`GlobalConfig account not found: ${GLOBAL_CONFIG_AMM}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Validate minimum size
|
|
200
|
+
if (encodedAccount.data.length < ANCHOR_DISCRIMINATOR_SIZE) {
|
|
201
|
+
throw new Error(`GlobalConfig deserialization failed: Account data too small: ${encodedAccount.data.length} bytes`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Skip Anchor's 8-byte discriminator
|
|
205
|
+
const dataWithoutDiscriminator = encodedAccount.data.slice(ANCHOR_DISCRIMINATOR_SIZE);
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const decoder = getGlobalConfigDecoder();
|
|
209
|
+
const globalConfig = decoder.decode(dataWithoutDiscriminator);
|
|
210
|
+
|
|
211
|
+
// Update cache
|
|
212
|
+
globalConfigCache = {
|
|
213
|
+
data: [globalConfig, GLOBAL_CONFIG_AMM],
|
|
214
|
+
expiresAt: Date.now() + TEN_MINUTES_MS,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return [globalConfig, GLOBAL_CONFIG_AMM];
|
|
218
|
+
} catch (error) {
|
|
219
|
+
throw new Error(`GlobalConfig deserialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Fetches the migrated AMM pool for a token
|
|
225
|
+
*
|
|
226
|
+
* @param baseMint - The token mint address (NOT WSOL)
|
|
227
|
+
* @returns Tuple of [PumpPoolData, pool PDA address]
|
|
228
|
+
* @throws Error if pool doesn't exist or deserialization fails
|
|
229
|
+
*/
|
|
230
|
+
export async function getPumpFunMigratedPool(baseMint: Address): Promise<[PumpPoolData, Address]> {
|
|
231
|
+
// 2s in-memory cache
|
|
232
|
+
const cacheKey = String(baseMint);
|
|
233
|
+
const cached = migratedPoolCache.get(cacheKey);
|
|
234
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
235
|
+
return cached.data;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const rpc = getDefaultSolanaRPC();
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
const poolAuthorityPda = await findPoolAuthorityPdaAmm(baseMint);
|
|
242
|
+
const poolPda = await findPoolPdaAmm(poolAuthorityPda, baseMint, WRAPPED_NATIVE_TOKEN_PUBKEY);
|
|
243
|
+
|
|
244
|
+
const encodedAccount = await fetchEncodedAccount(rpc, poolPda);
|
|
245
|
+
|
|
246
|
+
if (!encodedAccount.exists) {
|
|
247
|
+
throw new Error(`Migrated pool not found for token mint: ${baseMint}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const dataWithoutDiscriminator = encodedAccount.data.slice(ANCHOR_DISCRIMINATOR_SIZE);
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const decoder = getPumpPoolDataDecoder();
|
|
254
|
+
const poolData = decoder.decode(dataWithoutDiscriminator);
|
|
255
|
+
|
|
256
|
+
// Update short-lived cache
|
|
257
|
+
migratedPoolCache.set(cacheKey, {
|
|
258
|
+
data: [poolData, poolPda],
|
|
259
|
+
expiresAt: Date.now() + ONE_DAY_MS,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return [poolData, poolPda];
|
|
263
|
+
} catch (decodingError) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Failed to deserialize pool data: ${decodingError instanceof Error ? decodingError.message : String(decodingError)}`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
} catch (error) {
|
|
269
|
+
throw new Error(`Failed to fetch migrated pool: ${error instanceof Error ? error.message : String(error)}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Fetches and deserializes token account information for pool base and quote tokens
|
|
275
|
+
*
|
|
276
|
+
* @param poolBaseTokenAccount - Base token account public key
|
|
277
|
+
* @param poolQuoteTokenAccount - Quote token account public key
|
|
278
|
+
* @returns Tuple containing [baseTokenAccount, quoteTokenAccount]
|
|
279
|
+
* @throws Error if accounts don't exist or deserialization fails
|
|
280
|
+
*/
|
|
281
|
+
export async function getPoolTokensInfo(
|
|
282
|
+
poolBaseTokenAccount: Address,
|
|
283
|
+
poolQuoteTokenAccount: Address,
|
|
284
|
+
): Promise<[TokenAccount, TokenAccount]> {
|
|
285
|
+
// 2s in-memory cache
|
|
286
|
+
const cacheKey = `${poolBaseTokenAccount}-${poolQuoteTokenAccount}`;
|
|
287
|
+
const cached = poolTokensInfoCache.get(cacheKey);
|
|
288
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
289
|
+
return cached.data;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const rpc = getDefaultSolanaRPC();
|
|
293
|
+
|
|
294
|
+
const accountsInfo = await fetchEncodedAccounts(rpc, [poolBaseTokenAccount, poolQuoteTokenAccount]);
|
|
295
|
+
|
|
296
|
+
// Validate base token account exists
|
|
297
|
+
const baseAccountInfo = accountsInfo[0];
|
|
298
|
+
if (!baseAccountInfo || !baseAccountInfo.exists) {
|
|
299
|
+
throw new Error(`Base token account not found: ${poolBaseTokenAccount}`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Validate quote token account exists
|
|
303
|
+
const quoteAccountInfo = accountsInfo[1];
|
|
304
|
+
if (!quoteAccountInfo || !quoteAccountInfo.exists) {
|
|
305
|
+
throw new Error(`Quote token account not found: ${poolQuoteTokenAccount}`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const tokenDecoder = getTokenDecoder();
|
|
309
|
+
|
|
310
|
+
// Deserialize base token account
|
|
311
|
+
const baseDecoded = tokenDecoder.decode(baseAccountInfo.data);
|
|
312
|
+
const baseTokenAccount: TokenAccount = {
|
|
313
|
+
mint: baseDecoded.mint,
|
|
314
|
+
owner: baseDecoded.owner,
|
|
315
|
+
amount: baseDecoded.amount,
|
|
316
|
+
delegate: unwrapOption(baseDecoded.delegate),
|
|
317
|
+
state: baseDecoded.state,
|
|
318
|
+
isNative: unwrapOption(baseDecoded.isNative),
|
|
319
|
+
delegatedAmount: baseDecoded.delegatedAmount,
|
|
320
|
+
closeAuthority: unwrapOption(baseDecoded.closeAuthority),
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// Deserialize quote token account
|
|
324
|
+
const quoteDecoded = tokenDecoder.decode(quoteAccountInfo.data);
|
|
325
|
+
const quoteTokenAccount: TokenAccount = {
|
|
326
|
+
mint: quoteDecoded.mint,
|
|
327
|
+
owner: quoteDecoded.owner,
|
|
328
|
+
amount: quoteDecoded.amount,
|
|
329
|
+
delegate: unwrapOption(quoteDecoded.delegate),
|
|
330
|
+
state: quoteDecoded.state,
|
|
331
|
+
isNative: unwrapOption(quoteDecoded.isNative),
|
|
332
|
+
delegatedAmount: quoteDecoded.delegatedAmount,
|
|
333
|
+
closeAuthority: unwrapOption(quoteDecoded.closeAuthority),
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Update short-lived cache
|
|
337
|
+
poolTokensInfoCache.set(cacheKey, {
|
|
338
|
+
data: [baseTokenAccount, quoteTokenAccount],
|
|
339
|
+
expiresAt: Date.now() + TWO_SECONDS_MS,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
return [baseTokenAccount, quoteTokenAccount];
|
|
343
|
+
}
|