EIP-8058 - Contract Bytecode Deduplication Discount

Created 2025-10-22
Status Draft
Category Core
Type Standards Track
Authors
Requires

Abstract

This proposal introduces a gas discount for contract deployments when the bytecode being deployed already exists in the state. By leveraging EIP-2930 access lists, any contract address included in the access list automatically contributes its code hash to a deduplication check. When the deployed bytecode matches an existing code hash from the access list, the deployment avoids paying GAS_CODE_DEPOSIT * L costs since clients already store the bytecode and only need to link the new account to the existing code hash.

This EIP becomes particularly relevant with the adoption of EIP-8037, which increases GAS_CODE_DEPOSIT from 200 to 1,900 gas per byte. Under EIP-8037, deploying a 24kB contract would cost approximately 46.6M gas for code deposit alone, making the deduplication discount economically significant for applications that deploy identical bytecode multiple times.

Motivation

Currently, deploying duplicate bytecode costs the same as deploying new bytecode, even though execution clients don't store duplicated code in their databases. When the same bytecode is deployed multiple times, clients store only one copy and have multiple accounts point to the same code hash. Under EIP-8037's proposed gas costs, deploying a 24kB contract costs approximately 46.6M gas for code deposit alone (1,900 × 24,576). This charge is unfair for duplicate deployments where no additional storage is consumed.

A naive "check if code exists in database" approach would break consensus because different nodes have different database contents due to mostly Sync-mode differences:

Empirical analysis reveals that approximately 27,869 bytecodes existed in full-synced node databases with no live account pointing to them (as of the Cancun fork). A database lookup CodeExists(hash) would yield different results on different nodes, causing different gas costs and breaking consensus.

This proposal solves the problem by making deduplication checks implicit and deterministic through access lists, ensuring all nodes compute identical gas costs regardless of their database state. (Notice here that even if fully-synced clients have more codes, there are no accounts whose codeHash actually is referencing them. Thus, users can't profit from such discounts which keeps consensus safe).

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Implicit Deduplication via Access Lists

This proposal leverages the existing EIP-2930 access list structure without any modifications. No new fields or protocol changes are required.

CodeHash Access-Set Construction

Before transaction execution begins, build a set W (the "CodeHash Access-Set") as follows:

W = { codeHash(a) | a ∈ accessList, a exists in state, a has code }

Where:

Contract Creation Gas Accounting

When a contract creation transaction or opcode (CREATE/CREATE2) successfully completes and returns bytecode B of length L, compute H = keccak256(B) and apply the following gas charges:

Deduplication check:

Gas costs:

Implementation Pseudocode

# Before transaction execution:
W = set()
for tuple in tx.access_list:
    warm(tuple.address)  # per EIP-2930/EIP-2929 rules
    acc = load_account(tuple.address)
    if acc exists and acc.code is not empty:
        W.add(acc.codeHash)

# On successful CREATE/CREATE2:
H = keccak256(B)
if H in W:
    # Duplicate: no deposit gas
    link_codehash(new_account, H)
else:
    # New bytecode: charge and persist
    charge(GAS_CODE_DEPOSIT * len(B))
    persist_code(H, B)
    link_codehash(new_account, H)

Same-Block Deployments

Sequential transaction execution ensures that a deployment storing new code makes it visible to later transactions in the same block:

  1. Transaction T_A deploys bytecode B at address X
  2. Pays full GAS_CODE_DEPOSIT * L (no prior contract has this bytecode)
  3. Code is stored under hash H = keccak256(B)

  4. Later transaction T_B in the same block deploys the same bytecode B:

  5. T_B includes address X in its access list
  6. When T_B executes, W is built from the current state (including T_A's changes)
  7. Since X now exists and is in the access list, W contains H
  8. T_B's deployment gets the discount

While this only tries to formalize the behavior, it's important to remark that this kind of behavior is complex. As it requires control over tx ordering in order to abuse. Builders can't modify the access list as it is already signed with the transaction. Nevertheless, this could happen, thus is formalized here.

Edge Case: Simultaneous New Deployments

If two transactions in the same block both deploy identical new bytecode and neither references an existing contract with that bytecode in their access lists, both will pay full GAS_CODE_DEPOSIT * L.

Example:

This is acceptable because:

Rationale

Why Access-List Based Deduplication?

The access-list approach provides several critical properties:

  1. Deterministic behavior: The result depends only on the transaction's access list and current state, not on local database contents. All nodes compute the same gas cost.

  2. No reverse index requirement: Unlike other approaches, this doesn't require maintaining a codeHash → [accounts] reverse index, which would add significant complexity and storage overhead.

  3. Leverages existing infrastructure: Builds on EIP-2930 access lists and EIP-2929 access costs, requiring minimal protocol changes.

  4. Implicit optimization: Any address included in the access list automatically contributes to deduplication. This provides automatic gas optimization without requiring explicit flags or special handling.

  5. Avoids chain split risks: Since no new transaction structure is introduced, there's no risk of nodes rejecting transactions with unknown fields. The same transaction format works before and after the fork, with only the gas accounting rules changing at fork activation.

  6. Forward compatibility: All nodes enforce identical behavior. Wallets can add addresses to access lists to optimize gas, but this doesn't change transaction validity.

  7. Avoid having a code-root for state: At this point, clients handle code storage on their own ways. They don't have any consensus on the deployed existing codes (besides that all of the ones referenced in account's codehash fields exist). Changing this seems a lot more complex and unnecessary.

Backwards Compatibility

This proposal requires a scheduled network upgrade but is designed to be forward-compatible with existing transactions.

Transaction compatibility:

Wallet and tooling updates:

Node implementation:

Reference Implementation

Example Transaction

Deploying a contract with the same bytecode as the contract at 0x1234...5678:

{
  "from": "0xabcd...ef00",
  "to": null,
  "data": "0x608060405234801561001...",
  "accessList": [
    {
      "address": "0x1234567890123456789012345678901234567890",
      "storageKeys": []
    }
  ]
}

If the deployed bytecode hash matches codeHash(0x1234...5678) (which is automatically checked because the address is in the access list), the deployment receives the deduplication discount.

Security Considerations

Gas Cost Accuracy

The deduplication mechanism ensures that gas costs accurately reflect actual resource consumption. Duplicate deployments don't consume additional storage, so they shouldn't pay storage costs.

Denial of Service

The access-list mechanism prevents DoS attacks because:

Copyright

Copyright and related rights waived via CC0.