four-flap-meme-sdk 1.4.67 → 1.4.69
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.
|
@@ -105,34 +105,26 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
105
105
|
if (buyerFundsWei <= 0n) {
|
|
106
106
|
throw new Error('buyerFunds 需要大于 0');
|
|
107
107
|
}
|
|
108
|
-
// ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
|
|
108
|
+
// ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
|
|
109
109
|
const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
|
|
110
110
|
const erc20 = new Contract(tokenAddress, [
|
|
111
111
|
'function balanceOf(address) view returns (uint256)',
|
|
112
|
-
'function decimals() view returns (uint8)'
|
|
113
|
-
'function allowance(address,address) view returns (uint256)'
|
|
112
|
+
'function decimals() view returns (uint8)'
|
|
114
113
|
], provider);
|
|
115
|
-
const [buyQuote, sellerTokenBal, decimals
|
|
114
|
+
const [buyQuote, sellerTokenBal, decimals] = await Promise.all([
|
|
116
115
|
helper3.tryBuy(tokenAddress, 0n, buyerFundsWei),
|
|
117
116
|
erc20.balanceOf(seller.address),
|
|
118
|
-
erc20.decimals()
|
|
119
|
-
erc20.allowance(seller.address, TM_ADDRESS)
|
|
117
|
+
erc20.decimals()
|
|
120
118
|
]);
|
|
121
|
-
// ✅ 检查是否需要授权(授权额度小于 10 亿代币时需要授权)
|
|
122
|
-
const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', decimals);
|
|
123
|
-
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
124
119
|
const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
|
|
125
120
|
if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
|
|
126
121
|
throw new Error('报价失败:无法估算可买入的代币数量');
|
|
127
122
|
}
|
|
128
|
-
// ✅
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const slippageReductionBps = BigInt((buyCount - 1) * 50); // 每多1笔减少 0.5% = 50 bps
|
|
134
|
-
sellAmountWei = estimatedTokenAmount * (10000n - slippageReductionBps) / 10000n;
|
|
135
|
-
}
|
|
123
|
+
// ✅ 添加安全系数:减少 2% 卖出数量以应对报价误差/税/滑点
|
|
124
|
+
// 原因:Four.meme 报价可能有误差,实际买入的代币可能少于报价
|
|
125
|
+
// 小金额时误差影响更大,需要安全余量
|
|
126
|
+
const SELL_SAFETY_BPS = 200n; // 2% = 200 bps
|
|
127
|
+
const sellAmountWei = estimatedTokenAmount * (10000n - SELL_SAFETY_BPS) / 10000n;
|
|
136
128
|
// 卖方余额检查
|
|
137
129
|
if (!sameAddress && sellerTokenBal < sellAmountWei) {
|
|
138
130
|
throw new Error(`卖方代币余额不足: 需要 ${ethers.formatUnits(sellAmountWei, decimals)},实际 ${ethers.formatUnits(sellerTokenBal, decimals)}`);
|
|
@@ -151,7 +143,7 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
151
143
|
// ✅ 构建多笔买入和卖出交易
|
|
152
144
|
const buyUnsignedPromises = buyAmountsWei.map(amount => tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, amount, 0n, { value: amount }));
|
|
153
145
|
const sellUnsignedPromises = sellAmountsWei.map(amount => tmSeller.sellToken.populateTransaction(0n, tokenAddress, amount, 0n));
|
|
154
|
-
// ✅ 规划多笔交易 nonce
|
|
146
|
+
// ✅ 规划多笔交易 nonce(与 Flap 一致,不含授权)
|
|
155
147
|
const multiNoncePlan = await planMultiNonces({
|
|
156
148
|
buyer,
|
|
157
149
|
seller,
|
|
@@ -159,7 +151,6 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
159
151
|
sellCount,
|
|
160
152
|
extractProfit,
|
|
161
153
|
needBribeTx,
|
|
162
|
-
needApproval, // ✅ 新增
|
|
163
154
|
sameAddress,
|
|
164
155
|
nonceManager
|
|
165
156
|
});
|
|
@@ -204,35 +195,16 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
204
195
|
type: txType,
|
|
205
196
|
value: 0n
|
|
206
197
|
}));
|
|
207
|
-
// ✅ 授权交易(放在买入后、卖出前)
|
|
208
|
-
let approvalTx = null;
|
|
209
|
-
if (needApproval && multiNoncePlan.approvalNonce !== undefined) {
|
|
210
|
-
const approveInterface = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
|
|
211
|
-
const approveData = approveInterface.encodeFunctionData('approve', [TM_ADDRESS, ethers.MaxUint256]);
|
|
212
|
-
approvalTx = await seller.signTransaction({
|
|
213
|
-
to: tokenAddress,
|
|
214
|
-
data: approveData,
|
|
215
|
-
value: 0n,
|
|
216
|
-
nonce: multiNoncePlan.approvalNonce,
|
|
217
|
-
gasLimit: 80000n,
|
|
218
|
-
gasPrice,
|
|
219
|
-
chainId: chainIdNum,
|
|
220
|
-
type: txType
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
198
|
const [signedBuys, signedSells] = await Promise.all([
|
|
224
199
|
Promise.all(buySignPromises),
|
|
225
200
|
Promise.all(sellSignPromises)
|
|
226
201
|
]);
|
|
227
202
|
nonceManager.clearTemp();
|
|
228
|
-
// ✅ 组装交易列表:贿赂 → 买入(多笔) →
|
|
203
|
+
// ✅ 组装交易列表:贿赂 → 买入(多笔) → 卖出(多笔)(与 Flap 一致)
|
|
229
204
|
const allTransactions = [];
|
|
230
205
|
if (bribeTx)
|
|
231
206
|
allTransactions.push(bribeTx);
|
|
232
|
-
allTransactions.push(...signedBuys);
|
|
233
|
-
if (approvalTx)
|
|
234
|
-
allTransactions.push(approvalTx);
|
|
235
|
-
allTransactions.push(...signedSells);
|
|
207
|
+
allTransactions.push(...signedBuys, ...signedSells);
|
|
236
208
|
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
237
209
|
let profitHopWallets;
|
|
238
210
|
if (extractProfit && profitAmount > 0n && multiNoncePlan.profitNonce !== undefined) {
|
|
@@ -266,39 +238,38 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
266
238
|
};
|
|
267
239
|
}
|
|
268
240
|
/**
|
|
269
|
-
* ✅ 规划多笔交易 nonce
|
|
270
|
-
* 交易顺序:贿赂 → 买入(多笔) →
|
|
241
|
+
* ✅ 规划多笔交易 nonce(与 Flap 一致)
|
|
242
|
+
* 交易顺序:贿赂 → 买入(多笔) → 卖出(多笔) → 利润
|
|
271
243
|
*/
|
|
272
|
-
async function planMultiNonces({ buyer, seller, buyCount, sellCount, extractProfit, needBribeTx,
|
|
244
|
+
async function planMultiNonces({ buyer, seller, buyCount, sellCount, extractProfit, needBribeTx, sameAddress, nonceManager }) {
|
|
273
245
|
const profitNonceCount = extractProfit ? 1 : 0;
|
|
274
|
-
const approvalTxCount = needApproval ? 1 : 0;
|
|
275
246
|
if (sameAddress) {
|
|
276
|
-
//
|
|
247
|
+
// 同一地址:贿赂(可选) + 买入(多笔) + 卖出(多笔) + 利润(可选)
|
|
277
248
|
const bribeTxCount = needBribeTx ? 1 : 0;
|
|
278
|
-
const totalTxCount = bribeTxCount + buyCount +
|
|
249
|
+
const totalTxCount = bribeTxCount + buyCount + sellCount + profitNonceCount;
|
|
279
250
|
const nonces = await nonceManager.getNextNonceBatch(buyer, totalTxCount);
|
|
280
251
|
let idx = 0;
|
|
281
252
|
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
282
253
|
const buyerNonces = nonces.slice(idx, idx + buyCount);
|
|
283
254
|
idx += buyCount;
|
|
284
|
-
const approvalNonce = needApproval ? nonces[idx++] : undefined;
|
|
285
255
|
const sellerNonces = nonces.slice(idx, idx + sellCount);
|
|
286
256
|
idx += sellCount;
|
|
287
257
|
const profitNonce = extractProfit ? nonces[idx] : undefined;
|
|
288
|
-
return { buyerNonces, sellerNonces, bribeNonce,
|
|
258
|
+
return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
|
|
289
259
|
}
|
|
290
|
-
//
|
|
260
|
+
// 不同地址
|
|
261
|
+
// 买方:buyCount 个 nonce
|
|
262
|
+
// 卖方:贿赂(可选) + sellCount + 利润(可选) 个 nonce
|
|
291
263
|
const bribeTxCount = needBribeTx ? 1 : 0;
|
|
292
|
-
const sellerTxCount = bribeTxCount +
|
|
264
|
+
const sellerTxCount = bribeTxCount + sellCount + profitNonceCount;
|
|
293
265
|
const [buyerNonces, sellerNoncesAll] = await Promise.all([
|
|
294
266
|
nonceManager.getNextNonceBatch(buyer, buyCount),
|
|
295
267
|
nonceManager.getNextNonceBatch(seller, sellerTxCount)
|
|
296
268
|
]);
|
|
297
269
|
let idx = 0;
|
|
298
270
|
const bribeNonce = needBribeTx ? sellerNoncesAll[idx++] : undefined;
|
|
299
|
-
const approvalNonce = needApproval ? sellerNoncesAll[idx++] : undefined;
|
|
300
271
|
const sellerNonces = sellerNoncesAll.slice(idx, idx + sellCount);
|
|
301
272
|
idx += sellCount;
|
|
302
273
|
const profitNonce = extractProfit ? sellerNoncesAll[idx] : undefined;
|
|
303
|
-
return { buyerNonces, sellerNonces, bribeNonce,
|
|
274
|
+
return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
|
|
304
275
|
}
|
|
@@ -121,28 +121,11 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
121
121
|
buyerFundsWei: buyerFundsInfo.buyerFundsWei,
|
|
122
122
|
provider: context.provider
|
|
123
123
|
});
|
|
124
|
-
// ✅
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
'function allowance(address,address) view returns (uint256)',
|
|
130
|
-
'function decimals() view returns (uint8)'
|
|
131
|
-
], context.provider);
|
|
132
|
-
const [currentAllowance, tokenDecimals] = await Promise.all([
|
|
133
|
-
erc20ForApproval.allowance(seller.address, approvalTarget),
|
|
134
|
-
erc20ForApproval.decimals()
|
|
135
|
-
]);
|
|
136
|
-
const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', tokenDecimals);
|
|
137
|
-
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
138
|
-
// ✅ 多笔买入时减少卖出数量以应对滑点
|
|
139
|
-
// 原因:分批买入会推高价格,实际获得的代币 < 一次性买入的报价
|
|
140
|
-
// 每多一笔买入,减少 0.5% 的卖出数量(保守估计)
|
|
141
|
-
let adjustedSellAmount = quoteResult.quotedTokenOut;
|
|
142
|
-
if (buyCount > 1) {
|
|
143
|
-
const slippageReductionBps = BigInt((buyCount - 1) * 50); // 每多1笔减少 0.5% = 50 bps
|
|
144
|
-
adjustedSellAmount = quoteResult.quotedTokenOut * (10000n - slippageReductionBps) / 10000n;
|
|
145
|
-
}
|
|
124
|
+
// ✅ 添加安全系数:减少 2% 卖出数量以应对报价误差/税/滑点
|
|
125
|
+
// 原因:报价可能有误差,实际买入的代币可能少于报价
|
|
126
|
+
// 小金额时误差影响更大,需要安全余量
|
|
127
|
+
const SELL_SAFETY_BPS = 200n; // 2% = 200 bps
|
|
128
|
+
const adjustedSellAmount = quoteResult.quotedTokenOut * (10000n - SELL_SAFETY_BPS) / 10000n;
|
|
146
129
|
// ✅ 拆分买入和卖出金额
|
|
147
130
|
const buyAmountsWei = splitAmount(buyerFundsInfo.buyerFundsWei, buyCount);
|
|
148
131
|
const sellAmountsWei = splitAmount(adjustedSellAmount, sellCount);
|
|
@@ -195,7 +178,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
195
178
|
sameAddress,
|
|
196
179
|
extractProfit: profitAmount > 0n,
|
|
197
180
|
needBribeTx,
|
|
198
|
-
needApproval, // ✅ 新增
|
|
199
181
|
nonceManager
|
|
200
182
|
});
|
|
201
183
|
// ✅ 贿赂交易放在首位(由卖方发送,与利润交易同一钱包)
|
|
@@ -221,22 +203,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
221
203
|
chainId: context.chainId,
|
|
222
204
|
type: txType
|
|
223
205
|
})));
|
|
224
|
-
// ✅ 授权交易(放在买入后、卖出前)
|
|
225
|
-
let approvalTx = null;
|
|
226
|
-
if (needApproval && multiNoncePlan.approvalNonce !== undefined) {
|
|
227
|
-
const approveInterface = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
|
|
228
|
-
const approveData = approveInterface.encodeFunctionData('approve', [approvalTarget, ethers.MaxUint256]);
|
|
229
|
-
approvalTx = await seller.signTransaction({
|
|
230
|
-
to: tokenAddress,
|
|
231
|
-
data: approveData,
|
|
232
|
-
value: 0n,
|
|
233
|
-
nonce: multiNoncePlan.approvalNonce,
|
|
234
|
-
gasLimit: 80000n,
|
|
235
|
-
gasPrice,
|
|
236
|
-
chainId: context.chainId,
|
|
237
|
-
type: txType
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
206
|
// ✅ 并行签名所有卖出交易
|
|
241
207
|
const signedSells = await Promise.all(swapUnsignedArray.sellUnsignedArray.map((unsigned, i) => seller.signTransaction({
|
|
242
208
|
...unsigned,
|
|
@@ -260,14 +226,11 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
260
226
|
provider: context.provider,
|
|
261
227
|
buyerAddress: buyer.address
|
|
262
228
|
});
|
|
263
|
-
// ✅ 组装顺序:贿赂 → 买入(多笔) →
|
|
229
|
+
// ✅ 组装顺序:贿赂 → 买入(多笔) → 卖出(多笔)(与 Flap 一致)
|
|
264
230
|
const allTransactions = [];
|
|
265
231
|
if (bribeTx)
|
|
266
232
|
allTransactions.push(bribeTx);
|
|
267
|
-
allTransactions.push(...signedBuys);
|
|
268
|
-
if (approvalTx)
|
|
269
|
-
allTransactions.push(approvalTx);
|
|
270
|
-
allTransactions.push(...signedSells);
|
|
233
|
+
allTransactions.push(...signedBuys, ...signedSells);
|
|
271
234
|
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
272
235
|
const profitResult = await buildProfitTransaction({
|
|
273
236
|
provider: context.provider,
|
|
@@ -577,42 +540,40 @@ async function planNonces({ buyer, seller, sameAddress, extractProfit, needBribe
|
|
|
577
540
|
return { buyerNonce, sellerNonce };
|
|
578
541
|
}
|
|
579
542
|
/**
|
|
580
|
-
* ✅ 规划多笔交易 nonce
|
|
581
|
-
* 交易顺序:贿赂 → 买入(多笔) →
|
|
543
|
+
* ✅ 规划多笔交易 nonce(与 Flap 一致)
|
|
544
|
+
* 交易顺序:贿赂 → 买入(多笔) → 卖出(多笔) → 利润
|
|
582
545
|
*/
|
|
583
|
-
async function planMultiNonces({ buyer, seller, buyCount, sellCount, sameAddress, extractProfit, needBribeTx,
|
|
584
|
-
nonceManager }) {
|
|
546
|
+
async function planMultiNonces({ buyer, seller, buyCount, sellCount, sameAddress, extractProfit, needBribeTx, nonceManager }) {
|
|
585
547
|
const profitNonceCount = extractProfit ? 1 : 0;
|
|
586
|
-
const approvalTxCount = needApproval ? 1 : 0;
|
|
587
548
|
if (sameAddress) {
|
|
588
|
-
//
|
|
549
|
+
// 同一地址:贿赂(可选) + 买入(多笔) + 卖出(多笔) + 利润(可选)
|
|
589
550
|
const bribeTxCount = needBribeTx ? 1 : 0;
|
|
590
|
-
const totalTxCount = bribeTxCount + buyCount +
|
|
551
|
+
const totalTxCount = bribeTxCount + buyCount + sellCount + profitNonceCount;
|
|
591
552
|
const nonces = await nonceManager.getNextNonceBatch(buyer, totalTxCount);
|
|
592
553
|
let idx = 0;
|
|
593
554
|
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
594
555
|
const buyerNonces = nonces.slice(idx, idx + buyCount);
|
|
595
556
|
idx += buyCount;
|
|
596
|
-
const approvalNonce = needApproval ? nonces[idx++] : undefined;
|
|
597
557
|
const sellerNonces = nonces.slice(idx, idx + sellCount);
|
|
598
558
|
idx += sellCount;
|
|
599
559
|
const profitNonce = extractProfit ? nonces[idx] : undefined;
|
|
600
|
-
return { buyerNonces, sellerNonces, bribeNonce,
|
|
560
|
+
return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
|
|
601
561
|
}
|
|
602
|
-
//
|
|
562
|
+
// 不同地址
|
|
563
|
+
// 买方:buyCount 个 nonce
|
|
564
|
+
// 卖方:贿赂(可选) + sellCount + 利润(可选) 个 nonce
|
|
603
565
|
const bribeTxCount = needBribeTx ? 1 : 0;
|
|
604
|
-
const sellerTxCount = bribeTxCount +
|
|
566
|
+
const sellerTxCount = bribeTxCount + sellCount + profitNonceCount;
|
|
605
567
|
const [buyerNonces, sellerNoncesAll] = await Promise.all([
|
|
606
568
|
nonceManager.getNextNonceBatch(buyer, buyCount),
|
|
607
569
|
nonceManager.getNextNonceBatch(seller, sellerTxCount)
|
|
608
570
|
]);
|
|
609
571
|
let idx = 0;
|
|
610
572
|
const bribeNonce = needBribeTx ? sellerNoncesAll[idx++] : undefined;
|
|
611
|
-
const approvalNonce = needApproval ? sellerNoncesAll[idx++] : undefined;
|
|
612
573
|
const sellerNonces = sellerNoncesAll.slice(idx, idx + sellCount);
|
|
613
574
|
idx += sellCount;
|
|
614
575
|
const profitNonce = extractProfit ? sellerNoncesAll[idx] : undefined;
|
|
615
|
-
return { buyerNonces, sellerNonces, bribeNonce,
|
|
576
|
+
return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
|
|
616
577
|
}
|
|
617
578
|
/**
|
|
618
579
|
* 构建利润多跳转账交易(强制 2 跳中转)
|