app.ts
Overview
[app.ts](/projects/291/68852) is the main entry point for the Ethereum API server implemented using Express.js. It establishes a RESTful API and WebSocket server that provide access to Ethereum blockchain data and services. This server acts as a bridge between client applications (such as wallets and dApps) and blockchain indexers (like Blockbook or other indexers), exposing blockchain data through HTTP endpoints and real-time event subscriptions over WebSocket.
Key functionalities include:
Serving REST API endpoints with Ethereum-specific blockchain data.
Providing interactive API documentation using Swagger UI.
Connecting to an Ethereum blockchain indexer via WebSocket to receive real-time block and transaction events.
Processing and enriching incoming blocks and transactions with internal Ethereum transaction traces.
Managing WebSocket client connections and subscriptions through a central Registry.
Exposing Prometheus metrics for observability.
Implementing robust middleware for error handling, logging, and input validation.
This file integrates various components such as controllers, middleware, WebSocket clients, and service logic to deliver a cohesive API server tailored for the Ethereum blockchain.
Detailed Explanation
Constants and Environment Variables
Constant | Purpose |
|---|---|
`PORT` | The port number on which the Express server listens (defaults to 3000 if not set). |
`INDEXER_WS_URL` | WebSocket URL for connecting to the blockchain indexer (required). |
Optional API key for authenticating with the indexer’s WebSocket service. | |
`IS_LIQUIFY` | Boolean flag indicating if the indexer URL contains "liquify" (affects internal tx fetching). |
`IS_NOWNODES` | Boolean flag indicating if the indexer URL contains "nownodes" (affects API key usage). |
Logger
logger: An instance of the Logger class scoped tounchained,coinstacks,ethereum, andapinamespaces.Configured with a log level from the environment variable
LOG_LEVEL.
Express Application Setup
Instantiates an Express
app.Registers common middleware (logging, metrics, etc.) using
middleware.common(prometheus).Defines the following HTTP routes:
Route
Method
Description
`/health`
GET
Returns a JSON payload indicating server status and active WebSocket connections.
`/metrics`
GET
Exposes Prometheus metrics with proper content type.
`/public`
Static
Serves static files from a common API public directory.
Static
Serves the OpenAPI spec JSON file for Swagger UI.
`/docs`
GET
Serves interactive Swagger UI API documentation.
`/`
GET
Redirects to `/docs` for API documentation.
Registers routes defined in ./routes via
RegisterRoutes(app).Adds error handling middleware using middleware.errorHandler and
middleware.notFoundHandler.
Block and Transaction Handlers
These asynchronous handlers process blockchain events received from the indexer WebSocket client.
blockHandler
Type:
BlockHandler<NewBlock, Array<{ addresses: string[]; tx: evm.Tx }>>Purpose: Processes new Ethereum blocks.
Parameters:
block(NewBlock): The new block event received from the indexer.
Returns: An object containing a list of transactions
txswith associated addresses.Process:
Fetches block transactions (
blockbookTxs) usingservice.handleBlock(block.hash).Fetches internal Ethereum transactions (
internalTxs) differently depending on the indexer type (LIQUIFYor not).For each Blockbook transaction:
Processes the transaction via
service.handleTransaction.Attaches internal transactions if any.
Extracts all related addresses from both external and internal transactions, ensuring uniqueness.
Returns an array of { addresses, tx } objects representing enriched transactions.
transactionHandler
Type:
TransactionHandler<BlockbookTx, evm.Tx>Purpose: Processes individual Ethereum transactions.
Parameters:
blockbookTx (
BlockbookTx): The transaction event from the indexer.
Returns: An object with the transaction and its relevant addresses.
Process:
Processes the transaction with internal tracing, adjusting the trace method based on the indexer.
Extracts related addresses from the transaction and its internal transactions.
Returns the enriched transaction and associated addresses.
Registry
Instance:
registryof classRegistry.Role: Maintains subscription state, tracks WebSocket clients’ interested addresses, and routes blockchain events accordingly.
Configured with:
addressFormatter: Ethereum address formatting function (evm.formatAddress).blockHandlerandtransactionHandlerdefined above.
WebSocket Client and Server
WebSocket Client (blockbook)
Connects to the blockchain indexer WebSocket URL (
wsUrl), optionally with API key.Registers handlers for:
New blocks: calls both registry.onBlock and
gasOracle.onBlock.Transactions: calls
registry.onTransaction.
Responsible for receiving real-time blockchain events and triggering processing workflows.
WebSocket Server (wsServer)
Wraps the HTTP server to enable WebSocket connections for clients.
On new client connections:
Uses ConnectionHandler.start to initialize connection handling, passing the connection, registry, blockbook client, Prometheus metrics, and logger.
Manages client subscriptions and event dispatch.
Server Startup
Starts Express server listening on
PORT.Logs server start message.
Instantiates WebSocket server bound to the Express HTTP server.
Usage Example
import { app, wsServer, logger } from './app'
// Server is already listening on configured PORT.
// Clients can:
// - Access HTTP API (e.g., GET /health, /metrics, /docs).
// - Connect to WebSocket for real-time blockchain events.
// Example: REST API call to check health
fetch('http://localhost:3000/health')
.then(res => res.json())
.then(console.log)
// Example: WebSocket client subscribing to new blocks and transactions
const ws = new WebSocket('ws://localhost:3000')
ws.onmessage = (event) => {
console.log('Received event:', event.data)
}
Important Implementation Details
Internal Transaction Tracing: Ethereum transactions are enriched with internal transactions fetched either via
trace_transactionordebug_traceTransactiondepending on the indexer type (Liquify or others). This provides richer transaction data including contract calls and token transfers hidden inside transactions.Address Extraction: Both external and internal transaction addresses are combined using a Set to avoid duplicates. This enables efficient subscription tracking and event routing to clients interested in specific addresses.
Dynamic Indexer URL and API Key Handling: The WebSocket client URL and API key parameters adapt based on the environment variables and indexer type, supporting multiple indexer providers transparently.
Prometheus Metrics: Metrics are exposed on
/metricsand integrated into middleware for monitoring request rates, errors, and WebSocket client stats.Swagger UI: Interactive API documentation is served under
/docs, with a custom UI configuration that hides the top bar and applies branding.Middleware: Common middleware provides error handling, 404 not found responses, and logging.
Robustness: The app throws an error if the critical
INDEXER_WS_URLenvironment variable is missing, preventing misconfigured starts.
Interaction with Other System Components
Controller Layer: Interacts with
serviceandgasOraclecontrollers for Ethereum-specific business logic, such as fetching internal transactions and gas price estimation.Common API Libraries: Utilizes shared modules from
@shapeshiftoss/common-apifor middleware, connection handling, logging, and subscription registry management.Blockchain Indexer: Connects to external indexer services (Blockbook or Liquify) to receive blockchain events via WebSocket.
Clients: Exposes REST and WebSocket endpoints consumed by wallets, dApps, and monitoring tools.
Prometheus: Integrates with Prometheus metrics for observability, enabling dashboards and alerting.
Diagram
classDiagram
class App {
+express app
+WebSocket server wsServer
+startServer()
}
class BlockHandler {
+handleBlock(block: NewBlock): Promise<{txs: Array<{addresses: string[], tx: evm.Tx}>}>
}
class TransactionHandler {
+handleTransaction(tx: BlockbookTx): Promise<{addresses: string[], tx: evm.Tx}>
}
class Registry {
+onBlock()
+onTransaction()
+subscribe()
+unsubscribe()
}
class WebsocketClient {
+connect()
+blockHandler: Array<Function>
+transactionHandler: Function
}
class ConnectionHandler {
+start(connection, registry, blockbook, prometheus, logger)
}
App --> Registry : uses
App --> WebsocketClient : creates
App --> ConnectionHandler : uses for WebSocket connections
Registry --> BlockHandler : processes blocks
Registry --> TransactionHandler : processes transactions
WebsocketClient --> Registry : calls onBlock/onTransaction
ConnectionHandler --> Registry : manages subscriptions
Summary
The [app.ts](/projects/291/68852) file is a well-structured API server tailored for Ethereum blockchain data, bridging blockchain indexers and client applications. It combines REST endpoints, WebSocket real-time subscriptions, enriched transaction processing, monitoring, and documentation in one cohesive server implementation. Its modular design leverages common libraries and controllers, ensuring extensibility and maintainability within the broader ShapeShift API ecosystem.