Skip to content

[codex] add dynamic custom precompile gas meter#3644

Open
codchen wants to merge 1 commit into
codex/evmonly-staking-precompilefrom
codex/evmonly-staking-dynamic-gas
Open

[codex] add dynamic custom precompile gas meter#3644
codchen wants to merge 1 commit into
codex/evmonly-staking-precompilefrom
codex/evmonly-staking-dynamic-gas

Conversation

@codchen

@codchen codchen commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds dynamic gas accounting for evm-only custom precompiles, with the staking precompile using the shared meter through the existing store, balance-transfer, and log boundaries.

Changes

  • Add a reusable precompile gas meter for base gas, SLOAD/SSTORE-style storage costs, keccak slot derivation, native value transfers, and log emission.
  • Wrap storageBackedStore, nativeBalanceTransfer, and log emission in the custom precompile adapter so gas is charged based on the actual execution path.
  • Reduce staking RequiredGas to only base/input gas, avoiding double charging now that state access is dynamically metered.
  • Add executor coverage for dynamic store gas and update staking lifecycle tests to use realistic gas limits.

Validation

  • go test ./giga/evmonly/...
  • golangci-lint v2.8.0 run ./giga/evmonly/...

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 59.66851% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.14%. Comparing base (550f6fa) to head (bd57e4c).

Files with missing lines Patch % Lines
giga/evmonly/precompile_gas.go 68.46% 23 Missing and 18 partials ⚠️
giga/evmonly/precompile_adapter.go 34.69% 19 Missing and 13 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@                         Coverage Diff                          @@
##           codex/evmonly-staking-precompile    #3644      +/-   ##
====================================================================
- Coverage                             58.15%   58.14%   -0.01%     
====================================================================
  Files                                  2187     2185       -2     
  Lines                                178444   178547     +103     
====================================================================
+ Hits                                 103769   103824      +55     
- Misses                                65379    65409      +30     
- Partials                               9296     9314      +18     
Flag Coverage Δ
sei-chain-pr 56.21% <59.66%> (-0.11%) ⬇️
sei-db 70.41% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
giga/evmonly/precompiles/staking/staking.go 23.91% <100.00%> (-0.40%) ⬇️
giga/evmonly/precompile_adapter.go 66.35% <34.69%> (-8.65%) ⬇️
giga/evmonly/precompile_gas.go 68.46% <68.46%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedJun 29, 2026, 12:37 PM

@codchen codchen marked this pull request as ready for review June 26, 2026 03:42
@cursor

cursor Bot commented Jun 26, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes consensus-relevant gas and refund behavior for custom precompiles and staking; incorrect metering could cause unexpected OOG or undercharging, though coverage targets store OOG and staking e2e paths.

Overview
Introduces dynamic gas accounting for evm-only custom precompiles so state work is paid from the transaction gas budget instead of a flat upfront fee.

RunAndCalculateGas now uses a shared precompileGasMeter: it charges RequiredGas first, then meters Keccak for store key/slot derivation, EIP-2929/2200-style SLOAD/SSTORE (including refunds and access lists), native value transfers, and log emission. The adapter wires the meter into storageBackedStore, nativeBalanceTransfer, and a meteredLogSink; on exhaustion the precompile fails with ErrOutOfGas and store paths stop persisting changes.

The staking precompile drops fixed read/write gas in RequiredGas (now 700 + 16×input bytes only) so storage and transfers are not double-charged.

Tests assert higher gas use on successful store writes, OOG with no storage when gas is too low, and staking flows use realistic gas limits (8M).

Reviewed by Cursor Bugbot for commit bd57e4c. Bugbot is set up for automated code reviews on this repo. Configure here.

@codchen codchen force-pushed the codex/evmonly-staking-precompile branch from 27e3acc to 550f6fa Compare June 29, 2026 12:34
@codchen codchen force-pushed the codex/evmonly-staking-dynamic-gas branch from 56bf186 to bd57e4c Compare June 29, 2026 12:35

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bd57e4c. Configure here.

if value == (common.Hash{}) {
db.AddRefund(params.SstoreClearsScheduleRefundEIP3529)
}
return m.charge(gasAdd(cost, params.SstoreResetGasEIP2200-params.ColdSloadCostEIP2929), tracing.GasChangeCallPrecompiledContract)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSTORE reset gas undercharged

High Severity

When chargeSStore prices a non-zero slot rewrite, it adds SstoreResetGasEIP2200 - ColdSloadCostEIP2929 instead of SstoreResetGasEIP2200 on top of the cold/warm access cost already in cost. Warm updates pay about 2900 gas instead of 5000; cold updates pay about 5000 instead of 7100, so precompile storage writes cost less than normal EVM SSTORE.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bd57e4c. Configure here.

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dynamic precompile gas meter is implemented carefully and faithfully mirrors go-ethereum's EIP-2929/2200/3529 SLOAD/SSTORE/refund accounting, with refund state being snapshot-safe and OOG correctly surfaced and discarded; tests cover the new metering. The only issue is a now-orphaned helper function and a behavioral note about read-method gas.

Findings: 0 blocking | 4 non-blocking | 1 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • isTransaction in giga/evmonly/precompiles/staking/helpers.go was the only consumer of the removed read/write gas split and is now unused dead code (no remaining references in the package). Remove it — golangci-lint's unused check may flag this and fail CI even though the PR notes lint passed.
  • Behavior change worth noting: read-only query methods (validators, delegatorDelegations, etc.) previously cost a flat readGas (3000) and now meter per-SLOAD dynamically. Read-heavy paged queries (up to pageLimit=100 validators, each a multi-chunk read) can consume substantially more gas than before; callers/eth_call gas caps must account for this. This is more accurate, just a notable semantics change.
  • Codex and Cursor second-opinion passes both produced no material findings.
  • 1 suggestion(s)/nit(s) flagged inline on specific lines.

gas = writeGas
}
return gas + inputByteGas*uint64(len(input)) //nolint:gosec // input length is bounded by memory.
return baseGas + inputByteGas*uint64(len(input)) //nolint:gosec // input length is bounded by memory.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] With the read/write gas split removed here, isTransaction in helpers.go is no longer referenced anywhere in the package and is now dead code. Consider removing it — the unused linter may otherwise fail CI.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant