Ethernauta

EIP-1014 — CREATE / CREATE2 address derivation

The pair CREATE (since Frontier) and CREATE2 (EIP-1014) define where a deployed contract lands:

  • CREATE: address = keccak256(rlp([sender, nonce]))[12:]
  • CREATE2: address = keccak256(0xff ‖ sender ‖ salt ‖ keccak256(init_code))[12:]

CREATE2 is the basis for counterfactual deployments — account abstraction factories (ERC-4337), Safe modules, and EIP-6492 signatures all assume the deployed address is knowable in advance. The demo shows both derivations against EIP-1014 fixture vectors.

CREATE

from0x0000000000000000000000000000000000000042
nonce7

CREATE2

from0x00000000000000000000000000000000deadbeef
salt0x00000000000000000000000000000000000000000000000000000000cafebabe
keccak256(init code)0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b204a3902895
View on GitHub

The code (deriving addresses)

import "./demo.css"
import {
  type Address,
  AddressSchema,
  Bytes32Schema,
} from "@ethernauta/core"
import {
  get_contract_address,
  get_create2_address,
} from "@ethernauta/eip/1014"
import { useState } from "react"
import { parse } from "valibot"
import { Button } from "../../components/button"

// Stand-in `keccak256(init_code)` — any 32-byte hex works for
// the derivation; the caller is responsible for hashing their
// real init code (creation bytecode + constructor calldata)
// before passing it as `bytecodeHash`.
const BYTECODE_HASH = parse(
  Bytes32Schema,
  "0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b204a3902895",
)

const CREATE_FROM = parse(
  AddressSchema,
  "0x0000000000000000000000000000000000000042",
)
const CREATE_NONCE = 7n

const CREATE2_FROM = parse(
  AddressSchema,
  "0x00000000000000000000000000000000deadbeef",
)
const CREATE2_SALT = parse(
  Bytes32Schema,
  "0x00000000000000000000000000000000000000000000000000000000cafebabe",
)

export function DeployContractDemo() {
  const [create_address, set_create_address] =
    useState<Address | null>(null)
  const [create2_address, set_create2_address] =
    useState<Address | null>(null)
  const [error, set_error] = useState<string | null>(null)

  function derive() {
    set_error(null)
    try {
      set_create_address(
        get_contract_address({
          from: CREATE_FROM,
          nonce: CREATE_NONCE,
        }),
      )
      set_create2_address(
        get_create2_address({
          from: CREATE2_FROM,
          salt: CREATE2_SALT,
          bytecodeHash: BYTECODE_HASH,
        }),
      )
    } catch (e) {
      set_error(e instanceof Error ? e.message : String(e))
    }
  }

  return (
    <div className="deploy-contract-root">
      <Section title="CREATE">
        <Row label="from" value={CREATE_FROM} mono />
        <Row
          label="nonce"
          value={CREATE_NONCE.toString()}
        />
        {create_address && (
          <Row
            label="contract address"
            value={create_address}
            mono
            highlight
          />
        )}
      </Section>
      <Section title="CREATE2">
        <Row label="from" value={CREATE2_FROM} mono />
        <Row label="salt" value={CREATE2_SALT} mono />
        <Row
          label="keccak256(init code)"
          value={BYTECODE_HASH}
          mono
        />
        {create2_address && (
          <Row
            label="contract address"
            value={create2_address}
            mono
            highlight
          />
        )}
      </Section>
      {error && (
        <p className="deploy-contract-error">{error}</p>
      )}
      <div>
        <Button onClick={derive}>Derive addresses</Button>
      </div>
    </div>
  )
}

function Section({
  title,
  children,
}: {
  title: string
  children: React.ReactNode
}) {
  return (
    <section className="deploy-contract-section">
      <h4 className="deploy-contract-section-title">
        {title}
      </h4>
      {children}
    </section>
  )
}

function Row({
  label,
  value,
  mono,
  highlight,
}: {
  label: string
  value: string
  mono?: boolean
  highlight?: boolean
}) {
  return (
    <div
      className={
        highlight
          ? "deploy-contract-row is-highlight"
          : "deploy-contract-row"
      }
    >
      <span className="deploy-contract-row-label">
        {label}
      </span>
      <span
        className={
          mono
            ? "deploy-contract-row-value is-mono"
            : "deploy-contract-row-value"
        }
      >
        {value}
      </span>
    </div>
  )
}

Deploying through the wallet

deploy_contract ships in @ethernauta/eip/1014 for the signing half — it encodes constructor calldata and calls eth_signTransaction with no to (the EVM rule for creation transactions). The wallet fills nonce / gas / fees per the project invariant.

import "./demo.css"
import {
  type Address,
  AddressSchema,
  Bytes32Schema,
} from "@ethernauta/core"
import {
  get_contract_address,
  get_create2_address,
} from "@ethernauta/eip/1014"
import { useState } from "react"
import { parse } from "valibot"
import { Button } from "../../components/button"

// Stand-in `keccak256(init_code)` — any 32-byte hex works for
// the derivation; the caller is responsible for hashing their
// real init code (creation bytecode + constructor calldata)
// before passing it as `bytecodeHash`.
const BYTECODE_HASH = parse(
  Bytes32Schema,
  "0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b204a3902895",
)

const CREATE_FROM = parse(
  AddressSchema,
  "0x0000000000000000000000000000000000000042",
)
const CREATE_NONCE = 7n

const CREATE2_FROM = parse(
  AddressSchema,
  "0x00000000000000000000000000000000deadbeef",
)
const CREATE2_SALT = parse(
  Bytes32Schema,
  "0x00000000000000000000000000000000000000000000000000000000cafebabe",
)

export function DeployContractDemo() {
  const [create_address, set_create_address] =
    useState<Address | null>(null)
  const [create2_address, set_create2_address] =
    useState<Address | null>(null)
  const [error, set_error] = useState<string | null>(null)

  function derive() {
    set_error(null)
    try {
      set_create_address(
        get_contract_address({
          from: CREATE_FROM,
          nonce: CREATE_NONCE,
        }),
      )
      set_create2_address(
        get_create2_address({
          from: CREATE2_FROM,
          salt: CREATE2_SALT,
          bytecodeHash: BYTECODE_HASH,
        }),
      )
    } catch (e) {
      set_error(e instanceof Error ? e.message : String(e))
    }
  }

  return (
    <div className="deploy-contract-root">
      <Section title="CREATE">
        <Row label="from" value={CREATE_FROM} mono />
        <Row
          label="nonce"
          value={CREATE_NONCE.toString()}
        />
        {create_address && (
          <Row
            label="contract address"
            value={create_address}
            mono
            highlight
          />
        )}
      </Section>
      <Section title="CREATE2">
        <Row label="from" value={CREATE2_FROM} mono />
        <Row label="salt" value={CREATE2_SALT} mono />
        <Row
          label="keccak256(init code)"
          value={BYTECODE_HASH}
          mono
        />
        {create2_address && (
          <Row
            label="contract address"
            value={create2_address}
            mono
            highlight
          />
        )}
      </Section>
      {error && (
        <p className="deploy-contract-error">{error}</p>
      )}
      <div>
        <Button onClick={derive}>Derive addresses</Button>
      </div>
    </div>
  )
}

function Section({
  title,
  children,
}: {
  title: string
  children: React.ReactNode
}) {
  return (
    <section className="deploy-contract-section">
      <h4 className="deploy-contract-section-title">
        {title}
      </h4>
      {children}
    </section>
  )
}

function Row({
  label,
  value,
  mono,
  highlight,
}: {
  label: string
  value: string
  mono?: boolean
  highlight?: boolean
}) {
  return (
    <div
      className={
        highlight
          ? "deploy-contract-row is-highlight"
          : "deploy-contract-row"
      }
    >
      <span className="deploy-contract-row-label">
        {label}
      </span>
      <span
        className={
          mono
            ? "deploy-contract-row-value is-mono"
            : "deploy-contract-row-value"
        }
      >
        {value}
      </span>
    </div>
  )
}

Signable<Bytes> returns the signed raw transaction — broadcast it with eth_sendRawTransaction to get the hash, then derive the deployed address with get_contract_address({ from, nonce }).