303 lines
9.0 KiB
JavaScript
303 lines
9.0 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.PollingEventSubscriber = exports.PollingTransactionSubscriber = exports.PollingOrphanSubscriber = exports.PollingBlockTagSubscriber = exports.OnBlockSubscriber = exports.PollingBlockSubscriber = exports.getPollingSubscriber = void 0;
|
|
const index_js_1 = require("../utils/index.js");
|
|
function copy(obj) {
|
|
return JSON.parse(JSON.stringify(obj));
|
|
}
|
|
/**
|
|
* Return the polling subscriber for common events.
|
|
*
|
|
* @_docloc: api/providers/abstract-provider
|
|
*/
|
|
function getPollingSubscriber(provider, event) {
|
|
if (event === "block") {
|
|
return new PollingBlockSubscriber(provider);
|
|
}
|
|
if ((0, index_js_1.isHexString)(event, 32)) {
|
|
return new PollingTransactionSubscriber(provider, event);
|
|
}
|
|
(0, index_js_1.assert)(false, "unsupported polling event", "UNSUPPORTED_OPERATION", {
|
|
operation: "getPollingSubscriber", info: { event }
|
|
});
|
|
}
|
|
exports.getPollingSubscriber = getPollingSubscriber;
|
|
// @TODO: refactor this
|
|
/**
|
|
* A **PollingBlockSubscriber** polls at a regular interval for a change
|
|
* in the block number.
|
|
*
|
|
* @_docloc: api/providers/abstract-provider
|
|
*/
|
|
class PollingBlockSubscriber {
|
|
#provider;
|
|
#poller;
|
|
#interval;
|
|
// The most recent block we have scanned for events. The value -2
|
|
// indicates we still need to fetch an initial block number
|
|
#blockNumber;
|
|
/**
|
|
* Create a new **PollingBlockSubscriber** attached to %%provider%%.
|
|
*/
|
|
constructor(provider) {
|
|
this.#provider = provider;
|
|
this.#poller = null;
|
|
this.#interval = 4000;
|
|
this.#blockNumber = -2;
|
|
}
|
|
/**
|
|
* The polling interval.
|
|
*/
|
|
get pollingInterval() { return this.#interval; }
|
|
set pollingInterval(value) { this.#interval = value; }
|
|
async #poll() {
|
|
try {
|
|
const blockNumber = await this.#provider.getBlockNumber();
|
|
// Bootstrap poll to setup our initial block number
|
|
if (this.#blockNumber === -2) {
|
|
this.#blockNumber = blockNumber;
|
|
return;
|
|
}
|
|
// @TODO: Put a cap on the maximum number of events per loop?
|
|
if (blockNumber !== this.#blockNumber) {
|
|
for (let b = this.#blockNumber + 1; b <= blockNumber; b++) {
|
|
// We have been stopped
|
|
if (this.#poller == null) {
|
|
return;
|
|
}
|
|
await this.#provider.emit("block", b);
|
|
}
|
|
this.#blockNumber = blockNumber;
|
|
}
|
|
}
|
|
catch (error) {
|
|
// @TODO: Minor bump, add an "error" event to let subscribers
|
|
// know things went awry.
|
|
//console.log(error);
|
|
}
|
|
// We have been stopped
|
|
if (this.#poller == null) {
|
|
return;
|
|
}
|
|
this.#poller = this.#provider._setTimeout(this.#poll.bind(this), this.#interval);
|
|
}
|
|
start() {
|
|
if (this.#poller) {
|
|
return;
|
|
}
|
|
this.#poller = this.#provider._setTimeout(this.#poll.bind(this), this.#interval);
|
|
this.#poll();
|
|
}
|
|
stop() {
|
|
if (!this.#poller) {
|
|
return;
|
|
}
|
|
this.#provider._clearTimeout(this.#poller);
|
|
this.#poller = null;
|
|
}
|
|
pause(dropWhilePaused) {
|
|
this.stop();
|
|
if (dropWhilePaused) {
|
|
this.#blockNumber = -2;
|
|
}
|
|
}
|
|
resume() {
|
|
this.start();
|
|
}
|
|
}
|
|
exports.PollingBlockSubscriber = PollingBlockSubscriber;
|
|
/**
|
|
* An **OnBlockSubscriber** can be sub-classed, with a [[_poll]]
|
|
* implmentation which will be called on every new block.
|
|
*
|
|
* @_docloc: api/providers/abstract-provider
|
|
*/
|
|
class OnBlockSubscriber {
|
|
#provider;
|
|
#poll;
|
|
#running;
|
|
/**
|
|
* Create a new **OnBlockSubscriber** attached to %%provider%%.
|
|
*/
|
|
constructor(provider) {
|
|
this.#provider = provider;
|
|
this.#running = false;
|
|
this.#poll = (blockNumber) => {
|
|
this._poll(blockNumber, this.#provider);
|
|
};
|
|
}
|
|
/**
|
|
* Called on every new block.
|
|
*/
|
|
async _poll(blockNumber, provider) {
|
|
throw new Error("sub-classes must override this");
|
|
}
|
|
start() {
|
|
if (this.#running) {
|
|
return;
|
|
}
|
|
this.#running = true;
|
|
this.#poll(-2);
|
|
this.#provider.on("block", this.#poll);
|
|
}
|
|
stop() {
|
|
if (!this.#running) {
|
|
return;
|
|
}
|
|
this.#running = false;
|
|
this.#provider.off("block", this.#poll);
|
|
}
|
|
pause(dropWhilePaused) { this.stop(); }
|
|
resume() { this.start(); }
|
|
}
|
|
exports.OnBlockSubscriber = OnBlockSubscriber;
|
|
class PollingBlockTagSubscriber extends OnBlockSubscriber {
|
|
#tag;
|
|
#lastBlock;
|
|
constructor(provider, tag) {
|
|
super(provider);
|
|
this.#tag = tag;
|
|
this.#lastBlock = -2;
|
|
}
|
|
pause(dropWhilePaused) {
|
|
if (dropWhilePaused) {
|
|
this.#lastBlock = -2;
|
|
}
|
|
super.pause(dropWhilePaused);
|
|
}
|
|
async _poll(blockNumber, provider) {
|
|
const block = await provider.getBlock(this.#tag);
|
|
if (block == null) {
|
|
return;
|
|
}
|
|
if (this.#lastBlock === -2) {
|
|
this.#lastBlock = block.number;
|
|
}
|
|
else if (block.number > this.#lastBlock) {
|
|
provider.emit(this.#tag, block.number);
|
|
this.#lastBlock = block.number;
|
|
}
|
|
}
|
|
}
|
|
exports.PollingBlockTagSubscriber = PollingBlockTagSubscriber;
|
|
/**
|
|
* @_ignore:
|
|
*
|
|
* @_docloc: api/providers/abstract-provider
|
|
*/
|
|
class PollingOrphanSubscriber extends OnBlockSubscriber {
|
|
#filter;
|
|
constructor(provider, filter) {
|
|
super(provider);
|
|
this.#filter = copy(filter);
|
|
}
|
|
async _poll(blockNumber, provider) {
|
|
throw new Error("@TODO");
|
|
console.log(this.#filter);
|
|
}
|
|
}
|
|
exports.PollingOrphanSubscriber = PollingOrphanSubscriber;
|
|
/**
|
|
* A **PollingTransactionSubscriber** will poll for a given transaction
|
|
* hash for its receipt.
|
|
*
|
|
* @_docloc: api/providers/abstract-provider
|
|
*/
|
|
class PollingTransactionSubscriber extends OnBlockSubscriber {
|
|
#hash;
|
|
/**
|
|
* Create a new **PollingTransactionSubscriber** attached to
|
|
* %%provider%%, listening for %%hash%%.
|
|
*/
|
|
constructor(provider, hash) {
|
|
super(provider);
|
|
this.#hash = hash;
|
|
}
|
|
async _poll(blockNumber, provider) {
|
|
const tx = await provider.getTransactionReceipt(this.#hash);
|
|
if (tx) {
|
|
provider.emit(this.#hash, tx);
|
|
}
|
|
}
|
|
}
|
|
exports.PollingTransactionSubscriber = PollingTransactionSubscriber;
|
|
/**
|
|
* A **PollingEventSubscriber** will poll for a given filter for its logs.
|
|
*
|
|
* @_docloc: api/providers/abstract-provider
|
|
*/
|
|
class PollingEventSubscriber {
|
|
#provider;
|
|
#filter;
|
|
#poller;
|
|
#running;
|
|
// The most recent block we have scanned for events. The value -2
|
|
// indicates we still need to fetch an initial block number
|
|
#blockNumber;
|
|
/**
|
|
* Create a new **PollingTransactionSubscriber** attached to
|
|
* %%provider%%, listening for %%filter%%.
|
|
*/
|
|
constructor(provider, filter) {
|
|
this.#provider = provider;
|
|
this.#filter = copy(filter);
|
|
this.#poller = this.#poll.bind(this);
|
|
this.#running = false;
|
|
this.#blockNumber = -2;
|
|
}
|
|
async #poll(blockNumber) {
|
|
// The initial block hasn't been determined yet
|
|
if (this.#blockNumber === -2) {
|
|
return;
|
|
}
|
|
const filter = copy(this.#filter);
|
|
filter.fromBlock = this.#blockNumber + 1;
|
|
filter.toBlock = blockNumber;
|
|
const logs = await this.#provider.getLogs(filter);
|
|
// No logs could just mean the node has not indexed them yet,
|
|
// so we keep a sliding window of 60 blocks to keep scanning
|
|
if (logs.length === 0) {
|
|
if (this.#blockNumber < blockNumber - 60) {
|
|
this.#blockNumber = blockNumber - 60;
|
|
}
|
|
return;
|
|
}
|
|
for (const log of logs) {
|
|
this.#provider.emit(this.#filter, log);
|
|
// Only advance the block number when logs were found to
|
|
// account for networks (like BNB and Polygon) which may
|
|
// sacrifice event consistency for block event speed
|
|
this.#blockNumber = log.blockNumber;
|
|
}
|
|
}
|
|
start() {
|
|
if (this.#running) {
|
|
return;
|
|
}
|
|
this.#running = true;
|
|
if (this.#blockNumber === -2) {
|
|
this.#provider.getBlockNumber().then((blockNumber) => {
|
|
this.#blockNumber = blockNumber;
|
|
});
|
|
}
|
|
this.#provider.on("block", this.#poller);
|
|
}
|
|
stop() {
|
|
if (!this.#running) {
|
|
return;
|
|
}
|
|
this.#running = false;
|
|
this.#provider.off("block", this.#poller);
|
|
}
|
|
pause(dropWhilePaused) {
|
|
this.stop();
|
|
if (dropWhilePaused) {
|
|
this.#blockNumber = -2;
|
|
}
|
|
}
|
|
resume() {
|
|
this.start();
|
|
}
|
|
}
|
|
exports.PollingEventSubscriber = PollingEventSubscriber;
|
|
//# sourceMappingURL=subscriber-polling.js.map
|