service.ts


Overview

The [service.ts](/projects/291/69103) file implements the **EVM Service** class, a core component of the Unified API Layer specialized for Ethereum and EVM-compatible blockchains. It encapsulates the business logic needed to interact with blockchain data sources, node RPC endpoints, and external APIs, providing a unified interface for:

The service integrates multiple dependencies, including the Blockbook indexer, Ethereum node RPC endpoints, a gas oracle for fee estimation, and auxiliary utilities for error handling, request retries, and data formatting.

This file is essential for providing a consistent and enriched API for EVM chains, abstracting away the complexities of underlying blockchain data models, RPC protocols, and token standards.


Classes and Interfaces

ServiceArgs (Interface)

Arguments required to instantiate the `Service` class.

Property

Type

Description

`blockbook`

`Blockbook`

Blockbook client instance for blockchain data querying.

`gasOracle`

`GasOracle`

Gas oracle instance for gas fee estimation.

`explorerApiUrl`

`URL`

Base URL for the external blockchain explorer API.

`logger`

`Logger`

Logger instance for structured logging.

`client`

`PublicClient`

Ethereum client for RPC interactions.

`rpcUrl`

`string`

JSON-RPC endpoint URL of the blockchain node.

rpcApiKey?

`string`

Optional API key for RPC requests.


Service (Class)

Implements the core API business logic for EVM-compatible blockchains. This class fulfills the `API` interface and most of the [BaseAPI](/projects/291/69264) interface except `getInfo`.

Properties

Name

Type

Description

`blockbook`

`Blockbook`

Blockbook client for blockchain data.

`gasOracle`

`GasOracle`

Gas oracle for gas fee estimation.

`explorerApiUrl`

`URL`

External explorer API base URL.

`logger`

`Logger`

Structured logger scoped with namespace.

`client`

`PublicClient`

Ethereum client for RPC interactions.

`rpcUrl`

`string`

RPC endpoint URL for sending requests.

rpcApiKey?

`string`

Optional API key for RPC authentication.

Constructor

constructor(args: ServiceArgs)

Public Methods

getAccount(pubkey: string): Promise<Account>

Fetches account details for the given public key/address.

const account = await service.getAccount('0xabc123...');
console.log(account.balance);
console.log(account.tokens); // ERC20, ERC721, ERC1155 tokens

getTxHistory(pubkey: string, cursor?: string, pageSize = 10, from?: number, to?: number): Promise<TxHistory>

Retrieves paginated transaction history including internal transactions for the specified address.

const txHistory = await service.getTxHistory('0xabc123', undefined, 20);
console.log(txHistory.txs.length);
console.log(txHistory.cursor); // Use this for next page

getTransaction(txid: string): Promise<Tx>

Fetches detailed transaction data by transaction hash, including internal transactions when available.

const tx = await service.getTransaction('0xdeadbeef...');
console.log(tx.internalTxs); // Internal transactions if any

estimateGas(body: EstimateGasBody): Promise<GasEstimate>

Estimates the gas limit required for a transaction.

const gasEstimate = await service.estimateGas({
  from: '0xabc',
  to: '0xdef',
  value: '1000000000000000000', // 1 ETH in wei
  data: '0x',
});
console.log(gasEstimate.gasLimit);

getGasFees(): Promise<GasFees>

Fetches current gas fee estimates at different speed tiers.

const fees = await service.getGasFees();
console.log(fees.fast.maxFeePerGas);

sendTx(body: SendTxBody): Promise<string>

Broadcasts a raw signed transaction to the blockchain.

const txid = await service.sendTx({ hex: '0xf86c...' });
console.log(txid); // Transaction hash

handleBlock(hash: string): Promise<Array<BlockbookTx>>

Fetches all transactions within a block by block hash.


getTokenMetadata(address: string, id: string, type: TokenType): Promise<TokenMetadata>

Retrieves metadata for a token (NFT) by contract address, token ID, and token type (ERC721 or ERC1155).

const metadata = await service.getTokenMetadata('0xabc...', '123', 'erc721');
console.log(metadata.media.url);

doRpcRequest(req: RPCRequest | RPCRequest[]): Promise<RPCResponse | RPCResponse[]>

Performs a generic JSON-RPC request or batch request to the configured RPC endpoint.


Internal (Private) Methods and Implementation Details

Transaction Handling and Internal Transaction Extraction

These methods implement retry with exponential backoff on failures and parse call stack traces or explorer API responses to extract internal transaction calls.

Call Stack Processing

Pagination Helpers


Important Implementation Details and Algorithms


Interaction with Other System Components


Usage Examples

Fetching Account Info

const service = new Service({ blockbook, gasOracle, explorerApiUrl, logger, client, rpcUrl, rpcApiKey });
const account = await service.getAccount('0xYourAddress');
console.log(account.balance);
console.log(account.tokens);

Retrieving Transaction History with Pagination

const firstPage = await service.getTxHistory('0xYourAddress', undefined, 20);
console.log(firstPage.txs);

if (firstPage.cursor) {
  const secondPage = await service.getTxHistory('0xYourAddress', firstPage.cursor, 20);
  console.log(secondPage.txs);
}

Sending a Raw Transaction

const txid = await service.sendTx({ hex: '0xf86c...' });
console.log(`Transaction sent with hash: ${txid}`);

Estimating Gas Limit for a Transaction

const estimate = await service.estimateGas({
  from: '0xYourAddress',
  to: '0xRecipientAddress',
  value: '1000000000000000000', // 1 ETH in wei
  data: '0x',
});
console.log(`Estimated gas limit: ${estimate.gasLimit}`);

Getting Gas Fee Estimates

const fees = await service.getGasFees();
console.log(`Fast gas fee (maxFeePerGas): ${fees.fast.maxFeePerGas}`);

Fetching Token Metadata for an ERC721 Token

const metadata = await service.getTokenMetadata('0xNFTContractAddress', '12345', 'erc721');
console.log(metadata.name, metadata.media.url);

Mermaid Class Diagram

classDiagram
    class Service {
        -blockbook: Blockbook
        -gasOracle: GasOracle
        -explorerApiUrl: URL
        -logger: Logger
        -client: PublicClient
        -rpcUrl: string
        -rpcApiKey?: string
        +constructor(args: ServiceArgs)
        +getAccount(pubkey: string): Promise<Account>
        +getTxHistory(pubkey: string, cursor?: string, pageSize?: number, from?: number, to?: number): Promise<TxHistory>
        +getTransaction(txid: string): Promise<Tx>
        +estimateGas(body: EstimateGasBody): Promise<GasEstimate>
        +getGasFees(): Promise<GasFees>
        +sendTx(body: SendTxBody): Promise<string>
        +handleBlock(hash: string): Promise<Array<BlockbookTx>>
        +getTokenMetadata(address: string, id: string, type: TokenType): Promise<TokenMetadata>
        +doRpcRequest(req: RPCRequest | Array<RPCRequest>): Promise<RPCResponse | Array<RPCResponse>>
        -handleTransaction(tx: BlockbookTx): Tx
        -handleTransactionWithInternal(tx: BlockbookTx): Promise<Tx>
        -handleTransactionWithInternalTrace(tx: BlockbookTx, internalTxMethod?: InternalTxFetchMethod): Promise<Tx>
        -fetchInternalTxsByTxTrace(txid: string, retryCount?: number): Promise<Array<InternalTx> | undefined>
        -fetchInternalTxsByTxDebug(txid: string, retryCount?: number): Promise<Array<InternalTx> | undefined>
        -fetchInternalTxsByTxid(txid: string): Promise<Array<InternalTx> | undefined>
        -fetchInternalTxsByBlockDebug(blockHash: string, retryCount?: number): Promise<Record<string, Array<InternalTx> | undefined>>
        -fetchInternalTxsByBlockTrace(blockHash: string, retryCount?: number): Promise<Record<string, Array<InternalTx> | undefined>>
        -fetchInternalTxsByAddress(address: string, page: number, pageSize: number, from?: number, to?: number): Promise<Array<ExplorerInternal