Methodology
The proposed gas parameters come from the actual time each execution client takes to run
a targeted set of benchmarks: runtimes are collected from the Benchmarkoor API by
benchmarkoor-fetch and turned into gas numbers by evm-gasfit.
Five stages turn a wall-clock measurement into a gas number:
- Benchmark data collection — synthetic blocks stress one operation at a time; Benchmarkoor records each block's runtime and opcode counts.
- Runtime model — an NNLS regression isolates the per-unit runtime of each opcode, per client.
- Glue adjustment — the overhead of the supporting opcodes that surround the target in each fixture is netted out.
- Runtime → gas — the adjusted runtime is converted to gas at a fixed anchor rate.
- Worst-case selection — the proposal takes the slowest client for each parameter, then evaluates any derived parameters.
1. Benchmark data collection
The proposed gas costs are grounded in the actual time each execution client takes to run a specific set of benchmarks. Similar to the methodology of the Gas Cost Estimator project, we generate synthetic blocks that isolate and stress individual EVM operations and use them to derive the various gas parameters.
Concretely, to benchmark a single operation, different blocks are created by varying the number of times the target operation is executed and by changing the parameter values passed to it. These test blocks come from the EEST benchmark suite. The raw data that feeds this analysis was queried from the following two Benchmarkoor suites:
1abf5c96ab2bbf14d88b18464da7445e
The Benchmarkoor tool then runs each block and collects the metrics we need: the block's
total execution time and the number of times each operation was executed. Each block is
run multiple times on every client to account for variability in execution time. Runtimes
are pulled from the Benchmarkoor API by benchmarkoor-fetch; the benchmarks
ran on the amsterdam fork and runs are selected by the
.*-full.* run-id pattern. The compute suite drives the target-opcode fits,
while the stateful suite feeds the glue-opcode analysis. Each block contributes one row to
the model: a measured test_runtime_ms alongside the count of every opcode it
executed.
2. Runtime model (NNLS)
evm-gasfit solves one non-negative least squares (NNLS) regression per
(opcode spec, model variant, client) combination. It models the measured
runtime of each fixture as a linear function of how many times the target opcode runs:
test_runtime_ms = intercept + target_coef · opcount + Σ (param_i · opcount · value_i)
The target_coef — the per-unit runtime of the operation under study — is the
quantity we care about. The trailing sum carries variable-cost terms: the supporting gas
parameters whose cost is netted out of the fixture (for example the cold-access surcharge
that rides along with a warm access) get their own fixture parameters, while a state-access
operation with a single flat cost collapses to one coefficient. Combined operations such as
a cold write are fit whole — as a single access-plus-write cost — and the write-only delta
is recovered downstream as a derived parameter, not split out inside
the regression. Parameters that are constant across all
fixtures are dropped automatically — their column is indistinguishable from
target_coef — and a fit is skipped entirely if there are too few
observations or the opcount never varies.
The non-negativity constraint is the point of NNLS: gas costs cannot be negative, and an unconstrained fit would let correlated features take opposite signs to cancel noise. NNLS instead drives such a coefficient to exactly zero or spreads the signal across the remaining features.
Inference is a non-parametric bootstrap over the rows of the design
matrix (1000 iterations). Each coefficient gets a p-value — the bootstrap share of
near-zero estimates — and a 2.5 / 97.5 percentile confidence interval. A fit is flagged
as a poor-fit selection when its p-value exceeds 0.05 or its R²
falls below 0.5. Fits cover 6 clients:
besuerigonethrexgethnethermindreth
See the runtime model page for every per-fit regression summary, or the evm-gasfit NNLS docs for the full derivation.
3. Glue adjustment
Glue adjustment is enabled.
A benchmark never measures an opcode in isolation: the recorded runtime also includes the
glue opcodes that set up arguments, advance the program counter, and clean the
stack (pushes, dups, loads, control flow). Because NNLS attributes the entire
intercept + target_coef · opcount budget to the target, glue that scales with
the loop body inflates the estimate. Glue adjustment removes that overhead before the
runtime is converted to gas.
evm-gasfit works from a fixed set of canonical glue opcodes, fit in
dependency tiers (pure opcodes first, then cycle, then mixed tiers that lean on the
already-priced ones). The adjustment runs in three steps:
- Detect which glue opcodes actually contaminated each measurement, by
correlating each glue opcode's per-fixture count against the target
opcountand recording the count ratioΔglue / Δopcount. - Estimate a per-unit runtime (
glue_runtime_ms) for every (client, glue opcode) with its own tiered NNLS fit. - Subtract the total glue overhead from the target coefficient:
adjusted_target_coef = max(0, target_coef − Σ_g ratio_g · glue_runtime_ms_g)
A glue partner only contributes to the sum when its own fit clears the same p-value (< 0.05) and R² (≥ 0.5) gates; the confidence interval shifts with it and clips at zero. Skipped fits — targets left without a glue correction — are listed under Warnings. See the glue page for the per-fit detail and the evm-gasfit glue docs for the tier structure.
4. Runtime → gas
Each adjusted runtime coefficient is converted to gas at a fixed anchor rate of
100,000,000 gas/s (100 Mgas/s) — the
throughput EIP-8038 targets. With runtime_ms in milliseconds:
new_gas = ⌈ anchor_rate · runtime_ms / 1000 ⌉
Any op whose measured cost implies a slower throughput than the anchor lands above its current price and is flagged for a bump; the ceiling keeps the result an integer.
5. Worst-case selection & derived parameters
A single gas parameter is fit many times — once per client, sometimes across several fixtures. The proposal consolidates those in two passes, always erring toward the slowest observation so the cost holds on every implementation:
- Per client — among the rows that pass the p-value and R² gates, take
the one with the largest runtime; if none qualify, the best available is kept and
flagged
poor_fit. - Across clients — take the client with the largest runtime.
Finally, derived parameters (those defined in terms of others) are evaluated in
declaration order against the fitted values, the fork baseline, and any
new_params integer baselines, using a small arithmetic-only expression
language. The client comparison shows how
far the worst client sits from the rest of the field. The
evm-gasfit gas-params docs
cover the selection and derivation rules in full.
Parameter → model map
Every proposed value traces back to a specific set of model fits or to a formula over other parameters. The two tables below are the full provenance for this run, built directly from the fit artifacts.
Estimated parameters
Each parameter below is fit by NNLS (stage 2) over the listed model specs — the worst-case selector (stage 5) then picks the binding client and combo. Target opcodes are the operations whose runtime the fixtures stress; fixtures are the benchmark tests that feed the regression; coefficient is the regression term read off as the per-unit runtime. The exact winning combo and binding client per parameter are on the new-gas and goals pages.
| Parameter | Presets | Target opcodes | Fixtures | Coefficient |
|---|---|---|---|---|
COLD_STORAGE_ACCESS |
cold_storage_sload, cold_storage_sstore_access |
SLOAD, SSTORE |
test_sload_bloated, test_sstore_bloated |
target_coef |
COLD_STORAGE_WRITE |
cold_storage_sstore_write |
SSTORE |
test_sstore_bloated |
target_coef |
COLD_ACCOUNT_NOCODE_ACCESS |
cold_account_nocode_access |
BALANCE, CALL, CALLCODE, DELEGATECALL, EXTCODECOPY, EXTCODEHASH, EXTCODESIZE, STATICCALL |
test_account_access |
target_coef |
COLD_ACCOUNT_CODE_ACCESS |
cold_account_code_access |
BALANCE, CALL, CALLCODE, DELEGATECALL, EXTCODECOPY, EXTCODEHASH, EXTCODESIZE, STATICCALL |
test_account_access |
target_coef |
COLD_ACCOUNT_NOCODE_WRITE |
cold_account_nocode_write |
CALL, CALLCODE |
test_account_access |
target_coef |
COLD_ACCOUNT_CODE_WRITE |
cold_account_code_write |
CALL, CALLCODE |
test_account_access |
target_coef |
WARM_ACCESS |
warm_account_access, warm_storage_access_sload |
BALANCE, CALL, CALLCODE, DELEGATECALL, EXTCODEHASH, EXTCODESIZE, STATICCALL, SLOAD |
test_ext_account_query_warm, test_storage_sload_same_key_benchmark |
target_coef |
Derived parameters
These carry no per-client fit. They are evaluated in declaration order (stage 5)
over the estimated parameters above, the osaka
fork baseline, and the new_params integer baselines, using an
arithmetic-only expression language (max, + − × ÷, integer
literals). The formulas are taken verbatim from fit.yaml.
| Parameter | Formula |
|---|---|
STORAGE_WRITE |
max(0, COLD_STORAGE_WRITE - COLD_STORAGE_ACCESS) |
ACCOUNT_WRITE |
max(0, COLD_ACCOUNT_CODE_WRITE - COLD_ACCOUNT_CODE_ACCESS, COLD_ACCOUNT_NOCODE_WRITE - COLD_ACCOUNT_NOCODE_ACCESS) |
REFUND_STORAGE_CLEAR |
COLD_STORAGE_WRITE * 4800 / 5000 |
TX_ACCESS_LIST_STORAGE_KEY |
COLD_STORAGE_ACCESS |
TX_ACCESS_LIST_ADDRESS |
max(COLD_ACCOUNT_CODE_ACCESS, COLD_ACCOUNT_NOCODE_ACCESS) |
Gas-cost baseline
Proposed values are compared against the osaka
gas-cost table — distinct from the amsterdam fork the benchmarks ran on. The
baseline is the osaka GasCosts patched with any config
overrides and new_params baselines, and it is the intended cost reference for
the EIP-8038 proposal.
Reproducibility
Re-run the full pipeline end to end:
make fetch # → data/raw/ (needs a Benchmarkoor token in secrets.json)
make gasfit # → data/gasfit/
make site # renders this site into site/
The committed data/ directory makes the published site self-contained:
every figure and table on these pages traces back to those artifacts.