To see the contract that uses CREATE2, jump to Step 2.
The new opcode
CREATE2
was added to the Ethereum virtual machine nearly a year ago — at the end of February 2019. This opcode introduced a second method of calculating the address of a new smart contract (previously only CREATE
was available). Using CREATE2
is certainly more complex than the original CREATE
. You can no longer just write new Token()
in Solidity, and instead must resort to writing in assembly code.However
CREATE2
has an important property that makes it preferable in certain situations: it doesn’t rely on the current state of the deploying address. This means you can be sure the contract address calculated today would be the same as the address calculated 1 year from now. This is important is because you can interact with the address, and send it ETH, before the smart contract has been deployed to it.So, with few practical walk-throughs available online, I decided to create this simple explanatory blog to explain:CREATE
and CREATE2
each workCREATE2
in your smart contract, andnonce
keccak256(rlp.encode(deployingAddress, nonce))[12:]
keccak256(0xff ++ deployingAddr ++ salt ++ keccak256(bytecode))[12:]
For my worked example using
CREATE2
, I'm going to solve the . To complete the task defined on the challenge page, you need to create a contract that has 2 properties:name()
function that returns bytes32("smarx")
badc0de
somewhere in its address.To succeed in this challenge using the
CREATE
opcode we’d need to generate many private keys. For each of these we would calculate the corresponding Ethereum address, use a nonce of 0
to calculate the resulting contract address.Considering Capture the Ether was created in 2018,
CREATE2
was certainly not the intended solution for the problem — but I think it sounds like the nicer option.To use
CREATE2
to find an address containing badc0de
we need:CREATE2
)pragma solidity ^0.5.12;
contract BadCodeSmarx is IName {
function callAuthenticate(address _challenge) public {
FuzzyIdentityChallenge(_challenge).authenticate();
}
function name() external view returns (bytes32) {
return bytes32("smarx");
}
}
Running a quick
compile
, the bytecode can then be found inside /build/BadCodeSmarx.json
:"bytecode": "0x6080604052348000080fd5b5062..."
Now we can define a simple contract that is provided a salt, and uses
CREATE2
to deploy this bytecode:contract Deployer {
bytes contractBytecode = hex"6080604052348000080fd5b5061015d8066000f3fe6080604052348000080fd5b506004360003560e01c806306fdde031461003b5780637872ab49b600080fd5b61004361009d565b604052005180910390f35b61009b6004803603602081101561006f57600080fd5b873ffffffffffffffffffffffffffffffffffffffff092900c5565b005b60007f736d6000000000000000000000000000000000000000000000000905090565b8073ffffffffffffffffffffffffffffffffffffffff1663380c7a676040518163ffffffff1660e01b800604050087803bd57600080fd5b505af21573d6000803e3d6000fd5b505050505056fea265627a7a72315820fb2fc7a07f0eebf799c680bb1526641d2d905c19393adf340a04e48c9b527de964736f6c634300050c0032";
function deploy(bytes32 salt) public {
bytes memory bytecode = contractBytecode;
address addr;
assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
}
}
msg.value
. This is 0 for this example.So now this contract is ready to go. I deployed it on Ropsten testnet at: . Now that we know the address that will be deploying our
BadCodeSmarx
contract, and we have the bytecode, all we need to do is calculate a salt that will result in address containing badc0de
.To find a salt that will result in an address containing
badc0de
, we need a simple script to loop through each salt one by one, and calculate the address it would obtain.So as to ensure the script was calculating the resulting address correctly, I deployed a contract using salt
0x00...001
. I then used that contract address to ensure my script was correctly formatting and hashing parameters — and therefore producing the same address as CREATE2
does onchain.As a reminder — the formula for address creation is as follows, where
[12:]
means the first 12 bytes are removed to find the address.keccak256(0xff ++ deployingAddr ++ salt ++ keccak256(bytecode))[12:]
const eth = require('ethereumjs-util')
// 0xff ++ deployingAddress is fixed:
var string1 = '0xffca4dfd86a86c48c5d9c228bedbeb7f218a29c94b'
// Hash of the bytecode is fixed. Calculated with eth.keccak256():
var string2 = '4670da3f633e838c2746ca61c370ba3dbd257b86b28b78449f4185480e2aba51'
// In each loop, i is the value of the salt we are checking
for (var i = 0; i < 72057594037927936; i++) {
// 1. Convert i to hex, and it pad to 32 bytes:
var saltToBytes = i.toString(16).padStart(64, '0')
// 2. Concatenate this between the other 2 strings
var concatString = string1.concat(saltToBytes).concat(string2)
// 3. Hash the resulting string
var hashed = eth.bufferToHex(eth.keccak256(concatString))
// 4. Remove leading 0x and 12 bytes
// 5. Check if the result contains badc0de
if (hashed.substr(26).includes('badc0de')) {
console.log(saltToBytes)
break
}
}
0x00000000000000000000000000000000000000000000000000000000005b2bfe
Then all I had to do was execute
Deployer.deploy(0x00...005b2bfe)
. Lo and behold an instance of BadCodeSmarx
was deployed at:0xa905a3922a4ebfbc7d257cecdb1df04a3badc0de