UNPKG

cosmic-interchain-cli

Version:

A command-line utility for Cosmic Wire's interchain messaging protocol

230 lines 8.76 kB
import { ethers } from 'ethers'; import { stringify as yamlStringify } from 'yaml'; import { HypXERC20Lockbox__factory, HypXERC20__factory, IXERC20__factory, } from '@hyperlane-xyz/core'; import { EvmERC20WarpRouteReader, TokenStandard, } from '@hyperlane-xyz/sdk'; import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { createWarpRouteDeployConfig, readWarpCoreConfig, readWarpRouteDeployConfig, } from '../config/warp.js'; import { evaluateIfDryRunFailure } from '../deploy/dry-run.js'; import { runWarpRouteApply, runWarpRouteDeploy } from '../deploy/warp.js'; import { log, logGray, logGreen, logRed, logTable } from '../logger.js'; import { sendTestTransfer } from '../send/transfer.js'; import { indentYamlOrJson, writeYamlOrJson } from '../utils/files.js'; import { selectRegistryWarpRoute } from '../utils/tokens.js'; import { addressCommandOption, chainCommandOption, dryRunCommandOption, fromAddressCommandOption, outputFileCommandOption, strategyCommandOption, symbolCommandOption, warpCoreConfigCommandOption, warpDeploymentConfigCommandOption, } from './options.js'; import { messageOptions } from './send.js'; /** * Parent command */ export const warpCommand = { command: 'seif', describe: 'Manage interchain seif routes', builder: (yargs) => yargs .command(apply) .command(deploy) .command(init) .command(read) .command(send) .version(false) .demandCommand(), handler: () => log('Command required'), }; export const apply = { command: 'apply', describe: 'Update seif route contracts', builder: { config: warpDeploymentConfigCommandOption, symbol: { ...symbolCommandOption, demandOption: false, }, warp: { ...warpCoreConfigCommandOption, demandOption: false, }, strategy: { ...strategyCommandOption, demandOption: false }, }, handler: async ({ context, config, symbol, warp, strategy: strategyUrl }) => { logGray(`Interchain Seif Apply`); logGray('--------------------'); // @TODO consider creating a helper function for these dashes let warpCoreConfig; if (symbol) { warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); } else if (warp) { warpCoreConfig = readWarpCoreConfig(warp); } else { logRed(`Please specify either a symbol or seif config`); process.exit(0); } const warpDeployConfig = await readWarpRouteDeployConfig(config); await runWarpRouteApply({ context, warpDeployConfig, warpCoreConfig, strategyUrl, }); process.exit(0); }, }; export const deploy = { command: 'deploy', describe: 'Deploy Seif Route contracts', builder: { config: warpDeploymentConfigCommandOption, 'dry-run': dryRunCommandOption, 'from-address': fromAddressCommandOption, }, handler: async ({ context, config, dryRun }) => { logGray(`Interchain Seif Route Deployment${dryRun ? ' Dry-Run' : ''}`); logGray('------------------------------------------------'); try { await runWarpRouteDeploy({ context, warpRouteDeploymentConfigPath: config, }); } catch (error) { evaluateIfDryRunFailure(error, dryRun); throw error; } process.exit(0); }, }; export const init = { command: 'init', describe: 'Create a seif route configuration.', builder: { advanced: { type: 'boolean', describe: 'Create an advanced ISM', default: false, }, out: outputFileCommandOption('./configs/seif-route-deployment.yaml'), }, handler: async ({ context, advanced, out }) => { logGray('Interchain Seif Configure'); logGray('------------------------'); await createWarpRouteDeployConfig({ context, outPath: out, advanced, }); process.exit(0); }, }; export const read = { command: 'read', describe: 'Derive the seif route config from onchain artifacts', builder: { symbol: { ...symbolCommandOption, demandOption: false, }, chain: { ...chainCommandOption, demandOption: false, }, address: addressCommandOption('Address of the router contract to read.', false), config: outputFileCommandOption('./configs/seif-route-deployment.yaml', false, 'The path to output a Seif Config JSON or YAML file.'), }, handler: async ({ context, chain, address, config: configFilePath, symbol, }) => { logGray('Interchain Seif Reader'); logGray('---------------------'); const { multiProvider } = context; let addresses; if (symbol) { const warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); // TODO: merge with XERC20TokenAdapter and WarpRouteReader const xerc20Limits = await Promise.all(warpCoreConfig.tokens .filter((t) => t.standard === TokenStandard.EvmHypXERC20 || t.standard === TokenStandard.EvmHypXERC20Lockbox) .map(async (t) => { const provider = multiProvider.getProvider(t.chainName); const router = t.addressOrDenom; const xerc20Address = t.standard === TokenStandard.EvmHypXERC20Lockbox ? await HypXERC20Lockbox__factory.connect(router, provider).xERC20() : await HypXERC20__factory.connect(router, provider).wrappedToken(); const xerc20 = IXERC20__factory.connect(xerc20Address, provider); const mint = await xerc20.mintingCurrentLimitOf(router); const burn = await xerc20.burningCurrentLimitOf(router); const formattedLimits = objMap({ mint, burn }, (_, v) => ethers.utils.formatUnits(v, t.decimals)); return [t.chainName, formattedLimits]; })); if (xerc20Limits.length > 0) { logGray('xERC20 Limits:'); logTable(Object.fromEntries(xerc20Limits)); } addresses = Object.fromEntries(warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom])); } else if (chain && address) { addresses = { [chain]: address, }; } else { logGreen(`Please specify either a symbol or chain and address`); process.exit(0); } const config = await promiseObjAll(objMap(addresses, async (chain, address) => new EvmERC20WarpRouteReader(multiProvider, chain).deriveWarpRouteConfig(address))); if (configFilePath) { writeYamlOrJson(configFilePath, config, 'yaml'); logGreen(`✅ Seif route config written successfully to ${configFilePath}:\n`); } else { logGreen(`✅ Seif route config read successfully:\n`); } log(indentYamlOrJson(yamlStringify(config, null, 2), 4)); process.exit(0); }, }; const send = { command: 'send', describe: 'Send a test token transfer on a seif route', builder: { ...messageOptions, symbol: { ...symbolCommandOption, demandOption: false, }, warp: { ...warpCoreConfigCommandOption, demandOption: false, }, amount: { type: 'string', description: 'Amount to send (in smallest unit)', default: 1, }, recipient: { type: 'string', description: 'Token recipient address (defaults to sender)', }, }, handler: async ({ context, origin, destination, timeout, quick, relay, symbol, warp, amount, recipient, }) => { let warpCoreConfig; if (symbol) { warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); } else if (warp) { warpCoreConfig = readWarpCoreConfig(warp); } else { logRed(`Please specify either a symbol or seif config`); process.exit(0); } await sendTestTransfer({ context, warpCoreConfig, origin, destination, amount, recipient, timeoutSec: timeout, skipWaitForDelivery: quick, selfRelay: relay, }); process.exit(0); }, }; //# sourceMappingURL=warp.js.map