Revert "feat: Multi EVM Chain Signers" (#4910)
Reverts #4869 because it breaks commands that require usage of the `HYP_KEY` env variable or prompted for an owner address like `warp init` ![image](https://github.com/user-attachments/assets/c301c1ec-e69d-44e7-abab-714e215298db)pull/4578/merge
parent
61157097bc
commit
28a824ad7e
@ -1,5 +0,0 @@ |
|||||||
--- |
|
||||||
'@hyperlane-xyz/utils': minor |
|
||||||
--- |
|
||||||
|
|
||||||
Added `isPrivateKeyEvm` function for validating EVM private keys |
|
@ -1,5 +0,0 @@ |
|||||||
--- |
|
||||||
'@hyperlane-xyz/cli': minor |
|
||||||
--- |
|
||||||
|
|
||||||
Added strategy management CLI commands and MultiProtocolSigner implementation for flexible cross-chain signer configuration and management |
|
@ -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); |
|
||||||
}, |
|
||||||
}; |
|
@ -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<CommandType, () => 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<string, any>): ChainResolver { |
|
||||||
const commandKey = `${argv._[0]}:${argv._[1] || ''}`.trim() as CommandType; |
|
||||||
const createStrategy = |
|
||||||
this.strategyMap.get(commandKey) || (() => new SingleChainResolver()); |
|
||||||
return createStrategy(); |
|
||||||
} |
|
||||||
} |
|
@ -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<any>): Promise<ChainName[]> { |
|
||||||
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<string, any>, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
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<string, any>, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
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<string, any>, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
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<string, any>, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
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<string, any>, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
const strategy = await readChainSubmissionStrategyConfig(argv.strategy); |
|
||||||
return extractChainsFromObj(strategy); |
|
||||||
} |
|
||||||
|
|
||||||
private async resolveRelayerChains( |
|
||||||
argv: Record<string, any>, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
return argv.chains.split(',').map((item: string) => item.trim()); |
|
||||||
} |
|
||||||
|
|
||||||
private async getWarpRouteConfigChains( |
|
||||||
configPath: string, |
|
||||||
skipConfirmation: boolean, |
|
||||||
): Promise<ChainName[]> { |
|
||||||
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); |
|
||||||
} |
|
||||||
} |
|
@ -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<any>): Promise<ChainName[]> { |
|
||||||
argv.chain ||= await runSingleChainSelectionStep( |
|
||||||
argv.context.chainMetadata, |
|
||||||
'Select chain to connect:', |
|
||||||
); |
|
||||||
|
|
||||||
return [argv.chain]; // Explicitly return as single-item array
|
|
||||||
} |
|
||||||
} |
|
@ -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<any>): Promise<ChainName[]>; |
|
||||||
} |
|
@ -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<string, any>; // For any additional chain-specific params
|
|
||||||
} |
|
||||||
|
|
||||||
export interface IMultiProtocolSigner { |
|
||||||
getSignerConfig(chain: ChainName): Promise<SignerConfig> | SignerConfig; |
|
||||||
getSigner(config: SignerConfig): Signer; |
|
||||||
} |
|
||||||
|
|
||||||
export abstract class BaseMultiProtocolSigner implements IMultiProtocolSigner { |
|
||||||
constructor(protected config: ChainSubmissionStrategy) {} |
|
||||||
|
|
||||||
abstract getSignerConfig(chain: ChainName): Promise<SignerConfig>; |
|
||||||
abstract getSigner(config: SignerConfig): Signer; |
|
||||||
} |
|
@ -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<SignerConfig> { |
|
||||||
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<SignerConfig> { |
|
||||||
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); |
|
||||||
} |
|
||||||
} |
|
@ -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<ChainName, IMultiProtocolSigner>; |
|
||||||
protected readonly signers: Map<ChainName, Signer>; |
|
||||||
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<MultiProvider> { |
|
||||||
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<Signer> { |
|
||||||
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<typeof this.signers> { |
|
||||||
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<string> { |
|
||||||
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; |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue