Skip to content

Fix/relayer error handling#447

Merged
mani99brar merged 13 commits into
devfrom
fix/relayer-error-handling
Jul 21, 2026
Merged

Fix/relayer error handling#447
mani99brar merged 13 commits into
devfrom
fix/relayer-error-handling

Conversation

@mani99brar

@mani99brar mani99brar commented May 18, 2026

Copy link
Copy Markdown
Contributor

PR-Codex overview

This PR focuses on enhancing the validator-cli and relayer-cli projects by introducing RPC fallback mechanisms, updating dependencies, and refining error handling. It also improves the structure of the code for better maintainability and adds testing instructions in the documentation.

Detailed summary

  • Updated target in tsconfig.json to es2021.
  • Added new RPC_FAILURE and RPC_RECOVERED events in botEvents.ts.
  • Introduced @ethersproject/providers dependency.
  • Enhanced error handling in DataError and ExecutionError classes.
  • Implemented FallbackProvider for RPC calls in fallbackProvider.ts.
  • Updated getVeaInbox and getVeaOutbox to use JsonRpcProvider.
  • Refactored the relayer logic to support fallback providers.
  • Improved logger messages for better debugging.
  • Added detailed testing instructions in README.md.

The following files were skipped due to too many changes: relayer-cli/src/utils/relay.ts

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features
    • Support multiple comma-separated RPC endpoints with automatic failover plus RPC failure/recovery events
    • Relayer heartbeat notifications covering started/running/stopped lifecycle
  • Bug Fixes
    • Improved subgraph/data fetching and execution error handling with clearer chain/network context
    • More robust scheduling and shutdown behavior, including releasing all process lock files on exit
  • Documentation
    • Expanded relayer operational documentation (modes, env vars, local/Docker run steps, tests); added validator testing guidance and RPC env guidance
  • Tests
    • Updated tests for provider/emitter changes and stricter non-null error behavior
  • Chores
    • Updated Node.js Docker base image to 24.14 (alpine3.23)

@netlify

netlify Bot commented May 18, 2026

Copy link
Copy Markdown

Deploy Preview for veascan ready!

Name Link
🔨 Latest commit dbe9cb3
🔍 Latest deploy log https://app.netlify.com/projects/veascan/deploys/6a49e8227e1ea900089bf759
😎 Deploy Preview https://deploy-preview-447--veascan.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented May 18, 2026

Copy link
Copy Markdown

Deploy Preview for veashi-scan ready!

Name Link
🔨 Latest commit dbe9cb3
🔍 Latest deploy log https://app.netlify.com/projects/veashi-scan/deploys/6a49e822c9a42f000817cae2
😎 Deploy Preview https://deploy-preview-447--veashi-scan.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds multi-endpoint RPC fallback, structured error and event context, and provider-aware relay and watcher wiring across relayer-cli and validator-cli. It also updates lifecycle cleanup, logging, configuration parsing, documentation, and Docker base images.

Changes

Core Relayer Refactor: Multi-RPC, Error Handling, and Events

Layer / File(s) Summary
Error types, BotEvents, and RPC type widening
relayer-cli/src/utils/errors.ts, relayer-cli/src/utils/botEvents.ts, relayer-cli/src/consts/bridgeRoutes.ts, relayer-cli/src/utils/hashiHelpers/bridgeRoutes.ts
DataError and ExecutionError include chain/network context and optional causes; BotEvents adds error and RPC fallback events; bridge and Hashi RPC config now accepts string or string-array endpoints.
FallbackRpcProvider implementation
relayer-cli/src/utils/fallbackProvider.ts
New provider class rotates through configured endpoints, emits RPC failure/recovery events, fixes network detection from chainId, and lazily caches inner providers.
Ethers helpers adapted to provider instances
relayer-cli/src/utils/ethers.ts
Wallet and contract helpers now accept JsonRpcProvider instances and connect inbox, outbox, and batcher factories through the injected provider.
Bridge env parsing for multi-RPC
relayer-cli/src/consts/bridgeRoutes.ts
Required RPC env vars are split into arrays and mapped into bridge configs for supported chain IDs.
Hashi executor: emitter threading and multi-RPC integration
relayer-cli/src/utils/hashi.ts, relayer-cli/src/utils/hashi.test.ts
Hashi execution threads the emitter through executability checks, uses fallback providers for source and target RPCs, throws on missing bridge config, and updates tests to match the new call shapes.
Subgraph queries and proof builders raise DataError with context
relayer-cli/src/utils/graphQueries.ts, relayer-cli/src/utils/proof.ts
Graph and proof helpers now require network context and throw DataError with chain/network metadata when subgraph requests fail.
Relay execution with fallback RPC and structured failures
relayer-cli/src/utils/relay.ts
Relay paths normalize RPC arrays, build fallback providers, pass network into data fetches, and wrap failures in ExecutionError while emitting structured message-failure events.
Relayer scheduler and process integration
relayer-cli/src/relayer.ts
The relayer main loop now sends heartbeats, sets up exit handlers once, schedules due networks through processNetworkConfig, and computes the next sleep from the earliest pending run.
Lifecycle helpers and tests
relayer-cli/src/utils/relayerHelpers.ts, relayer-cli/src/utils/relayerHelpers.test.ts
Lock-file cleanup scans STATE_DIR for pid files, exit handling centralizes shutdown and cleanup, and tests cover the new cleanup and handler behavior.
Logger event handlers updated for new events
relayer-cli/src/utils/logger.ts
Logger wiring adds handlers for error context and RPC fallback events, and expands message failure logs to include chain, network, and error data.
Documentation and Docker base image updates
relayer-cli/README.md, relayer-cli/Dockerfile
The README now describes modes, environment variables, local execution, Docker, and tests, and the Dockerfile base images were updated.

Validator CLI Multi-RPC and watcher updates

Layer / File(s) Summary
Bridge routes and fallback providers
validator-cli/src/consts/bridgeRoutes.ts, validator-cli/src/utils/fallbackProvider.ts, validator-cli/src/utils/fallbackProviderV5.ts, validator-cli/src/utils/botEvents.ts
Validator bridge routes now parse RPC lists from env vars, the bridge type stores ordered RPC arrays, and new fallback providers rotate through endpoints while emitting RPC fallback events.
Provider-aware ethers helpers and WETH approval path
validator-cli/src/utils/ethers.ts, validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts
Validator ethers helpers now accept JsonRpcProvider instances, and the Arbitrum-to-Gnosis WETH approval flow builds its signer and contract through a fallback provider.
Watcher uses fallback providers for reads and contract calls
validator-cli/src/watcher.ts
The watcher switches its network processing to fallback providers, updates RPC inputs to arrays, and wires v6 contract calls and v5 read paths through the new provider types.
Validator docs and config updates
validator-cli/README.md, validator-cli/.env.dist, validator-cli/tsconfig.json
The validator README adds testing commands, the env template documents comma-separated RPC fallback order, and tsconfig sets an explicit ES2021 target.
Validator event logging
validator-cli/src/utils/logger.ts
Validator logging now records RPC fallback failure and recovery events with dedicated handlers.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler as relayer:start()
  participant NetworkProc as processNetworkConfig
  participant Relay as relay/relayBatch/relayAllFrom
  participant Fallback as FallbackRpcProvider
  participant Subgraph as Graph queries / proof helpers
  participant Outbox as VeaOutbox
  Scheduler->>NetworkProc: config, emitter
  NetworkProc->>Relay: execute with network context
  Relay->>Fallback: build from rpcOutbox
  Relay->>Subgraph: fetch count / proof / message data
  Subgraph-->>Relay: data or DataError
  Relay->>Outbox: send or execute message
  Outbox-->>Relay: success or error
  Relay-->>NetworkProc: result or ExecutionError
Loading

Possibly related PRs

  • kleros/vea#445: Related Hashi executor changes around message execution and HASHI_MESSAGE_FAILING handling.

Suggested reviewers: jaybuidl

Poem

🐰 I hop through endpoints, one by one,
When one goes quiet, another’s begun.
With logs and locks and signals in tune,
The relayer and validator both hum in June.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main theme of the PR: improving relayer error handling and related RPC fallback behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/relayer-error-handling

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

Comment thread relayer-cli/Dockerfile Dismissed
Comment thread relayer-cli/Dockerfile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (3)
relayer-cli/src/utils/relay.ts (1)

8-8: ⚡ Quick win

Remove unused DataError import.

DataError is not used in this file and is already reported by static analysis.

Proposed fix
-import { MissingEnvironmentVariable, InvalidChainId, DataError, ExecutionError } from "./errors";
+import { MissingEnvironmentVariable, InvalidChainId, ExecutionError } from "./errors";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relayer-cli/src/utils/relay.ts` at line 8, Remove the unused import DataError
from the import list in this module: update the import statement that currently
reads import { MissingEnvironmentVariable, InvalidChainId, DataError,
ExecutionError } from "./errors" to only include the used symbols
(MissingEnvironmentVariable, InvalidChainId, ExecutionError) so the file no
longer imports DataError.
relayer-cli/src/utils/hashi.ts (1)

65-68: ⚡ Quick win

Make this missing-config failure actionable and drop the dead return.

After the throw, Line 68 is unreachable, and the current exception text still does not tell the operator which Hashi fields are missing.

Suggested cleanup
   if (!yaruAddress || !yahoAddress || !hashiAddress) {
     emitter.emit(BotEvents.HASHI_NOT_CONFIGURED, targetChainId);
-    throw new MissingEnvironmentVariable(`Hashi bridge ${sourceChainId} -> ${targetChainId}`);
-    return 0;
+    const missing = [
+      !yaruAddress && "yaruAddress",
+      !yahoAddress && "yahoAddress",
+      !hashiAddress && "hashiAddress",
+    ]
+      .filter(Boolean)
+      .join(", ");
+
+    throw new MissingEnvironmentVariable(
+      `Missing Hashi bridge config for ${sourceChainId} -> ${targetChainId}: ${missing}`
+    );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relayer-cli/src/utils/hashi.ts` around lines 65 - 68, The current block
throws MissingEnvironmentVariable then returns dead code and doesn't say which
env vars are missing; remove the unreachable "return 0", compute which of
yaruAddress, yahoAddress, hashiAddress are falsy (e.g. build an array missing =
['YARU...'] filtered by their values), include that missing list in the
MissingEnvironmentVariable message and in the emitter payload (replace
emitter.emit(BotEvents.HASHI_NOT_CONFIGURED, targetChainId) with
emitter.emit(BotEvents.HASHI_NOT_CONFIGURED, { targetChainId, missing })) so
operators see exactly which fields are absent.
relayer-cli/src/utils/hashi.test.ts (1)

156-162: ⚡ Quick win

Add an array-sourceRPC case to this expectation.

This PR widens the contract to string | string[], but this suite only locks the single-endpoint path right now. A regression in the new failover wiring would still pass.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relayer-cli/src/utils/hashi.test.ts` around lines 156 - 162, The test only
asserts the single-string path for fetchAllMessageLogs but the API now accepts
string|string[]; add an additional assertion that validates the array form is
handled: call or expect fetchAllMessageLogs with sourceRPC as an array (e.g.,
["http://test.rpc"]) and the same other args (sourceChainId,
["http://test.rpc"], "0xYAHO", startBlockNumber, mockEmitter) or add a second
expect toHaveBeenCalledWith using that array form so the failover/array wiring
is covered; locate and update the test around the existing
expect(fetchAllMessageLogs).toHaveBeenCalledWith(...) assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@relayer-cli/README.md`:
- Around line 55-61: Update the README section that claims to "Generate contract
types(from root)" to use the actual build command rather than tests: replace the
current steps that change into the contracts folder and run `yarn test` with the
workspace build command `yarn workspace `@kleros/vea-contracts` build` so the
documented flow matches the relayer type-generation step; keep the initial `yarn
install` step.

In `@relayer-cli/src/consts/bridgeRoutes.ts`:
- Around line 67-72: The current rpcInbox and rpcOutbox initialization can
produce arrays like [undefined] when RPC_ARBITRUM_SEPOLIA or RPC_SEPOLIA are
unset; update the logic in bridgeRoutes where rpcInbox and rpcOutbox are built
to validate and normalize the env var: either throw a clear error if the env var
is missing (so the app fails early) or always run (process.env.VAR ?? "")
.split(",").map(s=>s.trim()).filter(Boolean) to produce a non-empty array of
endpoints; ensure the change references rpcInbox and rpcOutbox and results in a
validated array before being passed to FallbackRpcProvider.

In `@relayer-cli/src/relayer.ts`:
- Around line 36-42: The sendHeartbeat(...) failures are currently allowed to
throw and abort the relayer; wrap each await sendHeartbeat call (e.g., the
startup call sendHeartbeat("started", HEARTBEAT_URL!), the in-loop
sendHeartbeat("running", HEARTBEAT_URL!), and the other occurrence) in a
try/catch so heartbeat transport errors are caught, logged (include context and
the error), and not rethrown; keep the relayer loop and start() execution
continuing on heartbeat failures. Use the existing logging mechanism and
reference the sendHeartbeat function and HEARTBEAT_URL constant when making the
change.
- Around line 92-95: The epoch delay calculation uses seconds stored in
currentTS but then divides it by 1000 again; update the computation in the block
using getEpochPeriod(chainId) so the modulus uses currentTS directly (i.e.,
replace Math.floor(currentTS / 1000) % epochPeriod with currentTS % epochPeriod)
and keep timeLeft expressed in milliseconds by multiplying the remaining seconds
by 1000 (still add the 100*1000 jitter), ensuring variables currentTS,
epochPeriod, timeLeft and the return Date.now() + timeLeft are adjusted
accordingly.

In `@relayer-cli/src/utils/fallbackProvider.ts`:
- Around line 26-27: The label() helper currently falls back to returning the
raw endpoint URL (this.endpoints[i].url) which can leak credentials/tokens;
change label() to never emit the full URL — return endpoint.label when present,
otherwise return a sanitized identifier (e.g., hostname[:port] or a masked URL
that strips credentials and query string) by parsing this.endpoints[i].url.
Implement a small sanitizer utility used by label() and update the other places
that call label() (the spots around the other uses of this.endpoints[...] where
raw URLs were being emitted) to use the sanitized result so no credentials or
query tokens appear in logs/events.
- Around line 13-16: The constructor currently dereferences normalized[0].url
which throws for an empty endpoints array; add an explicit guard at the start of
the constructor to validate endpoints/normalized is non-empty (using the
constructor signature and normalized variable) and bail out with a clear,
structured error or emitter notification instead of letting a raw crash occur.
Specifically: check if normalized.length === 0 (or endpoints.length === 0) and
throw a descriptive Error (or emit an error via the provided EventEmitter)
before calling super, so you never access normalized[0].url when there are no
RPC endpoints.

In `@relayer-cli/src/utils/hashi.ts`:
- Around line 300-303: The code dereferences latestFinalized without checking
for null after calling provider.getBlock("finalized"); update the block lookup
in the FallbackRpcProvider usage so you check that latestFinalized is non-null
before accessing latestFinalized.number (e.g., call
provider.getBlock("finalized"), if result is null log an error/ warning and
either retry/fallback to provider.getBlock("latest") or throw a clear error),
ensuring the logic around toBlock uses the guarded value; refer to
provider.getBlock("finalized"), latestFinalized and toBlock when making the
change.

In `@relayer-cli/src/utils/hashiHelpers/bridgeRoutes.ts`:
- Around line 16-21: The current sourceRPC/targetRPC assignments (e.g.,
sourceRPC, targetRPC using process.env.RPC_ARBITRUM_SEPOLIA and
process.env.RPC_SEPOLIA) can yield undefined via the non-null assertion; change
them to always produce a string[] by reading the env var into a string (or empty
string if undefined), split on ',' and trim/filter out empty entries so the
value is always string[]; apply this pattern to all similar configs in
bridgeRoutes.ts (all places referencing RPC_ARBITRUM_SEPOLIA, RPC_SEPOLIA, etc.)
so downstream code no longer receives undefined.

In `@relayer-cli/src/utils/relay.ts`:
- Around line 83-91: The batch-path functions relayBatch and relayAllFrom
currently destructure bridgeConfig and use process.env.PRIVATE_KEY without
validation; update them to mirror the guards used in relay: call
fetchBridgeConfig(chainId) and if it returns falsy throw InvalidChainId (or the
same error type used in relay), validate process.env.PRIVATE_KEY and throw
MissingEnvironmentVariable (or the same env error) when absent, and only then
extract batcherAddress, veaContracts, rpcOutbox and construct
veaInbox/veaOutbox, rpcOutboxUrls, FallbackRpcProvider, batcher (fetchBatcher)
and veaOutbox (fetchVeaOutbox); ensure you reference the same helper functions
(fetchBridgeConfig, fetchBatcher, fetchVeaOutbox) and env var PRIVATE_KEY so the
error surfaces consistently instead of causing downstream failures.
- Line 198: The return currently does `lastNonce + 1` which yields 1 when
lastNonce is null; change the final return in the function that returns
Promise<number | null> to explicitly return null when lastNonce is null (e.g.
`return lastNonce === null ? null : lastNonce + 1`) so the function returns null
to indicate no relay occurred and only returns a number when a valid lastNonce
exists; update any nearby comments to match this behavior (the variable to
change is lastNonce and the function's declared return type Promise<number |
null> referenced by the early exit at line 162).

In `@relayer-cli/src/utils/relayerHelpers.ts`:
- Around line 119-124: The handleExit routine currently always calls
process.exit() and can be re-entered from the Node "exit" listener; change
handleExit(signature) to accept a boolean flag (e.g., shouldExit: boolean =
true) and an internal guarded "isExiting" flag to avoid double runs; when
invoked set isExiting and perform shutdownManager.triggerShutdown(),
emitter.emit(BotEvents.EXIT), await cleanupAllLockFiles(emitter) and only call
process.exit(exitCode) if shouldExit is true. Update the process signal handlers
to call handleExit() normally, but refactor the "exit" event listener to perform
only synchronous cleanup (e.g., shutdownManager.triggerShutdown() and
emitter.emit(BotEvents.EXIT)) without awaiting async work or calling
handleExit/process.exit to prevent re-entrancy.
- Around line 126-129: The addListenerOnce helper currently skips registering if
any listener exists globally (process.listenerCount(event) === 0), which can
silently prevent your handlers from being added; change addListenerOnce to
maintain an internal Set (e.g., registeredEvents) tracking events this module
has registered, and only skip registration when that Set already contains the
event; on first registration call process.on(event, handler) and add the event
to registeredEvents so subsequent calls from this module are no-ops while not
being affected by external listeners.

---

Nitpick comments:
In `@relayer-cli/src/utils/hashi.test.ts`:
- Around line 156-162: The test only asserts the single-string path for
fetchAllMessageLogs but the API now accepts string|string[]; add an additional
assertion that validates the array form is handled: call or expect
fetchAllMessageLogs with sourceRPC as an array (e.g., ["http://test.rpc"]) and
the same other args (sourceChainId, ["http://test.rpc"], "0xYAHO",
startBlockNumber, mockEmitter) or add a second expect toHaveBeenCalledWith using
that array form so the failover/array wiring is covered; locate and update the
test around the existing expect(fetchAllMessageLogs).toHaveBeenCalledWith(...)
assertion.

In `@relayer-cli/src/utils/hashi.ts`:
- Around line 65-68: The current block throws MissingEnvironmentVariable then
returns dead code and doesn't say which env vars are missing; remove the
unreachable "return 0", compute which of yaruAddress, yahoAddress, hashiAddress
are falsy (e.g. build an array missing = ['YARU...'] filtered by their values),
include that missing list in the MissingEnvironmentVariable message and in the
emitter payload (replace emitter.emit(BotEvents.HASHI_NOT_CONFIGURED,
targetChainId) with emitter.emit(BotEvents.HASHI_NOT_CONFIGURED, {
targetChainId, missing })) so operators see exactly which fields are absent.

In `@relayer-cli/src/utils/relay.ts`:
- Line 8: Remove the unused import DataError from the import list in this
module: update the import statement that currently reads import {
MissingEnvironmentVariable, InvalidChainId, DataError, ExecutionError } from
"./errors" to only include the used symbols (MissingEnvironmentVariable,
InvalidChainId, ExecutionError) so the file no longer imports DataError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1c1aff6-cc88-4a91-81ff-90e270bf0c0b

📥 Commits

Reviewing files that changed from the base of the PR and between 8911800 and f1143ae.

📒 Files selected for processing (17)
  • relayer-cli/Dockerfile
  • relayer-cli/README.md
  • relayer-cli/src/consts/bridgeRoutes.ts
  • relayer-cli/src/relayer.ts
  • relayer-cli/src/utils/botEvents.ts
  • relayer-cli/src/utils/errors.ts
  • relayer-cli/src/utils/ethers.ts
  • relayer-cli/src/utils/fallbackProvider.ts
  • relayer-cli/src/utils/graphQueries.ts
  • relayer-cli/src/utils/hashi.test.ts
  • relayer-cli/src/utils/hashi.ts
  • relayer-cli/src/utils/hashiHelpers/bridgeRoutes.ts
  • relayer-cli/src/utils/logger.ts
  • relayer-cli/src/utils/proof.ts
  • relayer-cli/src/utils/relay.ts
  • relayer-cli/src/utils/relayerHelpers.test.ts
  • relayer-cli/src/utils/relayerHelpers.ts

Comment thread relayer-cli/README.md
Comment thread relayer-cli/src/consts/bridgeRoutes.ts Outdated
Comment thread relayer-cli/src/relayer.ts
Comment thread relayer-cli/src/relayer.ts
Comment thread relayer-cli/src/utils/fallbackProvider.ts
Comment thread relayer-cli/src/utils/hashiHelpers/bridgeRoutes.ts
Comment thread relayer-cli/src/utils/relay.ts
Comment thread relayer-cli/src/utils/relay.ts Outdated
Comment thread relayer-cli/src/utils/relayerHelpers.ts
Comment thread relayer-cli/src/utils/relayerHelpers.ts
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
0.0% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
relayer-cli/src/utils/relay.ts (1)

83-87: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add explicit bridgeConfig validation before destructuring.

Line 83 and Line 153 can return an undefined config, and destructuring at Line 86 / Line 154 then throws a generic runtime error instead of surfacing a clear chain-config failure. Please mirror the explicit chain guard used elsewhere.

Proposed fix
   const bridgeConfig = fetchBridgeConfig(chainId);
+  if (!bridgeConfig) throw new InvalidChainId(chainId);
   const privateKey = process.env.PRIVATE_KEY;
   if (!privateKey) throw new MissingEnvironmentVariable("PRIVATE_KEY");
   const { batcherAddress, veaContracts, rpcOutbox } = bridgeConfig;
   const bridgeConfig = getBridgeConfig(chainId);
+  if (!bridgeConfig) throw new InvalidChainId(chainId);
   const { veaContracts, batcherAddress, rpcOutbox } = bridgeConfig;

Also applies to: 153-154

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relayer-cli/src/utils/relay.ts` around lines 83 - 87, The code destructures
bridgeConfig immediately after calling fetchBridgeConfig which can return
undefined; add an explicit guard after calling fetchBridgeConfig (in both places
where bridgeConfig is used) that checks if bridgeConfig is falsy and throws a
clear error (e.g., throw new Error(`Missing bridge config for chainId
${chainId}`)) before destructuring batcherAddress, veaContracts, rpcOutbox and
before accessing veaContracts[network].veaInbox.address so the failure surfaces
as a clear chain-config error instead of a generic runtime exception.
relayer-cli/src/relayer.ts (1)

36-36: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t let heartbeat transport failures stop the relayer loop.

Line 36, Line 42, and Line 54 await heartbeat calls directly; a transient heartbeat failure will throw and abort relay execution. Wrap each call in try/catch and log/emit exception context without rethrowing.

Also applies to: 42-42, 54-54

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relayer-cli/src/relayer.ts` at line 36, The relayer currently awaits
sendHeartbeat(…, HEARTBEAT_URL!) directly (e.g., the three sendHeartbeat calls
in relayer.ts), so a transient network error will throw and abort the relayer
loop; wrap each await sendHeartbeat(...) in its own try/catch, catch the error
and log or emit the exception context (including the status string and
HEARTBEAT_URL) using the existing logger/emit mechanism, and do not rethrow so
the relayer continues running; ensure you update the three call sites that
reference sendHeartbeat and HEARTBEAT_URL to follow this pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@relayer-cli/src/relayer.ts`:
- Line 36: The relayer currently awaits sendHeartbeat(…, HEARTBEAT_URL!)
directly (e.g., the three sendHeartbeat calls in relayer.ts), so a transient
network error will throw and abort the relayer loop; wrap each await
sendHeartbeat(...) in its own try/catch, catch the error and log or emit the
exception context (including the status string and HEARTBEAT_URL) using the
existing logger/emit mechanism, and do not rethrow so the relayer continues
running; ensure you update the three call sites that reference sendHeartbeat and
HEARTBEAT_URL to follow this pattern.

In `@relayer-cli/src/utils/relay.ts`:
- Around line 83-87: The code destructures bridgeConfig immediately after
calling fetchBridgeConfig which can return undefined; add an explicit guard
after calling fetchBridgeConfig (in both places where bridgeConfig is used) that
checks if bridgeConfig is falsy and throws a clear error (e.g., throw new
Error(`Missing bridge config for chainId ${chainId}`)) before destructuring
batcherAddress, veaContracts, rpcOutbox and before accessing
veaContracts[network].veaInbox.address so the failure surfaces as a clear
chain-config error instead of a generic runtime exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2c35f882-2870-45d1-8dac-e6ba526fb9d4

📥 Commits

Reviewing files that changed from the base of the PR and between f1143ae and e10c947.

📒 Files selected for processing (5)
  • relayer-cli/README.md
  • relayer-cli/src/consts/bridgeRoutes.ts
  • relayer-cli/src/relayer.ts
  • relayer-cli/src/utils/relay.ts
  • relayer-cli/src/utils/relayerHelpers.ts
✅ Files skipped from review due to trivial changes (1)
  • relayer-cli/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • relayer-cli/src/consts/bridgeRoutes.ts

@mani99brar
mani99brar marked this pull request as ready for review May 19, 2026 09:09
@mani99brar
mani99brar requested a review from jaybuidl as a code owner May 19, 2026 09:09
Comment thread validator-cli/Dockerfile
Comment thread validator-cli/Dockerfile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
validator-cli/Dockerfile (1)

6-6: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Pin both Node base images by digest.
validator-cli/Dockerfile:6,56 still use the mutable tag node:24.14-alpine3.23; pin both FROM lines to the same digest to keep builds reproducible and reduce supply-chain risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@validator-cli/Dockerfile` at line 6, Both Node base images are still using
the mutable node:24.14-alpine3.23 tag, so update the two FROM statements in
validator-cli/Dockerfile to reference the same pinned digest instead. Keep the
validator-build stage and the final runtime stage aligned on that exact digest
to make builds reproducible and avoid tag drift.

Source: Linters/SAST tools

validator-cli/src/utils/fallbackProvider.ts (1)

11-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mark never-reassigned members readonly.

Static analysis flags endpoints, emitter, and staticChainId as never reassigned.

-  private endpoints: RPCEndpoint[];
+  private readonly endpoints: RPCEndpoint[];
   private activeIndex = 0;
-  private emitter: EventEmitter;
-  private staticChainId?: number;
+  private readonly emitter: EventEmitter;
+  private readonly staticChainId?: number;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@validator-cli/src/utils/fallbackProvider.ts` around lines 11 - 14, The fields
in FallbackProvider that are never reassigned should be marked readonly to
satisfy the static analysis warning. Update the class member declarations for
endpoints, emitter, and staticChainId in FallbackProvider so they are immutable
after initialization, while leaving activeIndex mutable because it is
intentionally changed later.

Source: Linters/SAST tools

validator-cli/src/utils/fallbackProviderV5.ts (1)

12-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mark never-reassigned members readonly.

Static analysis flags endpoints and emitter as never reassigned.

-  private endpoints: RPCEndpoint[];
+  private readonly endpoints: RPCEndpoint[];
   private activeIndex = 0;
-  private emitter: EventEmitter;
+  private readonly emitter: EventEmitter;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@validator-cli/src/utils/fallbackProviderV5.ts` around lines 12 - 14, Mark the
never-reassigned members in FallbackProviderV5 as readonly so they match their
actual usage. Update the class fields endpoints and emitter in
fallbackProviderV5 and keep activeIndex mutable since it changes, making sure
the constructor still initializes the readonly members correctly.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@validator-cli/src/consts/bridgeRoutes.ts`:
- Around line 30-34: splitRpcUrls currently turns missing or empty values into
an empty array, which is fine for optional routerRPC but not for required
inboxRPC/outboxRPC. Update splitRpcUrls or its callers in bridgeRoutes to
validate required env vars early and throw a contextual error when inboxRPC or
outboxRPC is absent/blank, while preserving empty arrays only for routerRPC. Use
the existing splitRpcUrls helper and the inboxRPC/outboxRPC/routerRPC config
wiring to place the check where the values are parsed.

In `@validator-cli/src/utils/fallbackProvider.ts`:
- Around line 32-34: The fallback provider label method is exposing sensitive
RPC URLs when no explicit label is set, which can leak secrets through RPC
failure/recovery logs. Update FallbackProvider.label to avoid returning the raw
endpoint url and instead use a safe non-sensitive identifier for each endpoint,
matching the safer behavior expected for FallbackRpcProvider and its
RPC_FAILURE/RPC_RECOVERED logging paths.

In `@validator-cli/src/utils/fallbackProviderV5.ts`:
- Around line 26-28: The fallback in label() on FallbackProviderV5 currently
returns the raw endpoint url, which can expose embedded API keys in RPC URLs and
then propagate into RPC_FAILURE/RPC_RECOVERED logs. Update label(i) to return a
non-sensitive identifier instead of url when label is missing, and keep
logger.ts payloads using that sanitized label so rpc_failure and rpc_recovered
never emit secrets. Use the label() method in fallbackProviderV5 and the log
paths in logger.ts to verify the unsafe fallback is removed.
- Around line 1-24: Add `@ethersproject/providers` to the explicit dependencies
for `validator-cli`, since `FallbackProviderV5` imports `JsonRpcProvider`
directly and should not rely on hoisting or transitive resolution. Update the
package manifest for `validator-cli` so the module used by `FallbackProviderV5`
is declared, keeping the import in `fallbackProviderV5` unchanged.

In `@validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts`:
- Around line 31-34: `approveWeth()` is creating a new `FallbackRpcProvider`
instead of using the handler’s persistent outbox provider, which resets fallback
state on every call. Update `arbToGnosisHandler` so `approveWeth()` reuses the
`BaseTransactionHandler` outbox provider already available on the handler
instance, and keep the existing `getWallet`/`getWETH` flow wired to that shared
provider rather than constructing a fresh one.

---

Nitpick comments:
In `@validator-cli/Dockerfile`:
- Line 6: Both Node base images are still using the mutable
node:24.14-alpine3.23 tag, so update the two FROM statements in
validator-cli/Dockerfile to reference the same pinned digest instead. Keep the
validator-build stage and the final runtime stage aligned on that exact digest
to make builds reproducible and avoid tag drift.

In `@validator-cli/src/utils/fallbackProvider.ts`:
- Around line 11-14: The fields in FallbackProvider that are never reassigned
should be marked readonly to satisfy the static analysis warning. Update the
class member declarations for endpoints, emitter, and staticChainId in
FallbackProvider so they are immutable after initialization, while leaving
activeIndex mutable because it is intentionally changed later.

In `@validator-cli/src/utils/fallbackProviderV5.ts`:
- Around line 12-14: Mark the never-reassigned members in FallbackProviderV5 as
readonly so they match their actual usage. Update the class fields endpoints and
emitter in fallbackProviderV5 and keep activeIndex mutable since it changes,
making sure the constructor still initializes the readonly members correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6b9807f-b11c-435e-9890-49562af586b9

📥 Commits

Reviewing files that changed from the base of the PR and between e10c947 and 69d2a14.

📒 Files selected for processing (12)
  • validator-cli/.env.dist
  • validator-cli/Dockerfile
  • validator-cli/README.md
  • validator-cli/src/consts/bridgeRoutes.ts
  • validator-cli/src/utils/botEvents.ts
  • validator-cli/src/utils/ethers.ts
  • validator-cli/src/utils/fallbackProvider.ts
  • validator-cli/src/utils/fallbackProviderV5.ts
  • validator-cli/src/utils/logger.ts
  • validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts
  • validator-cli/src/watcher.ts
  • validator-cli/tsconfig.json
✅ Files skipped from review due to trivial changes (2)
  • validator-cli/README.md
  • validator-cli/.env.dist

Comment thread validator-cli/src/consts/bridgeRoutes.ts
Comment thread validator-cli/src/utils/fallbackProvider.ts
Comment thread validator-cli/src/utils/fallbackProviderV5.ts
Comment thread validator-cli/src/utils/fallbackProviderV5.ts
Comment thread validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts Outdated
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
14.7% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@mani99brar
mani99brar merged commit b90703e into dev Jul 21, 2026
16 of 17 checks passed
@mani99brar
mani99brar deleted the fix/relayer-error-handling branch July 21, 2026 07:42
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.

relayer-cli: proof generation returns empty array in case of an error

3 participants