feat: add `hyperlane hook read` + `hyperlane ism read` to cli (#3648)
parent
d6f25ed035
commit
af26342079
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/infra': minor |
||||
--- |
||||
|
||||
Moved Hook/ISM reading into CLI. |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Introduces `hyperlane hook read` and `hyperlane ism read` commands for deriving onchain Hook/ISM configs from an address on a given chain. |
@ -0,0 +1,6 @@ |
||||
--- |
||||
'@hyperlane-xyz/utils': minor |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Moved Hook/ISM config stringify into a general object stringify utility. |
@ -0,0 +1,53 @@ |
||||
import { CommandModule } from 'yargs'; |
||||
|
||||
import { readHookConfig } from '../hook/read.js'; |
||||
import { log } from '../logger.js'; |
||||
|
||||
import { |
||||
addressCommandOption, |
||||
chainCommandOption, |
||||
chainsCommandOption, |
||||
fileFormatOption, |
||||
outputFileOption, |
||||
} from './options.js'; |
||||
|
||||
/** |
||||
* Parent command |
||||
*/ |
||||
export const hookCommand: CommandModule = { |
||||
command: 'hook', |
||||
describe: 'Operations relating to Hooks', |
||||
builder: (yargs) => yargs.command(read).version(false).demandCommand(), |
||||
handler: () => log('Command required'), |
||||
}; |
||||
|
||||
// Examples for testing:
|
||||
// Fallback routing hook on polygon (may take 5s):
|
||||
// hyperlane hook read --chain polygon --address 0xca4cCe24E7e06241846F5EA0cda9947F0507C40C
|
||||
// IGP hook on inevm (may take 5s):
|
||||
// hyperlane hook read --chain inevm --address 0x19dc38aeae620380430C200a6E990D5Af5480117
|
||||
export const read: CommandModule = { |
||||
command: 'read', |
||||
describe: 'Reads onchain Hook configuration for a given address', |
||||
builder: (yargs) => |
||||
yargs.options({ |
||||
chains: chainsCommandOption, |
||||
chain: { |
||||
...chainCommandOption, |
||||
demandOption: true, |
||||
}, |
||||
address: addressCommandOption('Address of the Hook to read.', true), |
||||
format: fileFormatOption, |
||||
output: outputFileOption(), |
||||
}), |
||||
handler: async (argv: any) => { |
||||
await readHookConfig({ |
||||
chain: argv.chain, |
||||
address: argv.address, |
||||
chainConfigPath: argv.chains, |
||||
format: argv.format, |
||||
output: argv.output, |
||||
}); |
||||
process.exit(0); |
||||
}, |
||||
}; |
@ -0,0 +1,58 @@ |
||||
import { CommandModule } from 'yargs'; |
||||
|
||||
import { readIsmConfig } from '../ism/read.js'; |
||||
import { log } from '../logger.js'; |
||||
|
||||
import { |
||||
addressCommandOption, |
||||
chainCommandOption, |
||||
chainsCommandOption, |
||||
fileFormatOption, |
||||
outputFileOption, |
||||
} from './options.js'; |
||||
|
||||
/** |
||||
* Parent command |
||||
*/ |
||||
export const ismCommand: CommandModule = { |
||||
command: 'ism', |
||||
describe: 'Operations relating to ISMs', |
||||
builder: (yargs) => yargs.command(read).version(false).demandCommand(), |
||||
handler: () => log('Command required'), |
||||
}; |
||||
|
||||
// Examples for testing:
|
||||
// Top-level aggregation ISM on celo (may take 10s)
|
||||
// hyperlane ism read --chain celo --address 0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E
|
||||
// Aggregation ISM for bsc domain on inevm (may take 5s)
|
||||
// hyperlane ism read --chain inevm --address 0x79A7c7Fe443971CBc6baD623Fdf8019C379a7178
|
||||
// Test ISM on alfajores testnet
|
||||
// hyperlane ism read --chain alfajores --address 0xdB52E4853b6A40D2972E6797E0BDBDb3eB761966
|
||||
export const read: CommandModule = { |
||||
command: 'read', |
||||
describe: 'Reads onchain ISM configuration for a given address', |
||||
builder: (yargs) => |
||||
yargs.options({ |
||||
chains: chainsCommandOption, |
||||
chain: { |
||||
...chainCommandOption, |
||||
demandOption: true, |
||||
}, |
||||
address: addressCommandOption( |
||||
'Address of the Interchain Security Module to read.', |
||||
true, |
||||
), |
||||
format: fileFormatOption, |
||||
output: outputFileOption(), |
||||
}), |
||||
handler: async (argv: any) => { |
||||
await readIsmConfig({ |
||||
chain: argv.chain, |
||||
address: argv.address, |
||||
chainConfigPath: argv.chains, |
||||
format: argv.format, |
||||
output: argv.output, |
||||
}); |
||||
process.exit(0); |
||||
}, |
||||
}; |
@ -0,0 +1,51 @@ |
||||
import { ChainName, EvmHookReader } from '@hyperlane-xyz/sdk'; |
||||
import { Address, ProtocolType, stringifyObject } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { readChainConfigsIfExists } from '../config/chain.js'; |
||||
import { getMultiProvider } from '../context.js'; |
||||
import { log, logBlue, logRed } from '../logger.js'; |
||||
import { |
||||
FileFormat, |
||||
resolveFileFormat, |
||||
writeFileAtPath, |
||||
} from '../utils/files.js'; |
||||
|
||||
/** |
||||
* Read Hook config for a specified chain and address, logging or writing result to file. |
||||
*/ |
||||
export async function readHookConfig({ |
||||
chain, |
||||
address, |
||||
chainConfigPath, |
||||
format, |
||||
output, |
||||
}: { |
||||
chain: ChainName; |
||||
address: Address; |
||||
chainConfigPath: string; |
||||
format: FileFormat; |
||||
output?: string; |
||||
}): Promise<void> { |
||||
const customChains = readChainConfigsIfExists(chainConfigPath); |
||||
const multiProvider = getMultiProvider(customChains); |
||||
|
||||
if (multiProvider.getProtocol(chain) === ProtocolType.Ethereum) { |
||||
const hookReader = new EvmHookReader(multiProvider, chain); |
||||
const config = await hookReader.deriveHookConfig(address); |
||||
const stringConfig = stringifyObject( |
||||
config, |
||||
resolveFileFormat(output, format), |
||||
2, |
||||
); |
||||
if (!output) { |
||||
logBlue(`Hook Config at ${address} on ${chain}:`); |
||||
log(stringConfig); |
||||
} else { |
||||
writeFileAtPath(output, stringConfig + '\n'); |
||||
logBlue(`Hook Config written to ${output}.`); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
logRed('Unsupported chain. Currently this command supports EVM chains only.'); |
||||
} |
@ -0,0 +1,51 @@ |
||||
import { ChainName, EvmIsmReader } from '@hyperlane-xyz/sdk'; |
||||
import { Address, ProtocolType, stringifyObject } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { readChainConfigsIfExists } from '../config/chain.js'; |
||||
import { getMultiProvider } from '../context.js'; |
||||
import { log, logBlue, logRed } from '../logger.js'; |
||||
import { |
||||
FileFormat, |
||||
resolveFileFormat, |
||||
writeFileAtPath, |
||||
} from '../utils/files.js'; |
||||
|
||||
/** |
||||
* Read ISM config for a specified chain and address, logging or writing result to file. |
||||
*/ |
||||
export async function readIsmConfig({ |
||||
chain, |
||||
address, |
||||
chainConfigPath, |
||||
format, |
||||
output, |
||||
}: { |
||||
chain: ChainName; |
||||
address: Address; |
||||
chainConfigPath: string; |
||||
format: FileFormat; |
||||
output?: string; |
||||
}): Promise<void> { |
||||
const customChains = readChainConfigsIfExists(chainConfigPath); |
||||
const multiProvider = getMultiProvider(customChains); |
||||
|
||||
if (multiProvider.getProtocol(chain) === ProtocolType.Ethereum) { |
||||
const ismReader = new EvmIsmReader(multiProvider, chain); |
||||
const config = await ismReader.deriveIsmConfig(address); |
||||
const stringConfig = stringifyObject( |
||||
config, |
||||
resolveFileFormat(output, format), |
||||
2, |
||||
); |
||||
if (!output) { |
||||
logBlue(`ISM Config at ${address} on ${chain}:`); |
||||
log(stringConfig); |
||||
} else { |
||||
writeFileAtPath(output, stringConfig + '\n'); |
||||
logBlue(`ISM Config written to ${output}.`); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
logRed('Unsupported chain. Currently this command supports EVM chains only.'); |
||||
} |
@ -1,70 +0,0 @@ |
||||
import { EvmHookReader, EvmIsmReader, chainMetadata } from '@hyperlane-xyz/sdk'; |
||||
|
||||
import { mainnetConfigs } from '../config/environments/mainnet3/chains.js'; |
||||
import { testnetConfigs } from '../config/environments/testnet4/chains.js'; |
||||
import { Role } from '../src/roles.js'; |
||||
|
||||
import { |
||||
getArgs, |
||||
getMultiProviderForRole, |
||||
withContext, |
||||
withNetwork, |
||||
} from './agent-utils.js'; |
||||
|
||||
// Examples from <monorepo>/typescript/infra:
|
||||
// Fallback routing hook on polygon (may take 5s):
|
||||
// yarn tsx scripts/read-config.ts -e mainnet3 --type hook --network polygon --address 0xca4cCe24E7e06241846F5EA0cda9947F0507C40C
|
||||
// IGP hook on inevm (may take 5s):
|
||||
// yarn tsx scripts/read-config.ts -e mainnet3 --type hook --network inevm --address 0x19dc38aeae620380430C200a6E990D5Af5480117
|
||||
// Top-level aggregation ISM on celo (may take 10s)
|
||||
// yarn tsx scripts/read-config.ts -e mainnet3 --type ism --network celo --address 0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E
|
||||
// Aggregation ISM for bsc domain on inevm (may take 5s)
|
||||
// yarn tsx scripts/read-config.ts -e mainnet3 --type ism --network inevm --address 0x79A7c7Fe443971CBc6baD623Fdf8019C379a7178
|
||||
// Test ISM on alfajores testnet
|
||||
// yarn tsx scripts/read-config.ts -e testnet4 --type ism --network alfajores --address 0xdB52E4853b6A40D2972E6797E0BDBDb3eB761966
|
||||
|
||||
async function readConfig() { |
||||
const { environment, network, context, type, address, concurrency } = |
||||
await withContext(withNetwork(getArgs())) |
||||
.option('type', { |
||||
describe: 'Specify the type of config to read', |
||||
choices: ['ism', 'hook'], |
||||
demandOption: true, |
||||
}) |
||||
.number('concurrency') |
||||
.describe( |
||||
'concurrency', |
||||
'option to override the default concurrency level', |
||||
) |
||||
.string('address') |
||||
.describe('address', 'config address') |
||||
.demandOption('address') |
||||
.demandOption('network').argv; |
||||
|
||||
const multiProvider = await getMultiProviderForRole( |
||||
chainMetadata[network].isTestnet ? testnetConfigs : mainnetConfigs, |
||||
environment, |
||||
context, |
||||
Role.Deployer, |
||||
); |
||||
|
||||
if (type === 'ism') { |
||||
const ismReader = new EvmIsmReader(multiProvider, network, concurrency); |
||||
const config = await ismReader.deriveIsmConfig(address); |
||||
console.log(EvmIsmReader.stringifyConfig(config, 2)); |
||||
} else if (type === 'hook') { |
||||
const hookReader = new EvmHookReader(multiProvider, network, concurrency); |
||||
const config = await hookReader.deriveHookConfig(address); |
||||
console.log(EvmHookReader.stringifyConfig(config, 2)); |
||||
} else { |
||||
console.error('Invalid type specified. Please use "ism" or "hook".'); |
||||
process.exit(1); |
||||
} |
||||
|
||||
process.exit(0); |
||||
} |
||||
|
||||
readConfig().catch((e) => { |
||||
console.error(e); |
||||
process.exit(1); |
||||
}); |
Loading…
Reference in new issue