banner
zach

zach

github
twitter
medium

初識 Uniswap v1

Uniswap v1#

概覽 | Uniswap

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 需要按照恆定乘積的約定來換入換出
    image

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);
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。