four-flap-meme-sdk 1.5.3 → 1.5.5
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,49 @@ const ERC20_ABI = [
|
|
|
25
25
|
stateMutability: "nonpayable"
|
|
26
26
|
}
|
|
27
27
|
];
|
|
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
|
+
}
|
|
70
|
+
};
|
|
28
71
|
// ==================== 链常量 ====================
|
|
29
72
|
// 获取链配置
|
|
30
73
|
function getChainConfig(chain) {
|
|
@@ -149,6 +192,32 @@ export async function flapBundleCreateToDex(params) {
|
|
|
149
192
|
// 判断是否使用原生代币
|
|
150
193
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
151
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
|
+
}
|
|
152
221
|
// ✅ 获取链配置
|
|
153
222
|
const chainConfig = getChainConfig(chain);
|
|
154
223
|
const chainId = chainConfig.chainId;
|
|
@@ -365,40 +434,58 @@ export async function flapBundleCreateToDex(params) {
|
|
|
365
434
|
const curveBuyResults = await Promise.all(curveBuyTxPromises);
|
|
366
435
|
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
367
436
|
curveBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
368
|
-
// 4.
|
|
369
|
-
// ✅
|
|
370
|
-
// -
|
|
371
|
-
//
|
|
372
|
-
//
|
|
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,确保外盘交易成功
|
|
373
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;
|
|
374
454
|
const dexBuyTxPromises = dexWallets.map(async ({ wallet }, i) => {
|
|
375
455
|
const addr = wallet.address.toLowerCase();
|
|
376
456
|
const signedTxs = [];
|
|
377
457
|
const buyAmount = dexBuyAmounts[i];
|
|
378
|
-
|
|
379
|
-
// ✅ ERC20: 先构建 approve 交易
|
|
458
|
+
// ✅ ERC20: 先构建 approve 交易(approve 给 PancakeSwap Router)
|
|
380
459
|
if (!useNativeToken) {
|
|
381
460
|
const approveNonce = noncesMap.get(addr);
|
|
382
461
|
noncesMap.set(addr, approveNonce + 1);
|
|
383
|
-
const approveTx = await buildApproveTransaction(wallet, quoteToken,
|
|
384
|
-
buyAmount, approveNonce, gasPrice, txType, chainId
|
|
385
|
-
);
|
|
462
|
+
const approveTx = await buildApproveTransaction(wallet, quoteToken, smartRouterAddress, // ✅ approve 给 PancakeSwap Router
|
|
463
|
+
buyAmount, approveNonce, gasPrice, txType, chainId);
|
|
386
464
|
signedTxs.push(approveTx);
|
|
387
465
|
}
|
|
388
|
-
//
|
|
466
|
+
// 构建 PancakeSwap exactInputSingle 交易(使用 multicall 方式,与官方一致)
|
|
389
467
|
const buyNonce = noncesMap.get(addr);
|
|
390
468
|
noncesMap.set(addr, buyNonce + 1);
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
469
|
+
// 对于 BNB,使用 WBNB 作为 tokenIn(Router 会自动 wrap)
|
|
470
|
+
const tokenIn = useNativeToken ? wbnbAddress : quoteToken;
|
|
471
|
+
// ✅ 使用官方方式:先编码 exactInputSingle,再用 multicall 包装
|
|
472
|
+
const routerInterface = new ethers.Interface(PANCAKE_ROUTER_ABI);
|
|
473
|
+
const exactInputSingleParams = {
|
|
474
|
+
tokenIn,
|
|
475
|
+
tokenOut: tokenAddress,
|
|
476
|
+
fee: dexV3Fee, // ✅ 使用与 newTokenV4 创建池子匹配的费率
|
|
477
|
+
recipient: wallet.address,
|
|
478
|
+
amountIn: buyAmount,
|
|
479
|
+
amountOutMinimum: 0n, // 不设置滑点保护(Bundle 中可以接受)
|
|
480
|
+
sqrtPriceLimitX96: 0n
|
|
481
|
+
};
|
|
482
|
+
// 编码 exactInputSingle 调用
|
|
483
|
+
const exactInputSingleData = routerInterface.encodeFunctionData('exactInputSingle', [exactInputSingleParams]);
|
|
484
|
+
// 用 multicall 包装(官方要求的调用方式)
|
|
485
|
+
const multicallData = routerInterface.encodeFunctionData('multicall', [[exactInputSingleData]]);
|
|
400
486
|
const tx = {
|
|
401
|
-
|
|
487
|
+
to: smartRouterAddress,
|
|
488
|
+
data: multicallData,
|
|
402
489
|
nonce: buyNonce,
|
|
403
490
|
gasLimit: BigInt(500000),
|
|
404
491
|
chainId,
|