Skip to content

Commit

Permalink
start refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mixmix committed Aug 27, 2024
1 parent b59a656 commit 1b7aa6f
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 106 deletions.
68 changes: 68 additions & 0 deletions src/account/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Entropy from "@entropyxyz/sdk";
import { Command, Option } from 'commander'
import { randomAsHex } from '@polkadot/util-crypto'
import { EntropyAccount } from "./main";
import { ACCOUNTS_CONTENT } from './constants'
import * as config from '../config'
import { cliWrite, endpointOption, passwordOption } from "../common/utils-cli";

export async function entropyAccountCommand (entropy: Entropy, rootCommand: Command) {
const accountCommand = rootCommand.command('account')
.description('Commands to work with accounts on the Entropy Network')

entropyAccountList(entropy, accountCommand)
entropyAccountNew(entropy, accountCommand)
}

function entropyAccountList (entropy: Entropy, accountCommand: Command) {
accountCommand.command('list')
.alias('ls')
.description('List all accounts. Output is JSON of form [{ name, address, verifyingKeys }]')
.addOption(endpointOption())
.action(async (options) => {
// TODO: test if it's an encrypted account, if no password provided, throw because later on there's no protection from a prompt coming up
const storedConfig = await config.get()
const accountsCommand = new EntropyAccount(entropy, options.endpoint)
const accounts = accountsCommand.listAccounts(storedConfig.accounts)
cliWrite(accounts)
process.exit(0)
})
}

function entropyAccountNew (entropy: Entropy, accountCommand: Command) {
accountCommand.command('new')
.alias('new-account')
.alias('create')
.description('Create new entropy account from imported seed or from scratch. Output is JSON of form [{name, address}]')
.addOption(endpointOption())
.addOption(passwordOption())
.addOption(
new Option(
'-s, --seed',
'Seed used to create entropy account'
).default(randomAsHex(32))
)
.addOption(
new Option(
'-n, --name',
'Name of entropy account'
).makeOptionMandatory(true)
)
.addOption(
new Option(
'-pa, --path',
'Derivation path'
).default(ACCOUNTS_CONTENT.path.default)
)
.action(async (opts) => {
const storedConfig = await config.get()
const { seed, name, path, endpoint } = opts
const accountsCommand = new EntropyAccount(entropy, endpoint)

const newAccount = await accountsCommand.newAccount({ seed, name, path })
await accountsCommand.updateConfig(storedConfig, newAccount)
cliWrite({ name: newAccount.name, address: newAccount.address })
process.exit(0)
})

}
File renamed without changes.
4 changes: 2 additions & 2 deletions src/accounts/command.ts → src/account/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "./utils";
import { CreateAccountParams } from "./types";

export class AccountsCommand extends BaseCommand {
export class EntropyAccount extends BaseCommand {
constructor (entropy: Entropy, endpoint: string) {
super(entropy, endpoint, FLOW_CONTEXT)
}
Expand Down Expand Up @@ -118,4 +118,4 @@ export class AccountsCommand extends BaseCommand {
throw new Error('AccountsError: Unknown interaction action')
}
}
}
}
File renamed without changes.
File renamed without changes.
107 changes: 5 additions & 102 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,24 @@

/* NOTE: calling this file entropy.ts helps commander parse process.argv */
import { Command, Option } from 'commander'
import { randomAsHex } from '@polkadot/util-crypto'
import launchTui from './tui'
import * as config from './config'
import { EntropyTuiOptions } from './types'

import { cliSign } from './flows/sign/cli'
import { getSelectedAccount, stringify, updateConfig } from './common/utils'
import { endpointOption, currentAccountAddressOption, passwordOption } from './common/utils-cli'
import Entropy from '@entropyxyz/sdk'
import { initializeEntropy } from './common/initializeEntropy'
import { entropyAccountCommand } from './account/command'
import { EntropyAccount } from './account/main'
import { BalanceCommand } from './balance/command'
import { AccountsCommand } from './accounts/command'
import { ACCOUNTS_CONTENT } from './accounts/constants'
import { TransferCommand } from './transfer/command'

const program = new Command()
// Array of restructured commands to make it easier to migrate them to the new "flow"
const RESTRUCTURED_COMMANDS = ['balance', 'new-account']

function endpointOption (){
return new Option(
'-e, --endpoint <endpoint>',
[
'Runs entropy with the given endpoint and ignores network endpoints in config.',
'Can also be given a stored endpoint name from config eg: `entropy --endpoint test-net`.'
].join(' ')
)
.env('ENDPOINT')
.argParser(aliasOrEndpoint => {
/* see if it's a raw endpoint */
if (aliasOrEndpoint.match(/^wss?:\/\//)) return aliasOrEndpoint

/* look up endpoint-alias */
const storedConfig = config.getSync()
const endpoint = storedConfig.endpoints[aliasOrEndpoint]
if (!endpoint) throw Error('unknown endpoint alias: ' + aliasOrEndpoint)

return endpoint
})
.default('ws://testnet.entropy.xyz:9944/')
// NOTE: argParser is only run IF an option is provided, so this cannot be 'test-net'
}

function passwordOption (description?: string) {
return new Option(
'-p, --password <password>',
description || 'Password for the account'
)
}

function currentAccountAddressOption () {
const storedConfig = config.getSync()
return new Option(
'-a, --account <accountAddress>',
'Sets the current account for the session or defaults to the account stored in the config'
)
.env('ACCOUNT_ADDRESS')
.argParser(async (address) => {
if (address === storedConfig.selectedAccount) return address
// Updated selected account in config with new address from this option
const newConfigUpdates = { selectedAccount: address }
await config.set({ ...storedConfig, ...newConfigUpdates })

return address
})
.hideHelp()
.default(storedConfig.selectedAccount)
}

let entropy: Entropy

export async function loadEntropy (address: string, endpoint: string, password?: string): Promise<Entropy> {
Expand Down Expand Up @@ -122,54 +72,7 @@ program
launchTui(entropy, options)
})

/* list */
program.command('list')
.alias('ls')
.description('List all accounts. Output is JSON of form [{ name, address, verifyingKeys }]')
.addOption(endpointOption())
.action(async (options) => {
// TODO: test if it's an encrypted account, if no password provided, throw because later on there's no protection from a prompt coming up
const storedConfig = await config.get()
const accountsCommand = new AccountsCommand(entropy, options.endpoint)
const accounts = accountsCommand.listAccounts(storedConfig.accounts)
writeOut(accounts)
process.exit(0)
})

/* new account */
program.command('new-account')
.alias('new')
.description('Create new entropy account from imported seed or from scratch. Output is JSON of form [{name, address}]')
.addOption(endpointOption())
.addOption(passwordOption())
.addOption(
new Option(
'-s, --seed',
'Seed used to create entropy account'
).default(randomAsHex(32))
)
.addOption(
new Option(
'-n, --name',
'Name of entropy account'
).makeOptionMandatory(true)
)
.addOption(
new Option(
'-pa, --path',
'Derivation path'
).default(ACCOUNTS_CONTENT.path.default)
)
.action(async (opts) => {
const storedConfig = await config.get()
const { seed, name, path, endpoint } = opts
const accountsCommand = new AccountsCommand(entropy, endpoint)

const newAccount = await accountsCommand.newAccount({ seed, name, path })
await accountsCommand.updateConfig(storedConfig, newAccount)
writeOut({ name: newAccount.name, address: newAccount.address })
process.exit(0)
})
entropyAccountCommand(entropy, program)

/* register */
program.command('register')
Expand All @@ -192,7 +95,7 @@ program.command('register')
.action(async (address, opts) => {
const storedConfig = await config.get()
const { accounts } = storedConfig
const accountsCommand = new AccountsCommand(entropy, opts.endpoint)
const accountsCommand = new EntropyAccount(entropy, opts.endpoint)
writeOut('Attempting to register account with addtess: ' + address)
const accountToRegister = getSelectedAccount(accounts, address)
if (!accountToRegister) {
Expand Down
105 changes: 105 additions & 0 deletions src/common/utils-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Option } from 'commander'
import { getSelectedAccount, stringify } from './utils'
import * as config from '../config'
import Entropy from '@entropyxyz/sdk'
import { initializeEntropy } from './initializeEntropy'

export function cliWrite (result) {
const prettyResult = stringify(result)
process.stdout.write(prettyResult)
}

export function endpointOption () {
return new Option(
'-e, --endpoint <endpoint>',
[
'Runs entropy with the given endpoint and ignores network endpoints in config.',
'Can also be given a stored endpoint name from config eg: `entropy --endpoint test-net`.'
].join(' ')
)
.env('ENDPOINT')
.argParser(aliasOrEndpoint => {
/* see if it's a raw endpoint */
if (aliasOrEndpoint.match(/^wss?:\/\//)) return aliasOrEndpoint

/* look up endpoint-alias */
const storedConfig = config.getSync()
const endpoint = storedConfig.endpoints[aliasOrEndpoint]
if (!endpoint) throw Error('unknown endpoint alias: ' + aliasOrEndpoint)

return endpoint
})
.default('ws://testnet.entropy.xyz:9944/')
// NOTE: argParser is only run IF an option is provided, so this cannot be 'test-net'
}

export function passwordOption (description?: string) {
return new Option(
'-p, --password <password>',
description || 'Password for the account'
)
}

export function currentAccountAddressOption () {
const storedConfig = config.getSync()
return new Option(
'-a, --account <accountAddress>',
'Sets the current account for the session or defaults to the account stored in the config'
)
.env('ACCOUNT_ADDRESS')
.argParser(async (address) => {
if (address === storedConfig.selectedAccount) return address
// Updated selected account in config with new address from this option
const newConfigUpdates = { selectedAccount: address }
await config.set({ ...storedConfig, ...newConfigUpdates })

return address
})
.hideHelp()
.default(storedConfig.selectedAccount)
}


export function aliasOrAddressOption () {
return new Option(
'-a, --address <aliasOrAddress>',
'The alias or address of the verifying key to use for this command. Can be an alias or hex address.'
// TODO: describe default behaviour when "sessions" are introduced?
)
// QUESTION: as this is a function, this could be a viable way to set the VK?
// .default(process.env.ENTROPY_SESSION)
}

export async function loadEntropy (entropy: Entropy | undefined, address: string, endpoint: string, password?: string): Promise<Entropy> {
const storedConfig = config.getSync()
const selectedAccount = getSelectedAccount(storedConfig.accounts, address)

if (!selectedAccount) throw new Error(`AddressError: No account with address ${address}`)

// check if data is encrypted + we have a password
if (typeof selectedAccount.data === 'string' && !password) {
throw new Error('AuthError: This account requires a password, add --password <password>')
}

entropy = await initializeEntropy({ keyMaterial: selectedAccount.data, endpoint, password })

if (!entropy?.keyring?.accounts?.registration?.pair) {
throw new Error("Signer keypair is undefined or not properly initialized.")
}

return entropy
}

export async function reloadEntropy (entropy: Entropy, newAddress: string, oldAddress: string, endpoint: string): Promise<Entropy> {
try {
entropy = await loadEntropy(entropy, newAddress, endpoint)
} catch (error) {
if (error.message.includes('AddressError')) {
entropy = await loadEntropy(entropy, oldAddress, endpoint)
return entropy
}
throw error
}

return entropy
}
4 changes: 2 additions & 2 deletions src/tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { logo } from './common/ascii'
import { getSelectedAccount, print, updateConfig } from './common/utils'
import { EntropyLogger } from './common/logger'
import { BalanceCommand } from './balance/command'
import { AccountsCommand } from './accounts/command'
import { EntropyAccount } from './account/main'
import { TransferCommand } from './transfer/command'
import { loadEntropy } from './cli'

Expand Down Expand Up @@ -51,7 +51,7 @@ async function main (entropy: Entropy, choices, options, logger: EntropyLogger)
shouldInit = false
}
const balanceCommand = new BalanceCommand(entropy, options.endpoint)
const accountsCommand = new AccountsCommand(entropy, options.endpoint)
const accountsCommand = new EntropyAccount(entropy, options.endpoint)
const transferCommand = new TransferCommand(entropy, options.endpoint)

let storedConfig = await config.get()
Expand Down

0 comments on commit 1b7aa6f

Please sign in to comment.