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] Only substrate #329

Merged
merged 5 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ 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)
Expand Down
2 changes: 1 addition & 1 deletion src/account/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function persistVerifyingKeyToAccount (configPath: string, verifyin
const storedConfig = await config.get(configPath)
const { accounts } = storedConfig

const account = findAccountByAddressOrName(accounts, accountNameOrAddress || storedConfig.selectedAccount)
const account = findAccountByAddressOrName(accounts, accountNameOrAddress)
if (!account) throw Error(`Unable to persist verifyingKey "${verifyingKey}" to unknown account "${accountNameOrAddress}"`)

// persist to config, set selectedAccount
Expand Down
2 changes: 1 addition & 1 deletion src/balance/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { EntropySubstrateBase } from "src/common/entropy-substrate-base"

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

Expand Down
2 changes: 1 addition & 1 deletion src/common/load-entropy.ts
Original file line number Diff line number Diff line change
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
10 changes: 7 additions & 3 deletions src/common/substrate-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ export async function getLoadedSubstrate (endpoint: string) {
}

export async function closeSubstrate (substrate: any) {
// closing substrate
return await substrate.disconnect()
.catch(err => console.error('Error closing connection', err.message))
try {
// closing substrate
await substrate.disconnect()
} catch (error) {
console.error('SubstrateError: Error closing connection', error)
throw error
}
}
5 changes: 2 additions & 3 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
2 changes: 1 addition & 1 deletion 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
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.substrate)
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: entropy.keyring.accounts.registration.address,
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.substrate)
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
26 changes: 15 additions & 11 deletions src/transfer/main.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import Entropy from "@entropyxyz/sdk";
// @ts-ignore
import { Pair } from '@entropyxyz/sdk/keys'

import { EntropyBase } from "../common/entropy-base";
import { bitsToLilBits, formatDispatchError, getTokenDetails } from "../common/utils";

import { TransferOptions } from "./types";
import { EntropySubstrateBase } from 'src/common/entropy-substrate-base';

const FLOW_CONTEXT = 'ENTROPY_TRANSFER'

export class EntropyTransfer extends EntropyBase {
constructor (entropy: Entropy, endpoint: string) {
super({ entropy, endpoint, flowContext: FLOW_CONTEXT })
export class EntropyTransfer extends EntropySubstrateBase {
constructor (substrate: any, endpoint: string) {
super({ substrate, endpoint, flowContext: FLOW_CONTEXT })
}

// NOTE: a more accessible function which handles
// - setting `from`
// - converting `amount` (string => BigInt)
// - progress callbacks (optional)

async transfer (toAddress: string, amountInBits: string) {
const { decimals } = await getTokenDetails(this.entropy.substrate)
async transfer (from: Pair, toAddress: string, amountInBits: string) {
const { decimals } = await getTokenDetails(this.substrate)
const lilBits = bitsToLilBits(Number(amountInBits), decimals)

return this.rawTransfer({
from: this.entropy.keyring.accounts.registration.pair,
const transferStatus = await this.rawTransfer({
from,
to: toAddress,
lilBits
})

return transferStatus
}

private async rawTransfer (payload: TransferOptions): Promise<any> {
Expand All @@ -33,12 +37,12 @@ export class EntropyTransfer extends EntropyBase {
return new Promise((resolve, reject) => {
// WARN: await signAndSend is dangerous as it does not resolve
// after transaction is complete :melt:
this.entropy.substrate.tx.balances
this.substrate.tx.balances
.transferAllowDeath(to, lilBits)
// @ts-ignore
.signAndSend(from, ({ status, dispatchError }) => {
if (dispatchError) {
const error = formatDispatchError(this.entropy, dispatchError)
const error = formatDispatchError(this.substrate, dispatchError)
this.logger.error('There was an issue sending this transfer', error)
return reject(error)
}
Expand Down
2 changes: 1 addition & 1 deletion src/tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async function main (entropy: Entropy, choices: string[], opts: EntropyTuiOption
break
}
case 'Transfer': {
await entropyTransfer(entropy, opts)
await entropyTransfer(opts, storedConfig)
.catch(err => console.error('There was an error sending the transfer', err))
break
}
Expand Down
4 changes: 2 additions & 2 deletions tests/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ const endpoint = 'ws://127.0.0.1:9944'

async function fundAccount (t, entropy: Entropy) {
const { entropy: charlie } = await setupTest(t, { seed: charlieStashSeed })
const transfer = new EntropyTransfer(charlie, endpoint)
const transfer = new EntropyTransfer(charlie.substrate, endpoint)

await transfer.transfer(entropy.keyring.accounts.registration.address, "1000")
await transfer.transfer(charlie.keyring.accounts.registration.pair, entropy.keyring.accounts.registration.address, "1000")
}


Expand Down
4 changes: 2 additions & 2 deletions tests/faucet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function setupAndFundFaucet (t) {
const { run, entropy, endpoint } = await setupTest(t, { seed: eveSeed })

const account = new EntropyAccount(entropy, endpoint)
const transfer = new EntropyTransfer(entropy, endpoint)
const transfer = new EntropyTransfer(entropy.substrate, endpoint)
const faucet = new EntropyFaucet(entropy, endpoint)

// Deploy faucet program
Expand Down Expand Up @@ -65,7 +65,7 @@ async function setupAndFundFaucet (t) {
const verifyingKeys = await faucet.getAllFaucetVerifyingKeys(eveAddress)
// @ts-expect-error
const { chosenVerifyingKey, faucetAddress } = faucet.getRandomFaucet([], verifyingKeys)
await run('Transfer funds to faucet address', transfer.transfer(faucetAddress, "1000"))
await run('Transfer funds to faucet address', transfer.transfer(entropy.keyring.accounts.registration.pair, faucetAddress, "1000"))

return { faucetProgramPointer, chosenVerifyingKey, faucetAddress }
}
Expand Down
35 changes: 22 additions & 13 deletions tests/transfer.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import test from 'tape'
import { randomAsHex } from '@polkadot/util-crypto'
import Keyring from '@entropyxyz/sdk/keys';

import { lilBitsPerBits } from "../src/common/utils";
import { EntropyTransfer } from '../src/transfer/main'
import { EntropyAccount } from '../src/account/main'
import { EntropyBalance } from '../src/balance/main'
import { promiseRunner, setupTest } from './testing-utils'
import { closeSubstrate, getLoadedSubstrate } from '../src/common/substrate-utils'
import { lilBitsPerBits } from "../src/common/utils"
import { EntropyTransfer } from '../src/transfer/main'

import { setupTest } from './testing-utils'
import { charlieStashAddress, charlieStashSeed, DEFAULT_TOKEN_DECIMALS } from './testing-utils/constants.mjs'

test('Transfer', async (t) => {
/* Setup */
const { run, entropy: charlie, endpoint }= await setupTest(t, { seed: charlieStashSeed })
const { entropy: naynay } = await setupTest(t)

const transferService = new EntropyTransfer(charlie, endpoint)
const BalanceService = new EntropyBalance(charlie.substrate, endpoint)

const naynayAddress = naynay.keyring.accounts.registration.address

const testAccountSeed = randomAsHex(32)
const testAccountName = 'Test Account'
const naynay = await EntropyAccount.import({ name: testAccountName, seed: testAccountSeed })

// setuptest still needed to run here to start up wasm and get the config ready
const { run, endpoint }= await setupTest(t, { seed: charlieStashSeed })
const substrate = await run('load substrate', getLoadedSubstrate(endpoint))
const transferService = new EntropyTransfer(substrate, endpoint)
const BalanceService = new EntropyBalance(substrate, endpoint)

const naynayAddress = naynay.address
const charlieKeyring = new Keyring({ seed: charlieStashSeed, path: '', debug: true })
// Check initial balances
let naynayBalance = await run(
'getBalance (naynay)',
Expand All @@ -33,7 +42,7 @@ test('Transfer', async (t) => {
const inputAmount = "1.5"
await run(
'transfer',
transferService.transfer(naynayAddress, inputAmount)
transferService.transfer(charlieKeyring.accounts.registration.pair, naynayAddress, inputAmount)
)

// Re-Check balance
Expand All @@ -43,6 +52,6 @@ test('Transfer', async (t) => {
)
const expected = Number(inputAmount) * lilBitsPerBits(DEFAULT_TOKEN_DECIMALS)
t.equal(naynayBalance, expected, 'naynay is rolling in it!')

await run('closeSubstrate', closeSubstrate(substrate))
t.end()
})
Loading