feat: implement `hyperlane warp deploy` (#3920)

### Description
- Update `hyperlane warp deploy` command

### Drive-by changes
- Move Core deploy to core.ts
- Update Module return args to match TokenType

### Related issues
- Fixes
#[3541](https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3541)


### Backward compatibility
yes, but old command no longer applies

### Testing
Manual

---------

Signed-off-by: Paul Balaji <paul@hyperlane.xyz>
Co-authored-by: Paul Balaji <paul@hyperlane.xyz>
Co-authored-by: Yorke Rhodes <yorke@hyperlane.xyz>
Co-authored-by: J M Rossy <jm.rossy@gmail.com>
Co-authored-by: Noah Bayindirli 🥂 <noah@primeprotocol.xyz>
Co-authored-by: Noah Bayindirli 🥂 <noah@hyperlane.xyz>
pull/3937/head
Lee 5 months ago committed by GitHub
parent 63367c5504
commit 6db9fa9ada
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .changeset/slimy-toys-argue.md
  2. 6
      typescript/cli/ci-test.sh
  3. 59
      typescript/cli/src/commands/core.ts
  4. 94
      typescript/cli/src/commands/deploy.ts
  5. 35
      typescript/cli/src/commands/warp.ts
  6. 2
      typescript/sdk/src/index.ts
  7. 37
      typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts
  8. 1
      typescript/sdk/src/token/EvmERC20WarpModule.ts

@ -0,0 +1,6 @@
---
'@hyperlane-xyz/cli': minor
'@hyperlane-xyz/sdk': minor
---
Implement hyperlane warp deploy

@ -162,7 +162,7 @@ run_hyperlane_deploy_warp_dry_run() {
update_deployer_balance;
echo -e "\nDry-running warp route deployments to Alfajores"
yarn workspace @hyperlane-xyz/cli run hyperlane deploy warp \
yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \
--dry-run alfajores \
--overrides ${TEST_CONFIGS_PATH}/dry-run \
--config ${TEST_CONFIGS_PATH}/dry-run/warp-route-deployment.yaml \
@ -200,7 +200,7 @@ run_hyperlane_deploy_warp() {
update_deployer_balance;
echo -e "\nDeploying hypNative warp route"
yarn workspace @hyperlane-xyz/cli run hyperlane deploy warp \
yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \
--registry $REGISTRY_PATH \
--overrides " " \
--config $WARP_DEPLOY_CONFIG_PATH \
@ -213,7 +213,7 @@ run_hyperlane_deploy_warp() {
/tmp/warp-collateral-deployment.json \
echo "Deploying hypCollateral warp route"
yarn workspace @hyperlane-xyz/cli run hyperlane deploy warp \
yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \
--registry $REGISTRY_PATH \
--overrides " " \
--config /tmp/warp-collateral-deployment.json \

@ -4,7 +4,12 @@ import { CoreConfigSchema, EvmCoreReader, IsmConfig } from '@hyperlane-xyz/sdk';
import { createHookConfig } from '../config/hooks.js';
import { createIsmConfig, createTrustedRelayerConfig } from '../config/ism.js';
import { CommandModuleWithContext } from '../context/types.js';
import {
CommandModuleWithContext,
CommandModuleWithWriteContext,
} from '../context/types.js';
import { runCoreDeploy } from '../deploy/core.js';
import { evaluateIfDryRunFailure } from '../deploy/dry-run.js';
import {
log,
logBlue,
@ -13,10 +18,14 @@ import {
logRed,
} from '../logger.js';
import { detectAndConfirmOrPrompt } from '../utils/chains.js';
import { writeYamlOrJson } from '../utils/files.js';
import { readYamlOrJson, writeYamlOrJson } from '../utils/files.js';
import { deploy } from './deploy.js';
import { chainCommandOption, outputFileCommandOption } from './options.js';
import {
chainCommandOption,
dryRunCommandOption,
fromAddressCommandOption,
outputFileCommandOption,
} from './options.js';
/**
* Parent command
@ -34,6 +43,48 @@ export const coreCommand: CommandModule = {
handler: () => log('Command required'),
};
/**
* 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: CommandModuleWithWriteContext<{
chain: string;
config: string;
dryRun: string;
fromAddress: string;
}> = {
command: 'deploy',
describe: 'Deploy Hyperlane contracts',
builder: {
chain: chainCommandOption,
config: outputFileCommandOption(
'./configs/core-config.yaml',
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 }) => {
logGray(`Hyperlane permissionless deployment${dryRun ? ' dry-run' : ''}`);
logGray(`------------------------------------------------`);
try {
await runCoreDeploy({
context,
chain,
config: readYamlOrJson(configFilePath),
});
} catch (error: any) {
evaluateIfDryRunFailure(error, dryRun);
throw error;
}
process.exit(0);
},
};
export const configure: CommandModuleWithContext<{
ismAdvanced: boolean;
config: string;

@ -1,25 +1,13 @@
import { CommandModule } from 'yargs';
import {
CommandModuleWithContext,
CommandModuleWithWriteContext,
} from '../context/types.js';
import { CommandModuleWithContext } from '../context/types.js';
import { runKurtosisAgentDeploy } from '../deploy/agent.js';
import { runCoreDeploy } from '../deploy/core.js';
import { evaluateIfDryRunFailure } from '../deploy/dry-run.js';
import { runWarpRouteDeploy } from '../deploy/warp.js';
import { log, logGray } from '../logger.js';
import { readYamlOrJson } from '../utils/files.js';
import {
agentConfigCommandOption,
agentTargetsCommandOption,
chainCommandOption,
dryRunCommandOption,
fromAddressCommandOption,
originCommandOption,
outputFileCommandOption,
warpDeploymentConfigCommandOption,
} from './options.js';
/**
@ -29,11 +17,7 @@ export const deployCommand: CommandModule = {
command: 'deploy',
describe: 'Permissionlessly deploy a Hyperlane contracts or extensions',
builder: (yargs) =>
yargs
.command(warpCommand)
.command(agentCommand)
.version(false)
.demandCommand(),
yargs.command(agentCommand).version(false).demandCommand(),
handler: () => log('Command required'),
};
@ -64,77 +48,3 @@ const agentCommand: CommandModuleWithContext<{
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: CommandModuleWithWriteContext<{
chain: string;
config: string;
dryRun: string;
fromAddress: string;
}> = {
command: 'deploy',
describe: 'Deploy Hyperlane contracts',
builder: {
chain: chainCommandOption,
config: outputFileCommandOption(
'./configs/core-config.yaml',
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 }) => {
logGray(`Hyperlane permissionless deployment${dryRun ? ' dry-run' : ''}`);
logGray(`------------------------------------------------`);
try {
await runCoreDeploy({
context,
chain,
config: readYamlOrJson(configFilePath),
});
} catch (error: any) {
evaluateIfDryRunFailure(error, dryRun);
throw error;
}
process.exit(0);
},
};
/**
* Warp command
*/
const warpCommand: CommandModuleWithWriteContext<{
config: string;
'dry-run': string;
'from-address': string;
}> = {
command: 'warp',
describe: 'Deploy Warp Route contracts',
builder: {
config: warpDeploymentConfigCommandOption,
'dry-run': dryRunCommandOption,
'from-address': fromAddressCommandOption,
},
handler: async ({ context, config, dryRun }) => {
logGray(`Hyperlane warp route deployment${dryRun ? ' dry-run' : ''}`);
logGray('------------------------------------------------');
try {
await runWarpRouteDeploy({
context,
warpRouteDeploymentConfigPath: config,
});
} catch (error: any) {
evaluateIfDryRunFailure(error, dryRun);
throw error;
}
process.exit(0);
},
};

@ -7,6 +7,8 @@ import {
CommandModuleWithContext,
CommandModuleWithWriteContext,
} from '../context/types.js';
import { evaluateIfDryRunFailure } from '../deploy/dry-run.js';
import { runWarpRouteDeploy } from '../deploy/warp.js';
import { log, logGray, logGreen } from '../logger.js';
import { sendTestTransfer } from '../send/transfer.js';
import { writeFileAtPath } from '../utils/files.js';
@ -14,8 +16,11 @@ import { writeFileAtPath } from '../utils/files.js';
import {
addressCommandOption,
chainCommandOption,
dryRunCommandOption,
fromAddressCommandOption,
outputFileCommandOption,
warpCoreConfigCommandOption,
warpDeploymentConfigCommandOption,
} from './options.js';
import { MessageOptionsArgTypes, messageOptions } from './send.js';
@ -28,6 +33,7 @@ export const warpCommand: CommandModule = {
builder: (yargs) =>
yargs
.command(configure)
.command(deploy)
.command(read)
.command(send)
.version(false)
@ -36,6 +42,35 @@ export const warpCommand: CommandModule = {
handler: () => log('Command required'),
};
export const deploy: CommandModuleWithWriteContext<{
config: string;
'dry-run': string;
'from-address': string;
}> = {
command: 'deploy',
describe: 'Deploy Warp Route contracts',
builder: {
config: warpDeploymentConfigCommandOption,
'dry-run': dryRunCommandOption,
'from-address': fromAddressCommandOption,
},
handler: async ({ context, config, dryRun }) => {
logGray(`Hyperlane warp route deployment${dryRun ? ' dry-run' : ''}`);
logGray('------------------------------------------------');
try {
await runWarpRouteDeploy({
context,
warpRouteDeploymentConfigPath: config,
});
} catch (error: any) {
evaluateIfDryRunFailure(error, dryRun);
throw error;
}
process.exit(0);
},
};
export const configure: CommandModuleWithContext<{
ismAdvanced: boolean;
out: string;

@ -447,6 +447,7 @@ export { HypERC20App } from './token/app.js';
export { HypERC20Checker } from './token/checker.js';
export { TokenType } from './token/config.js';
export {
hypERC20factories,
HypERC20Factories,
HypERC721Factories,
TokenFactories,
@ -505,3 +506,4 @@ export { S3Config, S3Wrapper, S3Receipt } from './aws/s3.js';
export { canProposeSafeTransactions, getSafe, getSafeDelegates, getSafeService } from './utils/gnosisSafe.js';
export { EvmCoreModule, DeployedCoreAdresses } from './core/EvmCoreModule.js';
export { EvmERC20WarpModule } from './token/EvmERC20WarpModule.js';

@ -132,13 +132,16 @@ describe('EvmERC20WarpHyperlaneModule', async () => {
expect(tokenType).to.equal(TokenType.collateralVault);
// Validate onchain token values
const collateralVault = HypERC20CollateralVaultDeposit__factory.connect(
deployedTokenRoute,
signer,
const collateralVaultContract =
HypERC20CollateralVaultDeposit__factory.connect(
deployedTokenRoute,
signer,
);
await validateCoreValues(collateralVaultContract);
expect(await collateralVaultContract.vault()).to.equal(vault.address);
expect(await collateralVaultContract.wrappedToken()).to.equal(
token.address,
);
await validateCoreValues(collateralVault);
expect(await collateralVault.vault()).to.equal(vault.address);
expect(await collateralVault.wrappedToken()).to.equal(token.address);
});
it('should create with a a synthetic config', async () => {
@ -167,12 +170,15 @@ describe('EvmERC20WarpHyperlaneModule', async () => {
expect(tokenType).to.equal(TokenType.synthetic);
// Validate onchain token values
const synthetic = HypERC20__factory.connect(deployedTokenRoute, signer);
await validateCoreValues(synthetic);
expect(await synthetic.name()).to.equal(TOKEN_NAME);
expect(await synthetic.symbol()).to.equal(TOKEN_NAME);
expect(await synthetic.decimals()).to.equal(TOKEN_DECIMALS);
expect(await synthetic.totalSupply()).to.equal(TOKEN_SUPPLY);
const syntheticContract = HypERC20__factory.connect(
deployedTokenRoute,
signer,
);
await validateCoreValues(syntheticContract);
expect(await syntheticContract.name()).to.equal(TOKEN_NAME);
expect(await syntheticContract.symbol()).to.equal(TOKEN_NAME);
expect(await syntheticContract.decimals()).to.equal(TOKEN_DECIMALS);
expect(await syntheticContract.totalSupply()).to.equal(TOKEN_SUPPLY);
});
it('should create with a a native config', async () => {
@ -196,7 +202,10 @@ describe('EvmERC20WarpHyperlaneModule', async () => {
expect(tokenType).to.equal(TokenType.native);
// Validate onchain token values
const native = HypNative__factory.connect(deployedTokenRoute, signer);
await validateCoreValues(native);
const nativeContract = HypNative__factory.connect(
deployedTokenRoute,
signer,
);
await validateCoreValues(nativeContract);
});
});

@ -34,7 +34,6 @@ export class EvmERC20WarpModule extends HyperlaneModule<
>,
) {
super(args);
this.reader = new EvmERC20WarpRouteReader(multiProvider, args.chain);
}

Loading…
Cancel
Save