学习核心合约UniswapV2Pair,在父合约UniswapV2ERC20的基础上增加资产交易及流动性提供等功能。

交易对合约本身是erc20合约,代币表示流动性,代币在提供流动性的地址里,当提供流动性,就会增发代币给提供者,反正提取流动性,就烧掉提取者的代币

  1.  
  1. 1 pragma solidity =0.5.16;
  2. 2
  3. 3 import './interfaces/IUniswapV2Pair.sol';
  4. 4 import './UniswapV2ERC20.sol';
  5. 5 import './libraries/Math.sol';
  6. 6 import './libraries/UQ112x112.sol';
  7. 7 import './interfaces/IERC20.sol';
  8. 8 import './interfaces/IUniswapV2Factory.sol';
  9. 9 import './interfaces/IUniswapV2Callee.sol';
  10. 10
  11. 11 contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
  12. 12 using SafeMath for uint;
  13. 13 using UQ112x112 for uint224;
  14. 14
  15. 15 uint public constant MINIMUM_LIQUIDITY = 10**3;
  16. 16 bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
  17. 17
  18. 18 address public factory;
  19. 19 address public token0;
  20. 20 address public token1;
  21. 21
  22. 22 uint112 private reserve0; // uses single storage slot, accessible via getReserves
  23. 23 uint112 private reserve1; // uses single storage slot, accessible via getReserves
  24. 24 uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
  25. 25
  26. 26 uint public price0CumulativeLast;
  27. 27 uint public price1CumulativeLast;
  28. 28 uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
  29. 29
  30. 30 uint private unlocked = 1;
  31. 31 modifier lock() {
  32. 32 require(unlocked == 1, 'UniswapV2: LOCKED');
  33. 33 unlocked = 0;
  34. 34 _;
  35. 35 unlocked = 1;
  36. 36 }
  37. 37
  38. 38 function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
  39. 39 _reserve0 = reserve0;
  40. 40 _reserve1 = reserve1;
  41. 41 _blockTimestampLast = blockTimestampLast;
  42. 42 }
  43. 43
  44. 44 function _safeTransfer(address token, address to, uint value) private {
  45. 45 (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
  46. 46 require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
  47. 47 }
  48. 48
  49. 49 event Mint(address indexed sender, uint amount0, uint amount1);
  50. 50 event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
  51. 51 event Swap(
  52. 52 address indexed sender,
  53. 53 uint amount0In,
  54. 54 uint amount1In,
  55. 55 uint amount0Out,
  56. 56 uint amount1Out,
  57. 57 address indexed to
  58. 58 );
  59. 59 event Sync(uint112 reserve0, uint112 reserve1);
  60. 60
  61. 61 constructor() public {
  62. 62 factory = msg.sender;
  63. 63 }
  64. 64
  65. 65 // called once by the factory at time of deployment
  66. 66 function initialize(address _token0, address _token1) external {
  67. 67 require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
  68. 68 token0 = _token0;
  69. 69 token1 = _token1;
  70. 70 }
  71. 71
  72. 72 // update reserves and, on the first call per block, price accumulators
  73. 73 function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
  74. 74 require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
  75. 75 uint32 blockTimestamp = uint32(block.timestamp % 2**32);
  76. 76 uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
  77. 77 if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
  78. 78 // * never overflows, and + overflow is desired
  79. 79 price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
  80. 80 price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
  81. 81 }
  82. 82 reserve0 = uint112(balance0);
  83. 83 reserve1 = uint112(balance1);
  84. 84 blockTimestampLast = blockTimestamp;
  85. 85 emit Sync(reserve0, reserve1);
  86. 86 }
  87. 87
  88. 88 // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
  89. 89 function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
  90. 90 address feeTo = IUniswapV2Factory(factory).feeTo();
  91. 91 feeOn = feeTo != address(0);
  92. 92 uint _kLast = kLast; // gas savings
  93. 93 if (feeOn) {
  94. 94 if (_kLast != 0) {
  95. 95 uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
  96. 96 uint rootKLast = Math.sqrt(_kLast);
  97. 97 if (rootK > rootKLast) {
  98. 98 uint numerator = totalSupply.mul(rootK.sub(rootKLast));
  99. 99 uint denominator = rootK.mul(5).add(rootKLast);
  100. 100 uint liquidity = numerator / denominator;
  101. 101 if (liquidity > 0) _mint(feeTo, liquidity);
  102. 102 }
  103. 103 }
  104. 104 } else if (_kLast != 0) {
  105. 105 kLast = 0;
  106. 106 }
  107. 107 }
  108. 108
  109. 109 // this low-level function should be called from a contract which performs important safety checks
  110. 110 function mint(address to) external lock returns (uint liquidity) {
  111. 111 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
  112. 112 uint balance0 = IERC20(token0).balanceOf(address(this));
  113. 113 uint balance1 = IERC20(token1).balanceOf(address(this));
  114. 114 uint amount0 = balance0.sub(_reserve0);
  115. 115 uint amount1 = balance1.sub(_reserve1);
  116. 116
  117. 117 bool feeOn = _mintFee(_reserve0, _reserve1);
  118. 118 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
  119. 119 if (_totalSupply == 0) {
  120. 120 liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
  121. 121 _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
  122. 122 } else {
  123. 123 liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
  124. 124 }
  125. 125 require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
  126. 126 _mint(to, liquidity);
  127. 127
  128. 128 _update(balance0, balance1, _reserve0, _reserve1);
  129. 129 if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
  130. 130 emit Mint(msg.sender, amount0, amount1);
  131. 131 }
  132. 132
  133. 133 // this low-level function should be called from a contract which performs important safety checks
  134. 134 function burn(address to) external lock returns (uint amount0, uint amount1) {
  135. 135 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
  136. 136 address _token0 = token0; // gas savings
  137. 137 address _token1 = token1; // gas savings
  138. 138 uint balance0 = IERC20(_token0).balanceOf(address(this));
  139. 139 uint balance1 = IERC20(_token1).balanceOf(address(this));
  140. 140 uint liquidity = balanceOf[address(this)];
  141. 141
  142. 142 bool feeOn = _mintFee(_reserve0, _reserve1);
  143. 143 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
  144. 144 amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
  145. 145 amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
  146. 146 require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
  147. 147 _burn(address(this), liquidity);
  148. 148 _safeTransfer(_token0, to, amount0);
  149. 149 _safeTransfer(_token1, to, amount1);
  150. 150 balance0 = IERC20(_token0).balanceOf(address(this));
  151. 151 balance1 = IERC20(_token1).balanceOf(address(this));
  152. 152
  153. 153 _update(balance0, balance1, _reserve0, _reserve1);
  154. 154 if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
  155. 155 emit Burn(msg.sender, amount0, amount1, to);
  156. 156 }
  157. 157
  158. 158 // this low-level function should be called from a contract which performs important safety checks
  159. 159 function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
  160. 160 require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
  161. 161 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
  162. 162 require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
  163. 163
  164. 164 uint balance0;
  165. 165 uint balance1;
  166. 166 { // scope for _token{0,1}, avoids stack too deep errors
  167. 167 address _token0 = token0;
  168. 168 address _token1 = token1;
  169. 169 require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
  170. 170 if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
  171. 171 if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
  172. 172 if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
  173. 173 balance0 = IERC20(_token0).balanceOf(address(this));
  174. 174 balance1 = IERC20(_token1).balanceOf(address(this));
  175. 175 }
  176. 176 uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
  177. 177 uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
  178. 178 require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
  179. 179 { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
  180. 180 uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
  181. 181 uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
  182. 182 require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
  183. 183 }
  184. 184
  185. 185 _update(balance0, balance1, _reserve0, _reserve1);
  186. 186 emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
  187. 187 }
  188. 188
  189. 189 // force balances to match reserves
  190. 190 function skim(address to) external lock {
  191. 191 address _token0 = token0; // gas savings
  192. 192 address _token1 = token1; // gas savings
  193. 193 _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
  194. 194 _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
  195. 195 }
  196. 196
  197. 197 // force reserves to match balances
  198. 198 function sync() external lock {
  199. 199 _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
  200. 200 }
  201. 201 }
  1.  
  1.  

1 指定Solidity编译版本。

2    第三-第九行: 导入交易对合约需要实现的接口和交易对的父合约。

    导入Math库,导入UQ112x112这是自定义的数据格式库,在V2中使用两种代币的比值作为价格,为提高精度使用uint112来表示价格,UQ112x112 前面代表整数,后面代表小数。导入标准ERC20接口目的:获取交易对合约流通池的代币余额。

    导入IUniswapV2Factory合约,目的获取开发团队手续费地址。

    导入IUniswapV2Callee.sol.规定第三方合约要实现的接口格式。定义次接口可进行FlasSwap

3   定义UniswapV2Pair  实现了IUniswapV2Pair,并且继承UniswapV2ERC20.继承了父合约所有非私有接口与状态变量

4 12-13行 ;实现SafeMath、UQ112x112库函数。

5      定义最小流动性,在提供初始流动性时烧掉。

6   计算transfer的函数选择器。

7      18-20行:定义3个 address类型的公共状态变量,记录factory合约地址,2个代币的地址

8     21-23 reserve0,reserve1 记录最新的恒定乘积中的代币数量,blockTimestampLast  交易时的区块创建时间。

9  price0CumulativeLast  ,price1CumulativeLast ,j记录交易对中两种价格的累计值。

10   klast 记录恒定乘积中积的值,用于开发团队手续费计算

11      防止重入攻击,在函数修饰器中, _;表示执行被修饰的函数体,当函数被外部调用,unlocked 为0,函数执行完设置为1,在未执行完前,如果重入该函数,l

  1. 1 uint private unlocked = 1;
  2. 2 modifier lock() {
  3. 3 require(unlocked == 1, 'UniswapV2: LOCKED');
  4. 4 unlocked = 0;
  5. 5 _;
  6. 6 unlocked = 1;
  7. 7 }

12  getReserves函数  获取当前交易对的资产信息以及最后交易的区块时间

13  _safeTransfer 函数,使用call函数进行代币合约transfer的调用(函数选择器),检查返回值

14     定义四个event事件  mint swap burn sync  便于追踪。

15   constructor 构造器,工厂合约 factory 设为msg.sender。

16       initialize 初始化函数,由于使用create2创建合约,不能向构造函数传递参数。所以没有之间在构造函数传参。

17  _update 函数, 更新reserves,在每个block的第一次调用更新价格累积值。

  1. 1 // update reserves and, on the first call per block, price accumulators
  2. 2 function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
  3. 3 require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
  4. 4 uint32 blockTimestamp = uint32(block.timestamp % 2**32);
  5. 5 uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
  6. 6 if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
  7. 7 // * never overflows, and + overflow is desired
  8. 8 price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
  9. 9 price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
  10. 10 }
  11. 11 reserve0 = uint112(balance0);
  12. 12 reserve1 = uint112(balance1);
  13. 13 blockTimestampLast = blockTimestamp;
  14. 14 emit Sync(reserve0, reserve1);
  15. 15 }
  16. 16

1) 四个参数  当前合约代币的两种余额,保存的恒定乘积中两种代币数值,将保存的数值更新为实时代币余额,并且同时进行价格累计的计算。

     2)第一行验证余额不能大于uint112类型的最大值。

     3)存储槽位是256位,两个代币数量各112位, 剩下32位 记录当前区块时间

     4)计算当前block 和上一次block的时间差值

    5) if 语句 当同区块的第二笔交易,timeElapsed为0,就不会计算累积值

    6)计算价格的累积值

    7) 更新reserve的值,更新bloc的 时间为当前时间,触发同步事件

18  _mintFree 函数 计算并发送开发团队手续费,

  1. 1 function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
  2. 2 address feeTo = IUniswapV2Factory(factory).feeTo();
  3. 3 feeOn = feeTo != address(0);
  4. 4 uint _kLast = kLast; // gas savings
  5. 5 if (feeOn) {
  6. 6 if (_kLast != 0) {
  7. 7 uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
  8. 8 uint rootKLast = Math.sqrt(_kLast);
  9. 9 if (rootK > rootKLast) {
  10. 10 uint numerator = totalSupply.mul(rootK.sub(rootKLast));
  11. 11 uint denominator = rootK.mul(5).add(rootKLast);
  12. 12 uint liquidity = numerator / denominator;
  13. 13 if (liquidity > 0) _mint(feeTo, liquidity);
  14. 14 }
  15. 15 }
  16. 16 } else if (_kLast != 0) {
  17. 17 kLast = 0;
  18. 18 }
  19. 19 }

    1) 函数参数:交易对中保存的恒定乘积中的代币数,返回类型 bool

    2)获取开发团队手续费地址,判断地址是否是零地址来判断开关是否打开

    3) 局部变量保存恒定乘积中积的值。(减少gas)

    4) if 判断 ,如果手续费开关打开,计算手续费值

    5)下面就是具体数学运算。

19  mint 函数:提供流动性时增发流动性代币给提供者

  1. 1 function mint(address to) external lock returns (uint liquidity) {
  2. 2 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
  3. 3 uint balance0 = IERC20(token0).balanceOf(address(this));
  4. 4 uint balance1 = IERC20(token1).balanceOf(address(this));
  5. 5 uint amount0 = balance0.sub(_reserve0);
  6. 6 uint amount1 = balance1.sub(_reserve1);
  7. 7
  8. 8 bool feeOn = _mintFee(_reserve0, _reserve1);
  9. 9 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
  10. 10 if (_totalSupply == 0) {
  11. 11 liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
  12. 12 _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
  13. 13 } else {
  14. 14 liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
  15. 15 }
  16. 16 require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
  17. 17 _mint(to, liquidity);
  18. 18
  19. 19 _update(balance0, balance1, _reserve0, _reserve1);
  20. 20 if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
  21. 21 emit Mint(msg.sender, amount0, amount1);
  22. 22 }
  23. 23

      1)函数参数 接受流动性代币的地址,返回值 增发的数量,函数修饰器lock 防重入

      2) 获取当前交易对的resverse

      3)第三-四行 获取两个代币的当前余额, 再减去reserve(池子里两个代币原有 的数量)  就得到  两个代币的投入量。

      4)计算开发团队手续费

      5)使用局部变量保存已经发行了流动性的数量。减少gas

      6) if(判断是否是初次提供)如果是初次, 计算恒定乘积公式中积的平方根,再减去最初始的流动性值(1000). 如果不是, 则每种代币按比例计算增发的流动性数量那流动性则是取两个值中较小的那个。

      7)require验证  增发的流动性大于0,

      8)_mint(to,liquidity) 增发流动性给接收者.

      9):调用_update ,更新当前保存的恒定乘积中两个代币的值.

      10)如果手续费打开,则更新乘积值.这个值只再计算手续费会用

      11)触发Mint 事件

20 burn函数:通过燃烧流动性LP,来提取对应的代币,减少教育对的流动性

  1. 1 // this low-level function should be called from a contract which performs important safety checks
  2. 2 function burn(address to) external lock returns (uint amount0, uint amount1) {
  3. 3 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
  4. 4 address _token0 = token0; // gas savings
  5. 5 address _token1 = token1; // gas savings
  6. 6 uint balance0 = IERC20(_token0).balanceOf(address(this));
  7. 7 uint balance1 = IERC20(_token1).balanceOf(address(this));
  8. 8 uint liquidity = balanceOf[address(this)];
  9. 9
  10. 10 bool feeOn = _mintFee(_reserve0, _reserve1);
  11. 11 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
  12. 12 amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
  13. 13 amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
  14. 14 require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
  15. 15 _burn(address(this), liquidity);
  16. 16 _safeTransfer(_token0, to, amount0);
  17. 17 _safeTransfer(_token1, to, amount1);
  18. 18 balance0 = IERC20(_token0).balanceOf(address(this));
  19. 19 balance1 = IERC20(_token1).balanceOf(address(this));
  20. 20
  21. 21 _update(balance0, balance1, _reserve0, _reserve1);
  22. 22 if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
  23. 23 emit Burn(msg.sender, amount0, amount1, to);
  24. 24 }
  25. 25

        1) 参数 代币接收者的地址,返回值 提取的两种代币数量,lock 防重入。

        2) 先获取交易对的reserse,两个代币地址,保存在局部变量中,减少gas

        3)获取交易对合约地址拥有的两种代币数量.

        4)  获取当前合约地址的流动性代币余额,正常情况下,交易对合约里是不会有流动性代币的,因为所有流动性代币都是给到了流动性提供者的。而这里有值,其实是因为路由合约会先把用户的流动性代币划转到该配对合约里

        5)  计算手续

        6)  局部变量保存总流动性,节约gas

        7)按比例计算资产。提取数量=用户流动性/总流动性*代币总余额

8)require 提取数量大于0。

        9) _burn  将用户转让的流动性燃烧掉。

        10)_把相应的代币发送给接收者。

        11) 重新获取交易对合约地址所拥有的代币余额

        12)_uodate  更新当前保存的恒定乘积中两种代币的值

        13 更新Klast值

        14 触发燃烧事件

21  Swap函数:  交易对中 资产的交换

  1. 1 function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
  2. 2 require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
  3. 3 (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
  4. 4 require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
  5. 5 uint balance0;
  6. 6 uint balance1;
  7. 7 { // scope for _token{0,1}, avoids stack too deep errors
  8. 8 address _token0 = token0;
  9. 9 address _token1 = token1;
  10. 10 require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
  11. 11 if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
  12. 12 if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
  13. 13 if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
  14. 14 balance0 = IERC20(_token0).balanceOf(address(this));
  15. 15 balance1 = IERC20(_token1).balanceOf(address(this));
  16. 16 }
  17. 17 uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
  18. 18 uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
  19. 19 require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
  20. 20 { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
  21. 21 uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
  22. 22 uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
  23. 23 require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
  24. 24 }
  25. 25
  26. 26 _update(balance0, balance1, _reserve0, _reserve1);
  27. 27 emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
  28. 28 }
  29. 29

        1)4个参数 转出token0的数量,token1的数量,通常者两个值有一个为0,to为接收者地址,data参数执行回调时传递的数据,通过路由合约兑换 该值为0。Lock 防重入攻击

        2)第2行 检验传入参数不能为0,第三行,获取revrese。

         3)第四行:兑换出的数量必须小于reverse

        4) 定义局部变量 保存当前交易对的两种代币余额

        5) 7-16行用{},特殊语法,避免堆栈过深

        6)8-10行:局部变量保存代币合约地址,并校验 接收者地址不i能为要兑换的代币地址

        7)if amount0/1Out 谁大于0就转出,

        8) if 如果data参数为空,则使用路由合约兑换(普通交易都是data为0)若不为空,to 地址转为 IUniswapV2Callee 并调用其 uniswapV2Call() 函数,这其实就是一个回调函数,to 地址需要实现该接口。并把data传过去

        9)获取交易对合约地址的余额(此时已经减去了要转出的代币数量)

        10)17-19行: 计算转入的代币数量 根据amountIn = balance - (reserve - amountOut),实际转入的一个0一个不为0。然后验证 其中一个转入值要大于0

        11)20-24 行:防止堆栈过深, 进行最终的恒定乘积验证。公式:blance-0.003*amountOut。新的恒定乘积的积大于旧的值

        12)更新恒定乘积的值reserve为balance

        13)触发swap事件。

22 skim 函数 强制交易对合约中两种代币实际余额和保存的恒定乘积资产数量保存一致。如果有多则发送给调用者。任何人都可以调用

  1. 1 // force balances to match reserves
  2. 2 function skim(address to) external lock {
  3. 3 address _token0 = token0; // gas savings
  4. 4 address _token1 = token1; // gas savings
  5. 5 _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
  6. 6 _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
  7. 7 }

223 sync函数 和skim函数相反,强制保存的恒定乘积的资产数量为交易对合约中两种代币的实际余额。

剖析Defi之Uinswap_2的更多相关文章

  1. 剖析Defi之Uinswap_1

    学习UniswapERC20,它是交易对的父合约.UniswapV2ERC20 是流动性代币合约,也称为 LP Token.功能主要实习ERC20代币功能以及对线下签名授权. 1 pragma sol ...

  2. 剖析Defi之Uinswap_0

    Uniswap是什么,不需要讲了吧.YYDS(永远嘀神) 介绍几个概念: 恒定乘积算法:可以简单看作X * Y = K,这里K(乘积)保持不变,所以叫恒定乘积算法,该函数是一个反曲线. 自动流动性协议 ...

  3. 探索C#之6.0语法糖剖析

    阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和fin ...

  4. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  5. [C#] 剖析 AssemblyInfo.cs - 了解常用的特性 Attribute

    剖析 AssemblyInfo.cs - 了解常用的特性 Attribute [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5944391.html 序 ...

  6. Membership三步曲之进阶篇 - 深入剖析Provider Model

    Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...

  7. 《AngularJS深度剖析与最佳实践》简介

    由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...

  8. 探索c#之Async、Await剖析

    阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. ...

  9. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

随机推荐

  1. oracle 锁查询

    --v$lock中 id1 在锁模式是 TX 时保存的是 实物id 的前2段SELECT * FROM (SELECT s.SID, TRUNC(id1 / power(2, 16)) rbs, bi ...

  2. java生成cron表达式

    bean类: package com.cst.klocwork.service.cron; public class TaskScheduleModel { /** * 所选作业类型: * 1 -&g ...

  3. mybatis中返回自动生成的id

    当有时我们插入一条数据时,由于id很可能是自动生成的,如果我们想要返回这条刚插入的id怎么办呢. 在mysql数据中我们可以在insert下添加一个selectKey用以指定返回的类型和值:     ...

  4. feignclient发送get请求,传递参数为对象

    feignclient发送get请求,传递参数为对象.此时不能使用在地址栏传递参数的方式,需要将参数放到请求体中. 第一步: 修改application.yml中配置feign发送请求使用apache ...

  5. Java poi导出设置 Excel某些单元格不可编辑

    小白的总结,大神勿喷:需要转载请说明出处,如果有什么问题,欢迎留言 一.需求: 1.某一列 .某一行或某些单元格不可编辑,其他列可以编辑 二.期间遇到的问题 1.无法设置成不可编辑 2.设置为不可编辑 ...

  6. nvm命令

    1.安装node nvm install node版本 2.查看已安装版本 nvm list 3.切换node版本 nvm use node版本 4.查看版本 node -v

  7. 【React】组件书写记录

    时钟组件: 组件形式:数字时钟 https://blog.csdn.net/hahahahahahahaha__1/article/details/80688920 Javascript获取时间方法: ...

  8. 阿里云发布CloudOps白皮书,ECS自动化运维套件新升级

    12月10 日,2021云上架构与运维峰会上,阿里云发布业界首部<云上自动化运维白皮书>(简称CloudOps白皮书),并在其中提出了CloudOps成熟度模型.同时,阿里云还宣布了ECS ...

  9. C#内建接口:IEnumerable

    这节讲一下接口IEnumerable. 01 什么是Enumerable 在一些返回集合数据的接口中,我们经常能看到IEnumerable接口的身影.那什么是Enumerable呢?首先它跟C#中的e ...

  10. python模块(三)

    hashilib模块 hashilib模块的主要作用是加密,可以将明文数据通过一系列算法转化为秘闻数据. 目的是为了数据的安全. 加密算法包括md系列,sha系列,base系列,hmac系列. 基本使 ...