Skip to content

Conversation

bangjelkoski
Copy link
Collaborator

@bangjelkoski bangjelkoski commented May 6, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new integration for CometBFT (Tendermint) event indexing, including block and transaction event handlers.
    • Added a WebSocket client with automatic failover and reconnection for robust event streaming.
    • Provided a sample usage script and comprehensive TypeScript type definitions for event handling.
  • Chores

    • Added configuration files for environment variables, TypeScript, Jest, and npm packaging.
    • Included a README with support and licensing information, and an Apache 2.0 license file.
  • Refactor

    • Updated build configuration to exclude the new integrations directory from the main TypeScript build.

@coderabbitai
Copy link

coderabbitai bot commented May 6, 2025

Walkthrough

A new integration module for CometBFT (Tendermint) event indexing was added under integrations/tm. This includes configuration files, TypeScript source code for handling and subscribing to block and transaction events, a logger, type definitions, an example usage script, and package metadata. The build configuration was also updated to exclude the integrations directory.

Changes

File(s) Change Summary
integrations/tm/.env.example Added example environment variable file with LOG_LEVEL=info.
integrations/tm/.npmignore Added .npmignore to include only built JS and type declaration files in npm package.
integrations/tm/LICENSE Added Apache License 2.0 text.
integrations/tm/README.md Added README with installation, support links, and license info.
integrations/tm/jest.config.js Added Jest config extending base config and specifying TypeScript config for tests.
integrations/tm/package.json Added package manifest with metadata, entry points, exports, scripts, and dependencies.
integrations/tm/tsconfig.json Added TypeScript config targeting ES2020, module settings, and environment types.
integrations/tm/src/logger.ts Added Winston logger setup, exporting configured logger based on environment variable.
integrations/tm/src/types.ts Added TypeScript interfaces/types for CometBFT block and transaction events, message handlers.
integrations/tm/src/client/ws.ts Added WebSocketClient class for robust connection/reconnection to multiple endpoints and message handling.
integrations/tm/src/client/handlers/blocks.ts Added handleNewBlock async function to process new block events and log indexing activity.
integrations/tm/src/client/handlers/txs.ts Added handleNewTx async function to process new transaction events and log indexing activity.
integrations/tm/src/client/indexer.ts Added CometBFTIndexer class to manage event subscription, message dispatching, and handler invocation for block and transaction events.
integrations/tm/src/example.ts Added example script instantiating indexer, subscribing to events, and setting up a periodic timer.
tsconfig.build.json Updated to exclude the "integrations" directory from the build process.

Sequence Diagram(s)

sequenceDiagram
    participant ExampleScript
    participant CometBFTIndexer
    participant WebSocketClient
    participant CometBFTNode
    participant BlockHandler
    participant TxHandler

    ExampleScript->>CometBFTIndexer: new CometBFTIndexer(endpoints)
    ExampleScript->>CometBFTIndexer: subscribe()
    CometBFTIndexer->>WebSocketClient: send subscription for NewBlock and Tx
    WebSocketClient->>CometBFTNode: Connect and subscribe
    CometBFTNode-->>WebSocketClient: Event message (NewBlock or Tx)
    WebSocketClient->>CometBFTIndexer: onMessage(msg)
    CometBFTIndexer->>BlockHandler: handleNewBlock(data) (if NewBlock)
    CometBFTIndexer->>TxHandler: handleNewTx(data) (if Tx)
Loading

Poem

🐇
New blocks and transactions, events in the air,
WebSockets connect with resilient flair.
Logging each moment, with Winston’s delight,
TypeScript ensures everything’s typed just right.
With configs and licenses, the groundwork is laid—
Hopping through code, integrations are made!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

integrations/tm/src/example.ts

Oops! Something went wrong! :(

ESLint: 7.32.0

Error: Error while loading rule 'jest/unbound-method': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
Occurred while linting /integrations/tm/src/example.ts
at throwError (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:39:11)
at getParserServices (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:31:9)
at create (/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js:116:55)
at Object.create (/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:38:20)
at create (/node_modules/eslint-plugin-jest/lib/rules/unbound-method.js:47:88)
at Object.create (/node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:41:20)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:765:21)
at /node_modules/eslint/lib/linter/linter.js:937:31
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:882:34)

integrations/tm/jest.config.js

Oops! Something went wrong! :(

ESLint: 7.32.0

Error: Error while loading rule 'jest/unbound-method': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
Note: detected a parser other than @typescript-eslint/parser. Make sure the parser is configured to forward "parserOptions.project" to @typescript-eslint/parser.
Occurred while linting /integrations/tm/jest.config.js
at throwError (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:39:11)
at getParserServices (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:23:9)
at create (/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js:116:55)
at Object.create (/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:38:20)
at create (/node_modules/eslint-plugin-jest/lib/rules/unbound-method.js:47:88)
at Object.create (/node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:41:20)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:765:21)
at /node_modules/eslint/lib/linter/linter.js:937:31
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:882:34)

integrations/tm/src/logger.ts

Oops! Something went wrong! :(

ESLint: 7.32.0

Error: Error while loading rule 'jest/unbound-method': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
Occurred while linting /integrations/tm/src/logger.ts
at throwError (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:39:11)
at getParserServices (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:31:9)
at create (/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js:116:55)
at Object.create (/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:38:20)
at create (/node_modules/eslint-plugin-jest/lib/rules/unbound-method.js:47:88)
at Object.create (/node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:41:20)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:765:21)
at /node_modules/eslint/lib/linter/linter.js:937:31
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:882:34)

  • 5 others
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 11

🧹 Nitpick comments (21)
integrations/tm/LICENSE (1)

1-1: Update license year range to the current year.
The header states © 2021 - 2022, but the integration was added in 2025. Please update the year range to 2021 - 2025 (or just 2025) to reflect the accurate timeline.

- Copyright © 2021 - 2022 Injective Labs Inc. (https://injectivelabs.org/)
+ Copyright © 2021 - 2025 Injective Labs Inc. (https://injectivelabs.org/)
tsconfig.build.json (1)

47-47: Scope exclusion more precisely.
Excluding "integrations" will skip the entire directory but might be ambiguous. Consider using a glob pattern to clearly exclude all subdirectories:

-    "integrations",
+    "integrations/**",
integrations/tm/.npmignore (1)

1-4: Whitelist essential files explicitly.
While npm always includes package.json, README.md, and LICENSE by default, it's safer to explicitly whitelist them to prevent future omissions:

# .npmignore
- *
+ *
  !dist/**/*.d.ts
  !dist/**/*.js
+ !README.md
+ !LICENSE
integrations/tm/tsconfig.json (1)

1-13: DRY: Extend root TypeScript configuration.
Instead of duplicating compiler options, consider extending the project's base tsconfig.json and overriding only what’s necessary:

{
- "compilerOptions": {
-   "module": "ESNext",
-   "target": "ES2020",
-   "moduleResolution": "bundler",
-   "esModuleInterop": true,
-   "moduleDetection": "force",
-   "allowSyntheticDefaultImports": true,
-   "resolveJsonModule": true,
-   "isolatedModules": true,
-   "types": ["node", "jest"]
- }
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+   // override or add integration-specific options here
+ }
}

This promotes consistency and reduces maintenance overhead.

integrations/tm/src/logger.ts (2)

1-19: Logger implementation uses Winston effectively but has some duplication

The logger configuration is well-structured but contains duplicate format settings in line 9-12 and line 16. This redundancy isn't necessary since you're only using one transport.

Consider simplifying the config by removing the duplicate format:

export const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.simple(),
  ),
  transports: [
    new winston.transports.Console({
      level: LOG_LEVEL,
-     format: winston.format.simple(),
    }),
  ],
})

6-6: Add validation for LOG_LEVEL environment variable

The LOG_LEVEL value is used directly without validation. Invalid log levels might cause unexpected behavior.

Consider validating the log level against Winston's valid levels:

-const LOG_LEVEL = process.env.LOG_LEVEL || 'info'
+const validLogLevels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly']
+const LOG_LEVEL = validLogLevels.includes(process.env.LOG_LEVEL?.toLowerCase() || '') 
+  ? process.env.LOG_LEVEL 
+  : 'info'
integrations/tm/README.md (2)

25-25: Add alt text to image for accessibility

The image lacks alt text, which is important for accessibility.

-<a href="https://iili.io/mNneZN.md.png"><img src="https://iili.io/mNneZN.md.png" style="width: 300px; max-width: 100%; height: auto" />
+<a href="https://iili.io/mNneZN.md.png"><img src="https://iili.io/mNneZN.md.png" alt="Injective Labs Logo" style="width: 300px; max-width: 100%; height: auto" />
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

25-25: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


27-30: Fix license formatting and use Markdown syntax

The license section has formatting issues including incorrect use of a colon and bare URLs.

-Originally released by Injective Labs Inc. under: <br />
-Apache License <br />
-Version 2.0, January 2004 <br />
-http://www.apache.org/licenses/
+Originally released by Injective Labs Inc. under <br />
+Apache License <br />
+Version 2.0, January 2004 <br />
+[http://www.apache.org/licenses/](http://www.apache.org/licenses/)
🧰 Tools
🪛 LanguageTool

[typographical] ~27-~27: Do not use a colon (:) before a series that is introduced by a preposition (‘under’). Remove the colon or add a noun or a noun phrase after the preposition.
Context: ...ginally released by Injective Labs Inc. under:
Apache License
Version 2....

(RP_COLON)

🪛 markdownlint-cli2 (0.17.2)

30-30: Bare URL used
null

(MD034, no-bare-urls)

integrations/tm/src/example.ts (1)

10-12: Improve empty interval implementation with comments

The empty interval seems to be a placeholder to keep the process running, but lacks explanation.

Add a comment explaining the purpose of this interval:

setInterval(() => {
-  //
+  // Empty interval to keep the process running while waiting for WebSocket events
}, 1000)
integrations/tm/jest.config.js (2)

6-8: Update deprecated ts-jest configuration property

The property 'tsConfig' (camelCase) is deprecated in newer versions of ts-jest, which prefers 'tsconfig' (lowercase).

globals: {
  'ts-jest': {
-    tsConfig: 'tsconfig.build.esm.json',
+    tsconfig: 'tsconfig.build.esm.json',
  },
},

1-10: Consider documenting test configuration purpose

The Jest configuration extends a base config but doesn't provide context on what tests are expected to run with this setup.

Add a comment explaining the purpose of this configuration:

import baseConfig from '../../jest.config.js'

+/**
+ * Jest configuration for the TM integration tests
+ * Extends the base configuration and uses the ESM TypeScript config
+ */
export default {
  ...baseConfig,
  globals: {
    'ts-jest': {
      tsConfig: 'tsconfig.build.esm.json',
    },
  },
}
integrations/tm/src/client/handlers/blocks.ts (1)

4-10: Function is marked async but doesn't await anything

The function is declared as async but doesn't contain any awaited operations, which is unnecessary overhead until async operations are implemented.

This is fine if you plan to add async operations later as indicated by the comment, but for clarity you could add a TODO comment explaining the async intent:

export async function handleNewBlock(data: NewBlockEvent['value']) {
  const height = parseInt(data.block.header.height)

  logger.info(`🧱 Indexed block #${height}`)

-  // Add indexing logic...
+  // TODO: Add async indexing logic here
}
integrations/tm/src/client/handlers/txs.ts (1)

9-9: Consider extracting transaction details in the comment

The comment is a simple placeholder, but it would be more informative to mention what transaction details might be indexed.

-  // Add indexing logic...
+  // TODO: Add indexing logic to process transaction details (hash, gas used, etc.)
integrations/tm/package.json (1)

4-4: Add a meaningful description for the package

The description field is empty, which misses an opportunity to explain the package's purpose.

-  "description": "",
+  "description": "Integration module for CometBFT (Tendermint) WebSocket event subscription and indexing",
integrations/tm/src/client/ws.ts (2)

54-66: Potential retry accumulation in send method

The send method uses a setTimeout to retry sending if the connection isn't in OPEN state, but it doesn't limit retries which could lead to many queued calls.

Consider implementing a maximum retry count or an exponential backoff:

public send(data: object) {
  if (!this.ws) {
    logger.warn('⚠️ WebSocket not initialized. Cannot send.')
    return
  }

+ private sendRetryCount = 0
+ private maxSendRetries = 10

  if (this.ws.readyState === WebSocket.OPEN) {
+   this.sendRetryCount = 0
    this.ws.send(JSON.stringify(data))
  } else {
+   if (this.sendRetryCount >= this.maxSendRetries) {
+     logger.error('❌ Maximum send retries reached, dropping message')
+     this.sendRetryCount = 0
+     return
+   }
+   this.sendRetryCount++
    setTimeout(() => this.send(data), 100)
  }
}

5-67: Add connection state tracking and events

The class doesn't expose its connection state or emit events about state changes, making it difficult for consumers to react to connection status.

Consider implementing a simple event system or state accessors:

+ import { EventEmitter } from 'events'
import WebSocket from 'ws'
import { logger } from '../logger.js'
import { MessageHandler } from '../types.js'

+ export enum ConnectionState {
+   DISCONNECTED = 'disconnected',
+   CONNECTING = 'connecting',
+   CONNECTED = 'connected'
+ }

- export class WebSocketClient {
+ export class WebSocketClient extends EventEmitter {
  private endpoints: string[]
  private currentIndex = 0
  private ws: WebSocket | null = null
  private reconnectTimeout = 3000
  private messageHandler: MessageHandler
+ private _state: ConnectionState = ConnectionState.DISCONNECTED

  constructor(endpoints: string[], messageHandler: MessageHandler) {
+   super()
    this.endpoints = endpoints
    this.messageHandler = messageHandler
    this.connect()
  }

+ get state(): ConnectionState {
+   return this._state
+ }

+ private setState(state: ConnectionState): void {
+   if (this._state !== state) {
+     this._state = state
+     this.emit('stateChange', state)
+     logger.debug(`WebSocket state changed to: ${state}`)
+   }
+ }

  private connect() {
    const endpoint = this.endpoints[this.currentIndex]
    
+   this.setState(ConnectionState.CONNECTING)
    logger.debug(`🔌 Connecting to ${endpoint}`)
    
    this.ws = new WebSocket(endpoint)
    
    this.ws.on('open', () => {
+     this.setState(ConnectionState.CONNECTED)
      logger.debug(`✅ Connected to ${endpoint}`)
    })
    
    // ... rest of the code
    
    this.ws.on('close', () => {
+     this.setState(ConnectionState.DISCONNECTED)
      logger.warn(`⚠️ Disconnected from ${endpoint}`)
      
      // ... rest of the code
    })
  }
}
integrations/tm/src/client/indexer.ts (3)

14-25: Consider more robust ID generation and subscription confirmation.

The current implementation sends JSON-RPC subscription requests with randomly generated IDs, which works but could be improved.

Consider these improvements:

  1. Use a more deterministic ID generation method or UUID for better traceability
  2. Add confirmation handling for subscription requests
public subscribe() {
  const subs = [{ query: "tm.event='NewBlock'" }, { query: "tm.event='Tx'" }]

  for (const sub of subs) {
    this.ws.send({
      jsonrpc: '2.0',
      method: 'subscribe',
-     id: Math.floor(Math.random() * 1_000_000),
+     id: `sub_${sub.query}_${Date.now()}`, // More traceable ID format
      params: sub,
    })
  }
+
+  // Consider adding logging for subscription attempts
+  logger.info(`🔔 Subscribed to ${subs.length} event types`)
}

30-34: Fix typo in debug message.

There's a minor grammatical error in the debug message.

if (!data) {
-  logger.debug('📦 Message data doesnt exist or is undefined:', msg)
+  logger.debug('📦 Message data doesn\'t exist or is undefined:', msg)

  return
}

36-40: Fix typo in debug message.

There's a minor grammatical error in the debug message.

if (!data.type) {
-  logger.debug('📦 Message type doesnt exist or is undefined:', msg)
+  logger.debug('📦 Message type doesn\'t exist or is undefined:', msg)

  return
}
integrations/tm/src/types.ts (2)

1-23: Consider adding documentation to the complex event structure.

The NewBlockEvent interface correctly models the CometBFT event structure but lacks documentation that would make it more maintainable.

+ /**
+  * Represents a CometBFT NewBlock event structure.
+  * Contains information about newly added blocks in the blockchain.
+  */
export interface NewBlockEvent {
  type: 'tendermint/event/NewBlock'
  value: {
    block: {
      header: {
+       /** Block height as string (requires parsing to number) */
        height: string
+       /** ISO timestamp of when the block was created */
        time: string
+       /** Identifier for the blockchain */
        chain_id: string
      }
      data: {
        txs: {
          tx: {
            body: {
              messages: {
                type: string
              }[]
            }
          }
        }[]
      }
    }
  }
}

6-6: Consider parsing numeric values at the type level.

Several numeric values are represented as strings in the event types, which will require parsing in the handler functions.

Consider defining helper types or utility functions to handle the conversion from string to number consistently:

// Helper type for string numbers
export type StringNumber = string;

// Utility function to safely parse string to number
export const parseStringNumber = (value: StringNumber): number => {
  const parsed = parseInt(value, 10);
  return isNaN(parsed) ? 0 : parsed;
};

This would make handling these fields more consistent across the codebase.

Also applies to: 29-29, 40-41

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adf15fd and c6f4f29.

⛔ Files ignored due to path filters (1)
  • integrations/tm/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • integrations/tm/.env.example (1 hunks)
  • integrations/tm/.npmignore (1 hunks)
  • integrations/tm/LICENSE (1 hunks)
  • integrations/tm/README.md (1 hunks)
  • integrations/tm/jest.config.js (1 hunks)
  • integrations/tm/package.json (1 hunks)
  • integrations/tm/src/client/handlers/blocks.ts (1 hunks)
  • integrations/tm/src/client/handlers/txs.ts (1 hunks)
  • integrations/tm/src/client/indexer.ts (1 hunks)
  • integrations/tm/src/client/ws.ts (1 hunks)
  • integrations/tm/src/example.ts (1 hunks)
  • integrations/tm/src/logger.ts (1 hunks)
  • integrations/tm/src/types.ts (1 hunks)
  • integrations/tm/tsconfig.json (1 hunks)
  • tsconfig.build.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
integrations/tm/src/client/handlers/txs.ts (2)
integrations/tm/src/types.ts (1)
  • NewTxEvent (25-47)
integrations/tm/src/logger.ts (1)
  • logger (8-19)
integrations/tm/src/client/indexer.ts (5)
integrations/tm/src/client/ws.ts (1)
  • WebSocketClient (5-67)
integrations/tm/src/types.ts (3)
  • MessageHandlerArgs (54-56)
  • NewBlockEvent (1-23)
  • NewTxEvent (25-47)
integrations/tm/src/logger.ts (1)
  • logger (8-19)
integrations/tm/src/client/handlers/blocks.ts (1)
  • handleNewBlock (4-10)
integrations/tm/src/client/handlers/txs.ts (1)
  • handleNewTx (4-10)
integrations/tm/src/example.ts (2)
packages/networks/src/network.ts (1)
  • getNetworkEndpoints (65-66)
integrations/tm/src/client/indexer.ts (1)
  • CometBFTIndexer (7-53)
integrations/tm/src/client/handlers/blocks.ts (2)
integrations/tm/src/types.ts (1)
  • NewBlockEvent (1-23)
integrations/tm/src/logger.ts (1)
  • logger (8-19)
integrations/tm/src/client/ws.ts (2)
integrations/tm/src/types.ts (1)
  • MessageHandler (57-57)
integrations/tm/src/logger.ts (1)
  • logger (8-19)
🪛 LanguageTool
integrations/tm/README.md

[typographical] ~23-~23: If you want to indicate numerical ranges or time ranges, consider using an en dash.
Context: ...a> --- ## 🔓 License Copyright © 2021 - 2022 Injective Labs Inc. (https://inject...

(DASH_RULE)


[typographical] ~27-~27: Do not use a colon (:) before a series that is introduced by a preposition (‘under’). Remove the colon or add a noun or a noun phrase after the preposition.
Context: ...ginally released by Injective Labs Inc. under:
Apache License
Version 2....

(RP_COLON)

🪛 markdownlint-cli2 (0.17.2)
integrations/tm/README.md

3-3: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)


23-23: Bare URL used
null

(MD034, no-bare-urls)


25-25: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


30-30: Bare URL used
null

(MD034, no-bare-urls)


32-32: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

🔇 Additional comments (6)
integrations/tm/.env.example (1)

1-1:

❌ Incorrect review comment

Include all required environment variables.
Currently .env.example only defines LOG_LEVEL. To ensure users can configure the Tendermint integration, add placeholders for other environment variables such as the RPC endpoint, WebSocket endpoint, subscription topics, etc.

Run this script to list all process.env references and verify that .env.example covers them:


🏁 Script executed:

#!/bin/bash
# List environment variables used in the integration source
rg -o 'process\.env\.\w+' -r '$0' integrations/tm/src | sort | uniq

Length of output: 121


Only LOG_LEVEL Is Referenced in the Tendermint Integration
The grep output shows the integration source only uses process.env.LOG_LEVEL, so no other environment-variable placeholders are needed in integrations/tm/.env.example.

Keep .env.example as:

LOG_LEVEL=info

Likely an incorrect or invalid review comment.

integrations/tm/src/example.ts (1)

2-2:

✅ Verification successful

Verify the .js extension in import path

You're importing from a local file with the '.js' extension in a TypeScript file, which is unusual.

This might be intentional if you're using ES modules in your compiled output. If that's not the case, you should remove the extension:

-import { CometBFTIndexer } from './client/indexer.js'
+import { CometBFTIndexer } from './client/indexer'

🏁 Script executed:

#!/bin/bash
# Check if other TypeScript files in the project use .js extension in imports
rg -t ts "import.*from.*\.js['\"]" --glob "!**/example.ts"

Length of output: 67936


No change needed for .js extension in imports

Importing with the “.js” extension is deliberate and aligns with other TypeScript files targeting ES modules. We’ve verified numerous TS files (including integrations/tm/src/client/ws.ts, packages/utils/src/time.ts, and many more) consistently use “.js” extensions, so no update is required.

integrations/tm/src/client/indexer.ts (2)

1-5: Import structure looks good.

The imports correctly bring in the necessary components from relative paths with explicit .js extensions, which is appropriate for ES modules. The code imports the WebSocketClient, event handlers, types, and logger from their respective locations.


7-12: Constructor design is appropriate.

The class accepts an array of endpoint URLs to support failover scenarios and initializes the WebSocketClient with proper binding of the message handler. This approach provides good resilience if primary endpoints become unavailable.

integrations/tm/src/types.ts (2)

49-53: Union type provides good flexibility.

The CometBFTEvent union type provides a good balance between type safety for known events and flexibility for unknown event types.


54-57: Message handler types are well-defined.

The types correctly model the structure of incoming message data with optional properties, which is appropriate for handling various message formats and potential absence of data.

Comment on lines +191 to +193
Copyright [2022] [InjectiveLabs]

Licensed under the Apache License, Version 2.0 (the "License");
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace placeholder boilerplate with actual project information.
The appendix still has placeholder fields: [2022] [InjectiveLabs]. Update these to concrete values, for example:

- Copyright [2022] [InjectiveLabs]
+ Copyright © 2025 Injective Labs Inc.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Copyright [2022] [InjectiveLabs]
Licensed under the Apache License, Version 2.0 (the "License");
Copyright © 2025 Injective Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");

Comment on lines +1 to +36
# 🌟 Injective Protocol - Integration TM


---

## 📚 Installation

---

## ⛑ Support

Reach out to us at one of the following places!

- Website at <a href="https://injective.com" target="_blank">`injective.com`</a>
- Twitter at <a href="https://twitter.com/Injective_" target="_blank">`@Injective`</a>
- Discord at <a href="https://discord.com/invite/NK4qdbv" target="_blank">`Discord`</a>
- Telegram at <a href="https://t.me/joininjective" target="_blank">`Telegram`</a>

---

## 🔓 License

Copyright © 2021 - 2022 Injective Labs Inc. (https://injectivelabs.org/)

<a href="https://iili.io/mNneZN.md.png"><img src="https://iili.io/mNneZN.md.png" style="width: 300px; max-width: 100%; height: auto" />

Originally released by Injective Labs Inc. under: <br />
Apache License <br />
Version 2.0, January 2004 <br />
http://www.apache.org/licenses/


<p>&nbsp;</p>
<div align="center">
<sub><em>Powering the future of decentralized finance.</em></sub>
</div>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance README with more comprehensive documentation

The README lacks essential information about what this integration does, how to use it, and installation instructions.

  1. Add a proper introduction explaining the purpose and functionality of the TM integration
  2. Fill in the installation section with actual instructions
  3. Consider adding a "Usage" section that references the example.ts file
  4. Fix the formatting issues identified by static analysis
# 🌟 Injective Protocol - Integration TM

+## Introduction
+This integration provides a client for CometBFT (formerly Tendermint) WebSocket connections to subscribe and process blockchain events such as new blocks and transactions.

---

## 📚 Installation

+```bash
+# Install dependencies
+npm install
+
+# Copy environment variables
+cp .env.example .env
+```

---

+## 🚀 Usage
+
+```typescript
+import { CometBFTIndexer } from '@injectivelabs/tm'
+
+// Initialize indexer with WebSocket endpoints
+const indexer = new CometBFTIndexer(['wss://tm.injective.network/websocket'])
+
+// Subscribe to events
+await indexer.subscribe()
+```
+
+See `src/example.ts` for a complete usage example.
+
+---

## ⛑ Support
🧰 Tools
🪛 LanguageTool

[typographical] ~23-~23: If you want to indicate numerical ranges or time ranges, consider using an en dash.
Context: ...a> --- ## 🔓 License Copyright © 2021 - 2022 Injective Labs Inc. (https://inject...

(DASH_RULE)


[typographical] ~27-~27: Do not use a colon (:) before a series that is introduced by a preposition (‘under’). Remove the colon or add a noun or a noun phrase after the preposition.
Context: ...ginally released by Injective Labs Inc. under:
Apache License
Version 2....

(RP_COLON)

🪛 markdownlint-cli2 (0.17.2)

3-3: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)


23-23: Bare URL used
null

(MD034, no-bare-urls)


25-25: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


30-30: Bare URL used
null

(MD034, no-bare-urls)


32-32: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

Comment on lines +1 to +12
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { CometBFTIndexer } from './client/indexer.js'

const endpoints = [`${getNetworkEndpoints(Network.Mainnet).rpc}/websocket`]

const indexer = new CometBFTIndexer(endpoints)

await indexer.subscribe()

setInterval(() => {
//
}, 1000)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and cleanup logic

The example lacks error handling for the subscription process and cleanup logic for proper termination.

Consider adding proper error handling and termination handling:

import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { CometBFTIndexer } from './client/indexer.js'

const endpoints = [`${getNetworkEndpoints(Network.Mainnet).rpc}/websocket`]

const indexer = new CometBFTIndexer(endpoints)

-await indexer.subscribe()
+try {
+  await indexer.subscribe()
+  console.log('Successfully subscribed to events')
+} catch (error) {
+  console.error('Failed to subscribe to events:', error)
+  process.exit(1)
+}

+// Handle process termination
+process.on('SIGINT', () => {
+  console.log('Terminating indexer...')
+  // Add cleanup code here if needed
+  process.exit(0)
+})

setInterval(() => {
  // Empty interval to keep the process running while waiting for WebSocket events
}, 1000)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { CometBFTIndexer } from './client/indexer.js'
const endpoints = [`${getNetworkEndpoints(Network.Mainnet).rpc}/websocket`]
const indexer = new CometBFTIndexer(endpoints)
await indexer.subscribe()
setInterval(() => {
//
}, 1000)
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { CometBFTIndexer } from './client/indexer.js'
const endpoints = [`${getNetworkEndpoints(Network.Mainnet).rpc}/websocket`]
const indexer = new CometBFTIndexer(endpoints)
try {
await indexer.subscribe()
console.log('Successfully subscribed to events')
} catch (error) {
console.error('Failed to subscribe to events:', error)
process.exit(1)
}
// Handle process termination
process.on('SIGINT', () => {
console.log('Terminating indexer...')
// Add cleanup code here if needed
process.exit(0)
})
setInterval(() => {
// Empty interval to keep the process running while waiting for WebSocket events
}, 1000)

Comment on lines +4 to +10
export async function handleNewBlock(data: NewBlockEvent['value']) {
const height = parseInt(data.block.header.height)

logger.info(`🧱 Indexed block #${height}`)

// Add indexing logic...
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

The handler should include error handling for malformed data

The function assumes data will always have the expected structure, which could lead to runtime errors if the block format changes or if malformed data is received.

Consider adding error handling:

export async function handleNewBlock(data: NewBlockEvent['value']) {
+  try {
    const height = parseInt(data.block.header.height)
    
    logger.info(`🧱 Indexed block #${height}`)
    
    // Add indexing logic...
+  } catch (error) {
+    logger.error(`Failed to process block: ${error instanceof Error ? error.message : String(error)}`)
+  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function handleNewBlock(data: NewBlockEvent['value']) {
const height = parseInt(data.block.header.height)
logger.info(`🧱 Indexed block #${height}`)
// Add indexing logic...
}
export async function handleNewBlock(data: NewBlockEvent['value']) {
try {
const height = parseInt(data.block.header.height)
logger.info(`🧱 Indexed block #${height}`)
// Add indexing logic...
} catch (error) {
logger.error(`Failed to process block: ${error instanceof Error ? error.message : String(error)}`)
}
}

Comment on lines +4 to +10
export async function handleNewTx(data: NewTxEvent['value']) {
const height = parseInt(data.TxResult.height)

logger.info(`📨 Indexed Tx at height ${height}`)

// Add indexing logic...
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for the transaction handler

Similar to the block handler, this function lacks error handling for malformed data, which could lead to unexpected crashes.

Implement try/catch to gracefully handle potential errors:

export async function handleNewTx(data: NewTxEvent['value']) {
+  try {
    const height = parseInt(data.TxResult.height)
    
    logger.info(`📨 Indexed Tx at height ${height}`)
    
    // Add indexing logic...
+  } catch (error) {
+    logger.error(`Failed to process transaction: ${error instanceof Error ? error.message : String(error)}`)
+  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function handleNewTx(data: NewTxEvent['value']) {
const height = parseInt(data.TxResult.height)
logger.info(`📨 Indexed Tx at height ${height}`)
// Add indexing logic...
}
export async function handleNewTx(data: NewTxEvent['value']) {
try {
const height = parseInt(data.TxResult.height)
logger.info(`📨 Indexed Tx at height ${height}`)
// Add indexing logic...
} catch (error) {
logger.error(`Failed to process transaction: ${error instanceof Error ? error.message : String(error)}`)
}
}

Comment on lines +44 to +47
"devDependencies": {
"@types/node-persist": "^3.1.8",
"shx": "^0.3.4"
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing TypeScript and testing dependencies

The package has TypeScript source files but doesn't list TypeScript or any testing frameworks as dev dependencies.

Add the necessary development dependencies:

  "devDependencies": {
+    "@types/jest": "^29.5.0",
+    "@types/node": "^20.8.0",
+    "@types/ws": "^8.5.10",
    "@types/node-persist": "^3.1.8",
+    "jest": "^29.5.0",
+    "ts-jest": "^29.1.0",
+    "typescript": "^5.0.0",
    "shx": "^0.3.4"
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"devDependencies": {
"@types/node-persist": "^3.1.8",
"shx": "^0.3.4"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^20.8.0",
"@types/ws": "^8.5.10",
"@types/node-persist": "^3.1.8",
"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"typescript": "^5.0.0",
"shx": "^0.3.4"
},

Comment on lines +33 to +39
this.ws.on('message', (data: WebSocket.Data) => {
try {
this.messageHandler(JSON.parse(data.toString()))
} catch (err) {
logger.error('❌ Error parsing message:', err)
}
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling in message handler

The try/catch block only catches JSON parsing errors but doesn't handle errors that might occur within the message handler itself.

Expand error handling to catch all potential errors:

this.ws.on('message', (data: WebSocket.Data) => {
  try {
-   this.messageHandler(JSON.parse(data.toString()))
+   const parsedData = JSON.parse(data.toString())
+   this.messageHandler(parsedData)
  } catch (err) {
-   logger.error('❌ Error parsing message:', err)
+   if (err instanceof SyntaxError) {
+     logger.error('❌ Error parsing message:', err)
+   } else {
+     logger.error('❌ Error handling message:', err)
+   }
  }
})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.ws.on('message', (data: WebSocket.Data) => {
try {
this.messageHandler(JSON.parse(data.toString()))
} catch (err) {
logger.error('❌ Error parsing message:', err)
}
})
this.ws.on('message', (data: WebSocket.Data) => {
try {
const parsedData = JSON.parse(data.toString())
this.messageHandler(parsedData)
} catch (err) {
if (err instanceof SyntaxError) {
logger.error('❌ Error parsing message:', err)
} else {
logger.error('❌ Error handling message:', err)
}
}
})

Comment on lines +22 to +52
private connect() {
const endpoint = this.endpoints[this.currentIndex]

logger.debug(`🔌 Connecting to ${endpoint}`)

this.ws = new WebSocket(endpoint)

this.ws.on('open', () => {
logger.debug(`✅ Connected to ${endpoint}`)
})

this.ws.on('message', (data: WebSocket.Data) => {
try {
this.messageHandler(JSON.parse(data.toString()))
} catch (err) {
logger.error('❌ Error parsing message:', err)
}
})

this.ws.on('error', (err: Error) => {
logger.error('❌ WebSocket error:', err)
})

this.ws.on('close', () => {
logger.warn(`⚠️ Disconnected from ${endpoint}`)

this.currentIndex = (this.currentIndex + 1) % this.endpoints.length

setTimeout(() => this.connect(), this.reconnectTimeout)
})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Check for empty endpoints array

The class doesn't validate if the endpoints array is empty, which could lead to undefined behavior when trying to connect.

Add validation in the constructor and connect method:

constructor(endpoints: string[], messageHandler: MessageHandler) {
+ if (!endpoints.length) {
+   throw new Error('At least one endpoint must be provided')
+ }
  this.endpoints = endpoints
  this.messageHandler = messageHandler
  this.connect()
}

private connect() {
+ if (!this.endpoints.length) {
+   logger.error('❌ No endpoints available for connection')
+   return
+ }
  const endpoint = this.endpoints[this.currentIndex]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private connect() {
const endpoint = this.endpoints[this.currentIndex]
logger.debug(`🔌 Connecting to ${endpoint}`)
this.ws = new WebSocket(endpoint)
this.ws.on('open', () => {
logger.debug(`✅ Connected to ${endpoint}`)
})
this.ws.on('message', (data: WebSocket.Data) => {
try {
this.messageHandler(JSON.parse(data.toString()))
} catch (err) {
logger.error('❌ Error parsing message:', err)
}
})
this.ws.on('error', (err: Error) => {
logger.error('❌ WebSocket error:', err)
})
this.ws.on('close', () => {
logger.warn(`⚠️ Disconnected from ${endpoint}`)
this.currentIndex = (this.currentIndex + 1) % this.endpoints.length
setTimeout(() => this.connect(), this.reconnectTimeout)
})
}
constructor(endpoints: string[], messageHandler: MessageHandler) {
if (!endpoints.length) {
throw new Error('At least one endpoint must be provided')
}
this.endpoints = endpoints
this.messageHandler = messageHandler
this.connect()
}
private connect() {
if (!this.endpoints.length) {
logger.error('❌ No endpoints available for connection')
return
}
const endpoint = this.endpoints[this.currentIndex]
logger.debug(`🔌 Connecting to ${endpoint}`)
this.ws = new WebSocket(endpoint)
this.ws.on('open', () => {
logger.debug(`✅ Connected to ${endpoint}`)
})
this.ws.on('message', (data: WebSocket.Data) => {
try {
this.messageHandler(JSON.parse(data.toString()))
} catch (err) {
logger.error('❌ Error parsing message:', err)
}
})
this.ws.on('error', (err: Error) => {
logger.error('❌ WebSocket error:', err)
})
this.ws.on('close', () => {
logger.warn(`⚠️ Disconnected from ${endpoint}`)
this.currentIndex = (this.currentIndex + 1) % this.endpoints.length
setTimeout(() => this.connect(), this.reconnectTimeout)
})
}

Comment on lines +5 to +20
export class WebSocketClient {
private endpoints: string[]

private currentIndex = 0

private ws: WebSocket | null = null

private reconnectTimeout = 3000

private messageHandler: MessageHandler

constructor(endpoints: string[], messageHandler: MessageHandler) {
this.endpoints = endpoints
this.messageHandler = messageHandler
this.connect()
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add method to explicitly disconnect WebSocket

The WebSocketClient class doesn't provide a way to explicitly close the connection, which could lead to resources not being properly released when the client is no longer needed.

Add a disconnect method to properly clean up resources:

export class WebSocketClient {
  private endpoints: string[]

  private currentIndex = 0

  private ws: WebSocket | null = null

  private reconnectTimeout = 3000

+ private shouldReconnect = true

  private messageHandler: MessageHandler

  constructor(endpoints: string[], messageHandler: MessageHandler) {
    this.endpoints = endpoints
    this.messageHandler = messageHandler
    this.connect()
  }

+ public disconnect(): void {
+   this.shouldReconnect = false
+   if (this.ws) {
+     this.ws.close()
+     this.ws = null
+   }
+ }

Then update the close handler to respect the reconnection flag:

  this.ws.on('close', () => {
    logger.warn(`⚠️ Disconnected from ${endpoint}`)

    this.currentIndex = (this.currentIndex + 1) % this.endpoints.length
    
-   setTimeout(() => this.connect(), this.reconnectTimeout)
+   if (this.shouldReconnect) {
+     setTimeout(() => this.connect(), this.reconnectTimeout)
+   }
  })

Comment on lines +42 to +52
switch (data.type) {
case 'tendermint/event/NewBlock':
await handleNewBlock(data.value as NewBlockEvent['value'])
break
case 'tendermint/event/Tx':
await handleNewTx(data.value as NewTxEvent['value'])
break
default:
logger.warn('📦 Unhandled message type:', data.type)
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for async event handlers.

The function awaits event handlers but doesn't include try/catch blocks to handle potential errors.

switch (data.type) {
  case 'tendermint/event/NewBlock':
+   try {
      await handleNewBlock(data.value as NewBlockEvent['value'])
+   } catch (error) {
+     logger.error(`❌ Error handling NewBlock event: ${error.message}`, error)
+   }
    break
  case 'tendermint/event/Tx':
+   try {
      await handleNewTx(data.value as NewTxEvent['value'])
+   } catch (error) {
+     logger.error(`❌ Error handling Tx event: ${error.message}`, error)
+   }
    break
  default:
    logger.warn('📦 Unhandled message type:', data.type)
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switch (data.type) {
case 'tendermint/event/NewBlock':
await handleNewBlock(data.value as NewBlockEvent['value'])
break
case 'tendermint/event/Tx':
await handleNewTx(data.value as NewTxEvent['value'])
break
default:
logger.warn('📦 Unhandled message type:', data.type)
}
}
switch (data.type) {
case 'tendermint/event/NewBlock':
try {
await handleNewBlock(data.value as NewBlockEvent['value'])
} catch (error) {
logger.error(`❌ Error handling NewBlock event: ${error.message}`, error)
}
break
case 'tendermint/event/Tx':
try {
await handleNewTx(data.value as NewTxEvent['value'])
} catch (error) {
logger.error(`❌ Error handling Tx event: ${error.message}`, error)
}
break
default:
logger.warn('📦 Unhandled message type:', data.type)
}
}

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.

1 participant