-
-
Save DeviateFish/dbe1ba05455d8bf9f428878c81da17fc to your computer and use it in GitHub Desktop.
A "drop-in" replacement for the `create2` opcode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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