@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
325 lines • 15.2 kB
JavaScript
/* 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