Skip to main content

Try it Live

Run Address examples in the interactive playground
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
View the complete executable example at playground/src/examples/primitives/address/create2.ts.
import { Address } from '@tevm/voltaire'

const deployer = Address("0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e")
const salt = Bytes32() // 32-byte salt
const initCode = Bytecode([0x60, 0x80]) // Contract bytecode

const contractAddr = Address.calculateCreate2Address(deployer, salt, initCode)
console.log(contractAddr.toHex())

Algorithm

  1. Hash init code: initCodeHash = keccak256(initCode)
  2. Concatenate data: data = 0xff ++ sender (20 bytes) ++ salt (32 bytes) ++ initCodeHash (32 bytes)
  3. Hash and extract: address = keccak256(data)[12:32]
Pseudocode:
init_code_hash = keccak256(init_code)
data = [0xff] + sender_address + salt + init_code_hash
hash = keccak256(data)
contract_address = hash[12:32]
Total data size: 1 + 20 + 32 + 32 = 85 bytes

Salt Formats

Bigint salt: Converted to 32-byte big-endian:
const salt = 42n
// Becomes: [0, 0, ..., 0, 42] (32 bytes)
Uint8Array salt: Must be exactly 32 bytes:
const salt = Bytes32()
salt[0] = 0x12
salt[31] = 0x34
Hex salt: Convert from hex string:
import * as Hex from '@tevm/voltaire/Hex'
const salt = Hex.toBytes("0x0000000000000000000000000000000000000000000000000000000000000001")

Complete Example

import { Address } from '@tevm/voltaire'
import * as Hex from '@tevm/voltaire/Hex'

// Deployer (could be factory contract)
const factory = Address("0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e")

// Contract bytecode (initialization code)
const initCode = Hex.toBytes(
  "0x608060405234801561001057600080fd5b5060405161012..." // Full bytecode
)

// Deploy with different salts
function predictAddress(salt: bigint): Address {
  return factory.calculateCreate2Address(salt, initCode)
}

// Predict addresses
const addr1 = predictAddress(0n)
const addr2 = predictAddress(1n)
const addr3 = predictAddress(42n)

console.log('Salt 0:', addr1.toHex())
console.log('Salt 1:', addr2.toHex())
console.log('Salt 42:', addr3.toHex())

// Deploy to predicted address
async function deployWithSalt(salt: bigint) {
  const predicted = predictAddress(salt)

  // Deploy using CREATE2
  const tx = await factory.create2Deploy(salt, initCode)
  await tx.wait()

  console.log(`Deployed to ${tx.contractAddress}`)
  console.log(`Predicted: ${predicted.toHex()}`)
  console.log(`Match: ${tx.contractAddress === predicted.toHex()}`)
}

Use Cases

Deterministic Deployments

Deploy to same address across different chains:
import { Address } from '@tevm/voltaire'

const factory = Address("0x...")
const salt = 42n
const initCode = // ... bytecode

// Same address on all chains with same factory, salt, and code
const predictedAddress = factory.calculateCreate2Address(salt, initCode)

// Deploy on mainnet
await deployOnChain('mainnet', factory, salt, initCode)

// Deploy on testnet (same address)
await deployOnChain('goerli', factory, salt, initCode)

Counterfactual Addresses

Interact with contract before deployment:
import { Address } from '@tevm/voltaire'

// Predict address
const contractAddress = factory.calculateCreate2Address(salt, initCode)

// Send funds before deployment
await sendEth(contractAddress, amount)

// Deploy later
await factory.create2Deploy(salt, initCode)

// Contract now has funds

Vanity Addresses

Generate addresses with specific patterns:
import { Address } from '@tevm/voltaire'

function findVanitySalt(
  factory: Address,
  initCode: Uint8Array,
  prefix: string
): bigint {
  for (let salt = 0n; salt < 1000000n; salt++) {
    const addr = factory.calculateCreate2Address(salt, initCode)
    if (addr.toHex().startsWith(prefix)) {
      return salt
    }
  }
  throw new Error('Salt not found')
}

// Find salt that produces address starting with "0x0000"
const salt = findVanitySalt(factory, initCode, "0x0000")
const vanityAddress = factory.calculateCreate2Address(salt, initCode)

Upgradeable Proxies

Deploy proxies to deterministic addresses:
import { Address } from '@tevm/voltaire'

class ProxyFactory {
  constructor(
    private factory: Address,
    private proxyBytecode: Uint8Array
  ) {}

  predictProxy(owner: Address, salt: bigint): Address {
    // Include owner in salt for uniqueness
    const fullSalt = this.encodeSalt(owner, salt)
    return this.factory.calculateCreate2Address(fullSalt, this.proxyBytecode)
  }

  private encodeSalt(owner: Address, salt: bigint): Uint8Array {
    const saltBytes = Bytes32()
    // First 20 bytes: owner
    saltBytes.set(owner, 0)
    // Last 12 bytes: salt
    for (let i = 0; i < 12; i++) {
      saltBytes[31 - i] = Number((salt >> BigInt(i * 8)) & 0xffn)
    }
    return saltBytes
  }
}

EIP-1014

CREATE2 was introduced in EIP-1014 to enable:
  • Deterministic addresses independent of nonce
  • Counterfactual interactions (interact before deployment)
  • State channels with guaranteed addresses
  • Cross-chain consistency (same address on different chains)

CREATE vs CREATE2

FeatureCREATECREATE2
Formulakeccak256(rlp([addr, nonce]))[12:]keccak256(0xff ++ addr ++ salt ++ keccak256(code))[12:]
ParametersDeployer, nonceDeployer, salt, init code
DeterminismSequential (nonce-based)Arbitrary (salt-based)
RedeploymentDifferent addressSame address if inputs identical
Cross-chainDifferent addressesSame address possible

Performance

Cryptographic operations:
  • Two keccak256 hashes (init code + final data)
  • No RLP encoding required
Bundle size impact: Adds keccak256 (~5-10 KB)

Error Handling

import { Address } from '@tevm/voltaire'

const deployer = Address("0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e")
const initCode = Bytecode([0x60, 0x80])

// Valid salts
const addr1 = deployer.calculateCreate2Address(42n, initCode) // ✓
const addr2 = deployer.calculateCreate2Address(Bytes32(), initCode) // ✓

// Invalid: negative salt
try {
  deployer.calculateCreate2Address(-1n, initCode)
} catch (e) {
  console.error(e) // InvalidValueError: Salt cannot be negative
}

// Invalid: wrong salt length
try {
  deployer.calculateCreate2Address(Bytes16(), initCode)
} catch (e) {
  console.error(e) // Error: Salt must be 32 bytes
}

See Also