# Uniswap V3 的流動池機制 我們知道,Uniswap V3 在 AMM 的模型本身與 V2 最大的差別就是在同一個貨幣對裡面,會有多個流動池。每個流動池只負責在其「price range」價格裡面的換匯。 既然如此,很直覺的我們會想問一個問題:「價格的行為在每個流動池會一樣嗎?」講數學一點就是,對於每個流動池,$liquidityPool_i$,是不是都會有個別的 $x_i \times y_i = k_i$ 呢? 事實上,這樣的問題是以個別的流動池的角度去思考價格的行為,而這其實有點不恰當,應該以「價格區間」為出發點去思考會比較合適,因為在某個價格區間內,價格的行為是由一群有碰觸到這個價格區間的流動池共同決定的。 更詳細的內容,待到文章後續內容展開。 <!---<center> <img src="https://i.imgur.com/QwU9klZ.png" width=50% style="float:middle"> <p style = "font-size:10px"> 在V2,提供流動性可以直接指定兩個我要提供的兩個貨幣數量, 後續的計算大概也就是比大小。 </p> </center>---> ## 1. 符號定義與 V2 回顧 在這篇文章,我們會使用到一些計算範例,幫助讀者了解 unit of liquidity 的意涵,所以在這個章節,我們先定義一些符號,必且為讀者複習一下價格與流動池狀態的關係,方便我們後續對計算範例建立直覺上的理解。 ### 符號定義 首先,在範例中,我們會用到兩種 token,$ABC$ 與 $XYZ$。 我們定義,在 liquidity pool 中,$ABC$ 與 $XYZ$ 的存量分別為 $R_{ABC}$、$R_{XYZ}$。自然而然的,我們也去定義 $k := R_{ABC} \times R_{XYZ}$,以及價格 $p:=\frac{R_{XYZ}}{R_{ABC}}$。 這邊特別說明一下我們如此定義價格 $p$ 所隱含的經濟意涵。如果我們導入外匯市場的語言的話,我們是將 $ABC$ 視為 base currency,而 $XYZ$ 為 quote currency,所以 $p$ 其實是 $ABC/XYZ$ 的報價,也就是說 1 個 $ABC$ 得用 $p$ 個 $XYZ$ 去買。 ### 由 k 與 p 推得 $R_{ABC}$、$R_{XYZ}$ 根據上述定義,我們自然有下列聯立方程組: $$ \left\{ \begin{array}{c} R_{ABC} \times R_{XYZ} = k \\ \frac{R_{XYZ}}{R_{ABC}} = p \end{array} \right. $$ 我們可以輕易解得: $$ \left\{ \begin{array}{c} R_{ABC} = \sqrt{\frac{k}{p}} \\ R_{XYZ} = \sqrt{kp} \end{array} \right. $$ ### 價格與 $R_{ABC} \times R_{XYZ} = k$ 的方程式圖形 <center> <img src="https://i.imgur.com/W6L4xiQ.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> </p> </center> <!--(https://i.imgur.com/4cqMGR5.png)--> 圖中藍色的線上每個點都對應到一個不同的價格。我們可以輕易的知道,線上越右下的點所對應到的價格 $p$ 越貴(例如 $a$ 點對應到的價格 $p_a$),因為這時 X Reserves (也就是 $R_{XYZ}$) 較大,而 $R_{ABC}$ 較小,在流動池中 $ABC$ 相對稀有,因而要用較多的 $XYZ$ 去換,所以此時 $p$ 是較貴的。 順著這邏輯,我們知道 $a, b, c$ 三點對應到的價格 $p_{a}, p_{b}, p_{c}$ 有大小關係:$p_a > p_c > p_b$。 我們知道換匯是不會影響 liquidity pool 的 $k$,但會影響到價格,也就是換匯這個動作會導致 liquidity pool 的狀態在圖中藍色的線上移動,例如我們不斷的去買 ABC (將 XYZ 丟進 liquidity pool 並拿出 ABC),就會使得價格由現在的 $p_c$ 往較貴的 $p_a$ 去移動。 <center> <img src="https://i.imgur.com/zdcjVmj.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> </p> </center> ## 2. V3 的流動池 在這節我們會簡述 V3 流動池的一些規則。 ### 2.1 價格數列 ticks V3 最大的不同就是,現在流動性提供者可以指定自己提供的流動性只支援某價格區間內的換匯。但如果我們不去限定使用者指定的價格區間,可能會有使用者指定奇怪的上下界,像是$[\sqrt{2}, \pi]$。這大大增加往後運算的難度,因為價格邊界的可能性太多了。 所以在實作上 V3,V3多出了「ticks」的限制。ticks 在金融市場是指價格變動的最小單位,因為有 ticks,所以我們不會看到台積電的股價出現 $424\sqrt{2}$ 這種東西。在 V3 , ticks 則是預先指定好的價格數列。V3 的價格數列是公比為 0.0001 (一個bps) 的數列,也就是$\{..., 1.0001^{-2}, 1.0001^{-1}, 1, 1.0001, 1.0001^2, ...\}$ 又因為每個貨幣對價格波動的幅度又不太一樣,對於價格波動較大的貨幣對來說,上述精細的 ticks 反而會造成運算資源的浪費,所以除了 ticks 之外,V3 在實作上還有所謂的 tick spacing,也就是跳過價格數列元素的數量。 例如說,ABC/XYZ 的價格波動度很高,我們就會有較大的 tick spacing,也許是 60,那在這個貨幣對,可能出現的價格就會是: $\{..., 1.0001^{-120}, 1.0001^{-60}, 1, 1.0001^{60}, 1.0001^{120}, ...\}$ ### 2.2 現在的價格與流動池的價格區間 流動性提供者在指定流動池要指定其價格區間,為了方便,我們將價格區間的上界計作$p_{upper}$,下界計作$p_{lower}$。而現在的價格則計為$p_{current}$ 在指定價格區間時,可以分為兩種情況: 1. $p_{current} > p_{upper}$,此時,我們只提供$XYZ$ <center> <img src="https://i.imgur.com/S7loxq0.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> 情況一 </p> </center> 2. $p_{current} < p_{lower}$,此時,我們只提供$ABC$ <center> <img src="https://i.imgur.com/tV9H2KD.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> 情況二 </p> </center> 而 $p_{upper} > p_{current} > p_{lower}$ 的情況可以視作我同時提供兩個流動池,這兩個流動池的價格區間分別為$[p_{lower}, p_{current}]$ 與 $[p_{current}, p_{upper}]$,所以就不多贅述。 <center> <img src="https://i.imgur.com/PVbRUKN.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> 現價夾在中間的情況可以拆解為情況一與情況二 </p> </center> 接下來說明一下為什麼當價格落在區間外時,我們只需要提供單一貨幣。 <center> <img src="https://i.imgur.com/PVbRUKN.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> 現價夾在中間的情況可以拆解為情況一與情況二 </p> </center> 在情況一,原先 $p_{current} > p_{upper}$,如果我們希望 $p_{current}$ 要慢慢逼近 $p_{upper}$,直至穿越並進入我們的價格區間,也就是價格正在下降,好讓我們可以開始收費,這得要市場上一直在將 $ABC$ 丟進池子,換出 $XYZ$。而我們的流動池只需要滿足市場的這個需求,所以我們只要提供 $XYZ$ 供市場兌出。 <center> <img src="https://i.imgur.com/8nFlgEt.jpg" width=70% style="float:middle"> <p style = "font-size:10px"> 只有其中一種 Token 派得上用場 </p> </center> 我們如果提供 $ABC$ 去讓市場兌出,要用到我們提供的 $ABC$,是不可能的。因為市場兌出 $ABC$ 的行為只會讓價格向上,而現在的價格 $p_{current}$ 已經比我們的價格上界大了,也因此市場兌出 $ABC$ 的行為永遠不會讓 $p_{current}$ 進入我們的價格區間,也用不到我們提供的 $ABC$ 。作為一個理性的流動性提供者,我們沒有動機也沒有必要去提供 $ABC$。 而當價格完整的從 $p_{upper}$ 走過 $p_{lower}$ 並穿越時,作為理性的流動性提供者,我們提供的流動性應該要恰好被完整的利用,這樣才是將資本的效率極大化。也就是我們提供的 $XYZ$ 在此時要剛剛好被全數都換成 $ABC$。 至於情況二就是完全相反的情況,讀者可以自行演繹一遍做為練習。 最後點出 2.2 節的結論: 1. 當現在的價格落在價格區間外時,我們只需提供其中一種貨幣 2. 當價格完整的走過我們的價格區間時,我們提供的貨幣會完全的被換成另一種貨幣。 ## 3. 計算範例 現在我們開始要由計算範例去逐步理解 V3 關於流動池的數學機制,我們多定義一個觀念,unit of liquidity,$L:=\sqrt{k}=\sqrt{R_{ABC} \cdot R_{XYZ}}$。 ### 計算範例 1 考慮兩個價格 $p_{lower} = 1, p_{upper} = 1.00020001$,而現在池子的流動性有一個 unit of liquidity,也就是 $L = 1$。 | price | $L$ | $k$ | $R_{ABC}$ | $R_{XYZ}$ | | -------- | -------- | -------- | -------- | -------- | | $p_{lower}=1$ | 1 | $1^2 = 1$ | $\sqrt{\frac{k}{p}}=\sqrt{\frac{1}{1}}=1$ |$\sqrt{kp}=\sqrt{1\cdot 1}=1$| | $p_{upper}=1.00020001$ | 1 | $1^2=1$ |$\sqrt{\frac{k}{p}}=\sqrt{\frac{1}{1.00020001}}=0.9999$|$\sqrt{kp}=\sqrt{1\cdot 1.00020001}=1.0001$| |||差額|$0.0001$|$0.0001$| 假設我們現在就是以$p_{lower} = 1, p_{upper} = 1.00020001$當作我們流動池的上下界,根據此計算結果,並套用 2.2 節的結論。 結論 $(1)$ 告訴我們:當 $p_{current}<=p_{lower}$,我們的流動池要負責滿足市場將 $XYZ$ 兌出成 $ABC$ 的需求,所以我們只提供 $ABC$。 至於要提供多少呢?結論 $(2)$ 說,夠用就好。當價格從 $p_{lower}$ 走到 $p_{upper}$,會有減少 0.0001 的 $ABC$ 從池子裡被換走,所以我們只需要提供 0.0001 個 $ABC$。 這個計算範例立即帶出了 V3 宣稱的 capital efficiency 的效果。我只需要提供 0.0001 個 $ABC$,就可以模擬 V2 版本中,有 1 個 $ABC$ 與 1 個 $XYZ$ 的流動池,在 $[1, 1.00020001]$ 的行為,假設價格只會在$[1, 1.00020001]$ 區間波動,V3 資金的運用效率將會是 V2 的 20000 倍($(1+1)/0.0001 = 20000$)。 如果大家有讀過 V3 的白皮書的話,本文所謂「模擬的流動池」,其實就是白皮書的 virtual reserve。而 real reserve 就是我們計算出來為了模擬真正需要提供的 Token。 <center> <img src="https://i.imgur.com/xpf7mpq.png" width=70% style="float:middle"> <p style = "font-size:10px"> </p> </center> ### 計算範例 2 $p_{lower} = 1.0001^2, p_{upper} = 1.0001^{16}, L = 1$。 | price | $L$ | $k$ | $R_{ABC}$ | $R_{XYZ}$ | | -------- | -------- | -------- | -------- | -------- | | $p_{lower}=1.0001^2$ | 1 | $1^2 = 1$ | $\sqrt{\frac{k}{p}}=\sqrt{\frac{1}{1.0001^2}}=0.9999$ |$\sqrt{kp}=\sqrt{1\cdot 1.0001^2}=1.0001$| | $p_{upper}=1.0001^{16}$ | 1 | $1^2=1$ |$\sqrt{\frac{k}{p}}=\sqrt{\frac{1}{1.0001^{16}}}=0.9992$|$\sqrt{kp}=\sqrt{1\cdot 1.0001^{16}}=1.0008$| |||差額|$0.0007$|$0.0007$| ### 計算範例 3 $p_{lower} = 1.0001^2, p_{upper} = 1.0001^{16}, L = 2$。 | price | $L$ | $k$ | $R_{ABC}$ | $R_{XYZ}$ | | -------- | -------- | -------- | -------- | -------- | | $p_{lower}=1.0001^2$ | 2 | $2^2 = 4$ | $\sqrt{\frac{k}{p}}=\sqrt{\frac{4}{1.0001^2}}=1.9998$ |$\sqrt{kp}=\sqrt{4\cdot 1.0001^2}=2.0002$| | $p_{upper}=1.0001^{16}$ | 2 | $2^2=4$ |$\sqrt{\frac{k}{p}}=\sqrt{\frac{4}{1.0001^{16}}}=1.9984$|$\sqrt{kp}=\sqrt{4\cdot 1.0001^{16}}=2.0016$| |||差額|$0.0014$|$0.0014$| 從範例 1 與範例 2 中可以看到,當指定的價格區間不同時,我需要提供的 token 數量是不一樣的。 unit of liquidity 則給這些不同價格區間的流動池一個統一的衡量基準 -- 「我要模仿的流動池大小」。這樣統一的衡量基準是重要的,因為這樣才可以公平的去分配相同價格區間,但規模不一樣的流動池所應獲得的費用。 那以流動性提供者的角度來看,我做為投資人,我思考的角度是以「投入資金的數量」出發的。做為投資人,我會想知道我投入了 $\Delta_{ABC}$ 個 $ABC$ 相當於是多少 unit of liquidity,這關係到我能收取的 fee。 我們可以輕易地推導出這層關係: $$ \Delta_{ABC} = \sqrt{\frac{k}{p_{lower}}} - \sqrt{\frac{k}{p_{upper}}} = \frac{L}{\sqrt{p_{lower}}}-\frac{L}{\sqrt{p_{upper}}}=L(\frac{1}{\sqrt{p_{lower}}}-\frac{1}{\sqrt{p_{upper}}})\\ \Rightarrow L=\frac{\Delta_{ABC}}{\frac{1}{\sqrt{p_{lower}}}-\frac{1}{\sqrt{p_{upper}}}} $$ 以及 $$ \Delta_{XYZ} = \sqrt{k\cdot p_{upper}} - \sqrt{k \cdot p_{upper}} = L\cdot \sqrt{p_{upper}}-L\cdot {p_{lower}} = L\cdot (\sqrt{p_{upper}} - \sqrt{p_{lower}})\\ \Rightarrow L=\frac{\Delta_{XYZ}}{\sqrt{p_{upper}} - \sqrt{p_{lower}}} $$ ## 4. 結語 在本文我們用自己的語言重新詮釋官方白皮書中的 Virtual Reserve 與 Real Reserve。並介紹了 unit of liquidity,$L$,這是 V3 衡量各式不同流動池大小的基準。 最後回答一開始提到的問題:「對於每個流動池,$liquidityPool_i$,是不是都會有個別的 $x_i \times y_i = k_i$ 呢?」 答案是否定的 希望這次的介紹能夠幫助讀者更快速了解白皮書的內容,以及 V3 code 的實作邏輯。 ## 5. references 1. [Uniswap v3 詳解(二):創建交易對/提供流動性](https://liaoph.com/uniswap-v3-2/) 2. [Uniswap v3 Maths Explained: Capital Efficiency](https://medium.com/blockchain-development-notes/uniswap-v3-maths-explained-capital-efficiency-86257c44405a)