使用new在工厂合约部署合约
使用new在工厂合约部署合约,新的合约地址是通过工厂合约的地址和工厂合约对外发出交易的nonce值计算出来的。
new的用法很简单,就是new一个合约,并传入新合约构造函数所需的参数。其中Contract是要创建的合约名,x是合约地址,如果构造函数是payable,可以创建时转入_value数量的ETH,params是新合约构造函数的参数,规则如下:
Contract x = new Contract{value: _value}(params)
示例:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
contract Test {
address public owner;
uint public x;
constructor(uint _x) {
owner = msg.sender;
x = _x;
}
}
// 工厂合约:创建Test合约
contract TestFactory {
Test public addr; // 部署的合约地址
function create(uint _x) external {
addr= new Test(_x);
}
}
create2部署合约
create2方法是使用工厂合约的地址加盐值去计算新合约的地址,所以新合约的地址在被部署前就可以计算出来。
create2方法同样使用new来部署合约,加一个salt就可以使用create2来部署,规则如下:
Contract x = new Contract{salt: salt}(params)
部署函数如下:
event Deploy(address addr);
function deploy(uint _salt, uint _x) external {
Test _contract = new Test{salt: bytes32(_salt)}(_x);
emit Deploy(address(_contract));
}
预测地址是通过当前工厂合约的地址+salt+被部署合约的bytecode来计算,这就意味着工厂合约地址不变、salt不变、被部署合约不变的情况下,新合约地址也不会变,所以相同的salt只能使用一次,否则会发生重复部署合约的情况。新合约具有自毁功能,就可以使用之前的salt把合约部署到原来的地址上。
示例代码如下:
// 获取被部署合约bytecode,参数_x为被部署合约构造函数的参数
function getBytecode(uint _x) public pure returns(bytes memory) {
bytes memory bytecode = type(Test).creationCode;
return abi.encodePacked(bytecode, abi.encode(_x));
}
// 计算合约地址
function getNewAddr(bytes memory bytecode, uint _salt) public view returns(address) {
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff), // 固定字符串
address(this), // 当前工厂合约地址
_salt, // salt值
keccak256(bytecode) // 被部署合约机器码的hash
)
);
return address(uint160(uint(hash)));
}
Remix验证:计算的地址与实际部署的地址一致
通过内联汇编部署合约
使用内联汇编中的create(v, p, n)方法部署合约,返回地址不为0地址表示成功。
- v:部署合约时发送的主币数量
- p:内存中机器码开始的位置
- n:内存中机器码的大小
event Deploy(address);
// _bytecode:被部署合约机器码
function deploy(bytes memory _bytecode) external payable returns(address addr) {
assembly {
addr := create(callvalue(), add(_bytecode, 0x20), mload(_bytecode))
}
require(addr != address(0), "deploy failed");
emit Deploy(addr);
}