UNPKG

@graphprotocol/toolshed

Version:

A collection of tools and utilities for the Graph Protocol Typescript components

109 lines (94 loc) 3.17 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import type { Contract, ContractMethod, ContractMethodArgs, ContractRunner, ContractTransactionReceipt, ContractTransactionResponse, } from 'ethers' import fs from 'fs' import { logTxLogging } from '../lib/logger' /** * Wraps contract calls with a modified call function that logs the tx details * Also intercepts connect calls and wraps the returned contract with this function * * @remarks * The overriden functions will: * 1. Make the contract call * 2. Wait for tx confirmation * 3. Log the tx details and the receipt details, both to the console and to a file * * @param contract Contract to be wrapped * @param contractName Name of the contract * @returns the wrapped contract */ export function wrapTransactionCalls<T extends Contract>(contract: T, contractName: string): T { return new Proxy(contract, { get(target, prop) { const orig = Reflect.get(target, prop) // Intercept connect calls if (prop === 'connect') { return (runner: ContractRunner) => { const connected = orig.call(target, runner) as unknown as Contract return wrapTransactionCalls(connected, contractName) } } // Only intercept function calls if (typeof orig !== 'function') { return orig } // Only intercept function calls from the ABI let fn: ContractMethod<any[], any, any> | undefined try { fn = contract.getFunction(String(prop)) } catch (_) { return orig } // Only intercept state changing calls - aka transactions const fragment = fn.fragment if (['view', 'pure'].includes(fragment.stateMutability)) { return orig } // Finally, this is a transaction call so intercept it :D return async (...args: unknown[]) => { // Make the call const response = (await orig.apply(target, args)) as ContractTransactionResponse logContractTransaction(response, contractName, String(prop), args) // And wait for confirmation const receipt = await response.wait() if (receipt) { logContractTransactionReceipt(receipt) } return response } }, }) } function logContractTransaction( tx: ContractTransactionResponse, contractName: string, fn: string, args: ContractMethodArgs<any>, ) { const msg: string[] = [] msg.push(`> Sending transaction: ${contractName}.${fn}`) msg.push(` = Sender: ${tx.from}`) msg.push(` = Contract: ${tx.to}`) msg.push(` = Params: [ ${args.join(', ')} ]`) msg.push(` = TxHash: ${tx.hash}`) logToConsoleAndFile(msg) } function logContractTransactionReceipt(receipt: ContractTransactionReceipt) { const msg: string[] = [] msg.push(receipt.status ? ` ✔ Transaction succeeded!` : ` ✖ Transaction failed!`) logToConsoleAndFile(msg) } function logToConsoleAndFile(msg: string[]) { const isoDate = new Date().toISOString() const fileName = `tx-${isoDate.substring(0, 10)}.log` msg.map((line) => { logTxLogging(line) fs.appendFileSync(fileName, `[${isoDate}] ${line}\n`) }) }