diff --git a/typescript/infra/scripts/safes/parse-multicall.ts b/typescript/infra/scripts/safes/parse-multicall.ts index aaa671ee8..b81132e08 100644 --- a/typescript/infra/scripts/safes/parse-multicall.ts +++ b/typescript/infra/scripts/safes/parse-multicall.ts @@ -1,6 +1,8 @@ +import { normalizeConfig } from '@hyperlane-xyz/sdk'; import { stringifyObject, strip0x } from '@hyperlane-xyz/utils'; import { TransactionReader } from '../../src/tx/transaction-reader.js'; +import { readYaml } from '../../src/utils/utils.js'; import { getArgs } from '../agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; @@ -12,17 +14,32 @@ async function main() { const multiProvider = await config.getMultiProvider(); const { chainAddresses } = await getHyperlaneCore(environment, multiProvider); + // const yaml: any = readYaml('/Users/trevor/example-mismatch.yaml'); + // const mismatch = yaml[0]; + // const normalizedDerived = normalizeConfig(mismatch.derivedConfig); + // const normalizedExpected = normalizeConfig(mismatch.expectedIsmConfig); + // console.log('\n\n\n\n\n\nDerived:', stringifyObject(normalizedDerived, 'yaml', 2)); + // console.log('\n\n\n\n\n\nExpected:', stringifyObject(normalizedExpected, 'yaml', 2)); + + // return; + const reader = new TransactionReader( environment, multiProvider, 'ethereum', chainAddresses, + config.core, ); const results = await reader.read('ethereum', tx); - console.log('results', results); - console.log(stringifyObject(results, 'yaml', 2)); + + if (reader.errors.length) { + console.error('❌❌❌❌❌ Encountered fatal errors ❌❌❌❌❌'); + console.log(stringifyObject(reader.errors, 'yaml', 2)); + console.error('❌❌❌❌❌ Encountered fatal errors ❌❌❌❌❌'); + process.exit(1); + } } main().catch((err) => { diff --git a/typescript/infra/src/tx/transaction-reader.ts b/typescript/infra/src/tx/transaction-reader.ts index 34bd87a67..aa87690aa 100644 --- a/typescript/infra/src/tx/transaction-reader.ts +++ b/typescript/infra/src/tx/transaction-reader.ts @@ -4,20 +4,26 @@ import { MetaTransactionData, OperationType, } from '@safe-global/safe-core-sdk-types'; +import { deepEqual } from 'assert'; import { BigNumber, ethers } from 'ethers'; import { AnnotatedEV5Transaction, ChainMap, ChainName, + CoreConfig, + EvmIsmReader, HyperlaneReader, InterchainAccount, MultiProvider, + coreFactories, interchainAccountFactories, + normalizeConfig, } from '@hyperlane-xyz/sdk'; import { addressToBytes32, bytes32ToAddress, + deepEquals, eqAddress, retryAsync, sleep, @@ -60,6 +66,7 @@ export class TransactionReader extends HyperlaneReader { readonly multiProvider: MultiProvider, readonly chain: ChainName, readonly chainAddresses: ChainMap>, + readonly coreConfig: ChainMap, ) { super(multiProvider, chain); } @@ -74,11 +81,16 @@ export class TransactionReader extends HyperlaneReader { } async doRead(chain: ChainName, tx: AnnotatedEV5Transaction): Promise { - // If it's an ICA + // If it's to an ICA if (this.isIcaTransaction(chain, tx)) { return this.readIcaTransaction(chain, tx); } + // If it's to a Mailbox + if (this.isMailboxTransaction(chain, tx)) { + return this.readMailboxTransaction(chain, tx); + } + if (await this.isMultisendTransaction(chain, tx)) { return this.readMultisendTransaction(chain, tx); } @@ -163,6 +175,88 @@ export class TransactionReader extends HyperlaneReader { }); } + private async readMailboxTransaction( + chain: ChainName, + tx: AnnotatedEV5Transaction, + ): Promise { + if (!tx.data) { + console.log('No data in mailbox transaction'); + return undefined; + } + const { symbol } = await this.multiProvider.getNativeToken(chain); + const decoded = coreFactories.mailbox.interface.parseTransaction({ + data: tx.data, + value: tx.value, + }); + + const args = formatFunctionFragmentArgs( + decoded.args, + decoded.functionFragment, + ); + let prettyArgs = args; + if (decoded.functionFragment.name === 'setDefaultIsm') { + prettyArgs = await this.formatMailboxSetDefaultIsm(chain, args); + } + + return { + signature: decoded.signature, + args: prettyArgs, + }; + } + + ismDerivationsInProgress: ChainMap = {}; + + private async formatMailboxSetDefaultIsm( + chain: ChainName, + args: Record, + ): Promise { + const { _module: module } = args; + + const reader = new EvmIsmReader(this.multiProvider, chain); + const startTime = Date.now(); + console.log('Deriving ISM config...', chain); + this.ismDerivationsInProgress[chain] = true; + const derivedConfig = await reader.deriveIsmConfig(module); + delete this.ismDerivationsInProgress[chain]; + console.log( + 'Finished deriving ISM config', + chain, + 'in', + (Date.now() - startTime) / (1000 * 60), + 'mins', + ); + const remainingInProgress = Object.keys(this.ismDerivationsInProgress); + console.log( + 'Remaining derivations in progress:', + remainingInProgress.length, + 'chains', + remainingInProgress, + ); + const expectedIsmConfig = this.coreConfig[chain].defaultIsm; + + let insight = '✅ matches expected ISM config'; + if ( + !deepEquals( + normalizeConfig(derivedConfig), + normalizeConfig(expectedIsmConfig), + ) + ) { + this.errors.push({ + chain: chain, + module, + derivedConfig, + expectedIsmConfig, + info: 'Incorrect default ISM being set', + }); + insight = `❌ fatal mismatch of ISM config`; + } + + return { + module, + insight, + }; + } + private async readIcaCall( chain: ChainName, args: Record, @@ -334,6 +428,13 @@ export class TransactionReader extends HyperlaneReader { ); } + isMailboxTransaction(chain: ChainName, tx: AnnotatedEV5Transaction): boolean { + return ( + tx.to !== undefined && + eqAddress(tx.to, this.chainAddresses[chain].mailbox) + ); + } + async isMultisendTransaction( chain: ChainName, tx: AnnotatedEV5Transaction, @@ -362,8 +463,7 @@ export class TransactionReader extends HyperlaneReader { } const safe = safes[chain]; - - if (safe) { + if (!safe) { return undefined; }