four-flap-meme-sdk 1.2.1 → 1.2.2
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.
|
@@ -5,6 +5,7 @@ import { ADDRESSES } from '../../utils/constants.js';
|
|
|
5
5
|
import { FourClient, buildLoginMessage } from '../../clients/four.js';
|
|
6
6
|
import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit } from './config.js';
|
|
7
7
|
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
8
|
+
import { trySell } from '../tm.js';
|
|
8
9
|
// ==================== TokenManager2 ABI(仅需要的方法)====================
|
|
9
10
|
const TM2_ABI = [
|
|
10
11
|
'function createToken(bytes args, bytes signature) payable',
|
|
@@ -322,24 +323,34 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
322
323
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
323
324
|
const sellers = privateKeys.map((k) => new Wallet(k, provider));
|
|
324
325
|
const amountsWei = sellAmounts.map((a) => ethers.parseUnits(a, 18));
|
|
325
|
-
// ⚠️ Four.meme 内盘代币通过 TokenManager bonding curve
|
|
326
|
+
// ⚠️ Four.meme 内盘代币通过 TokenManager bonding curve 定价
|
|
327
|
+
// ✅ 优先使用用户提供的 minOutputAmounts,否则自动调用 trySell 获取
|
|
326
328
|
let minOuts;
|
|
327
329
|
let quotedOutputs = [];
|
|
328
330
|
if (minOutputAmounts && minOutputAmounts.length === sellers.length) {
|
|
329
331
|
// 用户提供了 minOutputAmounts,用于滑点保护和利润计算
|
|
330
332
|
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
331
|
-
// 反推实际收益(假设 minOut 是 95% 的收益)
|
|
332
333
|
quotedOutputs = minOuts.map(m => m * 100n / 95n);
|
|
333
334
|
}
|
|
334
335
|
else {
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
336
|
+
// ✅ 自动调用 trySell 获取每个钱包的预期收益
|
|
337
|
+
console.log('💰 自动获取卖出报价...');
|
|
338
|
+
const rpcUrl = config.customRpcUrl;
|
|
339
|
+
quotedOutputs = await Promise.all(amountsWei.map(async (amount, i) => {
|
|
340
|
+
try {
|
|
341
|
+
const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
|
|
342
|
+
const quoted = result.funds; // 预期收益
|
|
343
|
+
console.log(` 钱包 ${i}: 卖出 ${ethers.formatUnits(amount, 18)} 代币 → 预期收益 ${ethers.formatEther(quoted)} BNB`);
|
|
344
|
+
return quoted;
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
console.log(` ⚠️ 钱包 ${i}: 报价失败,使用 minOut = 0`);
|
|
348
|
+
return 0n;
|
|
349
|
+
}
|
|
350
|
+
}));
|
|
351
|
+
// minOuts 设为预期收益的 95%(作为滑点保护)
|
|
352
|
+
minOuts = quotedOutputs.map(q => q * 95n / 100n);
|
|
353
|
+
console.log('');
|
|
343
354
|
}
|
|
344
355
|
// ✅ Step 1: 检查代币余额和授权状态
|
|
345
356
|
const ERC20_ABI = [
|
|
@@ -4,6 +4,7 @@ import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.j
|
|
|
4
4
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
5
5
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit } from './config.js';
|
|
6
6
|
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
7
|
+
import { trySell } from '../tm.js';
|
|
7
8
|
// ==================== TokenManager2 ABI(仅需要的方法)====================
|
|
8
9
|
const TM2_ABI = [
|
|
9
10
|
'function buyTokenAMAP(uint256 origin, address token, address to, uint256 funds, uint256 minAmount) payable',
|
|
@@ -229,16 +230,6 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
229
230
|
if (privateKeys.length !== amounts.length) {
|
|
230
231
|
throw new Error('privateKeys and amounts length mismatch');
|
|
231
232
|
}
|
|
232
|
-
// ⚠️ 友好提示:利润提取需要 minFundsEach
|
|
233
|
-
if (!minFundsEach && shouldExtractProfit(config)) {
|
|
234
|
-
console.log('\n⚠️ 重要提示:未传入 minFundsEach 参数,将无法提取利润!');
|
|
235
|
-
console.log(' Four.meme 内盘代币无法自动报价,需要手动估算预期收益');
|
|
236
|
-
console.log(' 建议步骤:');
|
|
237
|
-
console.log(' 1. 在 four.meme 网站查看卖出预期收益');
|
|
238
|
-
console.log(' 2. 传入 minFundsEach 参数');
|
|
239
|
-
console.log(' 示例: minFundsEach: 0.1 (预计每个钱包收到 0.1 BNB)');
|
|
240
|
-
console.log(' 如果只是测试卖出,可以忽略此提示\n');
|
|
241
|
-
}
|
|
242
233
|
const merkle = new MerkleClient({
|
|
243
234
|
apiKey: config.apiKey,
|
|
244
235
|
chainId: 56,
|
|
@@ -248,12 +239,40 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
248
239
|
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
249
240
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
250
241
|
const gasMultiplier = config.gasLimitMultiplier ?? 1.0;
|
|
251
|
-
const minOut = minFundsEach ?? 0n;
|
|
252
242
|
const sellGasLimit = BigInt(Math.ceil(800000 * gasMultiplier));
|
|
253
243
|
const signedTxs = [];
|
|
254
244
|
const nonceManager = new NonceManager(provider);
|
|
255
245
|
const wallets = privateKeys.map((k) => new Wallet(k, provider));
|
|
256
246
|
const amountsWei = amounts.map((a) => ethers.parseUnits(a, 18));
|
|
247
|
+
// ✅ 自动获取每个钱包的预期收益(用于利润计算和滑点保护)
|
|
248
|
+
let quotedOutputs;
|
|
249
|
+
let minOuts;
|
|
250
|
+
if (minFundsEach !== undefined) {
|
|
251
|
+
// 用户提供了 minFundsEach,所有钱包使用相同的 minOut
|
|
252
|
+
const minOutWei = typeof minFundsEach === 'string' ? ethers.parseEther(minFundsEach) : minFundsEach;
|
|
253
|
+
minOuts = new Array(wallets.length).fill(minOutWei);
|
|
254
|
+
quotedOutputs = minOuts.map(m => m * 100n / 95n);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// ✅ 自动调用 trySell 获取每个钱包的预期收益
|
|
258
|
+
console.log('💰 自动获取卖出报价...');
|
|
259
|
+
const rpcUrl = config.customRpcUrl;
|
|
260
|
+
quotedOutputs = await Promise.all(amountsWei.map(async (amount, i) => {
|
|
261
|
+
try {
|
|
262
|
+
const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
|
|
263
|
+
const quoted = result.funds;
|
|
264
|
+
console.log(` 钱包 ${i}: 卖出 ${ethers.formatUnits(amount, 18)} 代币 → 预期收益 ${ethers.formatEther(quoted)} BNB`);
|
|
265
|
+
return quoted;
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
console.log(` ⚠️ 钱包 ${i}: 报价失败,使用 minOut = 0`);
|
|
269
|
+
return 0n;
|
|
270
|
+
}
|
|
271
|
+
}));
|
|
272
|
+
// minOuts 设为预期收益的 95%(作为滑点保护)
|
|
273
|
+
minOuts = quotedOutputs.map(q => q * 95n / 100n);
|
|
274
|
+
console.log('');
|
|
275
|
+
}
|
|
257
276
|
// ✅ Step 0: 检查代币余额
|
|
258
277
|
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
259
278
|
const balances = await Promise.all(wallets.map(w => tokenContract.balanceOf(w.address)));
|
|
@@ -288,7 +307,7 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
288
307
|
}
|
|
289
308
|
// ✅ Step 3: 构建卖出交易
|
|
290
309
|
const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
|
|
291
|
-
const sellUnsigned = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i],
|
|
310
|
+
const sellUnsigned = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], minOuts[i])));
|
|
292
311
|
const sellGasLimits = new Array(wallets.length).fill(sellGasLimit);
|
|
293
312
|
const sellNonces = await Promise.all(wallets.map((w) => nonceManager.getNextNonce(w)));
|
|
294
313
|
const signedSells = await Promise.all(sellUnsigned.map((unsigned, i) => wallets[i].signTransaction({
|
|
@@ -301,22 +320,26 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
301
320
|
type: getTxType(config)
|
|
302
321
|
})));
|
|
303
322
|
signedTxs.push(...signedSells);
|
|
304
|
-
// ✅
|
|
323
|
+
// ✅ 提取利润(基于每个钱包的预期收益)
|
|
305
324
|
const extractProfit = shouldExtractProfit(config);
|
|
306
|
-
if (extractProfit
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
325
|
+
if (extractProfit) {
|
|
326
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
327
|
+
if (quotedOutputs[i] > 0n) {
|
|
328
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
329
|
+
if (profit > 0n) {
|
|
330
|
+
const profitNonce = await nonceManager.getNextNonce(wallets[i]);
|
|
331
|
+
const profitTx = await wallets[i].signTransaction({
|
|
332
|
+
to: config.profitRecipient,
|
|
333
|
+
value: profit,
|
|
334
|
+
nonce: profitNonce,
|
|
335
|
+
gasPrice,
|
|
336
|
+
gasLimit: 21000n,
|
|
337
|
+
chainId: 56,
|
|
338
|
+
type: getTxType(config)
|
|
339
|
+
});
|
|
340
|
+
signedTxs.push(profitTx);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
320
343
|
}
|
|
321
344
|
}
|
|
322
345
|
nonceManager.clearTemp();
|