@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
84 lines • 5.08 kB
JavaScript
import { addBufferToGasLimit, addressToBytes32, objFilter, objMap, objMerge, } from '@hyperlane-xyz/utils';
import { filterOwnableContracts } from '../contracts/contracts.js';
import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer.js';
export class HyperlaneRouterDeployer extends HyperlaneDeployer {
async configureClients(contractsMap, configMap) {
for (const chain of Object.keys(contractsMap)) {
const contracts = contractsMap[chain];
const config = configMap[chain];
await super.configureClient(chain, this.router(contracts), config);
}
}
async enrollRemoteRouters(deployedContractsMap, _, foreignRouters = {}) {
this.logger.debug(`Enrolling deployed routers with each other (if not already)...`);
// Make all routers aware of each other.
// Routers that were deployed.
const deployedRouters = objMap(deployedContractsMap, (_, contracts) => this.router(contracts).address);
// All routers, including those that were deployed and those with existing deployments.
const allRouters = objMerge(deployedRouters, foreignRouters);
const allChains = Object.keys(allRouters);
await Promise.all(Object.entries(deployedContractsMap).map(async ([chain, contracts]) => {
const allRemoteChains = this.multiProvider
.getRemoteChains(chain)
.filter((c) => allChains.includes(c));
const enrollEntries = await Promise.all(allRemoteChains.map(async (remote) => {
const remoteDomain = this.multiProvider.getDomainId(remote);
const current = await this.router(contracts).routers(remoteDomain);
const expected = addressToBytes32(allRouters[remote]);
return current !== expected ? [remoteDomain, expected] : undefined;
}));
const entries = enrollEntries.filter((entry) => entry !== undefined);
const domains = entries.map(([id]) => id);
const addresses = entries.map(([, address]) => address);
// skip if no enrollments are needed
if (domains.length === 0) {
return;
}
await super.runIfOwner(chain, this.router(contracts), async () => {
const chains = domains.map((id) => this.multiProvider.getChainName(id));
this.logger.debug(`Enrolling remote routers (${chains.join(', ')}) on ${chain}`);
const router = this.router(contracts);
const estimatedGas = await router.estimateGas.enrollRemoteRouters(domains, addresses);
// deploy with 10% buffer on gas limit
const enrollTx = await router.enrollRemoteRouters(domains, addresses, {
gasLimit: addBufferToGasLimit(estimatedGas),
...this.multiProvider.getTransactionOverrides(chain),
});
await this.multiProvider.handleTx(chain, enrollTx);
});
}));
}
async transferOwnership(contractsMap, configMap) {
this.logger.debug(`Transferring ownership of ownables...`);
for (const chain of Object.keys(contractsMap)) {
const contracts = contractsMap[chain];
const ownables = (await filterOwnableContracts(contracts));
await this.transferOwnershipOfContracts(chain, configMap[chain], ownables);
}
}
async deploy(configMap) {
// Only deploy on chains that don't have foreign deployments.
const configMapToDeploy = objFilter(configMap, (_chainName, config) => !config.foreignDeployment);
// Create a map of chains that have foreign deployments.
const foreignDeployments = objFilter(objMap(configMap, (_, config) => config.foreignDeployment), (_chainName, foreignDeployment) => foreignDeployment !== undefined);
const deployedContractsMap = await super.deploy(configMapToDeploy);
await this.enrollRemoteRouters(deployedContractsMap, configMap, foreignDeployments);
await this.deployAndConfigureTokenFees(deployedContractsMap, configMap);
await this.configureClients(deployedContractsMap, configMap);
await this.transferOwnership(deployedContractsMap, configMap);
this.logger.debug(`Finished deploying router contracts for all chains.`);
return deployedContractsMap;
}
async deployAndConfigureTokenFees(deployedContractsMap, configMap) {
// Intentionally a no-op in the base deployer.
// Token-fee configuration is router-specific (e.g., fungible token routers)
// and should be implemented by subclasses that know their router interface.
for (const chain of Object.keys(deployedContractsMap)) {
const config = configMap[chain];
if (!config.tokenFee)
continue;
this.logger.debug(`Token fee config detected on ${chain}, no-op in base deployer. Must be handled by subclass if applicable`);
}
}
}
//# sourceMappingURL=HyperlaneRouterDeployer.js.map