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、allowance
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, currentAllowance] = await Promise.all([
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
- // 每多一笔买入,减少 0.5% 的卖出数量(保守估计)
131
- let sellAmountWei = estimatedTokenAmount;
132
- if (buyCount > 1) {
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, needApproval, sameAddress, nonceManager }) {
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 + approvalTxCount + sellCount + profitNonceCount;
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, approvalNonce, profitNonce };
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 + approvalTxCount + sellCount + profitNonceCount;
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, approvalNonce, profitNonce };
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
- // ✅ 检查是否需要授权(卖出代币需要先授权给 Router)
125
- const approvalTarget = routeParams.routeType === 'v2'
126
- ? PANCAKE_V2_ROUTER_ADDRESS
127
- : PANCAKE_V3_ROUTER_ADDRESS;
128
- const erc20ForApproval = new Contract(tokenAddress, [
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, needApproval, // ✅ 新增
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 + approvalTxCount + sellCount + profitNonceCount;
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, approvalNonce, profitNonce };
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 + approvalTxCount + sellCount + profitNonceCount;
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, approvalNonce, profitNonce };
576
+ return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
616
577
  }
617
578
  /**
618
579
  * 构建利润多跳转账交易(强制 2 跳中转)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.67",
3
+ "version": "1.4.69",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",