UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

292 lines 13.2 kB
import { expect } from 'chai'; import { constants } from 'ethers'; import hre from 'hardhat'; import { Mailbox__factory, ProxyAdmin__factory, TestRecipient__factory, TimelockController__factory, ValidatorAnnounce__factory, } from '@hyperlane-xyz/core'; import { objMap } from '@hyperlane-xyz/utils'; import { TestChainName } from '../consts/testChains.js'; import { HookType } from '../hook/types.js'; import { IsmType } from '../ism/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { randomAddress, testCoreConfig } from '../test/testUtils.js'; import { normalizeConfig } from '../utils/ism.js'; import { EvmCoreModule } from './EvmCoreModule.js'; describe('EvmCoreModule', async () => { const CHAIN = TestChainName.test4; const DELAY = 1892391283182; let config; let signer; let multiProvider; let evmCoreModule; let proxyAdminContract; let mailboxContract; let validatorAnnounceContract; let testRecipientContract; let timelockControllerContract; async function sendTxs(txs) { for (const tx of txs) { await multiProvider.sendTransaction(CHAIN, tx); } } before(async () => { [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); config = { ...testCoreConfig([CHAIN])[CHAIN], owner: signer.address, upgrade: { timelock: { delay: DELAY, roles: { executor: signer.address, proposer: signer.address, }, }, }, }; evmCoreModule = await EvmCoreModule.create({ chain: CHAIN, config, multiProvider, }); const { proxyAdmin, mailbox, validatorAnnounce, testRecipient, timelockController, } = evmCoreModule.serialize(); proxyAdminContract = ProxyAdmin__factory.connect(proxyAdmin, multiProvider.getProvider(CHAIN)); mailboxContract = Mailbox__factory.connect(mailbox, multiProvider.getProvider(CHAIN)); validatorAnnounceContract = ValidatorAnnounce__factory.connect(validatorAnnounce, multiProvider.getProvider(CHAIN)); testRecipientContract = TestRecipient__factory.connect(testRecipient, multiProvider.getProvider(CHAIN)); timelockControllerContract = TimelockController__factory.connect(timelockController, multiProvider.getProvider(CHAIN)); }); describe('Create', async () => { it('should create deploy an ICA', () => { const { interchainAccountRouter } = evmCoreModule.serialize(); expect(interchainAccountRouter).to.exist; }); it('should deploy ISM factories', () => { // Each ISM factory is a contract that is deployed by the core module // Ignore IGP because it's not part of the default config const { interchainGasPaymaster: _, ...coreContracts } = evmCoreModule.serialize(); objMap(coreContracts, (_, address) => { expect(address).to.exist; expect(address).to.not.equal(constants.AddressZero); }); }); it('should deploy proxyAdmin', () => { expect(evmCoreModule.serialize().proxyAdmin).to.exist; }); it('should set proxyAdmin owner to deployer', async () => { expect(await proxyAdminContract.owner()).to.equal(signer.address); }); it('should deploy mailbox', async () => { const mailboxAddress = evmCoreModule.serialize().mailbox; expect(mailboxAddress).to.exist; // Check that it's actually a mailbox by calling one of it's methods expect(await mailboxContract.localDomain()).to.equal(multiProvider.getDomainId(CHAIN)); }); it('should set mailbox owner to config owner', async () => { expect(await mailboxContract.owner()).to.equal(config.owner); }); it('should deploy mailbox default Ism', async () => { expect(await mailboxContract.defaultIsm()).to.not.equal(constants.AddressZero); }); it('should deploy mailbox default hook', async () => { expect(await mailboxContract.defaultHook()).to.not.equal(constants.AddressZero); }); it('should deploy mailbox required hook', async () => { expect(await mailboxContract.requiredHook()).to.not.equal(constants.AddressZero); }); it('should deploy validatorAnnounce', async () => { expect(evmCoreModule.serialize().validatorAnnounce).to.exist; expect(await validatorAnnounceContract.owner()).to.equal(signer.address); }); it('should deploy testRecipient', async () => { expect(evmCoreModule.serialize().testRecipient).to.exist; expect(await testRecipientContract.owner()).to.equal(signer.address); }); it('should deploy timelock if upgrade is set', async () => { expect(evmCoreModule.serialize().timelockController).to.exist; expect(await timelockControllerContract.getMinDelay()).to.equal(DELAY); }); }); describe(`${EvmCoreModule.prototype.update.name} (Ism Updates)`, async () => { const ismConfigToUpdate = [ { type: IsmType.TRUSTED_RELAYER, relayer: randomAddress(), }, { type: IsmType.FALLBACK_ROUTING, owner: randomAddress(), domains: {}, }, { type: IsmType.PAUSABLE, owner: randomAddress(), paused: false, }, ]; it('should deploy and set a new defaultIsm', async () => { for (const ismConfig of ismConfigToUpdate) { const evmCoreModuleInstance = new EvmCoreModule(multiProvider, { chain: CHAIN, config, addresses: { ...evmCoreModule.serialize(), }, }); const expectedConfig = { ...(await evmCoreModuleInstance.read()), defaultIsm: ismConfig, }; await sendTxs(await evmCoreModuleInstance.update(expectedConfig)); const updatedDefaultIsm = normalizeConfig((await evmCoreModuleInstance.read()).defaultIsm); expect(updatedDefaultIsm).to.deep.equal(ismConfig); } }); it('should not deploy and set a new Ism if the config is the same', async () => { const evmCoreModuleInstance = new EvmCoreModule(multiProvider, { chain: CHAIN, config, addresses: { ...evmCoreModule.serialize(), }, }); const existingConfig = await evmCoreModuleInstance.read(); const updateTxs = await evmCoreModuleInstance.update(existingConfig); expect(updateTxs.length).to.equal(0); }); it('should update a mutable Ism', async () => { const evmCoreModule = await EvmCoreModule.create({ multiProvider, chain: CHAIN, config: { ...config, defaultIsm: { type: IsmType.ROUTING, owner: signer.address, domains: {}, }, }, }); const defaultIsmToUpdate = normalizeConfig({ type: IsmType.ROUTING, owner: signer.address, domains: { test2: { type: IsmType.TEST_ISM }, }, }); const updates = await evmCoreModule.update({ ...config, defaultIsm: defaultIsmToUpdate, }); expect(updates.length).to.equal(1); await sendTxs(updates); const latestDefaultIsmConfig = normalizeConfig((await evmCoreModule.read()).defaultIsm); expect(latestDefaultIsmConfig).to.deep.equal(defaultIsmToUpdate); }); it('should update the owner only if they are different', async () => { const evmCoreModule = await EvmCoreModule.create({ chain: CHAIN, config, multiProvider, }); const newOwner = randomAddress(); let latestConfig = normalizeConfig(await evmCoreModule.read()); expect(latestConfig.owner).to.not.equal(newOwner); await sendTxs(await evmCoreModule.update({ ...config, owner: newOwner, })); latestConfig = normalizeConfig(await evmCoreModule.read()); expect(latestConfig.owner).to.equal(newOwner); // No op if the same owner const txs = await evmCoreModule.update({ ...config, owner: newOwner, }); expect(txs.length).to.equal(0); }); }); describe(`${EvmCoreModule.prototype.update.name} (Hook Updates)`, async () => { const hookFields = [ 'requiredHook', 'defaultHook', ]; const testHookUpdate = async (hookType, newHookConfig, hookAddressGetter) => { const evmCoreModuleInstance = new EvmCoreModule(multiProvider, { chain: CHAIN, config, addresses: { ...evmCoreModule.serialize(), }, }); const expectedConfig = { ...(await evmCoreModuleInstance.read()), [hookType]: newHookConfig, }; const updateTxs = await evmCoreModuleInstance.update(expectedConfig); expect(updateTxs.length).to.be.greaterThan(0); await sendTxs(updateTxs); const updatedConfig = await evmCoreModuleInstance.read(); expect(updatedConfig[hookType]).to.not.equal(constants.AddressZero); // Verify the hook was actually updated by checking the mailbox const newHookAddress = await hookAddressGetter(); expect(newHookAddress).to.not.equal(constants.AddressZero); }; const hookAddressGetters = { requiredHook: () => mailboxContract.requiredHook(), defaultHook: () => mailboxContract.defaultHook(), }; for (const hookField of hookFields) { it(`should update the ${hookField}`, async () => { const newHookConfig = { type: HookType.ROUTING, domains: {}, owner: signer.address, }; await testHookUpdate(hookField, newHookConfig, hookAddressGetters[hookField]); }); it(`should deploy a new ${hookField} when config requires new deployment`, async () => { const evmCoreModuleInstance = new EvmCoreModule(multiProvider, { chain: CHAIN, config, addresses: { ...evmCoreModule.serialize(), }, }); // Get the current hook address before update const initialConfig = await evmCoreModuleInstance.read(); const currentHookAddress = await hookAddressGetters[hookField](); // Create a different hook config that requires deployment const newHookConfig = { type: HookType.PROTOCOL_FEE, maxProtocolFee: '100000000000000000', // 0.1 ether in wei protocolFee: '50000000000000000', // 0.05 ether in wei beneficiary: randomAddress(), owner: signer.address, }; const expectedConfig = { ...initialConfig, [hookField]: newHookConfig, }; const updateTxs = await evmCoreModuleInstance.update(expectedConfig); expect(updateTxs.length).to.be.greaterThan(0); await sendTxs(updateTxs); const newHookAddress = await hookAddressGetters[hookField](); expect(newHookAddress).to.not.equal(currentHookAddress); expect(newHookAddress).to.not.equal(constants.AddressZero); }); } it('should not update hooks if config is the same', async () => { const evmCoreModuleInstance = new EvmCoreModule(multiProvider, { chain: CHAIN, config, addresses: { ...evmCoreModule.serialize(), }, }); const existingConfig = await evmCoreModuleInstance.read(); const updateTxs = await evmCoreModuleInstance.update(existingConfig); expect(updateTxs.length).to.equal(0); }); }); }); //# sourceMappingURL=EvmCoreModule.hardhat-test.js.map