four-flap-meme-sdk
Version:
SDK for Flap bonding curve and four.meme TokenManager
465 lines (464 loc) • 20 kB
JavaScript
import { ethers, Wallet } from 'ethers';
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
import { TM2_ABI } from '../../abis/common.js';
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount } from './config.js';
import { trySell } from '../tm.js';
const CHAIN_ID = 56;
const DEFAULT_GAS_LIMIT = 800000;
// ✅ TM2_ABI 从公共模块导入
// ✅ 本地 getGasLimit(FourAnyConfig 支持 bigint gasLimit)
function getGasLimit(config, defaultGas = DEFAULT_GAS_LIMIT) {
if (config.gasLimit !== undefined) {
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
}
const multiplier = config.gasLimitMultiplier ?? 1.0;
return BigInt(Math.ceil(defaultGas * multiplier));
}
/**
* 私有购买(单笔)(仅签名版本 - 不依赖 Merkle)
* ✅ 精简版:只负责签名交易,不提交到 Merkle
* ✅ 优化:并行获取 gasPrice 和 nonce
*/
export async function fourPrivateBuyMerkle(params) {
const { privateKey, tokenAddress, funds, to, config } = params;
// ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle)
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
chainId: CHAIN_ID,
name: 'BSC'
});
const wallet = new Wallet(privateKey, provider);
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
const txType = getTxType(config);
const gasLimit = getGasLimit(config);
const originalFundsWei = ethers.parseEther(funds);
// ✅ 利润提取配置(同步计算)
const extractProfit = shouldExtractProfit(config);
let profitWei = 0n;
let actualFundsWei = originalFundsWei;
if (extractProfit) {
const { profit, remaining } = calculateProfit(originalFundsWei, config);
profitWei = profit;
actualFundsWei = remaining;
}
const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
// ✅ 获取贿赂金额
const bribeAmount = getBribeAmount(config);
const needBribeTx = bribeAmount > 0n;
const needProfitTx = extractProfit && profitWei > 0n;
// ✅ 优化:并行获取 gasPrice、nonce 和构建未签名交易
const [gasPrice, baseNonce, unsigned] = await Promise.all([
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
wallet.getNonce(),
tm2.buyTokenAMAP.populateTransaction(0n, tokenAddress, to ?? wallet.address, actualFundsWei, 0n, { value: actualFundsWei })
]);
// ✅ 分配 nonces:贿赂 → 买入 → 利润
let nonceIdx = 0;
const bribeNonce = needBribeTx ? baseNonce + nonceIdx++ : undefined;
const buyNonce = baseNonce + nonceIdx++;
const profitNonce = needProfitTx ? baseNonce + nonceIdx : undefined;
// ✅ 并行签名所有交易(贿赂、买入、利润)
const signPromises = [];
// 贿赂交易
if (needBribeTx && bribeNonce !== undefined) {
signPromises.push(wallet.signTransaction({
to: BLOCKRAZOR_BUILDER_EOA,
value: bribeAmount,
nonce: bribeNonce,
gasPrice,
gasLimit: 21000n,
chainId: CHAIN_ID,
type: txType
}));
}
// 买入交易
signPromises.push(wallet.signTransaction({
...unsigned,
from: wallet.address,
nonce: buyNonce,
gasLimit,
gasPrice,
chainId: CHAIN_ID,
type: txType,
value: actualFundsWei
}));
// ✅ 并行签名完成后按顺序返回
const signedTxs = await Promise.all(signPromises);
// ✅ 利润多跳转账(强制 2 跳中转)
if (needProfitTx && profitNonce !== undefined) {
const profitHopResult = await buildProfitHopTransactions({
provider,
payerWallet: wallet,
profitAmount: profitWei,
profitRecipient: getProfitRecipient(),
hopCount: PROFIT_HOP_COUNT,
gasPrice,
chainId: CHAIN_ID,
txType,
startNonce: profitNonce
});
signedTxs.push(...profitHopResult.signedTransactions);
}
return {
signedTransactions: signedTxs
};
}
/**
* 私有卖出(单笔)(仅签名版本 - 不依赖 Merkle)
* ✅ 精简版:只负责签名交易,不提交到 Merkle
* ✅ 自动检查授权,智能处理授权+卖出
* ✅ 优化:并行获取 gasPrice、allowance、nonce
*/
export async function fourPrivateSellMerkle(params) {
const { privateKey, tokenAddress, amount, minFunds, config } = params;
// ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle)
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
chainId: CHAIN_ID,
name: 'BSC'
});
const wallet = new Wallet(privateKey, provider);
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
const txType = getTxType(config);
const sellGasLimit = getGasLimit(config);
const amountWei = ethers.parseUnits(amount, 18);
const minOut = minFunds ?? 0n;
const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
// ✅ 优化:并行获取 gasPrice、nonce 和构建未签名交易(已移除授权检查)
const [gasPrice, sellNonce, sellUnsigned] = await Promise.all([
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
wallet.getNonce(),
tm2.sellToken.populateTransaction(0n, tokenAddress, amountWei, 0n) // ✅ 已移除滑点保护:minOut 固定为 0
]);
// ✅ 已移除授权检查(前端负责确保授权)
// ✅ 利润计算(同步)
const extractProfit = shouldExtractProfit(config);
const { profit } = extractProfit ? calculateProfit(minOut, config) : { profit: 0n };
// ✅ 获取贿赂金额
const bribeAmount = getBribeAmount(config);
const needBribeTx = bribeAmount > 0n;
const needProfitTx = extractProfit && profit > 0n;
// ✅ 分配 nonces:贿赂 → 卖出 → 利润
let nonceIdx = 0;
const bribeNonce = needBribeTx ? sellNonce + nonceIdx++ : undefined;
const actualSellNonce = sellNonce + nonceIdx++;
const profitNonce = needProfitTx ? sellNonce + nonceIdx : undefined;
// ✅ 并行签名所有交易(贿赂、卖出、利润)
const signPromises = [];
// 贿赂交易
if (needBribeTx && bribeNonce !== undefined) {
signPromises.push(wallet.signTransaction({
to: BLOCKRAZOR_BUILDER_EOA,
value: bribeAmount,
nonce: bribeNonce,
gasPrice,
gasLimit: 21000n,
chainId: CHAIN_ID,
type: txType
}));
}
// 卖出交易
signPromises.push(wallet.signTransaction({
...sellUnsigned,
from: wallet.address,
nonce: actualSellNonce,
gasLimit: sellGasLimit,
gasPrice,
chainId: CHAIN_ID,
type: txType,
value: 0n
}));
// ✅ 并行签名完成后按顺序返回
const signedTxs = await Promise.all(signPromises);
// ✅ 利润多跳转账(强制 2 跳中转)
if (needProfitTx && profitNonce !== undefined) {
const profitHopResult = await buildProfitHopTransactions({
provider,
payerWallet: wallet,
profitAmount: profit,
profitRecipient: getProfitRecipient(),
hopCount: PROFIT_HOP_COUNT,
gasPrice,
chainId: CHAIN_ID,
txType,
startNonce: profitNonce
});
signedTxs.push(...profitHopResult.signedTransactions);
}
return {
signedTransactions: signedTxs
};
}
/**
* 批量私有购买(仅签名版本 - 不依赖 Merkle)
* ✅ 精简版:只负责签名交易,不提交到 Merkle
* ✅ 优化:并行获取 gasPrice 和 nonces,批量构建交易
*/
export async function fourBatchPrivateBuyMerkle(params) {
const { privateKeys, fundsList, tokenAddress, config } = params;
if (privateKeys.length !== fundsList.length) {
throw new Error('privateKeys and fundsList length mismatch');
}
// ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle)
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
chainId: CHAIN_ID,
name: 'BSC'
});
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
const txType = getTxType(config);
const finalGasLimit = getGasLimit(config);
const wallets = privateKeys.map((k) => new Wallet(k, provider));
const originalAmountsWei = fundsList.map((a) => ethers.parseEther(a));
// ✅ 利润提取配置(同步计算)
const extractProfit = shouldExtractProfit(config);
const { totalProfit, remainingAmounts } = calculateBatchProfit(originalAmountsWei, config);
const actualAmountsWei = remainingAmounts;
// ✅ 获取贿赂金额
const bribeAmount = getBribeAmount(config);
const needBribeTx = bribeAmount > 0n;
// ✅ 找出投入金额最多的钱包(作为贿赂和利润支付者)- 同步计算
let maxFundsIndex = 0;
let maxFunds = originalAmountsWei[0];
for (let i = 1; i < originalAmountsWei.length; i++) {
if (originalAmountsWei[i] > maxFunds) {
maxFunds = originalAmountsWei[i];
maxFundsIndex = i;
}
}
const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
const nonceManager = new NonceManager(provider);
const needProfitTx = extractProfit && totalProfit > 0n;
// ✅ 计算 payer 需要的 nonce 数量:贿赂(可选) + 买入 + 利润(可选)
const payerNonceCount = 1 + (needBribeTx ? 1 : 0) + (needProfitTx ? 1 : 0);
// ✅ 优化:并行获取 gasPrice、nonces 和构建未签名交易
const [gasPrice, unsignedList, noncesResult] = await Promise.all([
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
Promise.all(tm2Contracts.map((c, i) => c.buyTokenAMAP.populateTransaction(0n, tokenAddress, wallets[i].address, actualAmountsWei[i], 0n, { value: actualAmountsWei[i] }))),
(async () => {
if (payerNonceCount > 1) {
// payer 需要多个连续 nonce
const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxFundsIndex], payerNonceCount);
// 其他钱包各需要 1 个 nonce
const otherWallets = wallets.filter((_, i) => i !== maxFundsIndex);
const otherNonces = otherWallets.length > 0
? await nonceManager.getNextNoncesForWallets(otherWallets)
: [];
// 组装最终的 nonces 数组(保持原顺序)
const nonces = [];
let otherIdx = 0;
let nonceIdx = 0;
const bribeNonce = needBribeTx ? payerNonces[nonceIdx++] : undefined;
for (let i = 0; i < wallets.length; i++) {
if (i === maxFundsIndex) {
nonces.push(payerNonces[nonceIdx++]); // 买入交易
}
else {
nonces.push(otherNonces[otherIdx++]);
}
}
const profitNonce = needProfitTx ? payerNonces[nonceIdx] : undefined;
return { nonces, bribeNonce, profitNonce };
}
else {
// 所有钱包各 1 个 nonce
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
return { nonces, bribeNonce: undefined, profitNonce: undefined };
}
})()
]);
const { nonces, bribeNonce, profitNonce } = noncesResult;
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
const signedTxs = [];
if (needBribeTx && bribeNonce !== undefined) {
const bribeTx = await wallets[maxFundsIndex].signTransaction({
to: BLOCKRAZOR_BUILDER_EOA,
value: bribeAmount,
nonce: bribeNonce,
gasPrice,
gasLimit: 21000n,
chainId: CHAIN_ID,
type: txType
});
signedTxs.push(bribeTx);
}
// ✅ 并行签名所有买入交易
const signedList = await Promise.all(unsignedList.map((unsigned, i) => wallets[i].signTransaction({
...unsigned,
from: wallets[i].address,
nonce: nonces[i],
gasLimit: finalGasLimit,
gasPrice,
chainId: CHAIN_ID,
type: txType,
value: actualAmountsWei[i]
})));
signedTxs.push(...signedList);
// ✅ 利润多跳转账(强制 2 跳中转)
if (needProfitTx && profitNonce !== undefined) {
const profitHopResult = await buildProfitHopTransactions({
provider,
payerWallet: wallets[maxFundsIndex],
profitAmount: totalProfit,
profitRecipient: getProfitRecipient(),
hopCount: PROFIT_HOP_COUNT,
gasPrice,
chainId: CHAIN_ID,
txType,
startNonce: profitNonce
});
signedTxs.push(...profitHopResult.signedTransactions);
}
nonceManager.clearTemp();
return {
signedTransactions: signedTxs
};
}
/**
* 批量私有卖出(仅签名版本 - 不依赖 Merkle)
* ✅ 精简版:只负责签名交易,不提交到 Merkle
* ✅ 自动包含授权交易,无需前端单独调用
* ✅ 优化:并行获取所有查询数据,批量获取 nonces
*/
export async function fourBatchPrivateSellMerkle(params) {
const { privateKeys, amounts, tokenAddress, config, minFundsEach } = params;
if (privateKeys.length !== amounts.length) {
throw new Error('privateKeys and amounts length mismatch');
}
// ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle)
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
chainId: CHAIN_ID,
name: 'BSC'
});
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
const txType = getTxType(config);
const sellGasLimit = getGasLimit(config);
const wallets = privateKeys.map((k) => new Wallet(k, provider));
const amountsWei = amounts.map((a) => ethers.parseUnits(a, 18));
const rpcUrl = config.rpcUrl;
// ✅ 优化:并行获取 gasPrice 和 quotedOutputs(已移除授权和余额检查)
const [gasPrice, quotedOutputs] = await Promise.all([
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
// 获取预期收益(用于计算利润)
minFundsEach !== undefined
? Promise.resolve((() => {
const minOutWei = typeof minFundsEach === 'string' ? ethers.parseEther(minFundsEach) : minFundsEach;
return new Array(wallets.length).fill(minOutWei * 100n / 95n);
})())
: Promise.all(amountsWei.map(async (amount) => {
try {
const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
return result.funds;
}
catch {
return 0n;
}
}))
]);
// ✅ 已移除滑点保护:minOuts 固定为 0
const minOuts = new Array(wallets.length).fill(0n);
// ✅ 计算总利润和找出收益最高的钱包(同步计算)
const extractProfit = shouldExtractProfit(config);
let totalProfit = 0n;
let maxRevenueIndex = 0;
let maxRevenue = 0n;
if (extractProfit) {
for (let i = 0; i < wallets.length; i++) {
if (quotedOutputs[i] > 0n) {
const { profit } = calculateProfit(quotedOutputs[i], config);
totalProfit += profit;
if (quotedOutputs[i] > maxRevenue) {
maxRevenue = quotedOutputs[i];
maxRevenueIndex = i;
}
}
}
}
// ✅ 获取贿赂金额
const bribeAmount = getBribeAmount(config);
const needBribeTx = bribeAmount > 0n;
const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
const nonceManager = new NonceManager(provider);
const needProfitTx = extractProfit && totalProfit > 0n;
// ✅ 计算 payer 需要的 nonce 数量:贿赂(可选) + 卖出 + 利润(可选)
const payerNonceCount = 1 + (needBribeTx ? 1 : 0) + (needProfitTx ? 1 : 0);
// ✅ 优化:第二批并行获取 - nonces 和构建未签名交易
const [sellUnsigned, noncesResult] = await Promise.all([
Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], minOuts[i]))),
(async () => {
if (payerNonceCount > 1) {
// payer 需要多个连续 nonce
const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxRevenueIndex], payerNonceCount);
// 其他钱包各需要 1 个 nonce
const otherWallets = wallets.filter((_, i) => i !== maxRevenueIndex);
const otherNonces = otherWallets.length > 0
? await nonceManager.getNextNoncesForWallets(otherWallets)
: [];
// 组装最终的 nonces 数组(保持原顺序)
const nonces = [];
let otherIdx = 0;
let nonceIdx = 0;
const bribeNonce = needBribeTx ? payerNonces[nonceIdx++] : undefined;
for (let i = 0; i < wallets.length; i++) {
if (i === maxRevenueIndex) {
nonces.push(payerNonces[nonceIdx++]); // 卖出交易
}
else {
nonces.push(otherNonces[otherIdx++]);
}
}
const profitNonce = needProfitTx ? payerNonces[nonceIdx] : undefined;
return { nonces, bribeNonce, profitNonce };
}
else {
// 所有钱包各 1 个 nonce
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
return { nonces, bribeNonce: undefined, profitNonce: undefined };
}
})()
]);
const { nonces, bribeNonce, profitNonce } = noncesResult;
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
const signedTxs = [];
if (needBribeTx && bribeNonce !== undefined) {
const bribeTx = await wallets[maxRevenueIndex].signTransaction({
to: BLOCKRAZOR_BUILDER_EOA,
value: bribeAmount,
nonce: bribeNonce,
gasPrice,
gasLimit: 21000n,
chainId: CHAIN_ID,
type: txType
});
signedTxs.push(bribeTx);
}
// ✅ 并行签名所有卖出交易
const signedSells = await Promise.all(sellUnsigned.map((unsigned, i) => wallets[i].signTransaction({
...unsigned,
from: wallets[i].address,
nonce: nonces[i],
gasLimit: sellGasLimit,
gasPrice,
chainId: CHAIN_ID,
type: txType,
value: 0n
})));
signedTxs.push(...signedSells);
// ✅ 利润多跳转账(强制 2 跳中转)
if (needProfitTx && profitNonce !== undefined) {
const profitHopResult = await buildProfitHopTransactions({
provider,
payerWallet: wallets[maxRevenueIndex],
profitAmount: totalProfit,
profitRecipient: getProfitRecipient(),
hopCount: PROFIT_HOP_COUNT,
gasPrice,
chainId: CHAIN_ID,
txType,
startNonce: profitNonce
});
signedTxs.push(...profitHopResult.signedTransactions);
}
nonceManager.clearTemp();
return {
signedTransactions: signedTxs
};
}