--- a/Cargo.lock +++ b/Cargo.lock @@ -1228,6 +1228,26 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] +name = "enumflags2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3541,6 +3561,7 @@ "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", + "pallet-vesting", "parity-scale-codec", "serde", "sp-api", @@ -4020,6 +4041,20 @@ ] [[package]] +name = "pallet-vesting" +version = "2.0.0" +source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274" +dependencies = [ + "enumflags2", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] name = "parity-db" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -154,7 +154,8 @@ .collect(), }), pallet_treasury: Some(Default::default()), - pallet_sudo: Some(SudoConfig { key: root_key }), + pallet_sudo: Some(SudoConfig { key: root_key }), + pallet_vesting: Some(Default::default()), pallet_nft: Some(NftConfig { collection: vec![( 1, --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -45,6 +45,8 @@ pallet-timestamp = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } pallet-transaction-payment = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } pallet-transaction-payment-rpc-runtime-api = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } +pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } +pallet-vesting = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } sp-api = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } sp-block-builder = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } sp-consensus-aura = { default-features = false, version = '0.8.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } @@ -56,8 +58,6 @@ sp-std = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } sp-transaction-pool = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } sp-version = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } - -pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' } [features] default = ['std'] @@ -90,6 +90,9 @@ 'pallet-timestamp/std', 'pallet-transaction-payment/std', 'pallet-transaction-payment-rpc-runtime-api/std', + 'pallet-treasury/std', + 'pallet-vesting/std', + 'pallet-nft/std', 'sp-api/std', 'sp-block-builder/std', @@ -103,5 +106,4 @@ 'sp-transaction-pool/std', 'sp-version/std', - 'pallet-treasury/std', ] --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -17,7 +17,7 @@ use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - Convert, BlakeTwo256, Block as BlockT, IdentifyAccount, + Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, NumberFor, Saturating, Verify, }, transaction_validity::{TransactionSource, TransactionValidity}, @@ -412,6 +412,18 @@ type Call = Call; } +parameter_types! { + pub const MinVestedTransfer: Balance = 100 * DOLLARS; +} + +impl pallet_vesting::Trait for Runtime { + type Event = Event; + type Currency = Balances; + type BlockNumberToBalance = ConvertInto; + type MinVestedTransfer = MinVestedTransfer; + type WeightInfo = (); +} + /// Used for the module nft in `./nft.rs` impl pallet_nft::Trait for Runtime { type Event = Event; @@ -435,6 +447,7 @@ Sudo: pallet_sudo::{Module, Call, Config, Storage, Event}, Nft: pallet_nft::{Module, Call, Config, Storage, Event}, Treasury: pallet_treasury::{Module, Call, Storage, Config, Event}, + Vesting: pallet_vesting::{Module, Call, Config, Storage, Event}, } ); --- a/tests/src/blocks-production.test.ts +++ b/tests/src/blocks-production.test.ts @@ -2,7 +2,7 @@ import promisifySubstrate from "./substrate/promisify-substrate"; import { expect } from "chai"; -describe('Blocks Production', () => { +describe('Blocks Production smoke test', () => { it('Node produces new blocks', async () => { await usingApi(async api => { const blocksPromise = promisifySubstrate(api, () => { --- a/tests/src/connection.test.ts +++ b/tests/src/connection.test.ts @@ -7,7 +7,7 @@ const expect = chai.expect; -describe('Connection', () => { +describe('Connection smoke test', () => { it('Connection can be established', async () => { await usingApi(async api => { const health = await api.rpc.system.health(); @@ -16,11 +16,17 @@ }); it('Cannot connect to 255.255.255.255', async () => { + console.log = function () {}; + console.error = function () {}; + const neverConnectProvider = new WsProvider('ws://255.255.255.255:9944'); await expect((async () => { await usingApi(async api => { const health = await api.rpc.system.health(); }, { provider: neverConnectProvider }); })()).to.be.eventually.rejected; + + delete console.log; + delete console.error; }); }); \ No newline at end of file --- a/tests/src/contracts.test.ts +++ b/tests/src/contracts.test.ts @@ -1,13 +1,17 @@ +import { ApiPromise } from "@polkadot/api"; import { expect } from "chai"; -import usingApi from "./substrate/substrate-api"; +import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api"; import fs from "fs"; import { Abi, BlueprintPromise, CodePromise } from "@polkadot/api-contract"; import { IKeyringPair } from "@polkadot/types/types"; import { Keyring } from "@polkadot/api"; import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types"; +import { BigNumber } from 'bignumber.js'; +import { findUnusedAddress } from './util/helpers' const value = 0; const gasLimit = 3000n * 1000000n; +const endowment = `1000000000000000`; function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise { return new Promise(async (resolve, reject) => { @@ -25,7 +29,6 @@ function deployContract(alice: IKeyringPair, blueprint: BlueprintPromise) : Promise { return new Promise(async (resolve, reject) => { - const endowment = 1000000000000000n; const initValue = true; const unsub = await blueprint.tx @@ -39,28 +42,25 @@ }); } -function runTransaction(privateKey: IKeyringPair, extrinsic: SubmittableExtrinsic) { - return new Promise(async (resolve, reject) => { - extrinsic.signAndSend(privateKey, async result => { - if(!result.isInBlock) { - return; - } +async function prepareDeployer(api: ApiPromise) { + // Find unused address + const deployer = await findUnusedAddress(api); + + // Transfer balance to it + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri(`//Alice`); + let amount = new BigNumber(endowment); + amount = amount.plus(1e15); + const tx = api.tx.balances.transfer(deployer.address, amount.toFixed()); + await submitTransactionAsync(alice, tx); - if(result.findRecord('system', 'ExtrinsicSuccess')) { - resolve(); - } - else { - reject('Failed to flip value.'); - } - }) - }); + return deployer; } -describe('Contracts', () => { +describe('Contracts smoke test', () => { it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => { await usingApi(async api => { - const keyring = new Keyring({ type: 'sr25519' }); - const alice = keyring.addFromUri("//Alice"); + const deployer = await prepareDeployer(api); const wasm = fs.readFileSync('./src/flipper/flipper.wasm'); @@ -69,11 +69,11 @@ const code = new CodePromise(api, abi, wasm); - const blueprint = await deployBlueprint(alice, code); - const contract = (await deployContract(alice, blueprint))['contract']; + const blueprint = await deployBlueprint(deployer, code); + const contract = (await deployContract(deployer, blueprint))['contract']; const getFlipValue = async () => { - const result = await contract.query.get(alice.address, value, gasLimit); + const result = await contract.query.get(deployer.address, value, gasLimit); if(!result.result.isSuccess) { throw `Failed to get flipper value`; @@ -85,7 +85,7 @@ expect(initialGetResponse).to.be.true; const flip = contract.exec('flip', value, gasLimit); - await runTransaction(alice, flip); + await submitTransactionAsync(deployer, flip); const afterFlipGetResponse = await getFlipValue(); @@ -112,7 +112,7 @@ // const bob = new GenericAccountId(api.registry, bobsPublicKey); // const transfer = contractInstance.exec('balance_transfer', 0, 1000000000000n, [bob, new u128(api.registry, 1000000)]); - // await runTransaction(alicesPrivateKey, transfer); + // await submitTransactionAsync(alicesPrivateKey, transfer); // const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]); --- /dev/null +++ b/tests/src/creditFeesToTreasury.test.ts @@ -0,0 +1,107 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api"; +import { alicesPublicKey, bobsPublicKey } from "./accounts"; +import privateKey from "./substrate/privateKey"; +import { BigNumber } from 'bignumber.js'; +import { createCollectionExpectSuccess, getGenericResult } from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const Treasury = "5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z"; +const saneMinimumFee = 0.0001; +const saneMaximumFee = 0.01; + +describe('integration test: Fees must be credited to Treasury:', () => { + it('Total issuance does not change', async () => { + await usingApi(async (api) => { + const totalBefore = new BigNumber((await api.query.balances.totalIssuance()).toString()); + + const alicePrivateKey = privateKey('//Alice'); + const amount = new BigNumber(1); + const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed()); + + const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer)); + + const totalAfter = new BigNumber((await api.query.balances.totalIssuance()).toString()); + + expect(result.success).to.be.true; + expect(totalAfter.toFixed()).to.be.equal(totalBefore.toFixed()); + }); + }); + + it('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async () => { + await usingApi(async (api) => { + const alicePrivateKey = privateKey('//Alice'); + const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); + + const amount = new BigNumber(1); + const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed()); + const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer)); + + const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); + const fee = aliceBalanceBefore.minus(aliceBalanceAfter).minus(amount); + const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); + + expect(result.success).to.be.true; + expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed()); + }); + }); + + it('Treasury balance increased by failed tx fee', async () => { + await usingApi(async (api) => { + const bobPrivateKey = privateKey('//Bob'); + const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString()); + + const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0); + const result = getGenericResult(await submitTransactionAsync(bobPrivateKey, badTx)); + + const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString()); + const fee = bobBalanceBefore.minus(bobBalanceAfter); + const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); + + expect(result.success).to.be.false; + expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed()); + }); + }); + + it('NFT Transactions also send fees to Treasury', async () => { + await usingApi(async (api) => { + const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); + + await createCollectionExpectSuccess('A', 'B', 'C', 'NFT'); + + const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); + const fee = aliceBalanceBefore.minus(aliceBalanceAfter); + const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); + + expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed()); + }); + }); + + it('Fees are sane', async () => { + await usingApi(async (api) => { + const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); + + await createCollectionExpectSuccess('A', 'B', 'C', 'NFT'); + + const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); + const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); + const fee = aliceBalanceBefore.minus(aliceBalanceAfter); + const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); + + expect(fee.dividedBy(1e15).toNumber()).to.be.lessThan(0.01); + expect(fee.dividedBy(1e15).toNumber()).to.be.greaterThan(0.0001); + }); + }); + +}); + --- a/tests/src/crefitFeesToTreasury.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api"; -import { alicesPublicKey, bobsPublicKey } from "./accounts"; -import privateKey from "./substrate/privateKey"; -import { BigNumber } from 'bignumber.js'; -import { createCollectionExpectSuccess, getGenericResult } from './util/helpers'; - -chai.use(chaiAsPromised); -const expect = chai.expect; - -const Treasury = "5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z"; -const saneMinimumFee = 0.0001; -const saneMaximumFee = 0.01; - -describe('integration test: Fees must be credited to Treasury:', () => { - it('Total issuance does not change', async () => { - await usingApi(async (api) => { - const totalBefore = new BigNumber((await api.query.balances.totalIssuance()).toString()); - - const alicePrivateKey = privateKey('//Alice'); - const amount = new BigNumber(1); - const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed()); - - const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer)); - - const totalAfter = new BigNumber((await api.query.balances.totalIssuance()).toString()); - - expect(result.success).to.be.true; - expect(totalAfter.toFixed()).to.be.equal(totalBefore.toFixed()); - }); - }); - - it('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async () => { - await usingApi(async (api) => { - const alicePrivateKey = privateKey('//Alice'); - const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); - - const amount = new BigNumber(1); - const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed()); - const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer)); - - const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); - const fee = aliceBalanceBefore.minus(aliceBalanceAfter).minus(amount); - const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); - - expect(result.success).to.be.true; - expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed()); - }); - }); - - it('Treasury balance increased by failed tx fee', async () => { - await usingApi(async (api) => { - const bobPrivateKey = privateKey('//Bob'); - const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString()); - - const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0); - const result = getGenericResult(await submitTransactionAsync(bobPrivateKey, badTx)); - - const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString()); - const fee = bobBalanceBefore.minus(bobBalanceAfter); - const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); - - expect(result.success).to.be.false; - expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed()); - }); - }); - - it('NFT Transactions also send fees to Treasury', async () => { - await usingApi(async (api) => { - const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); - - await createCollectionExpectSuccess('A', 'B', 'C', 'NFT'); - - const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); - const fee = aliceBalanceBefore.minus(aliceBalanceAfter); - const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); - - expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed()); - }); - }); - - it('Fees are sane', async () => { - await usingApi(async (api) => { - const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); - - await createCollectionExpectSuccess('A', 'B', 'C', 'NFT'); - - const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString()); - const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString()); - const fee = aliceBalanceBefore.minus(aliceBalanceAfter); - const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore); - - expect(fee.dividedBy(1e15).toNumber()).to.be.lessThan(0.01); - expect(fee.dividedBy(1e15).toNumber()).to.be.greaterThan(0.0001); - }); - }); - -}); - --- a/tests/src/pallet-presence.test.ts +++ b/tests/src/pallet-presence.test.ts @@ -6,20 +6,34 @@ return api.runtimeMetadata.asLatest.modules.map(m => m.name.toString().toLowerCase()); } -describe('Pallet presence.', () => { - it('NFT pallet is present.', async () => { +// Pallets that must always be present +const requiredPallets = [ + 'nft', 'balances', 'contracts', 'randomnesscollectiveflip', 'system', 'timestamp', 'transactionpayment', 'treasury', 'vesting' +]; + +// Pallets that depend on consensus and governance configuration +const consensusPallets = [ + 'sudo', 'grandpa', 'aura' +]; + +describe('Pallet presence', () => { + it('Required pallets are present', async () => { await usingApi(async api => { - expect(getModuleNames(api)).to.include('nft'); + for (let i=0; i { + it('Governance and consensus pallets are present', async () => { await usingApi(async api => { - expect(getModuleNames(api)).to.include('balances'); + for (let i=0; i { + it('No extra pallets are included', async () => { await usingApi(async api => { - expect(getModuleNames(api)).to.include('contracts'); + expect(getModuleNames(api).length).to.be.equal(requiredPallets.length + consensusPallets.length); }); }); }); --- a/tests/src/transfer.test.ts +++ b/tests/src/transfer.test.ts @@ -3,6 +3,8 @@ import { alicesPublicKey, bobsPublicKey, ferdiesPublicKey } from "./accounts"; import privateKey from "./substrate/privateKey"; import getBalance from "./substrate/get-balance"; +import { BigNumber } from 'bignumber.js'; +import { findUnusedAddress } from './util/helpers' describe('Transfer', () => { it('Balance transfers', async () => { @@ -23,7 +25,8 @@ it('Inability to pay fees error message is correct', async () => { await usingApi(async api => { - const pk = privateKey('//Ferdie'); + // Find unused address + const pk = await findUnusedAddress(api); console.log = function () {}; console.error = function () {}; --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -1,10 +1,13 @@ import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; -import type { EventRecord } from '@polkadot/types/interfaces'; +import type { AccountId, EventRecord } from '@polkadot/types/interfaces'; +import { ApiPromise, Keyring } from "@polkadot/api"; import { default as usingApi, submitTransactionAsync } from "../substrate/substrate-api"; import privateKey from '../substrate/privateKey'; import { alicesPublicKey } from "../accounts"; import { strToUTF16, utf16ToStr, hexToStr } from '../util/util'; +import { IKeyringPair } from "@polkadot/types/types"; +import { BigNumber } from 'bignumber.js'; chai.use(chaiAsPromised); const expect = chai.expect; @@ -98,3 +101,14 @@ }); } +export async function findUnusedAddress(api: ApiPromise): Promise { + let bal = new BigNumber(0); + let unused; + do { + const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)); + const keyring = new Keyring({ type: 'sr25519' }); + unused = keyring.addFromUri(`//${randomSeed}`); + bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString()); + } while (bal.toFixed() != '0'); + return unused; +} \ No newline at end of file