初识 Uniswap v1

Uniswap v1

Overview | 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 的映射

Copy
@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：同上面的价格查询，也有四种交易方法

代币间兑换：同上面的价格查询，也有四种交易方法

Copy
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 共组合出四种价格查询方法：

Copy
// 价格查询
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);