cosmic-interchain-cli
Version:
A command-line utility for Cosmic Wire's interchain messaging protocol
112 lines • 5.07 kB
JavaScript
import { confirm } from '@inquirer/prompts';
import { ethers } from 'ethers';
import { getLocalProvider, } from '@hyperlane-xyz/sdk';
import { ProtocolType } from '@hyperlane-xyz/utils';
import { parseIsmConfig } from '../config/ism.js';
import { log, logBlue, logGray, logGreen, logPink, logTable, } from '../logger.js';
import { nativeBalancesAreSufficient } from '../utils/balances.js';
import { ENV } from '../utils/env.js';
import { assertSigner } from '../utils/keys.js';
import { completeDryRun } from './dry-run.js';
export async function runPreflightChecksForChains({ context, chains, minGas, chainsToGasCheck, }) {
log('Running pre-flight checks for chains...');
const { signer, multiProvider } = context;
if (!chains?.length)
throw new Error('Empty chain selection');
for (const chain of chains) {
const metadata = multiProvider.tryGetChainMetadata(chain);
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');
}
logGreen('✅ Chains are valid');
assertSigner(signer);
logGreen('✅ Signer is valid');
await nativeBalancesAreSufficient(multiProvider, signer, chainsToGasCheck ?? chains, minGas);
}
export async function runDeployPlanStep({ context, chain, }) {
const { signer, chainMetadata: chainMetadataMap, skipConfirmation } = context;
const address = await signer.getAddress();
logBlue('\nDeployment plan');
logGray('===============');
log(`Transaction signer and owner of new contracts: ${address}`);
log(`Deploying core contracts to network: ${chain}`);
const transformedChainMetadata = transformChainMetadataForDisplay(chainMetadataMap[chain]);
logTable(transformedChainMetadata);
log(`Note: There are several contracts required for each chain, but contracts in your provided registries will be skipped.`);
if (skipConfirmation)
return;
await confirmExistingMailbox(context, chain);
const isConfirmed = await confirm({
message: 'Is this deployment plan correct?',
});
if (!isConfirmed)
throw new Error('Deployment cancelled');
}
async function confirmExistingMailbox(context, chain) {
const addresses = await context.registry.getChainAddresses(chain);
if (addresses?.mailbox) {
const isConfirmed = await confirm({
message: `Mailbox already exists at ${addresses.mailbox}. Are you sure you want to deploy a new mailbox and overwrite existing registry artifacts?`,
default: false,
});
if (!isConfirmed) {
throw Error('Deployment cancelled');
}
}
}
// from parsed types
export function isISMConfig(config) {
return Object.values(config).some((c) => 'type' in c);
}
// directly from filepath
export function isZODISMConfig(filepath) {
return parseIsmConfig(filepath).success;
}
export async function prepareDeploy(context, userAddress, chains) {
const { multiProvider, isDryRun } = context;
const initialBalances = {};
await Promise.all(chains.map(async (chain) => {
const provider = isDryRun
? getLocalProvider(ENV.ANVIL_IP_ADDR, ENV.ANVIL_PORT)
: multiProvider.getProvider(chain);
const currentBalance = await provider.getBalance(userAddress);
initialBalances[chain] = currentBalance;
}));
return initialBalances;
}
export async function completeDeploy(context, command, initialBalances, userAddress, chains) {
const { multiProvider, isDryRun } = context;
if (chains.length > 0)
logPink(`⛽️ Gas Usage Statistics`);
for (const chain of chains) {
const provider = isDryRun
? getLocalProvider(ENV.ANVIL_IP_ADDR, ENV.ANVIL_PORT)
: multiProvider.getProvider(chain);
const currentBalance = await provider.getBalance(userAddress);
const balanceDelta = initialBalances[chain].sub(currentBalance);
if (isDryRun && balanceDelta.lt(0))
break;
logPink(`\t- Gas required for ${command} ${isDryRun ? 'dry-run' : 'deploy'} on ${chain}: ${ethers.utils.formatEther(balanceDelta)} ${multiProvider.getChainMetadata(chain).nativeToken?.symbol ?? 'ETH'}`);
}
if (isDryRun)
await completeDryRun(command);
}
export function toUpperCamelCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function transformChainMetadataForDisplay(chainMetadata) {
return {
Name: chainMetadata.name,
'Display Name': chainMetadata.displayName,
'Chain ID': chainMetadata.chainId,
'Domain ID': chainMetadata.domainId,
Protocol: chainMetadata.protocol,
'JSON RPC URL': chainMetadata.rpcUrls[0].http,
'Native Token: Symbol': chainMetadata.nativeToken?.symbol,
'Native Token: Name': chainMetadata.nativeToken?.name,
'Native Token: Decimals': chainMetadata.nativeToken?.decimals,
};
}
//# sourceMappingURL=utils.js.map