controller.ts
Overview
The [controller.ts](/projects/291/68853) file serves as the primary configuration and initialization module for the Bitcoin UTXO (Unspent Transaction Output) service within a larger modular blockchain application. It establishes network connectivity to blockchain indexer and RPC services, configures API keys, sets up logging, and provides utility functions for address formatting and public key validation. The file exports a ready-to-use service instance and integrates it with the UTXO controller layer, enabling seamless blockchain data retrieval and transaction handling for Bitcoin and Bitcoin-like networks.
Detailed Explanation
Environment Configuration and Validation
Environment Variables:
INDEXER_URL: Base URL for the blockchain indexer HTTP API.INDEXER_WS_URL: Base URL for the blockchain indexer WebSocket API.INDEXER_API_KEY: Optional API key for authenticating with the indexer.RPC_URL: URL for the blockchain RPC node.RPC_API_KEY: Optional API key for authenticating with the RPC node.LOG_LEVEL: Logging verbosity level.
Validation:
The file ensures that critical environment variablesINDEXER_URL,INDEXER_WS_URL, andRPC_URLare set at runtime. If missing, an error is thrown immediately to prevent misconfiguration.Network Type Flags:
Two boolean flags,IS_LIQUIFYandIS_NOWNODES, are computed by checking ifRPC_URLandINDEXER_URLcontain specific substrings ("liquify" or "nownodes") to adjust API endpoint construction and authentication.
Logger Initialization
export const logger = new Logger({
namespace: ['unchained', 'coinstacks', 'bitcoin', 'api'],
level: process.env.LOG_LEVEL,
})
A scoped logger instance is created using
@shapeshiftoss/loggerwith a namespace hierarchy that identifies logs as related to the Bitcoin stack within the "unchained" platform.The logging level is set dynamically via the environment variable
LOG_LEVEL.
API Endpoint Construction
Constructs URLs for HTTP, WebSocket, and RPC endpoints, appending API keys conditionally based on the network flags:
const httpURL = INDEXER_API_KEY && IS_LIQUIFY ? `${INDEXER_URL}/api=${INDEXER_API_KEY}` : INDEXER_URL
const wsURL = INDEXER_API_KEY && IS_LIQUIFY ? `${INDEXER_WS_URL}/api=${INDEXER_API_KEY}` : INDEXER_WS_URL
const rpcUrl = RPC_API_KEY && IS_LIQUIFY ? `${RPC_URL}/api=${RPC_API_KEY}` : RPC_URL
const apiKey = INDEXER_API_KEY && IS_NOWNODES ? INDEXER_API_KEY : undefined
const rpcApiKey = RPC_API_KEY && IS_NOWNODES ? RPC_API_KEY : undefined
This conditional logic supports different authentication schemes required by distinct indexer providers.
Blockbook Client Initialization
const blockbook = new Blockbook({ httpURL, wsURL, logger, apiKey })
Instantiates a
Blockbookclient that connects to the blockchain indexer APIs over HTTP and WebSocket.The
Blockbookclient handles querying blockchain data such as transactions, blocks, and addresses in a performant and event-driven manner.
Utility Functions
isXpub(pubkey: string): boolean
Purpose: Detects if a given public key string represents an extended public key (xpub/ypub/zpub).
Parameters:
pubkey— A string containing the public key.
Returns:
trueif the string starts with"xpub","ypub", or"zpub", otherwisefalse.Usage Example:
isXpub('xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKp4d6Bv...') // returns true
formatAddress(address: string): string
Purpose: Normalizes Bitcoin addresses by converting native SegWit bech32 addresses to lowercase.
Parameters:
address— A string containing a Bitcoin address.
Returns: The normalized address string.
If the address is bech32 encoded with
bcprefix (native Bitcoin mainnet), returns lowercase.Otherwise, returns the original address string unchanged.
Implementation Detail:
Uses
bech32.decodeUnsafeto decode the address prefix safely.
Usage Example:
formatAddress('bc1qw4j0qw0...') // returns 'bc1qw4j0qw0...' in lowercase formatAddress('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa') // returns as-is
Service Initialization and Export
export const service = new Service({
blockbook,
rpcUrl,
isXpub,
addressFormatter: formatAddress,
rpcApiKey,
})
Creates an instance of
Service(from the UTXO API layer), injecting dependencies:blockbookclient for indexer interactions.rpcUrlfor direct RPC node communication.Utility functions
isXpubandformatAddressfor key/address handling.Optional
rpcApiKeyfor authenticated RPC requests.
Integration with UTXO Controller
UTXO.service = service
Assigns the created
serviceinstance to the staticserviceproperty of theUTXOcontroller.This binding allows all
UTXOinstances to utilize the configured service for blockchain data operations, ensuring a single source of truth and shared configuration.
Important Implementation Details
Dynamic API Endpoint Configuration:
The file adapts to multiple providers (Liquify, NowNodes) by detecting substrings in URLs and appending API keys accordingly. This reduces hardcoding and improves flexibility.Use of Blockbook:
The Blockbook client abstracts complex blockchain data queries and provides both HTTP and WebSocket interfaces, enabling efficient real-time updates and historical data access.Address Normalization:
TheformatAddressfunction ensures compatibility with native SegWit addresses by normalizing to lowercase, which is important because bech32 is case-insensitive but some downstream processes require consistent casing.Static Service Attachment:
Attaching theserviceinstance statically to theUTXOclass ensures that all UTXO-related operations share the same underlying service implementation and configuration.
Interaction with Other Parts of the System
UTXO Module:
Imports theUTXOcontroller and assigns theserviceinstance, enabling it to perform blockchain queries and transaction management.Service Layer:
TheServiceclass imported from a common API package encapsulates the blockchain RPC and indexer logic.Logging System:
Uses a centralized logger that is consistent with the rest of the coinstacks and API services.Blockchain Indexer and RPC Nodes:
Connects to external blockchain data sources using URLs and API keys provided at runtime, abstracting those details away from higher-level components.
Usage Example
import { UTXO } from './controller'
// This UTXO instance automatically uses the configured service
const utxoInstance = new UTXO()
// Check if a string is an extended public key
console.log(utxoInstance.service.isXpub('xpub6...')) // true
// Format a Bitcoin address
const formattedAddress = utxoInstance.service.addressFormatter('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4')
console.log(formattedAddress) // 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
// Use UTXO methods that internally leverage the blockbook and RPC clients
utxoInstance.getTransaction('some-txid').then(tx => console.log(tx))
Mermaid Class Diagram
classDiagram
class controller {
<<module>>
+logger: Logger
+service: Service
+isXpub(pubkey: string): boolean
+formatAddress(address: string): string
}
class Blockbook {
+constructor(config)
+httpURL: string
+wsURL: string
+logger: Logger
+apiKey?: string
+subscribe()
+getTransaction(txid: string)
+getAddress(address: string)
...
}
class Service {
+constructor(config)
+blockbook: Blockbook
+rpcUrl: string
+isXpub(pubkey: string): boolean
+addressFormatter(address: string): string
+rpcApiKey?: string
+getTransaction(txid: string)
+getBalance(address: string)
...
}
class UTXO {
+static service: Service
+getTransaction(txid: string)
+getBalance(address: string)
...
}
controller ..> Blockbook : instantiates
controller ..> Service : instantiates
controller ..> UTXO : assigns service
Service *-- Blockbook : uses
UTXO ..> Service : uses (static property)
Summary
The [controller.ts](/projects/291/68853) file is a critical bootstrap and configuration script for Bitcoin UTXO blockchain data services. By handling environment-based setup, logging, client instantiation, and utility functions, it provides a centralized service instance that the UTXO controller layer uses to interact with blockchain network data through indexers and RPC nodes. The file's design supports multiple provider integrations and enforces consistent data formatting and key validation, facilitating reliable and flexible Bitcoin-related operations within the overall system.