Pressione ESC para fechar

Anti-MEV Stealth Swap: Construindo e Simulando Bundles

Na primeira parte, a gente montou o nosso "tunnel de comunicação seguro". Nesta parte, vamos direto ao que interessa: a lógica do swap. Nosso objetivo é construir uma transação para a Uniswap V3 de forma que ela não seja apenas privada, mas que tenha execução garantida.

1. Preparando a transação de swap

Para o nosso exemplo, vamos configurar uma troca de ETH por USDC. Para que a transação seja válida dentro de um bundle, precisamos preparar o objeto dela com antecedência, sem transmiti-la diretamente para a rede.

O que vamos precisar ter em mãos:

  • O endereço do Router da Uniswap V3.
  • O ABI (pelo menos o mínimo) para a função exactInputSingle.
  • O cálculo do gás levando em conta a prioridade.

 

Regra de ouro: Nos bundles do Flashbots, o gasPrice padrão é substituído pelo maxFeePerGas e pelo maxPriorityFeePerGas. É justamente o priorityFee (a "gorjeta" para o validador) que define se o seu bundle vai entrar no bloco ou não.

 

 

2. Código: Montagem e Simulação

Vamos adicionar ao nosso projeto o arquivo swap.ts. O grande destaque aqui é o método .simulate(). Essa é a "funcionalidade matadora" do Flashbots, que permite testar a execução da transação no estado atual da blockchain sem gastar um centavo de gás real.

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

// ABI mínima para interagir com o Router da Uniswap V3
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. Criamos a interface e preparamos os dados da transação
    const iface = new ethers.Interface(ROUTER_ABI);
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // Janela de 20 minutos
    
    const amountIn = ethers.parseEther("0.1"); // Vamos trocar 0.1 ETH
    const params = {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        fee: 3000, // Pool de 0.3%
        recipient: wallet.address,
        deadline: deadline,
        amountIn: amountIn,
        amountOutMinimum: 0, // Em produção, sempre calcule o slippage!
        sqrtPriceLimitX96: 0
    };

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

    // 2. Estrutura da transação
    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"), // A "propina" para o validador
        nonce: await wallet.getNonce()
    };

    // 3. Criamos o bundle assinado
    const signedBundle = await flashbotsProvider.signBundle([
        {
            signer: wallet,
            transaction: transaction
        }
    ]);

    // 4. SIMULAÇÃO (Etapa crucial)
    console.log("Iniciando simulação do bundle...");
    const simulation = await flashbotsProvider.simulate(signedBundle, nextBlockNumber);

    if ("error" in simulation) {
        console.error(`Erro na simulação: ${simulation.error.message}`);
        return;
    }

    console.log("Simulação bem-sucedida!", JSON.stringify(simulation, null, 2));
    return signedBundle;
}

 

3. Detalhe técnico: Por que a simulação é obrigatória?

No mempool "público", se a sua transação falhar (por exemplo, o limite de gás for insuficiente ou o preço mudar), ela ainda assim entra no bloco e você paga as taxas de gás de qualquer jeito.

No mundo do Flashbots:

  • Se a simulação der erro, você simplesmente não envia o bundle. Sem prejuízo.
  • Se o bundle for enviado, mas as condições do bloco mudarem e a transação deixar de ser vantajosa, o validador simplesmente a ignora.

Resumo da ópera: Você só paga o gás quando o seu Stealth Swap for efetivamente executado.

4. Como calcular a "Propina" (Priority Fee)

Os validadores escolhem os bundles pela ordem de rentabilidade para eles. A lucratividade de um bundle (Gas Price Score) é calculada assim:

fórmula de cálculo
 

Para um swap comum, 1-2 gwei de maxPriorityFeePerGas costuma ser suficiente. Mas em momentos de alta volatilidade, a briga por um lugar no bloco aperta, mesmo nessas trilhas privadas.

 

O que temos até agora?

Já temos um bundle assinado e verificado, pronto para o combate. Sabemos com certeza que o código da Uniswap vai rodar redondo e que o saldo da carteira cobre todos os custos.

Na próxima (e última) parte: vamos escrever o loop de espera do bloco, fazer o envio real do bundle e criar uma interface CLI amigável para a nossa ferramenta.

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....

Deixe seu parecer

O seu endereço de e-mail não será publicado. Campos obrigatórios estão marcados *