four-flap-meme-sdk 1.5.2 → 1.5.4
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/abis/common.js
CHANGED
|
@@ -228,6 +228,8 @@ export const FLAP_PORTAL_ABI = [
|
|
|
228
228
|
'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)',
|
|
229
229
|
// 交易
|
|
230
230
|
'function swapExactInput((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData)) external payable returns (uint256)',
|
|
231
|
+
// ✅ V3:支持 extensionData(注意:这里的 V3 指“扩展交易接口版本”,不是“外盘 V3 池子”)
|
|
232
|
+
'function swapExactInputV3((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData,bytes extensionData)) external payable returns (uint256)',
|
|
231
233
|
'function quoteExactInput((address inputToken,address outputToken,uint256 inputAmount)) external view returns (uint256)',
|
|
232
234
|
// 旧版兼容
|
|
233
235
|
'function buy(address token, uint256 minReceived) external payable returns (uint256)',
|
|
@@ -57,9 +57,15 @@ export interface FlapCreateToDexParams {
|
|
|
57
57
|
dexBuyers?: DexBuyerConfig[];
|
|
58
58
|
/** 外盘总买入金额(用于自动分配) */
|
|
59
59
|
dexTotalBuyAmount?: string;
|
|
60
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* 外盘池类型(V2 或 V3),默认 V3
|
|
62
|
+
* @deprecated 现在外盘买入统一改为走 Portal 的 swapExactInput/swapExactInputV3 进行自动路由,
|
|
63
|
+
* 不再需要手动指定池类型/fee;保留字段仅为兼容老调用方。
|
|
64
|
+
*/
|
|
61
65
|
dexPoolType?: DexPoolType;
|
|
62
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* @deprecated 同上:不再需要手动传 V3 fee;由合约记录的池子信息决定。
|
|
68
|
+
*/
|
|
63
69
|
v3Fee?: number;
|
|
64
70
|
/** 发币扩展参数 */
|
|
65
71
|
dexThresh?: number;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
11
11
|
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
|
|
12
12
|
import { FLAP_PORTAL_ADDRESSES, FLAP_ORIGINAL_PORTAL_ADDRESSES } from '../constants.js';
|
|
13
|
-
import { PROFIT_CONFIG,
|
|
13
|
+
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
14
14
|
import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA, PORTAL_ABI } from './config.js';
|
|
15
15
|
// ==================== ERC20 ABI ====================
|
|
16
16
|
const ERC20_ABI = [
|
|
@@ -25,107 +25,64 @@ const ERC20_ABI = [
|
|
|
25
25
|
stateMutability: "nonpayable"
|
|
26
26
|
}
|
|
27
27
|
];
|
|
28
|
-
// ====================
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
// ==================== PancakeSwap Router ABI ====================
|
|
29
|
+
const PANCAKE_ROUTER_ABI = [
|
|
30
|
+
{
|
|
31
|
+
type: "function",
|
|
32
|
+
name: "exactInputSingle",
|
|
33
|
+
inputs: [
|
|
34
|
+
{
|
|
35
|
+
name: "params",
|
|
36
|
+
type: "tuple",
|
|
37
|
+
components: [
|
|
38
|
+
{ name: "tokenIn", type: "address" },
|
|
39
|
+
{ name: "tokenOut", type: "address" },
|
|
40
|
+
{ name: "fee", type: "uint24" },
|
|
41
|
+
{ name: "recipient", type: "address" },
|
|
42
|
+
{ name: "amountIn", type: "uint256" },
|
|
43
|
+
{ name: "amountOutMinimum", type: "uint256" },
|
|
44
|
+
{ name: "sqrtPriceLimitX96", type: "uint160" }
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
outputs: [{ name: "amountOut", type: "uint256" }],
|
|
49
|
+
stateMutability: "payable"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: "function",
|
|
53
|
+
name: "multicall",
|
|
54
|
+
inputs: [{ name: "data", type: "bytes[]" }],
|
|
55
|
+
outputs: [{ name: "results", type: "bytes[]" }],
|
|
56
|
+
stateMutability: "payable"
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
// ==================== DEX 地址常量 ====================
|
|
60
|
+
const DEX_ADDRESSES = {
|
|
61
|
+
BSC: {
|
|
62
|
+
PANCAKE_SMART_ROUTER: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4',
|
|
63
|
+
WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
64
|
+
},
|
|
65
|
+
XLAYER: {
|
|
66
|
+
// xLayer 使用 PotatoSwap
|
|
67
|
+
PANCAKE_SMART_ROUTER: '0x0000000000000000000000000000000000000000', // TODO: 添加 xLayer DEX 地址
|
|
68
|
+
WBNB: '0x0000000000000000000000000000000000000000',
|
|
69
|
+
}
|
|
41
70
|
};
|
|
71
|
+
// ==================== 链常量 ====================
|
|
42
72
|
// 获取链配置
|
|
43
73
|
function getChainConfig(chain) {
|
|
44
74
|
if (chain === 'xlayer') {
|
|
45
75
|
return {
|
|
46
76
|
chainId: 196,
|
|
47
|
-
v2Router: XLAYER_POTATOSWAP_V2_ROUTER,
|
|
48
|
-
v3Router: XLAYER_POTATOSWAP_V3_ROUTER,
|
|
49
|
-
wrappedNative: XLAYER_WOKB,
|
|
50
77
|
nativeSymbol: 'OKB',
|
|
51
78
|
};
|
|
52
79
|
}
|
|
53
80
|
// 默认 BSC
|
|
54
81
|
return {
|
|
55
82
|
chainId: 56,
|
|
56
|
-
v2Router: BSC_PANCAKE_V2_ROUTER,
|
|
57
|
-
v3Router: BSC_PANCAKE_V3_ROUTER,
|
|
58
|
-
wrappedNative: BSC_WBNB,
|
|
59
83
|
nativeSymbol: 'BNB',
|
|
60
84
|
};
|
|
61
85
|
}
|
|
62
|
-
// PancakeSwap Router ABI
|
|
63
|
-
const PANCAKE_V3_ROUTER_ABI = [
|
|
64
|
-
{
|
|
65
|
-
"type": "function",
|
|
66
|
-
"name": "exactInputSingle",
|
|
67
|
-
"inputs": [
|
|
68
|
-
{
|
|
69
|
-
"name": "params",
|
|
70
|
-
"type": "tuple",
|
|
71
|
-
"components": [
|
|
72
|
-
{ "name": "tokenIn", "type": "address" },
|
|
73
|
-
{ "name": "tokenOut", "type": "address" },
|
|
74
|
-
{ "name": "fee", "type": "uint24" },
|
|
75
|
-
{ "name": "recipient", "type": "address" },
|
|
76
|
-
{ "name": "amountIn", "type": "uint256" },
|
|
77
|
-
{ "name": "amountOutMinimum", "type": "uint256" },
|
|
78
|
-
{ "name": "sqrtPriceLimitX96", "type": "uint160" }
|
|
79
|
-
]
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
"outputs": [{ "name": "amountOut", "type": "uint256" }],
|
|
83
|
-
"stateMutability": "payable"
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"type": "function",
|
|
87
|
-
"name": "multicall",
|
|
88
|
-
"inputs": [{ "name": "data", "type": "bytes[]" }],
|
|
89
|
-
"outputs": [{ "name": "results", "type": "bytes[]" }],
|
|
90
|
-
"stateMutability": "payable"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"type": "function",
|
|
94
|
-
"name": "swapExactTokensForTokens",
|
|
95
|
-
"inputs": [
|
|
96
|
-
{ "name": "amountIn", "type": "uint256" },
|
|
97
|
-
{ "name": "amountOutMin", "type": "uint256" },
|
|
98
|
-
{ "name": "path", "type": "address[]" },
|
|
99
|
-
{ "name": "to", "type": "address" }
|
|
100
|
-
],
|
|
101
|
-
"outputs": [{ "name": "amountOut", "type": "uint256" }],
|
|
102
|
-
"stateMutability": "payable"
|
|
103
|
-
},
|
|
104
|
-
];
|
|
105
|
-
// ==================== V3 费率配置 ====================
|
|
106
|
-
// USD1/USDT 使用 100 bps (0.01%),其他使用 2500 bps (0.25%)
|
|
107
|
-
const USD1_V3_FEE = 100;
|
|
108
|
-
const DEFAULT_V3_FEE = 2500;
|
|
109
|
-
const HIGH_V3_FEE = 10000;
|
|
110
|
-
/**
|
|
111
|
-
* 根据 lpFeeProfile 获取对应的 V3 费率
|
|
112
|
-
* 参考 Flap 文档: https://docs.flap.sh/flap/developers/launch-a-token
|
|
113
|
-
*
|
|
114
|
-
* @param lpFeeProfile V3LPFeeProfile 枚举值
|
|
115
|
-
* - 0 (STANDARD) = 0.25% on PancakeSwap
|
|
116
|
-
* - 1 (LOW) = 0.01% on PancakeSwap
|
|
117
|
-
* - 2 (HIGH) = 1%
|
|
118
|
-
*/
|
|
119
|
-
function lpFeeProfileToV3Fee(lpFeeProfile) {
|
|
120
|
-
if (lpFeeProfile === undefined)
|
|
121
|
-
return undefined;
|
|
122
|
-
switch (lpFeeProfile) {
|
|
123
|
-
case 0: return DEFAULT_V3_FEE; // STANDARD: 0.25%
|
|
124
|
-
case 1: return USD1_V3_FEE; // LOW: 0.01%
|
|
125
|
-
case 2: return HIGH_V3_FEE; // HIGH: 1%
|
|
126
|
-
default: return undefined;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
86
|
// ==================== 工具函数 ====================
|
|
130
87
|
/** 构建 ERC20 approve 交易 */
|
|
131
88
|
async function buildApproveTransaction(wallet, tokenAddress, spenderAddress, amount, nonce, gasPrice, txType, chainId = 56 // ✅ 添加 chainId 参数
|
|
@@ -235,6 +192,32 @@ export async function flapBundleCreateToDex(params) {
|
|
|
235
192
|
// 判断是否使用原生代币
|
|
236
193
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
237
194
|
const inputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
195
|
+
// ✅ 计算实际使用的 V3 费率
|
|
196
|
+
// 优先级:lpFeeProfile > v3Fee > 默认值(根据 quoteToken)
|
|
197
|
+
// lpFeeProfile: 0=STANDARD(0.25%), 1=LOW(0.01%), 2=HIGH(1%)
|
|
198
|
+
let actualV3Fee;
|
|
199
|
+
if (params.lpFeeProfile !== undefined) {
|
|
200
|
+
// 从 lpFeeProfile 转换为 V3 费率
|
|
201
|
+
switch (params.lpFeeProfile) {
|
|
202
|
+
case 1:
|
|
203
|
+
actualV3Fee = 100;
|
|
204
|
+
break; // LOW: 0.01%
|
|
205
|
+
case 2:
|
|
206
|
+
actualV3Fee = 10000;
|
|
207
|
+
break; // HIGH: 1%
|
|
208
|
+
default:
|
|
209
|
+
actualV3Fee = 2500;
|
|
210
|
+
break; // STANDARD: 0.25%
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (v3Fee !== undefined && v3Fee !== 2500) {
|
|
214
|
+
actualV3Fee = v3Fee;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// 根据 quoteToken 使用默认值(参考 Flap 官方配置)
|
|
218
|
+
// BNB → 0.25% (2500), USD1/稳定币 → 0.01% (100)
|
|
219
|
+
actualV3Fee = useNativeToken ? 2500 : 100;
|
|
220
|
+
}
|
|
238
221
|
// ✅ 获取链配置
|
|
239
222
|
const chainConfig = getChainConfig(chain);
|
|
240
223
|
const chainId = chainConfig.chainId;
|
|
@@ -414,12 +397,16 @@ export async function flapBundleCreateToDex(params) {
|
|
|
414
397
|
const buyNonce = noncesMap.get(addr);
|
|
415
398
|
noncesMap.set(addr, buyNonce + 1);
|
|
416
399
|
const portal = new Contract(portalAddress, PORTAL_ABI, wallet);
|
|
417
|
-
|
|
400
|
+
// ✅ 交易阶段统一优先使用 swapExactInputV3(支持 extensionData;无扩展时传 0x 即可)
|
|
401
|
+
// 说明:这里的 V3 指“交易接口版本(扩展支持)”,不是“外盘 V3 池子”
|
|
402
|
+
const extensionData = params.extensionData ?? '0x';
|
|
403
|
+
const unsigned = await portal.swapExactInputV3.populateTransaction({
|
|
418
404
|
inputToken,
|
|
419
405
|
outputToken: tokenAddress,
|
|
420
406
|
inputAmount: curveBuyAmounts[i],
|
|
421
407
|
minOutputAmount: 0n,
|
|
422
|
-
permitData: '0x' // 不使用 permit,使用链上 approve
|
|
408
|
+
permitData: '0x', // 不使用 permit,使用链上 approve
|
|
409
|
+
extensionData
|
|
423
410
|
}, useNativeToken ? { value: curveBuyAmounts[i] } : {});
|
|
424
411
|
// 最后一个买家触发毕业,需要更多 gas
|
|
425
412
|
const isLastBuyer = i === curveWallets.length - 1;
|
|
@@ -447,70 +434,58 @@ export async function flapBundleCreateToDex(params) {
|
|
|
447
434
|
const curveBuyResults = await Promise.all(curveBuyTxPromises);
|
|
448
435
|
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
449
436
|
curveBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
450
|
-
// 4.
|
|
451
|
-
// ✅
|
|
437
|
+
// 4. 外盘买入交易(直接调用 PancakeSwap Router)
|
|
438
|
+
// ✅ 重要修改:直接调用 PancakeSwap SmartRouter 的 exactInputSingle
|
|
439
|
+
// 参考官方代码: flap-bundle-release/app/v2/pvp/001_launch_buy.ts
|
|
440
|
+
//
|
|
441
|
+
// 原因:之前使用 Portal 的 swapExactInputV3 进行"自动路由",但这有风险:
|
|
442
|
+
// - 如果代币刚毕业,Portal 可能还没有正确记录池子信息
|
|
443
|
+
// - 导致路由失败或回退到内盘
|
|
444
|
+
//
|
|
445
|
+
// 新方案:毕业后直接调用 PancakeSwap Router,确保外盘交易成功
|
|
452
446
|
if (enableDexBuy && dexWallets.length > 0) {
|
|
447
|
+
// 获取 DEX 地址
|
|
448
|
+
const dexAddresses = chain === 'xlayer' ? DEX_ADDRESSES.XLAYER : DEX_ADDRESSES.BSC;
|
|
449
|
+
const smartRouterAddress = dexAddresses.PANCAKE_SMART_ROUTER;
|
|
450
|
+
const wbnbAddress = dexAddresses.WBNB;
|
|
451
|
+
// 获取 V3 费率
|
|
452
|
+
// 根据 lpFeeProfile 或 quoteToken 选择
|
|
453
|
+
const dexV3Fee = actualV3Fee;
|
|
453
454
|
const dexBuyTxPromises = dexWallets.map(async ({ wallet }, i) => {
|
|
454
455
|
const addr = wallet.address.toLowerCase();
|
|
455
456
|
const signedTxs = [];
|
|
456
457
|
const buyAmount = dexBuyAmounts[i];
|
|
457
|
-
// ✅
|
|
458
|
-
const smartRouter = dexPoolType === 'v3' ? chainConfig.v3Router : chainConfig.v2Router;
|
|
459
|
-
const wrappedNative = chainConfig.wrappedNative;
|
|
460
|
-
// ✅ 根据 lpFeeProfile 或 quoteToken 选择 V3 费率
|
|
461
|
-
// 优先级:
|
|
462
|
-
// 1. 如果传入了 lpFeeProfile,使用对应的费率(与 newTokenV4 创建的池子匹配)
|
|
463
|
-
// 2. 如果没传,根据 quoteToken 使用默认费率(参考 Flap 官方脚本)
|
|
464
|
-
// - BNB (原生代币) → 0.25% (2500)
|
|
465
|
-
// - USD1 等稳定币 → 0.01% (100)
|
|
466
|
-
const lpFeeBasedV3Fee = lpFeeProfileToV3Fee(params.lpFeeProfile);
|
|
467
|
-
const defaultV3Fee = useNativeToken ? DEFAULT_V3_FEE : USD1_V3_FEE;
|
|
468
|
-
const actualV3Fee = lpFeeBasedV3Fee ?? v3Fee ?? defaultV3Fee;
|
|
469
|
-
// ✅ ERC20: 先构建 approve 交易
|
|
458
|
+
// ✅ ERC20: 先构建 approve 交易(approve 给 PancakeSwap Router)
|
|
470
459
|
if (!useNativeToken) {
|
|
471
460
|
const approveNonce = noncesMap.get(addr);
|
|
472
461
|
noncesMap.set(addr, approveNonce + 1);
|
|
473
|
-
const approveTx = await buildApproveTransaction(wallet, quoteToken,
|
|
474
|
-
);
|
|
462
|
+
const approveTx = await buildApproveTransaction(wallet, quoteToken, smartRouterAddress, // ✅ approve 给 PancakeSwap Router
|
|
463
|
+
buyAmount, approveNonce, gasPrice, txType, chainId);
|
|
475
464
|
signedTxs.push(approveTx);
|
|
476
465
|
}
|
|
477
|
-
//
|
|
466
|
+
// 构建 PancakeSwap exactInputSingle 交易
|
|
478
467
|
const buyNonce = noncesMap.get(addr);
|
|
479
468
|
noncesMap.set(addr, buyNonce + 1);
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
};
|
|
494
|
-
multicallData = [routerInterface.encodeFunctionData('exactInputSingle', [params])];
|
|
495
|
-
value = useNativeToken ? buyAmount : 0n;
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
// V2: swapExactTokensForTokens
|
|
499
|
-
const path = useNativeToken ? [wrappedNative, tokenAddress] : [quoteToken, tokenAddress];
|
|
500
|
-
const swapData = routerInterface.encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, path, wallet.address]);
|
|
501
|
-
multicallData = [swapData];
|
|
502
|
-
value = useNativeToken ? buyAmount : 0n;
|
|
503
|
-
}
|
|
504
|
-
const calldata = routerInterface.encodeFunctionData('multicall', [multicallData]);
|
|
469
|
+
const router = new Contract(smartRouterAddress, PANCAKE_ROUTER_ABI, wallet);
|
|
470
|
+
// 对于 BNB,使用 WBNB 作为 tokenIn(Router 会自动 wrap)
|
|
471
|
+
const tokenIn = useNativeToken ? wbnbAddress : quoteToken;
|
|
472
|
+
const exactInputSingleParams = {
|
|
473
|
+
tokenIn,
|
|
474
|
+
tokenOut: tokenAddress,
|
|
475
|
+
fee: dexV3Fee, // ✅ 使用与 newTokenV4 创建池子匹配的费率
|
|
476
|
+
recipient: wallet.address,
|
|
477
|
+
amountIn: buyAmount,
|
|
478
|
+
amountOutMinimum: 0n, // 不设置滑点保护(Bundle 中可以接受)
|
|
479
|
+
sqrtPriceLimitX96: 0n
|
|
480
|
+
};
|
|
481
|
+
const unsigned = await router.exactInputSingle.populateTransaction(exactInputSingleParams, useNativeToken ? { value: buyAmount } : {});
|
|
505
482
|
const tx = {
|
|
506
|
-
|
|
507
|
-
data: calldata,
|
|
508
|
-
from: wallet.address,
|
|
483
|
+
...unsigned,
|
|
509
484
|
nonce: buyNonce,
|
|
510
485
|
gasLimit: BigInt(500000),
|
|
511
486
|
chainId,
|
|
512
487
|
type: txType,
|
|
513
|
-
value
|
|
488
|
+
value: useNativeToken ? buyAmount : 0n
|
|
514
489
|
};
|
|
515
490
|
if (txType === 2) {
|
|
516
491
|
tx.maxFeePerGas = gasPrice;
|