import { ArbL2ToL1HookConfig, HookType, HyperlaneCore, IsmType, MerkleTreeHookConfig, MultiProvider, } from '@hyperlane-xyz/sdk'; import { WithAddress, assert, deepFind, rootLogger, } from '@hyperlane-xyz/utils'; import { AggregationMetadataBuilder } from './aggregation.js'; import { ArbL2ToL1MetadataBuilder } from './arbL2ToL1.js'; import { OffchainLookupMetadataBuilder } from './ccipread.js'; import { decodeIsmMetadata } from './decode.js'; import { MultisigMetadataBuilder } from './multisig.js'; import { NullMetadataBuilder } from './null.js'; import { DynamicRoutingMetadataBuilder } from './routing.js'; import type { MetadataBuildResult, MetadataBuilder, MetadataContext, StructuredMetadata, } from './types.js'; export class BaseMetadataBuilder implements MetadataBuilder { public nullMetadataBuilder: NullMetadataBuilder; public multisigMetadataBuilder: MultisigMetadataBuilder; public aggregationMetadataBuilder: AggregationMetadataBuilder; public routingMetadataBuilder: DynamicRoutingMetadataBuilder; public arbL2ToL1MetadataBuilder: ArbL2ToL1MetadataBuilder; public ccipReadMetadataBuilder: OffchainLookupMetadataBuilder; public multiProvider: MultiProvider; protected logger = rootLogger.child({ module: 'BaseMetadataBuilder' }); constructor(core: HyperlaneCore) { this.multisigMetadataBuilder = new MultisigMetadataBuilder(core); this.aggregationMetadataBuilder = new AggregationMetadataBuilder(this); this.routingMetadataBuilder = new DynamicRoutingMetadataBuilder(this); this.nullMetadataBuilder = new NullMetadataBuilder(core.multiProvider); this.arbL2ToL1MetadataBuilder = new ArbL2ToL1MetadataBuilder(core); this.ccipReadMetadataBuilder = new OffchainLookupMetadataBuilder(core); this.multiProvider = core.multiProvider; } // assumes that all post dispatch hooks are included in dispatchTx logs async build( context: MetadataContext, maxDepth = 10, ): Promise { this.logger.debug( { context, maxDepth }, `Building ${context.ism.type} metadata`, ); assert(maxDepth > 0, 'Max depth reached'); const { ism, hook } = context; switch (ism.type) { case IsmType.TRUSTED_RELAYER: case IsmType.TEST_ISM: case IsmType.OP_STACK: case IsmType.PAUSABLE: case IsmType.CCIP: case IsmType.RATE_LIMITED: case IsmType.BLACKLIST: case IsmType.NET_FLOW_RATE_LIMITED: case IsmType.DELAYED_FLOW_ROUTER: return this.nullMetadataBuilder.build({ ...context, ism }); case IsmType.MERKLE_ROOT_MULTISIG: case IsmType.MESSAGE_ID_MULTISIG: case IsmType.STORAGE_MERKLE_ROOT_MULTISIG: case IsmType.STORAGE_MESSAGE_ID_MULTISIG: if (typeof hook === 'string') { throw new Error('Hook context must be an object (for multisig ISM)'); } // eslint-disable-next-line no-case-declarations const merkleTreeHook = deepFind( hook, (v): v is WithAddress => v.type === HookType.MERKLE_TREE && !!v.address, ); assert(merkleTreeHook, 'Merkle tree hook context not found'); return this.multisigMetadataBuilder.build({ ...context, ism, hook: merkleTreeHook, }); case IsmType.ROUTING: case IsmType.FALLBACK_ROUTING: case IsmType.AMOUNT_ROUTING: case IsmType.INTERCHAIN_ACCOUNT_ROUTING: case IsmType.MAILBOX_DEFAULT: return this.routingMetadataBuilder.build( { ...context, ism, }, maxDepth, ); case IsmType.AGGREGATION: case IsmType.STORAGE_AGGREGATION: return this.aggregationMetadataBuilder.build( { ...context, ism }, maxDepth, ); case IsmType.ARB_L2_TO_L1: { const hookConfig = hook as WithAddress; return this.arbL2ToL1MetadataBuilder.build({ ...context, ism, hook: hookConfig, }); } case IsmType.OFFCHAIN_LOOKUP: return this.ccipReadMetadataBuilder.build({ ...context, ism, }); default: throw new Error(`Unsupported ISM: ${JSON.stringify(ism)}`); } } static decode( metadata: string, context: MetadataContext, ): StructuredMetadata { return decodeIsmMetadata(metadata, context); } }