four-flap-meme-sdk 1.4.83 → 1.4.85
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.
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +2 -1
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +14 -7
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +2 -0
- package/dist/contracts/tm-bundle-merkle/swap.js +16 -4
- package/dist/contracts/tm-bundle-merkle/types.d.ts +10 -0
- package/dist/contracts/tm-bundle-merkle/utils.js +24 -14
- package/dist/flap/constants.d.ts +1 -1
- package/dist/flap/constants.js +1 -1
- package/dist/flap/portal-bundle-merkle/core.js +36 -5
- package/dist/flap/portal-bundle-merkle/create-to-dex.d.ts +8 -0
- package/dist/flap/portal-bundle-merkle/create-to-dex.js +163 -66
- package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +2 -0
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +25 -9
- package/dist/flap/portal-bundle-merkle/swap.d.ts +2 -0
- package/dist/flap/portal-bundle-merkle/swap.js +21 -8
- package/dist/flap/portal-bundle-merkle/types.d.ts +4 -0
- package/dist/flap/portal.d.ts +78 -0
- package/dist/flap/portal.js +77 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/pancake/bundle-buy-first.d.ts +2 -0
- package/dist/pancake/bundle-buy-first.js +13 -6
- package/dist/pancake/bundle-swap.d.ts +2 -0
- package/dist/pancake/bundle-swap.js +22 -9
- package/dist/utils/constants.d.ts +4 -2
- package/dist/utils/constants.js +4 -2
- package/dist/utils/holders-maker.js +1 -1
- package/package.json +1 -1
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* 功能:钱包B先买入代币 → 钱包A卖出相同数量 → 原子执行
|
|
5
5
|
*/
|
|
6
6
|
import { CommonBundleConfig } from '../../utils/bundle-helpers.js';
|
|
7
|
-
import { FourSignConfig } from './types.js';
|
|
7
|
+
import { FourSignConfig, UserType } from './types.js';
|
|
8
8
|
import type { GeneratedWallet } from '../../utils/wallet.js';
|
|
9
9
|
export interface FourBuyFirstSignConfig extends FourSignConfig {
|
|
10
10
|
reserveGasBNB?: number;
|
|
11
|
+
userType?: UserType;
|
|
11
12
|
}
|
|
12
13
|
export interface FourBuyFirstConfig extends CommonBundleConfig {
|
|
13
14
|
apiKey: string;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
7
7
|
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
|
|
8
|
-
import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
|
|
8
|
+
import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, PROFIT_CONFIG } from '../../utils/constants.js';
|
|
9
9
|
import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
|
|
10
10
|
import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from './config.js';
|
|
11
11
|
import { trySell } from '../tm.js';
|
|
@@ -26,8 +26,15 @@ const BRIBE_TX_COUNT = 1;
|
|
|
26
26
|
const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
|
|
27
27
|
/** 最大买卖交易数 */
|
|
28
28
|
const MAX_SWAP_TX_COUNT = MAX_BUNDLE_SIGNATURES - BRIBE_TX_COUNT - PROFIT_TX_COUNT; // 50 - 1 - 4 = 45
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* 根据 userType 获取每笔交易的利润率
|
|
31
|
+
*/
|
|
32
|
+
function getProfitRatePerTxBps(userType) {
|
|
33
|
+
if (userType === 'v1') {
|
|
34
|
+
return PROFIT_CONFIG.RATE_BPS_V1;
|
|
35
|
+
}
|
|
36
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // v0 或默认
|
|
37
|
+
}
|
|
31
38
|
/**
|
|
32
39
|
* 验证买卖笔数
|
|
33
40
|
*/
|
|
@@ -104,9 +111,9 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
104
111
|
throw new Error('单钱包模式需要提供 buyerPrivateKey 和 sellerPrivateKey');
|
|
105
112
|
}
|
|
106
113
|
validateSwapCounts(buyCount, sellCount);
|
|
107
|
-
// ✅
|
|
114
|
+
// ✅ 计算利润比例(根据 userType 动态调整)
|
|
108
115
|
const totalTxCount = buyCount + sellCount;
|
|
109
|
-
const profitRateBps =
|
|
116
|
+
const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
|
|
110
117
|
const chainIdNum = config.chainId ?? 56;
|
|
111
118
|
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
112
119
|
chainId: chainIdNum,
|
|
@@ -325,9 +332,9 @@ async function fourBundleBuyFirstMultiWallet(params) {
|
|
|
325
332
|
throw new Error('卖方钱包数量不能为0');
|
|
326
333
|
// 验证总交易数不超过限制
|
|
327
334
|
validateSwapCounts(buyCount, sellCount);
|
|
328
|
-
// ✅
|
|
335
|
+
// ✅ 计算利润比例(根据 userType 动态调整)
|
|
329
336
|
const totalTxCount = buyCount + sellCount;
|
|
330
|
-
const profitRateBps =
|
|
337
|
+
const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
|
|
331
338
|
const chainIdNum = config.chainId ?? 56;
|
|
332
339
|
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
333
340
|
chainId: chainIdNum,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { CommonBundleConfig } from '../../utils/bundle-helpers.js';
|
|
7
7
|
import { type GeneratedWallet } from '../../utils/wallet.js';
|
|
8
|
+
export type UserType = 'v0' | 'v1';
|
|
8
9
|
export interface FourSwapSignConfig {
|
|
9
10
|
rpcUrl: string;
|
|
10
11
|
gasLimit?: number | bigint;
|
|
@@ -14,6 +15,7 @@ export interface FourSwapSignConfig {
|
|
|
14
15
|
txType?: 0 | 2;
|
|
15
16
|
chainId?: number;
|
|
16
17
|
reserveGasBNB?: number;
|
|
18
|
+
userType?: UserType;
|
|
17
19
|
bribeAmount?: number;
|
|
18
20
|
}
|
|
19
21
|
export interface FourSwapConfig extends CommonBundleConfig {
|
|
@@ -7,6 +7,18 @@ import { ethers, Contract, Wallet } from 'ethers';
|
|
|
7
7
|
import { calculateSellAmount } from '../../utils/swap-helpers.js';
|
|
8
8
|
import { NonceManager, getOptimizedGasPrice, getGasLimit, getGasPriceConfig, getTxType, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
|
|
9
9
|
import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
|
|
10
|
+
/**
|
|
11
|
+
* 根据 userType 计算利润率
|
|
12
|
+
*/
|
|
13
|
+
function getProfitRateBps(userType) {
|
|
14
|
+
if (userType === 'v0') {
|
|
15
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // 0.06%
|
|
16
|
+
}
|
|
17
|
+
else if (userType === 'v1') {
|
|
18
|
+
return PROFIT_CONFIG.RATE_BPS_V1; // 0.05%
|
|
19
|
+
}
|
|
20
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // 默认 0.06%
|
|
21
|
+
}
|
|
10
22
|
import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
|
|
11
23
|
import { getBribeAmount, getProfitRecipient } from './config.js';
|
|
12
24
|
import { generateWallets } from '../../utils/wallet.js';
|
|
@@ -136,7 +148,7 @@ export async function fourBundleSwapMerkle(params) {
|
|
|
136
148
|
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
137
149
|
// 利润配置
|
|
138
150
|
const extractProfit = true;
|
|
139
|
-
const profitRateBps =
|
|
151
|
+
const profitRateBps = getProfitRateBps(config.userType);
|
|
140
152
|
const profitAmount = extractProfit && sellerWillGetBNB > 0n
|
|
141
153
|
? (sellerWillGetBNB * BigInt(profitRateBps)) / 10000n
|
|
142
154
|
: 0n;
|
|
@@ -358,7 +370,7 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
358
370
|
const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', decimals);
|
|
359
371
|
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
360
372
|
// 利润配置
|
|
361
|
-
const profitRateBps =
|
|
373
|
+
const profitRateBps = getProfitRateBps(config.userType);
|
|
362
374
|
const profitAmount = totalBuyerFunds > 0n
|
|
363
375
|
? (totalBuyerFunds * BigInt(profitRateBps)) / 10000n
|
|
364
376
|
: 0n;
|
|
@@ -578,8 +590,8 @@ export async function fourQuickBatchSwapMerkle(params) {
|
|
|
578
590
|
const estimatedBNBOut = sellQuote.funds;
|
|
579
591
|
console.log(`[fourQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
580
592
|
console.log(`[fourQuickBatchSwapMerkle] 预估卖出所得: ${ethers.formatEther(estimatedBNBOut)} BNB`);
|
|
581
|
-
// ✅
|
|
582
|
-
const profitRateBps =
|
|
593
|
+
// ✅ 计算利润(根据 userType 动态调整)
|
|
594
|
+
const profitRateBps = getProfitRateBps(config.userType);
|
|
583
595
|
const profitAmount = estimatedBNBOut > 0n
|
|
584
596
|
? (estimatedBNBOut * BigInt(profitRateBps)) / 10000n
|
|
585
597
|
: 0n;
|
|
@@ -244,6 +244,12 @@ export type DisperseMerkleParams = {
|
|
|
244
244
|
export type TokenPoolType = 'flap' | 'four' | 'v2' | 'v3';
|
|
245
245
|
/** 报价代币类型(池子的计价代币) */
|
|
246
246
|
export type QuoteTokenType = 'native' | 'usdt' | 'usdc';
|
|
247
|
+
/**
|
|
248
|
+
* 用户类型(用于利润费率)
|
|
249
|
+
* - v0: 万分之6 (6 bps = 0.06%)
|
|
250
|
+
* - v1: 万分之5 (5 bps = 0.05%)
|
|
251
|
+
*/
|
|
252
|
+
export type UserType = 'v0' | 'v1';
|
|
247
253
|
export type DisperseSignParams = {
|
|
248
254
|
fromPrivateKey: string;
|
|
249
255
|
recipients: string[];
|
|
@@ -266,6 +272,8 @@ export type DisperseSignParams = {
|
|
|
266
272
|
tokenPoolType?: TokenPoolType;
|
|
267
273
|
/** ✅ 新增:报价代币类型(池子的计价代币),默认 'native'(BNB/MON) */
|
|
268
274
|
quoteToken?: QuoteTokenType;
|
|
275
|
+
/** ✅ 新增:用户类型(用于利润费率),v0=万分之6,v1=万分之5,默认 v0 */
|
|
276
|
+
userType?: UserType;
|
|
269
277
|
};
|
|
270
278
|
/**
|
|
271
279
|
* ✅ 分发结果(简化版)
|
|
@@ -321,6 +329,8 @@ export type SweepSignParams = {
|
|
|
321
329
|
tokenPoolType?: TokenPoolType;
|
|
322
330
|
/** ✅ 新增:报价代币类型(池子的计价代币),默认 'native'(BNB/MON) */
|
|
323
331
|
quoteToken?: QuoteTokenType;
|
|
332
|
+
/** ✅ 新增:用户类型(用于利润费率),v0=万分之6,v1=万分之5,默认 v0 */
|
|
333
|
+
userType?: UserType;
|
|
324
334
|
};
|
|
325
335
|
/**
|
|
326
336
|
* ✅ 归集结果(简化版)
|
|
@@ -3,13 +3,23 @@ import { getOptimizedGasPrice, NonceManager, buildProfitHopTransactions, PROFIT_
|
|
|
3
3
|
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
4
4
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient } from './config.js';
|
|
5
5
|
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
|
|
6
|
-
// ==================== 本地利润计算(万分之三)====================
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* 根据用户类型获取利润费率(bps)
|
|
8
|
+
* - v0: 万分之6 (6 bps = 0.06%)
|
|
9
|
+
* - v1: 万分之5 (5 bps = 0.05%)
|
|
10
10
|
*/
|
|
11
|
-
function
|
|
12
|
-
|
|
11
|
+
function getProfitRateBps(userType) {
|
|
12
|
+
if (userType === 'v1')
|
|
13
|
+
return PROFIT_CONFIG.RATE_BPS_V1; // v1 用户:万分之5
|
|
14
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // v0 用户(默认):万分之6
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 计算利润金额
|
|
18
|
+
* ✅ 归集和分散专用:根据 userType 使用不同费率
|
|
19
|
+
*/
|
|
20
|
+
function calculateProfit(amount, userType) {
|
|
21
|
+
const rateBps = getProfitRateBps(userType);
|
|
22
|
+
const profit = (amount * BigInt(rateBps)) / 10000n;
|
|
13
23
|
const remaining = amount - profit;
|
|
14
24
|
return { profit, remaining };
|
|
15
25
|
}
|
|
@@ -196,7 +206,7 @@ async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chainI
|
|
|
196
206
|
* ✅ 优化版:支持 startNonce 参数避免并行调用时的 nonce 冲突
|
|
197
207
|
*/
|
|
198
208
|
export async function disperseWithBundleMerkle(params) {
|
|
199
|
-
const { fromPrivateKey, recipients, amount, amounts, tokenAddress, tokenDecimals, hopCount = 0, hopPrivateKeys, items, config, startNonce, tokenPoolType = 'v2', quoteToken = 'native' } = params;
|
|
209
|
+
const { fromPrivateKey, recipients, amount, amounts, tokenAddress, tokenDecimals, hopCount = 0, hopPrivateKeys, items, config, startNonce, tokenPoolType = 'v2', quoteToken = 'native', userType = 'v0' } = params;
|
|
200
210
|
// 快速返回空结果
|
|
201
211
|
if (!recipients || recipients.length === 0) {
|
|
202
212
|
return {
|
|
@@ -268,7 +278,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
268
278
|
totalAmountBeforeProfit += originalAmount;
|
|
269
279
|
let actualAmount = originalAmount;
|
|
270
280
|
if (extractProfit) {
|
|
271
|
-
const { profit, remaining } = calculateProfit(originalAmount);
|
|
281
|
+
const { profit, remaining } = calculateProfit(originalAmount, userType);
|
|
272
282
|
actualAmount = remaining;
|
|
273
283
|
totalProfit += profit;
|
|
274
284
|
}
|
|
@@ -313,7 +323,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
313
323
|
totalAmountBeforeProfit += originalAmount;
|
|
314
324
|
let actualAmount = originalAmount;
|
|
315
325
|
if (extractProfit) {
|
|
316
|
-
const { profit, remaining } = calculateProfit(originalAmount);
|
|
326
|
+
const { profit, remaining } = calculateProfit(originalAmount, userType);
|
|
317
327
|
actualAmount = remaining;
|
|
318
328
|
totalTokenProfit += profit; // 累计 ERC20 代币利润
|
|
319
329
|
}
|
|
@@ -410,7 +420,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
410
420
|
totalAmountBeforeProfit += originalAmountWei;
|
|
411
421
|
let amountWei = originalAmountWei;
|
|
412
422
|
if (extractProfit) {
|
|
413
|
-
const { profit, remaining } = calculateProfit(originalAmountWei);
|
|
423
|
+
const { profit, remaining } = calculateProfit(originalAmountWei, userType);
|
|
414
424
|
amountWei = remaining;
|
|
415
425
|
if (isNative) {
|
|
416
426
|
totalProfit += profit; // 原生币直接累加
|
|
@@ -631,7 +641,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
631
641
|
* ✅ 优化版:支持 startNonce 参数避免并行调用时的 nonce 冲突
|
|
632
642
|
*/
|
|
633
643
|
export async function sweepWithBundleMerkle(params) {
|
|
634
|
-
const { sourcePrivateKeys, target, ratioPct, ratios, amount, amounts, tokenAddress, tokenDecimals, skipIfInsufficient = true, hopCount = 0, hopPrivateKeys, sources, config, startNonce, tokenPoolType = 'v2', quoteToken = 'native' } = params;
|
|
644
|
+
const { sourcePrivateKeys, target, ratioPct, ratios, amount, amounts, tokenAddress, tokenDecimals, skipIfInsufficient = true, hopCount = 0, hopPrivateKeys, sources, config, startNonce, tokenPoolType = 'v2', quoteToken = 'native', userType = 'v0' } = params;
|
|
635
645
|
// 快速返回空结果
|
|
636
646
|
if (!sourcePrivateKeys || sourcePrivateKeys.length === 0) {
|
|
637
647
|
return {
|
|
@@ -754,7 +764,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
754
764
|
}
|
|
755
765
|
// 累计总利润
|
|
756
766
|
if (extractProfit && toSend > 0n) {
|
|
757
|
-
const { profit } = calculateProfit(toSend);
|
|
767
|
+
const { profit } = calculateProfit(toSend, userType);
|
|
758
768
|
totalProfit += profit;
|
|
759
769
|
}
|
|
760
770
|
}
|
|
@@ -772,7 +782,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
772
782
|
totalProfit = 0n;
|
|
773
783
|
for (let i = 0; i < sweepAmounts.length; i++) {
|
|
774
784
|
if (sweepAmounts[i] > 0n) {
|
|
775
|
-
totalProfit += calculateProfit(sweepAmounts[i]).profit;
|
|
785
|
+
totalProfit += calculateProfit(sweepAmounts[i], userType).profit;
|
|
776
786
|
}
|
|
777
787
|
}
|
|
778
788
|
}
|
|
@@ -907,7 +917,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
907
917
|
}
|
|
908
918
|
// 累计总代币利润(ERC20 归集)
|
|
909
919
|
if (extractProfit && toSend > 0n) {
|
|
910
|
-
const { profit } = calculateProfit(toSend);
|
|
920
|
+
const { profit } = calculateProfit(toSend, userType);
|
|
911
921
|
totalProfit += profit; // 先累计代币利润
|
|
912
922
|
}
|
|
913
923
|
}
|
|
@@ -1329,7 +1339,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1329
1339
|
let totalTokenProfit = 0n;
|
|
1330
1340
|
for (let i = 0; i < sweepAmounts.length; i++) {
|
|
1331
1341
|
if (sweepAmounts[i] > 0n) {
|
|
1332
|
-
const { profit } = calculateProfit(sweepAmounts[i]);
|
|
1342
|
+
const { profit } = calculateProfit(sweepAmounts[i], userType);
|
|
1333
1343
|
if (isNative) {
|
|
1334
1344
|
totalProfit += profit;
|
|
1335
1345
|
}
|
package/dist/flap/constants.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare const FLAP_ORIGINAL_PORTAL_ADDRESSES: {
|
|
|
27
27
|
*/
|
|
28
28
|
export declare const FLAP_TOKEN_IMPL_ADDRESSES: {
|
|
29
29
|
readonly BSC_NORMAL: "0x8b4329947e34b6d56d71a3385cac122bade7d78d";
|
|
30
|
-
readonly BSC_TAXED: "
|
|
30
|
+
readonly BSC_TAXED: "0x29e6383F0ce68507b5A72a53c2B118a118332aA8";
|
|
31
31
|
readonly BASE: "0xF3c514E04f83166E80718f29f0d34F206be40A0A";
|
|
32
32
|
readonly XLAYER: "0x12Dc83157Bf1cfCB8Db5952b3ba5bb56Cc38f8C9";
|
|
33
33
|
readonly MORPH: "0x8b4329947e34b6d56d71a3385cac122bade7d78d";
|
package/dist/flap/constants.js
CHANGED
|
@@ -29,7 +29,7 @@ export const FLAP_ORIGINAL_PORTAL_ADDRESSES = {
|
|
|
29
29
|
*/
|
|
30
30
|
export const FLAP_TOKEN_IMPL_ADDRESSES = {
|
|
31
31
|
BSC_NORMAL: '0x8b4329947e34b6d56d71a3385cac122bade7d78d',
|
|
32
|
-
BSC_TAXED: '
|
|
32
|
+
BSC_TAXED: '0x29e6383F0ce68507b5A72a53c2B118a118332aA8', // 官方文档: https://docs.flap.sh/flap/developers/deployed-contract-addresses
|
|
33
33
|
BASE: '0xF3c514E04f83166E80718f29f0d34F206be40A0A',
|
|
34
34
|
XLAYER: '0x12Dc83157Bf1cfCB8Db5952b3ba5bb56Cc38f8C9',
|
|
35
35
|
MORPH: '0x8b4329947e34b6d56d71a3385cac122bade7d78d', // 暂时使用 BSC NORMAL 地址
|
|
@@ -83,10 +83,37 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
83
83
|
const portalAddr = FLAP_PORTAL_ADDRESSES[chain];
|
|
84
84
|
const originalPortalAddr = FLAP_ORIGINAL_PORTAL_ADDRESSES[chain];
|
|
85
85
|
const portal = new ethers.Contract(originalPortalAddr, PORTAL_ABI, devWallet);
|
|
86
|
-
|
|
86
|
+
// ✅ 判断使用哪个版本的 newToken
|
|
87
|
+
// V4: 提供了 dexId 或 lpFeeProfile(支持 DEX 选择和 LP 费率配置)
|
|
88
|
+
// V3: 提供了 extensionID(支持扩展数据)
|
|
89
|
+
// V2: 默认
|
|
90
|
+
const useV4 = params.dexId !== undefined || params.lpFeeProfile !== undefined;
|
|
91
|
+
const useV3 = !useV4 && !!params.extensionID;
|
|
87
92
|
// ✅ 优化:并行获取 gasPrice、devWallet nonce 和 createTx
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
let createTxPromise;
|
|
94
|
+
if (useV4) {
|
|
95
|
+
// V4: 支持 DEX ID 和 LP 费率配置
|
|
96
|
+
createTxPromise = portal.newTokenV4.populateTransaction({
|
|
97
|
+
name: tokenInfo.name,
|
|
98
|
+
symbol: tokenInfo.symbol,
|
|
99
|
+
meta: tokenInfo.meta,
|
|
100
|
+
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
101
|
+
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
102
|
+
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
103
|
+
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
104
|
+
quoteToken: params.quoteToken || ZERO_ADDRESS,
|
|
105
|
+
quoteAmt: 0n,
|
|
106
|
+
beneficiary: devWallet.address,
|
|
107
|
+
permitData: '0x',
|
|
108
|
+
extensionID: params.extensionID ?? '0x' + '00'.repeat(32),
|
|
109
|
+
extensionData: params.extensionData ?? '0x',
|
|
110
|
+
dexId: (params.dexId ?? 0) & 0xff,
|
|
111
|
+
lpFeeProfile: (params.lpFeeProfile ?? 0) & 0xff
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (useV3) {
|
|
115
|
+
// V3: 支持扩展数据
|
|
116
|
+
createTxPromise = portal.newTokenV3.populateTransaction({
|
|
90
117
|
name: tokenInfo.name,
|
|
91
118
|
symbol: tokenInfo.symbol,
|
|
92
119
|
meta: tokenInfo.meta,
|
|
@@ -100,8 +127,11 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
100
127
|
permitData: '0x',
|
|
101
128
|
extensionID: params.extensionID,
|
|
102
129
|
extensionData: params.extensionData ?? '0x'
|
|
103
|
-
})
|
|
104
|
-
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// V2: 基础版本
|
|
134
|
+
createTxPromise = portal.newTokenV2.populateTransaction({
|
|
105
135
|
name: tokenInfo.name,
|
|
106
136
|
symbol: tokenInfo.symbol,
|
|
107
137
|
meta: tokenInfo.meta,
|
|
@@ -114,6 +144,7 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
114
144
|
beneficiary: devWallet.address,
|
|
115
145
|
permitData: '0x'
|
|
116
146
|
});
|
|
147
|
+
}
|
|
117
148
|
const [gasPrice, createTxUnsigned, devNonce] = await Promise.all([
|
|
118
149
|
resolveGasPrice(provider, config),
|
|
119
150
|
createTxPromise,
|
|
@@ -66,6 +66,14 @@ export interface FlapCreateToDexParams {
|
|
|
66
66
|
migratorType?: number;
|
|
67
67
|
taxRate?: number;
|
|
68
68
|
salt?: string;
|
|
69
|
+
/** V4 参数:DEX ID(0=DEX0, 1=DEX1, 2=DEX2) */
|
|
70
|
+
dexId?: number;
|
|
71
|
+
/** V4 参数:LP 费率档位(0=STANDARD 0.25%, 1=LOW 0.01%, 2=HIGH 1%) */
|
|
72
|
+
lpFeeProfile?: number;
|
|
73
|
+
/** V3 扩展 ID */
|
|
74
|
+
extensionID?: string;
|
|
75
|
+
/** V3 扩展数据 */
|
|
76
|
+
extensionData?: string;
|
|
69
77
|
/** 配置 */
|
|
70
78
|
config: CreateToDexSignConfig;
|
|
71
79
|
}
|
|
@@ -12,6 +12,19 @@ import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_
|
|
|
12
12
|
import { FLAP_PORTAL_ADDRESSES, FLAP_ORIGINAL_PORTAL_ADDRESSES } from '../constants.js';
|
|
13
13
|
import { PROFIT_CONFIG, ADDRESSES, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
14
14
|
import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA, PORTAL_ABI } from './config.js';
|
|
15
|
+
// ==================== ERC20 ABI ====================
|
|
16
|
+
const ERC20_ABI = [
|
|
17
|
+
{
|
|
18
|
+
type: "function",
|
|
19
|
+
name: "approve",
|
|
20
|
+
inputs: [
|
|
21
|
+
{ name: "spender", type: "address" },
|
|
22
|
+
{ name: "amount", type: "uint256" }
|
|
23
|
+
],
|
|
24
|
+
outputs: [{ name: "", type: "bool" }],
|
|
25
|
+
stateMutability: "nonpayable"
|
|
26
|
+
}
|
|
27
|
+
];
|
|
15
28
|
// ==================== 链常量 ====================
|
|
16
29
|
const BSC_PANCAKE_V2_ROUTER = ADDRESSES.BSC.PancakeV2Router;
|
|
17
30
|
const BSC_PANCAKE_V3_ROUTER = ADDRESSES.BSC.PancakeV3Router;
|
|
@@ -57,9 +70,36 @@ const PANCAKE_V3_ROUTER_ABI = [
|
|
|
57
70
|
],
|
|
58
71
|
"outputs": [{ "name": "amountOut", "type": "uint256" }],
|
|
59
72
|
"stateMutability": "payable"
|
|
60
|
-
}
|
|
73
|
+
},
|
|
61
74
|
];
|
|
75
|
+
// ==================== V3 费率配置 ====================
|
|
76
|
+
// USD1/USDT 使用 100 bps (0.01%),其他使用 2500 bps (0.25%)
|
|
77
|
+
const USD1_V3_FEE = 100;
|
|
78
|
+
const DEFAULT_V3_FEE = 2500;
|
|
62
79
|
// ==================== 工具函数 ====================
|
|
80
|
+
/** 构建 ERC20 approve 交易 */
|
|
81
|
+
async function buildApproveTransaction(wallet, tokenAddress, spenderAddress, amount, nonce, gasPrice, txType) {
|
|
82
|
+
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
83
|
+
const data = erc20Interface.encodeFunctionData('approve', [spenderAddress, amount]);
|
|
84
|
+
const tx = {
|
|
85
|
+
to: tokenAddress,
|
|
86
|
+
data,
|
|
87
|
+
from: wallet.address,
|
|
88
|
+
nonce,
|
|
89
|
+
gasLimit: BigInt(100000), // approve 交易 gas 较低
|
|
90
|
+
chainId: 56,
|
|
91
|
+
type: txType
|
|
92
|
+
};
|
|
93
|
+
if (txType === 2) {
|
|
94
|
+
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
95
|
+
tx.maxFeePerGas = gasPrice;
|
|
96
|
+
tx.maxPriorityFeePerGas = priorityFee;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
tx.gasPrice = gasPrice;
|
|
100
|
+
}
|
|
101
|
+
return wallet.signTransaction(tx);
|
|
102
|
+
}
|
|
63
103
|
function getGasLimit(config, defaultGas = 800000) {
|
|
64
104
|
if (config.gasLimit !== undefined) {
|
|
65
105
|
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
@@ -199,19 +239,67 @@ export async function flapBundleCreateToDex(params) {
|
|
|
199
239
|
const devNonce = noncesMap.get(devAddr);
|
|
200
240
|
noncesMap.set(devAddr, devNonce + 1);
|
|
201
241
|
const originalPortal = new Contract(originalPortalAddress, PORTAL_ABI, devWallet);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
242
|
+
// ✅ 判断使用哪个版本的 newToken
|
|
243
|
+
// V4: 提供了 dexId 或 lpFeeProfile(支持 DEX 选择和 LP 费率配置)
|
|
244
|
+
// V3: 提供了 extensionID(支持扩展数据)
|
|
245
|
+
// V2: 默认
|
|
246
|
+
const useV4 = params.dexId !== undefined || params.lpFeeProfile !== undefined;
|
|
247
|
+
const useV3 = !useV4 && !!params.extensionID;
|
|
248
|
+
let createUnsigned;
|
|
249
|
+
if (useV4) {
|
|
250
|
+
// V4: 支持 DEX ID 和 LP 费率配置(用于选择池子费率)
|
|
251
|
+
createUnsigned = await originalPortal.newTokenV4.populateTransaction({
|
|
252
|
+
name: tokenInfo.name,
|
|
253
|
+
symbol: tokenInfo.symbol,
|
|
254
|
+
meta: tokenInfo.meta,
|
|
255
|
+
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
256
|
+
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
257
|
+
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
258
|
+
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
259
|
+
quoteToken: inputToken,
|
|
260
|
+
quoteAmt: 0n,
|
|
261
|
+
beneficiary: devWallet.address,
|
|
262
|
+
permitData: '0x',
|
|
263
|
+
extensionID: params.extensionID ?? '0x' + '00'.repeat(32),
|
|
264
|
+
extensionData: params.extensionData ?? '0x',
|
|
265
|
+
dexId: (params.dexId ?? 0) & 0xff,
|
|
266
|
+
lpFeeProfile: (params.lpFeeProfile ?? 0) & 0xff
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
else if (useV3) {
|
|
270
|
+
// V3: 支持扩展数据
|
|
271
|
+
createUnsigned = await originalPortal.newTokenV3.populateTransaction({
|
|
272
|
+
name: tokenInfo.name,
|
|
273
|
+
symbol: tokenInfo.symbol,
|
|
274
|
+
meta: tokenInfo.meta,
|
|
275
|
+
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
276
|
+
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
277
|
+
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
278
|
+
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
279
|
+
quoteToken: inputToken,
|
|
280
|
+
quoteAmt: 0n,
|
|
281
|
+
beneficiary: devWallet.address,
|
|
282
|
+
permitData: '0x',
|
|
283
|
+
extensionID: params.extensionID,
|
|
284
|
+
extensionData: params.extensionData ?? '0x'
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// V2: 基础版本
|
|
289
|
+
createUnsigned = await originalPortal.newTokenV2.populateTransaction({
|
|
290
|
+
name: tokenInfo.name,
|
|
291
|
+
symbol: tokenInfo.symbol,
|
|
292
|
+
meta: tokenInfo.meta,
|
|
293
|
+
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
294
|
+
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
295
|
+
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
296
|
+
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
297
|
+
quoteToken: inputToken,
|
|
298
|
+
quoteAmt: 0n,
|
|
299
|
+
beneficiary: devWallet.address,
|
|
300
|
+
permitData: '0x'
|
|
301
|
+
});
|
|
302
|
+
}
|
|
215
303
|
const createTx = {
|
|
216
304
|
...createUnsigned,
|
|
217
305
|
from: devWallet.address,
|
|
@@ -232,17 +320,27 @@ export async function flapBundleCreateToDex(params) {
|
|
|
232
320
|
}
|
|
233
321
|
// 3. 内盘买入交易(包含毕业触发)
|
|
234
322
|
// 最后一个买入交易会触发毕业,需要更多 gas
|
|
323
|
+
// ✅ ERC20 需要先 approve,再买入
|
|
235
324
|
const curveBuyTxPromises = curveWallets.map(async ({ wallet }, i) => {
|
|
236
325
|
const addr = wallet.address.toLowerCase();
|
|
237
|
-
const
|
|
238
|
-
|
|
326
|
+
const signedTxs = [];
|
|
327
|
+
// ✅ ERC20: 先构建 approve 交易
|
|
328
|
+
if (!useNativeToken) {
|
|
329
|
+
const approveNonce = noncesMap.get(addr);
|
|
330
|
+
noncesMap.set(addr, approveNonce + 1);
|
|
331
|
+
const approveTx = await buildApproveTransaction(wallet, quoteToken, portalAddress, curveBuyAmounts[i], approveNonce, gasPrice, txType);
|
|
332
|
+
signedTxs.push(approveTx);
|
|
333
|
+
}
|
|
334
|
+
// 构建买入交易
|
|
335
|
+
const buyNonce = noncesMap.get(addr);
|
|
336
|
+
noncesMap.set(addr, buyNonce + 1);
|
|
239
337
|
const portal = new Contract(portalAddress, PORTAL_ABI, wallet);
|
|
240
338
|
const unsigned = await portal.swapExactInput.populateTransaction({
|
|
241
339
|
inputToken,
|
|
242
340
|
outputToken: tokenAddress,
|
|
243
341
|
inputAmount: curveBuyAmounts[i],
|
|
244
342
|
minOutputAmount: 0n,
|
|
245
|
-
permitData: '0x'
|
|
343
|
+
permitData: '0x' // 不使用 permit,使用链上 approve
|
|
246
344
|
}, useNativeToken ? { value: curveBuyAmounts[i] } : {});
|
|
247
345
|
// 最后一个买家触发毕业,需要更多 gas
|
|
248
346
|
const isLastBuyer = i === curveWallets.length - 1;
|
|
@@ -250,7 +348,7 @@ export async function flapBundleCreateToDex(params) {
|
|
|
250
348
|
const tx = {
|
|
251
349
|
...unsigned,
|
|
252
350
|
from: wallet.address,
|
|
253
|
-
nonce,
|
|
351
|
+
nonce: buyNonce,
|
|
254
352
|
gasLimit: buyGasLimit,
|
|
255
353
|
chainId: 56,
|
|
256
354
|
type: txType,
|
|
@@ -263,67 +361,63 @@ export async function flapBundleCreateToDex(params) {
|
|
|
263
361
|
else {
|
|
264
362
|
tx.gasPrice = gasPrice;
|
|
265
363
|
}
|
|
266
|
-
|
|
364
|
+
const signedBuy = await wallet.signTransaction(tx);
|
|
365
|
+
signedTxs.push(signedBuy);
|
|
366
|
+
return signedTxs;
|
|
267
367
|
});
|
|
268
|
-
const
|
|
269
|
-
|
|
368
|
+
const curveBuyResults = await Promise.all(curveBuyTxPromises);
|
|
369
|
+
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
370
|
+
curveBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
270
371
|
// 4. 外盘买入交易(PancakeSwap)
|
|
372
|
+
// ✅ ERC20 需要先 approve,再 swap
|
|
271
373
|
if (enableDexBuy && dexWallets.length > 0) {
|
|
272
374
|
const dexBuyTxPromises = dexWallets.map(async ({ wallet }, i) => {
|
|
273
375
|
const addr = wallet.address.toLowerCase();
|
|
274
|
-
const
|
|
275
|
-
noncesMap.set(addr, nonce + 1);
|
|
376
|
+
const signedTxs = [];
|
|
276
377
|
const buyAmount = dexBuyAmounts[i];
|
|
277
378
|
const smartRouter = dexPoolType === 'v3' ? BSC_PANCAKE_V3_ROUTER : BSC_PANCAKE_V2_ROUTER;
|
|
278
|
-
|
|
379
|
+
// ✅ 根据 quoteToken 选择 V3 费率
|
|
380
|
+
const actualV3Fee = v3Fee || (useNativeToken ? DEFAULT_V3_FEE : USD1_V3_FEE);
|
|
381
|
+
// ✅ ERC20: 先构建 approve 交易
|
|
382
|
+
if (!useNativeToken) {
|
|
383
|
+
const approveNonce = noncesMap.get(addr);
|
|
384
|
+
noncesMap.set(addr, approveNonce + 1);
|
|
385
|
+
const approveTx = await buildApproveTransaction(wallet, quoteToken, smartRouter, buyAmount, approveNonce, gasPrice, txType);
|
|
386
|
+
signedTxs.push(approveTx);
|
|
387
|
+
}
|
|
388
|
+
// 构建买入交易
|
|
389
|
+
const buyNonce = noncesMap.get(addr);
|
|
390
|
+
noncesMap.set(addr, buyNonce + 1);
|
|
391
|
+
let multicallData = [];
|
|
279
392
|
let value;
|
|
393
|
+
const routerInterface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
280
394
|
if (dexPoolType === 'v3') {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
value = buyAmount;
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
const params = {
|
|
297
|
-
tokenIn: quoteToken,
|
|
298
|
-
tokenOut: tokenAddress,
|
|
299
|
-
fee: v3Fee,
|
|
300
|
-
recipient: wallet.address,
|
|
301
|
-
amountIn: buyAmount,
|
|
302
|
-
amountOutMinimum: 0n,
|
|
303
|
-
sqrtPriceLimitX96: 0n
|
|
304
|
-
};
|
|
305
|
-
const exactInputSingleData = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('exactInputSingle', [params]);
|
|
306
|
-
calldata = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('multicall', [[exactInputSingleData]]);
|
|
307
|
-
value = 0n;
|
|
308
|
-
}
|
|
395
|
+
// V3: exactInputSingle
|
|
396
|
+
const params = {
|
|
397
|
+
tokenIn: useNativeToken ? BSC_WBNB : quoteToken,
|
|
398
|
+
tokenOut: tokenAddress,
|
|
399
|
+
fee: actualV3Fee,
|
|
400
|
+
recipient: wallet.address,
|
|
401
|
+
amountIn: buyAmount,
|
|
402
|
+
amountOutMinimum: 0n,
|
|
403
|
+
sqrtPriceLimitX96: 0n
|
|
404
|
+
};
|
|
405
|
+
multicallData = [routerInterface.encodeFunctionData('exactInputSingle', [params])];
|
|
406
|
+
value = useNativeToken ? buyAmount : 0n;
|
|
309
407
|
}
|
|
310
408
|
else {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
else {
|
|
317
|
-
const swapData = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, [quoteToken, tokenAddress], wallet.address]);
|
|
318
|
-
calldata = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('multicall', [[swapData]]);
|
|
319
|
-
value = 0n;
|
|
320
|
-
}
|
|
409
|
+
// V2: swapExactTokensForTokens
|
|
410
|
+
const path = useNativeToken ? [BSC_WBNB, tokenAddress] : [quoteToken, tokenAddress];
|
|
411
|
+
const swapData = routerInterface.encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, path, wallet.address]);
|
|
412
|
+
multicallData = [swapData];
|
|
413
|
+
value = useNativeToken ? buyAmount : 0n;
|
|
321
414
|
}
|
|
415
|
+
const calldata = routerInterface.encodeFunctionData('multicall', [multicallData]);
|
|
322
416
|
const tx = {
|
|
323
417
|
to: smartRouter,
|
|
324
418
|
data: calldata,
|
|
325
419
|
from: wallet.address,
|
|
326
|
-
nonce,
|
|
420
|
+
nonce: buyNonce,
|
|
327
421
|
gasLimit: BigInt(500000),
|
|
328
422
|
chainId: 56,
|
|
329
423
|
type: txType,
|
|
@@ -336,10 +430,13 @@ export async function flapBundleCreateToDex(params) {
|
|
|
336
430
|
else {
|
|
337
431
|
tx.gasPrice = gasPrice;
|
|
338
432
|
}
|
|
339
|
-
|
|
433
|
+
const signedBuy = await wallet.signTransaction(tx);
|
|
434
|
+
signedTxs.push(signedBuy);
|
|
435
|
+
return signedTxs;
|
|
340
436
|
});
|
|
341
|
-
const
|
|
342
|
-
|
|
437
|
+
const dexBuyResults = await Promise.all(dexBuyTxPromises);
|
|
438
|
+
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
439
|
+
dexBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
343
440
|
}
|
|
344
441
|
// 5. 利润多跳转账
|
|
345
442
|
let profitHopWallets;
|
|
@@ -7,9 +7,11 @@ import { CommonBundleConfig } from '../../utils/bundle-helpers.js';
|
|
|
7
7
|
import { FlapSignConfig } from './config.js';
|
|
8
8
|
import type { GeneratedWallet } from '../../utils/wallet.js';
|
|
9
9
|
export type FlapChain = 'bsc' | 'xlayer' | 'base';
|
|
10
|
+
export type UserType = 'v0' | 'v1';
|
|
10
11
|
export interface FlapBuyFirstSignConfig extends FlapSignConfig {
|
|
11
12
|
reserveGasETH?: number;
|
|
12
13
|
skipQuoteOnError?: boolean;
|
|
14
|
+
userType?: UserType;
|
|
13
15
|
}
|
|
14
16
|
export interface FlapBuyFirstConfig extends CommonBundleConfig {
|
|
15
17
|
apiKey: string;
|
|
@@ -66,8 +66,15 @@ const BRIBE_TX_COUNT = 1;
|
|
|
66
66
|
const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
|
|
67
67
|
/** 最大买卖交易数 */
|
|
68
68
|
const MAX_SWAP_TX_COUNT = MAX_BUNDLE_SIGNATURES - BRIBE_TX_COUNT - PROFIT_TX_COUNT; // 50 - 1 - 4 = 45
|
|
69
|
-
/**
|
|
70
|
-
|
|
69
|
+
/**
|
|
70
|
+
* 根据 userType 获取每笔交易的利润率
|
|
71
|
+
*/
|
|
72
|
+
function getProfitRatePerTxBps(userType) {
|
|
73
|
+
if (userType === 'v1') {
|
|
74
|
+
return PROFIT_CONFIG.RATE_BPS_V1;
|
|
75
|
+
}
|
|
76
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // v0 或默认
|
|
77
|
+
}
|
|
71
78
|
/**
|
|
72
79
|
* 验证买卖笔数
|
|
73
80
|
*/
|
|
@@ -124,9 +131,9 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
124
131
|
throw new Error('单钱包模式需要提供 buyerPrivateKey 和 sellerPrivateKey');
|
|
125
132
|
}
|
|
126
133
|
validateSwapCounts(buyCount, sellCount);
|
|
127
|
-
// ✅
|
|
134
|
+
// ✅ 计算利润比例(根据 userType 动态调整)
|
|
128
135
|
const totalTxCount = buyCount + sellCount;
|
|
129
|
-
const profitRateBps =
|
|
136
|
+
const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
|
|
130
137
|
const chainContext = createChainContext(chain, config.rpcUrl);
|
|
131
138
|
const buyer = new Wallet(buyerPrivateKey, chainContext.provider);
|
|
132
139
|
const seller = new Wallet(sellerPrivateKey, chainContext.provider);
|
|
@@ -404,12 +411,21 @@ async function estimateSellFunds(portal, tokenAddress, sellAmountWei, outputToke
|
|
|
404
411
|
return 0n;
|
|
405
412
|
}
|
|
406
413
|
}
|
|
407
|
-
|
|
414
|
+
/**
|
|
415
|
+
* 根据 userType 获取利润率(基点)
|
|
416
|
+
*/
|
|
417
|
+
function getProfitRateBps(userType) {
|
|
418
|
+
if (userType === 'v1') {
|
|
419
|
+
return PROFIT_CONFIG.RATE_BPS_V1; // 0.05%
|
|
420
|
+
}
|
|
421
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // v0 或默认 0.06%
|
|
422
|
+
}
|
|
423
|
+
function calculateProfitAmount(expectedFunds, userType) {
|
|
408
424
|
if (expectedFunds <= 0n) {
|
|
409
425
|
return 0n;
|
|
410
426
|
}
|
|
411
|
-
|
|
412
|
-
return (expectedFunds * BigInt(
|
|
427
|
+
const rateBps = getProfitRateBps(userType);
|
|
428
|
+
return (expectedFunds * BigInt(rateBps)) / 10000n;
|
|
413
429
|
}
|
|
414
430
|
/**
|
|
415
431
|
* 计算多笔交易的利润金额
|
|
@@ -621,9 +637,9 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
621
637
|
throw new Error('卖方钱包数量不能为0');
|
|
622
638
|
// 验证总交易数不超过限制
|
|
623
639
|
validateSwapCounts(buyCount, sellCount);
|
|
624
|
-
// ✅
|
|
640
|
+
// ✅ 计算利润比例(根据 userType 动态调整)
|
|
625
641
|
const totalTxCount = buyCount + sellCount;
|
|
626
|
-
const profitRateBps =
|
|
642
|
+
const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
|
|
627
643
|
const chainContext = createChainContext(chain, config.rpcUrl);
|
|
628
644
|
const nonceManager = new NonceManager(chainContext.provider);
|
|
629
645
|
// 创建所有钱包实例
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { CommonBundleConfig } from '../../utils/bundle-helpers.js';
|
|
7
7
|
import { type GeneratedWallet } from '../../utils/wallet.js';
|
|
8
|
+
export type UserType = 'v0' | 'v1';
|
|
8
9
|
export interface FlapSwapSignConfig {
|
|
9
10
|
rpcUrl: string;
|
|
10
11
|
gasLimit?: number | bigint;
|
|
@@ -17,6 +18,7 @@ export interface FlapSwapSignConfig {
|
|
|
17
18
|
skipQuoteOnError?: boolean;
|
|
18
19
|
skipApprovalCheck?: boolean;
|
|
19
20
|
bribeAmount?: number;
|
|
21
|
+
userType?: UserType;
|
|
20
22
|
}
|
|
21
23
|
export type FlapChain = 'bsc' | 'xlayer' | 'base';
|
|
22
24
|
export interface FlapSwapConfig extends CommonBundleConfig {
|
|
@@ -305,8 +305,8 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
305
305
|
quoteTokenDecimals,
|
|
306
306
|
provider: chainContext.provider
|
|
307
307
|
});
|
|
308
|
-
// ✅
|
|
309
|
-
const tokenProfitAmount = calculateProfitAmount(quote.quotedNative);
|
|
308
|
+
// ✅ 计算利润(基于卖出报价,根据 userType 动态调整)
|
|
309
|
+
const tokenProfitAmount = calculateProfitAmount(quote.quotedNative, config.userType);
|
|
310
310
|
// ✅ ERC20 输出:获取代币利润等值的原生代币(BNB)报价
|
|
311
311
|
let nativeProfitAmount = tokenProfitAmount; // 原生代币输出时直接使用
|
|
312
312
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
@@ -642,11 +642,24 @@ async function buildProfitTransaction({ provider, seller, profitAmount, profitNo
|
|
|
642
642
|
hopWallets: profitHopResult.hopWallets
|
|
643
643
|
};
|
|
644
644
|
}
|
|
645
|
-
|
|
645
|
+
/**
|
|
646
|
+
* 根据 userType 计算利润率
|
|
647
|
+
*/
|
|
648
|
+
function getProfitRateBps(userType) {
|
|
649
|
+
if (userType === 'v0') {
|
|
650
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // 0.06%
|
|
651
|
+
}
|
|
652
|
+
else if (userType === 'v1') {
|
|
653
|
+
return PROFIT_CONFIG.RATE_BPS_V1; // 0.05%
|
|
654
|
+
}
|
|
655
|
+
return PROFIT_CONFIG.RATE_BPS_V0; // 默认 0.06%
|
|
656
|
+
}
|
|
657
|
+
function calculateProfitAmount(quotedNative, userType) {
|
|
646
658
|
if (quotedNative <= 0n) {
|
|
647
659
|
return 0n;
|
|
648
660
|
}
|
|
649
|
-
|
|
661
|
+
const rateBps = getProfitRateBps(userType);
|
|
662
|
+
return (quotedNative * BigInt(rateBps)) / 10000n;
|
|
650
663
|
}
|
|
651
664
|
function countTruthy(values) {
|
|
652
665
|
return values.filter(Boolean).length;
|
|
@@ -740,8 +753,8 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
740
753
|
}
|
|
741
754
|
}
|
|
742
755
|
}));
|
|
743
|
-
// ✅
|
|
744
|
-
const tokenProfitAmount = calculateProfitAmount(quote.quotedNative);
|
|
756
|
+
// ✅ 计算利润(根据 userType 动态调整)
|
|
757
|
+
const tokenProfitAmount = calculateProfitAmount(quote.quotedNative, config.userType);
|
|
745
758
|
let nativeProfitAmount = tokenProfitAmount;
|
|
746
759
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
747
760
|
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, quoteToken, tokenProfitAmount, chainContext.chainId);
|
|
@@ -952,8 +965,8 @@ export async function flapQuickBatchSwapMerkle(params) {
|
|
|
952
965
|
console.log(`[flapQuickBatchSwapMerkle] 模式: ${useNativeToken ? '原生代币' : 'ERC20'}`);
|
|
953
966
|
console.log(`[flapQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
954
967
|
console.log(`[flapQuickBatchSwapMerkle] 预估卖出所得: ${outputFormatted}`);
|
|
955
|
-
// ✅
|
|
956
|
-
let tokenProfitAmount = calculateProfitAmount(estimatedOutput);
|
|
968
|
+
// ✅ 计算利润(根据 userType 动态调整)
|
|
969
|
+
let tokenProfitAmount = calculateProfitAmount(estimatedOutput, config.userType);
|
|
957
970
|
let nativeProfitAmount = tokenProfitAmount;
|
|
958
971
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
959
972
|
// ERC20 模式:将代币利润转换为等值原生代币
|
|
@@ -106,6 +106,10 @@ export type FlapCreateWithBundleBuySignParams = {
|
|
|
106
106
|
salt?: string;
|
|
107
107
|
extensionID?: string;
|
|
108
108
|
extensionData?: string;
|
|
109
|
+
/** DEX ID:0=DEX0, 1=DEX1, 2=DEX2(BSC: DEX0=PancakeSwap, Monad: DEX0=Uniswap, DEX1=PancakeSwap) */
|
|
110
|
+
dexId?: number;
|
|
111
|
+
/** LP 费率档位:0=STANDARD(0.25%), 1=LOW(0.01%), 2=HIGH(1%) */
|
|
112
|
+
lpFeeProfile?: number;
|
|
109
113
|
};
|
|
110
114
|
/**
|
|
111
115
|
* ✅ Flap 创建代币 + 购买结果(简化版)
|
package/dist/flap/portal.d.ts
CHANGED
|
@@ -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;
|
package/dist/flap/portal.js
CHANGED
|
@@ -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';
|
|
@@ -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
|
-
/**
|
|
56
|
-
|
|
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
|
-
// ✅
|
|
146
|
+
// ✅ 计算利润比例(根据 userType 动态调整)
|
|
140
147
|
const totalTxCount = buyCount + sellCount;
|
|
141
|
-
const profitRateBps =
|
|
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);
|
|
@@ -766,9 +773,9 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
766
773
|
throw new Error('卖方钱包数量不能为0');
|
|
767
774
|
// 验证总交易数不超过限制
|
|
768
775
|
validateSwapCounts(buyCount, sellCount);
|
|
769
|
-
// ✅
|
|
776
|
+
// ✅ 计算利润比例(根据 userType 动态调整)
|
|
770
777
|
const totalTxCount = buyCount + sellCount;
|
|
771
|
-
const profitRateBps =
|
|
778
|
+
const profitRateBps = getProfitRatePerTxBps(config.userType) * totalTxCount;
|
|
772
779
|
// ✅ 判断是否使用原生代币
|
|
773
780
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
774
781
|
const context = createPancakeContext(config);
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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: {
|
package/dist/utils/constants.js
CHANGED
|
@@ -23,10 +23,12 @@ export const PROFIT_CONFIG = {
|
|
|
23
23
|
RECIPIENT: '0xe8D0334fAf713884133640CAEe4ECdd2106AF103',
|
|
24
24
|
/** 利润比例(基点):30 bps = 0.3% = 千分之三(普通模式) */
|
|
25
25
|
RATE_BPS: 30,
|
|
26
|
-
/** 利润比例(基点):6 bps = 0.06% = 万分之六(资金利用率模式) */
|
|
27
|
-
RATE_BPS_CAPITAL: 6,
|
|
28
26
|
/** 利润比例(基点):6 bps = 0.06% = 万分之六(捆绑换手模式) */
|
|
29
27
|
RATE_BPS_SWAP: 6,
|
|
28
|
+
/** 利润比例(基点):6 bps = 0.06% = 万分之六(用户类型 v0) */
|
|
29
|
+
RATE_BPS_V0: 6,
|
|
30
|
+
/** 利润比例(基点):5 bps = 0.05% = 万分之五(用户类型 v1) */
|
|
31
|
+
RATE_BPS_V1: 5,
|
|
30
32
|
};
|
|
31
33
|
export const CHAIN = {
|
|
32
34
|
BSC: {
|
|
@@ -620,7 +620,7 @@ export async function holdersMaker(params) {
|
|
|
620
620
|
const totalBuyAmountForProfit = isERC20Mode
|
|
621
621
|
? ethers.parseEther(buyAmountPerHolder) * BigInt(holdersCount) // 按等值 BNB 计算
|
|
622
622
|
: buyAmountWei * BigInt(holdersCount);
|
|
623
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
623
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS;
|
|
624
624
|
const totalProfit = (totalBuyAmountForProfit * BigInt(profitRateBps)) / 10000n;
|
|
625
625
|
const profitPerBatch = totalProfit / BigInt(walletBatches.length);
|
|
626
626
|
console.log(`[HoldersMaker] 总利润: ${ethers.formatEther(totalProfit)} BNB`);
|