ERC-2470: Singleton Factory

but does ETC have create2

I have no idea, the only blockchain I know about is Ethereum.

Regarding “GenericFactory2”, it’s still clearly possible to collide addresses.

I created for you a contract that enables what “GenericFactory” does, but it’s guaranteed to not have colliding addresses, and allows payable! I named it “InitializerFactory” because it seemed more proper to what it does, however, I didn’t deployed or tested (just mentally), and I am releasing here under CC0, feel free to use it or take it for inspiration to fix “GenericFactory”.

/**
 * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
 * @notice Allows Creation of contracts that require one call after creation. 
 */
contract InitializerFactory {
    /**
     * @notice Deploys a deterministic address based on `_initCode`, `msg.value`, `_initCall`, `_salt`.
     * @param _initCode Initialization code.
     * @param _initCall Calldata to be made to created contract 
     * @param _salt Arbitrary value to modify resulting address.
     * @return Created contract address.
     */
    function deploy(bytes memory _initCode, bytes memory _initCall, bytes32 _salt)
        public
        payable
        returns (address payable createdContract)
    {
        bytes32 actualSalt = keccak256(abi.encodePacked(msg.value, _initCall, _salt));
        assembly {
            createdContract := create2(msg.value, add(_initCode, 0x20), mload(_initCode), actualSalt)
        }
        if(_initCall.length > 0){
            (bool success, bytes memory reason) = createdContract.call(_initCall);
            require(success, string(reason));
        }
    }
}

If you are satisfied with the contract you can deploy ERC-2470 on mainnet and use it to deploy this factory. Or you can give me the code and I deploy the GenericFactory3 with 2470.

Perhaps this would be the fixed “ConterfactualFactory”:

contract CounterfactualFactory
{
        constructor() internal {}

	function _create2(bytes memory _code, bytes32 _salt, uint256 _value, bytes memory _initCall)
	internal returns(address)
	{
		bytes memory code = _code;
		bytes32      salt = keccak256(abi.encodePacked(_salt, _value, _initCall));
		address      addr;
		// solium-disable-next-line security/no-inline-assembly
		assembly
		{
			addr := create2(_value, add(code, 0x20), mload(code), salt)
			if iszero(extcodesize(addr)) { revert(0, 0) }
		}
                if(_initCall.length > 0){
                        (bool success, bytes memory reason) = addr.call(_initCall);
                        require(success, string(reason));
                }
		return addr;
	}

	function _predictAddress(bytes memory _code, bytes32 _salt, uint256 _value, bytes memory _initCall) 
	internal view returns (address)
	{
		return address(bytes20(keccak256(abi.encodePacked(
			bytes1(0xff),
			address(this),
			keccak256(abi.encodePacked(_salt, _value, _initCall)),
			keccak256(_code)
		)) << 0x60));
	}
}

How?

The code you proposed is really similar to mine. The only difference I see is you made the function payable and put the msg.value in the actualSalt.
I see that you put the msg.value in the constructor (I would have put it in the .call(). It that something you plan to include in ERC2470? I’ve never seen payable constructor but who knows, it might be a thing …

You left open the direct _salt of create2 exposed, while in the other function you wrap around the parameters of callback. This means that in one chain one contract could have been initialized with the callback function, and other chain the same address could have been initialized without callback just by passing directly the same salt used in the other function/chain.

Therefore, if there is no callback, it should even hash it against an empty callback.

I don’t plan to include msg.value or payable in ERC2470, it don’t seem to be a Singleton use-case to initialize a contract with funds, however in Ethereum that possible, and as you intent to support any type of contract, I imagine you want to have payable in the constructor call too. To be honest, I never seen a contract using a payable constructor.

ERC2470 was done for other developers don’t have to replicate what ERC820 and ERC1820 every-time they need a key-less contract deploy. I have other plans for ERC2470 and development chains as well, so these infrastructure contracts, such as ERC2470 and the ones derived from it could be initialized at genesis of ganache development chains, for example.

After create2, this was not possible, but now as is possible, it seems a useful piece to have available for building Ethereum infrastructure of Singleton.

As you might now due your prior research on this topic. this also solves the problem of who gets to deploy a trustless contract, which technically is anyone, but in the case of ERC2470, a contract exists based on it’s code.

Other great advantage of ERC2470 is the vanity generator for ERC2470, in past, creating a vanity contract was complicated, usually involved brute-forcing key generation until found a nonce 0 contract that does the starting bytes desired, however for key-less deploys that greatly increases complexity.

2 Likes

https://etherscan.io/address/0xce0042B868300000d44A59004Da54A005ffdcf9f#code

1 Like

I recommend checking out GitHub - Zoltu/deterministic-deployment-proxy: An Ethereum proxy contract that can be used for deploying contracts to a deterministic address on any chain.. I (and others) have been using that for deterministic deployments of contracts for over a year now. It has the advantage of letting you just do a normal deployment transaction but set the to field on the transaction to the deployment proxy address. It also has the nice property that the deployment proxy deployment code is very tiny, so it can be inlined easily into say another EIP. :wink:

The reason I didn’t create an EIP is because I don’t think there is significant value in standardizing it. Anyone can deploy with whatever mechanisms they want. I can use my deployer and you can use your deployer and everything works just as well as if we both used the same deployer.

Something being a good idea, or a good design pattern doesn’t mean it needs to be standardized. There are many good design patterns that are not also standards, but end up widely used because they are good ideas. Generally, a standard should exist if you he a many to many relationship of actors who need to communicate with each other.

You have indicated that you plan to refer to this in other standards. The only place I can think of where this would make sense is a standard for a central registry. In such a case you can simply define the deployment process in that standard, like how EIP 1820 did, but in this case it would be a bit simpler as the steps are just “send this data to this contract address, if no contract exists at that address then send ETH to this address and then submit this transaction”.

3 Likes

ERC-2470 uses this yes, but it’s not only this, but also the “keyless deployment”. No one control any keys that are related to the deployment of ERC-2470 or it’s children.

Exactly, so instead of everyone that needs this feature of “deterministic keyless deploy”, don’t have to redo the process. they can just use ERC-2470. Also, they don’t have to mention about how to deploy the 2470 contract, because it would be described in the 2470 document.

I see that this is interesting to have as an EIP, so dev tools can integrate this behavior, so everyone uses the same thing instead of each one using a different or own implementation.

Perhaps I’m misunderstanding, but the deterministic-deployment-proxy I linked above also supports “keyless deployment”? That is, anyone can deploy the contract from any address and it will end up deployed at the same address on all chains no matter who the deployer is.

Ah, I see now, you also did the keyless deploy of the factory. Yes, it exactly the same thing.

1 Like

Seems like the only difference is that in 2470 the salt of create2 is exposed. This was done to make easier to create vanity addresses.

Also the yul implementation reverts if create2 fails, which the current eip 2470 implementation doesn’t. I would recommend to add this else quite some tools cannot properly estimate the transaction.

Hey, I would like to test this behavior before implement it. Can you give me an example of a tool/contract that fails to estimate the create2? I never experienced that with Singleton Factory, maybe it depends on what constructor is doing?

truffle fails on this you can tests this with https://github.com/gnosis/delegate-registry/commit/1a181e5aacca33a743cc3f51e7473728a8a063be

Also I find it very unexpected that the transaction doesn’t revert if the deployment fails.

1 Like

Thanks for reporting this issue.

I see the issue with the gas estimator. It looks like everytime create2 is used the check must be done, or the gas estimator should be fixed.

I ran the script you linked but there I had no issues, I just had issues when using it with Metamask directly on the etherscan dapp.

I’ve had other problems with gas estimator, like in GnosisMultisig, when the last confirmation is supposed to execute something, or even calling execute. As the execution failed generates an event informing the fail instead of reverting, its the “less gas cost” path the estimator finds.

Personally I think that this is not a issue with SingletonFactory contract, but with the gas estimator should be fixed for all cases, as this is not only affecting this case, but other systems as well.

For working around the issue, there are 3 paths forward for this:

  1. Creating a new singleton factory that reverts if deploy failed
    a. redeploying from a keyless deploy
    b. deploying in top of current singleton factory.
  2. Creating a wrapper contract around singleton factory that does this check.
  3. Using a custom gas calculation for this issue.

For 1,2, I’ll provide a version with that feature.

For 3.:

Can you see if this workaround works for you?

in migrations/utils/singleton_factory.js line 74:

    const blockGasLimit = (await web3.eth.getBlock('latest')).gasLimit
    const txGasLimit = await new web3.eth.Contract(SINGLETON_FACTORY_ABI, SINGLETON_FACTORY).methods.deploy(bytecode, salt).estimateGas({from: deployer, gas: blockGasLimit});
    const { tx } = await factory.deploy(bytecode, salt, { from: deployer, gas: txGasLimit });

Calling estimateGas will not fix this (that is what truffle does under the hood). We encountered that issues on multiple machines when deploying to mainnet, rinkeby and kovan. Adding a wrapper contract will increase the deployment costs (especially since there is discussion around increasing the gas costs for internal calls). Currently I work around by checking before deployment that the call succeeds (expected address is returned) when the estimated gas limit is set. I also wrote a js lib for that (it supports also @MicahZoltu version): https://github.com/gnosis/singleton-deployer there you can see that I do some checks for the gas limit before deployment: https://github.com/gnosis/singleton-deployer/blob/main/packages/core/src/factory.ts#L36

1 Like

Estimate gas don’t works even if defining the gas parameter to block gas limit? Where is the best place to report this issue? https://web3js.readthedocs.io/en/v1.3.0/web3-eth-contract.html#id38 as I understand, the gas parameter should aid on cases like this.

The intent of not checking the deploy was that this could be done externally, and the factory would just try to deploy with the gas provided.

I agree the wrapper contract is the worst way to go on this solution.

I liked you workaround

I think that, if we are unable to have a fix for gas estimate, than the best is to make a new keyless deploy. This might take some time as I will let the script find an address that contains as many zeros as possible.

What way you recommend of checking the contract deploy worked? Just check if address returned is not zero? Or also check if extcodesize is greater than zero?

So if I set the Gaslimit for the deployment tx to the block gas limit it would work. The estimateGas always tries to find the lowest value where the ethereum transaction is “successful” (aka not reverting).

Maybe I would say it is just unexpected, if described in the eip that you need to pay attention to the gas as the factory does not enforce success I don’t see an immediate need to fix it.

Checking that the returned value is != 0 should be enough, afaik that is how success is defined in the eip for create2.

I would collect more feedback before a redeploy. (E.g. add the fallback behaviour that is used in the yul implementation).

I agree that estimations are still annoying, especially for gas intensive, nested calls … Which deployment of a contract via factory is. Not sure should “fix” this :confused:

I opened an issue in ethereum-go: https://github.com/ethereum/go-ethereum/issues/21746