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.
- package/README.md +364 -0
- package/dist/100x-sdk.cjs.js +57601 -0
- package/dist/100x-sdk.cjs.js.map +1 -0
- package/dist/100x-sdk.esm.js +46234 -0
- package/dist/100x-sdk.esm.js.map +1 -0
- package/dist/100x-sdk.js +46245 -0
- package/dist/100x-sdk.js.map +1 -0
- package/dist/index.d.ts +386 -0
- package/package.json +69 -0
- package/src/idl/fun100x_localnet.json +3735 -0
- package/src/idl/fun100x_main.json +3735 -0
- package/src/index.js +50 -0
- package/src/modules/chain.js +1310 -0
- package/src/modules/fast.js +734 -0
- package/src/modules/param.js +171 -0
- package/src/modules/simulator/buy_sell_token.js +326 -0
- package/src/modules/simulator/calcLiq.js +778 -0
- package/src/modules/simulator/calc_sol_liq.js +268 -0
- package/src/modules/simulator/close_indices.js +217 -0
- package/src/modules/simulator/long_shrot_stop.js +1028 -0
- package/src/modules/simulator/stop_loss_utils.js +378 -0
- package/src/modules/simulator/utils.js +63 -0
- package/src/modules/simulator.js +386 -0
- package/src/modules/token.js +455 -0
- package/src/modules/tools.js +370 -0
- package/src/modules/trading.js +848 -0
- package/src/sdk.js +206 -0
- package/src/types/index.d.ts +386 -0
- package/src/utils/constants.js +57 -0
- package/src/utils/curve_amm.js +1215 -0
- package/src/utils/orderUtils.js +17 -0
|
@@ -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
|
+
|