uniswap v2 周邊源碼解析系列


前言:UniswapV2的周邊合約主要用做外部賬號和核心合約之間的橋梁,也就是用戶 => 周邊合約 => 核心合約。UniswapV2周邊合約主要包含接口定義,工具庫、Router和示例實現這四部分

 

該作者系列文章:

https://blog.csdn.net/weixin_39430411/category_10454309.html?spm=1001.2014.3001.5482

 

 

1,接口定義

 

2,工具庫

UniswapV2周邊合約的工具庫包含兩個部分,一部分是直接寫在項目里的,有三個合約:SafeMath,UniswapV2Library和UniswapV2OracleLibrary。另外一部分是Node.js依賴庫,需要使用yarn安裝的,也包含幾個庫。這其中SafeMath就是簡單的防溢出庫,在前面的系列學習中已經講過,這里不再學習研究。

3,Router

UniswapV2周邊合約的核心實現包含UniswapV2Router01.sol和UniswapV2Router02.sol,這里我們把它簡稱為Router1和Router2。查看它們實現的接口我們可以看到,Router2僅在Router1上多了幾個接口。那為什么會有兩個路由合約呢,我們到底用哪個呢?查看其官方文檔我們可以得到:

Because routers are stateless and do not hold token balances, they can be replaced safely and trustlessly, if necessary. This may happen if more efficient smart contract patterns are discovered, or if additional functionality is desired. For this reason, routers have release numbers, starting at 01. This is currently recommended release, 02.

上面那段話的大致意思就是因為Router合約是無狀態的並且不擁有任何代幣,因此必要的時候它們可以安全升級。當發現更高效的合約模式或者添加更多的功能時就可能升級它。因為這個原因,Router合約具有版本號,從01開始,當前推薦的版本是02。

這段話解釋了為什么會有兩個Router,那么它們的區別是什么呢?還是來看官方文檔:

UniswapV2Router01 should not be used any longer, because of the discovery of a low severity bug and the fact that some methods do not work with tokens that take fees on transfer. The current recommendation is to use UniswapV2Router02.

這段話是講因為在Router1中發現了一個低風險的bug,並且有些方法不支持使用轉移的代幣支付手續費,所以不再使用Router1,推薦使用Router2。

因此本文也是學習的UniswapV2Router02.sol,它的前半部分主要是流動性供給相關的函數(功能),后半部分主要是交易對資產交換相關的函數(功能)。由於篇幅較長,因此該合約學習計划分為上、下兩個部分來學習,內容分別為流動性供給函數和資產交換函數。這次先學習流動性供給部分。

UniswapV2Router02.sol(上)

https://blog.csdn.net/weixin_39430411/article/details/109152019

 

UniswapV2Router02.sol(下)

https://blog.csdn.net/weixin_39430411/article/details/109152084

 

immutable 不可變的。類似別的語言的final變量。也就是它初始化后值就無法再改變了。它和constant(常量)類似,但又有些不同。
主要區別在於:常量在編譯時就是確定值,而immutable狀態變量除了在定義的時候初始化外,還可以在構造器中初始化(合約創建的時候),並且在構造器中只能初始化,是讀取不了它們的值的。並不是所有數據類型都可以為immutable變量或者常量的類型,當前只支持值類型和字符串類型(string)。

override 通常用於函數定義中,代表它重寫了一個父函數。例如也可以用於函數修飾符來代表它被重寫,不過應用於狀態變量卻稍有不同。
// Public state variables can override external functions if the parameter and return types of the function matches the getter function of the variable:
重寫了 接口route1中的 function factory() external pure returns (address);

virtual,可被子合約重寫。正如前面所講,本合約是無狀態的,是可以升級和替代的,因此本合約所有的函數都是virtual的,方便新合約重寫它。

 

一、流動性供給接口分類
源碼中流動性供給的外部接口可以按照是提供流動性還是移除流動性分為兩大類,然后再根據初始資產/最終得到資產是ETH還是普通ERC20代幣做了進一步區分。然后移除流動性還增加了支持鏈下簽名消息授權的接口,最后移除流動性增加了支持使用轉移代幣支付手續費的接口。

注:下文中的TOKEN均為ERC20代幣。

1.1、增加流動性
addLiquidity,增加流動性,提供的初始資產為TOKEN/TOKEN。
addLiquidityETH,增加流動性,提供的初始資產為ETH/TOKEN。
1.2、移除流動性
removeLiquidity,移除流動性,得到的最終資產為TOKEN/TOKEN。
removeLiquidityETH,移除流動性,得到的最終資產為ETH/TOKEN。
1.3、移除流動性,支持使用鏈下簽名消息授權
removeLiquidityWithPermit函數,移除流動性,支持使用鏈下簽名消息授權,得到TOKEN/TOKEN。
removeLiquidityETHWithPermit函數,移除流動性,支持使用鏈下簽名消息授權,得到ETH/TOKEN。
1.4、移除流動性,支持使用轉移代幣支付手續費
removeLiquidityETHSupportingFeeOnTransferTokens函數,移除流動性,支持使用轉移代幣支付手續費,得到ETH/TOKEN。
1.5、移除流動性,同時支持使用鏈下簽名消息授權和使用轉移代幣支付手續費
removeLiquidityETHWithPermitSupportingFeeOnTransferTokens函數。功能同標題,得到ETH/TOKEN。
從上面分類也可以得出一些其它結論。

增加流動性沒有使用鏈下簽名消息授權,為什么呢?因為增加流動性其流動性代幣是直接增發,沒有使用第三方轉移,所以就沒有授權操作,不需要permit。

移除流動性時,支付使用轉移代幣支付手續費最后得到的一種資產為ETH,說明交易對為ERC20/WETH交易對,也就是不支持兩個此類代幣構成的交易對。原因未知,還需要進一步研究。

既然移除流動性有使用轉移代幣支付手續費,那么作為同一個交易對,移除流動性之前必定有增加流動性,因此增加流動性時實際上需要支持此類代幣的。但是代碼中又沒有明確寫出支持使用轉移代幣支付手續費接口。為什么呢?

個人猜想,未必正確:

是因為此類代幣轉移過程中有損耗,而損耗多少未知,所以無法精確知道到底要提前轉移多少代幣到交易對中,在進行按比例計算時會得到預期外的值。所以寫此類接口無法向用戶返回相關數量值。
如果用戶不考慮返回值的話,直接使用addLiquidity或者addLiquidityETH函數是可以對此類代幣進行增加流動性操作的。因為交易對計算注入代幣的數量時是以交易對合約地址當前代幣余額減去交易對合約資產池中的代幣余額,和損耗沒有任何關系,因此,增發的流動性是准確的。

 

二、資產交易函數分類
上面這么多swap函數,大家肯定看得眼花繚亂了👻👻👻。下面我們根據交易資產的種類和指定的是賣出資產數量/買進資產數量,對它們做一個簡單的分類:

2.1、 TOKEN => TOKEN
就是兩種ERC20代幣交易,可分為:

指定賣出代幣數量,得到另一種代幣,函數為swapExactTokensForTokens。
指定買進代幣數量,賣出另一種代幣,函數為swapTokensForExactTokens。
2.2、ETH => TOKEN
ETH兌換成ERC20代幣,也分為兩種:

指定賣出ETH數量,得到另一種ERC20代幣,函數為swapExactETHForTokens。
指定買進ERC20代幣數量,賣出ETH,函數為swapETHForExactTokens。
2.3、TOKEN => ETH
ERC20代幣兌換成ETH。等等,有人會說這不是和 ETH => TOKEN 一樣的么,既然能通過交易鏈實現 ETH => TOKEN,那么必能反向通過該交易鏈實現 TOKEN => ETH。

是這樣的沒錯,但是因為不能直接交易ETH,所以會涉及到一個ETH和WETH的相兌換(轉換發生在不同方向的交易鏈的不同階段),因此實現邏輯還是不同的,所以這里提供了另外兩個接口。

指定賣出ERC20代幣數量,得到ETH,函數為swapExactTokensForETH。
指定買進ETH數量,賣出另一種ERC20代幣,函數為swapTokensForExactETH。
2.4、支持FeeOnTransferTokens函數
此外還有三個支持FeeOnTransferTokens函數,分別為函數9、函數10,函數11。注意它們的函數名稱,均表示指定賣出資產數量。也就是說它們只能用於交易鏈中指定賣出資產數量這種場景,不支持指定買進資產的場景中進行的反向交易鏈數值計算,因此只有3個該類函數。

為什么會這樣呢?

個人認為是因為此類資產在轉移過程中可能會有損耗,但損耗到底多少是無法知曉的。因此指定買進資產數量反推賣出資產數量的話,是無法得到的。因為該值為計算得到的值加上損耗值。如果指定賣出資產數量的話,每個交易對的實際賣出資產數量和最終接收的買進資產數量均可以通過比較相應地址交易前后的資產余額來計算出,因此此種交易場景是可行的。

因此2.1-2.3三種交易類型每種類型只有一個支持FeeOnTransferTokens函數,分別為:

TOKEN => TOKEN 為 swapExactTokensForTokensSupportingFeeOnTransferTokens函數。
ETH => TOKEN 為swapExactETHForTokensSupportingFeeOnTransferTokens函數。
TOKEN => ETH 為swapExactTokensForETHSupportingFeeOnTransferTokens函數。
綜合得到Router2合約用於資產交易的對外接口共分四類9個接口。

 

從前面的學習中可以看出,雖然資產交易對外提供了四類共9個接口,但來回就是對兩個核心_swap函數的調用。其中支持使用轉移的代幣支付手續費的接口中,轉移資產的實際數量不再等於根據恆定乘積計算出來的結果值,而需要根據相應地址的兩次資產余額相減計算出來。交易鏈中如果有涉及到ETH交易的,需要在交易鏈的對應階段(開始或者結束階段)進行ETH/WETH的兌換。因為UniswapV2交易對全部為ERC20/ERC20交易對,因此交易鏈中間流程不可能有ETH出現。

 

4,示例實現

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM