feat(infra): Support warp config in infra from non-evm routes (#4291)

### Description

- Add in code warp configs for non-evm chains

### Testing

Manual
pull/4303/head
Mohammed Hussan 3 months ago committed by GitHub
parent 72aa438e27
commit 19f7d4fd90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/warm-foxes-jam.md
  2. 2
      .registryrc
  3. 34
      typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumNeutronEclipWarpConfig.ts
  4. 46
      typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.ts
  5. 28
      typescript/infra/config/environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.ts
  6. 46
      typescript/infra/config/environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.ts
  7. 15
      typescript/infra/config/warp.ts
  8. 5
      typescript/infra/scripts/agent-utils.ts
  9. 33
      typescript/infra/scripts/check-deploy.ts
  10. 37
      typescript/infra/src/utils/violation.ts
  11. 32
      typescript/sdk/src/contracts/contracts.ts
  12. 13
      typescript/sdk/src/router/HyperlaneRouterChecker.ts
  13. 9
      typescript/sdk/src/token/app.ts

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': minor
---
Supprt passing foreignDeployments to HypERC20App constructor

@ -1 +1 @@
4c4fadfba88b5ad1c310941eb282ae1fc07aa939 29f4901c1d1a63944b0f5f2ee50f499568260004

@ -0,0 +1,34 @@
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
} from '@hyperlane-xyz/sdk';
export const getArbitrumNeutronEclipWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const neutronRouter =
'6b04c49fcfd98bc4ea9c05cd5790462a39537c00028333474aebe6ddf20b73a3';
// @ts-ignore - foreignDeployment configs dont conform to the TokenRouterConfig
const neutron: TokenRouterConfig = {
foreignDeployment: neutronRouter,
};
const arbitrum: TokenRouterConfig = {
...routerConfig.arbitrum,
type: TokenType.synthetic,
name: 'Eclipse Fi',
symbol: 'ECLIP',
decimals: 6,
totalSupply: 0,
gas: 600_000,
interchainSecurityModule: '0x53a5c239d62ff35c98e0ec9612c86517748fff59', // TODO: we should replace this with an ISM config
};
return {
neutron,
arbitrum,
};
};

@ -0,0 +1,46 @@
import {
ChainMap,
IsmType,
RouterConfig,
TokenRouterConfig,
TokenType,
} from '@hyperlane-xyz/sdk';
export const getArbitrumNeutronTiaWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const neutronRouter =
'910926c4cf95d107237a9cf0b3305fe9c81351ebcba3d218ceb0e4935d92ceac';
// @ts-ignore - foreignDeployment configs dont conform to the TokenRouterConfig
const neutron: TokenRouterConfig = {
foreignDeployment: neutronRouter,
};
const arbitrum: TokenRouterConfig = {
...routerConfig.arbitrum,
interchainSecurityModule: {
type: IsmType.MESSAGE_ID_MULTISIG,
validators: [
'0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0',
'0xb65438a014fb05fbadcfe35bc6e25d372b6ba460',
'0xc79503a3e3011535a9c60f6d21f76f59823a38bd',
'0x42fa752defe92459370a052b6387a87f7de9b80c',
'0x54b2cca5091b098a1a993dec03c4d1ee9af65999',
'0x47aa126e05933b95c5eb90b26e6b668d84f4b25a',
],
threshold: 4,
},
type: TokenType.synthetic,
name: 'TIA',
symbol: 'TIA.n',
decimals: 6,
totalSupply: 0,
gas: 600_000,
};
return {
arbitrum,
neutron,
};
};

@ -0,0 +1,28 @@
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
} from '@hyperlane-xyz/sdk';
export const getInevmInjectiveINJWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const injectiveRouter = 'inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k';
// @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig
const injective: TokenRouterConfig = {
type: TokenType.native,
foreignDeployment: injectiveRouter,
};
const inevm: TokenRouterConfig = {
...routerConfig.inevm,
type: TokenType.native,
};
return {
injective,
inevm,
};
};

@ -0,0 +1,46 @@
import {
ChainMap,
IsmType,
RouterConfig,
TokenRouterConfig,
TokenType,
} from '@hyperlane-xyz/sdk';
export const getMantapacificNeutronTiaWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
const neutronRouter =
'0xc5fc6899019cb4a7649981d89eb7b1a0929d0a85b2d41802f3315129ad4b581a';
// @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig
const neutron: TokenRouterConfig = {
foreignDeployment: neutronRouter,
};
const mantapacific: TokenRouterConfig = {
...routerConfig.mantapacific,
interchainSecurityModule: {
type: IsmType.MESSAGE_ID_MULTISIG,
validators: [
'0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0',
'0xb65438a014fb05fbadcfe35bc6e25d372b6ba460',
'0xc79503a3e3011535a9c60f6d21f76f59823a38bd',
'0x42fa752defe92459370a052b6387a87f7de9b80c',
'0x54b2cca5091b098a1a993dec03c4d1ee9af65999',
'0x47aa126e05933b95c5eb90b26e6b668d84f4b25a',
],
threshold: 4,
},
type: TokenType.synthetic,
name: 'TIA',
symbol: 'TIA',
decimals: 6,
totalSupply: 0,
gas: 600_000,
};
return {
mantapacific,
neutron,
};
};

@ -9,11 +9,15 @@ import { getHyperlaneCore } from '../scripts/core-utils.js';
import { EnvironmentConfig } from '../src/config/environment.js'; import { EnvironmentConfig } from '../src/config/environment.js';
import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.js'; import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.js';
import { getArbitrumNeutronEclipWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronEclipWarpConfig.js';
import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js';
import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js'; import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js';
import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js'; import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js';
import { getEthereumVictionETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.js'; import { getEthereumVictionETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.js';
import { getEthereumVictionUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.js'; import { getEthereumVictionUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.js';
import { getEthereumVictionUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.js'; import { getEthereumVictionUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.js';
import { getInevmInjectiveINJWarpConfig } from './environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.js';
import { getMantapacificNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.js';
export enum WarpRouteIds { export enum WarpRouteIds {
Ancient8EthereumUSDC = 'USDC/ancient8-ethereum', Ancient8EthereumUSDC = 'USDC/ancient8-ethereum',
@ -36,14 +40,15 @@ export const warpConfigGetterMap: Record<
[WarpRouteIds.Ancient8EthereumUSDC]: getAncient8EthereumUSDCWarpConfig, [WarpRouteIds.Ancient8EthereumUSDC]: getAncient8EthereumUSDCWarpConfig,
[WarpRouteIds.EthereumInevmUSDC]: getEthereumInevmUSDCWarpConfig, [WarpRouteIds.EthereumInevmUSDC]: getEthereumInevmUSDCWarpConfig,
[WarpRouteIds.EthereumInevmUSDT]: getEthereumInevmUSDTWarpConfig, [WarpRouteIds.EthereumInevmUSDT]: getEthereumInevmUSDTWarpConfig,
// [WarpRouteIds.ArbitrumNeutronEclip]: getArbitrumNeutronEclipWarpConfig, // TODO [WarpRouteIds.ArbitrumNeutronEclip]: getArbitrumNeutronEclipWarpConfig,
// [WarpRouteIds.ArbitrumNeutronTIA]: getArbitrumNeutronTiaWarpConfig, // TODO [WarpRouteIds.ArbitrumNeutronTIA]: getArbitrumNeutronTiaWarpConfig,
// [WarpRouteIds.ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismEZETH]: getArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismEZETHWarpConfig, // TODO // [WarpRouteIds.ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismEZETH]:
// [WarpRouteIds.InevmInjectiveINJ]: getInevmInjectiveINJWarpConfig, // TODO // getRenzoEZETHWarpConfig, // TODO
[WarpRouteIds.InevmInjectiveINJ]: getInevmInjectiveINJWarpConfig,
[WarpRouteIds.EthereumVictionETH]: getEthereumVictionETHWarpConfig, [WarpRouteIds.EthereumVictionETH]: getEthereumVictionETHWarpConfig,
[WarpRouteIds.EthereumVictionUSDC]: getEthereumVictionUSDCWarpConfig, [WarpRouteIds.EthereumVictionUSDC]: getEthereumVictionUSDCWarpConfig,
[WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig, [WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig,
// [WarpRouteIds.MantapacificNeutronTIA]: getEthereumVictionUSDTWarpConfig, // TODO [WarpRouteIds.MantapacificNeutronTIA]: getMantapacificNeutronTiaWarpConfig,
}; };
export async function getWarpConfig( export async function getWarpConfig(

@ -448,10 +448,7 @@ export function getAddresses(environment: DeployEnvironment, module: Modules) {
} }
} }
export function getWarpAddresses( export function getWarpAddresses(warpRouteId: string) {
environment: DeployEnvironment,
warpRouteId: string,
) {
const registry = getRegistry(); const registry = getRegistry();
const warpRouteConfig = registry.getWarpRoute(warpRouteId); const warpRouteConfig = registry.getWarpRoute(warpRouteId);

@ -2,8 +2,6 @@ import { HelloWorldChecker } from '@hyperlane-xyz/helloworld';
import { import {
HypERC20App, HypERC20App,
HypERC20Checker, HypERC20Checker,
HypERC20Factories,
HyperlaneAddressesMap,
HyperlaneCoreChecker, HyperlaneCoreChecker,
HyperlaneIgp, HyperlaneIgp,
HyperlaneIgpChecker, HyperlaneIgpChecker,
@ -13,6 +11,8 @@ import {
InterchainAccountConfig, InterchainAccountConfig,
InterchainQuery, InterchainQuery,
InterchainQueryChecker, InterchainQueryChecker,
attachContractsMapAndGetForeignDeployments,
hypERC20factories,
} from '@hyperlane-xyz/sdk'; } from '@hyperlane-xyz/sdk';
import { objFilter } from '@hyperlane-xyz/utils'; import { objFilter } from '@hyperlane-xyz/utils';
@ -153,16 +153,39 @@ async function check() {
throw new Error('Warp route id required for warp module'); throw new Error('Warp route id required for warp module');
} }
const config = await getWarpConfig(multiProvider, envConfig, warpRouteId); const config = await getWarpConfig(multiProvider, envConfig, warpRouteId);
const addresses = getWarpAddresses(environment, warpRouteId); const addresses = getWarpAddresses(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 as HyperlaneAddressesMap<HypERC20Factories>, const { contractsMap, foreignDeployments } =
attachContractsMapAndGetForeignDeployments(
filteredAddresses,
hypERC20factories,
multiProvider,
);
// log error and return is foreign deployment chain is specifically checked
if (
(chain && foreignDeployments[chain]) ||
(fork && foreignDeployments[fork])
) {
console.log(
`${
chain ?? fork
} is non evm and it not compatible with warp checker tooling`,
);
return;
}
const app = new HypERC20App(
contractsMap,
multiProvider, multiProvider,
undefined,
foreignDeployments,
); );
const checker = new HypERC20Checker( const checker = new HypERC20Checker(

@ -98,17 +98,28 @@ function toLowerCaseValues(obj: any): any {
}, {}); }, {});
} }
function sortArraysByType(obj: AnyObject): AnyObject { function sortArraysByType(obj: any): any {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
return obj // Check if array elements are objects with a 'type' property
.sort((a, b) => { if (
if (a.type < b.type) return -1; obj.length > 0 &&
if (a.type > b.type) return 1; typeof obj[0] === 'object' &&
return 0; obj[0] !== null &&
}) 'type' in obj[0]
.map((item) => sortArraysByType(item)); ) {
return obj
.sort((a, b) => {
if (a.type < b.type) return -1;
if (a.type > b.type) return 1;
return 0;
})
.map((item) => sortArraysByType(item));
} else {
// For all other arrays, sort normally
return obj.sort().map((item) => sortArraysByType(item));
}
} else if (typeof obj === 'object' && obj !== null) { } else if (typeof obj === 'object' && obj !== null) {
const sortedObj: AnyObject = {}; const sortedObj: any = {};
Object.keys(obj).forEach((key) => { Object.keys(obj).forEach((key) => {
sortedObj[key] = sortArraysByType(obj[key]); sortedObj[key] = sortArraysByType(obj[key]);
}); });
@ -259,7 +270,9 @@ export function logViolationDetails(violations: CheckerViolation[]): void {
if (violation.type === CoreViolationType.Mailbox) { if (violation.type === CoreViolationType.Mailbox) {
const mailboxViolation = violation as MailboxViolation; const mailboxViolation = violation as MailboxViolation;
if (mailboxViolation.subType === MailboxViolationType.DefaultIsm) { if (mailboxViolation.subType === MailboxViolationType.DefaultIsm) {
console.log(`Mailbox violation ${mailboxViolation.subType} details:`); console.log(
`${violation.chain} mailbox violation ${mailboxViolation.subType} details:`,
);
logViolationDetail(violation); logViolationDetail(violation);
} }
} }
@ -267,7 +280,9 @@ export function logViolationDetails(violations: CheckerViolation[]): void {
if ( if (
violation.type === ConnectionClientViolationType.InterchainSecurityModule violation.type === ConnectionClientViolationType.InterchainSecurityModule
) { ) {
console.log(`Connection client violation ${violation.type} details:`); console.log(
`${violation.chain} connection client violation ${violation.type} details:`,
);
logViolationDetail(violation); logViolationDetail(violation);
} }
} }

@ -131,13 +131,43 @@ export function attachContractsMapAndGetForeignDeployments<
factories, factories,
); );
// TODO: This function shouldn't need to be aware of application types like collateral / synthetic / native etc. Ideally this should work for any app, not just warp routes. is it safe to assume this is always an object containing 1 key/value pair, and that the value will always be an address?
const foreignDeployments = objMap( const foreignDeployments = objMap(
filterChainMapExcludeProtocol( filterChainMapExcludeProtocol(
addressesMap, addressesMap,
ProtocolType.Ethereum, ProtocolType.Ethereum,
metadataManager, metadataManager,
), ),
(_, addresses) => hexOrBase58ToHex(addresses.router), (chain, addresses) => {
const router =
addresses.router ||
addresses.collateral ||
addresses.synthetic ||
addresses.native;
const protocolType = metadataManager.tryGetChainMetadata(chain)?.protocol;
if (!router || typeof router !== 'string') {
throw new Error(`Router address not found for ${chain}`);
}
if (!protocolType) {
throw new Error(`Protocol type not found for ${chain}`);
}
switch (protocolType) {
case ProtocolType.Ethereum:
throw new Error('Ethereum chain should not have foreign deployments');
case ProtocolType.Cosmos:
return router;
case ProtocolType.Sealevel:
return hexOrBase58ToHex(router);
default:
throw new Error(`Unsupported protocol type: ${protocolType}`);
}
},
); );
return { return {

@ -94,13 +94,22 @@ export class HyperlaneRouterChecker<
actualConfig = await ismReader.deriveIsmConfig(actualIsmAddress); actualConfig = await ismReader.deriveIsmConfig(actualIsmAddress);
} }
let expectedConfig = config.interchainSecurityModule;
if (typeof expectedConfig === 'string') {
expectedConfig = await ismReader.deriveIsmConfig(expectedConfig);
}
if (expectedConfig === undefined) {
expectedConfig = ethers.constants.AddressZero;
}
const violation: ClientViolation = { const violation: ClientViolation = {
chain, chain,
type: ClientViolationType.InterchainSecurityModule, type: ClientViolationType.InterchainSecurityModule,
contract: router, contract: router,
actual: actualConfig, actual: actualConfig,
expected: expected: expectedConfig,
config.interchainSecurityModule ?? ethers.constants.AddressZero,
description: `ISM config does not match deployed ISM`, description: `ISM config does not match deployed ISM`,
}; };
this.addViolation(violation); this.addViolation(violation);

@ -1,5 +1,8 @@
import { Logger } from 'pino';
import { TokenRouter } from '@hyperlane-xyz/core'; import { TokenRouter } from '@hyperlane-xyz/core';
import { objKeys } from '@hyperlane-xyz/utils'; import { ChainMap } from '@hyperlane-xyz/sdk';
import { Address, objKeys } from '@hyperlane-xyz/utils';
import { appFromAddressesMapHelper } from '../contracts/contracts.js'; import { appFromAddressesMapHelper } from '../contracts/contracts.js';
import { import {
@ -16,8 +19,10 @@ export class HypERC20App extends GasRouterApp<HypERC20Factories, TokenRouter> {
constructor( constructor(
contractsMap: HyperlaneContractsMap<HypERC20Factories>, contractsMap: HyperlaneContractsMap<HypERC20Factories>,
multiProvider: MultiProvider, multiProvider: MultiProvider,
logger?: Logger,
foreignDeployments: ChainMap<Address> = {},
) { ) {
super(contractsMap, multiProvider); super(contractsMap, multiProvider, logger, foreignDeployments);
} }
router(contracts: HyperlaneContracts<HypERC20Factories>): TokenRouter { router(contracts: HyperlaneContracts<HypERC20Factories>): TokenRouter {

Loading…
Cancel
Save