@kaiachain/web3js-ext
Version:
web3.js extension for kaiachain blockchain
146 lines (142 loc) • 6.49 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/send_tx_helper.ts
import { ContractExecutionError, InvalidResponseError, TransactionRevertedWithoutReasonError, TransactionRevertInstructionError, TransactionRevertWithCustomError, } from "web3-errors";
import { ALL_EVENTS_ABI, decodeEventABI, } from "web3-eth";
import { ethRpcMethods } from "web3-rpc-methods";
import { ETH_DATA_FORMAT, } from "web3-types";
import { isNullish } from "web3-validator";
import { getRevertReason } from "./get_revert_reason.js";
import { getTransactionError } from "./get_transaction_error.js";
import { getTransactionGasPricing } from "./get_transaction_gas_pricing.js";
import { trySendTransaction } from "./try_send_transaction.js";
import { watchTransactionForConfirmations } from "./watch_transaction_for_confirmations.js";
export class SendTxHelper {
constructor({ options, web3Context, promiEvent, returnFormat, }) {
this.options = {
checkRevertBeforeSending: true,
};
this.options = options;
this.web3Context = web3Context;
this.promiEvent = promiEvent;
this.returnFormat = returnFormat;
}
getReceiptWithEvents(data) {
const result = { ...(data ?? {}) };
if (this.options?.contractAbi && result.logs && result.logs.length > 0) {
result.events = {};
for (const log of result.logs) {
const event = decodeEventABI(ALL_EVENTS_ABI, log, this.options?.contractAbi, this.returnFormat);
if (event.event) {
result.events[event.event] = event;
}
}
}
return result;
}
async checkRevertBeforeSending(tx) {
if (this.options.checkRevertBeforeSending !== false) {
const reason = await getRevertReason(this.web3Context, tx, this.options.contractAbi);
if (reason !== undefined) {
throw await getTransactionError(this.web3Context, tx, undefined, undefined, this.options.contractAbi, reason);
}
}
}
emitSending(tx) {
if (this.promiEvent.listenerCount("sending") > 0) {
// @ts-ignore: web3.js has the same error
this.promiEvent.emit("sending", tx);
}
}
async populateGasPrice({ transactionFormatted, transaction, }) {
let result = transactionFormatted;
if (!this.options?.ignoreGasPricing &&
isNullish(transactionFormatted.gasPrice) &&
(isNullish(transaction.maxPriorityFeePerGas) ||
isNullish(transaction.maxFeePerGas))) {
result = {
...transactionFormatted,
// TODO gasPrice, maxPriorityFeePerGas, maxFeePerGas
// should not be included if undefined, but currently are
...(await getTransactionGasPricing(
// @ts-ignore: web3.js has the same error
transactionFormatted, this.web3Context, ETH_DATA_FORMAT)),
};
}
return result;
}
async signAndSend({ wallet, tx, }) {
if (wallet) {
// @ts-ignore: web3.js has the same error
const signedTransaction = await wallet.signTransaction(tx);
return trySendTransaction(this.web3Context, async () => ethRpcMethods.sendRawTransaction(this.web3Context.requestManager, signedTransaction.rawTransaction), signedTransaction.transactionHash);
}
return trySendTransaction(this.web3Context, async () => ethRpcMethods.sendTransaction(this.web3Context.requestManager, tx));
}
emitSent(tx) {
if (this.promiEvent.listenerCount("sent") > 0) {
// @ts-ignore: web3.js has the same error
this.promiEvent.emit("sent", tx);
}
}
emitTransactionHash(hash) {
if (this.promiEvent.listenerCount("transactionHash") > 0) {
this.promiEvent.emit("transactionHash", hash);
}
}
emitReceipt(receipt) {
if (this.promiEvent.listenerCount("receipt") > 0) {
this.promiEvent.emit("receipt",
// @ts-expect-error unknown type fix
receipt);
}
}
async handleError({ error, tx }) {
let _error = error;
if (_error instanceof ContractExecutionError && this.web3Context.handleRevert) {
_error = await getTransactionError(this.web3Context, tx, undefined, undefined, this.options?.contractAbi);
}
if ((_error instanceof InvalidResponseError ||
_error instanceof ContractExecutionError ||
_error instanceof TransactionRevertWithCustomError ||
_error instanceof TransactionRevertedWithoutReasonError ||
_error instanceof TransactionRevertInstructionError) &&
this.promiEvent.listenerCount("error") > 0) {
this.promiEvent.emit("error", _error);
}
return _error;
}
emitConfirmation({ receipt, transactionHash, }) {
if (this.promiEvent.listenerCount("confirmation") > 0) {
watchTransactionForConfirmations(this.web3Context, this.promiEvent, receipt, transactionHash, this.returnFormat);
}
}
async handleResolve({ receipt, tx }) {
if (this.options?.transactionResolver) {
return this.options?.transactionResolver(receipt);
}
if (receipt.status === BigInt(0)) {
const error = await getTransactionError(this.web3Context, tx,
// @ts-ignore: web3.js has the same error
receipt, undefined, this.options?.contractAbi);
if (this.promiEvent.listenerCount("error") > 0) {
this.promiEvent.emit("error", error);
}
throw error;
}
else {
return receipt;
}
}
}