1 Introducción -En el Bosque Oscuro
1 Introducción -A finales de una noche, Dan Robinson, un investigador de la firma de inversión Paradigm, vio una llamada de emergencia en el canal Uniswap Discord.Dosotro usuario, pero al propio contrato de Uniswap, donde parecían irrevocablemente perdidos.
Robinson, sin embargo, vio un rayo de esperanza. se dio cuenta de que cualquiera podía desencadenar laburn
función en el contrato, un mandato público que obligaría al contrato a liberar los tokens atrapados a la persona que lo llamó.
Reconociendo una oportunidad de ser un héroe de sombrero blanco, Robinson preparó la transacción de rescate. Pero sabía que no estaba solo. El mempool de Ethereum – la sala de espera pública para transacciones pendentes – es un terreno de caza para robots sofisticados conocidos como “frontrunners generalizados”.
Para superarlos, Robinson diseñó una inteligente transacción de dos partes, con la esperanza de obtener ambas partes minadas simultáneamente, dejando ninguna ventana para la intercepción. No funcionó. En el momento en que apareció su primera transacción, un bot la detectó, replicó toda la estrategia y saqueó los 12.000 dólares antes de que la segunda transacción de Robinson pudiera incluso ser confirmada.
En un ensayo ahora famoso sobre el incidente, Robinson dio un nombre a este hostil ecosistema invisible.Ethereum is a Dark Forest.
En este artículo, vamos a profundizar en el diseño de un sistema capaz de leer operaciones en tiempo real y datos de transacciones de blockchain, con el objetivo de realizar arbitraje.mathematical models underlying AMM pricingEntonces, delve en laalgorithms for opportunity detection and optimal price entryDefine elarchitectural componentsde nuestro país, y discutir elcritical strategiesnecesario para ejecutar de forma exitosa y segura el arbitraje en este entorno de altas apuestas.
El paisaje de DeFi: AMMs, liquidez y oportunidades de arbitraje
El “Dark Forest” descrito en nuestra introducción no es sólo un entorno hostil; es un ecosistema vibrante construido sobre un nuevo paradigma financiero: Financiación descentralizada (DeFi). En su núcleo, DeFi tiene como objetivo recrear los servicios financieros tradicionales en las redes de blockchain, eliminando la necesidad de intermediarios a través del uso de contratos inteligentes autoexecutados.
Automated Market Makers (AMMs): la columna vertebral del intercambio descentralizado
Los intercambios tradicionales se basan en libros de pedidos, donde compradores y vendedores ponen ofertas y solicitudes, y un motor de ajuste central facilita las transacciones. DeFi introduce un modelo radicalmente diferente: el Automated Market Maker (AMM). En lugar de ajustar directamente compradores y vendedores, los AMM aprovechan piscinas de liquidez - contratos inteligentes que tienen reservas de dos o más tokens - para facilitar las transacciones. Los usuarios, conocidos como proveedores de liquidez (LPs), depositan valores equivalentes de pares de tokens en estas piscinas, ganando una parte de las tarifas de comercio a cambio.
El precio de los activos dentro de un pool de AMM se determina algorítmicamente por unconstant product formula, pionero de Uniswap:
Aquí, x y y representan las cantidades de los dos tokens en el pool de liquidez, y k es una constante. Cuando un usuario negocia un token por otro, las cantidades de x y y en el pool cambian, pero su producto k debe permanecer constante. Este mecanismo ajusta dinámicamente el precio: comprar más de token A disminuirá su cantidad en el pool, aumentando así su precio en relación al token B, y viceversa.bonding curve, que dicta los puntos de precio disponibles para las transacciones.
Desde este modelo, es posible calcular deterministicamente el importe de salida (dy) de un swap, dado el importe de entrada (dx) y las reservas pre-swap de los dos tokens (x y y):
Key characteristics of AMMs:
- Siempre en liquidez: A diferencia de los libros de pedidos que pueden llegar a ser delgados, los AMMs siempre ofrecen liquidez siempre que haya tokens en el pool.
- Sin permiso: Cualquiera puede convertirse en proveedor de liquidez o comerciar en un AMM sin necesidad de aprobación.
- Descubrimiento de precios: Los precios se determinan por la proporción de activos dentro del pool, ajustándose con cada comercio.
- Slippage: Las grandes transacciones pueden mover significativamente el precio dentro de un pool, lo que conduce a un fenómeno conocido como slipage, donde el precio ejecutado es peor que el precio citado.
Mientras que el modelo x⋅y=k (a menudo referido como Uniswap V2) puso las bases, los AMMs han evolucionado. Uniswap V3, por ejemplo, introdujo “liquidez concentrada” (CLAMM), permitiendo a los LPs asignar su capital dentro de intervalos de precios específicos. Esto mejoró significativamente la eficiencia del capital, pero también aumentó la complejidad para los LPs y, por lo tanto, para los árbitros que necesitan rastrear la liquidez en varios intervalos.we will primarily focus on AMMsUtilizando la fórmula constante del producto (como las piscinas de estilo Uniswap V2), ya que proporcionan una comprensión fundamental antes de abordar modelos más complejos.
The Essence of Arbitrage in DeFi
El arbitraje, en su forma más pura, es la compra y venta simultánea de un activo en diferentes mercados para beneficiarse de una disparidad en su precio. En DeFi, esto se traduce en explotar las discrepancias de precios entre diferentes pool AMM, o entre un AMM y una Bolsa Centralizada (CEX), para el mismo par de tokens. La naturaleza inherente sin permiso de DeFi y la liquidez fragmentada en varios protocolos crean un terreno fértil para estas oportunidades.
Tipos de oportunidades de arbitraje en DeFi
- Arbitraje simple (Two-Leg): Esta es la forma más sencilla, involucrando dos lugares diferentes. Por ejemplo, si 1 ETH negocia por 2000 DAI en Uniswap A, pero 1 ETH negocia por 2010 DAI en Uniswap B, un árbitro puede comprar ETH en Uniswap A con DAI, y vender inmediatamente ese ETH por DAI en Uniswap B, sacando la diferencia de 10 DAI (menos tarifas de gas y deslizamiento).
- Arbitraje Triangular (Multi-Leg): Este tipo de arbitraje involucra tres o más activos dentro del mismo intercambio (o a través de múltiples intercambios) para formar un ciclo rentable. Por ejemplo, un árbitro podría comenzar con Token A, intercambiarlo por Token B, luego Token B por Token C, y finalmente Token C de vuelta a Token A, terminando con más de Token A de lo que comenzaron. Un ejemplo común de Uniswap podría ser: WETH -> DAI -> USDC -> WETH. Nuestro objetivo principal es implementar y operar el arbitraje Multi-Leg entre diferentes AMM DEX.
- Flash Loan Arbitration: Un aspecto poderoso y único de DeFi, los préstamos flash permiten a los usuarios prestar activos no colateralizados, usarlos para una serie de transacciones (como un arbitraje), y reembolsar el préstamo - todo dentro de una sola transacción blockchain. Si la secuencia entera de operaciones (préstamo, comercio, reembolso) no se puede completar con éxito dentro de esa única transacción, la transacción entera se vuelve, como si nunca hubiera ocurrido. Esto elimina la necesidad de capital anticipado significativo y disminuye significativamente la barrera a la entrada para el arbitraje a gran escala, pero también significa que toda la estrategia de arbitraje debe ser cuidadosamente orquestada dentro de una operación atómica. Incorporaremos una opción de
La carrera por el beneficio: desafíos y competencia
El paisaje DeFi es un mercado altamente eficiente. Las discrepancias de precios son volátiles, a menudo existentes por sólo milisegundos antes de ser explotados por robots sofisticados.
-
Gas Fees: Every interaction with a smart contract incurs a transaction fee (gas), which can vary significantly based on network congestion. A profitable arbitrage opportunity must yield enough profit to cover these costs.
-
Slippage: The larger the trade relative to the pool’s liquidity, the greater the slippage, eroding potential profits. Accurately modeling slippage is crucial for calculating true profitability.
-
Latency: The speed at which an arbitrage bot can detect an opportunity, calculate the optimal trade, construct a transaction, and submit it to the network is paramount. Even milliseconds can make the difference between profit and loss.
-
Frontrunning and MEV: As discussed in the introduction, the “Dark Forest” is dominated by generalized frontrunners. These bots actively monitor the mempool for pending profitable transactions, replicate them, and submit their own transaction with a higher gas price to ensure their transaction is included in a block before the original one. This phenomenon falls under the umbrella of Maximal Extractable Value (MEV), representing the total value that can be extracted from block production in excess of the standard block reward and gas fees by arbitraging, liquidating, or reordering transactions within a block. Successfully navigating this environment often requires advanced strategies like leveraging MEV-Boost relays or private transaction pools. To mitigate the risk of being intercepted in public mempools, our implementation will primarily operate on Base, an EVM-compatible Layer 2 (L2) blockchain. Base’s architecture, which currently does not expose a public mempool in the same manner as Ethereum’s Layer 1, offers a different environment for transaction submission, potentially reducing traditional frontrunning risks.
-
Complexity of AMMs: As AMMs evolve (e.g., Uniswap V3’s concentrated liquidity), the mathematical modeling and state tracking required for accurate arbitrage calculations become significantly more complex.
Understanding these foundational elements of DeFi, from the mechanics of AMMs to the cut-throat nature of arbitrage competition, sets the stage for designing a robust and effective arbitrage bot. In the next chapter, we will begin to lay out the architectural blueprint for such a system.
Diseño arquitectónico: Construir la infraestructura del bot de arbitraje
Construir un bot de arbitraje rentable en el “Dark Forest” de DeFi requiere una arquitectura que priorice la velocidad, la fiabilidad y la precisión. Cada milisegundo cuenta, y la capacidad de procesar datos en tiempo real, identificar oportunidades y ejecutar negocios rápidamente es primordial.Nuestro sistema está diseñado con estos imperativos en su núcleo, aprovechando el modelo de concurrencia de Go y un diseño modular, impulsado por eventos.
Go fue elegido como el lenguaje de desarrollo principal debido a su rendimiento excepcional, robustos primitivos concurrentes (goroutines y canales), y un fuerte ecosistema para la programación de red y las interacciones del sistema de bajo nivel.Estas características son críticas para manejar el alto rendimiento de los datos de blockchain y la necesidad de procesamiento paralelo en un sistema de arbitraje en tiempo real. Además, la eficiencia de Go se demuestra por su adopción en la infraestructura blockchain principal, como:go-ethereum
El cliente principal de Ethereum.
La arquitectura del bot está estructurada como unevent-driven systemcompuesto de varios servicios independientes (modulos), cada uno de los cuales se ejecuta en procesos paralelos (goroutines). Estos servicios se comunican de forma asíncrona mediante el envío de mensajes a través de los canales Go, lo que garantiza un diseño ligeramente acoplado y altamente responsivo.
Arquitectura general del sistema
La infraestructura del bot de arbitraje se puede visualizar como una tubería, donde los datos fluyen de la blockchain, se procesan y analizan, y culmina en la ejecución de transacciones lucrativas.
-
Blockchain Data Reader Service: Responsible for real-time ingestion of blockchain events data.
-
Market Graph Service: Maintains an in-memory representation of the DeFi market and identifies arbitrage paths.
-
Arbitrage Strategy Service: Evaluates detected opportunities for profitability and prepares trade instructions.
-
Transaction Builder Service: Constructs and dispatches blockchain transactions.
-
Honeywall Service: A post-execution checker that enhances security and maintains market integrity by identifying and blacklisting malicious pools.
This modularity allows each service to focus on a specific task, minimizing dependencies and optimizing performance for its particular workload. Communication between services is strictly asynchronous, leveraging Go’s channels for message passing, which naturally facilitates a non-blocking and highly concurrent operation.
Blockchain Data Reader Service: Los ojos y oídos de nuestro bot en el flujo de datos
Este servicio actúa como la interfaz principal del bot con los datos crudos en tiempo real que fluyen a través de la blockchain. En el “Dark Forest”, la información es moneda, y nuestra capacidad de ingerirla rápidamente y con precisión es primordial.extract crucial financial data pointsque alimentará nuestro motor de decisión de arbitraje.
- Conexión y ingestión de datos: El lector se conecta a un nodo de blockchain a través de WebSockets. Esta conexión persistente, bidireccional permite la recepción inmediata de nuevos encabezados de bloques y, más importante, los registros de eventos emitidos por contratos inteligentes. El servicio está configurado para escuchar específicamente los eventos de Swap, Mint, Burn y Sync de los contratos inteligentes de Exchange Decentralized (DEX). Estos eventos son cruciales ya que indican cambios en las reservas de los pools de liquidez, afectando directamente los precios de los tokens.
- Nuevos encabezados de bloques: Al suscribirse a nuevos encabezados de bloques, recibimos notificaciones inmediatas de cambios de estado. Cada nuevo bloque representa una imagen instantánea confirmada de la realidad actual de la blockchain, incluyendo nuevas transacciones, saldos actualizados y nuevos estados de pool de liquidez.
func (er *EthereumReader) SubscribePairs() error {
parsedABI := constants.PairAbi
// Set up the filter
query := ethereum.FilterQuery{
Topics: [][]common.Hash{
{
parsedABI.Events["Swap"].ID,
parsedABI.Events["Mint"].ID,
parsedABI.Events["Burn"].ID,
parsedABI.Events["Sync"].ID,
},
},
}
logs := make(chan types.Log)
sub, err := er.ethClient.SubscribeFilterLogs(context.Background(), query, logs)
if err != nil {
return err
}
// Start Routine to read swaps events
log.Println("[READING SWAPS...]")
go func() {
for {
select {
case err = <-sub.Err():
log.Println("[RESET CONNECTION...] Subscription error: ", err)
pairInfo := GraphMessage{
Ok: false,
}
*er.pairChan <- pairInfo
time.Sleep(5 * time.Minute)
er.ethClient = clients.ResetConnection()
er.SubscribePairs()
return
case vLog := <-logs:
start := time.Now()
pairAddress := vLog.Address
if er.filter.IsPairBlackListed(pairAddress.Hex()) {
continue
}
blockNumber := vLog.BlockNumber
if blockNumber > er.currentBlockNumber {
// New block detected, reset cache
er.lastUpdatedBlock = nil
er.lastUpdatedBlock = make(map[common.Address]uint64)
er.currentBlockNumber = blockNumber
}
// Check if already updated for this pair in current block
if _, exists := er.lastUpdatedBlock[pairAddress]; exists {
continue
}
t0, t1, f, r0, r1, err := er.getPairDataFromHelper(pairAddress)
if err != nil {
continue
}
dex := f.String()
router, err := constants.GetRouterAddressFromFactory(dex)
if err != nil {
continue
}
// Update cache
er.lastUpdatedBlock[pairAddress] = blockNumber
elapsed := time.Until(start)
pairInfo := GraphMessage{
Ok: true,
DexCheck: true,
Pair: pairAddress.Hex(),
Token0: Token{Address: t0.Hex()},
Token1: Token{Address: t1.Hex()},
Reserve0: r0,
Reserve1: r1,
Dex: router,
GetTime: elapsed,
}
*er.pairChan <- pairInfo
}
}
}()
return nil
}
- Contrato inteligente personalizado para agregar y pre-filtrar datos: Para optimizar la eficiencia y reducir las llamadas redundantes en cadena, el Reader utiliza un contrato inteligente personalizado, escrito específicamente para este propósito. Este contrato sirve como un agregador, proporcionando una llamada única y optimizada para recuperar las reservas y otra información agregada para múltiples pares de liquidez. Una funcionalidad clave de este contrato personalizado es su pre-verificación integrada para las características comunes de estafa o los impuestos de comercio excesivos dentro de un pool antes de devolver los datos. Esta filtración preliminar reduce significativamente el riesgo de interactuar con contratos maliciosos en el futuro, actuando como una primera línea de defensa contra piscinas potencialmente dañinas o no rentables.
- La lógica central reside en el método checkPair, que evalúa la seguridad de un par de tokens y devuelve datos agregados.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract ArbHelperMap {
mapping(address => address) public factoryToRouter;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
constructor() {
owner = msg.sender;
// Pre-populate known mappings
factoryToRouter[0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6] = 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24;
factoryToRouter[0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E] = 0x8cFe327CEc66d1C090Dd72bd0FF11d690C33a2Eb;
factoryToRouter[0xFDa619b6d20975be80A10332cD39b9a4b0FAa8BB] = 0x327Df1E6de05895d2ab08513aaDD9313Fe505d86;
factoryToRouter[0x71524B4f93c58fcbF659783284E38825f0622859] = 0x6BDED42c6DA8FBf0d2bA55B2fa120C5e0c8D7891;
factoryToRouter[0x3E84D913803b02A4a7f027165E8cA42C14C0FdE7] = 0x8c1A3cF8f83074169FE5D7aD50B978e1cD6b37c7;
factoryToRouter[0x9A9A171c69cC811dc6B59bB2f9990E34a22Fc971] = 0x1b7655aa64b7BD54077dE56B64a0f92BCba05b85;
}
function addFactoryRouter(address factory, address router) external onlyOwner {
require(factory != address(0) && router != address(0), "Zero address");
factoryToRouter[factory] = router;
}
struct Result {
bool success;
address token0;
address token1;
address factory;
uint112 reserve0;
uint112 reserve1;
}
// Helper function to get pair data
function _getPairData(address pairAddress) private view returns (
bool success,
address token0,
address token1,
address factory,
uint112 reserve0,
uint112 reserve1,
address router
) {
success = false;
try IPair(pairAddress).token0() returns (address _token0) {
token0 = _token0;
try IPair(pairAddress).token1() returns (address _token1) {
token1 = _token1;
try IPair(pairAddress).factory() returns (address _factory) {
factory = _factory;
try IPair(pairAddress).getReserves() returns (uint112 r0, uint112 r1, uint32) {
reserve0 = r0;
reserve1 = r1;
router = factoryToRouter[factory];
if (router != address(0)) {
success = true;
}
} catch {}
} catch {}
} catch {}
} catch {}
}
// Helper function to check if pair passes tax limit
function _checkTaxLimit(
address router,
address token0,
address token1,
uint amountIn,
uint maxTaxPermille
) private view returns (bool) {
address[] memory path = new address[](2);
path[0] = token0;
path[1] = token1;
try IRouter(router).getAmountsOut(amountIn, path) returns (uint[] memory buyOuts) {
if (buyOuts.length < 2) return false;
address[] memory reversePath = new address[](2);
reversePath[0] = token1;
reversePath[1] = token0;
try IRouter(router).getAmountsOut(buyOuts[1], reversePath) returns (uint[] memory sellOuts) {
if (sellOuts.length < 2) return false;
uint minReturn = amountIn - (amountIn * maxTaxPermille / 1000);
return sellOuts[1] >= minReturn;
} catch {
return false;
}
} catch {
return false;
}
}
function checkPair(address pairAddress, uint amountIn, uint maxTaxPermille) external view returns (Result memory r) {
// Initialize result with default values
r.success = false;
// Skip processing if pair address is zero
if (pairAddress == address(0)) return r;
// Get pair data
bool success;
address token0;
address token1;
address factory;
uint112 reserve0;
uint112 reserve1;
address router;
(success, token0, token1, factory, reserve0, reserve1, router) = _getPairData(pairAddress);
// If we couldn't get pair data or there's no router, return early
if (!success) return r;
// Check tax limits
bool passedTaxCheck = _checkTaxLimit(router, token0, token1, amountIn, maxTaxPermille);
// Populate result if tax check passed
if (passedTaxCheck) {
r.success = true;
r.token0 = token0;
r.token1 = token1;
r.factory = factory;
r.reserve0 = reserve0;
r.reserve1 = reserve1;
}
return r;
}
}
- Comunicación basada en eventos: Al recibir y procesar estos eventos, el Reader normaliza los datos y envía actualizaciones (por ejemplo, nuevos valores de reserva para un grupo específico) como mensajes a través de un canal Go al Servicio de gráficos de mercado.
Servicio de gráficos de mercado: mapear el mercado de DeFi
El Market Graph Service es la unidad central de inteligencia, que mantiene una representación en tiempo real, en memoria del mercado DeFi.directed graphy donde:
- Nodos: Representan las criptomonedas individuales (por ejemplo, WETH, USDC, DAI).
- Edges: Representa piscinas de liquidez en varios DEXes (por ejemplo, Uniswap V2 ETH/DAI pool, SushiSwap USDC/WETH pool). Cada borde está asociado con la tasa de cambio actual (implicada por las reservas) para el par de tokens que conecta.
- Estructura de datos y actualizaciones: Este servicio recibe actualizaciones del servicio Blockchain Data Reader a través de canales. Al recibir nuevos datos de reserva para un pool, actualiza el borde correspondiente en el gráfico.
- Precisión con BigInt: Todos los cálculos que involucran las cantidades de token y las tasas de cambio utilizan el paquete matemático / grande de Go (BigInt o BigFloat). Esto es crucial para mantener la precisión arbitraria, evitando inexactitudes de puntos flotantes que podrían conducir a oportunidades perdidas o cálculos de ganancias incorrectos. Esto es especialmente vital dada la naturaleza heterogénea de los importes en DeFi, que pueden abarcar de 8 a 18 (o más) dígitos significativos, haciendo inadecuada la aritmética de puntos flotantes estándar.
- Detección del Camino de Arbitraje: El algoritmo de Bellman-Ford: En el corazón de este servicio está la función FindArbitrage, que emplea un algoritmo de cruce de gráficos, específicamente Bellman-Ford. Este algoritmo es únicamente capaz de encontrar ciclos negativos dentro de un gráfico, que es precisamente lo que corresponde a una oportunidad de arbitraje en nuestro modelo de mercado (donde las tasas de cambio logarítmicas se utilizan como pesos de borde). A diferencia de muchos otros algoritmos de teoría de gráficos que se centran en encontrar el camino más eficiente, la capacidad de Bellman-Ford de detectar ciclos negativos lo hace excepcionalmente eficiente para aplicaciones tanto de DeFi como de finanzas cuantitativas donde se busca el beneficio de discrepancias cíclicas.
Servicio de Estrategia de Arbitraje: Identificar y optimizar los beneficios
Suscríbete a los eventos actualizados de laMarket Graph Service, el Servicio de Estrategia de Arbitraje monitora continuamente el gráfico del mercado para las rutas de arbitraje recién detectadas.
- Evaluación de la oportunidad: Cada vez que se actualiza el gráfico o se identifica un camino de arbitraje potencial por FindArbitrage, este servicio entra en acción.
- Cálculo óptimo del importe de entrada (Optimización convexa): Un paso crítico es determinar el importe de entrada óptimo (dx) para la secuencia de arbitraje. Este es un problema no trivial, ya que la rentabilidad es una función no lineal del importe de entrada, como se demuestra en el artículo 'Un análisis de los mercados Uniswap'. Se ve afectado por el deslizamiento y las tarifas a través de múltiples swaps. El servicio resuelve esto como un problema de optimización convexa, utilizando el paquete gonum/optimize de Go. Esto asegura que el importe de entrada elegido maximice el beneficio neto después de contabilizar todas las variables.
- Simulación de swaps: Antes de comprometerse a una transacción, el servicio realiza una ejecución simulada de todos los swaps dentro del camino de arbitraje detectado utilizando la fórmula constante del producto y la cantidad de entrada óptima calculada. Durante esta simulación, también se establecen cantidades mínimas de salida para cada paso de swap intermedio. Esto asegura que en caso de salida real inesperadamente baja (por ejemplo, debido a fluctuaciones repentinas de precios o alto deslizamiento en la cadena), la transacción se revertirá con pérdida mínima de gas, en lugar de proceder con un comercio no rentable o perdedor.
- Todas las tarifas: Incluidas las tarifas de negociación DEX (por ejemplo, 0,3% para Uniswap V2).
- Slippage: Modelando con precisión el impacto del precio de cada comercio dentro de la secuencia.
- Coste del gas: Estimación de las tarifas de gas necesarias para toda la transacción, teniendo en cuenta la cadena (base) y las condiciones actuales de la red.
- Sólo si el beneficio neto calculado es al menos el 0,5% del importe inicial de entrada (o un umbral configurable) es la oportunidad considerada viable.
- Notificación para Ejecución: Si una oportunidad rentable cumple los criterios, el Servicio de Estrategia de Arbitraje compila todos los detalles necesarios - la secuencia ordenada de swaps (edges), la cantidad de entrada óptima y cualquier otro parámetro relevante - y envía una notificación a través de un canal Go al Servicio de Constructor de Transacciones.
Servicio de constructor de transacciones: ejecución rápida
El Transaction Builder Service es el brazo de ejecución del bot, encargado de construir rápidamente y enviar la transacción de arbitraje a la blockchain.
- Construcción de transacciones: Al recibir una oportunidad del Servicio de Estrategia de Arbitraje, este servicio inmediatamente comienza a construir la transacción de blockchain atómica.
- Interacción de contratos inteligentes (intercambios atómicos): Este servicio interactúa con un contrato inteligente personalizado diseñado específicamente para ejecutar todas las operaciones de arbitraje (intercambios múltiples) dentro de una sola transacción atómica. Este contrato también maneja las aprobaciones de token dentro del mismo flujo de transacciones.
- Aquí está la función Solidity que gestiona una ejecución de arbitraje sin un préstamo flash, que requiere que el propietario (el bot) financie la cantidad inicialIn:
struct SwapStep {
address router;
address[] path;
uint minOut;
}
function executeArb(
address inputToken,
uint amountIn,
SwapStep[] calldata steps,
uint minFinalOut
) external onlyOwner returns (uint finalAmountOut) {
require(steps.length > 0, "No steps");
// Transfer tokens from msg.sender to contract
require(IERC20(inputToken).transferFrom(msg.sender, address(this), amountIn), "Transfer in failed");
address currentToken = inputToken;
uint currentAmount = amountIn;
for (uint i = 0; i < steps.length; i++) {
SwapStep calldata step = steps[i];
require(step.path[0] == currentToken, "Path mismatch");
address outputToken = step.path[step.path.length - 1];
// Save balance before swap
uint balanceBefore = IERC20(outputToken).balanceOf(address(this));
// Safe approve
require(IERC20(currentToken).approve(step.router, 0), "Reset approve failed");
require(IERC20(currentToken).approve(step.router, currentAmount), "Approve failed");
IUniswapV2Router(step.router).swapExactTokensForTokens(
currentAmount,
step.minOut,
step.path,
address(this),
block.timestamp
);
uint balanceAfter = IERC20(outputToken).balanceOf(address(this));
uint received = balanceAfter - balanceBefore;
require(received >= step.minOut, "Slippage too high");
currentToken = outputToken;
currentAmount = received;
}
require(currentAmount >= minFinalOut, "Final output too low");
require(IERC20(currentToken).transfer(owner, currentAmount), "Final transfer failed");
return currentAmount;
}
- Integración de préstamos flash: Si el importe óptimo para el arbitraje requiere un préstamo flash, el constructor integra la lógica de préstamos flash (préstamo → ejecutar swaps → reembolsos) en una sola transacción indivisible, utilizando un contrato personalizado que facilita esta operación atómica a través de la interfaz FlashLoanSimple de Aave.
- Aquí está la función de contrato de solidez ejecutarOperación (parte de un contrato de FlashLoanReceiver más grande) que es llamado por la Aave Pool y contiene la lógica de arbitraje usando los fondos prestados:
function startArbitrage(
address token,
uint256 amount,
SwapStep[] calldata steps,
uint256 minFinalOut
) external onlyOwner {
bytes memory params = abi.encode(steps, minFinalOut);
POOL.flashLoanSimple(address(this), token, amount, params, 0);
}
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external override returns (bool) {
require(msg.sender == address(POOL), "Untrusted lender");
require(initiator == address(this), "Unauthorized initiator");
(SwapStep[] memory steps, uint256 minFinalOut) = abi.decode(params, (SwapStep[], uint256));
// Execute the arbitrage
address currentToken = asset;
uint currentAmount = amount;
for (uint i = 0; i < steps.length; i++) {
SwapStep memory step = steps[i];
require(step.path[0] == currentToken, "Path mismatch");
address outputToken = step.path[step.path.length - 1];
// Save balance before swap
uint balanceBefore = IERC20(outputToken).balanceOf(address(this));
// Safe approve
require(IERC20(currentToken).approve(step.router, 0), "Reset approve failed");
require(IERC20(currentToken).approve(step.router, currentAmount), "Approve failed");
IUniswapV2Router(step.router).swapExactTokensForTokens(
currentAmount,
step.minOut,
step.path,
address(this),
block.timestamp
);
uint balanceAfter = IERC20(outputToken).balanceOf(address(this));
uint received = balanceAfter - balanceBefore;
require(received >= step.minOut, "Slippage too high");
currentToken = outputToken;
currentAmount = received;
}
require(currentAmount >= amount + premium, "Insufficient profit");
require(currentAmount >= minFinalOut, "Final output too low");
// Repay the loan
require(IERC20(asset).approve(address(POOL), amount + premium), "Approval failed");
// Transfer profits to owner
uint profit = IERC20(asset).balanceOf(address(this)) - (amount + premium);
if (profit > 0) {
require(IERC20(asset).transfer(owner, profit), "Profit transfer failed");
}
return true;
}
- Estimación y precio del gas: Estima dinámicamente el gas necesario para la transacción y establece un precio adecuado del gas (o tarifa de prioridad en L2 como Base) para garantizar la inclusión oportuna en un bloque.
- Transaction Dispatch: Una vez construida, la transacción firmada se envía al nodo blockchain de Base. La elección de Base es estratégica: a diferencia de Ethereum L1, la arquitectura actual de Base no cuenta con un mempool visible públicamente en el sentido tradicional. Esto significa que las transacciones son menos susceptibles a frontrunning generalizado directo por bots que escanean el mempool. Mientras que el Valor Extractable Máximo (MEV) todavía existe en L2, los mecanismos para su extracción difieren de L1, y el sniping de mempool directo se reduce significativamente, ofreciendo un entorno de ejecución más predecible para los arbitrajes.
- Feedback asíncrono: Después de enviar la transacción, el servicio envía una notificación al Servicio Honeywall para señalar que una transacción ha sido iniciada y requiere monitorización.
Servicio de Honeywall: Validación y seguridad después de la ejecución
El Servicio Honeywall actúa como un verificador crítico de post-execución y una capa de seguridad robusta para el bot de arbitraje.
- Monitorización de resultados de transacciones: Después de que el constructor de transacciones envíe una transacción, el servicio de Honeywall monitora su inclusión en un bloque y su resultado.
- Registro de ganancias: Si la transacción es exitosa y produce un beneficio (como se esperaba de la simulación), los detalles de los beneficios se registran para el seguimiento y análisis del rendimiento.
- Análisis de fallos: En caso de fracaso de la transacción, Honeywall analiza la razón del reverso.
- Detección de Honeypot / Scam y Blacklisting: Una característica clave de seguridad es su capacidad para identificar tokens "honeypot" o piscinas que implementan lógica engañosa (por ejemplo, permitiendo compras pero evitando ventas, o imponiendo impuestos ocultos excesivos sobre ventas).
- Integración de proveedores externos: se integra con un proveedor externo o una base de datos de contratos conocidos para referenciar los pares utilizados en transacciones fallidas, identificando así posibles fraudes.
- Listado negro dinámico: Si un par o un grupo específico se identifica como un pot de miel o problemático debido a impuestos inesperadamente altos, se añade inmediatamente a una lista negra respaldada por la base de datos.
- Integración del filtro de Bloom: Esta lista negra se gestiona de manera eficiente a través de un mecanismo de filtro de Bloom. Esto permite al Servicio de Reader de Datos de Blockchain comprobar rápidamente los pares recién observados contra la lista negra antes de incluso recuperar sus reservas o agregarlas al gráfico del mercado.
Conclusión del diseño arquitectónico
La arquitectura modular y impulsada por eventos implementada en Go, combinada con servicios especializados para la ingestión de datos, modelado de mercado, optimización de oportunidades, ejecución rápida y seguridad robusta, constituye la espina dorsal de nuestro bot de arbitraje de alto rendimiento.
Detección de oportunidad y ejecución óptima: el cerebro del bot
La verdadera inteligencia de un bot de arbitraje radica en su capacidad de identificar rápidamente y con precisión oportunidades rentables dentro de un mercado en constante cambio, y luego optimizar la ejecución para obtener los rendimientos máximos.Este capítulo profundiza en los algoritmos básicos y modelos matemáticos que impulsan el proceso de toma de decisiones de nuestro bot, desde el mapeo del mercado como un gráfico hasta el cálculo preciso de los tamaños de comercio óptimos y la simulación de los resultados.
Modelando el mercado de DeFi como un gráfico
Como se introdujo en la revisión arquitectónica, nuestro servicio de gráficos de mercado representa el paisaje de DeFi como un gráfico dirigido.nodes, mientras que los grupos de liquidez en varias Bolsas Descentralizadas (DEX) actúan comoedgesEl peso de cada borde representa el coste de la transacción a través de ese pool.
Para detectar de manera eficiente las oportunidades de arbitraje, que se manifiestan como ciclos rentables, transformamos el problema de encontrar una secuencia rentable de comercios en encontrar unanegative cycleEsta transformación se logra aplicando una función logarítmica a los tipos de cambio.
La necesidad de los logaritmos para la detección de ciclos
La idea básica detrás del arbitraje es multiplicar un importe inicial por una serie de tasas de cambio para terminar con más del activo original.TokenX
y el comercio paraTokenY
, then TokenY
porTokenZ
Y por finTokenZ
Volver aTokenX
El importe final sería:
Trabajar con productos en algoritmos de gráficos es complicado.Una técnica común en finanzas computacionales para transformar problemas multiplicativos en aditivos es aplicar un logaritmo. Al tomar el logaritmo natural de cada tipo de cambio, el producto se convierte en una suma:
Ahora, para un ciclo rentable, necesitamosln(Finales) > ln(A), lo que significaln(RateX→Y) + ln(RateY→Z) + ln(RateZ→X) > 0Sin embargo, los algoritmos típicos de camino más corto (como Bellman-Ford, que usamos) están diseñados para encontrar caminos con un mínimo desumoPara hacer que un ciclo rentable aparezca como un "ciclo negativo" en nuestro gráfico, simplemente negamos las tasas logarítmicas:
ln(RateX→Y) + ln(RateY→Z) + ln(RateZ→X) > 0Con esta transformación, una suma de pesos negativos que resulta en un valor negativo (es decir, un ciclo negativo) indica directamente una oportunidad de arbitraje rentable.
Gestionar con precisiónBigInt
Las cantidades de tokens en DeFi pueden variar enormemente, desde pequeñas fracciones (por ejemplo, para tokens ERC-20 con 18 lugares decimales) hasta números muy grandes (por ejemplo, stablecoins).Esta extrema heterogeneidad en magnitud, que abarca hasta 18 cifras significativas, hace que la aritmética de punto flotante estándar sea altamente susceptible a errores de precisión.
Para superar esto, nuestro servicio de gráficos de mercado y, de hecho, todos los cálculos que involucran los importes de token y las tasas de cambio dentro del bot, utilizan la tecnología de Go.math/big
El paquete, en particularBigInt
Para la aritmética entera yBigFloat
para las operaciones de punto flotante cuando sea necesario.BigFloat
Ofrece precisión arbitraria, aplicandolog
DosBigInt
oBigFloat
los valores requieren un manejo cuidadoso, como estándarmath.Log
Funciones que funcionan en nativofloat64
Implementaciones personalizadas o bibliotecas externas capaces de logaritmos de precisión arbitraria son esenciales aquí.
func getLogRate(reserve0, reserve1 *big.Int) *big.Float {
const prec = 1024
resIn := new(big.Float).SetPrec(prec).SetInt(reserve0)
resOut := new(big.Float).SetPrec(prec).SetInt(reserve1)
// Effective Rate
rate := new(big.Float).SetPrec(prec).Quo(resOut, resIn)
logRate := bigfloat.Log(rate)
return logRate.Neg(logRate)
}
Detección del camino de arbitraje: El algoritmo de Bellman-Ford
Una vez que el mercado DeFi es modelado con precisión como un gráfico con pesos de borde negativo logarítmico, la tarea de encontrar oportunidades de arbitraje se reduce a identificarnegative cyclesEn este gráfico, para ello, utilizamos elBellman-Ford algorithm.
Nominado después de Richard Bellman y Lester Ford Jr., Bellman-Ford es un versátil algoritmo de camino más corto capaz de manejar gráficos con pesos de borde negativo. A diferencia del algoritmo de Dijkstra, que falla en presencia de ciclos negativos, Bellman-Ford está diseñado específicamente para detectarlos. Su importancia histórica se extiende más allá de la informática teórica; ha encontrado aplicaciones en diversos campos, incluyendo el enrutamiento de red (donde ayuda a encontrar los caminos más baratos con costes variables) y, críticamente, en la financiación cuantitativa para identificar oportunidades de comercio rentables en los mercados de divisas.
El algoritmo funciona relajando iterativamente los bordes, encontrando progresivamente caminos más cortos a todos los nodos desde una fuente. Si, después de las iteraciones de V−1 (donde V es el número de vértices), una N-tercera iteración adicional encuentra un camino que todavía puede ser “relajado” (es decir, se puede encontrar un camino más corto), indica la presencia de un ciclo negativo. Esta propiedad lo hace perfecto para nuestro caso de uso: un ciclo negativo implica una secuencia de comercios que resulta en un beneficio neto, exactamente lo que busca un bot de arbitraje.
type Edge struct {
Pair string
From Token
To Token
LogRate *big.Float
Reserve0 *big.Int
Reserve1 *big.Int
Dex string
MinOut *big.Int
}
type Graph struct {
nodes map[string]Token
Edges map[string][]*Edge
pairChan *chan GraphMessage
dexCheckChan *chan DexDexMessage
subscriptions []*chan time.Duration
mu sync.RWMutex
}
// Bellman-Ford algorithm to find arbitrage cycles
func (g *Graph) FindArbitrage(source Token) ([]*Edge, bool) {
sourceKey := source.Address
g.mu.RLock()
defer g.mu.RUnlock()
distance := make(map[string]*big.Float)
predecessor := make(map[string]*Edge)
// 1. Init
for token := range g.nodes {
distance[token] = new(big.Float).SetInf(false)
}
distance[sourceKey] = new(big.Float).SetFloat64(0)
// 2. Relax edges V-1 times
for i := 0; i < len(g.nodes)-1; i++ {
for _, edgeList := range g.Edges {
for _, e := range edgeList {
from := e.From.Address
to := e.To.Address
if !distance[from].IsInf() && new(big.Float).Add(distance[from], e.LogRate).Cmp(distance[to]) < 0 {
distance[to].Add(distance[from], e.LogRate)
predecessor[to] = e
}
}
}
}
// 3. Negative cycle detection
var cycleStartToken string
for _, edgeList := range g.Edges {
for _, e := range edgeList {
from := e.From.Address
to := e.To.Address
if !distance[from].IsInf() && new(big.Float).Add(distance[from], e.LogRate).Cmp(distance[to]) < 0 {
cycleStartToken = to
break
}
}
if cycleStartToken != "" {
break
}
}
if cycleStartToken == "" {
return nil, false // No Arbitrage
}
// 4. detect first cycle node
visited := make(map[string]bool)
current := cycleStartToken
for !visited[current] {
visited[current] = true
edge := predecessor[current]
if edge == nil {
return nil, false // missing edge
}
current = edge.From.Address
}
// 5. Complete cycle
cycleStart := current
cycle := []*Edge{}
for {
edge := predecessor[current]
if edge == nil {
return nil, false // missing edge
}
cycle = append(cycle, edge)
current = edge.From.Address
if current == cycleStart {
break
}
}
// 6. Invert cycle
for i, j := 0, len(cycle)-1; i < j; i, j = i+1, j-1 {
cycle[i], cycle[j] = cycle[j], cycle[i]
}
return cycle, true
}
Cálculo de la cantidad de entrada óptima: maximizar el beneficio
Una vez que se identifica un ciclo negativo (oportunidad de arbitraje), el siguiente paso crítico es determinar eloptimal input amountEsto no es arbitrario; la rentabilidad de una oportunidad de arbitraje es una función no lineal del tamaño del comercio debido al deslizamiento inherente y las tarifas asociadas con los swaps AMM.
Como se detalla en “Un análisis de los mercados de Uniswap”, la fórmula de producto constante implica inherentemente una convexidad en la relación entre los importes de entrada y salida. En concreto, a medida que el tamaño del comercio aumenta, la tasa de cambio efectiva empeora debido a la invariancia del pool. Esto significa que hay un punto dulce: una cantidad demasiado pequeña puede no cubrir las tarifas de gas, mientras que una cantidad demasiado grande puede incurrir en un deslizamiento excesivo, erosionando los beneficios.
El problema de maximizar el beneficio es unconvex optimization problemPara una serie de N swaps en un camino de arbitraje, el importe final de salida (y por lo tanto el beneficio) se puede expresar como una función del importe de entrada inicial (dx ). Mientras que la solución analítica exacta para el arbitraje de múltiples patas puede ser compleja, especialmente con estructuras de tarifas variables y curvas de deslizamiento en diferentes AMMs, la función que representa el beneficio menos costes (incluyendo gas) generalmente es convexa.
Nuestro Servicio de Estrategia de Arbitraje aborda esto empleando un solucionador de optimización de Go'sgonum/optimize
Este solvente toma una función que representa el beneficio neto (beneficio de swaps menos tarifas estimadas de gas y cualquier préstamo de préstamo de flash) y encuentra el importe de entrada que maximiza este valor.amountOut
La fórmulady = (x + dx) / (dx⋅ y)
para cada paso en el camino de arbitraje, contabilizando las reservas intermedias, las tasas y el deslizamiento en cada etapa.
func getOptimalAmoutIn(edges []*Edge, decimals int) (*float64, error) {
factor := math.Pow10(decimals)
intMax, _ := constants.GetRouterReserveFromToken(edges[0].From.Address)
maxCapital := new(big.Float).Mul(new(big.Float).SetInt64(intMax), big.NewFloat(factor))
fee := big.NewFloat(0.997)
problem := optimize.Problem{
Func: func(x []float64) float64 {
delta := big.NewFloat(x[0])
if delta.Cmp(big.NewFloat(0)) < 0 || delta.Cmp(maxCapital) > 0 {
return math.Inf(1)
}
delta_i := new(big.Float).Set(delta)
for _, edge := range edges {
effectiveIn := new(big.Float).Mul(delta_i, fee)
reserveIn := new(big.Float).SetInt(edge.Reserve0)
reserveOut := new(big.Float).SetInt(edge.Reserve1)
num := new(big.Float).Mul(reserveOut, effectiveIn)
denom := new(big.Float).Add(reserveIn, effectiveIn)
delta_i = new(big.Float).Quo(num, denom)
}
profit := new(big.Float).Sub(delta_i, delta)
result, _ := profit.Float64()
return -result
},
}
result, err := optimize.Minimize(problem, []float64{1.0}, nil, nil)
if err != nil {
return nil, err
}
return &result.X[0], nil
}
Simulación de swaps y evaluación de la rentabilidad
Antes de que se envíe cualquier transacción, el Servicio de Estrategia de Arbitraje realiza una minuciosasimulated executionEste paso es crucial para verificar la rentabilidad real, dado las condiciones de mercado en tiempo real y los parámetros exactos del comercio propuesto.
La simulación utiliza las reservas actuales de las piscinas de liquidez involucradas y la cantidad de entrada óptima calculada. Para cada paso en el camino de múltiples patas, se aplica la fórmula específica AMM (por ejemplo, la fórmula de producto constante para piscinas similares a Uniswap V2) para calcular la salida esperada:
func (ab *ArbitrageBuilderV2) calculateProfitabilityWithSlippage(edges []*Edge, decimals int) (*big.Float, *big.Float, error) {
opt, err := getOptimalAmoutIn(edges, decimals)
if err != nil {
return nil, nil, err
}
optBig := new(big.Float).SetFloat64(*opt)
amount := new(big.Float).Set(optBig)
fee := big.NewFloat(0.997)
for _, edge := range edges {
if edge.Reserve0 == nil || edge.Reserve1 == nil ||
edge.Reserve0.Cmp(big.NewInt(0)) == 0 || edge.Reserve1.Cmp(big.NewInt(0)) == 0 {
return nil, nil, errors.New("edge has invalid reserves")
}
reserveIn := new(big.Float).SetInt(edge.Reserve0)
reserveOut := new(big.Float).SetInt(edge.Reserve1)
amountInWithFee := new(big.Float).Mul(amount, fee)
if amountInWithFee.Cmp(reserveIn) >= 0 {
return big.NewFloat(-1.0), nil, errors.New("amount exceeds available reserves")
}
// "x * y = k"
numerator := new(big.Float).Mul(reserveOut, amountInWithFee)
denominator := new(big.Float).Add(reserveIn, amountInWithFee)
amountOut := new(big.Float).Quo(numerator, denominator)
amount = amountOut
}
profit := new(big.Float).Sub(amount, optBig)
profit.Sub(profit, ab.EstimateGasCost(len(edges)))
profit.Sub(profit, new(big.Float).Mul(optBig, big.NewFloat(0.005)))
normalizedProfit := new(big.Float).Quo(profit, new(big.Float).SetFloat64(math.Pow10(decimals)))
return normalizedProfit, optBig, nil
}
La simulación también incluyeminimum output amount (minOut)controles para cada paso intermedio.minOut
Los valores se derivan de los resultados esperados simulados y se establecen como parámetros en la transacción real en la cadena.Si, debido a la latencia de la red, la precariedad o las condiciones de mercado inesperadas, un rendimiento real de un swap en la cadena es menor que su rendimiento especificadominOut
Este mecanismo es una salvaguardia vital, impidiendo que el bot complete una secuencia no rentable de transacciones y limitando las pérdidas a sólo el gas gastado en la transacción revertida.
Sólo si el beneficio neto final, después de todas las tarifas, los deslizamientos, los costes del gas y las primas del préstamo flash, excede un importe predefinido.profit threshold(por ejemplo, 0,5% del importe de entrada inicial) es la oportunidad considerada viable y pasada al Servicio de Transacciones para ejecución. Este umbral garantiza que el bot sólo persigue oportunidades con un margen significativo suficiente para justificar los costes computacionales y en cadena.
Ingeniería de transacciones: ejecución rápida en el bosque oscuro
Identificar una oportunidad de arbitraje rentable es sólo la mitad de la batalla; la otra, quizás más crítica, la mitad está en la capacidad de ejecutar el comercio con una velocidad y fiabilidad sin igual.En el hiper-competitivo “Dark Forest” de DeFi, donde las oportunidades son fugaces y los robots sofisticados viven por cada milisegundo, la ingeniería de transacciones se convierte en una forma de arte.Transaction Builder Servicediseñado para garantizar una ejecución rápida y segura.
El imperativo de la velocidad
La ventana de rentabilidad para las oportunidades de arbitraje en los intercambios descentralizados a menudo se mide en milisegundos. Las discrepancias de precios son rápidamente detectadas y explotadas por numerosos sistemas automatizados, creando una feroz carrera para ser el primero en incluir una transacción rentable en un nuevo bloque. Cualquier retraso, por menor que sea, puede resultar en que la oportunidad sea aprovechada por un competidor, lo que conduce a una transacción fallida y gastos de gas desperdiciados.
Optimización de la memoria para la construcción de transacciones instantáneas
Para lograr la velocidad necesaria, nuestro sistema prioriza tener todos los componentes de transacción esenciales fácilmente disponibles en la memoria, eliminando costosas operaciones de I/O o llamadas en cadena durante la fase crítica de construcción de transacciones.
- ABI preparsados y empacados: Aplicación de contratos inteligentes Interfaces binarias (ABI) definen cómo interactuar con contratos. En lugar de analizar las definiciones de ABI y las llamadas de la función de codificación en el vuelo para cada transacción, nuestro sistema preparsado y empacado las estructuras de datos necesarias de ABI y selectores de funciones en arreglos de bytes brutos. Estas secuencias de bytes precompuestas para las interacciones de contratos comunes (por ejemplo, swapExactTokensForTokens, flashLoanSimple, transferFrom) se almacenan en la memoria. Cuando se identifica una oportunidad de arbitraje, el constructor de transacciones puede ensamblar rápidamente los datos de llamada simplemente concatenando estos componentes pre-empacados con los
- Datos en cadena en caché para campos de transacciones: Para evitar las llamadas redundantes en cadena a metadatos de transacciones, una estructura de utilidad dedicada dentro del bot mantiene valores críticos, actualizados con frecuencia en la memoria:
- El noce (número de transacciones enviadas desde una dirección) es crucial para prevenir ataques de reproducción y asegurar el orden de transacciones. Se recoge una vez y luego se gestiona incrementalmente en la memoria, con robusto manejo de errores para re-sincronizar si una transacción falla o se incluye inesperadamente fuera de orden.
- Parámetros óptimos de gas: En lugar de interrogar a la red sobre los precios del gas (o las tarifas básicas / tarifas de prioridad en las cadenas EIP-1559) para cada transacción, el bot recoge periódicamente los parámetros óptimos del gas.
- Información de firma: La clave privada de la cartera del bot y el objeto de firma asociado (utilizado para la firma criptográfica de transacciones) se cargan en la memoria cuando se inicia.
Al mantener estos componentes vitales en la memoria, el Transaction Builder Service puede construir y firmar una transacción blockchain completa en sólo microsegundos, listo para el envío inmediato.
Selección de contratos inteligentes dinámicos: préstamos flash vs. swaps directos
El Servicio de Estrategia de Arbitraje transmite un camino de arbitraje optimizado y la cantidad de entrada óptima calculada al Constructor de Transacciones.amountIn
y si excede un umbral de capital predefinido (o si la estrategia lo requiere explícitamente), el constructor de transacciones selecciona dinámicamente entre dos contratos inteligentes primarios para ejecución:
- Contrato de ejecución de swap directo: Para oportunidades que pueden ser financiadas directamente por el capital propiedad del bot, el constructor utiliza la función executeArb (o similar) en un contrato proxy multi-swap personalizado. Como se muestra en el Capítulo 3, este contrato toma los tokens de entrada de la cartera del bot y ejecuta la secuencia completa de swaps dentro de una sola transacción atómica.
- Contrato Integrado de préstamos flash: Cuando el importe óptimo calculado para un arbitraje es significativamente mayor que el capital disponible del bot, el constructor se dirige a un contrato inteligente separado, personalizado diseñado para iniciar y gestionar préstamos flash. La función de inicio de arbitraje de este contrato (como se detalla en el Capítulo 3) solicita un préstamo flash de un protocolo como Aave, que luego llama de vuelta la función de ejecución del contrato. Dentro de la ejecución, la secuencia de arbitraje entera se realiza utilizando los fondos prestados, y el préstamo flash (plus una pequeña prima) se reembolsa - todo dentro de esa única transacción de blockchain atómica. Esto permite al bot capitalizar en oportunidades muy grandes y de alto beneficio sin requerir capital adicional sustancial, democratizando
Esta selección dinámica asegura una asignación eficiente de capital y una ejecución óptima de la estrategia basada en las especificidades de cada oportunidad detectada.
Mempool Dynamics: Navegando Ethereum L1 vs. Layer 2 Cadenas
Un aspecto crítico de la ejecución de la arbitraje es comprender el mecanismo de propagación de transacciones de la blockchain, en particular elmempool.
-
Ethereum L1 Mempool: On Ethereum’s Layer 1, the mempool is a public, transparent waiting room for all pending transactions. Transactions broadcasted by users or bots are relayed to various nodes across the network, becoming visible to anyone monitoring the mempool. This transparency is the breeding ground for generalized frontrunning bots (often referred to as “searchers” or “MEV bots”). These sophisticated entities continuously scan the mempool for profitable transactions (e.g., large swaps that cause significant price impact, liquidations, or other arbitrage attempts). Upon detecting such a transaction, they quickly construct an identical or similar transaction, replace the original recipient address with their own, and submit it with a higher gas price (or higher priority fee in EIP-1559) to ensure their transaction is included in the block before the original, thereby stealing the profit. This competitive landscape makes direct arbitrage on L1 highly challenging without leveraging specialized MEV relays.
-
Layer 2 (L2) Chains and Reduced Mempool Visibility (e.g., Base): Our bot strategically operates on Base, an EVM-compatible Layer 2 blockchain. The architecture of many L2s, including Base, fundamentally alters the traditional L1 mempool dynamic. Base does not currently expose a publicly visible mempool in the same manner as Ethereum Layer 1. Instead, transactions are typically sent directly to a centralized sequencer or a private mempool before being batched and committed to the L1.
This architectural difference significantly reduces the direct threat of generalized frontrunning. While MEV still exists on L2s (e.g., through sequencer-controlled ordering or other means), the immediate, public visibility of pending transactions that enables L1 frontrunning is largely absent. This provides a more predictable and secure execution environment for our arbitrage transactions, as the bot’s crafted atomic operations are less likely to be “sniped” before they even reach a block producer. This improved execution predictability contributes directly to higher success rates for profitable arbitrages.
Velocidad y seguridad de los nodos: la base de la ejecución fiable
La conexión al nodo blockchain es el único punto de entrada y salida para todos los datos y transacciones.
- Conexión de nodo de alto rendimiento: El servicio Transaction Builder se conecta a un proveedor de nodo de alto rendimiento dedicado (por ejemplo, Alchemy, Infura, o un nodo auto-hostado). Una conexión rápida y de baja latencia es esencial para minimizar el tiempo entre la firma de una transacción y su propagación a la red. Cualquier retraso de la red aquí se traduce directamente en oportunidades perdidas de arbitraje.
- Seguridad y integridad del nodo: La seguridad del nodo conectado es igualmente vital. Utilizar proveedores de nodo de buena reputación o asegurar un nodo auto-hostado altamente seguro es crucial para prevenir ataques man-in-the-middle o manipulación de datos. Un nodo comprometido podría potencialmente filtrar claves privadas, inyectar transacciones maliciosas o manipular datos, lo que conduce a pérdidas catastróficas. La dependencia de nuestro sistema en puntos finales privados de RPC (si están disponibles de proveedores) y canales de comunicación seguros (https para HTTP, wss para WebSockets) refuerza esta postura de seguridad.
Al optimizar meticulosamente la velocidad en todas las capas, desde las estructuras de datos en memoria y la precomputación hasta la selección estratégica de cadenas y la infraestructura robusta de nodos, nuestro bot de arbitraje está diseñado para superar a los competidores y aprovechar de forma segura las oportunidades volátiles dentro del paisaje DeFi.
Navegar por el bosque oscuro: retos, ética y perspectivas futuras
La construcción y operación de un bot de arbitraje en el DeFi “Dark Forest” es una prueba del poder de las tecnologías descentralizadas, pero también trae a la luz desafíos significativos y consideraciones éticas.
La constante batalla contra los actores maliciosos: el papel de Bloom
El optimismo inicial en torno a la naturaleza sin permiso de DeFi ha sido, por desgracia, atenuado por la proliferación de actores maliciosos.Honeywall Servicesirve como una última línea de defensa vital, pero la ingenuidad de estos malos actores exige constantemente contramedidas evolucionadas.
Una parte importante de esta defensa es laBloom filterUn filtro de Bloom es una estructura de datos probabilística que puede probar rápidamente y eficientemente si un elemento es miembro de un conjunto. Es altamente eficiente en el espacio, pero lleva una pequeña probabilidad de “falsos positivos” (indicando que un elemento está en el conjunto cuando no lo es), aunque nunca “falsos negativos”. En nuestro contexto, el filtro de Bloom se utiliza para pre-filtrar los datos de eventos entrantes del servicio de lector de datos de Blockchain. Contiene hashes de direcciones de parejas de liquidez maliciosas o de alto impuesto conocidas. Antes de cualquier procesamiento detallado o recogida de reservas, una rápida comprobación contra el filtro de Bloom puede descartar inmediatamente los pares problemáticos conocidos, evitando el desperdicio de recursos computacionales y riesgos potenciales.
A pesar de los sofisticados controles previos implementados en nuestra costumbreArbHelperMap
El contrato inteligente (especialmente el_checkTaxLimit
la lógica que simula un intercambio de rondas para evaluar los impuestos), algunas parejas maliciosas todavía logran eludir estas validaciones iniciales en la cadena.getAmountsOut
función (utilizada para las consultas de precios) para devolver resultados aparentemente normales, de bajo impuesto. sin embargo, la verdadera lógica de "honnypot" está embebida más profundamente en elswapExactTokensForTokens
o subyacentetransfer
Estas funciones podrían imponer impuestos ocultos excesivos (por ejemplo, 99%) sobre las operaciones de venta, o incluso restringir completamente las ventas, atrapando efectivamente los fondos.
Durante nuestra fase de pruebas, encontramos un número significativo de parejas engañosas. he recopilado personalmente algunas direcciones de parejas que pasaron con éxito el iniciogetAmountsOut
verifica pero revela impuestos ocultos o restricciones de venta sólo durante una transacción real (simulada o revertida) en una base de datos local. Este archivo db se hará público en el repositorio GitHub del proyecto, sirviendo como un recurso de la comunidad para ayudar a otros a evitar estas trampas.
Implicaciones éticas y la sombra del bosque oscuro
La analogía de “Dark Forest” es apta no solo para la competencia cortada entre los bots, sino también para el paisaje ético más amplio de DeFi. La eficiencia del arbitraje, aunque crucial para la salud del mercado y el descubrimiento de precios, viene con una realidad dura: los beneficios generados por los arbitrajes a menudo representan valor extraído de los participantes de mercado menos sofisticados.
La cultura perversa deFOMO (Fear Of Missing Out), junto con una falta general de comprensión de los mecanismos de blockchain y instrumentos financieros subyacentes, hace que muchos usuarios minoristas se vayan fácilmente a los mercados altamente volátiles, interactúen con nuevos protocolos y ejecuten transacciones sin tener plena conciencia de conceptos como el deslizamiento, el MEV o los impuestos de contratos ocultos.
Esta dinámica, aunque económicamente lógica para aquellos con herramientas avanzadas, arroja una sombra sobre la reputación de las tecnologías descentralizadas. La narración puede cambiar rápidamente de “empoderamiento financiero” a “comportamiento depredador”, erosionando la confianza en un espacio que de otro modo tiene una inmensa promesa. DeFi, en su núcleo, tiene como objetivo democratizar las finanzas, ofreciendo acceso sin permiso y transparencia. Sin embargo, la sofisticada naturaleza de los MEV y la prevalencia de las estafas pueden socavar inadvertidamente estos ideales, creando un sistema de dos niveles donde sólo los conocedores tecnológicos pueden realmente navegar de forma segura. Es imperativo que, como constructores, reconozcamos estas dimensiones éticas y defendamos una mayor educación de los usuarios, auditorías de seguridad robustas
Conclusión: Sigue navegando por el Bosque Oscuro
A pesar de las complejidades inherentes y los desafíos persistentes del paisaje DeFi, el viaje de ingeniería de este bot de arbitraje ha sido una notable validación de principios teóricos que cumplen con la implementación práctica.speed, precision, and data-driven insights, capaz de detectar y ejecutar oportunidades de arbitraje multi-piés.
Inicialmente, una expectativa común dentro del “Dark Forest” era que la gran mayoría del valor de arbitraje sería inmediatamente interceptado por jugadores grandes y bien diseñados, aprovechando los nodos auto-hostados y el acceso directo a los productores de bloques. sin embargo, nuestras pruebas y transacciones exitosas han demostrado que es posible que los bots más pequeños y bien diseñados encuentren y aprovechen consistentemente estas oportunidades efímeras:
Mientras que el arbitraje rentable en modelos AMM más antiguos como Uniswap V2 (que dependen principalmente de pooles de productos constantes) puede ser desafiante para mantener a largo plazo debido a la escalada de los costes del gas y la creciente competencia, el “Dark Forest” continúa evolucionando. Las implementaciones más recientes, como los Concentrated Liquidity AMMs (CLAMMs) de Uniswap V3, introducen nuevos vectores de arbitraje que requieren una modelización más sofisticada, pero a menudo producen retornos más altos debido a la mayor eficiencia del capital. Además, el campo creciente de arbitraje cross-chain, aprovechando puentes y protocolos de comunicación inter-blockchain, presenta una frontera más amplia de discrepancias de precio adicional entre diferentes redes de blockchain. Estas
Así que, mientras todavía soy pobre, puedo decir con confianza que me he convertido en un excelente navegador forestal.My compass is sharper, my map more detailed, and I understand the whispers of the canopy.
Proyecto Repositorio
Para aquellos ansiosos por profundizar en la implementación práctica y los datos muy reales detrás de algunas de las “trampas” que hemos discutido, una versión sanitaria de nuestra base de código y una base de datos poblada con pares de tokens maliciosos conocidos están disponibles en miEl repositorio de GitHubEstos 85 pares específicos, aunque numéricamente pequeños, generan un volumen de transacciones desproporcionadamente significativo ya que continúan tratando de atraer a los bots ingenuos a comercios no rentables.
El repositorio de GitHubReferencias
- Dan Robinson, Georgios Konstantopoulos: “Ethereum es un bosque oscuro”.
- Guillermo Angeris, Hsien-Tang Kao, Rei Chiang, Charlie Noyes y Tarun Chitra. “Un análisis de los mercados Uniswap”, Sistemas Cryptoeconómicos.
- Claudio Gebbia, “Análisis e implementación de robots de arbitraje en finanzas centralizadas y descentralizadas”, Universidad de Zúrich.
- Y. Zhang, Z. Li, T. Yan, Q. Liu, N. Vallarano y C. J. Tessone, “Maximización del beneficio en los circuitos de arbitraje”.