“与其感慨路难行,不如马上出发。”
工厂合约

在以太坊上,工厂合约可以用来部署和管理智能合约实例,并跟踪已部署合约的地址和其他相关信息。我们有两种方式实现工厂合约,分别对应 EVM 中有两个操作码:CREATECREATE2。这两种方式计算合约部署地址的方式不同:

  • bytes20(keccak256(senderAddress, nonce)) CREATE 创建的合约地址由 创建者地址创建者Nonce 确定
  • bytes20(keccak256(0xFF, senderAddress, salt, bytecode)) CREATE2 创建的合约地址由 创建者地址salt(可以由构造参数打包编码生成)要部署的智能合约字节码 确定

CREATE2 使得创建合约时有更多的灵活性。比如在创建合约前计算出合约地址,将该地址作为其他合约创建的依赖参数。

contract Product {
    uint32 public id;
    address public owner;

    constructor(uint32 _id) {
        id = _id;
        owner = msg.sender;
    }

    function deleteContract() external {
        require(msg.sender == owner, "You are not the owner");
        selfdestruct(payable(msg.sender));
    }
}

contract Factory {
    address[] public contracts;

    // CREATE 创建合约
    function create(uint32 id) public {
        Product p = new Product(id);
        contracts.push(address(p));
    }

    // CREATE2 创建合约
    function createSalted(uint32 id) public {
        bytes32 salt = keccak256(abi.encodePacked(id));
        Product p = new Product{salt: salt}(id);
        contracts.push(address(p));
    }

    // 获取 CREATE2 创建的合约地址
    function create2Address(uint32 id) public pure returns (address) {
        bytes32 salt = keccak256(abi.encodePacked(id));

        return address(uint160(uint(keccak256(abi.encodePacked(
            bytes1(0xff),
            address(this),
            salt,
            keccak256(abi.encodePacked(
                type(Product).creationCode,
                abi.encode(id)
            ))
        )))))
    }

    // 删除合约
    function deleteContract(uint index) public {
        require(index < contracts.length, "Index out of range");
        Product p = Product(contracts[index]);
        p.deleteContract();
        delete contracts[index];
    }
}

利用 CREATECREATE2,我们可以在销毁合约后,在原地址上部署另一份的不同合约,具体内容可以参考 在同一地址部署不同合约