four-flap-meme-sdk 1.4.30 → 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
  }
@@ -93,7 +93,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
93
93
  const txType = config.txType ?? 0;
94
94
  const nonceManager = new NonceManager(context.provider);
95
95
  // ✅ 修复:基于买入金额估算利润,而不是基于卖出预估(因为代币可能还没有流动性)
96
- // 在先买后卖的场景中,卖出收益 ≈ 买入金额(忽略滑点和手续费)
97
96
  const estimatedProfitFromSell = await estimateProfitAmount({
98
97
  provider: context.provider,
99
98
  tokenAddress,
@@ -299,7 +298,6 @@ async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, rout
299
298
  return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
300
299
  }
301
300
  function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
302
- // ✅ 已移除滑点保护:直接使用报价金额
303
301
  const estimatedBuyerNeed = quotedNative;
304
302
  const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
305
303
  if (buyerBalance < buyerNeedTotal) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.30",
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",