four-flap-meme-sdk 1.4.29 → 1.4.31

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,7 +15,6 @@ export interface FourBuyFirstConfig extends CommonBundleConfig {
15
15
  reserveGasBNB?: number;
16
16
  waitForConfirmation?: boolean;
17
17
  waitTimeoutMs?: number;
18
- skipApprovalCheck?: boolean;
19
18
  }
20
19
  export interface FourBundleBuyFirstSignParams {
21
20
  buyerPrivateKey: string;
@@ -41,7 +40,6 @@ export type FourBuyFirstResult = {
41
40
  sellerAddress: string;
42
41
  buyAmount: string;
43
42
  sellAmount: string;
44
- hasApproval?: boolean;
45
43
  profitAmount?: string;
46
44
  };
47
45
  };
@@ -52,18 +52,16 @@ export async function fourBundleBuyFirstMerkle(params) {
52
52
  if (buyerFundsWei <= 0n) {
53
53
  throw new Error('buyerFunds 需要大于 0');
54
54
  }
55
- // ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals、allowance
55
+ // ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
56
56
  const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
57
57
  const erc20 = new Contract(tokenAddress, [
58
58
  'function balanceOf(address) view returns (uint256)',
59
- 'function decimals() view returns (uint8)',
60
- 'function allowance(address,address) view returns (uint256)'
59
+ 'function decimals() view returns (uint8)'
61
60
  ], provider);
62
- const [buyQuote, sellerTokenBal, decimals, currentAllowance] = await Promise.all([
61
+ const [buyQuote, sellerTokenBal, decimals] = await Promise.all([
63
62
  helper3.tryBuy(tokenAddress, 0n, buyerFundsWei),
64
63
  erc20.balanceOf(seller.address),
65
- erc20.decimals(),
66
- erc20.allowance(seller.address, TM_ADDRESS)
64
+ erc20.decimals()
67
65
  ]);
68
66
  const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
69
67
  if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
@@ -74,14 +72,9 @@ export async function fourBundleBuyFirstMerkle(params) {
74
72
  if (!sameAddress && sellerTokenBal < sellAmountWei) {
75
73
  throw new Error(`卖方代币余额不足: 需要 ${ethers.formatUnits(sellAmountWei, decimals)},实际 ${ethers.formatUnits(sellerTokenBal, decimals)}`);
76
74
  }
77
- // ✅ 检查是否需要授权
78
- const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', decimals);
79
- const needApproval = currentAllowance < APPROVAL_THRESHOLD;
80
75
  // ✅ 优化:第三批并行 - trySell、构建交易、获取 nonces
81
76
  const tmBuyer = new Contract(TM_ADDRESS, TM_ABI, buyer);
82
77
  const tmSeller = new Contract(TM_ADDRESS, TM_ABI, seller);
83
- // ✅ 已移除滑点保护:minBuyAmount 固定为 0
84
- const minBuyAmount = 0n;
85
78
  // 预先规划 nonces
86
79
  const extractProfit = true;
87
80
  const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL; // 万分之三
@@ -93,8 +86,6 @@ export async function fourBundleBuyFirstMerkle(params) {
93
86
  let sellerNonceCount = 1; // 卖出交易
94
87
  if (needBribeTx)
95
88
  sellerNonceCount++; // 贿赂交易(由卖方发送)
96
- if (needApproval)
97
- sellerNonceCount++; // 授权交易
98
89
  if (extractProfit)
99
90
  sellerNonceCount++; // 利润交易
100
91
  // ✅ 优化:使用批量 nonce 获取(单次网络往返)
@@ -110,31 +101,22 @@ export async function fourBundleBuyFirstMerkle(params) {
110
101
  }));
111
102
  const [sellResult, buyUnsigned, sellUnsigned, noncesResult] = await Promise.all([
112
103
  trySell('BSC', config.rpcUrl, tokenAddress, sellAmountWei),
113
- tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, minBuyAmount, { value: buyerFundsWei }),
114
- tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n), // minSellFunds 后面更新
104
+ tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, 0n, { value: buyerFundsWei }),
105
+ tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n),
115
106
  getNoncesPromise
116
107
  ]);
117
108
  const { buyerNonces, sellerNonces } = noncesResult;
118
109
  const estimatedSellFunds = sellResult.funds;
119
- // ✅ 已移除滑点保护:minSellFunds 固定为 0
120
- const minSellFunds = 0n;
121
110
  const profitAmount = extractProfit ? (estimatedSellFunds * BigInt(profitRateBps)) / 10000n : 0n;
122
- // 更新卖出交易的 minSellFunds
123
- sellUnsigned.data = tmSeller.interface.encodeFunctionData('sellToken', [
124
- 0n, tokenAddress, sellAmountWei, minSellFunds
125
- ]);
126
111
  // 分配 nonces
127
112
  let buyerNonce;
128
113
  let sellerNonce;
129
114
  let bribeNonce;
130
- let approvalNonce;
131
115
  let profitNonce;
132
116
  if (sameAddress) {
133
117
  let idx = 0;
134
118
  if (needBribeTx)
135
119
  bribeNonce = sellerNonces[idx++];
136
- if (needApproval)
137
- approvalNonce = sellerNonces[idx++];
138
120
  buyerNonce = sellerNonces[idx++];
139
121
  sellerNonce = sellerNonces[idx++];
140
122
  if (extractProfit)
@@ -145,8 +127,6 @@ export async function fourBundleBuyFirstMerkle(params) {
145
127
  let idx = 0;
146
128
  if (needBribeTx)
147
129
  bribeNonce = sellerNonces[idx++];
148
- if (needApproval)
149
- approvalNonce = sellerNonces[idx++];
150
130
  sellerNonce = sellerNonces[idx++];
151
131
  if (extractProfit)
152
132
  profitNonce = sellerNonces[idx];
@@ -166,22 +146,6 @@ export async function fourBundleBuyFirstMerkle(params) {
166
146
  type: txType
167
147
  }));
168
148
  }
169
- // 授权交易
170
- let approvalTx = null;
171
- if (needApproval && approvalNonce !== undefined) {
172
- const approveInterface = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
173
- const approveData = approveInterface.encodeFunctionData('approve', [TM_ADDRESS, ethers.MaxUint256]);
174
- signPromises.push(seller.signTransaction({
175
- to: tokenAddress,
176
- data: approveData,
177
- value: 0n,
178
- nonce: approvalNonce,
179
- gasLimit: 80000n,
180
- gasPrice,
181
- chainId: chainIdNum,
182
- type: txType
183
- }));
184
- }
185
149
  // 买入和卖出交易
186
150
  signPromises.push(buyer.signTransaction({
187
151
  ...buyUnsigned,
@@ -222,19 +186,15 @@ export async function fourBundleBuyFirstMerkle(params) {
222
186
  let idx = 0;
223
187
  if (needBribeTx)
224
188
  bribeTx = signedTxs[idx++];
225
- if (needApproval)
226
- approvalTx = signedTxs[idx++];
227
189
  const signedBuy = signedTxs[idx++];
228
190
  const signedSell = signedTxs[idx++];
229
191
  if (extractProfit && profitAmount > 0n)
230
192
  profitTx = signedTxs[idx];
231
193
  nonceManager.clearTemp();
232
- // ✅ 组装交易列表:贿赂 → 授权 → 买入 → 卖出 → 利润
194
+ // ✅ 组装交易列表:贿赂 → 买入 → 卖出 → 利润
233
195
  const allTransactions = [];
234
196
  if (bribeTx)
235
197
  allTransactions.push(bribeTx);
236
- if (approvalTx)
237
- allTransactions.push(approvalTx);
238
198
  allTransactions.push(signedBuy, signedSell);
239
199
  if (profitTx)
240
200
  allTransactions.push(profitTx);
@@ -245,7 +205,6 @@ export async function fourBundleBuyFirstMerkle(params) {
245
205
  sellerAddress: seller.address,
246
206
  buyAmount: ethers.formatEther(buyerFundsWei),
247
207
  sellAmount: ethers.formatUnits(sellAmountWei, decimals),
248
- hasApproval: !!approvalTx,
249
208
  profitAmount: extractProfit ? ethers.formatEther(profitAmount) : undefined
250
209
  }
251
210
  };
@@ -18,7 +18,6 @@ export interface FlapBuyFirstConfig extends CommonBundleConfig {
18
18
  skipQuoteOnError?: boolean;
19
19
  waitForConfirmation?: boolean;
20
20
  waitTimeoutMs?: number;
21
- skipApprovalCheck?: boolean;
22
21
  }
23
22
  export interface FlapBundleBuyFirstSignParams {
24
23
  chain: FlapChain;
@@ -7,10 +7,8 @@ import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
8
8
  import { FLAP_PORTAL_ADDRESSES } from '../constants.js';
9
9
  import { PROFIT_CONFIG, ADDRESSES, ZERO_ADDRESS } from '../../utils/constants.js';
10
- import { ERC20_BALANCE_ABI, ERC20_ALLOWANCE_ABI, V2_ROUTER_QUOTE_ABI } from '../../abis/common.js';
10
+ import { ERC20_BALANCE_ABI, V2_ROUTER_QUOTE_ABI } from '../../abis/common.js';
11
11
  import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA, PORTAL_ABI } from './config.js';
12
- // ✅ ABI 别名
13
- const APPROVE_INTERFACE = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
14
12
  // ==================== 链常量 ====================
15
13
  const BSC_PANCAKE_V2_ROUTER = ADDRESSES.BSC.PancakeV2Router;
16
14
  const BSC_WBNB = ADDRESSES.BSC.WBNB;
@@ -105,7 +103,7 @@ export async function flapBundleBuyFirstMerkle(params) {
105
103
  ]);
106
104
  const { buyerFundsWei, buyerBalance } = buyerFundsResult;
107
105
  const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
108
- // ✅ 获取报价(已移除滑点保护)
106
+ // ✅ 获取报价
109
107
  const quoteResult = await quoteBuyerOutput({
110
108
  portalAddress: chainContext.portalAddress,
111
109
  tokenAddress,
@@ -144,29 +142,15 @@ export async function flapBundleBuyFirstMerkle(params) {
144
142
  }),
145
143
  estimateSellFunds(portalSeller, tokenAddress, sellAmountWei, outputToken)
146
144
  ]);
147
- // ✅ 修复:先构建授权交易(会消耗 nonce),再规划其他 nonce
148
- const approvalTx = await buildApprovalTransaction({
149
- tokenAddress,
150
- provider: chainContext.provider,
151
- seller,
152
- decimals: sellerInfo.decimals,
153
- portalAddress: chainContext.portalAddress,
154
- chainId: chainContext.chainId,
155
- config,
156
- nonceManager,
157
- gasPrice,
158
- txType
159
- });
160
145
  // ✅ 获取贿赂金额
161
146
  const bribeAmount = getBribeAmount(config);
162
147
  const needBribeTx = bribeAmount > 0n;
163
- // ✅ 修复:根据实际的授权情况规划 nonce(包含贿赂交易)
148
+ // ✅ 规划 nonce(已移除授权交易)
164
149
  const noncePlan = await planNonces({
165
150
  buyer,
166
151
  seller,
167
- approvalExists: approvalTx !== null, // ✅ 使用实际值
168
152
  extractProfit: true,
169
- needBribeTx, // ✅ 新增:是否需要贿赂交易
153
+ needBribeTx,
170
154
  sameAddress,
171
155
  nonceManager
172
156
  });
@@ -228,12 +212,10 @@ export async function flapBundleBuyFirstMerkle(params) {
228
212
  txType
229
213
  });
230
214
  nonceManager.clearTemp();
231
- // ✅ 组装顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
215
+ // ✅ 组装顺序:贿赂 → 买入 → 卖出 → 利润
232
216
  const allTransactions = [];
233
217
  if (bribeTx)
234
218
  allTransactions.push(bribeTx);
235
- if (approvalTx)
236
- allTransactions.push(approvalTx);
237
219
  allTransactions.push(signedBuy, signedSell);
238
220
  if (profitTx)
239
221
  allTransactions.push(profitTx);
@@ -321,12 +303,11 @@ async function quoteBuyerOutput({ portalAddress, tokenAddress, buyerFundsWei, pr
321
303
  throw new Error(`买入报价失败: ${error}`);
322
304
  }
323
305
  }
324
- // ✅ 已移除滑点保护:minOutToken 固定为 0
325
306
  const sellAmountWei = quotedToken;
326
307
  if (sellAmountWei <= 0n) {
327
308
  throw new Error('卖方卖出数量为 0:报价失败');
328
309
  }
329
- return { quotedToken, minOutToken: 0n, sellAmountWei };
310
+ return { quotedToken, sellAmountWei };
330
311
  }
331
312
  async function ensureSellerBalance({ tokenAddress, provider, seller, sellAmountWei, skipBalanceCheck }) {
332
313
  const erc20 = new Contract(tokenAddress, ERC20_BALANCE_ABI, provider);
@@ -339,25 +320,6 @@ async function ensureSellerBalance({ tokenAddress, provider, seller, sellAmountW
339
320
  }
340
321
  return { decimals };
341
322
  }
342
- async function buildApprovalTransaction({ tokenAddress, provider, seller, decimals, portalAddress, chainId, config, nonceManager, gasPrice, txType }) {
343
- const allowanceContract = new Contract(tokenAddress, ERC20_ALLOWANCE_ABI, provider);
344
- const currentAllowance = await allowanceContract.allowance(seller.address, portalAddress);
345
- const approvalThreshold = ethers.parseUnits('1000000000', decimals);
346
- if (currentAllowance >= approvalThreshold) {
347
- return null;
348
- }
349
- const approvalNonce = await nonceManager.getNextNonce(seller);
350
- return await seller.signTransaction({
351
- to: tokenAddress,
352
- data: APPROVE_INTERFACE.encodeFunctionData('approve', [portalAddress, ethers.MaxUint256]),
353
- value: 0n,
354
- nonce: approvalNonce,
355
- gasLimit: 80000n,
356
- gasPrice,
357
- chainId,
358
- type: txType
359
- });
360
- }
361
323
  async function estimateSellFunds(portal, tokenAddress, sellAmountWei, outputToken = ZERO_ADDRESS // ✅ 新增:输出代币地址
362
324
  ) {
363
325
  try {
@@ -379,41 +341,37 @@ function calculateProfitAmount(expectedFunds) {
379
341
  return (expectedFunds * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
380
342
  }
381
343
  /**
382
- * ✅ 优化:使用批量 nonce 获取
383
- * 交易顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
344
+ * ✅ 规划 nonce
345
+ * 交易顺序:贿赂 → 买入 → 卖出 → 利润
384
346
  */
385
- async function planNonces({ buyer, seller, approvalExists, extractProfit, needBribeTx, sameAddress, nonceManager }) {
347
+ async function planNonces({ buyer, seller, extractProfit, needBribeTx, sameAddress, nonceManager }) {
386
348
  if (sameAddress) {
387
- // 同一地址:使用 getNextNonceBatch 获取连续 nonce
388
- const txCount = countTruthy([needBribeTx, approvalExists, true, true, extractProfit]);
349
+ // 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
350
+ const txCount = countTruthy([needBribeTx, true, true, extractProfit]);
389
351
  const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
390
352
  let idx = 0;
391
353
  const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
392
- if (approvalExists)
393
- idx++;
394
354
  const buyerNonce = nonces[idx++];
395
355
  const sellerNonce = nonces[idx++];
396
356
  const profitNonce = extractProfit ? nonces[idx] : undefined;
397
357
  return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
398
358
  }
399
- if (needBribeTx || approvalExists || extractProfit) {
400
- // 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
401
- const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, extractProfit]);
402
- // ✅ 优化:并行获取 buyer 和 seller 的 nonce(JSON-RPC 批量请求)
359
+ if (needBribeTx || extractProfit) {
360
+ // 卖方需要多个 nonce:贿赂(可选) + 卖出 + 利润(可选)
361
+ const sellerTxCount = countTruthy([needBribeTx, true, extractProfit]);
362
+ // ✅ 并行获取 buyer 和 seller 的 nonce
403
363
  const [sellerNonces, buyerNonces] = await Promise.all([
404
364
  nonceManager.getNextNonceBatch(seller, sellerTxCount),
405
365
  nonceManager.getNextNoncesForWallets([buyer])
406
366
  ]);
407
367
  let idx = 0;
408
368
  const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
409
- if (approvalExists)
410
- idx++;
411
369
  const sellerNonce = sellerNonces[idx++];
412
370
  const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
413
371
  const buyerNonce = buyerNonces[0];
414
372
  return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
415
373
  }
416
- // ✅ 优化:使用 getNextNoncesForWallets 批量获取(单次网络往返)
374
+ // ✅ 使用 getNextNoncesForWallets 批量获取
417
375
  const nonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
418
376
  return { buyerNonce: nonces[0], sellerNonce: nonces[1] };
419
377
  }
@@ -14,7 +14,6 @@ export interface PancakeBuyFirstSignConfig {
14
14
  chainId?: number;
15
15
  reserveGasBNB?: number;
16
16
  skipQuoteOnError?: boolean;
17
- skipApprovalCheck?: boolean;
18
17
  bribeAmount?: number;
19
18
  }
20
19
  export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
@@ -43,7 +42,6 @@ export interface PancakeBuyFirstConfig extends CommonBundleConfig {
43
42
  reserveGasBNB?: number;
44
43
  waitForConfirmation?: boolean;
45
44
  waitTimeoutMs?: number;
46
- skipApprovalCheck?: boolean;
47
45
  }
48
46
  export interface PancakeBundleBuyFirstSignParams {
49
47
  buyerPrivateKey: string;
@@ -74,7 +72,6 @@ export type PancakeBuyFirstResult = {
74
72
  sellerAddress: string;
75
73
  buyAmount: string;
76
74
  sellAmount: string;
77
- hasApproval?: boolean;
78
75
  profitAmount?: string;
79
76
  };
80
77
  };
@@ -7,7 +7,7 @@ import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { NonceManager, getDeadline } from '../utils/bundle-helpers.js';
8
8
  import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA, ZERO_ADDRESS } from '../utils/constants.js';
9
9
  import { quoteV2, quoteV3, getTokenToNativeQuote, getWrappedNativeAddress } from '../utils/quote-helpers.js';
10
- import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI, ERC20_ALLOWANCE_ABI } from '../abis/common.js';
10
+ import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI } from '../abis/common.js';
11
11
  /**
12
12
  * 获取 Gas Limit
13
13
  */
@@ -44,8 +44,6 @@ const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
44
44
  // 常量
45
45
  const FLAT_FEE = 0n;
46
46
  const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
47
- // ✅ getDeadline 从 bundle-helpers.js 导入
48
- const APPROVE_INTERFACE = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
49
47
  export async function pancakeBundleBuyFirstMerkle(params) {
50
48
  const { buyerPrivateKey, sellerPrivateKey, tokenAddress, routeParams, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals = 18, startNonces // ✅ 可选:前端预获取的 nonces
51
49
  } = params;
@@ -70,7 +68,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
70
68
  buyerFundsWei: buyerFundsInfo.buyerFundsWei,
71
69
  provider: context.provider
72
70
  });
73
- const decimals = await getTokenDecimals(context.provider, tokenAddress);
74
71
  const swapUnsigned = await buildRouteTransactions({
75
72
  routeParams,
76
73
  buyerFundsWei: buyerFundsInfo.buyerFundsWei,
@@ -95,19 +92,7 @@ export async function pancakeBundleBuyFirstMerkle(params) {
95
92
  const gasPrice = await getGasPrice(context.provider, config);
96
93
  const txType = config.txType ?? 0;
97
94
  const nonceManager = new NonceManager(context.provider);
98
- const approvalTx = await ensureSellerApproval({
99
- tokenAddress,
100
- seller,
101
- provider: context.provider,
102
- decimals,
103
- chainId: context.chainId,
104
- config,
105
- nonceManager,
106
- gasPrice,
107
- txType
108
- });
109
95
  // ✅ 修复:基于买入金额估算利润,而不是基于卖出预估(因为代币可能还没有流动性)
110
- // 在先买后卖的场景中,卖出收益 ≈ 买入金额(忽略滑点和手续费)
111
96
  const estimatedProfitFromSell = await estimateProfitAmount({
112
97
  provider: context.provider,
113
98
  tokenAddress,
@@ -124,12 +109,11 @@ export async function pancakeBundleBuyFirstMerkle(params) {
124
109
  const needBribeTx = bribeAmount > 0n;
125
110
  // ✅ 如果前端传入了 startNonces,直接使用(性能优化,避免 nonce 冲突)
126
111
  const noncePlan = startNonces && startNonces.length >= (sameAddress ? 1 : 2)
127
- ? buildNoncePlanFromStartNonces(startNonces, sameAddress, !!approvalTx, profitAmount > 0n, needBribeTx)
112
+ ? buildNoncePlanFromStartNonces(startNonces, sameAddress, profitAmount > 0n, needBribeTx)
128
113
  : await planNonces({
129
114
  buyer,
130
115
  seller,
131
116
  sameAddress,
132
- approvalExists: !!approvalTx,
133
117
  extractProfit: profitAmount > 0n,
134
118
  needBribeTx,
135
119
  nonceManager
@@ -187,12 +171,10 @@ export async function pancakeBundleBuyFirstMerkle(params) {
187
171
  provider: context.provider,
188
172
  buyerAddress: buyer.address
189
173
  });
190
- // ✅ 组装顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
174
+ // ✅ 组装顺序:贿赂 → 买入 → 卖出 → 利润
191
175
  const allTransactions = [];
192
176
  if (bribeTx)
193
177
  allTransactions.push(bribeTx);
194
- if (approvalTx)
195
- allTransactions.push(approvalTx);
196
178
  allTransactions.push(signedBuy, signedSell);
197
179
  if (profitTx)
198
180
  allTransactions.push(profitTx);
@@ -205,7 +187,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
205
187
  ? ethers.formatEther(buyerFundsInfo.buyerFundsWei)
206
188
  : ethers.formatUnits(buyerFundsInfo.buyerFundsWei, quoteTokenDecimals),
207
189
  sellAmount: quoteResult.quotedTokenOut.toString(),
208
- hasApproval: !!approvalTx,
209
190
  profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
210
191
  }
211
192
  };
@@ -307,10 +288,6 @@ async function quoteTokenOutput({ routeParams, buyerFundsWei, provider }) {
307
288
  }
308
289
  return { quotedTokenOut: result.amountOut };
309
290
  }
310
- async function getTokenDecimals(provider, tokenAddress) {
311
- const erc20 = new Contract(tokenAddress, ['function decimals() view returns (uint8)'], provider);
312
- return await erc20.decimals();
313
- }
314
291
  /**
315
292
  * ✅ 使用 quote-helpers 统一报价(卖出 → 原生代币)
316
293
  */
@@ -321,7 +298,6 @@ async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, rout
321
298
  return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
322
299
  }
323
300
  function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
324
- // ✅ 已移除滑点保护:直接使用报价金额
325
301
  const estimatedBuyerNeed = quotedNative;
326
302
  const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
327
303
  if (buyerBalance < buyerNeedTotal) {
@@ -332,30 +308,6 @@ function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
332
308
  : buyerBalance - reserveGas;
333
309
  return { buyerNeedTotal, maxBuyerValue };
334
310
  }
335
- async function ensureSellerApproval({ tokenAddress, seller, provider, decimals, chainId, config, nonceManager, gasPrice, txType }) {
336
- if (config.skipApprovalCheck) {
337
- return null;
338
- }
339
- const erc20Contract = new Contract(tokenAddress, ERC20_ALLOWANCE_ABI, provider);
340
- // ✅ 使用官方 V2 Router 作为授权目标
341
- const currentAllowance = await erc20Contract.allowance(seller.address, PANCAKE_V2_ROUTER_ADDRESS);
342
- // ✅ 阈值:MaxUint256 / 2,如果授权额度超过这个值,认为是"无限授权"
343
- const halfMaxUint = ethers.MaxUint256 / 2n;
344
- if (currentAllowance >= halfMaxUint) {
345
- return null;
346
- }
347
- const approvalNonce = await nonceManager.getNextNonce(seller);
348
- return await seller.signTransaction({
349
- to: tokenAddress,
350
- data: APPROVE_INTERFACE.encodeFunctionData('approve', [PANCAKE_V2_ROUTER_ADDRESS, ethers.MaxUint256]),
351
- value: 0n,
352
- nonce: approvalNonce,
353
- gasLimit: 80000n,
354
- gasPrice,
355
- chainId,
356
- type: txType
357
- });
358
- }
359
311
  async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountToken, buyer, seller, tokenAddress, useNativeToken = true }) {
360
312
  const deadline = getDeadline();
361
313
  // ✅ ERC20 购买时,value 只需要 FLAT_FEE
@@ -425,25 +377,23 @@ async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, r
425
377
  }
426
378
  /**
427
379
  * ✅ 规划 nonce
428
- * 交易顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
380
+ * 交易顺序:贿赂 → 买入 → 卖出 → 利润
429
381
  */
430
- async function planNonces({ buyer, seller, sameAddress, approvalExists, extractProfit, needBribeTx, nonceManager }) {
382
+ async function planNonces({ buyer, seller, sameAddress, extractProfit, needBribeTx, nonceManager }) {
431
383
  if (sameAddress) {
432
- // 同一地址:贿赂(可选) + 授权(可选) + 买入 + 卖出 + 利润(可选)
433
- const txCount = countTruthy([needBribeTx, approvalExists, true, true, extractProfit]);
384
+ // 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
385
+ const txCount = countTruthy([needBribeTx, true, true, extractProfit]);
434
386
  const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
435
387
  let idx = 0;
436
388
  const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
437
- if (approvalExists)
438
- idx++;
439
389
  const buyerNonce = nonces[idx++];
440
390
  const sellerNonce = nonces[idx++];
441
391
  const profitNonce = extractProfit ? nonces[idx] : undefined;
442
392
  return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
443
393
  }
444
- if (needBribeTx || approvalExists || extractProfit) {
445
- // 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
446
- const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, extractProfit]);
394
+ if (needBribeTx || extractProfit) {
395
+ // 卖方需要多个 nonce:贿赂(可选) + 卖出 + 利润(可选)
396
+ const sellerTxCount = countTruthy([needBribeTx, true, extractProfit]);
447
397
  // ✅ 并行获取 seller 和 buyer 的 nonce
448
398
  const [sellerNonces, buyerNonce] = await Promise.all([
449
399
  nonceManager.getNextNonceBatch(seller, sellerTxCount),
@@ -451,8 +401,6 @@ async function planNonces({ buyer, seller, sameAddress, approvalExists, extractP
451
401
  ]);
452
402
  let idx = 0;
453
403
  const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
454
- if (approvalExists)
455
- idx++;
456
404
  const sellerNonce = sellerNonces[idx++];
457
405
  const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
458
406
  return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
@@ -520,14 +468,12 @@ function countTruthy(values) {
520
468
  * ✅ 从前端传入的 startNonces 构建 NoncePlan(用于性能优化,避免 nonce 冲突)
521
469
  * 顺序:同地址时 [baseNonce],不同地址时 [sellerNonce, buyerNonce]
522
470
  */
523
- function buildNoncePlanFromStartNonces(startNonces, sameAddress, approvalExists, profitNeeded, needBribeTx) {
471
+ function buildNoncePlanFromStartNonces(startNonces, sameAddress, profitNeeded, needBribeTx) {
524
472
  if (sameAddress) {
525
- // 同一地址:所有交易使用递增 nonce
473
+ // 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
526
474
  let idx = 0;
527
475
  const baseNonce = startNonces[0];
528
476
  const bribeNonce = needBribeTx ? baseNonce + idx++ : undefined;
529
- if (approvalExists)
530
- idx++;
531
477
  const buyerNonce = baseNonce + idx++;
532
478
  const sellerNonce = baseNonce + idx++;
533
479
  const profitNonce = profitNeeded ? baseNonce + idx : undefined;
@@ -537,8 +483,6 @@ function buildNoncePlanFromStartNonces(startNonces, sameAddress, approvalExists,
537
483
  let sellerIdx = 0;
538
484
  const sellerBaseNonce = startNonces[0];
539
485
  const bribeNonce = needBribeTx ? sellerBaseNonce + sellerIdx++ : undefined;
540
- if (approvalExists)
541
- sellerIdx++;
542
486
  const sellerNonce = sellerBaseNonce + sellerIdx++;
543
487
  const profitNonce = profitNeeded ? sellerBaseNonce + sellerIdx : undefined;
544
488
  const buyerNonce = startNonces[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.29",
3
+ "version": "1.4.31",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",