创建

两种方式:CREATE,CREATE2

都要求部署目的地址extcodesize为0

addresses - How is the address of an Ethereum contract computed? - Ethereum Stack Exchange

Deployer

把 32byte size 改成code长度,然后后面拼接上code就行

  |----------------------------size------------------------------|
7F000000000000000000000000000000000000000000000000000000000000000080602a6000396000f3

CREATE

除了在合约里使用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;
    }
}

具体题目参考:‣