Solidity CREATE2 创建合约(Uniswap createPair)
这个技术是 Uniswap 创建交易对 和 GasToken(ChiToken)的技术原理。利用 Solidity 的 CREATE2
创建可计算地址的合约,然后利用这个合约的 selfdestruct
来返还一些 做一些需要 固定地址(计算出地址) 的业务逻辑。
之前的版本还需要使用 create2
操作符来创建合约,现在的版本 0.8.12 都不需要了,发出来记录一下,省的后面用的时候又忘记。
做了一个 示例:可以带 ETH 调用 createChild
,来将附带 ETH 存入创建出来的子合约,然后可以调用 claim
方法将存入的 ETH 提取出来。并没有记录所有子合约的合约地址,而是根据计数推算出子合约合约地址进行调用。看下操作记录:
后记: 开始没有将 Child、Parent 合约拆分到两个文件里面,在 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);
}
}