four-flap-meme-sdk 1.1.87 → 1.1.89
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.
|
@@ -305,7 +305,7 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
305
305
|
* ✅ 自动检查授权,智能处理授权+卖出
|
|
306
306
|
*/
|
|
307
307
|
export async function batchSellWithBundleMerkle(params) {
|
|
308
|
-
const { privateKeys, sellAmounts, tokenAddress, config } = params;
|
|
308
|
+
const { privateKeys, sellAmounts, tokenAddress, minOutputAmounts, config } = params;
|
|
309
309
|
if (privateKeys.length === 0 || sellAmounts.length !== privateKeys.length) {
|
|
310
310
|
throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
|
|
311
311
|
}
|
|
@@ -321,6 +321,14 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
321
321
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
322
322
|
const sellers = privateKeys.map((k) => new Wallet(k, provider));
|
|
323
323
|
const amountsWei = sellAmounts.map((a) => ethers.parseUnits(a, 18));
|
|
324
|
+
// ✅ 解析 minOutputAmounts
|
|
325
|
+
let minOuts;
|
|
326
|
+
if (minOutputAmounts && minOutputAmounts.length === sellers.length) {
|
|
327
|
+
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
minOuts = new Array(sellers.length).fill(0n);
|
|
331
|
+
}
|
|
324
332
|
// ✅ Step 1: 使用 Multicall3 批量检查授权状态
|
|
325
333
|
const ERC20_ABI = [
|
|
326
334
|
'function approve(address spender, uint256 amount) returns (bool)',
|
|
@@ -353,7 +361,7 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
353
361
|
const nonceManager = new NonceManager(provider);
|
|
354
362
|
const signedTxs = [];
|
|
355
363
|
const tm2Contracts = sellers.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
|
|
356
|
-
const unsignedSells = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i],
|
|
364
|
+
const unsignedSells = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], minOuts[i])));
|
|
357
365
|
// ✅ 使用前端传入的 gasLimit,否则使用默认值
|
|
358
366
|
const finalGasLimit = getGasLimit(config);
|
|
359
367
|
const gasLimits = new Array(sellers.length).fill(finalGasLimit);
|
|
@@ -368,6 +376,29 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
368
376
|
type: getTxType(config)
|
|
369
377
|
})));
|
|
370
378
|
signedTxs.push(...signedList);
|
|
379
|
+
// ✅ 基于 minFunds 为每个钱包添加利润转账
|
|
380
|
+
const extractProfit = shouldExtractProfit(config);
|
|
381
|
+
if (extractProfit && minOuts.length > 0) {
|
|
382
|
+
// 为每个钱包添加利润转账(如果 minOut > 0)
|
|
383
|
+
for (let i = 0; i < sellers.length; i++) {
|
|
384
|
+
if (minOuts[i] > 0n) { // ⚠️ 保留这个判断:只对设置了 minOut 的钱包提取利润
|
|
385
|
+
const { profit } = calculateProfit(minOuts[i], config);
|
|
386
|
+
if (profit > 0n) {
|
|
387
|
+
const profitNonce = await nonceManager.getNextNonce(sellers[i]);
|
|
388
|
+
const profitTx = await sellers[i].signTransaction({
|
|
389
|
+
to: config.profitRecipient,
|
|
390
|
+
value: profit,
|
|
391
|
+
nonce: profitNonce,
|
|
392
|
+
gasPrice,
|
|
393
|
+
gasLimit: 21000n,
|
|
394
|
+
chainId: 56,
|
|
395
|
+
type: getTxType(config)
|
|
396
|
+
});
|
|
397
|
+
signedTxs.push(profitTx);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
371
402
|
// ✅ 清理临时 nonce 缓存
|
|
372
403
|
nonceManager.clearTemp();
|
|
373
404
|
// ✅ 简化返回:只返回签名交易
|
|
@@ -261,7 +261,7 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
261
261
|
* Flap Protocol: 批量卖出(Merkle 版本)
|
|
262
262
|
*/
|
|
263
263
|
export async function batchSellWithBundleMerkle(params) {
|
|
264
|
-
const { chain, privateKeys, sellAmounts, tokenAddress, config } = params;
|
|
264
|
+
const { chain, privateKeys, sellAmounts, tokenAddress, minOutputAmounts, config } = params;
|
|
265
265
|
if (privateKeys.length === 0 || sellAmounts.length !== privateKeys.length) {
|
|
266
266
|
throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
|
|
267
267
|
}
|
|
@@ -279,7 +279,34 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
279
279
|
const nonceManager = new NonceManager(provider);
|
|
280
280
|
const wallets = privateKeys.map(k => new Wallet(k, provider));
|
|
281
281
|
const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, 18));
|
|
282
|
-
|
|
282
|
+
// ✅ 自动获取报价以计算预期收益和利润
|
|
283
|
+
const portal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
|
|
284
|
+
// 并行获取所有报价
|
|
285
|
+
const quotedOutputs = await Promise.all(amountsWei.map(async (amount, index) => {
|
|
286
|
+
try {
|
|
287
|
+
const quoted = await portal.quoteExactInput({
|
|
288
|
+
inputToken: tokenAddress,
|
|
289
|
+
outputToken: '0x0000000000000000000000000000000000000000',
|
|
290
|
+
inputAmount: amount
|
|
291
|
+
});
|
|
292
|
+
console.log(`📊 卖出报价 [${index}]: ${ethers.formatEther(amount)} 代币 → ${ethers.formatEther(quoted)} BNB`);
|
|
293
|
+
return quoted;
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
console.warn(`⚠️ 报价失败 [${index}]:`, error);
|
|
297
|
+
return 0n;
|
|
298
|
+
}
|
|
299
|
+
}));
|
|
300
|
+
// ✅ 使用报价结果或用户提供的 minOutputAmounts
|
|
301
|
+
let minOuts;
|
|
302
|
+
if (minOutputAmounts && minOutputAmounts.length === wallets.length) {
|
|
303
|
+
// 用户提供了 minOutputAmounts,优先使用
|
|
304
|
+
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
// 使用报价结果作为 minOutputAmount(保守起见,使用 95% 的报价金额)
|
|
308
|
+
minOuts = quotedOutputs.map(quoted => quoted * 95n / 100n);
|
|
309
|
+
}
|
|
283
310
|
const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
|
|
284
311
|
const unsignedList = await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
|
|
285
312
|
inputToken: tokenAddress,
|
|
@@ -302,6 +329,44 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
302
329
|
type: getTxType(config)
|
|
303
330
|
})));
|
|
304
331
|
signedTxs.push(...signedList);
|
|
332
|
+
// ✅ 基于报价金额为每个钱包添加利润转账
|
|
333
|
+
const extractProfit = shouldExtractProfit(config);
|
|
334
|
+
console.log(`\n💰 利润提取配置: extractProfit=${extractProfit}, profitRecipient=${config.profitRecipient}, quotedOutputs.length=${quotedOutputs.length}`);
|
|
335
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
336
|
+
let totalProfitAdded = 0;
|
|
337
|
+
// 为每个钱包添加利润转账(基于完整报价金额,而非 minOut)
|
|
338
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
339
|
+
console.log(`\n🔍 钱包 [${i}]: quotedOutput=${ethers.formatEther(quotedOutputs[i])} BNB`);
|
|
340
|
+
if (quotedOutputs[i] > 0n) { // 只对报价成功的交易提取利润
|
|
341
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
342
|
+
console.log(` 💵 计算利润: ${ethers.formatEther(profit)} BNB (${config.profitRateBps || 30} bps)`);
|
|
343
|
+
if (profit > 0n) {
|
|
344
|
+
const profitNonce = await nonceManager.getNextNonce(wallets[i]);
|
|
345
|
+
const profitTx = await wallets[i].signTransaction({
|
|
346
|
+
to: config.profitRecipient,
|
|
347
|
+
value: profit,
|
|
348
|
+
nonce: profitNonce,
|
|
349
|
+
gasPrice,
|
|
350
|
+
gasLimit: 21000n,
|
|
351
|
+
chainId,
|
|
352
|
+
type: getTxType(config)
|
|
353
|
+
});
|
|
354
|
+
signedTxs.push(profitTx);
|
|
355
|
+
totalProfitAdded++;
|
|
356
|
+
console.log(` ✅ 已添加利润转账 (nonce: ${profitNonce})`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
console.log(` ⚠️ 跳过:报价为 0`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
console.log(`\n📊 总共添加了 ${totalProfitAdded} 个利润转账\n`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
if (!extractProfit) {
|
|
367
|
+
console.log(`⚠️ 未提取利润:profitRecipient=${config.profitRecipient}, profitRateBps=${config.profitRateBps}\n`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
305
370
|
// ✅ 清理临时 nonce 缓存
|
|
306
371
|
nonceManager.clearTemp();
|
|
307
372
|
// ✅ 简化返回:只返回签名交易
|
|
@@ -92,10 +92,32 @@ export async function flapPrivateSellMerkle(params) {
|
|
|
92
92
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
93
93
|
const gasMultiplier = config.gasLimitMultiplier ?? 1.0;
|
|
94
94
|
const amountWei = ethers.parseUnits(amount, 18);
|
|
95
|
-
|
|
96
|
-
? (typeof minOutputAmount === 'string' ? ethers.parseEther(minOutputAmount) : minOutputAmount)
|
|
97
|
-
: 0n;
|
|
95
|
+
// ✅ 自动获取报价以计算预期收益和利润
|
|
98
96
|
const portal = new ethers.Contract(portalAddr, PORTAL_ABI, wallet);
|
|
97
|
+
let quotedOutput;
|
|
98
|
+
let minOut;
|
|
99
|
+
if (minOutputAmount) {
|
|
100
|
+
// 用户提供了 minOutputAmount,优先使用
|
|
101
|
+
minOut = typeof minOutputAmount === 'string' ? ethers.parseEther(minOutputAmount) : minOutputAmount;
|
|
102
|
+
quotedOutput = minOut; // 使用用户提供的值作为报价
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// 自动获取报价
|
|
106
|
+
try {
|
|
107
|
+
quotedOutput = await portal.quoteExactInput({
|
|
108
|
+
inputToken: tokenAddress,
|
|
109
|
+
outputToken: '0x0000000000000000000000000000000000000000',
|
|
110
|
+
inputAmount: amountWei
|
|
111
|
+
});
|
|
112
|
+
// 使用报价的 95% 作为 minOutputAmount(保守策略)
|
|
113
|
+
minOut = quotedOutput * 95n / 100n;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// 报价失败,使用 0
|
|
117
|
+
quotedOutput = 0n;
|
|
118
|
+
minOut = 0n;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
99
121
|
const unsigned = await portal.swapExactInput.populateTransaction({
|
|
100
122
|
inputToken: tokenAddress,
|
|
101
123
|
outputToken: '0x0000000000000000000000000000000000000000',
|
|
@@ -117,10 +139,10 @@ export async function flapPrivateSellMerkle(params) {
|
|
|
117
139
|
};
|
|
118
140
|
const signed = await wallet.signTransaction(req);
|
|
119
141
|
signedTxs.push(signed);
|
|
120
|
-
// ✅
|
|
142
|
+
// ✅ 基于报价金额计算利润
|
|
121
143
|
const extractProfit = shouldExtractProfit(config);
|
|
122
|
-
if (extractProfit) {
|
|
123
|
-
const { profit } = calculateProfit(
|
|
144
|
+
if (extractProfit && quotedOutput > 0n) {
|
|
145
|
+
const { profit } = calculateProfit(quotedOutput, config);
|
|
124
146
|
if (profit > 0n) {
|
|
125
147
|
const profitTx = await wallet.signTransaction({
|
|
126
148
|
to: config.profitRecipient,
|
|
@@ -239,12 +261,24 @@ export async function flapBatchPrivateSellMerkle(params) {
|
|
|
239
261
|
const nonceManager = new NonceManager(provider);
|
|
240
262
|
const wallets = privateKeys.map(k => new Wallet(k, provider));
|
|
241
263
|
const amountsWei = amounts.map(a => ethers.parseUnits(a, 18));
|
|
264
|
+
// ✅ 自动获取报价以计算预期收益和利润
|
|
265
|
+
const portal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
|
|
266
|
+
// 并行获取所有报价
|
|
267
|
+
const quotedOutputs = await Promise.all(amountsWei.map(amount => portal.quoteExactInput({
|
|
268
|
+
inputToken: tokenAddress,
|
|
269
|
+
outputToken: '0x0000000000000000000000000000000000000000',
|
|
270
|
+
inputAmount: amount
|
|
271
|
+
}).catch(() => 0n) // 如果报价失败,返回 0n
|
|
272
|
+
));
|
|
273
|
+
// ✅ 使用报价结果或用户提供的 minOutputAmounts
|
|
242
274
|
let minOuts;
|
|
243
275
|
if (minOutputAmounts && minOutputAmounts.length === wallets.length) {
|
|
276
|
+
// 用户提供了 minOutputAmounts,优先使用
|
|
244
277
|
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
245
278
|
}
|
|
246
279
|
else {
|
|
247
|
-
|
|
280
|
+
// 使用报价结果作为 minOutputAmount(保守起见,使用 95% 的报价金额)
|
|
281
|
+
minOuts = quotedOutputs.map(quoted => quoted * 95n / 100n);
|
|
248
282
|
}
|
|
249
283
|
const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
|
|
250
284
|
const unsignedList = await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
|
|
@@ -267,13 +301,13 @@ export async function flapBatchPrivateSellMerkle(params) {
|
|
|
267
301
|
type: getTxType(config)
|
|
268
302
|
})));
|
|
269
303
|
signedTxs.push(...signedList);
|
|
270
|
-
// ✅
|
|
304
|
+
// ✅ 基于报价金额为每个钱包添加利润转账
|
|
271
305
|
const extractProfit = shouldExtractProfit(config);
|
|
272
|
-
if (extractProfit &&
|
|
273
|
-
//
|
|
306
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
307
|
+
// 为每个钱包添加利润转账(基于完整报价金额,而非 minOut)
|
|
274
308
|
for (let i = 0; i < wallets.length; i++) {
|
|
275
|
-
if (
|
|
276
|
-
const { profit } = calculateProfit(
|
|
309
|
+
if (quotedOutputs[i] > 0n) { // 只对报价成功的交易提取利润
|
|
310
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
277
311
|
if (profit > 0n) {
|
|
278
312
|
const profitNonce = await nonceManager.getNextNonce(wallets[i]);
|
|
279
313
|
const profitTx = await wallets[i].signTransaction({
|