Access Lists Specification
Flashblock-Level Access Lists (FAL) adapt EIP-7928 Block-Level Access Lists for chains that emit flashblocks, including Base. This specification defines their data structures, recording semantics, and validation rules.
Abstract
Section titled “Abstract”FAL records every account and storage location accessed during flashblock execution, along with post-execution values. This enables parallel disk reads, parallel transaction validation, and state reconstruction without full re-execution.
Motivation
Section titled “Motivation”Standard BAL (EIP-7928) targets L1 Ethereum where blocks are atomic units. Base produces flashblocks — incremental sub-block deltas streamed at 200ms intervals. FAL adapts the access-list concept to this architecture by:
- Operating on incremental flashblock deltas rather than complete blocks.
- Handling OP Stack-specific transaction types (L1 attributes, L1-to-L2 deposits).
- Tracking three separate fee vaults instead of a single
COINBASE. - Omitting beacon chain features absent from the OP Stack (EIP-4895 withdrawals, EIP-4788 beacon root, EIP-7002/7251 validator operations).
Key Differences from BAL
Section titled “Key Differences from BAL”| Aspect | BAL (EIP-7928) | FAL |
|---|---|---|
| Unit of operation | Complete block | Flashblock delta (incremental) |
| Hash storage | Block header | Flashblock metadata |
| Fee recipient | Single COINBASE | Three OP Stack fee vaults |
| Transaction types | Standard + withdrawals | Standard + L1 attributes + L1-to-L2 deposits |
| Beacon chain features | Included | Omitted |
OP Stack Fee Vaults
Section titled “OP Stack Fee Vaults”Instead of recording balance changes against COINBASE, FAL tracks three separate fee vault addresses:
| Vault | Address | Purpose |
|---|---|---|
| Sequencer Fee Vault | 0x4200000000000000000000000000000000000011 | Priority fees |
| Base Fee Vault | 0x4200000000000000000000000000000000000019 | Base fees |
| L1 Fee Vault | 0x420000000000000000000000000000000000001a | L1 data fees |
L1 Attributes Transaction
Section titled “L1 Attributes Transaction”The first transaction in each block (index 0) is the L1 attributes transaction. It updates the L1Block contract (0x4200000000000000000000000000000000000015) storage slots:
| Slot | Content |
|---|---|
| 0 | L1 block number |
| 1 | L1 base fee |
| 2 | L1 timestamp |
| 3 | Batcher hash |
| 4 | Sequence number |
| 5 | Blob base fee |
| 6 | L1 block hash |
L1-to-L2 User Deposits
Section titled “L1-to-L2 User Deposits”L1-to-L2 deposits are treated as regular transactions with block access indices 1..n. The FAL records sender and recipient addresses with their balance changes.
Specification
Section titled “Specification”Data Structures
Section titled “Data Structures”FlashblocksPayloadV1
Section titled “FlashblocksPayloadV1”pub struct FlashblocksPayloadV1 { pub payload_id: PayloadId, pub index: u64, pub base: Option<ExecutionPayloadBaseV1>, pub diff: ExecutionPayloadFlashblockDeltaV1, pub metadata: Value, // Contains "flashblock_access_list"}FlashblockAccessList
Section titled “FlashblockAccessList”pub struct FlashblockAccessList { pub min_tx_index: u64, // Inclusive starting transaction index pub max_tx_index: u64, // Exclusive ending transaction index pub account_changes: Vec<AccountChanges>, // All account state changes pub fal_hash: B256, // Keccak-256 of RLP-encoded changes}RLP Data Structures
Section titled “RLP Data Structures”The following Python-style type aliases define the RLP encoding:
Address = bytes # 20-byte Ethereum addressStorageKey = bytes # 32-byte storage slot keyStorageValue = bytes # 32-byte storage valueCodeData = bytes # Variable-length bytecodeBlockAccessIndex = uint16Balance = uint256Nonce = uint64
# Constants (OP Stack adapted)MAX_TXS = 30_000MAX_SLOTS = 300_000MAX_ACCOUNTS = 300_000MAX_CODE_SIZE = 24_576MAX_CODE_CHANGES = 1
# Change structuresStorageChange = [BlockAccessIndex, StorageValue]BalanceChange = [BlockAccessIndex, Balance]NonceChange = [BlockAccessIndex, Nonce]CodeChange = [BlockAccessIndex, CodeData]SlotChanges = [StorageKey, List[StorageChange]]
AccountChanges = [ Address, List[SlotChanges], # storage_changes List[StorageKey], # storage_reads List[BalanceChange], # balance_changes List[NonceChange], # nonce_changes List[CodeChange] # code_changes]
FlashblockAccessList = List[AccountChanges]Scope and Inclusion Rules
Section titled “Scope and Inclusion Rules”The following must be included in the FlashblockAccessList:
- Addresses with state changes — any account whose storage, balance, nonce, or code was modified.
- Inspection targets — targets of
BALANCE,EXTCODESIZE,EXTCODECOPY,EXTCODEHASH. - Call targets — targets of
CALL,CALLCODE,DELEGATECALL,STATICCALL. - Creation targets — addresses produced by
CREATEandCREATE2. - Transaction parties — senders and recipients of every transaction.
- Fee vault addresses — all three OP Stack fee vaults listed above.
SELFDESTRUCTbeneficiaries — addresses receiving self-destructed balances.- System contracts — L1Block contract (
0x4200...0015) and EIP-2935 block hash contract. - L1-to-L2 deposit recipients — recipients of cross-chain deposits.
- Precompiled contracts — when accessed during execution.
- L1Block during L1 attributes execution — always included in the first flashblock.
Addresses without state changes must still appear with empty change lists.
Ordering and Determinism
Section titled “Ordering and Determinism”To ensure deterministic encoding:
- Addresses are sorted lexicographically (bytewise ascending).
- Storage keys are sorted lexicographically within each account.
- Block access indices are sorted in ascending order within each change list.
BlockAccessIndex Assignment
Section titled “BlockAccessIndex Assignment”| Index Value | Assigned To |
|---|---|
0 | Pre-execution system contracts and L1 attributes transaction (first flashblock only, where min_tx_index = 0). |
min_tx_index … max_tx_index - 1 | Transactions within the flashblock, including L1-to-L2 deposits and regular transactions. |
n + 1 | Post-execution system contracts (final flashblock only). |
Recording Semantics
Section titled “Recording Semantics”Storage
Section titled “Storage”Writes (recorded in storage_changes):
- Any value change where the post-execution value differs from the pre-execution value.
- Zeroing a slot (pre-execution value exists, post-execution value is zero).
Reads (recorded in storage_reads):
SLOADwithout a corresponding write.- No-op writes where
SSTOREis called but the post-execution value equals the pre-execution value.
Balance Changes
Section titled “Balance Changes”Post-transaction balances are recorded for:
- Senders — after gas cost, transferred value, and L1 data fee are deducted (including gas refunds).
- Recipients — only when transferred value is greater than zero.
- Fee vaults — updated balance after each transaction.
SELFDESTRUCT/SENDALLbeneficiaries — balance after receiving funds.- Deposit recipients — balance after the deposit is credited.
For zero-value transfers, the address is included but omitted from balance_changes.
Code Changes
Section titled “Code Changes”Post-transaction runtime bytecode is recorded for:
- Contracts deployed via
CREATEorCREATE2. - Contracts modified via
SELFDESTRUCT(if applicable). - EIP-7702 delegation indicators.
Nonce Changes
Section titled “Nonce Changes”Post-transaction nonces are recorded for:
- EOA senders (incremented by transaction execution).
- Successful
CREATEperformers (nonce incremented). - Newly deployed contracts (nonce set to 1).
- EIP-7702 authorities (nonce incremented on successful delegation).
OP Stack Edge Cases
Section titled “OP Stack Edge Cases”- Precompiles: Include when accessed. Record a balance change if the precompile receives value.
SENDALL: Record both sender (balance to zero) and beneficiary (balance increased).SELFDESTRUCT: Include without nonce/code changes. Record balance-to-zero if pre-transaction balance was positive. Include storage reads.- Gas refunds: The final sender balance (after refunds) is the recorded value.
- Exceptional halts (reverts/out-of-gas): Record the final nonce and balance of the sender and fee vaults. Include all accessed addresses even if execution was reverted.
- EIP-7702: The authority address must be included with nonce/code changes after a successful delegation operation, and with empty changes if the delegation fails.
Empty State
Section titled “Empty State”When no state changes exist in a flashblock:
account_changesis an empty list.fal_hashequals the Keccak-256 of an empty RLP list:0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347.
Validation
Section titled “Validation”The following pseudocode describes FAL validation:
def validate_fal(flashblock, provided_fal): # 1. Extract the FAL from flashblock metadata fal = extract_fal(flashblock.metadata)
# 2. Verify the provided hash matches the encoded account_changes encoded = rlp_encode(fal.account_changes) expected_hash = keccak256(encoded) assert fal.fal_hash == expected_hash, "FAL hash mismatch"
# 3. Execute the flashblock and collect actual accesses actual_accesses = execute_flashblock(flashblock) actual_encoded = rlp_encode(actual_accesses) actual_hash = keccak256(actual_encoded)
# 4. Verify actual execution matches the provided FAL assert actual_hash == fal.fal_hash, "Execution mismatch"
return TruePerformance
Section titled “Performance”FAL enables the following optimizations:
- Parallel disk reads — storage slots and account data can be prefetched across transactions.
- Parallel transaction validation — independent transactions can be validated concurrently.
- State reconstruction without execution — post-state can be derived from the access list alone.
- Compact representation — expected size is approximately 40—50 KiB compressed on average, comparable to BAL.
Source
Section titled “Source”Canonical specification: docs/specs/access-lists.md