UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

206 lines 10.4 kB
import '@nomiclabs/hardhat-waffle'; import { assert, expect } from 'chai'; import hre from 'hardhat'; import sinon from 'sinon'; import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { TestChainName, testChains } from '../consts/testChains.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { IsmType, } from '../ism/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { testCoreConfig } from '../test/testUtils.js'; import { EvmCoreReader } from './EvmCoreReader.js'; import { EvmIcaModule } from './EvmIcaModule.js'; import { HyperlaneCore } from './HyperlaneCore.js'; import { HyperlaneCoreChecker } from './HyperlaneCoreChecker.js'; import { HyperlaneCoreDeployer } from './HyperlaneCoreDeployer.js'; describe('core', async () => { let multiProvider; let deployer; let core; let contracts; let coreConfig; let ismFactory; let signer; before(async () => { [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); const proxyFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); coreConfig = testCoreConfig(testChains, signer.address); const ismFactories = await proxyFactoryDeployer.deploy(coreConfig); ismFactory = new HyperlaneIsmFactory(ismFactories, multiProvider); deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory); contracts = await deployer.deploy(coreConfig); core = new HyperlaneCore(contracts, multiProvider); }); describe('idempotency', () => { beforeEach(async () => { contracts = await deployer.deploy(coreConfig); }); it('rotates default and required hooks and recovers artifacts', async () => { const getHooks = async (contracts) => promiseObjAll(objMap(contracts, async (_, { mailbox }) => ({ default: await mailbox.defaultHook(), required: await mailbox.requiredHook(), }))); const hooksBefore = await getHooks(contracts); const updatedConfig = objMap(coreConfig, (_, config) => ({ ...config, defaultHook: config.requiredHook, requiredHook: config.defaultHook, })); const [signer] = await hre.ethers.getSigners(); const nonceBefore = await signer.getTransactionCount(); const updatedContracts = await deployer.deploy(updatedConfig); const hooksAfter = await getHooks(updatedContracts); expect(hooksBefore).to.deep.equal(objMap(hooksAfter, (_, res) => ({ required: res.default, default: res.required, }))); // number of set hook transactions const numTransactions = 2 * testChains.length; const nonceAfter = await signer.getTransactionCount(); expect(nonceAfter).to.equal(nonceBefore + numTransactions); }); it('rotates default ISMs', async () => { const testIsm = await contracts.test1.mailbox.defaultIsm(); const updatedConfig = objMap(coreConfig, (_, config) => { const ismConfig = { type: IsmType.AGGREGATION, modules: [testIsm, testIsm], threshold: 2, }; return { ...config, defaultIsm: ismConfig, }; }); const [signer] = await hre.ethers.getSigners(); const nonceBefore = await signer.getTransactionCount(); await deployer.deploy(updatedConfig); // 3x1 for aggregation ISM deploy // 3x1 for setting ISM transaction for mailbox // 3x1 for setting ISM transaction for test recipient const numTransactions = 3 * testChains.length; const nonceAfter = await signer.getTransactionCount(); expect(nonceAfter).to.equal(nonceBefore + numTransactions); }); }); describe('CoreConfigReader', async () => { let icaRouterAddressMap; beforeEach(async () => { contracts = await deployer.deploy(coreConfig); const icaMap = {}; for (const chain of Object.keys(contracts)) { const { interchainAccountRouter } = (await EvmIcaModule.create({ chain, multiProvider: multiProvider, config: { commitmentIsm: { type: IsmType.OFFCHAIN_LOOKUP, urls: ['https://commitment-read-ism.hyperlane.xyz'], owner: signer.address, }, mailbox: contracts[chain].mailbox.address, owner: signer.address, }, })).serialize(); icaMap[chain] = interchainAccountRouter; } icaRouterAddressMap = icaMap; }); async function deriveCoreConfig(chainName, mailboxAddress, icaRouterAddress) { return new EvmCoreReader(multiProvider, chainName).deriveCoreConfig({ mailbox: mailboxAddress, interchainAccountRouter: icaRouterAddress, }); } it('should derive defaultIsm correctly', async () => { await promiseObjAll(objMap(contracts, async (chainName, contract) => { const coreConfigOnChain = await deriveCoreConfig(chainName, contract.mailbox.address, icaRouterAddressMap[chainName]); // Cast because we don't expect the 'string' type const { address: _, ...defaultIsmOnchain } = coreConfigOnChain.defaultIsm; const defaultIsmTest = coreConfig[chainName] .defaultIsm; expect(defaultIsmOnchain).to.deep.equal(defaultIsmTest); })); }); it('should derive defaultHook correctly', async () => { await promiseObjAll(objMap(contracts, async (chainName, contract) => { const coreConfigOnChain = await deriveCoreConfig(chainName, contract.mailbox.address, icaRouterAddressMap[chainName]); // Cast because we don't expect the 'string' type const { address: _, ...defaultHookOnchain } = coreConfigOnChain.defaultHook; const defaultHookTest = coreConfig[chainName] .defaultHook; expect(defaultHookOnchain).to.deep.equal(defaultHookTest); })); }); it('should derive requiredHook correctly', async () => { await promiseObjAll(objMap(contracts, async (chainName, contract) => { const coreConfigOnChain = await deriveCoreConfig(chainName, contract.mailbox.address, icaRouterAddressMap[chainName]); const { address: _, ...requiredHookOnchain } = coreConfigOnChain.requiredHook; const requiredHookTest = coreConfig[chainName].requiredHook; expect(requiredHookOnchain).to.deep.equal(requiredHookTest); })); }); }); describe('failure modes', async () => { beforeEach(async () => { deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory); const stub = sinon.stub(deployer, 'deployContracts'); stub.withArgs('test3', sinon.match.any).rejects(); // @ts-ignore deployer.deployContracts.callThrough(); try { await deployer.deploy(coreConfig); // eslint-disable-next-line no-empty } catch { } }); afterEach(async () => { sinon.restore(); }); it('persists partial failure', async () => { expect(deployer.deployedContracts).to.have.keys([ TestChainName.test1, TestChainName.test2, ]); }); it('can be resumed from partial (chain) failure', async () => { sinon.restore(); // restore normal deployer behavior and test3 will be deployed const result = await deployer.deploy(coreConfig); expect(result).to.have.keys(testChains); // Each test network key has entries about the other test networks, where ISM details are stored. // With this exception, the keys should be the same, so we check the intersections for equality. const testnetKeysIntersection = Object.keys(result.test1).filter((key) => Object.keys(result.test2).includes(key) && Object.keys(result.test3).includes(key)); assert(testnetKeysIntersection.length > 0, 'there are no common core contracts deployed between the local testnets'); }); it('can be resumed from partial contracts', async () => { sinon.restore(); // restore normal deployer behavior //@ts-ignore operand not optional, ignore for this test delete deployer.deployedContracts.test2.multisigIsm; //@ts-ignore operand not optional, ignore for this test delete deployer.deployedContracts.test2.mailbox; const result = await deployer.deploy(coreConfig); const testnetKeysIntersection = Object.keys(result.test1).filter((key) => Object.keys(result.test2).includes(key) && Object.keys(result.test3).includes(key)); assert(testnetKeysIntersection.length > 0, 'there are no common core contracts deployed between the local testnets'); }); it('times out ', async () => { // @ts-ignore deployer.chainTimeoutMs = 1; try { await deployer.deploy(coreConfig); } catch { // TODO: figure out how to test specific error case // expect(e.message).to.include('Timed out in 1ms'); } }); }); it('checks', async () => { const checker = new HyperlaneCoreChecker(multiProvider, core, coreConfig, ismFactory, {}); await checker.check(); }); }); //# sourceMappingURL=CoreDeployer.hardhat-test.js.map