diff --git a/.changeset/chilly-balloons-rule.md b/.changeset/chilly-balloons-rule.md deleted file mode 100644 index b339b7569..000000000 --- a/.changeset/chilly-balloons-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/utils': minor ---- - -Added `isPrivateKeyEvm` function for validating EVM private keys diff --git a/.changeset/spicy-gifts-hear.md b/.changeset/spicy-gifts-hear.md deleted file mode 100644 index 37d4efa28..000000000 --- a/.changeset/spicy-gifts-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Added strategy management CLI commands and MultiProtocolSigner implementation for flexible cross-chain signer configuration and management diff --git a/typescript/cli/cli.ts b/typescript/cli/cli.ts index 45cad33bb..77be0b86f 100644 --- a/typescript/cli/cli.ts +++ b/typescript/cli/cli.ts @@ -19,17 +19,15 @@ import { overrideRegistryUriCommandOption, registryUriCommandOption, skipConfirmationOption, - strategyCommandOption, } from './src/commands/options.js'; import { registryCommand } from './src/commands/registry.js'; import { relayerCommand } from './src/commands/relayer.js'; import { sendCommand } from './src/commands/send.js'; import { statusCommand } from './src/commands/status.js'; -import { strategyCommand } from './src/commands/strategy.js'; import { submitCommand } from './src/commands/submit.js'; import { validatorCommand } from './src/commands/validator.js'; import { warpCommand } from './src/commands/warp.js'; -import { contextMiddleware, signerMiddleware } from './src/context/context.js'; +import { contextMiddleware } from './src/context/context.js'; import { configureLogger, errorRed } from './src/logger.js'; import { checkVersion } from './src/utils/version-check.js'; import { VERSION } from './src/version.js'; @@ -51,14 +49,12 @@ try { .option('key', keyCommandOption) .option('disableProxy', disableProxyCommandOption) .option('yes', skipConfirmationOption) - .option('strategy', strategyCommandOption) .global(['log', 'verbosity', 'registry', 'overrides', 'yes']) .middleware([ (argv) => { configureLogger(argv.log as LogFormat, argv.verbosity as LogLevel); }, contextMiddleware, - signerMiddleware, ]) .command(avsCommand) .command(configCommand) @@ -70,7 +66,6 @@ try { .command(relayerCommand) .command(sendCommand) .command(statusCommand) - .command(strategyCommand) .command(submitCommand) .command(validatorCommand) .command(warpCommand) diff --git a/typescript/cli/src/commands/config.ts b/typescript/cli/src/commands/config.ts index 4a5c6b580..e72b72452 100644 --- a/typescript/cli/src/commands/config.ts +++ b/typescript/cli/src/commands/config.ts @@ -3,7 +3,6 @@ import { CommandModule } from 'yargs'; import { readChainConfigs } from '../config/chain.js'; import { readIsmConfig } from '../config/ism.js'; import { readMultisigConfig } from '../config/multisig.js'; -import { readChainSubmissionStrategyConfig } from '../config/strategy.js'; import { readWarpRouteDeployConfig } from '../config/warp.js'; import { CommandModuleWithContext } from '../context/types.js'; import { log, logGreen } from '../logger.js'; @@ -32,7 +31,6 @@ const validateCommand: CommandModule = { .command(validateChainCommand) .command(validateIsmCommand) .command(validateIsmAdvancedCommand) - .command(validateStrategyCommand) .command(validateWarpCommand) .version(false) .demandCommand(), @@ -78,19 +76,6 @@ const validateIsmAdvancedCommand: CommandModuleWithContext<{ path: string }> = { }, }; -const validateStrategyCommand: CommandModuleWithContext<{ path: string }> = { - command: 'strategy', - describe: 'Validates a Strategy config file', - builder: { - path: inputFileCommandOption(), - }, - handler: async ({ path }) => { - await readChainSubmissionStrategyConfig(path); - logGreen('Config is valid'); - process.exit(0); - }, -}; - const validateWarpCommand: CommandModuleWithContext<{ path: string }> = { command: 'warp', describe: 'Validate a Warp Route deployment config file', diff --git a/typescript/cli/src/commands/options.ts b/typescript/cli/src/commands/options.ts index baf0fa847..f23194c80 100644 --- a/typescript/cli/src/commands/options.ts +++ b/typescript/cli/src/commands/options.ts @@ -95,7 +95,6 @@ export const DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH = './configs/warp-route-deployment.yaml'; export const DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH = './configs/core-config.yaml'; -export const DEFAULT_STRATEGY_CONFIG_PATH = `${os.homedir()}/.hyperlane/strategies/default-strategy.yaml`; export const warpDeploymentConfigCommandOption: Options = { type: 'string', @@ -197,8 +196,8 @@ export const transactionsCommandOption: Options = { export const strategyCommandOption: Options = { type: 'string', description: 'The submission strategy input file path.', - alias: ['s', 'strategy'], - demandOption: false, + alias: 's', + demandOption: true, }; export const addressCommandOption = ( diff --git a/typescript/cli/src/commands/signCommands.ts b/typescript/cli/src/commands/signCommands.ts index 6bd617302..37d83096a 100644 --- a/typescript/cli/src/commands/signCommands.ts +++ b/typescript/cli/src/commands/signCommands.ts @@ -1,14 +1,7 @@ // Commands that send tx and require a key to sign. // It's useful to have this listed here so the context // middleware can request keys up front when required. -export const SIGN_COMMANDS = [ - 'apply', - 'deploy', - 'send', - 'status', - 'submit', - 'relayer', -]; +export const SIGN_COMMANDS = ['deploy', 'send', 'status', 'submit', 'relayer']; export function isSignCommand(argv: any): boolean { return ( @@ -16,15 +9,3 @@ export function isSignCommand(argv: any): boolean { (argv._.length > 1 && SIGN_COMMANDS.includes(argv._[1])) ); } - -export enum CommandType { - WARP_DEPLOY = 'warp:deploy', - WARP_SEND = 'warp:send', - WARP_APPLY = 'warp:apply', - WARP_READ = 'warp:read', - SEND_MESSAGE = 'send:message', - AGENT_KURTOSIS = 'deploy:kurtosis-agents', - STATUS = 'status:', - SUBMIT = 'submit:', - RELAYER = 'relayer:', -} diff --git a/typescript/cli/src/commands/strategy.ts b/typescript/cli/src/commands/strategy.ts deleted file mode 100644 index 414a3d48e..000000000 --- a/typescript/cli/src/commands/strategy.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { stringify as yamlStringify } from 'yaml'; -import { CommandModule } from 'yargs'; - -import { - createStrategyConfig, - readChainSubmissionStrategyConfig, -} from '../config/strategy.js'; -import { CommandModuleWithWriteContext } from '../context/types.js'; -import { log, logCommandHeader } from '../logger.js'; -import { indentYamlOrJson } from '../utils/files.js'; -import { maskSensitiveData } from '../utils/output.js'; - -import { - DEFAULT_STRATEGY_CONFIG_PATH, - outputFileCommandOption, - strategyCommandOption, -} from './options.js'; - -/** - * Parent command - */ -export const strategyCommand: CommandModule = { - command: 'strategy', - describe: 'Manage Hyperlane deployment strategies', - builder: (yargs) => - yargs.command(init).command(read).version(false).demandCommand(), - handler: () => log('Command required'), -}; - -export const init: CommandModuleWithWriteContext<{ - out: string; -}> = { - command: 'init', - describe: 'Creates strategy configuration', - builder: { - out: outputFileCommandOption(DEFAULT_STRATEGY_CONFIG_PATH), - }, - handler: async ({ context, out }) => { - logCommandHeader(`Hyperlane Strategy Init`); - - await createStrategyConfig({ - context, - outPath: out, - }); - process.exit(0); - }, -}; - -export const read: CommandModuleWithWriteContext<{ - strategy: string; -}> = { - command: 'read', - describe: 'Reads strategy configuration', - builder: { - strategy: { - ...strategyCommandOption, - demandOption: true, - default: DEFAULT_STRATEGY_CONFIG_PATH, - }, - }, - handler: async ({ strategy: strategyUrl }) => { - logCommandHeader(`Hyperlane Strategy Read`); - - const strategy = await readChainSubmissionStrategyConfig(strategyUrl); - const maskedConfig = maskSensitiveData(strategy); - log(indentYamlOrJson(yamlStringify(maskedConfig, null, 2), 4)); - - process.exit(0); - }, -}; diff --git a/typescript/cli/src/config/strategy.ts b/typescript/cli/src/config/strategy.ts deleted file mode 100644 index f57c7d337..000000000 --- a/typescript/cli/src/config/strategy.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { confirm, input, password, select } from '@inquirer/prompts'; -import { Wallet } from 'ethers'; -import { stringify as yamlStringify } from 'yaml'; - -import { - ChainSubmissionStrategy, - ChainSubmissionStrategySchema, - TxSubmitterType, -} from '@hyperlane-xyz/sdk'; -import { - ProtocolType, - assert, - errorToString, - isAddress, - isPrivateKeyEvm, -} from '@hyperlane-xyz/utils'; - -import { CommandContext } from '../context/types.js'; -import { errorRed, log, logBlue, logGreen, logRed } from '../logger.js'; -import { runSingleChainSelectionStep } from '../utils/chains.js'; -import { - indentYamlOrJson, - isFile, - 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: string, -): Promise { - log(`Reading submission strategy in ${filePath}`); - try { - const strategyConfig = readYamlOrJson(filePath); - - const parseResult = ChainSubmissionStrategySchema.parse(strategyConfig); - - return parseResult; - } catch (error) { - logRed(`⛔️ Error reading strategy config:`, errorToString(error)); - throw error; // Re-throw to let caller handle the error - } -} - -/** - * Safely reads chain submission strategy config, returns empty object if any errors occur - */ -export async function safeReadChainSubmissionStrategyConfig( - filePath: string, -): Promise { - try { - const trimmedFilePath = filePath.trim(); - if (!isFile(trimmedFilePath)) { - logBlue(`File ${trimmedFilePath} does not exist, returning empty config`); - return {}; - } - return await readChainSubmissionStrategyConfig(trimmedFilePath); - } catch (error) { - logRed( - `Failed to read strategy config, defaulting to empty config:`, - errorToString(error), - ); - return {}; - } -} - -export async function createStrategyConfig({ - context, - outPath, -}: { - context: CommandContext; - outPath: string; -}) { - let strategy: ChainSubmissionStrategy; - 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: Record = { 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: ChainSubmissionStrategy = { - ...strategy, - [chain]: { - submitter: submitter as ChainSubmissionStrategy[string]['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.`, - ); - } -} diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index a2cd19d57..1174d0156 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -21,8 +21,6 @@ import { promiseObjAll, } from '@hyperlane-xyz/utils'; -import { DEFAULT_STRATEGY_CONFIG_PATH } from '../commands/options.js'; -import { MultiProtocolSignerManager } from '../context/strategies/signer/MultiProtocolSignerManager.js'; import { CommandContext } from '../context/types.js'; import { errorRed, log, logBlue, logGreen } from '../logger.js'; import { runMultiChainSelectionStep } from '../utils/chains.js'; @@ -37,7 +35,6 @@ import { } from '../utils/input.js'; import { createAdvancedIsmConfig } from './ism.js'; -import { readChainSubmissionStrategyConfig } from './strategy.js'; const TYPE_DESCRIPTIONS: Record = { [TokenType.synthetic]: 'A new ERC20 with remote transfer functionality', @@ -125,6 +122,13 @@ export async function createWarpRouteDeployConfig({ }) { logBlue('Creating a new warp route deployment config...'); + const owner = await detectAndConfirmOrPrompt( + async () => context.signer?.getAddress(), + 'Enter the desired', + 'owner address', + 'signer', + ); + const warpChains = await runMultiChainSelectionStep({ chainMetadata: context.chainMetadata, message: 'Select chains to connect', @@ -134,31 +138,11 @@ export async function createWarpRouteDeployConfig({ requiresConfirmation: !context.skipConfirmation, }); - const strategyConfig = await readChainSubmissionStrategyConfig( - context.strategyPath ?? DEFAULT_STRATEGY_CONFIG_PATH, - ); - - const multiProtocolSigner = new MultiProtocolSignerManager( - strategyConfig, - warpChains, - context.multiProvider, - { key: context.key }, - ); - - const multiProviderWithSigners = await multiProtocolSigner.getMultiProvider(); - const result: WarpRouteDeployConfig = {}; let typeChoices = TYPE_CHOICES; for (const chain of warpChains) { logBlue(`${chain}: Configuring warp route...`); - const owner = await detectAndConfirmOrPrompt( - async () => multiProviderWithSigners.getSigner(chain).getAddress(), - 'Enter the desired', - 'owner address', - '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); diff --git a/typescript/cli/src/context/context.ts b/typescript/cli/src/context/context.ts index 0d1084fcc..f9dfb34ab 100644 --- a/typescript/cli/src/context/context.ts +++ b/typescript/cli/src/context/context.ts @@ -16,18 +16,14 @@ import { } from '@hyperlane-xyz/sdk'; import { isHttpsUrl, isNullish, rootLogger } from '@hyperlane-xyz/utils'; -import { DEFAULT_STRATEGY_CONFIG_PATH } from '../commands/options.js'; import { isSignCommand } from '../commands/signCommands.js'; -import { safeReadChainSubmissionStrategyConfig } from '../config/strategy.js'; import { PROXY_DEPLOYED_URL } from '../consts.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 } from '../utils/keys.js'; +import { getImpersonatedSigner, getSigner } from '../utils/keys.js'; -import { ChainResolverFactory } from './strategies/chain/ChainResolverFactory.js'; -import { MultiProtocolSignerManager } from './strategies/signer/MultiProtocolSignerManager.js'; import { CommandContext, ContextSettings, @@ -45,7 +41,6 @@ export async function contextMiddleware(argv: Record) { requiresKey, disableProxy: argv.disableProxy, skipConfirmation: argv.yes, - strategyPath: argv.strategy, }; if (!isDryRun && settings.fromAddress) throw new Error( @@ -57,44 +52,6 @@ export async function contextMiddleware(argv: Record) { argv.context = context; } -export async function signerMiddleware(argv: Record) { - const { key, requiresKey, multiProvider, strategyPath } = argv.context; - - if (!requiresKey && !key) return argv; - - const strategyConfig = await safeReadChainSubmissionStrategyConfig( - strategyPath ?? DEFAULT_STRATEGY_CONFIG_PATH, - ); - - /** - * 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 @@ -109,14 +66,18 @@ export async function getContext({ }: ContextSettings): Promise { const registry = getRegistry(registryUri, registryOverrideUri, !disableProxy); - const multiProvider = await getMultiProvider(registry); + let signer: ethers.Wallet | undefined = undefined; + if (key || requiresKey) { + ({ key, signer } = await getSigner({ key, skipConfirmation })); + } + const multiProvider = await getMultiProvider(registry, signer); return { registry, - requiresKey, chainMetadata: multiProvider.metadata, multiProvider, key, + signer, skipConfirmation: !!skipConfirmation, } as CommandContext; } diff --git a/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts b/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts deleted file mode 100644 index eb9fa135a..000000000 --- a/typescript/cli/src/context/strategies/chain/ChainResolverFactory.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CommandType } from '../../../commands/signCommands.js'; - -import { MultiChainResolver } from './MultiChainResolver.js'; -import { SingleChainResolver } from './SingleChainResolver.js'; -import { ChainResolver } from './types.js'; - -/** - * @class ChainResolverFactory - * @description Intercepts commands to determine the appropriate chain resolver strategy based on command type. - */ -export class ChainResolverFactory { - private static strategyMap: Map ChainResolver> = new Map([ - [CommandType.WARP_DEPLOY, () => MultiChainResolver.forWarpRouteConfig()], - [CommandType.WARP_SEND, () => MultiChainResolver.forOriginDestination()], - [CommandType.WARP_APPLY, () => MultiChainResolver.forWarpRouteConfig()], - [CommandType.WARP_READ, () => MultiChainResolver.forWarpCoreConfig()], - [CommandType.SEND_MESSAGE, () => MultiChainResolver.forOriginDestination()], - [CommandType.AGENT_KURTOSIS, () => MultiChainResolver.forAgentKurtosis()], - [CommandType.STATUS, () => MultiChainResolver.forOriginDestination()], - [CommandType.SUBMIT, () => MultiChainResolver.forStrategyConfig()], - [CommandType.RELAYER, () => MultiChainResolver.forRelayer()], - ]); - - /** - * @param argv - Command line arguments. - * @returns ChainResolver - The appropriate chain resolver strategy based on the command type. - */ - static getStrategy(argv: Record): ChainResolver { - const commandKey = `${argv._[0]}:${argv._[1] || ''}`.trim() as CommandType; - const createStrategy = - this.strategyMap.get(commandKey) || (() => new SingleChainResolver()); - return createStrategy(); - } -} diff --git a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts b/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts deleted file mode 100644 index c6d96b5dc..000000000 --- a/typescript/cli/src/context/strategies/chain/MultiChainResolver.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; -import { assert } from '@hyperlane-xyz/utils'; - -import { DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH } from '../../../commands/options.js'; -import { readChainSubmissionStrategyConfig } from '../../../config/strategy.js'; -import { logRed } from '../../../logger.js'; -import { - extractChainsFromObj, - runMultiChainSelectionStep, - runSingleChainSelectionStep, -} from '../../../utils/chains.js'; -import { - isFile, - readYamlOrJson, - runFileSelectionStep, -} from '../../../utils/files.js'; -import { getWarpCoreConfigOrExit } from '../../../utils/warp.js'; - -import { ChainResolver } from './types.js'; - -enum ChainSelectionMode { - ORIGIN_DESTINATION, - AGENT_KURTOSIS, - WARP_CONFIG, - WARP_READ, - STRATEGY, - RELAYER, -} - -// This class could be broken down into multiple strategies - -/** - * @title MultiChainResolver - * @notice Resolves chains based on the specified selection mode. - */ -export class MultiChainResolver implements ChainResolver { - constructor(private mode: ChainSelectionMode) {} - - async resolveChains(argv: ChainMap): Promise { - switch (this.mode) { - case ChainSelectionMode.WARP_CONFIG: - return this.resolveWarpRouteConfigChains(argv); - case ChainSelectionMode.WARP_READ: - return this.resolveWarpCoreConfigChains(argv); - case ChainSelectionMode.AGENT_KURTOSIS: - return this.resolveAgentChains(argv); - case ChainSelectionMode.STRATEGY: - return this.resolveStrategyChains(argv); - case ChainSelectionMode.RELAYER: - return this.resolveRelayerChains(argv); - case ChainSelectionMode.ORIGIN_DESTINATION: - default: - return this.resolveOriginDestinationChains(argv); - } - } - - private async resolveWarpRouteConfigChains( - argv: Record, - ): Promise { - argv.config ||= DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH; - argv.context.chains = await this.getWarpRouteConfigChains( - argv.config.trim(), - argv.skipConfirmation, - ); - return argv.context.chains; - } - - private async resolveWarpCoreConfigChains( - argv: Record, - ): Promise { - if (argv.symbol || argv.warp) { - const warpCoreConfig = await getWarpCoreConfigOrExit({ - context: argv.context, - warp: argv.warp, - symbol: argv.symbol, - }); - argv.context.warpCoreConfig = warpCoreConfig; - const chains = extractChainsFromObj(warpCoreConfig); - return chains; - } else if (argv.chain) { - return [argv.chain]; - } else { - throw new Error( - `Please specify either a symbol, chain and address or warp file`, - ); - } - } - - private async resolveAgentChains( - argv: Record, - ): Promise { - const { chainMetadata } = argv.context; - argv.origin = - argv.origin ?? - (await runSingleChainSelectionStep( - chainMetadata, - 'Select the origin chain', - )); - - if (!argv.targets) { - const selectedRelayChains = await runMultiChainSelectionStep({ - chainMetadata: chainMetadata, - message: 'Select chains to relay between', - requireNumber: 2, - }); - argv.targets = selectedRelayChains.join(','); - } - - return [argv.origin, ...argv.targets]; - } - - private async resolveOriginDestinationChains( - argv: Record, - ): Promise { - const { chainMetadata } = argv.context; - - argv.origin = - argv.origin ?? - (await runSingleChainSelectionStep( - chainMetadata, - 'Select the origin chain', - )); - - argv.destination = - argv.destination ?? - (await runSingleChainSelectionStep( - chainMetadata, - 'Select the destination chain', - )); - - return [argv.origin, argv.destination]; - } - - private async resolveStrategyChains( - argv: Record, - ): Promise { - const strategy = await readChainSubmissionStrategyConfig(argv.strategy); - return extractChainsFromObj(strategy); - } - - private async resolveRelayerChains( - argv: Record, - ): Promise { - return argv.chains.split(',').map((item: string) => item.trim()); - } - - private async getWarpRouteConfigChains( - configPath: string, - skipConfirmation: boolean, - ): Promise { - if (!configPath || !isFile(configPath)) { - assert(!skipConfirmation, 'Warp route deployment config is required'); - configPath = await runFileSelectionStep( - './configs', - 'Warp route deployment config', - 'warp', - ); - } else { - logRed(`Using warp route deployment config at ${configPath}`); - } - - // Alternative to readWarpRouteDeployConfig that doesn't use context for signer and zod validation - const warpRouteConfig = (await readYamlOrJson(configPath)) as Record< - string, - any - >; - - const chains = Object.keys(warpRouteConfig) as ChainName[]; - assert( - chains.length !== 0, - 'No chains found in warp route deployment config', - ); - - return chains; - } - - static forAgentKurtosis(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.AGENT_KURTOSIS); - } - - static forOriginDestination(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.ORIGIN_DESTINATION); - } - - static forRelayer(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.RELAYER); - } - - static forStrategyConfig(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.STRATEGY); - } - - static forWarpRouteConfig(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.WARP_CONFIG); - } - - static forWarpCoreConfig(): MultiChainResolver { - return new MultiChainResolver(ChainSelectionMode.WARP_READ); - } -} diff --git a/typescript/cli/src/context/strategies/chain/SingleChainResolver.ts b/typescript/cli/src/context/strategies/chain/SingleChainResolver.ts deleted file mode 100644 index 8dddaf3c4..000000000 --- a/typescript/cli/src/context/strategies/chain/SingleChainResolver.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; - -import { runSingleChainSelectionStep } from '../../../utils/chains.js'; - -import { ChainResolver } from './types.js'; - -/** - * @title SingleChainResolver - * @notice Strategy implementation for managing single-chain operations - * @dev Primarily used for operations like 'core:apply' and 'warp:read' - */ -export class SingleChainResolver implements ChainResolver { - /** - * @notice Determines the chain to be used for signing operations - * @dev Either uses the chain specified in argv or prompts for interactive selection - */ - async resolveChains(argv: ChainMap): Promise { - argv.chain ||= await runSingleChainSelectionStep( - argv.context.chainMetadata, - 'Select chain to connect:', - ); - - return [argv.chain]; // Explicitly return as single-item array - } -} diff --git a/typescript/cli/src/context/strategies/chain/types.ts b/typescript/cli/src/context/strategies/chain/types.ts deleted file mode 100644 index 9318bed8c..000000000 --- a/typescript/cli/src/context/strategies/chain/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; - -export interface ChainResolver { - /** - * Determines the chains to be used for signing - * @param argv Command arguments - * @returns Array of chain names - */ - resolveChains(argv: ChainMap): Promise; -} diff --git a/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts b/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts deleted file mode 100644 index b91242b42..000000000 --- a/typescript/cli/src/context/strategies/signer/BaseMultiProtocolSigner.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Signer } from 'ethers'; - -import { ChainName, ChainSubmissionStrategy } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; - -export interface SignerConfig { - privateKey: string; - address?: Address; // For chains like StarkNet that require address - extraParams?: Record; // For any additional chain-specific params -} - -export interface IMultiProtocolSigner { - getSignerConfig(chain: ChainName): Promise | SignerConfig; - getSigner(config: SignerConfig): Signer; -} - -export abstract class BaseMultiProtocolSigner implements IMultiProtocolSigner { - constructor(protected config: ChainSubmissionStrategy) {} - - abstract getSignerConfig(chain: ChainName): Promise; - abstract getSigner(config: SignerConfig): Signer; -} diff --git a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts b/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts deleted file mode 100644 index 030f11b5f..000000000 --- a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerFactory.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { password } from '@inquirer/prompts'; -import { Signer, Wallet } from 'ethers'; - -import { - ChainName, - ChainSubmissionStrategy, - ChainTechnicalStack, - MultiProvider, - TxSubmitterType, -} from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; - -import { - BaseMultiProtocolSigner, - IMultiProtocolSigner, - SignerConfig, -} from './BaseMultiProtocolSigner.js'; - -export class MultiProtocolSignerFactory { - static getSignerStrategy( - chain: ChainName, - strategyConfig: ChainSubmissionStrategy, - multiProvider: MultiProvider, - ): IMultiProtocolSigner { - const { protocol, technicalStack } = multiProvider.getChainMetadata(chain); - - switch (protocol) { - case ProtocolType.Ethereum: - if (technicalStack === ChainTechnicalStack.ZkSync) - return new ZKSyncSignerStrategy(strategyConfig); - return new EthereumSignerStrategy(strategyConfig); - default: - throw new Error(`Unsupported protocol: ${protocol}`); - } - } -} - -class EthereumSignerStrategy extends BaseMultiProtocolSigner { - async getSignerConfig(chain: ChainName): Promise { - const submitter = this.config[chain]?.submitter as { - type: TxSubmitterType.JSON_RPC; - privateKey?: string; - }; - - const privateKey = - submitter?.privateKey ?? - (await password({ - message: `Please enter the private key for chain ${chain}`, - })); - - return { privateKey }; - } - - getSigner(config: SignerConfig): Signer { - return new Wallet(config.privateKey); - } -} - -// 99% overlap with EthereumSignerStrategy for the sake of keeping MultiProtocolSignerFactory clean -// TODO: import ZKSync signer -class ZKSyncSignerStrategy extends BaseMultiProtocolSigner { - async getSignerConfig(chain: ChainName): Promise { - const submitter = this.config[chain]?.submitter as { - privateKey?: string; - }; - - const privateKey = - submitter?.privateKey ?? - (await password({ - message: `Please enter the private key for chain ${chain}`, - })); - - return { privateKey }; - } - - getSigner(config: SignerConfig): Signer { - return new Wallet(config.privateKey); - } -} diff --git a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts b/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts deleted file mode 100644 index 12f9c0f81..000000000 --- a/typescript/cli/src/context/strategies/signer/MultiProtocolSignerManager.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { Signer } from 'ethers'; -import { Logger } from 'pino'; - -import { - ChainName, - ChainSubmissionStrategy, - MultiProvider, -} from '@hyperlane-xyz/sdk'; -import { assert, rootLogger } from '@hyperlane-xyz/utils'; - -import { ENV } from '../../../utils/env.js'; - -import { IMultiProtocolSigner } from './BaseMultiProtocolSigner.js'; -import { MultiProtocolSignerFactory } from './MultiProtocolSignerFactory.js'; - -export interface MultiProtocolSignerOptions { - logger?: Logger; - key?: string; -} - -/** - * @title MultiProtocolSignerManager - * @dev Context manager for signers across multiple protocols - */ -export class MultiProtocolSignerManager { - protected readonly signerStrategies: Map; - protected readonly signers: Map; - public readonly logger: Logger; - - constructor( - protected readonly submissionStrategy: ChainSubmissionStrategy, - protected readonly chains: ChainName[], - protected readonly multiProvider: MultiProvider, - protected readonly options: MultiProtocolSignerOptions = {}, - ) { - this.logger = - options?.logger || - rootLogger.child({ - module: 'MultiProtocolSignerManager', - }); - this.signerStrategies = new Map(); - this.signers = new Map(); - this.initializeStrategies(); - } - - /** - * @notice Sets up chain-specific signer strategies - */ - protected initializeStrategies(): void { - for (const chain of this.chains) { - const strategy = MultiProtocolSignerFactory.getSignerStrategy( - chain, - this.submissionStrategy, - this.multiProvider, - ); - this.signerStrategies.set(chain, strategy); - } - } - - /** - * @dev Configures signers for EVM chains in MultiProvider - */ - async getMultiProvider(): Promise { - for (const chain of this.chains) { - const signer = await this.initSigner(chain); - this.multiProvider.setSigner(chain, signer); - } - - return this.multiProvider; - } - - /** - * @notice Creates signer for specific chain - */ - async initSigner(chain: ChainName): Promise { - const { privateKey } = await this.resolveConfig(chain); - - const signerStrategy = this.signerStrategies.get(chain); - assert(signerStrategy, `No signer strategy found for chain ${chain}`); - - return signerStrategy.getSigner({ privateKey }); - } - - /** - * @notice Creates signers for all chains - */ - async initAllSigners(): Promise { - const signerConfigs = await this.resolveAllConfigs(); - - for (const { chain, privateKey } of signerConfigs) { - const signerStrategy = this.signerStrategies.get(chain); - if (signerStrategy) { - this.signers.set(chain, signerStrategy.getSigner({ privateKey })); - } - } - - return this.signers; - } - - /** - * @notice Resolves all chain configurations - */ - private async resolveAllConfigs(): Promise< - Array<{ chain: ChainName; privateKey: string }> - > { - return Promise.all(this.chains.map((chain) => this.resolveConfig(chain))); - } - - /** - * @notice Resolves single chain configuration - */ - private async resolveConfig( - chain: ChainName, - ): Promise<{ chain: ChainName; privateKey: string }> { - const signerStrategy = this.signerStrategies.get(chain); - assert(signerStrategy, `No signer strategy found for chain ${chain}`); - - let privateKey: string; - - if (this.options.key) { - this.logger.info( - `Using private key passed via CLI --key flag for chain ${chain}`, - ); - privateKey = this.options.key; - } else if (ENV.HYP_KEY) { - this.logger.info(`Using private key from .env for chain ${chain}`); - privateKey = ENV.HYP_KEY; - } else { - privateKey = await this.extractPrivateKey(chain, signerStrategy); - } - - return { chain, privateKey }; - } - - /** - * @notice Gets private key from strategy - */ - private async extractPrivateKey( - chain: ChainName, - signerStrategy: IMultiProtocolSigner, - ): Promise { - const strategyConfig = await signerStrategy.getSignerConfig(chain); - assert( - strategyConfig.privateKey, - `No private key found for chain ${chain}`, - ); - - this.logger.info( - `Extracting private key from strategy config/user prompt for chain ${chain}`, - ); - return strategyConfig.privateKey; - } -} diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index 783797289..6c3a17c5f 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -6,7 +6,6 @@ import type { ChainMap, ChainMetadata, MultiProvider, - WarpCoreConfig, } from '@hyperlane-xyz/sdk'; export interface ContextSettings { @@ -17,7 +16,6 @@ export interface ContextSettings { requiresKey?: boolean; disableProxy?: boolean; skipConfirmation?: boolean; - strategyPath?: string; } export interface CommandContext { @@ -27,8 +25,6 @@ export interface CommandContext { skipConfirmation: boolean; key?: string; signer?: ethers.Signer; - warpCoreConfig?: WarpCoreConfig; - strategyPath?: string; } export interface WriteCommandContext extends CommandContext { diff --git a/typescript/cli/src/deploy/agent.ts b/typescript/cli/src/deploy/agent.ts index a36955a3f..ca490fc5f 100644 --- a/typescript/cli/src/deploy/agent.ts +++ b/typescript/cli/src/deploy/agent.ts @@ -21,7 +21,6 @@ export async function runKurtosisAgentDeploy({ relayChains?: string; agentConfigurationPath?: string; }) { - // Future works: decide what to do with this, since its handled in MultiChainResolver - AGENT_KURTOSIS mode if (!originChain) { originChain = await runSingleChainSelectionStep( context.chainMetadata, diff --git a/typescript/cli/src/deploy/core.ts b/typescript/cli/src/deploy/core.ts index afd586646..9bbd7bcbd 100644 --- a/typescript/cli/src/deploy/core.ts +++ b/typescript/cli/src/deploy/core.ts @@ -43,6 +43,7 @@ export async function runCoreDeploy(params: DeployParams) { let chain = params.chain; const { + signer, isDryRun, chainMetadata, dryRunChain, @@ -61,14 +62,13 @@ export async function runCoreDeploy(params: DeployParams) { 'Select chain to connect:', ); } + let apiKeys: ChainMap = {}; if (!skipConfirmation) apiKeys = await requestAndSaveApiKeys([chain], chainMetadata, registry); - const signer = multiProvider.getSigner(chain); - const deploymentParams: DeployParams = { - context: { ...context, signer }, + context, chain, config, }; diff --git a/typescript/cli/src/deploy/utils.ts b/typescript/cli/src/deploy/utils.ts index f5ac01a17..125e7b1e7 100644 --- a/typescript/cli/src/deploy/utils.ts +++ b/typescript/cli/src/deploy/utils.ts @@ -41,7 +41,7 @@ export async function runPreflightChecksForChains({ chainsToGasCheck?: ChainName[]; }) { log('Running pre-flight checks for chains...'); - const { multiProvider } = context; + const { signer, multiProvider } = context; if (!chains?.length) throw new Error('Empty chain selection'); for (const chain of chains) { @@ -49,14 +49,15 @@ export async function runPreflightChecksForChains({ if (!metadata) throw new Error(`No chain config found for ${chain}`); if (metadata.protocol !== ProtocolType.Ethereum) throw new Error('Only Ethereum chains are supported for now'); - const signer = multiProvider.getSigner(chain); - assertSigner(signer); - logGreen(`✅ ${chain} signer is valid`); } logGreen('✅ Chains are valid'); + assertSigner(signer); + logGreen('✅ Signer is valid'); + await nativeBalancesAreSufficient( multiProvider, + signer, chainsToGasCheck ?? chains, minGas, ); @@ -69,13 +70,8 @@ export async function runDeployPlanStep({ context: WriteCommandContext; chain: ChainName; }) { - const { - chainMetadata: chainMetadataMap, - multiProvider, - skipConfirmation, - } = context; - - const address = await multiProvider.getSigner(chain).getAddress(); + const { signer, chainMetadata: chainMetadataMap, skipConfirmation } = context; + const address = await signer.getAddress(); logBlue('\nDeployment plan'); logGray('==============='); @@ -128,7 +124,7 @@ export function isZODISMConfig(filepath: string): boolean { export async function prepareDeploy( context: WriteCommandContext, - userAddress: Address | null, + userAddress: Address, chains: ChainName[], ): Promise> { const { multiProvider, isDryRun } = context; @@ -138,9 +134,7 @@ export async function prepareDeploy( const provider = isDryRun ? getLocalProvider(ENV.ANVIL_IP_ADDR, ENV.ANVIL_PORT) : multiProvider.getProvider(chain); - const address = - userAddress ?? (await multiProvider.getSigner(chain).getAddress()); - const currentBalance = await provider.getBalance(address); + const currentBalance = await provider.getBalance(userAddress); initialBalances[chain] = currentBalance; }), ); @@ -151,7 +145,7 @@ export async function completeDeploy( context: WriteCommandContext, command: string, initialBalances: Record, - userAddress: Address | null, + userAddress: Address, chains: ChainName[], ) { const { multiProvider, isDryRun } = context; @@ -160,9 +154,7 @@ export async function completeDeploy( const provider = isDryRun ? getLocalProvider(ENV.ANVIL_IP_ADDR, ENV.ANVIL_PORT) : multiProvider.getProvider(chain); - const address = - userAddress ?? (await multiProvider.getSigner(chain).getAddress()); - const currentBalance = await provider.getBalance(address); + const currentBalance = await provider.getBalance(userAddress); const balanceDelta = initialBalances[chain].sub(currentBalance); if (isDryRun && balanceDelta.lt(0)) break; logPink( diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index a7827b5dc..639d5d5c8 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -100,7 +100,7 @@ export async function runWarpRouteDeploy({ context: WriteCommandContext; warpRouteDeploymentConfigPath?: string; }) { - const { skipConfirmation, chainMetadata, registry } = context; + const { signer, skipConfirmation, chainMetadata, registry } = context; if ( !warpRouteDeploymentConfigPath || @@ -147,8 +147,13 @@ export async function runWarpRouteDeploy({ minGas: MINIMUM_WARP_DEPLOY_GAS, }); - const initialBalances = await prepareDeploy(context, null, ethereumChains); + const userAddress = await signer.getAddress(); + const initialBalances = await prepareDeploy( + context, + userAddress, + ethereumChains, + ); const deployedContracts = await executeDeploy(deploymentParams, apiKeys); const warpCoreConfig = await getWarpCoreConfig( @@ -158,7 +163,13 @@ export async function runWarpRouteDeploy({ await writeDeploymentArtifacts(warpCoreConfig, context); - await completeDeploy(context, 'warp', initialBalances, null, ethereumChains!); + await completeDeploy( + context, + 'warp', + initialBalances, + userAddress, + ethereumChains, + ); } async function runDeployPlanStep({ context, warpDeployConfig }: DeployParams) { diff --git a/typescript/cli/src/read/warp.ts b/typescript/cli/src/read/warp.ts index 169593c5e..bd5d01e95 100644 --- a/typescript/cli/src/read/warp.ts +++ b/typescript/cli/src/read/warp.ts @@ -34,13 +34,11 @@ export async function runWarpRouteRead({ let addresses: ChainMap; if (symbol || warp) { - const warpCoreConfig = - context.warpCoreConfig ?? // this case is be handled by MultiChainHandler.forWarpCoreConfig() interceptor - (await getWarpCoreConfigOrExit({ - context, - warp, - symbol, - })); + const warpCoreConfig = await getWarpCoreConfigOrExit({ + context, + warp, + symbol, + }); // TODO: merge with XERC20TokenAdapter and WarpRouteReader const xerc20Limits = await Promise.all( diff --git a/typescript/cli/src/send/transfer.ts b/typescript/cli/src/send/transfer.ts index 2929b09c6..a89eb6aa9 100644 --- a/typescript/cli/src/send/transfer.ts +++ b/typescript/cli/src/send/transfer.ts @@ -40,8 +40,8 @@ export async function sendTestTransfer({ }: { context: WriteCommandContext; warpCoreConfig: WarpCoreConfig; - origin?: ChainName; // resolved in signerMiddleware - destination?: ChainName; // resolved in signerMiddleware + origin?: ChainName; + destination?: ChainName; amount: string; recipient?: string; timeoutSec: number; @@ -106,15 +106,10 @@ async function executeDelivery({ skipWaitForDelivery: boolean; selfRelay?: boolean; }) { - const { multiProvider, registry } = context; + const { signer, multiProvider, registry } = context; - const signer = multiProvider.getSigner(origin); - const recipientSigner = multiProvider.getSigner(destination); - - const recipientAddress = await recipientSigner.getAddress(); const signerAddress = await signer.getAddress(); - - recipient ||= recipientAddress; + recipient ||= signerAddress; const chainAddresses = await registry.getAddresses(); @@ -141,11 +136,12 @@ async function executeDelivery({ token = warpCore.findToken(origin, routerAddress)!; } + const senderAddress = await signer.getAddress(); const errors = await warpCore.validateTransfer({ originTokenAmount: token.amount(amount), destination, - recipient, - sender: signerAddress, + recipient: recipient ?? senderAddress, + sender: senderAddress, }); if (errors) { logRed('Error validating transfer', JSON.stringify(errors)); @@ -156,8 +152,8 @@ async function executeDelivery({ const transferTxs = await warpCore.getTransferRemoteTxs({ originTokenAmount: new TokenAmount(amount, token), destination, - sender: signerAddress, - recipient, + sender: senderAddress, + recipient: recipient ?? senderAddress, }); const txReceipts = []; @@ -176,7 +172,7 @@ async function executeDelivery({ const parsed = parseWarpRouteMessage(message.parsed.body); logBlue( - `Sent transfer from sender (${signerAddress}) on ${origin} to recipient (${recipient}) on ${destination}.`, + `Sent transfer from sender (${senderAddress}) on ${origin} to recipient (${recipient}) on ${destination}.`, ); logBlue(`Message ID: ${message.id}`); log(`Message:\n${indentYamlOrJson(yamlStringify(message, null, 2), 4)}`); diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 8d24cce50..8cb6be027 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -1,4 +1,3 @@ -import { ethers } from 'ethers'; import { $ } from 'zx'; import { ERC20Test__factory, ERC4626Test__factory } from '@hyperlane-xyz/core'; @@ -143,9 +142,6 @@ export async function deployToken(privateKey: string, chain: string) { key: privateKey, }); - // Future works: make signer compatible with protocol/chain stack - multiProvider.setSigner(chain, new ethers.Wallet(privateKey)); - const token = await new ERC20Test__factory( multiProvider.getSigner(chain), ).deploy('token', 'token', '100000000000000000000', 18); @@ -165,9 +161,6 @@ export async function deploy4626Vault( key: privateKey, }); - // Future works: make signer compatible with protocol/chain stack - multiProvider.setSigner(chain, new ethers.Wallet(privateKey)); - const vault = await new ERC4626Test__factory( multiProvider.getSigner(chain), ).deploy(tokenAddress, 'VAULT', 'VAULT'); diff --git a/typescript/cli/src/utils/balances.ts b/typescript/cli/src/utils/balances.ts index 2a6e6fcb8..4536353e5 100644 --- a/typescript/cli/src/utils/balances.ts +++ b/typescript/cli/src/utils/balances.ts @@ -8,9 +8,12 @@ import { logGray, logGreen, logRed } from '../logger.js'; export async function nativeBalancesAreSufficient( multiProvider: MultiProvider, + signer: ethers.Signer, chains: ChainName[], minGas: string, ) { + const address = await signer.getAddress(); + const sufficientBalances: boolean[] = []; for (const chain of chains) { // Only Ethereum chains are supported @@ -18,7 +21,7 @@ export async function nativeBalancesAreSufficient( logGray(`Skipping balance check for non-EVM chain: ${chain}`); continue; } - const address = multiProvider.getSigner(chain).getAddress(); + const provider = multiProvider.getProvider(chain); const gasPrice = await provider.getGasPrice(); const minBalanceWei = gasPrice.mul(minGas).toString(); diff --git a/typescript/cli/src/utils/chains.ts b/typescript/cli/src/utils/chains.ts index 7e2eaccd0..add11203d 100644 --- a/typescript/cli/src/utils/chains.ts +++ b/typescript/cli/src/utils/chains.ts @@ -171,36 +171,3 @@ function handleNewChain(chainNames: string[]) { process.exit(0); } } - -/** - * @notice Extracts chain names from a nested configuration object - * @param config Object to search for chain names - * @return Array of discovered chain names - */ -export function extractChainsFromObj(config: Record): string[] { - const chains: string[] = []; - - // Recursively search for chain/chainName fields - function findChainFields(obj: any) { - if (obj === null || typeof obj !== 'object') return; - - if (Array.isArray(obj)) { - obj.forEach((item) => findChainFields(item)); - return; - } - - if ('chain' in obj) { - chains.push(obj.chain); - } - - if ('chainName' in obj) { - chains.push(obj.chainName); - } - - // Recursively search in all nested values - Object.values(obj).forEach((value) => findChainFields(value)); - } - - findChainFields(config); - return chains; -} diff --git a/typescript/cli/src/utils/output.ts b/typescript/cli/src/utils/output.ts index 2e1acfdf4..442b8a090 100644 --- a/typescript/cli/src/utils/output.ts +++ b/typescript/cli/src/utils/output.ts @@ -54,50 +54,3 @@ export function formatYamlViolationsOutput( return highlightedLines.join('\n'); } - -/** - * @notice Masks sensitive key with dots - * @param key Sensitive key to mask - * @return Masked key - */ -export function maskSensitiveKey(key: string): string { - if (!key) return key; - const middle = '•'.repeat(key.length); - return `${middle}`; -} - -const SENSITIVE_PATTERNS = [ - 'privatekey', - 'key', - 'secret', - 'secretkey', - 'password', -]; - -const isSensitiveKey = (key: string) => { - const lowerKey = key.toLowerCase(); - return SENSITIVE_PATTERNS.some((pattern) => lowerKey.includes(pattern)); -}; - -/** - * @notice Recursively masks sensitive data in objects - * @param obj Object with potential sensitive data - * @return Object with masked sensitive data - */ -export function maskSensitiveData(obj: any): any { - if (!obj) return obj; - - if (typeof obj === 'object') { - const masked = { ...obj }; - for (const [key, value] of Object.entries(masked)) { - if (isSensitiveKey(key) && typeof value === 'string') { - masked[key] = maskSensitiveKey(value); - } else if (typeof value === 'object') { - masked[key] = maskSensitiveData(value); - } - } - return masked; - } - - return obj; -} diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts index cef774e73..1586ec6b2 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts @@ -15,8 +15,6 @@ export const EV5GnosisSafeTxBuilderPropsSchema = z.object({ export const EV5JsonRpcTxSubmitterPropsSchema = z.object({ chain: ZChainName, - userAddress: ZHash.optional(), - privateKey: ZHash.optional(), }); export const EV5ImpersonatedAccountTxSubmitterPropsSchema = diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index a244c810b..29a35b6b8 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -1,6 +1,6 @@ import { fromBech32, normalizeBech32, toBech32 } from '@cosmjs/encoding'; import { PublicKey } from '@solana/web3.js'; -import { Wallet, utils as ethersUtils } from 'ethers'; +import { utils as ethersUtils } from 'ethers'; import { isNullish } from './typeof.js'; import { Address, HexString, ProtocolType } from './types.js'; @@ -380,11 +380,3 @@ export function ensure0x(hexstr: string) { export function strip0x(hexstr: string) { return hexstr.startsWith('0x') ? hexstr.slice(2) : hexstr; } - -export function isPrivateKeyEvm(privateKey: string): boolean { - try { - return new Wallet(privateKey).privateKey === privateKey; - } catch { - throw new Error('Provided Private Key is not EVM compatible!'); - } -} diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index f4bd9779c..831441863 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -26,7 +26,6 @@ export { isValidAddressCosmos, isValidAddressEvm, isValidAddressSealevel, - isPrivateKeyEvm, isValidTransactionHash, isValidTransactionHashCosmos, isValidTransactionHashEvm,