UNPKG

viem

Version:

TypeScript Interface for Ethereum

131 lines 5.7 kB
import { hexToBigInt } from '../../utils/encoding/fromHex.js'; import { getAction } from '../../utils/getAction.js'; import { observe } from '../../utils/observe.js'; import { poll } from '../../utils/poll.js'; import { stringify } from '../../utils/stringify.js'; import { getBlockNumber, } from './getBlockNumber.js'; /** * Watches and returns incoming block numbers. * * - Docs: https://viem.sh/docs/actions/public/watchBlockNumber * - Examples: https://stackblitz.com/github/wevm/viem/tree/main/examples/blocks_watching-blocks * - JSON-RPC Methods: * - When `poll: true`, calls [`eth_blockNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber) on a polling interval. * - When `poll: false` & WebSocket Transport, uses a WebSocket subscription via [`eth_subscribe`](https://docs.alchemy.com/reference/eth-subscribe-polygon) and the `"newHeads"` event. * * @param client - Client to use * @param parameters - {@link WatchBlockNumberParameters} * @returns A function that can be invoked to stop watching for new block numbers. {@link WatchBlockNumberReturnType} * * @example * import { createPublicClient, watchBlockNumber, http } from 'viem' * import { mainnet } from 'viem/chains' * * const client = createPublicClient({ * chain: mainnet, * transport: http(), * }) * const unwatch = watchBlockNumber(client, { * onBlockNumber: (blockNumber) => console.log(blockNumber), * }) */ export function watchBlockNumber(client, { emitOnBegin = false, emitMissed = false, onBlockNumber, onError, poll: poll_, pollingInterval = client.pollingInterval, }) { const enablePolling = (() => { if (typeof poll_ !== 'undefined') return poll_; if (client.transport.type === 'webSocket') return false; if (client.transport.type === 'fallback' && client.transport.transports[0].config.type === 'webSocket') return false; return true; })(); let prevBlockNumber; const pollBlockNumber = () => { const observerId = stringify([ 'watchBlockNumber', client.uid, emitOnBegin, emitMissed, pollingInterval, ]); return observe(observerId, { onBlockNumber, onError }, (emit) => poll(async () => { try { const blockNumber = await getAction(client, getBlockNumber, 'getBlockNumber')({ cacheTime: 0 }); if (prevBlockNumber) { // If the current block number is the same as the previous, // we can skip. if (blockNumber === prevBlockNumber) return; // If we have missed out on some previous blocks, and the // `emitMissed` flag is truthy, let's emit those blocks. if (blockNumber - prevBlockNumber > 1 && emitMissed) { for (let i = prevBlockNumber + 1n; i < blockNumber; i++) { emit.onBlockNumber(i, prevBlockNumber); prevBlockNumber = i; } } } // If the next block number is greater than the previous, // it is not in the past, and we can emit the new block number. if (!prevBlockNumber || blockNumber > prevBlockNumber) { emit.onBlockNumber(blockNumber, prevBlockNumber); prevBlockNumber = blockNumber; } } catch (err) { emit.onError?.(err); } }, { emitOnBegin, interval: pollingInterval, })); }; const subscribeBlockNumber = () => { const observerId = stringify([ 'watchBlockNumber', client.uid, emitOnBegin, emitMissed, ]); return observe(observerId, { onBlockNumber, onError }, (emit) => { let active = true; let unsubscribe = () => (active = false); (async () => { try { const transport = (() => { if (client.transport.type === 'fallback') { const transport = client.transport.transports.find((transport) => transport.config.type === 'webSocket'); if (!transport) return client.transport; return transport.value; } return client.transport; })(); const { unsubscribe: unsubscribe_ } = await transport.subscribe({ params: ['newHeads'], onData(data) { if (!active) return; const blockNumber = hexToBigInt(data.result?.number); emit.onBlockNumber(blockNumber, prevBlockNumber); prevBlockNumber = blockNumber; }, onError(error) { emit.onError?.(error); }, }); unsubscribe = unsubscribe_; if (!active) unsubscribe(); } catch (err) { onError?.(err); } })(); return () => unsubscribe(); }); }; return enablePolling ? pollBlockNumber() : subscribeBlockNumber(); } //# sourceMappingURL=watchBlockNumber.js.map