UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

325 lines 15.2 kB
/* eslint-disable no-console */ import { expect } from 'chai'; import hre from 'hardhat'; import { randomInt } from '@hyperlane-xyz/utils'; import { TestChainName, testChains } from '../consts/testChains.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { randomAddress, randomDeployableIsmConfig, randomMultisigIsmConfig, } from '../test/testUtils.js'; import { HyperlaneIsmFactory } from './HyperlaneIsmFactory.js'; import { IsmType, } from './types.js'; import { moduleMatchesConfig } from './utils.js'; describe('HyperlaneIsmFactory', async () => { let ismFactoryDeployer; let ismFactory; let multiProvider; let exampleRoutingConfig; let mailboxAddress; let newMailboxAddress; let contractsMap = {}; const chain = TestChainName.test1; before(async () => { const [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); contractsMap = await ismFactoryDeployer.deploy(multiProvider.mapKnownChains(() => ({}))); ismFactory = new HyperlaneIsmFactory(contractsMap, multiProvider); mailboxAddress = (await new TestCoreDeployer(multiProvider, ismFactory).deployApp()).getContracts(chain).mailbox.address; newMailboxAddress = (await new TestCoreDeployer(multiProvider, ismFactory).deployApp()).getContracts(chain).mailbox.address; }); beforeEach(async () => { const [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); ismFactory = new HyperlaneIsmFactory(contractsMap, multiProvider); exampleRoutingConfig = { type: IsmType.ROUTING, owner: await multiProvider.getSignerAddress(chain), domains: Object.fromEntries(testChains .filter((c) => c !== TestChainName.test1 && c !== TestChainName.test4) .map((c) => [c, randomMultisigIsmConfig(3, 5)])), }; }); it('deploys a simple ism', async () => { const config = randomMultisigIsmConfig(3, 5); const ism = await ismFactory.deploy({ destination: chain, config }); const matches = await moduleMatchesConfig(chain, ism.address, config, ismFactory.multiProvider, ismFactory.getContracts(chain)); expect(matches).to.be.true; }); it('deploys a trusted relayer ism', async () => { const relayer = randomAddress(); const config = { type: IsmType.TRUSTED_RELAYER, relayer, }; const ism = (await ismFactory.deploy({ destination: chain, config, mailbox: mailboxAddress, })); const matches = await moduleMatchesConfig(chain, ism.address, config, ismFactory.multiProvider, ismFactory.getContracts(chain)); expect(matches).to.be.true; }); for (let i = 0; i < 16; i++) { it('deploys a random ism config', async () => { const config = randomDeployableIsmConfig(); let ismAddress; try { const ism = await ismFactory.deploy({ destination: chain, config, mailbox: mailboxAddress, }); ismAddress = ism.address; } catch (e) { console.error('Failed to deploy random ism config', e); console.error(JSON.stringify(config, null, 2)); process.exit(1); } try { const matches = await moduleMatchesConfig(chain, ismAddress, config, ismFactory.multiProvider, ismFactory.getContracts(chain)); expect(matches).to.be.true; } catch (e) { console.error('Failed to match random ism config', e); console.error(JSON.stringify(config, null, 2)); process.exit(1); } }); } for (const type of [IsmType.ROUTING, IsmType.FALLBACK_ROUTING]) { it(`deploys ${type} routingIsm with correct routes`, async () => { exampleRoutingConfig.type = type; const ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const matches = await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress); expect(matches).to.be.true; }); it(`update route in an existing ${type}`, async () => { exampleRoutingConfig.type = type; let matches = true; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; // changing the type of a domain should enroll the domain exampleRoutingConfig.domains['test2'].type = IsmType.MESSAGE_ID_MULTISIG; ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); matches = matches && existingIsm === ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(matches).to.be.true; }); it(`should skip deployment with warning if no chain metadata configured ${type}`, async () => { exampleRoutingConfig.type = type; let matches = true; exampleRoutingConfig.domains['test4'] = { type: IsmType.MESSAGE_ID_MULTISIG, threshold: 1, validators: [randomAddress()], }; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; matches = matches && existingIsm === ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); exampleRoutingConfig.domains['test5'] = { type: IsmType.MESSAGE_ID_MULTISIG, threshold: 1, validators: [randomAddress()], }; ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); matches = matches && existingIsm === ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(matches).to.be.true; }); it(`deletes route in an existing ${type}`, async () => { exampleRoutingConfig.type = type; let matches = true; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; // deleting the domain should unenroll the domain delete exampleRoutingConfig.domains['test3']; ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); matches = matches && existingIsm == ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(matches).to.be.true; }); it(`deletes route in an existing ${type} even if not in multiprovider`, async () => { exampleRoutingConfig.type = type; let matches = true; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; const domainsBefore = await ism.domains(); // deleting the domain and removing from multiprovider should unenroll the domain // NB: we'll deploy new multisigIsms for the domains bc of new factories but the routingIsm address should be the same because of existingIsmAddress delete exampleRoutingConfig.domains['test3']; multiProvider = multiProvider.intersect([ TestChainName.test1, TestChainName.test2, TestChainName.test4, ]).result; ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); ismFactory = new HyperlaneIsmFactory(await ismFactoryDeployer.deploy(multiProvider.mapKnownChains(() => ({}))), multiProvider); new TestCoreDeployer(multiProvider, ismFactory); ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); const domainsAfter = await ism.domains(); matches = matches && existingIsm == ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(domainsBefore.length - 1).to.equal(domainsAfter.length); expect(matches).to.be.true; }); it(`updates owner in an existing ${type}`, async () => { exampleRoutingConfig.type = type; let matches = true; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; // change the owner exampleRoutingConfig.owner = randomAddress(); ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); matches = matches && existingIsm == ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(matches).to.be.true; }); it(`no changes to an existing ${type} means no redeployment or updates`, async () => { exampleRoutingConfig.type = type; let matches = true; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; // using the same config should not change anything ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); matches = matches && existingIsm === ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(matches).to.be.true; }); it(`redeploy same config if the deployer doesn't have ownership of ${type}`, async () => { exampleRoutingConfig.type = type; let matches = true; exampleRoutingConfig.owner = randomAddress(); let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: mailboxAddress, }); matches = matches && existingIsm !== ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress)); expect(matches).to.be.true; }); } it(`should deploy a ${IsmType.AMOUNT_ROUTING}`, async () => { const config = { type: IsmType.AMOUNT_ROUTING, lowerIsm: randomDeployableIsmConfig(), upperIsm: randomDeployableIsmConfig(), threshold: randomInt(1e6, 1), }; const ism = await ismFactory.deploy({ destination: chain, config, mailbox: mailboxAddress, }); const matches = await moduleMatchesConfig(chain, ism.address, config, ismFactory.multiProvider, ismFactory.getContracts(chain), mailboxAddress); expect(matches).to.be.true; }); it(`redeploy same config if the mailbox address changes for defaultFallbackRoutingIsm`, async () => { exampleRoutingConfig.type = IsmType.FALLBACK_ROUTING; let matches = true; let ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, mailbox: mailboxAddress, }); const existingIsm = ism.address; ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, existingIsmAddress: ism.address, mailbox: newMailboxAddress, }); matches = matches && existingIsm !== ism.address && (await moduleMatchesConfig(chain, ism.address, exampleRoutingConfig, ismFactory.multiProvider, ismFactory.getContracts(chain), newMailboxAddress)); expect(matches).to.be.true; }); }); //# sourceMappingURL=HyperlaneIsmFactory.hardhat-test.js.map