Skip to content

Commit

Permalink
feat(cli): Support creating agent configs from CLI (#3938)
Browse files Browse the repository at this point in the history
### Description

- Add support for creating agent configs using the CLI
- registry agent-config command with a required --chains option
- This will pick up local registry data

Example usage:

`hyperlane registry agent-config --chains anvil8545`

<!--
What's included in this PR?
-->


### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

<!--
- Fixes #[issue number here]
-->

- Fixes
#[3720](#3720)

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->
Yes

### Testing

Manual

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->
  • Loading branch information
Mo-Hussain authored Jun 13, 2024
1 parent 6d30eed commit 35f8699
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/lovely-boxes-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/cli': minor
---

Add command to support creating agent configs
3 changes: 2 additions & 1 deletion typescript/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"tsx": "^4.7.1",
"yaml": "^2.4.1",
"yargs": "^17.7.2",
"zod": "^3.21.2"
"zod": "^3.21.2",
"zod-validation-error": "^3.3.0"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
Expand Down
7 changes: 7 additions & 0 deletions typescript/cli/src/commands/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ export const agentConfigCommandOption = (
default: defaultPath,
});

export const chainTargetsCommandOption: Options = {
type: 'string',
description: 'Comma-separated list of chain names',
alias: 'c',
demandOption: true,
};

export const outputFileCommandOption = (
defaultPath?: string,
demandOption = false,
Expand Down
60 changes: 56 additions & 4 deletions typescript/cli/src/commands/registry.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { CommandModule } from 'yargs';

import { CommandModuleWithContext } from '../context/types.js';
import { log, logBlue, logGray, logTable } from '../logger.js';
import { createAgentConfig } from '../config/agent.js';
import { CommandContext, CommandModuleWithContext } from '../context/types.js';
import { log, logBlue, logGray, logRed, logTable } from '../logger.js';

const ChainTypes = ['mainnet', 'testnet'];
type ChainType = (typeof ChainTypes)[number];
import {
chainTargetsCommandOption,
outputFileCommandOption,
} from './options.js';
import { ChainType, ChainTypes } from './types.js';

/**
* Parent command
Expand All @@ -16,6 +20,7 @@ export const registryCommand: CommandModule = {
yargs
.command(listCommand)
.command(addressesCommand)
.command(createAgentConfigCommand)
.version(false)
.demandCommand(),
handler: () => log('Command required'),
Expand Down Expand Up @@ -88,3 +93,50 @@ const addressesCommand: CommandModuleWithContext<{ name: string }> = {
}
},
};

/**
* agent-config command
*/
const createAgentConfigCommand: CommandModuleWithContext<{
chains: string;
out: string;
}> = {
command: 'agent-config',
describe: 'Create a new agent config',

builder: {
chains: chainTargetsCommandOption,
out: outputFileCommandOption(
'./configs/agent-config.json',
false,
'The path to output an agent config JSON file.',
),
},
handler: async ({
context,
chains,
out,
}: {
context: CommandContext;
chains: string;
out: string;
}) => {
const { multiProvider } = context;

const chainNames = chains.split(',');
const invalidChainNames = chainNames.filter(
(chainName) => !multiProvider.hasChain(chainName),
);
if (invalidChainNames.length > 0) {
logRed(
`Invalid chain names: ${invalidChainNames
.join(', ')
.replace(/, $/, '')}`,
);
process.exit(1);
}

await createAgentConfig({ context, chains: chainNames, out });
process.exit(0);
},
};
2 changes: 2 additions & 0 deletions typescript/cli/src/commands/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const ChainTypes = ['mainnet', 'testnet'];
export type ChainType = (typeof ChainTypes)[number];
75 changes: 75 additions & 0 deletions typescript/cli/src/config/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { fromError } from 'zod-validation-error';

import {
AgentConfigSchema,
ChainMap,
HyperlaneCore,
HyperlaneDeploymentArtifacts,
buildAgentConfig,
} from '@hyperlane-xyz/sdk';
import { objMap, promiseObjAll } from '@hyperlane-xyz/utils';

import { CommandContext } from '../context/types.js';
import { logBlue, logGreen, logRed } from '../logger.js';
import { writeYamlOrJson } from '../utils/files.js';

export async function createAgentConfig({
context,
chains,
out,
}: {
context: CommandContext;
chains: string[];
out: string;
}) {
logBlue('\nCreating agent config...');

const { registry, multiProvider, chainMetadata } = context;
const addresses = await registry.getAddresses();

const core = HyperlaneCore.fromAddressesMap(addresses, multiProvider);

const startBlocks = await promiseObjAll(
objMap(addresses, async (chain, _) => {
// If the index.from is specified in the chain metadata, use that.
const indexFrom = chainMetadata[chain].index?.from;
if (indexFrom !== undefined) {
return indexFrom;
}

const mailbox = core.getContracts(chain).mailbox;
try {
const deployedBlock = await mailbox.deployedBlock();
return deployedBlock.toNumber();
} catch (err) {
logRed(
`Failed to get deployed block to set an index for ${chain}, this is potentially an issue with rpc provider or a misconfiguration`,
);
process.exit(1);
}
}),
);

// @TODO: consider adding additional config used to pass in gas prices for Cosmos chains
const agentConfig = buildAgentConfig(
chains,
multiProvider,
addresses as ChainMap<HyperlaneDeploymentArtifacts>,
startBlocks,
);

try {
AgentConfigSchema.parse(agentConfig);
} catch (e) {
logRed(
`Agent config is invalid, this is possibly due to required contracts not being deployed. See details below:\n${fromError(
e,
).toString()}`,
);
process.exit(1);
}

logBlue(`Agent config is valid, writing to file ${out}`);
writeYamlOrJson(out, agentConfig, 'json');
logGreen(`✅ Agent config successfully written to ${out}`);
}
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5715,6 +5715,7 @@ __metadata:
yaml: "npm:^2.4.1"
yargs: "npm:^17.7.2"
zod: "npm:^3.21.2"
zod-validation-error: "npm:^3.3.0"
bin:
hyperlane: ./dist/cli.js
languageName: unknown
Expand Down Expand Up @@ -26173,6 +26174,15 @@ __metadata:
languageName: node
linkType: hard

"zod-validation-error@npm:^3.3.0":
version: 3.3.0
resolution: "zod-validation-error@npm:3.3.0"
peerDependencies:
zod: ^3.18.0
checksum: 19574cbc453c7a41105de572546e95191958f459dd93440f541a42c0ff209b56f1cd54e8f8ab1899430dd7c183e11cd16e8cace0bd4fc5d356ef772645210792
languageName: node
linkType: hard

"zod@npm:^3.21.2":
version: 3.21.2
resolution: "zod@npm:3.21.2"
Expand Down

0 comments on commit 35f8699

Please sign in to comment.