diff --git a/typescript/infra/scripts/safes/parse-txs.ts b/typescript/infra/scripts/safes/parse-txs.ts index e0a850b80..0d4a2947e 100644 --- a/typescript/infra/scripts/safes/parse-txs.ts +++ b/typescript/infra/scripts/safes/parse-txs.ts @@ -3,7 +3,7 @@ import { BigNumber } from 'ethers'; import { AnnotatedEV5Transaction } from '@hyperlane-xyz/sdk'; import { stringifyObject } from '@hyperlane-xyz/utils'; -import { TransactionReader } from '../../src/tx/transaction-reader.js'; +import { GovernTransactionReader } from '../../src/tx/govern-transaction-reader.js'; import { getSafeTx } from '../../src/utils/safe.js'; import { getArgs, @@ -22,7 +22,7 @@ async function main() { const multiProvider = await config.getMultiProvider(); const { chainAddresses } = await getHyperlaneCore(environment, multiProvider); - const reader = new TransactionReader( + const reader = new GovernTransactionReader( environment, multiProvider, chainAddresses, @@ -40,9 +40,14 @@ async function main() { value: BigNumber.from(safeTx.value), }; - const results = await reader.read(chain, tx); - console.log(`Finished reading tx ${txHash} on ${chain}`); - return [chain, results]; + try { + const results = await reader.read(chain, tx); + console.log(`Finished reading tx ${txHash} on ${chain}`); + return [chain, results]; + } catch (err) { + console.error('Error reading transaction', err, chain, tx); + process.exit(1); + } }), ); diff --git a/typescript/infra/src/tx/transaction-reader.ts b/typescript/infra/src/tx/govern-transaction-reader.ts similarity index 87% rename from typescript/infra/src/tx/transaction-reader.ts rename to typescript/infra/src/tx/govern-transaction-reader.ts index e03d1da61..871b3c978 100644 --- a/typescript/infra/src/tx/transaction-reader.ts +++ b/typescript/infra/src/tx/govern-transaction-reader.ts @@ -35,7 +35,47 @@ import { import { DeployEnvironment } from '../config/environment.js'; import { getSafeAndService } from '../utils/safe.js'; -export class TransactionReader { +interface GovernTransaction extends Record { + chain: ChainName; +} + +interface MultiSendTransaction { + index: number; + value: string; + operation: string; + decoded: GovernTransaction; +} + +interface MultiSendGovernTransactions extends GovernTransaction { + multisends: MultiSendTransaction[]; +} + +interface SetDefaultIsmInsight { + module: string; + insight: string; +} + +interface IcaRemoteCallInsight { + destination: { + domain: number; + chain: ChainName; + }; + router: { + address: string; + insight: string; + }; + ism: { + address: string; + insight: string; + }; + destinationIca: { + address: string; + insight: string; + }; + calls: GovernTransaction[]; +} + +export class GovernTransactionReader { errors: any[] = []; constructor( @@ -45,16 +85,10 @@ export class TransactionReader { readonly coreConfig: ChainMap, ) {} - async read(chain: ChainName, tx: AnnotatedEV5Transaction): Promise { - try { - return await this.doRead(chain, tx); - } catch (e) { - console.error('Error reading transaction', e, chain, tx); - throw e; - } - } - - async doRead(chain: ChainName, tx: AnnotatedEV5Transaction): Promise { + async read( + chain: ChainName, + tx: AnnotatedEV5Transaction, + ): Promise { // If it's to an ICA if (this.isIcaTransaction(chain, tx)) { return this.readIcaTransaction(chain, tx); @@ -78,6 +112,7 @@ export class TransactionReader { }); return { + chain, insight, tx, }; @@ -86,10 +121,9 @@ export class TransactionReader { private async readIcaTransaction( chain: ChainName, tx: AnnotatedEV5Transaction, - ): Promise { + ): Promise { if (!tx.data) { - console.log('No data in ICA transaction'); - return undefined; + throw new Error('No data in ICA transaction'); } const { symbol } = await this.multiProvider.getNativeToken(chain); const icaInterface = @@ -120,7 +154,7 @@ export class TransactionReader { 'callRemoteWithOverrides(uint32,bytes32,bytes32,(bytes32,uint256,bytes)[])' ].name ) { - prettyArgs = await this.readIcaCall(chain, args); + prettyArgs = await this.readIcaRemoteCall(chain, args); } return { @@ -128,6 +162,7 @@ export class TransactionReader { value: `${ethers.utils.formatEther(decoded.value)} ${symbol}`, signature: decoded.signature, args: prettyArgs, + chain, }; } @@ -135,7 +170,7 @@ export class TransactionReader { chain: ChainName, routerName: string, args: Record, - ): Promise { + ): Promise { const { _domains: domains, _addresses: addresses } = args; return domains.map((domain: number, index: number) => { const remoteChainName = this.multiProvider.getChainName(domain); @@ -172,10 +207,9 @@ export class TransactionReader { private async readMailboxTransaction( chain: ChainName, tx: AnnotatedEV5Transaction, - ): Promise { + ): Promise { if (!tx.data) { - console.log('⚠️ No data in mailbox transaction'); - return undefined; + throw new Error('⚠️ No data in mailbox transaction'); } const mailboxInterface = coreFactories.mailbox.interface; const decoded = mailboxInterface.parseTransaction({ @@ -196,13 +230,14 @@ export class TransactionReader { } return { + chain, to: `Mailbox (${chain} ${this.chainAddresses[chain].mailbox})`, signature: decoded.signature, args: prettyArgs, }; } - ismDerivationsInProgress: ChainMap = {}; + private ismDerivationsInProgress: ChainMap = {}; private async deriveIsmConfig( chain: string, @@ -240,7 +275,7 @@ export class TransactionReader { private async formatMailboxSetDefaultIsm( chain: ChainName, args: Record, - ): Promise { + ): Promise { const { _module: module } = args; const derivedConfig = this.deriveIsmConfig(chain, module); @@ -272,10 +307,10 @@ export class TransactionReader { }; } - private async readIcaCall( + private async readIcaRemoteCall( chain: ChainName, args: Record, - ): Promise { + ): Promise { const { _destination: destination, _router: router, @@ -355,7 +390,7 @@ export class TransactionReader { return { destination: { domain: destination, - chainName: remoteChainName, + chain: remoteChainName, }, router: { address: router, @@ -376,22 +411,22 @@ export class TransactionReader { private async readMultisendTransaction( chain: ChainName, tx: AnnotatedEV5Transaction, - ): Promise { + ): Promise { if (!tx.data) { - console.log('No data in multisend transaction'); - return undefined; + throw new Error('No data in multisend transaction'); } - const multisends = decodeMultiSendData(tx.data); + const multisendDatas = decodeMultiSendData(tx.data); const { symbol } = await this.multiProvider.getNativeToken(chain); - return Promise.all( - multisends.map(async (multisend, index) => { + const multisends = await Promise.all( + multisendDatas.map(async (multisend, index) => { const decoded = await this.read( chain, metaTransactionDataToEV5Transaction(multisend), ); return { + chain, index, value: `${ethers.utils.formatEther(multisend.value)} ${symbol}`, operation: formatOperationType(multisend.operation), @@ -399,6 +434,11 @@ export class TransactionReader { }; }), ); + + return { + chain, + multisends, + }; } isIcaTransaction(chain: ChainName, tx: AnnotatedEV5Transaction): boolean { @@ -432,7 +472,7 @@ export class TransactionReader { return eqAddress(multiSendCallOnlyAddress, tx.to); } - multiSendCallOnlyAddressCache: ChainMap = {}; + private multiSendCallOnlyAddressCache: ChainMap = {}; async getMultiSendCallOnlyAddress( chain: ChainName, @@ -469,8 +509,8 @@ function metaTransactionDataToEV5Transaction( function formatFunctionFragmentArgs( args: Result, fragment: ethers.utils.FunctionFragment, -): Record { - const accumulator: Record = {}; +): Record { + const accumulator: Record = {}; return fragment.inputs.reduce((acc, input, index) => { acc[input.name] = args[index]; return acc;