Skip to content

Commit

Permalink
account-restructure tweaks (#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
mixmix authored Sep 18, 2024
1 parent 3e091f9 commit 183e200
Show file tree
Hide file tree
Showing 23 changed files with 169 additions and 216 deletions.
62 changes: 25 additions & 37 deletions src/account/command.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import Entropy from "@entropyxyz/sdk"
import { Command, Option } from 'commander'
import { EntropyAccount } from "./main";
import { selectAndPersistNewAccount } from "./utils";
import { ACCOUNTS_CONTENT } from './constants'
import * as config from '../config'
import { cliWrite, currentAccountAddressOption, endpointOption, loadEntropy, passwordOption } from "../common/utils-cli";
import { findAccountNameByAddress, updateConfig } from "src/common/utils";
import { findAccountByAddressOrName } from "src/common/utils";

export function entropyAccountCommand () {
const accountCommand = new Command('account')
return new Command('account')
.description('Commands to work with accounts on the Entropy Network')
.addCommand(entropyAccountCreate())
.addCommand(entropyAccountImport())
.addCommand(entropyAccountList())
.addCommand(entropyAccountRegister())

return accountCommand
}

function entropyAccountCreate () {
const accountCreateCommand = new Command('create')
return new Command('create')
.alias('new')
.description('Create a new entropy account from scratch. Output is JSON of form {name, address}')
.addOption(passwordOption())
Expand All @@ -33,19 +32,18 @@ function entropyAccountCreate () {
const { path } = opts
const newAccount = await EntropyAccount.create({ name, path })

await persistAndSelectNewAccount(newAccount)
await selectAndPersistNewAccount(newAccount)

cliWrite({
name: newAccount.name,
address: newAccount.address
})
process.exit(0)
})
return accountCreateCommand
}

function entropyAccountImport () {
const accountImportCommand = new Command('import')
return new Command('import')
.description('Import an existing entropy account from seed. Output is JSON of form {name, address}')
.addOption(passwordOption())
.argument('<name>', 'A user friendly name for your new account.')
Expand All @@ -60,35 +58,18 @@ function entropyAccountImport () {
const { path } = opts
const newAccount = await EntropyAccount.import({ name, seed, path })

await persistAndSelectNewAccount(newAccount)
await selectAndPersistNewAccount(newAccount)

cliWrite({
name: newAccount.name,
address: newAccount.address
})
process.exit(0)
})
return accountImportCommand
}

async function persistAndSelectNewAccount (newAccount) {
const storedConfig = await config.get()
const { accounts } = storedConfig

const isExistingName = accounts.find(account => account.name === newAccount.name)
if (isExistingName) {
throw Error(`An account with name "${newAccount.name}" already exists. Choose a different name`)
}

accounts.push(newAccount)
await updateConfig(storedConfig, {
accounts,
selectedAccount: newAccount.address
})
}

function entropyAccountList () {
const accountListCommand = new Command('list')
return new Command('list')
.alias('ls')
.description('List all accounts. Output is JSON of form [{ name, address, verifyingKeys }]')
.action(async () => {
Expand All @@ -98,13 +79,11 @@ function entropyAccountList () {
cliWrite(accounts)
process.exit(0)
})
return accountListCommand
}

/* register */
function entropyAccountRegister () {
const accountRegisterCommand = new Command('register')
accountRegisterCommand
return new Command('register')
.description('Register an entropy account with a program')
.addOption(passwordOption())
.addOption(endpointOption())
Expand All @@ -123,20 +102,29 @@ function entropyAccountRegister () {
// )
// )
.action(async (opts) => {
const { account, endpoint, /* password */ } = opts
const storedConfig = await config.get()
const { accounts } = storedConfig
const entropy: Entropy = await loadEntropy(opts.account, opts.endpoint)
const AccountsService = new EntropyAccount(entropy, opts.endpoint)
const accountToRegister = findAccountNameByAddress(accounts, opts.account)
const accountToRegister = findAccountByAddressOrName(accounts, account)
if (!accountToRegister) {
throw new Error('AccountError: Unable to register non-existent account')
}
const updatedAccount = await AccountsService.registerAccount(accountToRegister)

const entropy: Entropy = await loadEntropy(accountToRegister.address, endpoint)
const accountService = new EntropyAccount(entropy, endpoint)
const updatedAccount = await accountService.registerAccount(accountToRegister)

const arrIdx = accounts.indexOf(accountToRegister)
accounts.splice(arrIdx, 1, updatedAccount)
await updateConfig(storedConfig, { accounts, selectedAccount: updatedAccount.address })
cliWrite("Your address" + updatedAccount.address + "has been successfully registered.")
await config.set({
...storedConfig,
accounts,
selectedAccount: updatedAccount.address
})

const verifyingKeys = updatedAccount?.data?.registration?.verifyingKeys
const verifyingKey = verifyingKeys[verifyingKeys.length - 1]
cliWrite(verifyingKey)
process.exit(0)
})
return accountRegisterCommand
}
37 changes: 20 additions & 17 deletions src/account/interaction.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import inquirer from "inquirer";
import Entropy from "@entropyxyz/sdk";

import { findAccountNameByAddress, print } from "../common/utils"
import { EntropyConfig } from "../config/types";
import { EntropyAccount } from './main'
import { selectAndPersistNewAccount } from "./utils";
import { findAccountByAddressOrName, print } from "../common/utils"
import { EntropyConfig } from "../config/types";
import * as config from "../config";

import {
manageAccountsQuestions,
newAccountQuestions,
selectAccountQuestions
} from "./utils"


export async function entropyManageAccounts (endpoint: string, storedConfig: EntropyConfig) {
/*
* @returns partialConfigUpdate | "exit" | undefined
*/
export async function entropyAccount (endpoint: string, storedConfig: EntropyConfig) {
const { accounts } = storedConfig
const { interactionChoice } = await inquirer.prompt(manageAccountsQuestions)

Expand All @@ -26,15 +30,13 @@ export async function entropyManageAccounts (endpoint: string, storedConfig: Ent
// isDebugMode = true
seed = seed.split('#debug')[0]
}

const newAccount = seed
? await EntropyAccount.import({ seed, name, path })
: await EntropyAccount.create({ name, path })
accounts.push(newAccount)

return {
accounts,
selectedAccount: newAccount.address
}
await selectAndPersistNewAccount(newAccount)
return
}

case 'select-account': {
Expand All @@ -43,12 +45,13 @@ export async function entropyManageAccounts (endpoint: string, storedConfig: Ent
return
}
const { selectedAccount } = await inquirer.prompt(selectAccountQuestions(accounts))
print('Current selected account is ' + selectedAccount)

return {
accounts: storedConfig.accounts,
await config.set({
...storedConfig,
selectedAccount: selectedAccount.address
}
})

print('Current selected account is ' + selectedAccount)
return
}

case 'list-account': {
Expand All @@ -71,16 +74,16 @@ export async function entropyManageAccounts (endpoint: string, storedConfig: Ent
}

export async function entropyRegister (entropy: Entropy, endpoint: string, storedConfig: EntropyConfig): Promise<Partial<EntropyConfig>> {
const AccountService = new EntropyAccount(entropy, endpoint)
const accountService = new EntropyAccount(entropy, endpoint)

const { accounts, selectedAccount } = storedConfig
const currentAccount = findAccountNameByAddress(accounts, selectedAccount)
const currentAccount = findAccountByAddressOrName(accounts, selectedAccount)
if (!currentAccount) {
print("No account selected to register")
return;
}
print("Attempting to register the address:", currentAccount.address)
const updatedAccount = await AccountService.registerAccount(currentAccount)
const updatedAccount = await accountService.registerAccount(currentAccount)
const arrIdx = accounts.indexOf(currentAccount)
accounts.splice(arrIdx, 1, updatedAccount)
print("Your address", updatedAccount.address, "has been successfully registered.")
Expand Down
6 changes: 4 additions & 2 deletions src/account/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export class EntropyAccount extends EntropyBase {
: undefined

return this.entropy.register(registerParams)
// NOTE: if "register" fails for any reason, core currently leaves the chain in a "polluted"
// state. To fix this we manually "prune" the dirty registration transaction.
.catch(async error => {
await this.pruneRegistration()
throw error
Expand All @@ -86,8 +88,8 @@ export class EntropyAccount extends EntropyBase {
// Register params to be defined from user input (arguments/options or inquirer prompts)
try {
const verifyingKey = await this.register(registerParams)

account?.data?.registration?.verifyingKeys?.push(verifyingKey)
// NOTE: this mutation triggers events in Keyring
account.data.registration.verifyingKeys.push(verifyingKey)
return account
} catch (error) {
this.logger.error('There was a problem registering', error)
Expand Down
25 changes: 23 additions & 2 deletions src/account/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import { EntropyAccountConfig } from "../config/types";
import { AccountListResults } from './types';
import * as config from "../config";
import { ACCOUNTS_CONTENT } from './constants';
import { generateAccountChoices } from '../common/utils';

const validateSeedInput = (seed) => {
export async function selectAndPersistNewAccount (newAccount) {
const storedConfig = await config.get()
const { accounts } = storedConfig

const isExistingName = accounts.find(account => account.name === newAccount.name)
if (isExistingName) {
throw Error(`An account with name "${newAccount.name}" already exists. Choose a different name`)
}
const isExistingAddress = accounts.find(account => account.address === newAccount.address)
if (isExistingAddress) {
throw Error(`An account with address "${newAccount.address}" already exists.`)
}

accounts.push(newAccount)
await config.set({
...storedConfig,
accounts,
selectedAccount: newAccount.address
})
}

function validateSeedInput (seed) {
if (seed.includes('#debug')) return true
if (seed.length === 66 && seed.startsWith('0x')) return true
if (seed.length === 64) return true
Expand Down
4 changes: 2 additions & 2 deletions src/balance/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { EntropyBalance } from "./main"

export async function entropyBalance (entropy, endpoint, storedConfig) {
try {
const BalanceService = new EntropyBalance(entropy, endpoint)
const balance = await BalanceService.getBalance(storedConfig.selectedAccount)
const balanceService = new EntropyBalance(entropy, endpoint)
const balance = await balanceService.getBalance(storedConfig.selectedAccount)
print(`Address ${storedConfig.selectedAccount} has a balance of: ${balance.toLocaleString('en-US')} BITS`)
} catch (error) {
console.error('There was an error retrieving balance', error)
Expand Down
41 changes: 5 additions & 36 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

/* NOTE: calling this file entropy.ts helps commander parse process.argv */
import { Command, Option } from 'commander'
import Entropy from '@entropyxyz/sdk'

import * as config from './config'
import { EntropyTuiOptions } from './types'
import { currentAccountAddressOption, endpointOption, loadEntropy } from './common/utils-cli'

Expand All @@ -14,33 +12,15 @@ import { entropyTransferCommand } from './transfer/command'
import { entropySignCommand } from './sign/command'
import { entropyBalanceCommand } from './balance/command'

let entropy: Entropy
async function setEntropyGlobal (address: string, endpoint: string, password?: string) {
if (entropy) {
const currentAddress = entropy?.keyring?.accounts?.registration?.address
if (address !== currentAddress) {
// QUESTION: Is it possible to hit this?
// - programmatic usage kills process after function call
// - tui usage manages mutation of entropy instance itself
await entropy.close()
entropy = await loadEntropy(address, endpoint, password)
}
}
else if (address && endpoint) {
entropy = await loadEntropy(address, endpoint, password)
}

return entropy
}

const program = new Command()

/* no command */
program
.name('entropy')
.description('CLI interface for interacting with entropy.xyz. Running without commands starts an interactive ui')
.addOption(endpointOption())
.addOption(currentAccountAddressOption())
.addOption(endpointOption())
// NOTE: I think this is currently unused
.addOption(
new Option(
'-d, --dev',
Expand All @@ -49,24 +29,13 @@ program
.env('DEV_MODE')
.hideHelp()
)
.hook('preAction', async (_thisCommand, actionCommand) => {
const commandName = actionCommand?.name()
await config.init()
if (commandName === 'account') return
// entropy not required for any account commands

const { account, endpoint, password } = actionCommand.opts()
const address = commandName === 'balance'
? actionCommand.args[0]
: account

await setEntropyGlobal(address, endpoint, password)
})
.addCommand(entropyBalanceCommand())
.addCommand(entropyAccountCommand())
.addCommand(entropyTransferCommand())
.addCommand(entropySignCommand())
.action((options: EntropyTuiOptions) => {
.action(async (options: EntropyTuiOptions) => {
const { account, endpoint } = options
const entropy = await loadEntropy(account, endpoint)
launchTui(entropy, options)
})

Expand Down
4 changes: 2 additions & 2 deletions src/common/entropy-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export abstract class EntropyBase {
protected logger: EntropyLogger
protected entropy: Entropy

constructor ({ entropy, endpoint, flowContext }: { entropy?: Entropy, endpoint: string, flowContext: string }) {
constructor ({ entropy, endpoint, flowContext }: { entropy: Entropy, endpoint: string, flowContext: string }) {
this.logger = new EntropyLogger(flowContext, endpoint)
this.entropy = entropy
}
}
}
Loading

0 comments on commit 183e200

Please sign in to comment.