---
name: erigon-implement-eip
description: Implement a new EIP for a hardfork under development in Erigon. Use when the user asks to implement, port, or wire up an EIP — covers spec lookup, dep analysis, prior-work check, implementation, lint, tests, and a wrap-up saved to `agentspecs/`.
argument-hint: "<fork> <eip-number> [devnet]"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill
---

# Implement EIP-$1 in the $0 fork

## Step 1 — Locate and understand the EIP specification

Fetch the specification from:

```
https://eips.ethereum.org/EIPS/eip-$1
```

Read the spec end-to-end and make sure you understand it. If there are any ambiguities or things that do not make sense in the EIP, bring these up and ask for clarifications. Once understood, map the EIP to the codebase — identify which packages, files, and existing constructs the EIP will touch.

## Step 2 — Find dependent and referenced EIPs

Identify any other EIPs that EIP-$1 depends on or references. Each such EIP can be fetched using the same URL pattern as in Step 1, substituting its number:

```
https://eips.ethereum.org/EIPS/eip-<number>
```

Try to understand these additional EIPs and map them to the code. If there are any ambiguities or contradictions with the new EIP, make sure to raise these and ask questions.

## Step 3 — Identify the hardforks of referenced / interplaying EIPs

Identify which hardforks contain the referenced EIPs, as well as any other EIPs already in the codebase that interplay with the new one. This provides more context and knowledge when analysing the code.

A list of forks and the EIPs they contain can be found at:

```
https://eips.ethereum.org/meta
```

They are organised as "meta EIPs" and usually start with `Hardfork Meta` followed by the name of the hardfork.

### Fork naming

More recent forks use portmanteau naming to combine the execution-layer (EL) fork name and the consensus-layer (CL) fork name. EL forks are named after cities; CL forks are named after stars. For example, **Glamsterdam** is a portmanteau of **Gloas** (CL) and **Amsterdam** (EL).

### EL vs CL scope

Some EIPs only affect the EL, others only the CL, and some affect both. Erigon has both an EL implementation (under the `execution` package) and a CL implementation (under the `cl` package), so all EIPs apply to one or both layers in Erigon.

### Reading a meta EIP

In some cases fork meta EIPs list which EIPs got included; in other cases they provide a summary of the changes instead of an EIP list.

The meta EIP for the **current fork under development** usually has these lists:
- **CFI** — EIPs Considered For Inclusion
- **SFI** — EIPs Scheduled For Inclusion
- **PFI** — EIPs Proposed For Inclusion
- **DFI** — EIPs Declined For Inclusion

## Step 4 — Locate the latest devnet specification

If a devnet name was provided as the third argument (`$2`), fetch its specification from:

```
https://notes.ethereum.org/@ethpandaops/$2
```

### What devnets are

Devnets are short-lived test networks where changes for a hardfork under development are validated. Core developers iterate through a series of devnets, progressively adding EIPs and refining their specs, until all hardfork changes are ready for release.

### What the devnet spec contains

A devnet spec typically lists:
- All EIPs being tested in that devnet
- What is new or changed compared to the previous devnet (e.g. newly added EIPs, revised EIP behaviour, client-config tweaks)

Use the spec to pinpoint which changes for EIP-$1 are in scope for this devnet and how they differ from prior devnet iterations. This is especially valuable when an earlier version of the EIP was already implemented for a previous devnet — the delta between devnets tells you exactly what needs updating (and ties directly into Step 6 below).

### When this step does not apply

The devnet argument is optional. If none was provided, skip this step — this is expected when an EIP is being implemented before any devnet spec exists for it.

## Step 5 — Reference the Ethereum Yellow Paper

Reference the Ethereum Yellow Paper for deeper information about the Ethereum protocol. It can be found at:

```
https://ethereum.github.io/yellowpaper/paper.pdf
```

Note however that the current version of it covers up to and including the Shanghai hard fork. Changes to the protocol after Shanghai can be inferred via the meta EIPs for the subsequent forks after Shanghai.

The yellow paper provides deep base knowledge that can be used to confirm correctness up to Shanghai, and later for parts that haven't been changed by subsequent hard forks. For parts that have been changed in subsequent hard forks, the corresponding EIP specs from those hardforks — read in chronological order — become the main source of truth.

## Step 6 — Check for previous work on this EIP

Check whether any previous work for this EIP has already been done in the codebase. This may happen if we've previously implemented this EIP but against an older / outdated spec for a previous devnet.

In this case, analyse the current code and compare it to the latest specs. Implement the necessary changes to address any discrepancies that you find.

## Step 7 — Implement the change

Implement the EIP changes in the code, based on the understanding gathered in Steps 1–6 and the codebase mapping you produced. Touch the packages, files, and existing constructs identified during that mapping.

If during implementation you discover something that is unclear or appears to contradict the spec, stop and ask for clarification rather than guessing.

After making code changes, run `make lint` and fix any reported issues. The linter is non-deterministic — run it repeatedly until it is clean.

## Step 8 — Run local tests

Run local tests using the `/erigon-test-all` skill. Analyse and fix any failures **in the implementation** (see also "Question the tests — do not silently fix them" below). Keep iterating until all tests are fixed.

### Which EL tests matter most

The most important tests when implementing a new EIP for the EL are the EEST spec test shards, exercised by the `cmd/evm` runners (`statetest`, `blocktest`, `enginextest`) via the Makefile targets:

- `make eest-spec-statetests-stable` / `…-devnet` — state-tests against the stable/devnet EEST fixtures
- `make eest-spec-blocktests-stable-sequential` / `…-devnet` — blockchain-tests against the stable/devnet EEST fixtures. The devnet shard always runs under `ERIGON_EXEC3_PARALLEL=true` (the in-development hardfork requires it); the `…-sequential` shard pins `ERIGON_EXEC3_PARALLEL=false`.
- `make eest-spec-blocktests-stable-parallel` — same fixtures as `…-stable-sequential` but with `ERIGON_EXEC3_PARALLEL=true`; useful for catching parallel-only regressions on stable fixtures.
- `make eest-spec-enginextests-stable-sequential` — engine-x tests against the stable EEST fixtures with `ERIGON_EXEC3_PARALLEL=false`. No devnet variant: the devnet tarball doesn't yet ship `blockchain_tests_engine_x/`.
- `make eest-spec-enginextests-stable-parallel` — same fixtures as `…-stable-sequential` but with `ERIGON_EXEC3_PARALLEL=true`; useful for catching parallel-only regressions on engine-x stable fixtures.
- `make eest-spec-enginextests-benchmark-{1m,5m,10m,30m,60m,100m,150m}-{sequential,parallel}` — engine-x tests against the per-gas-target benchmark fixtures, with `--time` per-test stats. Each gas target has a `-sequential` (`ERIGON_EXEC3_PARALLEL=false`) and `-parallel` (`ERIGON_EXEC3_PARALLEL=true`) variant.
- `make eest-spec-blocktests-stable-race-{pre-cancun,cancun,prague,osaka}-{sequential,parallel}` and `make eest-spec-blocktests-devnet-race-amsterdam` — race-detector variants split by fork. Each stable-race sub-shard has a `-sequential` / `-parallel` pair; the `-parallel` siblings exercise parallel exec3 under the race detector. The `blocktests-devnet-race-amsterdam` shard is always parallel (matches the non-race devnet behaviour).

The shard list / failure budgets / `exec3-parallel` flags are defined in `tools/eest-spec-shards.yml` (single source of truth shared with the CI workflow and the local runner script). See `EEST_SPEC_SHARDS` / `EEST_SPEC_RACE_SHARDS` in the root `Makefile` for the partition into non-race vs race targets.

**Pitfall: stale `evm` / `evm.race` binary.** When iterating on an EIP implementation, always invoke shards via `make eest-spec-<shard>` rather than `bash tools/run-eest-spec-test.sh <shard>` — the make target lists `evm` (or `evm.race`) as a prereq and `go build` is cache-aware, so a fresh binary is built before each run. The script invoked directly **bypasses** that rebuild, so the runners exercise whatever `build/bin/evm{,.race}` happens to be on disk against current fixtures — silently inflating failures (e.g. devnet shards "regressing" by thousands of tests) or hiding regressions when comparing budgets before/after a change.

For an EIP on a hardfork under development, the **`-devnet`** shards are the primary signal; the `-stable` shards (and `-benchmark-*` shards, where applicable) are regression checks against prior hardforks.

### Where the test fixtures come from

These protocol tests are defined by JSON test fixture files inside release tarballs (`eest_stable.tar.gz`, `eest_devnet.tar.gz`, `eest_benchmark.tar.gz`) pinned by `test-fixtures.json` and lazy-downloaded + extracted by `make test-fixtures-eest` (which the `eest-spec-*` Makefile targets depend on) into `test-fixtures-cache/<key>/`. The `cmd/evm` runners walk the extracted JSON files directly.

These test fixture files are generated by the Ethereum Python spec reference implementation, which can be found at:

```
https://github.com/ethereum/execution-specs
```

The code version which generates the test fixtures for a given hard fork lives on a branch following the `forks/<fork_name>` naming convention. For example, if the fork name is `osaka` then the branch name will be `forks/osaka`. The branch for the user-provided hard fork under development is `forks/$0`.

Sometimes new EIPs under development may not be in the `forks/$0` branch yet — they may be on a separate feature branch. To find it, look for a branch name that contains the provided EIP number `$1`. Once you find a branch (or several branches), ask the user to confirm which one to use.

The point of identifying the correct branch is to **read and analyse the right reference code** — i.e. the version of the Python spec implementation that corresponds to the fixtures we are testing against. Nothing in the local repo needs to be updated; this is purely about which code version the agent downloads / reads for cross-referencing while debugging or validating behaviour.

### Question the tests — do not silently fix them

It is **VERY IMPORTANT** to question the tests and validate they are doing the correct thing as per the EIP specifications. This applies to both EL spec tests (driven by the tarballs pinned in `test-fixtures.json`) and CL spec tests (driven by `cl/spectest` — see "Running CL spec tests" below).

Sometimes some of the protocol spec tests may have bugs or mistakes in them due to inaccurate implementation in the upstream reference implementations (the Python `execution-specs` for the EL, the `consensus-spec-tests` for the CL).

Or some of our other local tests (outside of the ones driven by the EEST tarballs pinned in `test-fixtures.json`, the `execution/tests/legacy-tests` submodule, or the `cl/spectest` fixtures) may have gaps in them, test for the wrong thing, or test for logic from before the new EIP — in which case the test should be amended to be explicitly for the correct corresponding previous hardfork, and a new test should be written for the new hardfork which this EIP will go into, covering both the old logic (prior to this EIP) and the new logic (after it).

When you run into such situations **do not attempt to fix those tests**. Instead write a summary of your findings and ask for them to be reviewed and new guidance to be given.

### Running CL spec tests

If the EIP affects the CL (or has any CL-side touchpoints), also run the consensus-spec tests under `cl/spectest`. These mirror what the `Consensus spec` GitHub workflow (`.github/workflows/test-integration-caplin.yml`) runs.

The flow is:

```
cd cl/spectest
make tests        # downloads the consensus-spec-tests fixture tarball (via tools/test-fixtures.sh) and unpacks it
make mainnet      # runs the full mainnet CL spec test suite
```

For faster iteration when only one fork is relevant, the `cl/spectest/Makefile` exposes per-fork targets such as `make capella`, `make deneb`, `make electra`, `make fulu`, etc. Use the one matching the fork the EIP belongs to. If no per-fork target exists yet for the new fork, add one or run `CGO_CFLAGS=-D__BLST_PORTABLE__ go test -run=/mainnet/<fork>/ -v --timeout 30m` from `cl/spectest` (the `CGO_CFLAGS` flag mirrors the Makefile and is required for the BLS portable build).

If `make tests` fails (e.g. fixture download issue), the CI workflow treats it as a soft skip — but locally you should investigate and fix it before relying on the CL test results.

## Step 9 — Wrap up

Once implementation is complete and tests are passing, produce a wrap-up summary of all the work done. The summary should cover:

- The EIP being implemented and the fork it targets
- All packages, files, and constructs that were touched
- Key design decisions and any deviations from the spec (with justification)
- Dependent / referenced EIPs that influenced the implementation
- Any outstanding questions, unresolved ambiguities, or test discrepancies that were flagged for review
- Test coverage status — which `eest-spec-*` shards now pass (and CL `cl/spectest` results, if applicable), and any that were skipped or remain open

Include the summary in your final reply, and also save it as a markdown file at:

```
agentspecs/eip-$1-implementation/summary.md
```

Create the `agentspecs/eip-$1-implementation/` directory if it does not already exist. The `agentspecs/` directory is gitignored, so these notes stay local.
