What if smart contracts were mutable?

One of the first things people usually learn when studying smart contracts is that they are immutable if deployed to the Ethereum blockchain. That means that their code (bytecode) will always stay the same unless you destroy the contract with self-destruct.

That actually changed with the Constantinople hardfork, where a new opcode (CREATE2) was added to the Ethereum Virtual Machine allowing contracts’ addresses to be known before they are created. Nowadays one can deploy a contract, destroy it and deploy a new one on that same address, changing the initial code from the smart code!

With that in mind, today we’ll talk about:

  • The main differences between creating contracts with CREATE and CREATE2
  • What problems CREATE2 solves
  • Why CREATE 2 can be awesome

Contract creation (CREATE vs CREATE2)

When creating a smart contract, by default Ethereum Virtual machine will run the opcode CREATE, which computes the new contract’s address deterministically from the address of its creator (sender) and how many transactions the creator has sent (nonce).

That means that it's difficult to reserve(park) an address in advance since every new transaction the sender does increases its nonce. You’d need to make sure that no other contracts are created and transactions are not done so that when you actually want to deploy your contract, you can use the expected nonce.

create(v, p, s): create new contract with code on memory starting at position p and ending at position (p+s) and send v Wei and return the new address. S is the length of the creation bytecode.

Imagine having 2 contracts that depend on one another to execute certain functions:

In order to set the proper permissions, you’d have to create both contracts, get both addresses, and then set them in the opposite contracts. You can optimize that by creating the second contract with the address of the first one already hard coded, but you’d still need to update the first one. Every update you perform after the contract is deployed costs gas (AKA money).

How can we optimize that and reduce costs?

By 2018, Constantinople’s hardfork added CREATE2, which gives users more control over the addresses of new contracts they create. In summary, CREATE2 creates a contract deterministically computed from the address of its creator (sender), a custom salt of 32 bytes, and the contract creation bytecode.

By having control over the salt, one can know where a new contract will be stored before it is even created!

Going back to the contract dependency example, since we can predict both contracts' addresses before they are deployed, now we can create both contracts with their proper permissions set.

create2(v, n, p, s): create new contract with code on memory starting at position p and ending at position (p+s) at address keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v wei and return the new address.

CREATE2 is awesome!

Although using CREATE2 is more “complex” than using CREATE, it comes with several benefits, some are:

  • Facilitates contract dependency
  • Facilitates contract validation and authentication
  • Enables the creation of metamorphic contracts (upgradable contracts)

Facilitates contract dependency

Knowing a contract’s address before it is created facilitates contract dependency. A contract Factory can create contracts with their dependencies already set up!

Facilitates contract validation and authentication

One can use a “logic contract” that validates if a contract has the necessary permissions to perform its actions. That can be done by comparing the sender’s address with the expected address of a contract that was deployed with CREATE2 via a contract Factory.

Also, when creating a contract, one can use the custom salts to determine different contract types such as pools and bridges. Then, a transaction can validate if the sender has the necessary permission and is from the correct contract type.

Enables the creation of metamorphic contracts (upgradable contracts)

One can create a contract A, at address 0xd8b…3fa8, self-destruct that contract and deploy a new contract (B), with a different bytecode(runtime bytecode), to the same address 0xd8b…3fa8 of the first one (A). Both contracts should have been created using CREATE2.

Ending with a Practical Example of CREATE2 saving the day

We all know how painful it can get for new users onboarding into Ethereum and the risks of messing some configurations up when setting up wallets for the first time. By knowing a user’s account address before it is created, one can ask users to transfer their funds from an exchange to that address and handle account creation without the user needing to deal with all of the steps required to set up a wallet.

A gas station relayer can deploy the contract on behalf of the user, registering an ephemeral externally owned account (EOA) to manage it. The reward for the deployment can be paid directly from the transferred funds, or even by the application itself, as part of the customer acquisition cost. Later, the user can gradually add more robust keys to their identity as they engage with the application.

To learn more about this process read Open Zeppelin’s Getting the most out of CREATE2 article.

The purpose of this article is to bring CREATE2’s awesomeness to light! All examples shown were oversimplified in order to facilitate the comprehension of the subjects being talked about. Future articles will dive into code when we talk about creating role access control systems and metamorphic contract factories for different contract types.

Thanks to Leonardo Viana for all the help provided since I started studying smart contracts! Let me know in the comments if I missed something or said something wrong!

References:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store