在以太坊上,工厂合约可以用来部署和管理智能合约实例,并跟踪已部署合约的地址和其他相关信息。我们有两种方式实现工厂合约,分别对应 EVM 中有两个操作码:CREATE 和 CREATE2。这两种方式计算合约部署地址的方式不同:
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];
}
}
利用 CREATE 和 CREATE2,我们可以在销毁合约后,在原地址上部署另一份的不同合约,具体内容可以参考 在同一地址部署不同合约