Skip to content

Update EIP-7907: Reduce code limit, increase cost per word, fix EXTCODESIZE issue #9910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions EIPS/eip-7907.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,50 @@

## Abstract

This EIP substantially increases the hard contract code size limit from 24KB (24576 bytes) introduced in [EIP-170](./eip-170.md) to 256KB, and adds gas metering. It introduces a gas cost of 2 gas per (32 byte) word for contract code exceeding 24KB, allowing deployment of contracts of any size while preventing DoS attacks through appropriate gas metering. Lastly, it also commensurately increases initcode size limit from 48KB, introduced in [EIP-3860](./eip-3860.md), to 512KB.
This EIP substantially increases the hard contract code size limit from 24KB (24576 bytes) introduced in [EIP-170](./eip-170.md) to 48KB, and adds gas metering. It introduces a gas cost of 4 gas per (32 byte) word for contract code exceeding 24KB, allowing deployment of contracts of any size while preventing DoS attacks through appropriate gas metering. Lastly, it also commensurately increases initcode size limit from 48KB, introduced in [EIP-3860](./eip-3860.md), to 96KB.

## Motivation

EIP-170 introduced a 24KB contract code size limit to prevent potential DoS attacks, as large contract code requires O(n) resource cost in terms of disk reads, VM preprocessing, and Merkle proof sizes, all of which are not directly compensated by gas fees. However, this limit restricts legitimate use cases for large contracts.

This EIP proposes a gas-based solution that allows contracts of larger size while ensuring that users loading large contracts pay gas proportional to the additional resources they consume. This approach aligns with Ethereum's gas model philosophy of paying for the resources consumed. A new limit has been set at 256KB, so that raising the gas limit does not break assumptions in the p2p layer.
This EIP proposes a gas-based solution that allows contracts of larger size while ensuring that users loading large contracts pay gas proportional to the additional resources they consume. This approach aligns with Ethereum's gas model philosophy of paying for the resources consumed. A new limit has been set at 48KB, so that raising the gas limit does not break assumptions in the p2p layer.

Improving developer experience is the primary motivation for increasing the contract size limit. The current 24KB ceiling forces developers to split functionality across multiple contracts, introduce proxies or delegatecall-based indirection, and rely on architectural patterns like the Diamond Standard—even when those patterns aren't otherwise necessary. These workarounds can increase code complexity, deployment costs, and audit surface. By raising the limit, developers can keep more logic in a single contract, improving readability and lowering gas usage by avoiding unnecessary cross-contract calls. This also makes smart contract development more accessible to newer developers, who can move from idea to deployment without first learning advanced contract composition patterns.

## 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174).

1. Update the [EIP-170](./eip-170.md) contract code size limit of 24KB (`0x6000` bytes) to 256KB (`0x40000` bytes).
2. Change the gas schedule for opcodes which load code. Specifically, the opcodes `CALL`, `STATICCALL`, `DELEGATECALL`, `CALLCODE` and `EXTCODECOPY` are modified so that `largeContractCost = ceil32(excess_contract_size) * GAS_INIT_CODE_WORD_COST // 32` gas is added to the access cost if the code is cold, where `excess_contract_size = max(0, contract_size - 0x6000)`, and `GAS_INIT_CODE_WORD_COST = 2`. (Cf. initcode metering: [EELS](https://github.com/ethereum/execution-specs/blob/1a587803e3e698407d204888b02342393f8b4fe5/src/ethereum/cancun/vm/gas.py#L269)). This introduces a new warm state for contract code - warm if the code has been loaded, cold if not.
1. Update the [EIP-170](./eip-170.md) contract code size limit of 24KB (`0x6000` bytes) to 48KB (`0xc000` bytes).
2. Change the gas schedule for opcodes which load code. Specifically, the opcodes `CALL`, `STATICCALL`, `DELEGATECALL`, `CALLCODE` and `EXTCODECOPY` are modified so that `largeContractCost = ceil32(excess_contract_size) * GAS_CODE_LOAD_WORD_COST // 32` gas is added to the access cost if the code is cold, where `excess_contract_size = max(0, contract_size - 0x6000)`, and `GAS_CODE_LOAD_WORD_COST = 4`. (Cf. initcode metering: [EELS](https://github.com/ethereum/execution-specs/blob/1a587803e3e698407d204888b02342393f8b4fe5/src/ethereum/cancun/vm/gas.py#L269)). This introduces a new warm state for contract code - warm if the code has been loaded, cold if not.
3. The cost for `EXTCODESIZE` is updated to acknowlege the potential for two database reads: once for the code hash and once for the code size associated with the code hash.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would EXTCODESIZE charge exactly? I may miss the info somewhere.

with the hash. In addition to the current pricing scheme defined under [EIP-2929](./eip-2929.md), the instruction will also be subject to regular storage pricing, e.g. `COLD_SLOAD_COST` and `WARM_SLOAD_COST`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does querying EXTCODESIZE make the account "code-warm", or is this another category? (warm account, warm code, warm extcodesize (?))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would make the account warm and code size for the account warm, but leave the code cold.

4. Update the [EIP-3860](./eip-3860.md) contract initcode size limit of 48KB (`0xc000` bytes) to 96KB (`0x18000` bytes).

| Contract | Gas changes (only opcodes that load code) | How? |
| ----------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| Cold account and code | Add `largeContractCost` to `COLD_ACCOUNT_ACCESS_COST=2600` | Contract not in access list nor accessed prior in the txn |
| Warm account, cold code | Add `largeContractCost` to `WARM_STORAGE_READ_COST=100` | Already accessed balance, storage, or included in access list ([EIP-2930](./eip-2930.md)) |
| Cold account, size, and code | Add `COLD_SLOAD_COST=2300`, `largeContractCost`, and `COLD_ACCOUNT_ACCESS_COST=2600` | Contract not in access list nor accessed prior in the txn |
| Warm account, cold size and code | Add `COLD_SLOAD_COST=2300`, `largeContractCost`, and `WARM_STORAGE_READ_COST=100` | Already accessed balance, storage, or included in access list ([EIP-2930](./eip-2930.md)) |
| Warm account and size, cold code | Add `WARM_STORAGE_READ_COST=100`, `largeContractCost`, and `WARM_STORAGE_READ_COST=100` | Already accessed code size via `EXTCODESIZE` ([EIP-2930](./eip-2930.md)) |
| Warm account and code | No change to existing gas schedule. `WARM_STORAGE_READ_COST=100` | Contract created with `CREATE`/`CREATE2`, or `CALL`, `STATICCALL`, `DELEGATECALL`, `CALLCODE` or `EXTCODECOPY` made on the contract, previously in the txn (opcodes that load contract code) |

`COLD_ACCOUNT_ACCESS_COST` and `WARM_STORAGE_READ_COST` are defined in [EIP-2929](./eip-2929.md#parameters).

3. Update the [EIP-3860](./eip-3860.md) contract initcode size limit of 48KB (`0xc000` bytes) to 512KB (`0x80000` bytes).
`COLD_ACCOUNT_ACCESS_COST`, `COLD_SLOAD_COST`, and `WARM_STORAGE_READ_COST` are defined in [EIP-2929](./eip-2929.md#parameters).

## Rationale

The gas cost of 2 per word was chosen in-line with [EIP-3860](./eip-3860.md). This accounts for:
The gas cost of 4 per word was chosen in-line with the per word code defined by [EIP-2929](./eip-2929.md)'s `COLD_ACCOUNT_ACCESS_COST. The value is derived from the current gas per word code of `ceil(2600 / (24676//32)) = 4` where `2600` is the current cold account load cost and `24676` is the maximum allow code size at that price. In general, this accounts for:

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "` is the current cold account ..."]

EIPS/eip-7907.md:47:237 MD038/no-space-in-code Spaces inside code span elements [Context: "` is the current cold account ..."]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "` where `"]

EIPS/eip-7907.md:47:224 MD038/no-space-in-code Spaces inside code span elements [Context: "` where `"]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "...current gas per word code of `"]

EIPS/eip-7907.md:47:109 MD038/no-space-in-code Spaces inside code span elements [Context: "...current gas per word code of `"]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "` is the current cold account ..."]

EIPS/eip-7907.md:47:237 MD038/no-space-in-code Spaces inside code span elements [Context: "` is the current cold account ..."]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "` where `"]

EIPS/eip-7907.md:47:224 MD038/no-space-in-code Spaces inside code span elements [Context: "` where `"]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "...current gas per word code of `"]

EIPS/eip-7907.md:47:109 MD038/no-space-in-code Spaces inside code span elements [Context: "...current gas per word code of `"]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "` is the current cold account ..."]

EIPS/eip-7907.md:47:237 MD038/no-space-in-code Spaces inside code span elements [Context: "` is the current cold account ..."]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "` where `"]

EIPS/eip-7907.md:47:224 MD038/no-space-in-code Spaces inside code span elements [Context: "` where `"]

Check failure on line 47 in EIPS/eip-7907.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Spaces inside code span elements [Context: "...current gas per word code of `"]

EIPS/eip-7907.md:47:109 MD038/no-space-in-code Spaces inside code span elements [Context: "...current gas per word code of `"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we should reuse 2600 in the excess gas calculation. One thought is whether the excess gas should include the cost of reading account from MPT.

The original 2600 gas accounts for:

  • cost for reading the account from MPT
  • cost for reading the code via codehash (from account)
  • jump analysis, memory, etc

When reading a large contract, since the first 2600 already covers the cost of reading the account, should the excess gas include the cost of reading the account or not? It is not, the 2600 per extra 24KB (or 3072 of 24KB using 4 gas per word) may be too conservative? (But I am fine if such a conservative design is intended.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is hard to say conclusively. This I err on the side of caution until we have empirical data to guide us. At a 3072 per additional 24KB, it should be pretty close to parity when you consider the overhead of preparing calls to other contracts in something like diamond standard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the marginal per-word cost of loading data should be lower than the initial 2600 gas per 24kb (which works out to about 3.38 gas per word). i am under the impression that a more comprehensive gas tuning across all opcodes is being prepared. but it still feels weird to price the marginal cost per word over the cost per-word of the initial 24kb, since it's a sequential load and no additional db access needs to be done. if 2 gas per word is considered too cheap, maybe 3 gas per word is better?


1. The additional disk I/O for retrieving larger contract code
2. The increased computational resources for preprocessing larger code for execution (a.k.a. "JUMPDEST analysis").
3. The growth in Merkle proof sizes for blocks containing very large contracts

This EIP introduces the gas cost as an additional cost for contracts exceeding 24KB. It could have been specified as a simpler `ceil32(contract_size) * 2 // 32`, without hardcoding the existing contract size limit. However, for the sake of being conservative and avoiding lowering the cost of loading existing contracts (which could be small, under the 24KB limit), the 24KB floor was added to the formula.
This EIP introduces the gas cost as an additional cost for contracts exceeding 24KB. It could have been specified as a simpler `ceil32(contract_size) * 4 // 32`, without hardcoding the existing contract size limit. However, for the sake of being conservative and avoiding lowering the cost of loading existing contracts (which could be small, under the 24KB limit), the 24KB floor was added to the formula.

The `EXTCODECOPY` opcode could theoretically be exempt from this, since clients could just load the parts of the bytecode which are actually requested. However, this might require a change at the protocol level, since the full code is required for the block witness. For this reason, `EXTCODECOPY` is included in the pricing scheme, and a carveout could be considered at a later date.

The new limit has been set at 256KB. This is significantly larger than the limit implied by the current block gas limit of 35mm (~170KB). The limit has been put in place so that increasing the gas limit won't have unexpected side effects at the db or p2p layer. For instance, in devp2p, the maximum packet size is 10MB (https://github.com/ethereum/devp2p/blob/5713591d0366da78a913a811c7502d9ca91d29a8/caps/eth.md#basic-operation). As of time of this writing, the maximum packet size in snap sync is even lower, at 512KB.
The new limit has been set at 48KB. The limit has been put in place so that increasing the gas limit won't have unexpected side effects at the db or p2p layer. For instance, in devp2p, the maximum packet size is 10MB (https://github.com/ethereum/devp2p/blob/5713591d0366da78a913a811c7502d9ca91d29a8/caps/eth.md#basic-operation). As of time of this writing, the maximum packet size in snap sync is even lower, at 96KB.

The limit for initcode has also been increased to 512KB, following the pattern set in EIP-3860 that the initcode limit is double the runtime code limit. While initcode is different from deployed code in that it does not live in the state and therefore isn't visible in devp2p or in the db, fully removing the limit could have unforeseen consequences.
The limit for initcode has also been increased to 96KB, following the pattern set in EIP-3860 that the initcode limit is double the runtime code limit. While initcode is different from deployed code in that it does not live in the state and therefore isn't visible in devp2p or in the db, fully removing the limit could have unforeseen consequences.

## Backwards Compatibility

Expand Down
Loading