Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save holgerd77/27a75084876fb7466ee5372e23b0bfdb to your computer and use it in GitHub Desktop.

Select an option

Save holgerd77/27a75084876fb7466ee5372e23b0bfdb to your computer and use it in GitHub Desktop.
EIP-7928_BAL_Spec_Improvement_Suggestions_Opus

Spec: https://github.com/ethereum/EIPs/blob/fad1a1acab0a433970e47e0b73a4f8a28c220675/EIPS/eip-7928.md (2026-02-03)

"In Ethereum we strive for a lean and easy to implement protocol. With your experiences from implementing core BAL parts gained in this chat - when analyzing the EIP spec in @EIPs/EIPS/eip-7928.md would you see potential enhancements or changes to the spec itself, which would significantly reduce implementation effort on the one hand and have no or only litte downsides (if present, please name them) on the other side?"

2026-02-05, Cursor, Sonnet 4.5

EIP-7928 Block Access List Spec Improvement Proposals

Table of Contents

Recommendations for Simplification

1. CRITICAL: Clarify Opcode Gas Charging vs. Gas Calculation

Current Gap (Lines 114-124): The spec defines "pre-state validation" costs but doesn't explicitly state when in the execution flow to record addresses relative to opcode gas charging.

Problem we encountered: Modern EVM implementations have this flow:

  1. Gas calculation function runs (computes gas cost)
  2. Gas calculation returns
  3. Interpreter charges gas from available gas ← Can OOG here!
  4. Opcode execution function runs

The spec's "pre-state validation" table helps, but doesn't clarify if addresses should be recorded:

  • Before step 3 (after gas calc returns but before charging)? → Causes EXTRA addresses
  • After step 3 (after gas charged but before execution)? → Works perfectly BUT never runs if step 3 OOGs

Recommendation: Add to section 114 (Gas Validation Before State Access):

#### Opcode Gas Charging Sequence

For state-accessing opcodes, implementations typically:

1. Calculate gas cost (pre-state validation)
2. Return calculated cost to interpreter
3. Interpreter charges gas from available gas  ← OOG can occur here
4. Execute opcode function (performs actual state access)

For BAL inclusion:
- Addresses/slots MUST be recorded in step 4 (after gas charging succeeds)
- If OOG occurs in step 3, the target MUST NOT be included
- Exception: Once a Message is created and execution begins, all touched 
  addresses MUST be preserved even if subsequent OOG occurs (per "Exceptional 
  halts" rule)

Impact:

  • Benefit: Eliminates the architectural ambiguity we spent hours debugging
  • Benefit: Makes it clear opcode execution functions (not gas functions) are the recording point
  • Downside: Adds implementation details to spec
  • Assessment: Worth it - this is the #1 implementation trap

2. Add Concrete Implementation Pattern for Net-Zero Detection

Current Requirement (Lines 209, 192):

"If an account's balance changes during a transaction, but its post-transaction balance is equal to its pre-transaction balance, then the change MUST NOT be recorded"

"Implementations MUST check the pre-transaction value to correctly distinguish between actual writes and no-op writes"

Problem: The spec requires the logic but doesn't suggest how to track pre-transaction values efficiently.

Recommendation: Add subsection under "Recording Semantics by Change Type":

#### Implementation Pattern: Original Value Tracking

For net-zero detection (balance and storage), implementations SHOULD:

1. Maintain a separate map of original values (pre-transaction state)
2. Capture original value on FIRST modification attempt, not at tx start
3. Use "first-write-wins" semantics for the original value map
4. After transaction completes, compare final vs. original:
   - If equal: Remove from changes (convert storage to read if needed)
   - If different: Keep in changes

Example: If balance goes 100 → 50 → 100 during a transaction:
- First modification captures original=100
- Final balance=100
- Net-zero detected, remove from balance_changes

Impact:

  • Benefit: Provides clear implementation path, reduces guesswork by ~30%
  • Benefit: Prevents inefficient "track everything at tx start" approaches
  • Downside: Non-normative guidance in normative section (but clearly marked as SHOULD)
  • Assessment: High value, low cost

3. Strengthen EIP-7702 Delegation Timing Language (Line 244)

Current (Line 244):

"The delegation target MUST NOT be included during delegation creation or modification and MUST only be included once it is actually loaded as an execution target"

Remaining Ambiguity: "Loaded as an execution target" is vague. We spent time determining if this means:

  • When delegation is checked during gas calculation?
  • When delegation code is fetched?
  • When Message is created with delegated codeAddress?

Recommendation: Replace with:

The delegation target MUST be included when:
1. A Message object is created with the delegation target as its codeAddress, OR
2. The delegation target's code is loaded via _loadCode() or equivalent 
   during message preparation

The delegation target MUST NOT be included merely because:
- Delegation authorization was validated
- Delegation target address was read from delegating account's code field
- Without subsequent execution of the delegated code

Impact:

  • Benefit: Crystal clear boundary (Message creation = include)
  • Benefit: Aligns with natural implementation checkpoints
  • Downside: None
  • Assessment: Clean win

4. Add "Checkpoint Preservation" Implementation Note

Current Gap: The spec mentions exceptional halts preserve addresses (line 241) but doesn't explain how this interacts with typical checkpoint/revert mechanisms.

Problem we encountered: We initially thought addresses added after a checkpoint would be lost on revert. We had to implement special revert() logic to preserve them.

Recommendation: Add new subsection to "Gas Validation Before State Access":

#### Checkpoint and Revert Behavior

Most implementations use checkpoint/revert mechanisms for state rollback.
For BAL correctness:

**Address Touches (Preserved on Revert):**
- When a checkpoint reverts, touched addresses MUST remain in the BAL
- If no state changes survive the revert, addresses appear with empty lists
- Storage slots accessed during reverted execution MUST appear in storage_reads

**State Changes (Discarded on Revert):**
- Balance/nonce/code/storage changes from reverted execution are discarded
- Only the final post-transaction values are recorded

**Implementation Pattern:**
```python
def revert_checkpoint():
    # Restore state to checkpoint
    state = checkpoint_stack.pop()
    
    # Preserve addresses but discard their changes
    for addr in current_accesses:
        if addr not in checkpoint_accesses:
            checkpoint_accesses[addr] = empty_changes()
        # Merge storage reads (including reverted writes)
        checkpoint_accesses[addr].storage_reads |= current_accesses[addr].storage_reads
        checkpoint_accesses[addr].storage_reads |= current_accesses[addr].storage_writes.keys()

**Impact:**
- ✅ **Benefit**: Would have saved us 3-4 hours of debugging the `revert()` logic
- ✅ **Benefit**: Makes "exceptional halts" requirement implementable
- ❌ **Downside**: Adds implementation pseudocode (non-normative)
- **Assessment**: High value for implementers

---

### **5. Clarify "Opcode Execution Function Runs" Timing**

**Current (Line 121-123):**
> "Once pre-state validation passes, the target is accessed and included in the BAL."

**Remaining Issue from Our Implementation:**
This statement is **technically incorrect** for our architecture and likely others:

**Actual execution flow:**
1. Pre-state validation runs → passes
2. Gas calculation function **returns gas cost**
3. **Interpreter charges the gas** ← Can still OOG here!
4. **Only NOW** does "target is accessed" happen (opcode function runs)

The spec says "once pre-state validation passes, target is accessed" but there's a gap between validation passing and actual access.

**Recommendation:**
Refine lines 121-123 to:

```markdown
Once pre-state validation passes, the target WILL BE accessed and included 
in the BAL, provided the interpreter successfully charges the opcode gas.

If out-of-gas occurs when the interpreter attempts to charge the calculated 
gas cost (after pre-state validation completes but before the opcode 
executes), the target MUST NOT be included.

Once the opcode execution function begins (gas successfully charged), the 
target MUST be included in the BAL even if subsequent out-of-gas occurs 
during execution.

Impact:

  • Benefit: Closes the timing gap we discovered
  • Benefit: Makes spec architecturally accurate
  • Downside: More verbose
  • Assessment: Critical for correctness

6. Simplify Net-Zero Balance Requirement (Alternative Approach)

Current (Lines 207-216): Requires net-zero detection for balances with multiple edge cases.

Our Experience: This added significant complexity:

  • originalBalances Map infrastructure
  • API changes to addBalanceChange()
  • Special handling in multiple locations
  • ~20% of implementation effort

Alternative Recommendation: Option A (Status Quo): Keep requirement, add implementation note (covered in #2)

Option B (Radical Simplification): Make net-zero detection OPTIONAL:

If an account's balance changes during a transaction, but its post-transaction 
balance equals its pre-transaction balance:

- Implementations MAY omit the balance_change (recommended for size optimization)
- Implementations MAY include the balance_change (simpler implementation)

The sender and recipient addresses MUST still be included in AccountChanges 
regardless of which approach is chosen.

Impact:

  • Benefit: Reduces implementation complexity by ~20%
  • Benefit: Allows simpler implementations to skip this optimization
  • Downside: Non-deterministic BAL size (implementations could differ)
  • Downside: Parallel execution might need to handle both formats
  • Assessment: Only viable if BAL format variance is acceptable

Summary Table (Updated Spec)

Recommendation Implementation Effort Reduction Spec Impact Priority
1. Opcode Gas Charging Timing ⭐⭐⭐⭐⭐ ~40% +8 lines (clarification) CRITICAL
2. Net-Zero Implementation Pattern ⭐⭐⭐ ~15% +12 lines (guidance) High
3. EIP-7702 Delegation Boundary ⭐⭐⭐ ~15% +5 lines (clarification) High
4. Checkpoint/Revert Guidance ⭐⭐⭐⭐ ~25% +20 lines (pseudocode) High
5. Clarify "Target Accessed" Timing ⭐⭐⭐⭐ ~30% +4 lines (precision) High
6. Make Net-Zero Optional (B) ⭐⭐⭐⭐⭐ ~20% +3 lines, breaks determinism Low (controversial)

Top 3 Recommendations for Maximum Impact:

🥇 Recommendation #1: Opcode Gas Charging Timing

Must-have. Closes the critical gap between gas calculation and gas charging. This is where we lost the most debugging time.

🥈 Recommendation #4: Checkpoint/Revert Guidance

High value. The "exceptional halts preserve addresses" requirement is impossible to implement correctly without understanding how to preserve addresses across reverts.

🥉 Recommendation #5: Clarify "Target Accessed" Timing

High value. Fixes technical inaccuracy in current spec (line 121) that caused confusion about when recording should happen.


Overall Assessment

The updated spec is significantly better! The "Gas Validation Before State Access" section addresses ~50% of the ambiguities we encountered. The remaining recommendations would:

  • Combined benefit: Reduce implementation effort by additional ~40-50%
  • Combined cost: ~30 lines of additional spec text (mostly non-normative guidance)
  • Trade-off: Strongly positive - this EIP is complex enough that extra guidance is justified

The biggest remaining gap is Recommendation #1 (opcode gas charging timing), which would have saved us several hours of trial-and-error during the OOG debugging session we just completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment