AggregationISM set for rc kathy (#2517)

### Description

- Using a separate set of validators for RC deployments and tests
- Defining a recursive AggregationISM config which aggregates over two
RoutingISMs, one for a set of MerkleRootMultisigISMs and one for a set
of MessageIdMultisigISMs for each origin domain.

### Drive-by changes

- Dployed MerkleRootMultisigISMFactory and MessageIdMultisigISMFactory
for testnet3 in hyperlane context

### Related issues

- Fixes #2224

### Backward compatibility

 Backward compatible?
 Yes

Any infrastructure implications? 
HyperlaneRouterChecker will fail silently if you provide an IsmConfig -
#2534


### Testing

Manual
Testnet

---------

Co-authored-by: Yorke Rhodes <email@yorke.dev>
Co-authored-by: Yorke Rhodes <yorke@hyperlane.xyz>
pull/2536/head
Kunal Arora 1 year ago committed by GitHub
parent aa92fe37db
commit e91be529db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/node.yml
  2. 8
      typescript/helloworld/src/deploy/deploy.ts
  3. 92
      typescript/infra/config/environments/testnet3/aggregationIsm.ts
  4. 26
      typescript/infra/config/environments/testnet3/helloworld.ts
  5. 3
      typescript/infra/config/environments/testnet3/helloworld/rc/addresses.json
  6. 8
      typescript/infra/config/environments/testnet3/helloworld/rc/verification.json
  7. 108
      typescript/infra/config/environments/testnet3/ism/verification.json
  8. 114
      typescript/infra/config/multisigIsm.ts
  9. 9
      typescript/infra/package.json
  10. 23
      typescript/infra/scripts/deploy.ts
  11. 8
      typescript/infra/scripts/utils.ts
  12. 6
      typescript/sdk/src/consts/chainMetadata.ts
  13. 36
      typescript/sdk/src/consts/environments/testnet.json
  14. 59
      typescript/sdk/src/deploy/HyperlaneDeployer.ts
  15. 174
      typescript/sdk/src/ism/HyperlaneIsmFactory.ts
  16. 2
      typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts
  17. 3
      typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts
  18. 3
      typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts
  19. 2
      typescript/sdk/src/providers/MultiProvider.ts
  20. 5
      typescript/sdk/src/router/HyperlaneRouterChecker.ts
  21. 16
      typescript/sdk/src/router/HyperlaneRouterDeployer.ts
  22. 6
      typescript/sdk/src/router/RouterApps.ts
  23. 3
      typescript/sdk/src/router/types.ts

@ -122,7 +122,7 @@ jobs:
strategy:
matrix:
environment: [testnet3, mainnet2]
module: [ism, core, igp, ica]
module: [ism, core, igp, ica, helloworld]
steps:
- uses: actions/checkout@v3

@ -1,6 +1,7 @@
import {
ChainName,
HyperlaneContracts,
HyperlaneIsmFactory,
HyperlaneRouterDeployer,
MultiProvider,
} from '@hyperlane-xyz/sdk';
@ -14,8 +15,11 @@ export class HelloWorldDeployer extends HyperlaneRouterDeployer<
HelloWorldConfig,
HelloWorldFactories
> {
constructor(multiProvider: MultiProvider) {
super(multiProvider, helloWorldFactories, {});
constructor(
multiProvider: MultiProvider,
readonly ismFactory?: HyperlaneIsmFactory,
) {
super(multiProvider, helloWorldFactories, { ismFactory });
}
router(contracts: HyperlaneContracts<HelloWorldFactories>): HelloWorld {

@ -0,0 +1,92 @@
import {
AggregationIsmConfig,
ChainMap,
ChainName,
IsmConfig,
ModuleType,
MultisigIsmConfig,
RoutingIsmConfig,
defaultMultisigIsmConfigs,
objFilter,
objMap,
} from '@hyperlane-xyz/sdk';
import { Contexts } from '../../contexts';
import { rcMultisigIsmConfigs } from '../../multisigIsm';
import { chainNames } from './chains';
import { owners } from './owners';
export const multisigIsms = (
local: ChainName,
type: MultisigIsmConfig['type'],
context: Contexts,
): ChainMap<MultisigIsmConfig> =>
objMap(
objFilter(
context === Contexts.ReleaseCandidate
? rcMultisigIsmConfigs
: defaultMultisigIsmConfigs,
(chain, config): config is MultisigIsmConfig =>
chain !== local && chainNames.includes(chain),
),
(_, config) => ({
...config,
type,
}),
);
/// Routing => Multisig ISM type
export const routingIsm = (
local: ChainName,
type: MultisigIsmConfig['type'],
context: Contexts,
): RoutingIsmConfig => {
const defaultMultisigIsmConfigs = multisigIsms(local, type, context);
return {
type: ModuleType.ROUTING,
domains: defaultMultisigIsmConfigs,
owner: owners[local],
};
};
/// 1/2 Aggregation => Routing => Multisig ISM
export const aggregationIsm = (
local: ChainName,
context: Contexts,
): AggregationIsmConfig => {
const config: AggregationIsmConfig = {
type: ModuleType.AGGREGATION,
modules: [
// ORDERING MATTERS
routingIsm(local, ModuleType.MERKLE_ROOT_MULTISIG, context),
routingIsm(local, ModuleType.MESSAGE_ID_MULTISIG, context),
],
threshold: 1,
};
return config;
};
const replacerEnum = (key: string, value: any) => {
if (key === 'type') {
switch (value) {
case ModuleType.AGGREGATION:
return 'AGGREGATION';
case ModuleType.ROUTING:
return 'ROUTING';
case ModuleType.MERKLE_ROOT_MULTISIG:
return 'MERKLE_ROOT_MULTISIG';
case ModuleType.LEGACY_MULTISIG:
return 'LEGACY_MULTISIG';
case ModuleType.MESSAGE_ID_MULTISIG:
return 'MESSAGE_ID_MULTISIG';
default:
return value;
}
}
return value;
};
export const printIsmConfig = (ism: IsmConfig): string => {
return JSON.stringify(ism, replacerEnum, 2);
};

@ -1,14 +1,21 @@
import { AgentConnectionType } from '@hyperlane-xyz/sdk';
import { HelloWorldConfig as HelloWorldContractsConfig } from '@hyperlane-xyz/helloworld';
import {
AgentConnectionType,
ChainMap,
RouterConfig,
objMap,
} from '@hyperlane-xyz/sdk';
import { HelloWorldConfig } from '../../../src/config';
import { HelloWorldKathyRunMode } from '../../../src/config/helloworld';
import { Contexts } from '../../contexts';
import { aggregationIsm } from './aggregationIsm';
import { environment } from './chains';
import hyperlaneAddresses from './helloworld/hyperlane/addresses.json';
import rcAddresses from './helloworld/rc/addresses.json';
export const hyperlane: HelloWorldConfig = {
export const hyperlaneHelloworld: HelloWorldConfig = {
addresses: hyperlaneAddresses,
kathy: {
docker: {
@ -28,7 +35,7 @@ export const hyperlane: HelloWorldConfig = {
},
};
export const releaseCandidate: HelloWorldConfig = {
export const releaseCandidateHelloworld: HelloWorldConfig = {
addresses: rcAddresses,
kathy: {
docker: {
@ -48,6 +55,15 @@ export const releaseCandidate: HelloWorldConfig = {
};
export const helloWorld = {
[Contexts.Hyperlane]: hyperlane,
[Contexts.ReleaseCandidate]: releaseCandidate,
[Contexts.Hyperlane]: hyperlaneHelloworld,
[Contexts.ReleaseCandidate]: releaseCandidateHelloworld,
};
export const helloWorldConfig = (
context: Contexts,
routerConfigMap: ChainMap<RouterConfig>,
): ChainMap<HelloWorldContractsConfig> =>
objMap(routerConfigMap, (chain, routerConfig) => ({
...routerConfig,
interchainSecurityModule: aggregationIsm(chain, context),
}));

@ -22,5 +22,8 @@
},
"arbitrumgoerli": {
"router": "0xaAF1BF6f2BfaE290ea8615066fd167e396a2f578"
},
"sepolia": {
"router": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217"
}
}

@ -62,5 +62,13 @@
"constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1",
"isProxy": false
}
],
"sepolia": [
{
"name": "Router",
"address": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217",
"constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f987d7edcb5890cb321437d8145e3d51131298b6",
"isProxy": false
}
]
}

@ -17,6 +17,18 @@
"address": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0x6525Ac4008E38e0E70DaEf59d5f0e1721bd8aA83",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x4C739E01f295B70762C0bA9D86123E1775C2f703",
"constructorArguments": "",
"isProxy": false
}
],
"fuji": [
@ -37,6 +49,18 @@
"address": "0xB24C91238eA30D59CF58CEB8dd5e4eaf70544d47",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0xA1e6d12a3F5F7e05E4D6cb39E71534F27fE29561",
"constructorArguments": "",
"isProxy": false
}
],
"mumbai": [
@ -57,6 +81,18 @@
"address": "0xdc8BC001FB649EfD51eEDD33151d428b917AaE3d",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0xbA38823853Fa2994823F3E62d3EAC5cC701C8CCA",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x5F8A06fb970775C0C317AF6AfA5aD45B11FBD342",
"constructorArguments": "",
"isProxy": false
}
],
"bsctestnet": [
@ -77,6 +113,18 @@
"address": "0x0CA314006fe0e7EF88ad2Bb69a7421aB2f1C5288",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0x8DA546024850D998Be3b65204c0F0f63C1f3B0A1",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x7Bc0bb71aE0E9bDC0Ac53e932870728D95FA28bF",
"constructorArguments": "",
"isProxy": false
}
],
"goerli": [
@ -97,6 +145,18 @@
"address": "0x129A80Fe557153B5F48B4292F8C177bACdcf7dB3",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0x4dD7716b876441355657a18c7E7b02129F88E3c0",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x14b0F0c0a59704E92f95252cE24Ef6aB1d679733",
"constructorArguments": "",
"isProxy": false
}
],
"sepolia": [
@ -117,6 +177,18 @@
"address": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0xA9999B4abC373FF2BB95B8725FABC96CA883d811",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0xCCC126d96efcc342BF2781A7d224D3AB1F25B19C",
"constructorArguments": "",
"isProxy": false
}
],
"moonbasealpha": [
@ -137,6 +209,18 @@
"address": "0x561331FafB7f2ABa77E77780178ADdD1A37bdaBD",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0x0616A79374e81eB1d2275eCe5837aD050f9c53f1",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x3D696c38Dd958e635f9077e65b64aA9cf7c92627",
"constructorArguments": "",
"isProxy": false
}
],
"optimismgoerli": [
@ -157,6 +241,18 @@
"address": "0x1D7b8aBa2494Ec82e80D6206d2B3df9f8C3F1862",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0xC5Bb8CDD44B6c56695df45c7AA8012a97dD6ED13",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x39a8711BF44165A2292Cb5cB43229659c2Bb11c9",
"constructorArguments": "",
"isProxy": false
}
],
"arbitrumgoerli": [
@ -177,6 +273,18 @@
"address": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MerkleRootMultisigIsmFactory",
"address": "0x0502Be39aE255D022013DC0aeAa52fDBCD5f0331",
"constructorArguments": "",
"isProxy": false
},
{
"name": "MessageIdMultisigIsmFactory",
"address": "0x71eAD731EBdd1334d80a89a572fDFA67830C504c",
"constructorArguments": "",
"isProxy": false
}
]
}

@ -0,0 +1,114 @@
import { ChainMap, ModuleType, MultisigIsmConfig } from '@hyperlane-xyz/sdk';
export const rcMultisigIsmConfigs: ChainMap<MultisigIsmConfig> = {
// ----------------- Mainnets -----------------
celo: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0xe7a82e210f512f8e9900d6bc2acbf7981c63e66e', // abacus
],
},
ethereum: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0xaea1adb1c687b061e5b60b9da84cb69e7b5fab44', // abacus
],
},
avalanche: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0x706976391e23dea28152e0207936bd942aba01ce', // abacus
],
},
polygon: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0xef372f6ff7775989b3ac884506ee31c79638c989', // abacus
],
},
bsc: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0x0823081031a4a6f97c6083775c191d17ca96d0ab', // abacus
],
},
arbitrum: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0x1a95b35fb809d57faf1117c1cc29a6c5df289df1', // abacus
],
},
optimism: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0x60e938bf280bbc21bacfd8bf435459d9003a8f98', // abacus
],
},
moonbeam: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0x0df7140811e309dc69638352545151ebb9d5e0fd', // abacus
],
},
gnosis: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: [
'0x15f48e78092a4f79febface509cfd76467c6cdbb', // abacus
],
},
// ----------------- Testnets -----------------
alfajores: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de'],
},
fuji: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0xd81ba169170a9b582812cf0e152d2c168572e21f'],
},
mumbai: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0xb537c4ce34e1cad718be52aa30b095e416eae46a'],
},
bsctestnet: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb'],
},
goerli: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0x9597ddb4ad2af237665559574b820596bb77ae7a'],
},
sepolia: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0x183f15924f3a464c54c9393e8d268eb44d2b208c'],
},
moonbasealpha: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d'],
},
optimismgoerli: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0x1d6798671ac532f2bf30c3a5230697a4695705e4'],
},
arbitrumgoerli: {
type: ModuleType.LEGACY_MULTISIG,
threshold: 1,
validators: ['0x6d13367c7cd713a4ea79a2552adf824bf1ecdd5e'],
},
};

@ -53,10 +53,11 @@
"prepublish": "yarn build",
"repository": "https://github.com/hyperlane-xyz/hyperlane-monorepo",
"scripts": {
"deploy-core": "ts-node scripts/deploy.ts -e test --context hyperlane -m core",
"deploy-igp": "ts-node scripts/deploy.ts -e test --context hyperlane -m igp",
"deploy-ism": "ts-node scripts/deploy.ts -e test --context hyperlane -m ism",
"deploy-hook": "ts-node scripts/deploy.ts -e testnet3 --context hyperlane -m hook",
"deploy-core": "ts-node scripts/deploy.ts -e test -m core",
"deploy-igp": "ts-node scripts/deploy.ts -e test -m igp",
"deploy-ism": "ts-node scripts/deploy.ts -e test -m ism",
"deploy-helloworld": "ts-node scripts/deploy.ts -e test -m helloworld",
"deploy-hook": "ts-node scripts/deploy.ts -e testnet3 -m hook",
"build": "tsc",
"clean": "rm -rf ./dist ./cache",
"check": "tsc --noEmit",

@ -1,5 +1,6 @@
import path from 'path';
import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld';
import {
ChainMap,
HyperlaneCoreDeployer,
@ -15,6 +16,8 @@ import {
objMap,
} from '@hyperlane-xyz/sdk';
import { Contexts } from '../config/contexts';
import { helloWorldConfig } from '../config/environments/testnet3/helloworld';
import { deployEnvToSdkEnv } from '../src/config/environment';
import { deployWithArtifacts } from '../src/deployment/deploy';
import { TestQuerySenderDeployer } from '../src/deployment/testcontracts/testquerysender';
@ -31,11 +34,17 @@ import {
getEnvironmentDirectory,
getModuleDirectory,
getRouterConfig,
withContext,
withModuleAndFork,
} from './utils';
async function main() {
const { module, fork, environment } = await withModuleAndFork(getArgs()).argv;
const {
context = Contexts.Hyperlane,
module,
fork,
environment,
} = await withContext(withModuleAndFork(getArgs())).argv;
const envConfig = getEnvironmentConfig(environment);
const multiProvider = await envConfig.getMultiProvider();
@ -106,12 +115,22 @@ async function main() {
queryRouterAddress: conf.router,
}));
deployer = new TestQuerySenderDeployer(multiProvider, igp);
} else if (module === Modules.HELLO_WORLD) {
const routerConfig = await getRouterConfig(environment, multiProvider);
config = helloWorldConfig(context, routerConfig);
const ismFactory = HyperlaneIsmFactory.fromEnvironment(
deployEnvToSdkEnv[environment],
multiProvider,
);
deployer = new HelloWorldDeployer(multiProvider, ismFactory);
} else {
console.log(`Skipping ${module}, deployer unimplemented`);
return;
}
const modulePath = getModuleDirectory(environment, module);
const modulePath = getModuleDirectory(environment, module, context);
console.log(`Deploying to ${modulePath}`);
const addresses = SDK_MODULES.includes(module)
? path.join(

@ -45,6 +45,7 @@ export enum Modules {
LIQUIDITY_LAYER = 'll',
TEST_QUERY_SENDER = 'testquerysender',
TEST_RECIPIENT = 'testrecipient',
HELLO_WORLD = 'helloworld',
}
export const SDK_MODULES = [
@ -76,6 +77,7 @@ export function withModuleAndFork<T>(args: yargs.Argv<T>) {
export function withContext<T>(args: yargs.Argv<T>) {
return args
.describe('context', 'deploy context')
.default('context', Contexts.Hyperlane)
.coerce('context', assertContext)
.demandOption('context');
}
@ -131,7 +133,7 @@ export async function getConfigsBasedOnArgs(argv?: {
environment: DeployEnvironment;
context: Contexts;
}) {
const { environment, context } = argv
const { environment, context = Contexts.Hyperlane } = argv
? argv
: await withContext(getArgs()).argv;
const envConfig = getEnvironmentConfig(environment);
@ -206,6 +208,7 @@ export function getEnvironmentDirectory(environment: DeployEnvironment) {
export function getModuleDirectory(
environment: DeployEnvironment,
module: Modules,
context?: Contexts,
) {
// for backwards compatibility with existing paths
const suffixFn = () => {
@ -216,6 +219,8 @@ export function getModuleDirectory(
return 'middleware/queries';
case Modules.LIQUIDITY_LAYER:
return 'middleware/liquidity-layer';
case Modules.HELLO_WORLD:
return `helloworld/${context}`;
default:
return module;
}
@ -255,6 +260,7 @@ export async function getRouterConfig(
deployEnvToSdkEnv[environment],
multiProvider,
);
const owners = getEnvironmentConfig(environment).owners;
const config: ChainMap<RouterConfig> = {};
const knownChains = multiProvider.intersect(

@ -565,7 +565,7 @@ export const test1: ChainMetadata = {
protocol: ProtocolType.Ethereum,
displayName: 'Test 1',
nativeToken: etherToken,
rpcUrls: [{ http: 'http://localhost:8545' }],
rpcUrls: [{ http: 'http://127.0.0.1:8545' }],
blockExplorers: [],
blocks: {
confirmations: 1,
@ -582,7 +582,7 @@ export const test2: ChainMetadata = {
protocol: ProtocolType.Ethereum,
displayName: 'Test 2',
nativeToken: etherToken,
rpcUrls: [{ http: 'http://localhost:8545' }],
rpcUrls: [{ http: 'http://127.0.0.1:8545' }],
blockExplorers: [],
blocks: {
confirmations: 1,
@ -599,7 +599,7 @@ export const test3: ChainMetadata = {
protocol: ProtocolType.Ethereum,
displayName: 'Test 3',
nativeToken: etherToken,
rpcUrls: [{ http: 'http://localhost:8545' }],
rpcUrls: [{ http: 'http://127.0.0.1:8545' }],
blockExplorers: [],
blocks: {
confirmations: 1,

@ -12,7 +12,9 @@
"aggregationIsmFactory": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821",
"routingIsmFactory": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2",
"interchainQueryRouter": "0xc341cBC69745C541d698cb2cB4eDb91c2F0413aE",
"interchainAccountRouter": "0x2b0db6161f2f7aE86b7eA07711354575a6D99667"
"interchainAccountRouter": "0x2b0db6161f2f7aE86b7eA07711354575a6D99667",
"merkleRootMultisigIsmFactory": "0x6525Ac4008E38e0E70DaEf59d5f0e1721bd8aA83",
"messageIdMultisigIsmFactory": "0x4C739E01f295B70762C0bA9D86123E1775C2f703"
},
"fuji": {
"storageGasOracle": "0xd44E79A697136888f0d720Fb6703400a9204FD39",
@ -27,7 +29,9 @@
"aggregationIsmFactory": "0x9fB5D10C07569F2EBdc8ec4432B3a52b6d0ad9A0",
"routingIsmFactory": "0xB24C91238eA30D59CF58CEB8dd5e4eaf70544d47",
"interchainQueryRouter": "0x7192d5Ad540E9fEfc3FD1845d41c18EE86980AAb",
"interchainAccountRouter": "0xb0811feF53FF499bd8E09018F8E568b95c42A721"
"interchainAccountRouter": "0xb0811feF53FF499bd8E09018F8E568b95c42A721",
"merkleRootMultisigIsmFactory": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98",
"messageIdMultisigIsmFactory": "0xA1e6d12a3F5F7e05E4D6cb39E71534F27fE29561"
},
"mumbai": {
"storageGasOracle": "0x8970bdA0B0A01d1bA2656b510B2859560e75a7c5",
@ -42,7 +46,9 @@
"aggregationIsmFactory": "0xD095B3ADa705e171f40187e10eD07Ee5fe291704",
"routingIsmFactory": "0xdc8BC001FB649EfD51eEDD33151d428b917AaE3d",
"interchainQueryRouter": "0xD786eC480Da58792175c9DDEdD99802Badf1037E",
"interchainAccountRouter": "0x03Ae748aee6E31560D4E46AdbCF15c6D60bD70e7"
"interchainAccountRouter": "0x03Ae748aee6E31560D4E46AdbCF15c6D60bD70e7",
"merkleRootMultisigIsmFactory": "0xbA38823853Fa2994823F3E62d3EAC5cC701C8CCA",
"messageIdMultisigIsmFactory": "0x5F8A06fb970775C0C317AF6AfA5aD45B11FBD342"
},
"bsctestnet": {
"storageGasOracle": "0x1a75f55e8f574CdB5abb1b2702b9caF2E5F7d4D6",
@ -57,7 +63,9 @@
"aggregationIsmFactory": "0xda72972291172B9966Dec7606d45d72e2b9f2470",
"routingIsmFactory": "0x0CA314006fe0e7EF88ad2Bb69a7421aB2f1C5288",
"interchainQueryRouter": "0x6117c92e1D05fD23Adc6077bA0d2956EE3175984",
"interchainAccountRouter": "0x404693BeD61D6B17F44738c0b4ddF9c2D65Ba0BF"
"interchainAccountRouter": "0x404693BeD61D6B17F44738c0b4ddF9c2D65Ba0BF",
"merkleRootMultisigIsmFactory": "0x8DA546024850D998Be3b65204c0F0f63C1f3B0A1",
"messageIdMultisigIsmFactory": "0x7Bc0bb71aE0E9bDC0Ac53e932870728D95FA28bF"
},
"goerli": {
"storageGasOracle": "0xce8E9D701A1DFfe672c1d8dB20De2B3fa6F4437D",
@ -72,7 +80,9 @@
"aggregationIsmFactory": "0x11413a6Ea76a55eAec950894ba8fdd7683E41b06",
"routingIsmFactory": "0x129A80Fe557153B5F48B4292F8C177bACdcf7dB3",
"interchainQueryRouter": "0x46A2B1C3E8a93C3613Ebf326235FbD3e2f65660F",
"interchainAccountRouter": "0x55486284a85d7b51a7bBfd343702414D65276fa6"
"interchainAccountRouter": "0x55486284a85d7b51a7bBfd343702414D65276fa6",
"merkleRootMultisigIsmFactory": "0x4dD7716b876441355657a18c7E7b02129F88E3c0",
"messageIdMultisigIsmFactory": "0x14b0F0c0a59704E92f95252cE24Ef6aB1d679733"
},
"moonbasealpha": {
"storageGasOracle": "0xAd754Dbc3F725259E49A90CAB347AeC343D432ed",
@ -86,7 +96,9 @@
"aggregationIsmFactory": "0x759c4Eb4575B651a9f0Fb46653dd7B2F32fD7310",
"routingIsmFactory": "0x561331FafB7f2ABa77E77780178ADdD1A37bdaBD",
"interchainAccountIsm": "0x4B7657A56f51B42A8702957EecFF8CBE86fF48C8",
"interchainAccountRouter": "0xFB03bC45D20848F94DAF6884A92795dd44dDE241"
"interchainAccountRouter": "0xFB03bC45D20848F94DAF6884A92795dd44dDE241",
"merkleRootMultisigIsmFactory": "0x0616A79374e81eB1d2275eCe5837aD050f9c53f1",
"messageIdMultisigIsmFactory": "0x3D696c38Dd958e635f9077e65b64aA9cf7c92627"
},
"optimismgoerli": {
"storageGasOracle": "0xdE72697715aAeC4CaBbD638C0Aba64488005C64b",
@ -101,7 +113,9 @@
"aggregationIsmFactory": "0x919AD6f6DFE1C17b89DAC2eE526F153C08afE305",
"routingIsmFactory": "0x1D7b8aBa2494Ec82e80D6206d2B3df9f8C3F1862",
"interchainQueryRouter": "0x6385E09099d889f912F90c47F10E903fe4feBF69",
"interchainAccountRouter": "0x6f393F8Dfb327d99c946e0Dd2f39F51B1aB446bf"
"interchainAccountRouter": "0x6f393F8Dfb327d99c946e0Dd2f39F51B1aB446bf",
"merkleRootMultisigIsmFactory": "0xC5Bb8CDD44B6c56695df45c7AA8012a97dD6ED13",
"messageIdMultisigIsmFactory": "0x39a8711BF44165A2292Cb5cB43229659c2Bb11c9"
},
"arbitrumgoerli": {
"storageGasOracle": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766",
@ -116,7 +130,9 @@
"aggregationIsmFactory": "0xa27F4a2bD624CF1cB3605c532E95E27BB0AC0BB3",
"routingIsmFactory": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd",
"interchainQueryRouter": "0x5b1E05e1fdDBc0f3d31c4E634ff4D5d84A56deEe",
"interchainAccountRouter": "0x3C636ccC50222a9eb54849C7a622D60a40928a5E"
"interchainAccountRouter": "0x3C636ccC50222a9eb54849C7a622D60a40928a5E",
"merkleRootMultisigIsmFactory": "0x0502Be39aE255D022013DC0aeAa52fDBCD5f0331",
"messageIdMultisigIsmFactory": "0x71eAD731EBdd1334d80a89a572fDFA67830C504c"
},
"sepolia": {
"storageGasOracle": "0x1D5EbC3e15e9ECDe0e3530C85899556797eeaea5",
@ -131,6 +147,8 @@
"aggregationIsmFactory": "0x01812D60958798695391dacF092BAc4a715B1718",
"routingIsmFactory": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c",
"interchainQueryRouter": "0x507C18fa4e3b0ce6beBD494488D62d1ed0fB0555",
"interchainAccountRouter": "0x9cA4A31af0f3a8fe2806599467912809D3e17ECB"
"interchainAccountRouter": "0x9cA4A31af0f3a8fe2806599467912809D3e17ECB",
"merkleRootMultisigIsmFactory": "0xA9999B4abC373FF2BB95B8725FABC96CA883d811",
"messageIdMultisigIsmFactory": "0xCCC126d96efcc342BF2781A7d224D3AB1F25B19C"
}
}

@ -17,6 +17,10 @@ import {
HyperlaneContractsMap,
HyperlaneFactories,
} from '../contracts';
import {
HyperlaneIsmFactory,
moduleMatchesConfig,
} from '../ism/HyperlaneIsmFactory';
import { MultiProvider } from '../providers/MultiProvider';
import { ConnectionClientConfig } from '../router/types';
import { ChainMap, ChainName } from '../types';
@ -28,6 +32,7 @@ import { getContractVerificationInput } from './verify/utils';
export interface DeployerOptions {
logger?: Debugger;
chainTimeoutMs?: number;
ismFactory?: HyperlaneIsmFactory;
}
export abstract class HyperlaneDeployer<
@ -67,7 +72,6 @@ export abstract class HyperlaneDeployer<
async deploy(
configMap: ChainMap<Config>,
): Promise<HyperlaneContractsMap<Factories>> {
await this.checkConfig(configMap);
const configChains = Object.keys(configMap);
const targetChains = this.multiProvider.intersect(
configChains,
@ -152,12 +156,13 @@ export abstract class HyperlaneDeployer<
`Initializing connection client (if not already) on ${local}...`,
);
await this.runIfOwner(local, connectionClient, async () => {
const txOverrides = this.multiProvider.getTransactionOverrides(local);
// set mailbox if not already set (and configured)
if (config.mailbox !== (await connectionClient.mailbox())) {
this.logger(`Set mailbox on (${local})`);
await this.multiProvider.handleTx(
local,
connectionClient.setMailbox(config.mailbox),
connectionClient.setMailbox(config.mailbox, txOverrides),
);
}
@ -171,21 +176,57 @@ export abstract class HyperlaneDeployer<
local,
connectionClient.setInterchainGasPaymaster(
config.interchainGasPaymaster,
txOverrides,
),
);
}
const currentIsm = await connectionClient.interchainSecurityModule();
// set interchain security module if not already set (and configured)
if (
config.interchainSecurityModule &&
config.interchainSecurityModule !==
(await connectionClient.interchainSecurityModule())
) {
this.logger(`Set interchain security module on ${local}`);
let configuredIsm;
if (config.interchainSecurityModule) {
if (typeof config.interchainSecurityModule === 'string') {
configuredIsm = config.interchainSecurityModule;
} else if (this.options?.ismFactory) {
const matches = await moduleMatchesConfig(
local,
currentIsm,
config.interchainSecurityModule,
this.multiProvider,
this.options.ismFactory.chainMap[local],
);
if (matches) {
// when the ISM recursively matches the IsmConfig, we don't need to deploy a new ISM
this.logger(
`ISM matches config for chain ${local}, skipping deploy`,
);
return;
}
const ism = await this.options.ismFactory.deploy(
local,
config.interchainSecurityModule,
);
await this.multiProvider.handleTx(
local,
connectionClient.setInterchainSecurityModule(
ism.address,
txOverrides,
),
);
configuredIsm = ism.address;
} else {
throw new Error('No ISM factory provided');
}
this.logger(
`Set interchain security module on ${local} at ${configuredIsm}`,
);
await this.multiProvider.handleTx(
local,
connectionClient.setInterchainSecurityModule(
config.interchainSecurityModule,
configuredIsm,
txOverrides,
),
);
}

@ -74,7 +74,23 @@ export class HyperlaneIsmFactory extends HyperlaneApp<IsmFactoryFactories> {
config.type === ModuleType.MESSAGE_ID_MULTISIG ||
config.type === ModuleType.LEGACY_MULTISIG
) {
this.logger(`Deploying Multisig ISM to ${chain} for verifying ${origin}`);
switch (config.type) {
case ModuleType.LEGACY_MULTISIG:
this.logger(
`Deploying Legacy Multisig ISM to ${chain} for verifying ${origin}`,
);
break;
case ModuleType.MERKLE_ROOT_MULTISIG:
this.logger(
`Deploying Merkle Root Multisig ISM to ${chain} for verifying ${origin}`,
);
break;
case ModuleType.MESSAGE_ID_MULTISIG:
this.logger(
`Deploying Message ID Multisig ISM to ${chain} for verifying ${origin}`,
);
break;
}
return this.deployMultisigIsm(chain, config, origin);
} else if (config.type === ModuleType.ROUTING) {
this.logger(
@ -104,6 +120,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<IsmFactoryFactories> {
.deploy();
await this.multiProvider.handleTx(chain, multisig.deployTransaction);
const originDomain = this.multiProvider.getDomainId(origin!);
this.logger(`Enrolling validators for ${originDomain}`);
await this.multiProvider.handleTx(
chain,
multisig.enrollValidators([originDomain], [config.validators]),
@ -119,6 +136,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<IsmFactoryFactories> {
config.type === ModuleType.MERKLE_ROOT_MULTISIG
? this.getContracts(chain).merkleRootMultisigIsmFactory
: this.getContracts(chain).messageIdMultisigIsmFactory;
address = await this.deployMOfNFactory(
chain,
multisigIsmFactory,
@ -161,6 +179,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<IsmFactoryFactories> {
moduleAddress,
this.multiProvider.getSigner(chain),
);
this.logger(`Transferring ownership of routing ISM to ${config.owner}`);
await this.multiProvider.handleTx(
chain,
await routingIsm.transferOwnership(config.owner),
@ -199,11 +218,15 @@ export class HyperlaneIsmFactory extends HyperlaneApp<IsmFactoryFactories> {
const address = await factory.getAddress(sorted, threshold);
const provider = this.multiProvider.getProvider(chain);
const code = await provider.getCode(address);
if (code === '0x') {
this.logger(
`Deploying new ${threshold} of ${values.length} address set to ${chain}`,
);
await factory.deploy(sorted, threshold);
const overrides = this.multiProvider.getTransactionOverrides(chain);
const hash = await factory.deploy(sorted, threshold, overrides);
await this.multiProvider.handleTx(chain, hash);
} else {
this.logger(
`Recovered ${threshold} of ${values.length} address set on ${chain}`,
@ -219,7 +242,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<IsmFactoryFactories> {
// body specific logic, as the sample message used when querying the ISM
// sets all of these to zero.
export async function moduleCanCertainlyVerify(
destModuleAddress: types.Address,
destModule: types.Address | IsmConfig,
multiProvider: MultiProvider,
origin: ChainName,
destination: ChainName,
@ -234,65 +257,95 @@ export async function moduleCanCertainlyVerify(
'0x',
);
const provider = multiProvider.getSignerOrProvider(destination);
const module = IInterchainSecurityModule__factory.connect(
destModuleAddress,
provider,
);
try {
const moduleType = await module.moduleType();
if (
moduleType === ModuleType.MERKLE_ROOT_MULTISIG ||
moduleType === ModuleType.LEGACY_MULTISIG ||
moduleType === ModuleType.MESSAGE_ID_MULTISIG
) {
const multisigModule = IMultisigIsm__factory.connect(
destModuleAddress,
provider,
);
const [, threshold] = await multisigModule.validatorsAndThreshold(
message,
);
return threshold > 0;
} else if (moduleType === ModuleType.ROUTING) {
const routingIsm = IRoutingIsm__factory.connect(
destModuleAddress,
provider,
);
const subModule = await routingIsm.route(message);
return moduleCanCertainlyVerify(
subModule,
multiProvider,
origin,
destination,
);
} else if (moduleType === ModuleType.AGGREGATION) {
const aggregationIsm = IAggregationIsm__factory.connect(
destModuleAddress,
provider,
);
const [subModules, threshold] = await aggregationIsm.modulesAndThreshold(
message,
);
let verified = 0;
for (const subModule of subModules) {
const canVerify = await moduleCanCertainlyVerify(
if (typeof destModule === 'string') {
const module = IInterchainSecurityModule__factory.connect(
destModule,
provider,
);
try {
const moduleType = await module.moduleType();
if (
moduleType === ModuleType.MERKLE_ROOT_MULTISIG ||
moduleType === ModuleType.LEGACY_MULTISIG ||
moduleType === ModuleType.MESSAGE_ID_MULTISIG
) {
const multisigModule = IMultisigIsm__factory.connect(
destModule,
provider,
);
const [, threshold] = await multisigModule.validatorsAndThreshold(
message,
);
return threshold > 0;
} else if (moduleType === ModuleType.ROUTING) {
const routingIsm = IRoutingIsm__factory.connect(destModule, provider);
const subModule = await routingIsm.route(message);
return moduleCanCertainlyVerify(
subModule,
multiProvider,
origin,
destination,
);
if (canVerify) {
verified += 1;
} else if (moduleType === ModuleType.AGGREGATION) {
const aggregationIsm = IAggregationIsm__factory.connect(
destModule,
provider,
);
const [subModules, threshold] =
await aggregationIsm.modulesAndThreshold(message);
let verified = 0;
for (const subModule of subModules) {
const canVerify = await moduleCanCertainlyVerify(
subModule,
multiProvider,
origin,
destination,
);
if (canVerify) {
verified += 1;
}
}
return verified >= threshold;
} else {
throw new Error(`Unsupported module type: ${moduleType}`);
}
} catch (e) {
logging.warn(`Error checking module ${destModule}: ${e}`);
return false;
}
} else {
// destModule is an IsmConfig
switch (destModule.type) {
case ModuleType.MERKLE_ROOT_MULTISIG:
case ModuleType.MESSAGE_ID_MULTISIG:
case ModuleType.LEGACY_MULTISIG:
return destModule.threshold > 0;
case ModuleType.ROUTING:
return moduleCanCertainlyVerify(
destModule.domains[destination],
multiProvider,
origin,
destination,
);
case ModuleType.AGGREGATION: {
let verified = 0;
for (const subModule of destModule.modules) {
const canVerify = await moduleCanCertainlyVerify(
subModule,
multiProvider,
origin,
destination,
);
if (canVerify) {
verified += 1;
}
}
return verified >= destModule.threshold;
}
return verified >= threshold;
} else {
throw new Error(`Unsupported module type: ${moduleType}`);
}
} catch (e) {
logging.warn(`Error checking module ${destModuleAddress}: ${e}`);
return false;
}
}
@ -313,9 +366,8 @@ export async function moduleMatchesConfig(
if (actualType !== config.type) return false;
let matches = true;
switch (config.type) {
case ModuleType.MERKLE_ROOT_MULTISIG:
case ModuleType.MESSAGE_ID_MULTISIG: {
// A MultisigIsm matches if validators and threshold match the config
case ModuleType.MERKLE_ROOT_MULTISIG: {
// A MerkleRootMultisigIsm matches if validators and threshold match the config
const expectedAddress =
await contracts.merkleRootMultisigIsmFactory.getAddress(
config.validators.sort(),
@ -324,6 +376,16 @@ export async function moduleMatchesConfig(
matches = utils.eqAddress(expectedAddress, module.address);
break;
}
case ModuleType.MESSAGE_ID_MULTISIG: {
// A MessageIdMultisigIsm matches if validators and threshold match the config
const expectedAddress =
await contracts.messageIdMultisigIsmFactory.getAddress(
config.validators.sort(),
config.threshold,
);
matches = utils.eqAddress(expectedAddress, module.address);
break;
}
case ModuleType.LEGACY_MULTISIG: {
const multisigIsm = LegacyMultisigIsm__factory.connect(
moduleAddress,

@ -22,7 +22,7 @@ export class InterchainAccountDeployer extends HyperlaneRouterDeployer<
InterchainAccountFactories
> {
constructor(multiProvider: MultiProvider) {
super(multiProvider, interchainAccountFactories, {});
super(multiProvider, interchainAccountFactories);
}
router(contracts: HyperlaneContracts<InterchainAccountFactories>): Router {

@ -75,6 +75,9 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
]
> {
const owner = await this.multiProvider.getSignerAddress(chain);
if (typeof config.interchainSecurityModule === 'object') {
throw new Error('Invalid ISM config');
}
return [
config.mailbox,
config.interchainGasPaymaster,

@ -37,6 +37,9 @@ export class InterchainQueryDeployer extends ProxiedRouterDeployer<
]
> {
const owner = await this.multiProvider.getSignerAddress(chain);
if (typeof config.interchainSecurityModule === 'object') {
throw new Error('Invalid ISM config');
}
return [
config.mailbox,
config.interchainGasPaymaster,

@ -230,7 +230,7 @@ export class MultiProvider {
if (TestChains.includes(name as CoreChainName)) {
this.providers[name] = new providers.JsonRpcProvider(
'http://localhost:8545',
'http://127.0.0.1:8545',
31337,
);
} else if (rpcUrls.length) {

@ -39,8 +39,11 @@ export class HyperlaneRouterChecker<
violationType: ConnectionClientViolationType,
) => {
const actual = await router[property]();
// TODO: check for IsmConfig
const expected =
this.configMap[chain][property] ?? ethers.constants.AddressZero;
typeof this.configMap[chain][property] === 'string'
? `${this.configMap[chain][property]}` ?? ethers.constants.AddressZero
: ethers.constants.AddressZero;
if (!utils.eqAddress(actual, expected)) {
const violation: ConnectionClientViolation = {
chain,

@ -1,3 +1,5 @@
import { ethers } from 'ethers';
import {
IInterchainGasPaymaster__factory,
Mailbox__factory,
@ -28,6 +30,7 @@ export abstract class HyperlaneRouterDeployer<
// such ISMs *may* need to override checkConfig to disable this check.
async checkConfig(configMap: ChainMap<Config>): Promise<void> {
for (const [local, config] of Object.entries(configMap)) {
this.logger(`Checking config for ${local}...`);
const signerOrProvider = this.multiProvider.getSignerOrProvider(local);
const localIgp = IInterchainGasPaymaster__factory.connect(
config.interchainGasPaymaster,
@ -37,10 +40,19 @@ export abstract class HyperlaneRouterDeployer<
config.mailbox,
signerOrProvider,
);
const localIsm =
config.interchainSecurityModule ?? (await localMailbox.defaultIsm());
let localIsm;
if (
!config.interchainSecurityModule ||
config.interchainSecurityModule === ethers.constants.AddressZero
) {
localIsm = await localMailbox.defaultIsm();
} else {
localIsm = config.interchainSecurityModule;
}
const remotes = Object.keys(configMap).filter((c) => c !== local);
for (const remote of remotes) {
this.logger(`Checking origin ${remote}...`);
// Try to confirm that the IGP supports delivery to all remotes
try {
await localIgp.quoteGasPayment(

@ -17,16 +17,14 @@ export abstract class RouterApp<
getSecurityModules = (): Promise<ChainMap<types.Address>> =>
promiseObjAll(
objMap(this.contractsMap, (_, contracts) =>
objMap(this.chainMap, (_, contracts) =>
this.router(contracts).interchainSecurityModule(),
),
);
getOwners = (): Promise<ChainMap<types.Address>> =>
promiseObjAll(
objMap(this.contractsMap, (_, contracts) =>
this.router(contracts).owner(),
),
objMap(this.chainMap, (_, contracts) => this.router(contracts).owner()),
);
}

@ -6,6 +6,7 @@ import type { types } from '@hyperlane-xyz/utils';
import { HyperlaneFactories } from '../contracts';
import { CheckerViolation } from '../deploy/types';
import { IsmConfig } from '../ism/types';
export type OwnableConfig = {
owner: types.Address;
@ -32,7 +33,7 @@ export type ProxiedFactories = HyperlaneFactories & {
export type ConnectionClientConfig = {
mailbox: types.Address;
interchainGasPaymaster: types.Address;
interchainSecurityModule?: types.Address;
interchainSecurityModule?: types.Address | IsmConfig;
};
export enum ConnectionClientViolationType {

Loading…
Cancel
Save