evm-blockchain-tools
Version:
This is a collection of resuseable tools to support development for EVM-powered blockchains
118 lines • 6.09 kB
JavaScript
;
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