Guide to using Create2.sol library in OpenZeppelin Contracts 2.5 to deploy a vault contract

Create2.sol

Create2.sol is a simple library for using the CREATE2 opcode, allowing for deployment and pre-computation of addresses when using it.

To learn more about all the cool things you can do with it, head to Getting the Most out of CREATE2

In this guide we will precompute the address where a contract will be deployed and send Ether to it. Then, we’ll deploy a contract to that same address, and use it to retrieve the funds previously sent there. (This is based on the guide Deploying Smart Contracts Using CREATE2 and the OpenZeppelin CLI )

Thanks to @k06a for requesting an example.

CREATE2

The whole idea behind this opcode is to make the resulting address independent of future events. Regardless of what may happen on the blockchain, it will always be possible to deploy the contract at the precomputed address.

New addresses are a function of:

  • 0xFF, a constant that prevents collisions with CREATE
  • The sender’s own address
  • A salt (an arbitrary value provided by the sender)
  • The to-be-deployed contract’s bytecode
new_address = hash(0xFF, sender, salt, bytecode)

CREATE2 guarantees that if sender ever deploys bytecode using CREATE2 and the provided salt, it will be stored in new_address.

Because bytecode is included in this computation other agents can rely on the fact that, if a contract is ever deployed to new_address, it will be one they know about. This is the key concept behind counterfactual deployments.

Deploy VaultFactory

We will use Remix to deploy and interacts with our contracts. (OpenZeppelin CLI 2.8 is planned to support deployment of regular contracts)

We will compute the address where Vault will be deployed, and send Ether there. Then, we will deploy Vault using VaultFactory which uses Create2.sol and finally call the withdraw method, retrieving the funds that were sent to it before deployment.

First create the Vault.sol and VaultFactory.sol contracts in Remix.

Next deploy VaultFactory.sol to a test network. (Either a VM or a public testnet, for this guide I will use public testnet Rinkeby)

My VaultFactory contract on Rinkeby:
https://rinkeby.etherscan.io/address/0xfea3f16aecd00a080403149fc5dd804494255657#code

Vault.sol

pragma solidity ^0.5.0;

import "https://github.com/OpenZeppelin/openzeppelin-sdk/blob/v2.7.1/packages/lib/contracts/Initializable.sol";

contract Vault is Initializable {
    address payable public owner;

    function initialize(address payable _owner) initializer public {
        owner = _owner;
    }

    function withdraw() public {
        require(owner == msg.sender);
        owner.transfer(address(this).balance);
    }
}

VaultFactory.sol

pragma solidity ^0.5.0;

import "./Vault.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/utils/Create2.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/utils/Address.sol";

contract VaultFactory {
    event VaultCreated(address vault);
    
    function deployVault(bytes32 salt, address payable owner) public {
        address vaultAddress;

        vaultAddress = Create2.deploy(salt, type(Vault).creationCode);
        Vault(vaultAddress).initialize(owner);
        
        emit VaultCreated(vaultAddress);
    }

    function computeAddress(bytes32 salt) public view returns (address) {
        return Create2.computeAddress(salt, type(Vault).creationCode);
    }
    
    function sendValue(bytes32 salt) external payable {
        address vaultAddress;
        
        vaultAddress = Create2.computeAddress(salt, type(Vault).creationCode);
        
        Address.sendValue(Address.toPayable(vaultAddress), msg.value); 
    }
}

Computing the Deployment Address

With VaultFactory deployed, we can get the factory to compute the address where our Vault contracts will be deployed using an arbitrary salt by calling computeAddress with a salt such as 0x0000000000000000000000000000000000000000000000000000000000000001

Which for my deployed VaultFactory gives an address of 0x262245f12519e61278A2d013721A6310661B6Cad

Interacting With the Counterfactual Contract

Under normal circumstances, sending funds to a random Ethereum address is a bad idea. Here however, we know we’ll be able to deploy Vault at the computed address and retrieve our funds. So let’s do it!

Send some (test) Ether to the address that we got from computeAddress.

I am going to use sendValue function on VaultFactory (mainly because I couldn’t see an easy way to send value to an address using Remix when using a VM).

In the transaction we can see that the address had 0.1 Ether sent to it.
https://rinkeby.etherscan.io/tx/0x01f28c4ca280b239ad2e923752142c2d71abaa2555f6468e8b5a8b21f420a3d6#statechange

Because the address has no bytecode and we don’t have its private keys, we cannot do much with it other than checking the funds are indeed there.

Next up is to get the funds out of the vault.

Withdrawing From Our Vault

First we need to deploy our Vault.

Use Remix to call deployVault on VaultFactory using the same salt as before and the owner of the Vault who can withdraw funds.

The transaction deploying our Vault:
https://rinkeby.etherscan.io/tx/0x14c7be217ab27a0adb4ef0263d6271da7c351a8f2275e4a133a35fd912a5ec95

If all went well, we should now be able to withdraw from our Vault.
Using Remix, show our Vault contract using atAddress so that we can interact with it.

Then call withdraw which will send the funds to the owner of the Vault

The withdraw transaction showing the change in Ether:
https://rinkeby.etherscan.io/tx/0x5919cef5a5a5e4a8932d20ac06d6ae2148ede459650492f91a1f8ff8965a0d46#statechange

Success! Just to be sure, let’s verify the Vault is indeed empty:
https://rinkeby.etherscan.io/address/0x262245f12519e61278A2d013721A6310661B6Cad

We’ve sent funds to an address we precomputed, knowing we’d be later able to deploy a contract there and retrieve them.

5 Likes

If you have feedback on the Create2.sol library, you can add to it here:

Consider updating the guide to mention using a pre-deployed at a deterministic address library like https://github.com/Zoltu/deterministic-deployment-proxy (or one of your own design).

The nice thing about using something like that is that you can deploy a contract to the same address on every network including all test networks, all private networks, etc. You can even deploy your vault factory with deterministic deployment proxy so that your vaults are all at deterministic addresses.

2 Likes

Hi @MicahZoltu,

That sounds great.
I have changed the post to a wiki so that anyone can edit and improve.

Now I just need to get my head around how to use or create a deterministic address library.

Hi. Is there any updated guide on this? The solidity version is very old (0.5) , initializable contract isn't in the same location on the latest repo, and Create2.computeAddress now seems internal so I can't call the function.