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.
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +23 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/utils/erc20.d.ts +61 -0
- package/dist/utils/erc20.js +157 -0
- package/package.json +1 -1
|
@@ -369,7 +369,10 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
369
369
|
routeType,
|
|
370
370
|
amountsWei
|
|
371
371
|
});
|
|
372
|
-
|
|
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:
|
|
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
|
-
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
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
|
|
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';
|
package/dist/utils/erc20.d.ts
CHANGED
|
@@ -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>;
|
package/dist/utils/erc20.js
CHANGED
|
@@ -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
|
+
}
|