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 && totalProfit > 0n && maxFundsIndex >= 0) {
228
+ if (extractProfit && maxFundsIndex >= 0) {
218
229
  const profitNonce = buyerNonces[maxFundsIndex] + 1;
219
- const profitHopResult = await buildProfitHopTransactions({
220
- provider,
221
- payerWallet: buyers[maxFundsIndex],
222
- profitAmount: totalProfit,
223
- profitRecipient: getProfitRecipient(),
224
- hopCount: PROFIT_HOP_COUNT,
225
- gasPrice,
226
- chainId,
227
- txType: getTxType(config),
228
- startNonce: profitNonce
229
- });
230
- profitTxs.push(...profitHopResult.signedTransactions);
231
- profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
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
- // 将代币利润转换为等值 BNB
279
- nativeProfitAmount = await getTokenToNativeQuote(provider, inputToken, totalProfit, chainId);
297
+ // 将代币利润转换为等值原生代币(BNB/OKB/MON)
298
+ nativeProfitAmount = await getTokenToNativeQuote(provider, inputToken, profitTokenAmount, chainId);
280
299
  }
281
300
  // ✅ 优化:如果前端传入了 nonces,需要调整以避免利润交易 nonce 冲突
282
301
  const presetNonces = config.nonces;
@@ -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
@@ -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 requiredAmount = amount === 'max' ? BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') : amount;
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
- if (currentAllowance >= requiredAmount) {
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
- ? BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
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
- if (currentAllowance >= requiredAmount) {
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
- if (currentAllowance >= requiredAmount) {
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.5.12",
3
+ "version": "1.5.14",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",