four-flap-meme-sdk 1.5.12 → 1.5.14
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.
|
@@ -13,6 +13,9 @@ const BSC_WBNB = ADDRESSES.BSC.WBNB;
|
|
|
13
13
|
// ✅ Monad 链常量
|
|
14
14
|
const MONAD_PANCAKE_V2_ROUTER = '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9';
|
|
15
15
|
const MONAD_WMON = ADDRESSES.MONAD.WMON;
|
|
16
|
+
// ✅ XLAYER 链常量(PotatoSwap V2)
|
|
17
|
+
const XLAYER_POTATOSWAP_V2_ROUTER = ADDRESSES.XLAYER.PotatoSwapV2Router;
|
|
18
|
+
const XLAYER_WOKB = ADDRESSES.XLAYER.WOKB;
|
|
16
19
|
const ROUTER_ABI = V2_ROUTER_QUOTE_ABI;
|
|
17
20
|
/**
|
|
18
21
|
* 获取 ERC20 代币 → 原生代币的报价
|
|
@@ -38,6 +41,12 @@ async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chainI
|
|
|
38
41
|
const amounts = await router.getAmountsOut(tokenAmount, [tokenAddress, MONAD_WMON]);
|
|
39
42
|
return amounts[1];
|
|
40
43
|
}
|
|
44
|
+
if (chainId === 196) {
|
|
45
|
+
// ✅ XLAYER: 使用 PotatoSwap V2 Router(USDT0 → WOKB)
|
|
46
|
+
const router = new Contract(XLAYER_POTATOSWAP_V2_ROUTER, ROUTER_ABI, provider);
|
|
47
|
+
const amounts = await router.getAmountsOut(tokenAmount, [tokenAddress, XLAYER_WOKB]);
|
|
48
|
+
return amounts[1];
|
|
49
|
+
}
|
|
41
50
|
// 其他链暂不支持报价,返回 0
|
|
42
51
|
return 0n;
|
|
43
52
|
}
|
|
@@ -171,15 +180,17 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
171
180
|
const useNativeToken = inputToken === ZERO_ADDRESS;
|
|
172
181
|
// ✅ 如果使用非原生代币(如 USDC),需要根据精度转换金额
|
|
173
182
|
let adjustedFundsList = fundsList;
|
|
183
|
+
let profitTokenAmount = totalProfit;
|
|
174
184
|
if (!useNativeToken && params.quoteTokenDecimals !== undefined && params.quoteTokenDecimals !== 18) {
|
|
175
185
|
const decimalsDiff = 18 - params.quoteTokenDecimals;
|
|
176
186
|
const divisor = BigInt(10 ** decimalsDiff);
|
|
177
187
|
adjustedFundsList = fundsList.map(amount => amount / divisor);
|
|
188
|
+
profitTokenAmount = totalProfit / divisor;
|
|
178
189
|
}
|
|
179
190
|
// ✅ 优化:并行获取 unsignedBuys 和 buyerNonces
|
|
180
191
|
const [unsignedBuys, buyerNonces] = await Promise.all([
|
|
181
192
|
populateBuyTransactionsWithQuote(buyers, portalAddr, tokenAddress, adjustedFundsList, inputToken, useNativeToken),
|
|
182
|
-
allocateBuyerNonces(buyers, extractProfit, maxFundsIndex, totalProfit, nonceManager)
|
|
193
|
+
allocateBuyerNonces(buyers, extractProfit, maxFundsIndex, useNativeToken ? totalProfit : profitTokenAmount, nonceManager)
|
|
183
194
|
]);
|
|
184
195
|
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
185
196
|
const bribeAmount = getBribeAmount(config);
|
|
@@ -214,21 +225,27 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
214
225
|
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
215
226
|
const profitTxs = [];
|
|
216
227
|
let profitHopWallets;
|
|
217
|
-
if (extractProfit &&
|
|
228
|
+
if (extractProfit && maxFundsIndex >= 0) {
|
|
218
229
|
const profitNonce = buyerNonces[maxFundsIndex] + 1;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
230
|
+
// ✅ 原生币:直接刮原生币;ERC20:先换算成原生币再刮(xLayer: USDT0→OKB)
|
|
231
|
+
const nativeProfitAmount = useNativeToken
|
|
232
|
+
? totalProfit
|
|
233
|
+
: await getTokenToNativeQuote(provider, inputToken, profitTokenAmount, chainId);
|
|
234
|
+
if (nativeProfitAmount > 0n) {
|
|
235
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
236
|
+
provider,
|
|
237
|
+
payerWallet: buyers[maxFundsIndex],
|
|
238
|
+
profitAmount: nativeProfitAmount,
|
|
239
|
+
profitRecipient: getProfitRecipient(),
|
|
240
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
241
|
+
gasPrice,
|
|
242
|
+
chainId,
|
|
243
|
+
txType: getTxType(config),
|
|
244
|
+
startNonce: profitNonce
|
|
245
|
+
});
|
|
246
|
+
profitTxs.push(...profitHopResult.signedTransactions);
|
|
247
|
+
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
248
|
+
}
|
|
232
249
|
}
|
|
233
250
|
nonceManager.clearTemp();
|
|
234
251
|
// ✅ 组装顺序:贿赂 → 创建代币 → 买入 → 利润多跳
|
|
@@ -264,6 +281,7 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
264
281
|
// ✅ 如果使用非原生代币(如 USDC),需要根据精度转换金额
|
|
265
282
|
// buyAmounts 是按 18 位精度传入的,需要转换为目标代币精度
|
|
266
283
|
let adjustedFundsList = fundsList;
|
|
284
|
+
let profitTokenAmount = totalProfit;
|
|
267
285
|
if (!useNativeToken && quoteTokenDecimals !== undefined && quoteTokenDecimals !== 18) {
|
|
268
286
|
// 从 18 位精度转换为目标精度
|
|
269
287
|
// 例如:0.1 ETH (18位) = 100000000000000000 wei
|
|
@@ -271,12 +289,13 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
271
289
|
const decimalsDiff = 18 - quoteTokenDecimals;
|
|
272
290
|
const divisor = BigInt(10 ** decimalsDiff);
|
|
273
291
|
adjustedFundsList = fundsList.map(amount => amount / divisor);
|
|
292
|
+
profitTokenAmount = totalProfit / divisor;
|
|
274
293
|
}
|
|
275
294
|
// ✅ ERC20 购买:获取代币利润等值的原生代币(BNB)报价
|
|
276
295
|
let nativeProfitAmount = totalProfit; // 原生代币购买时直接使用
|
|
277
296
|
if (!useNativeToken && extractProfit && totalProfit > 0n) {
|
|
278
|
-
//
|
|
279
|
-
nativeProfitAmount = await getTokenToNativeQuote(provider, inputToken,
|
|
297
|
+
// 将代币利润转换为等值原生代币(BNB/OKB/MON)
|
|
298
|
+
nativeProfitAmount = await getTokenToNativeQuote(provider, inputToken, profitTokenAmount, chainId);
|
|
280
299
|
}
|
|
281
300
|
// ✅ 优化:如果前端传入了 nonces,需要调整以避免利润交易 nonce 冲突
|
|
282
301
|
const presetNonces = config.nonces;
|
package/dist/utils/constants.js
CHANGED
|
@@ -98,6 +98,7 @@ export const ADDRESSES = {
|
|
|
98
98
|
USDT: '0x1e4a5963abfd975d8c9021ce480b42188849d41d', // 6位精度(不一定被 Flap 内盘支持)
|
|
99
99
|
USDC: '0x74b7f16337b8972027f6196a17a631ac6de26d22', // 6位精度(不一定被 Flap 内盘支持)
|
|
100
100
|
USDT0: '0x779ded0c9e1022225f8e0630b35a9b54be713736', // 6位精度(USD₮0)
|
|
101
|
+
// ✅ USD₮0(Tether USD0)
|
|
101
102
|
},
|
|
102
103
|
MORPH: {
|
|
103
104
|
// Flap Portal
|
package/dist/utils/erc20.js
CHANGED
|
@@ -2,6 +2,11 @@ import { Contract, Wallet, JsonRpcProvider, Interface, parseUnits } from 'ethers
|
|
|
2
2
|
import { ADDRESSES, ZERO_ADDRESS } from './constants.js';
|
|
3
3
|
import { NonceManager } from './bundle-helpers.js';
|
|
4
4
|
import { ERC20_ABI, MULTICALL3_ABI } from '../abis/common.js';
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// ✅ Max approval(与 BSC 策略一致:阈值判断,避免频繁重复授权)
|
|
7
|
+
// ============================================================================
|
|
8
|
+
const MAX_UINT256 = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
|
9
|
+
const MAX_APPROVAL_THRESHOLD = MAX_UINT256 / 2n;
|
|
5
10
|
/**
|
|
6
11
|
* 验证合约地址是否有效(是否部署了代码)
|
|
7
12
|
*/
|
|
@@ -408,7 +413,8 @@ export async function approveTokenRaw(rpcUrl, privateKey, tokenAddress, spenderA
|
|
|
408
413
|
await validateContractAddress(provider, normalizedToken, 'Token');
|
|
409
414
|
await validateContractAddress(provider, normalizedSpender, 'Spender');
|
|
410
415
|
const erc20 = new Contract(normalizedToken, ERC20_ABI, signer);
|
|
411
|
-
const
|
|
416
|
+
const isMax = amount === 'max';
|
|
417
|
+
const requiredAmount = isMax ? MAX_UINT256 : amount;
|
|
412
418
|
// 检查当前授权额度
|
|
413
419
|
let currentAllowance;
|
|
414
420
|
try {
|
|
@@ -418,7 +424,8 @@ export async function approveTokenRaw(rpcUrl, privateKey, tokenAddress, spenderA
|
|
|
418
424
|
throw new Error(`❌ 查询授权额度失败: ${error.message}`);
|
|
419
425
|
}
|
|
420
426
|
// 如果已经授权足够,直接返回
|
|
421
|
-
|
|
427
|
+
// ✅ max 授权:使用阈值判断,避免每次消耗一点 allowance 都重新授权
|
|
428
|
+
if (isMax ? (currentAllowance >= MAX_APPROVAL_THRESHOLD) : (currentAllowance >= requiredAmount)) {
|
|
422
429
|
return {
|
|
423
430
|
alreadyApproved: true,
|
|
424
431
|
currentAllowance,
|
|
@@ -495,8 +502,9 @@ export async function approveTokenBatchRaw(params) {
|
|
|
495
502
|
// ✅ 优化:批量创建钱包和合约实例
|
|
496
503
|
const wallets = privateKeys.map(key => new Wallet(key, provider));
|
|
497
504
|
const ownerAddresses = wallets.map(w => w.address);
|
|
505
|
+
const isMaxApprovals = amounts.map(a => a === 'max');
|
|
498
506
|
const requiredAmounts = amounts.map(amount => amount === 'max'
|
|
499
|
-
?
|
|
507
|
+
? MAX_UINT256
|
|
500
508
|
: amount);
|
|
501
509
|
// ==================== signOnly=true:只签名不提交 ====================
|
|
502
510
|
if (signOnly) {
|
|
@@ -517,8 +525,10 @@ export async function approveTokenBatchRaw(params) {
|
|
|
517
525
|
const ownerAddress = ownerAddresses[i];
|
|
518
526
|
const currentAllowance = currentAllowances[i];
|
|
519
527
|
const requiredAmount = requiredAmounts[i];
|
|
528
|
+
const isMax = isMaxApprovals[i];
|
|
520
529
|
// 如果已经授权足够,跳过
|
|
521
|
-
|
|
530
|
+
// ✅ max 授权:使用阈值判断(与 BSC 一致)
|
|
531
|
+
if (isMax ? (currentAllowance >= MAX_APPROVAL_THRESHOLD) : (currentAllowance >= requiredAmount)) {
|
|
522
532
|
return {
|
|
523
533
|
owner: ownerAddress,
|
|
524
534
|
alreadyApproved: true,
|
|
@@ -568,9 +578,11 @@ export async function approveTokenBatchRaw(params) {
|
|
|
568
578
|
const ownerAddress = ownerAddresses[i];
|
|
569
579
|
const currentAllowance = currentAllowances[i];
|
|
570
580
|
const requiredAmount = requiredAmounts[i];
|
|
581
|
+
const isMax = isMaxApprovals[i];
|
|
571
582
|
try {
|
|
572
583
|
// 如果已经授权足够,跳过
|
|
573
|
-
|
|
584
|
+
// ✅ max 授权:使用阈值判断(与 BSC 一致)
|
|
585
|
+
if (isMax ? (currentAllowance >= MAX_APPROVAL_THRESHOLD) : (currentAllowance >= requiredAmount)) {
|
|
574
586
|
return {
|
|
575
587
|
owner: ownerAddress,
|
|
576
588
|
alreadyApproved: true,
|
|
@@ -27,7 +27,7 @@ export declare const QUOTE_CONFIG: {
|
|
|
27
27
|
readonly v2Router: "0x881fb2f98c13d521009464e7d1cbf16e1b394e8e";
|
|
28
28
|
readonly v3Quoter: "";
|
|
29
29
|
readonly wrappedNative: "0xe538905cf8410324e03a5a23c1c177a474d59b2b";
|
|
30
|
-
readonly stableCoins: readonly ["0x1E4a5963aBFD975d8c9021ce480b42188849D41d"];
|
|
30
|
+
readonly stableCoins: readonly ["0x1E4a5963aBFD975d8c9021ce480b42188849D41d", "0x74b7f16337b8972027f6196a17a631ac6de26d22", "0x779ded0c9e1022225f8e0630b35a9b54be713736"];
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
export type SupportedChain = keyof typeof QUOTE_CONFIG;
|
|
@@ -39,6 +39,8 @@ export const QUOTE_CONFIG = {
|
|
|
39
39
|
wrappedNative: '0xe538905cf8410324e03a5a23c1c177a474d59b2b', // WOKB
|
|
40
40
|
stableCoins: [
|
|
41
41
|
'0x1E4a5963aBFD975d8c9021ce480b42188849D41d', // USDT
|
|
42
|
+
'0x74b7f16337b8972027f6196a17a631ac6de26d22', // USDC
|
|
43
|
+
'0x779ded0c9e1022225f8e0630b35a9b54be713736', // USD₮0
|
|
42
44
|
],
|
|
43
45
|
},
|
|
44
46
|
};
|