@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
131 lines • 6.9 kB
JavaScript
import { IPostDispatchHook__factory, } from '@hyperlane-xyz/core';
import { addBufferToGasLimit, isZeroishAddress, rootLogger, } from '@hyperlane-xyz/utils';
import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer.js';
import { HyperlaneHookDeployer } from '../hook/HyperlaneHookDeployer.js';
import { moduleMatchesConfig } from '../ism/utils.js';
import { TestRecipientDeployer } from './TestRecipientDeployer.js';
import { coreFactories } from './contracts.js';
export class HyperlaneCoreDeployer extends HyperlaneDeployer {
ismFactory;
hookDeployer;
testRecipient;
constructor(multiProvider, ismFactory, contractVerifier, concurrentDeploy = false, chainTimeoutMs = 1000 * 60 * 10) {
super(multiProvider, coreFactories, {
logger: rootLogger.child({ module: 'CoreDeployer' }),
chainTimeoutMs,
ismFactory,
contractVerifier,
concurrentDeploy,
});
this.ismFactory = ismFactory;
this.hookDeployer = new HyperlaneHookDeployer(multiProvider, {}, ismFactory, contractVerifier, concurrentDeploy);
this.testRecipient = new TestRecipientDeployer(multiProvider, contractVerifier, concurrentDeploy);
}
cacheAddressesMap(addressesMap) {
this.hookDeployer.cacheAddressesMap(addressesMap);
this.testRecipient.cacheAddressesMap(addressesMap);
super.cacheAddressesMap(addressesMap);
}
async deployMailbox(chain, config, proxyAdmin) {
const domain = this.multiProvider.getDomainId(chain);
const mailbox = await this.deployProxiedContract(chain, 'mailbox', 'mailbox', proxyAdmin, [domain]);
let defaultIsm = await mailbox.defaultIsm();
const matches = await moduleMatchesConfig(chain, defaultIsm, config.defaultIsm, this.multiProvider, this.ismFactory.getContracts(chain));
if (!matches) {
this.logger.debug('Deploying default ISM');
defaultIsm = await this.deployIsm(chain, config.defaultIsm, mailbox.address);
}
this.cachedAddresses[chain].interchainSecurityModule = defaultIsm;
const hookAddresses = { mailbox: mailbox.address, proxyAdmin };
this.logger.debug('Deploying default hook');
const defaultHook = await this.deployHook(chain, config.defaultHook, hookAddresses);
this.logger.debug('Deploying required hook');
const requiredHook = await this.deployHook(chain, config.requiredHook, hookAddresses);
const txOverrides = this.multiProvider.getTransactionOverrides(chain);
// Check if the mailbox has already been initialized
const currentDefaultIsm = await mailbox.defaultIsm();
if (isZeroishAddress(currentDefaultIsm)) {
// If the default ISM is the zero address, the mailbox hasn't been initialized
this.logger.debug('Initializing mailbox');
try {
const estimatedGas = await mailbox.estimateGas.initialize(config.owner, defaultIsm, defaultHook.address, requiredHook.address);
await this.multiProvider.handleTx(chain, mailbox.initialize(config.owner, defaultIsm, defaultHook.address, requiredHook.address, {
gasLimit: addBufferToGasLimit(estimatedGas),
...txOverrides,
}));
}
catch (e) {
// If we still get an error here, it's likely a genuine error
this.logger.error('Failed to initialize mailbox:', e);
throw e;
}
}
else {
// If the default ISM is not the zero address, the mailbox has likely been initialized
this.logger.debug('Mailbox appears to be already initialized');
}
await this.configureHook(chain, mailbox, defaultHook.address, (_mailbox) => _mailbox.defaultHook(), (_mailbox, _hook) => _mailbox.populateTransaction.setDefaultHook(_hook, { ...txOverrides }));
await this.configureHook(chain, mailbox, requiredHook.address, (_mailbox) => _mailbox.requiredHook(), (_mailbox, _hook) => _mailbox.populateTransaction.setRequiredHook(_hook, { ...txOverrides }));
await this.configureIsm(chain, mailbox, defaultIsm, (_mailbox) => _mailbox.defaultIsm(), (_mailbox, _module) => _mailbox.populateTransaction.setDefaultIsm(_module));
return mailbox;
}
async deployValidatorAnnounce(chain, mailboxAddress) {
const validatorAnnounce = await this.deployContract(chain, 'validatorAnnounce', [mailboxAddress]);
return validatorAnnounce;
}
async deployHook(chain, config, coreAddresses) {
if (typeof config === 'string') {
return IPostDispatchHook__factory.connect(config, this.multiProvider.getProvider(chain));
}
const hooks = await this.hookDeployer.deployContracts(chain, config, coreAddresses);
this.addDeployedContracts(chain, this.hookDeployer.deployedContracts[chain], this.hookDeployer.verificationInputs[chain]);
if (typeof config === 'string') {
return Object.values(hooks)[0];
}
else {
return hooks[config.type];
}
}
async deployIsm(chain, config, mailbox) {
const ism = await this.ismFactory.deploy({
destination: chain,
config,
mailbox,
});
this.addDeployedContracts(chain, this.ismFactory.deployedIsms[chain]);
return ism.address;
}
async deployTestRecipient(chain, interchainSecurityModule) {
const testRecipient = await this.testRecipient.deployContracts(chain, {
interchainSecurityModule,
});
this.addDeployedContracts(chain, testRecipient);
return testRecipient.testRecipient;
}
async deployContracts(chain, config) {
if (config.remove) {
// skip deploying to chains configured to be removed
return undefined;
}
const proxyAdmin = await this.deployContract(chain, 'proxyAdmin', []);
const mailbox = await this.deployMailbox(chain, config, proxyAdmin.address);
const validatorAnnounce = await this.deployValidatorAnnounce(chain, mailbox.address);
if (config.upgrade) {
const timelockController = await this.deployTimelock(chain, config.upgrade.timelock);
config.ownerOverrides = {
...config.ownerOverrides,
proxyAdmin: timelockController.address,
};
}
const testRecipient = await this.deployTestRecipient(chain, this.cachedAddresses[chain].interchainSecurityModule);
const contracts = {
mailbox,
proxyAdmin,
validatorAnnounce,
testRecipient,
};
await this.transferOwnershipOfContracts(chain, config, contracts);
return contracts;
}
}
//# sourceMappingURL=HyperlaneCoreDeployer.js.map