pinpet-sdk 0.1.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,300 @@
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) {
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
+ // console.log('simulateTokenBuy 获取的数据:');
53
+ // console.log('价格:', price);
54
+ // console.log('订单数量:', orders.length);
55
+ // 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}`));
56
+
57
+ // 调用 calcLiqTokenBuy 进行流动性计算
58
+ try {
59
+ const liqResult = calcLiqTokenBuy(
60
+ price,
61
+ buyTokenAmount,
62
+ orders,
63
+ this.sdk.MAX_ORDERS_COUNT,
64
+ passOrder
65
+ );
66
+
67
+ // console.log('\n=== calcLiqTokenBuy 返回结果 ===');
68
+ // console.log('自由流动性 SOL 总量:', liqResult.free_lp_sol_amount_sum.toString());
69
+ // console.log('自由流动性 Token 总量:', liqResult.free_lp_token_amount_sum.toString());
70
+ // console.log('锁定流动性 SOL 总量:', liqResult.lock_lp_sol_amount_sum.toString());
71
+ // console.log('锁定流动性 Token 总量:', liqResult.lock_lp_token_amount_sum.toString());
72
+ // console.log('是否包含无限流动性:', liqResult.has_infinite_lp);
73
+ // console.log('跳过的订单索引:', liqResult.pass_order_id);
74
+ // console.log('需要强平的订单数量:', liqResult.force_close_num);
75
+ // console.log('理想 SOL 使用量:', liqResult.ideal_lp_sol_amount.toString());
76
+ // console.log('实际 SOL 使用量:', liqResult.real_lp_sol_amount.toString());
77
+ // console.log('===============================\n');
78
+
79
+ // Convert to BigInt for calculations
80
+ const buyTokenAmountBig = BigInt(buyTokenAmount);
81
+ const freeTokenAmount = BigInt(liqResult.free_lp_token_amount_sum);
82
+ const realSolAmount = BigInt(liqResult.real_lp_sol_amount);
83
+ const idealSolAmount = BigInt(liqResult.ideal_lp_sol_amount);
84
+
85
+ // 1. Calculate completion percentage
86
+ let completionPercentage;
87
+ if (freeTokenAmount >= buyTokenAmountBig) {
88
+ completionPercentage = "100.0";
89
+ } else {
90
+ const percentage = Math.floor((Number(freeTokenAmount) / Number(buyTokenAmountBig)) * 1000) / 10;
91
+ completionPercentage = percentage.toFixed(1);
92
+ }
93
+
94
+ // 2. Calculate slippage percentage and get final SOL amount
95
+ let slippagePercentage;
96
+ let suggestedLiquidity;
97
+ let finalRealSolAmount = realSolAmount;
98
+
99
+ if (realSolAmount > 0n) {
100
+ // Normal case: calculate slippage
101
+ const diff = idealSolAmount > realSolAmount ? idealSolAmount - realSolAmount : realSolAmount - idealSolAmount;
102
+ const slippage = Math.floor((Number(diff) / Number(idealSolAmount)) * 1000) / 10;
103
+ slippagePercentage = slippage.toFixed(1);
104
+ } else {
105
+ // Special case: real SOL amount is 0, need to recalculate with suggested liquidity
106
+ const suggestedAmount = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
107
+
108
+ const recalcResult = calcLiqTokenBuy(
109
+ price,
110
+ suggestedAmount,
111
+ orders,
112
+ this.sdk.MAX_ORDERS_COUNT,
113
+ passOrder
114
+ );
115
+
116
+ const recalcRealSol = BigInt(recalcResult.real_lp_sol_amount);
117
+ const recalcIdealSol = BigInt(recalcResult.ideal_lp_sol_amount);
118
+
119
+ if (recalcRealSol <= 0n) {
120
+ throw new Error('Recalculated real SOL amount should be greater than 0');
121
+ }
122
+
123
+ finalRealSolAmount = recalcRealSol;
124
+
125
+ const diff = recalcIdealSol > recalcRealSol ? recalcIdealSol - recalcRealSol : recalcRealSol - recalcIdealSol;
126
+ const slippage = Math.floor((Number(diff) / Number(recalcIdealSol)) * 1000) / 10;
127
+ slippagePercentage = slippage.toFixed(1);
128
+ }
129
+
130
+ // 3. Calculate suggested liquidity
131
+ if (completionPercentage === "100.0") {
132
+ suggestedLiquidity = buyTokenAmountBig.toString();
133
+ } else {
134
+ const suggested = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
135
+ suggestedLiquidity = suggested.toString();
136
+ }
137
+
138
+ return {
139
+ liqResult,
140
+ completion: completionPercentage,
141
+ slippage: slippagePercentage,
142
+ suggestedTokenAmount: suggestedLiquidity,
143
+ suggestedSolAmount: finalRealSolAmount.toString()
144
+ };
145
+ } catch (error) {
146
+ console.error('calcLiqTokenBuy 调用失败:', error);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+
152
+ /**
153
+ * Simulate token sell transaction analysis
154
+ * @param {string} mint - Token address
155
+ * @param {bigint|string|number} sellTokenAmount - Token amount to sell (u64 format, precision 10^6)
156
+ * @param {string} passOrder - Optional order address to skip (won't be liquidated) 可选的跳过订单地址
157
+ * @param {Object|null} lastPrice - Token price info, default null
158
+ * @param {Object|null} ordersData - Orders response object, default null
159
+ * @returns {Promise<Object>} Token sell simulation result with the following structure:
160
+ * - liqResult: {Object} Complete liquidity calculation result from calcLiqTokenSell, containing:
161
+ * - free_lp_sol_amount_sum: {bigint} Total available free liquidity SOL obtainable from selling
162
+ * - free_lp_token_amount_sum: {bigint} Maximum tokens sellable without force closing orders
163
+ * - lock_lp_sol_amount_sum: {bigint} Total locked liquidity SOL amount (excluding skipped orders)
164
+ * - lock_lp_token_amount_sum: {bigint} Total locked liquidity token amount (excluding skipped orders)
165
+ * - has_infinite_lp: {boolean} Whether includes infinite liquidity to minimum price
166
+ * - pass_order_id: {number} Index of skipped order in array (-1 if none skipped)
167
+ * - force_close_num: {number} Number of orders that need force closure for target sell amount
168
+ * - ideal_lp_sol_amount: {bigint} Theoretical maximum SOL obtainable at current price
169
+ * - real_lp_sol_amount: {bigint} Actual SOL obtainable considering real liquidity distribution
170
+ * - completion: {string} Sell completion percentage as decimal string (e.g., "85.2", "100.0")
171
+ * - slippage: {string} Price slippage percentage as decimal string (e.g., "2.5", "0.8")
172
+ * - suggestedTokenAmount: {string} Recommended token amount to sell based on available liquidity
173
+ * - suggestedSolAmount: {string} Expected SOL amount from suggested token sale
174
+ */
175
+ async function simulateTokenSell(mint, sellTokenAmount, passOrder = null, lastPrice = null, ordersData = null) {
176
+ // 获取价格和订单数据
177
+ let price = lastPrice;
178
+ if (!price) {
179
+ price = await this.sdk.data.price(mint);
180
+ }
181
+
182
+ let orders;
183
+ if (!ordersData) {
184
+ const ordersResponse = await this.sdk.data.orders(mint, {
185
+ type: 'down_orders',
186
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
187
+ });
188
+ // 提取实际的订单数组
189
+ orders = ordersResponse.data.orders;
190
+ } else {
191
+ // 如果传入了 ordersData,从响应对象中提取订单数组并限制长度
192
+ orders = ordersData.data.orders.slice(0, this.sdk.MAX_ORDERS_COUNT + 1);
193
+ }
194
+
195
+ // console.log('simulateTokenSell 获取的数据:');
196
+ // console.log('价格:', price);
197
+ // console.log('订单数量:', orders.length);
198
+ // 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}`));
199
+
200
+ // 调用 calcLiqTokenSell 进行流动性计算
201
+ try {
202
+ const liqResult = calcLiqTokenSell(
203
+ price,
204
+ sellTokenAmount,
205
+ orders,
206
+ this.sdk.MAX_ORDERS_COUNT,
207
+ passOrder
208
+ );
209
+
210
+ // console.log('\n=== calcLiqTokenSell 返回结果 ===');
211
+ // console.log('自由流动性 SOL 总量:', liqResult.free_lp_sol_amount_sum.toString());
212
+ // console.log('自由流动性 Token 总量:', liqResult.free_lp_token_amount_sum.toString());
213
+ // console.log('锁定流动性 SOL 总量:', liqResult.lock_lp_sol_amount_sum.toString());
214
+ // console.log('锁定流动性 Token 总量:', liqResult.lock_lp_token_amount_sum.toString());
215
+ // console.log('是否包含无限流动性:', liqResult.has_infinite_lp);
216
+ // console.log('跳过的订单索引:', liqResult.pass_order_id);
217
+ // console.log('需要强平的订单数量:', liqResult.force_close_num);
218
+ // console.log('理想 SOL 获得量:', liqResult.ideal_lp_sol_amount.toString());
219
+ // console.log('实际 SOL 获得量:', liqResult.real_lp_sol_amount.toString());
220
+ // console.log('===============================\n');
221
+
222
+ // Convert to BigInt for calculations
223
+ const sellTokenAmountBig = BigInt(sellTokenAmount);
224
+ const freeTokenAmount = BigInt(liqResult.free_lp_token_amount_sum);
225
+ const realSolAmount = BigInt(liqResult.real_lp_sol_amount);
226
+ const idealSolAmount = BigInt(liqResult.ideal_lp_sol_amount);
227
+
228
+ // 1. Calculate completion percentage
229
+ let completionPercentage;
230
+ if (freeTokenAmount >= sellTokenAmountBig) {
231
+ completionPercentage = "100.0";
232
+ } else {
233
+ const percentage = Math.floor((Number(freeTokenAmount) / Number(sellTokenAmountBig)) * 1000) / 10;
234
+ completionPercentage = percentage.toFixed(1);
235
+ }
236
+
237
+ // 2. Calculate slippage percentage and get final SOL amount
238
+ let slippagePercentage;
239
+ let suggestedLiquidity;
240
+ let finalRealSolAmount = realSolAmount;
241
+
242
+ if (realSolAmount > 0n) {
243
+ // Normal case: calculate slippage
244
+ const diff = idealSolAmount > realSolAmount ? idealSolAmount - realSolAmount : realSolAmount - idealSolAmount;
245
+ const slippage = Math.floor((Number(diff) / Number(idealSolAmount)) * 1000) / 10;
246
+ slippagePercentage = slippage.toFixed(1);
247
+ } else {
248
+ // Special case: real SOL amount is 0, need to recalculate with suggested liquidity
249
+ const suggestedAmount = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
250
+
251
+ const recalcResult = calcLiqTokenSell(
252
+ price,
253
+ suggestedAmount,
254
+ orders,
255
+ this.sdk.MAX_ORDERS_COUNT,
256
+ passOrder
257
+ );
258
+
259
+ const recalcRealSol = BigInt(recalcResult.real_lp_sol_amount);
260
+ const recalcIdealSol = BigInt(recalcResult.ideal_lp_sol_amount);
261
+
262
+ if (recalcRealSol <= 0n) {
263
+ throw new Error('Recalculated real SOL amount should be greater than 0');
264
+ }
265
+
266
+ finalRealSolAmount = recalcRealSol;
267
+
268
+ const diff = recalcIdealSol > recalcRealSol ? recalcIdealSol - recalcRealSol : recalcRealSol - recalcIdealSol;
269
+ const slippage = Math.floor((Number(diff) / Number(recalcIdealSol)) * 1000) / 10;
270
+ slippagePercentage = slippage.toFixed(1);
271
+ }
272
+
273
+ // 3. Calculate suggested liquidity
274
+ if (completionPercentage === "100.0") {
275
+ suggestedLiquidity = sellTokenAmountBig.toString();
276
+ } else {
277
+ const suggested = (freeTokenAmount * BigInt(this.sdk.SUGGEST_LIQ_RATIO)) / 1000n;
278
+ suggestedLiquidity = suggested.toString();
279
+ }
280
+
281
+ return {
282
+ liqResult,
283
+ completion: completionPercentage,
284
+ slippage: slippagePercentage,
285
+ suggestedTokenAmount: suggestedLiquidity,
286
+ suggestedSolAmount: finalRealSolAmount.toString()
287
+ };
288
+ } catch (error) {
289
+ console.error('calcLiqTokenSell 调用失败:', error.message);
290
+ throw error;
291
+ }
292
+ }
293
+
294
+
295
+
296
+ module.exports = {
297
+ simulateTokenBuy,
298
+ simulateTokenSell
299
+ };
300
+