viem
Version:
131 lines • 5.7 kB
JavaScript
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