Dark Side of CREATE2 opcode

Jayakumar
Coinmonks
4 min readJun 14, 2022

--

The combo of create2 and self-destruct are deadly and we are gonna explore about this in this article.

Difference between CREATE and CREATE2 opcodes:

CREATE:

  • Hashing the address of the account that created it.
  • Hashing the ‘account nonce’, which is equivalent to the number of transactions completed by the account so far.
new_address = keccak256(sender, nonce);

CREATE2 :

  • 0xFF, a constant.
  • The address of the deployer, so the Smart Contracts address that sends the CREATE2.
  • A random salt.
  • The hashed Bytecode that will be deployed on that particular address.
new_address = keccak256(0xFF, sender, salt, bytecode);

For Greater context about CREATE2 and step by step tutorial,Kindly follow this link .

Let’s discuss about the dark side of CREATE2 when paired with self-destruct opcode.

  1. Can contracts be differentiated on the basis opcodes used during creation (i.e) CREATE or CREATE2 opcode ?
  • Yes, but this is tricked by using create2 opcode which makes create unsafe as well .
  • Example: Contract A creates Contract B with CREATE2, and then Contract B creates Contract C using the standard CREATE opcode. Then if we destroy both Contract B and C, we can then create Contract B and C again using the same addresses as before.
  • In other words, Contract B can be recreated with CREATE2, and Contract C can be recreated with the same addresses since the same ‘account nonce’ can be reutilised.

Code ? → Here we go!!!

// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8.12;contract Target { address public owner;constructor() {
owner = msg.sender;
}
function destroy() public {
selfdestruct(payable(msg.sender));
}

}
contract CreatorContract { event CreatorDeploy (address addr);
address public _targetaddr;
function deployTarget() external {
Target _contract = new Target();
emit CreatorDeploy(address(_contract));
_targetaddr = address (_contract);
}
function destroy() public {
selfdestruct(payable(msg.sender));
}
}contract CreatorFactoryContract { event CreatorFactoryDeploy(address addrofc);
address public _creatorContractaddr;
function deployCreator(uint _salt) external {
CreatorContract _creatorcontract = new CreatorContract{salt:bytes32(_salt)}();
emit CreatorFactoryDeploy(address(_creatorcontract));
_creatorContractaddr = address(_creatorcontract);
}
}

Copy the following to remix and follow the steps below:

  • Deploy CreatorFactoryContract and verify it
  • Deploy CreatorContract using deployCreator function in CreatorFactoryContract and keep a constant salt say 1.
  • Deploy Target contract using deploy Target function in CreatorContract and note down the addresses of the target and CreatorContract.
  • Destroy the Target contract using destroy function in it and subsequently destroy the CreatorContract using destroy function in it.
  • Redeploy both contracts using step 2 and 3.
  • Be surprised by address of both contracts.

Proof on Test-net — Here we go

Images of the contract states throughout the process

And this is just tip of the ice berg of create2 opcode possibility.

Will expand on this further in the next article as this as become quite lengthy.

Next topics : Changing the values of the variables using above methods and touching on metamorphism.

can’t wait till next out ? read here for more content about next topic → link .

Creator Notes : This is my first article so suggestions about doc format and content’s are welcome. Kindly bear with me till I pick the pace in this platform.

Contact me at → Mail. → linkedIn.

Join Coinmonks Telegram Channel and Youtube Channel learn about crypto trading and investing

Also, Read

--

--

Jayakumar
Coinmonks

Blockchain Developer → Solidity || De-Fi || NFT || Audits