Create2的功能

creat和create2的区别

使用Create在创建合约时,将根据创建合约的地址(通常为部署的钱包地址或者合约地址)和每次创建合约交易时的计数器(nonce)来计算合约的地址。nonce为(该地址发送交易的总数,对于合约账户每创建一个合约nonce+1)的哈希。
如果你指定了一个可选的 salt(一个bytes32值),那么合约创建将使用Create2的机制来生成新合约的地址。

create2功能说明

CREATE2的用法和之前讲的Create类似,同样是new一个合约,并传入新合约构造函数所需的参数,只不过要多传一个salt参数:
Contract x = new Contract{salt: _salt, value: _value}(params)
其中Contract是要创建的合约名,x是合约对象(地址),_salt是指定的盐;如果构造函数是payable,可以创建时转入_value数量的ETH,params是新合约构造函数的参数。
salt 常用的计算方式
1.使用user_id的哈希
2.使用部署钱包地址或者调用合约地址的哈希
3.在uniswapV2中使用交易对的地址哈希

create2如何计算地址

CREATE2的目的是为了让合约地址独立于未来的事件。不管未来区块链上发生了什么,你都可以把合约部署在事先计算好的地址上。用CREATE2创建的合约地址由4个部分决定:
1.0xFF:一个常数,避免和CREATE冲突
2.创建者地址
3.salt(盐):一个创建者给定的数值
4.待部署合约的字节码(bytecode)
newAddress = hash("0xFF",创建者地址, salt, bytecode)
CREATE2 确保,如果创建者使用 CREATE2 和提供的 salt 部署给定的合约bytecode,它将存储在 新地址 中。

在remix上验证create2

Pair.sol

contract Pair{
    address public factory; // 工厂合约地址
    address public token0; // 代币1
    address public token1; // 代币2

    constructor() payable {
        factory = msg.sender;
    }

    // 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;
    }
}

PairFactory2.sol

import "./Pair.sol";

contract PairFactory2{
        mapping(address => mapping(address => address)) public getPair; // 通过两个代币地址查Pair地址
        address[] public allPairs; // 保存所有Pair地址

        function createPair2(address tokenA, address tokenB) external returns (address pairAddr) {
            // 计算用tokenA和tokenB地址计算salt
            (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); //将tokenA和tokenB按大小排序
            bytes32 salt = keccak256(abi.encodePacked(token0, token1));
            // 用create2部署新合约
            Pair pair = new Pair{salt: salt}(); 
            // 调用新合约的initialize方法
            pair.initialize(tokenA, tokenB);
            // 更新地址map
            pairAddr = address(pair);
            allPairs.push(pairAddr);
            getPair[tokenA][tokenB] = pairAddr;
            getPair[tokenB][tokenA] = pairAddr;
        }

// 提前计算pair合约地址
function calculateAddr(address tokenA, address tokenB) public view returns(address predictedAddress){
    // 计算用tokenA和tokenB地址计算salt
    (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); //将tokenA和tokenB按大小排序
    bytes32 salt = keccak256(abi.encodePacked(token0, token1));
    // 计算合约地址方法 hash()
    predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
        bytes1(0xff),
        address(this),
        salt,
        keccak256(type(Pair).creationCode)
    )))));
}

}

对比计算出的合约地址和create2创建出来的合约地址:
微信图片_20220525010322.png

create2的实际应用场景

1.交易所为新用户预留创建钱包合约地址。
2.由 CREATE2 驱动的 factory 合约,在uniswapV2中交易对的创建是在 Factory中调用create2完成。这样做的好处是: 它可以得到一个确定的pair地址, 使得 Router中就可以通过 tokenA, tokenB 计算出pair地址, 不再需要执行一次 Factory.getPair(tokenA, tokenB) 的跨合约调用。