four-flap-meme-sdk 1.4.53 → 1.4.55
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/clients/four.js +1 -1
- package/dist/contracts/tm-bundle-merkle/swap-internal.d.ts +1 -1
- package/dist/contracts/tm-bundle-merkle/swap-internal.js +2 -1
- package/dist/contracts/tm-bundle-merkle/utils.js +8 -2
- package/dist/contracts/tm-bundle.js +7 -7
- package/dist/flap/portal-bundle-merkle/utils.js +6 -2
- package/dist/utils/constants.d.ts +0 -2
- package/dist/utils/constants.js +0 -3
- package/dist/utils/erc20.js +1 -1
- package/dist/utils/holders-maker.js +42 -26
- package/package.json +1 -1
package/dist/clients/four.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export class FourClient {
|
|
2
2
|
constructor(cfg) {
|
|
3
3
|
// 默认使用 Cloudflare Workers 代理,已配置 CORS,浏览器可直接使用
|
|
4
|
-
this.baseUrl = cfg?.baseUrl || 'https://
|
|
4
|
+
this.baseUrl = cfg?.baseUrl || 'https://bscfourapi.emit.tools';
|
|
5
5
|
}
|
|
6
6
|
async generateNonce(req) {
|
|
7
7
|
const r = await fetch(`${this.baseUrl}/v1/private/user/nonce/generate`, {
|
|
@@ -14,4 +14,5 @@ export const HELPER3_ABI = [
|
|
|
14
14
|
'function tryBuy(address token, uint256 amount, uint256 funds) view returns (address tokenManager, address quote, uint256 estimatedAmount, uint256 estimatedCost, uint256 estimatedFee, uint256 amountMsgValue, uint256 amountApproval, uint256 amountFunds)',
|
|
15
15
|
'function trySell(address token, uint256 amount) view returns (address tokenManager, address quote, uint256 funds, uint256 fee)'
|
|
16
16
|
];
|
|
17
|
-
|
|
17
|
+
// ✅ Four.meme 使用 TokenManagerOriginal(代理合约已移除)
|
|
18
|
+
export const TM_ADDRESS = ADDRESSES.BSC.TokenManagerOriginal;
|
|
@@ -700,7 +700,10 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
700
700
|
_batchGetBalances(provider, addresses)
|
|
701
701
|
]);
|
|
702
702
|
const gasCostBase = nativeGasLimit * gasPrice;
|
|
703
|
-
|
|
703
|
+
// ✅ 修复:利润多跳需要 PROFIT_HOP_COUNT + 1 笔交易的 gas(payer → hop1 → hop2 → 利润地址)
|
|
704
|
+
// ✅ 统一使用 21055n 作为利润多跳的 gas limit
|
|
705
|
+
const PROFIT_HOP_GAS_LIMIT = 21055n;
|
|
706
|
+
const profitTxGas = PROFIT_HOP_GAS_LIMIT * gasPrice * BigInt(PROFIT_HOP_COUNT + 1);
|
|
704
707
|
// ✅ 第一步:计算所有钱包的归集金额和利润,找出归集金额最大的钱包
|
|
705
708
|
const sweepAmounts = [];
|
|
706
709
|
let maxSweepIndex = -1;
|
|
@@ -843,7 +846,10 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
843
846
|
config.checkBnbForErc20NoHop ? _batchGetBalances(provider, addresses) : Promise.resolve([])
|
|
844
847
|
]);
|
|
845
848
|
const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
846
|
-
|
|
849
|
+
// ✅ 修复:利润多跳需要 PROFIT_HOP_COUNT + 1 笔交易的 gas
|
|
850
|
+
// ✅ 统一使用 21055n 作为利润多跳的 gas limit
|
|
851
|
+
const PROFIT_HOP_GAS_LIMIT_ERC20 = 21055n;
|
|
852
|
+
const profitTxGas = PROFIT_HOP_GAS_LIMIT_ERC20 * gasPrice * BigInt(PROFIT_HOP_COUNT + 1);
|
|
847
853
|
// ✅ 第一步:计算所有钱包的归集金额和利润,找出归集金额最大的钱包
|
|
848
854
|
const sweepAmounts = [];
|
|
849
855
|
let maxSweepIndex = -1;
|
|
@@ -176,7 +176,7 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
176
176
|
clickFun: false,
|
|
177
177
|
});
|
|
178
178
|
// 4. 构建交易
|
|
179
|
-
const tmAddr = ADDRESSES.BSC.
|
|
179
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
180
180
|
// ✅ 获取 gas price
|
|
181
181
|
let gasPrice;
|
|
182
182
|
if (club48) {
|
|
@@ -403,7 +403,7 @@ export async function batchBuyWithBundle(params) {
|
|
|
403
403
|
explorerEndpoint: config.club48ExplorerEndpoint
|
|
404
404
|
});
|
|
405
405
|
const blockOffset = config.bundleBlockOffset ?? 100;
|
|
406
|
-
const tmAddr = ADDRESSES.BSC.
|
|
406
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
407
407
|
const gasPrice = await club48.getMinGasPrice();
|
|
408
408
|
const signedTxs = [];
|
|
409
409
|
const nextNonceMap = new Map();
|
|
@@ -535,7 +535,7 @@ export async function batchSellWithBundle(params) {
|
|
|
535
535
|
explorerEndpoint: config.club48ExplorerEndpoint
|
|
536
536
|
});
|
|
537
537
|
const blockOffset = config.bundleBlockOffset ?? 100;
|
|
538
|
-
const tmAddr = ADDRESSES.BSC.
|
|
538
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
539
539
|
const gasPrice = await club48.getMinGasPrice();
|
|
540
540
|
const signedTxs = [];
|
|
541
541
|
const nextNonceMap = new Map();
|
|
@@ -666,7 +666,7 @@ export async function fourPrivateBuy(params) {
|
|
|
666
666
|
const provider = new JsonRpcProvider(rpcUrl);
|
|
667
667
|
const wallet = new Wallet(privateKey, provider);
|
|
668
668
|
const club48 = new Club48Client({ endpoint: club48Endpoint, explorerEndpoint: club48ExplorerEndpoint });
|
|
669
|
-
const tmAddr = ADDRESSES.BSC.
|
|
669
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
670
670
|
const gasPrice = await club48.getMinGasPrice();
|
|
671
671
|
const fundsWei = ethers.parseEther(funds);
|
|
672
672
|
const minAmount = 0n;
|
|
@@ -686,7 +686,7 @@ export async function fourPrivateSell(params) {
|
|
|
686
686
|
const provider = new JsonRpcProvider(rpcUrl);
|
|
687
687
|
const wallet = new Wallet(privateKey, provider);
|
|
688
688
|
const club48 = new Club48Client({ endpoint: club48Endpoint, explorerEndpoint: club48ExplorerEndpoint });
|
|
689
|
-
const tmAddr = ADDRESSES.BSC.
|
|
689
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
690
690
|
const gasPrice = await club48.getMinGasPrice();
|
|
691
691
|
const amountWei = ethers.parseUnits(amount, 18);
|
|
692
692
|
const minOut = minFunds ?? 0n;
|
|
@@ -707,7 +707,7 @@ export async function fourBatchPrivateBuy(params) {
|
|
|
707
707
|
throw new Error('privateKeys and fundsList length mismatch');
|
|
708
708
|
const provider = new JsonRpcProvider(rpcUrl);
|
|
709
709
|
const club48 = new Club48Client({ endpoint: club48Endpoint, explorerEndpoint: club48ExplorerEndpoint });
|
|
710
|
-
const tmAddr = ADDRESSES.BSC.
|
|
710
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
711
711
|
// ✅ 并行处理
|
|
712
712
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
713
713
|
const fundsWeiList = fundsList.map(f => ethers.parseEther(f));
|
|
@@ -757,7 +757,7 @@ export async function fourBatchPrivateSell(params) {
|
|
|
757
757
|
throw new Error('privateKeys and amounts length mismatch');
|
|
758
758
|
const provider = new JsonRpcProvider(rpcUrl);
|
|
759
759
|
const club48 = new Club48Client({ endpoint: club48Endpoint, explorerEndpoint: club48ExplorerEndpoint });
|
|
760
|
-
const tmAddr = ADDRESSES.BSC.
|
|
760
|
+
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
761
761
|
const minOut = minFundsEach ?? 0n;
|
|
762
762
|
// ✅ 并行处理
|
|
763
763
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
@@ -502,7 +502,9 @@ export async function flapSweepWithBundleMerkle(params) {
|
|
|
502
502
|
batchGetBalances(provider, addresses)
|
|
503
503
|
]);
|
|
504
504
|
const gasCostBase = nativeGasLimit * gasPrice;
|
|
505
|
-
|
|
505
|
+
// ✅ 统一使用 21055n 作为利润多跳的 gas limit,需要 PROFIT_HOP_COUNT + 1 笔交易
|
|
506
|
+
const PROFIT_HOP_GAS_LIMIT = 21055n;
|
|
507
|
+
const profitTxGas = PROFIT_HOP_GAS_LIMIT * gasPrice * BigInt(PROFIT_HOP_COUNT + 1);
|
|
506
508
|
// 第一步:计算所有钱包的归集金额,找出余额最大的钱包
|
|
507
509
|
const sweepAmounts = [];
|
|
508
510
|
let maxSweepIndex = -1;
|
|
@@ -633,7 +635,9 @@ export async function flapSweepWithBundleMerkle(params) {
|
|
|
633
635
|
batchGetBalances(provider, addresses)
|
|
634
636
|
]);
|
|
635
637
|
const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
636
|
-
|
|
638
|
+
// ✅ 统一使用 21055n 作为利润多跳的 gas limit,需要 PROFIT_HOP_COUNT + 1 笔交易
|
|
639
|
+
const PROFIT_HOP_GAS_LIMIT_ERC20 = 21055n;
|
|
640
|
+
const profitTxGas = PROFIT_HOP_GAS_LIMIT_ERC20 * gasPrice * BigInt(PROFIT_HOP_COUNT + 1);
|
|
637
641
|
const sweepAmounts = [];
|
|
638
642
|
let maxSweepIndex = -1;
|
|
639
643
|
let maxSweepAmount = 0n;
|
|
@@ -55,8 +55,6 @@ export declare const ADDRESSES: {
|
|
|
55
55
|
readonly USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d";
|
|
56
56
|
readonly BUSD: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56";
|
|
57
57
|
readonly TokenManagerOriginal: "0x5c952063c7fc8610FFDB798152D69F0B9550762b";
|
|
58
|
-
readonly TokenManagerV1Proxy: "0xf7F823d0E790219dBf727bDb971837574655fCB0";
|
|
59
|
-
readonly TokenManagerV2Proxy: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
|
|
60
58
|
readonly TokenManagerV1: "0xf7F823d0E790219dBf727bDb971837574655fCB0";
|
|
61
59
|
readonly TokenManagerV2: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
|
|
62
60
|
readonly TokenManagerHelper3: "0xF251F83e40a78868FcfA3FA4599Dad6494E46034";
|
package/dist/utils/constants.js
CHANGED
|
@@ -51,9 +51,6 @@ export const ADDRESSES = {
|
|
|
51
51
|
// ========== Four.meme 合约 ==========
|
|
52
52
|
// 原始合约(TokenManager2,创建代币专用,不收费)
|
|
53
53
|
TokenManagerOriginal: '0x5c952063c7fc8610FFDB798152D69F0B9550762b',
|
|
54
|
-
// 代理合约(收费版,仅交易)
|
|
55
|
-
TokenManagerV1Proxy: '0xf7F823d0E790219dBf727bDb971837574655fCB0',
|
|
56
|
-
TokenManagerV2Proxy: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
|
|
57
54
|
// ✅ 向后兼容别名(保持旧代码可用)
|
|
58
55
|
TokenManagerV1: '0xf7F823d0E790219dBf727bDb971837574655fCB0',
|
|
59
56
|
TokenManagerV2: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
|
package/dist/utils/erc20.js
CHANGED
|
@@ -299,7 +299,7 @@ function resolveSpenderAddress(chain, platform) {
|
|
|
299
299
|
MONAD: ADDRESSES.MONAD.FlapPortal, // ✅ Monad Flap Portal
|
|
300
300
|
},
|
|
301
301
|
four: {
|
|
302
|
-
// Four.meme 使用 TokenManagerOriginal
|
|
302
|
+
// ✅ Four.meme 使用 TokenManagerOriginal(代理合约已移除)
|
|
303
303
|
BSC: ADDRESSES.BSC.TokenManagerOriginal,
|
|
304
304
|
BASE: ADDRESSES.BASE.TokenManagerHelper3,
|
|
305
305
|
XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持 Four.meme
|
|
@@ -264,26 +264,35 @@ async function buildV2BuyTx(wallet, tokenAddress, buyAmount, nonce, gasPrice, ga
|
|
|
264
264
|
/**
|
|
265
265
|
* 构建 V3 单跳买入交易
|
|
266
266
|
* BNB → Token (使用 exactInputSingle + multicall)
|
|
267
|
+
*
|
|
268
|
+
* ✅ 修复:使用 ethers.Interface 手动编码 calldata,避免 multicall 重载问题
|
|
267
269
|
*/
|
|
268
270
|
async function buildV3BuyTx(wallet, tokenAddress, buyAmount, nonce, gasPrice, gasLimit, chainId, txType, v3Fee = 2500 // 默认 0.25% 手续费
|
|
269
271
|
) {
|
|
270
272
|
const deadline = getDeadline();
|
|
271
273
|
const v3RouterIface = new ethers.Interface(V3_ROUTER02_ABI);
|
|
272
274
|
// 构建 exactInputSingle calldata
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
275
|
+
const swapParams = {
|
|
276
|
+
tokenIn: WBNB_ADDRESS,
|
|
277
|
+
tokenOut: tokenAddress,
|
|
278
|
+
fee: v3Fee,
|
|
279
|
+
recipient: wallet.address,
|
|
280
|
+
amountIn: buyAmount,
|
|
281
|
+
amountOutMinimum: 0n,
|
|
282
|
+
sqrtPriceLimitX96: 0n
|
|
283
|
+
};
|
|
284
|
+
const exactInputSingleData = v3RouterIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
285
|
+
// ✅ 添加 refundETH,退还多余的 ETH
|
|
286
|
+
const refundETHData = v3RouterIface.encodeFunctionData('refundETH', []);
|
|
287
|
+
// ✅ 使用明确的函数签名编码 multicall,避免重载问题
|
|
288
|
+
const multicallData = v3RouterIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
289
|
+
deadline,
|
|
290
|
+
[exactInputSingleData, refundETHData]
|
|
291
|
+
]);
|
|
285
292
|
const tx = {
|
|
286
|
-
|
|
293
|
+
to: PANCAKE_V3_ROUTER_ADDRESS,
|
|
294
|
+
data: multicallData,
|
|
295
|
+
value: buyAmount,
|
|
287
296
|
nonce,
|
|
288
297
|
gasLimit: BigInt(gasLimit),
|
|
289
298
|
chainId
|
|
@@ -331,25 +340,32 @@ async function buildV2BuyTxWithERC20(wallet, tokenAddress, baseTokenAddress, buy
|
|
|
331
340
|
/**
|
|
332
341
|
* 构建 V3 买入交易(ERC20 输入)
|
|
333
342
|
* USDT/USDC → Token (使用 exactInputSingle + multicall)
|
|
343
|
+
*
|
|
344
|
+
* ✅ 修复:使用 ethers.Interface 手动编码 calldata,避免 multicall 重载问题
|
|
334
345
|
*/
|
|
335
346
|
async function buildV3BuyTxWithERC20(wallet, tokenAddress, baseTokenAddress, buyAmount, nonce, gasPrice, gasLimit, chainId, txType, v3Fee = 2500) {
|
|
336
347
|
const deadline = getDeadline();
|
|
337
348
|
const v3RouterIface = new ethers.Interface(V3_ROUTER02_ABI);
|
|
338
349
|
// 构建 exactInputSingle calldata
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
350
|
+
const swapParams = {
|
|
351
|
+
tokenIn: baseTokenAddress,
|
|
352
|
+
tokenOut: tokenAddress,
|
|
353
|
+
fee: v3Fee,
|
|
354
|
+
recipient: wallet.address,
|
|
355
|
+
amountIn: buyAmount,
|
|
356
|
+
amountOutMinimum: 0n,
|
|
357
|
+
sqrtPriceLimitX96: 0n
|
|
358
|
+
};
|
|
359
|
+
const exactInputSingleData = v3RouterIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
360
|
+
// ✅ 使用明确的函数签名编码 multicall,避免重载问题
|
|
361
|
+
const multicallData = v3RouterIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
362
|
+
deadline,
|
|
363
|
+
[exactInputSingleData]
|
|
364
|
+
]);
|
|
351
365
|
const tx = {
|
|
352
|
-
|
|
366
|
+
to: PANCAKE_V3_ROUTER_ADDRESS,
|
|
367
|
+
data: multicallData,
|
|
368
|
+
value: 0n,
|
|
353
369
|
nonce,
|
|
354
370
|
gasLimit: BigInt(gasLimit),
|
|
355
371
|
chainId
|