diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts index be9e607c8..6b10e5ffd 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts @@ -113,8 +113,14 @@ export class HyperlaneIsmFactory extends HyperlaneApp { .deploy(); await this.multiProvider.handleTx(chain, multisig.deployTransaction); const originDomain = this.multiProvider.getDomainId(origin!); - await multisig.enrollValidators([originDomain], [config.validators]); - await multisig.setThreshold(originDomain, config.threshold); + await this.multiProvider.handleTx( + chain, + multisig.enrollValidators([originDomain], [config.validators]), + ); + await this.multiProvider.handleTx( + chain, + multisig.setThreshold(originDomain, config.threshold), + ); address = multisig.address; } else { const multisigIsmFactory = @@ -163,7 +169,10 @@ export class HyperlaneIsmFactory extends HyperlaneApp { moduleAddress, this.multiProvider.getSigner(chain), ); - await routingIsm.transferOwnership(config.owner); + await this.multiProvider.handleTx( + chain, + await routingIsm.transferOwnership(config.owner), + ); const address = dispatchLogs[0].args['module']; return IRoutingIsm__factory.connect(address, signer); } diff --git a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts index 18d49a0f1..48a617e49 100644 --- a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts +++ b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts @@ -5,7 +5,7 @@ import { LiquidityLayerRouter, PortalAdapter, } from '@hyperlane-xyz/core'; -import { utils } from '@hyperlane-xyz/utils'; +import { types, utils } from '@hyperlane-xyz/utils'; import { HyperlaneContracts, HyperlaneContractsMap } from '../../contracts'; import { MultiProvider } from '../../providers/MultiProvider'; @@ -86,9 +86,10 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer< async enrollRemoteRouters( contractsMap: HyperlaneContractsMap, configMap: ChainMap, + foreignRouters: ChainMap, ): Promise { this.logger(`Enroll LiquidityLayerRouters with each other`); - await super.enrollRemoteRouters(contractsMap, configMap); + await super.enrollRemoteRouters(contractsMap, configMap, foreignRouters); this.logger(`Enroll CircleBridgeAdapters with each other`); // Hack to allow use of super.enrollRemoteRouters @@ -104,6 +105,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer< }), ) as unknown as HyperlaneContractsMap, configMap, + foreignRouters, ); this.logger(`Enroll PortalAdapters with each other`); @@ -120,6 +122,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer< }), ) as unknown as HyperlaneContractsMap, configMap, + foreignRouters, ); } diff --git a/typescript/sdk/src/router/GasRouterDeployer.ts b/typescript/sdk/src/router/GasRouterDeployer.ts index f934e2dad..5b8589c48 100644 --- a/typescript/sdk/src/router/GasRouterDeployer.ts +++ b/typescript/sdk/src/router/GasRouterDeployer.ts @@ -1,4 +1,5 @@ import { GasRouter } from '@hyperlane-xyz/core'; +import { types } from '@hyperlane-xyz/utils'; import { HyperlaneContracts, @@ -19,8 +20,9 @@ export abstract class GasRouterDeployer< async enrollRemoteRouters( contractsMap: HyperlaneContractsMap, configMap: ChainMap, + foreignRouters: ChainMap = {}, ): Promise { - await super.enrollRemoteRouters(contractsMap, configMap); + await super.enrollRemoteRouters(contractsMap, configMap, foreignRouters); this.logger(`Setting enrolled router destination gas...`); for (const [chain, contracts] of Object.entries(contractsMap)) { diff --git a/typescript/sdk/src/router/HyperlaneRouterChecker.ts b/typescript/sdk/src/router/HyperlaneRouterChecker.ts index df3318bc4..79d3ddb99 100644 --- a/typescript/sdk/src/router/HyperlaneRouterChecker.ts +++ b/typescript/sdk/src/router/HyperlaneRouterChecker.ts @@ -8,8 +8,10 @@ import { ChainName } from '../types'; import { RouterApp } from './RouterApps'; import { + ConnectionClientConfig, ConnectionClientViolation, ConnectionClientViolationType, + OwnableConfig, RouterConfig, } from './types'; @@ -33,7 +35,7 @@ export class HyperlaneRouterChecker< const router = this.app.router(this.app.getContracts(chain)); const checkConnectionClientProperty = async ( - property: keyof RouterConfig, + property: keyof (ConnectionClientConfig & OwnableConfig), violationType: ConnectionClientViolationType, ) => { const actual = await router[property](); diff --git a/typescript/sdk/src/router/HyperlaneRouterDeployer.ts b/typescript/sdk/src/router/HyperlaneRouterDeployer.ts index 62a4b2601..894f61b61 100644 --- a/typescript/sdk/src/router/HyperlaneRouterDeployer.ts +++ b/typescript/sdk/src/router/HyperlaneRouterDeployer.ts @@ -3,7 +3,7 @@ import { Mailbox__factory, Router, } from '@hyperlane-xyz/core'; -import { utils } from '@hyperlane-xyz/utils'; +import { types, utils } from '@hyperlane-xyz/utils'; import { HyperlaneContracts, @@ -15,6 +15,7 @@ import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; import { moduleCanCertainlyVerify } from '../ism/HyperlaneIsmFactory'; import { RouterConfig } from '../router/types'; import { ChainMap } from '../types'; +import { objFilter, objMap, objMerge } from '../utils/objects'; export abstract class HyperlaneRouterDeployer< Config extends RouterConfig, @@ -82,28 +83,35 @@ export abstract class HyperlaneRouterDeployer< } async enrollRemoteRouters( - contractsMap: HyperlaneContractsMap, + deployedContractsMap: HyperlaneContractsMap, _: ChainMap, + foreignRouters: ChainMap = {}, ): Promise { this.logger( `Enrolling deployed routers with each other (if not already)...`, ); // Make all routers aware of each other. - const deployedChains = Object.keys(contractsMap); - for (const [chain, contracts] of Object.entries(contractsMap)) { - // only enroll chains which are deployed - const deployedRemoteChains = this.multiProvider + + // Routers that were deployed. + const deployedRouters: ChainMap = objMap( + deployedContractsMap, + (_, contracts) => this.router(contracts).address, + ); + // All routers, including those that were deployed and those with existing deployments. + const allRouters = objMerge(deployedRouters, foreignRouters); + + const allChains = Object.keys(allRouters); + for (const [chain, contracts] of Object.entries(deployedContractsMap)) { + const allRemoteChains = this.multiProvider .getRemoteChains(chain) - .filter((c) => deployedChains.includes(c)); + .filter((c) => allChains.includes(c)); const enrollEntries = await Promise.all( - deployedRemoteChains.map(async (remote) => { + allRemoteChains.map(async (remote) => { const remoteDomain = this.multiProvider.getDomainId(remote); const current = await this.router(contracts).routers(remoteDomain); - const expected = utils.addressToBytes32( - this.router(contractsMap[remote]).address, - ); + const expected = utils.addressToBytes32(allRouters[remote]); return current !== expected ? [remoteDomain, expected] : undefined; }), ); @@ -151,12 +159,29 @@ export abstract class HyperlaneRouterDeployer< async deploy( configMap: ChainMap, ): Promise> { - const contractsMap = await super.deploy(configMap); + // Only deploy on chains that don't have foreign deployments. + const configMapToDeploy = objFilter( + configMap, + (_chainName, config): config is Config => !config.foreignDeployment, + ); + + // Create a map of chains that have foreign deployments. + const foreignDeployments: ChainMap = objFilter( + objMap(configMap, (_, config) => config.foreignDeployment), + (_chainName, foreignDeployment): foreignDeployment is string => + foreignDeployment !== undefined, + ); - await this.enrollRemoteRouters(contractsMap, configMap); - await this.initConnectionClients(contractsMap, configMap); - await this.transferOwnership(contractsMap, configMap); + const deployedContractsMap = await super.deploy(configMapToDeploy); + + await this.enrollRemoteRouters( + deployedContractsMap, + configMap, + foreignDeployments, + ); + await this.initConnectionClients(deployedContractsMap, configMap); + await this.transferOwnership(deployedContractsMap, configMap); - return contractsMap; + return deployedContractsMap; } } diff --git a/typescript/sdk/src/router/types.ts b/typescript/sdk/src/router/types.ts index 010f259d4..b621f377f 100644 --- a/typescript/sdk/src/router/types.ts +++ b/typescript/sdk/src/router/types.ts @@ -11,7 +11,13 @@ export type OwnableConfig = { owner: types.Address; }; -export type RouterConfig = ConnectionClientConfig & OwnableConfig; +export type ForeignDeploymentConfig = { + foreignDeployment?: types.Address; +}; + +export type RouterConfig = ConnectionClientConfig & + OwnableConfig & + ForeignDeploymentConfig; export type GasConfig = { gas: number;