UniswapV2核心合约-Factory代码全注释版

  • 时间:
  • 浏览:33
  • 来源:区块链技术网

<h1>代码部分:

pragma solidity =0.5.16;

import './interfaces/IUniswapV2Factory.sol';
import './UniswapV2Pair.sol';

contract UniswapV2Factory is IUniswapV2Factory {
    // 生成router02部署时用到的UniswapV2Libray - paiFor中的init code hash
    // bytes32 public constant INIT_CODE_PAIR_HASH = keccak256(abi.encodePacked(type(UniswapV2Pair).creationCode));

    // 收取手续费的地址
    address public feeTo;
    // 设置feeTo的权限者地址
    address public feeToSetter;
    // tokenA和tokenB的交易对地址存储(tokenA[tokenB] = pair)
    // 获取交易对的pair地址
    mapping(address => mapping(address => address)) public getPair;
    // 用来储存所有的pair
    address[] public allPairs;
    // 定义交易对创建事件,返回参数tokenA地址,tokenB地址,pair地址,allPairs长度(第几个交易对)
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    /**
     * @dev: 构造函数
     * @param {address} _feeToSetter: 手续费控制的权限者地址
     */
    constructor(address _feeToSetter) public {
        feeToSetter = _feeToSetter;
    }

    /**
     * @return 返回所有交易对的长度
     */
    function allPairsLength() external view returns (uint) {
        return allPairs.length;
    }

    /**
     * @dev: 创建tokenA和tokenB的交易对并获得pair地址
     * @param {address} tokenA
     * @param {address} tokenB
     * @return {address} 返回对应的pair地址
     */
    function createPair(address tokenA, address tokenB) external returns (address pair) {
        // 判断tokenA不等于TokenB
        require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
        // 将tokenA和tokenB进行比大小,如果tokenA小于tokenB,则交换给token0和token1
        // 因为地址的底层是uint160,所以有大小排序
        (address token0, address token1) = tokenA &lt; tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        // 判断token0不能为0地址
        // 此时判断0就等于同时判断了0和1,因为tokenA和tokenB已经进行过大小判断,0地址是绝对小于任何地址
        require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
        // 判断token0是否和token1有产生过交易对,如果没配对那么mapping就是0地址,如果非0地址代表已经配对过了
        require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient
        // 表达式type(x)可用于检索参数x的类型信息(x仅能是合约或整型常量)
        // type(x).creationCode 获得包含x的合约的bytecode,是bytes类型(不能在合同本身或继承的合约中使用,因为会引起循环引用)
        bytes memory bytecode = type(UniswapV2Pair).creationCode;
        // 将排序好的token对进行打包后通过keccak256得到hash值
        // 因为两个地址是为确定值,所以salt是可以通过链下计算出来
        bytes32 salt = keccak256(abi.encodePacked(token0, token1));
        // 内联汇编
        assembly {
            /**
             * @dev: create2方法 - 在已知交易对及salt的情况下创建一个新的交易对,返回新的交易对地址(针对此算法可以提前知道交易对的地址)
             * @notice 转注释2
             * @notice create2(V, P, N, S) - V: 发送V数量wei以太,P: 起始内存地址,N: bytecode长度,S: salt
             * @param {uint} 指创建合约后向合约发送x数量wei的以太币
             * @param {bytes} add(bytecode, 32) opcode的add方法,将bytecode偏移后32位字节处,因为前32位字节存的是bytecode长度
             * @param {bytes} mload(bytecode) opcode的方法,获得bytecode长度
             * @param {bytes} salt 盐值
             * @return {address} 返回新的交易对地址
             */
            pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
        // 将新的交易对地址初始化到pair合约中(因为create2函数创建合约时无法提供构造函数参数)
        IUniswapV2Pair(pair).initialize(token0, token1);
        // 将token0和token1的交易对地址设置到mapping中(0和1的双向交易对)
        getPair[token0][token1] = pair;
        getPair[token1][token0] = pair; // 双向交易对
        // 将新的交易对地址添加到allPairs数组中
        allPairs.push(pair);
        // 触发交易对创建事件
        emit PairCreated(token0, token1, pair, allPairs.length);
    }

    /**
     * @dev: 设置团队手续费开关
     * @notice 在uniswapV2中,用户交易代币时,会被收取交易额的千分之三手续费分配给所有流动性提供者.
     * @param {address} 不为零地址,则代表开启手续费开关(手续费中的1/6分给此地址),为零地址则代表关闭手续费开关
     */
    function setFeeTo(address _feeTo) external {
        require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
        feeTo = _feeTo;
    }

    /**
     * @dev: 转让设置feeTo的权限者
     * @notice 转注释1
     */
    function setFeeToSetter(address _feeToSetter) external {
        require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
        feeToSetter = _feeToSetter;
    }
    /**
        @dev: 注释1
        在转让权限时可能会出现一个情况,当管理员进行转让时可能输入错误的地址,正常情况下这种情况发生后将不可更改,并且将丧失永久管理权.
        有一种方法可以解决这类问题,就是使用一个中间地址值过渡一下,而被新设置owner的对象需要再调用一次方法才能完成权限的转移.
        如果原管理员发现转移地址错误了,可在目标错误地址未进行确认时及时更改过来
        address public owner;
        address public newOwner;
        // 进行转移权限到newOwner,如果发现错误可再一次设置
        function transferOwnership(address _newOwner) public onlyOwner {
            newOwner = _newOwner;
        }
        // 被转移权限的需调用此方法来确认接受此权限,此时将完成权限转移的设置
        function acceptOwnership() public {
            require(msg.sender == newOwner,"invalid operation");
            emit OwnershipTransferred(owner, newOwner);
            owner = newOwner;
            newOwner = address(0);
        }
     */

     /** 
        @dev: 注释2
        create2的知识扩展:
            因为以太坊evm中账号的内存管理是每个账号(包含合约)都有一个内存区域,该区域是线性的并且在字节等级上寻址,但是读取限定为256位(32字节)大小,写的时候可以为8位(1字节)或256位(32字节)大小.
            solidity中内联汇编访问本地变量时,如果本地变量为值类型,则直接使用该值;如果本地变量是引用类型(memory或calldata),则会使用memory或calldata中的内存地址,而不是值本身.
            solidity中动态大小的字节数组,是引用类型,类似string也是引用类型.
            所以在create2函数调用时使用了type(x).creationCode 来获得了x合约的bytecode,类型为bytes为引用类型.根据上述的内存读取限制和内联汇编访问本地引用类型的规则,它在内联汇编中的实际值为该字节数组的内存地址.
            因为bytecode开始的32字节存储的是creationCode的长度,从第二个32字节开始才是存的实际creationCode内容,所以create2函数中的第二个参数需要为实际creationCode内容,才进行了add(bytecode, 32)的方式将值偏移到后32字节后.

        create2的solidity方法:
            因为内联汇编的可读性较为差些,所以在solidity的0.6.1以上新增了加盐创建合约的create2方法,该方法直接通过new在合约类型后面加上salt选项来进行自定义加盐的合约创建,等效于内联汇编中的create2函数.
            示例代码:
                // SPDX-License-Identifier: GPL-3.0
                pragma solidity ^0.7.0;

                contract D {
                    uint public x;
                    constructor(uint a) {
                        x = a;
                    }
                }

                contract C {
                    function createDSalted(bytes32 salt, uint arg) public {
                        /// 这个复杂的表达式只是告诉我们,如何预先计算地址。
                        /// 这里仅仅用来说明。
                        /// 实际上,你仅仅需要 ``new D{salt: salt}(arg)``.
                        address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
                            bytes1(0xff),
                            address(this),
                            salt,
                            keccak256(abi.encodePacked(
                                type(D).creationCode,
                                arg
                            ))
                        )))));

                        D d = new D{salt: salt}(arg);
                        require(address(d) == predictedAddress);
                    }
                }
    */
}

猜你喜欢

Uniswap V2 学习笔记2. 交易算法

大家好,今天继续分享UniswapV2的学习心得,今天的内容是Uniswap的交易算法**Uniswap核心思想AB=K**在不考虑手续费的情况下,交易前后两个代币数量乘积不变

2022-07-30

Uniswap V2 源码学习 (三). 手续费和交易池估值

前面我们已经大致了解了uniswap的交易算法,今天我们一起看看Uniswap手续费是怎么计算的可能很多读者认为手续费计算并不重要,因为手续费对于用户而言就是扣掉千分之三的in

2022-07-30

Uniswap V2 源码学习 (四). 签名和路由

UniswapV2Pair的permit函数和签名算法.上次我们在研究router合约的时候,有一个removeLiquidityWithPermit函数,今天讲讲它和Pair

2022-07-30

如何对接 Uniswap V2 兑换代币

原文:https://medium.com/uv-labs/uniswap-testing-1d88ca523bf0译文出自:登链翻译计划译者:翻译小组校对:Tiny熊本文永久

2022-07-30

Uniswap V2 如何部署

最近在研究uniswap v2[2]版本逻辑和代码,接下来我们以一篇uniswap v2版本的部署,开启uniswap[3]的学习之路。

2021-12-13