Uniswap v1#
uniswap v1 是第一個版本,實現的功能和邏輯都比較簡單,它只支持 eth-erc20 的交易對,固定的手續費 3%
uniswap v1 實現的最基本功能就是恆定乘積做市商系統,在單個交易對內的 eth 和 erc20 代幣數目乘積總體保持恆定,在 AMM 機制中,每個交易者的對手方都是交易池本身
uniswap v1 的代碼就分為 factory 和 exchange 兩部分,下面分別對照代碼講解它的實現機制和原理
factory#
工廠合約,用來維護所有的 exchange 交易對,沒有核心的交易邏輯,核心的方法包括以下兩個:
initializeFactory
用來在創建時設置 exchangeTemplate 合約地址,用作後續所有的 exchange 交易對合約,只允許設置一次createExchange
用來創建 token 的交易對,create_with_code_of 是用來拷貝指定合約的代碼並部署新的合約
此外在 factory 中還記錄了以下映射關係,便於查詢交易對和 token 信息
- tokenCount:記錄當前創建的交易對總數
- token_to_exchange:記錄 token 到 exchange 交易對地址的映射
- exchange_to_token:記錄 exchange 交易對地址到 token 的映射
- id_to_token:記錄交易對 id 到 token 的映射
@public
def initializeFactory(template: address):
assert self.exchangeTemplate == ZERO_ADDRESS
assert template != ZERO_ADDRESS
self.exchangeTemplate = template
@public
def createExchange(token: address) -> address:
assert token != ZERO_ADDRESS
assert self.exchangeTemplate != ZERO_ADDRESS
assert self.token_to_exchange[token] == ZERO_ADDRESS
exchange: address = create_with_code_of(self.exchangeTemplate)
Exchange(exchange).setup(token)
self.token_to_exchange[token] = exchange
self.exchange_to_token[exchange] = token
token_id: uint256 = self.tokenCount + 1
self.tokenCount = token_id
self.id_to_token[token_id] = token
log.NewExchange(token, exchange)
return exchange
exchange#
接口#
每個交易對的交易核心內容在 exchange 合約中,核心方法如下接口所示,主要可以分為以下幾部分:
- 流動性管理:添加或移除流動性,主要使用對象是 lp
- 價格查詢:
- 計算賣出一定額度 token 可以換取多少 eth
- 計算賣出一定額度 eth 可以換取多少 token
- 計算買入一定額度 token 可以換取多少 eth
- 計算買入一定額度 eth 可以換取多少 token
- 提供 ETH 兌換代幣:同上面的價格查詢,也有四種交易方法
- 提供代幣兌換 ETH:同上面的價格查詢,也有四種交易方法
- 代幣間兌換:同上面的價格查詢,也有四種交易方法
interface UniswapExchangeInterface {
// 流動性
function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
// 價格查詢
function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
// 提供ETH以兌換代幣
function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256 tokens_bought);
function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256 tokens_bought);
function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256 eth_sold);
function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256 eth_sold);
// 提供代幣以兌換ETH
function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256 eth_bought);
function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256 eth_bought);
function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256 tokens_sold);
function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256 tokens_sold);
// 代幣之間的互換
function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256 tokens_bought);
function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_bought);
function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256 tokens_sold);
function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_sold);
}
核心流程#
由於 uniswap 中涉及的交易類型,交易方向比較多,看著會有些複雜,但 uniswap v1 的核心流程可以用這兩張圖體現
作為使用 uniswap 的兩種角色,lp 負責添加和移動流動性,player 負責在交易對中進行 swap
- 作為 lp,無論添加還是移除流動性都需要按比例進行
- 作為 player,在交易對中 swap 需要按照恆定乘積的約定來換入換出
uniswap 提供的方法有很多組,命名是有規則的:A-To-B-swap/transfer-input/output
- A 和 B 是 token 和 eth 組合
- swap transfer:交易類型二選一
- input output:交易方向二選一
代幣兌換#
雖然 uniswapv1 的交易對都是由 token-eth 組成的,但是也提供了 tokenA-tokenB 的兌換
實現方式是在兩個池子之間通過 eth 作為中間橋樑完成兩次 swap
價格換算#
根據 swap 時約定的恆定乘積,在價格查詢上有兩個最基本的方法:
getInputPrice
根據輸入計算輸出getOutputPrice
根據輸出計算輸入
並在此基礎之上衍生了這幾種方法,格式為:A-To-B-Input/Output
- A 是主動交易的 token,A-input:即計算買入 A 可以換出多少 B,A-output:即計算賣出 A 可以換出多少 B
因此對於 token-eth input-output 共組合出四種價格查詢方法:
// 價格查詢
function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);