UniswapV2 代码学习
UniswapV2Factory.sol
https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol
pragma solidity =0.5.16;
import './interfaces/IUniswapV2Factory.sol'; // 引入IUniswapV2Factory接口
import './UniswapV2Pair.sol'; // 引入UniswapV2Pair合约
contract UniswapV2Factory is IUniswapV2Factory {
address public feeTo; // 收取手续费的地址
address public feeToSetter; // 设置手续费接收地址的权限者
mapping(address => mapping(address => address)) public getPair; // 存储每对代币对应的交易对地址
address[] public allPairs; // 存储所有交易对地址的数组
event PairCreated(address indexed token0, address indexed token1, address pair, uint); // 交易对创建事件
constructor(address _feeToSetter) public { // 构造函数,初始化feeToSetter
feeToSetter = _feeToSetter;
}
function allPairsLength() external view returns (uint) { // 返回所有交易对的数量
return allPairs.length;
}
function createPair(address tokenA, address tokenB) external returns (address pair) { // 创建交易对函数
require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES'); // 检查两个代币地址是否相同
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); // 确定token0和token1的顺序
require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS'); // 检查地址是否为零地址
require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // 检查交易对是否已经存在
bytes memory bytecode = type(UniswapV2Pair).creationCode; // 获取UniswapV2Pair合约的字节码
bytes32 salt = keccak256(abi.encodePacked(token0, token1)); // 生成唯一的salt值
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt) // 使用create2部署合约
}
IUniswapV2Pair(pair).initialize(token0, token1); // 初始化交易对合约
getPair[token0][token1] = pair; // 在映射中存储交易对地址
getPair[token1][token0] = pair; // 反向存储以便查询
allPairs.push(pair); // 将交易对地址添加到数组中
emit PairCreated(token0, token1, pair, allPairs.length); // 触发交易对创建事件
}
function setFeeTo(address _feeTo) external { // 设置收取手续费的地址
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN'); // 检查调用者是否有权限
feeTo = _feeTo; // 设置新的收取手续费地址
}
function setFeeToSetter(address _feeToSetter) external { // 设置新的feeToSetter地址
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN'); // 检查调用者是否有权限
feeToSetter = _feeToSetter; // 设置新的feeToSetter地址
}
}
UniswapV2ERC20.sol
https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2ERC20.sol
pragma solidity =0.5.16;
import './interfaces/IUniswapV2ERC20.sol'; // 引入IUniswapV2ERC20接口
import './libraries/SafeMath.sol'; // 引入SafeMath库
contract UniswapV2ERC20 is IUniswapV2ERC20 {
using SafeMath for uint; // 使用SafeMath库进行安全数学运算
string public constant name = 'Uniswap V2'; // 代币名称
string public constant symbol = 'UNI-V2'; // 代币符号
uint8 public constant decimals = 18; // 小数位数
uint public totalSupply; // 总供应量
mapping(address => uint) public balanceOf; // 账户余额映射
mapping(address => mapping(address => uint)) public allowance; // 账户授权映射
bytes32 public DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; // Permit类型哈希
mapping(address => uint) public nonces; // 用于permit函数生成disgit
event Approval(address indexed owner, address indexed spender, uint value); // 授权事件
event Transfer(address indexed from, address indexed to, uint value); // 转账事件
constructor() public {
uint chainId;
assembly {
chainId := chainid // 获取链ID,0.8.0后使用block.chainid
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes('1')),
chainId,
address(this)
)
);
}
function _mint(address to, uint value) internal {
totalSupply = totalSupply.add(value); // 增加总供应量
balanceOf[to] = balanceOf[to].add(value); // 增加接收地址的余额
emit Transfer(address(0), to, value); // 触发转账事件
}
function _burn(address from, uint value) internal {
balanceOf[from] = balanceOf[from].sub(value); // 减少发送地址的余额
totalSupply = totalSupply.sub(value); // 减少总供应量
emit Transfer(from, address(0), value); // 触发转账事件
}
function _approve(address owner, address spender, uint value) private {
allowance[owner][spender] = value; // 设置授权额度
emit Approval(owner, spender, value); // 触发授权事件
}
function _transfer(address from, address to, uint value) private {
balanceOf[from] = balanceOf[from].sub(value); // 减少发送地址的余额
balanceOf[to] = balanceOf[to].add(value); // 增加接收地址的余额
emit Transfer(from, to, value); // 触发转账事件
}
function approve(address spender, uint value) external returns (bool) {
_approve(msg.sender, spender, value); // 授权
return true; // 返回true
}
function transfer(address to, uint value) external returns (bool) {
_transfer(msg.sender, to, value); // 转账
return true; // 返回true
}
function transferFrom(address from, address to, uint value) external returns (bool) {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value); // 减少授权额度
}
_transfer(from, to, value); // 转账
return true; // 返回true
}
// 授权 基于712 离线签名
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'UniswapV2: EXPIRED'); // 检查是否过期
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
); // 计算哈希值
address recoveredAddress = ecrecover(digest, v, r, s); // 还原签名地址为代币所有人
require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE'); // 检查地址有效
_approve(owner, spender, value); // owner授权spender额度
}
}
UniswapV2Pair.sol
https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol
pragma solidity =0.5.16;
import './interfaces/IUniswapV2Pair.sol'; // 引入IUniswapV2Pair接口
import './UniswapV2ERC20.sol'; // 引入UniswapV2ERC20合约
import './libraries/Math.sol'; // 引入Math库
import './libraries/UQ112x112.sol'; // 引入UQ112x112库
import './interfaces/IERC20.sol'; // 引入IERC20接口
import './interfaces/IUniswapV2Factory.sol'; // 引入IUniswapV2Factory接口
import './interfaces/IUniswapV2Callee.sol'; // 引入IUniswapV2Callee接口
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
using SafeMath for uint; // 使用SafeMath库进行安全数学运算
using UQ112x112 for uint224; // 使用UQ112x112库
uint public constant MINIMUM_LIQUIDITY = 10**3; // 最小流动性常量 1000wei
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)'))); // transfer函数的选择器
address public factory; // 工厂合约地址
address public token0; // 代币0的地址
address public token1; // 代币1的地址
uint112 private reserve0; // 代币0的储备量 缓存余额 uq112.112 无符号
uint112 private reserve1; // 代币1的储备量 缓存余额
uint32 private blockTimestampLast; // 最后一次更新reserve时的区块时间戳
uint public price0CumulativeLast; // 代币0的累计价格
uint public price1CumulativeLast; // 代币1的累计价格
uint public kLast; // 储备量乘积k值,用于流动性事件
uint private unlocked = 1; // 解锁状态变量
modifier lock() {
require(unlocked == 1, 'UniswapV2: LOCKED'); // 检查合约是否已锁定
unlocked = 0;
_;
unlocked = 1;
}
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0; // 返回代币0的储备量
_reserve1 = reserve1; // 返回代币1的储备量
_blockTimestampLast = blockTimestampLast; // 返回上一个区块的时间戳
}
function _safeTransfer(address token, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value)); // 调用代币的transfer函数
require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED'); // 确保转账成功
}
event Mint(address indexed sender, uint amount0, uint amount1); // 铸造事件
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
); // 交换事件
event Sync(uint112 reserve0, uint112 reserve1); // 同步事件
constructor() public {
factory = msg.sender; // 设置工厂合约地址为部署者地址
}
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // 确保只有工厂合约可以调用
token0 = _token0; // 设置代币0的地址
token1 = _token1; // 设置代币1的地址
}
//更新缓存余额
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW'); // 确保余额不溢出
uint32 blockTimestamp = uint32(block.timestamp % 2**32); // 获取当前区块时间戳
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // 计算时间间隔
// 有时间差(不同的区块),才做更新
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed; // 更新代币0的累计价格
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed; // 更新代币1的累计价格
}
reserve0 = uint112(balance0); // 更新代币0的储备量
reserve1 = uint112(balance1); // 更新代币1的储备量
blockTimestampLast = blockTimestamp; // 更新区块时间戳
emit Sync(reserve0, reserve1); // 触发同步事件
}
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
address feeTo = IUniswapV2Factory(factory).feeTo(); // 获取手续费接收地址
feeOn = feeTo != address(0); // 判断是否开启手续费
uint _kLast = kLast; // 获取上一次储备量乘积k值
if (feeOn) {
if (_kLast != 0) {
uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1)); // 计算当前k值的平方根
uint rootKLast = Math.sqrt(_kLast); // 计算上一次k值的平方根
if (rootK > rootKLast) {
uint numerator = totalSupply.mul(rootK.sub(rootKLast)); // 计算分子
uint denominator = rootK.mul(5).add(rootKLast); // 计算分母
uint liquidity = numerator / denominator; // 计算流动性
if (liquidity > 0) _mint(feeTo, liquidity); // 如果流动性大于0,铸造新的代币给手续费接收地址
}
}
} else if (_kLast != 0) {
kLast = 0; // 如果没有手续费,重置k值
}
}
function mint(address to) external lock returns (uint liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // 获取储备量
uint balance0 = IERC20(token0).balanceOf(address(this)); // 获取合约中代币0的余额
uint balance1 = IERC20(token1).balanceOf(address(this)); // 获取合约中代币1的余额
uint amount0 = balance0.sub(_reserve0);
uint amount1 = balance1.sub(_reserve1);
bool feeOn = _mintFee(_reserve0, _reserve1); // 计算手续费
uint _totalSupply = totalSupply; // 获取总供应量
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY); // 初次铸造,减去最小流动性
_mint(address(0), MINIMUM_LIQUIDITY); // 锁定最小流动性
} else {
liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1); // 计算流动性
}
require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED'); // 确保流动性大于0
_mint(to, liquidity); // 铸造流动性代币
_update(balance0, balance1, _reserve0, _reserve1); // 更新储备量
if (feeOn) kLast = uint(reserve0).mul(reserve1); // 如果有手续费,更新k值
emit Mint(msg.sender, amount0, amount1); // 触发铸造事件
}
function burn(address to) external lock returns (uint amount0, uint amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves();
address _token0 = token0; // 节省Gas费用
address _token1 = token1; // 节省Gas费用
uint balance0 = IERC20(_token0).balanceOf(address(this)); // 获取合约中代币0的余额
uint balance1 = IERC20(_token1).balanceOf(address(this)); // 获取合约中代币1的余额
uint liquidity = balanceOf[address(this)]; // 获取合约的流动性代币余额
bool feeOn = _mintFee(_reserve0, _reserve1); // 计算是否收取手续费
uint _totalSupply = totalSupply; // 必须在这里定义,因为totalSupply可能在_mintFee中更新
amount0 = liquidity.mul(balance0) / _totalSupply; // 使用余额确保按比例分配
amount1 = liquidity.mul(balance1) / _totalSupply; // 使用余额确保按比例分配
require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED'); // 确保烧毁的流动性足够
_burn(address(this), liquidity); // 烧毁流动性代币
_safeTransfer(_token0, to, amount0); // 安全转移代币0
_safeTransfer(_token1, to, amount1); // 安全转移代币1
balance0 = IERC20(_token0).balanceOf(address(this)); // 更新代币0的余额
balance1 = IERC20(_token1).balanceOf(address(this)); // 更新代币1的余额
_update(balance0, balance1, _reserve0, _reserve1); // 更新储备量
if (feeOn) kLast = uint(reserve0).mul(reserve1); // 如果有手续费,更新k值
emit Burn(msg.sender, amount0, amount1, to); // 触发燃烧事件
}
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT'); // 确保输出数量大于0
(uint112 _reserve0, uint112 _reserve1,) = getReserves();
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY'); // 确保有足够的流动性
uint balance0;
uint balance1;
{ // 为_token{0,1}设置作用域,避免堆栈过深错误
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO'); // 确保目标地址有效
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // 乐观转移代币0
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // 乐观转移代币1
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data); // 调用回调函数
balance0 = IERC20(_token0).balanceOf(address(this)); // 更新代币0的余额
balance1 = IERC20(_token1).balanceOf(address(this)); // 更新代币1的余额
}
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0; // 计算代币0的输入量
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0; // 计算代币1的输入量
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT'); // 确保输入量大于0
{ // 为reserve{0,1}Adjusted设置作用域,避免堆栈过深错误
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3)); // 调整后的代币0余额
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3)); // 调整后的代币1余额
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K'); // 确保恒定乘积公式成立
}
_update(balance0, balance1, _reserve0, _reserve1); // 更新储备量
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); // 触发交换事件
}
// 强制余额与储备量匹配
function skim(address to) external lock {
address _token0 = token0;
address _token1 = token1;
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0)); // 转移超出储备量的代币0
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1)); // 转移超出储备量的代币1
}
// 强制储备量与余额匹配
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1); // 更新储备量以匹配余额
}