Uniswap V2 數學與源碼解讀

來源:Gryphsis Academy

本篇研報是對 UniswapV2 協議的工作原理、項目構成、源碼分析等部分進行詳細解讀。 工作原理主要是涉及自動做市商( AMM ),本篇研報會描述流動性池的創建和管理過程,以及如何通過提供流動性賺取手續費;在項目構成方面,我們主要概述 UniswapV2 的架構,包括主要合約(如工廠合約、交換合約)及其功能;在源碼分析部分我們分析 UniswapV2 的智能合約源碼,解釋關鍵函數和數據結構的設計理念。

1.協議簡介

UniswapV2 是一種去中心化交易協議,基於以太坊區塊鏈,允許用戶無需信任中介即可進行加密貨幣的交易。與傳統的中心化交易所不同,UniswapV2 採用了自動做市商( AMM )模型,通過智能合約來管理交易和流動性池,從而實現完全的去中心化。

UniswapV2 的核心在於其恆定乘積公式( x * y = k ),其中 x y 分別代表流動性池中兩種不同資產的數量, k 是一個常數。這個公式確保了在每次交易後,池中的資產比例會重新平衡,從而為用戶提供流動性。這種設計使得交易過程透明且公平,用戶可以隨時添加或移除流動性,並通過交易手續費賺取收益。

UniswapV2 協議由多個智能合約構成,其中最主要的是工廠合約和交換合約。工廠合約負責創建和管理流動性池,而每個交換合約則對應一個特定的交易對(例如 ETH/DAI)。

此外,UniswapV2 還引入了路由器合約和庫函數,以提高交易效率和安全性。與其前身 UniswapV1 相比,UniswapV2 帶來了幾項重要改進。首先是閃電交換功能,允許用戶在單個交易中借入資產,只要在交易結束前歸還即可。其次是價格預言機,通過累積價格時間加權平均值( TWAP )來提供更可靠的價格數據。

此外,UniswapV2 支持任意 ERC-20 代幣的直接交易,而無需通過以太坊作為中介。UniswapV2 的成功不僅在於其技術創新,還在於其開放性和社區驅動的開發模式。任何人都可以自由使用和擴展 Uniswap 協議,這為去中心化金融( DeFi )生態系統帶來了無限的可能性。許多其他 DeFi 項目,如借貸平臺和穩定幣協議,都建立在 Uniswap 的基礎之上,形成了一個繁榮的生態系統。

總的來說,UniswapV2 通過其創新的協議設計和去中心化的運營模式,徹底改變了加密貨幣交易的方式,成為 DeFi 領域的重要基石。隨著技術的不斷發展和社區的持續創新,UniswapV2 的影響力將進一步擴大,為全球用戶帶來更多的金融自由和機會。

2.協議特性

2.1 ERC-20 Pairs

Uniswapv1 會使用以太坊 (ETH) 作為過渡貨幣,即如果用戶想進行 TokenA 和 TokenB 交換,需要先使用 TokenA 來換取 ETH ,之後再使用 ETH 來換取 TokenB ,儘管這種措施減少了流動性的分散,但它對流動性提供者造成了極大的成本壓力。每個流動性提供者都必須具備與 ETH 交換的接口,同時,流動性提供者所持有資產的價值隨著ETH的價格波動而波動,可能會導致嚴重的損失。

當兩個資產 ABC 和 XYZ 相關聯時(例如,它們都是美元穩定幣),Uniswap 上的流動性提供者在 ABC / XYZ對中通常會承受較小的永久損失,相較於 ABC/ETH 或 XYZ/ETH 對。此外,使用 ETH 作為強制過渡貨幣會增加交易者的成本。交易者需要支付的費用是直接購買 ABC/XYZ 對費用的兩倍,並且還會遭受兩次滑點。

在 UniswapV2 當中,允許流動性提供者為任意兩個 ERC-20 代幣創建交易對合約。雖然任意 ERC-20 代幣之間的交易對數量激增,可能會使找到特定資產交易路徑變得更加複雜,但這一問題可以通過更高級別的路由來解決(通過鏈外或鏈上路由器或聚合器)。

2.2 價格預言機

UniswapV1的價格計算方式是在 t 時刻,Uniswap 所提供的邊際價格(不包含手續費)可以通過用資產 a 的儲備量除以資產 b 的儲備量來獲得。具體的計算公式如下:

然而,UniswapV1 用作鏈上價格預言機並不安全,因為它非常容易被操縱。操縱者會在一個區塊的開頭大量賣出某種資產 A 以影響價格,然後在該區塊中間根據波動的價格執行其他合約操作(非 Uniswap 交易對合約),最後在區塊結束時買回相同數量的資產 A,使價格恢復正常水平。Uniswap V2 引入了價格累計機制,允許第三方使用某一區間的平均價格,從而大大增加了價格操縱的難度,並使得操縱行為無實質收益。

具體來說,Uniswap v2 通過在每個區塊開始時記錄價格的累積和來累積這個價格,其中有人與合約交互。每個價格根據自上一個區塊更新以來所經過的時間進行加權,根據區塊時間戳。這意味著,在任何給定時間(更新後),累積器的值應該是合約歷史中每一秒的現貨價格的總和。

為了估算從時間 t₁  到 t₂ 的時間加權平均價格,外部調用者可以在 t₁ 檢查累積器的值,然後在 t₂ 再次檢查,減去第一個值,然後除以經過的秒數。(注意,合約本身不存儲這個累積器的歷史值——調用者必須在周期開始時調用合約以讀取和存儲這個值。)

預言機的用戶可以選擇何時開始和結束這個周期。選擇一個更長的周期會使攻擊者操縱時間加權平均價格(TWAP)的成本更高,儘管這會導致價格的最新程度較低。由於採用了平均值,A/B 和 B/A 在某一區間內的平均值不再是倒數關係,因此 Uniswap V2 提供了這兩種價格。

2.3 價格計算精度

由於 Solidity 不支持非整數的數值數據類型,UniswapV2 採用 UQ112.112 數據格式來提升價格計算的精度,同時使用 uint112 來存儲交易對中的資產數量,並用 32 位來記錄當前區塊的創建時間。這種時間記錄方式在 100 年後,也就是 2106 年 2 月 7 日,會導致 Unix 時間戳溢出。這個是因為 Unix 時間戳是以自 1970 年 1 月 1 日(稱為 Unix 紀元)以來經過的秒數來表示時間的,而 uint32 能表示的範圍是從0到 2^{32} – 1 秒,即約 136 年。

為了確保系統在 2106 年 2 月 7 日及以後仍能正常工作,UniswapV2 的設計中要求預言機每隔一個溢出周期(約 136 年)至少檢查一次價格。這是因為在這種設計下,即使時間戳溢出,累積價格的方法仍然是溢出安全的,即使交易跨越溢出間隔也可以正確計算價格變化。通過這種方法,確保系統在長時間內保持準確和可靠。

2.4 閃電兌換

閃電兌換是一種在區塊鏈平臺上進行的即時加密貨幣交換,用戶可以在無需等待多個區塊確認的情況下快速完成不同加密貨幣之間的交易。這種交易通常由智能合約自動執行,確保交易雙方同時履行交換承諾,從而降低交易風險並提高效率。簡答來說,閃電兌換就是先欠再還進行交易,通過閃電兌換可以實現零成本套利。

我們通過一個實際例子來解釋一下閃電兌換,假定 UniswapV2 中存在一個 A/B 的交易對,我們可以先借 A ,從而從 Uniswap 當中獲取 B ,之後再使用 B 從其他的去中心化交易所上獲取 A0,之後歸還 Uniswap 等量的 A 即可。通過上面這個流程,我們就 0 成本的完成了 A0-A 之間的價差套利。我們並不需要任何本金,只需要支付 gas 費即可。如果發現鏈上其他的 dex 之間有價差,也可以對其進行類似的操作進行零成本的套利。

2.5 Protocol Fee

UniswapV2 引入了一個可以開啟和關閉的 0.05% 協議費。如果開啟,這個費用將發送到 factory 合約中指定的 feeTo 地址。最初,feeTo 是未設置的,因此不收取費用。一個預先指定的地址 feeToSetter 可以調用 UniswapV2 factory 合約中的 setFeeTo 函數,設置 feeTo 為不同的值。feeToSetter 還可以調用 setFeeToSetter 來更改 feeToSetter 地址本身。

如果設置了 feeTo 地址,協議將開始收取 5 個基點的費用,這是流動性提供者賺取的 30 個基點費用的 \frac{1}{6} 。也就是說,交易者將繼續支付 0.30% 的所有交易費用,其中 83.3%(即 0.25%)將支付給流動性提供者,16.6%(即 0.05%)將支付給 feeTo 地址。在每次交易時收取這 0.05% 的費用會增加額外的 gas 成本。為了避免這種情況,累計費用僅在存入或提取流動性時收取。合約會計算累計費用,並在代幣鑄造或銷毀之前立即將新的流動性代幣鑄造給費用受益人。

累計費用可以通過測量√k 的增長來計算(即 √(x·y) ),自上次收取費用以來。這公式給出了在 t₁ 和 t₂ 之間,作為 t₂ 時流動池中流動性的百分比的累計費用:

如果費用在 t₁ 之前被激活,feeTo 地址應當捕獲  t₁ 到 t₂ 之間累計費用到 ⅙。因此,我們想要鑄造新的流動性代幣到 feeTo 地址,代表 Φ·f₁,₂ 的池子, 其中Φ=⅙ 。

也就是說,我們希望選擇 sₘ 來滿足以下關係,其中 s₁ 是時間 t₁ 時的流通股的總量:

經過一些運算,包括用 1-

來代替 f₁,₂ 並求解 sₘ , 我們可以將其改寫為:

將 Φ 設置為 ⅙ ,我們會得到以下公式

下面,我們通過一個具體的例子來解釋一下。假設初始存款人將 100 DAI 和 1 ETH 存入一個配對中,獲得 10 股。一段時間後(沒有其他存款人參與該配對),他們嘗試提取資金,此時該配對中有 96 DAI 和 1.5 ETH。將這些值代入上述公式,我們得到如下結果:

2.6 交易費用計算

UniswapV1 交易費用的計算是公式是

這個公式意味著只是先減少支付數量後再執行恆定乘積公式;在 UniswapV2 的版本中,因為 Flash Swaps 機制的存在,交易費用的計算公式調整為

2.7 sync ()和skim()

sync() 用於將合約中暫存的資產數量更新為合約的當前實際值,主要用於處理那些比例失衡且沒有流動性提供者的情況。skim() 則用於處理合約內某種資產數量超過 uint112 最大值的情況,允許用戶提取超出 uint112 最大值的那部分資產。

2.8 處理非標準和不常用代幣

標準的 ERC20 代幣合約在轉移代幣後需要返回一個布爾值來表示轉移是否成功,但並非所有代幣都會這麼做。有些代幣沒有返回值。在 UniswapV1 中,對於沒有返回值的代幣轉移默認視為失敗,整個交易會被重置。而在 Uniswap V2 中,沒有返回值的代幣轉移被視為成功。

此外,UniswapV1 假設代幣轉移不會觸發交易對的重入,但支持 ERC777 hooks 的一些 ERC20 代幣打破了這一假設。為了支持這些代幣,UniswapV2 在所有公共狀態變量修改函數中增加了防重入鎖功能,同時也阻止了 Flash Swaps 中用戶自定義回調的重入。

2.9 流動性初始化設置

如果用戶為一個已經存在的交易對 A/B 提供流動性,那麼根據當前 A 和 B 的比例就可以計算出來要提供多少比例的 A 和 B 。但是在一個交易對初始化的時候,沒有可以參考的比例,拿這個時候應該怎麼處理呢?在 UniswapV1 的版本中,當新的流動性提供者將代幣存入現有的 Uniswap 代幣對時,將根據現有代幣數量計算所鑄造的流動性代幣數量。具體的計算公式如下:

對於第一個提供流動性的人來說,公式當中的是 Xstarting 是 0。在面對這種情況,UniswapV1 採用的方法是初始流動性的數值直接等於初始提供的 ETH 數量,這種初始流動性的提供的問題在於交易對的價值完全由初始流動性的比例所決定的,但是問題在於,沒有任何機制去擔保這個比例就是符合真實價值的。在 UniswapV2 當中,流動性的初始化可以用如下的公式

這個公式的意思是 Sminted 這是你將獲得的流動性代幣的數量, Xdeposited 這是你存入的第一種代幣的數量.例如,如果你存入的是 ETH,那 Xdeposited 就是你存入的 ETH 數量。 Ydeposited 這是你存入的第二種代幣的數量。

例如,如果你存入的是 DAI,那 Ydeposited 就是你存入的 DAI 數量。這個公式可以保證流動資金池中的份額永遠不會低於該池中的幾何平均值,但是這個公式的值也會隨著池子當中代幣數量的變化而變化,為了減弱資金池中代幣數量變化帶來的影響,UniswapV2 銷毀了最初的 1e- ¹⁵ 流動性,這個數值是最小流動性 1e-¹⁸ 的 1000 倍。儘管對於任何交易對來說這都微不足道,但卻顯著增加了利用這一機制獲利的攻擊者的成本。

2.10 WETH

由於交易以太坊原生貨幣 ETH 的接口與交易 ERC20 代幣的接口不同,許多協議不直接支持 ETH,而是使用一種替代品 WETH(包裝後的 ETH 代幣)。UniswapV1 是個例外,因為它的交易對中直接包含了 ETH,允許用戶直接使用 ETH 進行交易。然而,Uniswap V2 設計為支持任意 ERC20 代幣之間的交易對,直接支持 ETH 會使系統複雜化且增加風險。

因此,UniswapV2 中不直接支持 ETH,用戶在使用交易對前必須先將 ETH 轉換成 WETH。實際上,UniswapV2 內部自動將用戶提供的 ETH 轉換為 WETH,這樣簡化了用戶的操作,讓他們無需手動轉換 ETH 為 WETH。雖然對任何交易對來說這種轉換是微不足道的,但它有效地提高了系統的安全性和操作簡便性。

2.11 確定性交易對地址

無論是 UniswapV1 還是 UniswapV2,所有交易對都是通過單一的工廠(factory)合約創建的。在 UniswapV1 中,使用的是 create 操作碼,交易對合約的地址會受到創建順序的影響。而在 UniswapV2 中,採用了新的操作碼 create2 ,這種方法生成的地址是確定的。這意味著可以在鏈下提前計算出交易對的地址,而不需要查詢鏈上的狀態。

2.12 最大代幣數量

為了高效地實現預言機功能,Uniswap V2 採用 uint112 來保存代幣數量,這意味著其最大支持的代幣數量為 2¹¹² – 1 。對於精度為 18 的代幣來說,這個數值是足夠的,大約為 5192296858534828( 5.19e¹⁵)枚,即 5.19 千萬億枚。如果合約中的記錄值超過了這個限制,交易將會失敗並重置。正如之前提到的,任何人都可以使用 skim() 函數來恢復,通過移除流動性池中多餘的資產來解決這一問題。

3. UniswapV2原理解析

Uniswap 是一種自動化流動性協議,由恆定的產品公式提供支持,並在以太坊區塊鏈上的不可升級智能合約系統中實現。它消除了對可信中介機構的需求,優先考慮去中心化、抗審查和安全性。每個 Uniswap 智能合約或貨幣對都管理著一個由兩個 ERC-20 代幣儲備組成的流動性池。任何人都可以通過存入每個標的代幣的等值值來換取池代幣,從而成為資金池的流動性提供者 (LP)。這些代幣跟蹤總儲備中按比例分配的 LP 份額,並且可以隨時贖回標的資產。

首先先介紹 Uniswap 的自動做市商機制,下面展示了 UniswapV2 自動做市商(AMM)模型的函數圖像,是基於如下公式的:

其中, x 代表 Token A 的數量, y 代表 Token B 的數量, k 是一個常數,表示池中兩種代幣數量的乘積保持不變。交易者在 Uniswap 上進行交易時,通過向池中添加或移除代幣,改變了池中代幣的數量。根據恆定乘積公式,另一種代幣的數量會相應變化,以保持乘積 k 不變。這種變化決定了交易的價格。

例如,如果交易者希望用 Token A 交換 Token B,他們需要向池中增加一定數量的 Token A,這會導致池中的 Token B 數量減少,從而改變價格。交易者的操作會沿著這條曲線移動,改變代幣的數量和價格。曲線上任意一點都滿足恆定乘積關係。

UniswapV2 的工作原理大概可以分成三個部分,流動性提供者( Liquidity Provider),Uniswap 池(Uniswap Pool),交易者(Trader)。流動性提供者的作用是流動性提供者將兩種代幣(例如,Token A 和 Token B)存入 Uniswap 池中。

圖中顯示的例子中,流動性提供者存入了 10 個 Token A 和 1 個 Token B;Uniswap 池中儲備了各種代幣,例如圖中顯示的 100 個 Token A 和 10 個 Token B。

池中的流動性份額由流動性代幣表示,圖中顯示共有 12 個流動性代幣;交易者交易者可以向池中提交代幣並交換他們需要的另一種代幣。例如,交易者可以存入 10 個 Token A,並支付 0.3% 的手續費,從池中獲取 1 個 Token B。

我們先看流動性提供者 (LP) 是如何提供流通性的。如下圖所示,流動性提供者將代幣存入 Uniswap 池中,增加流動性。

例如,在圖中,流動性提供者存入了 3 個 Token A 和 1 個 Token B 。當流動性提供者存入代幣後,他們會收到代表其流動性份額的池代幣( Pool Tokens )。在圖中,流動性提供者獲得了 12.4 個池代幣。池中的代幣儲備會增加,例如,圖中池中的代幣儲備變為 1210 個 Token A 和 399 個 Token B 。更多的流動性有助於降低價格滑點,使交易更加穩定。

Uniswap 使用的恆定乘積公式 x · y = k 來確定價格曲線。增加的流動性擴展了低滑點區域,提升了交易的價格穩定性。流動性提供者通過存入代幣來增加池中的流動性,並獲得相應的流動性代幣作為回報。這不僅幫助了交易者獲得更穩定的價格,也為流動性提供者帶來了交易手續費的收益。

接下來,我們從交易者的視角去看,交易者是如何換取代幣以及交易行為會對 Uniswap Pool 產生什麼影響。

如下圖所示,交易者希望在 Uniswap 上交換代幣。例如,在圖中,交易者打算交換3個 Token A 。交易者輸入 3 個 Token A ,並支付 0.3% 的手續費。最終,交易者將獲得約 0.997 個 Token B 作為輸出。交易會改變池中的儲備平衡,從而導致新的價格。在交易前,池中有 1200 個 Token A 和 400 個 Token B 。

根據恆定乘積公式 x·y = k 交易後的池中將有大約 1203.009 個 Token A 和大約 399.003 個 Token B 。Uniswap 使用 x·y = k 的恆定乘積公式來定義價格曲線。隨著交易者的交換操作,池中的代幣數量變化,價格曲線也隨之調整,確定新的價格。

4. 源碼分析

4.1 核心操作流程圖解析

在這一節,我們會介紹在 UniswapV2 當中最常用的三個操作,即添加流動性,撤除流動性,交換代幣。我們會通過流程圖來分析他們調用的合約以及調用的函數,來更加深刻的理解 UniswapV2 的源碼。

4.1.1 添加流動性

用戶在添加流動性的時候,用戶首先調用 UniswapV2Router.sol 合約,提供 Token A 和 Token B 的數量,UniswapV2Router.sol 合約的 addLiquidity 函數接收用戶的請求並進行處理。

addLiquidity 函數進一步調用 UniswapV2Pair.sol 合約,在 UniswapV2Pair.sol 合約中,調用 mint 函數執行實際的流動性添加操作,mint 函數根據用戶提供的 Token A 和 Token B 的數量,計算應鑄造的流動性代幣(LP 代幣)的數量,並將這些 LP 代幣分配給用戶,流動性添加操作完成後,mint 函數調用 _update 函數更新儲備量。

4.1.2 交換代幣

用戶想要交換代幣的時候,首先調用 UniswapV2Router.sol 合約,提供輸入代幣數量和最小輸出代幣數量。

然後 UniswapV2Router 合約的 swapExactTokensForTokens 函數接收用戶的請求並進行處理,swapExactTokensForTokens 函數進一步調用 UniswapV2Pair.sol 合約,在 UniswapV2Pair.sol 合約中,調用 swap 函數執行實際的代幣交換, swap 函數根據輸入代幣數量和儲備量,計算應輸出的代幣數量,並執行交換,交換完成後,swap 函數調用 _update 函數更新儲備量和累計手續費。具體的流程如下所示:

4.1.3 撤除流動性

用戶首先調用 UniswapV2Router.sol 合約,提供要撤出 LP 代幣的數量,UniswapV2Router.sol 合約的 removeLiquidity 函數接收用戶的請求並進行處理,removeLiquidity 函數進一步調用 UniswapV2Pair.sol 合約,在 UniswapV2Pair.sol 合約中,調用 burn 函數執行實際的流動性撤出操作,burn 函數根據提供的 LP 代幣數量,計算應返還的 Token A 和 Token B 數量,並將這些代幣返還給用戶。

4.2 Core 合約

UniswapV2 Core 合約是去中心化交易平臺 Uniswap 的核心部分,負責實現其自動化做市商( AMM )功能。與傳統訂單簿不同,Uniswap 通過流動性池和恆定乘積公式 x·y = k 來實現交易。流動性提供者將兩種代幣存入池中,獲得流動性代幣作為憑證。用戶在進行交易時,合約根據池中代幣數量和恆定乘積公式計算交易價格。UniswapV2 引入了多項改進,包括 ERC20 pairs 直接交易,價格預言機的改進, 閃電貸以及協議費的調整。core 合約當中的核心組件包括下面三個文件:

  • UniswapV2Pair.sol :管理每個交易對的流動性池,處理代幣交換、流動性添加和移除

  • UniswapV2Factory.sol :負責創建和管理交易對

  • UniswapV2ERC20.sol :流動性代幣的標準實現,代表流動性提供者的份額

4.2.1 UniswapV2 Factory.sol

UniswapV2Factory 合約的作用是負責創建和管理交易對(流動性池)。該合約允許用戶創建新的交易對,並記錄所有創建的交易對。此外,它還管理交易費接收地址和設置者地址。UniswapV2Factory.sol 有五個函數,分別來看一下

  • constructor 函數:構造函數,用於初始化 UniswapV2Factory 合約。輸入是交易費設置者地址 _feeToSetter , 輸出是無。

  • allPairsLength 函數:返回所有創建的交易對的數量。 輸入是無, 輸出是所有交易對數量的 unit。

  • createPair 函數: 創建新的交易對。輸入是 tokenA 和 tokenB 的兩個代幣地址,輸出是創建的交易對地址 pair。

  • setFeeTo 函數: 設置交易費接受地址。 輸入是新的交易費接受地址 _feeTo, 輸出是無。

  • setFeeToSetter 函數: 設置新的交易費設置者地址。輸入是新的交易費設置者地址 _feeToSetter, 輸出是無。

具體的代碼解析如下:

createPair 函數

createPair 函數的作用是創建一個以 TokenA 和 TokenB 的交易對,在前端輸入 TokenA 和 TokenB 之後,會先檢查 TokenA 和 TokenB 是否是同一個幣種,之後會對 TokenA 和 TokenB 做一個簡單的排序,之後是檢查 Token0 的地址,要求 Token0 的地址不能是 0;

之後是通過 require(getPair[token0][token1] == address(0), ‘UniswapV2: PAIR_EXISTS’) ; 來檢查這個代幣對是否存在,只有存在了,才可以進行下去;後面通過 creationCode 來獲取 UniswapV2Pair 合約的創建字節碼;

之後使用 token0 和 token1 的哈希值作為鹽值,確保每個代幣對的地址是唯一的,因為如果代幣對的地址不唯一,那麼交易者添加流動性可能會添加到錯誤的池子當中;之後使用內聯彙編的 create2 指令創建合約,保證合約地址的唯一性和可預測性;

之後便是初始化新創建的代幣對合約,然後更新映射表,記錄這個代幣對合約的地址,然後將新創建的代幣對合約地址添加到所有代幣對的列表中,最後是觸發 PairCreated 事件,通知外部有新的代幣對創建。

4.2.2 UniswapV2 ERC20.sol

UniswapV2ERC20.sol 的主要功能是實現 ERC-20 代幣,它實現了 ERC20 標準的代幣功能,專門用於 UniswapV2 流動性池。合約包含鑄造( mint )、銷毀( burn )、批准( approve )和轉移( transfer )等基本操作。此外,它還支持 permit 功能,允許使用籤名來批准代幣轉移。我們來逐個看他包含的函數:

  • constructor 函數:初始化合約,設置 DOMAIN_SEPARATOR 用於 permit 功能。輸入無,輸出無。

  • _mint 函數:鑄造新的代幣,輸入是接收地址「 to 」,和鑄造數量「 value 」,輸出無

  • _burn 函數: 銷毀代幣,輸入是銷毀地址 from 和銷毀數量 value,輸出無。

  • _approve 函數: 批准代幣轉移,所有者地址 owner,批准地址 spender 和批准數量 value,輸出無。

  • _transfer 函數:轉移代幣, 輸入是轉出地址 from,接收地址 to 和轉移數量 value, 輸出無。

  • approve 函數:公開的批准函數,作用是調用 _approve 函數,輸入是approve 函數公開的批准函數, 輸出是返回布爾值 true 表示操作成功。

  • transfer 函數:作用是調用 _transfer 函數,輸入是接受地址 to 和轉移數量 value, 輸出是返回布爾值 true 表示操作成功

  • transferFrom 函數:公開的授權轉移函數。輸入是轉出地址 from,接收地址 to 和 轉移數量 value,輸出是 返回布爾值 true 表示操作成功

  • permit 函數: 使用籤名來批准代幣轉移,驗證籤名並調用 _approve 函數,輸入是所有者地址 owner,批准地址 spender,批准數量 value,截止時間 deadline,籤名參數 v、r、s,輸出無。

官方的源碼解析如下所示:

4.2.3 UniswapV2 Pair.sol

UniswapV2Pair 即交易對合約,實現了 Uniswap v2 的核心功能,即管理和操作每個交易對的流動性池。該合約負責處理代幣的交換、流動性的添加和移除,以及價格的累積計算。它確保在每次交易後,交易對的儲備和價格信息得到更新,並觸發相應的事件通知。UniswapV2Pair.sol 中有 11 個函數,具體入下面表格所示:

官方的 UniswapV2Pair.sol 的代碼和注釋如下:

UniswapV2Pair 是繼承 IUniswapV2Pair, UniswapV2ERC20,首先看看 IUniswapV2Pair 的源碼,看看 IUniswapV2Pair 如何定義接口:

之後定義了全局變量和修飾器

上面的 MINIMUM_LIQUIDITY 是一個常量,它設定了流動性池中必須保留的最小流動性代幣數量,以確保流動性提供者在任何時候都至少保留一定量的代幣,從而避免流動性枯竭,數值是 10 的3次方,在提供初始流動性時會被燃燒掉; SELECTOR 存儲的是代幣轉帳函數的 ABI(應用程式二進位接口)選擇器,它用於在智能合約中準確地識別和調用其他合約的 transfer 函數,確保在執行代幣轉移時使用正確的函數籤名;

factory 用於存儲交易對合約的Uniswap V2工廠合約的地址,Token0,Token1 用於存儲代幣地址,reserve0, reserve1 和 blockTimestampLast 這三個狀態變量記錄了最新的恆定乘積中兩種資產的數量和交易時的區塊(創建)時間;

而 price0CumulativeLast 和 price1CumulativeLast 變量用於記錄交易對中兩種價格的累計值,kLast 用於跟蹤 UniswapV2 交易對中兩種代幣儲備量乘積的最近狀態,作為一個關鍵參數來維持流動性池的價格穩定性和計算交易費用,主要用於團隊手續費的計算。

下面這一段修飾器是提供了一種鎖機制來防止重入攻擊,具體代碼解析如下:

上面的代碼當中的 _; 表示被修飾的函數體,這段代碼的大致邏輯是:定義了一個 lock 修飾符,它通過改變 unlocked 變量的狀態來確保在執行被修飾的函數期間合約不會被重新進入,從而防止重入攻擊和競態條件。

下面的 getReserves 函數的作用是提供一種方式來公開查詢並返回 UniswapV2 交易對合約當前的兩種代幣儲備量和最後更新時間戳的信息。

_safeTransfer 函數的作用是在智能合約內部執行代幣的轉移操作,並檢查轉移是否成功,如果失敗則拋出異常,確保了合約的安全性和代幣轉移的可靠性,下面是這一段代碼的詳細注釋:

下面的構造函數只是簡單的用於初始化 factory:

initialize 函數的作用是設置交易對合約所涉及的兩種代幣的地址,且只能由部署交易對的工廠合約(factory) 來調用,確保交易對的初始化過程是安全和受控的。

_update 函數的主要作用是確保交易對合約的儲備量和價格累積器能夠反映最新的狀態, 具體實現的方法是通過比較當前區塊的時間戳和上次更新的時間戳。_update 函數的四個輸入參數分別是:balance0 和 balance1 ,表示交易對中兩種代幣當前的餘額;_reserve0 和 _reserve1 ,表示函數調用前兩種代幣的儲備量。我們接下來用bullet point的方式來講解一下 _update 函數是如何實現的:

  1. 檢查餘額值是否可能導致溢出:通過 require語句確保傳入的 balance0 和 balance1 不會超過 uint112 的最大值,這是因為 reserve0 和 reserve1 在存儲時使用 uint112 類型,需要保證數據類型轉換的安全性。

  2. 記錄當前區塊時間:獲取當前區塊的時間戳,並將其與 blockTimestampLast 進行模2^32運算,得到 blockTimestamp 。這個操作是因為以太坊的區塊時間戳是32位的,而且我們只關心在一個區塊內的時間差,而不是絕對時間。

  3. 計算時間差:計算當前區塊時間與上次更新時間的差值 timeElapsed 。如果 timeElapsed 為0,表示這是同一區塊內的連續調用,因此不會更新價格累積值。

  4. 價格累積更新:如果時間差大於0,並且儲備量不為0,使用固定點數學庫 UQ112x112 來計算價格比例,並更新 price0CumulativeLast 和 price1CumulativeLast 。這裡的「never overflows」意味著由於時間間隔 timeElapsed 是 uint32 類型,與價格累積值(uint224)相乘不會導致溢出。「+ overflow is desired」指的是價格累積值允許溢出,因為價格計算使用的是變化量(delta)而不是絕對值,即使有溢出,計算平均價格時使用的變化量仍然是準確的。

  5. 更新儲備量:將新的餘額賦值給 reserve0 和 reserve1 ,更新流動性池的儲備量。

  6. 更新時間戳:將當前區塊時間戳賦值給 blockTimestampLast ,為下一次更新做準備。

  7. 觸發同步事件:通過 emit 關鍵字發出 Sync 事件,告知外部監聽者儲備量已經更新。

這種設計允許 UniswapV2 在處理大量交易時保持價格的連續性和準確性,即使在區塊時間戳或價格累積值可能溢出的情況下,依然能夠通過變化量來準確計算出平均交易價格。這是通過巧妙地利用固定點數學和時間差分來實現的。

在 UniswapV2 中,用戶每筆交易會被收取0.3%的手續費。這筆手續費中的六分之一將分配給開發團隊,而剩下的六分之五將作為獎勵給予流動性提供者。然而,如果每次交易都計算一次手續費,這將不可避免地增加用戶的 Gas 費用。

因此,在UniswapV2中,手續費會被累積起來,只有在流動性發生變化時才會對手續費進行分配。_mintFee函數首先檢查是否開啟了交易費用,並確定費用接收地址。如果交易費用未開啟,且之前有鑄造過費用(_kLast不為0),則重置 kLast 值。這種費用鑄造機制是 UniswapV2 的一部分,用於為流動性提供者提供額外的激勵;如果交易費用開啟,則根據下面的公式來計算手續費的值,

Sₘ 表示應該鑄造的手續費流動性代幣數量,k₁表示上一個流動性事件後的儲備的乘積k,k₂ 表示當前的儲備乘積k,S₁表示上一個流動性事件後的總流動性代幣供應量。

4.3 Periphery 合約

UniswapV2 周邊合約的主要作用是作為外部帳戶與核心合約之間的橋梁,包含接口定義、工具類庫、Router 和示例實現四部分內容。

4.3.1 Libraries

Libraries 文件夾下面包含了四個文件

  • SafeMath.sol

  • UniswapV2Library.sol

  • UniswapV2LiquidityMathLibrary.sol

  • UniswapV2OracleLibrary.sol

我們接下來分別對這四個sol文件進行詳細的解析

SafeMath.sol

SafeMath.sol 用於執行溢出安全的數學運算,這種安全數學運算對於避免整數溢出和下溢錯誤非常重要,特別是在區塊鏈和智能合約開發中。主要是包含三個函數。

  • add 函數:用於安全地執行兩個無符號整數的加法運算

  • sub 函數:用於安全地執行兩個無符號整數的減法運算

  • mul函數:用於安全地執行兩個無符號整數的乘法運算

具體的代碼注釋如下

UniswapV2Library.sol

UniswapV2Library.sol 提供了一些實用函數,用於與 Uniswap v2 交換對 (pairs) 進行交互和操作。

這些函數主要用於計算交易路徑、獲取儲備、計算價格和執行鏈式計算。該庫使用了一個名為 SafeMath 的庫來確保數學運算的安全性,避免整數溢出和下溢。 UniswapV2Library.sol 這個文件包含了八個函數:

  • sortTokens 函數:返回按地址排序的兩個代幣地址。輸入是兩個代幣地址 tokenA 和 tokenB。輸出是排序後的代幣地址 token0 和 token1。

  • pairFor 函數:計算出給定工廠地址和兩個代幣地址的對的地址,而無需進行外部調用。輸入是工廠地址 factory, 兩個代幣地址 tokenA 和 tokenB; 輸出是該對的地址 pair

  • getReserves 函數:獲取並排序一個對的儲備。輸入是工廠地址 factory,兩個代幣地址 tokenA 和 tokenB; 輸出是兩個代幣的儲備量 reserveA 和 reserve B

  • quote函數:根據給定資產數量和對的儲備,返回另一個資產的等價數量。輸入:資產數量 amountA ,儲備 reserveA 和 reserveB 。輸出是另外一個資產的數量 amountB。

  • getAmountOut函數: 根據輸入資產數量和對的儲備,返回另一個資產的最大輸出數量。 輸入是輸入資產數量 amountIn,儲備 reserveIn 和 reserveOut; 輸出是 輸出資產的數量 amountOut。

  • getAmountIn函數:根據輸出資產數量和對的儲備,返回所需輸入的另一個資產的數量。輸入是輸出資產數量 amountOut,儲備 reserveIn 和 reserveOut。

  • getAmountsOut函數:在任意數量的對上執行鏈式 getAmountOut 計算,輸入是工廠地址 factory ,輸入資產數量 amountIn ,路徑 path;輸出是每個路徑節點的資產數量數組 amounts。

  • getAmountsIn函數:在任意數量的對上執行鏈式 getAmountIn 計算,輸入是工廠地址 factory,輸出資產數量 amountOut,路徑 path ; 輸出是每個路徑節點的資產數量數組 amounts

UniswapV2Libray 源碼詳細注釋如下:

UniswapV2OracleLibrary.sol

UniswapV2OracleLibrary.sol 文件,提供了一些輔助方法,用於與預言機計算平均價格相關的操作。該庫包括獲取當前區塊時間戳和計算累積價格的方法,幫助節省gas費用和避免頻繁的同步調用。包含了兩個函數,具體如下:

  • currentBlockTimestamp 函數:返回當前區塊時間戳。 輸入無,輸出是當前區塊時間戳,類型為 uint32 。

  • currentCumulativePrices 函數:計算並返回累積價格。輸入是交易對地址 pair,輸出是累積價格 price0Cumulative 和 price1Cumulative 以及當前區塊時間戳 blockTimestamp。

UniswapV2OracleLibrary.sol 官方的源碼詳細注釋如下:

UniswapV2LiquidityMathLibrary.sol

UniswapV2LiquidityMathLibrary.sol 的官方源碼如下

  • computeProfitMaximizingTrade 函數:計算實現利潤最大化的交易方向和大小。

  • getReservesAfterArbitrage 函數:在觀察到的「真實價格」下,通過套利交易後獲取流動性池的儲備量。

  • computeLiquidityValue 函數:計算流動性價值,給定流動性池的所有參數。

  • getLiquidityValue 函數:獲取當前所有參數,並計算流動性金額的價值。

  • getLiquidityValueAfterArbitrageToPrice 函數:給定兩種代幣和它們的「真實價格」,以及流動性金額,返回流動性的價值。

UniswapV2Router02.sol

我們先看看 UniswapV2Router02 的源碼,這一部分的代碼可以大致分成六個大的部分,構造函數和修飾符, 接受ETH的函數, 添加流動性,移除流動性, 代幣交換, 庫函數。

構造函數和修飾符

  • constructor(address _factory, address _WETH): 初始化工廠合約地址和WETH合約地址。

  • modifier ensure(uint deadline): 確保交易在截止時間之前完成。

接收ETH的函數

  • receive() external payable: 接收ETH,只允許從WETH合約調用。

添加流動性

  • _addLiquidity: 內部函數,用於添加流動性,根據現有儲備量計算最優的代幣數量。

  • addLiquidity: 添加兩種ERC-20代幣的流動性。

  • addLiquidityETH: 添加ERC-20代幣和ETH的流動性。

移除流動性

  • removeLiquidity: 移除兩種ERC-20代幣的流動性。

  • removeLiquidityETH: 移除ERC-20代幣和ETH的流動性。

  • removeLiquidityWithPermit: 帶許可的移除兩種ERC-20代幣的流動性。

  • removeLiquidityETHWithPermit: 帶許可的移除ERC-20代幣和ETH的流動性。

  • removeLiquidityETHSupportingFeeOnTransferTokens: 移除ERC-20代幣和ETH的流動性,支持手續費代幣。

  • removeLiquidityETHWithPermitSupportingFeeOnTransferTokens: 帶許可的移除ERC-20代幣和ETH的流動性,支持手續費代幣。

代幣交換

  • _swap: 內部函數,執行代幣交換邏輯。

  • swapExactTokensForTokens: 使用確切數量的代幣交換另一種代幣。

  • swapTokensForExactTokens: 使用代幣交換確切數量的另一種代幣。

  • swapExactETHForTokens: 使用確切數量的ETH交換代幣。

  • swapTokensForExactETH: 使用代幣交換確切數量的ETH。

  • swapExactTokensForETH: 使用確切數量的代幣交換ETH。

  • swapETHForExactTokens: 使用ETH交換確切數量的代幣。

  • swapExactTokensForTokensSupportingFeeOnTransferTokens: 使用確切數量的代幣交換另一種代幣,支持手續費代幣。

  • swapExactETHForTokensSupportingFeeOnTransferTokens: 使用確切數量的ETH交換代幣,支持手續費代幣。

  • swapExactTokensForETHSupportingFeeOnTransferTokens: 使用確切數量的代幣交換ETH,支持手續費代幣。

庫函數

  • quote: 根據儲備量計算給定數量代幣A對應的代幣B數量。

  • getAmountOut: 計算給定輸入數量和儲備量的情況下可以獲得的輸出數量。

  • getAmountIn: 計算給定輸出數量和儲備量的情況下需要的輸入數量。

  • getAmountsOut: 根據路徑和輸入數量計算輸出數量。

  • getAmountsIn: 根據路徑和輸出數量計算輸入數量。

具體我們下面逐個函數的去看,UniswapV2Router02.sol 的合約通過下面這一段代碼繼承自 IUniswapV2Router02,

UniswapV2Router02 合約實現了 IUniswapV2Router02 接口,提供了以下關鍵功能:

  1. 添加流動性:允許用戶向流動性池中添加兩種代幣,以換取流動性提供者代幣。

  2. 移除流動性:允許流動性提供者將他們的流動性提供者代幣兌換回兩種代幣。

  3. 交易:允許用戶在不同代幣之間進行交易,支持直接交易和通過多個交易對的路徑交易。

  4. 報價計算:提供了一系列函數,用於計算給定輸入或輸出量時的交易細節。

ensure 修飾器用於檢查當前區塊時間是否超過最遲交易時間,確保用戶指定的交易不會因超時而失敗,有助於提高交易的安全性和可靠性。

構造函數初始化了工程合約地址和 weth 合約地址,這兩個地址在整個合約的生命周期中都是不變的。

receive 函數通常用於允許合約直接接收以太幣,而不是通過函數調用。在這個特定的例子中,receive 函數通過 assert 語句確保只有 WETH 合約可以向其發送以太幣。

_addLiquidity 函數的目的是計算在添加流動性時用戶需要存入的兩種代幣的最優數量。這個函數的接受有六個參數,tokenA 和 tokenB 是要添加流動性的兩種代幣的地址,amountADesired 和 amountBDesired 是用戶希望添加的兩種代幣的初始數量,amountAMin 和 amountBMin 是用戶能接受的最小添加數量,用於防止滑點過低,函數返回兩個參數:amountA 和 amountB,即實際添加的兩種代幣的數量。

這個計算考慮了流動性池中已有的代幣儲備量,以確保用戶添加的流動性是平衡的。如果流動性池是新的,沒有現有的儲備,用戶可以直接添加他們希望的數量。如果池中已經有儲備,函數將計算基於當前比例的最優添加數量,確保流動性添加後池中的代幣比例保持不變。

addLiquidity 的公共函數,用於向指定的兩種代幣( tokenA 和 tokenB )的流動性池添加流動性

addLiquidityETH 函數允許用戶向 UniswapV2 流動性池添加非以太幣代幣和以太幣,以換取相應的流動性代幣,這個函數有六個接受的參數,token 是要添加流動性的非以太幣代幣的地址,amountTokenDesired 是用戶想要添加的非以太幣代幣的數量,amountTokenMin 是用戶能接受的最小非以太幣添加數量,用於防止滑點過低,amountETHMin 是以太幣的最小添加數量,同樣用於防止滑點過低,to是接收新鑄造的流動性代幣的地址,deadline是交易的截止時間,用於防止交易超時。

函數返回三個參數:amountToken 和 amountETH 是實際添加到流動性池的非以太幣和以太幣的數量,liquidity 是新鑄造的流動性代幣的數量。

removeLiquidity 函數允許用戶從流動性池中移除他們之前添加的流動性,並按照比例獲得兩種代幣。burn 函數是 IUniswapV2Pair 流動性池合約中的一個底層函數,用於執行實際的流動性銷毀和代幣分配操作。

removeLiquidityETH 函數允許用戶從 UniswapV2 流動性池中移除他們之前與以太幣一起添加的流動性,並分別獲得相應的非以太幣和以太幣。函數首先調用 removeLiquidity ,之後通過 safeTransfer 提取對應的 token ,然後將燃燒流動性提取的 WETH 換成 ETH ,之後將兌換的 ETH 轉給接受者。

removeLiquidityWithPermit 函數使用戶能夠通過 ECDSA 籤名進行授權,從而允許合約代表用戶移除流動性,而無需用戶先前通過 approve 函數進行授權。

removeLiquidityETHWithPermit 函數結合了 removeLiquidityETH 和 permit 授權機制,允許用戶通過籤名一次性授權合約移除流動性,而不需要使用標準的 approve 模式。這在提供更好的用戶體驗和安全性的同時,也減少了交易的 gas 成本。

removeLiquidityETHSupportingFeeOnTransferTokens 函數允許用戶從 UniswapV2 流動性池中移除他們之前添加的與特定代幣和以太幣的流動性,同時考慮到某些代幣在轉帳時可能會收取費用。

函數首先調用 removeLiquidity 來執行流動性的移除,然後處理代幣的轉移,確保用戶獲得他們應得的代幣數量,最後將 WETH 轉換回以太幣並轉移給用戶。整個過程需要在用戶指定的交易截止時間之前完成。

removeLiquidityETHWithPermitSupportingFeeOnTransferTokens 函數結合了 removeLiquidityETHSupportingFeeOnTransferTokens 和 permit 授權機制,允許用戶通過籤名一次性授權合約移除流動性,同時處理可能在轉帳時收取費用的代幣。

這種方法提供了一種無需使用 approve 模式即可授權合約操作用戶資產的方式,從而減少了 gas 成本並提高了用戶體驗。

_swap 函數是一個內部函數,用於執行一系列代幣交換操作。它按照指定的路徑和數量,從一個代幣交換到另一個代幣,直到達到最終的代幣。這個函數是流動性池交互的核心部分,用於實現代幣的轉換和流動性池的更新。

swapExactTokensForTokens 函數是 UniswapV2 Router 的一個關鍵功能,允許用戶以一個確切的輸入量來交換至少一個最小輸出量的代幣。這個函數首先計算出整個交換路徑的輸出量,然後確保最終的輸出量滿足用戶的最小要求,接著安全地從用戶轉移代幣到流動性池,並執行交換操作。

swapTokensForExactTokens 函數允許用戶指定他們希望獲得的代幣數量,並提供不超過最大值的代幣以進行交換。這個函數首先計算出為了獲得 amountOut 所需的最大輸入量,然後確保這個輸入量不超過用戶指定的 amountInMax 。

swapExactETHForTokens 函數允許用戶以確切數量的 ETH 交換至少一定數量的另一種代幣。這個函數首先驗證交換路徑是否有效,然後計算所需的 WETH 數量,將ETH 存入 WETH 合約,然後執行交換操作,並將交換得到的代幣發送到用戶指定的地址。整個過程需要在用戶指定的交易截止時間之前完成。

swapExactETHForTokens 函數的作用是用於賣出制定數量的 ETH 換取其他 Token 。首先進行路徑有效檢查,確保 path 數組的第一個元素是 WETH 地址,因為 Uniswap 交易對為 ERC20/ERC20 交易對;

接下來是計算輸出金額,使用 UniswapV2Library.getAmountsOut 函數,根據用戶發送的 ETH 數量 msg.value 和代幣路徑 path ,計算出用戶能夠獲得的每種代幣的數量,並將結果存儲在 amounts 數組中;接下來是做最小輸出驗證,函數檢查 amounts 數組中最後一個元素(即目標代幣的數量)是否滿足用戶設定的最小輸出數量 amountOutMin 。

如果不滿足,將拋出一個錯誤;接下來是調用 WETH 代幣合約的 deposit 函數,將 msg.value 的 ETH 存入 WETH 合約,斷言 WETH 合約向指定的交易對合約地址轉移 amounts[0] 數量的 WETH,如果失敗則交易回滾,最後是調用內部函數 _swap ,執行實際的代幣交換過程。

swapTokensForExactETH 是賣出其他 Token 換取一定數量 ETH 的過程,函數接收五個參數,包括期望獲得的 ETH 數量 amountOut ,用戶願意提供的最大代幣數量 amountInMax ,代幣交換路徑 path ,接收 ETH 的地址 to ,以及交易的截止時間 deadline;

首先是路徑有效性檢查,確保交換路徑的最後一個元素是 WETH 地址,如果不是則拋出異常;接下來是輸入金額計算, 使用 UniswapV2Library.getAmountsIn 函數計算為了獲得 amountOut 數量的 ETH ,用戶需要提供的代幣數量;確保計算出的需要提供的代幣數量不超過用戶設定的最大數量,如果不是則拋出異常;使用 TransferHelper 的 safeTransferFrom 函數,從 msg.sender 地址安全轉移代幣到流動性池;

接下來調用內部函數 _swap ,執行實際的代幣交換過程;調用 WETH 合約的 withdraw 函數,將交換得到的 WETH 轉換回 ETH;最後使用 TransferHelper 的 safeTransferETH 函數,將獲得的 ETH 安全轉移到用戶指定的地址。

swapExactTokensForETH 函數的作用是用於以固定數量的代幣交換至少 amountOutMin 數量的 ETH,函數接收五個參數,包括用戶願意提供的代幣數量 amountIn ,期望獲得的最小 ETH 數量 amountOutMin ,代幣交換路徑 path ,接收 ETH 的地址 to ,以及交易的截止時間 deadline;函數的實現邏輯和 swapTokensForExactETH 的邏輯很像。

swapETHForExactTokens 的函數,它允許用戶使用 ETH 交換固定數量的代幣,實現邏輯和上面的 swapExactTokensForETH,swapTokensForExactETH 類似。

_swapSupportingFeeOnTransferTokens 函數支持交易費用的代幣交換的邏輯,通過遍歷代幣路徑,計算每個流動性池中需要交換的代幣數量,並執行交換操作。

swapExactTokensForTokensSupportingFeeOnTransferTokens 固定數量的代幣精確兌換至少期望數量的另一種代幣,同時處理了交易費用代幣的情況。

swapExactETHForTokensSupportingFeeOnTransferTokens 實現了用特定數量的 ETH 去換取其他 Token

swapExactTokensForETHSupportingFeeOnTransferTokens 函數實現了用戶能夠以固定數量的代幣精確兌換至少期望數量的 ETH,同時處理了交易費用代幣的情況,並確保了 ETH 的準確轉移。

quote 函數實現了根據用戶指定的代幣數量和流動性池中兩種代幣的儲備量,計算並返回用戶能夠交換得到的另一種代幣數量的功能。

getAmountOut 函數和 getAmountsOut 函數的計算原理都是恆定乘積算法。定義函數 getAmountOut ,用於計算給定輸入金額和儲備量後,用戶可以得到的輸出金額;定義函數 getAmountsOut ,用於計算給定輸入金額和交換路徑後,用戶可以得到的一系列輸出金額。

getAmountIn 和 getAmountsIn 的計算原理是根據恆定乘積算法,實現了制定買進資產數量的情況下,計算賣出資產的數量。

4.3.2 UniswapV2 Migrator.sol

UniswapV2Migrator.sol 用於將流動性從 Uniswap v1 遷移到 Uniswap v2。該合約包括接收 ETH 的能力,並使用 Uniswap v1 和 v2 的路由器和交換合約進行遷移操作。它確保用戶可以安全地將他們在 Uniswap v1 中的流動性轉移到 Uniswap v2 中。具體有下面三個函數,每個函數的具體功能如下:

  • constructor 函數:構造函數,用於初始化 UniswapV2Migrator 合約。輸入是 v1 工廠地址 _factoryV1 和 v2 路由器地址 _router; 輸出無

  • receive 函數:接收 ETH,允許合約接收來自任何 v1 交換和路由器的 ETH。輸入無,輸出無。

  • migrate 函數:輸入是代幣地址 token、最小代幣數量 amountTokenMin、最小 ETH 數量 amountETHMin、接收地址 to、截止時間 deadline;輸出是無

UniswapV2Migrator 的官方源碼詳細解析如下:

4.3.3 Interfaces 文件夾

interfaces 文件夾包含了用於與 UniswapV1 和 V2 交換、路由器、工廠、以及 WETH 和 ERC20 合約進行交互的接口定義。主要功能包括管理和遷移流動性、處理代幣交易、添加和移除流動性、以及包裝和解包 ETH。這些接口確保合約間的標準化交互。我們具體來看每個文件的內容。

IUniswapV1Exchange.sol

這個文件定義了一個接口 IUniswapV1Exchange,用於與 UniswapV1 交換合約進行交互。交換合約主要負責處理代幣與 ETH 之間的交易和流動性管理。包含如下四個函數:

  • balanceOf 函數:返回某地址在交換合約中的餘額。

  • transferFrom 函數:從一個地址轉移代幣到另一個地址。

  • tokenToEthSwapInput 函數:用於將代幣交換為以太幣。

  • ethToTokenSwapInput 函數:將以太幣交換為代幣。

IUniswapV1Factory.sol

IUniswapV1Factory.sol 它用於與 UniswapV1 的工廠合約進行交互。具體的代碼解析如下:

IERC20.sol

IERC20.sol 定義了一個名為 IERC20 的接口,它遵循了以太坊代幣標準( ERC-20 )

IUniswapV2migrator.sol

IUniswapV2migrator.sol 用於與Uniswap V2的遷移合約進行交互,詳細的代碼注釋如下:

IUniswapV2Router01.sol

IUniswapV2Router01 接口提供了多種功能,包括流動性的添加和移除、代幣的交換等,這些功能是構建去中心化交易平臺和流動性池的核心。通過這個接口,用戶可以方便地與 UniswapV2 協議進行交互。

IUniswapV2Router02.sol

IUniswapV2Router02.sol 接口中的方法主要增加了對轉帳費用的支持,這允許流動性提供者或代幣持有者在代幣轉帳時獲得一部分費用作為回報。這些方法的實現通常會在底層調用相應的代幣合約,以實現轉帳時的費用收取。

IWETH.sol

IWETH.sol 定義了一個名為 IWETH 的接口,它代表了以太坊上的一個標準接口,用於與包裝以太幣( Wrapped Ether,簡稱WETH )合約交互。

  • Related Posts

    Wintermute Ventures:我們為什麼投資Euler?

    2025年4月18日,做市商Wintermute宣布旗下投資…

    Glassnode:我們正在經歷牛熊轉換嗎?

    來源:Glassnode;編譯:白水,比特鏈視界 摘要 宏觀…

    發佈留言

    發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

    You Missed

    歷史性轉折:比特幣正在成為避險資產

    • By jakiro
    • 19 4 月, 2025
    • 3 views
    歷史性轉折:比特幣正在成為避險資產

    是什麼讓加密貨幣rug pull事件頻發?

    • By jakiro
    • 18 4 月, 2025
    • 8 views
    是什麼讓加密貨幣rug pull事件頻發?

    Wintermute Ventures:我們為什麼投資Euler?

    • By jakiro
    • 18 4 月, 2025
    • 9 views
    Wintermute Ventures:我們為什麼投資Euler?

    川普可以將鮑威爾炒魷魚嗎?會帶來什麼經濟風險?

    • By jakiro
    • 18 4 月, 2025
    • 10 views
    川普可以將鮑威爾炒魷魚嗎?會帶來什麼經濟風險?

    Glassnode:我們正在經歷牛熊轉換嗎?

    • By jakiro
    • 18 4 月, 2025
    • 10 views
    Glassnode:我們正在經歷牛熊轉換嗎?

    The Post Web加速器首批8個入選項目速覽

    • By jakiro
    • 17 4 月, 2025
    • 12 views
    The Post Web加速器首批8個入選項目速覽
    Home
    News
    School
    Search