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

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.