@hyperlane-xyz/cli
Version:
A command-line utility for common Hyperlane operations
106 lines • 4.81 kB
JavaScript
import { confirm, input, password, select } from '@inquirer/prompts';
import { Wallet } from 'ethers';
import { stringify as yamlStringify } from 'yaml';
import { ChainSubmissionStrategySchema, TxSubmitterType, } from '@hyperlane-xyz/sdk';
import { ProtocolType, assert, isAddress, isPrivateKeyEvm, } from '@hyperlane-xyz/utils';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
import { runSingleChainSelectionStep } from '../utils/chains.js';
import { indentYamlOrJson, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js';
import { maskSensitiveData } from '../utils/output.js';
/**
* Reads and validates a chain submission strategy configuration from a file
*/
export async function readChainSubmissionStrategyConfig(filePath) {
log(`Reading submission strategy in ${filePath}`);
const strategyConfig = readYamlOrJson(filePath);
const parseResult = ChainSubmissionStrategySchema.parse(strategyConfig);
return parseResult;
}
export async function createStrategyConfig({ context, outPath, }) {
let strategy;
try {
const strategyObj = await readYamlOrJson(outPath);
strategy = ChainSubmissionStrategySchema.parse(strategyObj);
}
catch {
strategy = writeYamlOrJson(outPath, {}, 'yaml');
}
const chain = await runSingleChainSelectionStep(context.chainMetadata);
const chainProtocol = context.chainMetadata[chain].protocol;
if (!context.skipConfirmation &&
strategy &&
Object.prototype.hasOwnProperty.call(strategy, chain)) {
const isConfirmed = await confirm({
message: `Default strategy for chain ${chain} already exists. Are you sure you want to overwrite existing strategy config?`,
default: false,
});
assert(isConfirmed, 'Strategy initialization cancelled by user.');
}
const isEthereum = chainProtocol === ProtocolType.Ethereum;
const submitterType = isEthereum
? await select({
message: 'Select the submitter type',
choices: Object.values(TxSubmitterType).map((value) => ({
name: value,
value: value,
})),
})
: TxSubmitterType.JSON_RPC; // Do other non-evm chains support gnosis and account impersonation?
const submitter = { type: submitterType };
switch (submitterType) {
case TxSubmitterType.JSON_RPC:
submitter.privateKey = await password({
message: 'Enter the private key for JSON-RPC submission:',
validate: (pk) => (isEthereum ? isPrivateKeyEvm(pk) : true),
});
submitter.userAddress = isEthereum
? await new Wallet(submitter.privateKey).getAddress()
: await input({
message: 'Enter the user address for JSON-RPC submission:',
});
submitter.chain = chain;
break;
case TxSubmitterType.IMPERSONATED_ACCOUNT:
submitter.userAddress = await input({
message: 'Enter the user address to impersonate',
validate: (address) => isAddress(address) ? true : 'Invalid Ethereum address',
});
assert(submitter.userAddress, 'User address is required for impersonated account');
break;
case TxSubmitterType.GNOSIS_SAFE:
case TxSubmitterType.GNOSIS_TX_BUILDER:
submitter.safeAddress = await input({
message: 'Enter the Safe address',
validate: (address) => isAddress(address) ? true : 'Invalid Safe address',
});
submitter.chain = chain;
if (submitterType === TxSubmitterType.GNOSIS_TX_BUILDER) {
submitter.version = await input({
message: 'Enter the Safe version (default: 1.0)',
default: '1.0',
});
}
break;
default:
throw new Error(`Unsupported submitter type: ${submitterType}`);
}
const strategyResult = {
...strategy,
[chain]: {
submitter: submitter,
},
};
try {
const strategyConfig = ChainSubmissionStrategySchema.parse(strategyResult);
logBlue(`Strategy configuration is valid. Writing to file ${outPath}:\n`);
const maskedConfig = maskSensitiveData(strategyConfig);
log(indentYamlOrJson(yamlStringify(maskedConfig, null, 2), 4));
writeYamlOrJson(outPath, strategyConfig);
logGreen('✅ Successfully created a new strategy configuration.');
}
catch {
// don't log error since it may contain sensitive data
errorRed(`The strategy configuration is invalid. Please review the submitter settings.`);
}
}
//# sourceMappingURL=strategy.js.map