Counterfactual creation of proxies with CREATE2 in ZeppelinOS

design
proxies

#1

Hey folks! We have been discussing about adding support for CREATE2 for proxies in zos, so you could “park” an address before you actually create the proxy. This is the basis for counterfactual deployment: by knowing where your contract will be deployed, you can start interacting with it (by sending funds, for instance) even before it is actually deployed.

Note that this is a very different thing to supporting proxy-less upgradeability via CREATE2. Here, we are only talking about being able to reserve an address for deploying an upgradeable instance (ie a proxy).

How would this work? We are considering on adding two new options to the zos create command: one for providing a salt, that will be used in computing the deployment address, and the other for querying the address instead of actually creating the contract.

For instance, let’s say you want to use this pattern to deploy a contract Foo. Instead of the regular zos create Foo, you could run:

$ zos create Foo --salt mysupersalt --query
> Contract would be deployed at 0x13ea86b358aa83543783742567c2964283cde77f

Then, when you want to actually deploy the contract, you can remove the query flag to actually run the creation.

$ zos create Foo --salt mysupersalt
> Contract has been deployed at 0x13ea86b358aa83543783742567c2964283cde77f

Of course, different salts yield different deployment addresses:

$ zos create Foo --salt anothersalt
> Contract has been deployed at 0x1c3d2d14cb40ee63ba3c56128946a1bac98344c3

Initialization arguments should not be used when calculating the deployment address - this allows you to decide the arguments only when you want to actually deploy.

$ zos create Foo --salt mysupersalt --args "foo"
> Contract has been deployed at 0x13ea86b358aa83543783742567c2964283cde77f

$ zos create Foo --salt mysupersalt --args "bar"
> Error: contract already deployed at 0x13ea86b358aa83543783742567c2964283cde77f

However, what is used for calculating the deployment address is the sender. This allows you to reserve an address, knowing that no one else (even if they know the salt you will be using) can deploy there.

$ zos create Foo --salt mysupersalt --from 0x94e6b68e770c9de517be57aef6f7653279eb24fa
> Contract has been deployed at 0x13ea86b358aa83543783742567c2964283cde77f

$ zos create Foo --salt mysupersalt --from 0xecf8e68f0d487c866bee8266f580211e176642ea
> Contract has been deployed at 0x64f70539776f08c5ef505254c2426f3e47a5204d

This could be implemented by adding a new method on the ProxyAdmin contract, or by spawning a new Factory contract altogether, that act as a CREATE2 factory for Proxies, where the actual deployment salt is calculated from the salt provided as a parameter and the msg.sender.

We will start working on this soon. Please share your thoughts about this!


February 2019 development update
#2

If:
$ zos create Foo --salt mysupersalt --query > Contract would be deployed at 0x13ea86b358aa83543783742567c2964283cde77f

What would:

$ zos create Foo --salt mysupersalt --query --from 0x94e6b68e770c9de517be57aef6f7653279eb24fa

Do?

And do we have a sample use case of counterfactual creation of proxies in ZeppelinOS?


#3

It would yield a different address, assuming 0x94e6b68e770c9de517be57aef6f7653279eb24fa was not your default address.

We are in talks with @jamesyoung and @chris.whinfrey about using this for deployments of universal login contracts :slight_smile:


#4

This is perfect! Really excited about this! :grinning:

A good example use case is that you could create a contract based account/Universal Login EVM package. New users could get a “reserved address” (the language we used for an address of an undeployed contract) where they can deposit funds. Then, a third party (“relayer”) can deploy the contract for them and receive a gas refund. This creates a single step user experience to onboard new users onto a contract based account.

The alternative three step experience is:

  • obtain ETH held in a key based account
  • deploy contract based account with key based account
  • send remaining ETH to contract based account

#5

Correct me if i’m wrong/ things changed…
According to EIP1014 counterfactual address is generated as KEC(RPL(0xFF, ADDR, Salt, KEC(Bytecode)) where ADDR is contract address that’ll run create2 function, NOT the msg.sender/tx.origin.

e.g., USER A&B signed a offchain contract to generate counterfactual address, fund that address.
IF anything goes wrong/ it’s time to settle. Anyone from User A/B/3rd party can deploy that contract on chain paying the fees, bytecode + salt.

Solc/assembly

address addr;
assembly {addr: = create2(0, add(byteCode, 0x20), mload(byteCode), salt)}

#6

@nepalbitcoin Everything you said is correct. It’s possible to calculate the deployment address with the sender if the sender is used to calculate the salt in the contract that does the create2 deployment. For example, the salt that is used in create2 could be the hash of the sender and another salt passed in by the sender. That way a different sender would result in a different salt used by create2 and thus a different address.


#7

Exactly! We actually have a longer discussion about that on this OpenZeppelin issue, regarding which information should be part of the salt (initialization data, sender, signer, etc). For this particular case, it would seem that just sender should do the trick.


#8

For those of you interested, we have just released a 2.3.0-alpha.0 version of zos that includes this feature - under the command zos create2. Though not ready for production use, we’d love if you could test it out and share your feedback!