cosmic-interchain-cli
Version:
A command-line utility for Cosmic Wire's interchain messaging protocol
168 lines • 7.04 kB
JavaScript
import { input, select } from '@inquirer/prompts';
import { stringify as yamlStringify } from 'yaml';
import { IsmType, TokenType, WarpCoreConfigSchema, WarpRouteDeployConfigSchema, } from '@hyperlane-xyz/sdk';
import { assert, objMap, promiseObjAll } from '@hyperlane-xyz/utils';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
import { runMultiChainSelectionStep } from '../utils/chains.js';
import { indentYamlOrJson, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js';
import { detectAndConfirmOrPrompt } from '../utils/input.js';
import { createAdvancedIsmConfig } from './ism.js';
const TYPE_DESCRIPTIONS = {
[TokenType.synthetic]: 'A new ERC20 with remote transfer functionality',
[TokenType.collateral]: 'Extends an existing ERC20 with remote transfer functionality',
[TokenType.native]: 'Extends the native token with remote transfer functionality',
[TokenType.collateralVault]: 'Extends an existing ERC4626 with remote transfer functionality',
[TokenType.collateralFiat]: 'Extends an existing FiatToken with remote transfer functionality',
[TokenType.XERC20]: 'Extends an existing xERC20 with Warp Route functionality',
[TokenType.XERC20Lockbox]: 'Extends an existing xERC20 Lockbox with Warp Route functionality',
// TODO: describe
[TokenType.fastSynthetic]: '',
[TokenType.syntheticUri]: '',
[TokenType.fastCollateral]: '',
[TokenType.collateralUri]: '',
[TokenType.nativeScaled]: '',
};
const TYPE_CHOICES = Object.values(TokenType).map((type) => ({
name: type,
value: type,
description: TYPE_DESCRIPTIONS[type],
}));
async function fillDefaults(context, config) {
return promiseObjAll(objMap(config, async (chain, config) => {
let mailbox = config.mailbox;
if (!mailbox) {
const addresses = await context.registry.getChainAddresses(chain);
assert(addresses, `No addresses found for chain ${chain}`);
mailbox = addresses.mailbox;
}
let owner = config.owner;
if (!owner) {
owner =
(await context.signer?.getAddress()) ??
(await context.multiProvider.getSignerAddress(chain));
}
return {
owner,
mailbox,
...config,
};
}));
}
export async function readWarpRouteDeployConfig(filePath, context) {
let config = readYamlOrJson(filePath);
if (!config)
throw new Error(`No warp route deploy config found at ${filePath}`);
if (context) {
config = await fillDefaults(context, config);
}
return WarpRouteDeployConfigSchema.parse(config);
}
export function isValidWarpRouteDeployConfig(config) {
return WarpRouteDeployConfigSchema.safeParse(config).success;
}
export async function createWarpRouteDeployConfig({ context, outPath, advanced = false, }) {
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(context.chainMetadata, 'Select chains to connect', 1);
const result = {};
for (const chain of warpChains) {
logBlue(`${chain}: Configuring warp route...`);
const type = await select({
message: `Select ${chain}'s token type`,
choices: TYPE_CHOICES,
});
// TODO: restore NFT prompting
const isNft = type === TokenType.syntheticUri || type === TokenType.collateralUri;
const mailbox = await detectAndConfirmOrPrompt(async () => {
const addresses = await context.registry.getChainAddresses(chain);
return addresses?.mailbox;
}, `For ${chain}, enter the`, 'mailbox address', 'hyperlane-registry');
const interchainSecurityModule = advanced
? await createAdvancedIsmConfig(context)
: createDefaultWarpIsmConfig(owner);
switch (type) {
case TokenType.collateral:
case TokenType.XERC20:
case TokenType.XERC20Lockbox:
case TokenType.collateralFiat:
case TokenType.collateralUri:
case TokenType.fastCollateral:
result[chain] = {
mailbox,
type,
owner,
isNft,
interchainSecurityModule,
token: await input({
message: `Enter the existing token address on chain ${chain}`,
}),
};
break;
case TokenType.collateralVault:
result[chain] = {
mailbox,
type,
owner,
isNft,
interchainSecurityModule,
token: await input({
message: `Enter the ERC-4626 vault address on chain ${chain}`,
}),
};
break;
default:
result[chain] = {
mailbox,
type,
owner,
isNft,
interchainSecurityModule,
};
}
}
try {
const warpRouteDeployConfig = WarpRouteDeployConfigSchema.parse(result);
logBlue(`Warp Route config is valid, writing to file ${outPath}:\n`);
log(indentYamlOrJson(yamlStringify(warpRouteDeployConfig, null, 2), 4));
writeYamlOrJson(outPath, warpRouteDeployConfig, 'yaml');
logGreen('✅ Successfully created new warp route deployment config.');
}
catch (e) {
errorRed(`Warp route deployment config is invalid, please see https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/cli/examples/warp-route-deployment.yaml for an example.`);
throw e;
}
}
// Note, this is different than the function above which reads a config
// for a DEPLOYMENT. This gets a config for using a warp route (aka WarpCoreConfig)
export function readWarpCoreConfig(filePath) {
const config = readYamlOrJson(filePath);
if (!config)
throw new Error(`No warp route config found at ${filePath}`);
return WarpCoreConfigSchema.parse(config);
}
/**
* Creates a default configuration for an ISM with a TRUSTED_RELAYER and FALLBACK_ROUTING.
*
* Properties relayer and owner are both set as input owner.
*
* @param owner - The address of the owner of the ISM.
* @returns The default Aggregation ISM configuration.
*/
function createDefaultWarpIsmConfig(owner) {
return {
type: IsmType.AGGREGATION,
modules: [
{
type: IsmType.TRUSTED_RELAYER,
relayer: owner,
},
{
type: IsmType.FALLBACK_ROUTING,
domains: {},
owner,
},
],
threshold: 1,
};
}
//# sourceMappingURL=warp.js.map