@hyperlane-xyz/cli
Version:
A command-line utility for common Hyperlane operations
183 lines • 7.3 kB
JavaScript
import { confirm } from '@inquirer/prompts';
import { getRegistry } from '@hyperlane-xyz/registry/fs';
import { MultiProvider, } from '@hyperlane-xyz/sdk';
import { isNullish, rootLogger } from '@hyperlane-xyz/utils';
import { isSignCommand } from '../commands/signCommands.js';
import { readChainSubmissionStrategyConfig } from '../config/strategy.js';
import { forkNetworkToMultiProvider, verifyAnvil } from '../deploy/dry-run.js';
import { logBlue } from '../logger.js';
import { runSingleChainSelectionStep } from '../utils/chains.js';
import { detectAndConfirmOrPrompt } from '../utils/input.js';
import { getImpersonatedSigner, getSigner } from '../utils/keys.js';
import { ChainResolverFactory } from './strategies/chain/ChainResolverFactory.js';
import { MultiProtocolSignerManager } from './strategies/signer/MultiProtocolSignerManager.js';
export async function contextMiddleware(argv) {
const isDryRun = !isNullish(argv.dryRun);
const requiresKey = isSignCommand(argv);
const settings = {
registryUris: [
...argv.registry,
...(argv.overrides ? [argv.overrides] : []),
],
key: argv.key,
fromAddress: argv.fromAddress,
requiresKey,
disableProxy: argv.disableProxy,
skipConfirmation: argv.yes,
strategyPath: argv.strategy,
authToken: argv.authToken,
};
if (!isDryRun && settings.fromAddress)
throw new Error("'--from-address' or '-f' should only be used for dry-runs");
const context = isDryRun
? await getDryRunContext(settings, argv.dryRun)
: await getContext(settings);
argv.context = context;
}
export async function signerMiddleware(argv) {
const { key, requiresKey, multiProvider, strategyPath } = argv.context;
if (!requiresKey)
return argv;
const strategyConfig = strategyPath
? await readChainSubmissionStrategyConfig(strategyPath)
: {};
/**
* Intercepts Hyperlane command to determine chains.
*/
const chainStrategy = ChainResolverFactory.getStrategy(argv);
/**
* Resolves chains based on the chain strategy.
*/
const chains = await chainStrategy.resolveChains(argv);
/**
* Extracts signer config
*/
const multiProtocolSigner = new MultiProtocolSignerManager(strategyConfig, chains, multiProvider, { key });
/**
* @notice Attaches signers to MultiProvider and assigns it to argv.multiProvider
*/
argv.multiProvider = await multiProtocolSigner.getMultiProvider();
argv.multiProtocolSigner = multiProtocolSigner;
return argv;
}
/**
* Retrieves context for the user-selected command
* @returns context for the current command
*/
export async function getContext({ registryUris, key, requiresKey, skipConfirmation, disableProxy = false, strategyPath, authToken, }) {
const registry = getRegistry({
registryUris,
enableProxy: !disableProxy,
logger: rootLogger,
authToken,
});
//Just for backward compatibility
let signerAddress = undefined;
if (key) {
let signer;
({ key, signer } = await getSigner({ key, skipConfirmation }));
signerAddress = await signer.getAddress();
}
const multiProvider = await getMultiProvider(registry);
return {
registry,
requiresKey,
chainMetadata: multiProvider.metadata,
multiProvider,
key,
skipConfirmation: !!skipConfirmation,
signerAddress,
strategyPath,
};
}
/**
* Retrieves dry-run context for the user-selected command
* @returns dry-run context for the current command
*/
export async function getDryRunContext({ registryUris, key, fromAddress, skipConfirmation, disableProxy = false, authToken, }, chain) {
const registry = getRegistry({
registryUris,
enableProxy: !disableProxy,
logger: rootLogger,
authToken,
});
const chainMetadata = await registry.getMetadata();
if (!chain) {
if (skipConfirmation)
throw new Error('No chains provided');
chain = await runSingleChainSelectionStep(chainMetadata, 'Select chain to dry-run against:');
}
logBlue(`Dry-running against chain: ${chain}`);
await verifyAnvil();
let multiProvider = await getMultiProvider(registry);
multiProvider = await forkNetworkToMultiProvider(multiProvider, chain);
const { impersonatedKey, impersonatedSigner } = await getImpersonatedSigner({
fromAddress,
key,
skipConfirmation,
});
multiProvider.setSharedSigner(impersonatedSigner);
return {
registry,
chainMetadata: multiProvider.metadata,
key: impersonatedKey,
signer: impersonatedSigner,
multiProvider: multiProvider,
skipConfirmation: !!skipConfirmation,
isDryRun: true,
dryRunChain: chain,
};
}
/**
* Retrieves a new MultiProvider based on all known chain metadata & custom user chains
* @param customChains Custom chains specified by the user
* @returns a new MultiProvider
*/
async function getMultiProvider(registry, signer) {
const chainMetadata = await registry.getMetadata();
const multiProvider = new MultiProvider(chainMetadata);
if (signer)
multiProvider.setSharedSigner(signer);
return multiProvider;
}
/**
* Requests and saves Block Explorer API keys for the specified chains, prompting the user if necessary.
*
* @param chains - The list of chain names to request API keys for.
* @param chainMetadata - The chain metadata, used to determine if an API key is already configured.
* @param registry - The registry used to update the chain metadata with the new API key.
* @returns A mapping of chain names to their API keys.
*/
export async function requestAndSaveApiKeys(chains, chainMetadata, registry) {
const apiKeys = {};
for (const chain of chains) {
if (chainMetadata[chain]?.blockExplorers?.[0]?.apiKey) {
apiKeys[chain] = chainMetadata[chain].blockExplorers[0].apiKey;
continue;
}
const wantApiKey = await confirm({
default: false,
message: `Do you want to use an API key to verify on this (${chain}) chain's block explorer`,
});
if (wantApiKey) {
apiKeys[chain] = await detectAndConfirmOrPrompt(async () => {
const blockExplorers = chainMetadata[chain].blockExplorers;
if (!(blockExplorers && blockExplorers.length > 0))
return;
for (const blockExplorer of blockExplorers) {
/* The current apiKeys mapping only accepts one key, even if there are multiple explorer options present. */
if (blockExplorer.apiKey)
return blockExplorer.apiKey;
}
return undefined;
}, `Enter an API key for the ${chain} explorer`, `${chain} api key`, `${chain} metadata blockExplorers config`);
chainMetadata[chain].blockExplorers[0].apiKey = apiKeys[chain];
await registry.updateChain({
chainName: chain,
metadata: chainMetadata[chain],
});
}
}
return apiKeys;
}
//# sourceMappingURL=context.js.map