687 讀數
687 讀數

进入黑暗森林:工程工作DeFi仲裁

经过 Simone Convertini45m2025/05/31
Read on Terminal Reader

太長; 讀書

如何构建一个运作的DeFi仲裁机器人,利用AMM之间的价格差异。
featured image - 进入黑暗森林:工程工作DeFi仲裁
Simone Convertini HackerNoon profile picture

1. Introduction —进入黑暗森林

1、介绍 -

某天晚上,投资公司Paradigm的研究员Dan Robinson在Uniswap Discord频道看到了一次紧急电话,有人意外发送了价值大约12000美元的代币。另一个用户,但给Uniswap合同本身,在那里他们似乎无可挽回地失去了。

然而,罗宾逊看到了希望的光芒,他意识到任何人都可以触发希望。burn合同的功能,一个公共命令,这将迫使合同释放被困的代币给呼叫它的人。

承认有机会成为一个白帽英雄,罗宾逊准备了救援交易,但他知道他并不孤单。以太坊池 - 公共等待室等待正在进行的交易 - 是被称为“通用前沿者”的复杂机器人的猎场。

为了超越他们,罗宾逊设计了一个聪明的两部分交易,希望同时挖掘两部分,不留下拦截窗口。它没有起作用。在他的第一笔交易出现的那一刻,一个机器人发现了它,复制了整个策略,并抢走了12000美元,直到罗宾逊的第二笔交易甚至可以得到确认。

在关于这起事件的著名文章中,罗宾逊给了这个敌对的、看不见的生态系统一个名字,他发现了区块链的一个基本真理:Ethereum is a Dark Forest.

在本文中,我们将深入研究能够读取区块链交易的实时操作和数据的系统的设计,以执行仲裁的目标。mathematical models underlying AMM pricing然后,深入到algorithms for opportunity detection and optimal price entry,定义了architectural components我们的博客,并讨论critical strategies需要在这个高风险的环境中成功和安全地执行仲裁。


DeFi景观:AMM,流动性和仲裁机会

在我们的介绍中描述的“黑森林”不仅是一个敌对的环境,它是一个充满活力的生态系统,建立在一个新的金融范式:分散金融(DeFi)。在其核心,DeFi旨在重塑区块链网络上的传统金融服务,消除通过使用自执行智能合约的中介者的需求。

Automated Market Makers (AMMs): The Backbone of Decentralized Exchange

传统的交易所依靠订单簿,买家和卖家提出报价和请求,中央匹配引擎促进交易。DeFi引入了一种截然不同的模式:自动化市场制造商(AMM)。而不是直接匹配买家和卖家,AMM利用流动性池(持有两个或多个代币的储备的智能合约)来促进交易。

AMM池内的资产价格是由一个算法定义的。constant product formula由 Uniswap 创建:

在这里,x和y代表了流动性池中的两个代币的数量,k是一个常数。当用户交易一个代币换一个代币时,池中的x和y的数量会改变,但他们的产品k必须保持不变。这个机制动态地调整价格:购买更多的代币A会在池中减少其数量,从而增加其价格与代币B相对,反之亦然。bonding curve,这决定了交易可用的价格点。

从这个模型中,可以决定性地计算交换的输出金额(dy),考虑到两个代币(x和y)的输入金额(dx)和预交换储备:

Key characteristics of AMMs:

  • 总是流动性:与可能变薄的订单簿不同,AMM总是提供流动性,只要池中有代币。
  • 无许可:任何人都可以成为流动性提供者或在不需要批准的情况下在AMM进行交易。
  • 价格发现:价格由池中资产的比例决定,并与每个交易进行调整。
  • 滑坡:大型交易可以显著地移动池内的价格,导致一个被称为滑坡的现象,其中执行的价格比报价更差。

虽然x⋅y=k模型(常被称为Uniswap V2)奠定了基础,但AMM已经进化了,例如,Uniswap V3引入了“集中流动性”(CLAMM),允许LP在特定价格范围内分配其资本。we will primarily focus on AMMs使用常规产品公式(如Uniswap V2类型池),因为它们在处理更复杂的模型之前提供了基本的理解。

The Essence of Arbitrage in DeFi

仲裁,在其最纯粹的形式,是在不同的市场同时购买和出售资产,以从价格差异中获利。在DeFi中,这意味着利用不同的AMM池之间或AMM和中央交易所(CEX)之间的价格差异,相同的代币对。DeFi的固有无许可性和各种协议的分散流动性为这些机会创造了肥沃的土壤。在这个新兴的金融空间中,高波动性和缺乏监管往往导致了显著的价格差异,这是仲裁者的生命血液。

DeFi中的仲裁机会类型

  • 简单仲裁(Two-Leg):这是最简单的形式,涉及两个不同的场所. 例如,如果1 ETH在Uniswap A上交易2000 DAI,但1 ETH在Uniswap B上交易2010 DAI,仲裁员可以以DAI购买Uniswap A上的ETH,并立即出售该ETH在Uniswap B上的DAI,以10 DAI的差异为口袋(减掉气费和滑坡)。
  • 三角仲裁(Multi-Leg):这种类型的仲裁涉及在同一交易所内(或跨多个交易所)的三个或更多资产,形成一个盈利循环,例如,一个仲裁者可以从代币A开始,用代币B交换,然后代币B用代币C交换,最后代币C返回代币A,最终用代币A的代币A比他们开始的更多。
  • Flash Loan Arbitrage: DeFi的强大而独特的一面,Flash Loans允许用户借用非担保资产,使用它们进行一系列交易(如仲裁),并在单一区块链交易中偿还贷款,如果整个操作序列(借贷,贸易,偿还)无法在单一交易中成功完成,整个交易将被返回,就像它从未发生过一样。这消除了大量前期资本的需要,并显著降低了大规模仲裁的入境障碍,但这也意味着整个仲裁策略必须在单个原子操作中仔细进行编排。

争取利润的竞赛:挑战和竞争

DeFi 景观是一个高效率的市场,价格差异是缓慢的,通常只存在几毫秒钟,然后被复杂的机器人利用。

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

建筑设计:构建仲裁机器人基础设施

在DeFi的“黑森林”中建立一个盈利的仲裁机器人需要一个优先考虑速度,可靠性和精度的架构,每毫秒都很重要,并且处理实时数据,识别机会和快速执行交易的能力至关重要。

Go 被选为主要开发语言,因为其卓越的性能、强大的同步原理(goroutines 和频道),以及网络编程和低层次系统交互的强大生态系统,这些功能对于处理区块链数据的高流量和在实时仲裁系统中进行并行处理至关重要。go-ethereum,主要的以太坊客户端。

机器人的架构是作为一个event-driven system由几个独立的服务(模块)组成,每一个在并行流程(goroutines)中运行,这些服务通过通过Go渠道发送消息以无同步的方式通信,确保一个宽松的连接和高度响应的设计,这种方法允许有效的资源利用,简化故障隔离,并允许单个组件无缝扩展。

总体系统架构

仲裁机器人的基础设施可以被视为一条管道,其中数据从区块链中流动,被处理和分析,并 culminates在执行盈利的交易。

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

区块链数据阅读器服务:数据流中的我们的机器人的眼睛和耳朵

在“黑暗森林”中,信息是货币,我们快速准确地摄入信息的能力至关重要。extract crucial financial data points这将为我们的仲裁决策引擎提供动力。

  • 连接和数据摄入:Reader通过WebSockets连接到区块链节点。这种持久的双向连接允许立即接收新的区块头,更重要的是,智能合约发出的事件日志。该服务配置为专门听取分散交易所(DEX)智能合约的Swap,Mint, Burn和Sync事件。
  • 新区块头:通过订阅新的区块头,我们会立即收到状态变化的通知,每个新区块代表了区块链的当前现实的确认截图,包括新的交易,更新的余额和新的流动性池状态。
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
}
  • 用于数据聚合和预过滤的自定义智能合约:为了优化效率并减少连锁呼叫的冗余,Reader 使用专门为此目的编写的自定义智能合约。本合约作为一个聚合器,提供一个单一的,优化的呼叫,以获取储备和其他汇总信息,用于多个流动性对。该自定义合约的关键功能是其内置的预先检查在数据返回之前的常见欺诈特征或池内的过度交易税。
  • 下面是这个辅助合同的 Solidity 实现,核心逻辑在于 checkPair 方法,它评估了代币对的安全性,并返回了汇总数据。
// 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;
    }
}
  • 以事件为导向的通信:接收和处理这些事件后,Reader 将数据正常化,并通过 Go 通道向市场图表服务发送更新(例如,特定池的新备份值)以消息形式。

市场图表服务:绘制DeFi市场

市场图形服务是中央情报单位,维护了DeFi市场的实时内存表示。directed graph,在哪里:

  • 节点:代表单个加密货币(例如WETH,USDC,DAI)。
  • 边缘:代表各种 DEXes 的流动性池(例如 Uniswap V2 ETH/DAI 池, SushiSwap USDC/WETH 池)。
  • 数据结构和更新:此服务通过渠道接收来自区块链数据读取器服务的更新。接收池的新备份数据时,它更新了图表中的相应边缘。
  • BigInt 的准确性:所有涉及代币金额和汇率的计算都使用 Go 的数学/大包(BigInt 或 BigFloat)。这对于保持任意的准确性至关重要,防止浮点不准确性导致错过机会或错误的利润计算。
  • 仲裁路径检测:贝尔曼 - 福特算法:本服务的核心是FindArbitrage函数,该算法使用图形穿越算法,特别是贝尔曼 - 福特. 这个算法独特地能够在图表中找到负周期,这正是我们市场模型中的仲裁机会(在那里Logarithmic汇率被用作边缘重量)。

仲裁战略服务:识别和优化利润

订阅更新事件从Market Graph Service,仲裁策略服务不断监控新发现的仲裁路径的市场图表。

  • 机会评估:每当图表被更新或一个潜在的仲裁路径被FindArbitrage识别时,该服务开始运作,它采取了市场图服务发现的负周期(仲裁路径)并启动了综合盈利能力计算。
  • 优化输入金额计算(Convex Optimization):一个关键步骤是确定仲裁序列的最佳输入金额(dx)。这是一个非微妙的问题,因为盈利性是输入金额的非线性功能,正如《Uniswap市场分析》论文所示的那样。它受到跨多个交换的滑坡和费用的影响。该服务通过使用Go的gonum/optimize包来解决这种问题,确保所选择的输入金额在对所有变量进行核算后最大限度地提高净利润。
  • 交换模拟:在进行交易之前,该服务使用常态产品公式和计算的最佳输入量进行检测的仲裁路径内的所有交换模拟执行。在此模拟过程中,每个中间交换步骤也设置了最低输出金额。这确保在意外低实际输出的情况下(例如,由于突然的价格波动或链上的高滑动),交易将以最小的气体损失返回,而不是继续进行不盈利或损失的交易。
  • 所有费用:包括DEX交易费用(例如,Uniswap V2的0.3%)。
  • Slippage:准确地模拟每个交易在序列中的价格影响。
  • 天然气成本:对整个交易所需的天然气费用的估计,考虑到链(基地)和当前的网络条件。
  • 利润门槛:只有计算的净利润至少为初始输入金额(或可配置的门槛)的5%时,机会被认为是可行的。
  • 执行通知:如果一个盈利的机会符合标准,仲裁策略服务将汇编所有必要的细节 - 交换的顺序(边缘),最佳输入量和任何其他相关参数 - 并通过Go渠道向交易构建服务发送通知。

交易构建服务: Swift Execution

交易构建服务是机器人的执行臂,任务是快速构建和提交仲裁交易到区块链上。

  • 交易建设:收到仲裁战略服务的机会后,该服务立即开始构建原子区块链交易。
  • 智能合约互动(原子交换):此服务与专门设计的定制智能合约相互作用,以在单一原子交易中执行所有仲裁操作(多个交换)。
  • 以下是 Solidity 函数,该函数在没有闪存贷款的情况下处理仲裁执行,要求所有者(机器人)资助初始金额In:
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;
}
  • Flash Loan 集成:如果仲裁的最佳金额需要一个闪存贷款,建设者将闪存贷款逻辑(借用 →执行交换 →偿还)整合到一个单一的,不可分割的交易中,使用一个自定义合约,通过Aave的FlashLoanSimple接口促进这种原子操作。
  • 以下是 Solidity 合同函数执行操作(一个更大的 FlashLoanReceiver 合同的一部分),该函数由 Aave Pool 调用,并包含使用借款资金的仲裁逻辑:
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;
}
  • 气体估计和价格:动态估计交易所需的气体,并设置适当的气体价格(或优先收费L2如Base),以确保及时纳入区块。
  • 交易发送:一旦构建,签署的交易被发送到Base区块链节点。Base的选择是战略性的:与Ethereum L1不同,Base的当前架构在传统意义上没有公众可见的Mepool,这意味着交易更不容易通过机器人扫描Mepool进行直接的通用前进。
  • 非同步反馈:在发送交易后,服务会向 Honeywall 服务发送通知,表示交易已经开始并需要监控。

霍尼沃尔服务:执行后验证和安全

霍尼沃尔服务作为一个关键的执行后检查器和对仲裁机器人的强大安全层,其作用是验证执行交易的结果,并防止恶意行为者。

  • 交易结果监控:交易构建器发送交易后,霍尼沃尔服务监控其在块中的包含及其结果。
  • Profit Logging: If the transaction is successful and yields a profit (as expected from the simulation), the profit details are logged for performance tracking and analysis.
  • 失败分析:在交易失败的情况下,Honeywall分析了逆转的原因。
  • Honeypot / Scam 检测和黑名单:一个关键的安全功能是它能够识别实施欺骗性逻辑的“honeypot”代币或池(例如,允许购买但防止销售,或对销售征收过多的隐藏税)。
  • 外部供应商集成:它与外部供应商或已知蜂蜜合同的数据库集成,以交叉引用在失败交易中使用的对,从而识别潜在的欺诈行为。
  • 动态黑名单:如果一个特定的对或池被识别为蜂蜜锅或由于意外的高税收而存在问题,它会立即被添加到数据库支持的黑名单中,从而确保机器人在未来操作中避免这些风险资产。
  • 布隆过滤器集成:这个黑名单通过布隆过滤器机制进行高效管理,这允许区块链数据阅读器服务快速检查新观察的对比对黑名单,甚至在恢复其储备或将其添加到市场图表之前。

建筑设计的结论

Go 所实施的模块化、事件驱动的架构,加上数据摄入、市场建模、机遇优化、快速执行和强大的安全性等专业服务,构成了我们高性能仲裁机器人的支柱,这种设计确保系统能够以无与伦比的速度对流动的市场机遇作出反应,同时减少了 DeFi “Dark Forest” 固有的重大风险。

机会检测和最佳执行:机器人的大脑

仲裁机器人的真正智慧在于它能够在不断变化的市场中快速准确地识别盈利机遇,然后优化执行以获得最大回报,本章深入了解支撑我们的机器人的决策过程的核心算法和数学模型,从绘制市场作为图表到精确计算最佳交易尺寸和模拟结果。

模拟DeFi市场作为图表

正如在架构概述中所介绍的那样,我们的市场图表服务代表了DeFi景观作为一个定向图表,在这个模型中,个别代币(例如WETH,DAI,USDC)作为nodes,而各种分散交易所(DEX)的流动性聚合物则作为edges连接这些代币. 每个边缘的重量代表了通过该池进行交易的成本。

为了有效地检测作为盈利周期而表现的仲裁机会,我们将找到一个盈利的交易序列的问题转化为找到一个盈利的交易周期。negative cycle在我们的图表中,这种转换是通过将一个逻辑函数应用于汇率来实现的。

循环检测的逻辑必要性

仲裁背后的核心想法是以一系列汇率来倍增一个起始金额,以便最终获得更多的原始资产。TokenX并将其交易为TokenY然后TokenYTokenZ,最后TokenZ返回TokenX, our final amount would be:

与图表算法中的产品合作是繁琐的。在计算金融中,将倍增问题转化为附加问题的一种常见技术是应用一个算法。

Now, for a profitable cycle, we need ln(最终) > ln(A),这意味着ln(RateX→Y) + ln(RateY→Z) + ln(RateZ→X) > 0然而,典型的最短路径算法(如我们使用的Bellman-Ford)旨在找到最少路径。为了使一个盈利的周期在我们的图表中显示为“负周期”,我们简单地否定了逻辑速率:

ln(RateX→Y) + ln(RateY→Z) + ln(RateZ→X) > 0

通过这种转换,产生负值的负重量的总和(即负周期)直接表明一个有利可图的仲裁机会。

处理精度与BigInt

DeFi 中的代币数量可以非常不同,从小部分(例如,为 ERC-20 代币的 18 个十字位)到非常大的数字(例如,稳定币)。这种极端的不等式性,涵盖高达 18 个重要数字,使得标准浮点算法高度容易出现精度错误。

为了克服这一点,我们的市场图表服务,事实上,在机器人内部涉及代币金额和汇率的所有计算,都使用了Go的功能。math/big包装,具体BigInt对于整数算法和BigFloat在必要时进行浮点操作。BigFloat提供任意的精度,应用logBigIntBigFloat值需要谨慎的处理,作为标准math.Log功能在 native 上运行float64在这里,自定义实现或能够任意精度算法的外部库是必不可少的。

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)
}

仲裁路径检测:贝尔曼 - 福特算法

一旦DeFi市场被准确地模拟成具有逻辑负边缘重量的图表,寻找仲裁机会的任务就减少到识别。negative cycles在此图中,我们使用Bellman-Ford algorithm.

以理查德·贝尔曼(Richard Bellman)和莱斯特·福特(Lester Ford Jr.)命名,贝尔曼-福特(Bellman-Ford)是一个多功能的最短路径算法,能够处理负边缘重量的图表,与戴克斯特拉(Dijkstra)的算法不同,它在负周期存在时失败,贝尔曼-福特(Bellman-Ford)是专门设计来检测它们的。

The algorithm works by iteratively relaxing edges, progressively finding shorter paths to all nodes from a source. If, after ∣V∣−1 iterations (where ∣V∣ is the number of vertices), an additional N-th iteration finds a path that can still be “relaxed” (i.e., a shorter path can be found), it indicates the presence of a negative cycle. This property makes it perfect for our use case: a negative cycle implies a sequence of trades that results in a net gain, exactly what an arbitrage bot seeks.


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
}

最佳输入金额计算:最大化利润

一旦确定了负循环(仲裁机会),下一个关键步骤是确定optimal input amount(dx)对于序列中的初始交易,这不是任意的;仲裁机会的盈利性是贸易规模的非线性功能,原因是与AMM交换相关的固有滑动和费用。

As detailed in “An analysis of Uniswap markets”, the constant product formula inherently implies a convexity in the relationship between input and output amounts. Specifically, as the trade size increases, the effective exchange rate worsens due to the pool’s invariant. This means there’s a sweet spot: too small an amount might not cover gas fees, while too large an amount might incur excessive slippage, eroding profits.

利润最大化的问题是convex optimization problem在仲裁途径中进行一系列N交换时,最终输出金额(因此利润)可以作为初始输入金额(dx)的函数来表达,但对于多腿仲裁的精确分析解决方案可能很复杂,尤其是在不同的AMM上有不同的收费结构和滑坡曲线的情况下,代表利润减成本(包括天然气)的函数通常是弯曲的,这使我们能够使用数值优化技术找到全球最大值。

我们的仲裁策略服务通过使用Go的优化解决方案来解决这一问题gonum/optimize这个解析器采取一个代表净利润的函数(从交换中获得的利润减去估计的天然气费用和任何闪存贷款费率),并找到最大限度地提高此值的输入金额。amountOut公式dy = (x + dx) / (dx⋅ y)对于仲裁路径的每个步骤,在每个步骤中考虑中间储备,费用和滑坡。

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
}

交换模拟和盈利能力评估

在任何交易发送之前,仲裁策略服务会进行精心策划。simulated execution这一步对于验证实际盈利能力至关重要,考虑到实时市场条件和拟议交易的准确参数。

该模拟使用了所涉及的流动性池的当前储备和计算的最佳输入量。对于多脚路径的每个步骤,它应用了特定AMM公式(例如,Uniswap V2类池的恒定产品公式)来计算预期的输出:

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
}

重要的是,模拟也包含了minimum output amount (minOut)检查每一个中间步骤。minOut值是从模拟的预期输出中提取的,并在实际的链上交易中设置为参数,如果由于网络延迟、前进或意想不到的市场条件,实际的链上的交换收益小于其指定的。minOut这个机制是至关重要的保障,防止机器人完成一个不利的交易序列,并将损失限制在仅用于逆转交易的气体上。

只有当最终的净利润,在所有费用,滑坡,天然气成本和闪电贷款费率之后,超过预定值profit threshold(例如,初始输入金额的 0.5%) 是被认为是可行的机会,并转移到交易构建服务进行执行. 这个门槛确保机器人只追求有足够显著的利润率来证明计算和链上的成本。

5. Transaction Engineering: Swift Execution in the Dark Forest

识别一个盈利的仲裁机会只是一半的战斗;另一个,可能更关键的一半,在于能够以无与伦比的速度和可靠性执行交易。在DeFi的超级竞争激烈的“黑森林”,在那里机会是飞行和复杂的机器人每毫秒使用,交易工程成为一种艺术形式。Transaction Builder Service旨在确保闪电快速和安全的执行。

速度的必要性

分散交易所的仲裁机会的盈利性窗口通常以毫秒计量。价格差异被众多自动化系统快速检测和利用,创造了一个激烈的竞赛,成为第一个将盈利交易纳入新区块。任何延迟,无论小,都可能导致竞争对手抓住机会,导致失败的交易和浪费的天然气费用。

内存优化用于即时交易构建

为了达到所需的速度,我们的系统优先考虑将所有关键交易组件轻松存储在内存中,从而在关键交易构建阶段消除昂贵的 I/O 操作或链上呼叫。

  • 预分和包装ABI:智能合约应用 二进制接口(ABI)定义了如何与合约互动。而不是对每个交易进行ABI定义和编码函数调用,我们的系统预分和包装所需的ABI数据结构和函数选择器到原始字节(bytes)系列中。这些预计算的比特序列用于常见合约互动(例如,swapExactTokensForTokens,flashLoanSimple, transferFrom)存储在内存中。
  • 用于交易字段的存储链上的数据:为了避免对交易元数据的过剩连锁调用,机器人内部的专用实用性结构在内存中保持关键的、经常更新的值:
  • 帐户非:非(从一个地址发送的交易数量)对于防止重播攻击和确保交易订单至关重要,它被收集一次,然后在内存中逐步管理,如果一个交易失败或意外被列入排序时,可重复同步。
  • 最佳气体参数:而不是为每个交易查询网络的气体价格(或 EIP-1559 链上的基本费 / 优先级费),机器人会定期检索最佳气体参数。
  • 签名者信息:机器人钱包的私钥和相关签名者对象(用于加密签名交易)在初始化时被加载到内存中,从而防止在关键执行阶段发生任何磁盘输出或密钥衍生,从而确保交易几乎可以立即签名。

通过将这些关键组件存储在记忆中,交易构建服务可以在微秒内构建和签署完整的区块链交易,并准备立即发送。

动态智能合约选择:闪存贷款与直接交换

仲裁策略服务将优化的仲裁路径和计算的最佳输入量传递给交易构建者。amountIn无论它是否超过预定义的资本门槛(或战略明确要求),交易构建器都会在执行的两个主要智能合约之间进行动态选择:

  • 直接交换执行合同:对于可以直接由机器人所拥有的资本资助的机会,开发者在自定义的多交换代理合同中使用 executeArb 函数(或类似) 正如第 3 章所示,该合同从机器人钱包中取出输入代币,并在单一的原子交易中执行整个交换序列。
  • Flash Loan 集成合同:当计算的仲裁的最佳金额显著大于机器人可用的资本时,建设者瞄准一个单独的,定制的智能合约,旨在启动和管理闪存贷款。本合同的起始Arbitrage功能(如第3章所述)要求从像Aave这样的协议中获得闪存贷款,然后召回合同的执行操作功能。在执行操作中,整个仲裁序列使用借款资金进行,闪存贷款(加上小额奖金)被偿还 - 所有这些都是在单一的原子区块链交易中。

这种动态选择确保了有效的资本配置和基于每个发现的机会的具体特点的最佳战略执行。

Mempool Dynamics:导航以太坊L1与层2链

仲裁执行的一个关键方面是了解区块链的交易扩散机制,特别是mempool.

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

节点速度与安全:可靠执行的基础

连接到区块链节点是所有数据和交易的单一输入和出口点,其速度和安全性至关重要:

  • 高性能节点连接:交易构建服务连接到专门的高性能节点提供商(例如,Alchemy,Infura或自托节点)。快速和低延迟连接至关重要,以最大限度地减少交易签署与网络传播之间的时间。
  • 节点安全性和完整性:连接节点的安全性同样至关重要。使用可靠的节点提供商或确保高度安全的自托节点对于防止中间人攻击或数据篡改至关重要。受损节点可能会泄露私钥、注入恶意交易或操纵数据,导致灾难性损失。 我们的系统依赖私人RPC终端(如果提供商可用)和安全通信渠道(https for HTTP,wss for WebSockets)增强了这种安全态度。

通过精心优化每个层次的速度,从内存数据结构和预计算到战略链选择和强大的节点基础设施,我们的仲裁机器人旨在超越竞争对手,并安全地利用DeFi景观中的机遇。

导航黑暗森林:挑战,道德和未来前景

在DeFi“黑森林”建立和运营仲裁机器人是分散技术的力量的证据,但它也带来了重大挑战和道德考虑,虽然我们的系统证明了自动仲裁的理论和实践可行性,但认识到对抗的环境及其更广泛的影响至关重要。

反恶意演员的不断战斗:布鲁姆的角色

遗憾的是,围绕DeFi无许可的本质的初步乐观主义受到恶意行为者的扩散所抑制。Honeywall Service这是一个至关重要的最后一道防线,但这些坏行为者的创造力不断要求不断发展反制措施。

这一防御的关键组成部分是Bloom filter一个Bloom过滤器是一种概率数据结构,可以快速有效地测试一个元素是否是集中的成员,它具有高度的空间效率,但具有很小的“假阳性”的概率(表明一个元素在集中的时候没有),但从来没有“假阳性”。在我们的背景下,Bloom过滤器被用来预先过滤来自Blockchain Data Reader服务的入口事件数据。它包含已知恶意或高税率流动性对地址的哈希。

尽管在我们的习惯中实施的精心预先检查ArbHelperMap智能合约(特别是_checkTaxLimit通过模拟轮渡交换来评估税收的逻辑,一些恶意对仍然能够绕过这些初始的链上的验证。getAmountsOut函数(用于价格查询)返回看似正常的,低税率的输出,然而,真正的“蜂蜜”逻辑被嵌入更深的实际swapExactTokensForTokens或底层transfer这些功能可能会对销售操作征收过多的隐藏税(例如99%)或甚至完全限制销售,有效地捕捉资金。

在我们的测试阶段,我们遇到了大量这样的欺骗性对,我亲自收集了一些成功通过最初的对地址。getAmountsOut只有在本地数据库中进行实际(模拟或逆转)交易时才会发现隐藏的税收或销售限制。这个 db 文件将在该项目的 GitHub 存储库中公开提供,作为一个社区资源来帮助其他人避免这些陷阱。

道德影响与黑暗森林的阴影

“黑森林”的类似性不仅适用于机器人之间的极端竞争,而且适用于DeFi的更广泛的道德环境,仲裁的效率虽然对市场健康和价格发现至关重要,但也伴随着一个严峻的现实:仲裁者所产生的利润往往代表了从不太复杂的市场参与者中提取的价值。

普遍的文化的FOMO (Fear Of Missing Out),加上对底层区块链机制和金融工具的普遍缺乏理解,使得许多零售用户在这种环境中很容易获取猎物,他们进入高度波动的市场,与新协议互动,并执行交易,而无需完全了解滑坡,MEV或隐藏合同税等概念。

这种动态,虽然对于那些具有先进工具的人来说是经济上的逻辑,但会对分散技术的声誉造成阴影。 叙述可以迅速从“财务赋权”转变为“掠夺行为”,破坏对另一个空间的信任,其核心是对金融进行民主化,提供无许可的访问和透明度。 然而,MEV的复杂性和欺诈的普遍性可以无意间破坏这些理想,创造一个只有技术熟练者才能真正安全地导航的双层系统。 作为建设者,我们必须认识到这些道德层面,并倡导更高的用户教育,强大的安全审计和保护弱势参与者的机制。

结论:仍在航行黑暗森林

尽管DeFi景观固有的复杂性和持续的挑战,但该仲裁机器人工程的旅程是理论原理与实际实施的显著验证,我们成功地展示了基于事件的基于Go的架构的力量,优化为speed, precision, and data-driven insights,能够检测和执行多腿仲裁机会。

最初,“黑暗森林”内部的一种普遍预期是,绝大多数仲裁价值将立即被大型,资源良好的玩家拦截,利用自我托管节点和直接访问区块生产商,然而,我们的测试和成功的交易表明,更小的,精心设计的机器人确实可以不断地找到和利用这些短暂的机会:

虽然在旧的AMM模型上的盈利仲裁,如Uniswap V2(主要依赖于恒久性的产品池)可能难以维持长期,由于加剧的天然气成本和竞争,“黑暗森林”继续演变。更新的实施,如Uniswap V3的集中流动性AMM(CLAMM),引入新的仲裁矢量,需要更复杂的建模,但通常会由于增加的资本效率而产生更高的回报。此外,跨链仲裁,利用桥梁和区块链间通信协议的日益增长的领域呈现了更大的边界,从不同区块链网络的附加价值价格差异中退出。这些持续的发展表明,这种具有挑战性的环境将继续成为复杂和适应性机器人的肥沃地带。

因此,虽然我仍然很穷,但我可以自信地说,我已经成为了一个优秀的森林航海家。My compass is sharper, my map more detailed, and I understand the whispers of the canopy.

项目库存

对于那些渴望深入实践实施和我们讨论的一些“陷阱”背后的非常真实的数据的人来说,我们代码库的清洁版本和载有已知恶意代币对的数据库在我的文件中可用。GitHub 存储库这些85个特定对,虽然数值很小,但会产生不成比例的交易量,因为它们不断地试图诱导天真机器人进入不盈利的交易,这是一个强烈的提醒这个生态系统中总是存在的危险,并强调强大的安全检查的关键需求。

GitHub 存储库

参考

  • 丹·罗宾逊,乔治·康斯坦托波洛斯:“以太坊是一个黑暗的森林”,* 范式。
  • Guillermo Angeris,Hsien-Tang Kao,Rei Chiang,Charlie Noyes和Tarun Chitra. “Uniswap市场分析”,加密经济系统。
  • 克劳迪奥·盖比亚:“中央和分散金融中仲裁机器人的分析和实施”,苏黎世大学。
  • Y. Zhang,Z. Li,T. Yan,Q. Liu,N. Vallarano和C. J. Tessone, “仲裁循环中的利润最大化”。


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks