Get Address Balance
Overview
The library provides methods to query the satoshi balance of one or more Bitcoin addresses. Both single and batch lookups are supported, with an optional filter to exclude ordinal-bearing UTXOs from the reported balance.
Single Address Balance
Use the getBalance() method to retrieve the total satoshi balance for a single Bitcoin address. It accepts the target address and an optional filterOrdinals flag.
When the filterOrdinals flag is set to true, any UTXOs containing ordinal inscriptions are excluded from the balance calculation, ensuring the returned value reflects only the spendable satoshis.
The result is returned as a bigint.
import { JSONRpcProvider } from 'opnet';
import { networks } from '@btc-vision/bitcoin';
const network = networks.regtest;
const provider = new JSONRpcProvider({ url: 'https://regtest.opnet.org', network });
// Get balance (filters ordinals by default)
const balance = await provider.getBalance('bcrt1p...');
console.log('Balance:', balance, 'satoshis');// Get total balance including ordinals
const totalBalance = await provider.getBalance(wallet.p2tr, false);
// Get spendable balance excluding ordinals
const spendableBalance = await provider.getBalance(wallet.p2tr, true);
console.log('Total balance:', totalBalance);
console.log('Spendable balance:', spendableBalance);
console.log('Ordinal value:', totalBalance - spendableBalance);Multiple Address Balances
The getBalances() method retrieves satoshi balances for multiple addresses in a single request. It accepts an array of addresses and an optional filterOrdinals flag, returning a Record<string, bigint> that maps each address to its balance.
Use this method instead of calling getBalance() in a loop to reduce network round-trips.
// Get balances for multiple addresses efficiently
const addresses = [
'bcrt1p...address1...',
'bcrt1p...address2...',
'bcrt1p...address3...',
];
const balances = await provider.getBalances(addresses, true);
// balances is Record<string, bigint>
for (const [address, balance] of Object.entries(balances)) {
console.log(`${address}: ${balance} sats`);
}async function getTotalBalance(
provider: JSONRpcProvider,
addresses: string[]
): Promise<bigint> {
const balances = await provider.getBalances(addresses, true);
let total = 0n;
for (const balance of Object.values(balances)) {
total += balance;
}
return total;
}
// Usage
const total = await getTotalBalance(provider, [
wallet.p2tr,
wallet.p2wpkh,
]);
console.log('Total across addresses:', total, 'sats');
Formatting Balances
import { BitcoinUtils } from 'opnet';
const balanceSats = await provider.getBalance(address);
const balanceBTC = BitcoinUtils.formatUnits(balanceSats, 8);
console.log(`Balance: ${balanceBTC} BTC`);function formatBalance(satoshis: bigint): string {
if (satoshis >= 100_000_000n) {
// Format as BTC for large amounts
const btc = Number(satoshis) / 100_000_000;
return `${btc.toFixed(8)} BTC`;
} else if (satoshis >= 1_000_000n) {
// Format as mBTC
const mbtc = Number(satoshis) / 100_000;
return `${mbtc.toFixed(5)} mBTC`;
} else {
// Format as sats
return `${satoshis.toLocaleString()} sats`;
}
}
const balance = await provider.getBalance(address);
console.log('Balance:', formatBalance(balance));Wallet Balance Tracking
Implementing a balance cache avoids redundant network requests when the same addresses are queried repeatedly. The BalanceService below stores results locally and invalidates them on new block confirmations.
class BalanceService {
private provider: JSONRpcProvider;
private cache: Map<string, { balance: bigint; timestamp: number }> = new Map();
private cacheTimeout = 30000; // 30 seconds
constructor(provider: JSONRpcProvider) {
this.provider = provider;
}
async getBalance(
address: string,
useCache: boolean = true
): Promise<bigint> {
if (useCache) {
const cached = this.cache.get(address);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.balance;
}
}
const balance = await this.provider.getBalance(address, true);
this.cache.set(address, { balance, timestamp: Date.now() });
return balance;
}
async getMultipleBalances(
addresses: string[],
useCache: boolean = true
): Promise<Record<string, bigint>> {
if (!useCache) {
return this.provider.getBalances(addresses, true);
}
const result: Record<string, bigint> = {};
const addressesToFetch: string[] = [];
// Check cache first
for (const address of addresses) {
const cached = this.cache.get(address);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
result[address] = cached.balance;
} else {
addressesToFetch.push(address);
}
}
// Fetch uncached addresses
if (addressesToFetch.length > 0) {
const freshBalances = await this.provider.getBalances(
addressesToFetch,
true
);
for (const [address, balance] of Object.entries(freshBalances)) {
result[address] = balance;
this.cache.set(address, { balance, timestamp: Date.now() });
}
}
return result;
}
clearCache(address?: string): void {
if (address) {
this.cache.delete(address);
} else {
this.cache.clear();
}
}
}
// Usage
const balanceService = new BalanceService(provider);
const balance = await balanceService.getBalance(wallet.p2tr);
console.log('Balance:', balance);
// Clear cache after transaction
balanceService.clearCache(wallet.p2tr);Balance Monitoring
Polling
Polling periodically for balance changes allows applications to detect incoming payments or withdrawals without a persistent WebSocket connection. The approach below queries the provider at a fixed interval and triggers a callback when the balance differs from the previously known value.
async function monitorBalance(
provider: JSONRpcProvider,
address: string,
callback: (balance: bigint) => void,
intervalMs: number = 10000
): Promise<() => void> {
let previousBalance = await provider.getBalance(address, true);
callback(previousBalance);
const intervalId = setInterval(async () => {
try {
const currentBalance = await provider.getBalance(address, true);
if (currentBalance !== previousBalance) {
callback(currentBalance);
previousBalance = currentBalance;
}
} catch (error) {
console.error('Error checking balance:', error);
}
}, intervalMs);
// Return cleanup function
return () => clearInterval(intervalId);
}
// Usage
const stopMonitoring = await monitorBalance(
provider,
wallet.p2tr,
(balance) => {
console.log('Balance updated:', balance, 'sats');
},
5000 // Check every 5 seconds
);
// Later: stop monitoring
// stopMonitoring();Real-Time
For real-time balance monitoring without polling overhead, subscribe to new block notifications via the WebSocketRpcProvider and re-query the balance on each confirmed block. This approach reacts immediately to on-chain changes while avoiding unnecessary requests between blocks.
import { WebSocketRpcProvider } from 'opnet';
const wsProvider = new WebSocketRpcProvider({ url: 'wss://regtest.opnet.org/ws', network });
// Subscribe to block notifications
wsProvider.subscribeToBlocks((block) => {
// Check balance after each new block
provider.getBalance(wallet.p2tr).then((balance) => {
console.log(`Block ${block.height}: Balance = ${balance} sats`);
});
});