four-flap-meme-sdk 1.4.84 → 1.4.86

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.
@@ -25,6 +25,30 @@ export declare enum MigratorType {
25
25
  V3_MIGRATOR = 0,
26
26
  V2_MIGRATOR = 1
27
27
  }
28
+ /**
29
+ * V3 LP 费率档位
30
+ * 决定代币迁移到 Uniswap V3 或 PancakeSwap V3 时使用的费率
31
+ */
32
+ export declare enum V3LPFeeProfile {
33
+ /** 标准费率: PancakeSwap 0.25%, Uniswap 0.3% */
34
+ LP_FEE_PROFILE_STANDARD = 0,
35
+ /** 低费率: PancakeSwap 0.01%, Uniswap 0.05% (BNB 链需要白名单) */
36
+ LP_FEE_PROFILE_LOW = 1,
37
+ /** 高费率: 1% (适用于特殊交易对) */
38
+ LP_FEE_PROFILE_HIGH = 2
39
+ }
40
+ /**
41
+ * DEX ID
42
+ * 决定代币迁移到哪个 DEX
43
+ * - BSC: DEX0 = PancakeSwap
44
+ * - xLayer: DEX0 = PotatoSwap
45
+ * - Monad: DEX0 = Uniswap, DEX1 = PancakeSwap, DEX2 = Monday
46
+ */
47
+ export declare enum DEXId {
48
+ DEX0 = 0,
49
+ DEX1 = 1,
50
+ DEX2 = 2
51
+ }
28
52
  export type TokenStateV2 = {
29
53
  status: number;
30
54
  reserve: bigint;
@@ -120,6 +144,27 @@ export type NewTokenV3Params = {
120
144
  extensionID: string;
121
145
  extensionData?: string;
122
146
  };
147
+ /**
148
+ * newTokenV4 参数
149
+ * 相比 V3 新增了 DEX 选择和 LP 费率配置
150
+ */
151
+ export type NewTokenV4Params = {
152
+ name: string;
153
+ symbol: string;
154
+ meta: string;
155
+ dexThresh: number;
156
+ salt: string;
157
+ taxRate: number;
158
+ migratorType: number;
159
+ quoteToken: string;
160
+ quoteAmt: bigint;
161
+ beneficiary: string;
162
+ permitData?: string;
163
+ extensionID: string;
164
+ extensionData?: string;
165
+ dexId: number;
166
+ lpFeeProfile: number;
167
+ };
123
168
  export declare class FlapPortal {
124
169
  private rpcUrl;
125
170
  protected portal: string;
@@ -174,6 +219,39 @@ export declare class FlapPortalWriter extends FlapPortal {
174
219
  newTokenV3(args: NewTokenV3Params & {
175
220
  msgValue?: bigint;
176
221
  }): Promise<any>;
222
+ /**
223
+ * 创建代币 V4 版本
224
+ * 相比 V3 新增了 DEX 选择和 LP 费率配置
225
+ *
226
+ * @param args 创建代币参数
227
+ * @param args.dexId DEX ID,选择迁移到哪个 DEX(使用 DEXId 枚举)
228
+ * @param args.lpFeeProfile LP 费率档位(使用 V3LPFeeProfile 枚举)
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * await portal.newTokenV4({
233
+ * name: 'My Token',
234
+ * symbol: 'MTK',
235
+ * meta: 'bafkreicwlkpvrcqg4bbhyp2fnhdwbqos5ghka6gdjra3tdkgxxs74hqsze',
236
+ * dexThresh: DexThreshType.FOUR_FIFTHS,
237
+ * salt: '0x...',
238
+ * taxRate: 0,
239
+ * migratorType: MigratorType.V3_MIGRATOR,
240
+ * quoteToken: '0x0000000000000000000000000000000000000000',
241
+ * quoteAmt: 0n,
242
+ * beneficiary: '0x...',
243
+ * extensionID: '0x0000000000000000000000000000000000000000000000000000000000000000',
244
+ * dexId: DEXId.DEX0,
245
+ * lpFeeProfile: V3LPFeeProfile.LP_FEE_PROFILE_STANDARD,
246
+ * msgValue: parseEther('0.01'),
247
+ * });
248
+ * ```
249
+ *
250
+ * @note BNB 链上使用 LP_FEE_PROFILE_LOW (0.01%) 需要白名单地址
251
+ */
252
+ newTokenV4(args: NewTokenV4Params & {
253
+ msgValue?: bigint;
254
+ }): Promise<any>;
177
255
  claim(token: string): Promise<{
178
256
  txHash: string;
179
257
  tokenAmount: bigint;
@@ -31,6 +31,32 @@ export var MigratorType;
31
31
  MigratorType[MigratorType["V3_MIGRATOR"] = 0] = "V3_MIGRATOR";
32
32
  MigratorType[MigratorType["V2_MIGRATOR"] = 1] = "V2_MIGRATOR";
33
33
  })(MigratorType || (MigratorType = {}));
34
+ /**
35
+ * V3 LP 费率档位
36
+ * 决定代币迁移到 Uniswap V3 或 PancakeSwap V3 时使用的费率
37
+ */
38
+ export var V3LPFeeProfile;
39
+ (function (V3LPFeeProfile) {
40
+ /** 标准费率: PancakeSwap 0.25%, Uniswap 0.3% */
41
+ V3LPFeeProfile[V3LPFeeProfile["LP_FEE_PROFILE_STANDARD"] = 0] = "LP_FEE_PROFILE_STANDARD";
42
+ /** 低费率: PancakeSwap 0.01%, Uniswap 0.05% (BNB 链需要白名单) */
43
+ V3LPFeeProfile[V3LPFeeProfile["LP_FEE_PROFILE_LOW"] = 1] = "LP_FEE_PROFILE_LOW";
44
+ /** 高费率: 1% (适用于特殊交易对) */
45
+ V3LPFeeProfile[V3LPFeeProfile["LP_FEE_PROFILE_HIGH"] = 2] = "LP_FEE_PROFILE_HIGH";
46
+ })(V3LPFeeProfile || (V3LPFeeProfile = {}));
47
+ /**
48
+ * DEX ID
49
+ * 决定代币迁移到哪个 DEX
50
+ * - BSC: DEX0 = PancakeSwap
51
+ * - xLayer: DEX0 = PotatoSwap
52
+ * - Monad: DEX0 = Uniswap, DEX1 = PancakeSwap, DEX2 = Monday
53
+ */
54
+ export var DEXId;
55
+ (function (DEXId) {
56
+ DEXId[DEXId["DEX0"] = 0] = "DEX0";
57
+ DEXId[DEXId["DEX1"] = 1] = "DEX1";
58
+ DEXId[DEXId["DEX2"] = 2] = "DEX2";
59
+ })(DEXId || (DEXId = {}));
34
60
  // Portal ABI (使用 ethers 标准格式)
35
61
  const PORTAL_ABI = [
36
62
  // Token 状态查询
@@ -52,6 +78,7 @@ const PORTAL_ABI = [
52
78
  // 创建代币
53
79
  'function newTokenV2((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData)) external payable returns (address)',
54
80
  'function newTokenV3((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData,bytes32 extensionID,bytes extensionData)) external payable returns (address)',
81
+ 'function newTokenV4((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData,bytes32 extensionID,bytes extensionData,uint8 dexId,uint8 lpFeeProfile)) external payable returns (address)',
55
82
  // 受益人领取
56
83
  'function claim(address token) external returns (uint256,uint256)',
57
84
  'function delegateClaim(address token) external returns (uint256,uint256)',
@@ -315,6 +342,56 @@ export class FlapPortalWriter extends FlapPortal {
315
342
  }, { value: args.msgValue });
316
343
  return await tx.wait();
317
344
  }
345
+ /**
346
+ * 创建代币 V4 版本
347
+ * 相比 V3 新增了 DEX 选择和 LP 费率配置
348
+ *
349
+ * @param args 创建代币参数
350
+ * @param args.dexId DEX ID,选择迁移到哪个 DEX(使用 DEXId 枚举)
351
+ * @param args.lpFeeProfile LP 费率档位(使用 V3LPFeeProfile 枚举)
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * await portal.newTokenV4({
356
+ * name: 'My Token',
357
+ * symbol: 'MTK',
358
+ * meta: 'bafkreicwlkpvrcqg4bbhyp2fnhdwbqos5ghka6gdjra3tdkgxxs74hqsze',
359
+ * dexThresh: DexThreshType.FOUR_FIFTHS,
360
+ * salt: '0x...',
361
+ * taxRate: 0,
362
+ * migratorType: MigratorType.V3_MIGRATOR,
363
+ * quoteToken: '0x0000000000000000000000000000000000000000',
364
+ * quoteAmt: 0n,
365
+ * beneficiary: '0x...',
366
+ * extensionID: '0x0000000000000000000000000000000000000000000000000000000000000000',
367
+ * dexId: DEXId.DEX0,
368
+ * lpFeeProfile: V3LPFeeProfile.LP_FEE_PROFILE_STANDARD,
369
+ * msgValue: parseEther('0.01'),
370
+ * });
371
+ * ```
372
+ *
373
+ * @note BNB 链上使用 LP_FEE_PROFILE_LOW (0.01%) 需要白名单地址
374
+ */
375
+ async newTokenV4(args) {
376
+ const tx = await this.contract.newTokenV4({
377
+ name: args.name,
378
+ symbol: args.symbol,
379
+ meta: args.meta,
380
+ dexThresh: args.dexThresh,
381
+ salt: args.salt,
382
+ taxRate: args.taxRate,
383
+ migratorType: args.migratorType,
384
+ quoteToken: args.quoteToken,
385
+ quoteAmt: args.quoteAmt,
386
+ beneficiary: args.beneficiary,
387
+ permitData: args.permitData ?? '0x',
388
+ extensionID: args.extensionID,
389
+ extensionData: args.extensionData ?? '0x',
390
+ dexId: args.dexId,
391
+ lpFeeProfile: args.lpFeeProfile,
392
+ }, { value: args.msgValue });
393
+ return await tx.wait();
394
+ }
318
395
  // 受益人领取收益
319
396
  async claim(token) {
320
397
  const tx = await this.contract.claim(token);
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { TM1, type FourChainV1 } from './contracts/tm1.js';
11
11
  export { TM2, type FourChainV2 } from './contracts/tm2.js';
12
12
  export { Helper3, Helper3Writer } from './contracts/helper3.js';
13
13
  export { CDPV2 } from './flap/curve.js';
14
- export { FlapPortal, FlapPortalWriter, type FlapChain, type PortalConfig, type TokenStateV2, type TokenStateV3, type TokenStateV4, type TokenStateV5, type QuoteExactInputParams, type ExactInputParams, type ExactInputV3Params, type NewTokenV3Params, TokenStatus, TokenVersion, DexThreshType, MigratorType } from './flap/portal.js';
14
+ export { FlapPortal, FlapPortalWriter, type FlapChain, type PortalConfig, type TokenStateV2, type TokenStateV3, type TokenStateV4, type TokenStateV5, type QuoteExactInputParams, type ExactInputParams, type ExactInputV3Params, type NewTokenV3Params, type NewTokenV4Params, TokenStatus, TokenVersion, DexThreshType, MigratorType, V3LPFeeProfile, DEXId } from './flap/portal.js';
15
15
  export { uploadTokenMeta, type TokenMetaInput } from './flap/ipfs.js';
16
16
  export { buildPermitPiggybackAuto } from './flap/permit.js';
17
17
  export { predictVanityTokenAddressByChain, findSaltEndingByChain } from './flap/vanity.js';
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ export { TM1 } from './contracts/tm1.js';
29
29
  export { TM2 } from './contracts/tm2.js';
30
30
  export { Helper3, Helper3Writer } from './contracts/helper3.js';
31
31
  export { CDPV2 } from './flap/curve.js';
32
- export { FlapPortal, FlapPortalWriter, TokenStatus, TokenVersion, DexThreshType, MigratorType } from './flap/portal.js';
32
+ export { FlapPortal, FlapPortalWriter, TokenStatus, TokenVersion, DexThreshType, MigratorType, V3LPFeeProfile, DEXId } from './flap/portal.js';
33
33
  export { uploadTokenMeta } from './flap/ipfs.js';
34
34
  export { buildPermitPiggybackAuto } from './flap/permit.js';
35
35
  export { predictVanityTokenAddressByChain, findSaltEndingByChain } from './flap/vanity.js';
@@ -15,8 +15,10 @@ export interface PancakeBuyFirstSignConfig {
15
15
  chainId?: number;
16
16
  reserveGasBNB?: number;
17
17
  skipQuoteOnError?: boolean;
18
+ userType?: UserType;
18
19
  bribeAmount?: number;
19
20
  }
21
+ export type UserType = 'v0' | 'v1';
20
22
  export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
21
23
  export interface V2RouteParams {
22
24
  routeType: 'v2';
@@ -6,7 +6,7 @@
6
6
  import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
8
8
  import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA, ZERO_ADDRESS } from '../utils/constants.js';
9
- import { quoteV2, quoteV3, getTokenToNativeQuote, getWrappedNativeAddress } from '../utils/quote-helpers.js';
9
+ import { quoteV2, quoteV3, getTokenToNativeQuote } from '../utils/quote-helpers.js';
10
10
  import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI } from '../abis/common.js';
11
11
  /**
12
12
  * 获取 Gas Limit
@@ -52,8 +52,15 @@ const BRIBE_TX_COUNT = 1;
52
52
  const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
53
53
  /** 最大买卖交易数 */
54
54
  const MAX_SWAP_TX_COUNT = MAX_BUNDLE_SIGNATURES - BRIBE_TX_COUNT - PROFIT_TX_COUNT; // 50 - 1 - 4 = 45
55
- /** 每笔交易利润比例(基点):6 bps = 0.06% = 万分之六 */
56
- const PROFIT_RATE_PER_TX_BPS = 6;
55
+ /**
56
+ * 根据 userType 获取每笔交易的利润率
57
+ */
58
+ function getProfitRatePerTxBps(userType) {
59
+ if (userType === 'v1') {
60
+ return PROFIT_CONFIG.RATE_BPS_V1;
61
+ }
62
+ return PROFIT_CONFIG.RATE_BPS_V0; // v0 或默认
63
+ }
57
64
  /**
58
65
  * 验证买卖笔数
59
66
  */
@@ -136,9 +143,9 @@ export async function pancakeBundleBuyFirstMerkle(params) {
136
143
  throw new Error('单钱包模式需要提供 buyerPrivateKey 和 sellerPrivateKey');
137
144
  }
138
145
  validateSwapCounts(buyCount, sellCount);
139
- // ✅ 计算利润比例:每笔万分之3
146
+ // ✅ 计算利润比例(根据 userType 动态调整)
140
147
  const totalTxCount = buyCount + sellCount;
141
- const profitRateBps = PROFIT_RATE_PER_TX_BPS * totalTxCount;
148
+ const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
142
149
  // ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
143
150
  const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
144
151
  const context = createPancakeContext(config);
@@ -173,31 +180,22 @@ export async function pancakeBundleBuyFirstMerkle(params) {
173
180
  tokenAddress,
174
181
  useNativeToken
175
182
  });
176
- const quotedNative = await quoteSellerNative({
177
- provider: context.provider,
178
- tokenAddress,
179
- sellAmountToken: quoteResult.quotedTokenOut,
180
- routeParams // ✅ 传递路由参数
181
- });
182
- const buyerNeed = calculateBuyerNeed({
183
- quotedNative,
184
- buyerBalance: buyerFundsInfo.buyerBalance,
185
- reserveGas: buyerFundsInfo.reserveGas
186
- });
187
183
  const finalGasLimit = getGasLimit(config);
188
184
  const gasPrice = await getGasPrice(context.provider, config);
189
185
  const txType = config.txType ?? 0;
190
186
  const nonceManager = new NonceManager(context.provider);
191
- // ✅ 修复:基于买入金额估算利润(使用动态利润比例)
192
- const estimatedProfitFromSell = await estimateProfitAmount({
193
- provider: context.provider,
194
- tokenAddress,
195
- sellAmountToken: quoteResult.quotedTokenOut,
196
- routeParams
197
- });
198
- // 使用动态利润比例
199
- const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : buyerFundsInfo.buyerFundsWei;
200
- const profitAmount = (profitBase * BigInt(profitRateBps)) / 10000n;
187
+ // ✅ 直接使用买入金额作为利润基础(先买后卖模式)
188
+ // profitRateBps = 每笔交易利润率(bps) * 交易笔数
189
+ // 例如:v1用户,1买1卖 → 5 * 2 = 10 bps = 0.1%
190
+ const tokenProfitAmount = (buyerFundsInfo.buyerFundsWei * BigInt(profitRateBps)) / 10000n;
191
+ // ✅ ERC20 购买:将代币利润转换为等值 BNB(因为利润是用 BNB 转账的)
192
+ let profitAmount = tokenProfitAmount;
193
+ if (!useNativeToken && tokenProfitAmount > 0n && quoteToken) {
194
+ const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
195
+ const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
196
+ profitAmount = await getTokenToNativeQuote(context.provider, quoteToken, tokenProfitAmount, 'BSC', version, fee);
197
+ console.log(`[pancakeBundleBuyFirstMerkle] ERC20→BNB 利润转换: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(profitAmount)} BNB`);
198
+ }
201
199
  // ✅ 获取贿赂金额
202
200
  const bribeAmount = config.bribeAmount && config.bribeAmount > 0
203
201
  ? ethers.parseEther(String(config.bribeAmount))
@@ -399,100 +397,6 @@ async function quoteTokenOutput({ routeParams, buyerFundsWei, provider }) {
399
397
  }
400
398
  return { quotedTokenOut: result.amountOut };
401
399
  }
402
- /**
403
- * ✅ 使用 quote-helpers 统一报价(卖出 → 原生代币)
404
- */
405
- async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, routeParams }) {
406
- const wbnb = getWrappedNativeAddress('BSC');
407
- const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
408
- const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
409
- return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
410
- }
411
- function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
412
- const estimatedBuyerNeed = quotedNative;
413
- const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
414
- if (buyerBalance < buyerNeedTotal) {
415
- throw new Error(`买方余额不足:\n - 需要: ${ethers.formatEther(buyerNeedTotal)} BNB\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
416
- }
417
- const maxBuyerValue = estimatedBuyerNeed > 0n
418
- ? estimatedBuyerNeed
419
- : buyerBalance - reserveGas;
420
- return { buyerNeedTotal, maxBuyerValue };
421
- }
422
- async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountToken, buyer, seller, tokenAddress, useNativeToken = true }) {
423
- const deadline = getDeadline();
424
- if (routeParams.routeType === 'v2') {
425
- const { v2Path } = routeParams;
426
- const reversePath = [...v2Path].reverse();
427
- // ✅ 使用官方 V2 Router
428
- const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
429
- const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
430
- if (useNativeToken) {
431
- // ✅ BNB 池子:使用 swapExactETHForTokens / swapExactTokensForETH
432
- const buyValue = buyerFundsWei + FLAT_FEE;
433
- const buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
434
- const sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
435
- return { buyUnsigned, sellUnsigned };
436
- }
437
- else {
438
- // ✅ ERC20 池子(如 USDT):使用 swapExactTokensForTokens
439
- const buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyerFundsWei, 0n, v2Path, buyer.address, deadline);
440
- const sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
441
- return { buyUnsigned, sellUnsigned };
442
- }
443
- }
444
- if (routeParams.routeType === 'v3-single') {
445
- const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
446
- const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
447
- const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
448
- const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
449
- // ✅ V3 池子的处理(ERC20 模式 value = 0,因为通过代币授权支付)
450
- const buyValue = useNativeToken ? buyerFundsWei + FLAT_FEE : 0n;
451
- // 买入交易
452
- const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
453
- tokenIn: v3TokenIn,
454
- tokenOut: v3TokenOut,
455
- fee: v3Fee,
456
- recipient: buyer.address,
457
- amountIn: buyerFundsWei,
458
- amountOutMinimum: 0n,
459
- sqrtPriceLimitX96: 0n
460
- }]);
461
- const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
462
- // 卖出交易
463
- if (useNativeToken) {
464
- // ✅ BNB 池子:卖出后 unwrap WBNB 为 BNB
465
- const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
466
- tokenIn: v3TokenOut,
467
- tokenOut: v3TokenIn,
468
- fee: v3Fee,
469
- recipient: PANCAKE_V3_ROUTER_ADDRESS,
470
- amountIn: sellAmountToken,
471
- amountOutMinimum: 0n,
472
- sqrtPriceLimitX96: 0n
473
- }]);
474
- const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
475
- const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
476
- return { buyUnsigned, sellUnsigned };
477
- }
478
- else {
479
- // ✅ ERC20 池子:卖出后直接获得 ERC20
480
- const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
481
- tokenIn: v3TokenOut,
482
- tokenOut: v3TokenIn,
483
- fee: v3Fee,
484
- recipient: seller.address, // 直接发送到卖方地址
485
- amountIn: sellAmountToken,
486
- amountOutMinimum: 0n,
487
- sqrtPriceLimitX96: 0n
488
- }]);
489
- const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData]);
490
- return { buyUnsigned, sellUnsigned };
491
- }
492
- }
493
- // V3 多跳暂不支持官方合约
494
- throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
495
- }
496
400
  /**
497
401
  * ✅ 构建多笔买入和卖出交易
498
402
  */
@@ -576,60 +480,6 @@ async function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmo
576
480
  }
577
481
  throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
578
482
  }
579
- /**
580
- * ✅ 使用 quote-helpers 统一报价(估算利润)
581
- */
582
- async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, routeParams }) {
583
- try {
584
- const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
585
- const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
586
- const estimatedSellFunds = await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
587
- if (estimatedSellFunds <= 0n) {
588
- return 0n;
589
- }
590
- // 万分之六
591
- return (estimatedSellFunds * BigInt(PROFIT_CONFIG.RATE_BPS_SWAP)) / 10000n;
592
- }
593
- catch {
594
- return 0n;
595
- }
596
- }
597
- /**
598
- * ✅ 规划 nonce
599
- * 交易顺序:贿赂 → 买入 → 卖出 → 利润
600
- */
601
- async function planNonces({ buyer, seller, sameAddress, extractProfit, needBribeTx, nonceManager }) {
602
- if (sameAddress) {
603
- // 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
604
- const txCount = countTruthy([needBribeTx, true, true, extractProfit]);
605
- const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
606
- let idx = 0;
607
- const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
608
- const buyerNonce = nonces[idx++];
609
- const sellerNonce = nonces[idx++];
610
- const profitNonce = extractProfit ? nonces[idx] : undefined;
611
- return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
612
- }
613
- if (needBribeTx || extractProfit) {
614
- // 卖方需要多个 nonce:贿赂(可选) + 卖出 + 利润(可选)
615
- const sellerTxCount = countTruthy([needBribeTx, true, extractProfit]);
616
- // ✅ 并行获取 seller 和 buyer 的 nonce
617
- const [sellerNonces, buyerNonce] = await Promise.all([
618
- nonceManager.getNextNonceBatch(seller, sellerTxCount),
619
- nonceManager.getNextNonce(buyer)
620
- ]);
621
- let idx = 0;
622
- const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
623
- const sellerNonce = sellerNonces[idx++];
624
- const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
625
- return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
626
- }
627
- const [buyerNonce, sellerNonce] = await Promise.all([
628
- nonceManager.getNextNonce(buyer),
629
- nonceManager.getNextNonce(seller)
630
- ]);
631
- return { buyerNonce, sellerNonce };
632
- }
633
483
  /**
634
484
  * ✅ 规划多笔交易 nonce(与 Flap 一致)
635
485
  * 交易顺序:贿赂 → 买入(多笔) → 卖出(多笔) → 利润
@@ -722,33 +572,6 @@ async function validateFinalBalances({ sameAddress, buyerFundsWei, buyerBalance,
722
572
  }
723
573
  // ERC20 余额已在 calculateBuyerFunds 中检查过
724
574
  }
725
- function countTruthy(values) {
726
- return values.filter(Boolean).length;
727
- }
728
- /**
729
- * ✅ 从前端传入的 startNonces 构建 NoncePlan(用于性能优化,避免 nonce 冲突)
730
- * 顺序:同地址时 [baseNonce],不同地址时 [sellerNonce, buyerNonce]
731
- */
732
- function buildNoncePlanFromStartNonces(startNonces, sameAddress, profitNeeded, needBribeTx) {
733
- if (sameAddress) {
734
- // 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
735
- let idx = 0;
736
- const baseNonce = startNonces[0];
737
- const bribeNonce = needBribeTx ? baseNonce + idx++ : undefined;
738
- const buyerNonce = baseNonce + idx++;
739
- const sellerNonce = baseNonce + idx++;
740
- const profitNonce = profitNeeded ? baseNonce + idx : undefined;
741
- return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
742
- }
743
- // 不同地址
744
- let sellerIdx = 0;
745
- const sellerBaseNonce = startNonces[0];
746
- const bribeNonce = needBribeTx ? sellerBaseNonce + sellerIdx++ : undefined;
747
- const sellerNonce = sellerBaseNonce + sellerIdx++;
748
- const profitNonce = profitNeeded ? sellerBaseNonce + sellerIdx : undefined;
749
- const buyerNonce = startNonces[1];
750
- return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
751
- }
752
575
  /**
753
576
  * ✅ 多钱包捆绑换手
754
577
  * - 多个买方钱包执行买入(每个钱包1笔)
@@ -766,9 +589,9 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
766
589
  throw new Error('卖方钱包数量不能为0');
767
590
  // 验证总交易数不超过限制
768
591
  validateSwapCounts(buyCount, sellCount);
769
- // ✅ 计算利润比例:每笔万分之6
592
+ // ✅ 计算利润比例(根据 userType 动态调整)
770
593
  const totalTxCount = buyCount + sellCount;
771
- const profitRateBps = PROFIT_RATE_PER_TX_BPS * totalTxCount;
594
+ const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
772
595
  // ✅ 判断是否使用原生代币
773
596
  const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
774
597
  const context = createPancakeContext(config);
@@ -824,15 +647,17 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
824
647
  const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
825
648
  // ✅ 将代币平均分配给卖方
826
649
  const sellAmountsWei = splitAmount(quoteResult.quotedTokenOut, sellCount);
827
- // ✅ 第二批并行:估算利润(可以和交易构建并行,但需要先获取报价)
828
- const estimatedProfitFromSell = await estimateProfitAmount({
829
- provider: context.provider,
830
- tokenAddress,
831
- sellAmountToken: quoteResult.quotedTokenOut,
832
- routeParams
833
- });
834
- const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : totalFundsWei;
835
- const profitAmount = (profitBase * BigInt(profitRateBps)) / 10000n;
650
+ // ✅ 直接使用总交易金额作为利润基础(多钱包模式)
651
+ // profitRateBps = 每笔交易利润率(bps) * 交易笔数
652
+ const tokenProfitAmount = (totalFundsWei * BigInt(profitRateBps)) / 10000n;
653
+ // ✅ ERC20 购买:将代币利润转换为等值 BNB(因为利润是用 BNB 转账的)
654
+ let profitAmount = tokenProfitAmount;
655
+ if (!useNativeToken && tokenProfitAmount > 0n && quoteToken) {
656
+ const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
657
+ const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
658
+ profitAmount = await getTokenToNativeQuote(context.provider, quoteToken, tokenProfitAmount, 'BSC', version, fee);
659
+ console.log(`[pancakeBundleBuyFirstMultiWallet] ERC20→BNB 利润转换: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(profitAmount)} BNB`);
660
+ }
836
661
  // ✅ 第三批并行:构建并签名所有交易
837
662
  const allTransactions = [];
838
663
  // 1. 贿赂交易(由主卖方支付)- 先处理以确定 nonce 偏移
@@ -1,5 +1,6 @@
1
1
  import { CommonBundleConfig } from '../utils/bundle-helpers.js';
2
2
  import { type GeneratedWallet } from '../utils/wallet.js';
3
+ export type UserType = 'v0' | 'v1';
3
4
  export interface PancakeSwapSignConfig {
4
5
  rpcUrl: string;
5
6
  gasLimit?: number | bigint;
@@ -9,6 +10,7 @@ export interface PancakeSwapSignConfig {
9
10
  txType?: 0 | 2;
10
11
  chainId?: number;
11
12
  reserveGasBNB?: number;
13
+ userType?: UserType;
12
14
  bribeAmount?: number;
13
15
  }
14
16
  export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
@@ -439,11 +439,24 @@ async function planNonces({ seller, buyer, sameAddress, approvalExists, profitNe
439
439
  ]);
440
440
  return { sellerNonce, buyerNonce };
441
441
  }
442
- function calculateProfitAmount(estimatedBNBOut) {
442
+ /**
443
+ * 根据 userType 计算利润率
444
+ */
445
+ function getProfitRateBps(userType) {
446
+ if (userType === 'v0') {
447
+ return PROFIT_CONFIG.RATE_BPS_V0; // 0.06%
448
+ }
449
+ else if (userType === 'v1') {
450
+ return PROFIT_CONFIG.RATE_BPS_V1; // 0.05%
451
+ }
452
+ return PROFIT_CONFIG.RATE_BPS_V0; // 默认 0.06%
453
+ }
454
+ function calculateProfitAmount(estimatedBNBOut, userType) {
443
455
  if (estimatedBNBOut <= 0n) {
444
456
  return 0n;
445
457
  }
446
- return (estimatedBNBOut * BigInt(PROFIT_CONFIG.RATE_BPS_SWAP)) / 10000n;
458
+ const rateBps = getProfitRateBps(userType);
459
+ return (estimatedBNBOut * BigInt(rateBps)) / 10000n;
447
460
  }
448
461
  /**
449
462
  * ✅ 获取 ERC20 代币 → 原生代币(BNB)的报价
@@ -619,8 +632,8 @@ export async function pancakeBundleSwapMerkle(params) {
619
632
  // 如果输出是 ERC20(如 USDT),需要先转换为 BNB 等值
620
633
  let profitAmount;
621
634
  if (useNativeToken) {
622
- // 输出是 BNB,直接计算利润
623
- profitAmount = calculateProfitAmount(quoteResult.estimatedBNBOut);
635
+ // 输出是 BNB,直接计算利润(根据 userType 动态调整)
636
+ profitAmount = calculateProfitAmount(quoteResult.estimatedBNBOut, config.userType);
624
637
  console.log(`[pancakeBundleSwapMerkle] 原生代币利润: ${ethers.formatEther(profitAmount)} BNB`);
625
638
  }
626
639
  else {
@@ -629,7 +642,7 @@ export async function pancakeBundleSwapMerkle(params) {
629
642
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
630
643
  const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, quoteResult.estimatedBNBOut, // 这实际上是 ERC20 数量
631
644
  version, fee);
632
- profitAmount = calculateProfitAmount(estimatedBNBValue);
645
+ profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
633
646
  console.log(`[pancakeBundleSwapMerkle] ERC20→BNB 报价: ${ethers.formatUnits(quoteResult.estimatedBNBOut, quoteTokenDecimals)} ${quoteToken?.slice(0, 10)}... → ${ethers.formatEther(estimatedBNBValue)} BNB, 利润: ${ethers.formatEther(profitAmount)} BNB`);
634
647
  }
635
648
  // ✅ 获取贿赂金额
@@ -940,14 +953,14 @@ export async function pancakeBatchSwapMerkle(params) {
940
953
  // ✅ 修复:利润计算应基于 BNB 数量,不是 ERC20 数量
941
954
  let profitAmount;
942
955
  if (useNativeToken) {
943
- profitAmount = calculateProfitAmount(estimatedBNBOut);
956
+ profitAmount = calculateProfitAmount(estimatedBNBOut, config.userType);
944
957
  console.log(`[pancakeBatchSwapMerkle] 原生代币利润: ${ethers.formatEther(profitAmount)} BNB`);
945
958
  }
946
959
  else {
947
960
  const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
948
961
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
949
962
  const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, estimatedBNBOut, version, fee);
950
- profitAmount = calculateProfitAmount(estimatedBNBValue);
963
+ profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
951
964
  console.log(`[pancakeBatchSwapMerkle] ERC20→BNB 报价: ${ethers.formatUnits(estimatedBNBOut, quoteTokenDecimals)} → ${ethers.formatEther(estimatedBNBValue)} BNB, 利润: ${ethers.formatEther(profitAmount)} BNB`);
952
965
  }
953
966
  // 计算利润 nonce
@@ -1143,14 +1156,14 @@ export async function pancakeQuickBatchSwapMerkle(params) {
1143
1156
  // ✅ 计算利润(万分之六)
1144
1157
  let profitAmount;
1145
1158
  if (useNativeToken) {
1146
- profitAmount = calculateProfitAmount(estimatedOutput);
1159
+ profitAmount = calculateProfitAmount(estimatedOutput, config.userType);
1147
1160
  }
1148
1161
  else {
1149
1162
  // ERC20 模式:需要将 ERC20 价值转换为 BNB
1150
1163
  const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
1151
1164
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
1152
1165
  const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, estimatedOutput, version, fee);
1153
- profitAmount = calculateProfitAmount(estimatedBNBValue);
1166
+ profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
1154
1167
  console.log(`[pancakeQuickBatchSwapMerkle] ERC20→BNB 报价: ${outputFormatted} → ${ethers.formatEther(estimatedBNBValue)} BNB`);
1155
1168
  }
1156
1169
  const distributableAmount = estimatedOutput - (useNativeToken ? profitAmount : 0n);
@@ -17,10 +17,12 @@ export declare const PROFIT_CONFIG: {
17
17
  readonly RECIPIENT: "0xe8D0334fAf713884133640CAEe4ECdd2106AF103";
18
18
  /** 利润比例(基点):30 bps = 0.3% = 千分之三(普通模式) */
19
19
  readonly RATE_BPS: 30;
20
- /** 利润比例(基点):6 bps = 0.06% = 万分之六(资金利用率模式) */
21
- readonly RATE_BPS_CAPITAL: 6;
22
20
  /** 利润比例(基点):6 bps = 0.06% = 万分之六(捆绑换手模式) */
23
21
  readonly RATE_BPS_SWAP: 6;
22
+ /** 利润比例(基点):6 bps = 0.06% = 万分之六(用户类型 v0) */
23
+ readonly RATE_BPS_V0: 6;
24
+ /** 利润比例(基点):5 bps = 0.05% = 万分之五(用户类型 v1) */
25
+ readonly RATE_BPS_V1: 5;
24
26
  };
25
27
  export declare const CHAIN: {
26
28
  BSC: {