four-flap-meme-sdk 4.0.13 → 4.1.0

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.
Files changed (26) hide show
  1. package/dist/bundle/four-meme/core/sell-flow.js +3 -6
  2. package/dist/bundle/four-meme/private/sell.js +3 -3
  3. package/dist/chains/bsc/pancake/bundle-buy-first-build.d.ts +5 -3
  4. package/dist/chains/bsc/pancake/bundle-buy-first-build.js +25 -23
  5. package/dist/chains/bsc/pancake/bundle-buy-first-multi-wallet.js +6 -0
  6. package/dist/chains/bsc/pancake/bundle-buy-first-types.d.ts +4 -0
  7. package/dist/chains/bsc/pancake/bundle-buy-first.js +6 -0
  8. package/dist/chains/bsc/pancake/bundle-swap-types.d.ts +4 -0
  9. package/dist/chains/bsc/pancake/hop-chain.js +2 -0
  10. package/dist/chains/bsc/pancake/quote.d.ts +10 -1
  11. package/dist/chains/bsc/pancake/quote.js +30 -0
  12. package/dist/chains/bsc/pancake/quote.test.d.ts +1 -0
  13. package/dist/chains/bsc/pancake/quote.test.js +16 -0
  14. package/dist/chains/bsc/pancake/swap-batch.js +16 -7
  15. package/dist/chains/bsc/pancake/swap-build.d.ts +1 -1
  16. package/dist/chains/bsc/pancake/swap-build.js +9 -18
  17. package/dist/chains/bsc/pancake/swap-quick.js +31 -38
  18. package/dist/chains/bsc/pancake/swap-single.js +10 -1
  19. package/dist/chains/eni/platforms/daoaas/portal-direct-submit.js +2 -6
  20. package/dist/domains/flap/portal-bundle-merkle/core-helpers.d.ts +1 -1
  21. package/dist/domains/flap/portal-bundle-merkle/core-helpers.js +5 -3
  22. package/dist/domains/flap/portal-bundle-merkle/private/helpers.js +1 -1
  23. package/dist/domains/flap/portal-bundle-merkle/private/sell.js +16 -19
  24. package/dist/domains/flap/portal-bundle-merkle/swap-helpers.js +2 -0
  25. package/dist/domains/flap/portal-bundle-merkle/swap-quick-batch.js +12 -29
  26. package/package.json +1 -1
@@ -131,8 +131,7 @@ async function resolveSellOutputs(params) {
131
131
  try {
132
132
  const result = await trySell('BSC', rpcUrl, tokenAddress, amountsWei[0]);
133
133
  const quotedOutputs = [result.funds];
134
- // ✅ 已移除滑点保护:minOuts 固定为 0
135
- const minOuts = [0n];
134
+ const minOuts = [(result.funds * 9900n) / 10000n];
136
135
  return { minOuts, quotedOutputs };
137
136
  }
138
137
  catch {
@@ -165,8 +164,7 @@ async function resolveSellOutputs(params) {
165
164
  }
166
165
  return 0n;
167
166
  });
168
- // ✅ 已移除滑点保护:minOuts 固定为 0
169
- const minOuts = quotedOutputs.map(() => 0n);
167
+ const minOuts = quotedOutputs.map((q) => (q * 9900n) / 10000n);
170
168
  return { minOuts, quotedOutputs };
171
169
  }
172
170
  catch {
@@ -180,8 +178,7 @@ async function resolveSellOutputs(params) {
180
178
  return 0n;
181
179
  }
182
180
  }));
183
- // ✅ 已移除滑点保护:minOuts 固定为 0
184
- const minOuts = quotedOutputs.map(() => 0n);
181
+ const minOuts = quotedOutputs.map((q) => (q * 9900n) / 10000n);
185
182
  return { minOuts, quotedOutputs };
186
183
  }
187
184
  }
@@ -31,7 +31,7 @@ export async function fourPrivateSellMerkle(params) {
31
31
  const [gasPrice, sellNonce, sellUnsigned] = await Promise.all([
32
32
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
33
33
  wallet.getNonce(),
34
- tm2.sellToken.populateTransaction(0n, tokenAddress, amountWei, 0n), // ✅ 已移除滑点保护:minOut 固定为 0
34
+ tm2.sellToken.populateTransaction(0n, tokenAddress, amountWei, minOut),
35
35
  ]);
36
36
  // ✅ 已移除授权检查(前端负责确保授权)
37
37
  // ✅ 利润计算(同步)
@@ -133,8 +133,8 @@ export async function fourBatchPrivateSellMerkle(params) {
133
133
  }
134
134
  })),
135
135
  ]);
136
- // ✅ 已移除滑点保护:minOuts 固定为 0
137
- const minOuts = new Array(wallets.length).fill(0n);
136
+ // 报价 × (1 - 100bps)
137
+ const minOuts = quotedOutputs.map((q) => (q * 9900n) / 10000n);
138
138
  // ✅ 计算总利润和找出收益最高的钱包(同步计算)
139
139
  const extractProfit = shouldExtractProfit();
140
140
  let totalProfit = 0n;
@@ -6,20 +6,22 @@ import type { MultiSwapTxParams, MultiSwapUnsignedResult, RouteParams } from './
6
6
  /**
7
7
  * ✅ 构建多笔买入和卖出交易
8
8
  */
9
- export declare function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmountsWei, buyer, seller, useNativeToken, v2RouterAddress, }: MultiSwapTxParams): Promise<MultiSwapUnsignedResult>;
10
- export declare function buildSingleBuyTx({ routeParams, buyAmount, buyer, useNativeToken, deadline, v2RouterAddress, }: {
9
+ export declare function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmountsWei, buyer, seller, useNativeToken, v2RouterAddress, buyAmountOutMins, sellAmountOutMins, }: MultiSwapTxParams): Promise<MultiSwapUnsignedResult>;
10
+ export declare function buildSingleBuyTx({ routeParams, buyAmount, buyer, useNativeToken, deadline, v2RouterAddress, amountOutMin, }: {
11
11
  routeParams: RouteParams;
12
12
  buyAmount: bigint;
13
13
  buyer: Wallet;
14
14
  useNativeToken: boolean;
15
15
  deadline: bigint;
16
16
  v2RouterAddress?: string;
17
+ amountOutMin?: bigint;
17
18
  }): Promise<ethers.TransactionLike>;
18
- export declare function buildSingleSellTx({ routeParams, sellAmount, seller, useNativeToken, deadline, v2RouterAddress, }: {
19
+ export declare function buildSingleSellTx({ routeParams, sellAmount, seller, useNativeToken, deadline, v2RouterAddress, amountOutMin, }: {
19
20
  routeParams: RouteParams;
20
21
  sellAmount: bigint;
21
22
  seller: Wallet;
22
23
  useNativeToken: boolean;
23
24
  deadline: bigint;
24
25
  v2RouterAddress?: string;
26
+ amountOutMin?: bigint;
25
27
  }): Promise<ethers.TransactionLike>;
@@ -7,8 +7,10 @@ import { FLAT_FEE, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_
7
7
  /**
8
8
  * ✅ 构建多笔买入和卖出交易
9
9
  */
10
- export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmountsWei, buyer, seller, useNativeToken = true, v2RouterAddress, }) {
10
+ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmountsWei, buyer, seller, useNativeToken = true, v2RouterAddress, buyAmountOutMins, sellAmountOutMins, }) {
11
11
  const deadline = getDeadline();
12
+ const buyMinAt = (i) => buyAmountOutMins?.[i] ?? 0n;
13
+ const sellMinAt = (i) => sellAmountOutMins?.[i] ?? 0n;
12
14
  if (routeParams.routeType === 'v2') {
13
15
  const { v2Path } = routeParams;
14
16
  const reversePath = [...v2Path].reverse();
@@ -18,17 +20,17 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
18
20
  // ✅ 根据是否使用原生代币选择不同的函数
19
21
  if (useNativeToken) {
20
22
  // BNB 池子:使用 swapExactETHForTokens / swapExactTokensForETH
21
- const buyUnsignedArray = await Promise.all(buyAmountsWei.map((amount) => {
23
+ const buyUnsignedArray = await Promise.all(buyAmountsWei.map((amount, i) => {
22
24
  const buyValue = amount + FLAT_FEE;
23
- return v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
25
+ return v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(buyMinAt(i), v2Path, buyer.address, deadline, { value: buyValue });
24
26
  }));
25
- const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount) => v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, reversePath, seller.address, deadline)));
27
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount, i) => v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(amount, sellMinAt(i), reversePath, seller.address, deadline)));
26
28
  return { buyUnsignedArray, sellUnsignedArray };
27
29
  }
28
30
  else {
29
31
  // ✅ ERC20 池子(如 USDT):使用 swapExactTokensForTokens
30
- const buyUnsignedArray = await Promise.all(buyAmountsWei.map((amount) => v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, v2Path, buyer.address, deadline)));
31
- const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount) => v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, reversePath, seller.address, deadline)));
32
+ const buyUnsignedArray = await Promise.all(buyAmountsWei.map((amount, i) => v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(amount, buyMinAt(i), v2Path, buyer.address, deadline)));
33
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount, i) => v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(amount, sellMinAt(i), reversePath, seller.address, deadline)));
32
34
  return { buyUnsignedArray, sellUnsignedArray };
33
35
  }
34
36
  }
@@ -38,7 +40,7 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
38
40
  const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
39
41
  const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
40
42
  // 构建多笔买入交易(ERC20 模式 value = 0,因为通过代币授权支付)
41
- const buyUnsignedArray = await Promise.all(buyAmountsWei.map((amount) => {
43
+ const buyUnsignedArray = await Promise.all(buyAmountsWei.map((amount, i) => {
42
44
  const buyValue = useNativeToken ? amount + FLAT_FEE : 0n;
43
45
  const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
44
46
  {
@@ -47,7 +49,7 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
47
49
  fee: v3Fee,
48
50
  recipient: buyer.address,
49
51
  amountIn: amount,
50
- amountOutMinimum: 0n,
52
+ amountOutMinimum: buyMinAt(i),
51
53
  sqrtPriceLimitX96: 0n,
52
54
  },
53
55
  ]);
@@ -56,7 +58,7 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
56
58
  // ✅ 根据是否使用原生代币构建不同的卖出交易
57
59
  if (useNativeToken) {
58
60
  // BNB 池子:卖出后 unwrap WBNB 为 BNB
59
- const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount) => {
61
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount, i) => {
60
62
  const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
61
63
  {
62
64
  tokenIn: v3TokenOut,
@@ -64,18 +66,18 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
64
66
  fee: v3Fee,
65
67
  recipient: PANCAKE_V3_ROUTER_ADDRESS,
66
68
  amountIn: amount,
67
- amountOutMinimum: 0n,
69
+ amountOutMinimum: sellMinAt(i),
68
70
  sqrtPriceLimitX96: 0n,
69
71
  },
70
72
  ]);
71
- const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
73
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [sellMinAt(i), seller.address]);
72
74
  return v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
73
75
  }));
74
76
  return { buyUnsignedArray, sellUnsignedArray };
75
77
  }
76
78
  else {
77
79
  // ✅ ERC20 池子:卖出后直接获得 ERC20
78
- const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount) => {
80
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map((amount, i) => {
79
81
  const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
80
82
  {
81
83
  tokenIn: v3TokenOut,
@@ -83,7 +85,7 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
83
85
  fee: v3Fee,
84
86
  recipient: seller.address, // 直接发送到卖方地址
85
87
  amountIn: amount,
86
- amountOutMinimum: 0n,
88
+ amountOutMinimum: sellMinAt(i),
87
89
  sqrtPriceLimitX96: 0n,
88
90
  },
89
91
  ]);
@@ -95,15 +97,15 @@ export async function buildMultiRouteTransactions({ routeParams, buyAmountsWei,
95
97
  throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
96
98
  }
97
99
  // ✅ 构建单笔买入交易
98
- export async function buildSingleBuyTx({ routeParams, buyAmount, buyer, useNativeToken, deadline, v2RouterAddress, }) {
100
+ export async function buildSingleBuyTx({ routeParams, buyAmount, buyer, useNativeToken, deadline, v2RouterAddress, amountOutMin = 0n, }) {
99
101
  if (routeParams.routeType === 'v2') {
100
102
  const { v2Path } = routeParams;
101
103
  const v2Router = new Contract(v2RouterAddress || PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
102
104
  if (useNativeToken) {
103
- return await v2Router.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyAmount });
105
+ return await v2Router.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(amountOutMin, v2Path, buyer.address, deadline, { value: buyAmount });
104
106
  }
105
107
  else {
106
- return await v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, 0n, v2Path, buyer.address, deadline);
108
+ return await v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, amountOutMin, v2Path, buyer.address, deadline);
107
109
  }
108
110
  }
109
111
  if (routeParams.routeType === 'v3-single') {
@@ -118,7 +120,7 @@ export async function buildSingleBuyTx({ routeParams, buyAmount, buyer, useNativ
118
120
  fee: v3Fee,
119
121
  recipient: buyer.address,
120
122
  amountIn: buyAmount,
121
- amountOutMinimum: 0n,
123
+ amountOutMinimum: amountOutMin,
122
124
  sqrtPriceLimitX96: 0n,
123
125
  },
124
126
  ]);
@@ -127,16 +129,16 @@ export async function buildSingleBuyTx({ routeParams, buyAmount, buyer, useNativ
127
129
  throw new Error('V3 多跳路由暂不支持');
128
130
  }
129
131
  // ✅ 构建单笔卖出交易
130
- export async function buildSingleSellTx({ routeParams, sellAmount, seller, useNativeToken, deadline, v2RouterAddress, }) {
132
+ export async function buildSingleSellTx({ routeParams, sellAmount, seller, useNativeToken, deadline, v2RouterAddress, amountOutMin = 0n, }) {
131
133
  if (routeParams.routeType === 'v2') {
132
134
  const { v2Path } = routeParams;
133
135
  const reversePath = [...v2Path].reverse();
134
136
  const v2Router = new Contract(v2RouterAddress || PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
135
137
  if (useNativeToken) {
136
- return await v2Router.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmount, 0n, reversePath, seller.address, deadline);
138
+ return await v2Router.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmount, amountOutMin, reversePath, seller.address, deadline);
137
139
  }
138
140
  else {
139
- return await v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmount, 0n, reversePath, seller.address, deadline);
141
+ return await v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmount, amountOutMin, reversePath, seller.address, deadline);
140
142
  }
141
143
  }
142
144
  if (routeParams.routeType === 'v3-single') {
@@ -151,11 +153,11 @@ export async function buildSingleSellTx({ routeParams, sellAmount, seller, useNa
151
153
  fee: v3Fee,
152
154
  recipient: PANCAKE_V3_ROUTER_ADDRESS,
153
155
  amountIn: sellAmount,
154
- amountOutMinimum: 0n,
156
+ amountOutMinimum: amountOutMin,
155
157
  sqrtPriceLimitX96: 0n,
156
158
  },
157
159
  ]);
158
- const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
160
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [amountOutMin, seller.address]);
159
161
  return await v3Router.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
160
162
  }
161
163
  else {
@@ -166,7 +168,7 @@ export async function buildSingleSellTx({ routeParams, sellAmount, seller, useNa
166
168
  fee: v3Fee,
167
169
  recipient: seller.address,
168
170
  amountIn: sellAmount,
169
- amountOutMinimum: 0n,
171
+ amountOutMinimum: amountOutMin,
170
172
  sqrtPriceLimitX96: 0n,
171
173
  },
172
174
  ]);
@@ -11,6 +11,7 @@ import { createPancakeContext, getGasPrice, quoteTokenOutput } from './bundle-bu
11
11
  import { validateSwapCounts } from './bundle-buy-first-plan.js';
12
12
  import { buildSingleBuyTx, buildSingleSellTx } from './bundle-buy-first-build.js';
13
13
  import { buildProfitTransaction } from './bundle-buy-first-profit.js';
14
+ import { splitQuotedMins } from './quote.js';
14
15
  /**
15
16
  * ✅ 多钱包捆绑换手
16
17
  * - 多个买方钱包执行买入(每个钱包1笔)
@@ -88,6 +89,9 @@ export async function pancakeBundleBuyFirstMultiWallet(params) {
88
89
  }
89
90
  // ✅ 将代币平均分配给卖方
90
91
  const sellAmountsWei = splitAmount(actualTokenOut, sellCount);
92
+ const slippageBps = config.slippageBps ?? 100;
93
+ const buyAmountOutMins = splitQuotedMins(quoteResult.quotedTokenOut, buyAmountsWei, slippageBps);
94
+ const sellAmountOutMins = splitQuotedMins(totalFundsWei, sellAmountsWei, slippageBps);
91
95
  // 买合计万三 + 卖合计万三。ERC-20 先把买入合计折成 BNB 再计提,避免 ANTFUN 微额报价为 0
92
96
  let profitAmount = 0n;
93
97
  if (useNativeToken) {
@@ -140,6 +144,7 @@ export async function pancakeBundleBuyFirstMultiWallet(params) {
140
144
  useNativeToken,
141
145
  deadline,
142
146
  v2RouterAddress: config.v2RouterAddress,
147
+ amountOutMin: buyAmountOutMins[i],
143
148
  });
144
149
  return buyer.signTransaction({
145
150
  ...unsigned,
@@ -160,6 +165,7 @@ export async function pancakeBundleBuyFirstMultiWallet(params) {
160
165
  useNativeToken,
161
166
  deadline,
162
167
  v2RouterAddress: config.v2RouterAddress,
168
+ amountOutMin: sellAmountOutMins[i],
163
169
  });
164
170
  return seller.signTransaction({
165
171
  ...unsigned,
@@ -16,6 +16,8 @@ export interface PancakeBuyFirstSignConfig {
16
16
  skipQuoteOnError?: boolean;
17
17
  userType?: UserType;
18
18
  bribeAmount?: number;
19
+ /** 滑点基点,默认 100 = 1%。 */
20
+ slippageBps?: number;
19
21
  v2RouterAddress?: string;
20
22
  }
21
23
  export type UserType = 'v0' | 'v1';
@@ -131,6 +133,8 @@ export type MultiSwapTxParams = {
131
133
  seller: Wallet;
132
134
  useNativeToken?: boolean;
133
135
  v2RouterAddress?: string;
136
+ buyAmountOutMins?: bigint[];
137
+ sellAmountOutMins?: bigint[];
134
138
  };
135
139
  export type MultiSwapUnsignedResult = {
136
140
  buyUnsignedArray: ethers.TransactionLike[];
@@ -6,6 +6,7 @@ import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
6
  import { NonceManager } from '../../../utils/bundle-helpers.js';
7
7
  import { BLOCKRAZOR_BUILDER_EOA, ZERO_ADDRESS, GAS_LIMITS, calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
8
8
  import { quoteRoundTripProfitToNative } from '../../../core/quote/quote-helpers.js';
9
+ import { splitQuotedMins } from './quote.js';
9
10
  import { buildMultiRouteTransactions, buildProfitTransaction, calculateBuyerFunds, createPancakeContext, getGasPrice, pancakeBundleBuyFirstMultiWallet, planMultiNonces, quoteTokenOutput, splitAmount, validateFinalBalances, validateSwapCounts, } from './bundle-buy-first-helpers.js';
10
11
  export async function pancakeBundleBuyFirstMerkle(params) {
11
12
  const { buyerPrivateKey, buyerPrivateKeys, sellerPrivateKey, sellerPrivateKeys, routeParams, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals = 18, buyCount: _buyCount, sellCount: _sellCount, taxRateBps = 0, // ✅ 税率(基点),用于计算实际可卖出代币数量
@@ -66,6 +67,9 @@ export async function pancakeBundleBuyFirstMerkle(params) {
66
67
  actualTokenOut = (quoteResult.quotedTokenOut * BigInt(10000 - taxRateBps)) / 10000n;
67
68
  }
68
69
  const sellAmountsWei = splitAmount(actualTokenOut, sellCount);
70
+ const slippageBps = config.slippageBps ?? 100;
71
+ const buyAmountOutMins = splitQuotedMins(quoteResult.quotedTokenOut, buyAmountsWei, slippageBps);
72
+ const sellAmountOutMins = splitQuotedMins(buyerFundsInfo.buyerFundsWei, sellAmountsWei, slippageBps);
69
73
  // ✅ 构建多笔买入和卖出交易
70
74
  const swapUnsignedArray = await buildMultiRouteTransactions({
71
75
  routeParams,
@@ -75,6 +79,8 @@ export async function pancakeBundleBuyFirstMerkle(params) {
75
79
  seller,
76
80
  useNativeToken,
77
81
  v2RouterAddress: config.v2RouterAddress,
82
+ buyAmountOutMins,
83
+ sellAmountOutMins,
78
84
  });
79
85
  const finalGasLimit = getGasLimit(config, 800000);
80
86
  const gasPrice = await getGasPrice(context.provider, config);
@@ -53,6 +53,8 @@ export type BuildSwapParams = {
53
53
  tokenAddress: string;
54
54
  useNativeToken?: boolean;
55
55
  v2RouterAddress?: string;
56
+ sellAmountOutMin?: bigint;
57
+ buyAmountOutMin?: bigint;
56
58
  };
57
59
  export type BuyerBudgetParams = {
58
60
  buyer: Wallet;
@@ -95,6 +97,8 @@ export interface PancakeSwapSignConfig {
95
97
  reserveGasBNB?: number;
96
98
  userType?: UserType;
97
99
  bribeAmount?: number;
100
+ /** 滑点基点,默认 100 = 1%。买卖 amountOutMin 都按报价下浮。 */
101
+ slippageBps?: number;
98
102
  v2RouterAddress?: string;
99
103
  }
100
104
  export interface PancakeSwapConfig extends CommonBundleConfig {
@@ -206,6 +206,8 @@ export async function buildBNBHopChainForERC20(payer, path, finalGasAmount, gasP
206
206
  const fromWallet = path.hopWallets[i];
207
207
  const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
208
208
  const amount = i === hopCount - 1 ? finalGasAmount : amountsPerHop[i + 1];
209
+ if (i === hopCount - 1 && amount === 0n)
210
+ continue;
209
211
  const tx = {
210
212
  to: toAddress,
211
213
  value: amount,
@@ -1,5 +1,14 @@
1
1
  import { ethers } from 'ethers';
2
- import { type QuoteBNBResult, type QuoteSellParams, type UserType } from './bundle-swap-types.js';
2
+ import { type QuoteBNBResult, type QuoteSellParams, type RouteParams, type UserType } from './bundle-swap-types.js';
3
+ /** 100 bps = 1%。上限 50%,避免写成 0 输出。 */
4
+ export declare function applyPancakeSlippage(quotedOut: bigint, slippageBps?: number): bigint;
5
+ export declare function splitQuotedMins(quotedTotal: bigint, amounts: bigint[], slippageBps?: number): bigint[];
6
+ export declare function quoteBuyOutput(params: {
7
+ routeParams: RouteParams;
8
+ buyAmountWei: bigint;
9
+ provider: ethers.JsonRpcProvider;
10
+ v2RouterAddress?: string;
11
+ }): Promise<bigint>;
3
12
  export declare function quoteSellOutput({ routeParams, sellAmountWei, provider, v2RouterAddress, }: QuoteSellParams): Promise<QuoteBNBResult>;
4
13
  /**
5
14
  * 根据 userType 计算利润率
@@ -1,6 +1,36 @@
1
1
  import { ADDRESSES, getProfitRateBps as getSharedProfitRateBps, calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
2
2
  import { quoteV2, quoteV3 } from '../../../core/quote/quote-helpers.js';
3
3
  import { WBNB_ADDRESS, } from './bundle-swap-types.js';
4
+ /** 100 bps = 1%。上限 50%,避免写成 0 输出。 */
5
+ export function applyPancakeSlippage(quotedOut, slippageBps = 100) {
6
+ const bps = Math.max(0, Math.min(5000, Math.floor(Number.isFinite(slippageBps) ? slippageBps : 100)));
7
+ return (quotedOut * BigInt(10000 - bps)) / 10000n;
8
+ }
9
+ export function splitQuotedMins(quotedTotal, amounts, slippageBps = 100) {
10
+ const total = amounts.reduce((sum, amt) => sum + amt, 0n);
11
+ if (total <= 0n)
12
+ return amounts.map(() => 0n);
13
+ return amounts.map((amt) => applyPancakeSlippage((quotedTotal * amt) / total, slippageBps));
14
+ }
15
+ export async function quoteBuyOutput(params) {
16
+ const { routeParams, buyAmountWei, provider, v2RouterAddress } = params;
17
+ if (buyAmountWei <= 0n)
18
+ return 0n;
19
+ if (routeParams.routeType === 'v2') {
20
+ const reversePath = [...routeParams.v2Path].reverse();
21
+ const tokenIn = reversePath[0];
22
+ const tokenOut = reversePath[reversePath.length - 1];
23
+ if (!tokenIn || !tokenOut)
24
+ return 0n;
25
+ const result = await quoteV2(provider, tokenIn, tokenOut, buyAmountWei, 'BSC', v2RouterAddress);
26
+ return result.amountOut;
27
+ }
28
+ if (routeParams.routeType === 'v3-single') {
29
+ const result = await quoteV3(provider, routeParams.v3TokenOut, routeParams.v3TokenIn, buyAmountWei, 'BSC', routeParams.v3Fee);
30
+ return result.amountOut;
31
+ }
32
+ return 0n;
33
+ }
4
34
  export async function quoteSellOutput({ routeParams, sellAmountWei, provider, v2RouterAddress, }) {
5
35
  if (routeParams.routeType === 'v2') {
6
36
  const { v2Path } = routeParams;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { applyPancakeSlippage, splitQuotedMins } from './quote.js';
3
+ describe('applyPancakeSlippage', () => {
4
+ it('drops 1% by default', () => {
5
+ expect(applyPancakeSlippage(1000000n)).toBe(990000n);
6
+ });
7
+ it('clamps out-of-range bps', () => {
8
+ expect(applyPancakeSlippage(1000000n, -10)).toBe(1000000n);
9
+ expect(applyPancakeSlippage(1000000n, 9000)).toBe(500000n);
10
+ });
11
+ });
12
+ describe('splitQuotedMins', () => {
13
+ it('splits quoted output then applies slippage', () => {
14
+ expect(splitQuotedMins(1000000n, [250000n, 750000n], 100)).toEqual([247500n, 742500n]);
15
+ });
16
+ });
@@ -6,7 +6,7 @@ import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
6
  import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { calculateSellAmount } from '../../../utils/swap-helpers.js';
8
8
  import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
9
- import { BRIBE_GAS_LIMIT, ERC20_BALANCE_OF_ABI, FLAT_FEE, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, WBNB_ADDRESS, ZERO_ADDRESS, calculateProfitAmount, createPancakeContext, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, quoteSellOutput, } from './bundle-swap-helpers.js';
9
+ import { BRIBE_GAS_LIMIT, ERC20_BALANCE_OF_ABI, FLAT_FEE, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, WBNB_ADDRESS, ZERO_ADDRESS, calculateProfitAmount, createPancakeContext, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, quoteSellOutput, quoteBuyOutput, applyPancakeSlippage, } from './bundle-swap-helpers.js';
10
10
  /**
11
11
  * PancakeSwap 批量换手(一卖多买)
12
12
  *
@@ -88,6 +88,15 @@ export async function pancakeBatchSwapMerkle(params) {
88
88
  }
89
89
  }
90
90
  }));
91
+ const slippageBps = config.slippageBps ?? 100;
92
+ const sellMinOut = applyPancakeSlippage(estimatedBNBOut, slippageBps);
93
+ const buyQuotedOuts = await Promise.all(buyAmountsWei.map((amt) => quoteBuyOutput({
94
+ routeParams,
95
+ buyAmountWei: amt,
96
+ provider: context.provider,
97
+ v2RouterAddress: config.v2RouterAddress,
98
+ })));
99
+ const buyMinOuts = buyQuotedOuts.map((quoted) => applyPancakeSlippage(quoted, slippageBps));
91
100
  const deadline = getDeadline();
92
101
  const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
93
102
  const batchV2Addr = config.v2RouterAddress || PANCAKE_V2_ROUTER_ADDRESS;
@@ -97,11 +106,11 @@ export async function pancakeBatchSwapMerkle(params) {
97
106
  const v2RouterSeller = new Contract(batchV2Addr, PANCAKE_V2_ROUTER_ABI, seller);
98
107
  if (useNativeToken) {
99
108
  // ✅ 原生代币模式(BNB):Token → WBNB → BNB
100
- sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
109
+ sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, sellMinOut, v2Path, seller.address, deadline);
101
110
  }
102
111
  else {
103
112
  // ✅ ERC20 模式(USDT):Token → USDT
104
- sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
113
+ sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, sellMinOut, v2Path, seller.address, deadline);
105
114
  }
106
115
  }
107
116
  else if (routeParams.routeType === 'v3-single') {
@@ -114,7 +123,7 @@ export async function pancakeBatchSwapMerkle(params) {
114
123
  fee: v3Fee,
115
124
  recipient: useNativeToken ? PANCAKE_V3_ROUTER_ADDRESS : seller.address,
116
125
  amountIn: sellAmountWei,
117
- amountOutMinimum: 0n,
126
+ amountOutMinimum: sellMinOut,
118
127
  sqrtPriceLimitX96: 0n,
119
128
  },
120
129
  ]);
@@ -140,11 +149,11 @@ export async function pancakeBatchSwapMerkle(params) {
140
149
  const v2RouterBuyer = new Contract(batchV2Addr, PANCAKE_V2_ROUTER_ABI, buyer);
141
150
  if (useNativeToken) {
142
151
  // ✅ 原生代币模式(BNB):BNB → WBNB → Token
143
- return await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
152
+ return await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(buyMinOuts[i] ?? 0n, reversePath, buyer.address, deadline, { value: buyValue });
144
153
  }
145
154
  else {
146
155
  // ✅ ERC20 模式(USDT):USDT → Token
147
- return await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, 0n, reversePath, buyer.address, deadline);
156
+ return await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, buyMinOuts[i] ?? 0n, reversePath, buyer.address, deadline);
148
157
  }
149
158
  }
150
159
  else if (routeParams.routeType === 'v3-single') {
@@ -157,7 +166,7 @@ export async function pancakeBatchSwapMerkle(params) {
157
166
  fee: v3Fee,
158
167
  recipient: buyer.address,
159
168
  amountIn: buyAmount,
160
- amountOutMinimum: 0n,
169
+ amountOutMinimum: buyMinOuts[i] ?? 0n,
161
170
  sqrtPriceLimitX96: 0n,
162
171
  },
163
172
  ]);
@@ -2,7 +2,7 @@ import { ethers } from 'ethers';
2
2
  import { type BalanceValidationParams, type BuildSwapParams, type BuyerBudget, type BuyerBudgetParams, type NoncePlan, type PancakeContext, type PancakeSwapConfig, type PancakeSwapSignConfig, type PlanNonceParams, type SwapUnsignedResult } from './bundle-swap-types.js';
3
3
  export declare function createPancakeContext(config: PancakeSwapSignConfig): PancakeContext;
4
4
  export declare function getGasPrice(provider: ethers.JsonRpcProvider, config: PancakeSwapSignConfig | PancakeSwapConfig): Promise<bigint>;
5
- export declare function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken, v2RouterAddress, }: BuildSwapParams): Promise<SwapUnsignedResult>;
5
+ export declare function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken, v2RouterAddress, sellAmountOutMin, buyAmountOutMin, }: BuildSwapParams): Promise<SwapUnsignedResult>;
6
6
  export declare function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, useNativeToken, quoteToken, quoteTokenDecimals, provider, }: BuyerBudgetParams): Promise<BuyerBudget>;
7
7
  /**
8
8
  * ✅ 从前端传入的 startNonces 构建 NoncePlan(用于性能优化)
@@ -23,7 +23,7 @@ export async function getGasPrice(provider, config) {
23
23
  }
24
24
  return gasPrice;
25
25
  }
26
- export async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true, v2RouterAddress, }) {
26
+ export async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true, v2RouterAddress, sellAmountOutMin = 0n, buyAmountOutMin = 0n, }) {
27
27
  void tokenAddress;
28
28
  const deadline = getDeadline();
29
29
  const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE : 0n;
@@ -36,19 +36,12 @@ export async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmo
36
36
  let sellUnsigned;
37
37
  let buyUnsigned;
38
38
  if (useNativeToken) {
39
- // ✅ 原生代币模式(BNB)
40
- // 卖出:Token → WBNB → BNB
41
- sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
42
- // 买入:BNB → WBNB → Token
43
- buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
39
+ sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, sellAmountOutMin, v2Path, seller.address, deadline);
40
+ buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmountOutMin, reversePath, buyer.address, deadline, { value: buyValue });
44
41
  }
45
42
  else {
46
- // ✅ ERC20 模式(如 USDT)
47
- // 卖出:Token → USDT
48
- sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
49
- // 买入:USDT → Token
50
- buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmountBNB, // USDT 数量
51
- 0n, reversePath, buyer.address, deadline);
43
+ sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, sellAmountOutMin, v2Path, seller.address, deadline);
44
+ buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmountBNB, buyAmountOutMin, reversePath, buyer.address, deadline);
52
45
  }
53
46
  return { sellUnsigned, buyUnsigned };
54
47
  }
@@ -57,21 +50,19 @@ export async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmo
57
50
  const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
58
51
  const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
59
52
  const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
60
- // 卖出:Token → WBNB,需要 unwrapWETH9
61
53
  const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
62
54
  {
63
55
  tokenIn: v3TokenIn,
64
56
  tokenOut: v3TokenOut,
65
57
  fee: v3Fee,
66
- recipient: PANCAKE_V3_ROUTER_ADDRESS, // 先发给 Router
58
+ recipient: PANCAKE_V3_ROUTER_ADDRESS,
67
59
  amountIn: sellAmountWei,
68
- amountOutMinimum: 0n,
60
+ amountOutMinimum: sellAmountOutMin,
69
61
  sqrtPriceLimitX96: 0n,
70
62
  },
71
63
  ]);
72
- const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
64
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [sellAmountOutMin, seller.address]);
73
65
  const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
74
- // 买入:WBNB → Token
75
66
  const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
76
67
  {
77
68
  tokenIn: v3TokenOut,
@@ -79,7 +70,7 @@ export async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmo
79
70
  fee: v3Fee,
80
71
  recipient: buyer.address,
81
72
  amountIn: buyAmountBNB,
82
- amountOutMinimum: 0n,
73
+ amountOutMinimum: buyAmountOutMin,
83
74
  sqrtPriceLimitX96: 0n,
84
75
  },
85
76
  ]);
@@ -7,7 +7,7 @@ import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { calculateSellAmount } from '../../../utils/swap-helpers.js';
8
8
  import { NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
9
9
  import { ERC20_ABI } from '../../../abis/common.js';
10
- import { BRIBE_GAS_LIMIT, FLAT_FEE, NATIVE_TRANSFER_GAS_LIMIT, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, WBNB_ADDRESS, ZERO_ADDRESS, buildBNBHopChainForERC20, buildERC20HopChain, buildNativeHopChain, calculateProfitAmount, createPancakeContext, generateDisperseHopPaths, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, quoteSellOutput, } from './bundle-swap-helpers.js';
10
+ import { BRIBE_GAS_LIMIT, FLAT_FEE, NATIVE_TRANSFER_GAS_LIMIT, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, WBNB_ADDRESS, ZERO_ADDRESS, buildBNBHopChainForERC20, buildERC20HopChain, buildNativeHopChain, calculateProfitAmount, createPancakeContext, generateDisperseHopPaths, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, quoteSellOutput, quoteBuyOutput, applyPancakeSlippage, } from './bundle-swap-helpers.js';
11
11
  /**
12
12
  * PancakeSwap 快捷批量换手(资金自动流转)
13
13
  *
@@ -176,18 +176,22 @@ export async function pancakeQuickBatchSwapMerkle(params) {
176
176
  sellerRequired = bribeAmount + sellerGasCost;
177
177
  }
178
178
  else {
179
- // ERC20:卖方还要给每个买方转一笔买入 Gas(hop=0 直转 / hop>0 多跳)
180
179
  sellerGasCost =
181
- gasPrice *
182
- (BRIBE_GAS_LIMIT +
183
- finalGasLimit +
184
- (NATIVE_TRANSFER_GAS_LIMIT + ERC20_TRANSFER_GAS) * BigInt(buyers.length) +
185
- BRIBE_GAS_LIMIT);
186
- sellerRequired = bribeAmount + sellerGasCost + gasPrice * finalGasLimit * BigInt(buyers.length);
180
+ gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
181
+ sellerRequired = bribeAmount + sellerGasCost;
187
182
  }
188
183
  if (sellerBalance < sellerRequired) {
189
184
  throw new Error(`主钱包 BNB 余额不足: 需要约 ${ethers.formatEther(sellerRequired)} BNB (贿赂: ${ethers.formatEther(bribeAmount)}, Gas: ${ethers.formatEther(sellerGasCost)}), 实际 ${ethers.formatEther(sellerBalance)} BNB`);
190
185
  }
186
+ const slippageBps = config.slippageBps ?? 100;
187
+ const sellMinOut = applyPancakeSlippage(estimatedOutput, slippageBps);
188
+ const buyQuotedOuts = await Promise.all(transferAmountsWei.map((amt) => quoteBuyOutput({
189
+ routeParams,
190
+ buyAmountWei: amt,
191
+ provider: context.provider,
192
+ v2RouterAddress: config.v2RouterAddress,
193
+ })));
194
+ const buyMinOuts = buyQuotedOuts.map((quoted) => applyPancakeSlippage(quoted, slippageBps));
191
195
  // ==================== 规划 Nonce ====================
192
196
  // ✅ 如果前端传入了 startNonces,直接使用(性能优化)
193
197
  let sellerNonce = startNonces && startNonces.length > 0 ? startNonces[0] : await nonceManager.getNextNonce(seller);
@@ -214,11 +218,11 @@ export async function pancakeQuickBatchSwapMerkle(params) {
214
218
  const v2RouterSeller = new Contract(quickV2Addr, PANCAKE_V2_ROUTER_ABI, seller);
215
219
  if (useNativeToken) {
216
220
  // ✅ 原生代币模式(BNB):Token → WBNB → BNB
217
- sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
221
+ sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, sellMinOut, v2Path, seller.address, deadline);
218
222
  }
219
223
  else {
220
224
  // ✅ ERC20 模式(USDT):Token → USDT
221
- sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
225
+ sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, sellMinOut, v2Path, seller.address, deadline);
222
226
  }
223
227
  }
224
228
  else if (routeParams.routeType === 'v3-single') {
@@ -231,7 +235,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
231
235
  fee: v3Fee,
232
236
  recipient: useNativeToken ? PANCAKE_V3_ROUTER_ADDRESS : seller.address,
233
237
  amountIn: sellAmountWei,
234
- amountOutMinimum: 0n,
238
+ amountOutMinimum: sellMinOut,
235
239
  sqrtPriceLimitX96: 0n,
236
240
  },
237
241
  ]);
@@ -258,7 +262,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
258
262
  type: txType,
259
263
  });
260
264
  // ==================== 3. 转账交易(支持多跳)====================
261
- const buyerGasCost = gasPrice * finalGasLimit;
265
+ // 买地址 gas 由用户自备,卖方只转买入金额 / 计价币
262
266
  // ✅ 生成多跳路径
263
267
  const hopPaths = generateDisperseHopPaths(buyers.map((b) => b.address), disperseHopCount, context.provider);
264
268
  // 收集所有中间钱包信息
@@ -272,7 +276,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
272
276
  const transferNonces = buyers.map((_, i) => sellerNonce + i);
273
277
  sellerNonce += buyers.length;
274
278
  transferTxs = await Promise.all(buyers.map((buyer, i) => {
275
- const transferValue = transferAmountsWei[i] + FLAT_FEE + buyerGasCost;
279
+ const transferValue = transferAmountsWei[i] + FLAT_FEE;
276
280
  return seller.signTransaction({
277
281
  to: buyer.address,
278
282
  value: transferValue,
@@ -286,30 +290,21 @@ export async function pancakeQuickBatchSwapMerkle(params) {
286
290
  }
287
291
  else {
288
292
  const erc20Interface = new ethers.Interface(ERC20_ABI);
289
- transferTxs = [];
290
- for (let i = 0; i < buyers.length; i++) {
291
- const buyer = buyers[i];
292
- transferTxs.push(await seller.signTransaction({
293
- to: buyer.address,
294
- value: buyerGasCost,
295
- nonce: sellerNonce++,
296
- gasPrice,
297
- gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
298
- chainId: context.chainId,
299
- type: txType,
300
- }));
293
+ const transferNonces = buyers.map((_, i) => sellerNonce + i);
294
+ sellerNonce += buyers.length;
295
+ transferTxs = await Promise.all(buyers.map((buyer, i) => {
301
296
  const transferData = erc20Interface.encodeFunctionData('transfer', [buyer.address, transferAmountsWei[i]]);
302
- transferTxs.push(await seller.signTransaction({
297
+ return seller.signTransaction({
303
298
  to: quoteToken,
304
299
  data: transferData,
305
300
  value: 0n,
306
- nonce: sellerNonce++,
301
+ nonce: transferNonces[i],
307
302
  gasPrice,
308
303
  gasLimit: ERC20_TRANSFER_GAS,
309
304
  chainId: context.chainId,
310
305
  type: txType,
311
- }));
312
- }
306
+ });
307
+ }));
313
308
  }
314
309
  }
315
310
  else {
@@ -318,7 +313,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
318
313
  // BNB 多跳转账
319
314
  // ✅ 修复:payer 每次只发送 1 笔交易(到第一个hop钱包),hop钱包用自己的nonce=0
320
315
  const hopChains = await Promise.all(hopPaths.map((path, i) => {
321
- const finalAmount = transferAmountsWei[i] + FLAT_FEE + buyerGasCost;
316
+ const finalAmount = transferAmountsWei[i] + FLAT_FEE;
322
317
  const payerNonce = sellerNonce + i; // ✅ payer 每个 buyer 只用 1 个 nonce
323
318
  return buildNativeHopChain(seller, path, finalAmount, gasPrice, context.chainId, txType, payerNonce);
324
319
  }));
@@ -328,12 +323,10 @@ export async function pancakeQuickBatchSwapMerkle(params) {
328
323
  else {
329
324
  // ERC20 多跳转账:先转 BNB(给中间钱包 gas),再转 ERC20
330
325
  // ✅ 修复:payer 每次只发送 1 笔交易(到第一个hop钱包),hop钱包用自己的nonce=0/1
331
- // 1. 构建 BNB 多跳链(为中间钱包和最终钱包预留 gas)
326
+ // 1. 构建 BNB 多跳链(只给中间钱包 gas,买地址自备)
332
327
  const bnbHopChains = await Promise.all(hopPaths.map((path, i) => {
333
- // 最终钱包需要的 gas(用于买入交易)
334
- const finalGasAmount = buyerGasCost;
335
- const payerNonce = sellerNonce + i; // ✅ payer 每个 buyer 只用 1 个 nonce
336
- return buildBNBHopChainForERC20(seller, path, finalGasAmount, gasPrice, context.chainId, txType, payerNonce);
328
+ const payerNonce = sellerNonce + i;
329
+ return buildBNBHopChainForERC20(seller, path, 0n, gasPrice, context.chainId, txType, payerNonce);
337
330
  }));
338
331
  const bnbTxs = bnbHopChains.flat();
339
332
  sellerNonce += buyers.length; // ✅ payer 只增加 buyers.length 个 nonce
@@ -362,10 +355,10 @@ export async function pancakeQuickBatchSwapMerkle(params) {
362
355
  const reversePath = [...v2Path].reverse();
363
356
  const v2RouterBuyer = new Contract(quickV2Addr, PANCAKE_V2_ROUTER_ABI, buyer);
364
357
  if (useNativeToken) {
365
- buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
358
+ buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(buyMinOuts[i] ?? 0n, reversePath, buyer.address, deadline, { value: buyValue });
366
359
  }
367
360
  else {
368
- buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, 0n, reversePath, buyer.address, deadline);
361
+ buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, buyMinOuts[i] ?? 0n, reversePath, buyer.address, deadline);
369
362
  }
370
363
  }
371
364
  else if (routeParams.routeType === 'v3-single') {
@@ -378,7 +371,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
378
371
  fee: v3Fee,
379
372
  recipient: buyer.address,
380
373
  amountIn: buyAmount,
381
- amountOutMinimum: 0n,
374
+ amountOutMinimum: buyMinOuts[i] ?? 0n,
382
375
  sqrtPriceLimitX96: 0n,
383
376
  },
384
377
  ]);
@@ -6,7 +6,7 @@ import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
6
  import { ethers, Wallet } from 'ethers';
7
7
  import { calculateSellAmount } from '../../../utils/swap-helpers.js';
8
8
  import { NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
9
- import { BRIBE_GAS_LIMIT, ZERO_ADDRESS, buildNoncePlanFromNonces, buildSwapTransactions, WBNB_ADDRESS, calculateBuyerBudget, calculateProfitAmount, createPancakeContext, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, planNonces, quoteSellOutput, validateFinalBalances, } from './bundle-swap-helpers.js';
9
+ import { BRIBE_GAS_LIMIT, ZERO_ADDRESS, buildNoncePlanFromNonces, buildSwapTransactions, WBNB_ADDRESS, calculateBuyerBudget, calculateProfitAmount, createPancakeContext, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, planNonces, quoteSellOutput, applyPancakeSlippage, quoteBuyOutput, validateFinalBalances, } from './bundle-swap-helpers.js';
10
10
  export async function pancakeBundleSwapMerkle(params) {
11
11
  const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces, // ✅ 可选:前端预获取的 nonces
12
12
  } = params;
@@ -41,6 +41,13 @@ export async function pancakeBundleSwapMerkle(params) {
41
41
  quoteTokenDecimals,
42
42
  provider: context.provider,
43
43
  });
44
+ const quotedBuyOut = await quoteBuyOutput({
45
+ routeParams,
46
+ buyAmountWei: buyerBudget.buyAmountBNB,
47
+ provider: context.provider,
48
+ v2RouterAddress: config.v2RouterAddress,
49
+ });
50
+ const slippageBps = config.slippageBps ?? 100;
44
51
  const swapUnsigned = await buildSwapTransactions({
45
52
  routeParams,
46
53
  sellAmountWei,
@@ -50,6 +57,8 @@ export async function pancakeBundleSwapMerkle(params) {
50
57
  tokenAddress,
51
58
  useNativeToken,
52
59
  v2RouterAddress: config.v2RouterAddress,
60
+ sellAmountOutMin: applyPancakeSlippage(quoteResult.estimatedBNBOut, slippageBps),
61
+ buyAmountOutMin: applyPancakeSlippage(quotedBuyOut, slippageBps),
53
62
  });
54
63
  const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
55
64
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
@@ -146,12 +146,10 @@ export async function directBatchBuyForSubmit(params) {
146
146
  }
147
147
  const transactions = [];
148
148
  const totalFlow = amounts.reduce((sum, amount) => sum + amount, 0n);
149
- const isBatch = privateKeys.length > 1;
150
149
  for (let i = 0; i < privateKeys.length; i++) {
151
150
  const wallet = new Wallet(privateKeys[i], provider);
152
151
  const preview = await query.previewBuy(token, amounts[i]);
153
- const effectiveSlippage = isBatch ? Math.min(9500, slippageBps + i * 2000) : slippageBps;
154
- const minOut = applySlippage(preview, effectiveSlippage);
152
+ const minOut = applySlippage(preview, slippageBps);
155
153
  const calldata = encodeBuyCall(token, minOut);
156
154
  const nonce = await wallet.getNonce('pending');
157
155
  const signedTx = await wallet.signTransaction({
@@ -201,13 +199,11 @@ export async function directBatchSellForSubmit(params) {
201
199
  const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
202
200
  const transactions = [];
203
201
  const estimatedOuts = [];
204
- const isBatch = privateKeys.length > 1;
205
202
  for (let i = 0; i < privateKeys.length; i++) {
206
203
  const wallet = wallets[i];
207
204
  const preview = await query.previewSell(token, tokenAmounts[i]);
208
205
  estimatedOuts.push(preview);
209
- const effectiveSlippage = isBatch ? Math.min(9500, slippageBps + i * 2000) : slippageBps;
210
- const minOut = applySlippage(preview, effectiveSlippage);
206
+ const minOut = applySlippage(preview, slippageBps);
211
207
  const calldata = encodeSellCall(token, tokenAmounts[i], minOut);
212
208
  const nonce = await wallet.getNonce('pending');
213
209
  const signedTx = await wallet.signTransaction({
@@ -96,5 +96,5 @@ export declare function buildProfitMetadata(extractProfit: boolean, totalBuyAmou
96
96
  * 比 Promise.all 更高效,N 个报价只需 1 次网络请求
97
97
  */
98
98
  export declare function quoteSellOutputsWithQuote(portal: ethers.Contract, tokenAddress: string, amountsWei: bigint[], outputToken: string): Promise<bigint[]>;
99
- export declare function resolveMinOutputs(_provided: (string | bigint)[] | undefined, walletCount: number, _quotedOutputs: bigint[]): any[];
99
+ export declare function resolveMinOutputs(provided: (string | bigint)[] | undefined, walletCount: number, quotedOutputs: bigint[]): bigint[];
100
100
  export {};
@@ -284,7 +284,9 @@ export async function quoteSellOutputsWithQuote(portal, tokenAddress, amountsWei
284
284
  }));
285
285
  }
286
286
  }
287
- export function resolveMinOutputs(_provided, walletCount, _quotedOutputs) {
288
- // ✅ 已移除滑点保护:minOutput 固定为 0
289
- return Array(walletCount).fill(0n);
287
+ export function resolveMinOutputs(provided, walletCount, quotedOutputs) {
288
+ if (provided && provided.length === walletCount) {
289
+ return provided.map((m) => (typeof m === 'string' ? ethers.parseEther(m) : m));
290
+ }
291
+ return quotedOutputs.map((q) => (q * 9900n) / 10000n);
290
292
  }
@@ -61,7 +61,7 @@ export async function resolveSingleSellOutputs(portal, tokenAddress, amountWei,
61
61
  });
62
62
  return {
63
63
  quotedOutput,
64
- minOut: 0n, // ✅ 已移除滑点保护:minOut 固定为 0
64
+ minOut: quotedOutput > 0n ? (quotedOutput * 9900n) / 10000n : 0n,
65
65
  };
66
66
  }
67
67
  catch {
@@ -16,19 +16,18 @@ export async function flapPrivateSellMerkle(params) {
16
16
  const amountWei = ethers.parseUnits(amount, 18);
17
17
  const portal = new ethers.Contract(portalAddr, PORTAL_ABI, wallet);
18
18
  // ✅ 并行获取 gasPrice、nonce、报价和构建未签名交易
19
- const [gasPrice, currentNonce, { quotedOutput }, unsigned] = await Promise.all([
19
+ const [gasPrice, currentNonce, { quotedOutput, minOut }] = await Promise.all([
20
20
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
21
- // ✅ 使用 pending nonce,避免 xLayer 等链上“已有 pending 交易”导致 nonce too low
22
21
  provider.getTransactionCount(wallet.address, 'pending'),
23
22
  resolveSingleSellOutputs(portal, tokenAddress, amountWei, minOutputAmount),
24
- portal.swapExactInput.populateTransaction({
25
- inputToken: tokenAddress,
26
- outputToken: ZERO_ADDRESS,
27
- inputAmount: amountWei,
28
- minOutputAmount: 0n, // ✅ 已移除滑点保护
29
- permitData: '0x',
30
- }),
31
23
  ]);
24
+ const unsigned = await portal.swapExactInput.populateTransaction({
25
+ inputToken: tokenAddress,
26
+ outputToken: ZERO_ADDRESS,
27
+ inputAmount: amountWei,
28
+ minOutputAmount: minOut,
29
+ permitData: '0x',
30
+ });
32
31
  const gasLimit = resolveGasLimit(config.gasLimitMultiplier);
33
32
  const txType = getTxType(config);
34
33
  // ✅ 获取贿赂金额
@@ -103,9 +102,8 @@ export async function flapBatchPrivateSellMerkle(params) {
103
102
  const portal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
104
103
  const portals = wallets.map((w) => new ethers.Contract(portalAddr, PORTAL_ABI, w));
105
104
  // ✅ 并行获取 gasPrice、所有 nonces、所有报价和构建未签名交易
106
- const [gasPrice, initialNonces, quotedOutputs, unsignedList] = await Promise.all([
105
+ const [gasPrice, initialNonces, quotedOutputs] = await Promise.all([
107
106
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
108
- // ✅ 使用 pending nonce,避免 xLayer 等链上“已有 pending 交易”导致 nonce too low
109
107
  Promise.all(wallets.map((w) => provider.getTransactionCount(w.address, 'pending'))),
110
108
  Promise.all(amountsWei.map((amount) => portal.quoteExactInput
111
109
  .staticCall({
@@ -114,15 +112,14 @@ export async function flapBatchPrivateSellMerkle(params) {
114
112
  inputAmount: amount,
115
113
  })
116
114
  .catch(() => 0n))),
117
- // ✅ 并行构建未签名交易(已移除滑点保护:minOuts 固定为 0)
118
- Promise.all(portals.map((p, i) => p.swapExactInput.populateTransaction({
119
- inputToken: tokenAddress,
120
- outputToken: ZERO_ADDRESS,
121
- inputAmount: amountsWei[i],
122
- minOutputAmount: 0n,
123
- permitData: '0x',
124
- }))),
125
115
  ]);
116
+ const unsignedList = await Promise.all(portals.map((p, i) => p.swapExactInput.populateTransaction({
117
+ inputToken: tokenAddress,
118
+ outputToken: ZERO_ADDRESS,
119
+ inputAmount: amountsWei[i],
120
+ minOutputAmount: (quotedOutputs[i] * 9900n) / 10000n,
121
+ permitData: '0x',
122
+ })));
126
123
  const extractProfit = shouldExtractProfit();
127
124
  const { totalProfit, maxRevenueIndex } = summarizeSellProfits(quotedOutputs, extractProfit);
128
125
  // ✅ 获取贿赂金额
@@ -165,6 +165,8 @@ export async function buildBNBHopChainForERC20(payer, path, finalGasAmount, gasP
165
165
  const fromWallet = path.hopWallets[i];
166
166
  const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
167
167
  const amount = i === hopCount - 1 ? finalGasAmount : amountsPerHop[i + 1];
168
+ if (i === hopCount - 1 && amount === 0n)
169
+ continue;
168
170
  signedTxs.push(await fromWallet.signTransaction({
169
171
  to: toAddress,
170
172
  value: amount,
@@ -139,15 +139,9 @@ export async function flapQuickBatchSwapMerkle(params) {
139
139
  sellerRequired = bribeAmount + sellerGasCost;
140
140
  }
141
141
  else {
142
- const reserveForBuyers = ethers.parseEther((config.reserveGasETH || 0.0005).toString());
143
142
  sellerGasCost =
144
- gasPrice *
145
- (BRIBE_GAS_LIMIT +
146
- finalGasLimit +
147
- (NATIVE_TRANSFER_GAS_LIMIT + ERC20_TRANSFER_GAS) * BigInt(buyers.length) +
148
- BRIBE_GAS_LIMIT);
149
- sellerRequired =
150
- bribeAmount + sellerGasCost + (reserveForBuyers + gasPrice * finalGasLimit) * BigInt(buyers.length);
143
+ gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
144
+ sellerRequired = bribeAmount + sellerGasCost;
151
145
  }
152
146
  if (sellerBalance < sellerRequired) {
153
147
  throw new Error(`主钱包 ${chainContext.nativeToken} 余额不足: 需要约 ${ethers.formatEther(sellerRequired)} (贿赂: ${ethers.formatEther(bribeAmount)}, Gas: ${ethers.formatEther(sellerGasCost)}), 实际 ${ethers.formatEther(sellerBalance)}`);
@@ -189,8 +183,6 @@ export async function flapQuickBatchSwapMerkle(params) {
189
183
  });
190
184
  const signedSell = await seller.signTransaction(sellTx);
191
185
  // ==================== 3. 转账交易(支持多跳)====================
192
- const reserveGas = ethers.parseEther((config.reserveGasETH || 0.0005).toString());
193
- const buyerGasCost = gasPrice * finalGasLimit;
194
186
  // ✅ 生成多跳路径
195
187
  const hopPaths = generateDisperseHopPaths(buyers.map((b) => b.address), disperseHopCount, chainContext.provider);
196
188
  // 收集所有中间钱包信息
@@ -204,7 +196,7 @@ export async function flapQuickBatchSwapMerkle(params) {
204
196
  const transferNonces = buyers.map((_, i) => sellerNonce + i);
205
197
  sellerNonce += buyers.length;
206
198
  transferTxs = await Promise.all(buyers.map((buyer, i) => {
207
- const transferValue = transferAmountsWei[i] + reserveGas + buyerGasCost;
199
+ const transferValue = transferAmountsWei[i];
208
200
  return seller.signTransaction({
209
201
  to: buyer.address,
210
202
  value: transferValue,
@@ -218,30 +210,21 @@ export async function flapQuickBatchSwapMerkle(params) {
218
210
  }
219
211
  else {
220
212
  const erc20Interface = new ethers.Interface(ERC20_ABI);
221
- transferTxs = [];
222
- for (let i = 0; i < buyers.length; i++) {
223
- const buyer = buyers[i];
224
- transferTxs.push(await seller.signTransaction({
225
- to: buyer.address,
226
- value: reserveGas + buyerGasCost,
227
- nonce: sellerNonce++,
228
- gasPrice,
229
- gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
230
- chainId: chainContext.chainId,
231
- type: txType,
232
- }));
213
+ const transferNonces = buyers.map((_, i) => sellerNonce + i);
214
+ sellerNonce += buyers.length;
215
+ transferTxs = await Promise.all(buyers.map((buyer, i) => {
233
216
  const transferData = erc20Interface.encodeFunctionData('transfer', [buyer.address, transferAmountsWei[i]]);
234
- transferTxs.push(await seller.signTransaction({
217
+ return seller.signTransaction({
235
218
  to: quoteToken,
236
219
  data: transferData,
237
220
  value: 0n,
238
- nonce: sellerNonce++,
221
+ nonce: transferNonces[i],
239
222
  gasPrice,
240
223
  gasLimit: ERC20_TRANSFER_GAS,
241
224
  chainId: chainContext.chainId,
242
225
  type: txType,
243
- }));
244
- }
226
+ });
227
+ }));
245
228
  }
246
229
  }
247
230
  else {
@@ -250,7 +233,7 @@ export async function flapQuickBatchSwapMerkle(params) {
250
233
  // 原生代币多跳转账
251
234
  // ✅ 修复:payer 每次只发送 1 笔交易(到第一个hop钱包),hop钱包用自己的nonce=0
252
235
  const hopChains = await Promise.all(hopPaths.map((path, i) => {
253
- const finalAmount = transferAmountsWei[i] + reserveGas + buyerGasCost;
236
+ const finalAmount = transferAmountsWei[i];
254
237
  const payerNonce = sellerNonce + i; // ✅ payer 每个 buyer 只用 1 个 nonce
255
238
  return buildNativeHopChain(seller, path, finalAmount, gasPrice, chainContext.chainId, txType, payerNonce);
256
239
  }));
@@ -262,7 +245,7 @@ export async function flapQuickBatchSwapMerkle(params) {
262
245
  // ✅ 修复:payer 每次只发送 1 笔交易(到第一个hop钱包),hop钱包用自己的nonce=0/1
263
246
  // 1. 构建 BNB 多跳链
264
247
  const bnbHopChains = await Promise.all(hopPaths.map((path, i) => {
265
- const finalGasAmount = buyerGasCost;
248
+ const finalGasAmount = 0n;
266
249
  const payerNonce = sellerNonce + i; // ✅ payer 每个 buyer 只用 1 个 nonce
267
250
  return buildBNBHopChainForERC20(seller, path, finalGasAmount, gasPrice, chainContext.chainId, txType, payerNonce);
268
251
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "4.0.13",
3
+ "version": "4.1.0",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",