# 合约地址预测

By [Smithereens](https://paragraph.com/@soyoung) · 2022-04-07

---

应用场景：

1.  类似 uniswap 去中心化交易所池子合约计算。Uniswap v2 工厂合约计算 pair 合约地址的利用了该特性。但其中需要注意的点是：当我们使用 type(constract).creationCode 时需要注意合约元数据的变化，编译器版本的不同也有可能影响到字节码的变化，具体文档如下。（自行部署 Uniswap 代码时可能遇到该问题）
    
    [https://learnblockchain.cn/docs/solidity/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode](https://learnblockchain.cn/docs/solidity/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode)
    
    [https://github.com/Uniswap/v2-periphery/blob/master/contracts/libraries/UniswapV2Library.sol](https://github.com/Uniswap/v2-periphery/blob/master/contracts/libraries/UniswapV2Library.sol)
    
    [https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol](https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol)
    
2.  解决交易所充值账号问题
    
    如果为每个用户创建一个地址，则私钥的维护是个问题，如果单独的一个合约地址那必须要先先部署（浪费gas）才能看到地址，所以利用地址预测的方式，需要的时候再创建。
    

示例代码如下：

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    contract Factory {
        // Returns the address of the newly deployed contract
        function deploy(
            address _owner,
            uint _foo,
            bytes32 _salt
        ) public payable returns (address) {
            // This syntax is a newer way to invoke create2 without assembly, you just need to pass salt
            // https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2
            return address(new TestContract{salt: _salt}(_owner, _foo));
        }
    }
    
    // This is the older way of doing it using assembly
    contract FactoryAssembly {
        event Deployed(address addr, uint salt);
    
        // 1. Get bytecode of contract to be deployed
        // NOTE: _owner and _foo are arguments of the TestContract's constructor
        function getBytecode(address _owner, uint _foo) public pure returns (bytes memory) {
            bytes memory bytecode = type(TestContract).creationCode;
    
            return abi.encodePacked(bytecode, abi.encode(_owner, _foo));
        }
    
        // 2. Compute the address of the contract to be deployed
        // NOTE: _salt is a random number used to create an address
        function getAddress(bytes memory bytecode, uint _salt)
            public
            view
            returns (address)
        {
            bytes32 hash = keccak256(
                abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode))
            );
    
            // NOTE: cast last 20 bytes of hash to address
            return address(uint160(uint(hash)));
        }
    
        // 3. Deploy the contract
        // NOTE:
        // Check the event log Deployed which contains the address of the deployed TestContract.
        // The address in the log should equal the address computed from above.
        function deploy(bytes memory bytecode, uint _salt) public payable {
            address addr;
    
            /*
            NOTE: How to call create2
    
            create2(v, p, n, s)
            create new contract with code at memory p to p + n
            and send v wei
            and return the new address
            where new address = first 20 bytes of keccak256(0xff + address(this) + s + keccak256(mem[p…(p+n)))
                  s = big-endian 256-bit value
            */
            assembly {
                addr := create2(
                    callvalue(), // wei sent with current call
                    // Actual code starts after skipping the first 32 bytes
                    add(bytecode, 0x20),
                    mload(bytecode), // Load the size of code contained in the first 32 bytes
                    _salt // Salt from function arguments
                )
    
                if iszero(extcodesize(addr)) {
                    revert(0, 0)
                }
            }
    
            emit Deployed(addr, _salt);
        }
    }
    
    contract TestContract {
        address public owner;
        uint public foo;
    
        constructor(address _owner, uint _foo) payable {
            owner = _owner;
            foo = _foo;
        }
    
        function getBalance() public view returns (uint) {
            return address(this).balance;
        }
    }

---

*Originally published on [Smithereens](https://paragraph.com/@soyoung/3Z9o3yYGBG4DPIpAF4TW)*
