app.ts
Overview
`app.ts` serves as the main entry point for the Avalanche blockchain API server within the ShapeShift "unchained" project ecosystem. This file sets up an Express-based HTTP server combined with WebSocket support to provide real-time blockchain data indexing and querying capabilities. It integrates core components such as block and transaction handlers, metrics collection with Prometheus, API documentation via Swagger UI, and WebSocket client connections to an external blockchain data provider (Blockbook).
The file orchestrates how new blocks and transactions are processed, parsed, and exposed through REST and WebSocket interfaces, enabling clients to interact with the Avalanche blockchain data efficiently and in near real-time.
Key Functionalities
HTTP API Server Setup: Express server configured with middleware, health check, metrics endpoint, and Swagger UI docs.
WebSocket Server Setup: A WebSocket server layered on the HTTP server for real-time client connections.
Block and Transaction Processing: Uses handlers to parse new blocks and transactions from the Blockbook WebSocket stream and transform them into enriched data models.
Metrics Collection: Integrates Prometheus for monitoring API and blockchain indexing health.
Route Registration: Imports and registers REST API routes.
External Dependency Integration: Connects to a Blockbook WebSocket endpoint with API key authentication for blockchain data feeds.
Logging: Uses a scoped logger instance.
Classes, Functions, and Methods
1. Express app instance (app)
Purpose: HTTP server instance handling REST API requests.
Setup:
Common middleware including Prometheus metrics.
Routes for health check, metrics, API docs, and static assets.
Error handling middleware.
Usage Example:
app.get('/health', async (_, res) => res.json({ status: 'up', asset: 'avalanche', connections: wsServer.clients.size }))
2. blockHandler
const blockHandler: BlockHandler<NewBlock, Array<{ addresses: Array<string>; tx: evm.Tx }>> = async (block) => { ... }
Type: Asynchronous function conforming to
BlockHandlerinterface.Parameters:
block(NewBlock): Represents a new blockchain block notification.
Returns: Promise resolving to an object containing a
txsarray. Each element includes:addresses: Array of involved addresses (unique).tx: Parsed transaction data of typeevm.Tx.
Functionality:
Concurrently fetches block transactions and internal transactions using
service.For each transaction:
Processes the transaction via
service.handleTransaction.Attaches internal transactions.
Extracts all involved addresses (from both external and internal transactions).
Returns structured transaction array.
Usage Context: Used by the
Registryto handle new blocks streamed from the blockchain.
3. transactionHandler
const transactionHandler: TransactionHandler<BlockbookTx, evm.Tx> = async (blockbookTx) => { ... }
Type: Asynchronous function conforming to
TransactionHandlerinterface.Parameters:
blockbookTx(BlockbookTx): A single transaction object from Blockbook.
Returns: Promise resolving to an object containing:
addresses: Array of involved addresses (unique).tx: Parsed transaction object of typeevm.Tx.
Functionality:
Processes the transaction with internal tracing via
service.handleTransactionWithInternalTrace.Extracts all involved addresses from the transaction and its internal traces.
Usage Context: Used by the
Registryto handle individual transactions streamed from the blockchain.
4. registry
const registry = new Registry({ addressFormatter: evm.formatAddress, blockHandler, transactionHandler })
Type: Instance of
Registry.Purpose: Core orchestrator for blockchain data handling, which uses provided handlers to process blocks and transactions, standardizes addresses using
evm.formatAddress.Role: Acts as a mediator between the WebSocket client and application data handlers, managing subscriptions and event processing.
5. blockbook
const blockbook = new WebsocketClient(INDEXER_WS_URL, {
apiKey: INDEXER_API_KEY,
blockHandler: [registry.onBlock.bind(registry), gasOracle.onBlock.bind(gasOracle)],
transactionHandler: registry.onTransaction.bind(registry),
})
Type: Instance of
WebsocketClientfrom@shapeshiftoss/blockbook.Parameters:
INDEXER_WS_URL: WebSocket URL for the blockchain indexer.apiKey: API key for authentication.blockHandler: Array of handlers for new blocks, including registry and gasOracle.transactionHandler: Handler for new transactions from the registry.
Purpose: Connects to the blockchain indexer service to receive real-time block and transaction data.
Note: Integrates multiple handlers to process incoming data streams.
6. WebSocket Server (wsServer)
const wsServer = new Server({ server })
wsServer.on('connection', (connection) => {
ConnectionHandler.start(connection, registry, blockbook, prometheus, logger)
})
Type: Instance of
ws.Server.Purpose: Listens for client WebSocket connections on the same HTTP server.
On Connection: Invokes
ConnectionHandler.startto initialize client sessions, passing references to:registryfor blockchain data.blockbookWebSocket client.prometheusfor metrics.loggerfor logging.
Usage: Enables real-time communication and subscription-based data delivery to connected clients.
7. Prometheus Metrics (prometheus)
Type: Instance of
Prometheusfrom@shapeshiftoss/common-api.Purpose: Collects and exposes metrics related to the Avalanche coinstack blockchain API.
Endpoints:
/metrics: Serves Prometheus metrics in the required content type.
Integration: Middleware tracks API usage and performance.
8. Logger (logger)
Type: Instance of
Loggerfrom@shapeshiftoss/logger.Configuration:
Namespace:
['unchained', 'coinstacks', 'avalanche', 'api']Log level from environment variable
LOG_LEVEL.
Purpose: Provides structured logging for debugging and monitoring.
Important Implementation Details and Algorithms
Concurrency in Block Processing: The
blockHandlerfetches block transactions and internal transactions concurrently usingPromise.all, improving performance for block data enrichment.Address Extraction: Both block and transaction handlers aggregate addresses from external and internal transaction data, ensuring comprehensive coverage of all involved parties in a transaction.
Registry Usage: The
Registryclass abstracts blockchain event processing, ensuring that blocks and transactions are processed uniformly and that address formatting is consistent viaevm.formatAddress.Middleware Usage: Common middleware is applied globally for tasks such as request logging, error handling, and metrics collection.
Swagger UI Integration: API documentation is hosted and served with customized styling and routing for easy developer reference.
WebSocket Multiplexing: The Blockbook WebSocket client supports multiple handlers for blocks, allowing simultaneous processing by different subsystems (e.g.,
registryandgasOracle).
Interaction with Other Parts of the System
@shapeshiftoss/common-api: Provides shared utilities such as middleware, Prometheus metrics, and blockchain abstractions (evm,Registry).@shapeshiftoss/blockbook: Supplies WebSocket client and types for blockchain data streaming../controller: Contains business logic for transaction handling and gas oracle integration../routes: Registers REST API routes for the server.External Blockchain Indexer: The app connects to an external Blockbook WebSocket endpoint configured by environment variables (
INDEXER_WS_URL,INDEXER_API_KEY) to receive live blockchain data.Clients: REST clients consume the HTTP API, and real-time clients connect via WebSocket for subscription-based blockchain data.
Prometheus Monitoring: Integrates with the system-wide monitoring infrastructure to expose runtime metrics.
Usage Example
Starting the server (assuming environment variables set):
INDEXER_WS_URL=wss://blockbook.indexer.example.com/ws \
INDEXER_API_KEY=apikey123 \
PORT=3000 \
LOG_LEVEL=info \
node dist/app.js
Clients can:
Query health:
GET /healthFetch metrics:
GET /metricsAccess API docs:
GET /docsConnect via WebSocket for live block and transaction updates.
Mermaid Diagram: Structure of app.ts
classDiagram
class App {
+express app
+start()
}
class Registry {
+onBlock()
+onTransaction()
}
class WebsocketClient {
+constructor(url, options)
+connect()
}
class BlockHandler {
<<function>>
}
class TransactionHandler {
<<function>>
}
class Prometheus {
+register
+metrics()
}
class Logger {
+info()
+error()
}
class ConnectionHandler {
+start(connection, registry, blockbook, prometheus, logger)
}
App --> "1" Registry : uses
App --> "1" WebsocketClient : connects to
App --> "1" Prometheus : monitors
App --> "1" Logger : logs
App --> "1" ConnectionHandler : manages WS connections
Registry --> "1" BlockHandler : processes blocks
Registry --> "1" TransactionHandler : processes transactions
WebsocketClient --> "many" BlockHandler : invokes on block
WebsocketClient --> "1" TransactionHandler : invokes on transaction
Summary
`app.ts` is a comprehensive server bootstrap file that integrates Express and WebSocket servers with blockchain data handlers, metrics, logging, and API documentation. It acts as the core runtime environment for the Avalanche coinstack API in the ShapeShift "unchained" ecosystem, enabling clients to query and subscribe to blockchain data efficiently and reliably. The file demonstrates a clean separation of concerns, modular handler usage, and real-time data streaming integration.