initnmjnjnj

This commit is contained in:
ZOMBIIIIIII
2026-06-17 23:23:41 +03:00
parent 7ac5086abe
commit 6f01f4af34
2 changed files with 58 additions and 36 deletions

View File

@@ -463,30 +463,32 @@ export async function executeAddLiquidity(p: {
throw badRequest(`Недостаточно ${pool.sym1}: нужно ${formatSmallestUnits(a1.toString(), pool.dec1)}, есть ${formatSmallestUnits(bal.toString(), pool.dec1)}`);
}
}
// WETH-нога при useNativeEth: заворачиваем ТОЛЬКО недостающее (учитываем уже имеющийся WETH от прошлых
// попыток — не плодим WETH и не требуем лишний ETH). wethDeficit переиспользуется в шаге wrap ниже.
let wethDeficit = 0n;
if (p.useNativeEth) {
const wrapAmt = t0IsWeth ? a0 : t1IsWeth ? a1 : 0n;
if (wrapAmt > 0n) {
const wethLegAddr = t0IsWeth ? pool.token0 : t1IsWeth ? pool.token1 : '';
if (wrapAmt > 0n && wethLegAddr) {
let wethBal = 0n;
try { wethBal = await readErc20Balance('ETH', wethLegAddr, p.expectedFromAddress); } catch { /* assume 0 */ }
wethDeficit = wethBal >= wrapAmt ? 0n : wrapAmt - wethBal;
const ethBal = await readEvmNativeBalance('ETH', p.expectedFromAddress);
const need = wrapAmt + ethers.utils.parseEther('0.001').toBigInt(); // wrap + буфер на газ
const need = wethDeficit + ethers.utils.parseEther('0.001').toBigInt(); // недостающий WETH + буфер на газ
if (ethBal < need) {
throw badRequest(`Недостаточно ETH: нужно ~${formatSmallestUnits(need.toString(), 18)} (wrap + газ), есть ${formatSmallestUnits(ethBal.toString(), 18)}`);
throw badRequest(`Недостаточно ETH: нужно ~${formatSmallestUnits(need.toString(), 18)} (wrap недостающего WETH + газ), есть ${formatSmallestUnits(ethBal.toString(), 18)}`);
}
}
}
// useNativeEth: если у пула есть WETH-нога — заворачиваем нужный ETH в WETH (WETH.deposit, ждёт
// 1 conf). Дальше WETH = обычный ERC20: fee/approve/mint без изменений. Юзеру нужен ETH + второй токен.
// useNativeEth: заворачиваем ТОЛЬКО недостающий WETH (wethDeficit). Если WETH уже хватает — пропускаем
// (используем имеющийся). Дальше WETH = обычный ERC20: fee/approve/mint без изменений.
let wrapTxid: string | undefined;
if (p.useNativeEth) {
let wrapAmount = 0n;
if (isWeth(pool.token0)) wrapAmount = a0;
else if (isWeth(pool.token1)) wrapAmount = a1;
if (wrapAmount > 0n) {
const w = await signAndBroadcastWrapEth({
mnemonic: p.mnemonic, expectedFromAddress: p.expectedFromAddress, amountWei: wrapAmount.toString(),
});
wrapTxid = w.txid;
}
if (p.useNativeEth && wethDeficit > 0n) {
const w = await signAndBroadcastWrapEth({
mnemonic: p.mnemonic, expectedFromAddress: p.expectedFromAddress, amountWei: wethDeficit.toString(),
});
wrapTxid = w.txid;
}
// app fee 0.7% по каждому токену (net = то что реально пойдёт в пул)

View File

@@ -101,7 +101,7 @@ export async function signAndBroadcastEvmApprove(p: SignEvmApproveParams): Promi
const signer = wallet.connect(provider);
const token = new ethers.Contract(p.token, ERC20_ABI, signer);
// Fee tier: используем provider.getFeeData() — это OK для approve (low priority).
// Fee tier: provider.getFeeData() — OK для approve (low priority). Считаем один раз на все tx.
const feeData = await provider.getFeeData();
const capWei = ethers.utils.parseUnits(String(MAX_GAS_PRICE_GWEI), 'gwei');
let maxFeePerGas = feeData.maxFeePerGas ?? ethers.utils.parseUnits('30', 'gwei');
@@ -109,27 +109,47 @@ export async function signAndBroadcastEvmApprove(p: SignEvmApproveParams): Promi
if (maxFeePerGas.gt(capWei)) maxFeePerGas = capWei;
if (maxPriorityFeePerGas.gt(maxFeePerGas)) maxPriorityFeePerGas = maxFeePerGas;
const nonce = await provider.getTransactionCount(wallet.address, 'pending');
const data = token.interface.encodeFunctionData('approve', [p.spender, ethers.BigNumber.from(p.amount)]);
// Отправить один approve(spender, value), дождаться 1 conf. Nonce берётся 'pending' перед КАЖДОЙ tx
// (после reset предыдущая уже подтверждена → следующая получит инкремент).
const sendOne = async (value: ethers.BigNumber): Promise<string> => {
const nonce = await provider.getTransactionCount(wallet.address, 'pending');
const data = token.interface.encodeFunctionData('approve', [p.spender, value]);
const sent = await signer.sendTransaction({
to: p.token, data, value: 0, chainId: expectedChainId, nonce,
gasLimit: APPROVE_GAS_LIMIT, type: 2, maxFeePerGas, maxPriorityFeePerGas,
});
await Promise.race([
sent.wait(1),
new Promise((_, reject) => setTimeout(() => reject(new Error('approve confirm timeout')), 60_000)),
]);
return sent.hash;
};
const sent = await signer.sendTransaction({
to: p.token,
data,
value: 0,
chainId: expectedChainId,
nonce,
gasLimit: APPROVE_GAS_LIMIT,
type: 2,
maxFeePerGas,
maxPriorityFeePerGas,
});
// Wait 1 conf чтобы bridge tx (next) видел updated allowance
await Promise.race([
sent.wait(1),
new Promise((_, reject) => setTimeout(() => reject(new Error('approve confirm timeout')), 60_000)),
]);
logger.info(`EVM approve confirmed: chain=${p.chain} token=${p.token} spender=${p.spender} amount=${p.amount} txid=${sent.hash}`);
return { txid: sent.hash };
const needed = ethers.BigNumber.from(p.amount);
// Текущий allowance — для idempotency + USDT-safe reset. (amount '0'/revoke ниже не попадает в эти ветки.)
let current = ethers.BigNumber.from(0);
try {
current = await token.allowance(wallet.address, p.spender);
} catch (err: any) {
logger.warn(`EVM approve: allowance read failed (assume 0): ${err?.message}`);
}
// Уже достаточно → не шлём лишнюю tx (повторы, и WETH-нога с остаточным allowance).
if (needed.gt(0) && current.gte(needed)) {
logger.info(`EVM approve skip: allowance ${current.toString()} >= needed ${needed.toString()} (token=${p.token} spender=${p.spender})`);
return { txid: '' };
}
// USDT-style: контракт реверит approve(ненулевое) пока allowance != 0 → сначала reset в 0, потом ставим нужное.
if (needed.gt(0) && current.gt(0)) {
logger.info(`EVM approve reset-to-0 (USDT-safe): ${current.toString()} → 0 → ${needed.toString()} (token=${p.token} spender=${p.spender})`);
await sendOne(ethers.constants.Zero);
}
const txid = await sendOne(needed);
logger.info(`EVM approve confirmed: chain=${p.chain} token=${p.token} spender=${p.spender} amount=${p.amount} txid=${txid}`);
return { txid };
}
/**