Skip to content

test(rpc): add dedicated pending-transaction lifecycle coverage #2693

Description

@Unique-Divine

#2692 removes a timing-dependent assertion from the contract deployment integration test in file eth/rpc/rpcapi/api_eth_test.go. That test used to assume a transaction disappears from eth_getPendingTransactions after a single block increment. In practice, BroadcastSync only guarantees CheckTx admission—it does not define when the mempool drops a transaction or when the RPC layer stops listing it as pending.

The contract test still validates that a confirmed deployment is absent from the pending set, but only after receipt confirmation via helper function waitForEthReceipt. That keeps the deployment scenario stable without encoding mempool timing assumptions into unrelated assertions.

This issue tracks the gap that remains: we lack dedicated coverage for pending-transaction lifecycle semantics on their own.

What the RPC layer does today

Method GetPendingTransactions on struct EthAPI in file eth/rpc/rpcapi/api_eth.go serves eth_getPendingTransactions. It delegates to method PendingTransactions on struct Backend in file eth/rpc/rpcapi/chain_info.go, which reads unconfirmed transactions from the CometBFT mempool via RPC UnconfirmedTxs and decodes each into a Cosmos sdk.Tx. Only messages of type evm.MsgEthereumTx are surfaced as Ethereum JSON-RPC transactions.

File eth/rpc/rpcapi/chain_info_test.go already has a positive case: broadcast without waiting for a block, then assert the transaction appears in the pending list. What we do not have is explicit, synchronized coverage for the transition from pending to confirmed—or for how long a transaction may linger in the pending query after inclusion.

Why this matters

Wallets, indexers, and WebSocket subscribers (eth_newPendingTransactions, pending filters in file eth/rpc/rpcapi/api_eth_filters.go) depend on predictable pending semantics. If a client polls eth_getPendingTransactions immediately after broadcast, or one block after broadcast but before receipt availability, the answer may still include the transaction even though execution has already committed. Tests that conflate "one block passed" with "no longer pending" will flake under load or on slower localnets.

Separating lifecycle coverage from contract-deployment tests keeps each test focused: deployment tests assert contract behavior; pending tests assert RPC/mempool behavior.

Desired coverage (behavioral, not implementation)

Add integration or RPC-level tests that treat pending lifecycle as the subject under test:

  • Establish expected eth_getPendingTransactions behavior while a transaction is genuinely unconfirmed (still in mempool, no receipt).
  • Establish expected behavior after receipt confirmation—the transaction must not appear in the pending list.
  • Synchronize on receipt availability (polling eth_getTransactionReceipt or an equivalent explicit signal) rather than assuming one block increment implies mempool removal.
  • On failure, log transaction hash, current block height, pending count, and whether a receipt exists—enough context to distinguish "still in mempool" from "RPC bug."
  • Keep this suite independent of contract-deployment assertions in Test_SmartContract.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    S-triageStatus: This issue is waiting on initial triage. More Info: https://tinyurl.com/25uty9w5

    Type

    No type

    Projects

    Status
    ⚡ Building 🧱

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions