AlertSourceDiscuss
Skip to content

EIP-7981: Increase access list cost

Price access lists for data footpring to reduce maximum block size

⚠️ DraftCore

Draft Notice

This EIP is in the process of being drafted. The content of this EIP is not final and can change at any time; this EIP is not yet suitable for use in production. Thank you!

AuthorsToni Wahrstätter
Created2024-12-27

Abstract

This EIP charges access lists for their data footprint, closing a loophole that allows circumventing EIP-7623 floor pricing. This effectively reduces the worst-case block size by ~21% with minimal impact on users.

Motivation

Access lists are only priced for storage but not for their data.

Furthermore, access lists can circumvent the EIP-7623 floor pricing by contributing to EVM gas while still leaving a non-negligible data footprint. This enables to achieve the maximal possible block size by combining access lists with calldata at a certain ratio.

Specification

ParameterValueSource
ACCESS_LIST_ADDRESS_COST2400EIP-2930
ACCESS_LIST_STORAGE_KEY_COST1900EIP-2930
TOTAL_COST_FLOOR_PER_TOKEN10EIP-7623

Let access_list_nonzero_bytes and access_list_zero_bytes be the count of non-zero and zero bytes respectively in the addresses (20 bytes each) and storage keys (32 bytes each) contained within the access list.

The current formula for access list costs in EIP-2930 is:

python
access_list_cost = (
    ACCESS_LIST_ADDRESS_COST * access_list_addresses
    + ACCESS_LIST_STORAGE_KEY_COST * access_list_storage_keys
)

The formula for access list costs changes to:

python
# Standard access list functionality cost
standard_access_list_cost = (
    ACCESS_LIST_ADDRESS_COST * access_list_addresses
    + ACCESS_LIST_STORAGE_KEY_COST * access_list_storage_keys
)

# Additional data cost for access list bytes
access_list_tokens = access_list_nonzero_bytes * 4 + access_list_zero_bytes
access_list_data_cost = access_list_tokens * TOTAL_COST_FLOOR_PER_TOKEN

# Total access list cost
access_list_cost = standard_access_list_cost + access_list_data_cost

Transactions pay both the existing EIP-2930 functionality costs plus 40 gas per non-zero byte and 10 gas per zero byte counted across all addresses and storage keys in the access list.

Rationale

Adding 40 gas per non-zero byte and 10 gas per zero byte ensures consistent pricing across all transaction data:

  • Address (20 bytes, typically mostly non-zero): ~3200 gas (2400 + 800 assuming all non-zero)
  • Storage key (32 bytes, typically mostly non-zero): ~3180 gas (1900 + 1280 assuming all non-zero)

No threshold mechanism is used. The per-byte costs are always applied to maintain simplicity and prevent circumvention.

The additional cost makes EIP-2930 access lists economically irrational for gas optimization, effectively deprecating their use while maintaining compatibility.

Access List Size Impact

At a gas limit of 60M:

  • 1 address + 31,578 storage keys: 2400 + (31,578 × 1900) = 59,998,200 gas (~1,012 KB)

With data pricing (assuming all non-zero bytes):

  • 1 address + 18,866 storage keys: 3200 + (18,866 × 3180) = 59,998,280 gas (~604 KB)

Maximum Block Size Impact

The maximum possible Snappy compressed block size can be achieved by using access lists and calldata together at a ratio were the transaction keeps paying the lower calldata price.

At a gas limit of 60M, the maximum possible compressed block size will be reduced from 2.71 MiB to 2.13 MiB.

Backwards Compatibility

This is a backwards incompatible gas repricing that requires a scheduled network upgrade.

Requires updates to gas estimation in wallets and nodes. Normal usage patterns remain largely unaffected.

Test Cases

Case 1: Normal Transaction

  • Addresses: 5 (100 bytes, assume all non-zero)
  • Storage keys: 10 (320 bytes, assume all non-zero)
  • Old cost: 5 × 2400 + 10 × 1900 = 31,000 gas
  • New cost: 5 × 3200 + 10 × 3180 = 47,800 gas
  • Additional cost: 16,800 gas (54.2% increase)

Case 2: Large Access List Transaction

  • Addresses: 1000 (20,000 bytes, assume all non-zero)
  • Storage keys: 0
  • Old cost: 1000 × 2400 = 2,400,000 gas
  • New cost: 1000 × 3200 = 3,200,000 gas
  • Additional cost: 800,000 gas (33.3% increase)

Case 3: Combined Access List + Calldata

  • Addresses: 500 (10,000 bytes, assume all non-zero)
  • Calldata: 5,000 bytes
  • Old access list cost: 500 × 2400 = 1,200,000 gas
  • Old calldata cost: 5,000 × 4 = 20,000 gas (standard rate, avoiding floor)
  • New access list cost: 500 × 3200 = 1,600,000 gas
  • New calldata cost: Applied through EIP-7623 mechanism
  • Result: Can no longer circumvent EIP-7623 floor pricing

Reference Implementation

The following is the EELS (Ethereum Execution Layer Specification) implementation:

python
TX_ACCESS_LIST_NONZERO_BYTE_COST = Uint(40)
TX_ACCESS_LIST_ZERO_BYTE_COST = Uint(10)

def count_access_list_bytes(access_list: Tuple[Access, ...]) -> Tuple[int, int]:
    """
    Count zero and non-zero bytes in access list addresses and storage keys.
    
    Returns a tuple of (zero_bytes, nonzero_bytes).
    """
    zero_bytes = 0
    nonzero_bytes = 0
    
    for access in access_list:
        # Count bytes in address (20 bytes)
        for byte in access.account:
            if byte == 0:
                zero_bytes += 1
            else:
                nonzero_bytes += 1
        
        # Count bytes in storage keys (32 bytes each)
        for slot in access.slots:
            for byte in slot:
                if byte == 0:
                    zero_bytes += 1
                else:
                    nonzero_bytes += 1
    
    return zero_bytes, nonzero_bytes

def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
    """
    Calculates the gas that is charged before execution is started.
    """
    # ... existing calldata and base cost calculations ...
    
    access_list_cost = Uint(0)
    access_list_data_cost = Uint(0)
    
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction, 
                      BlobTransaction, SetCodeTransaction)):
        # Standard EIP-2930 access list functionality costs
        for access in tx.access_list:
            access_list_cost += TX_ACCESS_LIST_ADDRESS_COST
            access_list_cost += (
                ulen(access.slots) * TX_ACCESS_LIST_STORAGE_KEY_COST
            )
        
        # EIP-7981: Additional data cost for access list bytes
        zero_bytes, nonzero_bytes = count_access_list_bytes(tx.access_list)
        access_list_data_cost = (
            Uint(zero_bytes) * TX_ACCESS_LIST_ZERO_BYTE_COST +
            Uint(nonzero_bytes) * TX_ACCESS_LIST_NONZERO_BYTE_COST
        )
    
    return (
        Uint(
            TX_BASE_COST
            + data_cost
            + create_cost
            + access_list_cost
            + access_list_data_cost  # Added by EIP-7981
            + auth_cost
        ),
        calldata_floor_gas_cost,
    )

Security Considerations

Reduces maximum block size from access lists, improving network stability. The additional cost is proportional to data usage while maintaining access list utility for backwards compatibility.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Toni Wahrstätter, "EIP-7981: Increase access list cost[DRAFT]," Ethereum Improvement Proposals, no. 7981, 2024/12/27. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7981.