Solidity CREATE2 创建合约(Uniswap createPair)

这个技术是 Uniswap 创建交易对 和 GasToken(ChiToken)的技术原理。利用 Solidity 的 CREATE2 创建可计算地址的合约,然后利用这个合约的 selfdestruct 来返还一些 做一些需要 固定地址(计算出地址) 的业务逻辑。

之前的版本还需要使用 create2 操作符来创建合约,现在的版本 0.8.12 都不需要了,发出来记录一下,省的后面用的时候又忘记。

做了一个 示例:可以带 ETH 调用 createChild,来将附带 ETH 存入创建出来的子合约,然后可以调用 claim 方法将存入的 ETH 提取出来。并没有记录所有子合约的合约地址,而是根据计数推算出子合约合约地址进行调用。看下操作记录:

  1. 存入 100 ETH
  2. 提取前面存入的 100 ETH
  3. 后面进行了 多次存取 来验证技术实现的对不对。

后记: 开始没有将 ChildParent 合约拆分到两个文件里面,在 Remix 里面测试时发现计算出的子合约的合约地址是错误的,结果是因为 Remix 里面复制的 bytecode 是整个文件的 bytecode,而不是单个选中的合约的 bytecode,导致 hash 计算错误出现问题。

Child 子合约

用来储存 ETH 资产

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;

contract Child {
    address public owner;

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

    function destruct(address payable to) public {
        assert(owner == msg.sender);
        selfdestruct(to);
    }
}

Parent 控制器

用来部署子合约,调用销毁子合约、计算子合约合约地址

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;

import "./Child.sol";

contract Parent {
    bytes32 public constant CHILD_BYTECODE_KECCAK32 =
        0x3777cc71780224f9113b98623e11e85919892f48d89f0264112bb5be178f2d2e;

    uint256 public nonce;
    uint256 public claimNonce;

    event ChildCreated(address addr);

    function balance() public view returns (uint256) {
        return nonce - claimNonce;
    }

    function claim() public {
        require(claimNonce < nonce, "no remaining balance");
        Child child = Child(computeChild(++claimNonce));
        child.destruct(payable(msg.sender));
    }

    function createChild() public payable {
        assert(msg.value > 0);
        emit ChildCreated(
            address(
                new Child{
                    salt: keccak256(abi.encode(++nonce)),
                    value: msg.value
                }()
            )
        );
    }

    function computeChild(uint256 _nonce) public view returns (address) {
        return
            address(
                uint160(
                    uint256(
                        keccak256(
                            abi.encodePacked(
                                hex"ff",
                                address(this),
                                keccak256(abi.encode(_nonce)),
                                CHILD_BYTECODE_KECCAK32
                            )
                        )
                    )
                )
            );
    }

    function k3ccak256(bytes calldata data) public pure returns (bytes32) {
        return keccak256(data);
    }
}

Comments