|
|
|
@ -1,24 +1,11 @@ |
|
|
|
|
import { ethers } from 'ethers'; |
|
|
|
|
import { stringify as yamlStringify } from 'yaml'; |
|
|
|
|
import { CommandModule } from 'yargs'; |
|
|
|
|
|
|
|
|
|
import { |
|
|
|
|
HypXERC20Lockbox__factory, |
|
|
|
|
HypXERC20__factory, |
|
|
|
|
IXERC20__factory, |
|
|
|
|
} from '@hyperlane-xyz/core'; |
|
|
|
|
import { |
|
|
|
|
ChainMap, |
|
|
|
|
ChainSubmissionStrategySchema, |
|
|
|
|
EvmERC20WarpRouteReader, |
|
|
|
|
TokenStandard, |
|
|
|
|
WarpCoreConfig, |
|
|
|
|
} from '@hyperlane-xyz/sdk'; |
|
|
|
|
import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; |
|
|
|
|
import { ChainSubmissionStrategySchema } from '@hyperlane-xyz/sdk'; |
|
|
|
|
|
|
|
|
|
import { runWarpRouteCheck } from '../check/warp.js'; |
|
|
|
|
import { |
|
|
|
|
createWarpRouteDeployConfig, |
|
|
|
|
readWarpCoreConfig, |
|
|
|
|
readWarpRouteDeployConfig, |
|
|
|
|
} from '../config/warp.js'; |
|
|
|
|
import { |
|
|
|
@ -27,7 +14,8 @@ import { |
|
|
|
|
} from '../context/types.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 { log, logCommandHeader, logGreen } from '../logger.js'; |
|
|
|
|
import { runWarpRouteRead } from '../read/warp.js'; |
|
|
|
|
import { sendTestTransfer } from '../send/transfer.js'; |
|
|
|
|
import { |
|
|
|
|
indentYamlOrJson, |
|
|
|
@ -35,13 +23,15 @@ import { |
|
|
|
|
removeEndingSlash, |
|
|
|
|
writeYamlOrJson, |
|
|
|
|
} from '../utils/files.js'; |
|
|
|
|
import { selectRegistryWarpRoute } from '../utils/tokens.js'; |
|
|
|
|
import { getWarpCoreConfigOrExit } from '../utils/input.js'; |
|
|
|
|
|
|
|
|
|
import { |
|
|
|
|
DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, |
|
|
|
|
addressCommandOption, |
|
|
|
|
chainCommandOption, |
|
|
|
|
dryRunCommandOption, |
|
|
|
|
fromAddressCommandOption, |
|
|
|
|
inputFileCommandOption, |
|
|
|
|
outputFileCommandOption, |
|
|
|
|
strategyCommandOption, |
|
|
|
|
symbolCommandOption, |
|
|
|
@ -59,6 +49,7 @@ export const warpCommand: CommandModule = { |
|
|
|
|
builder: (yargs) => |
|
|
|
|
yargs |
|
|
|
|
.command(apply) |
|
|
|
|
.command(check) |
|
|
|
|
.command(deploy) |
|
|
|
|
.command(init) |
|
|
|
|
.command(read) |
|
|
|
@ -104,17 +95,14 @@ export const apply: CommandModuleWithWriteContext<{ |
|
|
|
|
strategy: strategyUrl, |
|
|
|
|
receiptsDir, |
|
|
|
|
}) => { |
|
|
|
|
logGray(`Hyperlane Warp Apply`); |
|
|
|
|
logGray('--------------------'); // @TODO consider creating a helper function for these dashes
|
|
|
|
|
let warpCoreConfig: WarpCoreConfig; |
|
|
|
|
if (symbol) { |
|
|
|
|
warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); |
|
|
|
|
} else if (warp) { |
|
|
|
|
warpCoreConfig = readWarpCoreConfig(warp); |
|
|
|
|
} else { |
|
|
|
|
logRed(`Please specify either a symbol or warp config`); |
|
|
|
|
process.exit(0); |
|
|
|
|
} |
|
|
|
|
logCommandHeader('Hyperlane Warp Apply'); |
|
|
|
|
|
|
|
|
|
const warpCoreConfig = await getWarpCoreConfigOrExit({ |
|
|
|
|
symbol, |
|
|
|
|
warp, |
|
|
|
|
context, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (strategyUrl) |
|
|
|
|
ChainSubmissionStrategySchema.parse(readYamlOrJson(strategyUrl)); |
|
|
|
|
const warpDeployConfig = await readWarpRouteDeployConfig(config); |
|
|
|
@ -143,8 +131,9 @@ export const deploy: CommandModuleWithWriteContext<{ |
|
|
|
|
'from-address': fromAddressCommandOption, |
|
|
|
|
}, |
|
|
|
|
handler: async ({ context, config, dryRun }) => { |
|
|
|
|
logGray(`Hyperlane Warp Route Deployment${dryRun ? ' Dry-Run' : ''}`); |
|
|
|
|
logGray('------------------------------------------------'); |
|
|
|
|
logCommandHeader( |
|
|
|
|
`Hyperlane Warp Route Deployment${dryRun ? ' Dry-Run' : ''}`, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
await runWarpRouteDeploy({ |
|
|
|
@ -171,11 +160,10 @@ export const init: CommandModuleWithContext<{ |
|
|
|
|
describe: 'Create an advanced ISM', |
|
|
|
|
default: false, |
|
|
|
|
}, |
|
|
|
|
out: outputFileCommandOption('./configs/warp-route-deployment.yaml'), |
|
|
|
|
out: outputFileCommandOption(DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH), |
|
|
|
|
}, |
|
|
|
|
handler: async ({ context, advanced, out }) => { |
|
|
|
|
logGray('Hyperlane Warp Configure'); |
|
|
|
|
logGray('------------------------'); |
|
|
|
|
logCommandHeader('Hyperlane Warp Configure'); |
|
|
|
|
|
|
|
|
|
await createWarpRouteDeployConfig({ |
|
|
|
|
context, |
|
|
|
@ -208,7 +196,7 @@ export const read: CommandModuleWithContext<{ |
|
|
|
|
false, |
|
|
|
|
), |
|
|
|
|
config: outputFileCommandOption( |
|
|
|
|
'./configs/warp-route-deployment.yaml', |
|
|
|
|
DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, |
|
|
|
|
false, |
|
|
|
|
'The path to output a Warp Config JSON or YAML file.', |
|
|
|
|
), |
|
|
|
@ -220,75 +208,14 @@ export const read: CommandModuleWithContext<{ |
|
|
|
|
config: configFilePath, |
|
|
|
|
symbol, |
|
|
|
|
}) => { |
|
|
|
|
logGray('Hyperlane Warp Reader'); |
|
|
|
|
logGray('---------------------'); |
|
|
|
|
|
|
|
|
|
const { multiProvider } = context; |
|
|
|
|
|
|
|
|
|
let addresses: ChainMap<string>; |
|
|
|
|
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(); |
|
|
|
|
logCommandHeader('Hyperlane Warp Reader'); |
|
|
|
|
|
|
|
|
|
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, |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
const config = await runWarpRouteRead({ |
|
|
|
|
context, |
|
|
|
|
chain, |
|
|
|
|
address, |
|
|
|
|
symbol, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (configFilePath) { |
|
|
|
|
writeYamlOrJson(configFilePath, config, 'yaml'); |
|
|
|
@ -346,15 +273,11 @@ const send: CommandModuleWithWriteContext< |
|
|
|
|
amount, |
|
|
|
|
recipient, |
|
|
|
|
}) => { |
|
|
|
|
let warpCoreConfig: WarpCoreConfig; |
|
|
|
|
if (symbol) { |
|
|
|
|
warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); |
|
|
|
|
} else if (warp) { |
|
|
|
|
warpCoreConfig = readWarpCoreConfig(warp); |
|
|
|
|
} else { |
|
|
|
|
logRed(`Please specify either a symbol or warp config`); |
|
|
|
|
process.exit(0); |
|
|
|
|
} |
|
|
|
|
const warpCoreConfig = await getWarpCoreConfigOrExit({ |
|
|
|
|
symbol, |
|
|
|
|
warp, |
|
|
|
|
context, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
await sendTestTransfer({ |
|
|
|
|
context, |
|
|
|
@ -370,3 +293,44 @@ const send: CommandModuleWithWriteContext< |
|
|
|
|
process.exit(0); |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export const check: CommandModuleWithContext<{ |
|
|
|
|
config: string; |
|
|
|
|
symbol?: string; |
|
|
|
|
warp?: string; |
|
|
|
|
}> = { |
|
|
|
|
command: 'check', |
|
|
|
|
describe: |
|
|
|
|
'Verifies that a warp route configuration matches the on chain configuration.', |
|
|
|
|
builder: { |
|
|
|
|
symbol: { |
|
|
|
|
...symbolCommandOption, |
|
|
|
|
demandOption: false, |
|
|
|
|
}, |
|
|
|
|
warp: { |
|
|
|
|
...warpCoreConfigCommandOption, |
|
|
|
|
demandOption: false, |
|
|
|
|
}, |
|
|
|
|
config: inputFileCommandOption({ |
|
|
|
|
defaultPath: DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, |
|
|
|
|
description: 'The path to a warp route deployment configuration file', |
|
|
|
|
}), |
|
|
|
|
}, |
|
|
|
|
handler: async ({ context, config, symbol, warp }) => { |
|
|
|
|
logCommandHeader('Hyperlane Warp Check'); |
|
|
|
|
|
|
|
|
|
const warpRouteConfig = await readWarpRouteDeployConfig(config, context); |
|
|
|
|
const onChainWarpConfig = await runWarpRouteRead({ |
|
|
|
|
context, |
|
|
|
|
warp, |
|
|
|
|
symbol, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
await runWarpRouteCheck({ |
|
|
|
|
onChainWarpConfig, |
|
|
|
|
warpRouteConfig, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
process.exit(0); |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|