UniswapV2-periphery有Migrator跟Router合约,其中Router1已经弃用,因此直接解释Router2。 在Router2中使用到了UniswapV2Library合约(可以作为背景知识看一看,很基础,前面的文章提到过相关知识,这里不在赘述)。

Migrator.sol

pragma solidity =0.6.6;import '@uniswap/lib/contracts/libraries/TransferHelper.sol';import './interfaces/IUniswapV2Migrator.sol';
import './interfaces/V1/IUniswapV1Factory.sol';
import './interfaces/V1/IUniswapV1Exchange.sol';
import './interfaces/IUniswapV2Router01.sol';
import './interfaces/IERC20.sol';// 该合约负责将V1迁移到V2
contract UniswapV2Migrator is IUniswapV2Migrator {IUniswapV1Factory immutable factoryV1; // immutable相当于常量,可以在构造函数中设置,之后不能修改 访问他们相对来说更节省gasIUniswapV2Router01 immutable router;constructor(address _factoryV1, address _router) public {factoryV1 = IUniswapV1Factory(_factoryV1);router = IUniswapV2Router01(_router);}// needs to accept ETH from any v1 exchange and the router. ideally this could be enforced, as in the router,// but it's not possible because it requires a call to the v1 factory, which takes too much gas// 该函数旨在表明本合约可以接受其他地址的ETH转账receive() external payable {} function migrate(address token, uint amountTokenMin, uint amountETHMin, address to, uint deadline) // 应该是V1主动调用本函数进行迁移externaloverride{// V1是token/ETH交易对 因此输入token即可查询对应地址IUniswapV1Exchange exchangeV1 = IUniswapV1Exchange(factoryV1.getExchange(token));uint liquidityV1 = exchangeV1.balanceOf(msg.sender);// 将流动性代币UNI1转移到本合约中require(exchangeV1.transferFrom(msg.sender, address(this), liquidityV1), 'TRANSFER_FROM_FAILED');// 在本合约移除流动性 销毁UNI1 返回token/ETH到本合约中(uint amountETHV1, uint amountTokenV1) = exchangeV1.removeLiquidity(liquidityV1, 1, 1, uint(-1));// 授权给router合约进行路径查找后的token转账TransferHelper.safeApprove(token, address(router), amountTokenV1);// 通过router为该token/ETH池子添加流动性(uint amountTokenV2, uint amountETHV2,) = router.addLiquidityETH{value: amountETHV1}(token,amountTokenV1,amountTokenMin,amountETHMin,to,deadline); if (amountTokenV1 > amountTokenV2) {TransferHelper.safeApprove(token, address(router), 0); // be a good blockchain citizen, reset allowance to 0TransferHelper.safeTransfer(token, msg.sender, amountTokenV1 - amountTokenV2);} else if (amountETHV1 > amountETHV2) {// addLiquidityETH guarantees that all of amountETHV1 or amountTokenV1 will be used, hence this else is safeTransferHelper.safeTransferETH(msg.sender, amountETHV1 - amountETHV2);}}
}

Router02.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;address public immutable override factory;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;}receive() external payable {assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract}// **** ADD LIQUIDITY ****// 您可以将流动性直接存入核心合约(使用UniswapV2Pair::mint(在新标签页中打开)↗ ),// 但核心合约只检查它自己没有被欺骗,因此如果汇率在您提交交易和执行交易之间发生变化,// 您将面临损失价值的风险。如果你使用外围合约,它会计算出你应该存入的金额并立即存入,// 因此汇率不会改变,你也不会损失任何东西。// ***********************function _addLiquidity(address tokenA,address tokenB,uint amountADesired, // 希望存入的最大数量 uint amountBDesired,uint amountAMin, // 希望存入的最小数量uint amountBMin) internal virtual returns (uint amountA, uint amountB) { //流动性提供者通常指定一个最低值,因为他们希望将交易限制在接近当前汇率的汇率。// create the pair if it doesn't exist yetif (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) { // 如果合约不存在就创建个新合约IUniswapV2Factory(factory).createPair(tokenA, tokenB);}(uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);if (reserveA == 0 && reserveB == 0) {(amountA, amountB) = (amountADesired, amountBDesired); // 如果合约还没创建,则多少都可以存,但具体但看初始化函数 因为过低的话可能会回滚} else {uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); // 以我存的amountADesire,计算需要我存入对应多少的amountBif (amountBOptimal <= amountBDesired) { // 如果比我预期的少,那么一定要比我预期最少的多require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');(amountA, amountB) = (amountADesired, amountBOptimal);} else { // 如果我需要存入的amountB超过我的预期,那么我就换一种算法 算假如要存入一定数量的amountBDesir,则需要存入的amountA数量,uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);assert(amountAOptimal <= amountADesired);require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');(amountA, amountB) = (amountAOptimal, amountBDesired); // 返回实际的tokenA/tokenB存储数量}}}function addLiquidity(address tokenA,address tokenB,uint amountADesired,uint amountBDesired,uint amountAMin,uint amountBMin,address to,uint deadline) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); // 获得实际的存储tokenA/tokenB量address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); // 通过使用库函数pairFOr获得该代币对的合约地址,这样会比询问Facoty更省gasTransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);liquidity = IUniswapV2Pair(pair).mint(to); // 返回存入的amountA、amountB、铸造的流动性代币数 }function addLiquidityETH( // 添加ETH/token代币对的流动性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}(); // ETH已经转入本合约了,再通过本合约转给WETH合约,转换成WETHassert(IWETH(WETH).transfer(pair, amountETH));liquidity = IUniswapV2Pair(pair).mint(to);// refund dust eth, if anyif (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); // 如果转入的ETH多了,退回去}// **** REMOVE LIQUIDITY ****function removeLiquidity(address tokenA,address tokenB,uint liquidity,uint amountAMin,uint amountBMin,address to,uint deadline) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // 调用V2Pair下的ERC20合约的transfeerFrom函数转移UNI2代币 *注意此处是transferFrom函数,本合约使用资金需要获得msg.sender的approve 之后将UNI2转到本合约(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);// 将UNI2转入该合约,该合约将UNI2销毁并计算即将换出多少amount0、1,并将对应的token转到对应账户上(address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB); // 当一个函数返回多个值,但我们只对其中的一些感兴趣时,这就是我们只获得那些值的方式。从gas的角度来说,这比读取一个值并且从不使用它要便宜一些。(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');// 最后返回能获得多少amountA,amountB}function removeLiquidityETH(address token,uint liquidity,uint amountTokenMin,uint amountETHMin,address to,uint deadline) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) { // 流程:UNI2->本合约->销毁UNi2->将token,ETH转到本合约->token、ETH转给用户(amountToken, amountETH) = removeLiquidity(token,WETH,liquidity,amountTokenMin,amountETHMin,address(this), // 注意此函数的to是address(this),而不是直接返还用户,如果to是用户的话则直接转给用户deadline); // 同理,此处也需要获得用户approve才有使用权限 TransferHelper.safeTransfer(token, to, amountToken);IWETH(WETH).withdraw(amountETH); // 流动性池是使用WETH,用户存入的是ETH,合约将其换成了WETH在存进流动性池。因此此处是将WETH换回ETH再给用户 TransferHelper.safeTransferETH(to, amountETH); //转给to}// 这些函数通过中继元事务,允许没有以太币的用户使用许可机制从池中退出。(TODO:没有ETH支付gas费?)// 以太坊上的交易需要以太币(ETH),这相当于真实的货币。如果你有ERC-20代币但没有ETH,你不能发送交易,所以你不能用它们做任何事情// 因此permit消息允许其他ETh持有者帮忙发送此次流动性移除交易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;// V2Pair下的ERC20合约中实现了permit()函数,验证此approve消息是否真实。可查看[core-ERC20部分](https://blog.csdn.net/weixin_43380357/article/details/129779650?spm=1001.2014.3001.5501)IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);  // TODO:这一套下来跟直接调用removeLiquidity有什么不一样吗?// perimit结束意味着msg.sender给本合约进行了授权,本合约可以支配其资金 即将①msg.sender给合约权限 ②合约转账两笔交易二合一了;否则要先执行 ①approve ②removeLiquidity(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) ————支持转账(收费ETH)的代币****function removeLiquidityETHSupportingFeeOnTransferTokens(address token,uint liquidity,uint amountTokenMin,uint amountETHMin,address to,uint deadline) public virtual override ensure(deadline) returns (uint amountETH) { // 先写此函数是为了“解耦合”,此函数使用了removeLiquidity,使用了transferFrom,// 因此也需要进行approve授权,为此下面也写了一个带permit的二合一函数————removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(, 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// 在进行_swap,swap之前已经把token转进目标代币对合约中了// 在swap函数中通过读取实际余额与缓存余额的差来计算实际存入了多少function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual { // amounts[]代表amountIn,amountOut1,amountOut2...,path代表token0,token1,token2..的地址,_to是最终收款人for (uint i; i < path.length - 1; i++) { // 都是从0开始计数,自己想(address input, address output) = (path[i], path[i + 1]);(address token0,) = UniswapV2Library.sortTokens(input, output); // 排个序 好方便计算 地址较小的排在前面uint amountOut = amounts[i + 1]; // 此时amount[i]代表要进行交换的币种,amount[i+1]代表要换回的币种(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); // 既然一种是换出去的,那么该币种自然amountOut=unint(0),而换回来的币种的amount_Out=amountOutaddress to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; // 其中output=path[i+1],to就是换出的token要去的地址// 此处已经将token0转入了,需要进行token1/token2的转换。这里就不再将token1转回msg.sender合约或者是转给用户了// 而是在这里进行第二次的代币转换,直接将token1转给token1/token2代币对合约。因为在swap前需要将token转入目的合约,否则swap将执行失败。// 看下面的函数,IUniswapV2Pair(token0/token1代币对地址).swap(amount0Out, amount1Out, token1/token2代币对地址(或者是最终的用户地址), new bytes(0))IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap( // 计算出对应代币地址,开始swapamount0Out, amount1Out, to, new bytes(0));}}function swapExactTokensForTokens(uint amountIn, // 输入即将swap的token0数量uint amountOutMin, // 期望得到的最少token1数量address[] calldata path, // 可能经过的所有地址,TODO:如果token0到token3要用到0,1,2,3种代币,则path代表他们各自的token地址address to,uint deadline) external virtual override ensure(deadline) returns (uint[] memory amounts) {amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); // amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);// 计算amountIn应该能换出多少amountOut并存到amounts[]中,其中amounts[0] = amountIn;// 由于交易串行执行,因此在本交易执行的时候,所有数据,包括瞬时汇率都是确切的,因此最终会换回多少token在一笔交易中都可以精确算出来的require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); // 要求最终能换出的amouts要比用户最小预期的多TransferHelper.safeTransferFrom(// 将amount0代币转到token0/token1地址中 *注意:此处是transferFrom,因此需要allowance[owner][router02]额度path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]); _swap(amounts, path, to);}function swapTokensForExactTokens(uint amountOut, // 输入期望换回的token1数量uint amountInMax, // 可接受的最大token0成本address[] calldata path,address to,uint deadline) external virtual override ensure(deadline) returns (uint[] memory amounts) {amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); // 当一切都被计算出来后,amouts就代表着[0]:应该付出的成本 [1]:换回token1数量[2]...[3]...[4]:最终能换回来的换回token4数量,由于amountOut已经确认了,因此[4]=amountOutrequire(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);}// 确切的ETH能换出多少token 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]}(); // 将用户msg.sender存进来的ETH拿去换到WETHassert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0])); // 将WETH存到WETH/token合约中_swap(amounts, path, to);}// 以下是相同的套路function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)externalvirtualoverrideensure(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)externalvirtualoverrideensure(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)externalvirtualoverridepayableensure(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 anyif (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); // 如果ETH多了就退回给用户}// **** SWAP (supporting fee-on-transfer tokens————代币transfer也要收手续费的token) ****// requires the initial amount to have already been sent to the first pairfunction _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual { // 因为每一个可能都要收手续费,所以没法一下子给出所有amount[],需要一个个去计算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// 获取两个token地址的reserve存量(uint reserve0, uint reserve1,) = pair.getReserves();(uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);// TODO:暂时还没研究出来下面这行与 swapExactTokensForTokens 函数的 amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); 的区别在哪// 通过“ERC20合约记录的实时balance余额”与“pair合约缓存的reserve”的差值计算amountInputamountInput = 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(// 通过收款前后的差值计算实际到账金额,确保不比用户预设最低金额amountOutMin低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)externalvirtualoverridepayableensure(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)externalvirtualoverrideensure(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)publicpurevirtualoverridereturns (uint amountOut){return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);}function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)publicpurevirtualoverridereturns (uint amountIn){return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);}function getAmountsOut(uint amountIn, address[] memory path)publicviewvirtualoverridereturns (uint[] memory amounts){return UniswapV2Library.getAmountsOut(factory, amountIn, path);}function getAmountsIn(uint amountOut, address[] memory path)publicviewvirtualoverridereturns (uint[] memory amounts){return UniswapV2Library.getAmountsIn(factory, amountOut, path);}
}

其他推荐参考文献:https://ethereum.org/zh-tw/developers/tutorials/uniswap-v2-annotated-code/#periphery-contracts

Uniswap V2-periphery 智能合约代码“行级”解析相关推荐

  1. Uniswap V2-Core 部分智能合约代码解析

    通过阅读代码导入的包,我决定将代码的阅读顺序定为:ERC20,Pair,Factory ERC20.sol pragma solidity=0.5.16;import './interfaces/IU ...

  2. usdt智能合约代码

    以太链usdt智能合约代码 /***Submitted for verification at Etherscan.io on 2017-11-28 */pragma solidity ^0.4.17 ...

  3. 【Solidity】零基础入门Solidity编写智能合约代码

    如果你有其他语言基础,可以很快入门,如果新手建议了解即可,以下是课程目录: 1.智能合约概述 2.区块链基础 3.以太坊虚拟机 4.安装Solidity编译器 5.从源代码编译 6.CMake参数 7 ...

  4. bsc heco eth浏览器开源智能合约代码,图文说明

    文章目录 配置 基本配置 其他配置 library 构造参数 方式一 方式二 开源流程示例 情况1 基本配置都正确,没有构造参数和library 情况2 补上library,不填构造参数 情况3 填写 ...

  5. solidity采坑日记之智能合约返回事件内容解析

    最近在用java调用solidity智能合约方法的时候,遇到了方法Log解析的问题. 正常在调用合约后,如果该合约有event时间,那么执行结果会返回对应的log日志,但是返回的log日志是0x开头的 ...

  6. 以太坊智能合约代码查看

    https://www.stateofthedapps.com/rankings 这是dapp排行榜,选择想看的应用后,下面有合约地址,在打开的etherscan页面可以看code标签页,里面有验证好 ...

  7. 亿图图示组件简介和箱子计算面积及代码行级优化

    class Box陈元才 # 构造器方法 def initialize(宽,高) @宽度, @高度 = 宽, 高 end # 实例方法默认是 public 的def 计算面积获取宽度() * 获取高度 ...

  8. 机器学习随机森林实战:声纳信号分类(附python代码保姆级解析)

    随机森林是一种很常用的机器学习算法,"随机"表示每棵树的训练样本随机以及训练时的特征随机.         训练形成的多棵决策树形成了"森林",计算时我们把每棵 ...

  9. 一文读懂Uniswap V2的改进与创新

    Uniswap V2是链上交易所的下一个迭代产品. Uniswap是Ethereum区块链上的一个链上流动性协议,它可以实现无信任的代币交换,这意味着所有的交易都是由智能合约执行的,而不需要中介或受信 ...

最新文章

  1. 扩展typeof来判断js变量的类型
  2. python量化投资必背代码-重磅!我把自己耗费两年用Python写的量化投资代码开源了!...
  3. 提交优化Oracle Tuning Log File Sync 等待事件的几种策略
  4. oracle忘记实例名,Oracle的安装和MS SQL Server实例名
  5. 关于前台调用后台事件__doPostBack函数
  6. 苏州飘“彩云” 五年规模破百亿元
  7. shell脚本启动kafka集群的多台节点
  8. 牛客16438 回文日期
  9. kafka 单机配置
  10. ajax响应码,ajax处理响应(三)(示例代码)
  11. 【代码笔记】iOS-使用MD5加密
  12. qt电容触摸屏实现长按_电阻屏和电容屏有什么区别?究竟谁更胜一筹?
  13. 数学建模预测模型实例(二)---表白墙影响力量化模型
  14. retrofit应用详解与源码解析--源码解析
  15. 学习总结《反本能---如何对抗你的习以为常》
  16. 微信小程序 实现美团外卖 菜单分类,左右联动 效果
  17. 产业分析:股权激励市场实践
  18. 基于嘉立创双层板E5071C的低成本TRL校准
  19. 记录一下StamPS+SBAS的过程
  20. pytorch 中 expand ()函数

热门文章

  1. 2016年计算机基础知识试题及答案,2016年计算机二级基础知识试题及答案
  2. Bankless:Maker DAO的生存危机
  3. Selenium WebDriver简介
  4. PHP-RSA公钥加密
  5. CF821 B. Okabe and Banana Trees 简单数学
  6. 遇见未知的自己(一)
  7. linux vim latex,vim使用latex(vimtex+vim-live-preview)
  8. 【蓝桥杯选拔赛真题38】python目标值判断 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析
  9. 大学英语拓展课程系列计算机英语课后答案,《科技英语》课后习题答案完整版.doc...
  10. Laravel5使用qq邮箱smtp发邮件配置及测试