diff --git a/.changeset/silver-peas-mate.md b/.changeset/silver-peas-mate.md new file mode 100644 index 0000000000..54a188eeda --- /dev/null +++ b/.changeset/silver-peas-mate.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +updates the warp deployment config schema to be closer to the ica routing schema diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 17e6c083d7..71eca0dc09 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -731,8 +731,9 @@ async function enrollRemoteRouters( mutatedWarpRouteConfig.remoteRouters = otherChains.reduce((remoteRouters, otherChain) => { - remoteRouters[multiProvider.getDomainId(otherChain)] = - deployedRoutersAddresses[otherChain]; + remoteRouters[multiProvider.getDomainId(otherChain)] = { + address: deployedRoutersAddresses[otherChain], + }; return remoteRouters; }, {}); diff --git a/typescript/sdk/src/ica/schemas.ts b/typescript/sdk/src/ica/schemas.ts index de438650a0..69ed2e1a3e 100644 --- a/typescript/sdk/src/ica/schemas.ts +++ b/typescript/sdk/src/ica/schemas.ts @@ -1,14 +1,18 @@ import { z } from 'zod'; import { ZHash } from '../metadata/customZodTypes.js'; +import { RemoteRouterDomain, RemoteRouterRouter } from '../router/types.js'; import { DerivedOwnableSchema } from '../schemas.js'; export const RemoteIcaRouterConfigSchema = z.record( - z.string(), - z.object({ - address: ZHash, - interchainSecurityModule: ZHash.optional(), - }), + RemoteRouterDomain, + RemoteRouterRouter.merge( + z.object({ + interchainSecurityModule: ZHash.optional().describe( + 'Optional ISM override to be used on the chain', + ), + }), + ), ); export const IcaRouterConfigSchema = z.object({ @@ -21,23 +25,13 @@ export const IcaRouterConfigSchema = z.object({ remoteIcaRouters: RemoteIcaRouterConfigSchema.optional(), }); -export const DerivedRemoteIcaRouterConfigSchema = z.record( - z.string(), - z.object({ - address: ZHash, - interchainSecurityModule: ZHash.optional().describe( - 'Optional ISM override to be used on the chain', - ), - }), -); - export const DerivedIcaRouterConfigSchema = DerivedOwnableSchema.merge( z .object({ owner: ZHash, mailbox: ZHash, proxyAdmin: DerivedOwnableSchema, - remoteIcaRouters: DerivedRemoteIcaRouterConfigSchema, + remoteIcaRouters: RemoteIcaRouterConfigSchema, }) .strict(), ); diff --git a/typescript/sdk/src/router/types.ts b/typescript/sdk/src/router/types.ts index d950e43a30..9329ed7d9d 100644 --- a/typescript/sdk/src/router/types.ts +++ b/typescript/sdk/src/router/types.ts @@ -75,8 +75,10 @@ export const ForeignDeploymentConfigSchema = z.object({ foreignDeployment: z.string().optional(), }); -const RemoteRouterDomain = z.string(); -const RemoteRouterRouter = z.string().startsWith('0x'); +export const RemoteRouterDomain = z.string(); +export const RemoteRouterRouter = z.object({ + address: z.string().startsWith('0x'), +}); export const RemoteRoutersSchema = z.record( RemoteRouterDomain, RemoteRouterRouter, diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts index 3acfacb880..82d3190daa 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts @@ -48,7 +48,9 @@ import { TokenRouterConfig } from './schemas.js'; const randomRemoteRouters = (n: number) => { const routers: RemoteRouters = {}; for (let domain = 0; domain < n; domain++) { - routers[domain] = randomAddress(); + routers[domain] = { + address: randomAddress(), + }; } return routers; }; @@ -580,7 +582,9 @@ describe('EvmERC20WarpHyperlaneModule', async () => { txs = await evmERC20WarpModule.update({ ...config, remoteRouters: { - 3: randomAddress(), + 3: { + address: randomAddress(), + }, }, }); @@ -679,7 +683,9 @@ describe('EvmERC20WarpHyperlaneModule', async () => { ...baseConfig, type: TokenType.native, remoteRouters: { - [domain]: randomAddress(), + [domain]: { + address: randomAddress(), + }, }, }; diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.ts b/typescript/sdk/src/token/EvmERC20WarpModule.ts index 7d1b84a51a..fd497196e5 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.ts @@ -145,14 +145,25 @@ export class EvmERC20WarpModule extends HyperlaneModule< return []; } - // We normalize the addresses for comparison - actualConfig.remoteRouters = normalizeConfig(actualConfig.remoteRouters); - expectedConfig.remoteRouters = normalizeConfig( - expectedConfig.remoteRouters, - ); assert(actualConfig.remoteRouters, 'actualRemoteRouters is undefined'); assert(expectedConfig.remoteRouters, 'actualRemoteRouters is undefined'); + // We normalize the addresses for comparison + actualConfig.remoteRouters = Object.fromEntries( + Object.entries(actualConfig.remoteRouters).map(([key, value]) => [ + key, + // normalizeConfig removes the address property but we don't want to lose that info + { ...normalizeConfig(value), address: normalizeConfig(value.address) }, + ]), + ); + expectedConfig.remoteRouters = Object.fromEntries( + Object.entries(expectedConfig.remoteRouters).map(([key, value]) => [ + key, + // normalizeConfig removes the address property but we don't want to lose that info + { ...normalizeConfig(value), address: normalizeConfig(value.address) }, + ]), + ); + const { remoteRouters: actualRemoteRouters } = actualConfig; const { remoteRouters: expectedRemoteRouters } = expectedConfig; @@ -171,7 +182,7 @@ export class EvmERC20WarpModule extends HyperlaneModule< [ Object.keys(expectedRemoteRouters).map((k) => Number(k)), Object.values(expectedRemoteRouters).map((a) => - addressToBytes32(a), + addressToBytes32(a.address), ), ], ), diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts index 782acc3d28..28f9163797 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts @@ -356,7 +356,7 @@ describe('ERC20WarpRouterReader', async () => { ); expect(Object.keys(derivedConfig.remoteRouters!).length).to.equal(1); expect( - derivedConfig.remoteRouters![otherChainMetadata.domainId!], + derivedConfig.remoteRouters![otherChainMetadata.domainId!].address, ).to.be.equal(warpRoute[otherChain].collateral.address); }); }); diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index d65f4fad7b..7940cde7af 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -28,7 +28,11 @@ import { DeployedOwnableConfig } from '../deploy/types.js'; import { EvmHookReader } from '../hook/EvmHookReader.js'; import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { MultiProvider } from '../providers/MultiProvider.js'; -import { DestinationGas, RemoteRouters } from '../router/types.js'; +import { + DestinationGas, + RemoteRouters, + RemoteRoutersSchema, +} from '../router/types.js'; import { ChainNameOrId } from '../types.js'; import { HyperlaneReader } from '../utils/HyperlaneReader.js'; @@ -267,13 +271,18 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { ); const domains = await warpRoute.domains(); - return Object.fromEntries( + const routers = Object.fromEntries( await Promise.all( domains.map(async (domain) => { - return [domain, bytes32ToAddress(await warpRoute.routers(domain))]; + return [ + domain, + { address: bytes32ToAddress(await warpRoute.routers(domain)) }, + ]; }), ), ); + + return RemoteRoutersSchema.parse(routers); } async fetchProxyAdminConfig(