Skip to main content

ChainGate 1.3 — Real-time events in the SDK: onBlock, onBalance, onTransaction

· 4 min read

Earlier this week the API learned to push events over WebSocket. Today the SDK does the work for you: subscribe with a callback and stop polling. New blocks, balance changes, transactions, pending activity and contract interactions arrive as typed events on Bitcoin, Litecoin, Dogecoin, Bitcoin Cash, Bitcoin Testnet, Ethereum and Avalanche — no sockets to open, no reconnection code, no JSON to parse.

import { ChainGate } from 'chaingate'

const cg = new ChainGate()
const btc = cg.explore(cg.networks.bitcoin)

btc.onBlock((block) => console.log(`Block ${block.height} — ${block.numTxs} transactions`))

btc.onBalance('bc1q...', async ({ confirmed }) => {
console.log('New balance:', confirmed.base(), 'BTC', '≈', await confirmed.toCurrency('usd'), 'USD')
})

Events for any address, or for your wallet​

Explorers follow any address you pass. Connectors follow the wallet's own address — derived for you, with the usual address options:

// Any address, no wallet needed
const eth = cg.explore(cg.networks.ethereum)
eth.onTransaction('0x...', ({ height }) => console.log('Activity in block', height))
eth.onContractInteraction('0x...', ({ contract }) => console.log('New contract:', contract))

// Your wallet
const wallet = cg.connect(cg.networks.litecoin, myWallet)
wallet.onTransaction(({ received, amount, txid }) => {
console.log(received ? 'Received' : 'Sent', amount.base(), 'LTC in', txid)
})
wallet.onBalance(({ confirmed }) => console.log(confirmed.base(), 'LTC'), { index: 3 })
MethodWhat you receive
onBlock(cb) / onFullBlock(cb)Every new block — a summary, or the whole block
onBalance(address, cb) / onPendingBalance(address, cb)The confirmed balance after every change, or the pending delta
onTransaction(address, cb) / onPendingTransaction(address, cb)Every confirmed transaction of an address, or every pending one
onMempoolTransaction(cb)Every pending transaction on the network
onContractInteraction(address, cb)Each new contract an address interacts with (Ethereum, Avalanche)
onError(cb)Connection and subscription errors

Every event is a plain, typed object — camelCase fields and Amount values, so .base(), .min() and .toCurrency() work exactly as they do everywhere else in the SDK.


One handle to rule them​

Every method returns a Subscription right away:

const sub = wallet.onBalance(({ confirmed }) => render(confirmed))

await sub.ready // the server confirmed the subscription
sub.address // the address being followed — the wallet's, once derived
sub.unsubscribe() // done

Invalid addresses are rejected before anything is sent, and ready rejects if the server refuses a subscription.


Connections you never have to think about​

All the subscriptions of a network share one connection, opened with the first subscription and closed when the last one goes away — so a script that unsubscribes from everything simply exits. Subscribing twice to the same thing (even with the address written differently) costs a single subscription.

If the connection drops, it is re-established with backoff and every subscription is restored. A silent connection is checked with a ping and replaced if it stopped answering. Subscriptions are sent at a steady pace that never trips the rate limits, and everything that goes wrong on a connection is reported through onError() — including RateLimitError / RateLimitQuotaError when a limit is reached.

btc.onError((error) => console.warn('Events:', error.message))

Confirmations without polling​

broadcasted.onConfirmed() used to check the transaction every few seconds. In 1.3 it is driven by events: the SDK follows the sender's transactions and fires the moment the block that includes yours is finalized — with a single lookup when the subscription starts, and none at all on UTXO networks when the event itself carries the confirmation.

const broadcasted = await tx.signAndBroadcast()

broadcasted.onConfirmed(({ blockHeight }) => {
console.log('Confirmed in block', blockHeight)
})

Nothing changes in your code: same method, same callback, same cancel function — and a new callback registered after cancelling all the previous ones picks the tracking back up.


Highlights​

ChangeDescription
onBlock / onFullBlockNew blocks on every natively supported network
onBalance / onPendingBalanceConfirmed balance changes and pending deltas per address
onTransaction / onPendingTransactionConfirmed and pending transactions per address
onMempoolTransactionEvery pending transaction on the network
onContractInteractionNew contract interactions on Ethereum and Avalanche
onErrorConnection and subscription errors, including rate limits
Subscriptionready, address and unsubscribe() on every subscription
Shared connectionsOne per network, opened and closed on demand, reconnected automatically
Event-driven onConfirmed()Transaction confirmations fire as soon as the block is finalized

Works in Node.js 22+, Bun and the browser, with no new dependency. Events are included with the free API key and also work without one.

npm install chaingate@latest

Read the real-time events guide, grab your free API key at ChainGate's Dashboard, star us on GitHub and join the Discord to tell us what you're building.