Skip to content

Conversation

@Unique-Divine
Copy link
Member

Stabilize TestTraceTx by fixing ERC-20 transfer recipient to deterministic address

Problem and root cause

TestTraceTx failed intermittently on gas and gasUsed assertions for the ERC-20 transfer case, with variations in steps of 12 gas.
Intrinsic gas charges depend on calldata byte values. Since the Istanbul upgrade (EIP-2028), each zero byte costs 4 gas and each non-zero byte costs 16 gas.
When the recipient address was randomly generated, the number of non-zero bytes in the 20-byte address changed between runs, altering intrinsic gas in increments of 12 and producing nondeterministic results.

Fix

Use a fixed recipient address (0x0000000000000000000000000000000000010f2C, created with gethcommon.BigToAddress(big.NewInt(69_420))) when building the ERC-20 transfer calldata.
This ensures the non-zero/zero byte pattern in calldata remains constant, stabilizing intrinsic gas and making gas and gasUsed consistent across runs.

Why this works

  • ERC-20 calldata layout: 4-byte selector + 32-byte address (left-padded) + 32-byte amount.
  • Each non-zero byte adds 12 gas relative to a zero byte.
  • A deterministic address locks the byte pattern and prevents per-run drift in intrinsic gas.

Changes

  • Updated DeployAndExecuteERC20Transfer to use a fixed recipient address.
  • Adjusted expected gas and gasUsed in test cases to reflect the deterministic intrinsic gas.

Verification

  • Reproduced the issue with random recipients, showing ±12×N differences in gas.
  • After applying the fix, test results remain consistent across repeated local and CI runs.

References

@Unique-Divine Unique-Divine requested a review from a team as a code owner October 28, 2025 09:00
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

📝 Walkthrough

Walkthrough

Fixed non-deterministic gas values in ERC20 transfer trace tests by introducing deterministic recipient logic in test utilities and updating corresponding expected gas values in test assertions. Updated changelog and release notes.

Changes

Cohort / File(s) Change Summary
Changelog Updates
CHANGELOG.md
Added unreleased entry referencing PR #2418 for evmstate/test trace stability fix; updated v2.8.0 release date from 2025-10-24 to 2025-10-28 and expanded version section with additional entries.
Test Determinism Fix
x/evm/evmtest/tx.go
Introduced fixed recipient constant in DeployAndExecuteERC20Transfer to replace dynamic address selection; added detailed comments explaining deterministic gas behavior and Istanbul/EIP-2028 calldata gas mechanics rationale.
Test Value Updates
x/evm/evmstate/grpc_query_test.go
Adjusted hardcoded gas and gasUsed values in TraceResCallTracer_ERC20Transfer from 51250/34150 to 51046/33946 with hexadecimal representations (0xc766/0x849a) to reflect stabilized gas calculations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify fixed recipient logic correctly stabilizes gas calculations per Istanbul/EIP-2028 calldata mechanics
  • Confirm adjusted gas values (51046/33946) align with deterministic payload and are mathematically correct
  • Validate that determinism improvements eliminate flakiness without masking underlying issues

Possibly related PRs

Suggested labels

x: evm

Suggested reviewers

  • onikonychev
  • k-yang

Poem

🐰 A steadfast hop through flaky tests we go,
With fixed recipients, the gas flows just so.
No more surprises, no values astray,
Istanbul's rules keep chaos at bay! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "fix(evmstate/test): stabilize trace tx tests with deterministic ERC20 transfer recipient" accurately summarizes the main change across all modified files. It clearly conveys that the PR addresses instability in test trace tests by implementing deterministic behavior for ERC20 transfer recipients. The title is specific, follows conventional commit format, and concisely captures the core issue being resolved without vague terminology or extraneous information.
Linked Issues Check ✅ Passed The PR successfully addresses all coding requirements from issue #2417. It identifies and documents the root cause of nondeterminism: random recipient addresses in ERC-20 transfer calldata produce variable zero/non-zero byte patterns, affecting intrinsic gas per EIP-2028 (4 gas per zero byte, 16 per non-zero byte, yielding 12 gas increments between runs). The fix implements deterministic behavior by using a fixed recipient address (0x0000000000000000000000000000000000010f2C), and corresponding test values are adjusted from 51250/34150 to 51046/33946 gas. The changes ensure TestTraceTx passes consistently across repeated runs, fulfilling all objectives stated in the issue.
Out of Scope Changes Check ✅ Passed All changes in the PR are directly scoped to resolving the identified gas nondeterminism issue. The CHANGELOG.md update documents the fix for PR #2418, the grpc_query_test.go modifications adjust expected gas values to reflect deterministic calculations, and the tx.go changes implement the core fix by introducing fixed recipient logic in DeployAndExecuteERC20Transfer. No unrelated refactoring, feature additions, or cosmetic changes are present that would extend beyond the stated objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ud/gas-test-trace-tx

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09e58ab and 4ae0d1a.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • x/evm/evmstate/grpc_query_test.go (1 hunks)
  • x/evm/evmtest/tx.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
x/evm/evmtest/tx.go (2)
x/evm/evmtest/test_deps.go (1)
  • TestDeps (27-35)
x/evm/tx.pb.go (3)
  • MsgEthereumTx (40-51)
  • MsgEthereumTx (55-55)
  • MsgEthereumTx (56-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: lint
  • GitHub Check: e2e-evm
  • GitHub Check: build
  • GitHub Check: unit-tests
  • GitHub Check: proto-gen-rs
  • GitHub Check: heavy-tests
🔇 Additional comments (5)
x/evm/evmstate/grpc_query_test.go (1)

55-56: LGTM! Gas values correctly updated for deterministic recipient.

The gas values have been accurately adjusted to reflect the deterministic intrinsic gas cost when using the fixed recipient address. The reduction of 204 gas units (51250→51046 and 34150→33946) matches the expected savings from having 3 non-zero bytes in the fixed recipient versus ~20 in a typical random address: (20-3) × 12 = 204 gas per EIP-2028. The inline hex comments are a nice touch for quick verification.

x/evm/evmtest/tx.go (2)

102-141: Excellent documentation and implementation!

The comprehensive documentation clearly explains the root cause of the non-determinism and the solution. The inline comments effectively break down:

  1. The EIP-2028 calldata gas mechanics (4 gas for zero bytes, 16 for non-zero bytes)
  2. How ABI encoding pads the address parameter
  3. Why the specific fixed recipient address was chosen (only 3 non-zero bytes)
  4. The mathematical basis for the gas variance (±12 per byte difference)

The implementation using gethcommon.BigToAddress(big.NewInt(69_420)) correctly produces the address 0x0000000000000000000000000000000000010f2C, which ensures stable, reproducible gas costs across test runs.


158-158: Correct usage of deterministic recipient.

The function now uses fixedRecipient instead of generating a random address, which eliminates the source of non-determinism in the gas costs. This change is consistent with the updated gas assertions in the test file.

CHANGELOG.md (2)

50-50: LGTM! Changelog entry clearly describes the fix.

The entry accurately summarizes the PR's objective: stabilizing trace tx tests by using a deterministic ERC20 transfer recipient.


52-52: Release date updated.

The v2.8.0 release date has been updated from 2025-10-24 to 2025-10-28.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses the non-determinism in TestTraceTx by using a fixed recipient address for ERC-20 transfers. The explanation provided in the code comments and PR description is excellent and clearly outlines the problem and the solution. The changes are well-implemented and correctly adjust the expected gas values in the tests. I have one minor suggestion to improve code maintainability by replacing a magic number with a named constant.

Comment on lines +127 to +141
var (
// Use a deterministic recipient to avoid nondeterministic intrinsic gas from calldata
// (non-zero vs zero byte costs). A fixed address ensures stable gas/gasUsed in traces.
//
// This address, "0x0000000000000000000000000000000000010f2C", has only 3
// non-zero bytes in its 20-byte payload (01, 0f, 2c). This means we expect
// it to have lower intrinsic gas than a "typical" random address that has
// ~20 non-zero bytes. Lower by (20 - 3) * 12 = 204 gas units, based on
// EIP-2028 (Istanbul upgrade).
//
// Istanbul reduced the calldata charge to 4 gas
// per zero byte and 16 per non-zero byte, which is why changing the address
// bytes moves intrinsic gas in steps of 12.
fixedRecipient = gethcommon.BigToAddress(big.NewInt(69_420))
)

Choose a reason for hiding this comment

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

medium

To improve readability and maintainability, consider defining 69_420 as a named constant. This avoids using a magic number directly in the function body. The var block for a single variable is also slightly unconventional. This suggestion refactors the block to use a const.

Suggested change
var (
// Use a deterministic recipient to avoid nondeterministic intrinsic gas from calldata
// (non-zero vs zero byte costs). A fixed address ensures stable gas/gasUsed in traces.
//
// This address, "0x0000000000000000000000000000000000010f2C", has only 3
// non-zero bytes in its 20-byte payload (01, 0f, 2c). This means we expect
// it to have lower intrinsic gas than a "typical" random address that has
// ~20 non-zero bytes. Lower by (20 - 3) * 12 = 204 gas units, based on
// EIP-2028 (Istanbul upgrade).
//
// Istanbul reduced the calldata charge to 4 gas
// per zero byte and 16 per non-zero byte, which is why changing the address
// bytes moves intrinsic gas in steps of 12.
fixedRecipient = gethcommon.BigToAddress(big.NewInt(69_420))
)
const fixedRecipientInt = 69_420
// Use a deterministic recipient to avoid nondeterministic intrinsic gas from calldata
// (non-zero vs zero byte costs). A fixed address ensures stable gas/gasUsed in traces.
//
// This address, "0x0000000000000000000000000000000000010f2C", has only 3
// non-zero bytes in its 20-byte payload (01, 0f, 2c). This means we expect
// it to have lower intrinsic gas than a "typical" random address that has
// ~20 non-zero bytes. Lower by (20 - 3) * 12 = 204 gas units, based on
// EIP-2028 (Istanbul upgrade).
//
// Istanbul reduced the calldata charge to 4 gas
// per zero byte and 16 per non-zero byte, which is why changing the address
// bytes moves intrinsic gas in steps of 12.
fixedRecipient := gethcommon.BigToAddress(big.NewInt(fixedRecipientInt))

@codecov
Copy link

codecov bot commented Oct 28, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 59.24%. Comparing base (09e58ab) to head (4ae0d1a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2418   +/-   ##
=======================================
  Coverage   59.23%   59.24%           
=======================================
  Files         359      359           
  Lines       24190    24193    +3     
=======================================
+ Hits        14329    14332    +3     
  Misses       8635     8635           
  Partials     1226     1226           
Files with missing lines Coverage Δ
x/evm/evmtest/tx.go 54.41% <100.00%> (+0.68%) ⬆️
🚀 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.

@Unique-Divine Unique-Divine merged commit b47e3bd into main Oct 28, 2025
11 checks passed
@Unique-Divine Unique-Divine deleted the ud/gas-test-trace-tx branch October 28, 2025 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] small gas non-determinism in TestEvmState/TestTraceTx

3 participants