import { ethers, PopulatedTransaction } from 'ethers'; import { TransactionReceipt } from '@ethersproject/abstract-provider'; import { TransactionMetadata } from '../types'; export class TransactionManagerRequest { contract: ethers.Contract; method: string; args: Array; overrides: ethers.providers.TransactionRequest; chainId: number; gasMultiplier?: number; metaData?: TransactionMetadata; constructor( contract: ethers.Contract, method: string, args: Array, overrides: ethers.providers.TransactionRequest, chainId: number, gasMultiplier?: number, metaData?: TransactionMetadata, ) { this.contract = contract; this.method = method; this.args = args; this.overrides = overrides; this.chainId = chainId; this.gasMultiplier = gasMultiplier; this.metaData = metaData; } // add factory method to initialize transaction manager request static newTransactionManagerRequest( contract: ethers.Contract, method: string, args: Array, overrides: ethers.providers.TransactionRequest, chainId: number, gasMultiplier?: number, metadata?: TransactionMetadata, ): TransactionManagerRequest { return new TransactionManagerRequest( contract, method, args, overrides, chainId, gasMultiplier, metadata, ); } // get transaction async getTransaction(): Promise { // get unsigned transaction const unsignedTx: PopulatedTransaction = await this.contract.populateTransaction[ this.method ](...this.args, this.overrides); // set chainId in unsigned transaction unsignedTx.chainId = this.chainId; return unsignedTx; } async callStaticTransaction( transaction: PopulatedTransaction, ): Promise { // "data","to","chainId" const request: ethers.providers.TransactionRequest = { from: transaction.from, nonce: transaction.nonce, gasLimit: transaction.gasLimit, gasPrice: transaction.gasPrice, value: transaction.value, type: transaction.type, maxPriorityFeePerGas: transaction.maxPriorityFeePerGas, maxFeePerGas: transaction.maxFeePerGas, }; // get unsigned transaction const result = await this.contract.callStatic[this.method]( ...this.args, request, ); return result; } async ethCallOnTransaction( transaction: PopulatedTransaction, provider: ethers.providers.Provider, ): Promise { // "data","to","chainId" const result = await provider.call(transaction); const functionName = this.contract.interface.getFunction(this.method); const decodedResult: ethers.utils.Result = this.contract.interface.decodeFunctionResult( functionName, result, ); return decodedResult; } /** * * @param transactionReceipt Transaction Receipt parse event logs * @returns */ private parseEventLogs(transactionReceipt: TransactionReceipt): Array { const logs = transactionReceipt?.logs || []; // parse logs return logs.map((log) => { // There is a possibility that the log is not related to the contract // we are trying to parse. In that case, we will get an error. // We will just ignore those logs. try { return this.contract.interface.parseLog(log); } catch (e) { return { name: 'Unknown', values: {}, log: log, }; } }); } /** * * @param transactionReceipt Transaction Receipt for transaction * @returns */ public isTransactionFailed(transactionReceipt?: TransactionReceipt): boolean { if (transactionReceipt) { const logs = this.parseEventLogs(transactionReceipt); const failedEventName = this.metaData?.failedEventName; if (failedEventName) { const failedEvent = logs.find((log) => log.name === failedEventName); return failedEvent ? true : false; } return false; } return true; } }