山石网科
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

目 录
一. 简介
UniswapV2是一个建立在以太坊上的主流去中心化加密交易平台(DEX),实现了自动化做市商(AMM)。
AMM要实现自动完成与买卖方的交易,需要满足以下几个特性:
- AMM要持有资产,由于要双向报价,所以需要同时持有两种资产。
- AMM资产池应该能实现充值与提现。
- AMM应该根据市场情况自动调整价格。
- AMM要能通过交易赚取利润。
二. 原理
UniswapV2部署在以太坊上,其本身是一个支持ERC-20标准的token合约,其定义在合约文件UniswapV2ERC20.sol中。
UniswapV2可以完成任意两种ERC-20token的交换,其实现在uniswapV2Pair合约中,该合约主要提供以下3种功能:
- 流动性追踪,追踪交易池中的代币余额,并提供流动性代币。
- 自动做市,通过算法计算出市场价格。
- 去中心化预言机,供外部使用。
2.1 流动性
什么是流动性?在uniswap中可以被认为是在一个交易对合约中的两种代币的总和,如果用户同时向合约质押两种代币,则被称为增加流动性。由所有流动性汇成的池子为流动性池。而向流动性池中添加流动性的的人被称为流动性提供者(LP)。而流动性池会根据流动性提供者(LP)对池子的贡献,会给LP一些凭证,这个凭证使用uniswap中的ERC-20token合约实现,LP提供流动性时,token合约给LP铸相应的币,减少流动性的时候会销毁相应的token,这个凭证被称为LPs。而每个LP对池子的贡献对于池子的比重被称为流动性池份额。
2.2 功能
2.2.1 质押
首先工厂合约会初始化创建一个交易对(pair)合约,在这个合约的池子中,两种代币的初始值都是0,需要LP质押一定数量的两种代币来启动池子。
在池子中的两种代币的相对价值是由两种代币的数量比例决定的
,意思是两种代币的总价值应该一直保持相同,而交易会改变两种代币的数量,所以价格也在一直变化,其公式为:
其中两种代币的数量分别为 , , 而 是一个常数。
第一个LP在质押的两个代币的数量分别为 , , 那么获得的LPS ,满足公式:
而在交易过程中,合约会收取一定比例的手续费,而手续费也会放回到流动性池中,相当于流动性的总价值变多,而LPs的总量没有变化,所以相当于单个LPs的价值升高。升高的价值就是LP获取的收益。
而如果LPs的单价过高,高到1wei(以太坊中的最小单位)个LPs的价值也非常高,这时候再往池子总添加流动性的成本就会非常高,不利于维持交易的流动性。为了防止这种情况,uniswap设置了一个最小流动性,该值为1000wei,创建流动性池的时候,都要向0地址锁定这个值的LPS,如果认为要把LPs价格提的很高,那么他就需要再0地址锁定的时候付出更多的成本。
接下来在有LP继续添加流动性的时候,新铸造的LPs为:
, 为原先池子中两种代币的数量, , 为新添加的两种代币的数量, 合约会按照两种代币新添加值与原值比例小的那个,再与原LPs的数量相乘,作为新铸造的LPs。为了利益最大化,LP通常会按照现有池子的比例提供两种代币,即:
LPs由LP持有,并且其他性质与ERC-20一样,可以进行自由交易。
2.2.2 自动做市
首先我们已经知道流动池中的两种代币满足恒定乘积公式:
那么如果想用 换 ,则满足:
换出来的y为:
如果交易量较小的话,交易价格就近似于两种代币在池子中的比值。
交易过程:
- 交易之前,Pair合约中两种token的数量 , 满足:
- 用户发送token 到pair合约。
- 调用合约中的swap接口,计算获取到的 token, 并发送这些token给用户。
- 完成交易之后,pair合约中两种代币的数量 应满足:
2.2.3 价格预言
UniswapV2中通过测量和记录每一个块的第一次交易之前的价格来进行,从而避免操纵价格预言机所产生的攻击,计算出的叫个为上一个区块中的相对价格的平均累计值:
2.2.4 闪电贷
UniswapV2允许用户在同一原子交易中完成借贷于还款。用户可以不用向pair合约做任何抵押就可以借走池子中的所有 代币,只要在交易结束后还上足够数量的 代币即可,则回退整个交易。
交易过程:
- 交易之前pair合约中两种token的数量应满足:
- 用户调用合约中的swap函数,指定要借贷的数量与回调函数的相关参数。
- pair合约向用户发送相应的代币。
- 合约调用用户指定的回调函数。
- 检查回调函数函数完成之后,池子中的两种代币数量应满足:
若不满足则回退整个交易。
三. 源码解析
3.1 UniswapV2ERC20.sol
该合约为一个ERC-20合约,定义了LPtoken代币
pragma solidity =0.5.16; import './interfaces/IUniswapV2ERC20.sol'; import './libraries/SafeMath.sol'; contract UniswapV2ERC20 is IUniswapV2ERC20 { using SafeMath for uint; // token名称 string public constant name = 'Uniswap V2'; // token的标志 string public constant symbol = 'UNI-V2'; // 精度 uint8 public constant decimals = 18; // token的总供应量 uint public totalSupply; // 每个地址的余额 mapping(address => uint) public balanceOf; // 每个地址对每个地址的授权数量 mapping(address => mapping(address => uint)) public allowance; // EIP-712中规定的DOMAIN_SEPARATOR bytes32 public DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); // EIP-712中规定的TYPEHASH bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; // 地址与其nonce值,用于避免重放 mapping(address => uint) public nonces; event Approval(address indexed owner, address indexed spender, uint value); event Transfer(address indexed from, address indexed to, uint value); constructor() public { // 当前链的id uint chainId; assembly { // 通过内联汇编获取chainid chainId := chainid } // 获取DOMAIN_SEPARATOR 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); // 触发事件,0地址向目标地址发送相应的代币 emit Transfer(address(0), to, value); } // 销毁 销毁某个地址的一定数量的代币 function _burn(address from, uint value) internal { // 减小余额 balanceOf[from] = balanceOf[from].sub(value); // 减小相应的总供应量 totalSupply = totalSupply.sub(value); // 触发事件 目标地址向0地址发送相应数量的代币 emit Transfer(from, address(0), value); } // 授权 function _approve(address owner, address spender, uint value) private { // 修改相应的allowance 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; } // 调用者向某地址转账 function transfer(address to, uint value) external returns (bool) { _transfer(msg.sender, to, value); return 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; } // 授权 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); // 确保地址不是0地址且地址是owner地址 require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE'); // 授权 _approve(owner, spender, value); } } |
3.2 UniswapV2Pair.sol
该合约时unswap的核心合约,该合约定义了两种代币组成的交易对,代币存在这个合约中并组成交易池
pragma solidity =0.5.16; import './interfaces/IUniswapV2Pair.sol'; import './UniswapV2ERC20.sol'; import './libraries/Math.sol'; import './libraries/UQ112x112.sol'; import './interfaces/IERC20.sol'; import './interfaces/IUniswapV2Factory.sol'; import './interfaces/IUniswapV2Callee.sol'; contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 { using SafeMath for uint; // UQ112x112库,将224分为两个部分,一个112最为整数部分,另一个112作为浮点数 using UQ112x112 for uint224; // 最小流动性定义, uint public constant MINIMUM_LIQUIDITY = 10**3; // transfer的函数签名 bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)'))); // factory合约的地址 address public factory; // 交易对中的两个token地址 address public token0; address public token1; //表示两个token的储备量,用uint112存储 uint112 private reserve0; // uses single storage slot, accessible via getReserves uint112 private reserve1; // uses single storage slot, accessible via getReserves // 用于判断是不是区块的第一笔交易的时间戳 uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves // 价格的最后累计,用于周边合约的预言机 uint public price0CumulativeLast; uint public price1CumulativeLast; // 最近一次流动性事件之后的k值 uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event // 用于防止重入攻击 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; _reserve1 = reserve1; _blockTimestampLast = blockTimestampLast; } // transfer函数 function _safeTransfer(address token, address to, uint value) private { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value)); 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); // 因为该合约由factory部署,那么msg.sender就是factory的地址 constructor() public { factory = msg.sender; } // 因为该合约是使用create2的方式部署,所以构造函数不能有任何参数,就需要initalize函数初始化两个token地址 // called once by the factory at time of deployment function initialize(address _token0, address _token1) external { require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check token0 = _token0; token1 = _token1; } // 更新储备量 // update reserves and, on the first call per block, price accumulators 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; // overflow is desired // 间隔时间大于0 储备量大于0 if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { // * never overflows, and + overflow is desired // 计算累计价格 // 累计价格 += 储备量/储备量*时间流逝 price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed; price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed; } // 将余额赋值给储备量 reserve0 = uint112(balance0); reserve1 = uint112(balance1); blockTimestampLast = blockTimestamp; emit Sync(reserve0, reserve1); } // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k) function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) { // 获取平台收取手续费地址 address feeTo = IUniswapV2Factory(factory).feeTo(); // 开启平台收取手续费 feeOn = feeTo != address(0); uint _kLast = kLast; // gas savings if (feeOn) { if (_kLast != 0) { // 计算现在k的平方根 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); } } } else if (_kLast != 0) { kLast = 0; } } // 给流动性提供者铸币 // 1.先获取两token的储备量和本合约中两种代币的余额 // 2.通过余额和储备量相减获取增加量 // 3.如果是初始化,则流动性为两增加量的算术平方根减去最小流动性,如果新加流动性,则按照两种代币数量增加小的为准增加流动性 // 4.将Lp mint给提供流动性的地址 // 5.更新两种代币的储备量 // this low-level function should be called from a contract which performs important safety checks function mint(address to) external lock returns (uint liquidity) { // 获取两个token的储备量 (uint112 _reserve0, uint112 _reserve1,) = geReserves(); // gas savings // 这个合约中两个token的余额 uint balance0 = IERC20(token0).balanceOf(address(this)); uint balance1 = IERC20(token1).balanceOf(address(this)); // 获取两种代币的增加量 uint amount0 = balance0.sub(_reserve0); uint amount1 = balance1.sub(_reserve1); // 是否开启手续费 bool feeOn = _mintFee(_reserve0, _reserve1); // 获取总供应量 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee // 如果总供应量为0,即初始化的时候 if (_totalSupply == 0) { // 流动性为两数的算数平方根减去最小流动性 liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY); // 向0地址锁定最小流动性 _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens } else { // 如果流动性不为0,则按照两种代币的增加比例添加新的流动性 liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1); } // 确认流动性大于0 require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED'); // 向指定地址铸造相应的流动性 _mint(to, liquidity); // 更新储备量 _update(balance0, balance1, _reserve0, _reserve1); // 如果开启收费,则k值为两储备量相乘 if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date emit Mint(msg.sender, amount0, amount1); } // 撤回流动性 // 1.获取合约中两种代币的储备量与余额 // 2.获取路由合约发送到本合约的Lptoken // 3.按照Lp在总供应量中的比例取走相应数量的代币 // 4.销毁本合约中所有的流动性 // 5.更新余额和供应量 // this low-level function should be called from a contract which performs important safety checks function burn(address to) external lock returns (uint amount0, uint amount1) { // 获取两种代币储备量 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings address _token0 = token0; // gas savings address _token1 = token1; // gas savings // 两种代币在合约中的余额 uint balance0 = IERC20(_token0).balanceOf(address(this)); uint balance1 = IERC20(_token1).balanceOf(address(this)); // 流动性为用户由路由合约发送到本pair合约的要销毁的金额 uint liquidity = balanceOf[address(this)]; bool feeOn = _mintFee(_reserve0, _reserve1); // 获取总供应量 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee // amount0 与 amount1为用户可以取走的两种代币的数量 // 按照用户在总供应量中的比例取走相应的代币,其中已经含有相应的手续费奖励 amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution // 确保取出的的代币数量都大于0 require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED'); // 销毁本合约内的所有流动性 _burn(address(this), liquidity); // 将取走的代币分别发送给用户 _safeTransfer(_token0, to, amount0); _safeTransfer(_token1, to, amount1); //更新合约内余额 balance0 = IERC20(_token0).balanceOf(address(this)); balance1 = IERC20(_token1).balanceOf(address(this)); _update(balance0, balance1, _reserve0, _reserve1); if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date emit Burn(msg.sender, amount0, amount1, to); } // 交易token,需要输入两种token的量,token要发送到的地址,data用于闪电贷的回调 // this low-level function should be called from a contract which performs important safety checks function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { // 确保至少一个数量大于0 require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT'); // 获取两种代币的储备量 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings // 确保要有足够的余额 require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY'); uint balance0; uint balance1; { // scope for _token{0,1}, avoids stack too deep errors address _token0 = token0; address _token1 = token1; // 发送地址不能为这两个token合约 require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO'); // 发送代币 if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens // 如果data有长度,则调用to的接口进行闪电贷 if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data); // 获取合约中两种代币的余额 balance0 = IERC20(_token0).balanceOf(address(this)); balance1 = IERC20(_token1).balanceOf(address(this)); } // amountIn = balance - (_reserve - amountOut) // 根据取出的储备量、原有的储备量及最新的余额,反求出输入的金额 uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0; // 确保输入的金额至少有一个大于0 require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT'); { // scope for reserve{0,1}Adjusted, avoids stack too deep errors // 调整后的余额 = (1 - 0.3%)* 原余额 uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3)); uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3)); // 新k值应该大于旧的k值,增加值为手续费 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); } // 将合约中多余的储备量转移到指定地址,让余额等于储备量 // force balances to match reserves function skim(address to) external lock { address _token0 = token0; // gas savings address _token1 = token1; // gas savings _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0)); _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1)); } // 强制更新储备量,让储备量与余额相等 // force reserves to match balances function sync() external lock { _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1); } } |
手续费:
每一笔交易都有0.3%的手续费,可以理解为用户对换出去的另一种代币数量少于用k值计算出的值,这时pair合约中的另一种代币的减少量小于使用 算出的理论值,意味着现在另一种代币的余额比预期的更大了,所以 也变大了。手续费计算公式:
其中, 为LPtoken的总供应量, 为两个时期的 值。
3.3 UniswapV2Factory.sol
该合约用于部署pair合约:
pragma solidity =0.5.16; import './interfaces/IUniswapV2Factory.sol'; import './UniswapV2Pair.sol'; contract UniswapV2Factory is IUniswapV2Factory { // 设置0.05%的手续费给开发者,默认不开启 address public feeTo; // 设置feeTo地址的地址,相当于管理员 address public feeToSetter; // 每两个erc20代币之间的交易对合约地址 mapping(address => mapping(address => address)) public getPair; // 所有的交易对 address[] public allPairs; event PairCreated(address indexed token0, address indexed token1, address pair, uint); // 0.05%手续费管理员 constructor(address _feeToSetter) public { feeToSetter = _feeToSetter; } // 获取交易对数量 function allPairsLength() external view returns (uint) { return allPairs.length; } // 创建交易对 function createPair(address tokenA, address tokenB) external returns (address pair) { // 两个token的合约地址不能相同 require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES'); // 两个token按照地址从小到大排序 (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); // 两个地址不能为0地址 require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS'); // 检查交易对是否存在 require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient // 获取模板合约UniswapV2Pair的合约字节码 bytes memory bytecode = type(UniswapV2Pair).creationCode; // 生成salt用于create2创建合约,salt值固定 bytes32 salt = keccak256(abi.encodePacked(token0, token1)); // pair为交易对地址 assembly { pair := create2(0, add(bytecode, 32), mload(bytecode), salt) } // 调用pair合约中的initialize方法 IUniswapV2Pair(pair).initialize(token0, token1); // 将交易对合约的地址放入getPair表中 getPair[token0][token1] = pair; getPair[token1][token0] = pair; // populate mapping in the reverse direction // pair合约地址push进allPairs allPairs.push(pair); emit PairCreated(token0, token1, pair, allPairs.length); } // 设置收取0.05%手续费的地址 function setFeeTo(address _feeTo) external { require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN'); feeTo = _feeTo; } // 更改owner function setFeeToSetter(address _feeToSetter) external { require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN'); feeToSetter = _feeToSetter; } } |
3.4 UniswapV2Router02.sol
pragma solidity =0.6.6; import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import './interfaces/IUniswapV2Router02.sol'; import './libraries/UniswapV2Library.sol'; import './libraries/SafeMath.sol'; import './interfaces/IERC20.sol'; import './interfaces/IWETH.sol'; contract UniswapV2Router02 is IUniswapV2Router02 { using SafeMath for uint; // factory合约地址 address public immutable override factory; // WETH合约地址 address public immutable override WETH; // 确保时间在范围之内 modifier ensure(uint deadline) { require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED'); _; } constructor(address _factory, address _WETH) public { factory = _factory; WETH = _WETH; } // 只接收从WTEH传来的以太币 receive() external payable { assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract } // **** ADD LIQUIDITY **** // 增加流动性 ,用户需要提供一个范围 function _addLiquidity( address tokenA, // tokenA地址 address tokenB, // tokenB地址 uint amountADesired,// 期望添加A的数量 uint amountBDesired,// 期望添加B的数量 uint amountAMin, // 添加A的最小数量 uint amountBMin // 添加B的最小数量 ) internal virtual returns (uint amountA, uint amountB) { // 实际添加的AB的数量 // create the pair if it doesn't exist yet // 如果交易对不存在则创建一个 if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) { IUniswapV2Factory(factory).createPair(tokenA, tokenB); } // 获取factory合约中两种代币的储备量 (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB); if (reserveA == 0 && reserveB == 0) { // 对于两种代币都为空的情况,那么则会按照期望添加的量进行添加 (amountA, amountB) = (amountADesired, amountBDesired); } else { // fatory合约中有这两种代币的储量,则按照规则添加 uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); (amountA, amountB) = (amountAOptimal, amountBDesired); } } } // 外部调用的增加流动性的函数 function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, // Lp发送地址 uint deadline // 过期时间 ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { // 实际添加的两种代币的量 (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); // 交易对 address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); // 向交易对合约发送两种代币 TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); // 交易对合约向to地址发送Lp liquidity = IUniswapV2Pair(pair).mint(to); } // 添加eth与另外一种token的流动性 function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) { (amountToken, amountETH) = _addLiquidity( token, WETH, amountTokenDesired, msg.value, amountTokenMin, amountETHMin ); address pair = UniswapV2Library.pairFor(factory, token, WETH); TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); IWETH(WETH).deposit{value: amountETH}(); assert(IWETH(WETH).transfer(pair, amountETH)); liquidity = IUniswapV2Pair(pair).mint(to); // refund dust eth, if any if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); } // 移除流动性 // **** REMOVE LIQUIDITY **** function removeLiquidity( address tokenA, // tokenA地址 address tokenB, // tokenB地址 uint liquidity, // 销毁Lp数量 uint amountAMin, uint amountBMin, address to, // 取回的ABtoken的发送地址 uint deadline // 过期时间 ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) { // 获取交易对 address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); // 向交易对发送Lptoken,合约并不会检验 IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair // 交易对合约完成销毁并发送两种代币到to地址 (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to); // 获取返回的amount0是哪个token (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); // 如果换回的值不够,则交易失败 require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); } function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) { (amountToken, amountETH) = removeLiquidity( token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline ); TransferHelper.safeTransfer(token, to, amountToken); IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } // 通过签名授权减少流动性 function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s // 签名 ) external virtual override returns (uint amountA, uint amountB) { address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); uint value = approveMax ? uint(-1) : liquidity; IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline); } function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint amountToken, uint amountETH) { address pair = UniswapV2Library.pairFor(factory, token, WETH); uint value = approveMax ? uint(-1) : liquidity; IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline); } // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) **** function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) public virtual override ensure(deadline) returns (uint amountETH) { (, amountETH) = removeLiquidity( token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline ); TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this))); IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint amountETH) { address pair = UniswapV2Library.pairFor(factory, token, WETH); uint value = approveMax ? uint(-1) : liquidity; IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); amountETH = removeLiquidityETHSupportingFeeOnTransferTokens( token, liquidity, amountTokenMin, amountETHMin, to, deadline ); } // **** SWAP **** // 对换代币 // requires the initial amount to have already been sent to the first pair function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual { // 将path依次向后遍历,每次取两个地址 for (uint i; i < path.length - 1; i++) { // 前一个地址作为被卖代币,to为目标买入代币 (address input, address output) = (path[i], path[i + 1]); // 获取两个地址中小的那个 (address token0,) = UniswapV2Library.sortTokens(input, output); // 获取输出金额 uint amountOut = amounts[i + 1]; (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); // 最后一个地址为买入代币接收地址 address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; // 执行对换 IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap( amount0Out, amount1Out, to, new bytes(0) ); } } function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { // 计算可以对换到的目标代币数量 amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); // 最后一步得到的币要大于给定的最低值 require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); // 向pair合约转钱 TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); // 执行对换 _swap(amounts, path, to); } function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external virtual override payable ensure(deadline) returns (uint[] memory amounts) { require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH'); amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); IWETH(WETH).deposit{value: amounts[0]}(); assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0])); _swap(amounts, path, to); } function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external virtual override ensure(deadline) returns (uint[] memory amounts) { require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH'); amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); } function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external virtual override ensure(deadline) returns (uint[] memory amounts) { require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH'); amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); } function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external virtual override payable ensure(deadline) returns (uint[] memory amounts) { require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH'); amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); IWETH(WETH).deposit{value: amounts[0]}(); assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0])); _swap(amounts, path, to); // refund dust eth, if any if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); } // **** SWAP (supporting fee-on-transfer tokens) **** // requires the initial amount to have already been sent to the first pair function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual { for (uint i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); (address token0,) = UniswapV2Library.sortTokens(input, output); IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)); uint amountInput; uint amountOutput; { // scope to avoid stack too deep errors (uint reserve0, uint reserve1,) = pair.getReserves(); (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput); amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput); } (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0)); address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; pair.swap(amount0Out, amount1Out, to, new bytes(0)); } } function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) { TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn ); uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); require( IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT' ); } function swapExactETHForTokensSupportingFeeOnTransferTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override payable ensure(deadline) { require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH'); uint amountIn = msg.value; IWETH(WETH).deposit{value: amountIn}(); assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn)); uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); require( IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT' ); } function swapExactTokensForETHSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) { require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn ); _swapSupportingFeeOnTransferTokens(path, address(this)); uint amountOut = IERC20(WETH).balanceOf(address(this)); require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); IWETH(WETH).withdraw(amountOut); TransferHelper.safeTransferETH(to, amountOut); } // **** LIBRARY FUNCTIONS **** function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) { return UniswapV2Library.quote(amountA, reserveA, reserveB); } function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public pure virtual override returns (uint amountOut) { return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut); } function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure virtual override returns (uint amountIn) { return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut); } function getAmountsOut(uint amountIn, address[] memory path) public view virtual override returns (uint[] memory amounts) { return UniswapV2Library.getAmountsOut(factory, amountIn, path); } function getAmountsIn(uint amountOut, address[] memory path) public view virtual override returns (uint[] memory amounts) { return UniswapV2Library.getAmountsIn(factory, amountOut, path); } } |
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)