Deterministic Deployment Proxy: Magic wrapped in magic

TL;DR: Ethereum contract deployed with a one-time-use account (so it has a deterministic address) that can be used to deploy arbitrary contracts via CREATE2 (so they end up with deterministic addresses).

This allows anyone to deploy a contract to any EVM chain and be sure that the contract lives at the same address on every chain. Similar to how EIP-1820 was deployed, but without the need to hard-code gas-price and gas-limit.

5 Likes

This is definitely magic magic. Thanks for sharing @MicahZoltu!

1 Like

So many potential cross-chain applications, including ERC-725 identity which now uses proxy contracts.

https://erc725alliance.org/

I updated the code to use Yul, which brought costs down a bit.

Yul source:

object "Proxy" {
	// deployment code
	code {
		let size := datasize("runtime")
		datacopy(0, dataoffset("runtime"), size)
		return(0, size)
	}
	object "runtime" {
		// deployed code
		code {
			calldatacopy(0, 0, calldatasize())
			mstore(0, create2(callvalue(), 0, calldatasize(), 0))
			return(12, 20)
		}
	}
}

The signed deterministic deployment transaction is now:

f8748085174876e800830186a08080a3601580600e600039806000f350fe366000600037600036600034f56000526014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222

The init code for the proxy contract is now:

601580600e600039806000f350fe366000600037600036600034f56000526014600cf3

The deployed proxy’s code is now:

366000600037600036600034f56000526014600cf3

For fun, I was able to gas golf the deployment code to:

6f3d36363d3d373d34f53d526014600cf33d5260106010f3

and the proxy code to:

3d36363d3d373d34f53d526014600cf3

However, I decided there was value in having Yul source code available for the deployed contract rather than just raw bytecode, so I decided to eat the extra size and costs (they are marginal).

2 Likes

Another nice thing about the switch to Yul is that deploying contracts is now done by supplying only the deployment bytecode as transaction data and put the proxy as the to. This means you can take a normal deployment transaction and only change the to field on it from null to the proxy address (currently 0xb8744b44784dab81e6ab1e73ea3faa47887157b6) and it will deploy via the proxy.

1 Like

Is this because the “runtime” object from above behaves as a fallback function? Otherwise I don’t see how you could take a deployment tx as it is and not have to compute the function selector.

The proxy isn’t written in Solidity, it is written in Yul, so we don’t have to play by the normal rules. :smile:

It is Solidity that has the concept of “parameters” and “ABI”. Yul being just assembly more or less doesn’t have any higher level primitives like that. Your contract can reference calldata via the opcode, but there is no automatic interpretation of it.

1 Like

Hi, @MicahZoltu, what did you mean as a ‘one-time-use account’

You create a transaction and then make up a signature (e.g., 0xaaaaaaaa…aaaaa), then “recover” the address from it. You don’t have the private key, so you can not sign any additional transactions from that address, it can only ever submit that one transaction.

2 Likes