
通過代碼識別 DeFi 中的套利機會
TechFlow Selected深潮精選

通過代碼識別 DeFi 中的套利機會
本文將分析部分去中心化交易所以及聚合器在合約代碼上可能存在的套利機會。
概述
去中心化金融(英語:Decentralized finance,俗稱 DeFi)是一種創建於區塊鏈上的金融,它不依賴券商、交易所或銀行等金融機構提供金融工具,而是利用區塊鏈上的智能合約進行金融活動。在 DeFi 中存在了大量的套利機會,包括但不限於清算、差價套利。本文將分析部分去中心化交易所(DEX)以及聚合器(Aggregator)在合約代碼上可能存在的套利機會。
分析
Uniswap
Uniswap 是一個採用了自動做市商(AMM)模型的去中心化的加密貨幣交易平臺,目前有兩個流行的版本,分別是 Uniswap V2 和 Uniswap V3,我們將分別分析其中可能存在的套利機會。
Uniswap V2 Router
在 Uniswap V2 中,用戶一般是通過 Router 合約與 Pair 合約以及 Factory 合約進行交互。通常來說 Router 只是會在交易中中轉代幣,而不會存儲代幣,但由於種種原因,如空投、轉賬失誤導致 Router 合約中存儲了某些代幣。那麼如何將這些代幣提取出來呢?
通過分析 Uniswap V2 Router 02 合約的代碼,發現存在 removeLiquidityETHSupportingFeeOnTransferTokens 函數:

該函數用於移除其中一個代幣為 WETH 的流動性,其內部調用 removeLiquidity 函數時傳入的 to 的地址為 address(this),也就是會將兩種代幣先轉移到 Router 合約中,然後 Router 合約再將兩種代幣轉移到指定的地址。這裡雖然轉移的 WETH 的數量是 removeLiquidity 返回的,無法修改,但是轉移的另一種 Token 的數量是 balanceOf(address(this)),即 Router 合約中的該代幣的餘額。
因此根據上述分析,我們能得到一個套利的流程:
監控到 Router 02 合約存在 ERC 20 代幣;
-
監控到 Router 02 合約存在 ERC 20 代幣;
-
調用 addLiquidityETH 添加該 ERC 20 代幣和 WETH 的流動性;
-
調用 removeLiquidityETHSupportingFeeOnTransferTokens 移除流動性。
侷限性:
-
如果該代幣之前沒有和 WETH 組流動性,當第一次添加流動性時會損失一小部分流動性(MINIMUM_LIQUIDITY);
-
暫時未發現提取 Router 02 合約中的 WETH 和 ETH 的方法。
Uniswap V2 Pair
Uniswap V2 Pair 合約,即所謂的流動性池,存儲著提供流動性的 2 種代幣,因為 Pair 合約中使用的是 reserve 來記錄餘額而不是 balanceOf(address(this)),因此有人直接誤轉流動性代幣到合約中時會出現 balance 和 reserve 出現差值,而 Pair 合約中存在平衡函數 skim,我們可以調用該函數將這差值數量的代幣給提取出來:

可以看到該函數會將流動性池中兩種流動性代幣的 balance 和 reserve 差值數量的代幣轉移到 to 地址。
流動性池中除了這兩種代幣外,也會因為誤轉、空投等原因存在其他的 ERC 20 代幣,如何提取這一部分的代幣呢?
對 Pair 合約的代碼分析後發現無法提取這一部分代幣,只有一種情況例外:當流動性池中存在該池的 LP 代幣時。
出現這種情況我們可以調用 Pair 合約的 burn 函數,移除流動性,取出相應的兩種流動性代幣:

Uniswap V3 SwapRouter
Uniswap V3 的 SwapRouter 合約中也會存在和 Uniswap V2 Router 一樣的情況,存在 ERC 20 代幣和 ETH,但是幸運的是 SwapRouter 合約提供了幾個函數可以方便提取其中的代幣。
提取 ERC 20 代幣我們可以使用 sweepToken 函數:

提取 ETH 我們可以使用 refundETH 函數:

也能夠直接調用 unwrapWETH 9 函數將 WETH 還原成 ETH 並提取出來:

以上是對 Uniswap V3 SwapRouter 合約的套利分析。
在對 Uniswap V3 Pool 合約的代碼進行分析後,發現沒有辦法提取其合約中的其他代幣,也不存在如 Uniswap V2 Pair 合約中 balance 和 reserve 有差值的情況。
SushiSwap
SushiSwap 最初是一個 Uniswap 的分叉項目,後來發展成為一個獨立的生態系統,提供了許多不同的金融服務和產品。
因為 SushiSwap 和 Uniswap V2 一樣,因此上述的針對 Uniswap V2 的套利手段對與 SushiSwap 也同樣適用。
SushiXSwap
SushiXSwap 是 SushiSwap 推出的基於 LayerZero 的全鏈交易協議,支持的網絡包括 Optimism、Arbitrum、Fantom、BNB Chain、Polygon 和 Avalanche。用戶可以在支持的網絡以及資產之間進行跨鏈交易。
如何提取 SushiXSwap 合約中的代幣呢?
SushiXSwap 中主要的功能都通過 cook 函數實現,該函數提供了一系列的操作,支持操作列表如下:

其中有一個操作 ACTION_DST_WITHDRAW_TOKEN,其代碼實現如下:

首先將傳入 cook 函數的 data 進行解碼,然後判斷 amount 是否等於 0 ,等於 0 則將 amount 的值設為該合約的 ERC 20 代幣的餘額或者 ETH 的餘額。最後調用 _transferTokens 將代幣轉移到指定的地址:

因此我們只需要構造傳入 cook 函數的 actions 和 datas,即將 actions 設置為 ACTION_DST_WITHDRAW_TOKEN ,在 data 中構造想要轉移的代幣、接收地址、數量,即可轉移出 SushiXSwap 合約中的代幣。
Sushi BentoBox
Sushi BentoBox 是 SushiSwap 生態系統中的一個組件。BentoBox 是一個高度靈活的去中心化金融(DeFi)利率優化產品。簡單來說,它是一個允許用戶存儲、借用和賺取利息的智能合約平臺。BentoBox 的主要目的是優化用戶在 DeFi 領域中的收益。
以太坊上的 BentoBox 合約中存儲了大量了代幣,那麼該合約是否存在套利的空間呢?
在 BentoBox 合約中用戶可以通過 deposit 函數進行存款操作,函數的實現如下:

可以看到用戶傳入指定的代幣地址,扣款地址,接收地址,數量,股份數量,函數首先做了一系列校驗,然後將 amount 或者 share 進行轉換,關鍵點在 195 - 198 行,這裡做了一個校驗 :amount <= _tokenBalanceOf(token).sub(total.elastic)。
在 BentoBox 合約中某種代幣的餘額使用的是 total.elastic 來記錄,類似 Uniswap Pair 合約中的 reserve,某些情況下會和 _tokenBalanceOf(token) 產生差值, 我們可以利用 deposit 函數這裡的特性,將差值部分真實轉換成自己在 BentoBox 合約中的餘額。
因此我們傳入參數時將 token 設置為存在差值的代幣地址,將 amount 的值設置為差值,然後將 from 設置為 BentoBox 合約的地址,將 to 設置為自己的地址,在 207 行時由於地址為 BentoBox 合約地址,因此不會進行轉賬,只是平衡了total.elastic 和 _tokenBalanceOf(token) 的值,將其轉換為 to 地址在合約內的餘額。
DODO
DODO 是一個去中心化交易平臺,使用獨創的主動做市商(PMM)算法為 Web3 資產提供高效的鏈上流動性。DODO 既自己提供流動性,也聚合其它交易所的流動性。
DODO 有一系列合約,其中用戶會通過 DODO V2 Proxy 02 合約進行代幣的兌換。和 Uniswap Router 合約類似,該合約也會因為各種原因存在一些代幣,我們應當如何提取這些代幣?
DODO V2 Proxy 02
在 DODO V2 Proxy 02 合約中存在 externalSwap函數,用來調用 DODO 聚合的外部平臺進行兌換,如 0x , 1inch,代碼實現如下:

1719-1721 行在對傳入的參數做校驗,然後 1724 行校驗 fromToken 是否為 ETH,不是的話則會將調用者的代幣轉移到合約中,然後進行授權,在分析了 DODOAPPROVE 合約的代碼後發現只需要將 fromTokenAmount 設置為 0 即可繞過:

然後會對調用的外部合約做校驗,是白名單內的才能夠調用,這裡的 swapTarget,calldataConcat都是由用戶可控的,因此可以將 swapTarget 設置為 0x 或者 1inch 的合約地址,然後 calldataConcat 設置為其合約的 view 函數的編碼,從而讓返回的值為 true,也能通過後面的 require 校驗:

接下來會將合約中的 toToken,全部轉移給調用者,這裡的 toToken 可以是 ERC 20 代幣,也可以是 ETH,發送完後會進行最小的預期數量校驗,我們將 minReturnAmount 的值設置為非常小的值即可通過。最後兩個函數調用無關緊要。

通過以上的步驟我們就能夠提取出 DODO V2 Proxy 02 合約中的 ERC 20 代幣以及 ETH。
1inch
1inch是一個去中心化交易所(DEX)聚合器,它從多個 DEX 中彙集流動性,以便為用戶提供最佳的代幣兌換價格。通過整合來自不同來源的流動性,1inch幫助用戶優化交易並在各個平臺之間找到最優惠的價格。1inch的智能合約自動在各個去中心化交易所之間進行交易,使用戶能夠輕鬆地在不同交易所之間獲取最佳價格和最低滑點。此外,1inch還提供了其他功能,如流動性挖礦和治理代幣。
1inch 的主要合約是 AggregationRouter,現在使用較多的是 V 4 和 V 5 版本,這兩個合約也會因為各種原因存在一些代幣,我們可以通過構造的傳入函數中的參數,提取合約中的代幣。
AggregationRouterV 5
AggregationRouterV 5 合約存在 swap 函數,其實現如下:

校驗了 desc 中的 minReturnAmount 後,從 desc 中獲取 srcToken 和 dstToken,接下來 986-997 行可以通過構造 desc 結構體中的 flags 和 srcToken 進行繞過:

然後執行函數 _execute, 這裡會進行 call 調用,並會校驗執行狀態,由於 executor 由用戶傳入,因此這裡我們可以使用 0 地址進行繞過:

然後獲取合約中 dstToken 的餘額。1007-1018 行我們可以構造 desc 中 flags 以及 minReturnAmount 進行繞過:

最後會將合約中的 dstToken 餘額都轉到 dstReceiver 地址中,該地址也由用戶控制:

通過以上的步驟,我們能構造傳入 swap 函數的參數從而將 AggregationRouterV 5 合約中的代幣提走。
AggregationRouterV 4
AggregationRouterV 4 與 AggregationRouterV 5 差別不大,AggregationRouterV 4 中也存在 swap 函數,實現如下:

可以發現跟 AggregationRouterV 5 的 swap 函數的實現是一樣的,只是 AggregationRouterV 5 對 call 進行了優化,因此使用和 AggregationRouterV 5 一樣的方法即可提取出存在 AggregationRouterV 4 合約中的代幣。
總結
本文簡單介紹了部分去中心化交易所以及聚合器,並探討了其中可能存在的套利,從合約代碼層面分析了套利的原理,但在實際中能否成功還和諸多因素相關,如 GAS,節點速度等。
歡迎加入深潮 TechFlow 官方社群
Telegram 訂閱群:https://t.me/TechFlowDaily
Twitter 官方帳號:https://x.com/TechFlowPost
Twitter 英文帳號:https://x.com/BlockFlow_News














