four-flap-meme-sdk 1.4.13 → 1.4.15
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.
|
@@ -8,7 +8,6 @@ export interface PancakeSwapSignConfig {
|
|
|
8
8
|
txType?: 0 | 2;
|
|
9
9
|
chainId?: number;
|
|
10
10
|
reserveGasBNB?: number;
|
|
11
|
-
skipApprovalCheck?: boolean;
|
|
12
11
|
bribeAmount?: number;
|
|
13
12
|
}
|
|
14
13
|
export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
|
|
@@ -19,7 +18,6 @@ export interface PancakeSwapConfig extends CommonBundleConfig {
|
|
|
19
18
|
reserveGasBNB?: number;
|
|
20
19
|
waitForConfirmation?: boolean;
|
|
21
20
|
waitTimeoutMs?: number;
|
|
22
|
-
skipApprovalCheck?: boolean;
|
|
23
21
|
}
|
|
24
22
|
export interface V2RouteParams {
|
|
25
23
|
routeType: 'v2';
|
|
@@ -67,7 +65,6 @@ export type PancakeSwapResult = {
|
|
|
67
65
|
buyerAddress: string;
|
|
68
66
|
sellAmount: string;
|
|
69
67
|
buyAmount: string;
|
|
70
|
-
hasApproval?: boolean;
|
|
71
68
|
profitAmount?: string;
|
|
72
69
|
};
|
|
73
70
|
};
|
|
@@ -102,7 +99,6 @@ export interface PancakeBatchSwapResult {
|
|
|
102
99
|
buyerAddresses: string[];
|
|
103
100
|
sellAmount: string;
|
|
104
101
|
buyAmounts: string[];
|
|
105
|
-
hasApproval?: boolean;
|
|
106
102
|
profitAmount?: string;
|
|
107
103
|
};
|
|
108
104
|
}
|
|
@@ -7,31 +7,6 @@ function createPancakeContext(config) {
|
|
|
7
7
|
});
|
|
8
8
|
return { chainId, provider };
|
|
9
9
|
}
|
|
10
|
-
async function ensureSellerApproval({ tokenAddress, seller, provider, decimals, chainId, config, nonceManager, gasPrice, txType }) {
|
|
11
|
-
if (config.skipApprovalCheck) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
const erc20 = new Contract(tokenAddress, ERC20_ALLOWANCE_ABI, provider);
|
|
15
|
-
const currentAllowance = await erc20.allowance(seller.address, PANCAKE_PROXY_ADDRESS);
|
|
16
|
-
// ✅ 阈值:MaxUint256 / 2,如果授权额度超过这个值,认为是"无限授权"
|
|
17
|
-
// 这样可以检测到用户之前的 MaxUint256 授权
|
|
18
|
-
const halfMaxUint = ethers.MaxUint256 / 2n;
|
|
19
|
-
if (currentAllowance >= halfMaxUint) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
// ✅ 使用共享的 NonceManager
|
|
23
|
-
const approvalNonce = await nonceManager.getNextNonce(seller);
|
|
24
|
-
return await seller.signTransaction({
|
|
25
|
-
to: tokenAddress,
|
|
26
|
-
data: APPROVE_INTERFACE.encodeFunctionData('approve', [PANCAKE_PROXY_ADDRESS, ethers.MaxUint256]),
|
|
27
|
-
value: 0n,
|
|
28
|
-
nonce: approvalNonce,
|
|
29
|
-
gasLimit: 80000n,
|
|
30
|
-
gasPrice,
|
|
31
|
-
chainId,
|
|
32
|
-
type: txType
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
10
|
async function quoteSellOutput({ routeParams, sellAmountWei, provider }) {
|
|
36
11
|
console.log(`[quoteSellOutput] 开始报价, routeType=${routeParams.routeType}, sellAmount=${sellAmountWei} wei`);
|
|
37
12
|
// ==================== V2 报价 ====================
|
|
@@ -312,13 +287,6 @@ const PANCAKE_PROXY_ADDRESS = ADDRESSES.BSC.PancakeProxy;
|
|
|
312
287
|
// 代理合约手续费
|
|
313
288
|
const FLAT_FEE = 0n; // ✅ 已移除合约固定手续费
|
|
314
289
|
const WBNB_ADDRESS = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
315
|
-
// ✅ STABLE_COINS 和 V3_FEE_TIERS 已移至 ../utils/quote-helpers.ts
|
|
316
|
-
const ERC20_ALLOWANCE_ABI = [
|
|
317
|
-
'function allowance(address,address) view returns (uint256)',
|
|
318
|
-
'function approve(address spender,uint256 amount) returns (bool)',
|
|
319
|
-
'function decimals() view returns (uint8)'
|
|
320
|
-
];
|
|
321
|
-
const APPROVE_INTERFACE = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
|
|
322
290
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
323
291
|
const ERC20_BALANCE_OF_ABI = ['function balanceOf(address) view returns (uint256)'];
|
|
324
292
|
/**
|
|
@@ -343,18 +311,6 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
343
311
|
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
344
312
|
]);
|
|
345
313
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
346
|
-
// ✅ 先构建授权交易(会消耗 nonce)
|
|
347
|
-
const approvalTx = await ensureSellerApproval({
|
|
348
|
-
tokenAddress,
|
|
349
|
-
seller,
|
|
350
|
-
provider: context.provider,
|
|
351
|
-
decimals,
|
|
352
|
-
chainId: context.chainId,
|
|
353
|
-
config,
|
|
354
|
-
nonceManager, // ✅ 共享 NonceManager
|
|
355
|
-
gasPrice,
|
|
356
|
-
txType
|
|
357
|
-
});
|
|
358
314
|
const quoteResult = await quoteSellOutput({
|
|
359
315
|
routeParams,
|
|
360
316
|
sellAmountWei,
|
|
@@ -401,14 +357,14 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
401
357
|
? ethers.parseEther(String(config.bribeAmount))
|
|
402
358
|
: 0n;
|
|
403
359
|
const needBribeTx = bribeAmount > 0n;
|
|
404
|
-
// ✅ 使用共享的 NonceManager 规划 nonce
|
|
360
|
+
// ✅ 使用共享的 NonceManager 规划 nonce
|
|
405
361
|
const noncePlan = await planNonces({
|
|
406
362
|
seller,
|
|
407
363
|
buyer,
|
|
408
364
|
sameAddress,
|
|
409
|
-
approvalExists:
|
|
365
|
+
approvalExists: false, // ✅ 已移除授权
|
|
410
366
|
profitNeeded: profitAmount > 0n,
|
|
411
|
-
needBribeTx,
|
|
367
|
+
needBribeTx,
|
|
412
368
|
nonceManager
|
|
413
369
|
});
|
|
414
370
|
// ✅ 并行签名所有交易
|
|
@@ -477,12 +433,10 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
477
433
|
provider: context.provider,
|
|
478
434
|
buyerAddress: buyer.address
|
|
479
435
|
});
|
|
480
|
-
// ✅ 组装顺序:贿赂 →
|
|
436
|
+
// ✅ 组装顺序:贿赂 → 卖出 → 买入 → 利润
|
|
481
437
|
const signedTransactions = [];
|
|
482
438
|
if (bribeTx)
|
|
483
439
|
signedTransactions.push(bribeTx);
|
|
484
|
-
if (approvalTx)
|
|
485
|
-
signedTransactions.push(approvalTx);
|
|
486
440
|
signedTransactions.push(signedSell, signedBuy);
|
|
487
441
|
if (profitTx)
|
|
488
442
|
signedTransactions.push(profitTx);
|
|
@@ -495,7 +449,6 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
495
449
|
buyAmount: useNativeToken
|
|
496
450
|
? ethers.formatEther(buyerBudget.buyAmountBNB)
|
|
497
451
|
: ethers.formatUnits(buyerBudget.buyAmountBNB, quoteTokenDecimals),
|
|
498
|
-
hasApproval: !!approvalTx,
|
|
499
452
|
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
500
453
|
}
|
|
501
454
|
};
|
|
@@ -532,18 +485,6 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
532
485
|
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
533
486
|
]);
|
|
534
487
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
535
|
-
// ✅ 构建授权交易(如果需要)
|
|
536
|
-
const approvalTx = await ensureSellerApproval({
|
|
537
|
-
tokenAddress,
|
|
538
|
-
seller,
|
|
539
|
-
provider: context.provider,
|
|
540
|
-
decimals,
|
|
541
|
-
chainId: context.chainId,
|
|
542
|
-
config,
|
|
543
|
-
nonceManager,
|
|
544
|
-
gasPrice,
|
|
545
|
-
txType
|
|
546
|
-
});
|
|
547
488
|
// ✅ 获取卖出报价
|
|
548
489
|
const quoteResult = await quoteSellOutput({
|
|
549
490
|
routeParams,
|
|
@@ -713,12 +654,10 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
713
654
|
signedSellPromise,
|
|
714
655
|
...signedBuyPromises
|
|
715
656
|
]);
|
|
716
|
-
// 4. 按顺序组装交易数组:贿赂 →
|
|
657
|
+
// 4. 按顺序组装交易数组:贿赂 → 卖出 → 买入 → 利润
|
|
717
658
|
const signedTransactions = [];
|
|
718
659
|
if (bribeTx)
|
|
719
660
|
signedTransactions.push(bribeTx); // 贿赂(首位)
|
|
720
|
-
if (approvalTx)
|
|
721
|
-
signedTransactions.push(approvalTx); // 授权(如果有)
|
|
722
661
|
signedTransactions.push(signedSell); // 卖出
|
|
723
662
|
signedTransactions.push(...signedBuys); // 多个买入
|
|
724
663
|
if (profitTx)
|
|
@@ -732,7 +671,6 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
732
671
|
buyAmounts: buyAmountsWei.map(amt => useNativeToken
|
|
733
672
|
? ethers.formatEther(amt)
|
|
734
673
|
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
735
|
-
hasApproval: !!approvalTx,
|
|
736
674
|
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
737
675
|
}
|
|
738
676
|
};
|
|
@@ -755,12 +693,10 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
755
693
|
// ✅ 判断是否使用原生代币
|
|
756
694
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
757
695
|
const WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'.toLowerCase();
|
|
758
|
-
// ✅
|
|
696
|
+
// ✅ 校验买方数量(子钱包已预留 BNB,不需要主钱包转 Gas)
|
|
759
697
|
// BNB 模式:贿赂(1) + 卖出(1) + 转账(N) + 买入(N) + 利润(1) ≤ 50 → 2N + 3 ≤ 50 → N ≤ 23
|
|
760
|
-
// ERC20 模式:贿赂(1) + 卖出(1) + ERC20转账(N) +
|
|
761
|
-
const
|
|
762
|
-
const MAX_BUYERS_ERC20 = 15;
|
|
763
|
-
const MAX_BUYERS = useNativeToken ? MAX_BUYERS_NATIVE : MAX_BUYERS_ERC20;
|
|
698
|
+
// ERC20 模式:贿赂(1) + 卖出(1) + ERC20转账(N) + 买入(N) + 利润(1) ≤ 50 → 2N + 3 ≤ 50 → N ≤ 23
|
|
699
|
+
const MAX_BUYERS = 23;
|
|
764
700
|
if (buyerPrivateKeys.length === 0) {
|
|
765
701
|
throw new Error('至少需要一个买方钱包');
|
|
766
702
|
}
|
|
@@ -895,12 +831,10 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
895
831
|
sellerRequired = bribeAmount + sellerGasCost;
|
|
896
832
|
}
|
|
897
833
|
else {
|
|
898
|
-
// ERC20
|
|
899
|
-
//
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const buyerGasNeeded = (gasPrice * finalGasLimit + FLAT_FEE) * BigInt(buyers.length);
|
|
903
|
-
sellerRequired = bribeAmount + sellerGasCost + buyerGasNeeded;
|
|
834
|
+
// ERC20 模式:子钱包已预留 BNB,不需要主钱包转 Gas
|
|
835
|
+
// 卖方 Gas: 贿赂(21000) + 卖出(gasLimit) + N个ERC20转账(65000 each) + 利润(21000)
|
|
836
|
+
sellerGasCost = gasPrice * (21000n + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + 21000n);
|
|
837
|
+
sellerRequired = bribeAmount + sellerGasCost;
|
|
904
838
|
}
|
|
905
839
|
if (sellerBalance < sellerRequired) {
|
|
906
840
|
throw new Error(`主钱包 BNB 余额不足: 需要约 ${ethers.formatEther(sellerRequired)} BNB (贿赂: ${ethers.formatEther(bribeAmount)}, Gas: ${ethers.formatEther(sellerGasCost)}), 实际 ${ethers.formatEther(sellerBalance)} BNB`);
|
|
@@ -967,7 +901,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
967
901
|
}
|
|
968
902
|
}
|
|
969
903
|
else {
|
|
970
|
-
// ✅ ERC20 模式:ERC20 transfer
|
|
904
|
+
// ✅ ERC20 模式:ERC20 transfer 调用(子钱包已预留 BNB,不需要转 Gas)
|
|
971
905
|
const erc20Interface = new ethers.Interface([
|
|
972
906
|
'function transfer(address to, uint256 amount) returns (bool)'
|
|
973
907
|
]);
|
|
@@ -988,20 +922,6 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
988
922
|
});
|
|
989
923
|
transferTxs.push(transferTx);
|
|
990
924
|
}
|
|
991
|
-
// ERC20 模式:额外转账 Gas 费用给买家(用于支付 FLAT_FEE 和买入 Gas)
|
|
992
|
-
for (let i = 0; i < buyers.length; i++) {
|
|
993
|
-
const buyerGasCost = gasPrice * finalGasLimit + FLAT_FEE;
|
|
994
|
-
const gasTx = await seller.signTransaction({
|
|
995
|
-
to: buyers[i].address,
|
|
996
|
-
value: buyerGasCost,
|
|
997
|
-
nonce: sellerNonce++,
|
|
998
|
-
gasPrice,
|
|
999
|
-
gasLimit: 21000n,
|
|
1000
|
-
chainId: context.chainId,
|
|
1001
|
-
type: txType
|
|
1002
|
-
});
|
|
1003
|
-
transferTxs.push(gasTx);
|
|
1004
|
-
}
|
|
1005
925
|
}
|
|
1006
926
|
console.log(`[pancakeQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
1007
927
|
// ==================== 4. 买入交易 ====================
|
|
@@ -1010,7 +930,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1010
930
|
const buyAmount = transferAmountsWei[i];
|
|
1011
931
|
const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
|
|
1012
932
|
// BNB 模式:value = buyAmount + FLAT_FEE
|
|
1013
|
-
// ERC20 模式:value = FLAT_FEE
|
|
933
|
+
// ERC20 模式:value = FLAT_FEE(买入金额通过授权支付)
|
|
1014
934
|
const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
|
|
1015
935
|
let buyUnsigned;
|
|
1016
936
|
if (routeParams.routeType === 'v2') {
|
|
@@ -1053,6 +973,8 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1053
973
|
}
|
|
1054
974
|
nonceManager.clearTemp();
|
|
1055
975
|
// ==================== 组装交易数组 ====================
|
|
976
|
+
// BNB 模式:贿赂 → 卖出 → 转账 → 买入 → 利润
|
|
977
|
+
// ERC20 模式:贿赂 → 卖出 → ERC20转账 → BNB Gas转账 → 买入 → 利润
|
|
1056
978
|
const signedTransactions = [];
|
|
1057
979
|
if (bribeTx)
|
|
1058
980
|
signedTransactions.push(bribeTx);
|