100x-sdk 1.0.1

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.
@@ -0,0 +1,171 @@
1
+ const { PublicKey, Transaction, SystemProgram } = require('@solana/web3.js');
2
+ const anchor = require('@coral-xyz/anchor');
3
+ // 统一使用 buffer 包,所有平台一致
4
+ const { Buffer } = require('buffer');
5
+
6
+ /**
7
+ * Parameter Module
8
+ * Handles creation and management of partner parameters
9
+ */
10
+ class ParamModule {
11
+ constructor(sdk) {
12
+ this.sdk = sdk;
13
+ }
14
+
15
+ /**
16
+ * Create partner parameters
17
+ * @param {Object} params - Creation parameters
18
+ * @param {PublicKey} params.partner - Partner public key
19
+ * @returns {Promise<Object>} Object containing transaction, signers and account info
20
+ */
21
+ async createParams({ partner }) {
22
+ console.log('Param Module - CreateParams:', {
23
+ partner: partner.toString()
24
+ });
25
+
26
+ // Calculate Admin account address (globally unique)
27
+ const [adminAccount] = PublicKey.findProgramAddressSync(
28
+ [Buffer.from("admin")],
29
+ this.sdk.programId
30
+ );
31
+
32
+ // Calculate partner parameters account address (using partner address as seed)
33
+ const [paramsAccount] = PublicKey.findProgramAddressSync(
34
+ [Buffer.from("params"), partner.toBuffer()],
35
+ this.sdk.programId
36
+ );
37
+
38
+ console.log('Calculated account addresses:');
39
+ console.log(' Admin account:', adminAccount.toString());
40
+ console.log(' Partner parameters account:', paramsAccount.toString());
41
+
42
+ // Create transaction instructions
43
+ const createParamsIx = await this.sdk.program.methods
44
+ .createParams()
45
+ .accounts({
46
+ partner: partner,
47
+ adminAccount: adminAccount,
48
+ params: paramsAccount,
49
+ systemProgram: SystemProgram.programId,
50
+ })
51
+ .instruction();
52
+
53
+ // Create transaction and add instructions
54
+ const transaction = new Transaction();
55
+ transaction.add(createParamsIx);
56
+
57
+ console.log('Partner parameters creation transaction built');
58
+
59
+ return {
60
+ transaction,
61
+ signers: [], // createParams doesn't need additional signers, only partner signature
62
+ accounts: {
63
+ partner,
64
+ adminAccount,
65
+ paramsAccount
66
+ }
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Get partner parameters account data
72
+ * @param {PublicKey} partner - Partner public key
73
+ * @returns {Promise<Object>} Parameters account data
74
+ */
75
+ async getParams(partner) {
76
+ // 计算合作伙伴参数账户地址
77
+ const [paramsAccount] = PublicKey.findProgramAddressSync(
78
+ [Buffer.from("params"), partner.toBuffer()],
79
+ this.sdk.programId
80
+ );
81
+
82
+ try {
83
+ // Check if account exists
84
+ const accountInfo = await this.sdk.connection.getAccountInfo(paramsAccount);
85
+ if (!accountInfo) {
86
+ throw new Error('Partner parameters account does not exist');
87
+ }
88
+
89
+ // Try to parse data using Anchor
90
+ if (this.sdk.program.account.params) {
91
+ const paramsData = await this.sdk.program.account.params.fetch(paramsAccount);
92
+ return {
93
+ address: paramsAccount,
94
+ data: paramsData
95
+ };
96
+ } else {
97
+ // If Anchor is not available, return basic info
98
+ return {
99
+ address: paramsAccount,
100
+ accountInfo: accountInfo
101
+ };
102
+ }
103
+ } catch (error) {
104
+ throw new Error(`Failed to get partner parameters: ${error.message}`);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Get Admin account data
110
+ * @returns {Promise<Object>} Admin account data
111
+ */
112
+ async getAdmin() {
113
+ // Calculate Admin account address
114
+ const [adminAccount] = PublicKey.findProgramAddressSync(
115
+ [Buffer.from("admin")],
116
+ this.sdk.programId
117
+ );
118
+
119
+ try {
120
+ // 检查账户是否存在
121
+ const accountInfo = await this.sdk.connection.getAccountInfo(adminAccount);
122
+ if (!accountInfo) {
123
+ throw new Error('Admin account does not exist');
124
+ }
125
+
126
+ // 尝试使用 Anchor 解析数据
127
+ if (this.sdk.program.account.admin) {
128
+ const adminData = await this.sdk.program.account.admin.fetch(adminAccount);
129
+ return {
130
+ address: adminAccount,
131
+ data: adminData
132
+ };
133
+ } else {
134
+ // 如果 Anchor 不可用,返回基本信息
135
+ return {
136
+ address: adminAccount,
137
+ accountInfo: accountInfo
138
+ };
139
+ }
140
+ } catch (error) {
141
+ throw new Error(`Failed to get Admin account: ${error.message}`);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Calculate partner parameters account address
147
+ * @param {PublicKey} partner - Partner public key
148
+ * @returns {PublicKey} Parameters account address
149
+ */
150
+ getParamsAddress(partner) {
151
+ const [paramsAccount] = PublicKey.findProgramAddressSync(
152
+ [Buffer.from("params"), partner.toBuffer()],
153
+ this.sdk.programId
154
+ );
155
+ return paramsAccount;
156
+ }
157
+
158
+ /**
159
+ * Calculate Admin account address
160
+ * @returns {PublicKey} Admin account address
161
+ */
162
+ getAdminAddress() {
163
+ const [adminAccount] = PublicKey.findProgramAddressSync(
164
+ [Buffer.from("admin")],
165
+ this.sdk.programId
166
+ );
167
+ return adminAccount;
168
+ }
169
+ }
170
+
171
+ module.exports = ParamModule;
@@ -0,0 +1,326 @@
1
+
2
+
3
+ const { calcLiqTokenBuy, calcLiqTokenSell } = require('./calcLiq');
4
+
5
+ /**
6
+ * Simulate token buy transaction - calculate if target token amount can be purchased
7
+ * 模拟以 Token 数量为目标的买入交易 - 计算是否能买到指定数量的 Token
8
+ * @param {string} mint - Token address 代币地址
9
+ * @param {bigint|string|number} buyTokenAmount - Target token amount to buy 目标购买的 Token 数量
10
+ * @param {string} passOrder - Optional order address to skip (won't be liquidated) 可选的跳过订单地址
11
+ * @param {Object|null} lastPrice - Token price info, default null
12
+ * @param {Object|null} ordersData - Orders response object, default null
13
+ * @returns {Promise<Object>} Token buy simulation result with the following structure:
14
+ * - liqResult: {Object} Complete liquidity calculation result from calcLiqTokenBuy, containing:
15
+ * - free_lp_sol_amount_sum: {bigint} Total available free liquidity SOL amount
16
+ * - free_lp_token_amount_sum: {bigint} Total available free liquidity token amount
17
+ * - lock_lp_sol_amount_sum: {bigint} Total locked liquidity SOL amount
18
+ * - lock_lp_token_amount_sum: {bigint} Total locked liquidity token amount
19
+ * - has_infinite_lp: {boolean} Whether includes infinite liquidity beyond last order
20
+ * - pass_order_id: {number} Index of skipped order in array (-1 if none skipped)
21
+ * - force_close_num: {number} Number of orders that need force closure for target amount
22
+ * - ideal_lp_sol_amount: {bigint} Theoretical minimum SOL required at current price
23
+ * - real_lp_sol_amount: {bigint} Actual SOL required considering real liquidity distribution
24
+ * - completion: {string} Purchase completion percentage as decimal string (e.g., "85.2", "100.0")
25
+ * - slippage: {string} Price slippage percentage as decimal string (e.g., "2.5", "0.8")
26
+ * - suggestedTokenAmount: {string} Recommended token amount to buy based on available liquidity
27
+ * - suggestedSolAmount: {string} Required SOL amount for suggested token purchase
28
+ */
29
+ async function simulateTokenBuy(mint, buyTokenAmount, passOrder = null, lastPrice = null, ordersData = null, curveData = null) {
30
+ // 获取价格和订单数据
31
+
32
+ //console.log('simulateTokenBuy', mint, buyTokenAmount, passOrder, lastPrice, ordersData);
33
+
34
+ let price = lastPrice;
35
+ if (!price) {
36
+ price = await this.sdk.data.price(mint);
37
+ }
38
+
39
+ let orders;
40
+ if (!ordersData) {
41
+ const ordersResponse = await this.sdk.data.orders(mint, {
42
+ type: 'up_orders',
43
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
44
+ });
45
+ // 提取实际的订单数组
46
+ orders = ordersResponse.data.orders;
47
+ } else {
48
+ // 如果传入了 ordersData,从响应对象中提取订单数组并限制长度
49
+ orders = ordersData.data.orders.slice(0, this.sdk.MAX_ORDERS_COUNT + 1);
50
+ }
51
+
52
+ // 获取动态流动池参数(支持外部传入,避免重复请求)
53
+ if (!curveData) {
54
+ try {
55
+ curveData = await this.sdk.chain.getCurveAccount(mint, { skipBalances: true });
56
+ } catch (error) {
57
+ throw new Error(`Failed to get curve account data: ${error.message}`);
58
+ }
59
+ }
60
+
61
+ // 调用 calcLiqTokenBuy 进行流动性计算(传入动态流动池参数)
62
+ try {
63
+ const liqResult = calcLiqTokenBuy(
64
+ price,
65
+ buyTokenAmount,
66
+ orders,
67
+ this.sdk.MAX_ORDERS_COUNT,
68
+ passOrder,
69
+ curveData.initialVirtualSol,
70
+ curveData.initialVirtualToken
71
+ );
72
+
73
+ // console.log("liqResult:",liqResult);
74
+
75
+
76
+ // console.log('\n=== calcLiqTokenBuy 返回结果 ===');
77
+ // console.log('自由流动性 SOL 总量:', liqResult.free_lp_sol_amount_sum.toString());
78
+ // console.log('自由流动性 Token 总量:', liqResult.free_lp_token_amount_sum.toString());
79
+ // console.log('锁定流动性 SOL 总量:', liqResult.lock_lp_sol_amount_sum.toString());
80
+ // console.log('锁定流动性 Token 总量:', liqResult.lock_lp_token_amount_sum.toString());
81
+ // console.log('是否包含无限流动性:', liqResult.has_infinite_lp);
82
+ // console.log('跳过的订单索引:', liqResult.pass_order_id);
83
+ // console.log('需要强平的订单数量:', liqResult.force_close_num);
84
+ // console.log('理想 SOL 使用量:', liqResult.ideal_lp_sol_amount.toString());
85
+ // console.log('实际 SOL 使用量:', liqResult.real_lp_sol_amount.toString());
86
+ // console.log('===============================\n');
87
+
88
+ // Convert to BigInt for calculations
89
+ const buyTokenAmountBig = BigInt(buyTokenAmount);
90
+ const freeTokenAmount = BigInt(liqResult.free_lp_token_amount_sum);
91
+ const realSolAmount = BigInt(liqResult.real_lp_sol_amount);
92
+ const idealSolAmount = BigInt(liqResult.ideal_lp_sol_amount);
93
+
94
+ // 1. Calculate completion percentage
95
+ let completionPercentage;
96
+ if (freeTokenAmount >= buyTokenAmountBig) {
97
+ completionPercentage = "100.0";
98
+ } else {
99
+ const percentage = Math.floor((Number(freeTokenAmount) / Number(buyTokenAmountBig)) * 1000) / 10;
100
+ completionPercentage = percentage.toFixed(1);
101
+ }
102
+
103
+ // 2. Calculate slippage percentage and get final SOL amount
104
+ let slippagePercentage;
105
+ let suggestedLiquidity;
106
+ let finalRealSolAmount = realSolAmount;
107
+
108
+ if (realSolAmount > 0n) {
109
+ // Normal case: calculate slippage
110
+ const diff = idealSolAmount > realSolAmount ? idealSolAmount - realSolAmount : realSolAmount - idealSolAmount;
111
+ const slippage = Math.floor((Number(diff) / Number(idealSolAmount)) * 1000) / 10;
112
+ slippagePercentage = slippage.toFixed(1);
113
+ } else {
114
+ // Special case: real SOL amount is 0, need to recalculate with suggested liquidity
115
+ const suggestedAmount = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
116
+
117
+ const recalcResult = calcLiqTokenBuy(
118
+ price,
119
+ suggestedAmount,
120
+ orders,
121
+ this.sdk.MAX_ORDERS_COUNT,
122
+ passOrder,
123
+ curveData.initialVirtualSol,
124
+ curveData.initialVirtualToken
125
+ );
126
+
127
+ const recalcRealSol = BigInt(recalcResult.real_lp_sol_amount);
128
+ const recalcIdealSol = BigInt(recalcResult.ideal_lp_sol_amount);
129
+
130
+ if (recalcRealSol <= 0n) {
131
+ throw new Error('Recalculated real SOL amount should be greater than 0');
132
+ }
133
+
134
+ finalRealSolAmount = recalcRealSol;
135
+
136
+ const diff = recalcIdealSol > recalcRealSol ? recalcIdealSol - recalcRealSol : recalcRealSol - recalcIdealSol;
137
+ const slippage = Math.floor((Number(diff) / Number(recalcIdealSol)) * 1000) / 10;
138
+ slippagePercentage = slippage.toFixed(1);
139
+ }
140
+
141
+ // 3. Calculate suggested liquidity
142
+ if (completionPercentage === "100.0") {
143
+ suggestedLiquidity = buyTokenAmountBig.toString();
144
+ } else {
145
+ const suggested = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
146
+ suggestedLiquidity = suggested.toString();
147
+ }
148
+
149
+ return {
150
+ liqResult,
151
+ completion: completionPercentage,
152
+ slippage: slippagePercentage,
153
+ suggestedTokenAmount: suggestedLiquidity,
154
+ suggestedSolAmount: finalRealSolAmount.toString()
155
+ };
156
+ } catch (error) {
157
+ console.error('calcLiqTokenBuy 调用失败:', error);
158
+ throw error;
159
+ }
160
+ }
161
+
162
+
163
+ /**
164
+ * Simulate token sell transaction analysis
165
+ * @param {string} mint - Token address
166
+ * @param {bigint|string|number} sellTokenAmount - Token amount to sell (u64 format, precision 10^9)
167
+ * @param {string} passOrder - Optional order address to skip (won't be liquidated) 可选的跳过订单地址
168
+ * @param {Object|null} lastPrice - Token price info, default null
169
+ * @param {Object|null} ordersData - Orders response object, default null
170
+ * @returns {Promise<Object>} Token sell simulation result with the following structure:
171
+ * - liqResult: {Object} Complete liquidity calculation result from calcLiqTokenSell, containing:
172
+ * - free_lp_sol_amount_sum: {bigint} Total available free liquidity SOL obtainable from selling
173
+ * - free_lp_token_amount_sum: {bigint} Maximum tokens sellable without force closing orders
174
+ * - lock_lp_sol_amount_sum: {bigint} Total locked liquidity SOL amount (excluding skipped orders)
175
+ * - lock_lp_token_amount_sum: {bigint} Total locked liquidity token amount (excluding skipped orders)
176
+ * - has_infinite_lp: {boolean} Whether includes infinite liquidity to minimum price
177
+ * - pass_order_id: {number} Index of skipped order in array (-1 if none skipped)
178
+ * - force_close_num: {number} Number of orders that need force closure for target sell amount
179
+ * - ideal_lp_sol_amount: {bigint} Theoretical maximum SOL obtainable at current price
180
+ * - real_lp_sol_amount: {bigint} Actual SOL obtainable considering real liquidity distribution
181
+ * - completion: {string} Sell completion percentage as decimal string (e.g., "85.2", "100.0")
182
+ * - slippage: {string} Price slippage percentage as decimal string (e.g., "2.5", "0.8")
183
+ * - suggestedTokenAmount: {string} Recommended token amount to sell based on available liquidity
184
+ * - suggestedSolAmount: {string} Expected SOL amount from suggested token sale
185
+ */
186
+ async function simulateTokenSell(mint, sellTokenAmount, passOrder = null, lastPrice = null, ordersData = null, curveData = null) {
187
+ // 获取价格和订单数据
188
+ let price = lastPrice;
189
+ if (!price) {
190
+ price = await this.sdk.data.price(mint);
191
+ }
192
+
193
+ //console.log("price=",price);
194
+
195
+ let orders;
196
+ if (!ordersData) {
197
+ const ordersResponse = await this.sdk.data.orders(mint, {
198
+ type: 'down_orders',
199
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
200
+ });
201
+ // 提取实际的订单数组
202
+ orders = ordersResponse.data.orders;
203
+ } else {
204
+ // 如果传入了 ordersData,从响应对象中提取订单数组并限制长度
205
+ orders = ordersData.data.orders.slice(0, this.sdk.MAX_ORDERS_COUNT + 1);
206
+ }
207
+
208
+ // 获取动态流动池参数(支持外部传入,避免重复请求)
209
+ if (!curveData) {
210
+ try {
211
+ curveData = await this.sdk.chain.getCurveAccount(mint, { skipBalances: true });
212
+ } catch (error) {
213
+ throw new Error(`Failed to get curve account data: ${error.message}`);
214
+ }
215
+ }
216
+
217
+ // console.log('simulateTokenSell 获取的数据:');
218
+ // console.log('价格:', price);
219
+ // console.log('订单数量:', orders.length);
220
+ // orders.forEach((order, i) => console.log(`订单${i}: start=${order.lock_lp_start_price}, end=${order.lock_lp_end_price}, sol=${order.lock_lp_sol_amount}, token=${order.lock_lp_token_amount}`));
221
+
222
+ // 调用 calcLiqTokenSell 进行流动性计算(传入动态流动池参数)
223
+ try {
224
+ const liqResult = calcLiqTokenSell(
225
+ price,
226
+ sellTokenAmount,
227
+ orders,
228
+ this.sdk.MAX_ORDERS_COUNT,
229
+ passOrder,
230
+ curveData.initialVirtualSol,
231
+ curveData.initialVirtualToken
232
+ );
233
+
234
+ // console.log('\n=== calcLiqTokenSell 返回结果 ===');
235
+ // console.log('自由流动性 SOL 总量:', liqResult.free_lp_sol_amount_sum.toString());
236
+ // console.log('自由流动性 Token 总量:', liqResult.free_lp_token_amount_sum.toString());
237
+ // console.log('锁定流动性 SOL 总量:', liqResult.lock_lp_sol_amount_sum.toString());
238
+ // console.log('锁定流动性 Token 总量:', liqResult.lock_lp_token_amount_sum.toString());
239
+ // console.log('是否包含无限流动性:', liqResult.has_infinite_lp);
240
+ // console.log('跳过的订单索引:', liqResult.pass_order_id);
241
+ // console.log('需要强平的订单数量:', liqResult.force_close_num);
242
+ // console.log('理想 SOL 获得量:', liqResult.ideal_lp_sol_amount.toString());
243
+ // console.log('实际 SOL 获得量:', liqResult.real_lp_sol_amount.toString());
244
+ // console.log('===============================\n');
245
+
246
+ // Convert to BigInt for calculations
247
+ const sellTokenAmountBig = BigInt(sellTokenAmount);
248
+ const freeTokenAmount = BigInt(liqResult.free_lp_token_amount_sum);
249
+ const realSolAmount = BigInt(liqResult.real_lp_sol_amount);
250
+ const idealSolAmount = BigInt(liqResult.ideal_lp_sol_amount);
251
+
252
+ // 1. Calculate completion percentage
253
+ let completionPercentage;
254
+ if (freeTokenAmount >= sellTokenAmountBig) {
255
+ completionPercentage = "100.0";
256
+ } else {
257
+ const percentage = Math.floor((Number(freeTokenAmount) / Number(sellTokenAmountBig)) * 1000) / 10;
258
+ completionPercentage = percentage.toFixed(1);
259
+ }
260
+
261
+ // 2. Calculate slippage percentage and get final SOL amount
262
+ let slippagePercentage;
263
+ let suggestedLiquidity;
264
+ let finalRealSolAmount = realSolAmount;
265
+
266
+ if (realSolAmount > 0n) {
267
+ // Normal case: calculate slippage
268
+ const diff = idealSolAmount > realSolAmount ? idealSolAmount - realSolAmount : realSolAmount - idealSolAmount;
269
+ const slippage = Math.floor((Number(diff) / Number(idealSolAmount)) * 1000) / 10;
270
+ slippagePercentage = slippage.toFixed(1);
271
+ } else {
272
+ // Special case: real SOL amount is 0, need to recalculate with suggested liquidity
273
+ const suggestedAmount = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
274
+
275
+ const recalcResult = calcLiqTokenSell(
276
+ price,
277
+ suggestedAmount,
278
+ orders,
279
+ this.sdk.MAX_ORDERS_COUNT,
280
+ passOrder,
281
+ curveData.initialVirtualSol,
282
+ curveData.initialVirtualToken
283
+ );
284
+
285
+ const recalcRealSol = BigInt(recalcResult.real_lp_sol_amount);
286
+ const recalcIdealSol = BigInt(recalcResult.ideal_lp_sol_amount);
287
+
288
+ if (recalcRealSol <= 0n) {
289
+ throw new Error('Recalculated real SOL amount should be greater than 0');
290
+ }
291
+
292
+ finalRealSolAmount = recalcRealSol;
293
+
294
+ const diff = recalcIdealSol > recalcRealSol ? recalcIdealSol - recalcRealSol : recalcRealSol - recalcIdealSol;
295
+ const slippage = Math.floor((Number(diff) / Number(recalcIdealSol)) * 1000) / 10;
296
+ slippagePercentage = slippage.toFixed(1);
297
+ }
298
+
299
+ // 3. Calculate suggested liquidity
300
+ if (completionPercentage === "100.0") {
301
+ suggestedLiquidity = sellTokenAmountBig.toString();
302
+ } else {
303
+ const suggested = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
304
+ suggestedLiquidity = suggested.toString();
305
+ }
306
+
307
+ return {
308
+ liqResult,
309
+ completion: completionPercentage,
310
+ slippage: slippagePercentage,
311
+ suggestedTokenAmount: suggestedLiquidity,
312
+ suggestedSolAmount: finalRealSolAmount.toString()
313
+ };
314
+ } catch (error) {
315
+ console.error('calcLiqTokenSell 调用失败:', error.message);
316
+ throw error;
317
+ }
318
+ }
319
+
320
+
321
+
322
+ module.exports = {
323
+ simulateTokenBuy,
324
+ simulateTokenSell
325
+ };
326
+