From 5c5bcb2e21e0fe3442aeed36d3125652c0b0c3ef Mon Sep 17 00:00:00 2001 From: Mohamed Ismail Date: Thu, 25 Jan 2024 08:58:24 +0100 Subject: [PATCH] feat(add-account): refactor update add-from-wallet command utils --- .../src/account/commands/accountAddManual.ts | 32 +-- .../src/account/commands/accountAddWallet.ts | 85 ++++-- .../tools/kadena-cli/src/account/types.ts | 23 +- .../src/account/utils/addHelpers.ts | 257 +----------------- .../src/account/utils/getAccountDetails.ts | 12 +- .../account/utils/validateAccountDetails.ts | 21 +- 6 files changed, 119 insertions(+), 311 deletions(-) diff --git a/packages/tools/kadena-cli/src/account/commands/accountAddManual.ts b/packages/tools/kadena-cli/src/account/commands/accountAddManual.ts index be6136dc1f1..2afb548bf7e 100644 --- a/packages/tools/kadena-cli/src/account/commands/accountAddManual.ts +++ b/packages/tools/kadena-cli/src/account/commands/accountAddManual.ts @@ -8,31 +8,10 @@ import { updateAccountDetailsPrompt } from '../../prompts/account.js'; import { createCommand } from '../../utils/createCommand.js'; import { globalOptions } from '../../utils/globalOptions.js'; import { sanitizeFilename } from '../../utils/helpers.js'; -import type { - IAccountDetailsResult, - IAddAccountManualConfig, -} from '../types.js'; +import { getUpdatedConfig } from '../utils/addHelpers.js'; import { validateAccountDetails } from '../utils/validateAccountDetails.js'; import { writeConfigInFile } from '../utils/writeConfigInFile.js'; -export const getUpdatedConfig = ( - config: IAddAccountManualConfig, - accountDetails: IAccountDetailsResult, - updateOption: string, -) => { - if (updateOption === 'userInput') { - return config; - } else { - const updatedConfig = { - ...config, - publicKeys: accountDetails.publicKeys.join(','), - publicKeysConfig: accountDetails.publicKeys, - predicate: accountDetails.predicate, - }; - return updatedConfig; - } -}; - export const addAccountManualCommand: ( program: Command, version: string, @@ -56,8 +35,11 @@ export const addAccountManualCommand: ( const filePath = path.join(defaultAccountPath, `${sanitizedAlias}.yaml`); try { - const [{ config: newConfig, accountDetails }, isConfigAreSame] = - await validateAccountDetails(config); + const { + config: newConfig, + accountDetails, + isConfigAreSame, + } = await validateAccountDetails(config); if (isConfigAreSame) { await writeConfigInFile(filePath, newConfig); @@ -81,7 +63,7 @@ export const addAccountManualCommand: ( if (error.message.includes('row not found')) { console.log( chalk.red( - `The account ${config.accountName} is not on chain yet. To create it on-chain, transfer funds to it from ${config.networkConfig.network} and use "fund" command.`, + `The account is not on chain yet. To create it on-chain, transfer funds to it from ${config.networkConfig.network} and use "fund" command.`, ), ); return; diff --git a/packages/tools/kadena-cli/src/account/commands/accountAddWallet.ts b/packages/tools/kadena-cli/src/account/commands/accountAddWallet.ts index c85883b2c5e..fb2a0468f41 100644 --- a/packages/tools/kadena-cli/src/account/commands/accountAddWallet.ts +++ b/packages/tools/kadena-cli/src/account/commands/accountAddWallet.ts @@ -9,15 +9,30 @@ import type { IKeyPair } from '@kadena/types'; import { defaultAccountPath } from '../../constants/account.js'; import { WALLET_DIR } from '../../constants/config.js'; import { printWalletKeys } from '../../keys/utils/keysDisplay.js'; +import { IWallet, getWallet } from '../../keys/utils/keysHelpers.js'; +import { updateAccountDetailsPrompt } from '../../prompts/account.js'; import { services } from '../../services/index.js'; import { createCommand } from '../../utils/createCommand.js'; import { globalOptions } from '../../utils/globalOptions.js'; import { sanitizeFilename } from '../../utils/helpers.js'; -import { IAddAccountManualConfig } from '../types.js'; -import { - validateAccountDetails, - writeConfigInFile, -} from '../utils/addHelpers.js'; +import type { IAddAccountManualConfig } from '../types.js'; +import { getUpdatedConfig } from '../utils/addHelpers.js'; +import { validateAccountDetails } from '../utils/validateAccountDetails.js'; +import { writeConfigInFile } from '../utils/writeConfigInFile.js'; + +async function getAllPublicKeysFromWallet( + keyWalletConfig: IWallet, +): Promise> { + const publicKeysList: Array = []; + for (const key of keyWalletConfig.keys) { + const content = await services.filesystem.readFile( + path.join(WALLET_DIR, keyWalletConfig?.folder, key), + ); + const parsed = content !== null ? (yaml.load(content) as IKeyPair) : null; + publicKeysList.push(parsed?.publicKey || ''); + } + return publicKeysList.filter((key) => !!key); +} export const addAccountWalletCommand: ( program: Command, @@ -43,32 +58,58 @@ export const addAccountWalletCommand: ( return; } - const publicKeysList: Array = []; - for (const key of keyWalletConfig.keys) { - const content = await services.filesystem.readFile( - path.join(WALLET_DIR, keyWalletConfig?.folder, key), - ); - const parsed = content !== null ? (yaml.load(content) as IKeyPair) : null; - publicKeysList.push(parsed?.publicKey); - } - - publicKeysList.filter((key) => !!key); + const publicKeys = await getAllPublicKeysFromWallet(keyWalletConfig); const selectPublicKeys = await checkbox({ message: 'Select public keys to add to account', - choices: publicKeysList.map((key) => ({ value: key })), + choices: publicKeys.map((key) => ({ value: key })), }); - (config as unknown as IAddAccountManualConfig).publicKeysConfig = - selectPublicKeys as string[]; + const updatedConfig = { + ...config, + publicKeys: selectPublicKeys.join(','), + publicKeysConfig: selectPublicKeys, + }; const sanitizedAlias = sanitizeFilename(config.accountAlias).toLowerCase(); const filePath = path.join(defaultAccountPath, `${sanitizedAlias}.yaml`); - const newConfig = await validateAccountDetails( - config as unknown as IAddAccountManualConfig, - ); + try { + const { + config: newConfig, + accountDetails, + isConfigAreSame, + } = await validateAccountDetails(updatedConfig); + + if (isConfigAreSame) { + await writeConfigInFile(filePath, newConfig); + } else { + const updateOption = await updateAccountDetailsPrompt(); + + const updatedConfig = getUpdatedConfig( + newConfig, + accountDetails, + updateOption, + ); - await writeConfigInFile(filePath, newConfig); + await writeConfigInFile(filePath, updatedConfig); + } + console.log( + chalk.green( + `\nThe account configuration "${updatedConfig.accountAlias}" has been saved.\n`, + ), + ); + } catch (error) { + if (error.message.includes('row not found')) { + console.log( + chalk.red( + `The account is not on chain yet. To create it on-chain, transfer funds to it from ${updatedConfig.networkConfig.network} and use "fund" command.`, + ), + ); + return; + } + console.log(chalk.red(`${error.message}`)); + process.exit(1); + } }, ); diff --git a/packages/tools/kadena-cli/src/account/types.ts b/packages/tools/kadena-cli/src/account/types.ts index 2b2353f025a..5f527e3624c 100644 --- a/packages/tools/kadena-cli/src/account/types.ts +++ b/packages/tools/kadena-cli/src/account/types.ts @@ -3,12 +3,27 @@ import type { INetworkCreateOptions } from '../networks/utils/networkHelpers.js' export type Predicate = 'keys-all' | 'keys-2' | 'keys-any'; -export interface IAddAccountManualConfig extends INetworkCreateOptions { +export interface IAccountConfig { accountAlias: string; - accountName?: string; fungible: string; - predicate: Predicate; + predicate: string; + network: string; chainId: ChainId; - publicKeysConfig: string[]; networkConfig: INetworkCreateOptions; + quiet?: boolean; + publicKeys: string; + publicKeysConfig: string[]; +} + +export interface IAddAccountWalletConfig extends IAccountConfig { + keyWallet: string; +} + +export interface IAddAccountManualConfig extends IAccountConfig { + accountName?: string; +} + +export interface IAccountDetailsResult { + publicKeys: string[]; + predicate: string; } diff --git a/packages/tools/kadena-cli/src/account/utils/addHelpers.ts b/packages/tools/kadena-cli/src/account/utils/addHelpers.ts index 904774c9fab..23fcbe8b60f 100644 --- a/packages/tools/kadena-cli/src/account/utils/addHelpers.ts +++ b/packages/tools/kadena-cli/src/account/utils/addHelpers.ts @@ -1,251 +1,22 @@ -import { select } from '@inquirer/prompts'; -import { createPrincipal } from '@kadena/client-utils/built-in'; -import { details } from '@kadena/client-utils/coin'; -import chalk from 'chalk'; -import yaml from 'js-yaml'; - -import type { ChainId } from '@kadena/types'; -import { services } from '../../services/index.js'; -import type { IAddAccountManualConfig, Predicate } from '../types.js'; +import { IAccountDetailsResult, IAddAccountManualConfig } from '../types.js'; export const isEmpty = (value?: string): boolean => value === undefined || value === '' || value === null; -export async function writeAlias( - config: IAddAccountManualConfig, - filePath: string, -): Promise { - const { publicKeysConfig, predicate, accountName, fungible } = config; - await services.filesystem.ensureDirectoryExists(filePath); - await services.filesystem.writeFile( - filePath, - yaml.dump({ - name: accountName, - fungible, - publicKeys: publicKeysConfig.filter((key: string) => !!key), - predicate, - }), - ); -} - -export const validatePublicKeys = ( - publicKeysConfig: string[], - keys: string[], -): boolean => { - const publicKeys = publicKeysConfig.filter((key: string) => !!key); - - const hasSamePublicKeysLength = publicKeys.length === keys.length; - - return publicKeys.length === 0 - ? true - : hasSamePublicKeysLength && keys.every((key) => publicKeys.includes(key)); -}; - -interface IAccountDetailsResult { - publicKeys: string[]; - predicate?: Predicate; -} - -export async function getAccountDetailsFromChain( - accountName: string, - networkId: string, - chainId: string, - networkHost: string, -): Promise { - try { - const accountDetails = await details( - accountName, - networkId, - chainId as ChainId, - networkHost, - ); - const { guard: { keys = [], pred } = {} } = accountDetails as { - guard: { keys: string[]; pred: string }; - }; - - return { - publicKeys: keys, - predicate: pred as Predicate, - }; - } catch (e) { - if (e.message?.includes('row not found') === true) { - console.log( - chalk.red( - `The account ${accountName} is not on chain yet. To create it on-chain, transfer funds to it from ${networkId} and use "fund" command.`, - ), - ); - process.exit(1); - } - console.log( - chalk.red( - 'There was an error getting the account details. Please try again.', - ), - ); - process.exit(1); - } -} - -export async function compareAndUpdateConfig( +export const getUpdatedConfig = ( config: IAddAccountManualConfig, accountDetails: IAccountDetailsResult, -): Promise { - const { publicKeys, predicate } = accountDetails; - const isSameKeys = validatePublicKeys(config.publicKeysConfig, publicKeys); - - if (!isSameKeys || config.predicate !== predicate) { - const updateOption = await select({ - message: - 'The account details do not match the account details on the chain. Do you want to continue?', - choices: [ - { value: 'userInput', name: 'Add, anyway with user inputs' }, - { value: 'chain', name: 'Add with values from the chain' }, - ], - }); - - if (updateOption === 'userInput') { - return config; - } else { - const updatedConfig = { - ...config, - publicKeysConfig: publicKeys, - predicate: predicate as Predicate, - }; - return updatedConfig; - } - } else { + updateOption: string, +) => { + if (updateOption === 'userInput') { return config; + } else { + const updatedConfig = { + ...config, + publicKeys: accountDetails.publicKeys.join(','), + publicKeysConfig: accountDetails.publicKeys, + predicate: accountDetails.predicate, + }; + return updatedConfig; } -} - -export async function createAccountName( - config: IAddAccountManualConfig, -): Promise { - const publicKeys = config.publicKeysConfig.filter((key: string) => !!key); - - if (publicKeys.length === 0) { - console.log( - chalk.red( - 'No public keys provided. Please provide minimum one public key to create an account name', - ), - ); - return; - } - - if (isEmpty(config.predicate)) { - console.log( - chalk.red('No predicate provided. Please provide a predicate.'), - ); - return; - } - - try { - const accountName = await createPrincipal( - { - keyset: { - pred: config.predicate, - keys: publicKeys, - }, - }, - { - host: config.networkConfig.networkHost, - defaults: { - networkId: config.networkConfig.networkId, - meta: { - chainId: config.chainId, - }, - }, - }, - ); - - return accountName; - } catch (e) { - console.log( - chalk.red('There was an error creating the account. Please try again.'), - ); - process.exit(1); - } -} - -export async function getAccountDetails( - config: IAddAccountManualConfig, -): Promise { - const { - accountName, - chainId, - networkConfig: { networkHost, networkId }, - } = config; - - const { publicKeys, predicate } = await getAccountDetailsFromChain( - accountName as string, - networkId, - chainId, - networkHost, - ); - - return compareAndUpdateConfig(config, { - publicKeys, - predicate, - }); -} - -export async function createAccount( - config: IAddAccountManualConfig, -): Promise { - const accountName = await createAccountName(config); - if (isEmpty(accountName)) { - console.log( - chalk.red('There was an error creating the account. Please try again.'), - ); - process.exit(1); - } - - return { - ...config, - accountName, - }; -} - -export async function validateAccountDetails( - config: IAddAccountManualConfig, -): Promise { - const { accountName } = config; - if (isEmpty(accountName)) { - const updatedConfig = await createAccount(config); - return getAccountDetails(updatedConfig); - } - - return getAccountDetails(config); -} - -export async function validateConfigFileExistence( - filePath: string, -): Promise { - if (await services.filesystem.fileExists(filePath)) { - console.log( - chalk.red(`\nThe account configuration "${filePath}" already exists.\n`), - ); - return true; - } - return false; -} - -export async function writeConfigInFile( - filePath: string, - config: IAddAccountManualConfig, -): Promise { - if (await validateConfigFileExistence(filePath)) { - console.log( - chalk.yellow( - `\nThe existing account configuration "${config.accountAlias}" will not be updated.\n`, - ), - ); - process.exit(1); - } - - await writeAlias(config, filePath); - console.log( - chalk.green( - `\nThe account configuration "${config.accountAlias}" has been saved.\n`, - ), - ); -} +}; diff --git a/packages/tools/kadena-cli/src/account/utils/getAccountDetails.ts b/packages/tools/kadena-cli/src/account/utils/getAccountDetails.ts index 62370ac6937..26a0dc4b49b 100644 --- a/packages/tools/kadena-cli/src/account/utils/getAccountDetails.ts +++ b/packages/tools/kadena-cli/src/account/utils/getAccountDetails.ts @@ -1,13 +1,13 @@ import { details } from '@kadena/client-utils/coin'; import { ChainId } from '@kadena/types'; -import { - IAccountDetailsResult, - IAddAccountManualConfig, - Predicate, -} from '../types.js'; +import { IAccountConfig, IAccountDetailsResult, Predicate } from '../types.js'; + +interface IAccountDetails extends IAccountConfig { + accountName: string; +} export async function getAccountDetailsFromChain( - config: IAddAccountManualConfig, + config: IAccountDetails, ): Promise { const { accountName, diff --git a/packages/tools/kadena-cli/src/account/utils/validateAccountDetails.ts b/packages/tools/kadena-cli/src/account/utils/validateAccountDetails.ts index 8b5e68ee663..1edcf68c095 100644 --- a/packages/tools/kadena-cli/src/account/utils/validateAccountDetails.ts +++ b/packages/tools/kadena-cli/src/account/utils/validateAccountDetails.ts @@ -1,20 +1,21 @@ import { IAccountDetailsResult, IAddAccountManualConfig } from '../types.js'; -import { isEmpty } from './addHelpers.js'; import { compareConfigAndAccountDetails } from './compareConfigAndAccountDetails.js'; import { createAccountName } from './createAccountName.js'; import { getAccountDetailsFromChain } from './getAccountDetails.js'; interface IValidateAccountDetails { + isConfigAreSame: boolean; config: IAddAccountManualConfig; accountDetails: IAccountDetailsResult; } export async function validateAccountDetails( config: IAddAccountManualConfig, -): Promise<[IValidateAccountDetails, boolean]> { - const accountName = isEmpty(config.accountName) - ? await createAccountName(config) - : config.accountName; +): Promise { + const accountName = + config.accountName === undefined + ? await createAccountName(config) + : config.accountName; const configWithAccountName = { ...config, @@ -30,11 +31,9 @@ export async function validateAccountDetails( accountDetails, ); - return [ - { - config: configWithAccountName, - accountDetails, - }, + return { + config: configWithAccountName, + accountDetails, isConfigAreSame, - ]; + }; }