freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

区块链2.0的杀手应用—UniswapV2
2022-11-11 11:00:28
所属地 广东省

目    录

  1. 一. 简介
  2. 二. 原理

2.1         流动性

2.2         功能

2.2.1       质押

2.2.2       自动做市

2.2.3       价格预言

2.2.4       闪电贷

  1. 三. 源码解析

3.1         UniswapV2ERC20.sol

3.2         UniswapV2Pair.sol

3.3         UniswapV2Factory.sol

3.4         UniswapV2Router02.sol


一.     简介

UniswapV2是一个建立在以太坊上的主流去中心化加密交易平台(DEX),实现了自动化做市商(AMM)。

AMM要实现自动完成与买卖方的交易,需要满足以下几个特性:

  1. AMM要持有资产,由于要双向报价,所以需要同时持有两种资产。
  2. AMM资产池应该能实现充值与提现。
  3. AMM应该根据市场情况自动调整价格。
  4. AMM要能通过交易赚取利润。

二.     原理

UniswapV2部署在以太坊上,其本身是一个支持ERC-20标准的token合约,其定义在合约文件UniswapV2ERC20.sol中。

UniswapV2可以完成任意两种ERC-20token的交换,其实现在uniswapV2Pair合约中,该合约主要提供以下3种功能:

  1. 流动性追踪,追踪交易池中的代币余额,并提供流动性代币。
  2. 自动做市,通过算法计算出市场价格。
  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为:

如果交易量较小的话,交易价格就近似于两种代币在池子中的比值。

交易过程

  1. 交易之前,Pair合约中两种token的数量 , 满足:
  2. 用户发送token 到pair合约。
  3. 调用合约中的swap接口,计算获取到的 token, 并发送这些token给用户。
  4. 完成交易之后,pair合约中两种代币的数量 应满足:

2.2.3          价格预言

UniswapV2中通过测量和记录每一个块的第一次交易之前的价格来进行,从而避免操纵价格预言机所产生的攻击,计算出的叫个为上一个区块中的相对价格的平均累计值:

2.2.4          闪电贷

UniswapV2允许用户在同一原子交易中完成借贷于还款。用户可以不用向pair合约做任何抵押就可以借走池子中的所有 代币,只要在交易结束后还上足够数量的 代币即可,则回退整个交易。

交易过程:

  1. 交易之前pair合约中两种token的数量应满足:
  2. 用户调用合约中的swap函数,指定要借贷的数量与回调函数的相关参数。
  3. pair合约向用户发送相应的代币。
  4. 合约调用用户指定的回调函数。
  5. 检查回调函数函数完成之后,池子中的两种代币数量应满足:

若不满足则回退整个交易。

三.     源码解析

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

}

}


# 区块链安全 # uniswap系统开发
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录