Skip to content

Instantly share code, notes, and snippets.

@DeviateFish
Created February 14, 2019 07:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DeviateFish/dbe1ba05455d8bf9f428878c81da17fc to your computer and use it in GitHub Desktop.
Save DeviateFish/dbe1ba05455d8bf9f428878c81da17fc to your computer and use it in GitHub Desktop.
A "drop-in" replacement for the `create2` opcode
pragma solidity >=0.4.22 <0.6.0;
// This is just hand-written pseudo-solidity, so it probably won't compile. You should be able to get the gist, however.
// First, we start with the contract that we're committing to deploying:
/// @title A widget
contract Machine {
// This is our contract that does stuff. We'll be committing to deploying this contract in the future.
constructor() public {
// etc
}
}
// Second, we create a factory to create these contracts
/// @title A factory
contract Factory {
// This thing just builds machines. We won't call it directly anyway, it's here for us to delegatecall later.
function build() public {
new Machine();
}
}
// So we deploy these two contracts, and these enable creation of those contracts in the future, in a fixed, managed way.
// Then, we need another tool
/// @title this is our commitment to deploy
contract Commitment {
// We'll have to assume this is actually a factory. If it isn't, our delegatecall will fail, but oh well.
address public factory;
constructor(address _factory) public {
factory = _factory;
}
function commit() public {
factory.delegatecall(bytes4(keccak256("build()")));
selfdestruct(msg.sender);
}
}
// cool, so now we have all of our pieces.
// `Factory` is basically the "init_code" of the contract
// So, if we want to use this, we can do something like this (either in another contract, or in js or whatever):
// address comm = new Commitment((address)someFactory);
// Now that we have the commitment address, we can calculate where the code we committed to deploying will be deployed:
// committed_address = address(keccak256(0xd6, 0x94, comm, 0x01));
// totally just cribbed that from here btw, excellent answer:
// https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed btw
// Now we know what address we will be deploying to, and know what code will be deployed there.
// We've also committed to deploying exactly 0 or 1 times, depending on if `commit` is actually called.
// We've also deployed the contract and the means of deploying that contract (the factory) separately from the commitment, so we can
// make ccreating `Commitment`s as cheap as possible. The pattern of using a factory with a delegatecall also makes
// the commitment generic enough to be used with any number of factories (provided the share constructor argumments, perhaps)
// Finally, the factory can even be parameterized to deploy different contracts based on the parameters passed to `build`,
// enabling different code to be deployed to the committed address based on context at commit time.
// So, what's missing? Well, we can't bring dead contracts back from the dead with this pattern :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment