four-flap-meme-sdk 1.2.3 → 1.2.5
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.
|
@@ -15,6 +15,16 @@ const PANCAKE_PROXY_ABI = [
|
|
|
15
15
|
'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)',
|
|
16
16
|
'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)'
|
|
17
17
|
];
|
|
18
|
+
// PancakeSwap V2 Router ABI(用于报价)
|
|
19
|
+
const PANCAKE_V2_ROUTER_ABI = [
|
|
20
|
+
'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
|
|
21
|
+
];
|
|
22
|
+
// PancakeSwap V3 QuoterV2 ABI(用于报价)
|
|
23
|
+
const PANCAKE_V3_QUOTER_ABI = [
|
|
24
|
+
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)'
|
|
25
|
+
];
|
|
26
|
+
const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
27
|
+
const PANCAKE_V3_QUOTER_ADDRESS = '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997';
|
|
18
28
|
// ERC20 ABI
|
|
19
29
|
const ERC20_ABI = [
|
|
20
30
|
'function approve(address spender, uint256 amount) external returns (bool)',
|
|
@@ -398,7 +408,6 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
398
408
|
const tokenDecimals = await getTokenDecimals(tokenAddress, provider);
|
|
399
409
|
const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, tokenDecimals));
|
|
400
410
|
// ✅ Step 1: 使用 Multicall3 批量检查授权状态
|
|
401
|
-
console.log('🔍 使用 Multicall3 批量检查授权状态...');
|
|
402
411
|
const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), pancakeProxyAddress);
|
|
403
412
|
// 找出需要授权的钱包索引
|
|
404
413
|
const needApprovalIndexes = [];
|
|
@@ -408,75 +417,58 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
408
417
|
needApprovalIndexes.push(i);
|
|
409
418
|
}
|
|
410
419
|
}
|
|
411
|
-
console.log(` - 总钱包数: ${sellers.length}`);
|
|
412
|
-
console.log(` - 需要授权: ${needApprovalIndexes.length}`);
|
|
413
|
-
console.log(` - 已有授权: ${sellers.length - needApprovalIndexes.length}\n`);
|
|
414
420
|
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
415
421
|
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
416
422
|
if (needApprovalIndexes.length > 0) {
|
|
417
|
-
console.log(`⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权`);
|
|
418
|
-
console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
|
|
419
423
|
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
420
424
|
}
|
|
421
|
-
// ✅
|
|
422
|
-
console.log('💰 提交卖出交易...');
|
|
423
|
-
// 🔍 调试信息 - 详细参数
|
|
424
|
-
console.log('📊 卖出详情:');
|
|
425
|
-
console.log(` [基本信息]`);
|
|
426
|
-
console.log(` - 链: BSC`);
|
|
427
|
-
console.log(` - 代币地址: ${tokenAddress}`);
|
|
428
|
-
console.log(` - PancakeProxy: ${pancakeProxyAddress}`);
|
|
429
|
-
console.log(` - 卖家数量: ${sellers.length}`);
|
|
430
|
-
console.log(` - 路由类型: ${routeType}`);
|
|
431
|
-
console.log(`\n [卖家信息]`);
|
|
432
|
-
sellers.forEach((seller, i) => {
|
|
433
|
-
console.log(` - 钱包 ${i + 1}: ${seller.address}`);
|
|
434
|
-
});
|
|
435
|
-
console.log(`\n [卖出金额]`);
|
|
436
|
-
sellAmounts.forEach((amount, i) => {
|
|
437
|
-
console.log(` - 钱包 ${i + 1}: ${amount} TOKEN (${amountsWei[i].toString()} Wei)`);
|
|
438
|
-
});
|
|
439
|
-
// 计算 minOutputAmounts(通常是 BNB/WBNB,18 位)
|
|
425
|
+
// ✅ 自动获取报价或使用用户提供的 minOutputAmounts
|
|
440
426
|
let minOuts;
|
|
427
|
+
let quotedOutputs;
|
|
441
428
|
if (params.minOutputAmounts && params.minOutputAmounts.length === sellers.length) {
|
|
429
|
+
// 用户提供了 minOutputAmounts
|
|
442
430
|
minOuts = params.minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
443
|
-
|
|
444
|
-
params.minOutputAmounts.forEach((min, i) => {
|
|
445
|
-
console.log(` - 钱包 ${i + 1}: ${min} BNB`);
|
|
446
|
-
});
|
|
431
|
+
quotedOutputs = minOuts.map(m => m * 100n / 95n); // 反推预期收益
|
|
447
432
|
}
|
|
448
433
|
else {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
434
|
+
// ✅ 自动调用 PancakeSwap 获取报价
|
|
435
|
+
quotedOutputs = await Promise.all(amountsWei.map(async (amount) => {
|
|
436
|
+
try {
|
|
437
|
+
if (routeType === 'v2') {
|
|
438
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
439
|
+
const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
|
|
440
|
+
return amounts[amounts.length - 1];
|
|
441
|
+
}
|
|
442
|
+
else if (routeType === 'v3-single') {
|
|
443
|
+
const quoter = new Contract(PANCAKE_V3_QUOTER_ADDRESS, PANCAKE_V3_QUOTER_ABI, provider);
|
|
444
|
+
const result = await quoter.quoteExactInputSingle.staticCall({
|
|
445
|
+
tokenIn: tokenAddress,
|
|
446
|
+
tokenOut: params.v3TokenOut,
|
|
447
|
+
amountIn: amount,
|
|
448
|
+
fee: params.v3Fee,
|
|
449
|
+
sqrtPriceLimitX96: 0
|
|
450
|
+
});
|
|
451
|
+
return result[0];
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
// v3-multi 使用 v2 备选
|
|
455
|
+
if (params.v2Path && params.v2Path.length >= 2) {
|
|
456
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
457
|
+
const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
|
|
458
|
+
return amounts[amounts.length - 1];
|
|
459
|
+
}
|
|
460
|
+
return 0n;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
return 0n;
|
|
465
|
+
}
|
|
466
|
+
}));
|
|
467
|
+
// minOuts 设为预期收益的 95%(滑点保护)
|
|
468
|
+
minOuts = quotedOutputs.map(q => q * 95n / 100n);
|
|
463
469
|
}
|
|
464
470
|
// 卖出不需要发送 BNB,只需要 flatFee
|
|
465
471
|
const needBNB = false;
|
|
466
|
-
console.log(`\n [交易费用]`);
|
|
467
|
-
console.log(` - 需要发送 BNB: ${needBNB ? '是' : '否'}`);
|
|
468
|
-
console.log(` - 每笔手续费: 0.0001 BNB`);
|
|
469
|
-
console.log(` - 总手续费: ${ethers.formatEther(BigInt(sellers.length) * ethers.parseEther('0.0001'))} BNB`);
|
|
470
|
-
console.log(`\n [Gas 配置]`);
|
|
471
|
-
console.log(` - Gas Price: ${ethers.formatUnits(gasPrice, 'gwei')} Gwei`);
|
|
472
|
-
console.log(` - Gas Limit: ${config.gasLimit || 'Auto (800000 * multiplier)'}`);
|
|
473
|
-
if (config.gasLimitMultiplier) {
|
|
474
|
-
console.log(` - Gas Multiplier: ${config.gasLimitMultiplier}x`);
|
|
475
|
-
}
|
|
476
|
-
console.log(`\n [Bundle 配置]`);
|
|
477
|
-
console.log(` - Block Offset: ${blockOffset}`);
|
|
478
|
-
console.log(` - Wait Confirmation: ${config.waitForConfirmation ?? false}`);
|
|
479
|
-
console.log(` - Timeout: ${(config.waitTimeoutMs ?? 120000) / 1000}s\n`);
|
|
480
472
|
// 构建交易
|
|
481
473
|
const proxies = sellers.map(w => new Contract(pancakeProxyAddress, PANCAKE_PROXY_ABI, w));
|
|
482
474
|
let unsignedSells;
|
|
@@ -502,20 +494,15 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
502
494
|
throw new Error(`Unsupported routeType: ${routeType}`);
|
|
503
495
|
}
|
|
504
496
|
// ✅ Step 4: 验证钱包余额(确保有足够 BNB 支付手续费)
|
|
505
|
-
console.log('🔍 验证钱包余额...');
|
|
506
497
|
const balances = await Promise.all(sellers.map(w => provider.getBalance(w.address)));
|
|
507
498
|
const minRequiredBNB = ethers.parseEther('0.001'); // 至少需要 0.001 BNB(包括 gas 和手续费)
|
|
508
499
|
const insufficientBalances = balances.filter(b => b < minRequiredBNB);
|
|
509
500
|
if (insufficientBalances.length > 0) {
|
|
510
|
-
|
|
511
|
-
balances.forEach((b, i) => {
|
|
512
|
-
console.log(` 钱包 ${i + 1}: ${ethers.formatEther(b)} BNB`);
|
|
513
|
-
});
|
|
501
|
+
throw new Error(`${insufficientBalances.length} 个钱包 BNB 余额不足 0.001 BNB`);
|
|
514
502
|
}
|
|
515
503
|
// ✅ 使用前端传入的 gasLimit,否则使用默认值
|
|
516
504
|
const finalGasLimit = getGasLimit(config);
|
|
517
505
|
// ✅ 直接签名和提交(内联处理以支持利润转账)
|
|
518
|
-
console.log('🚀 签名卖出交易...\n');
|
|
519
506
|
const nonceManager = new NonceManager(provider);
|
|
520
507
|
const nonces = await Promise.all(sellers.map(w => nonceManager.getNextNonce(w)));
|
|
521
508
|
const signedTxs = await Promise.all(unsignedSells.map((unsigned, i) => sellers[i].signTransaction({
|
|
@@ -528,13 +515,12 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
528
515
|
type: getTxType(config),
|
|
529
516
|
value: unsigned.value // ✅ 显式保留 value(手续费)
|
|
530
517
|
})));
|
|
531
|
-
// ✅
|
|
518
|
+
// ✅ 基于预期收益为每个钱包添加利润转账
|
|
532
519
|
const extractProfit = shouldExtractProfit(config);
|
|
533
|
-
if (extractProfit &&
|
|
534
|
-
// 为每个钱包添加利润转账(如果 minOut > 0)
|
|
520
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
535
521
|
for (let i = 0; i < sellers.length; i++) {
|
|
536
|
-
if (
|
|
537
|
-
const { profit } = calculateProfit(
|
|
522
|
+
if (quotedOutputs[i] > 0n) {
|
|
523
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
538
524
|
if (profit > 0n) {
|
|
539
525
|
const profitNonce = await nonceManager.getNextNonce(sellers[i]);
|
|
540
526
|
const profitTx = await sellers[i].signTransaction({
|
|
@@ -106,15 +106,11 @@ export async function fourPrivateSellMerkle(params) {
|
|
|
106
106
|
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
107
107
|
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
108
108
|
if (currentAllowance < APPROVAL_THRESHOLD) {
|
|
109
|
-
console.log('⚠️ 警告:检测到钱包需要授权');
|
|
110
|
-
console.log(' 请先完成授权后再调用卖出方法');
|
|
111
109
|
throw new Error(`需要授权:钱包 ${wallet.address} 尚未授权。请先完成授权后再卖出。`);
|
|
112
110
|
}
|
|
113
111
|
else {
|
|
114
|
-
console.log('✅ 已有足够授权额度,跳过授权\n');
|
|
115
112
|
}
|
|
116
113
|
// ✅ Step 3: 卖出交易
|
|
117
|
-
console.log('💰 提交卖出交易...');
|
|
118
114
|
const nonceManager = new NonceManager(provider);
|
|
119
115
|
const signedTxs = [];
|
|
120
116
|
const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
|
|
@@ -104,16 +104,22 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
104
104
|
const extractProfit = shouldExtractProfit(config);
|
|
105
105
|
let totalProfit = 0n;
|
|
106
106
|
let totalBuyAmount = 0n;
|
|
107
|
-
//
|
|
108
|
-
const fundsList =
|
|
107
|
+
// 计算每个买家的实际购买金额和利润
|
|
108
|
+
const fundsList = [];
|
|
109
|
+
const profitList = [];
|
|
110
|
+
buyAmounts.forEach((v) => {
|
|
109
111
|
const originalAmount = ethers.parseEther(v);
|
|
110
112
|
totalBuyAmount += originalAmount;
|
|
111
113
|
if (extractProfit) {
|
|
112
114
|
const { remaining, profit } = calculateProfit(originalAmount, config);
|
|
113
115
|
totalProfit += profit;
|
|
114
|
-
|
|
116
|
+
fundsList.push(remaining); // ✅ 扣除利润后的金额用于购买
|
|
117
|
+
profitList.push(profit); // ✅ 记录每个买家的利润
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
fundsList.push(originalAmount);
|
|
121
|
+
profitList.push(0n);
|
|
115
122
|
}
|
|
116
|
-
return originalAmount;
|
|
117
123
|
});
|
|
118
124
|
const minOuts = new Array(buyers.length).fill(0n);
|
|
119
125
|
const buyPortals = buyers.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
|
|
@@ -141,19 +147,23 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
141
147
|
})));
|
|
142
148
|
signedTxs.push(...signedBuys);
|
|
143
149
|
buyTxs.push(...signedBuys);
|
|
144
|
-
// ✅
|
|
150
|
+
// ✅ 添加利润转账(每个买家单独转自己的利润)
|
|
145
151
|
if (extractProfit && totalProfit > 0n && buyers.length > 0) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
for (let i = 0; i < buyers.length; i++) {
|
|
153
|
+
if (profitList[i] > 0n) {
|
|
154
|
+
const profitNonce = await nonceManager.getNextNonce(buyers[i]);
|
|
155
|
+
const profitTx = await buyers[i].signTransaction({
|
|
156
|
+
to: config.profitRecipient,
|
|
157
|
+
value: profitList[i], // ✅ 每个买家转自己的利润
|
|
158
|
+
nonce: profitNonce,
|
|
159
|
+
gasPrice,
|
|
160
|
+
gasLimit: 21000n,
|
|
161
|
+
chainId,
|
|
162
|
+
type: getTxType(config)
|
|
163
|
+
});
|
|
164
|
+
signedTxs.push(profitTx);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
157
167
|
}
|
|
158
168
|
// ✅ 清理临时 nonce 缓存
|
|
159
169
|
nonceManager.clearTemp();
|
|
@@ -227,12 +227,7 @@ export async function approvePancakeProxyBatch(params) {
|
|
|
227
227
|
// 只授权不足的
|
|
228
228
|
const needApproval = wallets.filter((_, i) => allowances[i] < amountsBigInt[i]);
|
|
229
229
|
const needApprovalAmounts = amountsBigInt.filter((amount, i) => allowances[i] < amount);
|
|
230
|
-
console.log(`🔍 PancakeProxy 授权检查:`);
|
|
231
|
-
console.log(` - 总钱包数: ${wallets.length}`);
|
|
232
|
-
console.log(` - 需要授权: ${needApproval.length}`);
|
|
233
|
-
console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
|
|
234
230
|
if (needApproval.length === 0) {
|
|
235
|
-
console.log('✅ 所有钱包已授权,无需再次授权。');
|
|
236
231
|
return {
|
|
237
232
|
success: true,
|
|
238
233
|
approvedCount: 0,
|
|
@@ -257,22 +252,17 @@ export async function approvePancakeProxyBatch(params) {
|
|
|
257
252
|
type: getTxType(config)
|
|
258
253
|
})));
|
|
259
254
|
nonceManager.clearTemp();
|
|
260
|
-
console.log(`✅ 已生成 ${signedTxs.length} 个授权交易签名`);
|
|
261
255
|
// ✅ 内部提交到 Merkle
|
|
262
256
|
const currentBlock = await provider.getBlockNumber();
|
|
263
257
|
const targetBlock = currentBlock + blockOffset;
|
|
264
|
-
console.log(`📤 正在提交授权交易到 Merkle...`);
|
|
265
258
|
const bundleResult = await merkle.sendBundle({
|
|
266
259
|
transactions: signedTxs,
|
|
267
260
|
targetBlock: targetBlock
|
|
268
261
|
});
|
|
269
|
-
console.log(`✅ 授权交易已提交,bundleHash: ${bundleResult.bundleHash}`);
|
|
270
262
|
// ✅ 等待授权确认
|
|
271
|
-
console.log(`⏳ 等待授权交易确认(约5-10秒)...`);
|
|
272
263
|
const confirmResults = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, 60000);
|
|
273
264
|
const successCount = confirmResults.filter(r => r.success).length;
|
|
274
265
|
if (successCount > 0) {
|
|
275
|
-
console.log(`✅ 授权已确认,共 ${successCount} 笔交易成功`);
|
|
276
266
|
return {
|
|
277
267
|
success: true,
|
|
278
268
|
approvedCount: successCount,
|
|
@@ -281,7 +271,6 @@ export async function approvePancakeProxyBatch(params) {
|
|
|
281
271
|
};
|
|
282
272
|
}
|
|
283
273
|
else {
|
|
284
|
-
console.log(`⚠️ 授权交易未确认`);
|
|
285
274
|
return {
|
|
286
275
|
success: false,
|
|
287
276
|
approvedCount: 0,
|