Skip to content

Conversation

@alpe
Copy link
Contributor

@alpe alpe commented Jan 22, 2026

Overview

This was a blind spot in e2e tests.
Added 2 new tests for basic coverage (mostly AI generated)

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedJan 22, 2026, 3:06 PM

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @alpe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the end-to-end test coverage for EVM functionality by introducing tests for smart contract deployment, state manipulation, and event emission. These additions aim to validate core EVM interactions within the system, addressing a previously identified testing gap and improving overall system reliability.

Highlights

  • New E2E Test File: A new end-to-end test file, evm_contract_e2e_test.go, has been added to cover EVM contract interactions.
  • Contract Deployment & Interaction Test: Introduces TestEvmContractDeploymentAndInteraction to verify the deployment of a simple storage contract, followed by storing and retrieving a value.
  • EVM Event Testing: Adds TestEvmContractEvents to ensure that EVM events are correctly emitted and can be retrieved both from transaction receipts and using eth_getLogs.
  • Testing Utilities: Includes new helper functions (setupTestSequencer, deployContract) to streamline the setup and execution of EVM-related E2E tests.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link
Contributor

claude bot commented Jan 22, 2026

Claude finished @alpe's task in 2m 22s —— View job


Code Review Complete

I've performed a comprehensive review of the EVM contract interaction tests. This is a valuable addition to test coverage that addresses an important blind spot. Below are my findings organized by priority and category.


Critical Issues

1. Inefficient Error Handling in require.Eventually Blocks (High Priority)

Locations: Lines 99-102, 196-199, 283-286

The current implementation continues retrying even when a transaction has definitively failed (status == 0). This wastes test time and produces unclear timeout errors.

Current pattern:

require.Eventually(t, func() bool {
    receipt, err = client.TransactionReceipt(ctx, storeTxHash)
    return err == nil && receipt != nil
}, 15*time.Second, 500*time.Millisecond, "Store transaction should be included")
require.Equal(t, uint64(1), receipt.Status, "Store tx failed! GasUsed: %d", receipt.GasUsed)

Issue: If the transaction fails (status 0), the Eventually block will retry for the full timeout period, then the test fails at the subsequent require.Equal.

Recommended fix: Check status inside the Eventually block and fail immediately:

require.Eventually(t, func() bool {
    var err error
    receipt, err = client.TransactionReceipt(ctx, storeTxHash)
    if err != nil {
        return false // Not found yet, retry
    }
    require.NotNil(t, receipt, "Receipt should not be nil")
    require.Equal(t, uint64(1), receipt.Status, 
        "Store tx failed! Status: %d, GasUsed: %d", receipt.Status, receipt.GasUsed)
    return true
}, 15*time.Second, 500*time.Millisecond, "Store transaction should be included")

This pattern should be applied at:

  • evm_contract_e2e_test.go:99-103 (store transaction)
  • evm_contract_e2e_test.go:196-201 (trigger transaction)
  • evm_contract_e2e_test.go:283-287 (deployment transaction in helper)

Fix this →


High Priority Issues

2. Magic Numbers - Hardcoded Gas Parameters (High Priority)

Locations: Lines 82-83, 180-181, 268-269

Gas values are hardcoded throughout the tests, making them brittle and hard to maintain.

Hardcoded values:

  • Gas: 500000 (lines 82, 180)
  • GasPrice: big.NewInt(30000000000) (lines 83, 181, 269)
  • Gas: 3000000 (line 268 in deployContract)

Impact: If gas requirements change or if different contracts need different gas limits, you'll need to update multiple locations.

Recommendation: Define constants at package level (similar to DefaultGasLimit in evm_test_common.go:182):

const (
    DefaultContractGas      = 500000
    DefaultDeploymentGas    = 3000000
    DefaultGasPrice         = 30000000000 // 30 gwei
)

Then use:

Gas:      DefaultContractGas,
GasPrice: big.NewInt(DefaultGasPrice),

Fix this →

3. Misleading Log Messages (High Priority)

Locations: Lines 72, 108

Log messages reference function names that don't match the smart contract.

Issues:

  • Line 72: "Calling set(42)..." → Contract function is named store()
  • Line 108: "Calling get() to verify state..." → Contract function is named retrieve()

Recommendation: Update to match contract source:

t.Log("Calling store(42)...")  // Line 72
t.Log("Calling retrieve() to verify state...")  // Line 108

Fix this →

4. Fragile Bytecode Decoding (Medium Priority)

Location: Line 261

The bytecode decoding unconditionally prepends "0x" which will fail if the input already has that prefix.

Current code:

bytecode, err := hexutil.Decode("0x" + bytecodeStr)

Issue: hexutil.Decode handles both prefixed and unprefixed hex strings. Unconditionally adding "0x" breaks if bytecodeStr already contains it.

Recommendation: Either:

  1. Use hexutil.Decode(bytecodeStr) directly (handles both cases), OR
  2. Strip any existing prefix first:
cleanBytecode := strings.TrimPrefix(bytecodeStr, "0x")
bytecode, err := hexutil.Decode("0x" + cleanBytecode)

Option 1 is simpler and more robust.

Fix this →


Medium Priority Issues

5. Inconsistent Nonce Management Pattern

Locations: Lines 56, 67, 95, 158, 169, 192

The test uses a local globalNonce variable that's manually incremented, which differs from the pattern in evm_sequencer_e2e_test.go and evm_test_common.go where nonce is managed by passing a pointer to helper functions.

Current pattern:

var globalNonce uint64 = 0
// ... later
contractAddress, nextNonce := deployContract(t, ctx, client, StorageContractBytecode, globalNonce, privateKey, chainIDInt)
globalNonce = nextNonce

Project pattern (from evm_sequencer_e2e_test.go:67):

var globalNonce uint64 = 0
tx := evm.GetRandomTransaction(t, TestPrivateKey, TestToAddress, DefaultChainID, DefaultGasLimit, &globalNonce)
// Nonce is automatically incremented inside the helper

Recommendation: Consider aligning with the project pattern for consistency. However, since your approach is explicit and clear, this is lower priority. The key is ensuring the pattern is consistently applied within this file.

6. Missing Context Cancellation

Locations: Lines 55, 113, 157, 222

All ctx := t.Context() calls lack timeout configuration. While t.Context() is cancelled when the test ends, explicit timeouts improve clarity and prevent hanging tests.

Current:

ctx := t.Context()
result, err := client.CallContract(ctx, callMsg, nil)

Recommendation (optional): Add explicit timeouts for external operations:

ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
result, err := client.CallContract(ctx, callMsg, nil)

This is especially important for network operations that could hang.

7. Lack of Input Validation in Helper Function

Location: Line 260 (deployContract function)

The deployContract helper doesn't validate its inputs (e.g., empty bytecode, nil private key, nil chainID). While the test context makes invalid inputs unlikely, defensive programming would catch configuration errors earlier.

Recommendation:

func deployContract(t *testing.T, ctx context.Context, client *ethclient.Client, bytecodeStr string, nonce uint64, privateKey *ecdsa.PrivateKey, chainID *big.Int) (common.Address, uint64) {
    t.Helper()
    require.NotEmpty(t, bytecodeStr, "bytecode must not be empty")
    require.NotNil(t, privateKey, "private key must not be nil")
    require.NotNil(t, chainID, "chain ID must not be nil")
    // ... rest of function
}

Low Priority / Style Issues

8. Inconsistent Variable Naming

Location: Line 195 (triggerReceipt)

The variable triggerReceipt is declared outside the Eventually block but only used after it, while other similar blocks (lines 98, 282) use inline declarations. Consistency would improve readability.

Current (line 195):

var triggerReceipt *types.Receipt
require.Eventually(t, func() bool {
    triggerReceipt, err = client.TransactionReceipt(ctx, triggerTxHash)
    return err == nil && triggerReceipt != nil
}, ...)

Alternative (consistent with line 98):

var receipt *types.Receipt
require.Eventually(t, func() bool {
    receipt, err = client.TransactionReceipt(ctx, triggerTxHash)
    return err == nil && receipt != nil
}, ...)
triggerReceipt := receipt

Or use the variable directly without the separate name.

9. Test Cleanup Could Be More Explicit

Locations: Lines 53, 154

The cleanup function only closes the client, but doesn't verify the sequencer stopped cleanly. This is likely fine for E2E tests, but explicit cleanup improves test hygiene.

Current:

cleanup := func() {
    client.Close()
}

Optional enhancement:

cleanup := func() {
    if client != nil {
        client.Close()
    }
    // sut.Cleanup() is called by t.Cleanup elsewhere
}

10. Empty Comment Could Be More Descriptive

Location: Line 110

// Data: empty (to trigger get path)
retrieveData := []byte{}

The comment "to trigger get path" is unclear. The contract's retrieve() function is triggered by any call with empty data since it's the fallback/default execution path in this minimal bytecode.

Better comment:

// Call retrieve() with empty calldata - the contract's fallback execution path
retrieveData := []byte{}

Positive Observations

  1. Good test documentation: The contract source code is included as comments (lines 24-34, 132-143), making it easy to understand what's being tested.

  2. Clear test flow: The numbered steps and log messages make the test flow easy to follow.

  3. Proper use of testify/require: Appropriate assertions with helpful error messages.

  4. Good helper function design: The deployContract helper is reusable and well-structured.

  5. Comprehensive event testing: The event verification test covers both receipt logs and eth_getLogs, which is thorough.

  6. Build tag usage: Correct use of //go:build evm to conditionally compile EVM tests.


Security & Best Practices

No Critical Security Issues Found

The tests properly:

  • Use test-only private keys (from TestPrivateKey constant)
  • Don't expose sensitive data in logs
  • Use appropriate gas limits to prevent infinite loops
  • Validate transaction receipts before proceeding

Best Practice Alignment

Aligned with project patterns:

  • Uses t.Helper() in helper functions ✓
  • Uses require for assertions ✓
  • Uses t.TempDir() for test isolation ✓
  • Follows existing E2E test structure ✓

Minor deviations:

  • Nonce management differs slightly from evm_sequencer_e2e_test.go (but still correct)
  • Could benefit from the same comprehensive logging as evm_sequencer_e2e_test.go:95-97

Performance Considerations

  1. Eventually intervals are reasonable: 500ms polling is consistent with project standards (see evm_test_common.go:191).

  2. Gas values are appropriate: The gas limits are generous but reasonable for test contracts.

  3. Test isolation is good: Each test uses its own temp directory and sequencer instance.

  4. Potential optimization: Consider combining both tests into a single comprehensive test (similar to TestEvmSequencerComprehensiveE2E) to reduce setup overhead. However, separate tests may be preferable for clarity.


Summary & Recommendations

Priority Actions:

  1. Fix the fail-fast issue in require.Eventually blocks (Critical) - Prevents wasted test time and unclear errors
  2. Extract magic numbers to constants (High) - Improves maintainability
  3. Fix misleading log messages (High) - Prevents confusion during debugging
  4. Fix fragile bytecode decoding (Medium) - Prevents potential future bugs

Optional Improvements:

  • Add explicit context timeouts for external operations
  • Add input validation to helper functions
  • Align nonce management pattern with other E2E tests
  • Consider consolidating tests for performance (optional)

Overall Assessment:
This is a solid, well-structured addition to the test suite. The tests are clear, properly isolated, and provide valuable coverage for a previously untested area. The issues identified are primarily about robustness, maintainability, and consistency with existing patterns rather than fundamental problems.

The code is production-ready after addressing the critical fail-fast issue. The other issues can be addressed at your discretion based on team priorities.


Tasks

  • Read and analyze test file
  • Review code quality & architecture
  • Review error handling & resilience
  • Review security considerations
  • Review performance & resource efficiency
  • Review testing patterns & reliability
  • Review documentation & developer experience
  • Compile and post review feedback

Copy link
Contributor

@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 adds valuable end-to-end tests for EVM contract deployment, interaction, and event handling, which is a great step towards improving test coverage. My review focuses on enhancing the robustness, readability, and maintainability of these new tests. I've suggested changes to correct misleading log messages, improve the test failure logic to fail faster and provide clearer errors, and refactor hardcoded values into constants.

Comment on lines 98 to 111
require.Eventually(t, func() bool {
receipt, err := client.TransactionReceipt(ctx, storeTxHash)
if err != nil {
return false
}
if receipt != nil {
if receipt.Status == 1 {
return true
}
t.Logf("Store tx failed! Status: %d, GasUsed: %d", receipt.Status, receipt.GasUsed)
return false
}
return false
}, 15*time.Second, 500*time.Millisecond, "Store transaction should be included")
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The current implementation of require.Eventually will continue to retry until timeout even if the transaction has definitively failed (i.e., receipt.Status == 0). This slows down test execution and can produce a less informative timeout error message. It's better to fail fast in case of a transaction failure by using require assertions inside the Eventually block.

	require.Eventually(t, func() bool {
		receipt, err := client.TransactionReceipt(ctx, storeTxHash)
		if err != nil {
			return false // Not found yet, retry
		}
		if receipt.Status != 1 {
			t.Logf("Store tx failed! Status: %d, GasUsed: %d", receipt.Status, receipt.GasUsed)
			require.FailNow(t, "transaction failed with status 0")
		}
		return true
	}, 15*time.Second, 500*time.Millisecond, "Store transaction should be included")

Comment on lines 205 to 212
require.Eventually(t, func() bool {
receipt, err := client.TransactionReceipt(ctx, triggerTxHash)
if err == nil && receipt != nil && receipt.Status == 1 {
triggerReceipt = receipt
return true
}
return false
}, 15*time.Second, 500*time.Millisecond, "Trigger transaction should be included")
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The current implementation of require.Eventually will continue to retry until timeout even if the transaction has definitively failed (i.e., receipt.Status == 0). This slows down test execution and can produce a less informative timeout error message. It's better to fail fast in case of a transaction failure.

	require.Eventually(t, func() bool {
		receipt, err := client.TransactionReceipt(ctx, triggerTxHash)
		if err != nil {
			return false // Not found yet, retry
		}
		if receipt.Status != 1 {
			t.Logf("Trigger tx failed! Status: %d, GasUsed: %d", receipt.Status, receipt.GasUsed)
			require.FailNow(t, "transaction failed with status 0")
		}
		triggerReceipt = receipt
		return true
	}, 15*time.Second, 500*time.Millisecond, "Trigger transaction should be included")

Comment on lines 294 to 301
require.Eventually(t, func() bool {
receipt, err := client.TransactionReceipt(ctx, deployTxHash)
if err == nil && receipt != nil && receipt.Status == 1 {
contractAddress = receipt.ContractAddress
return true
}
return false
}, 20*time.Second, 500*time.Millisecond, "Contract deployment should be included")
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The current implementation of require.Eventually will continue to retry until timeout even if the transaction has definitively failed (i.e., receipt.Status == 0). This slows down test execution and can produce a less informative timeout error message. It's better to fail fast in case of a transaction failure.

	require.Eventually(t, func() bool {
		receipt, err := client.TransactionReceipt(ctx, deployTxHash)
		if err != nil {
			return false // Not found yet, retry
		}
		if receipt.Status != 1 {
			t.Logf("Contract deployment tx failed! Status: %d, GasUsed: %d", receipt.Status, receipt.GasUsed)
			require.FailNow(t, "transaction failed with status 0")
		}
		contractAddress = receipt.ContractAddress
		return true
	}, 20*time.Second, 500*time.Millisecond, "Contract deployment should be included")

t.Logf("✅ Contract deployed at: %s", contractAddress.Hex())

// 2. Call store(42) -> 42 is 0x2a
t.Log("Calling set(42)...")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The log message "Calling set(42)..." is inconsistent with the smart contract's function name, which is store. To improve clarity and consistency with the contract's source code provided in the comments, please update the log message.

Suggested change
t.Log("Calling set(42)...")
t.Log("Calling store(42)...")

Comment on lines +82 to +83
Gas: 500000, // Should be plenty for simple SSTORE
GasPrice: big.NewInt(30000000000),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The values for Gas (500000) and GasPrice (30000000000) are hardcoded. These "magic numbers" are repeated in TestEvmContractEvents and deployContract, making them difficult to maintain. It would be better to define them as named constants at the top of the file for better clarity and easier modification.

t.Log("✅ Store transaction confirmed")

// 3. Call retrieve() and verify result
t.Log("Calling get() to verify state...")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The log message "Calling get() to verify state..." is inconsistent with the smart contract's function name, which is retrieve. To improve clarity and consistency with the contract's source code, please update the log message.

t.Log("Calling retrieve() to verify state...")

// deployContract helps deploy a contract and waits for its inclusion.
// Returns the deployed contract address and the next nonce.
func deployContract(t *testing.T, ctx context.Context, client *ethclient.Client, bytecodeStr string, nonce uint64, privateKey *ecdsa.PrivateKey, chainID *big.Int) (common.Address, uint64) {
bytecode, err := hexutil.Decode("0x" + bytecodeStr)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The line hexutil.Decode("0x" + bytecodeStr) is fragile. If bytecodeStr already contains a "0x" prefix, the decoding will fail. The hexutil.Decode function can handle strings with or without the "0x" prefix. To make this helper function more robust and reusable, it's better to remove the explicit prefix concatenation.

	bytecode, err := hexutil.Decode(bytecodeStr)

@codecov
Copy link

codecov bot commented Jan 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.96%. Comparing base (340d5cf) to head (4f626a0).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3006   +/-   ##
=======================================
  Coverage   57.96%   57.96%           
=======================================
  Files         110      110           
  Lines       10526    10526           
=======================================
  Hits         6101     6101           
  Misses       3774     3774           
  Partials      651      651           
Flag Coverage Δ
combined 57.96% <ø> (ø)

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alpe alpe added this pull request to the merge queue Jan 22, 2026
Merged via the queue into main with commit 429b8c5 Jan 22, 2026
28 checks passed
@alpe alpe deleted the alex/tests branch January 22, 2026 17:23
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.

3 participants