Нажмите ESC, чтобы закрыть

Анти MEV Стелс Своп: Сборка и симуляция бандла

 В первой части  мы построили «защищенный канал связи». В этой части мы переходим к написанию логики обмена. Наша задача — создать транзакцию для Uniswap V3 так, чтобы она не просто была приватной, но и гарантированно исполнилась.

1. Подготовка транзакции обмена

Для примера реализуем обмен ETH на USDC. Чтобы транзакция была валидной для бандла, нам нужно подготовить её объект заранее, не отправляя в сеть.

Нам понадобятся:

  • Адрес Router Uniswap V3.
  • ABI (хотя бы минимальный) для функции exactInputSingle.
  • Расчет газа с учетом приоритета.

 

Важное правило: В Flashbots бандлах цена газа (gasPrice) заменяется на maxFeePerGas и maxPriorityFeePerGas. Именно priorityFee (чаевые валидатору) определяет, попадет ли ваш бандл в блок.

 

 

2. Код: Сборка и симуляция

Добавим в наш проект файл swap.ts. Основной акцент здесь — на методе .simulate(). Это «киллер-фича» Flashbots, которая позволяет проверить выполнение транзакции на текущем состоянии блокчейна, не тратя ни цента на реальный газ.

import { ethers } from "ethers";
import { FlashbotsBundleRawTransaction } from "@flashbots/ethers-provider-bundle";

// Минимальный ABI для взаимодействия с Uniswap V3 Router
const ROUTER_ABI = [
    "function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96)) external payable returns (uint256 amountOut)"
];

const ROUTER_ADDRESS = "0xE592427A0AEce92De3Edee1F18E0157C05861564";

export async function createAndSimulateBundle(wallet: ethers.Wallet, flashbotsProvider: any, provider: ethers.Provider) {
    const block = await provider.getBlock("latest");
    const nextBlockNumber = block!.number + 1;

    // 1. Формируем интерфейс и данные транзакции
    const iface = new ethers.Interface(ROUTER_ABI);
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 минут
    
    const amountIn = ethers.parseEther("0.1"); // Меняем 0.1 ETH

    const params = {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        fee: 3000, // 0.3%
        recipient: wallet.address,
        deadline: deadline,
        amountIn: amountIn,
        amountOutMinimum: 0, // В продакшене всегда считайте слиппейдж!
        sqrtPriceLimitX96: 0
    };

    const data = iface.encodeFunctionData("exactInputSingle", [params]);

    // 2. Структура транзакции
    const transaction = {
        to: ROUTER_ADDRESS,
        value: amountIn,
        data: data,
        chainId: 1,
        type: 2, // EIP-1559
        gasLimit: 250000,
        maxFeePerGas: ethers.parseUnits("50", "gwei"),
        maxPriorityFeePerGas: ethers.parseUnits("2", "gwei"), // "Взятка" валидатору
        nonce: await wallet.getNonce()
    };

    // 3. Создаем подписанный бандл
    const signedBundle = await flashbotsProvider.signBundle([
        {
            signer: wallet,
            transaction: transaction
        }
    ]);

    // 4. СИМУЛЯЦИЯ (Критически важный этап)
    console.log("Запуск симуляции бандла...");
    const simulation = await flashbotsProvider.simulate(signedBundle, nextBlockNumber);

    if ("error" in simulation) {
        console.error(`Ошибка симуляции: ${simulation.error.message}`);
        return;
    }

    console.log("Симуляция успешна!", JSON.stringify(simulation, null, 2));
    return signedBundle;
}

 

3. Технический нюанс: Почему симуляция обязательна?

В «публичном» эфире, если ваша транзакция зафейлится (например, не хватило лимита газа или изменилась цена), она все равно попадет в блок, и вы заплатите за газ.

В мире Flashbots:

  • Если симуляция показывает ошибку — вы просто не отправляете бандл.
  • Если бандл отправлен, но условия в блоке изменились и транзакция стала невыгодной — валидатор её просто проигнорирует.

Итог: Вы платите за газ только тогда, когда ваш Stealth Swap действительно исполнился.

4. Как рассчитать «взятку» (Priority Fee)

Валидаторы включают бандлы в порядке убывания их прибыльности для себя. Прибыльность бандла (Gas Price Score) считается как:

formula7
 

Если вы делаете обычный свап, maxPriorityFeePerGas в 1-2 gwei обычно достаточно. Но во время высокой волатильности конкуренция за место в блоке растет даже на приватных рельсах.

 

Что мы имеем теперь?

У нас есть подписанный и проверенный бандл, который готов к отправке. Мы точно знаем, что код Uniswap отработает корректно, а баланса кошелька хватит на покрытие всех расходов.

В следующей (заключительной) части: мы напишем цикл ожидания блока, реализуем реальную отправку бандла и сделаем удобный CLI-интерфейс для нашего инструмента.

Sying Yu

I am a blockchain developer specializing in building secure, scalable, and innovative decentralized solutions. My expertise covers smart contracts, payment systems, and integrating crypto with fiat to optimize financial workflows. I thrive on creating modern, efficient tools for the evolving digital economy....

Поделитесь своим мнением

Ваш e-mail не будет опубликован. Обязательные поля отмечены *