app.ts
Overview
`app.ts` is the main entry point for the ShapeShift Arbitrum One API server. It sets up an Express-based HTTP server combined with WebSocket support to provide real-time blockchain data streaming and REST API endpoints. This file integrates several key components:
HTTP API: Serves REST endpoints for health checks, metrics, and Swagger API documentation.
WebSocket Client & Server: Connects to a blockchain indexer WebSocket (Blockbook) to receive live updates of blocks and transactions, and broadcasts relevant updates to connected clients.
Handlers and Registry: Uses
BlockHandlerandTransactionHandlerimplementations wrapped by aRegistryto process blockchain data.Middleware and Monitoring: Integrates common middleware for request handling and Prometheus for metrics collection.
Logging: Uses a namespaced logger for consistent, configurable logging within the Arbitrum stack.
This file essentially acts as the glue between the blockchain data sources, business logic processing, API exposure, and real-time client communication.
Detailed Explanation
Imports and Configuration
Imports express, ws (WebSocket Server), swagger-ui-express for API docs.
Imports blockchain-specific utilities and handlers from
@shapeshiftosspackages.Loads environment variables for port, WebSocket URL, and API key.
Throws an error if the INDEXER_WS_URL is not set.
Detects the type of indexer provider based on the URL (
liquifyornownodes).
Logger Initialization
export const logger = new Logger({
namespace: ['unchained', 'coinstacks', 'arbitrum', 'api'],
level: process.env.LOG_LEVEL,
})
Creates a namespaced logger scoped to the Arbitrum API stack.
Log level is configurable via environment variable.
Prometheus Metrics
const prometheus = new Prometheus({ coinstack: 'arbitrum' })
Initializes Prometheus monitoring for the Arbitrum coinstack.
Used for recording metrics exposed on
/metricsendpoint.
Express Application Setup
const app = express()
app.use(...middleware.common(prometheus))
Creates an Express app.
Adds common middleware for request parsing, logging, and metrics instrumentation.
HTTP Routes
GET /health
Returns JSON with API status, asset name, and current WebSocket client connections count.
app.get('/health', async (_, res) => res.json({ status: 'up', asset: 'arbitrum', connections: wsServer.clients.size }))GET /metrics
Returns Prometheus metrics collected by the application.
Swagger UI and Static Files
Serves Swagger UI at
/docswith custom styling and static assets like favicon and swagger JSON.Fallback Route
Redirects root
/and unmatched routes to/docs.Error Handling Middleware
Attaches middleware for error handling and 404 not found responses.
Blockchain Handlers
BlockHandler
const blockHandler: BlockHandler<NewBlock, Array<{ addresses: Array<string>; tx: evm.Tx }>> = async (block) => {
// Fetch blockbook transactions and internal transactions in parallel
// Process each transaction with internal traces and aggregate addresses involved
// Return array of { addresses, tx } pairs for registry consumption
}
Processes a new block event.
Fetches block transactions and internal transactions.
Augments transactions with internal traces.
Extracts unique blockchain addresses from both external and internal transfers.
Returns enriched transaction data for further processing.
TransactionHandler
const transactionHandler: TransactionHandler<BlockbookTx, evm.Tx> = async (blockbookTx) => {
// Process a single transaction with internal tracing
// Extract unique addresses involved
// Return { addresses, tx }
}
Processes individual transaction events.
Similar address extraction and internal trace handling as block handler.
Registry Setup
const registry = new Registry({ addressFormatter: evm.formatAddress, blockHandler, transactionHandler })
Manages registered handlers for blocks and transactions.
Uses
evm.formatAddressto standardize addresses.Receives processed blockchain data and coordinates subsequent processing and event dispatch.
WebSocket Client to Blockchain Indexer
const blockbook = new WebsocketClient(wsUrl, {
blockHandler: [registry.onBlock.bind(registry), gasOracle.onBlock.bind(gasOracle)],
transactionHandler: registry.onTransaction.bind(registry),
apiKey,
})
Connects to a blockchain indexer via WebSocket.
Routes incoming block and transaction events to the registry and gas oracle handlers.
Supports API key authentication for certain providers.
HTTP and WebSocket Server Initialization
const server = app.listen(PORT, () => logger.info('Server started'))
const wsServer = new Server({ server })
wsServer.on('connection', (connection) => {
ConnectionHandler.start(connection, registry, blockbook, prometheus, logger)
})
Starts the HTTP server on configured port.
Creates WebSocket server on top of the HTTP server.
On new WS connection, starts client lifecycle management via
ConnectionHandler.
Usage Examples
Access Health Endpoint
curl http://localhost:3000/health
Returns:
{
"status": "up",
"asset": "arbitrum",
"connections": 0
}
Connect WebSocket Client
Clients connect to the same server via WS, and receive real-time blockchain updates processed by `ConnectionHandler`.
Important Implementation Details
Parallel Data Fetching: The block handler fetches blockbook transactions and internal transactions concurrently to optimize latency.
Address Deduplication: Uses
Setto ensure unique addresses involved in transactions are passed downstream.Dynamic WebSocket URL: Supports different indexer providers (Liquify, Nownodes) with conditional URL and API key handling.
Registry Pattern: Centralizes blockchain event handling, decoupling data reception from business logic processing.
Middleware Integration: Standardized middleware chain ensures observability and error resilience.
Swagger API Docs: API documentation is hosted alongside the REST API for developer usability.
Interaction with Other Parts of the System
@shapeshiftoss/common-api: Provides middleware, handler interfaces, and utilities.
@shapeshiftoss/blockbook: Supplies WebSocket client and types for blockchain event streaming.
Controller Layer (
./controller): Business logic for gas oracle and service handling (e.g., transaction processing).Routes (
./routes): Registers REST API routes for the Express app.Logger: Provides contextual logging across the app.
Prometheus: Collects and exposes metrics for monitoring and alerting.
WebSocket Clients: External clients connect to receive live blockchain updates.
Visual Diagram
classDiagram
class App {
+expressApp: Express
+wsServer: WebSocket.Server
+logger: Logger
+prometheus: Prometheus
+registry: Registry
+blockbook: WebsocketClient
+start()
}
class Registry {
+addressFormatter()
+blockHandler()
+transactionHandler()
+onBlock()
+onTransaction()
}
class WebsocketClient {
+connect()
+blockHandler[]
+transactionHandler()
}
class ConnectionHandler {
+start(connection, registry, blockbook, prometheus, logger)
}
class BlockHandler {
+handleBlock()
}
class TransactionHandler {
+handleTransaction()
}
App --> expressApp : uses
App --> wsServer : creates
App --> logger : logs
App --> prometheus : collects metrics
App --> registry : holds
App --> blockbook : connects to indexer
wsServer --> ConnectionHandler : on connection
blockbook --> Registry : sends blocks/txs
Registry --> BlockHandler : delegates block processing
Registry --> TransactionHandler : delegates tx processing
Summary
`app.ts` is the core orchestration file for the ShapeShift Arbitrum One API, combining Express HTTP server with WebSocket blockchain streaming. It leverages modular handlers, middleware, and monitoring to deliver a robust API and real-time data service, serving as a vital backbone for interacting with Arbitrum blockchain data in both RESTful and streaming contexts.