four-flap-meme-sdk 1.5.6 → 1.5.9
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/README.zh-CN.pdf +0 -0
- package/dist/abis/common.js +2 -0
- package/dist/contracts/tm-bundle-merkle/config.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/config.js +0 -1
- package/dist/contracts/tm-bundle-merkle/core.js +13 -9
- package/dist/contracts/tm-bundle-merkle/submit.js +58 -0
- package/dist/flap/portal-bundle-merkle/config.d.ts +0 -1
- package/dist/flap/portal-bundle-merkle/config.js +0 -1
- package/dist/flap/portal-bundle-merkle/create-to-dex.d.ts +8 -2
- package/dist/flap/portal-bundle-merkle/create-to-dex.js +170 -128
- package/dist/flap/portal-bundle-merkle/encryption.d.ts +16 -0
- package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
- package/dist/flap/portal-bundle-merkle/private.js +8 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/package.json +5 -41
- package/dist/sol/constants.d.ts +0 -150
- package/dist/sol/constants.js +0 -188
- package/dist/sol/dex/blockrazor/client.d.ts +0 -51
- package/dist/sol/dex/blockrazor/client.js +0 -96
- package/dist/sol/dex/blockrazor/constants.d.ts +0 -34
- package/dist/sol/dex/blockrazor/constants.js +0 -55
- package/dist/sol/dex/blockrazor/geyser.d.ts +0 -128
- package/dist/sol/dex/blockrazor/geyser.js +0 -530
- package/dist/sol/dex/blockrazor/index.d.ts +0 -18
- package/dist/sol/dex/blockrazor/index.js +0 -23
- package/dist/sol/dex/blockrazor/send.d.ts +0 -135
- package/dist/sol/dex/blockrazor/send.js +0 -254
- package/dist/sol/dex/blockrazor/types.d.ts +0 -191
- package/dist/sol/dex/blockrazor/types.js +0 -5
- package/dist/sol/dex/index.d.ts +0 -10
- package/dist/sol/dex/index.js +0 -16
- package/dist/sol/dex/jup/client.d.ts +0 -33
- package/dist/sol/dex/jup/client.js +0 -110
- package/dist/sol/dex/jup/index.d.ts +0 -16
- package/dist/sol/dex/jup/index.js +0 -148
- package/dist/sol/dex/jup/legacy.d.ts +0 -623
- package/dist/sol/dex/jup/legacy.js +0 -416
- package/dist/sol/dex/jup/lend.d.ts +0 -640
- package/dist/sol/dex/jup/lend.js +0 -603
- package/dist/sol/dex/jup/portfolio.d.ts +0 -362
- package/dist/sol/dex/jup/portfolio.js +0 -367
- package/dist/sol/dex/jup/price.d.ts +0 -173
- package/dist/sol/dex/jup/price.js +0 -220
- package/dist/sol/dex/jup/recurring.d.ts +0 -437
- package/dist/sol/dex/jup/recurring.js +0 -320
- package/dist/sol/dex/jup/send.d.ts +0 -282
- package/dist/sol/dex/jup/send.js +0 -295
- package/dist/sol/dex/jup/studio.d.ts +0 -457
- package/dist/sol/dex/jup/studio.js +0 -488
- package/dist/sol/dex/jup/tokens.d.ts +0 -767
- package/dist/sol/dex/jup/tokens.js +0 -697
- package/dist/sol/dex/jup/trigger.d.ts +0 -511
- package/dist/sol/dex/jup/trigger.js +0 -397
- package/dist/sol/dex/jup/types.d.ts +0 -433
- package/dist/sol/dex/jup/types.js +0 -5
- package/dist/sol/dex/jup/ultra.d.ts +0 -646
- package/dist/sol/dex/jup/ultra.js +0 -853
- package/dist/sol/dex/meteora/client.d.ts +0 -76
- package/dist/sol/dex/meteora/client.js +0 -219
- package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +0 -61
- package/dist/sol/dex/meteora/damm-v1-bundle.js +0 -112
- package/dist/sol/dex/meteora/damm-v1.d.ts +0 -118
- package/dist/sol/dex/meteora/damm-v1.js +0 -315
- package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +0 -82
- package/dist/sol/dex/meteora/damm-v2-bundle.js +0 -242
- package/dist/sol/dex/meteora/damm-v2.d.ts +0 -172
- package/dist/sol/dex/meteora/damm-v2.js +0 -632
- package/dist/sol/dex/meteora/dbc-bundle.d.ts +0 -123
- package/dist/sol/dex/meteora/dbc-bundle.js +0 -304
- package/dist/sol/dex/meteora/dbc.d.ts +0 -192
- package/dist/sol/dex/meteora/dbc.js +0 -619
- package/dist/sol/dex/meteora/dlmm-bundle.d.ts +0 -39
- package/dist/sol/dex/meteora/dlmm-bundle.js +0 -189
- package/dist/sol/dex/meteora/dlmm.d.ts +0 -157
- package/dist/sol/dex/meteora/dlmm.js +0 -671
- package/dist/sol/dex/meteora/index.d.ts +0 -25
- package/dist/sol/dex/meteora/index.js +0 -65
- package/dist/sol/dex/meteora/types.d.ts +0 -787
- package/dist/sol/dex/meteora/types.js +0 -110
- package/dist/sol/dex/orca/index.d.ts +0 -10
- package/dist/sol/dex/orca/index.js +0 -16
- package/dist/sol/dex/orca/orca-bundle.d.ts +0 -41
- package/dist/sol/dex/orca/orca-bundle.js +0 -173
- package/dist/sol/dex/orca/orca.d.ts +0 -65
- package/dist/sol/dex/orca/orca.js +0 -474
- package/dist/sol/dex/orca/types.d.ts +0 -263
- package/dist/sol/dex/orca/types.js +0 -38
- package/dist/sol/dex/orca/wavebreak-bundle.d.ts +0 -34
- package/dist/sol/dex/orca/wavebreak-bundle.js +0 -198
- package/dist/sol/dex/orca/wavebreak-types.d.ts +0 -227
- package/dist/sol/dex/orca/wavebreak-types.js +0 -23
- package/dist/sol/dex/orca/wavebreak.d.ts +0 -78
- package/dist/sol/dex/orca/wavebreak.js +0 -497
- package/dist/sol/dex/pump/index.d.ts +0 -9
- package/dist/sol/dex/pump/index.js +0 -14
- package/dist/sol/dex/pump/pump-bundle.d.ts +0 -92
- package/dist/sol/dex/pump/pump-bundle.js +0 -383
- package/dist/sol/dex/pump/pump-swap-bundle.d.ts +0 -103
- package/dist/sol/dex/pump/pump-swap-bundle.js +0 -380
- package/dist/sol/dex/pump/pump-swap.d.ts +0 -46
- package/dist/sol/dex/pump/pump-swap.js +0 -199
- package/dist/sol/dex/pump/pump.d.ts +0 -35
- package/dist/sol/dex/pump/pump.js +0 -352
- package/dist/sol/dex/pump/types.d.ts +0 -215
- package/dist/sol/dex/pump/types.js +0 -5
- package/dist/sol/dex/raydium/index.d.ts +0 -8
- package/dist/sol/dex/raydium/index.js +0 -12
- package/dist/sol/dex/raydium/launchlab.d.ts +0 -68
- package/dist/sol/dex/raydium/launchlab.js +0 -210
- package/dist/sol/dex/raydium/raydium-bundle.d.ts +0 -64
- package/dist/sol/dex/raydium/raydium-bundle.js +0 -324
- package/dist/sol/dex/raydium/raydium.d.ts +0 -40
- package/dist/sol/dex/raydium/raydium.js +0 -366
- package/dist/sol/dex/raydium/types.d.ts +0 -240
- package/dist/sol/dex/raydium/types.js +0 -5
- package/dist/sol/index.d.ts +0 -11
- package/dist/sol/index.js +0 -18
- package/dist/sol/jito/bundle.d.ts +0 -90
- package/dist/sol/jito/bundle.js +0 -263
- package/dist/sol/jito/index.d.ts +0 -7
- package/dist/sol/jito/index.js +0 -7
- package/dist/sol/jito/tip.d.ts +0 -51
- package/dist/sol/jito/tip.js +0 -83
- package/dist/sol/jito/types.d.ts +0 -100
- package/dist/sol/jito/types.js +0 -5
- package/dist/sol/nozomi/client.d.ts +0 -63
- package/dist/sol/nozomi/client.js +0 -222
- package/dist/sol/nozomi/index.d.ts +0 -8
- package/dist/sol/nozomi/index.js +0 -8
- package/dist/sol/nozomi/tip.d.ts +0 -50
- package/dist/sol/nozomi/tip.js +0 -80
- package/dist/sol/nozomi/types.d.ts +0 -96
- package/dist/sol/nozomi/types.js +0 -5
- package/dist/sol/token/create-complete.d.ts +0 -115
- package/dist/sol/token/create-complete.js +0 -235
- package/dist/sol/token/create-token.d.ts +0 -57
- package/dist/sol/token/create-token.js +0 -230
- package/dist/sol/token/index.d.ts +0 -9
- package/dist/sol/token/index.js +0 -14
- package/dist/sol/token/metadata-upload.d.ts +0 -86
- package/dist/sol/token/metadata-upload.js +0 -173
- package/dist/sol/token/metadata.d.ts +0 -92
- package/dist/sol/token/metadata.js +0 -274
- package/dist/sol/token/types.d.ts +0 -153
- package/dist/sol/token/types.js +0 -5
- package/dist/sol/types.d.ts +0 -176
- package/dist/sol/types.js +0 -7
- package/dist/sol/utils/balance.d.ts +0 -160
- package/dist/sol/utils/balance.js +0 -638
- package/dist/sol/utils/connection.d.ts +0 -78
- package/dist/sol/utils/connection.js +0 -168
- package/dist/sol/utils/index.d.ts +0 -9
- package/dist/sol/utils/index.js +0 -9
- package/dist/sol/utils/lp-inspect.d.ts +0 -129
- package/dist/sol/utils/lp-inspect.js +0 -900
- package/dist/sol/utils/transfer.d.ts +0 -196
- package/dist/sol/utils/transfer.js +0 -307
- package/dist/sol/utils/wallet.d.ts +0 -107
- package/dist/sol/utils/wallet.js +0 -210
package/README.zh-CN.pdf
ADDED
|
Binary file
|
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)',
|
|
@@ -45,7 +45,6 @@ export declare function shouldExtractProfit(config?: FourAnyConfig): boolean;
|
|
|
45
45
|
export declare function getProfitRateBps(config?: FourAnyConfig): number;
|
|
46
46
|
/**
|
|
47
47
|
* 获取利润接收地址
|
|
48
|
-
* ✅ 硬编码:0xe8D0334fAf713884133640CAEe4ECdd2106AF103
|
|
49
48
|
*/
|
|
50
49
|
export declare function getProfitRecipient(config?: FourAnyConfig): string;
|
|
51
50
|
/**
|
|
@@ -54,6 +54,9 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
54
54
|
}
|
|
55
55
|
const accessToken = await loginFourClient(devWallet, fourClient);
|
|
56
56
|
const imgUrl = await resolveTokenImage(fourClient, tokenInfo, accessToken);
|
|
57
|
+
// ✅ 兼容:历史调用可能把 Dev 首买金额塞在 buyAmounts[0],而不是 tokenInfo.preSale
|
|
58
|
+
// Four 官方 API/链上 createArg 使用 preSale 表达“创建者预购金额”
|
|
59
|
+
const effectivePreSaleStr = (tokenInfo.preSale && String(tokenInfo.preSale).trim().length > 0 ? String(tokenInfo.preSale) : String(buyAmounts[0] ?? '0'));
|
|
57
60
|
const createResp = await fourClient.createToken(accessToken, {
|
|
58
61
|
name: tokenInfo.name,
|
|
59
62
|
shortName: tokenInfo.symbol,
|
|
@@ -64,7 +67,7 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
64
67
|
webUrl: tokenInfo.webUrl,
|
|
65
68
|
twitterUrl: tokenInfo.twitterUrl,
|
|
66
69
|
telegramUrl: tokenInfo.telegramUrl,
|
|
67
|
-
preSale:
|
|
70
|
+
preSale: effectivePreSaleStr || '0',
|
|
68
71
|
onlyMPC: false,
|
|
69
72
|
lpTradingFee: 0.0025,
|
|
70
73
|
symbol: 'BNB',
|
|
@@ -83,7 +86,8 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
83
86
|
// ✅ 单笔交易: 和 Four.meme 官网一样
|
|
84
87
|
// 计算利润
|
|
85
88
|
const extractProfit = shouldExtractProfit(config);
|
|
86
|
-
|
|
89
|
+
// ✅ 对 create 流程来说,“买入金额”就是 preSale(创建者预购)
|
|
90
|
+
const originalBuyAmount = ethers.parseEther(effectivePreSaleStr || '0');
|
|
87
91
|
let actualBuyFunds;
|
|
88
92
|
let profitAmount;
|
|
89
93
|
if (extractProfit) {
|
|
@@ -99,13 +103,13 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
99
103
|
const bribeAmount = getBribeAmount(config);
|
|
100
104
|
const needBribeTx = bribeAmount > 0n;
|
|
101
105
|
const b0AmountWei = ethers.parseEther(params.b0Amount ?? '0');
|
|
102
|
-
const preSaleWei =
|
|
103
|
-
// ✅
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
+
const preSaleWei = effectivePreSaleStr ? ethers.parseEther(effectivePreSaleStr) : 0n;
|
|
107
|
+
// ✅ 关键修复:
|
|
108
|
+
// createToken 的预购金额由 createArg.preSale 决定;因此这里只需要支付:创建费 + b0Amount + preSale
|
|
109
|
+
// 不要把“其它买入金额”额外塞进 msg.value,否则会被合约退回,造成“看起来没买到”的误解。
|
|
110
|
+
const valueWei = PLATFORM_CREATE_FEE + b0AmountWei + preSaleWei;
|
|
106
111
|
const tmCreate = new ethers.Contract(tmCreateAddr, TM2_ABI, devWallet);
|
|
107
|
-
const createTxUnsigned = await tmCreate.createToken.populateTransaction(createResp.createArg, createResp.signature, { value: valueWei }
|
|
108
|
-
);
|
|
112
|
+
const createTxUnsigned = await tmCreate.createToken.populateTransaction(createResp.createArg, createResp.signature, { value: valueWei });
|
|
109
113
|
// ✅ 贿赂交易放在首位(由 devWallet 发送)
|
|
110
114
|
let bribeNonce;
|
|
111
115
|
if (needBribeTx) {
|
|
@@ -125,7 +129,7 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
125
129
|
...createTxUnsigned,
|
|
126
130
|
from: devWallet.address,
|
|
127
131
|
nonce: await nonceManager.getNextNonce(devWallet),
|
|
128
|
-
gasLimit: getGasLimit(config, 1500000),
|
|
132
|
+
gasLimit: getGasLimit(config, 1500000),
|
|
129
133
|
gasPrice,
|
|
130
134
|
chainId,
|
|
131
135
|
type: getTxType(config),
|
|
@@ -250,6 +250,30 @@ export async function submitMultipleBundlesToBlockRazorParallel(bundles, config)
|
|
|
250
250
|
const promises = bundles.map(signedTransactions => submitBundleToBlockRazor(signedTransactions, config));
|
|
251
251
|
return await Promise.all(promises);
|
|
252
252
|
}
|
|
253
|
+
function shouldTreatBroadcastErrorAsMaybeSent(msg) {
|
|
254
|
+
const m = String(msg || '').toLowerCase();
|
|
255
|
+
// 常见:交易已被节点接收/已在 mempool/已上链,但节点返回报错
|
|
256
|
+
return (m.includes('already known') ||
|
|
257
|
+
m.includes('known transaction') ||
|
|
258
|
+
m.includes('nonce too low') ||
|
|
259
|
+
m.includes('nonce has already been used') ||
|
|
260
|
+
m.includes('nonce expired') ||
|
|
261
|
+
m.includes('replacement transaction underpriced'));
|
|
262
|
+
}
|
|
263
|
+
async function recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage) {
|
|
264
|
+
if (!shouldTreatBroadcastErrorAsMaybeSent(errorMessage))
|
|
265
|
+
return undefined;
|
|
266
|
+
try {
|
|
267
|
+
const hash = ethers.keccak256(signedTx);
|
|
268
|
+
if (!hash)
|
|
269
|
+
return undefined;
|
|
270
|
+
const tx = await provider.getTransaction(hash);
|
|
271
|
+
return tx ? hash : undefined;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
253
277
|
/**
|
|
254
278
|
* 并行广播到 RPC(用于不支持 Bundle 的链,如 Monad)
|
|
255
279
|
*
|
|
@@ -333,6 +357,17 @@ export async function submitDirectToRpc(signedTransactions, config) {
|
|
|
333
357
|
catch (error) {
|
|
334
358
|
const errorMessage = error?.message || String(error);
|
|
335
359
|
console.error(`❌ [${chainName}] 交易 ${i + 1}/${totalTransactions} 广播失败:`, errorMessage);
|
|
360
|
+
// ✅ 兼容:nonce too low / already known 等情况下,交易可能已经被接收
|
|
361
|
+
const recovered = await recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage);
|
|
362
|
+
if (recovered) {
|
|
363
|
+
if (config.waitForConfirmation) {
|
|
364
|
+
try {
|
|
365
|
+
await provider.waitForTransaction(recovered, 1, config.confirmationTimeout ?? 30000);
|
|
366
|
+
}
|
|
367
|
+
catch { }
|
|
368
|
+
}
|
|
369
|
+
return { index: i, success: true, txHash: recovered, error: errorMessage };
|
|
370
|
+
}
|
|
336
371
|
return {
|
|
337
372
|
index: i,
|
|
338
373
|
success: false,
|
|
@@ -431,6 +466,29 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
|
|
|
431
466
|
}
|
|
432
467
|
catch (error) {
|
|
433
468
|
const errorMessage = error?.message || String(error);
|
|
469
|
+
// ✅ 兼容:nonce too low / already known 等情况下,交易可能已经被接收
|
|
470
|
+
const recovered = await recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage);
|
|
471
|
+
if (recovered) {
|
|
472
|
+
// 顺序模式依赖确认:尽量等待一下
|
|
473
|
+
try {
|
|
474
|
+
const receipt = await provider.waitForTransaction(recovered, 1, confirmationTimeout);
|
|
475
|
+
if (receipt && receipt.status === 1) {
|
|
476
|
+
results.push({ index: i, success: true, txHash: recovered, error: errorMessage });
|
|
477
|
+
txHashes.push(recovered);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const errorMsg = `交易执行失败(status=${receipt?.status})`;
|
|
481
|
+
results.push({ index: i, success: false, txHash: recovered, error: `${errorMessage} | ${errorMsg}` });
|
|
482
|
+
errors.push(`交易 ${i + 1}: ${errorMessage} | ${errorMsg}`);
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// 等待超时也先认为“已广播”,交给上层自行确认
|
|
487
|
+
results.push({ index: i, success: true, txHash: recovered, error: errorMessage });
|
|
488
|
+
txHashes.push(recovered);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
434
492
|
results.push({
|
|
435
493
|
index: i,
|
|
436
494
|
success: false,
|
|
@@ -46,7 +46,6 @@ export declare function shouldExtractProfit(config?: FlapAnyConfig): boolean;
|
|
|
46
46
|
export declare function getProfitRateBps(config?: FlapAnyConfig): number;
|
|
47
47
|
/**
|
|
48
48
|
* 获取利润接收地址
|
|
49
|
-
* ✅ 硬编码:0xe8D0334fAf713884133640CAEe4ECdd2106AF103
|
|
50
49
|
*/
|
|
51
50
|
export declare function getProfitRecipient(config?: FlapAnyConfig): string;
|
|
52
51
|
/**
|
|
@@ -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,87 +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
86
|
// ==================== 工具函数 ====================
|
|
110
87
|
/** 构建 ERC20 approve 交易 */
|
|
111
88
|
async function buildApproveTransaction(wallet, tokenAddress, spenderAddress, amount, nonce, gasPrice, txType, chainId = 56 // ✅ 添加 chainId 参数
|
|
@@ -158,13 +135,43 @@ function splitAmount(totalAmount, count) {
|
|
|
158
135
|
allocated += amount;
|
|
159
136
|
}
|
|
160
137
|
amounts.push(totalAmount - allocated);
|
|
161
|
-
// 随机打乱
|
|
162
|
-
for (let i = amounts.length - 1; i > 0; i--) {
|
|
163
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
164
|
-
[amounts[i], amounts[j]] = [amounts[j], amounts[i]];
|
|
165
|
-
}
|
|
166
138
|
return amounts;
|
|
167
139
|
}
|
|
140
|
+
/** 解析输入金额字符串为 bigint(按是否原生币选择 parseEther / parseUnits) */
|
|
141
|
+
function parseQuoteAmount(amount, useNativeToken, quoteTokenDecimals) {
|
|
142
|
+
const v = String(amount ?? '').trim();
|
|
143
|
+
if (!v)
|
|
144
|
+
return 0n;
|
|
145
|
+
return useNativeToken ? ethers.parseEther(v) : ethers.parseUnits(v, quoteTokenDecimals);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 计算每个买家的买入金额(优先使用 params 传入的 buyAmount;否则 fallback 到按总额随机拆分)
|
|
149
|
+
* 说明:memeweb 前端已根据 average/random/custom 计算出每个钱包 buyAmount;
|
|
150
|
+
* 若 SDK 这里再次 splitAmount,会导致实际 buyAmount 与 UI/预期不一致,并引发余额不足(insufficient funds)。
|
|
151
|
+
*/
|
|
152
|
+
function resolveBuyerAmounts(buyers, totalAmountStr, useNativeToken, quoteTokenDecimals) {
|
|
153
|
+
const provided = buyers.map(b => b?.buyAmount).filter(v => v != null && String(v).trim() !== '');
|
|
154
|
+
const allProvided = provided.length === buyers.length;
|
|
155
|
+
if (allProvided) {
|
|
156
|
+
// ✅ 所有买家都显式提供了 buyAmount:按传入金额构建,但要修正“浮点/四舍五入导致总和偏差”
|
|
157
|
+
const amounts = buyers.map(b => parseQuoteAmount(String(b.buyAmount), useNativeToken, quoteTokenDecimals));
|
|
158
|
+
const totalWei = parseQuoteAmount(totalAmountStr, useNativeToken, quoteTokenDecimals);
|
|
159
|
+
const sum = amounts.reduce((a, b) => a + b, 0n);
|
|
160
|
+
const delta = totalWei - sum;
|
|
161
|
+
if (delta !== 0n && amounts.length > 0) {
|
|
162
|
+
// 优先修正最后一个金额(对应 create-to-dex 里“最后一单可能触发毕业”的假设)
|
|
163
|
+
const lastIdx = amounts.length - 1;
|
|
164
|
+
const next = amounts[lastIdx] + delta;
|
|
165
|
+
if (next <= 0n) {
|
|
166
|
+
throw new Error(`买入金额总和与总额不一致且无法修正:sum=${sum} total=${totalWei} delta=${delta}`);
|
|
167
|
+
}
|
|
168
|
+
amounts[lastIdx] = next;
|
|
169
|
+
}
|
|
170
|
+
return amounts;
|
|
171
|
+
}
|
|
172
|
+
const totalWei = parseQuoteAmount(totalAmountStr, useNativeToken, quoteTokenDecimals);
|
|
173
|
+
return splitAmount(totalWei, buyers.length);
|
|
174
|
+
}
|
|
168
175
|
// ==================== 主函数 ====================
|
|
169
176
|
/**
|
|
170
177
|
* Flap 发币 + 一键买到外盘捆绑交易
|
|
@@ -185,6 +192,32 @@ export async function flapBundleCreateToDex(params) {
|
|
|
185
192
|
// 判断是否使用原生代币
|
|
186
193
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
187
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
|
+
}
|
|
188
221
|
// ✅ 获取链配置
|
|
189
222
|
const chainConfig = getChainConfig(chain);
|
|
190
223
|
const chainId = chainConfig.chainId;
|
|
@@ -231,17 +264,11 @@ export async function flapBundleCreateToDex(params) {
|
|
|
231
264
|
const bribeAmount = getBribeAmount(config);
|
|
232
265
|
const needBribeTx = bribeAmount > 0n;
|
|
233
266
|
// ✅ 计算内盘买入金额
|
|
234
|
-
const
|
|
235
|
-
? ethers.parseEther(curveTotalBuyAmount)
|
|
236
|
-
: ethers.parseUnits(curveTotalBuyAmount, quoteTokenDecimals);
|
|
237
|
-
const curveBuyAmounts = splitAmount(curveTotalWei, curveBuyers.length);
|
|
267
|
+
const curveBuyAmounts = resolveBuyerAmounts(curveBuyers, curveTotalBuyAmount, useNativeToken, quoteTokenDecimals);
|
|
238
268
|
// ✅ 计算外盘买入金额
|
|
239
269
|
let dexBuyAmounts = [];
|
|
240
270
|
if (enableDexBuy && dexBuyers.length > 0 && dexTotalBuyAmount) {
|
|
241
|
-
|
|
242
|
-
? ethers.parseEther(dexTotalBuyAmount)
|
|
243
|
-
: ethers.parseUnits(dexTotalBuyAmount, quoteTokenDecimals);
|
|
244
|
-
dexBuyAmounts = splitAmount(dexTotalWei, dexBuyers.length);
|
|
271
|
+
dexBuyAmounts = resolveBuyerAmounts(dexBuyers, dexTotalBuyAmount, useNativeToken, quoteTokenDecimals);
|
|
245
272
|
}
|
|
246
273
|
// ✅ 计算利润
|
|
247
274
|
const totalBuyAmount = curveBuyAmounts.reduce((a, b) => a + b, 0n)
|
|
@@ -249,7 +276,7 @@ export async function flapBundleCreateToDex(params) {
|
|
|
249
276
|
const profitAmount = (totalBuyAmount * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
250
277
|
// ==================== 构建交易 ====================
|
|
251
278
|
const allTransactions = [];
|
|
252
|
-
//
|
|
279
|
+
// 使用第一个内盘买家作为贿赂支付者(提高 bundle 打包优先级)
|
|
253
280
|
const mainBuyerWallet = curveWallets[0].wallet;
|
|
254
281
|
// 1. 贿赂交易
|
|
255
282
|
if (needBribeTx) {
|
|
@@ -370,16 +397,26 @@ export async function flapBundleCreateToDex(params) {
|
|
|
370
397
|
const buyNonce = noncesMap.get(addr);
|
|
371
398
|
noncesMap.set(addr, buyNonce + 1);
|
|
372
399
|
const portal = new Contract(portalAddress, PORTAL_ABI, wallet);
|
|
373
|
-
|
|
400
|
+
// ✅ 交易阶段统一优先使用 swapExactInputV3(支持 extensionData;无扩展时传 0x 即可)
|
|
401
|
+
// 说明:这里的 V3 指“交易接口版本(扩展支持)”,不是“外盘 V3 池子”
|
|
402
|
+
const extensionData = params.extensionData ?? '0x';
|
|
403
|
+
const unsigned = await portal.swapExactInputV3.populateTransaction({
|
|
374
404
|
inputToken,
|
|
375
405
|
outputToken: tokenAddress,
|
|
376
406
|
inputAmount: curveBuyAmounts[i],
|
|
377
407
|
minOutputAmount: 0n,
|
|
378
|
-
permitData: '0x' // 不使用 permit,使用链上 approve
|
|
408
|
+
permitData: '0x', // 不使用 permit,使用链上 approve
|
|
409
|
+
extensionData
|
|
379
410
|
}, useNativeToken ? { value: curveBuyAmounts[i] } : {});
|
|
380
411
|
// 最后一个买家触发毕业,需要更多 gas
|
|
381
412
|
const isLastBuyer = i === curveWallets.length - 1;
|
|
382
|
-
|
|
413
|
+
// ⚠️ 触发毕业(创建 Pancake V3 池 + Mint 初始流动性)会非常耗 gas。
|
|
414
|
+
// 链上成功样例 0x29b173... 的 gasLimit=8,000,000,gasUsed≈6,647,149。
|
|
415
|
+
// Flap 官方示例脚本也使用 last_buy=8_000_000。
|
|
416
|
+
const LAST_BUY_GAS_LIMIT = BigInt(8000000);
|
|
417
|
+
const buyGasLimit = isLastBuyer
|
|
418
|
+
? (finalGasLimit > LAST_BUY_GAS_LIMIT ? finalGasLimit : LAST_BUY_GAS_LIMIT)
|
|
419
|
+
: finalGasLimit;
|
|
383
420
|
const tx = {
|
|
384
421
|
...unsigned,
|
|
385
422
|
from: wallet.address,
|
|
@@ -403,63 +440,66 @@ export async function flapBundleCreateToDex(params) {
|
|
|
403
440
|
const curveBuyResults = await Promise.all(curveBuyTxPromises);
|
|
404
441
|
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
405
442
|
curveBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
406
|
-
// 4.
|
|
407
|
-
// ✅
|
|
443
|
+
// 4. 外盘买入交易(直接调用 PancakeSwap Router)
|
|
444
|
+
// ✅ 重要修改:直接调用 PancakeSwap SmartRouter 的 exactInputSingle
|
|
445
|
+
// 参考官方代码: flap-bundle-release/app/v2/pvp/001_launch_buy.ts
|
|
446
|
+
//
|
|
447
|
+
// 原因:之前使用 Portal 的 swapExactInputV3 进行"自动路由",但这有风险:
|
|
448
|
+
// - 如果代币刚毕业,Portal 可能还没有正确记录池子信息
|
|
449
|
+
// - 导致路由失败或回退到内盘
|
|
450
|
+
//
|
|
451
|
+
// 新方案:毕业后直接调用 PancakeSwap Router,确保外盘交易成功
|
|
408
452
|
if (enableDexBuy && dexWallets.length > 0) {
|
|
453
|
+
// 获取 DEX 地址
|
|
454
|
+
const dexAddresses = chain === 'xlayer' ? DEX_ADDRESSES.XLAYER : DEX_ADDRESSES.BSC;
|
|
455
|
+
const smartRouterAddress = dexAddresses.PANCAKE_SMART_ROUTER;
|
|
456
|
+
const wbnbAddress = dexAddresses.WBNB;
|
|
457
|
+
// 获取 V3 费率
|
|
458
|
+
// 根据 lpFeeProfile 或 quoteToken 选择
|
|
459
|
+
const dexV3Fee = actualV3Fee;
|
|
409
460
|
const dexBuyTxPromises = dexWallets.map(async ({ wallet }, i) => {
|
|
410
461
|
const addr = wallet.address.toLowerCase();
|
|
411
462
|
const signedTxs = [];
|
|
412
463
|
const buyAmount = dexBuyAmounts[i];
|
|
413
|
-
// ✅
|
|
414
|
-
const smartRouter = dexPoolType === 'v3' ? chainConfig.v3Router : chainConfig.v2Router;
|
|
415
|
-
const wrappedNative = chainConfig.wrappedNative;
|
|
416
|
-
// ✅ 根据 quoteToken 选择 V3 费率
|
|
417
|
-
const actualV3Fee = v3Fee || (useNativeToken ? DEFAULT_V3_FEE : USD1_V3_FEE);
|
|
418
|
-
// ✅ ERC20: 先构建 approve 交易
|
|
464
|
+
// ✅ ERC20: 先构建 approve 交易(approve 给 PancakeSwap Router)
|
|
419
465
|
if (!useNativeToken) {
|
|
420
466
|
const approveNonce = noncesMap.get(addr);
|
|
421
467
|
noncesMap.set(addr, approveNonce + 1);
|
|
422
|
-
const approveTx = await buildApproveTransaction(wallet, quoteToken,
|
|
423
|
-
);
|
|
468
|
+
const approveTx = await buildApproveTransaction(wallet, quoteToken, smartRouterAddress, // ✅ approve 给 PancakeSwap Router
|
|
469
|
+
buyAmount, approveNonce, gasPrice, txType, chainId);
|
|
424
470
|
signedTxs.push(approveTx);
|
|
425
471
|
}
|
|
426
|
-
//
|
|
472
|
+
// 构建 PancakeSwap exactInputSingle 交易(使用 multicall 方式,与官方一致)
|
|
427
473
|
const buyNonce = noncesMap.get(addr);
|
|
428
474
|
noncesMap.set(addr, buyNonce + 1);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const swapData = routerInterface.encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, path, wallet.address]);
|
|
450
|
-
multicallData = [swapData];
|
|
451
|
-
value = useNativeToken ? buyAmount : 0n;
|
|
452
|
-
}
|
|
453
|
-
const calldata = routerInterface.encodeFunctionData('multicall', [multicallData]);
|
|
475
|
+
// 对于 BNB,使用 WBNB 作为 tokenIn(Router 会自动 wrap)
|
|
476
|
+
const tokenIn = useNativeToken ? wbnbAddress : quoteToken;
|
|
477
|
+
// ✅ 使用官方方式:先编码 exactInputSingle,再用 multicall 包装
|
|
478
|
+
const routerInterface = new ethers.Interface(PANCAKE_ROUTER_ABI);
|
|
479
|
+
const exactInputSingleParams = {
|
|
480
|
+
tokenIn,
|
|
481
|
+
tokenOut: tokenAddress,
|
|
482
|
+
fee: dexV3Fee, // ✅ 使用与 newTokenV4 创建池子匹配的费率
|
|
483
|
+
recipient: wallet.address,
|
|
484
|
+
amountIn: buyAmount,
|
|
485
|
+
amountOutMinimum: 0n, // 不设置滑点保护(Bundle 中可以接受)
|
|
486
|
+
sqrtPriceLimitX96: 0n
|
|
487
|
+
};
|
|
488
|
+
// 编码 exactInputSingle 调用
|
|
489
|
+
const exactInputSingleData = routerInterface.encodeFunctionData('exactInputSingle', [exactInputSingleParams]);
|
|
490
|
+
// 用 multicall 包装(官方要求的调用方式)
|
|
491
|
+
const multicallData = routerInterface.encodeFunctionData('multicall', [[exactInputSingleData]]);
|
|
492
|
+
// ⚠️ Pancake SmartRouter 外盘买的 gas 不能太小,否则 bundle 预执行会直接失败。
|
|
493
|
+
// Flap 官方示例配置 dex_buy=5_000_000(flap-bundle-release/app/configs/global.ts)。
|
|
494
|
+
const DEX_BUY_GAS_LIMIT = BigInt(8000000);
|
|
454
495
|
const tx = {
|
|
455
|
-
to:
|
|
456
|
-
data:
|
|
457
|
-
from: wallet.address,
|
|
496
|
+
to: smartRouterAddress,
|
|
497
|
+
data: multicallData,
|
|
458
498
|
nonce: buyNonce,
|
|
459
|
-
gasLimit:
|
|
499
|
+
gasLimit: DEX_BUY_GAS_LIMIT,
|
|
460
500
|
chainId,
|
|
461
501
|
type: txType,
|
|
462
|
-
value
|
|
502
|
+
value: useNativeToken ? buyAmount : 0n
|
|
463
503
|
};
|
|
464
504
|
if (txType === 2) {
|
|
465
505
|
tx.maxFeePerGas = gasPrice;
|
|
@@ -479,11 +519,13 @@ export async function flapBundleCreateToDex(params) {
|
|
|
479
519
|
// 5. 利润多跳转账
|
|
480
520
|
let profitHopWallets;
|
|
481
521
|
if (profitAmount > 0n) {
|
|
482
|
-
|
|
483
|
-
|
|
522
|
+
// ✅ 调整:利润从 Dev 钱包(devPrivateKey)支付,避免第一个内盘买家余额不足导致 want 过高
|
|
523
|
+
// 注意:利润交易会额外消耗原生币(profitAmount + gas),请确保 Dev 钱包留足余额
|
|
524
|
+
const devAddr = devWallet.address.toLowerCase();
|
|
525
|
+
const profitNonce = noncesMap.get(devAddr);
|
|
484
526
|
const profitResult = await buildProfitHopTransactions({
|
|
485
527
|
provider,
|
|
486
|
-
payerWallet:
|
|
528
|
+
payerWallet: devWallet,
|
|
487
529
|
profitAmount,
|
|
488
530
|
profitRecipient: getProfitRecipient(),
|
|
489
531
|
hopCount: PROFIT_HOP_COUNT,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECDH + AES-GCM 加密工具(浏览器兼容)
|
|
3
|
+
* 用于将签名交易用服务器公钥加密
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 用服务器公钥加密签名交易(ECDH + AES-GCM)
|
|
7
|
+
*
|
|
8
|
+
* @param signedTransactions 签名后的交易数组
|
|
9
|
+
* @param publicKeyBase64 服务器提供的公钥(Base64 格式)
|
|
10
|
+
* @returns JSON 字符串 {e: 临时公钥, i: IV, d: 密文}
|
|
11
|
+
*/
|
|
12
|
+
export declare function encryptWithPublicKey(signedTransactions: string[], publicKeyBase64: string): Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* 验证公钥格式(Base64)
|
|
15
|
+
*/
|
|
16
|
+
export declare function validatePublicKey(publicKeyBase64: string): boolean;
|