four-flap-meme-sdk 1.4.13 → 1.4.14
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,7 +693,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
755
693
|
// ✅ 判断是否使用原生代币
|
|
756
694
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
757
695
|
const WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'.toLowerCase();
|
|
758
|
-
// ✅
|
|
696
|
+
// ✅ 校验买方数量(已移除授权交易)
|
|
759
697
|
// BNB 模式:贿赂(1) + 卖出(1) + 转账(N) + 买入(N) + 利润(1) ≤ 50 → 2N + 3 ≤ 50 → N ≤ 23
|
|
760
698
|
// ERC20 模式:贿赂(1) + 卖出(1) + ERC20转账(N) + BNB Gas转账(N) + 买入(N) + 利润(1) ≤ 50 → 3N + 3 ≤ 50 → N ≤ 15
|
|
761
699
|
const MAX_BUYERS_NATIVE = 23;
|
|
@@ -895,11 +833,11 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
895
833
|
sellerRequired = bribeAmount + sellerGasCost;
|
|
896
834
|
}
|
|
897
835
|
else {
|
|
898
|
-
// ERC20
|
|
899
|
-
// - 贿赂(21000) + 卖出(gasLimit) + N个ERC20转账(65000 each) + N个BNB Gas转账(21000 each) + 利润(21000)
|
|
900
|
-
// -
|
|
836
|
+
// ERC20 模式(已移除授权):
|
|
837
|
+
// - 卖方 Gas: 贿赂(21000) + 卖出(gasLimit) + N个ERC20转账(65000 each) + N个BNB Gas转账(21000 each) + 利润(21000)
|
|
838
|
+
// - 买方 Gas: N个买入(gasLimit each) + N个FLAT_FEE
|
|
901
839
|
sellerGasCost = gasPrice * (21000n + finalGasLimit + (ERC20_TRANSFER_GAS + 21000n) * BigInt(buyers.length) + 21000n);
|
|
902
|
-
const buyerGasNeeded =
|
|
840
|
+
const buyerGasNeeded = gasPrice * finalGasLimit * BigInt(buyers.length) + FLAT_FEE * BigInt(buyers.length);
|
|
903
841
|
sellerRequired = bribeAmount + sellerGasCost + buyerGasNeeded;
|
|
904
842
|
}
|
|
905
843
|
if (sellerBalance < sellerRequired) {
|
|
@@ -988,7 +926,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
988
926
|
});
|
|
989
927
|
transferTxs.push(transferTx);
|
|
990
928
|
}
|
|
991
|
-
// ERC20 模式:额外转账 Gas
|
|
929
|
+
// ERC20 模式:额外转账 Gas 费用给买家(用于支付买入 Gas 和 FLAT_FEE)
|
|
992
930
|
for (let i = 0; i < buyers.length; i++) {
|
|
993
931
|
const buyerGasCost = gasPrice * finalGasLimit + FLAT_FEE;
|
|
994
932
|
const gasTx = await seller.signTransaction({
|
|
@@ -1010,7 +948,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1010
948
|
const buyAmount = transferAmountsWei[i];
|
|
1011
949
|
const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
|
|
1012
950
|
// BNB 模式:value = buyAmount + FLAT_FEE
|
|
1013
|
-
// ERC20 模式:value = FLAT_FEE
|
|
951
|
+
// ERC20 模式:value = FLAT_FEE(买入金额通过授权支付)
|
|
1014
952
|
const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
|
|
1015
953
|
let buyUnsigned;
|
|
1016
954
|
if (routeParams.routeType === 'v2') {
|
|
@@ -1053,6 +991,8 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1053
991
|
}
|
|
1054
992
|
nonceManager.clearTemp();
|
|
1055
993
|
// ==================== 组装交易数组 ====================
|
|
994
|
+
// BNB 模式:贿赂 → 卖出 → 转账 → 买入 → 利润
|
|
995
|
+
// ERC20 模式:贿赂 → 卖出 → ERC20转账 → BNB Gas转账 → 买入 → 利润
|
|
1056
996
|
const signedTransactions = [];
|
|
1057
997
|
if (bribeTx)
|
|
1058
998
|
signedTransactions.push(bribeTx);
|