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

Faucet feature for SwankyNode #192

Merged
merged 11 commits into from
Feb 12, 2024
65 changes: 65 additions & 0 deletions src/commands/account/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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 } from "../../lib/errors.js";
import { formatBalance } from "@polkadot/util";

export class Balance extends SwankyCommand<typeof Balance> {
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<void> {
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 api = (await this.spinner.runCommand(async () => {
const api = await ChainApi.create(networkUrl);
await api.start();
return api.apiInst;
}, "Connecting to node")) as ApiPromise;

const decimals = api.registry.chainDecimals[0];
formatBalance.setDefaults({ unit: "UNIT", decimals });

const { nonce, data: balance } = await api.query.system.account<AccountInfo>(
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();
}
}
29 changes: 27 additions & 2 deletions src/commands/account/create.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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";
import { ApiError } from "../../lib/errors.js";
import { LOCAL_FAUCET_AMOUNT } from "../../lib/consts.js";
export class CreateAccount extends SwankyCommand<typeof CreateAccount> {
static description = "Create a new dev account in config";

Expand Down Expand Up @@ -35,7 +37,7 @@ export class CreateAccount extends SwankyCommand<typeof CreateAccount> {
);
}

let tmpMnemonic = "";
let tmpMnemonic: string;
if (flags.generate) {
tmpMnemonic = ChainAccount.generate();
console.log(
Expand Down Expand Up @@ -84,5 +86,28 @@ export class CreateAccount extends SwankyCommand<typeof CreateAccount> {
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 () => {
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
);
}
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
}
52 changes: 52 additions & 0 deletions src/commands/account/faucet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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";

export class Faucet extends SwankyCommand<typeof Faucet> {
static description = "Transfer some tokens from faucet 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<void> {
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 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 ${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
);
}
}
9 changes: 5 additions & 4 deletions src/commands/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -174,15 +175,15 @@ export class Init extends SwankyCommand<typeof Init> {
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,
},
];

Expand Down
3 changes: 2 additions & 1 deletion src/lib/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/lib/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 KEYPAIR_TYPE = "sr25519";
export const ALICE_URI = "//Alice";
export const BOB_URI = "//Bob";
31 changes: 28 additions & 3 deletions src/lib/substrate-api.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
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";
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";
import { ApiError, UnknownError } from "./errors.js";
import { ALICE_URI, KEYPAIR_TYPE, LOCAL_FAUCET_AMOUNT } from "./consts.js";
import { BN_TEN } from "@polkadot/util";

export type AbiType = Abi;
// const AUTO_CONNECT_MS = 10_000; // [ms]
Expand Down Expand Up @@ -210,7 +212,6 @@ export class ChainApi {
if (handler) handler(result);
});
}

public async deploy(
abi: Abi,
wasm: Buffer,
Expand Down Expand Up @@ -247,4 +248,28 @@ export class ChainApi {
});
});
}

public async faucet(accountData: AccountData) : Promise<void> {
const keyring = new Keyring({ type: KEYPAIR_TYPE });
const alicePair = keyring.addFromUri(ALICE_URI);

const chainDecimals = this._api.registry.chainDecimals[0];
const amount = new BN(LOCAL_FAUCET_AMOUNT).mul(BN_TEN.pow(new BN(chainDecimals)));

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) {
ipapandinas marked this conversation as resolved.
Show resolved Hide resolved
const transferEvent = events.find(({ event }) => event?.method === "Transfer");
if (!transferEvent) {
reject();
return;
}
resolve();
this._provider.disconnect();
}
}).catch(error => reject(error));
});
}
}
Loading