four-flap-meme-sdk
Version:
SDK for Flap bonding curve and four.meme TokenManager
210 lines (209 loc) • 8.9 kB
JavaScript
/**
* Four.meme 内盘捆绑换手(Merkle Bundle)- 先买后卖
*
* 功能:钱包B先买入代币 → 钱包A卖出相同数量 → 原子执行
*/
import { ethers, Contract, Wallet } from 'ethers';
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from './config.js';
import { trySell } from '../tm.js';
// ✅ 本地 getGasLimit(FourAnyConfig 支持 bigint gasLimit)
function getGasLimit(config, defaultGas = 800000) {
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));
}
export async function fourBundleBuyFirstMerkle(params) {
const { buyerPrivateKey, buyerFunds, buyerFundsPercentage, sellerPrivateKey, tokenAddress, config } = params;
const chainIdNum = config.chainId ?? 56;
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
chainId: chainIdNum,
name: 'BSC'
});
const buyer = new Wallet(buyerPrivateKey, provider);
const seller = new Wallet(sellerPrivateKey, provider);
const sameAddress = buyer.address.toLowerCase() === seller.address.toLowerCase();
const finalGasLimit = getGasLimit(config);
const txType = getTxType(config);
const nonceManager = new NonceManager(provider);
const reserveGas = ethers.parseEther((config.reserveGasBNB || 0.0005).toString());
// ✅ 优化:第一批并行 - buyerBalance、gasPrice
const [buyerBalance, gasPrice] = await Promise.all([
provider.getBalance(buyer.address),
getOptimizedGasPrice(provider, getGasPriceConfig(config))
]);
// 计算买方投入资金
let buyerFundsWei;
if (buyerFunds !== undefined) {
buyerFundsWei = ethers.parseEther(String(buyerFunds));
}
else if (buyerFundsPercentage !== undefined) {
const pct = Math.max(0, Math.min(100, buyerFundsPercentage));
const spendable = buyerBalance > reserveGas ? buyerBalance - reserveGas : 0n;
buyerFundsWei = (spendable * BigInt(Math.round(pct * 100))) / 10000n;
}
else {
throw new Error('必须提供 buyerFunds 或 buyerFundsPercentage');
}
if (buyerFundsWei <= 0n) {
throw new Error('buyerFunds 需要大于 0');
}
// ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
const erc20 = new Contract(tokenAddress, [
'function balanceOf(address) view returns (uint256)',
'function decimals() view returns (uint8)'
], provider);
const [buyQuote, sellerTokenBal, decimals] = await Promise.all([
helper3.tryBuy(tokenAddress, 0n, buyerFundsWei),
erc20.balanceOf(seller.address),
erc20.decimals()
]);
const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
throw new Error('报价失败:无法估算可买入的代币数量');
}
const sellAmountWei = estimatedTokenAmount;
// 卖方余额检查
if (!sameAddress && sellerTokenBal < sellAmountWei) {
throw new Error(`卖方代币余额不足: 需要 ${ethers.formatUnits(sellAmountWei, decimals)},实际 ${ethers.formatUnits(sellerTokenBal, decimals)}`);
}
// ✅ 优化:第三批并行 - trySell、构建交易、获取 nonces
const tmBuyer = new Contract(TM_ADDRESS, TM_ABI, buyer);
const tmSeller = new Contract(TM_ADDRESS, TM_ABI, seller);
// 预先规划 nonces
const extractProfit = true;
const profitRateBps = PROFIT_CONFIG.RATE_BPS_SWAP; // 万分之六
// ✅ 获取贿赂金额
const bribeAmount = getBribeAmount(config);
const needBribeTx = bribeAmount > 0n;
// 计算需要的 nonce 数量
let buyerNonceCount = 1; // 买入交易
let sellerNonceCount = 1; // 卖出交易
if (needBribeTx)
sellerNonceCount++; // 贿赂交易(由卖方发送)
if (extractProfit)
sellerNonceCount++; // 利润交易
// ✅ 优化:使用批量 nonce 获取(单次网络往返)
const getNoncesPromise = sameAddress
? nonceManager.getNextNonceBatch(seller, buyerNonceCount + sellerNonceCount)
.then(nonces => ({ buyerNonces: [], sellerNonces: nonces }))
: nonceManager.getNextNoncesForWallets([buyer, seller])
.then(initialNonces => ({
// buyer 只需要 1 个 nonce
buyerNonces: [initialNonces[0]],
// seller 需要 sellerNonceCount 个连续 nonce,从初始 nonce 开始
sellerNonces: Array.from({ length: sellerNonceCount }, (_, i) => initialNonces[1] + i)
}));
const [sellResult, buyUnsigned, sellUnsigned, noncesResult] = await Promise.all([
trySell('BSC', config.rpcUrl, tokenAddress, sellAmountWei),
tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, 0n, { value: buyerFundsWei }),
tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n),
getNoncesPromise
]);
const { buyerNonces, sellerNonces } = noncesResult;
const estimatedSellFunds = sellResult.funds;
const profitAmount = extractProfit ? (estimatedSellFunds * BigInt(profitRateBps)) / 10000n : 0n;
// 分配 nonces
let buyerNonce;
let sellerNonce;
let bribeNonce;
let profitNonce;
if (sameAddress) {
let idx = 0;
if (needBribeTx)
bribeNonce = sellerNonces[idx++];
buyerNonce = sellerNonces[idx++];
sellerNonce = sellerNonces[idx++];
if (extractProfit)
profitNonce = sellerNonces[idx];
}
else {
buyerNonce = buyerNonces[0];
let idx = 0;
if (needBribeTx)
bribeNonce = sellerNonces[idx++];
sellerNonce = sellerNonces[idx++];
if (extractProfit)
profitNonce = sellerNonces[idx];
}
// ✅ 并行签名所有交易
const signPromises = [];
// ✅ 贿赂交易放在首位
let bribeTx = null;
if (needBribeTx && bribeNonce !== undefined) {
signPromises.push(seller.signTransaction({
to: BLOCKRAZOR_BUILDER_EOA,
value: bribeAmount,
nonce: bribeNonce,
gasPrice,
gasLimit: 21000n,
chainId: chainIdNum,
type: txType
}));
}
// 买入和卖出交易
signPromises.push(buyer.signTransaction({
...buyUnsigned,
from: buyer.address,
nonce: buyerNonce,
gasLimit: finalGasLimit,
gasPrice,
chainId: chainIdNum,
type: txType,
value: buyerFundsWei
}),
// ✅ 卖出交易 value 必须为 0,不能发送原生代币
seller.signTransaction({
...sellUnsigned,
from: seller.address,
nonce: sellerNonce,
gasLimit: finalGasLimit,
gasPrice,
chainId: chainIdNum,
type: txType,
value: 0n // ✅ 卖出交易不发送原生代币
}));
const signedTxs = await Promise.all(signPromises);
// 解析签名结果
let idx = 0;
if (needBribeTx)
bribeTx = signedTxs[idx++];
const signedBuy = signedTxs[idx++];
const signedSell = signedTxs[idx++];
nonceManager.clearTemp();
// ✅ 组装交易列表:贿赂 → 买入 → 卖出
const allTransactions = [];
if (bribeTx)
allTransactions.push(bribeTx);
allTransactions.push(signedBuy, signedSell);
// ✅ 利润多跳转账(强制 2 跳中转)
if (extractProfit && profitAmount > 0n && profitNonce !== undefined) {
const profitHopResult = await buildProfitHopTransactions({
provider,
payerWallet: seller,
profitAmount: profitAmount,
profitRecipient: getProfitRecipient(),
hopCount: PROFIT_HOP_COUNT,
gasPrice,
chainId: chainIdNum,
txType,
startNonce: profitNonce
});
allTransactions.push(...profitHopResult.signedTransactions);
}
return {
signedTransactions: allTransactions,
metadata: {
buyerAddress: buyer.address,
sellerAddress: seller.address,
buyAmount: ethers.formatEther(buyerFundsWei),
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
profitAmount: extractProfit ? ethers.formatEther(profitAmount) : undefined
}
};
}