UNPKG

@holographxyz/cli

Version:
169 lines (168 loc) 9.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const inquirer = tslib_1.__importStar(require("inquirer")); const fs = tslib_1.__importStar(require("fs-extra")); const core_1 = require("@oclif/core"); const bignumber_1 = require("@ethersproject/bignumber"); const units_1 = require("@ethersproject/units"); const networks_1 = require("@holographxyz/networks"); const config_1 = require("../../utils/config"); const utils_1 = require("../../utils/utils"); const network_monitor_1 = require("../../utils/network-monitor"); const validation_1 = require("../../utils/validation"); class BridgeContract extends core_1.Command { static description = 'Bridge a Holographable contract from source chain to destination chain. You need to have a deployment config JSON file. Use the "contract:create" command to create or extract one.'; static examples = [ '$ <%= config.bin %> <%= command.id %> --sourceNetwork="goerli" --destinationNetwork="fuji" --deploymentConfig="./MyContract.json"', ]; static flags = { sourceNetwork: core_1.Flags.string({ description: 'The network from which contract deploy request will be sent', parse: validation_1.validateNetwork, options: networks_1.supportedShortNetworks, multiple: false, required: false, }), destinationNetwork: core_1.Flags.string({ description: 'The network on which the contract will be deployed', parse: validation_1.validateNetwork, options: networks_1.supportedShortNetworks, multiple: false, required: false, }), deploymentConfig: core_1.Flags.string({ description: 'The config file to use', parse: validation_1.validateNonEmptyString, multiple: false, required: false, }), }; /** * Contract class variables */ networkMonitor; async run() { this.log('Loading user configurations...'); const { userWallet, configFile, supportedNetworksOptions } = await (0, config_1.ensureConfigFileIsValid)(this.config.configDir, undefined, true); const { flags } = await this.parse(BridgeContract); this.log('User configurations loaded'); const sourceNetwork = await (0, validation_1.checkOptionFlag)(supportedNetworksOptions, flags.sourceNetwork, 'Select the network from which contract deploy request will be sent'); const destinationNetwork = await (0, validation_1.checkOptionFlag)(supportedNetworksOptions, flags.destinationNetwork, 'Select the network on which the contract will be deployed', sourceNetwork); let deploymentConfig; const deploymentConfigFile = await (0, validation_1.checkStringFlag)(flags.deploymentConfig, 'Enter the config file to use'); if (await fs.pathExists(deploymentConfigFile)) { deploymentConfig = (await fs.readJson(deploymentConfigFile)); } else { throw new Error('The file "' + deploymentConfigFile + '" does not exist'); } const configHash = utils_1.web3.utils.keccak256('0x' + deploymentConfig.config.contractType.slice(2) + deploymentConfig.config.chainType.slice(2) + deploymentConfig.config.salt.slice(2) + utils_1.web3.utils.keccak256(deploymentConfig.config.byteCode).slice(2) + utils_1.web3.utils.keccak256(deploymentConfig.config.initCode).slice(2) + deploymentConfig.signer.slice(2)); this.networkMonitor = new network_monitor_1.NetworkMonitor({ parent: this, configFile, networks: [sourceNetwork, destinationNetwork], debug: this.debug, userWallet, verbose: false, }); core_1.CliUx.ux.action.start('Loading network RPC providers'); await this.networkMonitor.run(true); core_1.CliUx.ux.action.stop(); core_1.CliUx.ux.action.start(`Checking that contract is not already deployed on ${networks_1.networks[destinationNetwork].shortKey} network`); const contractAddress = await this.networkMonitor.registryContract .connect(this.networkMonitor.providers[destinationNetwork]) .getContractTypeAddress(configHash); core_1.CliUx.ux.action.stop(); if (contractAddress !== utils_1.zeroAddress) { this.networkMonitor.structuredLogError(destinationNetwork, `Contract already deployed at ${contractAddress}`); this.exit(); } const data = (0, utils_1.generateInitCode)(['tuple(bytes32,uint32,bytes32,bytes,bytes)', 'tuple(bytes32,bytes32,uint8)', 'address'], [ [ deploymentConfig.config.contractType, deploymentConfig.config.chainType, deploymentConfig.config.salt, deploymentConfig.config.byteCode, deploymentConfig.config.initCode, ], [deploymentConfig.signature.r, deploymentConfig.signature.s, deploymentConfig.signature.v], deploymentConfig.signer, ]); const TESTGASLIMIT = bignumber_1.BigNumber.from('10000000'); let payload = await this.networkMonitor.bridgeContract .connect(this.networkMonitor.providers[sourceNetwork]) .callStatic.getBridgeOutRequestPayload(networks_1.networks[destinationNetwork].holographId, this.networkMonitor.factoryAddress, '0x' + 'ff'.repeat(32), '0x' + 'ff'.repeat(32), data); let estimatedGas = TESTGASLIMIT.sub(await this.networkMonitor.operatorContract .connect(this.networkMonitor.providers[destinationNetwork]) .callStatic.jobEstimator(payload, { gasLimit: TESTGASLIMIT })); const gasPricing = this.networkMonitor.gasPrices[destinationNetwork]; let gasPrice = gasPricing.isEip1559 ? gasPricing.maxFeePerGas : gasPricing.gasPrice; gasPrice = gasPrice.add(gasPrice.div(bignumber_1.BigNumber.from('4'))); payload = await this.networkMonitor.bridgeContract .connect(this.networkMonitor.providers[sourceNetwork]) .callStatic.getBridgeOutRequestPayload(networks_1.networks[destinationNetwork].holographId, this.networkMonitor.factoryAddress, estimatedGas, // allow LZ module to set gas price // '0x' + '00'.repeat(32), gasPrice, data); const fees = await this.networkMonitor.bridgeContract .connect(this.networkMonitor.providers[sourceNetwork]) .callStatic.getMessageFee(networks_1.networks[destinationNetwork].holographId, estimatedGas, gasPrice, payload); const total = fees[0].add(fees[1]); estimatedGas = TESTGASLIMIT.sub(await this.networkMonitor.operatorContract .connect(this.networkMonitor.providers[destinationNetwork]) .callStatic.jobEstimator(payload, { value: total, gasLimit: TESTGASLIMIT })); this.log('hlg fee', (0, units_1.formatUnits)(fees[0], 'ether'), 'ether'); this.log('lz fee', (0, units_1.formatUnits)(fees[1], 'ether'), 'ether'); this.log('lz gasPrice', (0, units_1.formatUnits)(fees[2], 'gwei'), 'GWEI'); this.log('our estimated gasPrice', (0, units_1.formatUnits)(gasPrice, 'gwei'), 'GWEI'); this.log('estimated gas usage', estimatedGas.toNumber()); const blockchainPrompt = await inquirer.prompt([ { name: 'shouldContinue', message: 'Next steps submit the transaction, would you like to proceed?', type: 'confirm', default: true, }, ]); if (!blockchainPrompt.shouldContinue) { this.log('Dropping command, no blockchain transactions executed'); this.exit(); } core_1.CliUx.ux.action.start('Making beam request...'); const receipt = await this.networkMonitor.executeTransaction({ network: sourceNetwork, contract: this.networkMonitor.bridgeContract.connect(this.networkMonitor.providers[destinationNetwork]), methodName: 'bridgeOutRequest', args: [ networks_1.networks[destinationNetwork].holographId, this.networkMonitor.factoryAddress, estimatedGas, gasPrice, data, ], waitForReceipt: true, value: total.add(total.div(bignumber_1.BigNumber.from('4'))), }); core_1.CliUx.ux.action.stop(); if (receipt === null) { this.networkMonitor.structuredLogError(sourceNetwork, `Failed to confirm that the transaction was mined`); this.exit(); } else { const jobHash = this.networkMonitor.decodeCrossChainMessageSentEvent(receipt, this.networkMonitor.operatorAddress); if (jobHash === undefined) { this.log('Failed to extract cross-chain job hash transaction receipt'); } this.log(`Cross-chain beaming from ${networks_1.networks[sourceNetwork].shortKey} network, to ${networks_1.networks[destinationNetwork].shortKey} network has started under job hash ${jobHash}`); } this.exit(); } } exports.default = BridgeContract;