@kaiachain/web3js-ext
Version:
web3.js extension for kaiachain blockchain
139 lines (135 loc) • 6.37 kB
JavaScript
;
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
// Taken from https://github.com/web3/web3.js/blob/v4.3.0/packages/web3-eth/src/utils/reject_if_block_timeout.ts
Object.defineProperty(exports, "__esModule", { value: true });
exports.rejectIfBlockTimeout = rejectIfBlockTimeout;
const web3_errors_1 = require("web3-errors");
const web3_eth_1 = require("web3-eth");
const web3_utils_1 = require("web3-utils");
function resolveByPolling(web3Context, starterBlockNumber, transactionHash) {
const pollingInterval = web3Context.transactionPollingInterval;
const [intervalId, promiseToError] = (0, web3_utils_1.rejectIfConditionAtInterval)(async () => {
let lastBlockNumber;
try {
lastBlockNumber = await (0, web3_eth_1.getBlockNumber)(web3Context, web3_eth_1.NUMBER_DATA_FORMAT);
}
catch (error) {
console.warn("An error happen while trying to get the block number", error);
return undefined;
}
const numberOfBlocks = lastBlockNumber - starterBlockNumber;
if (numberOfBlocks >= web3Context.transactionBlockTimeout) {
return new web3_errors_1.TransactionBlockTimeoutError({
starterBlockNumber,
numberOfBlocks,
transactionHash,
});
}
return undefined;
}, pollingInterval);
const clean = () => {
clearInterval(intervalId);
};
return [promiseToError, { clean }];
}
async function resolveBySubscription(web3Context, starterBlockNumber, transactionHash) {
// The following variable will stay true except if the data arrived,
// or if watching started after an error had occurred.
let needToWatchLater = true;
let subscription;
let resourceCleaner;
// internal helper function
function revertToPolling(reject, previousError) {
if (previousError) {
console.warn("error happened at subscription. So revert to polling...", previousError);
}
resourceCleaner.clean();
needToWatchLater = false;
const [promiseToError, newResourceCleaner] = resolveByPolling(web3Context, starterBlockNumber, transactionHash);
resourceCleaner.clean = newResourceCleaner.clean;
promiseToError.catch((error) => reject(error));
}
try {
subscription = (await web3Context.subscriptionManager?.subscribe("newHeads"));
resourceCleaner = {
clean: () => {
// Remove the subscription, if it was not removed somewhere
// else by calling, for example, subscriptionManager.clear()
if (subscription.id) {
web3Context.subscriptionManager
?.removeSubscription(subscription)
.then(() => {
// Subscription ended successfully
})
.catch(() => {
// An error happened while ending subscription. But no need to take any action.
});
}
},
};
}
catch (error) {
return resolveByPolling(web3Context, starterBlockNumber, transactionHash);
}
const promiseToError = new Promise((_, reject) => {
try {
subscription.on("data", (lastBlockHeader) => {
needToWatchLater = false;
if (!lastBlockHeader?.number) {
return;
}
const numberOfBlocks = Number(BigInt(lastBlockHeader.number) - BigInt(starterBlockNumber));
if (numberOfBlocks >= web3Context.transactionBlockTimeout) {
// Transaction Block Timeout is known to be reached by subscribing to new heads
reject(new web3_errors_1.TransactionBlockTimeoutError({
starterBlockNumber,
numberOfBlocks,
transactionHash,
}));
}
});
subscription.on("error", (error) => {
revertToPolling(reject, error);
});
}
catch (error) {
revertToPolling(reject, error);
}
// Fallback to polling if tx receipt didn't arrived in "blockHeaderTimeout" [10 seconds]
setTimeout(() => {
if (needToWatchLater) {
revertToPolling(reject);
}
}, web3Context.blockHeaderTimeout * 1000);
});
return [promiseToError, resourceCleaner];
}
/* TODO: After merge, there will be constant block mining time (exactly 12 second each block, except slot missed that currently happens in <1% of slots. ) so we can optimize following function
for POS NWs, we can skip checking getBlockNumber(); after interval and calculate only based on time that certain num of blocked are mined after that for internal double check, can do one getBlockNumber() call and timeout.
*/
async function rejectIfBlockTimeout(web3Context, transactionHash) {
const { provider } = web3Context.requestManager;
let callingRes;
const starterBlockNumber = await (0, web3_eth_1.getBlockNumber)(web3Context, web3_eth_1.NUMBER_DATA_FORMAT);
// TODO: once https://github.com/web3/web3.js/issues/5521 is implemented, remove checking for `enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout`
if (provider.supportsSubscriptions?.() &&
web3Context.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout) {
callingRes = await resolveBySubscription(web3Context, starterBlockNumber, transactionHash);
}
else {
callingRes = resolveByPolling(web3Context, starterBlockNumber, transactionHash);
}
return callingRes;
}