@hyperlane-xyz/cli
Version:
A command-line utility for common Hyperlane operations
166 lines • 6.34 kB
JavaScript
import { stringify as yamlStringify } from 'yaml';
import { DeployedCoreAddressesSchema, normalizeConfig, } from '@hyperlane-xyz/sdk';
import { diffObjMerge } from '@hyperlane-xyz/utils';
import { createCoreDeployConfig, readCoreDeployConfigs, } from '../config/core.js';
import { runCoreApply, runCoreDeploy } from '../deploy/core.js';
import { evaluateIfDryRunFailure } from '../deploy/dry-run.js';
import { log, logCommandHeader, logGreen } from '../logger.js';
import { executeCoreRead } from '../read/core.js';
import { logYamlIfUnderMaxLines, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js';
import { formatYamlViolationsOutput } from '../utils/output.js';
import { DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, chainCommandOption, dryRunCommandOption, fromAddressCommandOption, inputFileCommandOption, outputFileCommandOption, } from './options.js';
/**
* Parent command
*/
export const coreCommand = {
command: 'core',
describe: 'Manage core Hyperlane contracts & configs',
builder: (yargs) => yargs
.command(apply)
.command(check)
.command(deploy)
.command(init)
.command(read)
.version(false)
.demandCommand(),
handler: () => log('Command required'),
};
export const apply = {
command: 'apply',
describe: 'Applies onchain Core configuration updates for a given mailbox address',
builder: {
chain: {
...chainCommandOption,
demandOption: true,
},
config: outputFileCommandOption(DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, true, 'The path to output a Core Config JSON or YAML file.'),
},
handler: async ({ context, chain, config: configFilePath }) => {
logCommandHeader(`Hyperlane Core Apply`);
const addresses = (await context.registry.getChainAddresses(chain));
DeployedCoreAddressesSchema.parse(addresses);
const config = readCoreDeployConfigs(configFilePath);
await runCoreApply({
context,
chain,
config,
deployedCoreAddresses: addresses,
});
process.exit(0);
},
};
/**
* Generates a command module for deploying Hyperlane contracts, given a command
*
* @param commandName - the deploy command key used to look up the deployFunction
* @returns A command module used to deploy Hyperlane contracts.
*/
export const deploy = {
command: 'deploy',
describe: 'Deploy Hyperlane contracts',
builder: {
chain: chainCommandOption,
config: outputFileCommandOption(DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, false, 'The path to a JSON or YAML file with a core deployment config.'),
'dry-run': dryRunCommandOption,
'from-address': fromAddressCommandOption,
},
handler: async ({ context, chain, config: configFilePath, dryRun }) => {
logCommandHeader(`Hyperlane Core deployment${dryRun ? ' dry-run' : ''}`);
try {
await runCoreDeploy({
context,
chain,
config: readYamlOrJson(configFilePath),
});
}
catch (error) {
evaluateIfDryRunFailure(error, dryRun);
throw error;
}
process.exit(0);
},
};
export const init = {
command: 'init',
describe: 'Create a core configuration, including ISMs and hooks.',
builder: {
advanced: {
type: 'boolean',
describe: 'Create an advanced ISM & hook configuration',
default: false,
},
config: outputFileCommandOption(DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, false, 'The path to output a Core Config JSON or YAML file.'),
},
handler: async ({ context, advanced, config: configFilePath }) => {
logCommandHeader('Hyperlane Core Configure');
await createCoreDeployConfig({
context,
configFilePath,
advanced,
});
process.exit(0);
},
};
export const read = {
command: 'read',
describe: 'Reads onchain Core configuration for a given mailbox address',
builder: {
chain: {
...chainCommandOption,
demandOption: true,
},
mailbox: {
type: 'string',
description: 'Mailbox address used to derive the core config',
},
config: outputFileCommandOption(DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, false, 'The path to output a Core Config JSON or YAML file.'),
},
handler: async ({ context, chain, mailbox, config: configFilePath }) => {
logCommandHeader('Hyperlane Core Read');
const coreConfig = await executeCoreRead({
context,
chain,
mailbox,
});
writeYamlOrJson(configFilePath, coreConfig, 'yaml');
logGreen(`✅ Core config written successfully to ${configFilePath}:\n`);
logYamlIfUnderMaxLines(coreConfig);
process.exit(0);
},
};
export const check = {
command: 'check',
describe: 'Reads onchain Core configuration for a given mailbox address and compares it with a provided file',
builder: {
chain: {
...chainCommandOption,
demandOption: true,
},
mailbox: {
type: 'string',
description: 'Mailbox address used to derive the core config. If not provided it will be inferred from the registry',
},
config: inputFileCommandOption({
defaultPath: DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH,
description: 'The path to a Core Config JSON or YAML file.',
demandOption: false,
}),
},
handler: async ({ context, chain, mailbox, config: configFilePath }) => {
logCommandHeader('Hyperlane Core Check');
const expectedCoreConfig = await readYamlOrJson(configFilePath);
const onChainCoreConfig = await executeCoreRead({
context,
chain,
mailbox,
});
const { mergedObject, isInvalid } = diffObjMerge(normalizeConfig(onChainCoreConfig), normalizeConfig(expectedCoreConfig));
if (isInvalid) {
log(formatYamlViolationsOutput(yamlStringify(mergedObject, null, 2)));
process.exit(1);
}
logGreen(`No violations found`);
process.exit(0);
},
};
//# sourceMappingURL=core.js.map