From 0db7c0137fe8957ffa447fd587b05584ac6972b9 Mon Sep 17 00:00:00 2001 From: prxgr4mm3r Date: Wed, 15 Nov 2023 16:41:02 +0200 Subject: [PATCH 01/10] Add faucet and balance commands --- src/commands/account/balance.ts | 40 +++++++++++++++++++++++++++ src/commands/account/faucet.ts | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/commands/account/balance.ts create mode 100644 src/commands/account/faucet.ts diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts new file mode 100644 index 00000000..02900483 --- /dev/null +++ b/src/commands/account/balance.ts @@ -0,0 +1,40 @@ +import { Args, Flags } from "@oclif/core"; +import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; +import { resolveNetworkUrl } from "../../lib/index.js"; +import { AccountData } from "../../types/index.js"; +import { SwankyCommand } from "../../lib/swankyCommand.js"; +import { ConfigError } from "../../lib/errors.js"; + +export class Balance extends SwankyCommand { + static description = "Balance of an account"; + + static args = { + alias: Args.string({ + name: "alias", + required: true, + description: "Alias of account to be used", + }), + }; + async run(): Promise { + const { args } = await this.parse(Balance); + + const accountData = this.swankyConfig.accounts.find( + (account: AccountData) => account.alias === args.alias + ); + if (!accountData) { + throw new ConfigError("Provided account alias not found in swanky.config.json"); + } + + const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); + + const wsProvider = new WsProvider(networkUrl); + const api = await ApiPromise.create({ provider: wsProvider }); + await api.isReady; + + const balance = (await api.query.system.account(accountData.address)).data.free.toBigInt(); + + this.log(`Account balance now is: ${balance}`); + + await wsProvider.disconnect(); + } +} diff --git a/src/commands/account/faucet.ts b/src/commands/account/faucet.ts new file mode 100644 index 00000000..a8408175 --- /dev/null +++ b/src/commands/account/faucet.ts @@ -0,0 +1,49 @@ +import { Args, Flags } from "@oclif/core"; +import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; +import { resolveNetworkUrl } from "../../lib/index.js"; +import { AccountData } from "../../types/index.js"; +import { SwankyCommand } from "../../lib/swankyCommand.js"; +import { ConfigError } from "../../lib/errors.js"; + +export class Faucet extends SwankyCommand { + static description = "Faucet some tokens to an account"; + + static aliases = [`account:faucet`]; + + static args = { + alias: Args.string({ + name: "alias", + required: true, + description: "Alias of account to be used", + }), + }; + async run(): Promise { + const { args } = await this.parse(Faucet); + + const accountData = this.swankyConfig.accounts.find( + (account: AccountData) => account.alias === args.alias + ); + if (!accountData) { + throw new ConfigError("Provided account alias not found in swanky.config.json"); + } + + const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); + + const wsProvider = new WsProvider(networkUrl); + const api = await ApiPromise.create({ provider: wsProvider }); + await api.isReady; + + const keyring = new Keyring({ type: 'sr25519' }); + + const alicePair = keyring.addFromUri('//Alice'); + + await this.spinner.runCommand(async () => { + await api.tx.balances + .transfer(accountData.address, BigInt(100000000000000000000n)) + .signAndSend(alicePair); + + await wsProvider.disconnect(); + }, `Fauceting 100000000000000000000 units to ${args.alias}`); + + } +} From 84ab0bdcc9db82a14dad798f6bd608c2bd4c0167 Mon Sep 17 00:00:00 2001 From: prxgr4mm3r Date: Wed, 15 Nov 2023 17:07:43 +0200 Subject: [PATCH 02/10] Add automatically transfer an amount of tokens from Alice account when account is created --- src/commands/account/create.ts | 16 ++++++++++++++-- src/commands/account/faucet.ts | 26 +++++++++----------------- src/lib/substrate-api.ts | 16 +++++++++++++--- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/commands/account/create.ts b/src/commands/account/create.ts index 5c4fc4c4..65f46ff1 100644 --- a/src/commands/account/create.ts +++ b/src/commands/account/create.ts @@ -1,6 +1,6 @@ import { Flags } from "@oclif/core"; import chalk from "chalk"; -import { ChainAccount, encrypt } from "../../lib/index.js"; +import { ChainAccount, ChainApi, encrypt, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import inquirer from "inquirer"; import { SwankyCommand } from "../../lib/swankyCommand.js"; @@ -35,7 +35,7 @@ export class CreateAccount extends SwankyCommand { ); } - let tmpMnemonic = ""; + let tmpMnemonic: string; if (flags.generate) { tmpMnemonic = ChainAccount.generate(); console.log( @@ -84,5 +84,17 @@ export class CreateAccount extends SwankyCommand { accountData.alias )} stored to config` ); + + const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); + + const api = (await this.spinner.runCommand(async () => { + const api = await ChainApi.create(networkUrl); + await api.start(); + return api; + }, "Connecting to node")) as ChainApi; + + + await this.spinner.runCommand( async () => {await api.faucet(accountData)} + , `Fauceting 100000000000000000000 units to ${accountData.alias}`); } } diff --git a/src/commands/account/faucet.ts b/src/commands/account/faucet.ts index a8408175..c87c5b81 100644 --- a/src/commands/account/faucet.ts +++ b/src/commands/account/faucet.ts @@ -1,6 +1,5 @@ -import { Args, Flags } from "@oclif/core"; -import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; -import { resolveNetworkUrl } from "../../lib/index.js"; +import { Args } from "@oclif/core"; +import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; import { ConfigError } from "../../lib/errors.js"; @@ -29,21 +28,14 @@ export class Faucet extends SwankyCommand { const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); - const wsProvider = new WsProvider(networkUrl); - const api = await ApiPromise.create({ provider: wsProvider }); - await api.isReady; + const api = (await this.spinner.runCommand(async () => { + const api = await ChainApi.create(networkUrl); + await api.start(); + return api; + }, "Connecting to node")) as ChainApi; - const keyring = new Keyring({ type: 'sr25519' }); - - const alicePair = keyring.addFromUri('//Alice'); - - await this.spinner.runCommand(async () => { - await api.tx.balances - .transfer(accountData.address, BigInt(100000000000000000000n)) - .signAndSend(alicePair); - - await wsProvider.disconnect(); - }, `Fauceting 100000000000000000000 units to ${args.alias}`); + await this.spinner.runCommand( async () => {await api.faucet(accountData)} + , `Fauceting 100000000000000000000 units to ${args.alias}`); } } diff --git a/src/lib/substrate-api.ts b/src/lib/substrate-api.ts index 343d35f4..72da18c5 100644 --- a/src/lib/substrate-api.ts +++ b/src/lib/substrate-api.ts @@ -1,5 +1,5 @@ import { ApiPromise } from "@polkadot/api/promise"; -import { WsProvider } from "@polkadot/api"; +import { Keyring, WsProvider } from "@polkadot/api"; import { SignerOptions } from "@polkadot/api/types"; import { Codec, ITuple } from "@polkadot/types-codec/types"; import { ISubmittableResult } from "@polkadot/types/types"; @@ -7,7 +7,7 @@ import { TypeRegistry } from "@polkadot/types"; import { DispatchError, BlockHash } from "@polkadot/types/interfaces"; import { ChainAccount } from "./account.js"; import BN from "bn.js"; -import { ChainProperty, ExtrinsicPayload } from "../types/index.js"; +import { ChainProperty, ExtrinsicPayload, AccountData } from "../types/index.js"; import { KeyringPair } from "@polkadot/keyring/types"; import { Abi, CodePromise } from "@polkadot/api-contract"; @@ -210,7 +210,6 @@ export class ChainApi { if (handler) handler(result); }); } - public async deploy( abi: Abi, wasm: Buffer, @@ -247,4 +246,15 @@ export class ChainApi { }); }); } + + public async faucet(accountData: AccountData) { + const keyring = new Keyring({ type: "sr25519" }); + const alicePair = keyring.addFromUri("//Alice"); + + await this._api.tx.balances + .transfer(accountData.address, BigInt(100000000000000000000n)) + .signAndSend(alicePair); + + await this._provider.disconnect(); + } } From 580a90a07464f76e5b1fe5b404476b90a15bccd8 Mon Sep 17 00:00:00 2001 From: prxgr4mm3r Date: Wed, 15 Nov 2023 17:09:42 +0200 Subject: [PATCH 03/10] Clean up imports --- src/commands/account/balance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index 02900483..5bad5f67 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -1,5 +1,5 @@ -import { Args, Flags } from "@oclif/core"; -import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; +import { Args } from "@oclif/core"; +import { ApiPromise, WsProvider } from "@polkadot/api"; import { resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; From 48088de296ef9c382775bcdb50136fa1c6d5133e Mon Sep 17 00:00:00 2001 From: prxgr4mm3r Date: Thu, 25 Jan 2024 15:27:08 +0200 Subject: [PATCH 04/10] Fixes --- src/commands/account/balance.ts | 27 +++++++++++++++++---------- src/commands/account/create.ts | 17 +++++++++++++++-- src/commands/account/faucet.ts | 21 ++++++++++++++++----- src/commands/init/index.ts | 9 +++++---- src/lib/consts.ts | 5 +++++ src/lib/substrate-api.ts | 27 +++++++++++++++++++++------ 6 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index 5bad5f67..b64dcf82 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -1,9 +1,10 @@ import { Args } from "@oclif/core"; import { ApiPromise, WsProvider } from "@polkadot/api"; -import { resolveNetworkUrl } from "../../lib/index.js"; +import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; import { ConfigError } from "../../lib/errors.js"; +import { formatBalance } from "@polkadot/util"; export class Balance extends SwankyCommand { static description = "Balance of an account"; @@ -27,14 +28,20 @@ export class Balance extends SwankyCommand { const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); - const wsProvider = new WsProvider(networkUrl); - const api = await ApiPromise.create({ provider: wsProvider }); - await api.isReady; - - const balance = (await api.query.system.account(accountData.address)).data.free.toBigInt(); - - this.log(`Account balance now is: ${balance}`); - - await wsProvider.disconnect(); + const api = (await this.spinner.runCommand(async () => { + const api = await ChainApi.create(networkUrl); + await api.start(); + return api.apiInst; + }, "Connecting to node")) as ApiPromise; + + const {nonce, data: balance} = (await api.query.system.account(accountData.address)); + const decimals = api.registry.chainDecimals[0]; + console.log('Raw balance:', balance.free.toBigInt()) + formatBalance.setDefaults({ unit: 'UNIT', decimals }); + const defaults = formatBalance.getDefaults(); + const free = formatBalance(balance.free, { withSiFull: true }); + const reserved = formatBalance(balance.reserved, { withSiFull: true }); + console.log('Formatted balance:', `{"free": "${free}", "unit": "${defaults.unit}", "reserved": "${reserved}", "nonce": "${nonce.toHuman()}"}`); + await api.disconnect(); } } diff --git a/src/commands/account/create.ts b/src/commands/account/create.ts index 65f46ff1..55e31248 100644 --- a/src/commands/account/create.ts +++ b/src/commands/account/create.ts @@ -4,6 +4,8 @@ import { ChainAccount, ChainApi, encrypt, resolveNetworkUrl } from "../../lib/in import { AccountData } from "../../types/index.js"; import inquirer from "inquirer"; import { SwankyCommand } from "../../lib/swankyCommand.js"; +import { ApiError } from "../../lib/errors.js"; +import { LOCAL_FAUCET_AMOUNT } from "../../lib/consts.js"; export class CreateAccount extends SwankyCommand { static description = "Create a new dev account in config"; @@ -94,7 +96,18 @@ export class CreateAccount extends SwankyCommand { }, "Connecting to node")) as ChainApi; - await this.spinner.runCommand( async () => {await api.faucet(accountData)} - , `Fauceting 100000000000000000000 units to ${accountData.alias}`); + await this.spinner.runCommand( + async () => { + try { + await api.faucet(accountData); + } catch (cause) { + throw new ApiError("Error transferring tokens from faucet account", { cause }); + } + }, + `Transferring ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, + `Transferred ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, + `Failed to transfer ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, + true + ); } } diff --git a/src/commands/account/faucet.ts b/src/commands/account/faucet.ts index c87c5b81..ced47748 100644 --- a/src/commands/account/faucet.ts +++ b/src/commands/account/faucet.ts @@ -2,10 +2,11 @@ import { Args } from "@oclif/core"; import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; -import { ConfigError } from "../../lib/errors.js"; +import { ApiError, ConfigError } from "../../lib/errors.js"; +import { LOCAL_FAUCET_AMOUNT } from "../../lib/consts.js"; export class Faucet extends SwankyCommand { - static description = "Faucet some tokens to an account"; + static description = "Transfer some tokens from faucet to an account"; static aliases = [`account:faucet`]; @@ -34,8 +35,18 @@ export class Faucet extends SwankyCommand { return api; }, "Connecting to node")) as ChainApi; - - await this.spinner.runCommand( async () => {await api.faucet(accountData)} - , `Fauceting 100000000000000000000 units to ${args.alias}`); + await this.spinner.runCommand( + async () => { + try { + await api.faucet(accountData); + } catch (cause) { + throw new ApiError("Error transferring tokens from faucet account", { cause }); + } + }, + `Transferring ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${args.alias}`, + `Transferred ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${args.alias}`, + `Failed to transfer ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${args.alias}`, + true + ); } } diff --git a/src/commands/init/index.ts b/src/commands/init/index.ts index 45177372..27390b06 100644 --- a/src/commands/init/index.ts +++ b/src/commands/init/index.ts @@ -19,6 +19,7 @@ import { getTemplates, } from "../../lib/index.js"; import { + ALICE_URI, BOB_URI, DEFAULT_ASTAR_NETWORK_URL, DEFAULT_NETWORK_URL, DEFAULT_SHIBUYA_NETWORK_URL, @@ -174,15 +175,15 @@ export class Init extends SwankyCommand { this.configBuilder.accounts = [ { alias: "alice", - mnemonic: "//Alice", + mnemonic: ALICE_URI, isDev: true, - address: new ChainAccount("//Alice").pair.address, + address: new ChainAccount(ALICE_URI).pair.address, }, { alias: "bob", - mnemonic: "//Bob", + mnemonic: BOB_URI, isDev: true, - address: new ChainAccount("//Bob").pair.address, + address: new ChainAccount(BOB_URI).pair.address, }, ]; diff --git a/src/lib/consts.ts b/src/lib/consts.ts index d48b2b67..3f72917c 100644 --- a/src/lib/consts.ts +++ b/src/lib/consts.ts @@ -5,3 +5,8 @@ export const DEFAULT_SHIBUYA_NETWORK_URL = "wss://shibuya.public.blastapi.io"; export const ARTIFACTS_PATH = "artifacts"; export const TYPED_CONTRACTS_PATH = "typedContracts"; + +export const LOCAL_FAUCET_AMOUNT = 100; +export const KEYRING_TYPE = "sr25519"; +export const ALICE_URI = "//Alice"; +export const BOB_URI = "//Bob"; \ No newline at end of file diff --git a/src/lib/substrate-api.ts b/src/lib/substrate-api.ts index 72da18c5..8ec17993 100644 --- a/src/lib/substrate-api.ts +++ b/src/lib/substrate-api.ts @@ -12,6 +12,8 @@ import { ChainProperty, ExtrinsicPayload, AccountData } from "../types/index.js" import { KeyringPair } from "@polkadot/keyring/types"; import { Abi, CodePromise } from "@polkadot/api-contract"; import { ApiError, UnknownError } from "./errors.js"; +import { ALICE_URI, LOCAL_FAUCET_AMOUNT } from "./consts.js"; +import { BN_TEN } from "@polkadot/util"; export type AbiType = Abi; // const AUTO_CONNECT_MS = 10_000; // [ms] @@ -247,14 +249,27 @@ export class ChainApi { }); } - public async faucet(accountData: AccountData) { + public async faucet(accountData: AccountData) : Promise { const keyring = new Keyring({ type: "sr25519" }); - const alicePair = keyring.addFromUri("//Alice"); + const alicePair = keyring.addFromUri(ALICE_URI); - await this._api.tx.balances - .transfer(accountData.address, BigInt(100000000000000000000n)) - .signAndSend(alicePair); + const chainDecimals = this._api.registry.chainDecimals[0]; + const amount = new BN(LOCAL_FAUCET_AMOUNT).mul(BN_TEN.pow(new BN(chainDecimals))); - await this._provider.disconnect(); + const tx = this._api.tx.balances.transfer(accountData.address, amount); + + return new Promise((resolve, reject) => { + this.signAndSend(alicePair, tx, {}, ({ status, events }) => { + if (status.isInBlock || status.isFinalized) { + const transferEvent = events.find(({ event }) => event?.method === "Transfer"); + if (!transferEvent) { + reject(); + return; + } + resolve(); + this._provider.disconnect(); + } + }).catch(error => reject(error)); + }); } } From 7ffbd02541d39cd8171fc7f342d00ae559112bc0 Mon Sep 17 00:00:00 2001 From: prxgr4mm3r Date: Thu, 25 Jan 2024 15:29:01 +0200 Subject: [PATCH 05/10] Clean imports --- src/commands/account/balance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index b64dcf82..3b669dfa 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -1,5 +1,5 @@ import { Args } from "@oclif/core"; -import { ApiPromise, WsProvider } from "@polkadot/api"; +import { ApiPromise } from "@polkadot/api"; import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; From 050692559b678de5295b5b60e4dcca6290e48a6e Mon Sep 17 00:00:00 2001 From: Igor Papandinas Date: Fri, 26 Jan 2024 08:38:03 +0100 Subject: [PATCH 06/10] feat: KEYPAIR_TYPE constant created --- src/lib/account.ts | 3 ++- src/lib/consts.ts | 2 +- src/lib/substrate-api.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/account.ts b/src/lib/account.ts index 04897451..dc529cb0 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -2,6 +2,7 @@ import { mnemonicGenerate } from "@polkadot/util-crypto"; import { Keyring } from "@polkadot/keyring"; import { KeyringPair } from "@polkadot/keyring/types"; import { ChainProperty, KeypairType } from "../types/index.js"; +import { KEYPAIR_TYPE } from "./consts.js"; interface IChainAccount { pair: KeyringPair; @@ -17,7 +18,7 @@ export class ChainAccount implements IChainAccount { return mnemonicGenerate(); } - constructor(mnemonic: string, type: KeypairType = "sr25519") { + constructor(mnemonic: string, type: KeypairType = KEYPAIR_TYPE) { this._keyringType = type; this._keyring = new Keyring({ type: type }); this._mnemonic = mnemonic; diff --git a/src/lib/consts.ts b/src/lib/consts.ts index 3f72917c..d974b65c 100644 --- a/src/lib/consts.ts +++ b/src/lib/consts.ts @@ -7,6 +7,6 @@ export const ARTIFACTS_PATH = "artifacts"; export const TYPED_CONTRACTS_PATH = "typedContracts"; export const LOCAL_FAUCET_AMOUNT = 100; -export const KEYRING_TYPE = "sr25519"; +export const KEYPAIR_TYPE = "sr25519"; export const ALICE_URI = "//Alice"; export const BOB_URI = "//Bob"; \ No newline at end of file diff --git a/src/lib/substrate-api.ts b/src/lib/substrate-api.ts index 8ec17993..665e866f 100644 --- a/src/lib/substrate-api.ts +++ b/src/lib/substrate-api.ts @@ -12,7 +12,7 @@ import { ChainProperty, ExtrinsicPayload, AccountData } from "../types/index.js" import { KeyringPair } from "@polkadot/keyring/types"; import { Abi, CodePromise } from "@polkadot/api-contract"; import { ApiError, UnknownError } from "./errors.js"; -import { ALICE_URI, LOCAL_FAUCET_AMOUNT } from "./consts.js"; +import { ALICE_URI, KEYPAIR_TYPE, LOCAL_FAUCET_AMOUNT } from "./consts.js"; import { BN_TEN } from "@polkadot/util"; export type AbiType = Abi; @@ -250,7 +250,7 @@ export class ChainApi { } public async faucet(accountData: AccountData) : Promise { - const keyring = new Keyring({ type: "sr25519" }); + const keyring = new Keyring({ type: KEYPAIR_TYPE }); const alicePair = keyring.addFromUri(ALICE_URI); const chainDecimals = this._api.registry.chainDecimals[0]; From 460b6b4ec36f4dc966f0b26f24cbdf6520c8a611 Mon Sep 17 00:00:00 2001 From: Igor Papandinas Date: Fri, 26 Jan 2024 09:15:16 +0100 Subject: [PATCH 07/10] feat: Log total and transferrable account balances --- src/commands/account/balance.ts | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index 3b669dfa..1c43b0e3 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -1,5 +1,6 @@ import { Args } from "@oclif/core"; import { ApiPromise } from "@polkadot/api"; +import type { AccountInfo, Balance as BalanceType } from "@polkadot/types/interfaces"; import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; @@ -34,14 +35,31 @@ export class Balance extends SwankyCommand { return api.apiInst; }, "Connecting to node")) as ApiPromise; - const {nonce, data: balance} = (await api.query.system.account(accountData.address)); const decimals = api.registry.chainDecimals[0]; - console.log('Raw balance:', balance.free.toBigInt()) - formatBalance.setDefaults({ unit: 'UNIT', decimals }); - const defaults = formatBalance.getDefaults(); - const free = formatBalance(balance.free, { withSiFull: true }); - const reserved = formatBalance(balance.reserved, { withSiFull: true }); - console.log('Formatted balance:', `{"free": "${free}", "unit": "${defaults.unit}", "reserved": "${reserved}", "nonce": "${nonce.toHuman()}"}`); + formatBalance.setDefaults({ unit: "UNIT", decimals }); + + const { nonce, data: balance } = await api.query.system.account( + accountData.address + ); + const { free, reserved, miscFrozen, feeFrozen } = balance; + + let frozen: BalanceType; + if (feeFrozen.gt(miscFrozen)) { + frozen = feeFrozen; + } else { + frozen = miscFrozen; + } + + const transferrableBalance = free.sub(frozen); + const totalBalance = free.add(reserved); + + console.log("Transferrable Balance:", formatBalance(transferrableBalance)); + if (!transferrableBalance.eq(totalBalance)) { + console.log("Total Balance:", formatBalance(totalBalance)); + console.log("Raw Balances:", balance.toHuman()); + } + console.log("Account Nonce:", nonce.toHuman()); + await api.disconnect(); } } From 619e39af70b8976c9394430c3655369de515489e Mon Sep 17 00:00:00 2001 From: prxgr4mm3r Date: Wed, 7 Feb 2024 20:35:24 +0200 Subject: [PATCH 08/10] fixes --- src/commands/account/balance.ts | 7 +++++-- src/lib/substrate-api.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index 1c43b0e3..edde0881 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -4,7 +4,7 @@ import type { AccountInfo, Balance as BalanceType } from "@polkadot/types/interf import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; -import { ConfigError } from "../../lib/errors.js"; +import { ConfigError, InputError } from "../../lib/errors.js"; import { formatBalance } from "@polkadot/util"; export class Balance extends SwankyCommand { @@ -13,13 +13,16 @@ export class Balance extends SwankyCommand { static args = { alias: Args.string({ name: "alias", - required: true, description: "Alias of account to be used", }), }; async run(): Promise { const { args } = await this.parse(Balance); + if(!args.alias) { + throw new InputError("Missing argument! Please provide an alias to get the balance of an account."); + } + const accountData = this.swankyConfig.accounts.find( (account: AccountData) => account.alias === args.alias ); diff --git a/src/lib/substrate-api.ts b/src/lib/substrate-api.ts index 665e866f..188b44b8 100644 --- a/src/lib/substrate-api.ts +++ b/src/lib/substrate-api.ts @@ -269,7 +269,7 @@ export class ChainApi { resolve(); this._provider.disconnect(); } - }).catch(error => reject(error)); + }).catch(error => reject(error)).finally(() => this._provider.disconnect()); }); } } From c1f773c1468582c4d4463135b12f39d8b99411c4 Mon Sep 17 00:00:00 2001 From: Igor Papandinas Date: Mon, 12 Feb 2024 20:57:46 +0100 Subject: [PATCH 09/10] chore: SwankyAccountCommand class created --- src/commands/account/balance.ts | 6 ++- src/commands/account/create.ts | 32 +++---------- src/commands/account/faucet.ts | 37 ++++----------- src/commands/account/swankyAccountCommands.ts | 45 +++++++++++++++++++ src/lib/substrate-api.ts | 9 ++-- 5 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 src/commands/account/swankyAccountCommands.ts diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index edde0881..baa538f6 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -19,8 +19,10 @@ export class Balance extends SwankyCommand { async run(): Promise { const { args } = await this.parse(Balance); - if(!args.alias) { - throw new InputError("Missing argument! Please provide an alias to get the balance of an account."); + if (!args.alias) { + throw new InputError( + "Missing argument! Please provide an alias account to get the balance from. Example usage: `swanky account balance `" + ); } const accountData = this.swankyConfig.accounts.find( diff --git a/src/commands/account/create.ts b/src/commands/account/create.ts index 55e31248..a9000c4a 100644 --- a/src/commands/account/create.ts +++ b/src/commands/account/create.ts @@ -1,12 +1,11 @@ import { Flags } from "@oclif/core"; import chalk from "chalk"; -import { ChainAccount, ChainApi, encrypt, resolveNetworkUrl } from "../../lib/index.js"; +import { ChainAccount, encrypt } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; import inquirer from "inquirer"; -import { SwankyCommand } from "../../lib/swankyCommand.js"; -import { ApiError } from "../../lib/errors.js"; -import { LOCAL_FAUCET_AMOUNT } from "../../lib/consts.js"; -export class CreateAccount extends SwankyCommand { +import { SwankyAccountCommand } from "./swankyAccountCommands.js"; + +export class CreateAccount extends SwankyAccountCommand { static description = "Create a new dev account in config"; static flags = { @@ -87,27 +86,6 @@ export class CreateAccount extends SwankyCommand { )} stored to config` ); - const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); - - const api = (await this.spinner.runCommand(async () => { - const api = await ChainApi.create(networkUrl); - await api.start(); - return api; - }, "Connecting to node")) as ChainApi; - - - await this.spinner.runCommand( - async () => { - try { - await api.faucet(accountData); - } catch (cause) { - throw new ApiError("Error transferring tokens from faucet account", { cause }); - } - }, - `Transferring ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, - `Transferred ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, - `Failed to transfer ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, - true - ); + await this.performFaucetTransfer(accountData, true); } } diff --git a/src/commands/account/faucet.ts b/src/commands/account/faucet.ts index ced47748..2850fde8 100644 --- a/src/commands/account/faucet.ts +++ b/src/commands/account/faucet.ts @@ -1,11 +1,9 @@ import { Args } from "@oclif/core"; -import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; import { AccountData } from "../../types/index.js"; -import { SwankyCommand } from "../../lib/swankyCommand.js"; -import { ApiError, ConfigError } from "../../lib/errors.js"; -import { LOCAL_FAUCET_AMOUNT } from "../../lib/consts.js"; +import { ConfigError } from "../../lib/errors.js"; +import { SwankyAccountCommand } from "./swankyAccountCommands.js"; -export class Faucet extends SwankyCommand { +export class Faucet extends SwankyAccountCommand { static description = "Transfer some tokens from faucet to an account"; static aliases = [`account:faucet`]; @@ -17,36 +15,19 @@ export class Faucet extends SwankyCommand { description: "Alias of account to be used", }), }; + async run(): Promise { const { args } = await this.parse(Faucet); - const accountData = this.swankyConfig.accounts.find( - (account: AccountData) => account.alias === args.alias - ); + const accountData = this.findAccountByAlias(args.alias); if (!accountData) { throw new ConfigError("Provided account alias not found in swanky.config.json"); } - const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); - - const api = (await this.spinner.runCommand(async () => { - const api = await ChainApi.create(networkUrl); - await api.start(); - return api; - }, "Connecting to node")) as ChainApi; + await this.performFaucetTransfer(accountData); + } - await this.spinner.runCommand( - async () => { - try { - await api.faucet(accountData); - } catch (cause) { - throw new ApiError("Error transferring tokens from faucet account", { cause }); - } - }, - `Transferring ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${args.alias}`, - `Transferred ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${args.alias}`, - `Failed to transfer ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${args.alias}`, - true - ); + findAccountByAlias(alias: string): AccountData | undefined { + return this.swankyConfig.accounts.find(account => account.alias === alias); } } diff --git a/src/commands/account/swankyAccountCommands.ts b/src/commands/account/swankyAccountCommands.ts new file mode 100644 index 00000000..b884f203 --- /dev/null +++ b/src/commands/account/swankyAccountCommands.ts @@ -0,0 +1,45 @@ +import { Command } from "@oclif/core"; +import chalk from "chalk"; +import { AccountData, ChainApi, resolveNetworkUrl } from "../../index.js"; +import { LOCAL_FAUCET_AMOUNT } from "../../lib/consts.js"; +import { SwankyCommand } from "../../lib/swankyCommand.js"; +import { ApiError } from "../../lib/errors.js"; + +export abstract class SwankyAccountCommand extends SwankyCommand { + async performFaucetTransfer(accountData: AccountData, canBeSkipped = false) { + let api: ChainApi | null = null; + try { + api = (await this.spinner.runCommand(async () => { + const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); + const api = await ChainApi.create(networkUrl); + await api.start(); + return api; + }, "Connecting to node")) as ChainApi; + + if (api) + await this.spinner.runCommand( + async () => { + if (api) await api.faucet(accountData); + }, + `Transferring ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, + `Transferred ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, + `Failed to transfer ${LOCAL_FAUCET_AMOUNT} units from faucet account to ${accountData.alias}`, + true + ); + } catch (cause) { + if (cause instanceof Error) { + if (cause.message.includes('ECONNREFUSED') && canBeSkipped) { + this.warn(`Unable to connect to the node. Skipping faucet transfer for ${chalk.yellowBright(accountData.alias)}.`); + } else { + throw new ApiError("Error transferring tokens from faucet account", { cause }); + } + } else { + throw new ApiError("An unknown error occurred during faucet transfer", { cause: new Error(String(cause)) }); + } + } finally { + if (api) { + await api.disconnect(); + } + } + } +} diff --git a/src/lib/substrate-api.ts b/src/lib/substrate-api.ts index 188b44b8..42bef37c 100644 --- a/src/lib/substrate-api.ts +++ b/src/lib/substrate-api.ts @@ -103,6 +103,10 @@ export class ChainApi { return this._registry; } + public async disconnect(): Promise { + await this._provider.disconnect(); + } + public async start(): Promise { const chainProperties = await this._api.rpc.system.properties(); @@ -249,7 +253,7 @@ export class ChainApi { }); } - public async faucet(accountData: AccountData) : Promise { + public async faucet(accountData: AccountData): Promise { const keyring = new Keyring({ type: KEYPAIR_TYPE }); const alicePair = keyring.addFromUri(ALICE_URI); @@ -267,9 +271,8 @@ export class ChainApi { return; } resolve(); - this._provider.disconnect(); } - }).catch(error => reject(error)).finally(() => this._provider.disconnect()); + }).catch((error) => reject(error)); }); } } From 449f834224b75471da142660d2cc79fd8994b6c1 Mon Sep 17 00:00:00 2001 From: Igor Papandinas Date: Mon, 12 Feb 2024 21:10:03 +0100 Subject: [PATCH 10/10] chore: findAccountByAlias created --- src/commands/account/balance.ts | 11 ++--------- src/commands/account/faucet.ts | 10 ---------- src/commands/contract/deploy.ts | 10 ++-------- src/lib/contractCall.ts | 10 ++-------- src/lib/swankyCommand.ts | 15 ++++++++++++++- 5 files changed, 20 insertions(+), 36 deletions(-) diff --git a/src/commands/account/balance.ts b/src/commands/account/balance.ts index baa538f6..4a9caf85 100644 --- a/src/commands/account/balance.ts +++ b/src/commands/account/balance.ts @@ -2,9 +2,8 @@ import { Args } from "@oclif/core"; import { ApiPromise } from "@polkadot/api"; import type { AccountInfo, Balance as BalanceType } from "@polkadot/types/interfaces"; import { ChainApi, resolveNetworkUrl } from "../../lib/index.js"; -import { AccountData } from "../../types/index.js"; import { SwankyCommand } from "../../lib/swankyCommand.js"; -import { ConfigError, InputError } from "../../lib/errors.js"; +import { InputError } from "../../lib/errors.js"; import { formatBalance } from "@polkadot/util"; export class Balance extends SwankyCommand { @@ -25,13 +24,7 @@ export class Balance extends SwankyCommand { ); } - const accountData = this.swankyConfig.accounts.find( - (account: AccountData) => account.alias === args.alias - ); - if (!accountData) { - throw new ConfigError("Provided account alias not found in swanky.config.json"); - } - + const accountData = this.findAccountByAlias(args.alias); const networkUrl = resolveNetworkUrl(this.swankyConfig, ""); const api = (await this.spinner.runCommand(async () => { diff --git a/src/commands/account/faucet.ts b/src/commands/account/faucet.ts index 2850fde8..02b6b965 100644 --- a/src/commands/account/faucet.ts +++ b/src/commands/account/faucet.ts @@ -1,6 +1,4 @@ import { Args } from "@oclif/core"; -import { AccountData } from "../../types/index.js"; -import { ConfigError } from "../../lib/errors.js"; import { SwankyAccountCommand } from "./swankyAccountCommands.js"; export class Faucet extends SwankyAccountCommand { @@ -20,14 +18,6 @@ export class Faucet extends SwankyAccountCommand { const { args } = await this.parse(Faucet); const accountData = this.findAccountByAlias(args.alias); - if (!accountData) { - throw new ConfigError("Provided account alias not found in swanky.config.json"); - } - await this.performFaucetTransfer(accountData); } - - findAccountByAlias(alias: string): AccountData | undefined { - return this.swankyConfig.accounts.find(account => account.alias === alias); - } } diff --git a/src/commands/contract/deploy.ts b/src/commands/contract/deploy.ts index 7b2e5482..a0b2ca0a 100644 --- a/src/commands/contract/deploy.ts +++ b/src/commands/contract/deploy.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { writeJSON } from "fs-extra/esm"; import { cryptoWaitReady } from "@polkadot/util-crypto/crypto"; import { resolveNetworkUrl, ChainApi, ChainAccount, decrypt, AbiType } from "../../lib/index.js"; -import { AccountData, Encrypted } from "../../types/index.js"; +import { Encrypted } from "../../types/index.js"; import inquirer from "inquirer"; import chalk from "chalk"; import { Contract } from "../../lib/contract.js"; @@ -70,13 +70,7 @@ export class DeployContract extends SwankyCommand { ); } - const accountData = this.swankyConfig.accounts.find( - (account: AccountData) => account.alias === flags.account - ); - if (!accountData) { - throw new ConfigError("Provided account alias not found in swanky.config.json"); - } - + const accountData = this.findAccountByAlias(flags.account); const mnemonic = accountData.isDev ? (accountData.mnemonic as string) : decrypt( diff --git a/src/lib/contractCall.ts b/src/lib/contractCall.ts index 246e97d0..9c9e0b2f 100644 --- a/src/lib/contractCall.ts +++ b/src/lib/contractCall.ts @@ -1,5 +1,5 @@ import { AbiType, ChainAccount, ChainApi, decrypt, resolveNetworkUrl } from "./index.js"; -import { AccountData, ContractData, DeploymentData, Encrypted } from "../types/index.js"; +import { ContractData, DeploymentData, Encrypted } from "../types/index.js"; import { Args, Command, Flags, Interfaces } from "@oclif/core"; import inquirer from "inquirer"; import chalk from "chalk"; @@ -77,13 +77,7 @@ export abstract class ContractCall extends SwankyComma this.deploymentInfo = deploymentData; - const accountData = this.swankyConfig.accounts.find( - (account: AccountData) => account.alias === flags.account || "alice" - ); - if (!accountData) { - throw new ConfigError("Provided account alias not found in swanky.config.json"); - } - + const accountData = this.findAccountByAlias(flags.account || "alice"); const networkUrl = resolveNetworkUrl(this.swankyConfig, flags.network ?? ""); const api = await ChainApi.create(networkUrl); this.api = api; diff --git a/src/lib/swankyCommand.ts b/src/lib/swankyCommand.ts index b2d6c625..4f2b5a73 100644 --- a/src/lib/swankyCommand.ts +++ b/src/lib/swankyCommand.ts @@ -1,6 +1,7 @@ import { Command, Flags, Interfaces } from "@oclif/core"; +import chalk from "chalk"; import { getSwankyConfig, Spinner } from "./index.js"; -import { SwankyConfig } from "../types/index.js"; +import { AccountData, SwankyConfig } from "../types/index.js"; import { writeJSON } from "fs-extra/esm"; import { BaseError, ConfigError, UnknownError } from "./errors.js"; import { swankyLogger } from "./logger.js"; @@ -51,6 +52,18 @@ export abstract class SwankyCommand extends Command { Full command: ${JSON.stringify(process.argv)}`); } + protected findAccountByAlias(alias: string): AccountData { + const accountData = this.swankyConfig.accounts.find( + (account: AccountData) => account.alias === alias + ); + + if (!accountData) { + throw new ConfigError(`Provided account alias ${chalk.yellowBright(alias)} not found in swanky.config.json`); + } + + return accountData; + } + protected async storeConfig() { await writeJSON("swanky.config.json", this.swankyConfig, { spaces: 2 }); }