UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

122 lines 7.06 kB
import { CCIPHook__factory, CCIPIsm__factory } from '@hyperlane-xyz/core'; import { ZERO_ADDRESS_HEX_32, addressToBytes32, assert, rootLogger, } from '@hyperlane-xyz/utils'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer.js'; import { CCIPContractCache, getCCIPChainSelector, getCCIPRouterAddress, isSupportedCCIPLane, } from './utils.js'; export class HyperlaneCCIPDeployer extends HyperlaneDeployer { core; ccipContractCache = new CCIPContractCache(); constructor(multiProvider, core, contractVerifier) { super(multiProvider, {}, { logger: rootLogger.child({ module: 'HyperlaneCCIPDeployer' }), contractVerifier, }); this.core = core; } cacheAddressesMap(addressesMap) { super.cacheAddressesMap(addressesMap); this.ccipContractCache.cacheAddressesMap(addressesMap); } async deployContracts(origin, config) { const unsupportedLanes = await this.checkCCIPLanesSupport(origin, config); if (unsupportedLanes.length > 0) { this.logger.warn(`CCIP lanes not configured from ${origin} to ${unsupportedLanes.join(', ')}. Skipping these chains.`); unsupportedLanes.forEach((lane) => config.delete(lane)); } // Deploy ISMs from chain to each destination chain concurrently. // This is done in parallel since the ISM is deployed on discrete destination chains. await Promise.all(Array.from(config).map(async (destination) => { // Deploy CCIP ISM for this origin->destination pair await this.deployCCIPIsm(origin, destination); })); // On the origin chain, deploy hooks for each destination chain in series. // This is done in series to avoid nonce contention on the origin chain. for (const destination of config) { await this.deployCCIPHook(origin, destination); } // Authorize hooks for each destination chain concurrently. // This is done in parallel since the ISM is deployed on discrete destination chains. await Promise.all(Array.from(config).map(async (destination) => { await this.authorizeHook(origin, destination); })); this.ccipContractCache.writeBack(this.cachedAddresses); return {}; } async checkCCIPLanesSupport(origin, config) { this.logger.info(`Checking CCIP lanes exist from ${origin} to ${Array.from(config).join(', ')}`); // Check if the CCIP lane is supported for each destination chain const isSupportedLane = await Promise.all(Array.from(config).map(async (destination) => { const isSupported = await isSupportedCCIPLane({ origin, destination, multiProvider: this.multiProvider, }); return { destination, isSupported }; })); // Return the destination chains that are not supported return isSupportedLane .filter((result) => !result.isSupported) .map((result) => result.destination); } async authorizeHook(origin, destination) { const ccipIsmAddress = this.ccipContractCache.getIsm(origin, destination); assert(ccipIsmAddress, `CCIP ISM not found for ${origin} -> ${destination}`); const ccipHookAddress = this.ccipContractCache.getHook(origin, destination); assert(ccipHookAddress, `CCIP Hook not found for ${origin} -> ${destination}`); const bytes32HookAddress = addressToBytes32(ccipHookAddress); const ccipIsm = CCIPIsm__factory.connect(ccipIsmAddress, this.multiProvider.getSigner(destination)); const authorizedHook = await ccipIsm.authorizedHook(); this.logger.debug('Authorized hook on ism %s: %s', ccipIsm.address, authorizedHook); // If the hook is already set, return if (authorizedHook === bytes32HookAddress) { this.logger.info('Authorized hook already set on ism %s', ccipIsm.address); return; } // If not already set, must not be initialised yet if (authorizedHook !== ZERO_ADDRESS_HEX_32) { this.logger.error('Authorized hook mismatch on ism %s, expected %s, got %s', ccipIsm.address, bytes32HookAddress, authorizedHook); throw new Error('Authorized hook mismatch'); } // If not initialised, set the hook this.logger.info('Setting authorized hook %s on ism %s on destination %s', ccipHookAddress, ccipIsm.address, destination); await this.multiProvider.handleTx(destination, ccipIsm.setAuthorizedHook(bytes32HookAddress, this.multiProvider.getTransactionOverrides(destination))); } async deployCCIPIsm(origin, destination) { const cachedIsm = this.ccipContractCache.getIsm(origin, destination); if (cachedIsm) { this.logger.debug('CCIP ISM already deployed for %s -> %s: %s', origin, destination, cachedIsm); return; } const ccipOriginChainSelector = getCCIPChainSelector(origin); const ccipIsmChainRouterAddress = getCCIPRouterAddress(destination); assert(ccipOriginChainSelector, `CCIP chain selector not found for ${origin}`); assert(ccipIsmChainRouterAddress, `CCIP router address not found for ${origin}`); const ccipIsm = await this.deployContractFromFactory(destination, new CCIPIsm__factory(), 'CCIPIsm', [ccipIsmChainRouterAddress, ccipOriginChainSelector], undefined, false); this.ccipContractCache.setIsm(origin, destination, ccipIsm); } async deployCCIPHook(origin, destination) { // Grab the ISM from the cache const ccipIsmAddress = this.ccipContractCache.getIsm(origin, destination); assert(ccipIsmAddress, `CCIP ISM not found for ${origin} -> ${destination}`); const cachedHook = this.ccipContractCache.getHook(origin, destination); if (cachedHook) { this.logger.debug('CCIP Hook already deployed for %s -> %s: %s', origin, destination, cachedHook); return; } const mailbox = this.core[origin].mailbox; assert(mailbox, `Mailbox address is required for ${origin}`); const ccipDestinationChainSelector = getCCIPChainSelector(destination); const ccipOriginChainRouterAddress = getCCIPRouterAddress(origin); assert(ccipDestinationChainSelector, `CCIP chain selector not found for ${destination}`); assert(ccipOriginChainRouterAddress, `CCIP router address not found for ${destination}`); const destinationDomain = this.multiProvider.getDomainId(destination); const ccipHook = await this.deployContractFromFactory(origin, new CCIPHook__factory(), 'CCIPHook', [ ccipOriginChainRouterAddress, ccipDestinationChainSelector, mailbox, destinationDomain, addressToBytes32(ccipIsmAddress), ], undefined, false); this.ccipContractCache.setHook(origin, destination, ccipHook); } } //# sourceMappingURL=HyperlaneCCIPDeployer.js.map