Skip to content

Commit bfe4d2e

Browse files
authored
fix: resolve import cycle warnings (#7980)
1 parent 6f6529c commit bfe4d2e

88 files changed

Lines changed: 454 additions & 374 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/fix-import-cycles.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@hyperlane-xyz/cli': patch
3+
'@hyperlane-xyz/infra': patch
4+
'@hyperlane-xyz/relayer': patch
5+
'@hyperlane-xyz/sdk': patch
6+
---
7+
8+
Import cycles flagged by oxlint were resolved by extracting shared code into dedicated leaf modules, performing a hard cutover (no backcompat re-exports), and using dependency injection for submitter factories and aggregation metadata decoding. The `import/no-cycle` lint rule is now enforced as an error.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hyperlane-xyz/provider-sdk': major
3+
---
4+
5+
Breaking: the `./protocol` subpath no longer re-exports `ProtocolType`, `ProtocolTypeValue`, or `ProtocolSmallestUnit`. These were moved to the new `./protocolType` module to break an import cycle. Import them from the main `@hyperlane-xyz/provider-sdk` entry instead.

oxlint.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"import/no-named-as-default": "warn",
3939
"import/no-duplicates": "warn",
4040
"@typescript-eslint/no-require-imports": "warn",
41-
"import/no-cycle": "off",
41+
"import/no-cycle": "error",
4242
"guard-for-in": "error",
4343
"import/no-self-import": "error",
4444
"no-console": "error",

typescript/cli/src/config/warp.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ import {
4141
getWarpRouteIdFromWarpDeployConfig,
4242
setProxyAdminConfig,
4343
} from '../utils/input.js';
44-
import { resolveWarpRouteId } from '../utils/warp.js';
45-
4644
import { createAdvancedIsmConfig } from './ism.js';
4745

4846
const TYPE_DESCRIPTIONS: Record<DeployableTokenType, string> = {
@@ -465,27 +463,3 @@ function createFallbackRoutingConfig(owner: Address): IsmConfig {
465463
owner,
466464
};
467465
}
468-
469-
export async function getWarpRouteDeployConfig({
470-
context,
471-
warpRouteId,
472-
}: {
473-
context: CommandContext;
474-
warpRouteId?: string;
475-
}): Promise<{
476-
config: WarpRouteDeployConfigMailboxRequired;
477-
resolvedWarpRouteId: string;
478-
}> {
479-
const resolvedWarpRouteId = await resolveWarpRouteId({
480-
warpRouteId,
481-
context,
482-
promptByDeploymentConfigs: true,
483-
});
484-
485-
const config = await readWarpRouteDeployConfig({
486-
context,
487-
warpRouteId: resolvedWarpRouteId,
488-
});
489-
490-
return { config, resolvedWarpRouteId };
491-
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { confirm } from '@inquirer/prompts';
2+
3+
import { type IRegistry } from '@hyperlane-xyz/registry';
4+
import {
5+
type ChainMap,
6+
type ChainMetadata,
7+
type ChainName,
8+
ExplorerFamily,
9+
} from '@hyperlane-xyz/sdk';
10+
11+
import { detectAndConfirmOrPrompt } from '../utils/input.js';
12+
13+
export async function requestAndSaveApiKeys(
14+
chains: ChainName[],
15+
chainMetadata: ChainMap<ChainMetadata>,
16+
registry: IRegistry,
17+
): Promise<ChainMap<string>> {
18+
const apiKeys: ChainMap<string> = {};
19+
20+
for (const chain of chains) {
21+
const blockExplorer = chainMetadata[chain]?.blockExplorers?.[0];
22+
if (blockExplorer?.family !== ExplorerFamily.Etherscan) {
23+
continue;
24+
}
25+
if (blockExplorer?.apiKey) {
26+
apiKeys[chain] = blockExplorer.apiKey;
27+
continue;
28+
}
29+
const wantApiKey = await confirm({
30+
default: false,
31+
message: `Do you want to use an API key to verify on this (${chain}) chain's block explorer`,
32+
});
33+
if (wantApiKey) {
34+
apiKeys[chain] = await detectAndConfirmOrPrompt(
35+
async () => {
36+
const blockExplorers = chainMetadata[chain].blockExplorers;
37+
if (!(blockExplorers && blockExplorers.length > 0)) return;
38+
for (const blockExplorer of blockExplorers) {
39+
/* The current apiKeys mapping only accepts one key, even if there are multiple explorer options present. */
40+
if (blockExplorer.apiKey) return blockExplorer.apiKey;
41+
}
42+
return undefined;
43+
},
44+
`Enter an API key for the ${chain} explorer`,
45+
`${chain} api key`,
46+
`${chain} metadata blockExplorers config`,
47+
);
48+
chainMetadata[chain].blockExplorers![0].apiKey = apiKeys[chain];
49+
await registry.updateChain({
50+
chainName: chain,
51+
metadata: chainMetadata[chain],
52+
});
53+
}
54+
}
55+
56+
return apiKeys;
57+
}

typescript/cli/src/context/context.ts

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { confirm } from '@inquirer/prompts';
21
import { type ethers } from 'ethers';
32

43
import { loadProtocolProviders } from '@hyperlane-xyz/deploy-sdk';
@@ -11,9 +10,7 @@ import { type IRegistry } from '@hyperlane-xyz/registry';
1110
import { getRegistry } from '@hyperlane-xyz/registry/fs';
1211
import {
1312
type ChainMap,
14-
type ChainMetadata,
1513
type ChainName,
16-
ExplorerFamily,
1714
MultiProtocolProvider,
1815
MultiProvider,
1916
} from '@hyperlane-xyz/sdk';
@@ -27,7 +24,6 @@ import {
2724

2825
import { isSignCommand } from '../commands/signCommands.js';
2926
import { readChainSubmissionStrategyConfig } from '../config/strategy.js';
30-
import { detectAndConfirmOrPrompt } from '../utils/input.js';
3127
import { getSigner } from '../utils/keys.js';
3228

3329
import { createAltVMSigners } from './altvm.js';
@@ -315,60 +311,6 @@ async function getMultiProtocolProvider(registry: IRegistry) {
315311
return new MultiProtocolProvider(chainMetadata);
316312
}
317313

318-
/**
319-
* Requests and saves Block Explorer API keys for the specified chains, prompting the user if necessary.
320-
*
321-
* @param chains - The list of chain names to request API keys for.
322-
* @param chainMetadata - The chain metadata, used to determine if an API key is already configured.
323-
* @param registry - The registry used to update the chain metadata with the new API key.
324-
* @returns A mapping of chain names to their API keys.
325-
*/
326-
export async function requestAndSaveApiKeys(
327-
chains: ChainName[],
328-
chainMetadata: ChainMap<ChainMetadata>,
329-
registry: IRegistry,
330-
): Promise<ChainMap<string>> {
331-
const apiKeys: ChainMap<string> = {};
332-
333-
for (const chain of chains) {
334-
const blockExplorer = chainMetadata[chain]?.blockExplorers?.[0];
335-
if (blockExplorer?.family !== ExplorerFamily.Etherscan) {
336-
continue;
337-
}
338-
if (blockExplorer?.apiKey) {
339-
apiKeys[chain] = blockExplorer.apiKey;
340-
continue;
341-
}
342-
const wantApiKey = await confirm({
343-
default: false,
344-
message: `Do you want to use an API key to verify on this (${chain}) chain's block explorer`,
345-
});
346-
if (wantApiKey) {
347-
apiKeys[chain] = await detectAndConfirmOrPrompt(
348-
async () => {
349-
const blockExplorers = chainMetadata[chain].blockExplorers;
350-
if (!(blockExplorers && blockExplorers.length > 0)) return;
351-
for (const blockExplorer of blockExplorers) {
352-
/* The current apiKeys mapping only accepts one key, even if there are multiple explorer options present. */
353-
if (blockExplorer.apiKey) return blockExplorer.apiKey;
354-
}
355-
return undefined;
356-
},
357-
`Enter an API key for the ${chain} explorer`,
358-
`${chain} api key`,
359-
`${chain} metadata blockExplorers config`,
360-
);
361-
chainMetadata[chain].blockExplorers![0].apiKey = apiKeys[chain];
362-
await registry.updateChain({
363-
chainName: chain,
364-
metadata: chainMetadata[chain],
365-
});
366-
}
367-
}
368-
369-
return apiKeys;
370-
}
371-
372314
/**
373315
* Ensures EVM signers are attached to multiProvider for the specified chains.
374316
* This is useful for commands that do interactive chain selection after the

typescript/cli/src/context/strategies/chain/chainResolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { ProtocolType, assert, isEVMLike } from '@hyperlane-xyz/utils';
1414
import { CommandType } from '../../../commands/signCommands.js';
1515
import { readCoreDeployConfigs } from '../../../config/core.js';
1616
import { getTransactions } from '../../../config/submit.js';
17-
import { getWarpRouteDeployConfig } from '../../../config/warp.js';
1817
import { readChainSubmissionStrategy } from '../../../deploy/warp.js';
1918
import { type ExtendedSubmissionStrategy } from '../../../submitters/types.js';
2019
import {
@@ -24,9 +23,10 @@ import {
2423
import { getOrderedWarpSendChains } from '../../../utils/warp-send.js';
2524
import {
2625
getWarpConfigs,
26+
getWarpRouteDeployConfig,
2727
getWarpCoreConfigOrExit,
2828
} from '../../../utils/warp.js';
29-
import { requestAndSaveApiKeys } from '../../context.js';
29+
import { requestAndSaveApiKeys } from '../../apiKeys.js';
3030

3131
/**
3232
* Resolves chains based on command type.

typescript/cli/src/deploy/warp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ import {
6969
rootLogger,
7070
} from '@hyperlane-xyz/utils';
7171

72-
import { requestAndSaveApiKeys } from '../context/context.js';
72+
import { requestAndSaveApiKeys } from '../context/apiKeys.js';
7373
import { type WriteCommandContext } from '../context/types.js';
7474
import {
7575
errorRed,

typescript/cli/src/ism/deploy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from '@hyperlane-xyz/sdk';
1818
import { type Address, assert, isEVMLike, mustGet } from '@hyperlane-xyz/utils';
1919

20-
import { requestAndSaveApiKeys } from '../context/context.js';
20+
import { requestAndSaveApiKeys } from '../context/apiKeys.js';
2121
import { type WriteCommandContext } from '../context/types.js';
2222
import {
2323
completeDeploy,

typescript/cli/src/utils/warp.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,14 @@ export async function resolveWarpRouteId(args: {
145145
);
146146
}
147147

148-
return (await search({
148+
return search<string>({
149149
message: `Multiple routes found for "${symbol}". Select one:`,
150150
source: (term) =>
151151
matchingIds.filter((id) =>
152152
id.toLowerCase().includes(term?.toLowerCase() || ''),
153153
),
154154
pageSize: 20,
155-
})) as string;
155+
});
156156
}
157157

158158
assert(
@@ -178,14 +178,14 @@ export async function resolveWarpRouteId(args: {
178178
return routeIds[0];
179179
}
180180

181-
return (await search({
181+
return search<string>({
182182
message: 'Select a warp route:',
183183
source: (term) =>
184184
routeIds.filter((id) =>
185185
id.toLowerCase().includes(term?.toLowerCase() || ''),
186186
),
187187
pageSize: 20,
188-
})) as string;
188+
});
189189
}
190190

191191
export async function getWarpConfigs({
@@ -219,6 +219,30 @@ export async function getWarpConfigs({
219219
return { warpDeployConfig, warpCoreConfig, resolvedWarpRouteId };
220220
}
221221

222+
export async function getWarpRouteDeployConfig({
223+
context,
224+
warpRouteId,
225+
}: {
226+
context: CommandContext;
227+
warpRouteId?: string;
228+
}): Promise<{
229+
config: WarpRouteDeployConfigMailboxRequired;
230+
resolvedWarpRouteId: string;
231+
}> {
232+
const resolvedWarpRouteId = await resolveWarpRouteId({
233+
warpRouteId,
234+
context,
235+
promptByDeploymentConfigs: true,
236+
});
237+
238+
const config = await readWarpRouteDeployConfig({
239+
context,
240+
warpRouteId: resolvedWarpRouteId,
241+
});
242+
243+
return { config, resolvedWarpRouteId };
244+
}
245+
222246
export function filterWarpConfigsToMatchingChains(
223247
warpDeployConfig: WarpRouteDeployConfigMailboxRequired,
224248
warpCoreConfig: WarpCoreConfig,

0 commit comments

Comments
 (0)