两种方式:CREATE,CREATE2
都要求部署目的地址extcodesize为0
addresses - How is the address of an Ethereum contract computed? - Ethereum Stack Exchange
把 32byte size 改成code长度,然后后面拼接上code就行
|----------------------------size------------------------------|
7F000000000000000000000000000000000000000000000000000000000000000080602a6000396000f3
除了在合约里使用CREATE opcode,这也是EOA创建新合约的方式
地址计算:
def calc_create_addr(sender: str, nonce: int):
import rlp
return Web3.to_checksum_address(
Web3.keccak(rlp.encode([
bytes.fromhex(sender.removeprefix("0x")),
nonce
]))[-20:]
)
contract ContractAddressCalculator {
function calculateAddress(address creator, uint256 nonce) public pure returns (address) {
// 当nonce为0、1或2时,RLP编码后的结果会有所不同
bytes memory data;
if (nonce == 0x00) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x80));
} else if (nonce <= 0x7f) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, uint8(nonce));
} else if (nonce <= 0xff) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x81), uint8(nonce));
} else if (nonce <= 0xffff) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x82), uint16(nonce));
} else if (nonce <= 0xffffff) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x83), uint24(nonce));
} else if (nonce <= 0xffffffff) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x84), uint32(nonce));
}
// ...
bytes32 hash = keccak256(data);
return address(uint160(uint256(hash)));
}
}
sender就是合约创建者(EOA或者合约),nonce就是transaction里那个nonce,对于合约来说,nonce只有在其创建新合约时会递增,且EIP-161开始,合约的nonce初始值将为1。
需要注意的是,contract在selfdestruct后又被重新创建,nonce会被重置为初始值(1),可以利用这个特性在同一个地址上部署不同的合约(与CREATE2相比,需要一个额外的deployer合约,但好处是init_code不需要一样,写起来方便许多)
一个简单的例子,可以传入任意deploy_code部署到同一地址,重新部署前需要将原合约销毁:
contract ReDeployerInstance {
function deploy(bytes memory code) external returns (address) {
uint256 code_length = code.length;
address addr = address(0);
assembly {
addr := create(0, add(code, 0x20), code_length)
}
require(addr != address(0), "create failed");
return addr;
}
// selfdestruct will reset nonce, so CREATE will deploy contract to same address
function kill() external {
selfdestruct(payable(msg.sender));
}
}
contract ReDeployer {
// selfdestruct will delete contract only after the whole transaction suceess
address public deploy_address;
function deploy(bytes memory code) external returns (address) {
ReDeployerInstance deployer = new ReDeployerInstance{salt: "deployer"}();
deploy_address = deployer.deploy(code);
deployer.kill();
return deploy_address;
}
}
具体题目参考:‣