10

I am forking the Sushiswap Exchange code for educational purposes, and I have come across a variable in the code called INIT_CODE_HASH. I am struggling to understand exactly how this variable works, and how it is used in the various DeFi platforms.

My limited understanding is that INIT_CODE_HASH is used to verify which function of a contract a caller is calling, by using the hash of the init code (Not really sure what the init code is). For some reason I don't quite understand, this is both for security and efficiency purposes.

My Question:

What is INIT_CODE_HASH, and how is it calculated / used?

4 Answers 4

2

Init code hash is basically the keccak256 hash of the contracts bytecode.

Usage of Init Code

Init code hash is needed "Since address calculation depends on hashing the init code. Init code is the code that, when executed, produces the runtime bytecode that will be placed into the state, and which typically is used by high level languages to implement a ‘constructor’.

"EIP-1014: Skinny CREATE2," Ethereum Improvement Proposals, no. 1014, April 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1014.

Usage of Init Code

The primary usage of init code is for mining addresses. You can check out https://github.com/0age/create2crunch for an example of how we can leverage CREATE2 and a contract's init code to 'mine' more efficient addresses for contract deployment.

Here is a simple bash script that uses forge to generate the code init hash for you:

https://github.com/sambacha/foundry-scripts/tree/master/scripts/deploy

Script

#!/usr/bin/env bash

# Function to exit on error with a message
exit_on_error() {
  echo "Error: $1"
  exit 1
}

# Function to display usage information
usage() {
  echo "Usage: $0 <contract_path> <contract_name> [-v]"
  echo "  <contract_path> : Path to the Solidity contract file"
  echo "  <contract_name> : Name of the Solidity contract"
  echo "  -v              : Enable verbose output (optional)"
  exit 1
}

# Check if foundry is installed
if ! command -v forge &> /dev/null; then
  exit_on_error "Foundry is not installed. Please install it first."
fi

# Check if correct number of arguments is provided
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
  usage
fi

# Variables from arguments
CONTRACT_PATH=$1
CONTRACT_NAME=$2
VERBOSE=false

# Check if verbose option is enabled
if [ "$#" -eq 3 ] && [ "$3" == "-v" ]; then
  VERBOSE=true
fi

# Validate contract path
if [ ! -f "$CONTRACT_PATH" ]; then
  exit_on_error "Contract path '$CONTRACT_PATH' does not exist or is not a file."
fi

# Get the directory of the contract path
CONTRACT_DIR=$(dirname "$CONTRACT_PATH")
# shellcheck disable=SC2034
CONTRACT_FILE=$(basename "$CONTRACT_PATH")

# Change to the contract directory
cd "$CONTRACT_DIR" || exit_on_error "Failed to change directory to '$CONTRACT_DIR'."

# Step 1: Compile the contract
echo "Compiling the contract..."
if $VERBOSE; then
  forge build --contracts . || exit_on_error "Failed to compile the contract."
else
  forge build --contracts . > /dev/null 2>&1 || exit_on_error "Failed to compile the contract."
fi

# Step 2: Generate the init code
echo "Generating init code..."
INIT_CODE=$(forge inspect "$CONTRACT_NAME" bytecode) || exit_on_error "Failed to generate init code."

# Step 3: Calculate the init code hash
echo "Calculating init code hash..."
INIT_CODE_HASH=$(echo -n "$INIT_CODE" | keccak-256sum | cut -d' ' -f1) || exit_on_error "Failed to calculate init code hash."

# Step 4: Write the init code hash to a file
OUTPUT_FILE="${CONTRACT_NAME}_INIT_CODE_HASH.txt"
echo "$INIT_CODE_HASH" > "$OUTPUT_FILE" || exit_on_error "Failed to write init code hash to file."

# Clean up temporary files (if any)
# (Add commands here if there are any temporary files to clean up)

# Step 5: Output the result
echo "Init code hash has been written to $OUTPUT_FILE"
if $VERBOSE; then
  echo "Init code: $INIT_CODE"
  echo "Init code hash: $INIT_CODE_HASH"
fi

# Revert to original directory
cd - > /dev/null || exit
6

https://www.codegrepper.com/code-examples/javascript/calculate+init+code+hash+nodejs

This shows how it's used with COMPUTED_INIT_CODE_HASH (https://www.codegrepper.com/code-examples/javascript/calculate+init+code+hash+nodejs):

import { INIT_CODE_HASH } from '../src/constants'

import { bytecode } from '@uniswap/v2-core/build/UniswapV2Pair.json'
import { keccak256 } from '@ethersproject/solidity'

const COMPUTED_INIT_CODE_HASH = keccak256(['bytes'], [`0x${bytecode}`])
4

INIT_CODE_HASH is a hash of the initcode of a contract. The initcode is the code that creates the bytecode that is stored on-chain. You can read more about the types of bytecode here.

Most dapps, including Sushiswap, use this variable to create a new contract with create2. When a dapp wants to create a contract with a known address, they will use the create2 opcode, which uses the hashed initcode as a parameter. The EIP for it is here.

You can see that Sushiswap uses it in this getCreate2Address address:

[tokens[1].address]: getCreate2Address(
            FACTORY_ADDRESS,
            keccak256(['bytes'], [pack(['address', 'address'], [tokens[0].address, tokens[1].address])]),
            INIT_CODE_HASH
          )
0

anyone have a good idea how the INIT_CODE_HASH value is calculated to begin with? AI's seem to think its a hash of the deployed pool(s) bytecode.

I am deploying mocks of the [v3] factory, NonfungiblePositionManager, and Pool contracts. This involves some slight modifications of the contracts -- a thing that will likely alter theresulting abi and bytecode of the deployed pools, so I need a different INIT_CODE_HASH in order to replicate these pool addresses deterministically. For sure 'computeAddress' in the PoolAddress.sol library is not giving me the address of my deployed pool contracts.

I've tried AI's suggestions of hashing the bytecode of the deployed pool contract (i also tried inputing the mock factory address), using this as the value of INIT_CODE_HASH, then using this as a means to compute/ replicate the pool address(s). So far it does not work. The code for this [i.e. a mock version of PoolAddress.sol] is,

    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity 0.7.6;
    pragma abicoder v2;
    
    contract ComputePoolAddress {

        // get bytecode of deployed pool, or any other contract
        function getContractByteCode(address _addr) public view returns (bytes memory) {
           uint length;
           assembly {
               length := extcodesize(_addr)
           }
           bytes memory bytecode = new bytes(length);
           assembly {
               extcodecopy(_addr, add(bytecode, 0x20), 0, length)
           }
           return bytecode;
       }
    
        // compute hash of bytecode
       function computePoolInitCodeHash(address _contractAddress) public view returns (bytes32) {
           // Get the bytecode of the pool contract
           bytes memory poolBytecode = getContractByteCode(_contractAddress);
    
           // Compute the keccak256 hash of the bytecode
           bytes32 hash = keccak256(poolBytecode);
           
           return hash;
       }
    
        // compute [replicate] deployed pool address deterministically
        function computeAddress(address factory,
                                address token0,
                                address token1,
                                uint24 fee,
                                address contract_) 
                                public view returns (address pool) 
        {
            require(token0 < token1, "token1 > token0");
            bytes32 POOL_INIT_CODE_HASH = computePoolInitCodeHash(contract_);
            pool = address(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex'ff',
                            factory,
                            keccak256(abi.encode(token0, token1, fee)),
                            POOL_INIT_CODE_HASH
                            )
                        )
                    )
                );
            }
    
  • note that compiling this with pragma solidity 0.8.0 or higher will cause errors because of casting an address to a uint256 }
1
  • Did you manage to figure this out?
    – kadet
    Commented Jul 3 at 19:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.