feat(infra): supporting generating warp configs for EVM routes (#4248)

### Description

- Add in code configs for EVM base warp routes
- Allow checker tooling (check-deploy) to read and run against the
config
- Read Warp addresses from registry when run check-deploy for warp
modules

### Drive-by changes

- clean up ISM config comparison violation message to handle ISM
addressed

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

Yes

### Testing

Manual
pull/4292/head
Mohammed Hussan 4 months ago committed by GitHub
parent 021f4bf97d
commit 72aa438e27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 49
      typescript/infra/config/environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.ts
  2. 45
      typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.ts
  3. 44
      typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.ts
  4. 40
      typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.ts
  5. 44
      typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.ts
  6. 43
      typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.ts
  7. 98
      typescript/infra/config/warp.ts
  8. 24
      typescript/infra/scripts/agent-utils.ts
  9. 33
      typescript/infra/scripts/check-deploy.ts
  10. 11
      typescript/infra/scripts/deploy.ts
  11. 23
      typescript/infra/scripts/get-warp-route-ids.ts
  12. 1
      typescript/infra/src/config/warp.ts
  13. 16
      typescript/infra/src/utils/violation.ts
  14. 10
      typescript/sdk/src/core/HyperlaneCoreChecker.ts
  15. 59
      typescript/sdk/src/router/HyperlaneRouterChecker.ts

@ -0,0 +1,49 @@
import { ethers } from 'ethers';
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk';
import { tokens } from '../../../../../src/config/warp.js';
export const getAncient8EthereumUSDCWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const ismConfig = buildAggregationIsmConfigs(
'ethereum',
['ancient8'],
defaultMultisigConfigs,
).ancient8;
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.collateral,
token: tokens.ethereum.USDC,
interchainSecurityModule: ismConfig,
// This hook was recovered from running the deploy script
// for the hook module. The hook configuration is the Ethereum
// default hook for the Ancient8 remote (no routing).
hook: '0x19b2cF952b70b217c90FC408714Fbc1acD29A6A8',
};
// @ts-ignore - The types as they stand require a synthetic to specify
// TokenMetadata, but in practice these are actually inferred from a
// collateral config. To avoid needing to specify the TokenMetadata, just
// ts-ignore for synthetic tokens.
const ancient8: TokenRouterConfig = {
...routerConfig.ancient8,
type: TokenType.synthetic,
// Uses the default ISM
interchainSecurityModule: ethers.constants.AddressZero,
};
return {
ethereum,
ancient8,
};
};

@ -0,0 +1,45 @@
import { ethers } from 'ethers';
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk';
import { tokens } from '../../../../../src/config/warp.js';
export const getEthereumInevmUSDCWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
// TODO: seems to be evidence that this ISM should have been set https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3233/commits/dc8d50c9c49cdea8417fbe9dad090dc13f078fff
// checker tooling suggests that it has not been set zero address for ISM is being used
// run yarn tsx ./scripts/check-deploy.ts -e mainnet3 -f ethereum -m warp --warpRouteId USDC/ethereum-inevm
const ismConfig = buildAggregationIsmConfigs(
'ethereum',
['inevm'],
defaultMultisigConfigs,
).inevm;
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.collateral,
token: tokens.ethereum.USDC,
hook: '0xb87AC8EA4533AE017604E44470F7c1E550AC6F10', // aggregation of IGP and Merkle, arbitrary config not supported for now
interchainSecurityModule: ismConfig,
};
const inevm: TokenRouterConfig = {
...routerConfig.inevm,
type: TokenType.synthetic,
interchainSecurityModule: ethers.constants.AddressZero,
};
return {
ethereum,
inevm,
};
};

@ -0,0 +1,44 @@
import { ethers } from 'ethers';
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk';
import { tokens } from '../../../../../src/config/warp.js';
export const getEthereumInevmUSDTWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
// TODO: there may be evidence that this ISM should have been set https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3233/commits/dc8d50c9c49cdea8417fbe9dad090dc13f078fff, although the USDC token address is being used in the commit
// checker tooling suggests that this ISM has not been set, zero address for ISM is being used
// run yarn tsx ./scripts/check-deploy.ts -e mainnet3 -f ethereum -m warp --warpRouteId USDT/ethereum-inevm
const ismConfig = buildAggregationIsmConfigs(
'ethereum',
['inevm'],
defaultMultisigConfigs,
).inevm;
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.collateral,
token: tokens.ethereum.USDT,
hook: '0xb87AC8EA4533AE017604E44470F7c1E550AC6F10',
interchainSecurityModule: ismConfig,
};
const inevm: TokenRouterConfig = {
...routerConfig.inevm,
type: TokenType.synthetic,
interchainSecurityModule: ethers.constants.AddressZero,
};
return {
ethereum,
inevm,
};
};

@ -0,0 +1,40 @@
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk';
export const getEthereumVictionETHWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const ismConfig = buildAggregationIsmConfigs(
'ethereum',
['viction'],
defaultMultisigConfigs,
).viction;
const viction: TokenRouterConfig = {
...routerConfig.viction,
type: TokenType.synthetic,
name: 'ETH',
symbol: 'ETH',
decimals: 18,
totalSupply: 0,
gas: 50_000,
};
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.native,
gas: 65_000,
interchainSecurityModule: ismConfig,
};
return {
viction,
ethereum,
};
};

@ -0,0 +1,44 @@
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk';
import { tokens } from '../../../../../src/config/warp.js';
export const getEthereumVictionUSDCWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
// commit that the config was copied from https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3067/commits/7ed5b460034ea5e140c6ff86bcd6baf6ebb824c4#diff-fab5dd1a27c76e4310699c57ccf92ab6274ef0acf17e079b17270cedf4057775R109
const ismConfig = buildAggregationIsmConfigs(
'ethereum',
['viction'],
defaultMultisigConfigs,
).viction;
const viction: TokenRouterConfig = {
...routerConfig.viction,
type: TokenType.synthetic,
name: 'USDC',
symbol: 'USDC',
decimals: 18,
totalSupply: 0,
gas: 75_000,
};
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.collateral,
token: tokens.ethereum.USDC,
gas: 65_000,
interchainSecurityModule: ismConfig,
};
return {
viction,
ethereum,
};
};

@ -0,0 +1,43 @@
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk';
import { tokens } from '../../../../../src/config/warp.js';
export const getEthereumVictionUSDTWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const ismConfig = buildAggregationIsmConfigs(
'ethereum',
['viction'],
defaultMultisigConfigs,
).viction;
const viction: TokenRouterConfig = {
...routerConfig.viction,
type: TokenType.synthetic,
name: 'USDT',
symbol: 'USDT',
decimals: 6,
totalSupply: 0,
gas: 75_000,
};
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.collateral,
token: tokens.ethereum.USDT,
gas: 65_000,
interchainSecurityModule: ismConfig,
};
return {
viction,
ethereum,
};
};

@ -1,73 +1,63 @@
import { ethers } from 'ethers';
import { import {
ChainMap, ChainMap,
HyperlaneIsmFactory,
MultiProvider, MultiProvider,
RouterConfig,
TokenRouterConfig, TokenRouterConfig,
TokenType,
buildAggregationIsmConfigs,
defaultMultisigConfigs,
} from '@hyperlane-xyz/sdk'; } from '@hyperlane-xyz/sdk';
import { Modules, getAddresses } from '../scripts/agent-utils.js';
import { getHyperlaneCore } from '../scripts/core-utils.js'; import { getHyperlaneCore } from '../scripts/core-utils.js';
import { EnvironmentConfig } from '../src/config/environment.js'; import { EnvironmentConfig } from '../src/config/environment.js';
import { tokens } from '../src/config/warp.js';
import { DEPLOYER } from './environments/mainnet3/owners.js'; import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.js';
import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js';
import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js';
import { getEthereumVictionETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.js';
import { getEthereumVictionUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.js';
import { getEthereumVictionUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.js';
export enum WarpRouteIds {
Ancient8EthereumUSDC = 'USDC/ancient8-ethereum',
ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismEZETH = 'EZETH/arbitrum-base-blast-bsc-ethereum-fraxtal-linea-mode-optimism',
ArbitrumNeutronEclip = 'ECLIP/arbitrum-neutron',
ArbitrumNeutronTIA = 'TIA/arbitrum-neutron',
EthereumInevmUSDC = 'USDC/ethereum-inevm',
EthereumInevmUSDT = 'USDT/ethereum-inevm',
EthereumVictionETH = 'ETH/ethereum-viction',
EthereumVictionUSDC = 'USDC/ethereum-viction',
EthereumVictionUSDT = 'USDT/ethereum-viction',
InevmInjectiveINJ = 'INJ/inevm-injective',
MantapacificNeutronTIA = 'TIA/mantapacific-neutron',
}
export const warpConfigGetterMap: Record<
string,
(routerConfig: ChainMap<RouterConfig>) => Promise<ChainMap<TokenRouterConfig>>
> = {
[WarpRouteIds.Ancient8EthereumUSDC]: getAncient8EthereumUSDCWarpConfig,
[WarpRouteIds.EthereumInevmUSDC]: getEthereumInevmUSDCWarpConfig,
[WarpRouteIds.EthereumInevmUSDT]: getEthereumInevmUSDTWarpConfig,
// [WarpRouteIds.ArbitrumNeutronEclip]: getArbitrumNeutronEclipWarpConfig, // TODO
// [WarpRouteIds.ArbitrumNeutronTIA]: getArbitrumNeutronTiaWarpConfig, // TODO
// [WarpRouteIds.ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismEZETH]: getArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismEZETHWarpConfig, // TODO
// [WarpRouteIds.InevmInjectiveINJ]: getInevmInjectiveINJWarpConfig, // TODO
[WarpRouteIds.EthereumVictionETH]: getEthereumVictionETHWarpConfig,
[WarpRouteIds.EthereumVictionUSDC]: getEthereumVictionUSDCWarpConfig,
[WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig,
// [WarpRouteIds.MantapacificNeutronTIA]: getEthereumVictionUSDTWarpConfig, // TODO
};
export async function getWarpConfig( export async function getWarpConfig(
multiProvider: MultiProvider, multiProvider: MultiProvider,
envConfig: EnvironmentConfig, envConfig: EnvironmentConfig,
warpRouteId: string,
): Promise<ChainMap<TokenRouterConfig>> { ): Promise<ChainMap<TokenRouterConfig>> {
const { core } = await getHyperlaneCore(envConfig.environment, multiProvider); const { core } = await getHyperlaneCore(envConfig.environment, multiProvider);
const ismFactory = HyperlaneIsmFactory.fromAddressesMap(
getAddresses(envConfig.environment, Modules.PROXY_FACTORY),
multiProvider,
);
const owner = DEPLOYER;
// "Manually" deploying an ISM because the warp deployer doesn't support
// ISM objects at the moment, and the deploy involves strictly recoverable ISMs.
const ism = await ismFactory.deploy({
destination: 'ethereum',
config: buildAggregationIsmConfigs(
'ethereum',
['ancient8'],
defaultMultisigConfigs,
).ancient8,
});
const routerConfig = core.getRouterConfig(envConfig.owners); const routerConfig = core.getRouterConfig(envConfig.owners);
const ethereum: TokenRouterConfig = { const warpConfigGetter = warpConfigGetterMap[warpRouteId];
...routerConfig.ethereum, if (!warpConfigGetter) {
type: TokenType.collateral, throw new Error(`Unknown warp route: ${warpRouteId}`);
token: tokens.ethereum.USDC, }
interchainSecurityModule: ism.address,
// This hook was recovered from running the deploy script
// for the hook module. The hook configuration is the Ethereum
// default hook for the Ancient8 remote (no routing).
hook: '0x19b2cF952b70b217c90FC408714Fbc1acD29A6A8',
owner,
};
// @ts-ignore - The types as they stand require a synthetic to specify
// TokenMetadata, but in practice these are actually inferred from a
// collateral config. To avoid needing to specify the TokenMetadata, just
// ts-ignore for synthetic tokens.
const ancient8: TokenRouterConfig = {
...routerConfig.ancient8,
type: TokenType.synthetic,
// Uses the default ISM
interchainSecurityModule: ethers.constants.AddressZero,
owner,
};
return { return warpConfigGetter(routerConfig);
ethereum,
ancient8,
};
} }

@ -1,7 +1,11 @@
import path, { join } from 'path'; import path, { join } from 'path';
import yargs, { Argv } from 'yargs'; import yargs, { Argv } from 'yargs';
import { ChainAddresses, IRegistry } from '@hyperlane-xyz/registry'; import {
ChainAddresses,
IRegistry,
warpConfigToWarpAddresses,
} from '@hyperlane-xyz/registry';
import { import {
ChainMap, ChainMap,
ChainName, ChainName,
@ -142,6 +146,10 @@ export function withChains<T>(args: Argv<T>) {
); );
} }
export function withWarpRouteId<T>(args: Argv<T>) {
return args.describe('warpRouteId', 'warp route id').string('warpRouteId');
}
export function withProtocol<T>(args: Argv<T>) { export function withProtocol<T>(args: Argv<T>) {
return args return args
.describe('protocol', 'protocol type') .describe('protocol', 'protocol type')
@ -440,6 +448,20 @@ export function getAddresses(environment: DeployEnvironment, module: Modules) {
} }
} }
export function getWarpAddresses(
environment: DeployEnvironment,
warpRouteId: string,
) {
const registry = getRegistry();
const warpRouteConfig = registry.getWarpRoute(warpRouteId);
if (!warpRouteConfig) {
throw new Error(`Warp route config for ${warpRouteId} not found`);
}
return warpConfigToWarpAddresses(warpRouteConfig);
}
export function writeAddresses( export function writeAddresses(
environment: DeployEnvironment, environment: DeployEnvironment,
module: Modules, module: Modules,

@ -2,6 +2,8 @@ import { HelloWorldChecker } from '@hyperlane-xyz/helloworld';
import { import {
HypERC20App, HypERC20App,
HypERC20Checker, HypERC20Checker,
HypERC20Factories,
HyperlaneAddressesMap,
HyperlaneCoreChecker, HyperlaneCoreChecker,
HyperlaneIgp, HyperlaneIgp,
HyperlaneIgpChecker, HyperlaneIgpChecker,
@ -27,17 +29,20 @@ import { logViolationDetails } from '../src/utils/violation.js';
import { import {
Modules, Modules,
getAddresses,
getArgs as getRootArgs, getArgs as getRootArgs,
getWarpAddresses,
withChain, withChain,
withContext, withContext,
withModuleAndFork, withModuleAndFork,
withWarpRouteId,
} from './agent-utils.js'; } from './agent-utils.js';
import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js';
import { getHelloWorldApp } from './helloworld/utils.js'; import { getHelloWorldApp } from './helloworld/utils.js';
function getArgs() { function getArgs() {
return withChain(withModuleAndFork(withContext(getRootArgs()))) return withChain(
withWarpRouteId(withModuleAndFork(withContext(getRootArgs()))),
)
.boolean('asDeployer') .boolean('asDeployer')
.default('asDeployer', false) .default('asDeployer', false)
.boolean('govern') .boolean('govern')
@ -46,8 +51,16 @@ function getArgs() {
} }
async function check() { async function check() {
const { fork, govern, module, environment, context, chain, asDeployer } = const {
await getArgs(); fork,
govern,
module,
environment,
context,
chain,
asDeployer,
warpRouteId,
} = await getArgs();
const envConfig = getEnvironmentConfig(environment); const envConfig = getEnvironmentConfig(environment);
let multiProvider = await envConfig.getMultiProvider(); let multiProvider = await envConfig.getMultiProvider();
@ -136,15 +149,21 @@ async function check() {
); );
governor = new ProxiedRouterGovernor(checker); governor = new ProxiedRouterGovernor(checker);
} else if (module === Modules.WARP) { } else if (module === Modules.WARP) {
const config = await getWarpConfig(multiProvider, envConfig); if (!warpRouteId) {
const addresses = getAddresses(environment, Modules.WARP); throw new Error('Warp route id required for warp module');
}
const config = await getWarpConfig(multiProvider, envConfig, warpRouteId);
const addresses = getWarpAddresses(environment, warpRouteId);
const filteredAddresses = Object.keys(addresses) // filter out changes not in config const filteredAddresses = Object.keys(addresses) // filter out changes not in config
.filter((key) => key in config) .filter((key) => key in config)
.reduce((obj, key) => { .reduce((obj, key) => {
obj[key] = addresses[key]; obj[key] = addresses[key];
return obj; return obj;
}, {} as typeof addresses); }, {} as typeof addresses);
const app = HypERC20App.fromAddressesMap(filteredAddresses, multiProvider); const app = HypERC20App.fromAddressesMap(
filteredAddresses as HyperlaneAddressesMap<HypERC20Factories>,
multiProvider,
);
const checker = new HypERC20Checker( const checker = new HypERC20Checker(
multiProvider, multiProvider,

@ -46,6 +46,7 @@ import {
withConcurrentDeploy, withConcurrentDeploy,
withContext, withContext,
withModuleAndFork, withModuleAndFork,
withWarpRouteId,
} from './agent-utils.js'; } from './agent-utils.js';
import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js';
@ -58,9 +59,12 @@ async function main() {
buildArtifactPath, buildArtifactPath,
chains, chains,
concurrentDeploy, concurrentDeploy,
warpRouteId,
} = await withContext( } = await withContext(
withConcurrentDeploy( withConcurrentDeploy(
withChains(withModuleAndFork(withBuildArtifactPath(getArgs()))), withChains(
withModuleAndFork(withWarpRouteId(withBuildArtifactPath(getArgs()))),
),
), ),
).argv; ).argv;
const envConfig = getEnvironmentConfig(environment); const envConfig = getEnvironmentConfig(environment);
@ -116,11 +120,14 @@ async function main() {
concurrentDeploy, concurrentDeploy,
); );
} else if (module === Modules.WARP) { } else if (module === Modules.WARP) {
if (!warpRouteId) {
throw new Error('Warp route ID is required for WARP module');
}
const ismFactory = HyperlaneIsmFactory.fromAddressesMap( const ismFactory = HyperlaneIsmFactory.fromAddressesMap(
getAddresses(envConfig.environment, Modules.PROXY_FACTORY), getAddresses(envConfig.environment, Modules.PROXY_FACTORY),
multiProvider, multiProvider,
); );
config = await getWarpConfig(multiProvider, envConfig); config = await getWarpConfig(multiProvider, envConfig, warpRouteId);
deployer = new HypERC20Deployer( deployer = new HypERC20Deployer(
multiProvider, multiProvider,
ismFactory, ismFactory,

@ -0,0 +1,23 @@
import { getRegistry } from '../config/registry.js';
async function main() {
const registry = await getRegistry();
const registryContents = await registry.listRegistryContent();
const warpRoutes = registryContents.deployments.warpRoutes;
const warpRouteIds = Object.keys(warpRoutes);
const warpRouteIdsTable = warpRouteIds.map((warpRouteId) => {
return { 'Warp Route IDs': warpRouteId };
});
console.table(warpRouteIdsTable, ['Warp Route IDs']);
}
main()
.then()
.catch((e) => {
console.error(e);
process.exit(1);
});

@ -5,5 +5,6 @@ import { Address } from '@hyperlane-xyz/utils';
export const tokens: ChainMap<Record<string, Address>> = { export const tokens: ChainMap<Record<string, Address>> = {
ethereum: { ethereum: {
USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7',
}, },
}; };

@ -208,6 +208,22 @@ function preProcessConfig(config: any) {
} }
function logViolationDetail(violation: CheckerViolation): void { function logViolationDetail(violation: CheckerViolation): void {
if (
typeof violation.expected === 'string' ||
typeof violation.actual === 'string'
) {
if (typeof violation.expected === 'string') {
console.log(
`Address provided for expected config: ${violation.expected}`,
);
}
if (typeof violation.actual === 'string') {
console.log(`Address provided for actual config: ${violation.actual}`);
}
console.log('Config comparison not possible');
return;
}
const preProcessedExpectedConfig = preProcessConfig(violation.expected); const preProcessedExpectedConfig = preProcessConfig(violation.expected);
const preProcessedActualConfig = preProcessConfig(violation.actual); const preProcessedActualConfig = preProcessConfig(violation.actual);

@ -5,7 +5,7 @@ import { assert, eqAddress } from '@hyperlane-xyz/utils';
import { BytecodeHash } from '../consts/bytecode.js'; import { BytecodeHash } from '../consts/bytecode.js';
import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js';
import { proxyImplementation } from '../deploy/proxy.js'; import { proxyImplementation } from '../deploy/proxy.js';
import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js';
import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js';
import { collectValidators, moduleMatchesConfig } from '../ism/utils.js'; import { collectValidators, moduleMatchesConfig } from '../ism/utils.js';
import { MultiProvider } from '../providers/MultiProvider.js'; import { MultiProvider } from '../providers/MultiProvider.js';
@ -79,14 +79,18 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
if (!matches) { if (!matches) {
const ismReader = new EvmIsmReader(this.ismFactory.multiProvider, chain); const ismReader = new EvmIsmReader(this.ismFactory.multiProvider, chain);
const derivedConfig = await ismReader.deriveIsmConfig(actualIsmAddress); let actualConfig: string | DerivedIsmConfig =
ethers.constants.AddressZero;
if (actualIsmAddress !== ethers.constants.AddressZero) {
actualConfig = await ismReader.deriveIsmConfig(actualIsmAddress);
}
const violation: MailboxViolation = { const violation: MailboxViolation = {
type: CoreViolationType.Mailbox, type: CoreViolationType.Mailbox,
subType: MailboxViolationType.DefaultIsm, subType: MailboxViolationType.DefaultIsm,
contract: mailbox, contract: mailbox,
chain, chain,
actual: derivedConfig, actual: actualConfig,
expected: config.defaultIsm, expected: config.defaultIsm,
}; };
this.addViolation(violation); this.addViolation(violation);

@ -1,8 +1,10 @@
import { ethers } from 'ethers';
import { addressToBytes32, assert, eqAddress } from '@hyperlane-xyz/utils'; import { addressToBytes32, assert, eqAddress } from '@hyperlane-xyz/utils';
import { HyperlaneFactories } from '../contracts/types.js'; import { HyperlaneFactories } from '../contracts/types.js';
import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js';
import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js';
import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js';
import { moduleMatchesConfig } from '../ism/utils.js'; import { moduleMatchesConfig } from '../ism/utils.js';
import { MultiProvider } from '../providers/MultiProvider.js'; import { MultiProvider } from '../providers/MultiProvider.js';
@ -74,39 +76,34 @@ export class HyperlaneRouterChecker<
} }
} }
if (config.interchainSecurityModule) { const actualIsmAddress = await router.interchainSecurityModule();
const actualIsmAddress = await router.interchainSecurityModule();
if (
typeof config.interchainSecurityModule !== 'string' &&
!this.ismFactory
) {
throw Error(
'ISM factory not provided to HyperlaneRouterChecker, cannot check object-based ISM config',
);
}
const matches = await moduleMatchesConfig(
chain,
actualIsmAddress,
config.interchainSecurityModule,
this.multiProvider,
this.ismFactory?.chainMap[chain] ?? ({} as any),
);
if (!matches) { const matches = await moduleMatchesConfig(
const ismReader = new EvmIsmReader(this.multiProvider, chain); chain,
const derivedConfig = await ismReader.deriveIsmConfig(actualIsmAddress); actualIsmAddress,
config.interchainSecurityModule ?? ethers.constants.AddressZero,
this.multiProvider,
this.ismFactory?.chainMap[chain] ?? ({} as any),
);
const violation: ClientViolation = { if (!matches) {
chain, const ismReader = new EvmIsmReader(this.multiProvider, chain);
type: ClientViolationType.InterchainSecurityModule, let actualConfig: string | DerivedIsmConfig =
contract: router, ethers.constants.AddressZero;
actual: derivedConfig, if (actualIsmAddress !== ethers.constants.AddressZero) {
expected: config.interchainSecurityModule, actualConfig = await ismReader.deriveIsmConfig(actualIsmAddress);
description: `ISM config does not match deployed ISM`,
};
this.addViolation(violation);
} }
const violation: ClientViolation = {
chain,
type: ClientViolationType.InterchainSecurityModule,
contract: router,
actual: actualConfig,
expected:
config.interchainSecurityModule ?? ethers.constants.AddressZero,
description: `ISM config does not match deployed ISM`,
};
this.addViolation(violation);
} }
} }

Loading…
Cancel
Save