four-flap-meme-sdk 1.1.87 → 1.1.89

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.
@@ -305,7 +305,7 @@ export async function batchBuyWithBundleMerkle(params) {
305
305
  * ✅ 自动检查授权,智能处理授权+卖出
306
306
  */
307
307
  export async function batchSellWithBundleMerkle(params) {
308
- const { privateKeys, sellAmounts, tokenAddress, config } = params;
308
+ const { privateKeys, sellAmounts, tokenAddress, minOutputAmounts, config } = params;
309
309
  if (privateKeys.length === 0 || sellAmounts.length !== privateKeys.length) {
310
310
  throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
311
311
  }
@@ -321,6 +321,14 @@ export async function batchSellWithBundleMerkle(params) {
321
321
  const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
322
322
  const sellers = privateKeys.map((k) => new Wallet(k, provider));
323
323
  const amountsWei = sellAmounts.map((a) => ethers.parseUnits(a, 18));
324
+ // ✅ 解析 minOutputAmounts
325
+ let minOuts;
326
+ if (minOutputAmounts && minOutputAmounts.length === sellers.length) {
327
+ minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
328
+ }
329
+ else {
330
+ minOuts = new Array(sellers.length).fill(0n);
331
+ }
324
332
  // ✅ Step 1: 使用 Multicall3 批量检查授权状态
325
333
  const ERC20_ABI = [
326
334
  'function approve(address spender, uint256 amount) returns (bool)',
@@ -353,7 +361,7 @@ export async function batchSellWithBundleMerkle(params) {
353
361
  const nonceManager = new NonceManager(provider);
354
362
  const signedTxs = [];
355
363
  const tm2Contracts = sellers.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
356
- const unsignedSells = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], 0n)));
364
+ const unsignedSells = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], minOuts[i])));
357
365
  // ✅ 使用前端传入的 gasLimit,否则使用默认值
358
366
  const finalGasLimit = getGasLimit(config);
359
367
  const gasLimits = new Array(sellers.length).fill(finalGasLimit);
@@ -368,6 +376,29 @@ export async function batchSellWithBundleMerkle(params) {
368
376
  type: getTxType(config)
369
377
  })));
370
378
  signedTxs.push(...signedList);
379
+ // ✅ 基于 minFunds 为每个钱包添加利润转账
380
+ const extractProfit = shouldExtractProfit(config);
381
+ if (extractProfit && minOuts.length > 0) {
382
+ // 为每个钱包添加利润转账(如果 minOut > 0)
383
+ for (let i = 0; i < sellers.length; i++) {
384
+ if (minOuts[i] > 0n) { // ⚠️ 保留这个判断:只对设置了 minOut 的钱包提取利润
385
+ const { profit } = calculateProfit(minOuts[i], config);
386
+ if (profit > 0n) {
387
+ const profitNonce = await nonceManager.getNextNonce(sellers[i]);
388
+ const profitTx = await sellers[i].signTransaction({
389
+ to: config.profitRecipient,
390
+ value: profit,
391
+ nonce: profitNonce,
392
+ gasPrice,
393
+ gasLimit: 21000n,
394
+ chainId: 56,
395
+ type: getTxType(config)
396
+ });
397
+ signedTxs.push(profitTx);
398
+ }
399
+ }
400
+ }
401
+ }
371
402
  // ✅ 清理临时 nonce 缓存
372
403
  nonceManager.clearTemp();
373
404
  // ✅ 简化返回:只返回签名交易
@@ -90,6 +90,7 @@ export type FourBatchSellMerkleParams = {
90
90
  privateKeys: string[];
91
91
  sellAmounts: string[];
92
92
  tokenAddress: string;
93
+ minOutputAmounts?: (string | bigint)[];
93
94
  config: FourBundleMerkleConfig;
94
95
  };
95
96
  /** ✅ 批量卖出结果(简化版) */
@@ -261,7 +261,7 @@ export async function batchBuyWithBundleMerkle(params) {
261
261
  * Flap Protocol: 批量卖出(Merkle 版本)
262
262
  */
263
263
  export async function batchSellWithBundleMerkle(params) {
264
- const { chain, privateKeys, sellAmounts, tokenAddress, config } = params;
264
+ const { chain, privateKeys, sellAmounts, tokenAddress, minOutputAmounts, config } = params;
265
265
  if (privateKeys.length === 0 || sellAmounts.length !== privateKeys.length) {
266
266
  throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
267
267
  }
@@ -279,7 +279,34 @@ export async function batchSellWithBundleMerkle(params) {
279
279
  const nonceManager = new NonceManager(provider);
280
280
  const wallets = privateKeys.map(k => new Wallet(k, provider));
281
281
  const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, 18));
282
- const minOuts = new Array(wallets.length).fill(0n);
282
+ // 自动获取报价以计算预期收益和利润
283
+ const portal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
284
+ // 并行获取所有报价
285
+ const quotedOutputs = await Promise.all(amountsWei.map(async (amount, index) => {
286
+ try {
287
+ const quoted = await portal.quoteExactInput({
288
+ inputToken: tokenAddress,
289
+ outputToken: '0x0000000000000000000000000000000000000000',
290
+ inputAmount: amount
291
+ });
292
+ console.log(`📊 卖出报价 [${index}]: ${ethers.formatEther(amount)} 代币 → ${ethers.formatEther(quoted)} BNB`);
293
+ return quoted;
294
+ }
295
+ catch (error) {
296
+ console.warn(`⚠️ 报价失败 [${index}]:`, error);
297
+ return 0n;
298
+ }
299
+ }));
300
+ // ✅ 使用报价结果或用户提供的 minOutputAmounts
301
+ let minOuts;
302
+ if (minOutputAmounts && minOutputAmounts.length === wallets.length) {
303
+ // 用户提供了 minOutputAmounts,优先使用
304
+ minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
305
+ }
306
+ else {
307
+ // 使用报价结果作为 minOutputAmount(保守起见,使用 95% 的报价金额)
308
+ minOuts = quotedOutputs.map(quoted => quoted * 95n / 100n);
309
+ }
283
310
  const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
284
311
  const unsignedList = await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
285
312
  inputToken: tokenAddress,
@@ -302,6 +329,44 @@ export async function batchSellWithBundleMerkle(params) {
302
329
  type: getTxType(config)
303
330
  })));
304
331
  signedTxs.push(...signedList);
332
+ // ✅ 基于报价金额为每个钱包添加利润转账
333
+ const extractProfit = shouldExtractProfit(config);
334
+ console.log(`\n💰 利润提取配置: extractProfit=${extractProfit}, profitRecipient=${config.profitRecipient}, quotedOutputs.length=${quotedOutputs.length}`);
335
+ if (extractProfit && quotedOutputs.length > 0) {
336
+ let totalProfitAdded = 0;
337
+ // 为每个钱包添加利润转账(基于完整报价金额,而非 minOut)
338
+ for (let i = 0; i < wallets.length; i++) {
339
+ console.log(`\n🔍 钱包 [${i}]: quotedOutput=${ethers.formatEther(quotedOutputs[i])} BNB`);
340
+ if (quotedOutputs[i] > 0n) { // 只对报价成功的交易提取利润
341
+ const { profit } = calculateProfit(quotedOutputs[i], config);
342
+ console.log(` 💵 计算利润: ${ethers.formatEther(profit)} BNB (${config.profitRateBps || 30} bps)`);
343
+ if (profit > 0n) {
344
+ const profitNonce = await nonceManager.getNextNonce(wallets[i]);
345
+ const profitTx = await wallets[i].signTransaction({
346
+ to: config.profitRecipient,
347
+ value: profit,
348
+ nonce: profitNonce,
349
+ gasPrice,
350
+ gasLimit: 21000n,
351
+ chainId,
352
+ type: getTxType(config)
353
+ });
354
+ signedTxs.push(profitTx);
355
+ totalProfitAdded++;
356
+ console.log(` ✅ 已添加利润转账 (nonce: ${profitNonce})`);
357
+ }
358
+ }
359
+ else {
360
+ console.log(` ⚠️ 跳过:报价为 0`);
361
+ }
362
+ }
363
+ console.log(`\n📊 总共添加了 ${totalProfitAdded} 个利润转账\n`);
364
+ }
365
+ else {
366
+ if (!extractProfit) {
367
+ console.log(`⚠️ 未提取利润:profitRecipient=${config.profitRecipient}, profitRateBps=${config.profitRateBps}\n`);
368
+ }
369
+ }
305
370
  // ✅ 清理临时 nonce 缓存
306
371
  nonceManager.clearTemp();
307
372
  // ✅ 简化返回:只返回签名交易
@@ -92,10 +92,32 @@ export async function flapPrivateSellMerkle(params) {
92
92
  const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
93
93
  const gasMultiplier = config.gasLimitMultiplier ?? 1.0;
94
94
  const amountWei = ethers.parseUnits(amount, 18);
95
- const minOut = minOutputAmount
96
- ? (typeof minOutputAmount === 'string' ? ethers.parseEther(minOutputAmount) : minOutputAmount)
97
- : 0n;
95
+ // 自动获取报价以计算预期收益和利润
98
96
  const portal = new ethers.Contract(portalAddr, PORTAL_ABI, wallet);
97
+ let quotedOutput;
98
+ let minOut;
99
+ if (minOutputAmount) {
100
+ // 用户提供了 minOutputAmount,优先使用
101
+ minOut = typeof minOutputAmount === 'string' ? ethers.parseEther(minOutputAmount) : minOutputAmount;
102
+ quotedOutput = minOut; // 使用用户提供的值作为报价
103
+ }
104
+ else {
105
+ // 自动获取报价
106
+ try {
107
+ quotedOutput = await portal.quoteExactInput({
108
+ inputToken: tokenAddress,
109
+ outputToken: '0x0000000000000000000000000000000000000000',
110
+ inputAmount: amountWei
111
+ });
112
+ // 使用报价的 95% 作为 minOutputAmount(保守策略)
113
+ minOut = quotedOutput * 95n / 100n;
114
+ }
115
+ catch (error) {
116
+ // 报价失败,使用 0
117
+ quotedOutput = 0n;
118
+ minOut = 0n;
119
+ }
120
+ }
99
121
  const unsigned = await portal.swapExactInput.populateTransaction({
100
122
  inputToken: tokenAddress,
101
123
  outputToken: '0x0000000000000000000000000000000000000000',
@@ -117,10 +139,10 @@ export async function flapPrivateSellMerkle(params) {
117
139
  };
118
140
  const signed = await wallet.signTransaction(req);
119
141
  signedTxs.push(signed);
120
- // ✅ 基于 minFunds 计算利润
142
+ // ✅ 基于报价金额计算利润
121
143
  const extractProfit = shouldExtractProfit(config);
122
- if (extractProfit) {
123
- const { profit } = calculateProfit(minOut, config);
144
+ if (extractProfit && quotedOutput > 0n) {
145
+ const { profit } = calculateProfit(quotedOutput, config);
124
146
  if (profit > 0n) {
125
147
  const profitTx = await wallet.signTransaction({
126
148
  to: config.profitRecipient,
@@ -239,12 +261,24 @@ export async function flapBatchPrivateSellMerkle(params) {
239
261
  const nonceManager = new NonceManager(provider);
240
262
  const wallets = privateKeys.map(k => new Wallet(k, provider));
241
263
  const amountsWei = amounts.map(a => ethers.parseUnits(a, 18));
264
+ // ✅ 自动获取报价以计算预期收益和利润
265
+ const portal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
266
+ // 并行获取所有报价
267
+ const quotedOutputs = await Promise.all(amountsWei.map(amount => portal.quoteExactInput({
268
+ inputToken: tokenAddress,
269
+ outputToken: '0x0000000000000000000000000000000000000000',
270
+ inputAmount: amount
271
+ }).catch(() => 0n) // 如果报价失败,返回 0n
272
+ ));
273
+ // ✅ 使用报价结果或用户提供的 minOutputAmounts
242
274
  let minOuts;
243
275
  if (minOutputAmounts && minOutputAmounts.length === wallets.length) {
276
+ // 用户提供了 minOutputAmounts,优先使用
244
277
  minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
245
278
  }
246
279
  else {
247
- minOuts = new Array(wallets.length).fill(0n);
280
+ // 使用报价结果作为 minOutputAmount(保守起见,使用 95% 的报价金额)
281
+ minOuts = quotedOutputs.map(quoted => quoted * 95n / 100n);
248
282
  }
249
283
  const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
250
284
  const unsignedList = await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
@@ -267,13 +301,13 @@ export async function flapBatchPrivateSellMerkle(params) {
267
301
  type: getTxType(config)
268
302
  })));
269
303
  signedTxs.push(...signedList);
270
- // ✅ 基于 minFunds 为每个钱包添加利润转账
304
+ // ✅ 基于报价金额为每个钱包添加利润转账
271
305
  const extractProfit = shouldExtractProfit(config);
272
- if (extractProfit && minOuts.length > 0) {
273
- // 为每个钱包添加利润转账(如果 minOut > 0
306
+ if (extractProfit && quotedOutputs.length > 0) {
307
+ // 为每个钱包添加利润转账(基于完整报价金额,而非 minOut)
274
308
  for (let i = 0; i < wallets.length; i++) {
275
- if (minOuts[i] > 0n) { // ⚠️ 保留这个判断:只对设置了 minOut 的钱包提取利润
276
- const { profit } = calculateProfit(minOuts[i], config);
309
+ if (quotedOutputs[i] > 0n) { // 只对报价成功的交易提取利润
310
+ const { profit } = calculateProfit(quotedOutputs[i], config);
277
311
  if (profit > 0n) {
278
312
  const profitNonce = await nonceManager.getNextNonce(wallets[i]);
279
313
  const profitTx = await wallets[i].signTransaction({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.1.87",
3
+ "version": "1.1.89",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",