Safari can't open the page on Mac? Here are the best ways to fix it. best ways to fix it

Why is my Mac so slow? Best ways to speed it up. Best ways to speed it up

XChainJS Transaction Example: How to Check, Track, and Monitor Blockchain Transactions






XChainJS Transaction Example: Check & Track Blockchain Tx






XChainJS Transaction Example: How to Check, Track, and Monitor Blockchain Transactions

If you’ve spent more than five minutes in Web3 development, you know the drill: you broadcast a transaction, hold your breath, and then frantically paste the hash into a blockchain transaction explorer to see if the universe cooperated. That workflow is fine for a quick manual check — but the moment you’re building a production-grade crypto wallet or a DeFi protocol, you need programmatic transaction tracking baked directly into your backend.

XChainJS is a TypeScript blockchain SDK built exactly for this use case. It provides a unified, chain-agnostic API to interact with Bitcoin, Ethereum, THORChain, Cosmos, and a growing list of other networks — without forcing you to learn a different RPC dialect for every chain you touch. One of its most immediately useful capabilities is blockchain transaction lookup: you hand it a transaction hash, it hands you back structured data. This article walks through how that works in practice, with working TypeScript code you can drop straight into a project.

We’ll cover setting up the SDK, performing a transaction hash lookup, monitoring crypto transaction status in real time, and extending the same pattern to cross-chain environments. By the end, you’ll have a reusable module for blockchain tx monitoring that can serve as the foundation of any wallet or DeFi backend.

Why XChainJS for Blockchain Transaction Tracking?

The honest answer to “why not just use ethers.js or web3.js?” is: those libraries are excellent, but they’re Ethereum-centric. The moment your users want to track a Bitcoin transaction or monitor a cross-chain swap through THORChain, you’re pulling in additional libraries and writing adapter code to normalize the response shapes. XChainJS inverts this problem — it defines a single XChainClient interface that every chain package implements. Your web3 transaction tracking code looks identical whether you’re querying Ethereum mainnet or the Bitcoin network.

From a blockchain backend development perspective, this uniformity is not just a convenience — it’s an architectural advantage. You can write generic middleware that accepts a chain identifier and a tx hash, instantiates the correct client, and returns normalized transaction data. No switch-case sprawl, no chain-specific error handling scattered across your codebase. This is the kind of abstraction that saves you from rewriting everything when your roadmap suddenly includes “and also support Avalanche.”

The SDK is also TypeScript-first, which matters for teams that value type safety in blockchain developer tools. The TxData, TxHistoryParams, and TxPage interfaces are well-typed, so your IDE will catch mismatches before your tests do. For teams doing serious web3 wallet development or building DeFi dashboards, this drastically reduces the integration surface where bugs like to hide.

Installation and Initial Setup

XChainJS is modular by design. The core package defines the shared interfaces, and you install chain-specific packages only for the networks you need. For this example, we’ll work with Ethereum and Bitcoin to demonstrate both the pattern and the cross-chain portability. Start by adding the packages:

# Core client interface
npm install @xchainjs/xchain-client

# Ethereum support (EVM-compatible chains)
npm install @xchainjs/xchain-ethereum ethers

# Bitcoin support
npm install @xchainjs/xchain-bitcoin

# Utility package (asset helpers, base amounts)
npm install @xchainjs/xchain-util

Each chain client requires provider configuration. For Ethereum, XChainJS uses ethers.js under the hood, so you’ll need an RPC endpoint — Infura, Alchemy, or any other JSON-RPC provider. For Bitcoin, it defaults to the Sochain API but supports UTXO providers like Blockcypher and Blockstream Esplora. Keeping providers configurable is important for production deployments where you want to avoid rate limits from a single source.

A quick note on environment hygiene before we write a single line of business logic: keep your API keys and mnemonic phrases in environment variables, never hardcoded. If you’re doing read-only blockchain transaction lookup — which is what this guide covers — you don’t even need a wallet phrase. The client can be initialized in a read-only mode by providing only the network configuration, which significantly reduces your attack surface.

XChainJS Transaction Example: Check Transaction Hash

The workhorse method for any xchainjs transaction example is getTransactionData(txHash). It takes a transaction hash string and returns a Tx object containing the sender, recipient, asset type, amount, and — critically — the transaction’s current state. Here’s a complete working implementation:

// check-transaction.ts
import { Client as EthClient } from '@xchainjs/xchain-ethereum'
import { Network } from '@xchainjs/xchain-client'

// Initialize read-only Ethereum client
const ethClient = new EthClient({
  network: Network.Mainnet,
  etherscanApiKey: process.env.ETHERSCAN_API_KEY!,
  infuraCreds: {
    projectId: process.env.INFURA_PROJECT_ID!,
  },
})

/**
 * Check transaction status by hash
 * @param txHash - The blockchain transaction hash to look up
 */
async function checkTransactionHash(txHash: string): Promise {
  try {
    console.log(`\n🔍 Looking up transaction: ${txHash}`)

    // Core XChainJS call — works identically across supported chains
    const txData = await ethClient.getTransactionData(txHash)

    console.log('\n✅ Transaction Found:')
    console.log('----------------------------')
    console.log(`Hash:      ${txData.hash}`)
    console.log(`Asset:     ${txData.asset.symbol}`)
    console.log(`Type:      ${txData.type}`)

    // Parse sender and recipient from transfers array
    txData.transfers.forEach((transfer, idx) => {
      console.log(`\nTransfer #${idx + 1}:`)
      console.log(`  From:    ${transfer.from}`)
      console.log(`  To:      ${transfer.to}`)
      console.log(`  Amount:  ${transfer.amount.amount().toString()} (base units)`)
    })

    console.log('\nRaw response object:')
    console.log(JSON.stringify(txData, null, 2))

  } catch (error) {
    if (error instanceof Error) {
      console.error(`❌ Transaction lookup failed: ${error.message}`)
    } else {
      console.error('❌ Unknown error during transaction lookup')
    }
  }
}

// Example usage — replace with a real tx hash
const TX_HASH = '0xYourTransactionHashHere'
checkTransactionHash(TX_HASH)

When you run this, the SDK handles the RPC call, parses the response, and returns a normalized Tx object. The transfers array is particularly useful — it abstracts ERC-20 transfers, native ETH sends, and complex multi-hop swaps into the same consistent structure. You’re not writing custom parsing logic for each transaction type; XChainJS does that for you.

The same pattern, with a different import, gives you Bitcoin transaction data. You swap @xchainjs/xchain-ethereum for @xchainjs/xchain-bitcoin and initialize with your UTXO provider config. The checkTransactionHash function body doesn’t change at all. This is the practical payoff of the unified interface — you write the blockchain transaction API integration once and apply it everywhere.

One thing worth noting: getTransactionData returns the transaction as recorded on-chain, but it doesn’t directly expose block confirmation count in all chain implementations. For robust crypto transaction confirmation checks, you’ll want to combine the tx block height with the current block height from getBlockNumber() or equivalent. We’ll cover that pattern in the monitoring section below.

Monitoring Crypto Transaction Status in Real Time

A one-shot lookup is useful, but real web3 transaction monitoring means watching a transaction from broadcast until it reaches sufficient confirmation depth. The standard approach is a polling loop with exponential backoff — aggressive enough to catch fast confirmations, gentle enough not to exhaust your RPC provider’s rate limits. XChainJS doesn’t ship a built-in watcher (by design — it’s an SDK, not a framework), but composing one from its primitives is straightforward.

// tx-monitor.ts
import { Client as EthClient } from '@xchainjs/xchain-ethereum'
import { Network } from '@xchainjs/xchain-client'

const ethClient = new EthClient({
  network: Network.Mainnet,
  etherscanApiKey: process.env.ETHERSCAN_API_KEY!,
  infuraCreds: { projectId: process.env.INFURA_PROJECT_ID! },
})

interface MonitorConfig {
  txHash: string
  requiredConfirmations: number   // e.g. 12 for ETH, 6 for BTC
  pollIntervalMs: number          // base poll interval
  maxAttempts: number             // circuit breaker
}

async function monitorTransaction(config: MonitorConfig): Promise {
  const { txHash, requiredConfirmations, pollIntervalMs, maxAttempts } = config
  let attempts = 0

  console.log(`\n🚀 Starting monitoring for tx: ${txHash}`)
  console.log(`   Required confirmations: ${requiredConfirmations}`)

  while (attempts < maxAttempts) {
    attempts++

    try {
      // Fetch the transaction data
      const txData = await ethClient.getTransactionData(txHash)

      // Get current block number via the underlying provider
      // Note: expose via client config or use ethers provider directly
      const provider = ethClient.getProvider()
      const currentBlock = await provider.getBlockNumber()

      // Ethers.js TransactionResponse includes blockNumber when mined
      const receipt = await provider.getTransactionReceipt(txHash)

      if (!receipt || receipt.blockNumber === null) {
        console.log(`[Attempt ${attempts}] ⏳ Transaction pending — not yet mined.`)
      } else {
        const confirmations = currentBlock - receipt.blockNumber
        const status = receipt.status === 1 ? '✅ Success' : '❌ Reverted'

        console.log(
          `[Attempt ${attempts}] Block: ${receipt.blockNumber} | ` +
          `Confirmations: ${confirmations}/${requiredConfirmations} | ` +
          `Status: ${status}`
        )

        if (confirmations >= requiredConfirmations) {
          console.log(`\n🎉 Transaction confirmed with ${confirmations} confirmations!`)
          console.log(`   Asset: ${txData.asset.symbol}`)
          return
        }
      }
    } catch (err) {
      console.warn(`[Attempt ${attempts}] ⚠️  RPC error — retrying...`, err)
    }

    // Exponential backoff capped at 30s
    const delay = Math.min(pollIntervalMs * Math.pow(1.5, attempts - 1), 30_000)
    console.log(`   Next check in ${(delay / 1000).toFixed(1)}s...\n`)
    await new Promise(resolve => setTimeout(resolve, delay))
  }

  console.error(`\n⏰ Max attempts (${maxAttempts}) reached. Transaction status unknown.`)
}

// Run the monitor
monitorTransaction({
  txHash: '0xYourTransactionHashHere',
  requiredConfirmations: 12,
  pollIntervalMs: 3_000,
  maxAttempts: 40,
})

The exponential backoff here is intentional and important. A fresh transaction might confirm in 15 seconds or 15 minutes depending on gas price and network congestion. Starting with a 3-second poll and doubling until you hit the 30-second cap means you react quickly to fast confirmations while not hammering your RPC provider during slow periods. For DeFi transaction tracking in production, you’d typically move this logic into a queue system (BullMQ, Redis-backed) so it survives process restarts.

Notice that we’re using both getTransactionData from XChainJS and the underlying ethers.js provider.getTransactionReceipt. This is a pragmatic hybrid approach: XChainJS gives you the normalized asset and transfer data; ethers.js gives you the low-level receipt with block number and status. In a production system, you might wrap both into a unified TransactionStatus type that your frontend can consume directly.

Retrieving Blockchain Transaction History with getHistory

Beyond single-tx lookups, XChainJS exposes getHistory(params) for fetching a paginated list of transactions associated with a wallet address. This is the foundation of any blockchain transaction history feature — whether you’re building a wallet dashboard, an analytics tool, or a compliance reporting module. The call signature is consistent across chains:

// tx-history.ts
import { Client as EthClient } from '@xchainjs/xchain-ethereum'
import { Network } from '@xchainjs/xchain-client'
import { baseToAsset } from '@xchainjs/xchain-util'

const ethClient = new EthClient({
  network: Network.Mainnet,
  etherscanApiKey: process.env.ETHERSCAN_API_KEY!,
  infuraCreds: { projectId: process.env.INFURA_PROJECT_ID! },
})

async function getWalletHistory(address: string): Promise {
  try {
    const history = await ethClient.getTransactions({
      address,
      limit: 10,
      offset: 0,
    })

    console.log(`\n📜 Transaction History for: ${address}`)
    console.log(`   Total transactions found: ${history.total}`)
    console.log('-------------------------------------------\n')

    history.txs.forEach((tx, idx) => {
      const amount = baseToAsset(tx.transfers[0]?.amount ?? { amount: () => '0' } as any)
      console.log(`[${idx + 1}] Hash:   ${tx.hash}`)
      console.log(`     Asset:  ${tx.asset.symbol}`)
      console.log(`     Type:   ${tx.type}`)
      console.log(`     Date:   ${tx.date?.toISOString() ?? 'N/A'}`)
      console.log('')
    })

  } catch (error) {
    console.error('Failed to fetch transaction history:', error)
  }
}

// Example: check history for a public Ethereum address
getWalletHistory('0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe')

The getTransactions method (the history API) accepts limit and offset for pagination, making it straightforward to implement infinite scroll or page-based navigation in a frontend. The returned TxPage object includes a total count, so you can build accurate pagination controls without a separate count query. For high-volume addresses, consider adding date range filtering — not all chain clients support it natively, but Ethereum via Etherscan’s API does.

It’s worth distinguishing the use cases clearly: getTransactionData(hash) is your transaction hash lookup for a specific known transaction — perfect for post-send confirmation flows. getTransactions(params) is your history feed — ideal for displaying a wallet’s activity log. Both are needed in a complete crypto wallet transaction check implementation, and XChainJS surfaces both through the same client instance.

For teams building analytics pipelines, the history endpoint can be polled on a schedule to detect incoming transactions without WebSocket infrastructure. It’s not as real-time as event subscriptions, but it’s dramatically simpler to operate and perfectly adequate for many DeFi dashboards where near-real-time (sub-minute latency) is acceptable.

Cross-Chain Transaction Monitoring in Practice

The most compelling use case for XChainJS over chain-specific libraries is cross-chain transaction monitoring — tracking the full lifecycle of a swap or transfer that touches multiple blockchains. Think of a THORChain swap: a user sends Bitcoin on the Bitcoin network, and after routing through THORChain’s settlement layer, USDC arrives on Ethereum. Two chains, two transaction hashes, one user experience that needs to surface both.

// cross-chain-monitor.ts
import { Client as BtcClient } from '@xchainjs/xchain-bitcoin'
import { Client as EthClient } from '@xchainjs/xchain-ethereum'
import { Network } from '@xchainjs/xchain-client'

// Initialize both clients
const btcClient = new BtcClient({ network: Network.Mainnet })

const ethClient = new EthClient({
  network: Network.Mainnet,
  etherscanApiKey: process.env.ETHERSCAN_API_KEY!,
  infuraCreds: { projectId: process.env.INFURA_PROJECT_ID! },
})

interface CrossChainTx {
  sourceTxHash: string
  sourceChain: 'bitcoin'
  destinationTxHash: string
  destinationChain: 'ethereum'
}

async function monitorCrossChainSwap(swap: CrossChainTx): Promise {
  console.log('\n🌐 Cross-Chain Swap Monitor')
  console.log('==============================')

  // Check source chain (Bitcoin)
  try {
    const sourceTx = await btcClient.getTransactionData(swap.sourceTxHash)
    console.log(`\n[Source - Bitcoin]`)
    console.log(`  Hash:   ${sourceTx.hash}`)
    console.log(`  Asset:  ${sourceTx.asset.symbol}`)
    console.log(
      `  Amount: ${sourceTx.transfers[0]?.amount.amount().toString()} sats`
    )
  } catch (e) {
    console.error('  ❌ Source tx not found or pending')
  }

  // Check destination chain (Ethereum)
  try {
    const destTx = await ethClient.getTransactionData(swap.destinationTxHash)
    console.log(`\n[Destination - Ethereum]`)
    console.log(`  Hash:   ${destTx.hash}`)
    console.log(`  Asset:  ${destTx.asset.symbol}`)
    console.log(
      `  Amount: ${destTx.transfers[0]?.amount.amount().toString()} wei`
    )
  } catch (e) {
    console.error('  ❌ Destination tx not found or pending')
  }
}

// Example swap tracking
monitorCrossChainSwap({
  sourceTxHash: 'your-bitcoin-tx-hash-here',
  sourceChain: 'bitcoin',
  destinationTxHash: '0xYourEthereumTxHashHere',
  destinationChain: 'ethereum',
})

This pattern scales directly to any combination of XChainJS-supported chains. The getTransactionData interface guarantees you’ll get back a normalized response regardless of the underlying chain’s data format — Bitcoin’s UTXO model and Ethereum’s account model produce structurally identical Tx objects at the XChainJS layer. That’s a meaningful abstraction when you’re building a product that needs to present a unified view of a multi-leg cross-chain operation.

In a production THORChain integration, you’d typically combine this with THORChain’s own transaction tracking API (Midgard or THORNode) to get the swap status between the two chain legs. XChainJS handles the chain-level lookups; Midgard handles the protocol-level routing status. The combination gives you complete visibility into the full swap lifecycle for a robust DeFi transaction tracking implementation.

One architectural consideration for teams building cross-chain transaction monitoring at scale: maintain a mapping table that links source transaction hashes to destination transaction hashes. This is your source of truth for correlating multi-chain activity. Whether you store this in PostgreSQL, Redis, or an event-sourced system depends on your consistency requirements — but the XChainJS API layer itself is stateless and fits cleanly into any of those architectures.

Putting It All Together: A Production-Ready Transaction Checker Module

The individual pieces we’ve covered — single-tx lookup, polling monitor, history fetcher, cross-chain checker — compose naturally into a unified module. For teams building a crypto wallet backend or a DeFi protocol dashboard, here’s what a production-ready web3 transaction checker module structure looks like:

  • ChainClientFactory — instantiates the correct XChainJS client based on a chain enum, with provider config injected from environment variables.
  • TransactionService — wraps getTransactionData and getTransactions with retry logic, error normalization, and response caching (Redis with a short TTL).
  • ConfirmationWatcher — a queue-backed worker that polls confirmation depth and emits events (WebSocket or webhook) when thresholds are reached.
  • CrossChainCorrelator — maintains the hash-to-hash mapping and calls the appropriate chain client for each leg of a swap.

Caching deserves special attention in any blockchain transaction API implementation. Confirmed transactions don’t change — their data is immutable once finalized. Caching them aggressively (hours or indefinitely) saves RPC calls and improves response latency for history queries. Pending transactions, on the other hand, should bypass cache entirely or use a very short TTL (5–10 seconds). A simple Redis strategy with key patterns like tx:{chain}:{hash} handles this cleanly with TTL overrides based on confirmation status.

Error handling in blockchain contexts is more nuanced than typical API integrations. RPC nodes go offline, rate limits get hit, and some transaction hashes genuinely don’t exist (user paste error, wrong network). A robust implementation distinguishes between these cases — network errors trigger retries with backoff, rate limit errors trigger provider rotation, and “not found” errors return a typed TransactionNotFoundError that the frontend can surface as a clear user message rather than a generic 500. XChainJS throws standard JavaScript errors with descriptive messages, so wrapping them in typed custom errors is a thin layer of work with significant UX payoff.

Semantic Keyword Reference

xchainjs
xchainjs transaction example
check transaction hash
blockchain transaction lookup
web3 transaction tracking
crypto transaction status
transaction confirmation check
blockchain tx monitoring
xchainjs transaction api
web3 transaction checker
blockchain transaction explorer
transaction hash lookup
typescript blockchain sdk
web3 development
blockchain transaction api
crypto wallet transaction check
cross chain transaction monitoring
blockchain backend development
web3 transaction monitoring
defi transaction tracking
blockchain transaction history
web3 wallet development
crypto transaction confirmation
blockchain developer tools
web3 transaction example

FAQ

How do I check a transaction hash using XChainJS?

Call getTransactionData(txHash) on the XChainJS client for your target chain. Pass the transaction hash as a string argument and await the result. The returned Tx object includes the sender and recipient addresses, asset type, transfer amounts in base units, and the transaction type. For confirmation depth, combine with the chain’s current block height using the underlying provider. See the full XChainJS transaction example on dev.to for a complete working implementation.

How does XChainJS handle cross-chain transaction monitoring?

XChainJS defines a unified XChainClient interface that every chain-specific package implements. This means getTransactionData(txHash) behaves identically whether you’re querying Bitcoin (@xchainjs/xchain-bitcoin), Ethereum (@xchainjs/xchain-ethereum), THORChain (@xchainjs/xchain-thorchain), or any other supported network. For multi-leg cross-chain swaps, you instantiate one client per chain and call each independently, then correlate the results via a shared hash-mapping layer in your application. No chain-specific parsing code required.

What is the difference between getTransactionData and getTransactions in XChainJS?

getTransactionData(txHash) retrieves a single transaction by its exact hash — ideal for post-send confirmation flows and blockchain transaction lookup by hash. getTransactions(params) returns a paginated list of transactions for a given wallet address, making it the right choice for building blockchain transaction history views, activity feeds, or compliance audit trails. Use the former when you know the specific tx hash; use the latter when you need a history view for an address.


Condividi su:
Cooperativa Tric - Teatri di Bari - P.iva : 07685700721 - Privacy - Cookies