Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: optional mailbox on warp config #5500

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions .changeset/forty-mugs-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@hyperlane-xyz/cli': minor
'@hyperlane-xyz/sdk': minor
---

Make mailbox optional on warp deploy config
4 changes: 2 additions & 2 deletions typescript/cli/src/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ const validateWarpCommand: CommandModuleWithContext<{ path: string }> = {
builder: {
path: inputFileCommandOption(),
},
handler: async ({ path }) => {
await readWarpRouteDeployConfig(path);
handler: async ({ path, context }) => {
await readWarpRouteDeployConfig(path, context);
logGreen('Config is valid');
process.exit(0);
},
Expand Down
2 changes: 1 addition & 1 deletion typescript/cli/src/commands/warp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const apply: CommandModuleWithWriteContext<{

if (strategyUrl)
ChainSubmissionStrategySchema.parse(readYamlOrJson(strategyUrl));
const warpDeployConfig = await readWarpRouteDeployConfig(config);
const warpDeployConfig = await readWarpRouteDeployConfig(config, context);

await runWarpRouteApply({
context,
Expand Down
39 changes: 11 additions & 28 deletions typescript/cli/src/config/warp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,10 @@ import {
WarpCoreConfig,
WarpCoreConfigSchema,
WarpRouteDeployConfig,
WarpRouteDeployConfigMailboxRequired,
WarpRouteDeployConfigSchema,
} from '@hyperlane-xyz/sdk';
import {
Address,
assert,
isAddress,
objMap,
promiseObjAll,
} from '@hyperlane-xyz/utils';
import { Address, assert, objMap, promiseObjAll } from '@hyperlane-xyz/utils';

import { CommandContext } from '../context/types.js';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
Expand Down Expand Up @@ -96,15 +91,18 @@ async function fillDefaults(

export async function readWarpRouteDeployConfig(
filePath: string,
context?: CommandContext,
): Promise<WarpRouteDeployConfig> {
context: CommandContext,
): Promise<WarpRouteDeployConfigMailboxRequired> {
let config = readYamlOrJson(filePath);
if (!config)
throw new Error(`No warp route deploy config found at ${filePath}`);
if (context) {
config = await fillDefaults(context, config as any);
}
return WarpRouteDeployConfigSchema.parse(config);

config = await fillDefaults(context, config as any);

//fillDefaults would have added a mailbox to the config if it was missing
return WarpRouteDeployConfigSchema.parse(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use WarpRouteDeployConfigMailboxRequired to parse then to avoid the type cast?

config,
) as WarpRouteDeployConfigMailboxRequired;
}

export function isValidWarpRouteDeployConfig(config: any) {
Expand Down Expand Up @@ -142,16 +140,6 @@ export async function createWarpRouteDeployConfig({
'signer',
);

// default to the mailbox from the registry and if not found ask to the user to submit one
const chainAddresses = await context.registry.getChainAddresses(chain);

const mailbox =
chainAddresses?.mailbox ??
(await input({
validate: isAddress,
message: `Could not retrieve mailbox address from the registry for chain "${chain}". Please enter a valid mailbox address:`,
}));

const proxyAdmin: DeployedOwnableConfig = await setProxyAdminConfig(
context,
chain,
Expand Down Expand Up @@ -198,7 +186,6 @@ export async function createWarpRouteDeployConfig({
case TokenType.collateralUri:
case TokenType.fastCollateral:
result[chain] = {
mailbox,
type,
owner,
proxyAdmin,
Expand All @@ -211,7 +198,6 @@ export async function createWarpRouteDeployConfig({
break;
case TokenType.syntheticRebase:
result[chain] = {
mailbox,
type,
owner,
isNft,
Expand All @@ -226,7 +212,6 @@ export async function createWarpRouteDeployConfig({
break;
case TokenType.collateralVaultRebase:
result[chain] = {
mailbox,
type,
owner,
proxyAdmin,
Expand All @@ -241,7 +226,6 @@ export async function createWarpRouteDeployConfig({
break;
case TokenType.collateralVault:
result[chain] = {
mailbox,
type,
owner,
proxyAdmin,
Expand All @@ -254,7 +238,6 @@ export async function createWarpRouteDeployConfig({
break;
default:
result[chain] = {
mailbox,
type,
owner,
proxyAdmin,
Expand Down
27 changes: 16 additions & 11 deletions typescript/cli/src/deploy/warp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
WarpCoreConfig,
WarpCoreConfigSchema,
WarpRouteDeployConfig,
WarpRouteDeployConfigMailboxRequired,
WarpRouteDeployConfigSchema,
attachContractsMap,
connectContractsMap,
Expand Down Expand Up @@ -86,7 +87,7 @@ import {

interface DeployParams {
context: WriteCommandContext;
warpDeployConfig: WarpRouteDeployConfig;
warpDeployConfig: WarpRouteDeployConfigMailboxRequired;
}

interface WarpApplyParams extends DeployParams {
Expand Down Expand Up @@ -191,7 +192,7 @@ async function executeDeploy(
? new HypERC721Deployer(multiProvider)
: new HypERC20Deployer(multiProvider); // TODO: replace with EvmERC20WarpModule

const config: WarpRouteDeployConfig =
const config: WarpRouteDeployConfigMailboxRequired =
isDryRun && dryRunChain
? { [dryRunChain]: warpDeployConfig[dryRunChain] }
: warpDeployConfig;
Expand Down Expand Up @@ -236,11 +237,11 @@ async function writeDeploymentArtifacts(
}

async function resolveWarpIsmAndHook(
warpConfig: WarpRouteDeployConfig,
warpConfig: WarpRouteDeployConfigMailboxRequired,
context: WriteCommandContext,
ismFactoryDeployer: HyperlaneProxyFactoryDeployer,
contractVerifier?: ContractVerifier,
): Promise<WarpRouteDeployConfig> {
): Promise<WarpRouteDeployConfigMailboxRequired> {
return promiseObjAll(
objMap(warpConfig, async (chain, config) => {
const chainAddresses = await context.registry.getChainAddresses(chain);
Expand Down Expand Up @@ -560,12 +561,12 @@ async function extendWarpRoute(
const warpCoreChains = Object.keys(warpCoreConfigByChain);

// Split between the existing and additional config
const existingConfigs: WarpRouteDeployConfig = objFilter(
const existingConfigs: WarpRouteDeployConfigMailboxRequired = objFilter(
warpDeployConfig,
(chain, _config): _config is any => warpCoreChains.includes(chain),
);

let extendedConfigs: WarpRouteDeployConfig = objFilter(
let extendedConfigs: WarpRouteDeployConfigMailboxRequired = objFilter(
warpDeployConfig,
(chain, _config): _config is any => !warpCoreChains.includes(chain),
);
Expand Down Expand Up @@ -646,12 +647,14 @@ async function updateExistingWarpRoute(
staticAggregationHookFactory,
staticMerkleRootWeightedMultisigIsmFactory,
staticMessageIdWeightedMultisigIsmFactory,
mailbox,
} = registryAddresses[chain];
const configWithMailbox = { ...config, mailbox };

const evmERC20WarpModule = new EvmERC20WarpModule(
multiProvider,
{
config,
config: configWithMailbox,
chain,
addresses: {
deployedTokenRoute,
Expand All @@ -666,7 +669,9 @@ async function updateExistingWarpRoute(
},
contractVerifier,
);
transactions.push(...(await evmERC20WarpModule.update(config)));
transactions.push(
...(await evmERC20WarpModule.update(configWithMailbox)),
);
});
}),
);
Expand All @@ -693,9 +698,9 @@ export function readChainSubmissionStrategy(
*/
async function deriveMetadataFromExisting(
multiProvider: MultiProvider,
existingConfigs: WarpRouteDeployConfig,
extendedConfigs: WarpRouteDeployConfig,
): Promise<WarpRouteDeployConfig> {
existingConfigs: WarpRouteDeployConfigMailboxRequired,
extendedConfigs: WarpRouteDeployConfigMailboxRequired,
): Promise<WarpRouteDeployConfigMailboxRequired> {
const existingTokenMetadata = await HypERC20Deployer.deriveTokenMetadata(
multiProvider,
existingConfigs,
Expand Down
27 changes: 2 additions & 25 deletions typescript/cli/src/tests/warp/warp-init.e2e-test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { expect } from 'chai';
import { Wallet } from 'ethers';

import { ChainAddresses } from '@hyperlane-xyz/registry';
import {
ChainMap,
ChainName,
TokenType,
WarpRouteDeployConfig,
Expand All @@ -16,15 +14,13 @@ import {
CHAIN_NAME_2,
CHAIN_NAME_3,
CONFIRM_DETECTED_OWNER_STEP,
CORE_CONFIG_PATH,
DEFAULT_E2E_TEST_TIMEOUT,
KeyBoardKeys,
SELECT_ANVIL_2_AND_ANVIL_3_STEPS,
SELECT_ANVIL_2_FROM_MULTICHAIN_PICKER,
SELECT_MAINNET_CHAIN_TYPE_STEP,
TestPromptAction,
WARP_CONFIG_PATH_2,
deployOrUseExistingCore,
deployToken,
handlePrompts,
} from '../commands/helpers.js';
Expand All @@ -33,38 +29,21 @@ import { hyperlaneWarpInit } from '../commands/warp.js';
describe('hyperlane warp init e2e tests', async function () {
this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT);

let chain2Addresses: ChainAddresses = {};
let chain3Addresses: ChainAddresses = {};
let initialOwnerAddress: Address;
let chainMapAddresses: ChainMap<ChainAddresses> = {};

before(async function () {
[chain2Addresses, chain3Addresses] = await Promise.all([
deployOrUseExistingCore(CHAIN_NAME_2, CORE_CONFIG_PATH, ANVIL_KEY),
deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY),
]);

chainMapAddresses = {
[CHAIN_NAME_2]: chain2Addresses,
[CHAIN_NAME_3]: chain3Addresses,
};

const wallet = new Wallet(ANVIL_KEY);
initialOwnerAddress = wallet.address;
});

describe('hyperlane warp init --yes', () => {
function assertWarpConfig(
warpConfig: WarpRouteDeployConfig,
chainMapAddresses: ChainMap<ChainAddresses>,
chainName: ChainName,
) {
expect(warpConfig[chainName]).not.to.be.undefined;

const chain2TokenConfig = warpConfig[chainName];
expect(chain2TokenConfig.mailbox).equal(
chainMapAddresses[chainName].mailbox,
);
expect(chain2TokenConfig.owner).equal(initialOwnerAddress);
expect(chain2TokenConfig.type).equal(TokenType.native);
}
Expand Down Expand Up @@ -94,7 +73,7 @@ describe('hyperlane warp init e2e tests', async function () {
const warpConfig: WarpRouteDeployConfig =
readYamlOrJson(WARP_CONFIG_PATH_2);

assertWarpConfig(warpConfig, chainMapAddresses, CHAIN_NAME_2);
assertWarpConfig(warpConfig, CHAIN_NAME_2);
});

it('it should generate a warp deploy config with a 2 chains warp route (native->native)', async function () {
Expand Down Expand Up @@ -123,7 +102,7 @@ describe('hyperlane warp init e2e tests', async function () {
readYamlOrJson(WARP_CONFIG_PATH_2);

[CHAIN_NAME_2, CHAIN_NAME_3].map((chainName) =>
assertWarpConfig(warpConfig, chainMapAddresses, chainName),
assertWarpConfig(warpConfig, chainName),
);
});

Expand Down Expand Up @@ -165,15 +144,13 @@ describe('hyperlane warp init e2e tests', async function () {
expect(warpConfig[CHAIN_NAME_2]).not.to.be.undefined;

const chain2TokenConfig = warpConfig[CHAIN_NAME_2];
expect(chain2TokenConfig.mailbox).equal(chain2Addresses.mailbox);
expect(chain2TokenConfig.owner).equal(initialOwnerAddress);
expect(chain2TokenConfig.type).equal(TokenType.collateral);
expect((chain2TokenConfig as any).token).equal(erc20Token.address);

expect(warpConfig[CHAIN_NAME_3]).not.to.be.undefined;

const chain3TokenConfig = warpConfig[CHAIN_NAME_3];
expect(chain3TokenConfig.mailbox).equal(chain3Addresses.mailbox);
expect(chain3TokenConfig.owner).equal(initialOwnerAddress);
expect(chain3TokenConfig.type).equal(TokenType.synthetic);
});
Expand Down
1 change: 1 addition & 0 deletions typescript/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ export {
TokenMetadata,
TokenMetadataSchema,
WarpRouteDeployConfig,
WarpRouteDeployConfigMailboxRequired,
WarpRouteDeployConfigSchema,
WarpRouteDeployConfigSchemaErrors,
} from './token/types.js';
Expand Down
Loading
Loading