@holographxyz/cli
Version:
Holograph operator CLI
201 lines (200 loc) • 10.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const inquirer = tslib_1.__importStar(require("inquirer"));
const core_1 = require("@oclif/core");
const color_1 = tslib_1.__importDefault(require("@oclif/color"));
const networks_1 = require("@holographxyz/networks");
const bignumber_1 = require("@ethersproject/bignumber");
const units_1 = require("@ethersproject/units");
const core_chain_service_1 = tslib_1.__importDefault(require("../../services/core-chain-service"));
const operator_chain_service_1 = tslib_1.__importDefault(require("../../services/operator-chain-service"));
const token_chain_service_1 = tslib_1.__importDefault(require("../../services/token-chain-service"));
const config_1 = require("../../utils/config");
const network_monitor_1 = require("../../utils/network-monitor");
const utils_1 = require("../../utils/utils");
const validation_1 = require("../../utils/validation");
const _1 = tslib_1.__importDefault(require("."));
/**
* Bond
* Description: Bond and operator into a pod.
*/
class Bond extends core_1.Command {
static description = 'Bond in to a pod.';
static examples = ['$ <%= config.bin %> <%= command.id %> --network <string> --pod <number> --amount <number>'];
static flags = {
...network_monitor_1.networkFlag,
pod: core_1.Flags.integer({
description: 'Pod number to join',
}),
amount: core_1.Flags.integer({
description: 'Amount of tokens to deposit',
}),
};
networkMonitor;
async run() {
const { flags } = await this.parse(Bond);
let { pod, amount } = flags;
let prompt;
this.log(color_1.default.red('WARNING: To bond you must first have an operator running with the same wallet on the chain you are bonding to. Failure to do so will result in a loss of funds.'));
prompt = await inquirer.prompt([
{
name: 'continue',
message: 'Do you have the operator with the wallet you are bonding from running on the network and are ready to proceed?',
type: 'confirm',
default: false,
},
]);
if (!prompt.continue) {
this.log('Operator is not ready to bond, please start an operator first.');
this.exit();
}
this.log('Loading user configurations...');
const { userWallet, configFile, supportedNetworksOptions } = await (0, config_1.ensureConfigFileIsValid)(this.config.configDir, undefined, true);
const network = await (0, validation_1.checkOptionFlag)(supportedNetworksOptions, flags.network, 'Select the network to bond to');
this.log(`Joining network: ${networks_1.networks[network].shortKey}`);
this.networkMonitor = new network_monitor_1.NetworkMonitor({
parent: this,
configFile,
networks: [network],
debug: this.debug,
userWallet,
verbose: false,
});
core_1.CliUx.ux.action.start('Loading network RPC provider');
await this.networkMonitor.run(true);
core_1.CliUx.ux.action.stop();
// Setup the contracts and chain services
const coreChainService = new core_chain_service_1.default(network, this.networkMonitor);
await coreChainService.initialize();
const tokenContract = await coreChainService.getUtilityToken();
const tokenChainService = new token_chain_service_1.default(network, this.networkMonitor, tokenContract);
const operatorContract = await coreChainService.getOperator();
const operatorChainService = new operator_chain_service_1.default(network, this.networkMonitor, operatorContract);
const operator = operatorChainService.operator;
const currentHlgBalance = (await tokenChainService.balanceOf(coreChainService.wallet.address));
this.log(`Current HLG balance: ${(0, units_1.formatUnits)(currentHlgBalance, 'ether')}`);
if ((await operator.getBondedAmount(coreChainService.wallet.address)) > 0) {
prompt = await inquirer.prompt([
{
name: 'continue',
message: 'You are already bonded on this network. Would you like to unbond?',
type: 'confirm',
default: true,
},
]);
if (!prompt.continue) {
this.log('You are already bonded on this network. Please unbond first.');
this.exit();
}
this.log(`Unbonding operator ${coreChainService.wallet.address} from network: ${networks_1.networks[network].shortKey}`);
const unbondReceipt = await operatorChainService.unbondUtilityToken();
if (unbondReceipt === null) {
this.log(color_1.default.red(`Could not confirm the success of unbonding transaction.`));
this.exit();
}
prompt = await inquirer.prompt([
{
name: 'continue',
message: 'Would you like to rebond?',
type: 'confirm',
default: true,
},
]);
if (!prompt.continue) {
this.log('Thank you. Come again.');
this.exit();
}
}
if (!currentHlgBalance.gt(bignumber_1.BigNumber.from('0'))) {
this.log('No HLG balance found, please deposit HLG into your wallet before bonding.');
this.exit();
}
this.log('Checking pods available...');
const totalPods = await operator.getTotalPods();
this.log(`Total Pods: ${totalPods}`);
// Get the bond amounts for each pod
const allPodBondAmounts = [];
for (let i = 1; i <= totalPods; i++) {
allPodBondAmounts.push(await operator.getPodBondAmounts(i));
}
const podChoices = allPodBondAmounts.map((podBondAmounts, index) => {
return `${index + 1} - ${(0, units_1.formatUnits)(podBondAmounts.current, 'ether')} HLG`;
});
if (!pod) {
prompt = await inquirer.prompt([
{
name: 'pod',
message: 'Enter the pod number to join',
type: 'list',
choices: podChoices,
},
]);
pod = Number.parseInt(prompt.pod.split(' - ')[0], 10);
this.log(`Joining pod: ${pod}`);
}
const podBondAmounts = await operator.getPodBondAmounts(pod);
this.log(`Pod ${pod} has a base bond amount of ${(0, units_1.formatUnits)(podBondAmounts.base, 'ether')} and currently requires ${(0, units_1.formatUnits)(podBondAmounts.current, 'ether')} to bond.`);
this.log(`Enter an amount greater or equal to: ${(0, units_1.formatUnits)(podBondAmounts.current, 'ether')} to bond.`);
if (!amount) {
prompt = await inquirer.prompt([
{
name: 'amount',
message: `Enter the amount of tokens to deposit (Units in ether)`,
type: 'number',
validate: async (input) => {
const inputBN = bignumber_1.BigNumber.from((0, utils_1.toLong18)(input));
if (typeof input === 'number' && input > 0 && inputBN.gte(podBondAmounts.current)) {
return true;
}
return 'Input is not a valid bond amount';
},
},
]);
amount = prompt.amount;
}
this.log(`Bonding from ${coreChainService.wallet.address} to pod ${pod} on ${networks_1.networks[network].shortKey} network for ${amount} tokens`);
core_1.CliUx.ux.action.start('Calculating gas amounts and prices');
const estimatedGas = await operatorChainService.estimateGasForBondUtilityToken(coreChainService.wallet.address, (0, utils_1.toLong18)(amount), pod);
core_1.CliUx.ux.action.stop();
this.log(`Transaction is estimated to cost a total of ${(0, units_1.formatUnits)(estimatedGas, 'ether')} ${networks_1.networks[network].tokenSymbol}`);
if (estimatedGas.gt(await coreChainService.getBalance())) {
this.log(`You do not have enough ${networks_1.networks[network].tokenSymbol} to cover the transaction cost. Please deposit more ${networks_1.networks[network].tokenSymbol} into your wallet before bonding.`);
this.exit();
}
prompt = await inquirer.prompt([
{
name: 'continue',
message: 'Next steps submit the transaction, would you like to proceed?',
type: 'confirm',
default: true,
},
]);
if (!prompt.continue) {
this.log('Dropping command, no blockchain transactions executed');
this.exit();
}
const receipt = await operatorChainService.bondUtilityToken(coreChainService.wallet.address, (0, utils_1.toLong18)(amount), pod);
if (receipt === null) {
this.log(color_1.default.red(`Could not confirm the success of transaction.`));
this.exit();
}
this.log(color_1.default.green(`Welcome operator! Your wallet ${coreChainService.wallet.address} has bonded ${amount} eth to pod ${pod} on ${networks_1.networks[network].shortKey} 🎉` +
`\nAgain please make sure your operator remains operational! ` +
`Failure will result in slashed funds!`));
prompt = await inquirer.prompt([
{
name: 'continue',
message: "Last chance to start your operator if you don't have it running already. Would you like to proceed?",
type: 'confirm',
default: true,
},
]);
if (!prompt.continue) {
this.log('Successfully bonded. Exiting...');
this.exit();
}
await _1.default.run(['--mode', 'auto']);
}
}
exports.default = Bond;