service.ts
Overview
The [service.ts](/projects/291/69094) file implements the **UTXO blockchain service layer** for the Unified API Layer architecture. It provides a concrete `Service` class that interacts with UTXO-based blockchain data sources, primarily leveraging the **Blockbook indexer** and **RPC node endpoints**, to facilitate blockchain data retrieval and transaction management.
This service abstracts the complexities of UTXO blockchain querying, including handling extended public keys (xpubs), addresses, transaction history pagination, raw transaction fetching, UTXO retrieval, fee estimation, and transaction broadcasting. It converts raw blockchain data into consistent, API-friendly models and manages error handling uniformly.
**Key responsibilities:**
Fetch account details by address or xpub.
Retrieve paginated transaction history with cursor-based pagination.
Get detailed transaction data and raw hex transactions.
Retrieve unspent transaction outputs (UTXOs).
Broadcast raw transactions via JSON-RPC.
Estimate network fees for various confirmation speeds.
Handle block data retrieval and transaction processing.
Class: Service
The central class exported by this file, `Service`, implements the blockchain-specific API logic for UTXO chains and conforms to the `API` interface (an extension of [BaseAPI](/projects/291/69264) excluding `getInfo`).
Constructor
constructor(args: ServiceArgs)
Initializes a new instance of the service.
Parameters:
args.blockbook: Blockbook
Instance of Blockbook indexer client used to query blockchain data.args.rpcUrl: string
JSON-RPC endpoint URL for broadcasting transactions.args.rpcApiKey?: string
Optional API key for authenticated RPC calls.args.isXpub: (pubkey: string) => boolean
Function to determine if a given string is an extended public key (xpub).args.addressFormatter?: AddressFormatter
Optional function to normalize addresses (default converts to lowercase).
Properties set:
this.blockbook— Blockbook client instance.this.rpcUrl— RPC endpoint URL.this.rpcApiKey— RPC API key if provided.this.isXpub— xpub detection function.this.formatAddress— address normalization function.
Methods
async getAccount(pubkey: string): Promise<Account>
Fetches account details for a given public key (address or xpub).
Parameters:
pubkey— A blockchain address or extended public key (xpub).
Returns:
A Promise resolving to anAccountobject containing:pubkey: The canonical public key or address.balance: Confirmed balance as a string.unconfirmedBalance: Unconfirmed balance as a string.addresses: Array of related addresses with balances.nextReceiveAddressIndex: Index to the next unused receiving address.nextChangeAddressIndex: Index to the next unused change address.
Implementation details:
If
pubkeyis an xpub, callsblockbook.getXpub()to retrieve token balances and derived addresses.Otherwise, formats the address and calls
blockbook.getAddress()for basic info.Processes tokens and addresses returned by Blockbook to build the
addressesarray.Calculates next unused address indexes for receiving and change addresses by inspecting token derivation paths.
Wraps all errors with a unified error handler.
Usage example:
const account = await service.getAccount('xpub6CUGRU...'); console.log(account.balance);
async getTxHistory(pubkey: string, cursor?: string, pageSize = 10): Promise<TxHistory>
Retrieves paginated transaction history for an address or xpub.
Parameters:
pubkey— Address or xpub to fetch transactions for.cursor— Optional base64-encoded pagination cursor.pageSize— Number of transactions per page (default 10).
Returns:
Promise resolving to aTxHistoryobject containing:pubkey: The requested public key.cursor: Next page cursor string orundefinedif no more pages.txs: Array of processed transactions (Tx[]).
Implementation details:
Validates
pageSizefalls within allowed range.Decodes the cursor from base64 JSON to extract the current page number; defaults to page 1.
Calls either
blockbook.getXpub()orblockbook.getAddress()with pagination parameters.Increments page number for next cursor if more pages exist, encodes it back into base64 JSON.
Maps Blockbook transactions to internal
Txmodel usinghandleTransaction.Error handling wraps any Blockbook or decoding errors.
Usage example:
const history = await service.getTxHistory('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', undefined, 20); console.log(history.txs.length);
async getTransaction(txid: string): Promise<Tx>
Fetches detailed transaction information for a given transaction ID.
Parameters:
txid— Transaction hash.
Returns:
Promise resolving to a processedTxobject.Implementation details:
Calls
blockbook.getTransaction(txid)to fetch raw data.Processes the raw Blockbook transaction using
handleTransaction.Wraps errors with
handleError.
Usage example:
const tx = await service.getTransaction('b6f6991d...'); console.log(tx.confirmations);
async getRawTransaction(txid: string): Promise<RawTx>
Fetches the raw transaction hex and related data for a transaction.
Parameters:
txid— Transaction hash.
Returns:
Promise resolving to aRawTxobject representing raw blockchain transaction data.Implementation details:
Uses
blockbook.getTransactionSpecific(txid)which returns raw transaction details.Casts the result as
RawTx.Wraps errors with
handleError.
Usage example:
const rawTx = await service.getRawTransaction('b6f6991d...'); console.log(rawTx.hex);
async getUtxos(pubkey: string): Promise<Utxo[]>
Retrieves all unspent transaction outputs (UTXOs) for the given address or xpub.
Parameters:
pubkey— Address or xpub.
Returns:
Promise resolving to an array ofUtxoobjects.Implementation details:
Calls
blockbook.getUtxo(pubkey).Ensures each UTXO object contains a valid
addressproperty; if missing, assigns the inputpubkey.Wraps errors with
handleError.
Usage example:
const utxos = await service.getUtxos('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'); console.log(utxos.length);
async sendTx(body: SendTxBody): Promise<string>
Broadcasts a raw transaction to the blockchain network.
Parameters:
body— Object containing:hex: Raw transaction hex string to broadcast.
Returns:
Promise resolving to the transaction ID string if successful.Implementation details:
Constructs a JSON-RPC request with method
sendrawtransactionand transaction hex as parameter.Sends the request via an Axios instance (
axiosNoRetry) to the configuredrpcUrl.If
rpcApiKeyis provided, includes it asapi-keyheader.Checks response for errors; throws if no result.
Returns the transaction ID on success.
Wraps errors with
handleError.
Usage example:
const txid = await service.sendTx({ hex: '0200000001...' }); console.log(`Broadcasted tx: ${txid}`);
async getNetworkFees(): Promise<NetworkFees>
Fetches current recommended network fees for different confirmation speeds.
Returns:
Promise resolving to aNetworkFeesobject with keysfast,average, andslow. Each value is aNetworkFeeobject containing:blocksUntilConfirmation: Expected blocks to confirm.satsPerKiloByte: Fee rate in satoshis per kilobyte.
Implementation details:
Defines typical block confirmation targets: fast (2), average (5), slow (10).
Calls
blockbook.estimateFees()with these targets.Converts returned fee rates (BTC per byte) to satoshis per kilobyte.
Returns an object mapping speeds to fee info.
Uses
bignumber.jsfor precise arithmetic.Wraps errors with
handleError.
Usage example:
const fees = await service.getNetworkFees(); console.log(fees.fast.satsPerKiloByte);
async handleBlock(hash: string): Promise<BlockbookTx[]>
Fetches all transactions for a block by hash.
Parameters:
hash— Block hash.
Returns:
Promise resolving to an array ofBlockbookTxtransactions in the block.Implementation details:
Calls
blockbook.getBlock(hash)to get the first page of transactions and total pages.If multiple pages exist, loops through remaining pages and accumulates transactions.
Returns the complete transaction list.
Wraps errors with
handleError.
Usage example:
const blockTxs = await service.handleBlock('0000000000000000000...'); console.log(blockTxs.length);
handleTransaction(tx: BlockbookTx): Tx
Converts a raw Blockbook transaction into the internal `Tx` model.
Parameters:
tx— ABlockbookTxobject from Blockbook.
Returns:
ATxobject with normalized and enriched fields.Implementation details:
Maps Blockbook fields such as
txid,blockHash,blockHeight,timestamp,confirmations,value,fee,hex.Converts inputs (
vin) mapping each input's txid, vout, sequence, coinbase flag, scriptSig (if hex present), addresses, and value.Converts outputs (
vout) mapping value, output index, scriptPubKey, and address info.Special logic to detect OP_RETURN outputs and attach
opReturndata if applicable.Ensures consistent format for use by API consumers.
Usage example:
const txModel = service.handleTransaction(blockbookTx); console.log(txModel.txid);
Important Implementation Details and Algorithms
Address vs XPUB Handling:
Many methods detect if the inputpubkeyis an extended public key (xpub) using the providedisXpubfunction. This detection determines whether to call Blockbook'sgetXpub()orgetAddress()methods, which have different API signatures and data formats.Cursor-Based Pagination:
ThegetTxHistorymethod implements pagination using a cursor encoded as a base64 JSON string containing the current page number. This approach enables stateless pagination by the client.Fee Rate Conversion:
Blockbook returns fee estimates in BTC per byte as decimals, which are converted to satoshis per kilobyte by multiplying by 100,000,000 (satoshis per BTC) and 1000 (bytes per kilobyte). This normalization ensures consistent fee units.Error Handling:
All asynchronous methods wrap external calls withhandleErrorutility, which normalizes errors into a consistent API error format.Axios Instance without Retry:
The service uses a dedicated Axios instance (axiosNoRetry) for JSON-RPC calls with a 5-second timeout and no retry logic, ensuring fast failure and clear error reporting on transaction broadcasting.Address Formatting:
Addresses are normalized using a customizable formatter function (addressFormatter), defaulting to lowercase conversion. This ensures address consistency for lookups.
Interactions with Other System Components
Blockbook Indexer:
The service extensively uses theBlockbookclient to query blockchain data such as addresses, xpubs, transactions, blocks, UTXOs, and fee estimates. Blockbook provides a REST interface to an indexed blockchain node.Blockchain Node RPC Endpoint:
Raw transactions are broadcast via a JSON-RPC call to the configured node RPC URL, optionally authenticated with an API key.Unified API Controllers:
TheServiceclass is consumed by the UTXO API controller layer, which exposes HTTP REST endpoints. Controllers invoke service methods to fulfill client requests.Common Utilities:
The service uses shared utilities likehandleError,rpcId, andvalidatePageSizefrom the broader API codebase for consistent error handling, request ID generation, and input validation.
Visual Diagram
classDiagram
class Service {
-blockbook: Blockbook
-rpcUrl: string
-rpcApiKey?: string
-formatAddress: AddressFormatter
+isXpub(pubkey: string): boolean
+constructor(args: ServiceArgs)
+getAccount(pubkey: string): Promise<Account>
+getTxHistory(pubkey: string, cursor?: string, pageSize: number): Promise<TxHistory>
+getTransaction(txid: string): Promise<Tx>
+getRawTransaction(txid: string): Promise<RawTx>
+getUtxos(pubkey: string): Promise<Utxo[]>
+sendTx(body: SendTxBody): Promise<string>
+getNetworkFees(): Promise<NetworkFees>
+handleBlock(hash: string): Promise<BlockbookTx[]>
+handleTransaction(tx: BlockbookTx): Tx
}
Summary
The [service.ts](/projects/291/69094) file provides the backbone of the UTXO blockchain support in the Unified API Layer. It encapsulates all business logic and external data source integration needed to serve blockchain data and transactions in a normalized, paginated, and consistent manner.
By abstracting Blockbook and node RPC interactions behind clean methods, it allows API controllers and consumers to operate without concern for the underlying blockchain data complexities. The service handles important UTXO-specific concerns such as address vs. xpub logic, cursor pagination, UTXO retrieval, and fee conversions, ensuring robust and reliable blockchain API functionality.
This design supports extensibility, testability, and maintainability within the modular Unified API Layer framework.