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);