Smart Contract Deterministic Deployment

Posted on Tue 31 October 2023 in tech

Deterministic Deployment for Ethereum

Foundry

Deterministic deployment is an undocumented feature of forge in Foundry. Deterministic deployment refers to predictable deployment of smart contracts to the same address every time they are deployed. A deterministic contract address can be useful for several reasons:

  1. Trust: Users or other contracts can trust a specific address if they know it will always refer to the intended contract.
  2. Interoperability: Projects can hard-code contract addresses if they're guaranteed to stay consistent across different deployments.
  3. Upgrades: For projects that have proxy upgrade patterns, knowing where the new versions of the contract will live can be very beneficial.

To achieve deterministic deployment in early versions of solidity one had to manipulate the factors that determine the contract address. When you deploy a contract on Ethereum, its address is determined by two pieces of information:

  1. Deployer Address (sender): The Ethereum address that's deploying the contract.
  2. Nonce: The number of transactions (including previous contract deployments) sent from the deployer's address.

The method for determining the address of a new contract on Ethereum is:

contract address = keccak256(abi.encodePacked(sender, nonce))[12:]

Here, keccak256 is Ethereum's hash function, and abi.encodePacked uses RLP. RLP stands for Recursive Length Prefix, which is Ethereum's serialization method. The [12:] part means we take the last 20 bytes of the hash since Ethereum addresses are 20 bytes long and the hash itself is 32 bytes.

In recent versions of solidity, it is possible to achieve a deterministic deployment by specifying the salt that is used for determining the contract address. This must be accessed via the contract creator and is supported by the hardhat-deploy plugin for the Hardhat development environment.

Deterministic deployment is also possible in foundry and although the feature is currently undocumented, it works and is ready for use.

To deploy a contract with a deterministic address using Foundry, follow these steps:

A common tool that can help achieve deterministic deployments in Ethereum development is the hardhat-deploy plugin for the Hardhat development environment. It provides features that make it easier to manage and reason about contract deployments. However, deterministic deployment is also possible in foundry and although the feature is currently undocumented, it works and is ready for use.

To deploy a contract with a deterministic address use the following steps:

1. Write a script that dumps the contract code to a file

function storeCode() external {
    bytes memory createCode = abi.encodePacked(type(Contract).creationCode, abi.encode(parameter1, parameter2, ...));
    vm.writeFile("./Contract.bin", vm.toString(createCode));
}

2. Mine the salt for a vanity address based on the contract

$ cast create2 --starts-with 0x1234 --ends-with 1 --deployer 0x4e59b44847b379578588920cA78FbF26c0B4956C --init-code $(cat Contract.bin)   
Starting to generate deterministic contract address...
Successfully found contract address in 0 seconds.
Address: 0x12340bB90e1Fe222037074F712888776Ea523b11
Salt: 47682136829081730604951122655683675694472584175693661373626966164411505470654   

3. Convert the salt from decimal to hex for use in initializing the contract

$ cast --to-base 47682136829081730604951122655683675694472584175693661373626966164411505470654 16
0x696b22100b22466a1fd3fcb481e3bc01c4897f29d63f85c363d3dee8fd106cbe

4. Initialize the contract with the mined salt for deployment

     bytes32 _vanitySalt = vm.envBytes32("CONTRACT_SALT");
     Contract contract = new Contract{ salt: _vanitySalt }(parameter1, parameter2);

Once the contract is deployed it will have the mined address. This works particularly well for the proxy upgrade pattern because it ensures that the contract address will be stable over time.