four-flap-meme-sdk 1.2.51 → 1.2.53

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.
@@ -369,7 +369,10 @@ export async function pancakeProxyBatchSellMerkle(params) {
369
369
  routeType,
370
370
  amountsWei
371
371
  });
372
- await ensureFeeBalances(provider, sellers);
372
+ const finalGasLimit = getGasLimit(config);
373
+ const extractProfit = shouldExtractProfit(config);
374
+ // 检查 BNB 余额是否足够支付 gas 费(包括可能的利润转账 gas)
375
+ await ensureFeeBalances(provider, sellers, gasPrice, finalGasLimit, extractProfit);
373
376
  const proxies = createPancakeProxies(sellers, ADDRESSES.BSC.PancakeProxy);
374
377
  const unsignedSells = await buildSellTransactions({
375
378
  routeType,
@@ -386,7 +389,7 @@ export async function pancakeProxyBatchSellMerkle(params) {
386
389
  unsignedTxs: unsignedSells,
387
390
  wallets: sellers,
388
391
  nonces,
389
- gasLimit: getGasLimit(config),
392
+ gasLimit: finalGasLimit,
390
393
  gasPrice,
391
394
  chainId,
392
395
  config
@@ -567,12 +570,25 @@ async function resolveSellOutputs({ params, provider, tokenAddress, routeType, a
567
570
  minOuts: quotedOutputs.map(q => q * 95n / 100n)
568
571
  };
569
572
  }
570
- async function ensureFeeBalances(provider, wallets) {
573
+ async function ensureFeeBalances(provider, wallets, gasPrice, gasLimit, needProfitGas = false) {
571
574
  const balances = await Promise.all(wallets.map(w => provider.getBalance(w.address)));
572
- const minRequiredBNB = ethers.parseEther('0.001');
573
- const insufficient = balances.filter(b => b < minRequiredBNB).length;
574
- if (insufficient > 0) {
575
- throw new Error(`${insufficient} 个钱包 BNB 余额不足 0.001 BNB`);
575
+ // 计算单笔卖出交易的 gas 费用
576
+ const sellGasCost = gasLimit * gasPrice;
577
+ // 利润转账的 gas 费用(如果需要)
578
+ const profitGasCost = needProfitGas ? 21000n * gasPrice : 0n;
579
+ // 总共需要的最小余额
580
+ const minRequiredBNB = sellGasCost + profitGasCost;
581
+ const insufficientWallets = [];
582
+ for (let i = 0; i < wallets.length; i++) {
583
+ // 对于可能需要支付利润的钱包(收入最高的),需要额外的 gas
584
+ // 这里保守估计,要求所有钱包都有足够的余额
585
+ const required = minRequiredBNB;
586
+ if (balances[i] < required) {
587
+ insufficientWallets.push(`钱包 ${i} (${wallets[i].address}): 需要 ${ethers.formatEther(required)} BNB,实际 ${ethers.formatEther(balances[i])} BNB`);
588
+ }
589
+ }
590
+ if (insufficientWallets.length > 0) {
591
+ throw new Error(`${insufficientWallets.length} 个钱包 BNB 余额不足:\n${insufficientWallets.join('\n')}`);
576
592
  }
577
593
  }
578
594
  async function buildSellTransactions({ routeType, params, proxies, wallets, tokenAddress, amountsWei, minOuts }) {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * as Abis from './abis/index.js';
2
2
  export { ADDRESSES, CHAIN } from './utils/constants.js';
3
3
  export { isExclusiveOnChain, isExclusiveOffChain } from './utils/mpcExclusive.js';
4
- export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, type EnsureAllowanceBatchItemResult } from './utils/erc20.js';
4
+ export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, type EnsureAllowanceBatchItemResult, type ApproveTokenBatchParams, type ApproveTokenBatchResult } from './utils/erc20.js';
5
5
  export { parseFourError, type FourErrorCode } from './utils/errors.js';
6
6
  export { getTokenManagerV1, getTokenManagerV2, getTokenManagerHelper3, getTokenManagerV1Writer, getTokenManagerV2Writer, getTokenManagerHelper3Writer, getTokenManagerAddress, type ChainName } from './utils/contract-factory.js';
7
7
  export { FourClient, buildLoginMessage, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp } from './clients/four.js';
package/dist/index.js CHANGED
@@ -9,7 +9,9 @@ ensureSellApprovalV2, checkSellApprovalV2,
9
9
  // Four.meme 通用授权(默认 V2)
10
10
  ensureSellApproval, checkSellApproval,
11
11
  // Flap Protocol 授权
12
- ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch } from './utils/erc20.js';
12
+ ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch,
13
+ // ✅ 通用授权方法(适用于 Flap、Four、V2、V3 等所有场景)
14
+ checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch } from './utils/erc20.js';
13
15
  export { parseFourError } from './utils/errors.js';
14
16
  export { getTokenManagerV1, getTokenManagerV2, getTokenManagerHelper3, getTokenManagerV1Writer, getTokenManagerV2Writer, getTokenManagerHelper3Writer, getTokenManagerAddress } from './utils/contract-factory.js';
15
17
  export { FourClient, buildLoginMessage } from './clients/four.js';
@@ -91,3 +91,64 @@ export declare function checkFlapSellApprovalBatch(chain: 'BSC' | 'BASE' | 'XLAY
91
91
  * @returns 每个地址的授权额度数组
92
92
  */
93
93
  export declare function batchCheckAllowances(provider: JsonRpcProvider, tokenAddress: string, owners: string[], spender: string): Promise<bigint[]>;
94
+ /**
95
+ * ✅ 通用方法:检查 ERC20 代币授权额度(只读,不发送交易)
96
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
97
+ *
98
+ * @param rpcUrl - RPC 节点地址
99
+ * @param tokenAddress - 代币合约地址
100
+ * @param ownerAddress - 代币持有者地址
101
+ * @param spenderAddress - 被授权的合约地址(如 Router、Portal、TokenManager 等)
102
+ * @returns 当前授权额度(bigint)
103
+ */
104
+ export declare function checkAllowance(rpcUrl: string, tokenAddress: string, ownerAddress: string, spenderAddress: string): Promise<bigint>;
105
+ /**
106
+ * ✅ 通用方法:授权 ERC20 代币给指定合约(自动检查,只在不足时才发送交易)
107
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
108
+ *
109
+ * @param rpcUrl - RPC 节点地址
110
+ * @param privateKey - 私钥
111
+ * @param tokenAddress - 代币合约地址
112
+ * @param spenderAddress - 被授权的合约地址(如 Router、Portal、TokenManager 等)
113
+ * @param amount - 授权数量(bigint),传 'max' 或 ethers.MaxUint256 表示最大授权
114
+ * @returns 授权结果
115
+ */
116
+ export declare function approveToken(rpcUrl: string, privateKey: string, tokenAddress: string, spenderAddress: string, amount: bigint | 'max'): Promise<EnsureAllowanceResult>;
117
+ /**
118
+ * ✅ 通用方法:批量检查多个钱包的授权额度(只读,不发送交易)
119
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
120
+ *
121
+ * @param rpcUrl - RPC 节点地址
122
+ * @param tokenAddress - 代币合约地址
123
+ * @param ownerAddresses - 代币持有者地址数组
124
+ * @param spenderAddress - 被授权的合约地址
125
+ * @returns 每个地址的授权额度数组
126
+ */
127
+ export declare function checkAllowanceBatch(rpcUrl: string, tokenAddress: string, ownerAddresses: string[], spenderAddress: string): Promise<bigint[]>;
128
+ export type ApproveTokenBatchParams = {
129
+ rpcUrl: string;
130
+ privateKeys: string[];
131
+ tokenAddress: string;
132
+ spenderAddress: string;
133
+ amounts: (bigint | 'max')[];
134
+ };
135
+ export type ApproveTokenBatchResult = {
136
+ success: boolean;
137
+ approvedCount: number;
138
+ results: Array<{
139
+ owner: string;
140
+ alreadyApproved: boolean;
141
+ currentAllowance: bigint;
142
+ requiredAllowance: bigint;
143
+ txHash?: string;
144
+ error?: string;
145
+ }>;
146
+ };
147
+ /**
148
+ * ✅ 通用方法:批量授权 ERC20 代币(自动检查,只在不足时才发送交易)
149
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
150
+ *
151
+ * @param params - 批量授权参数
152
+ * @returns 批量授权结果
153
+ */
154
+ export declare function approveTokenBatch(params: ApproveTokenBatchParams): Promise<ApproveTokenBatchResult>;
@@ -279,3 +279,160 @@ export async function batchCheckAllowances(provider, tokenAddress, owners, spend
279
279
  }
280
280
  });
281
281
  }
282
+ /**
283
+ * ✅ 通用方法:检查 ERC20 代币授权额度(只读,不发送交易)
284
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
285
+ *
286
+ * @param rpcUrl - RPC 节点地址
287
+ * @param tokenAddress - 代币合约地址
288
+ * @param ownerAddress - 代币持有者地址
289
+ * @param spenderAddress - 被授权的合约地址(如 Router、Portal、TokenManager 等)
290
+ * @returns 当前授权额度(bigint)
291
+ */
292
+ export async function checkAllowance(rpcUrl, tokenAddress, ownerAddress, spenderAddress) {
293
+ const provider = new JsonRpcProvider(rpcUrl);
294
+ // 验证地址
295
+ await validateContractAddress(provider, tokenAddress, 'Token');
296
+ await validateContractAddress(provider, spenderAddress, 'Spender');
297
+ const erc20 = new Contract(tokenAddress, ERC20_ABI, provider);
298
+ try {
299
+ const allowance = await erc20.allowance(ownerAddress, spenderAddress);
300
+ return allowance;
301
+ }
302
+ catch (error) {
303
+ throw new Error(`❌ 查询授权额度失败: ${error.message}`);
304
+ }
305
+ }
306
+ /**
307
+ * ✅ 通用方法:授权 ERC20 代币给指定合约(自动检查,只在不足时才发送交易)
308
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
309
+ *
310
+ * @param rpcUrl - RPC 节点地址
311
+ * @param privateKey - 私钥
312
+ * @param tokenAddress - 代币合约地址
313
+ * @param spenderAddress - 被授权的合约地址(如 Router、Portal、TokenManager 等)
314
+ * @param amount - 授权数量(bigint),传 'max' 或 ethers.MaxUint256 表示最大授权
315
+ * @returns 授权结果
316
+ */
317
+ export async function approveToken(rpcUrl, privateKey, tokenAddress, spenderAddress, amount) {
318
+ const provider = new JsonRpcProvider(rpcUrl);
319
+ const signer = new Wallet(privateKey, provider);
320
+ const ownerAddress = signer.address;
321
+ // 验证地址
322
+ await validateContractAddress(provider, tokenAddress, 'Token');
323
+ await validateContractAddress(provider, spenderAddress, 'Spender');
324
+ const erc20 = new Contract(tokenAddress, ERC20_ABI, signer);
325
+ const requiredAmount = amount === 'max' ? BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') : amount;
326
+ // 检查当前授权额度
327
+ let currentAllowance;
328
+ try {
329
+ currentAllowance = await erc20.allowance(ownerAddress, spenderAddress);
330
+ }
331
+ catch (error) {
332
+ throw new Error(`❌ 查询授权额度失败: ${error.message}`);
333
+ }
334
+ // 如果已经授权足够,直接返回
335
+ if (currentAllowance >= requiredAmount) {
336
+ return {
337
+ alreadyApproved: true,
338
+ currentAllowance,
339
+ requiredAllowance: requiredAmount
340
+ };
341
+ }
342
+ // 发送授权交易
343
+ try {
344
+ const tx = await erc20.approve(spenderAddress, requiredAmount);
345
+ const receipt = await tx.wait();
346
+ return {
347
+ alreadyApproved: false,
348
+ currentAllowance,
349
+ requiredAllowance: requiredAmount,
350
+ txReceipt: receipt
351
+ };
352
+ }
353
+ catch (error) {
354
+ throw new Error(`❌ 授权交易失败: ${error.message}`);
355
+ }
356
+ }
357
+ /**
358
+ * ✅ 通用方法:批量检查多个钱包的授权额度(只读,不发送交易)
359
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
360
+ *
361
+ * @param rpcUrl - RPC 节点地址
362
+ * @param tokenAddress - 代币合约地址
363
+ * @param ownerAddresses - 代币持有者地址数组
364
+ * @param spenderAddress - 被授权的合约地址
365
+ * @returns 每个地址的授权额度数组
366
+ */
367
+ export async function checkAllowanceBatch(rpcUrl, tokenAddress, ownerAddresses, spenderAddress) {
368
+ const provider = new JsonRpcProvider(rpcUrl);
369
+ // 验证地址
370
+ await validateContractAddress(provider, tokenAddress, 'Token');
371
+ await validateContractAddress(provider, spenderAddress, 'Spender');
372
+ return batchCheckAllowances(provider, tokenAddress, ownerAddresses, spenderAddress);
373
+ }
374
+ /**
375
+ * ✅ 通用方法:批量授权 ERC20 代币(自动检查,只在不足时才发送交易)
376
+ * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
377
+ *
378
+ * @param params - 批量授权参数
379
+ * @returns 批量授权结果
380
+ */
381
+ export async function approveTokenBatch(params) {
382
+ const { rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts } = params;
383
+ if (privateKeys.length === 0 || amounts.length !== privateKeys.length) {
384
+ throw new Error('❌ 私钥数量和授权数量必须匹配');
385
+ }
386
+ const provider = new JsonRpcProvider(rpcUrl);
387
+ // 验证地址
388
+ await validateContractAddress(provider, tokenAddress, 'Token');
389
+ await validateContractAddress(provider, spenderAddress, 'Spender');
390
+ const results = [];
391
+ let approvedCount = 0;
392
+ for (let i = 0; i < privateKeys.length; i++) {
393
+ const signer = new Wallet(privateKeys[i], provider);
394
+ const ownerAddress = signer.address;
395
+ const requiredAmount = amounts[i] === 'max'
396
+ ? BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
397
+ : amounts[i];
398
+ try {
399
+ const erc20 = new Contract(tokenAddress, ERC20_ABI, signer);
400
+ // 检查当前授权
401
+ const currentAllowance = await erc20.allowance(ownerAddress, spenderAddress);
402
+ if (currentAllowance >= requiredAmount) {
403
+ results.push({
404
+ owner: ownerAddress,
405
+ alreadyApproved: true,
406
+ currentAllowance,
407
+ requiredAllowance: requiredAmount
408
+ });
409
+ continue;
410
+ }
411
+ // 发送授权交易
412
+ const tx = await erc20.approve(spenderAddress, requiredAmount);
413
+ const receipt = await tx.wait();
414
+ results.push({
415
+ owner: ownerAddress,
416
+ alreadyApproved: false,
417
+ currentAllowance,
418
+ requiredAllowance: requiredAmount,
419
+ txHash: receipt.hash
420
+ });
421
+ approvedCount++;
422
+ }
423
+ catch (error) {
424
+ results.push({
425
+ owner: ownerAddress,
426
+ alreadyApproved: false,
427
+ currentAllowance: 0n,
428
+ requiredAllowance: requiredAmount,
429
+ error: error.message
430
+ });
431
+ }
432
+ }
433
+ return {
434
+ success: approvedCount > 0,
435
+ approvedCount,
436
+ results
437
+ };
438
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.51",
3
+ "version": "1.2.53",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",