UNPKG

evm-blockchain-tools

Version:

This is a collection of resuseable tools to support development for EVM-powered blockchains

118 lines 6.09 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BlockchainTransactionRegistry = void 0; const pino_1 = __importDefault(require("pino")); const mvc_common_toolkit_1 = require("mvc-common-toolkit"); const ethers_1 = require("ethers"); const constants_1 = require("../common/constants"); const services_1 = require("../services"); const utils_1 = require("../utils"); class BlockchainTransactionRegistry { constructor(transactionStorage, signerPicker, taskQueue) { this.transactionStorage = transactionStorage; this.taskQueue = taskQueue; this.logger = (0, pino_1.default)(); this.signerPicker = signerPicker || new services_1.RoundRobinSignerPicker(transactionStorage); } sendContractTransaction(contract, method, params, options) { return __awaiter(this, void 0, void 0, function* () { const signerPicker = (options === null || options === void 0 ? void 0 : options.signerPicker) || this.signerPicker; const pickedSigner = yield signerPicker.pick(contract.address, contract.signerList); const signerAddress = yield pickedSigner.getAddress(); const queueName = `signer_queue_${signerAddress.toLowerCase()}`; const taskName = `exec_${method}`; return this.taskQueue.push(queueName, taskName, () => __awaiter(this, void 0, void 0, function* () { let generatedTxHash; try { const { isOverride, nextNonce } = yield this.reconcileSignerLastTx(pickedSigner, signerAddress); const gasPrice = yield (0, utils_1.getOptimizedGasPriceV2)(pickedSigner.provider, constants_1.AdditionalGas.toString(), (options === null || options === void 0 ? void 0 : options.minGas) || constants_1.MinGas.toString(), { useOverridingGas: isOverride, }); const { signedTransaction, txHash } = yield contract.generateTransaction({ data: params, functionName: method, nonce: nextNonce, }, { signer: pickedSigner, gasPrice, }); yield this.transactionStorage.create({ nonce: nextNonce, txHash, signedTransaction, signerAddress, status: constants_1.TransactionStatus.SCHEDULED, metadata: { isOverride, }, }); generatedTxHash = txHash; if (!contract.provider.sendTransaction) { throw new Error("provider has no sendTransaction method"); } yield mvc_common_toolkit_1.timeoutHelper.runWithTimeout(() => __awaiter(this, void 0, void 0, function* () { const submittedTx = yield pickedSigner.provider.sendTransaction(signedTransaction); yield submittedTx.wait(1); yield this.transactionStorage.updateByTxHash(txHash, { status: "executed", }); }), (options === null || options === void 0 ? void 0 : options.timeoutInMs) || 30000); } catch (error) { this.logger.error(error.message, error.stack); if (generatedTxHash) { yield this.transactionStorage.updateByTxHash(generatedTxHash, { status: constants_1.TransactionStatus.FAILED, metadata: error, }); } } })); }); } reconcileSignerLastTx(signer, signerAddress) { return __awaiter(this, void 0, void 0, function* () { const lastTx = yield this.transactionStorage.findSignerLastTransaction(signerAddress); if (!lastTx) { const count = yield signer.getTransactionCount(); return { nextNonce: String(count), isOverride: false, }; } const { txHash, status, nonce } = lastTx; if (status === constants_1.TransactionStatus.SCHEDULED) { throw new Error(`txHash ${txHash} for signer ${signerAddress} is still pending`); } if (status === constants_1.TransactionStatus.FAILED) { const foundTx = yield signer.provider.getTransaction(txHash); if (!foundTx) { return { nextNonce: nonce, isOverride: true, }; } } const nextNonce = ethers_1.BigNumber.from(nonce).add(1).toString(); return { nextNonce, isOverride: false, }; }); } } exports.BlockchainTransactionRegistry = BlockchainTransactionRegistry; //# sourceMappingURL=blockchain-transaction-registry.js.map