Skip to content

Commit 0d8624d

Browse files
refactor: optional mailbox on warp config (#5500)
### Description This PR updates the warp route deployment configuration to handle mailbox addresses more consistently. Key changes: - Makes mailbox optional in the WarpRouteDeployConfig schema - Introduces a new WarpRouteDeployConfigMailboxRequired type for internal use - Updates the config filling logic to handle mailbox addresses through the context - Removes direct mailbox address prompting during config creation ### Drive-by changes - Cleaned up some unused imports - Improved type safety around mailbox requirements - Updated tests to reflect new mailbox handling ### Related issues #5258 ### Backward compatibility Yes - This change maintains backward compatibility while improving the internal handling of mailbox addresses. ### Testing Unit Tests - Existing tests have been updated to accommodate the new mailbox handling approach
1 parent 2714508 commit 0d8624d

14 files changed

Lines changed: 121 additions & 92 deletions

File tree

.changeset/forty-mugs-deny.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@hyperlane-xyz/cli': minor
3+
'@hyperlane-xyz/sdk': minor
4+
---
5+
6+
Make mailbox optional on warp deploy config

.changeset/violet-beers-hang.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,21 @@ Removes `HypNativeScaled` in favor of `HypNative` with `scale` parameter.
1313
If you want to keep the same behavior as before, you can set `scale` to `1` in all your routes.
1414

1515
### `HypNativeScaled` Usage
16+
1617
```diff
1718
- HypNativeScaled(scale, mailbox)
1819
+ HypNative(scale, mailbox)
1920
```
2021

2122
### `HypERC20` Usage
23+
2224
```diff
2325
- HypERC20(decimals, mailbox)
2426
+ HypERC20(decimals, scale, mailbox)
2527
```
2628

2729
### `HypERC20Collateral` Usage
30+
2831
```diff
2932
- HypERC20Collateral(erc20, mailbox)
3033
+ HypERC20Collateral(erc20, scale, mailbox)

typescript/cli/src/commands/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ const validateWarpCommand: CommandModuleWithContext<{ path: string }> = {
9797
builder: {
9898
path: inputFileCommandOption(),
9999
},
100-
handler: async ({ path }) => {
101-
await readWarpRouteDeployConfig(path);
100+
handler: async ({ path, context }) => {
101+
await readWarpRouteDeployConfig(path, context);
102102
logGreen('Config is valid');
103103
process.exit(0);
104104
},

typescript/cli/src/commands/warp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export const apply: CommandModuleWithWriteContext<{
115115

116116
if (strategyUrl)
117117
ChainSubmissionStrategySchema.parse(readYamlOrJson(strategyUrl));
118-
const warpDeployConfig = await readWarpRouteDeployConfig(config);
118+
const warpDeployConfig = await readWarpRouteDeployConfig(config, context);
119119

120120
await runWarpRouteApply({
121121
context,

typescript/cli/src/config/warp.ts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,11 @@ import {
1111
WarpCoreConfig,
1212
WarpCoreConfigSchema,
1313
WarpRouteDeployConfig,
14+
WarpRouteDeployConfigMailboxRequired,
15+
WarpRouteDeployConfigMailboxRequiredSchema,
1416
WarpRouteDeployConfigSchema,
1517
} from '@hyperlane-xyz/sdk';
16-
import {
17-
Address,
18-
assert,
19-
isAddress,
20-
objMap,
21-
promiseObjAll,
22-
} from '@hyperlane-xyz/utils';
18+
import { Address, assert, objMap, promiseObjAll } from '@hyperlane-xyz/utils';
2319

2420
import { CommandContext } from '../context/types.js';
2521
import { errorRed, log, logBlue, logGreen } from '../logger.js';
@@ -96,15 +92,16 @@ async function fillDefaults(
9692

9793
export async function readWarpRouteDeployConfig(
9894
filePath: string,
99-
context?: CommandContext,
100-
): Promise<WarpRouteDeployConfig> {
95+
context: CommandContext,
96+
): Promise<WarpRouteDeployConfigMailboxRequired> {
10197
let config = readYamlOrJson(filePath);
10298
if (!config)
10399
throw new Error(`No warp route deploy config found at ${filePath}`);
104-
if (context) {
105-
config = await fillDefaults(context, config as any);
106-
}
107-
return WarpRouteDeployConfigSchema.parse(config);
100+
101+
config = await fillDefaults(context, config as any);
102+
103+
//fillDefaults would have added a mailbox to the config if it was missing
104+
return WarpRouteDeployConfigMailboxRequiredSchema.parse(config);
108105
}
109106

110107
export function isValidWarpRouteDeployConfig(config: any) {
@@ -142,16 +139,6 @@ export async function createWarpRouteDeployConfig({
142139
'signer',
143140
);
144141

145-
// default to the mailbox from the registry and if not found ask to the user to submit one
146-
const chainAddresses = await context.registry.getChainAddresses(chain);
147-
148-
const mailbox =
149-
chainAddresses?.mailbox ??
150-
(await input({
151-
validate: isAddress,
152-
message: `Could not retrieve mailbox address from the registry for chain "${chain}". Please enter a valid mailbox address:`,
153-
}));
154-
155142
const proxyAdmin: DeployedOwnableConfig | undefined =
156143
await setProxyAdminConfig(context, chain);
157144

@@ -193,7 +180,6 @@ export async function createWarpRouteDeployConfig({
193180
case TokenType.collateralUri:
194181
case TokenType.fastCollateral:
195182
result[chain] = {
196-
mailbox,
197183
type,
198184
owner,
199185
proxyAdmin,
@@ -206,7 +192,6 @@ export async function createWarpRouteDeployConfig({
206192
break;
207193
case TokenType.syntheticRebase:
208194
result[chain] = {
209-
mailbox,
210195
type,
211196
owner,
212197
isNft,
@@ -221,7 +206,6 @@ export async function createWarpRouteDeployConfig({
221206
break;
222207
case TokenType.collateralVaultRebase:
223208
result[chain] = {
224-
mailbox,
225209
type,
226210
owner,
227211
proxyAdmin,
@@ -236,7 +220,6 @@ export async function createWarpRouteDeployConfig({
236220
break;
237221
case TokenType.collateralVault:
238222
result[chain] = {
239-
mailbox,
240223
type,
241224
owner,
242225
proxyAdmin,
@@ -249,7 +232,6 @@ export async function createWarpRouteDeployConfig({
249232
break;
250233
default:
251234
result[chain] = {
252-
mailbox,
253235
type,
254236
owner,
255237
proxyAdmin,

typescript/cli/src/deploy/warp.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
WarpCoreConfig,
4747
WarpCoreConfigSchema,
4848
WarpRouteDeployConfig,
49+
WarpRouteDeployConfigMailboxRequired,
4950
WarpRouteDeployConfigSchema,
5051
attachContractsMap,
5152
connectContractsMap,
@@ -88,7 +89,7 @@ import {
8889

8990
interface DeployParams {
9091
context: WriteCommandContext;
91-
warpDeployConfig: WarpRouteDeployConfig;
92+
warpDeployConfig: WarpRouteDeployConfigMailboxRequired;
9293
}
9394

9495
interface WarpApplyParams extends DeployParams {
@@ -193,7 +194,7 @@ async function executeDeploy(
193194
? new HypERC721Deployer(multiProvider)
194195
: new HypERC20Deployer(multiProvider); // TODO: replace with EvmERC20WarpModule
195196

196-
const config: WarpRouteDeployConfig =
197+
const config: WarpRouteDeployConfigMailboxRequired =
197198
isDryRun && dryRunChain
198199
? { [dryRunChain]: warpDeployConfig[dryRunChain] }
199200
: warpDeployConfig;
@@ -238,11 +239,11 @@ async function writeDeploymentArtifacts(
238239
}
239240

240241
async function resolveWarpIsmAndHook(
241-
warpConfig: WarpRouteDeployConfig,
242+
warpConfig: WarpRouteDeployConfigMailboxRequired,
242243
context: WriteCommandContext,
243244
ismFactoryDeployer: HyperlaneProxyFactoryDeployer,
244245
contractVerifier?: ContractVerifier,
245-
): Promise<WarpRouteDeployConfig> {
246+
): Promise<WarpRouteDeployConfigMailboxRequired> {
246247
return promiseObjAll(
247248
objMap(warpConfig, async (chain, config) => {
248249
const registryAddresses = await context.registry.getAddresses();
@@ -573,12 +574,12 @@ async function extendWarpRoute(
573574
const warpCoreChains = Object.keys(warpCoreConfigByChain);
574575

575576
// Split between the existing and additional config
576-
const existingConfigs: WarpRouteDeployConfig = objFilter(
577+
const existingConfigs: WarpRouteDeployConfigMailboxRequired = objFilter(
577578
warpDeployConfig,
578579
(chain, _config): _config is any => warpCoreChains.includes(chain),
579580
);
580581

581-
let extendedConfigs: WarpRouteDeployConfig = objFilter(
582+
let extendedConfigs: WarpRouteDeployConfigMailboxRequired = objFilter(
582583
warpDeployConfig,
583584
(chain, _config): _config is any => !warpCoreChains.includes(chain),
584585
);
@@ -660,12 +661,14 @@ async function updateExistingWarpRoute(
660661
staticAggregationHookFactory,
661662
staticMerkleRootWeightedMultisigIsmFactory,
662663
staticMessageIdWeightedMultisigIsmFactory,
664+
mailbox,
663665
} = registryAddresses[chain];
666+
const configWithMailbox = { ...config, mailbox };
664667

665668
const evmERC20WarpModule = new EvmERC20WarpModule(
666669
multiProvider,
667670
{
668-
config,
671+
config: configWithMailbox,
669672
chain,
670673
addresses: {
671674
deployedTokenRoute,
@@ -681,7 +684,9 @@ async function updateExistingWarpRoute(
681684
ccipContractCache,
682685
contractVerifier,
683686
);
684-
transactions.push(...(await evmERC20WarpModule.update(config)));
687+
transactions.push(
688+
...(await evmERC20WarpModule.update(configWithMailbox)),
689+
);
685690
});
686691
}),
687692
);
@@ -708,9 +713,9 @@ export function readChainSubmissionStrategy(
708713
*/
709714
async function deriveMetadataFromExisting(
710715
multiProvider: MultiProvider,
711-
existingConfigs: WarpRouteDeployConfig,
712-
extendedConfigs: WarpRouteDeployConfig,
713-
): Promise<WarpRouteDeployConfig> {
716+
existingConfigs: WarpRouteDeployConfigMailboxRequired,
717+
extendedConfigs: WarpRouteDeployConfigMailboxRequired,
718+
): Promise<WarpRouteDeployConfigMailboxRequired> {
714719
const existingTokenMetadata = await HypERC20Deployer.deriveTokenMetadata(
715720
multiProvider,
716721
existingConfigs,

typescript/cli/src/tests/warp/warp-init.e2e-test.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { expect } from 'chai';
22
import { Wallet } from 'ethers';
33

4-
import { ChainAddresses } from '@hyperlane-xyz/registry';
54
import {
6-
ChainMap,
75
ChainName,
86
TokenType,
97
WarpRouteDeployConfig,
@@ -16,15 +14,13 @@ import {
1614
CHAIN_NAME_2,
1715
CHAIN_NAME_3,
1816
CONFIRM_DETECTED_OWNER_STEP,
19-
CORE_CONFIG_PATH,
2017
DEFAULT_E2E_TEST_TIMEOUT,
2118
KeyBoardKeys,
2219
SELECT_ANVIL_2_AND_ANVIL_3_STEPS,
2320
SELECT_ANVIL_2_FROM_MULTICHAIN_PICKER,
2421
SELECT_MAINNET_CHAIN_TYPE_STEP,
2522
TestPromptAction,
2623
WARP_CONFIG_PATH_2,
27-
deployOrUseExistingCore,
2824
deployToken,
2925
handlePrompts,
3026
} from '../commands/helpers.js';
@@ -33,38 +29,21 @@ import { hyperlaneWarpInit } from '../commands/warp.js';
3329
describe('hyperlane warp init e2e tests', async function () {
3430
this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT);
3531

36-
let chain2Addresses: ChainAddresses = {};
37-
let chain3Addresses: ChainAddresses = {};
3832
let initialOwnerAddress: Address;
39-
let chainMapAddresses: ChainMap<ChainAddresses> = {};
4033

4134
before(async function () {
42-
[chain2Addresses, chain3Addresses] = await Promise.all([
43-
deployOrUseExistingCore(CHAIN_NAME_2, CORE_CONFIG_PATH, ANVIL_KEY),
44-
deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY),
45-
]);
46-
47-
chainMapAddresses = {
48-
[CHAIN_NAME_2]: chain2Addresses,
49-
[CHAIN_NAME_3]: chain3Addresses,
50-
};
51-
5235
const wallet = new Wallet(ANVIL_KEY);
5336
initialOwnerAddress = wallet.address;
5437
});
5538

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

6446
const chain2TokenConfig = warpConfig[chainName];
65-
expect(chain2TokenConfig.mailbox).equal(
66-
chainMapAddresses[chainName].mailbox,
67-
);
6847
expect(chain2TokenConfig.owner).equal(initialOwnerAddress);
6948
expect(chain2TokenConfig.type).equal(TokenType.native);
7049
expect(chain2TokenConfig.interchainSecurityModule).undefined;
@@ -96,7 +75,7 @@ describe('hyperlane warp init e2e tests', async function () {
9675
const warpConfig: WarpRouteDeployConfig =
9776
readYamlOrJson(WARP_CONFIG_PATH_2);
9877

99-
assertWarpConfig(warpConfig, chainMapAddresses, CHAIN_NAME_2);
78+
assertWarpConfig(warpConfig, CHAIN_NAME_2);
10079
});
10180

10281
it('it should generate a warp deploy config with a 2 chains warp route (native->native)', async function () {
@@ -125,7 +104,7 @@ describe('hyperlane warp init e2e tests', async function () {
125104
readYamlOrJson(WARP_CONFIG_PATH_2);
126105

127106
[CHAIN_NAME_2, CHAIN_NAME_3].map((chainName) =>
128-
assertWarpConfig(warpConfig, chainMapAddresses, chainName),
107+
assertWarpConfig(warpConfig, chainName),
129108
);
130109
});
131110

@@ -167,15 +146,13 @@ describe('hyperlane warp init e2e tests', async function () {
167146
expect(warpConfig[CHAIN_NAME_2]).not.to.be.undefined;
168147

169148
const chain2TokenConfig = warpConfig[CHAIN_NAME_2];
170-
expect(chain2TokenConfig.mailbox).equal(chain2Addresses.mailbox);
171149
expect(chain2TokenConfig.owner).equal(initialOwnerAddress);
172150
expect(chain2TokenConfig.type).equal(TokenType.collateral);
173151
expect((chain2TokenConfig as any).token).equal(erc20Token.address);
174152

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

177155
const chain3TokenConfig = warpConfig[CHAIN_NAME_3];
178-
expect(chain3TokenConfig.mailbox).equal(chain3Addresses.mailbox);
179156
expect(chain3TokenConfig.owner).equal(initialOwnerAddress);
180157
expect(chain3TokenConfig.type).equal(TokenType.synthetic);
181158
});

0 commit comments

Comments
 (0)