Skip to content

Commit

Permalink
Derive CoreConfig from Mailbox address (#3657)
Browse files Browse the repository at this point in the history
### Description
Creates a new class to derive CoreConfig from Mailbox and their
respective hooks and isms

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

- Fixes #3578 

### Backward compatibility
Yes

### Testing
Unit Tests through CoreDeployer.hardhat-test.ts
  • Loading branch information
ltyu authored Apr 24, 2024
1 parent 6f35e4c commit 811ecfb
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-dryers-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': minor
---

Add EvmCoreReader, minor updates.
1 change: 1 addition & 0 deletions typescript/sdk/src/consts/crud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_CONTRACT_READ_CONCURRENCY = 20;
70 changes: 68 additions & 2 deletions typescript/sdk/src/core/CoreDeployer.hardhat-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { objMap, promiseObjAll } from '@hyperlane-xyz/utils';
import { TestChains } from '../consts/chains.js';
import { HyperlaneContractsMap } from '../contracts/types.js';
import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js';
import { HookConfig } from '../hook/types.js';
import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js';
import { DerivedIsmConfigWithAddress } from '../ism/read.js';
import { AggregationIsmConfig, IsmType } from '../ism/types.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { testCoreConfig } from '../test/testUtils.js';
Expand All @@ -18,6 +20,7 @@ import { HyperlaneCore } from './HyperlaneCore.js';
import { HyperlaneCoreChecker } from './HyperlaneCoreChecker.js';
import { HyperlaneCoreDeployer } from './HyperlaneCoreDeployer.js';
import { CoreFactories } from './contracts.js';
import { EvmCoreReader } from './read.js';
import { CoreConfig } from './types.js';

describe('core', async () => {
Expand All @@ -38,9 +41,7 @@ describe('core', async () => {
const ismFactories = await proxyFactoryDeployer.deploy(coreConfig);
ismFactory = new HyperlaneIsmFactory(ismFactories, multiProvider);
deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory);
});

it('deploys', async () => {
contracts = await deployer.deploy(coreConfig);
core = new HyperlaneCore(contracts, multiProvider);
});
Expand Down Expand Up @@ -120,6 +121,71 @@ describe('core', async () => {
});
});

describe('CoreConfigReader', async () => {
beforeEach(async () => {
contracts = await deployer.deploy(coreConfig);
});

async function deriveCoreConfig(chainName: string, mailboxAddress: string) {
return await new EvmCoreReader(multiProvider, chainName).deriveCoreConfig(
mailboxAddress,
);
}
it('should derive defaultIsm correctly', async () => {
await promiseObjAll(
objMap(contracts, async (chainName, contract) => {
const coreConfigOnChain = await deriveCoreConfig(
chainName,
contract.mailbox.address,
);

// Cast because we don't expect the 'string' type
const defaultIsmOnchain =
coreConfigOnChain.defaultIsm as DerivedIsmConfigWithAddress;
const defaultIsmTest = coreConfig[chainName]
.defaultIsm as DerivedIsmConfigWithAddress;

expect(defaultIsmOnchain.type).to.be.equal(defaultIsmTest.type);
}),
);
});
it('should derive defaultHook correctly', async () => {
await promiseObjAll(
objMap(contracts, async (chainName, contract) => {
const coreConfigOnChain = await deriveCoreConfig(
chainName,
contract.mailbox.address,
);

// Cast because we don't expect the 'string' type
const defaultHookOnchain =
coreConfigOnChain.defaultHook as HookConfig;
const defaultHookTest = coreConfig[chainName]
.defaultHook as HookConfig;

expect(defaultHookOnchain.type).to.be.equal(defaultHookTest.type);
}),
);
});
it('should derive requiredHook correctly', async () => {
await promiseObjAll(
objMap(contracts, async (chainName, contract) => {
const coreConfigOnChain = await deriveCoreConfig(
chainName,
contract.mailbox.address,
);
const requiredHookOnchain = coreConfigOnChain.requiredHook;
const requiredHookTest = coreConfig[chainName].requiredHook;

// Test all the fields
objMap(requiredHookTest, (key, value) => {
expect(requiredHookOnchain[key]).to.be.equal(value);
});
}),
);
});
});

describe('failure modes', async () => {
beforeEach(async () => {
deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory);
Expand Down
61 changes: 61 additions & 0 deletions typescript/sdk/src/core/read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { providers } from 'ethers';

import { Mailbox__factory } from '@hyperlane-xyz/core';
import { Address, objMap, promiseObjAll } from '@hyperlane-xyz/utils';

import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/crud.js';
import { EvmHookReader } from '../hook/read.js';
import { EvmIsmReader } from '../ism/read.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainName } from '../types.js';

import { CoreConfig } from './types.js';

interface CoreReader {
deriveCoreConfig(address: Address): Promise<CoreConfig>;
}

export class EvmCoreReader implements CoreReader {
provider: providers.Provider;
evmHookReader: EvmHookReader;
evmIsmReader: EvmIsmReader;
constructor(
protected readonly multiProvider: MultiProvider,
protected readonly chain: ChainName,
protected readonly concurrency: number = DEFAULT_CONTRACT_READ_CONCURRENCY,
) {
this.provider = this.multiProvider.getProvider(chain);
this.evmHookReader = new EvmHookReader(multiProvider, chain, concurrency);
this.evmIsmReader = new EvmIsmReader(multiProvider, chain, concurrency);
}

/**
* Derives the core configuration for a given Mailbox address.
*
* @param address - The address of the Mailbox contract.
* @returns A promise that resolves to the CoreConfig object, containing the owner, default ISM, default Hook, and required Hook configurations.
*/
async deriveCoreConfig(address: Address): Promise<CoreConfig> {
const mailbox = Mailbox__factory.connect(address, this.provider);
const [defaultIsm, defaultHook, requiredHook] = await Promise.all([
mailbox.defaultIsm(),
mailbox.defaultHook(),
mailbox.requiredHook(),
]);

// Parallelize each configuration request
const results = await promiseObjAll(
objMap(
{
owner: mailbox.owner(),
defaultIsm: this.evmIsmReader.deriveIsmConfig(defaultIsm),
defaultHook: this.evmHookReader.deriveHookConfig(defaultHook),
requiredHook: this.evmHookReader.deriveHookConfig(requiredHook),
},
async (_, readerCall) => readerCall,
),
);

return results as CoreConfig;
}
}
5 changes: 3 additions & 2 deletions typescript/sdk/src/hook/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
rootLogger,
} from '@hyperlane-xyz/utils';

import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/crud.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainName } from '../types.js';

Expand Down Expand Up @@ -74,8 +75,8 @@ export class EvmHookReader implements HookReader {

constructor(
protected readonly multiProvider: MultiProvider,
chain: ChainName,
protected readonly concurrency: number = 20,
protected readonly chain: ChainName,
protected readonly concurrency: number = DEFAULT_CONTRACT_READ_CONCURRENCY,
) {
this.provider = this.multiProvider.getProvider(chain);
}
Expand Down
1 change: 1 addition & 0 deletions typescript/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export {
} from './gas/types.js';
export { HyperlaneHookDeployer } from './hook/HyperlaneHookDeployer.js';
export { EvmHookReader } from './hook/read.js';
export { EvmCoreReader } from './core/read.js';
export {
AggregationHookConfig,
DomainRoutingHookConfig,
Expand Down
5 changes: 3 additions & 2 deletions typescript/sdk/src/ism/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
rootLogger,
} from '@hyperlane-xyz/utils';

import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/crud.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainName } from '../types.js';

Expand Down Expand Up @@ -63,8 +64,8 @@ export class EvmIsmReader implements IsmReader {

constructor(
protected readonly multiProvider: MultiProvider,
chain: ChainName,
protected readonly concurrency: number = 20,
protected readonly chain: ChainName,
protected readonly concurrency: number = DEFAULT_CONTRACT_READ_CONCURRENCY,
) {
this.provider = this.multiProvider.getProvider(chain);
}
Expand Down

0 comments on commit 811ecfb

Please sign in to comment.