UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

271 lines 13.2 kB
import { ProxyAdmin__factory } from '@hyperlane-xyz/core'; import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; import { ProtocolType, isObjEmpty, objFilter, objKeys, objMap, promiseObjAll, rootLogger, } from '@hyperlane-xyz/utils'; import { CCIPContractCache } from '../ccip/utils.js'; import { EvmHookModule } from '../hook/EvmHookModule.js'; import { CosmosNativeIsmModule } from '../ism/CosmosNativeIsmModule.js'; import { EvmIsmModule } from '../ism/EvmIsmModule.js'; import { CosmosNativeWarpModule } from '../token/CosmosNativeWarpModule.js'; import { EvmERC20WarpModule } from '../token/EvmERC20WarpModule.js'; import { hypERC20factories } from '../token/contracts.js'; import { CosmosNativeDeployer } from '../token/cosmosnativeDeploy.js'; import { HypERC20Deployer, HypERC721Deployer } from '../token/deploy.js'; import { extractIsmAndHookFactoryAddresses } from '../utils/ism.js'; import { HyperlaneProxyFactoryDeployer } from './HyperlaneProxyFactoryDeployer.js'; import { ContractVerifier } from './verify/ContractVerifier.js'; import { ExplorerLicenseType } from './verify/types.js'; export async function executeWarpDeploy(warpDeployConfig, multiProvider, multiProtocolSigner, registryAddresses, apiKeys) { const contractVerifier = new ContractVerifier(multiProvider, apiKeys, coreBuildArtifact, ExplorerLicenseType.MIT); const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider, contractVerifier); // For each chain in WarpRouteConfig, deploy each Ism Factory, if it's not in the registry // Then return a modified config with the ism and/or hook address as a string const modifiedConfig = await resolveWarpIsmAndHook(warpDeployConfig, multiProvider, multiProtocolSigner, registryAddresses, ismFactoryDeployer, contractVerifier); let deployedContracts = {}; // get unique list of protocols const protocols = Array.from(new Set(Object.keys(modifiedConfig).map((chainName) => multiProvider.getProtocol(chainName)))); for (const protocol of protocols) { const protocolSpecificConfig = objFilter(modifiedConfig, (chainName, _) => multiProvider.getProtocol(chainName) === protocol); if (isObjEmpty(protocolSpecificConfig)) { continue; } switch (protocol) { case ProtocolType.Ethereum: { const deployer = warpDeployConfig.isNft ? new HypERC721Deployer(multiProvider) : new HypERC20Deployer(multiProvider); // TODO: replace with EvmERC20WarpModule const evmContracts = await deployer.deploy(protocolSpecificConfig); deployedContracts = { ...deployedContracts, ...objMap(evmContracts, (_, contracts) => getRouter(contracts).address), }; break; } case ProtocolType.CosmosNative: { const signersMap = objMap(protocolSpecificConfig, (chain, _) => multiProtocolSigner.getCosmosNativeSigner(chain)); const deployer = new CosmosNativeDeployer(multiProvider, signersMap); deployedContracts = { ...deployedContracts, ...(await deployer.deploy(protocolSpecificConfig)), }; break; } default: { throw new Error(`Protocol type ${protocol} not supported`); } } } return deployedContracts; } async function resolveWarpIsmAndHook(warpConfig, multiProvider, multiProtocolSigner, registryAddresses, ismFactoryDeployer, contractVerifier) { return promiseObjAll(objMap(warpConfig, async (chain, config) => { const ccipContractCache = new CCIPContractCache(registryAddresses); const chainAddresses = registryAddresses[chain]; if (!chainAddresses) { throw `Registry factory addresses not found for ${chain}.`; } config.interchainSecurityModule = await createWarpIsm({ ccipContractCache, chain, chainAddresses, multiProvider, multiProtocolSigner, contractVerifier, ismFactoryDeployer, warpConfig: config, }); // TODO write test config.hook = await createWarpHook({ ccipContractCache, chain, chainAddresses, multiProvider, contractVerifier, ismFactoryDeployer, warpConfig: config, }); return config; })); } /** * Deploys the Warp ISM for a given config * * @returns The deployed ism address */ async function createWarpIsm({ ccipContractCache, chain, chainAddresses, multiProvider, multiProtocolSigner, contractVerifier, warpConfig, }) { const { interchainSecurityModule } = warpConfig; if (!interchainSecurityModule || typeof interchainSecurityModule === 'string') { rootLogger.info(`Config Ism is ${!interchainSecurityModule ? 'empty' : interchainSecurityModule}, skipping deployment.`); return interchainSecurityModule; } rootLogger.info(`Loading registry factory addresses for ${chain}...`); rootLogger.info(`Creating ${interchainSecurityModule.type} ISM for token on ${chain} chain...`); rootLogger.info(`Finished creating ${interchainSecurityModule.type} ISM for token on ${chain} chain.`); const protocolType = multiProvider.getProtocol(chain); switch (protocolType) { case ProtocolType.Ethereum: { const evmIsmModule = await EvmIsmModule.create({ chain, mailbox: chainAddresses.mailbox, multiProvider: multiProvider, proxyFactoryFactories: extractIsmAndHookFactoryAddresses(chainAddresses), config: interchainSecurityModule, ccipContractCache, contractVerifier, }); const { deployedIsm } = evmIsmModule.serialize(); return deployedIsm; } case ProtocolType.CosmosNative: { const signer = multiProtocolSigner.getCosmosNativeSigner(chain); const cosmosIsmModule = await CosmosNativeIsmModule.create({ chain, multiProvider: multiProvider, addresses: { mailbox: chainAddresses.mailbox, }, config: interchainSecurityModule, signer, }); const { deployedIsm } = cosmosIsmModule.serialize(); return deployedIsm; } default: throw new Error(`Protocol type ${protocolType} not supported`); } } async function createWarpHook({ ccipContractCache, chain, chainAddresses, multiProvider, contractVerifier, warpConfig, }) { const { hook } = warpConfig; if (!hook || typeof hook === 'string') { rootLogger.info(`Config Hook is ${!hook ? 'empty' : hook}, skipping deployment.`); return hook; } rootLogger.info(`Loading registry factory addresses for ${chain}...`); rootLogger.info(`Creating ${hook.type} Hook for token on ${chain} chain...`); const protocolType = multiProvider.getProtocol(chain); switch (protocolType) { case ProtocolType.Ethereum: { rootLogger.info(`Loading registry factory addresses for ${chain}...`); rootLogger.info(`Creating ${hook.type} Hook for token on ${chain} chain...`); // If config.proxyadmin.address exists, then use that. otherwise deploy a new proxyAdmin const proxyAdminAddress = warpConfig.proxyAdmin?.address ?? (await multiProvider.handleDeploy(chain, new ProxyAdmin__factory(), [])) .address; const evmHookModule = await EvmHookModule.create({ chain, multiProvider: multiProvider, coreAddresses: { mailbox: chainAddresses.mailbox, proxyAdmin: proxyAdminAddress, }, config: hook, ccipContractCache, contractVerifier, proxyFactoryFactories: extractIsmAndHookFactoryAddresses(chainAddresses), }); rootLogger.info(`Finished creating ${hook.type} Hook for token on ${chain} chain.`); const { deployedHook } = evmHookModule.serialize(); return deployedHook; } case ProtocolType.CosmosNative: { rootLogger.info(`No warp hooks for Cosmos Native chains, skipping deployment.`); return hook; } default: throw new Error(`Protocol type ${protocolType} not supported`); } } export async function enrollCrossChainRouters({ multiProvider, multiProtocolSigner, registryAddresses, warpDeployConfig, }, deployedContracts) { const resolvedConfigMap = objMap(warpDeployConfig, (_, config) => ({ gas: 0, // TODO: protocol specific gas?, ...config, })); const configMapToDeploy = objFilter(resolvedConfigMap, (_, config) => !config.foreignDeployment); const allChains = Object.keys(configMapToDeploy); for (const chain of allChains) { const protocol = multiProvider.getProtocol(chain); const allRemoteChains = multiProvider .getRemoteChains(chain) .filter((c) => allChains.includes(c)); const protocolTransactions = {}; switch (protocol) { case ProtocolType.Ethereum: { const { domainRoutingIsmFactory, staticMerkleRootMultisigIsmFactory, staticMessageIdMultisigIsmFactory, staticAggregationIsmFactory, staticAggregationHookFactory, staticMerkleRootWeightedMultisigIsmFactory, staticMessageIdWeightedMultisigIsmFactory, } = registryAddresses[chain]; const evmWarpModule = new EvmERC20WarpModule(multiProvider, { chain, config: configMapToDeploy[chain], addresses: { deployedTokenRoute: deployedContracts[chain], domainRoutingIsmFactory, staticMerkleRootMultisigIsmFactory, staticMessageIdMultisigIsmFactory, staticAggregationIsmFactory, staticAggregationHookFactory, staticMerkleRootWeightedMultisigIsmFactory, staticMessageIdWeightedMultisigIsmFactory, }, }); const actualConfig = await evmWarpModule.read(); const expectedConfig = { ...actualConfig, remoteRouters: (() => { const routers = {}; for (const c of allRemoteChains) { routers[multiProvider.getDomainId(c).toString()] = { address: deployedContracts[c], }; } return routers; })(), }; const transactions = await evmWarpModule.update(expectedConfig); if (transactions.length) { protocolTransactions[ProtocolType.Ethereum] = { [chain]: transactions, }; } break; } case ProtocolType.CosmosNative: { const signer = multiProtocolSigner.getCosmosNativeSigner(chain); const cosmosNativeWarpModule = new CosmosNativeWarpModule(multiProvider, { chain, config: configMapToDeploy[chain], addresses: { deployedTokenRoute: deployedContracts[chain], }, }, signer); const actualConfig = await cosmosNativeWarpModule.read(); const expectedConfig = { ...actualConfig, remoteRouters: (() => { const routers = {}; for (const c of allRemoteChains) { routers[multiProvider.getDomainId(c).toString()] = { address: deployedContracts[c], }; } return routers; })(), }; const transactions = await cosmosNativeWarpModule.update(expectedConfig); if (transactions.length) { protocolTransactions[ProtocolType.CosmosNative] = { [chain]: transactions, }; } break; } default: { throw new Error(`Protocol type ${protocol} not supported`); } } } } function getRouter(contracts) { for (const key of objKeys(hypERC20factories)) { if (contracts[key]) return contracts[key]; } throw new Error('No matching contract found.'); } //# sourceMappingURL=warp.js.map