# Nethermind: EIP-2780 / EIP-7976 / EIP-7981 intrinsic gas review (Amsterdam)

I read the code only. Nothing was run against any network or node. Amsterdam/Glamsterdam is not live on mainnet, so full details are given.

## Pins (taken from api.github.com on 2026-10-27)
- Nethermind `master`: `d48833af88ec4224c86afadd1c596e8f619c406b` (NM below)
- execution-specs `forks/bogota`: `7f2f4c9bf6d73b09ecb8b24eb14d08bf0cf0247b` (ES below), `src/ethereum/forks/amsterdam/`
- go-ethereum `master`: `8817f99f92852f8ac53e596de6202dd9e3494065` (GE below)
- EIP text from ethereum/EIPs master. EIP-2780, EIP-7976 and EIP-7981 are all `status: Last Call`.

## Candidates

**None.** I found no case where Nethermind's intrinsic gas, floor gas, validity check or gas settlement differs from the spec at the pins above. The closest thing is the order of the validity checks (below). Rejection is the same either way, so I did not raise it as a candidate.

- Order of checks (not a candidate). NM `Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs#L1041-L1066` checks the EIP-8037 cap (`ExceedsCap`: execution > 16,777,216 or floor > cap) **before** checking `gas_limit < intrinsic` and `gas_limit < floor`. ES `transactions.py#L671-L684` checks the gas limit first, then the cap. Every one of these conditions makes the transaction invalid in both, so validity is the same. Only the error string can differ (for example, "intrinsic gas exceeds cap" rather than "insufficient intrinsic gas"). That could matter for exception mapping in tests. Severity: cosmetic.

## Rules checked and found correct

| Rule | Spec (ES unless stated) | Nethermind (NM) | Geth (GE) |
|---|---|---|---|
| Amsterdam turns on 2780/7976/7981/8038/8037; 8131 stays off | — | `src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs#L9-L27` | — |
| TX_BASE 12000, TX_VALUE_COST 6000 | `vm/gas.py#L143-L144` | `src/Nethermind/Nethermind.Core/GasCostOf.cs#L100-L101` | `core/state_transition.go#L172,L191` |
| Recipient term: cold 3000 for a non-self call; CREATE_ACCESS 12000 (9000+3000) for create; nothing for self-transfer | `transactions.py#L736-L747`, `vm/gas.py#L76-L90` | `src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs#L779-L780,L845-L850,L865-L880`; `src/Nethermind/Nethermind.Core/Eip8038Constants.cs#L15-L23` | `core/state_transition.go#L163-L194` (same) |
| Value term only on a non-create, non-self transfer with value > 0; a create with value pays no TX_VALUE_COST | `transactions.py#L741-L747` | `EthereumGasPolicy.cs#L870-L878` | `core/state_transition.go#L187-L192` |
| Calldata standard cost: tokens (zero=1, nonzero=4) × 4 | `transactions.py#L732-L734,L801-L810` | `IntrinsicGasCalculator.cs#L46-L51`, `EthereumGasPolicy.cs#L853-L854` | `#L93-L112` (16/4 per byte) |
| Init code: 2 per 32-byte word, rounded up, execution side only (not in floor) | `transactions.py#L741-L743,L779-L797`, `vm/gas.py#L864` | `src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs#L20-L23`; it is in `executionGas` but not in `floorBase` at `EthereumGasPolicy.cs#L792-L798` | `#L114-L119`; floor uses `intrinsicBaseGasEIP2780` only (`#L247-L249`) |
| Access-list entry cost under EIP-8038: 2900 per address, 2000 per key | `vm/gas.py#L147-L152` | `Eip8038Constants.cs#L10-L25`, `IntrinsicGasCalculator.cs#L69-L74` | `#L127-L140` |
| EIP-7981 surcharge: 80 tokens per address, 128 per key, ×16 (1280/2048), added to **both** execution and floor | `transactions.py#L749-L766,L782-L795` | tokens `IntrinsicGasCalculator.cs#L54-L57` (20×4, 32×4); execution side `#L74`; floor side `#L164` | execution `#L143-L157`; floor `#L211-L226` |
| EIP-7976 floor: len(data)×4 tokens ×16; floor base = TX_BASE + recipient + value | `transactions.py#L775-L786` | `IntrinsicGasCalculator.cs#L105-L106,L160-L164`; `EthereumGasPolicy.cs#L798` | `#L197-L255` |
| EIP-7702 per-auth execution cost 101×16+3000+3000+2×100 = 7816 | `vm/gas.py#L157-L162` | `IntrinsicGasCalculator.cs#L94-L98`, `Eip8038Constants.cs#L26-L29` | `#L85-L88` |
| Validity: gas ≥ intrinsic, gas ≥ floor, both ≤ TX_MAX_GAS_LIMIT, gas ≤ TX_MAX_TOTAL_GAS_LIMIT | `transactions.py#L640-L641,L671-L684` | `TransactionProcessor.cs#L1033-L1066` | `#L592-L595,L690-L695` |
| Floor applied after the refund cap; boundary where floor equals execution resolves to max() | `vm/gas.py#L1211-L1214` | `TransactionProcessor.cs#L1938-L1976` (`spentGasAfterFloor = Math.Max(operationGas, floorGasLong)`) | — |
| Block gas (EIP-7778) is pre-refund, floored | `vm/gas.py#L1216-L1222` | `TransactionProcessor.cs` `CalculateBlockGas` (after `Refund`) | — |
| Txpool and block validation use the same formula | — | `src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs#L35-L87` (IntrinsicGasTxValidator); `src/Nethermind/Nethermind.TxPool/Filters/MalformedTxFilter.cs#L30-L41,L87-L93`; `BlockValidator.cs#L341-L361` | — |
| Self-transfer detection when the sender has not been recovered yet | — | `EthereumGasPolicy.cs#L779-L780`. A missing sender is priced as non-self, the higher price, so a false reject is possible but not a false accept. The block validator recovers the sender and retries (`BlockValidator.cs#L351-L361`); the processor recovers it before computing intrinsic gas (`TransactionProcessor.cs#L296-L297`). | — |
| First Amsterdam block | — | Every term is gated on `IsEip2780/7976/7981/8038Enabled` of the block's spec. The memo is keyed on the spec instance and the self-transfer flag (`EthereumGasPolicy.cs#L756-L768`), so a pre-fork value is not reused across the fork. | — |

**Disproof attempts.** I recomputed each constant by hand: 3000+9000, 3000−100, 2100−100, 20×4×16=1280, 32×4×16=2048, and 101×16+3000+3000+200=7816. I checked that init code is not in the floor base in either ES or NM. I checked that a create with value gets no TX_VALUE_COST in ES, NM and GE. I checked that EIP-8131 (content floor) is off in Amsterdam, so the `IsEip8131Enabled` branch at `IntrinsicGasCalculator.cs#L163` does not apply.

**EIP prose against spec.** EIP-7981 says "added as a flat surcharge to the gas used calculation" (https://eips.ethereum.org/EIPS/eip-7981). ES, NM and GE all add it to both intrinsic and floor, which gives the same numbers.

## Known-check
- NethermindEth/nethermind searches for "2780", "7976", "7981", "intrinsic gas amsterdam" and "floor gas" (GitHub search API). Related closed PRs: #14468 (recover EIP-2780 senders in the block validator), #13929 and #12228 (EIP-8131 content floor, closed), #14944. None reports a mismatch with the spec. Result for the order-of-checks note: **not found**.
- execution-specs Test Release Tracker issues: #3889 `bogota-devnet@v2.0.0` (open), #3788 and #3787 (hegota, open). I did not inspect individual failures.
- hive.ethpandaops.io/glamsterdam/listing.jsonl: `eels/consume-engine` nethermind `2.3.0-preview+a183d792`, 44225 tests, 239 fails, 2026-10-26 (already indexed). I did not attribute those failures to the rules above.

## URLs read
- https://api.github.com/repos/NethermindEth/nethermind/commits/master
- https://api.github.com/repos/ethereum/execution-specs/commits/forks/bogota
- https://api.github.com/repos/ethereum/go-ethereum/commits/master
- https://github.com/NethermindEth/nethermind (shallow fetch at the pin)
- https://github.com/ethereum/execution-specs (shallow fetch at the pin)
- https://raw.githubusercontent.com/ethereum/go-ethereum/8817f99f92852f8ac53e596de6202dd9e3494065/core/state_transition.go
- https://raw.githubusercontent.com/ethereum/EIPs/master/EIPS/eip-2780.md, eip-7976.md, eip-7981.md
- https://api.github.com/search/issues (queries listed above)
- https://hive.ethpandaops.io/glamsterdam/listing.jsonl
