feat(cli): display formatted deployment plan to confirm warp deploy (#4053)

### Description

- adds formatted deployment plan to confirm warp deploy on `hyperlane
warp deploy`

### Drive-by changes

- none

### Related issues

- fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4129

### Backward compatibility

- yes

### Testing

- manual
- ci-test

Example display:
```
➜  cli git:(06-25-feat_cli_display_formatted_deployment_plan_to_confirm_warp_deploy) ✗ hl warp deploy --registry /Users/nbayindirli/workplace/Hyperlane/hyperlane-registry --verbosity trace
Hyperlane CLI

Hyperlane Warp Route Deployment
------------------------------------------------
Using warp route deployment config at ./configs/warp-route-deployment.yaml

Warp Route Deployment Plan
==========================
📋 Token Standard: ERC20
📋 Warp Route Config:
┌─────────┬───────┬─────────────┬──────────────────────────────────────────────┬──────────────────────────────────────────────┬───────────────────────┐
│ (index) │ NFT?  │ Type        │ Owner                                        │ Mailbox                                      │ ISM Config(s)         │
├─────────┼───────┼─────────────┼──────────────────────────────────────────────┼──────────────────────────────────────────────┼───────────────────────┤
│ alpha   │ false │ 'native'    │ '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb' │ '0xf87B1E8fb8ee9F12bB0C0c77ac19C28773fdbff3' │ 'See table(s) below.' │
│ beta    │ false │ 'synthetic' │ '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb' │ '0x0b1D3Dd7162101Ad0c9A9Fe91c40CDE3410DaAFC' │ 'See table(s) below.' │
└─────────┴───────┴─────────────┴──────────────────────────────────────────────┴──────────────────────────────────────────────┴───────────────────────┘
📋 alpha ISM Config(s):
┌───────────┬────────────────────────┐
│ (index)   │ Values                 │
├───────────┼────────────────────────┤
│ Type      │ 'staticAggregationIsm' │
│ Threshold │ 1                      │
│ Modules   │ 'See table(s) below.'  │
└───────────┴────────────────────────┘
┌─────────┬──────────────────────────────────────────────┐
│ (index) │ Values                                       │
├─────────┼──────────────────────────────────────────────┤
│ Type    │ 'trustedRelayerIsm'                          │
│ Relayer │ '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb' │
└─────────┴──────────────────────────────────────────────┘
┌─────────────────┬──────────────────────────────────────────────┐
│ (index)         │ Values                                       │
├─────────────────┼──────────────────────────────────────────────┤
│ Type            │ 'defaultFallbackRoutingIsm'                  │
│ Owner           │ '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb' │
│ Owner Overrides │ 'Undefined'                                  │
│ Domains         │ 'See warp config for domain specification.'  │
└─────────────────┴──────────────────────────────────────────────┘
📋 beta ISM Config(s):
┌───────────┬────────────────────────┐
│ (index)   │ Values                 │
├───────────┼────────────────────────┤
│ Type      │ 'staticAggregationIsm' │
│ Threshold │ 1                      │
│ Modules   │ 'See table(s) below.'  │
└───────────┴────────────────────────┘
┌─────────┬──────────────────────────────────────────────┐
│ (index) │ Values                                       │
├─────────┼──────────────────────────────────────────────┤
│ Type    │ 'trustedRelayerIsm'                          │
│ Relayer │ '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb' │
└─────────┴──────────────────────────────────────────────┘
┌─────────────────┬──────────────────────────────────────────────┐
│ (index)         │ Values                                       │
├─────────────────┼──────────────────────────────────────────────┤
│ Type            │ 'defaultFallbackRoutingIsm'                  │
│ Owner           │ '0x16F4898F47c085C41d7Cc6b1dc72B91EA617dcBb' │
│ Owner Overrides │ 'Undefined'                                  │
│ Domains         │ 'See warp config for domain specification.'  │
└─────────────────┴──────────────────────────────────────────────┘
? Is this deployment plan correct? (Y/n)
```
pull/4135/head
Noah Bayindirli 🥂 4 months ago committed by GitHub
parent 49986aa929
commit d00f2ffc00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/four-bottles-hide.md
  2. 2
      typescript/cli/src/commands/warp.ts
  3. 143
      typescript/cli/src/deploy/warp.ts

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/cli': minor
---
Displays formatted deployment plan to confirm warp deploy.

@ -115,7 +115,7 @@ export const deploy: CommandModuleWithWriteContext<{
'from-address': fromAddressCommandOption,
},
handler: async ({ context, config, dryRun }) => {
logGray(`Hyperlane warp route deployment${dryRun ? ' dry-run' : ''}`);
logGray(`Hyperlane Warp Route Deployment${dryRun ? ' Dry-Run' : ''}`);
logGray('------------------------------------------------');
try {

@ -3,6 +3,8 @@ import { stringify as yamlStringify } from 'yaml';
import { IRegistry } from '@hyperlane-xyz/registry';
import {
AggregationIsmConfig,
ChainName,
EvmERC20WarpModule,
EvmIsmModule,
HypERC20Deployer,
@ -10,10 +12,16 @@ import {
HyperlaneAddresses,
HyperlaneContractsMap,
HyperlaneProxyFactoryDeployer,
IsmType,
MultiProvider,
MultisigIsmConfig,
OpStackIsmConfig,
PausableIsmConfig,
ProxyFactoryFactoriesAddresses,
RoutingIsmConfig,
TOKEN_TYPE_TO_STANDARD,
TokenFactories,
TrustedRelayerIsmConfig,
WarpCoreConfig,
WarpRouteDeployConfig,
getTokenConnectionId,
@ -95,8 +103,6 @@ export async function runWarpRouteDeploy({
warpDeployConfig: warpRouteConfig,
};
logBlue('Warp route deployment plan');
await runDeployPlanStep(deploymentParams);
const chains = Object.keys(warpRouteConfig);
@ -118,10 +124,7 @@ export async function runWarpRouteDeploy({
async function runDeployPlanStep({ context, warpDeployConfig }: DeployParams) {
const { skipConfirmation } = context;
logBlue('\nDeployment plan');
logGray('===============');
log(`Using token standard ${warpDeployConfig.isNft ? 'ERC721' : 'ERC20'}`);
logTable(warpDeployConfig);
displayWarpDeployPlan(warpDeployConfig);
if (skipConfirmation || context.isDryRun) return;
@ -383,3 +386,131 @@ export async function runWarpRouteApply(params: ApplyParams) {
}),
);
}
function displayWarpDeployPlan(deployConfig: WarpRouteDeployConfig) {
logBlue('\nWarp Route Deployment Plan');
logGray('==========================');
log(`📋 Token Standard: ${deployConfig.isNft ? 'ERC721' : 'ERC20'}`);
const { transformedDeployConfig, transformedIsmConfigs } =
transformDeployConfigForDisplay(deployConfig);
log('📋 Warp Route Config:');
logTable(transformedDeployConfig);
objMap(transformedIsmConfigs, (chain, ismConfigs) => {
log(`📋 ${chain} ISM Config(s):`);
ismConfigs.forEach((ismConfig) => {
logTable(ismConfig);
});
});
}
/* only used for transformIsmForDisplay type-sense */
type IsmConfig =
| RoutingIsmConfig // type, owner, ownerOverrides, domain
| AggregationIsmConfig // type, modules, threshold
| MultisigIsmConfig // type, validators, threshold
| OpStackIsmConfig // type, origin, nativeBridge
| PausableIsmConfig // type, owner, paused, ownerOverrides
| TrustedRelayerIsmConfig; // type, relayer
function transformDeployConfigForDisplay(deployConfig: WarpRouteDeployConfig) {
const transformedIsmConfigs: Record<ChainName, any[]> = {};
const transformedDeployConfig = objMap(deployConfig, (chain, config) => {
if (config.interchainSecurityModule)
transformedIsmConfigs[chain] = transformIsmConfigForDisplay(
config.interchainSecurityModule as IsmConfig,
);
return {
'NFT?': config.isNft ?? false,
Type: config.type,
Owner: config.owner,
Mailbox: config.mailbox,
'ISM Config(s)': config.interchainSecurityModule
? 'See table(s) below.'
: 'No ISM config(s) specified.',
};
});
return {
transformedDeployConfig,
transformedIsmConfigs,
};
}
function transformIsmConfigForDisplay(ismConfig: IsmConfig): any[] {
const ismConfigs: any[] = [];
switch (ismConfig.type) {
case IsmType.AGGREGATION:
ismConfigs.push({
Type: ismConfig.type,
Threshold: ismConfig.threshold,
Modules: 'See table(s) below.',
});
ismConfig.modules.forEach((module) => {
ismConfigs.push(...transformIsmConfigForDisplay(module as IsmConfig));
});
return ismConfigs;
case IsmType.ROUTING:
return [
{
Type: ismConfig.type,
Owner: ismConfig.owner,
'Owner Overrides': ismConfig.ownerOverrides ?? 'Undefined',
Domains: 'See warp config for domain specification.',
},
];
case IsmType.FALLBACK_ROUTING:
return [
{
Type: ismConfig.type,
Owner: ismConfig.owner,
'Owner Overrides': ismConfig.ownerOverrides ?? 'Undefined',
Domains: 'See warp config for domain specification.',
},
];
case IsmType.MERKLE_ROOT_MULTISIG:
return [
{
Type: ismConfig.type,
Validators: ismConfig.validators,
Threshold: ismConfig.threshold,
},
];
case IsmType.MESSAGE_ID_MULTISIG:
return [
{
Type: ismConfig.type,
Validators: ismConfig.validators,
Threshold: ismConfig.threshold,
},
];
case IsmType.OP_STACK:
return [
{
Type: ismConfig.type,
Origin: ismConfig.origin,
'Native Bridge': ismConfig.nativeBridge,
},
];
case IsmType.PAUSABLE:
return [
{
Type: ismConfig.type,
Owner: ismConfig.owner,
'Paused ?': ismConfig.paused,
'Owner Overrides': ismConfig.ownerOverrides ?? 'Undefined',
},
];
case IsmType.TRUSTED_RELAYER:
return [
{
Type: ismConfig.type,
Relayer: ismConfig.relayer,
},
];
default:
return [ismConfig];
}
}

Loading…
Cancel
Save