Skip to main content

Code structure

The watch-tower codebase is organized into several key directories:
src/
├── commands/          # CLI command implementations
├── domain/            # Business logic and domain models
│   ├── events/        # Event processing
│   └── polling/       # Order polling and filtering
├── services/          # External service integrations
├── types/             # TypeScript type definitions
│   └── generated/     # Auto-generated contract types
└── utils/             # Utility functions and helpers

Main execution flow

The watch-tower follows this execution flow:

1. Initialization

  • Entry point: src/index.ts
  • Parses CLI arguments using Commander.js
  • Validates configuration against JSON schema
  • Initializes logging system

2. Command execution

  • Run command: src/commands/run.ts
    • Opens database connection
    • Starts REST API server (unless disabled)
    • Initializes chain contexts for each configured network
    • Sets up signal handlers for graceful shutdown

3. Chain monitoring

  • Chain context: src/services/chain.ts
    • Creates provider connection (WebSocket or HTTP)
    • Loads registry from database
    • Warms up by syncing historical blocks
    • Subscribes to new blocks

4. Event processing

  • Event handling: src/domain/events/
    • Monitors for ConditionalOrderCreated events
    • Indexes new conditional orders in registry
    • Processes events atomically per block

5. Order polling

  • Polling logic: src/domain/polling/
    • Checks each registered conditional order
    • Determines if discrete orders should be created
    • Filters orders based on configured policies
    • Posts valid orders to OrderBook API

6. Persistence

  • Storage: src/services/storage.ts
    • Writes registry state to LevelDB
    • Tracks last processed block
    • Maintains ACID guarantees via atomic batching

Key modules

Commands (src/commands/)

Implements CLI commands:
  • run.ts: Main watch-tower operation
  • dumpDb.ts: Database inspection utility

Domain (src/domain/)

Contains core business logic:
  • events/: Event processing logic
  • polling/: Order polling and placement
    • poll.ts: Main polling loop
    • filtering/: Order filtering policies

Services (src/services/)

External service integrations:
  • api.ts: REST API server (Express)
  • chain.ts: Blockchain interaction (ethers.js)
  • storage.ts: Database operations (LevelDB)

Types (src/types/)

Type definitions:
  • model.ts: Domain models (Registry, ConditionalOrder)
  • generated/: TypeChain-generated contract types
  • types.d.ts: Global type declarations

Utils (src/utils/)

Shared utilities:
  • logging.ts: Logging configuration
  • metrics.ts: Prometheus metrics
  • contracts.ts: Contract helper functions
  • context.ts: Execution context utilities

Execution context

The watch-tower uses a context pattern to pass dependencies:
interface ExecutionContext {
  registry: Registry;          // State management
  notificationsEnabled: boolean;
  slack?: Slack;               // Optional notifications
  storage: DBService;          // Database access
}

Concurrency model

  • Single-threaded: Node.js event loop
  • Async/await: For I/O operations
  • Block-level sequencing: Processes blocks in order
  • Event batching: Groups events within blocks
  • Atomic writes: Database operations batched per block

Error handling

  • Graceful degradation: Continues on non-critical errors
  • Block retry: Re-processes blocks on error
  • Exit on critical failure: Database write failures
  • Watchdog timer: Monitors block processing health

Build docs developers (and LLMs) love