Fun with CREATE2
By: Jaypee Gee
Tags: Ethereum, Solidity, Create2

Co-authored with pldespaigne.

Photo by Pawel Nolbert on Unsplash

Since the Canstantinople release of Ethereum (January 2019) a new instruction (or opcode) named create2 is available in the EVM. This instruction is not meant to replace the former create instruction. Their respective goals and usage are different while both allow the deployment of Smart Contracts on an Ethereum blockchain. What distinguishes them from each other is the particular way in which each one calculates the address of the smart contract before deploying it.

First things first, we need to be clear about few things. Indeed, it is necessary to distinguish DeployCode from RuntimeCode.

DeployCode (sometimes called initCode) is bytecode obtained after compilation of the source code (ex: Solidity) and then before deployment, including constructor’s code and also constructor’s parameter(s).

RuntimeCode is bytecode actually stored in the blockchain after deployment, without the constructor’s code and once the constructor’s code has been executed.

NB : Remix uses the term Bytecode as a synonym for DeployCode and Runtime Bytecode for the aforementioned RuntimeCode.

Now we can focus on the create and create2 instructions.

The create instruction requires as input the DeployCode of the smart contract to actually deploy it. create then returns the smart contract addresses computed thanks to the deployer's address issuer (which can be an EOA or a Smart Contract address) and its nonce (incremented at each transaction).

The create2 instruction in addition to the smart contract DeployCode as input parameter, requires also a salt (a 256 bits value). The returned address is computed thanks to the current address, the salt and the hash of the DeployCode, and that's it! In short, nothing we can't control. Consequently, it means that a smart contract address can be pre-computed without the inconvenience of the nonce changing at each transaction.

Here is the official definition of the create2 instruction (feel free to skip it), but for a detailed description you can consult the EIP-1014 submitted by Vitalik Buterin himself.

create2(v, p, n, s) create new contract with code mem[p…(p+n)) at address keccak256(0xff . this . s . keccak256(mem[p…(p+n))) and send v wei and return the new address, where 0xff is a 8 byte value, this is the current contract's address as a 20 byte value and s is a big-endian 256-bit value.

Pre-compute the contract address

Let’s have some fun now ! Find below a purposely simple smart contract we called Zombie.sol. This contract comes with a killme() function used to destroy the contract to show later that we can redeploy this same contract at the very same exact address without difficulties.

Zombie.sol

pragma solidity ^0.5.8; contract Zombie {

uint256 public value;



constructor() public {

value = 1;

}

function setValue(uint256 _value) public {

value = _value;

}

function killme() public {

selfdestruct(msg.sender);

}

}

In order to pre-compute the address and later deploy this contract with create2 we will use a "Factory" contract. You can find one on Goerli at create2.eth (0xebe6...11f7) address. Find below, the source code of the two functions we are going to use, namely computeAddress() and deploy() .

Factory.sol



// based on contract written by @ricmoo // create2 playground by @pldespaigne // based on contract written by @miguelmota pragma solidity >0.4.99 <0.6.0; contract Factory {

event Deployed(address addr, uint256 salt);



function computeAddress(bytes memory code, uint256 salt) public view returns(address) {

uint8 prefix = 0xff;

bytes32 initCodeHash = keccak256(abi.encodePacked(code));

bytes32 hash = keccak256(abi.encodePacked(prefix, address(this), salt, initCodeHash));

return address(uint160(uint256(hash)));

}



function deploy(bytes memory code, uint256 salt) public returns(address) {

address addr;

assembly {

addr := create2(0, add(code, 0x20), mload(code), salt)

if iszero(extcodesize(addr)) {

revert(0, 0)

}

}

emit Deployed(addr, salt);

return addr;

}

}

For now, we will not explain in detail this source code above.

The computeAddress() function requires the contract's DeployCode, so, our first step is to obtain this DeployCode from the Zombie.sol contract. We can use Remix to achieve this. Cut'n paste Zombie.sol source code in the Remix editor, select the right compiler version (0.5.8), launch compilation and copy the produced DeployCode.

Paste this hexadecimal string starting with 608060405234… in the computeAddress() function of the “Factory” available online here. Do not forget to (1) prefix the string with 0x, and (2) remove the double quotes.

Alternatively, you can cut’n paste the string below if you don’t want to mess with Remix.

Zombie DeployCode

0x608060405234801561001057600080fd5b50600160008190555061011e806100286000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c806324d97a4a1460415780633fa4f24514604957806355241077146065575b600080fd5b60476090565b005b604f60a9565b6040518082815260200191505060405180910390f35b608e60048036036020811015607957600080fd5b810190808035906020019092919050505060af565b005b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60005481565b806000819055507f4eb405514a39e0e076ebb055cbd8c59320e434aa390808e13d45e7f0b61a64506000546040518082815260200191505060405180910390a15056fea165627a7a72305820cde26bd9373cc77db51e3cf3916dfd9fc8a735009013bb5acdac1068f6f4cf930029

Below is a snapshot of etherscan.io used here to simplify the interaction with the “Factory” contract. We choose 4242 as salt value.

Click the Query button. We obtain an address. If we actually deploy (with the create2 instruction) the Zombie contract, it will be deployed for sure at the said address 0x48f94bc7f6ee9d434630767f8e635438b0672457 . Let’s check this !

Deploy the contract at the pre-computed address

Once again we will use the “Factory” contract to simplify the deployment procedure by using its deploy() function.

Because we are now going to change the state of the (Goerli) blockchain, we need some funds and a wallet. We assume in this article that readers know how to use Metamask and obtain some (Goerli) Ether from the faucet (https://goerli-faucet.slock.it).

Cut’n paste the same bytecode as above (prefixed by 0x and without double quotes) in the code field and set the salt value with the same value chosen before (i.e. 4242).

Click the Write button and, this time, accept to pay a fee to actually deploy the Zombie contract (do not forget to connect etherscan.io with Metamask).

We will now use Remix to interact with the Zombie contract. If not yet done, you will have to paste the Zombie.sol source code, compile it and select Injected Web3 at the top left of the Remix interface.

Set the pre-computed address ( 0x48f94bc7f6ee9d434630767f8e635438b0672457 ) in the At Address field. Immediately after you should be able to interact with the Zombie contract. Feel free to change the value with setValue() function. By default, the constructor of the Smart Contract set value to 1.

Now destroy the contract by calling selfdestruct instruction via the killme() function, and ask again for the value. It should be reset to zero. The contract has gone !