Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NayNay] Balance for any address & transfer (using only substrate api) #315

Merged
merged 26 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
30ef58a
[NayNay] Balance for any address
rh0delta Nov 22, 2024
a20921f
updated changelog
rh0delta Nov 22, 2024
fc8b428
not needed change
rh0delta Nov 22, 2024
4bbfa6c
fn name change;
rh0delta Nov 22, 2024
3c920e9
updated sdk to new rc
rh0delta Dec 2, 2024
850fcd9
Merge branch 'dev' into naynay/any-balance
rh0delta Dec 4, 2024
d094af4
added tests
rh0delta Dec 4, 2024
62bf5e6
added failure test
rh0delta Dec 4, 2024
e3c403c
could fix failing test
rh0delta Dec 4, 2024
5d76c1d
fixed failing test
rh0delta Dec 4, 2024
4a73996
Merge branch 'dev' into naynay/any-balance
rh0delta Dec 10, 2024
89885a9
Merge branch 'dev' into naynay/any-balance
rh0delta Dec 11, 2024
b3c5ce3
Merge branch 'dev' into naynay/any-balance
rh0delta Dec 11, 2024
de949b8
Merge branch 'naynay/any-balance' of https://github.com/entropyxyz/cl…
rh0delta Dec 11, 2024
c4410e5
found another nano
rh0delta Dec 11, 2024
f710ff7
updated tests
rh0delta Dec 11, 2024
624e64f
mixmix - fixes to naynay/any-balance (#327)
mixmix Dec 13, 2024
eb38e97
Merge branch 'dev' into naynay/any-balance
rh0delta Dec 13, 2024
d1f6fa6
updated to only use generated substrate instead of entropy instance f…
rh0delta Dec 16, 2024
3e103b8
[NayNay] Only substrate
rh0delta Dec 16, 2024
afe5fa0
updated changelog
rh0delta Dec 16, 2024
6fa210a
completely removed entropy instance from full transfer flow
rh0delta Dec 18, 2024
3223ce1
updated to use class instantiation for balance, created new substrate…
rh0delta Dec 18, 2024
b2c0875
Merge branch 'naynay/any-balance' into naynay/substrate-overload
rh0delta Dec 18, 2024
811cf73
pr updates
rh0delta Dec 18, 2024
b5089be
Merge pull request #329 from entropyxyz/naynay/substrate-overload
frankiebee Dec 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil
- Shared
- updated return data displayed to user on account creation (create or import) [#311](https://github.com/entropyxyz/cli/pull/311)
- Balance now displays the number of BITS to the nearest 4 decimal places [#306](https://github.com/entropyxyz/cli/pull/306)
- removed use of entropy instance from transfer flow [#329](https://github.com/entropyxyz/cli/pull/329)

- CLI
- updated balance command to take in any address, and be able to return the balance for the inputted address [#315](https://github.com/entropyxyz/cli/pull/315)

- TUI
- updated regsitration and transfer flow to use progress loader to provide a signal to the user something is happening [#324](https://github.com/entropyxyz/cli/pull/324)
- removed use of progress bar throughout TUI [#324](https://github.com/entropyxyz/cli/pull/324)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
},
"homepage": "https://github.com/entropyxyz/cli#readme",
"dependencies": {
"@entropyxyz/sdk": "0.4.0",
"@entropyxyz/sdk": "0.4.1-0",
frankiebee marked this conversation as resolved.
Show resolved Hide resolved
"ajv": "^8.17.1",
"commander": "^12.1.0",
"env-paths": "^3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/account/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function selectAndPersistNewAccount (configPath: string, newAccount
})
}

export async function persistVerifyingKeyToAccount (configPath: string, verifyingKey: string, accountNameOrAddress: string) {
export async function persistVerifyingKeyToAccount (configPath: string, verifyingKey: string, accountNameOrAddress?: string) {
const storedConfig = await config.get(configPath)
const { accounts } = storedConfig

Expand Down
47 changes: 23 additions & 24 deletions src/balance/command.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,37 @@
import { Command } from "commander";
import Entropy from "@entropyxyz/sdk";

// @ts-expect-error
import { isValidSubstrateAddress } from '@entropyxyz/sdk/utils'
import { EntropyBalance } from "./main";
import { BalanceInfo } from "./types";
import { configOption, endpointOption, cliWrite } from "../common/utils-cli";
import { findAccountByAddressOrName, getTokenDetails, lilBitsToBits, round } from "../common/utils";
import { loadEntropyCli } from "../common/load-entropy"
import * as config from "../config";
import { EntropyConfigAccount } from "src/config/types";
import { closeSubstrate, getLoadedSubstrate } from "src/common/substrate-utils";

export function entropyBalanceCommand () {
const balanceCommand = new Command('balance')
// account can no longer be a required argument if a user wishes to
// view the balances of all accounts
balanceCommand
.description('Command to retrieive the balance of an account on the Entropy Network')
.argument('[account] <address|name>', [
.argument('[account]', [
'The address an account address whose balance you want to query.',
'Can also be the human-readable name of one of your accounts'
'Can also be the human-readable name of one of your accounts.'
].join(' '))
.option('-a, --all', 'Get balances for all admin accounts in the config')
.addOption(configOption())
.addOption(endpointOption())
.action(async (account, opts) => {
const substrate = await getLoadedSubstrate(opts.endpoint)
const BalanceService = new EntropyBalance(substrate, opts.endpoint)
const { decimals, symbol } = await getTokenDetails(substrate)
const toBits = (lilBits: number) => round(lilBitsToBits(lilBits, decimals))
const { accounts } = await config.get(opts.config)

let entropy: Entropy
if (!account && opts.all) {
const tempAddress = accounts[0].address
entropy = await loadEntropyCli({ account: tempAddress, ...opts })
} else if (account && !opts.all) {
entropy = await loadEntropyCli({ account, ...opts })
} else {
return balanceCommand.help()
}

const balanceService = new EntropyBalance(entropy, opts.endpoint)
const { decimals, symbol } = await getTokenDetails(entropy)
const toBits = (nanoBits: number) => round(lilBitsToBits(nanoBits, decimals))

if (opts.all) {
// Balances for all admin accounts
const addresses: string[] = accounts.map((acct: EntropyConfigAccount) => acct.address)
const balances = await balanceService.getBalances(addresses)
const balances = await BalanceService.getBalances(addresses)
.then((infos: BalanceInfo[]) => {
return infos.map(info => {
return {
Expand All @@ -56,13 +45,23 @@ export function entropyBalanceCommand () {
})
cliWrite(balances)
} else {
let address = findAccountByAddressOrName(accounts, account)?.address
if (!address) {
// provided account does not exist in the users config
if (isValidSubstrateAddress(account)) address = account
else {
// account is either null or not a valid substrate address
console.error(`Provided [account=${account}] is not a valid substrate address`)
process.exit(1)
}
}
// Balance for singular account
const address = findAccountByAddressOrName(accounts, account)?.address
const balance = await balanceService.getBalance(address)
const balance = await BalanceService.getAnyBalance(address)
.then(toBits)
cliWrite({ account, balance, symbol })
}

// closing substrate
await closeSubstrate(substrate)
process.exit(0)
})

Expand Down
18 changes: 11 additions & 7 deletions src/balance/interaction.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { EntropyBalance } from "./main"
import { findAccountByAddressOrName, getTokenDetails, print, round, lilBitsToBits } from "src/common/utils"

import { closeSubstrate, getLoadedSubstrate } from '../common/substrate-utils'
import { findAccountByAddressOrName, getTokenDetails, print, round, lilBitsToBits } from "../common/utils"
import { EntropyTuiOptions } from '../types'

export async function entropyBalance (entropy, opts: EntropyTuiOptions, storedConfig) {
import { EntropyBalance } from "./main"

export async function entropyBalance (opts: EntropyTuiOptions, storedConfig) {
try {
const substrate = await getLoadedSubstrate(opts.endpoint)
const BalanceService = new EntropyBalance(substrate, opts.endpoint)
// grabbing decimals from chain spec as that is the source of truth for the value
const { decimals, symbol } = await getTokenDetails(entropy)
const balanceService = new EntropyBalance(entropy, opts.endpoint)
const { decimals, symbol } = await getTokenDetails(substrate)
const address = findAccountByAddressOrName(storedConfig.accounts, storedConfig.selectedAccount)?.address
const lilBalance = await balanceService.getBalance(address)
const lilBalance = await BalanceService.getAnyBalance(address)
const balance = round(lilBitsToBits(lilBalance, decimals))
print(`Entropy Account [${storedConfig.selectedAccount}] (${address}) has a balance of: ${balance} ${symbol}`)
// closing substrate
await closeSubstrate(substrate)
} catch (error) {
console.error('There was an error retrieving balance', error)
}
Expand Down
16 changes: 7 additions & 9 deletions src/balance/main.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import Entropy from "@entropyxyz/sdk"
import { EntropyBase } from "../common/entropy-base"
import * as BalanceUtils from "./utils"
import { BalanceInfo } from "./types"
import { EntropySubstrateBase } from "src/common/entropy-substrate-base"

const FLOW_CONTEXT = 'ENTROPY-BALANCE'
export class EntropyBalance extends EntropyBase {
constructor (entropy: Entropy, endpoint: string) {
super({ entropy, endpoint, flowContext: FLOW_CONTEXT })
export class EntropyBalance extends EntropySubstrateBase {
constructor (substrate: any, endpoint: string) {
super({ substrate, endpoint, flowContext: FLOW_CONTEXT })
}

async getBalance (address: string): Promise<number> {
const accountInfo = (await this.entropy.substrate.query.system.account(address)) as any
async getAnyBalance (address: string) {
const accountInfo = (await this.substrate.query.system.account(address)) as any
const balance = parseInt(BalanceUtils.hexToBigInt(accountInfo.data.free).toString())

this.logger.log(`Current balance of ${address}: ${balance}`, EntropyBalance.name)
return balance
}

async getBalances (addresses: string[]): Promise<BalanceInfo[]> {
return Promise.all(
addresses.map(async address => {
return this.getBalance(address)
return this.getAnyBalance(address)
.then((balance: number) => {
return { address, balance }
})
Expand Down
13 changes: 13 additions & 0 deletions src/common/entropy-substrate-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { EntropyLogger } from "./logger";

export abstract class EntropySubstrateBase {
protected logger: EntropyLogger
protected substrate: any
protected endpoint: string

constructor ({ substrate, endpoint, flowContext }: { substrate: any, endpoint: string, flowContext: string }) {
this.logger = new EntropyLogger(flowContext, endpoint)
this.substrate = substrate
this.endpoint = endpoint
}
}
4 changes: 2 additions & 2 deletions src/common/load-entropy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ async function loadEntropy (opts: LoadEntropyOpts): Promise<Entropy> {
const storedConfig = await opts.config.get()
if (!storedConfig) throw Error('no config!!') // TEMP: want to see if we hit this!

let account = resolveAccount(storedConfig, opts.account)
let account = resolveAccount(storedConfig, opts.account || storedConfig.selectedAccount)
const endpoint = resolveEndpoint(storedConfig, opts.endpoint)
// NOTE: while it would be nice to parse opts --account, --endpoint with Commander
// the argParser for these Options does not have access to the --config option,
Expand Down Expand Up @@ -211,7 +211,7 @@ async function setupRegistrationSubAccount (account: EntropyConfigAccount, confi
}

const keyringCache = {}
async function loadKeyring (account: EntropyConfigAccount) {
export async function loadKeyring (account: EntropyConfigAccount) {
const { address } = account.data.admin || {}
if (!address) throw new Error('Cannot load keyring, no admin address')

Expand Down
18 changes: 18 additions & 0 deletions src/common/substrate-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @ts-expect-error
import { createSubstrate } from '@entropyxyz/sdk/utils'

export async function getLoadedSubstrate (endpoint: string) {
const substrate = createSubstrate(endpoint)
await substrate.isReadyOrError
return substrate
}

export async function closeSubstrate (substrate: any) {
try {
// closing substrate
await substrate.disconnect()
} catch (error) {
console.error('SubstrateError: Error closing connection', error)
throw error
}
}
9 changes: 4 additions & 5 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Entropy } from '@entropyxyz/sdk'
import { Buffer } from 'node:buffer'
import { homedir } from 'node:os'
import { join } from 'node:path'
Expand Down Expand Up @@ -111,11 +110,11 @@ export function absolutePath (somePath: string) {
}
}

export function formatDispatchError (entropy: Entropy, dispatchError) {
export function formatDispatchError (substrate: any, dispatchError) {
let msg: string
if (dispatchError.isModule) {
// for module errors, we have the section indexed, lookup
const decoded = entropy.substrate.registry.findMetaError(
const decoded = substrate.registry.findMetaError(
dispatchError.asModule
)
const { docs, name, section } = decoded
Expand Down Expand Up @@ -147,9 +146,9 @@ export async function jumpStartNetwork (entropy, endpoint): Promise<any> {

// caching details to reduce number of calls made to the rpc endpoint
let tokenDetails: TokenDetails
export async function getTokenDetails (entropy): Promise<TokenDetails> {
export async function getTokenDetails (substrate): Promise<TokenDetails> {
if (tokenDetails) return tokenDetails
const chainProperties = await entropy.substrate.rpc.system.properties()
const chainProperties = await substrate.rpc.system.properties()
const decimals = chainProperties.tokenDecimals.toHuman()[0]
const symbol = chainProperties.tokenSymbol.toHuman()[0]
tokenDetails = { decimals: parseInt(decimals), symbol }
Expand Down
8 changes: 4 additions & 4 deletions src/faucet/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export async function entropyFaucet (entropy: Entropy, opts: EntropyTuiOptions,
throw new Error("Keys are undefined")
}

const { decimals } = await getTokenDetails(entropy)
const { decimals } = await getTokenDetails(entropy.substrate)
const amount = bitsToLilBits(2, decimals)
const faucetService = new EntropyFaucet(entropy, opts.endpoint)
const verifyingKeys = await faucetService.getAllFaucetVerifyingKeys()
// @ts-expect-error
return sendMoneyFromRandomFaucet(entropy, options.endpoint, verifyingKeys, amount.toString(), logger)
// @ts-expect-error verifyingKeys
return sendMoneyFromRandomFaucet(entropy, opts.endpoint, verifyingKeys, amount.toString(), logger)
}

// Method that takes in the initial list of verifying keys (to avoid multiple calls to the rpc) and recursively retries each faucet until
Expand All @@ -41,7 +41,7 @@ async function sendMoneyFromRandomFaucet (entropy: Entropy, endpoint: string, ve
faucetSpinner.start()
}
const faucetService = new EntropyFaucet(entropy, endpoint)
const { decimals, symbol } = await getTokenDetails(entropy)
const { decimals, symbol } = await getTokenDetails(entropy.substrate)
const selectedAccountAddress = entropy.keyring.accounts.registration.address
let chosenVerifyingKey: string
try {
Expand Down
6 changes: 3 additions & 3 deletions src/faucet/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class EntropyFaucet extends EntropyBase {
// status would still be set, but in the case of error we can shortcut
// to just check it (so an error would indicate InBlock or Finalized)
if (dispatchError) {
const error = formatDispatchError(this.entropy, dispatchError)
const error = formatDispatchError(this.entropy.substrate, dispatchError)
return reject(error)
}
if (status.isFinalized) resolve(status)
Expand Down Expand Up @@ -71,11 +71,11 @@ export class EntropyFaucet extends EntropyBase {
faucetProgramPointer = FAUCET_PROGRAM_POINTER
}: SendMoneyParams
): Promise<any> {
const balanceService = new EntropyBalance(this.entropy, this.endpoint)
const programService = new EntropyProgram(this.entropy, this.endpoint)
const BalanceService = new EntropyBalance(this.entropy.substrate, this.endpoint)

// check balance of faucet address
const balance = await balanceService.getBalance(faucetAddress)
const balance = await BalanceService.getAnyBalance(faucetAddress)
if (balance <= 0) throw new Error('FundsError: Faucet Account does not have funds')

// check verifying key has ONLY the exact program installed
Expand Down
20 changes: 13 additions & 7 deletions src/transfer/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Command } from "commander"

import { EntropyTransfer } from "./main"
import { accountOption, configOption, endpointOption, cliWrite } from "../common/utils-cli"
import { loadEntropyCli } from "../common/load-entropy"
import { getTokenDetails } from "../common/utils"
import { loadKeyring } from "../common/load-entropy"
import { findAccountByAddressOrName, getTokenDetails } from "../common/utils"
import * as config from "../config";
import { closeSubstrate, getLoadedSubstrate } from "src/common/substrate-utils"

export function entropyTransferCommand () {
const transferCommand = new Command('transfer')
Expand All @@ -16,18 +18,22 @@ export function entropyTransferCommand () {
.addOption(endpointOption())
.action(async (destination, amount, opts) => {
// TODO: destination as <name|address> ?
const entropy = await loadEntropyCli(opts)
const transferService = new EntropyTransfer(entropy, opts.endpoint)
const { symbol } = await getTokenDetails(entropy)
const { accounts, selectedAccount } = await config.get(opts.config)
const substrate = await getLoadedSubstrate(opts.endpoint)
const account = findAccountByAddressOrName(accounts, opts.account || selectedAccount)
const loadedKeyring = await loadKeyring(account)
const transferService = new EntropyTransfer(substrate, opts.endpoint)
const { symbol } = await getTokenDetails(substrate)

await transferService.transfer(destination, amount)
await transferService.transfer(loadedKeyring.accounts.registration.pair, destination, amount)

cliWrite({
source: opts.account,
source: loadedKeyring.accounts.registration.address,
destination,
amount,
symbol
})
await closeSubstrate(substrate)
process.exit(0)
})
return transferCommand
Expand Down
18 changes: 13 additions & 5 deletions src/transfer/interaction.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import inquirer from "inquirer"
import yoctoSpinner from "yocto-spinner"

import { getTokenDetails, print } from "../common/utils"
import { findAccountByAddressOrName, getTokenDetails, print } from "../common/utils"
import { EntropyTransfer } from "./main"
import { transferInputQuestions } from "./utils"

import { EntropyTuiOptions } from '../types'
import { EntropyConfig } from "src/config/types"
import { closeSubstrate, getLoadedSubstrate } from "src/common/substrate-utils"
import { loadKeyring } from "src/common/load-entropy"

const transferSpinner = yoctoSpinner()
const SPINNER_TEXT = 'Transferring funds...'

export async function entropyTransfer (entropy, opts: EntropyTuiOptions) {
export async function entropyTransfer (opts: EntropyTuiOptions, storedConfig: EntropyConfig) {
transferSpinner.text = SPINNER_TEXT
if (transferSpinner.isSpinning) transferSpinner.stop()
try {
const { symbol } = await getTokenDetails(entropy)
const transferService = new EntropyTransfer(entropy, opts.endpoint)
const substrate = await getLoadedSubstrate(opts.endpoint)
const currentAccount = findAccountByAddressOrName(storedConfig.accounts, opts.account || storedConfig.selectedAccount)
const loadedKeyring = await loadKeyring(currentAccount)
const { symbol } = await getTokenDetails(substrate)
const transferService = new EntropyTransfer(substrate, opts.endpoint)
const { amount, recipientAddress } = await inquirer.prompt(transferInputQuestions)
if (!transferSpinner.isSpinning) transferSpinner.start()
await transferService.transfer(recipientAddress, amount)
await transferService.transfer(loadedKeyring.accounts.registration.pair, recipientAddress, amount)
await closeSubstrate(opts.endpoint)
if (transferSpinner.isSpinning) transferSpinner.stop()
print('')
print(`Transaction successful: Sent ${amount} ${symbol} to ${recipientAddress}`)
print('')
print('Press enter to return to main menu')
} catch (error) {
transferSpinner.text = 'Transfer failed...'
await closeSubstrate(opts.endpoint)
if (transferSpinner.isSpinning) transferSpinner.stop()
print.error('TransferError:', error.message);
}
Expand Down
Loading
Loading