Drücken Sie ESC, um zu schließen

Anti-MEV Stealth Swap: Bundles bauen & simulieren

Im ersten Teil haben wir unsere "gesicherte Verbindung“ aufgebaut. In diesem Teil kümmern wir uns um die eigentliche Swap-Logik. Unser Ziel ist es, eine Uniswap-V3-Transaktion so aufzusetzen, dass sie nicht nur privat bleibt, sondern auch garantiert ausgeführt wird.

1. Vorbereitung der Swap-Transaktion

Als Beispiel führen wir einen Swap von ETH in USDC aus. Damit die Transaktion für ein Bundle gültig ist, müssen wir das Transaktionsobjekt vorab erstellen, ohne es direkt an das Netzwerk zu senden.

Was wir dafür brauchen:

  • Die Adresse des Uniswap V3 Routers.
  • Das ABI (zumindest das Nötigste) für die Funktion exactInputSingle.
  • Eine Gas-Berechnung, die die Priorität berücksichtigt.

 

Goldene Regel: In Flashbots-Bundles wird der klassische gasPrice durch maxFeePerGas und maxPriorityFeePerGas ersetzt. Vor allem die priorityFee (das „Trinkgeld“ für den Validator) entscheidet darüber, ob dein Bundle überhaupt in einen Block kommt.

 

 

2. Code: Zusammenbau und Simulation

Wir fügen unserem Projekt die Datei swap.ts hinzu. Das Herzstück hier ist die Methode .simulate(). Das ist das absolute Killer-Feature von Flashbots: Damit kannst du prüfen, ob die Transaktion auf dem aktuellen Stand der Blockchain durchgeht, ohne auch nur einen Cent für echtes Gas auszugeben.

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

// Minimales ABI für die Interaktion mit dem 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. Interface erstellen und Transaktionsdaten vorbereiten
    const iface = new ethers.Interface(ROUTER_ABI);
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 Minuten Zeitfenster
    
    const amountIn = ethers.parseEther("0.1"); // Wir tauschen 0.1 ETH
    const params = {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        fee: 3000, // 0.3% Pool
        recipient: wallet.address,
        deadline: deadline,
        amountIn: amountIn,
        amountOutMinimum: 0, // In Produktion: Immer Slippage berechnen!
        sqrtPriceLimitX96: 0
    };

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

    // 2. Struktur der Transaktion
    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"), // Das "Bestechungsgeld" für den Validator
        nonce: await wallet.getNonce()
    };

    // 3. Signiertes Bundle erstellen
    const signedBundle = await flashbotsProvider.signBundle([
        {
            signer: wallet,
            transaction: transaction
        }
    ]);

    // 4. SIMULATION (Der entscheidende Schritt)
    console.log("Starte Bundle-Simulation...");
    const simulation = await flashbotsProvider.simulate(signedBundle, nextBlockNumber);

    if ("error" in simulation) {
        console.error(`Simulation fehlgeschlagen: ${simulation.error.message}`);
        return;
    }

    console.log("Simulation erfolgreich!", JSON.stringify(simulation, null, 2));
    return signedBundle;
}

 

3. Technischer Hintergrund: Warum ist die Simulation Pflicht?

Im "öffentlichen" Ethereum-Netzwerk landet eine fehlgeschlagene Transaktion (z. B. wegen zu wenig Gas oder Preisänderungen) trotzdem im Block – und du zahlst die Gas-Gebühren umsonst.

In der Flashbots-Welt sieht das anders aus:

  • Spuckt die Simulation einen Fehler aus, sendest du das Bundle gar nicht erst ab.
  • Wird das Bundle gesendet, aber die Bedingungen im Block ändern sich so, dass der Trade unrentabel wird, ignoriert der Validator es einfach.

Fazit: Du zahlst Gas nur dann, wenn dein Stealth Swap auch wirklich erfolgreich ausgeführt wurde.

4. Wie man das "Bestechungsgeld" (Priority Fee) berechnet

Validatoren nehmen Bundles basierend auf deren Profitabilität auf. Die Attraktivität eines Bundles (Gas Price Score) wird so berechnet:

Berechnungsformel
 

Für einen normalen Swap reichen meist 1-2 gwei als maxPriorityFeePerGas. Aber bei hoher Volatilität steigt der Kampf um den Platz im Block auch auf den privaten Schienen ordentlich an.

 

Wo stehen wir jetzt?

Wir haben ein signiertes und geprüftes Bundle am Start, das bereit für den Versand ist. Wir wissen sicher, dass der Uniswap-Code sauber läuft und das Wallet genug Deckung für alle Kosten hat.

Im nächsten (und letzten) Teil: Wir schreiben die Block-Warteschleife, setzen den echten Versand des Bundles um und bauen ein schickes CLI-Interface für unser Tool.

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

Diskussion beitreten

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *