four-flap-meme-sdk 1.1.94 → 1.1.96
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.
|
@@ -322,28 +322,56 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
322
322
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
323
323
|
const sellers = privateKeys.map((k) => new Wallet(k, provider));
|
|
324
324
|
const amountsWei = sellAmounts.map((a) => ethers.parseUnits(a, 18));
|
|
325
|
-
//
|
|
325
|
+
// ⚠️ Four.meme 内盘代币通过 TokenManager bonding curve 定价,不在 PancakeSwap 上
|
|
326
|
+
// ✅ 使用用户提供的 minOutputAmounts,或默认为 0(不限制最小输出)
|
|
326
327
|
let minOuts;
|
|
328
|
+
let quotedOutputs = [];
|
|
327
329
|
if (minOutputAmounts && minOutputAmounts.length === sellers.length) {
|
|
330
|
+
// 用户提供了 minOutputAmounts,优先使用
|
|
328
331
|
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
332
|
+
quotedOutputs = minOuts.map(m => m * 100n / 95n); // 反推报价(用于利润计算)
|
|
333
|
+
console.log(`📝 使用用户提供的 minOutputAmounts`);
|
|
334
|
+
console.log(`💰 minOutputAmounts:`);
|
|
335
|
+
minOuts.forEach((minOut, i) => {
|
|
336
|
+
console.log(` 钱包 ${i}: ${ethers.formatEther(minOut)} BNB`);
|
|
337
|
+
});
|
|
329
338
|
}
|
|
330
339
|
else {
|
|
340
|
+
// Four.meme 内盘代币:默认 minOutputAmount = 0(不限制),不提取利润
|
|
331
341
|
minOuts = new Array(sellers.length).fill(0n);
|
|
342
|
+
quotedOutputs = new Array(sellers.length).fill(0n);
|
|
343
|
+
console.log(`📝 Four.meme 内盘代币:minOutputAmount = 0(不限制最小输出)`);
|
|
344
|
+
console.log(`⚠️ 无法自动计算卖出收益,建议手动传入 minOutputAmounts 以提取利润`);
|
|
332
345
|
}
|
|
333
|
-
|
|
346
|
+
console.log('');
|
|
347
|
+
// ✅ Step 1: 检查代币余额和授权状态
|
|
334
348
|
const ERC20_ABI = [
|
|
335
349
|
'function approve(address spender, uint256 amount) returns (bool)',
|
|
336
|
-
'function allowance(address owner, address spender) view returns (uint256)'
|
|
350
|
+
'function allowance(address owner, address spender) view returns (uint256)',
|
|
351
|
+
'function balanceOf(address owner) view returns (uint256)'
|
|
337
352
|
];
|
|
338
|
-
|
|
353
|
+
// 检查代币余额
|
|
354
|
+
console.log('🔍 检查代币余额...');
|
|
355
|
+
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
356
|
+
const balances = await Promise.all(sellers.map(w => tokenContract.balanceOf(w.address)));
|
|
357
|
+
for (let i = 0; i < sellers.length; i++) {
|
|
358
|
+
const balance = balances[i];
|
|
359
|
+
const sellAmount = amountsWei[i];
|
|
360
|
+
console.log(` 钱包 ${i}: 余额 ${ethers.formatUnits(balance, 18)}, 卖出 ${ethers.formatUnits(sellAmount, 18)}`);
|
|
361
|
+
if (balance < sellAmount) {
|
|
362
|
+
throw new Error(`钱包 ${i} (${sellers[i].address.slice(0, 8)}...) 代币余额不足:需要 ${ethers.formatUnits(sellAmount, 18)},实际 ${ethers.formatUnits(balance, 18)}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
console.log('\n🔍 使用 Multicall3 批量检查授权状态...');
|
|
339
366
|
const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), tmAddr);
|
|
340
367
|
// 找出需要授权的钱包索引
|
|
341
|
-
//
|
|
368
|
+
// ✅ 检查授权额度是否足够卖出
|
|
342
369
|
const needApprovalIndexes = [];
|
|
343
|
-
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
344
370
|
for (let i = 0; i < sellers.length; i++) {
|
|
345
|
-
|
|
371
|
+
// ✅ 检查授权额度是否 >= 卖出数量
|
|
372
|
+
if (allowances[i] < amountsWei[i]) {
|
|
346
373
|
needApprovalIndexes.push(i);
|
|
374
|
+
console.log(` ⚠️ 钱包 ${i}: 授权不足 (${ethers.formatUnits(allowances[i], 18)} < ${ethers.formatUnits(amountsWei[i], 18)})`);
|
|
347
375
|
}
|
|
348
376
|
}
|
|
349
377
|
console.log(` - 总钱包数: ${sellers.length}`);
|
|
@@ -376,14 +404,17 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
376
404
|
type: getTxType(config)
|
|
377
405
|
})));
|
|
378
406
|
signedTxs.push(...signedList);
|
|
379
|
-
// ✅
|
|
407
|
+
// ✅ 基于报价金额为每个钱包添加利润转账
|
|
380
408
|
const extractProfit = shouldExtractProfit(config);
|
|
381
|
-
if (extractProfit &&
|
|
382
|
-
|
|
409
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
410
|
+
console.log(`💰 提取利润交易...`);
|
|
411
|
+
let totalProfitExtracted = 0n;
|
|
412
|
+
// 为每个钱包添加利润转账(基于完整报价金额,而非 minOut)
|
|
383
413
|
for (let i = 0; i < sellers.length; i++) {
|
|
384
|
-
if (
|
|
385
|
-
const { profit } = calculateProfit(
|
|
414
|
+
if (quotedOutputs[i] > 0n) { // 只对报价成功的交易提取利润
|
|
415
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
386
416
|
if (profit > 0n) {
|
|
417
|
+
console.log(` ✅ 钱包 ${i}: 从 ${ethers.formatEther(quotedOutputs[i])} BNB 中提取 ${ethers.formatEther(profit)} BNB (${config.profitRateBps / 100}%)`);
|
|
387
418
|
const profitNonce = await nonceManager.getNextNonce(sellers[i]);
|
|
388
419
|
const profitTx = await sellers[i].signTransaction({
|
|
389
420
|
to: config.profitRecipient,
|
|
@@ -395,9 +426,11 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
395
426
|
type: getTxType(config)
|
|
396
427
|
});
|
|
397
428
|
signedTxs.push(profitTx);
|
|
429
|
+
totalProfitExtracted += profit;
|
|
398
430
|
}
|
|
399
431
|
}
|
|
400
432
|
}
|
|
433
|
+
console.log(` 💵 总利润: ${ethers.formatEther(totalProfitExtracted)} BNB → ${config.profitRecipient}\n`);
|
|
401
434
|
}
|
|
402
435
|
// ✅ 清理临时 nonce 缓存
|
|
403
436
|
nonceManager.clearTemp();
|