Uniswap:在以太坊上建立自動化做市商-AMM 解碼


在本系列關於AMM(自動做市商)的第一個案例研究中,我們以Uniswap 為例。Uniswap 是以太坊上最常用的三個AMM 之一,可以認為是第一個常數函數AMM。
該協議的第一個版本於2018 年11 月Devcon 4 期間推出。它是一個去中心化交易平台,使用恆定功能協議為交易者提供流動性。

Uniswap V1 的設計很簡單:一個專門為以太坊用戶交易ERC-20 代幣的DEX。其開發團隊希望建立一個模型,其中去中心化、安全性和抗審查性都是最佳的。
Uniswap 的原始碼是開源的,可以在此GitHub 儲存庫中找到。

Uniswap 原理

Uniswap 在其去中心化代幣交換協議的核心使用恆定乘積公式。其用戶可以交易任何類型的ERC-20 代幣。Uniswap 是基於一組智能合約。平台上列出的每個ERC-20 都有其專用合約,稱為交換合約。

一個名為Uniswap Factory 的程式允許任何使用者為任何ERC-20 建立交換合約。因此,工廠是一個寄存器,它將添加到整個系統的所有令牌匯集在一起。

每個交易合約都有兩個儲備金(金庫、數位保險箱)。一個有以太幣(ETH) 餘額,另一個有ERC-20 餘額,與合約相對應。這些儲備也稱為該ERC-20 代幣的流動性池。任何以太坊用戶都可以將流動性(ETH 和ERC-20)存入池中。當他們的資金被鎖定在智能合約中時,該用戶就成為流動性提供者(LP)。然後他將因其貢獻而獲得獎勵。

此獎勵與權益成正比,即流動性提供者存入的資金量。為了追蹤存入池中的流動性,該協議使用一種特殊的代幣。這些代幣稱為LP 代幣(流動性提供者代幣),是在添加流動性時創建的。如果流動性提供者決定提取資金,相應的LP 代幣將被銷毀。最後,他相應份額的儲備金被發送給他。

Uniswap:在以太坊上建立自動做市商– AMM 解碼-1

Uniswap V1 特點

Uniswap 開發團隊已經建立了一個模型的基礎,在接下來的幾個月裡,許多AMM 都受到了啟發。該平台的功能很基本,注重使用者體驗:

透過Uniswap Factory 整合任何ERC-20 代幣;
單筆交易為ETH/ERC-20 對;
能夠使用以太坊名稱服務(ENS) 從任何錢包購買任何ERC-20 代幣;
創建私人和個人化的Uniswap DEX;
行動端介面(前端)的實現。

目標是實現低交易費用(汽油費)(結果證明這是一個大問題)和最大程度的安全性。Uniswap V1 智能合約是在Vyper 中編碼的。他們使用一種稱為輕量級形式驗證的方法進行審核。

Uniswap 是如何運作的?

Uniswap 的自動做市(AMM) 演算法明智地使用恆定乘積公式:x*y = k。代幣定價對流動性敏感。它由常數乘積自動決定。每個掉期合約(每個ETH/ERC-20 對一個)包含該對每個元素的流動性。

價格是根據兩個儲備(流動性池)的相對規模和交易所(交易)規模的函數計算的。

代幣互換

當交易者想要以任何方向(ETH 到ERC-20 或ERC-20 到ETH)交易代幣時,只需將流動性添加到相應的池中即可。同時,從相反儲備中提取流動性。由於以太幣是以太坊網路的主要燃料,並存在於每個ETH/ERC-20 交易所中,因此它是用於在單筆交易中將ERC-20 代幣交換為其他ERC-20 代幣的支付中介。Uniswap 用戶還可以在單一交易中將代幣交換並轉移到與原始地址不同的地址。

因此,沒有訂單簿:Uniswap 用戶用協議的儲備交換他們的代幣。

三個變數決定代幣的價格:

專門用於交換的ERC-20 的ETH 儲備的大小(數量);
ERC-20代幣儲備的規模(數量);
出售(輸入)或購買(輸出)的數量。

對流動性的影響

恆定乘積公式使用ETH 儲備與對應ERC-20 代幣之間的比率來設定代幣的價格。因此,任何交換都會影響這個比率:

當出售ETH 換取ERC-20 代幣時,ETH 儲備會增加,而ERC-20 儲備會減少。
當使用ERC-20 代幣購買ETH 時,ETH 儲備會減少,而ERC-20 儲備會增加。

因此,這意味著交易規模越大,對池中ETH/ERC-20 比率的影響就越大。因此,交易量會影響價格滑點。AMM 所報出的價格可以稱為“相對價格”,因為它可以從代幣的整體市場價值中得出。

旨在減少滑點的參與者是套利者。因此,他們將在必要時增加流動性並利用價格波動。

ETH/ERC-20 訂單

對於銷售訂單(精確輸入),採購數量(輸出)計算如下:

// ERC20 的ETH 供應商
const inputAmount = userInputEthValue
const inputReserve = web3.eth.getBalance(exchangeAddress)
const outputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()

// ERC20 的ETH 供應商
const inputAmount = userInputTokenValue
const inputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()
const outputReserve = web3.eth.getBalance(exchangeAddress)

// 輸出金額(quantité achetée)
const numerator = inputAmount * outputReserve * 997
const denominator = inputReserve * 1000 + inputAmount * 997
const outputAmount = 分子/ 分母

對於採購訂單(確切的輸出),成本(輸入)計算如下:

// Achat d’ERC20 avec des ETH
const outputAmount = userInputTokenValue
const inputReserve = web3.eth.getBalance(exchangeAddress)
const outputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()

// ERC20 的ETH
const outputAmount = userInputEthValue
const inputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()
const outputReserve = web3.eth.getBalance(exchangeAddress)

// 計算
const 分子= 輸出量* inputReserve * 1000
const 分母= (outputReserve – 輸出量) * 997
const inputAmount = 分子/ 分母+ 1

應付給流動性提供者的費用如下:

費用= 輸入金額* 0.003

匯率是輸出量除以輸入量:

消耗率= 輸出量/ 輸入量

ERC-20/ERC-20訂單

在一對ERC-20 代幣之間進行交易時,我們需要4 個變數來決定價格:

首先,輸入ERC-20代幣的ETH池的大小;
然後是輸入代幣在ERC-20 中的儲備大小;
接下來,ERC-20輸出的ETH池的大小;
最後,輸出ERC-20代幣池的大小。

當然,出售的代幣數量(確切的輸入)和購買的代幣數量(輸出)也是必要的參數。

對於銷售訂單(精確輸入),我們計算購買數量(產出)如下:

//代幣A (ERC20) 與ETH 的轉換
const inputAmountA = userInputTokenAValue
const inputReserveA = tokenContractA.methods.balanceOf(exchangeAddressA).call()
const outputReserveA = web3.eth.getBalance(exchangeAddressA)

const 分子A = 輸入量A * 輸出保留A * 997
常量分母A = 輸入保留A * 1000 + 輸入量A * 997
常量輸出量A = 分子A / 分母A

// ETH 與代幣B 的轉換
const inputAmountB = outputAmountA
const inputReserveB = web3.eth.getBalance(exchangeAddressB)
const outputReserveB = tokenContract.methods.balanceOf(exchangeAddressB).call()

const 分子B = 輸入量B * 輸出保留B * 997
常量分母B = 輸入保留B * 1000 + 輸入量B * 997
常量輸出量B = 分子B / 分母B

對於採購訂單(確切的輸出),成本(輸入)計算如下:

//代幣B 和ETH
const outputAmountB = userInputTokenBValue
const inputReserveB = web3.eth.getBalance(exchangeAddressB)
const outputReserveB = tokenContractB.methods.balanceOf(exchangeAddressB).call()

// 計算
const numeratorB = outputAmountB * inputReserveB * 1000
const denominatorB = (outputReserveB – outputAmountB) * 997
const inputAmountB = numeratorB / denominatorB + 1

// Achat d’ETH avec代幣A
const outputAmountA = userInputEthValue
const inputReserveA = tokenContractA.methods.balanceOf(exchangeAddressA).call()
const outputReserveA = web3.eth.getBalance(exchangeAddressA)

// 計算
const numeratorA = 輸出量A * inputReserveA * 1000
const 分母A = (outputReserveA – 輸出量A) * 997
const inputAmountA = 分子A / 分母A + 1

在這種情況下,流動性提供者費用適用兩次(0.3% + 0.3%):

常量交換AFee = 輸入金額A * 0.003
常數交換BFee = 輸入金額B * 0.003

由於用戶只攜帶代幣A,因此可以這樣表示:

const 組合費用= 輸入金額A * 0.00591

那麼匯率是:

消耗率= 輸出量B / 輸入量 A

仲裁

套利機制確保資產的相對價格不會與其內在價格偏離太多。有了這些套利機會,流動性也會定期添加到儲備中。事實上,如果價格大幅波動,套利者可以透過增加資金池的流動性來獲利。

費用和LP 代幣

Uniswap 費用為0.3%。這些佣金適用於每筆交易,相應的代幣將添加到流動性池中。因此,這確保了儲備中的資產數量將隨著時間和使用而增加,無論配對中兩種資產之間的比率如何變化。

這些費用有利於流動性提供者。當LP 想要提取資金時,相應的LP 代幣就會被燒毀。這些代幣決定了流動性提供者擁有多少資金池。因此,費用會按照所提供的流動性的比例添加到提取的資金中作為獎勵。

使用「標準」Uniswap 池(使用Factory 建立),流動性提供者只能為單一專用ERC-20 池建立LP 代幣。這確保了給定ERC-20 的流動性不會分散在多個儲備中。

流動性代幣(LP代幣)是高度可分割的。流動性提供者可以隨時決定銷毀它們。然後他們按照貢獻的比例收到資金。

深入研究Uniswap 程式碼

在這一部分中,我們將回顧Uniswap 協定的主要功能。

流動性鑄造功能

每個掉期合約都包含ETH 儲備及其相關的ERC-20。因此,這些是每個合約的ETH 和ERC-20 餘額:

const ethReserve = web3.eth.getBalance(exchangeAddress)
const tokenReserve = tokenContract.methods.balanceOf(exchangeAddress)

增加流動性

當流動性提供者存入儲備金時,該協定使用addLiquidity() 函數。然後創建流動性代幣:

Exchange.methods.addLiquidity(min_liquidity,max_tokens,截止日期).send({ value:ethAmount })

LP 代幣的數量取決於發送到該函數的ETH 數量。流動性提供者也必須存入相應價值的ERC-20代幣。因此,它是定義初始匯率的池中的第一個流動性提供者。如果它偏離外部市場的匯率,那麼套利者可以使價格恢復平衡,但代價是流動性提供者的損失。

ethAmount 是存入ETH 的確切金額,是LP 提供的流動性總額的一半。此變數用於決定要存入的ERC-20 數量。
max_tokens 用於限制匯率波動。對於第一個流動性提供者來說,這就是存入的代幣的確切數量。
min_liquidity 透過組合前兩個變數來限制流動性代幣的創建速率。
截止日期允許您設定一個時間限制,在此之後交易將無法執行。因此,礦工沒有能力持有已簽署的交易並根據對自己有利的市場價格執行交易。

取現金

removeLiquidity() 函數允許有限合夥人提取他們的儲備份額:

Exchange.methods.removeLiquidity(金額,min_eth,min_tokens,截止日期).send()

amount 指定要銷毀的流動性代幣的數量。將其除以流動性代幣總量即可得出流動性提供者提取的ETH 和ERC20 的百分比。

因此,這些流動性的提取比例與提取時準備金的比例相同。同樣,如果匯率不好,那麼就有一個套利機會來修正價格。

訂單調整

Uniswap AMM 完全部署在鏈上。因此,在交易簽署(交易啟動)的時刻和包含該交易的區塊之間存在延遲。這會導致價格波動。

可以調整賣單的最小銷售數量或買單的最大購買數量。同樣,我們可以設定一個截止日期,超過該截止日期後交易將不再執行。因此,交易者可以保證,如果不滿足所需條件(數量、價格和延遲),他的訂單將不會被執行。

交易的截止日期以秒為單位定義:

web3.eth.getBlock(‘latest’, (error, block) => {
Deadline = block.timestamp + 300 // 交易在300 秒(5 分鐘)後過期
})

例如,延遲300秒後,該交易將不會被礦工收錄。這些交易截止日期有兩個優點:

交易者可以阻止礦工長時間「暫停」交易。事實上,他們可能會根據市場走勢將它們納入一個區塊中;
這限制了用戶天然氣成本的不確定性。

個性化流動性儲備

雖然在Factory 註冊的公共儲備與其專用的DEX 相關聯,但Uniswap 用戶可以建立自訂池。他們有自己的設置。價格形成機制可能不同,創建流動性代幣的費用或系統也可能不同。

當然,偏離Uniswap 的初始模型可能會導致協議安全的問題。

Uniswap 類型AMM 的挑戰

設計恆定功能AMM 面臨許多需要克服的挑戰。每個去中心化AMM 設計都是協議所需屬性和由此產生的限制之間的折衷。

第一個限制顯然是建立一個確保流動性提供者資金最大安全的系統。然後我們必須最大化有限合夥人的投資回報,鼓勵他們存入準備金。最後,資本配置必須是最優的,以確保市場擁有最佳的流動性。

無常損失

當流動性提供者在準備金率改變後提取資金時,就會發生無常損失。

當流動性提供者銷毀其LP 代幣時,為了從流動性池中提取其以太坊和ERC-20 份額,這些資金將按當前匯率提取,由準備金率決定:

ethWithdrawn = ehtPool * (燒毀金額/ 總金額)
tokensWithdrawn = tokenPool * (銷毀金額/ 總金額)

流動性提供者面臨的風險

隨著市場價格波動,如果當前準備金率與初始準備金率(存款時)不同,流動性提供者可能會損失價值。這就是我們所說的無常損失:存入AMM儲備中的兩種資產的初始價值與其當前價值之間的差額。

這是流動性提供者面臨的主要風險之一:透過借貸和鎖定其代幣而不是簡單地將其安全地保存在錢包中而損失價值。無常損失的風險取決於為池提供資金的流動性提供者的數量以及存在的代幣數量。

無論資產對的價格走向如何,都會發生無常損失。唯一重要的是存款價格和提款價格之間的差額的絕對值。

無常損失的機制

因此,無常損失是LP 代幣提取時的價值與標的資產的理論價值(如果它們沒有被鎖定在儲備金中)之間的差額。我們將透過一個例子來說明這個機制。

假設Bob 將10 ETH 和相應數量的ERC-20 存入流動性池。存款時,1 ETH = 1000 美元。因此,以50/50 的比例,Bob 必須將10 ETH + 10,000 美元存入儲備金。

如果池子的總價值為100,000 美元(50 ETH 和50,000 美元),那麼Bob 擁有池子的20% – (20000/100000)*100。

因此,鮑勃通過存入他的以太幣和穩定幣份額來為該池做出貢獻。他可以隨時透過銷毀他的LP 代幣來提取他的股份。我們說的是份額,而不是固定數量的籌碼!

如果存入資產(在我們的例子中是ETH)的價值在藉貸期間發生變化,鮑勃將遭受無常損失。以太幣的當前價格與初始價格偏離越大,損失就越大。

我們使用「無常」一詞是因為如果資產價格回到其初始價值,流動性提供者不會損失任何東西。交易費用作為對流動性提供者的獎勵,有助於抵消潛在的無常損失。

無常損失的計算

假設自從Bob 存入10 ETH 以來,以太幣的價格從1000 美元上漲到2000 美元。恆定的乘積函數為我們提供:

以太幣價格如下:

貨幣對中每種資產的流動性計算如下:

eth_liquidity_pool = sqrt(constant_product / eth_price)

token_liquidity_pool = sqrt(constant_product * eth_price)

如果以太幣的新價格為2000 美元,則:

ETH流動性= 35,355

USDT流動性= 70 710,6

我們可以使用常數積來驗證k 確實相同:

如果Bob 希望從儲備中提取他的份額,他將獲得池中以太幣和USDT 數量的20%:

35 ETH 或7 ETH 的20%;
70 710 USDT 的20% 相當於14142 USDT。

如果我們計算其資產的總價值,我們會得到:

7 * 2000 + 14 142 = 28 142 USDT

如果他沒有存款,鮑伯的資產總價值將為:

10 * 2000 + 10 000 = 30 000 USDT

因此,Bob 遭受了30000 – 28142 = 1858 USDT 的無常損失。

模型化

無常損失可以建模為當前價格(作為初始價格的百分比)和總資產價值變化(也作為百分比)的函數:

Uniswap:在以太坊上建立自動做市商– AMM 解碼-2來源:AlfaBlock

我們可以觀察到:

1.25 倍的價格變動會帶來0.6% 的無常損失
1,5x => 2%
1,75x => 3,8 %
2x => 5,7 %
3x => 13,4 %
4x => 20 %
5x => 25.5%。

因此,養殖期間累積的費用必須補償價格波動造成的無常損失。因此,如果流動性提供者想要產生利潤,這些變化不應該太大。

減少無常損失

由於加密貨幣市場高度波動,只有一種方法可以避免無常損失。這僅涉及為穩定幣對提供流動性!

同樣,透過為包括穩定幣在內的資產對提供流動性,可以降低無常損失的風險。因此,貨幣對中的資產波動性越大,風險就越高。然而,如果流動性提供者採取「僅穩定幣」策略,則不會從牛市中受益。

因此,想要降低無常損失風險的流動性提供者將選擇低波動性加密貨幣和代幣(如果存在)。他們還必須謹慎選擇何時透過銷毀流動性代幣來提取資金。

在使用AMM 的大交易量DEX 上,在正常市場條件下,費用可以補償無常損失。這就是農業對流動性提供者保持獲利的方式。

資本效率

像Uniswap 這樣的常數函數AMM 還有另一個值得一提的缺點。這牽涉到存入資金池的資本相對低效率。

這些儲備需要比傳統中心化平台的訂單簿大得多的流動性。因此,只有當處於恆定乘積函數雙曲線的極值部分時,才能獲得大量流動性。也正是當我們趨向於曲線的這些漸近線之一時,該函數對價格的影響最強。因此,相應的流動性雖然可以獲得,但永遠不會被理性交易者使用。

這種現像是Uniswap 等恆定功能AMM 的典型現象。我們談論「惰性流動性」。在中心化交易所,做市商可以準確地確定在給定價格水準下他們想要在訂單簿中放置多少流動性。CFMM 的情況並非如此:流動性提供者對價格水準沒有影響。

透過使用其他函數定義流動性池之間的關係可以提高資本效率。還有流動性集中機制,我們將在本AMM 系列的後續文章中探討。Curve 和Uniswap V3 的情況尤其如此。

滑倒

上面我們已經量化了交易對代幣價格的影響。這樣我們就知道大批量交易是如何導致滑點的。這是常數函數AMM 的典型缺點。對於透過執行大量訂單來消耗過多流動性的交易者來說,這會導致巨大的滑點成本。

Uniswap:DeFi AMM 的概念證明

最後,讓我們提一下Uniswap 是在Vitalik Buterin 本人的建議下誕生的。2016年,他鼓勵開發者和研究人員設計一個以AMM為基礎的去中心化交易平台。當時,鏈上去中心化交易所的利差非常高。維塔利克隨後建議轉向自動化做市商,類似於預測市場中使用的做市商。

Hayden Adams 開始致力於這個項目,幾個月後部署了第一個版本。作為概念證明,Uniswap 迅速發展,吸引了世界各地許多用戶的流動性。真正的革命是它的經濟激勵機制,它吸引了整個以太坊社群的資金。

由於採用以太坊基金會的資助,Uniswap 計畫得以發展。開發團隊很快就得到了Paradigm 的資助。儘管遇到障礙,團隊不斷改進他們的產品,如今,Uniswap 已成為整個加密產業中最有價值的聚合去中心化交易所。

Uniswap 也充當了整整一代AMM 的基礎。它的一些智能合約仍然被更現代的平台使用。因此,它是成功通過時間考驗的AMM模型。

當然,DeFi 世界一直在發酵。許多自動做市商根據其用例採用了不同的定價功能。我們將在接下來的文章中看到,可以重新定義這些功能,以交換特定的加密資產,例如穩定幣。同樣,Uniswap 實驗使得開發和提供眾多功能和經濟激勵機製成為可能,以改善致力於去中心化金融的AMM。

資訊來源:由0x資訊編譯自JOURNALDUCOIN。版權歸作者Morgan Phuc所有,未經許可,不得轉載!

Total
0
Shares
Related Posts