@ -1,4 +1,5 @@
import { confirm } from '@inquirer/prompts' ;
import { groupBy } from 'lodash-es' ;
import { stringify as yamlStringify } from 'yaml' ;
import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js' ;
@ -53,6 +54,7 @@ import {
Address ,
ProtocolType ,
assert ,
isObjEmpty ,
objFilter ,
objKeys ,
objMap ,
@ -64,14 +66,7 @@ import { readWarpRouteDeployConfig } from '../config/warp.js';
import { MINIMUM_WARP_DEPLOY_GAS } from '../consts.js' ;
import { getOrRequestApiKeys } from '../context/context.js' ;
import { WriteCommandContext } from '../context/types.js' ;
import {
log ,
logBlue ,
logGray ,
logGreen ,
logRed ,
logTable ,
} from '../logger.js' ;
import { log , logBlue , logGray , logGreen , logTable } from '../logger.js' ;
import { getSubmitterBuilder } from '../submit/submit.js' ;
import {
indentYamlOrJson ,
@ -438,17 +433,13 @@ export async function runWarpRouteApply(
params : WarpApplyParams ,
) : Promise < void > {
const { warpDeployConfig , warpCoreConfig , context } = params ;
const { registry , multiProvider , chainMetadata , skipConfirmation } = context ;
const { chainMetadata , skipConfirmation } = context ;
WarpRouteDeployConfigSchema . parse ( warpDeployConfig ) ;
WarpCoreConfigSchema . parse ( warpCoreConfig ) ;
const addresses = await registry . getAddresses ( ) ;
const warpCoreConfigByChain = Object . fromEntries (
warpCoreConfig . tokens . map ( ( token ) = > [
token . chainName ,
token ,
] ) /* Necessary for O(1) reads below */ ,
warpCoreConfig . tokens . map ( ( token ) = > [ token . chainName , token ] ) ,
) ;
const chains = Object . keys ( warpDeployConfig ) ;
@ -457,94 +448,119 @@ export async function runWarpRouteApply(
if ( ! skipConfirmation )
apiKeys = await getOrRequestApiKeys ( chains , chainMetadata ) ;
const contractVerifier = new ContractVerifier (
multiProvider ,
apiKeys ,
coreBuildArtifact ,
ExplorerLicenseType . MIT ,
) ;
const transactions : AnnotatedEV5Transaction [ ] = [
. . . ( await extendWarpRoute (
params ,
apiKeys ,
warpDeployConfig ,
warpCoreConfigByChain ,
) ) ,
. . . ( await updateExistingWarpRoute (
params ,
apiKeys ,
warpDeployConfig ,
warpCoreConfigByChain ,
) ) ,
] ;
if ( transactions . length == 0 )
return logGreen ( ` Warp config is the same as target. No updates needed. ` ) ;
const warpDeployChains = Object . keys ( warpDeployConfig ) ;
await submitWarpApplyTransactions ( params , groupBy ( transactions , 'chainId' ) ) ;
}
async function extendWarpRoute (
params : WarpApplyParams ,
apiKeys : ChainMap < string > ,
warpDeployConfig : WarpRouteDeployConfig ,
warpCoreConfigByChain : ChainMap < WarpCoreConfig [ ' tokens ' ] [ number ] > ,
) {
logBlue ( 'Extending Warp Route' ) ;
const { multiProvider } = params . context ;
const warpCoreChains = Object . keys ( warpCoreConfigByChain ) ;
if ( warpDeployChains . length === warpCoreChains . length ) {
logGray ( 'Updating deployed Warp Routes' ) ;
await promiseObjAll (
objMap ( warpDeployConfig , async ( chain , config ) = > {
try {
config . ismFactoryAddresses = addresses [
chain
] as ProxyFactoryFactoriesAddresses ;
const evmERC20WarpModule = new EvmERC20WarpModule (
multiProvider ,
{
config ,
chain ,
addresses : {
deployedTokenRoute :
warpCoreConfigByChain [ chain ] . addressOrDenom ! ,
} ,
} ,
contractVerifier ,
) ;
const transactions = await evmERC20WarpModule . update ( config ) ;
if ( transactions . length == 0 )
return logGreen (
` Warp config on ${ chain } is the same as target. No updates needed. ` ,
) ;
await submitWarpApplyTransactions ( chain , params , transactions ) ;
} catch ( e ) {
logRed ( ` Warp config on ${ chain } failed to update. ` , e ) ;
}
} ) ,
) ;
} else if ( warpDeployChains . length > warpCoreChains . length ) {
logGray ( 'Extending deployed Warp configs' ) ;
// Split between the existing and additional config
const existingConfigs : WarpRouteDeployConfig = objFilter (
warpDeployConfig ,
( chain , _config ) : _config is any = > warpCoreChains . includes ( chain ) ,
) ;
// Split between the existing and additional config
const existingConfigs : WarpRouteDeployConfig = objFilter (
warpDeployConfig ,
( chain , _config ) : _config is any = > warpCoreChains . includes ( chain ) ,
) ;
let extendedConfigs : WarpRouteDeployConfig = objFilter (
warpDeployConfig ,
( chain , _config ) : _config is any = > ! warpCoreChains . includes ( chain ) ,
) ;
let extendedConfigs : WarpRouteDeployConfig = objFilter (
warpDeployConfig ,
( chain , _config ) : _config is any = > ! warpCoreChains . includes ( chain ) ,
) ;
extendedConfigs = await deriveMetadataFromExisting (
multiProvider ,
existingConfigs ,
extendedConfigs ,
) ;
if ( isObjEmpty ( extendedConfigs ) ) return [ ] ;
const newDeployedContracts = await executeDeploy (
{
// TODO: use EvmERC20WarpModule when it's ready
context ,
warpDeployConfig : extendedConfigs ,
} ,
apiKeys ,
) ;
extendedConfigs = await deriveMetadataFromExisting (
multiProvider ,
existingConfigs ,
extendedConfigs ,
) ;
const mergedRouters = mergeAllRouters (
multiProvider ,
existingConfigs ,
newDeployedContracts ,
warpCoreConfigByChain ,
) ;
const newDeployedContracts = await executeDeploy (
{
// TODO: use EvmERC20WarpModule when it's ready
context : params.context ,
warpDeployConfig : extendedConfigs ,
} ,
apiKeys ,
) ;
await enrollRemoteRouters ( params , mergedRouters ) ;
const mergedRouters = mergeAllRouters (
multiProvider ,
existingConfigs ,
newDeployedContracts ,
warpCoreConfigByChain ,
) ;
const updatedWarpCoreConfig = await getWarpCoreConfig (
params ,
mergedRouters ,
) ;
WarpCoreConfigSchema . parse ( updatedWarpCoreConfig ) ;
await writeDeploymentArtifacts ( updatedWarpCoreConfig , context ) ;
} else {
throw new Error ( 'Unenrolling warp routes is currently not supported' ) ;
}
const updatedWarpCoreConfig = await getWarpCoreConfig ( params , mergedRouters ) ;
WarpCoreConfigSchema . parse ( updatedWarpCoreConfig ) ;
await writeDeploymentArtifacts ( updatedWarpCoreConfig , params . context ) ;
return enrollRemoteRouters ( params , mergedRouters ) ;
}
async function updateExistingWarpRoute (
params : WarpApplyParams ,
apiKeys : ChainMap < string > ,
warpDeployConfig : WarpRouteDeployConfig ,
warpCoreConfigByChain : ChainMap < WarpCoreConfig [ ' tokens ' ] [ number ] > ,
) {
logBlue ( 'Updating deployed Warp Routes' ) ;
const { multiProvider , registry } = params . context ;
const addresses = await registry . getAddresses ( ) ;
const contractVerifier = new ContractVerifier (
multiProvider ,
apiKeys ,
coreBuildArtifact ,
ExplorerLicenseType . MIT ,
) ;
const transactions : AnnotatedEV5Transaction [ ] = [ ] ;
await promiseObjAll (
objMap ( warpDeployConfig , async ( chain , config ) = > {
const deployedConfig = warpCoreConfigByChain [ chain ] ;
if ( ! deployedConfig )
return logGray (
` Missing artifacts for ${ chain } . Probably new deployment. Skipping update... ` ,
) ;
config . ismFactoryAddresses = addresses [
chain
] as ProxyFactoryFactoriesAddresses ;
const evmERC20WarpModule = new EvmERC20WarpModule (
multiProvider ,
{
config ,
chain ,
addresses : {
deployedTokenRoute : deployedConfig.addressOrDenom ! ,
} ,
} ,
contractVerifier ,
) ;
transactions . push ( . . . ( await evmERC20WarpModule . update ( config ) ) ) ;
} ) ,
) ;
return transactions ;
}
/ * *
@ -617,7 +633,7 @@ function mergeAllRouters(
async function enrollRemoteRouters (
params : WarpApplyParams ,
deployedContractsMap : HyperlaneContractsMap < HypERC20Factories > ,
) : Promise < void > {
) : Promise < AnnotatedEV5Transaction [ ] > {
logBlue ( ` Enrolling deployed routers with each other... ` ) ;
const { multiProvider } = params . context ;
const deployedRouters : ChainMap < Address > = objMap (
@ -625,6 +641,7 @@ async function enrollRemoteRouters(
( _ , contracts ) = > getRouter ( contracts ) . address ,
) ;
const allChains = Object . keys ( deployedRouters ) ;
const transactions : AnnotatedEV5Transaction [ ] = [ ] ;
await promiseObjAll (
objMap ( deployedContractsMap , async ( chain , contracts ) = > {
await retryAsync ( async ( ) = > {
@ -660,10 +677,12 @@ async function enrollRemoteRouters(
return logGreen (
` Warp config on ${ chain } is the same as target. No updates needed. ` ,
) ;
await submitWarpApplyTransactions ( chain , params , mutatedConfigTxs ) ;
transactions . push ( . . . mutatedConfigTxs ) ;
} ) ;
} ) ,
) ;
return transactions ;
}
function getRouter ( contracts : HyperlaneContracts < HypERC20Factories > ) {
@ -805,29 +824,36 @@ function transformIsmConfigForDisplay(ismConfig: IsmConfig): any[] {
* Submits a set of transactions to the specified chain and outputs transaction receipts
* /
async function submitWarpApplyTransactions (
chain : string ,
params : WarpApplyParams ,
transactions : AnnotatedEV5Transaction [ ] ,
) {
const submitter : TxSubmitterBuilder < ProtocolType > =
await getWarpApplySubmitter ( {
chain ,
context : params.context ,
strategyUrl : params.strategyUrl ,
} ) ;
chainTransactions : Record < string , AnnotatedEV5Transaction [ ] > ,
) : Promise < void > {
const { multiProvider } = params . context ;
await promiseObjAll (
objMap ( chainTransactions , async ( chainId , transactions ) = > {
const chain = multiProvider . getChainName ( chainId ) ;
const submitter : TxSubmitterBuilder < ProtocolType > =
await getWarpApplySubmitter ( {
chain ,
context : params.context ,
strategyUrl : params.strategyUrl ,
} ) ;
const transactionReceipts = await submitter . submit ( . . . transactions ) ;
if ( transactionReceipts ) {
const receiptPath = ` ${ params . receiptsDir } / ${ chain } - ${
submitter . txSubmitterType
} - $ { Date . now ( ) } - receipts . json ` ;
writeYamlOrJson ( receiptPath , transactionReceipts ) ;
logGreen ( ` Transactions receipts successfully written to ${ receiptPath } ` ) ;
}
const transactionReceipts = await submitter . submit ( . . . transactions ) ;
if ( transactionReceipts ) {
const receiptPath = ` ${ params . receiptsDir } / ${ chain } - ${
submitter . txSubmitterType
} - $ { Date . now ( ) } - receipts . json ` ;
writeYamlOrJson ( receiptPath , transactionReceipts ) ;
logGreen (
` Transactions receipts successfully written to ${ receiptPath } ` ,
) ;
}
return logGreen (
` ✅ Warp route update success with ${ submitter . txSubmitterType } on ${ chain } : \ n \ n ` ,
indentYamlOrJson ( yamlStringify ( transactionReceipts , null , 2 ) , 0 ) ,
logGreen (
` ✅ Warp route update success with ${ submitter . txSubmitterType } on ${ chain } : \ n \ n ` ,
indentYamlOrJson ( yamlStringify ( transactionReceipts , null , 2 ) , 0 ) ,
) ;
} ) ,
) ;
}