此篇文章教你如何在部署合约前就可以确定合约地址
一、合约源码
让我们创建一个工厂合约,它包含两个合约。
- 第一个是Demo合约,其中一个函数可以读取全县所有者的钱包地址。
- 第二个合约是工厂合约,它可以在部署Demo合约前获取其合约地址。
这个合约将使用Solidity文档中所说的CREATE2操作码:加“盐”的合约创建 / create2。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ContractDemo {
address public owner;
// Only owners can call transactions marked with this modifier
modifier onlyOwner() {
require(owner == msg.sender, "Caller is not the owner");
_;
}
constructor(address _owner) payable {
owner = _owner;
}
function getOwner() public view returns (address) {
return owner;
}
}
contract Factory {
// Returns the address of the newly deployed contract
function deploy(
uint _salt
) public payable returns (address) {
return address(new ContractDemo{salt: bytes32(_salt)}(msg.sender));
}
// 获取待部署合约字节码
function getBytecode()
public
view
returns (bytes memory)
{
bytes memory bytecode = type(ContractDemo).creationCode;
return abi.encodePacked(bytecode, abi.encode(msg.sender));
}
/** 获取待部署合约地址
params:
_salt: 随机整数,用于预计算地址
*/
function getAddress(uint256 _salt)
public
view
returns (address)
{
// Get a hash concatenating args passed to encodePacked
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff), // 0
address(this), // address of factory contract
_salt, // a random salt
keccak256(getBytecode()) // the wallet contract bytecode
)
);
// Cast last 20 bytes of hash to address
return address(uint160(uint256(hash)));
}
}
二、合约部署
在Remix中选择部署选项,并将要部署的合约切换到 Factory,点击部署:
部署被确认后,选择已部署的合约,我们就可以利用Factory合约获取Demo合约的合约地址了。
getAddress函数返回一个新的Demo实例的预计算的地址。传递一个salt参数,就可返回这个地址。为了简单,我们将使用111作为盐,但它可以是任何uint256值。
让我们把111作为参数传给getAddress函数,并在Remix中执行:
在这个特殊的例子中,预先计算的地址是0x9aBE429F1484622d7511aA9bc2C361EE6967b20b
。
接下来我们部署 Demo 合约,检查它是否正确部署到之前预先计算的地址上。在Remix中,在Factory合约实例中找到Deploy函数,并传递111作为盐。等待交易,并前往区块链浏览器确认它是否正确部署:
在交易细节部分(在区块链浏览器上)选择Internal Txns
标签:
在页面上,我们看到CREATE2函数被我们的工厂合约调用,一个新的Demo合约被创建。点击创建的合约的地址,可以看到与之前预先计算的相同。