Skip to content

feat: Lander EVM, periodically update finalized nonce and detect any messages that need reprocessing#7197

Merged
kamiyaa merged 25 commits intomainfrom
jeff/nonce-update
Oct 23, 2025
Merged

feat: Lander EVM, periodically update finalized nonce and detect any messages that need reprocessing#7197
kamiyaa merged 25 commits intomainfrom
jeff/nonce-update

Conversation

@kamiyaa
Copy link
Copy Markdown
Contributor

@kamiyaa kamiyaa commented Oct 13, 2025

Description

Add 2 new methods to AdaptsChain

    /// How often to poll for txs that need to be reprocessed
    fn reprocess_txs_poll_rate(&self) -> Option<Duration> {
        None
    }

    /// Get a list of txs that need to be reprocessed
    async fn get_reprocess_txs(&self) -> Result<Vec<Transaction>, LanderError> {
        Ok(Vec::new())
    }
  • add a new task to inclusion stage that checks for txs that need reprocessing periodically based on reprocess_txs_poll_rate()
  • also updates finalized nonce in the process

Related issues

Summary by CodeRabbit

  • New Features

    • Automatic transaction reprocessing pipeline with adaptive polling intervals.
    • Background task periodically fetches and injects reprocessable transactions into the inclusion pool.
    • Chain adapters can report polling cadence and supply transactions to reprocess.
  • Chores

    • Broadened access to nonce/state lookup methods and adjusted nonce updater visibility to support reprocessing flow.
  • Tests

    • Added mocks and tests for reprocessing polling and single-run reprocessing behavior.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Oct 13, 2025

⚠️ No Changeset found

Latest commit: 7f9968a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@kamiyaa kamiyaa marked this pull request as draft October 13, 2025 18:50
@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (4011a45) to head (7f9968a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #7197   +/-   ##
=====================================
  Coverage   0.00%       0           
=====================================
  Files          1       0    -1     
  Lines         14       0   -14     
=====================================
+ Misses        14       0   -14     
Components Coverage Δ
core ∅ <ø> (∅)
hooks ∅ <ø> (∅)
isms ∅ <ø> (∅)
token ∅ <ø> (∅)
middlewares ∅ <ø> (∅)
🚀 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.

@kamiyaa kamiyaa marked this pull request as ready for review October 13, 2025 19:15
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 13, 2025

📝 Walkthrough

Walkthrough

This PR exposes NonceUpdater at crate scope, promotes several nonce DB methods to public, extends the AdaptsChain trait with reprocess-tx polling and retrieval, implements those in the Ethereum adapter, spawns a concurrent inclusion-stage task to poll and enqueue reprocessable transactions, and updates tests/mocks to cover the new flow.

Changes

Cohort / File(s) Summary
Nonce exports
rust/main/lander/src/adapter/chains/ethereum/nonce.rs
Replaces the test-only re-export with a crate-visible re-export: pub(crate) use updater::NonceUpdater.
Nonce manager
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs
Adds std::time::Duration import and updates NonceUpdater::new call to pass provider.clone() and state.clone() (argument formatting adjusted).
Nonce state DB
rust/main/lander/src/adapter/chains/ethereum/nonce/state/db.rs
Promotes get_tracked_tx, get_tracked_tx_uuid, and get_finalized_nonce from pub(super) to pub.
Ethereum adapter
rust/main/lander/src/adapter/chains/ethereum/adapter.rs
Adds reprocess_txs_poll_rate(&self) -> Option<Duration> and async fn get_reprocess_txs(&self) -> Result<Vec<Transaction>, LanderError> to support nonce-reorg-aware reprocessing retrieval.
AdaptsChain trait
rust/main/lander/src/adapter/core.rs
Adds trait methods with defaults: reprocess_txs_poll_rate() and async get_reprocess_txs() (no-op defaults).
Inclusion stage
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs
Spawns a new task and adds pub async fn receive_reprocess_txs(...) that polls adapter for reprocessable txs and inserts them into the inclusion pool.
Inclusion stage tests
rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs
Adds test_processing_reprocess_txs and adjusts mocks to control reprocess_txs_poll_rate behavior.
Test utilities / mocks
rust/main/lander/src/tests/test_utils.rs
Extends AdaptsChain mock with reprocess_txs_poll_rate() and async get_reprocess_txs() implementations.

Sequence Diagram(s)

sequenceDiagram
    participant InclusionStage as Inclusion Stage
    participant Adapter as Ethereum Adapter
    participant NonceDB as Nonce State DB
    participant Pool as Inclusion Pool

    Note over InclusionStage,Adapter: receive_reprocess_txs loop (polling)
    loop every poll_rate
        InclusionStage->>Adapter: get_reprocess_txs()
        Adapter->>NonceDB: get_finalized_nonce() (old)
        NonceDB-->>Adapter: old_nonce
        Adapter->>Adapter: run NonceUpdater / scan chain
        Adapter->>NonceDB: get_finalized_nonce() (new)
        NonceDB-->>Adapter: new_nonce
        alt new_nonce < old_nonce
            Adapter->>NonceDB: get_tracked_tx(nonce) for each nonce in (new+1..old)
            NonceDB-->>Adapter: tx / missing
            Adapter-->>InclusionStage: Vec<Transaction>
            InclusionStage->>Pool: insert txs by UUID
        else new_nonce >= old_nonce
            Adapter-->>InclusionStage: empty Vec
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • yjamin
  • ameten

Poem

Nonces stir and shuffle, back from the fog,
A gentle poll brings strays out from the bog,
The adapter hums, the pool gets fed,
Quiet fixes stitched where logic tread,
A tidy chain, one less thing to clog.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description addresses the main content (Description and Related Issues sections of the template) with substantive information about the new trait methods and the new task in the inclusion stage, plus links to two related Linear issues. However, the description is missing three required template sections: Drive-by changes, Backward compatibility, and Testing. These sections are either left blank or not addressed at all, which means the description covers only about 2 out of 5 template sections. This falls short of being "mostly complete" according to the template requirements. Fill out the remaining template sections to provide reviewers with a complete picture. Specifically, add information about whether there are any drive-by changes included, confirm backward compatibility status with a clear Yes/No statement and any infrastructure implications, and specify what kind of testing these changes have undergone (None/Manual/Unit Tests, or reference the test additions shown in the pull request like test_processing_reprocess_txs).
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: Lander EVM, periodically update finalized nonce and detect any messages that need reprocessing" is clear and directly reflects the main changes in the pull request. It describes the core objective—periodic finalized nonce updates and transaction reprocessing detection—which aligns well with the additions of the two new AdaptsChain trait methods, the new inclusion stage task, and the underlying infrastructure changes. The title is concise and specific enough that someone reviewing commit history would understand the purpose without ambiguity.
✨ 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 jeff/nonce-update

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
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (1)

54-62: The background task has no graceful shutdown.

The spawned task runs indefinitely with no way to stop it. If the NonceManager is dropped or the application needs to shut down cleanly, this task will keep running until the process terminates.

Consider holding a JoinHandle and implementing Drop or adding a shutdown method that uses a cancellation token.

rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (2)

39-44: Consider adding observability for successful updates.

Right now there's no way to tell if the periodic updater is working correctly. Adding a debug log or metric on successful updates would help with monitoring and troubleshooting.

Example:

pub async fn run(&self) {
    loop {
        match self.update_latest_finalized_nonce().await {
            Ok(()) => {
                tracing::debug!(
                    address = ?self.address,
                    "Updated finalized nonce"
                );
            }
            Err(e) => {
                tracing::warn!(
                    address = ?self.address,
                    error = ?e,
                    "Failed to update finalized nonce"
                );
            }
        }
        tokio::time::sleep(self.poll_rate).await;
    }
}

53-57: The underflow case might be worth logging.

When next_nonce is zero (possible on a fresh chain or after a reset), the update is silently skipped. While this is correct behavior, logging it could help with debugging unexpected nonce states.

Example:

 let finalized_nonce = next_nonce.checked_sub(U256::one());
 
 if let Some(finalized_nonce) = finalized_nonce {
     self.state.update_boundary_nonces(&finalized_nonce).await?;
+} else {
+    tracing::debug!(
+        address = ?self.address,
+        "Skipped nonce update: next_nonce is zero"
+    );
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5af78 and 6d161ba.

📒 Files selected for processing (3)
  • rust/main/lander/src/adapter/chains/ethereum/nonce.rs (1 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (3 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce.rs
🧬 Code graph analysis (2)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)
  • new (23-37)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (2)
  • new (32-71)
  • address (120-128)
⏰ 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). (11)
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (cosmwasm)
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: yarn-install
  • GitHub Check: build-and-push-to-gcr
  • GitHub Check: test-rs
  • GitHub Check: lander-coverage
  • GitHub Check: lint-rs
🔇 Additional comments (1)
rust/main/lander/src/adapter/chains/ethereum/nonce.rs (1)

6-6: Looks solid.

The module declaration follows the existing pattern and integrates cleanly with the other internal modules.

Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs Outdated
Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 13, 2025

📝 Walkthrough

Walkthrough

Adds a periodic nonce updater module, wires it into the Ethereum nonce manager with a capped poll interval based on estimated block time (max 5s), and spawns its async loop. The updater repeatedly fetches the latest finalized nonce via the provider and updates shared nonce state.

Changes

Cohort / File(s) Summary of changes
Nonce module wiring
rust/main/lander/src/adapter/chains/ethereum/nonce.rs
Declares new periodic_updater module.
Manager integration
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs
Computes capped poll_rate from chain config, instantiates PeriodicNonceUpdater, and spawns its async run loop alongside existing updater. Minor comments/control-flow alignment.
Periodic updater implementation
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs
Adds PeriodicNonceUpdater with new, run, and update_latest_finalized_nonce. Periodically queries provider for next nonce at finalized block, derives finalized nonce boundary, and updates shared NonceManagerState. Error mapping to NonceError::ProviderError. Uses Arc and async/await.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Manager
  participant PeriodicNonceUpdater as Periodic Updater
  participant Provider as EVM Provider
  participant State as NonceManagerState

  Manager->>PeriodicNonceUpdater: new(address, reorg_period, poll_rate, provider, state)
  Manager->>PeriodicNonceUpdater: spawn run()

  rect rgb(240, 248, 255)
    loop every poll_rate (≤ 5s)
      PeriodicNonceUpdater->>Provider: get next nonce (finalized block)
      Provider-->>PeriodicNonceUpdater: next_nonce | error
      alt success
        PeriodicNonceUpdater->>State: update finalized nonce boundary
        State-->>PeriodicNonceUpdater: ack
      else error
        Note right of PeriodicNonceUpdater: Map to NonceError::ProviderError
      end
      PeriodicNonceUpdater-->>PeriodicNonceUpdater: sleep(poll_rate)
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

New gears hum in the noncey bog,
A steady tick—no need to slog.
Finalized whispers, fetched on time,
Boundaries set, all neat and prime.
Loop and snooze, then fetch once more—
Quiet steps across the chainy floor. 🐾⏳

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description outlines the core functionality and references the related issue but omits several required template sections including Drive-by changes, Backward compatibility, and Testing, making the template incomplete. This lack of structured information means reviewers lack context on compatibility and test coverage. Please add the missing “Drive-by changes,” “Backward compatibility,” and “Testing” sections to fully comply with the project’s PR template and provide clarity on minor changes, compatibility impact, and test coverage.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely describes the main change of introducing a background task to update finalized nonces, matching the modifications in the code and highlighting the feature nature of the PR. It avoids vague wording and directly reflects the primary functionality added.
✨ 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 jeff/nonce-update

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
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (6)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (3)

13-13: Tidy up the import

{self, PeriodicNonceUpdater} brings the module name into scope but it’s unused. Trim the swampy bits:

-use crate::adapter::chains::ethereum::nonce::periodic_updater::{self, PeriodicNonceUpdater};
+use crate::adapter::chains::ethereum::nonce::periodic_updater::PeriodicNonceUpdater;

As per coding guidelines, clippy will also nudge you here.


54-57: Comment semantics: it’s a 5s floor, not a cap

Code uses .max(5s), which ensures a minimum 5s interval (slower polling on faster chains). The comment says “cap at 5s”, which reads like a maximum. Suggest rewording to “floor at 5s (minimum interval)” to avoid head‑scratching later.


60-62: Consider keeping a JoinHandle or naming the task

Right now the periodic task is fire‑and‑forget. Keeping a handle (or naming the task) helps with shutdown and observability.

Example:

-        tokio::spawn(async move {
-            periodic_updater.run().await;
-        });
+        let _periodic_task = tokio::spawn(async move {
+            periodic_updater.run().await;
+        });
+        // Optionally store `_periodic_task` on `NonceManager` for controlled shutdown.
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (3)

8-10: Drop unused imports

These aren’t used; let’s keep it clean and odor‑free.

-use tokio::sync::Mutex;
-use tokio::time::Instant;

Also run clippy; it’ll bark at these too.


39-44: Don’t swallow errors; log them (and consider interval to avoid drift)

Right now errors vanish into the bog. At least log them; optionally switch to tokio::time::interval to keep a steady beat.

-    pub async fn run(&self) {
-        loop {
-            let _ = self.update_latest_finalized_nonce().await;
-            tokio::time::sleep(self.poll_rate).await;
-        }
-    }
+    pub async fn run(&self) {
+        let mut ticker = tokio::time::interval(self.poll_rate);
+        loop {
+            ticker.tick().await;
+            if let Err(e) = self.update_latest_finalized_nonce().await {
+                tracing::warn!("periodic nonce update failed for {}: {e}", self.address);
+            } else {
+                tracing::debug!("periodic nonce update succeeded for {}", self.address);
+            }
+        }
+    }

Optional: add a small randomized initial jitter to avoid herd effects when many updaters start together.


46-59: Verify monotonic boundary semantics; guard against regressions

You compute finalized = next - 1 and update state if Some. Please confirm state.update_boundary_nonces never regresses the highest finalized nonce (e.g., transient provider/reorg quirks). If it doesn’t enforce monotonicity internally, add a guard.

Would you like a patch that reads current finalized from state (if exposed) and only updates when newer?

Also, add unit tests covering:

  • next=0 (no update)
  • next>0 (updates to next-1)
  • provider error path (logged, loop continues)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5af78 and 6d161ba.

📒 Files selected for processing (3)
  • rust/main/lander/src/adapter/chains/ethereum/nonce.rs (1 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (3 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/nonce.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs
🧬 Code graph analysis (2)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (2)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)
  • new (23-37)
rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (1)
  • new (24-49)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (2)
  • new (32-71)
  • address (120-128)
⏰ 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). (11)
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (cosmwasm)
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: yarn-install
  • GitHub Check: build-and-push-to-gcr
  • GitHub Check: test-rs
  • GitHub Check: lander-coverage
  • GitHub Check: lint-rs
🔇 Additional comments (1)
rust/main/lander/src/adapter/chains/ethereum/nonce.rs (1)

6-6: Module wiring looks good

mod periodic_updater; is correctly added to the module tree. No concerns.

Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs Outdated
Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs Outdated
Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs Outdated
Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs Outdated
Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)

37-42: Errors are getting lost in the swamp.

The run() loop silently swallows all errors from update_latest_finalized_nonce() with that let _ = .... When updates fail—provider hiccups, network issues, whatever—there's no way to know about it. That's a visibility gap that'll make debugging harder down the road.

Add some logging so you know when things go sideways:

 pub async fn run(&self) {
     loop {
-        let _ = self.update_latest_finalized_nonce().await;
+        if let Err(e) = self.update_latest_finalized_nonce().await {
+            tracing::warn!(
+                address = ?self.address,
+                error = ?e,
+                "Failed to update finalized nonce"
+            );
+        }
         tokio::time::sleep(self.poll_rate).await;
     }
 }
🧹 Nitpick comments (2)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (1)

20-20: Remove unused self import

At rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs:20, replace:

  • use super::periodic_updater::{self, PeriodicNonceUpdater};
  • use super::periodic_updater::PeriodicNonceUpdater;
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)

44-57: Consolidate nonce boundary update logic
Both periodic_updater.rs:update_latest_finalized_nonce and updater.rs:update_boundaries_immediately share identical code – extract it into a shared helper to cut duplication.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d161ba and 92182d5.

📒 Files selected for processing (2)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (3 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs
🧬 Code graph analysis (2)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (2)
  • new (32-71)
  • address (120-128)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (3)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)
  • new (21-35)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)
  • new (74-127)
rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (1)
  • new (24-49)
⏰ 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). (11)
  • GitHub Check: infra-test
  • GitHub Check: build-and-push-to-gcr
  • GitHub Check: e2e-matrix (cosmwasm)
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: test-rs
  • GitHub Check: lint-rs
  • GitHub Check: lander-coverage
🔇 Additional comments (4)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (2)

46-52: Provider cloning is correct here.

Using provider.clone() instead of moving provider directly is necessary since the provider is reused for the periodic updater below. The Arc clone is cheap and maintains shared ownership.


54-62: The spawned task will run, no worries.

Despite the past comment's concern, tokio::spawn starts the task immediately—you don't need to await the handle for it to run. The task will keep going in the background as long as the runtime's alive. Since periodic_updater.run() is an infinite loop, it'll keep ticking until shutdown.

That said, you're not capturing any panics or errors from the spawned task since the JoinHandle is dropped. But looking at the run() implementation, it won't panic (errors are swallowed), so this approach should work fine for a fire-and-forget background updater.

rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (2)

1-18: Struct and imports look solid.

The field order and types are consistent with the NonceUpdater pattern. No unused imports visible in the current version.


21-35: Constructor follows the right pattern.

Matches the style of NonceUpdater::new and properly initializes all fields. Straightforward and correct.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)

38-49: Errors are silently swallowed in the update loop.

The run() loop ignores all errors from update_state_boundaries_immediately() with let _ = .... When nonce updates fail (provider errors, network issues, etc.), there's no visibility into these failures.

Add error logging:

     pub async fn run(&self) {
         loop {
-            let _ = NonceUpdater::update_state_boundaries_immediately(
+            if let Err(e) = NonceUpdater::update_state_boundaries_immediately(
                 &self.provider,
                 &self.state,
                 &self.address,
                 &self.reorg_period,
             )
-            .await;
+            .await
+            {
+                tracing::warn!(
+                    address = ?self.address,
+                    error = ?e,
+                    "Failed to update finalized nonce"
+                );
+            }
             tokio::time::sleep(self.poll_rate).await;
         }
     }
🧹 Nitpick comments (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1)

22-36: Match the field order in struct initialization.

The constructor initializes fields in a different order than they're declared in the struct. Lines 33-34 have state before provider, but the struct definition has provider before state (lines 17-18).

Apply this diff to match the struct order:

         Self {
             address,
             reorg_period,
             poll_rate,
-            state,
             provider,
+            state,
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92182d5 and c1e19d7.

📒 Files selected for processing (3)
  • rust/main/lander/src/adapter/chains/ethereum/nonce.rs (1 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (1 hunks)
  • rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs
  • rust/main/lander/src/adapter/chains/ethereum/nonce.rs
🧬 Code graph analysis (1)
rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (2)
rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (2)
  • new (24-49)
  • update_state_boundaries_immediately (71-89)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (2)
  • new (32-71)
  • address (120-128)
⏰ 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). (11)
  • GitHub Check: infra-test
  • GitHub Check: test-rs
  • GitHub Check: lander-coverage
  • GitHub Check: lint-rs
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (cosmwasm)
  • GitHub Check: build-and-push-to-gcr
🔇 Additional comments (6)
rust/main/lander/src/adapter/chains/ethereum/nonce.rs (2)

2-2: LGTM! Making NonceUpdater crate-visible.

The change properly exposes NonceUpdater at crate level, which is needed by the new periodic updater. Clean and straightforward.


7-7: LGTM! Adding periodic updater module.

The new module fits well into the existing nonce subsystem structure.

rust/main/lander/src/adapter/chains/ethereum/nonce/periodic_updater.rs (2)

1-11: LGTM! Import declarations are clean.

All imports are used and necessary for the implementation.


13-19: LGTM! Struct definition is straightforward.

Fields are well-chosen for the periodic update task.

rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (2)

62-69: LGTM! Clean refactoring.

Extracting the boundary update logic into a static method makes it reusable by the periodic updater while keeping the existing behavior intact.


71-89: LGTM! Extracted method is well-structured.

The new static method properly encapsulates the boundary update logic. Taking references to Arc parameters is fine here since callers already have Arcs. The error handling with map_err is appropriate, and the conditional update when finalized_nonce is present is correct.

@kamiyaa kamiyaa changed the title feat: add a background task that updates the finalized nonce feat: Lander EVM, periodically update finalized nonce and detect any messages that need reprocessing Oct 17, 2025
Comment thread rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs
Comment thread rust/main/lander/src/adapter/chains/ethereum/adapter.rs Outdated
Comment thread rust/main/lander/src/adapter/chains/ethereum/adapter.rs Outdated
Comment thread rust/main/lander/src/dispatcher/stages/inclusion_stage.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)

210-213: Silent error handling could hide problems.

The wildcard pattern here swallows all errors from get_reprocess_txs() without any logging. When things go sideways, you'll be scratching your head wondering why reprocessing isn't happening.

Add some logging so you can actually see what's going wrong:

     let txs = match state.adapter.get_reprocess_txs().await {
         Ok(s) => s,
-        _ => continue,
+        Err(err) => {
+            tracing::warn!(?err, domain, "Failed to get reprocess txs, will retry");
+            continue;
+        }
     };
🧹 Nitpick comments (2)
rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (1)

562-625: Consider verifying mock invocations to strengthen test reliability.

The test checks that transactions end up in the pool, but doesn't verify that get_reprocess_txs was actually called. Since the mock uses an Option wrapper that gets consumed, the test could theoretically pass even if the reprocess path never executed.

You could add an expectation on the mock to verify it's called at least once:

+    use std::sync::atomic::{AtomicUsize, Ordering};
+    use std::sync::Arc as StdArc;
+
+    let call_count = StdArc::new(AtomicUsize::new(0));
+    let call_count_clone = call_count.clone();
+
     let mut txs_created_option = Some(txs_created.clone());
     mock_adapter.expect_get_reprocess_txs().returning(move || {
+        call_count_clone.fetch_add(1, Ordering::SeqCst);
         if let Some(txs) = txs_created_option.take() {
             Ok(txs)
         } else {
             Ok(Vec::new())
         }
     });

Then assert the call count after the test:

     assert!(are_all_txs_in_pool(txs_created.clone(), &pool).await);
+    assert!(call_count.load(Ordering::SeqCst) > 0, 
+        "get_reprocess_txs should be called at least once");
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)

187-224: Function signature implies it can return an error, but it never does.

This function returns Result<(), LanderError> but the only actual return is the early Ok(()) on line 196. After that, it loops forever. The signature suggests error handling that doesn't really exist in practice.

Consider changing the signature to better reflect the behavior:

-pub async fn receive_reprocess_txs(
+pub async fn receive_reprocess_txs(
     domain: String,
     pool: InclusionStagePool,
     state: DispatcherState,
-) -> Result<(), LanderError> {
+) {
     let poll_rate = match state.adapter.reprocess_txs_poll_rate() {
         Some(s) => s,
-        None => return Ok(()),
+        None => return,
     };

This makes it clear that the function either returns early if there's no polling configured, or runs indefinitely.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c97b84 and 3107996.

📒 Files selected for processing (5)
  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1 hunks)
  • rust/main/lander/src/adapter/core.rs (1 hunks)
  • rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (2 hunks)
  • rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (1 hunks)
  • rust/main/lander/src/tests/test_utils.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • rust/main/lander/src/adapter/core.rs
  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/dispatcher/stages/inclusion_stage.rs
  • rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs
  • rust/main/lander/src/tests/test_utils.rs
🧬 Code graph analysis (3)
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)
rust/main/lander/src/dispatcher/core.rs (3)
  • tokio (77-77)
  • tokio (79-79)
  • spawn (73-184)
rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (4)
rust/main/lander/src/tests/test_utils.rs (3)
  • tmp_dbs (43-53)
  • create_random_txs_and_store_them (73-89)
  • are_all_txs_in_pool (95-101)
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)
  • new (43-56)
rust/main/lander/src/dispatcher/stages/state.rs (1)
  • new (40-54)
rust/main/lander/src/dispatcher/metrics.rs (2)
  • new (60-199)
  • dummy_instance (313-317)
rust/main/lander/src/tests/test_utils.rs (2)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (2)
  • reprocess_txs_poll_rate (641-645)
  • get_reprocess_txs (647-693)
rust/main/lander/src/adapter/core.rs (2)
  • reprocess_txs_poll_rate (122-124)
  • get_reprocess_txs (133-135)
⏰ 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). (11)
  • GitHub Check: infra-test
  • GitHub Check: build-and-push-to-gcr
  • GitHub Check: test-rs
  • GitHub Check: lint-rs
  • GitHub Check: lander-coverage
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: e2e-matrix (cosmwasm)
🔇 Additional comments (2)
rust/main/lander/src/tests/test_utils.rs (1)

38-39: Mock extensions look good.

These additions properly extend the MockAdapter to support the new AdaptsChain trait methods for reprocess functionality.

rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)

67-70: Task spawning follows established patterns.

The new receive_reprocess_txs task is properly spawned alongside the existing tasks, using the same instrumentation and joining approach.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (1)

49-70: Add missing mock expectation in two tests.

The InclusionStage implementation calls state.adapter.reprocess_txs_poll_rate() during run(). Two tests are missing this mock expectation:

  • test_unincluded_txs_reach_mempool (lines 49-87)
  • test_transaction_not_ready_for_resubmission (lines 235-259)

Both need to add:

mock_adapter
    .expect_reprocess_txs_poll_rate()
    .returning(|_| None);

Without this, the tests will panic with "No matching expectation found" when the stage runs.

♻️ Duplicate comments (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)

695-716: Critical: Infinite loop at U256::MAX boundary.

When old_finalized_nonce equals U256::MAX, the loop at line 697 never terminates. Here's why:

  • Line 697: while nonce <= old_finalized_nonce allows nonce to reach U256::MAX
  • Line 713: saturating_add(U256::one()) returns U256::MAX when already at MAX (saturating arithmetic)
  • Loop condition remains true forever, hanging the reprocess task

Add an explicit break before the saturating_add to handle the boundary:

             } else {
                 debug!(
                     ?nonce,
                     ?tx_uuid,
                     "No transaction found for nonce in reorg range"
                 );
             }
+            if nonce == old_finalized_nonce {
+                break;
+            }
             nonce = nonce.saturating_add(U256::one());
         }

Consider adding a test case that covers nonce values near U256::MAX to prevent regressions.

🧹 Nitpick comments (1)
rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (1)

583-646: Test coverage could be more thorough.

This test verifies that transactions from get_reprocess_txs() are inserted into the pool, but doesn't validate:

  • Whether the finalized nonce is actually updated
  • The nonce comparison logic (new < old scenario)
  • Edge cases like empty transaction ranges or boundary conditions
  • Whether reprocessed transactions are eventually processed (they stay pending here)

Consider adding assertions or separate tests to cover:

  1. Verify the mock's get_reprocess_txs is actually called during the test run
  2. Test the nonce boundary logic more directly (mock different old/new finalized nonces)
  3. Add a test where reprocessed transactions transition through the inclusion stage to the finality stage

Example enhancement to verify the mock was called:

let call_count = Arc::new(AtomicU32::new(0));
let call_count_clone = call_count.clone();
mock_adapter.expect_get_reprocess_txs().returning(move || {
    call_count_clone.fetch_add(1, Ordering::SeqCst);
    // ... existing logic
});
// ... after test runs
assert!(call_count.load(Ordering::SeqCst) > 0, "get_reprocess_txs should be called");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3107996 and 9be4a9a.

📒 Files selected for processing (2)
  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1 hunks)
  • rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs
  • rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs
🧬 Code graph analysis (2)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (3)
rust/main/lander/src/adapter/core.rs (3)
  • reprocess_txs_poll_rate (122-124)
  • get_reprocess_txs (133-135)
  • tx (69-73)
rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (1)
  • new (24-49)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (1)
  • new (31-60)
rust/main/lander/src/dispatcher/stages/inclusion_stage/tests.rs (4)
rust/main/lander/src/tests/test_utils.rs (3)
  • tmp_dbs (43-53)
  • create_random_txs_and_store_them (73-89)
  • are_all_txs_in_pool (95-101)
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)
  • new (43-56)
rust/main/lander/src/dispatcher/stages/state.rs (1)
  • new (40-54)
rust/main/lander/src/dispatcher/metrics.rs (2)
  • new (60-199)
  • dummy_instance (313-317)
⏰ 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). (1)
  • GitHub Check: build-and-push-to-gcr
🔇 Additional comments (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)

664-668: Solid rate limiting for reprocess polling.

The logic caps the poll rate at 5 seconds minimum to avoid hammering the nonce queries on fast chains, while still being responsive enough for reorg detection. Clean implementation.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)

674-720: Reprocessing logic looks solid, but there's a tiny edge case lurking.

The flow here makes sense—grab the old nonce, update boundaries, check if there's been a reorg (new < old), then walk through the reverted range collecting transactions. The sentinel UUID check at line 703 and the debug logs are good touches.

One thing though: if old_finalized_nonce somehow ends up at U256::MAX and the loop reaches that value, saturating_add will keep returning MAX, so the while nonce <= old_finalized_nonce condition never breaks. Yeah, U256::MAX is astronomically unlikely for a real nonce (we're talking 2^256 - 1 here), but it's an easy fix if you want to be extra safe.

You could add a break before the increment:

             } else {
                 debug!(
                     ?nonce,
                     ?tx_uuid,
                     "No transaction found for nonce in reorg range"
                 );
             }
+            if nonce == old_finalized_nonce {
+                break;
+            }
             nonce = nonce.saturating_add(U256::one());
         }

But honestly, this is more defensive programming than anything you'd actually hit in the real world.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9be4a9a and c2c66de.

📒 Files selected for processing (1)
  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs
🧬 Code graph analysis (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (3)
rust/main/lander/src/adapter/core.rs (3)
  • reprocess_txs_poll_rate (122-124)
  • get_reprocess_txs (133-135)
  • tx (69-73)
rust/main/lander/src/adapter/chains/ethereum/nonce/updater.rs (1)
  • new (24-49)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (1)
  • new (31-60)
⏰ 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). (12)
  • GitHub Check: build-and-push-to-gcr
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: e2e-matrix (cosmwasm)
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: yarn-install
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: agent-configs (mainnet3)
  • GitHub Check: lander-coverage
  • GitHub Check: test-rs
  • GitHub Check: lint-rs
🔇 Additional comments (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)

668-672: Nice polling rate logic here.

The cap at 5 seconds makes sense—keeps the nonce queries from going wild on chains with real short block times. The comment explains the reasoning well, and the implementation's clean.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (1)

210-213: Swallowing errors here is gonna make troubleshooting a proper headache.

When get_reprocess_txs fails, the wildcard match on line 212 just keeps trucking along without a peep. Could be a network hiccup, could be corrupted state—you won't know until things get really sideways.

Log the error so you've got breadcrumbs when things go wrong:

         let txs = match state.adapter.get_reprocess_txs().await {
             Ok(s) => s,
-            _ => continue,
+            Err(err) => {
+                warn!(?err, "Failed to fetch reprocess transactions, will retry");
+                continue;
+            }
         };
🧹 Nitpick comments (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)

707-724: Nonce loop has a theoretical edge case, though it's about as likely as finding dragon eggs in your swamp.

If old_finalized_nonce ever reaches U256::MAX and your loop counter hits it too, the saturating_add on line 723 will keep returning MAX instead of overflowing. The condition nonce <= old_finalized_nonce stays true forever, and you've got yourself an infinite loop.

Now, reaching a nonce of 2^256 is... let's just say you'd need more transactions than atoms in the observable universe. So this is more about keeping the code theoretically sound than fixing something that'll actually bite you.

If you want the extra layer of protection, add a break before incrementing:

             }
+            if nonce == old_finalized_nonce {
+                break;
+            }
             nonce = nonce.saturating_add(U256::one());
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2c66de and 7f9968a.

📒 Files selected for processing (2)
  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1 hunks)
  • rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/main/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Run cargo clippy for Rust code linting

Files:

  • rust/main/lander/src/adapter/chains/ethereum/adapter.rs
  • rust/main/lander/src/dispatcher/stages/inclusion_stage.rs
🧬 Code graph analysis (1)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (2)
rust/main/lander/src/adapter/core.rs (3)
  • reprocess_txs_poll_rate (122-124)
  • get_reprocess_txs (133-135)
  • tx (69-73)
rust/main/lander/src/adapter/chains/ethereum/nonce/manager.rs (1)
  • new (31-60)
⏰ 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). (11)
  • GitHub Check: infra-test
  • GitHub Check: build-and-push-to-gcr
  • GitHub Check: e2e-matrix (radix)
  • GitHub Check: e2e-matrix (evm)
  • GitHub Check: e2e-matrix (sealevel)
  • GitHub Check: e2e-matrix (starknet)
  • GitHub Check: e2e-matrix (cosmosnative)
  • GitHub Check: e2e-matrix (cosmwasm)
  • GitHub Check: lint-rs
  • GitHub Check: lander-coverage
  • GitHub Check: test-rs
🔇 Additional comments (3)
rust/main/lander/src/adapter/chains/ethereum/adapter.rs (1)

674-678: Poll rate logic looks solid.

Capping at 5 seconds keeps you from hammering the nonce queries during reorgs. Clean and sensible.

rust/main/lander/src/dispatcher/stages/inclusion_stage.rs (2)

67-70: Task spawning looks good.

Follows the same pattern as the other tasks, properly instrumented and all that.


187-197: Early exit logic is clean.

If there's no poll rate configured, you bail out early. Makes sense for chains that don't support this yet.

@kamiyaa kamiyaa added this pull request to the merge queue Oct 23, 2025
Merged via the queue into main with commit 21532b6 Oct 23, 2025
40 checks passed
@kamiyaa kamiyaa deleted the jeff/nonce-update branch October 23, 2025 13:58
@github-project-automation github-project-automation Bot moved this from In Review to Done in Hyperlane Tasks Oct 23, 2025
@github-actions github-actions Bot mentioned this pull request Oct 29, 2025
This was referenced Nov 5, 2025
danwt pushed a commit to dymensionxyz/hyperlane-monorepo that referenced this pull request Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants