git.delta.rocks / unique-network / refs/commits / fbf17896e9db

difftreelog

Merge branch 'develop' into feature/NFTPAR-240

Greg Zaitsev2020-12-23parents: #3db9a79 #8686594.patch.diff
in: master

33 files changed

modifiedCargo.lockdiffbeforeafterboth
--- 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"
@@ -3502,6 +3522,8 @@
  "sc-rpc-api",
  "sc-service",
  "sc-transaction-pool",
+ "serde",
+ "serde_json",
  "sp-api",
  "sp-block-builder",
  "sp-blockchain",
@@ -3541,6 +3563,7 @@
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-treasury",
+ "pallet-vesting",
  "parity-scale-codec",
  "serde",
  "sp-api",
@@ -4020,6 +4043,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"
modifiedLICENSEdiffbeforeafterboth
--- a/LICENSE
+++ b/LICENSE
@@ -1,24 +1,15 @@
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
+USETECH PROFESSIONAL CONFIDENTIAL
+__________________
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
+ [2019] - [2020] UseTech Professional LTD. 
+ All Rights Reserved.
 
-For more information, please refer to <http://unlicense.org>
+NOTICE:  All information contained herein is, and remains
+the property of UseTech Professional LTD. and its suppliers,
+if any. The intellectual and technical concepts contained
+herein are proprietary to UseTech Professional LTD.
+and its suppliers and may be covered by U.S. and Foreign Patents,
+patents in process, and are protected by trade secret or copyright law.
+Dissemination of this information or reproduction of this material
+is strictly forbidden unless prior written permission is obtained
+from UseTech Professional LTD..
modifiedREADME.mddiffbeforeafterboth
--- a/README.md
+++ b/README.md
@@ -52,28 +52,27 @@
 
 2. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.
 
-3. Install Rust Toolchain 1.44.0:
+3. Install Toolchain and make it default:
 
 ```bash
-rustup install 1.44.0
+rustup toolchain install nightly-2020-10-01
+rustup default nightly-2020-10-01
 ```
 
-4. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)
+4. Add wasm target for default toolchain:
+
 ```bash
-rustup toolchain list
-rustup default 1.44.0-x86_64-unknown-linux-gnu
+rustup target add wasm32-unknown-unknown
 ```
 
-5. Install nightly toolchain and add wasm target for it:
-
+5. Build:
 ```bash
-rustup toolchain install nightly-2020-05-01
-rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu
+cargo build
 ```
 
-6. Build:
+optionally, build in release:
 ```bash
-cargo build
+cargo build --release
 ```
 
 ## Run
@@ -134,4 +133,8 @@
 
 ## UI custom types
 
-Moved to [runtime_types.json](./runtime_types.json).
\ No newline at end of file
+Moved to [runtime_types.json](./runtime_types.json).
+
+## Running Integration Tests
+
+See [tests/README.md](./tests/README.md).
\ No newline at end of file
modifiednode/Cargo.tomldiffbeforeafterboth
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -58,6 +58,9 @@
 substrate-frame-rpc-system = {version = '2.0.0', git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi'}
 pallet-contracts-rpc = {version = '0.8.0', git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi'} 
 
+serde = { version = "1.0.102", features = ["derive"] }
+serde_json = "1.0.41"
+
 [features]
 default = []
 runtime-benchmarks = ['nft-runtime/runtime-benchmarks']
modifiednode/build.rsdiffbeforeafterboth
--- a/node/build.rs
+++ b/node/build.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
 
 fn main() {
modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 // use nft_runtime::{
 //     AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
 //     SystemConfig, WASM_BINARY,
@@ -9,6 +14,7 @@
 use sp_core::{sr25519, Pair, Public};
 use sp_finality_grandpa::AuthorityId as GrandpaId;
 use sp_runtime::traits::{IdentifyAccount, Verify};
+use serde_json::map::Map;
 
 // Note this is the URL for the telemetry server
 //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
@@ -41,6 +47,11 @@
 pub fn development_config() -> Result<ChainSpec, String> {
 	let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;
 
+	let mut properties = Map::new();
+	properties.insert("tokenSymbol".into(), "UniqueTest".into());
+	properties.insert("tokenDecimals".into(), 15.into());
+	properties.insert("ss58Format".into(), 42.into()); // Generic Substrate wildcard (SS58 checksum preimage)
+
 	Ok(ChainSpec::from_genesis(
 		// Name
 		"Development",
@@ -71,7 +82,7 @@
 		// Protocol ID
 		None,
 		// Properties
-		None,
+		Some(properties),
 		// Extensions
 		None,
 	))
@@ -132,6 +143,11 @@
     endowed_accounts: Vec<AccountId>,
     enable_println: bool,
 ) -> GenesisConfig {
+
+	let vested_accounts = vec![
+		get_account_id_from_seed::<sr25519::Public>("Bob"),
+	];
+
     GenesisConfig {
         system: Some(SystemConfig {
             code: wasm_binary.to_vec(),
@@ -154,7 +170,14 @@
                 .collect(),
 		}),
 		pallet_treasury: Some(Default::default()),
-        pallet_sudo: Some(SudoConfig { key: root_key }),
+		pallet_sudo: Some(SudoConfig { key: root_key }),
+		pallet_vesting: Some(VestingConfig {
+            vesting: vested_accounts
+                .iter()
+                .cloned()
+                .map(|k| (k, 1000, 100, 1 << 98))
+                .collect(),
+        }),
         pallet_nft: Some(NftConfig {
             collection: vec![(
                 1,
modifiednode/src/main.rsdiffbeforeafterboth
--- a/node/src/main.rs
+++ b/node/src/main.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 //! Substrate Node Template CLI library.
 #![warn(missing_docs)]
 
modifiednode/src/service.rsdiffbeforeafterboth
--- a/node/src/service.rs
+++ b/node/src/service.rs
@@ -1,5 +1,10 @@
 //! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
 
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 use std::sync::Arc;
 use std::time::Duration;
 use sc_client_api::{ExecutorProvider, RemoteBackend};
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 #![recursion_limit = "1024"]
 
 #![cfg_attr(not(feature = "std"), no_std)]
modifiedruntime/Cargo.tomldiffbeforeafterboth
--- 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',
 ]
modifiedruntime/src/lib.rsdiffbeforeafterboth
before · runtime/src/lib.rs
1//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.23#![cfg_attr(not(feature = "std"), no_std)]4// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.5#![recursion_limit = "256"]67// Make the WASM binary available.8#[cfg(feature = "std")]9include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1011use pallet_contracts_rpc_runtime_api::ContractExecResult;12use pallet_grandpa::fg_primitives;13use pallet_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};14use sp_api::impl_runtime_apis;15use sp_consensus_aura::sr25519::AuthorityId as AuraId;16use sp_core::{ crypto::KeyTypeId, crypto::Public, OpaqueMetadata };17use sp_runtime::{18    create_runtime_str, generic, impl_opaque_keys,19    traits::{20        Convert, BlakeTwo256, Block as BlockT, IdentifyAccount, 21        IdentityLookup, NumberFor, Saturating, Verify,22    },23    transaction_validity::{TransactionSource, TransactionValidity},24    ApplyExtrinsicResult, MultiSignature,25};26use sp_std::prelude::*;27#[cfg(feature = "std")]28use sp_version::NativeVersion;29use sp_version::RuntimeVersion;3031// A few exports that help ease life for downstream crates.32pub use pallet_balances::Call as BalancesCall;33pub use pallet_contracts::Schedule as ContractsSchedule;34pub use frame_support::{35    construct_runtime,36    dispatch::DispatchResult,37    parameter_types,38    traits::{39        Currency, ExistenceRequirement, Get, KeyOwnerProofSystem, OnUnbalanced, Randomness,40        WithdrawReason, LockIdentifier,41    },42    weights::{43        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},44        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,45        WeightToFeePolynomial,46    },47    StorageValue,48};49#[cfg(any(feature = "std", test))]50pub use sp_runtime::BuildStorage;51use sp_runtime:: { Perbill, Permill, Percent, ModuleId };52use frame_system::{self as system, EnsureRoot };53use sp_std::{marker::PhantomData};5455pub use pallet_timestamp::Call as TimestampCall;5657/// Struct that handles the conversion of Balance -> `u64`. This is used for58/// staking's election calculation.59pub struct CurrencyToVoteHandler;6061impl CurrencyToVoteHandler {62	fn factor() -> Balance {63		(Balances::total_issuance() / u64::max_value() as Balance).max(1)64	}65}6667impl Convert<Balance, u64> for CurrencyToVoteHandler {68	fn convert(x: Balance) -> u64 {69		(x / Self::factor()) as u6470	}71}7273impl Convert<u128, Balance> for CurrencyToVoteHandler {74	fn convert(x: u128) -> Balance {75		x * Self::factor()76	}77}7879/// Re-export a nft pallet80/// TODO: Check this re-export. Is this safe and good style?81extern crate pallet_nft;82pub use pallet_nft::*;8384/// An index to a block.85pub type BlockNumber = u32;8687/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.88pub type Signature = MultiSignature;8990/// Some way of identifying an account on the chain. We intentionally make it equivalent91/// to the public key of our transaction signing scheme.92pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;9394/// The type for looking up accounts. We don't expect more than 4 billion of them, but you95/// never know...96pub type AccountIndex = u32;9798/// Balance of an account.99pub type Balance = u128;100101/// Index of a transaction in the chain.102pub type Index = u32;103104/// A hash of some data used by the chain.105pub type Hash = sp_core::H256;106107/// Digest item type.108pub type DigestItem = generic::DigestItem<Hash>;109110mod nft_weights;111112/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know113/// the specifics of the runtime. They can then be made to be agnostic over specific formats114/// of data like extrinsics, allowing for them to continue syncing the network through upgrades115/// to even the core data structures.116pub mod opaque {117    use super::*;118119    pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;120121    /// Opaque block header type.122    pub type Header = generic::Header<BlockNumber, BlakeTwo256>;123    /// Opaque block type.124    pub type Block = generic::Block<Header, UncheckedExtrinsic>;125    /// Opaque block identifier type.126    pub type BlockId = generic::BlockId<Block>;127128    impl_opaque_keys! {129        pub struct SessionKeys {130            pub aura: Aura,131            pub grandpa: Grandpa,132        }133    }134}135136/// This runtime version.137pub const VERSION: RuntimeVersion = RuntimeVersion {138    spec_name: create_runtime_str!("nft"),139    impl_name: create_runtime_str!("nft"),140    authoring_version: 1,141    spec_version: 2,142    impl_version: 1,143    apis: RUNTIME_API_VERSIONS,144    transaction_version: 1,145};146147pub const MILLISECS_PER_BLOCK: u64 = 6000;148149pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;150151// These time units are defined in number of blocks.152pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);153pub const HOURS: BlockNumber = MINUTES * 60;154pub const DAYS: BlockNumber = HOURS * 24;155156/// The version information used to identify this runtime when compiled natively.157#[cfg(feature = "std")]158pub fn native_version() -> NativeVersion {159    NativeVersion {160        runtime_version: VERSION,161        can_author_with: Default::default(),162    }163}164165/// Provides a membership set with only the configured aura users166pub struct ValiudatorsOnly<Runtime: pallet_aura::Trait>(PhantomData<Runtime>);167impl frame_support::traits::Contains<AccountId> for ValiudatorsOnly<Runtime> {168	fn contains(t: &AccountId) -> bool {169        let arr: [u8; 32] = *t.as_ref();170        let raw_key: Vec<u8> = Vec::from(arr);171172        match pallet_aura::Module::<Runtime>::authorities().iter().find(|auth| auth.to_raw_vec() == raw_key) {173            Some(_) => true,174            None => false,175        }  176	}177	fn sorted_members() -> Vec<AccountId> {178        let mut members: Vec<AccountId> = Vec::new();179        for auth in pallet_aura::Module::<Runtime>::authorities() {180            let mut arr: [u8; 32] = Default::default(); 181            let bor_arr = auth.clone().to_raw_vec();182            let slice = bor_arr.as_slice();183            arr.copy_from_slice(slice);184            members.push(AccountId::from(arr));185        }186        members  187	}188	fn count() -> usize {189        pallet_aura::Module::<Runtime>::authorities().len()190	}191}192193impl frame_support::traits::ContainsLengthBound for ValiudatorsOnly<Runtime> {194	fn min_len() -> usize {195		1196	}197	fn max_len() -> usize {198		100199	}200}201202parameter_types! {203    pub const BlockHashCount: BlockNumber = 2400;204    /// We allow for 2 seconds of compute with a 6 second average block time.205    pub const MaximumBlockWeight: Weight = 2 * WEIGHT_PER_SECOND;206    pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);207    /// Assume 10% of weight for average on_initialize calls.208    pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()209        .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();210    pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;211    pub const Version: RuntimeVersion = VERSION;212}213214impl system::Trait for Runtime {215    /// The basic call filter to use in dispatchable.216    type BaseCallFilter = ();217    /// The identifier used to distinguish between accounts.218    type AccountId = AccountId;219    /// The aggregated dispatch type that is available for extrinsics.220    type Call = Call;221    /// The lookup mechanism to get account ID from whatever is passed in dispatchers.222    type Lookup = IdentityLookup<AccountId>;223    /// The index type for storing how many extrinsics an account has signed.224    type Index = Index;225    /// The index type for blocks.226    type BlockNumber = BlockNumber;227    /// The type for hashing blocks and tries.228    type Hash = Hash;229    /// The hashing algorithm used.230    type Hashing = BlakeTwo256;231    /// The header type.232    type Header = generic::Header<BlockNumber, BlakeTwo256>;233    /// The ubiquitous event type.234    type Event = Event;235    /// The ubiquitous origin type.236    type Origin = Origin;237    /// Maximum number of block number to block hash mappings to keep (oldest pruned first).238    type BlockHashCount = BlockHashCount;239    /// Maximum weight of each block.240    type MaximumBlockWeight = MaximumBlockWeight;241    /// The weight of database operations that the runtime can invoke.242    type DbWeight = RocksDbWeight;243    /// The weight of the overhead invoked on the block import process, independent of the244    /// extrinsics included in that block.245    type BlockExecutionWeight = BlockExecutionWeight;246    /// The base weight of any extrinsic processed by the runtime, independent of the247    /// logic of that extrinsic. (Signature verification, nonce increment, fee, etc...)248    type ExtrinsicBaseWeight = ExtrinsicBaseWeight;249    /// The maximum weight that a single extrinsic of `Normal` dispatch class can have,250    /// idependent of the logic of that extrinsics. (Roughly max block weight - average on251    /// initialize cost).252    type MaximumExtrinsicWeight = MaximumExtrinsicWeight;253    /// Maximum size of all encoded transactions (in bytes) that are allowed in one block.254    type MaximumBlockLength = MaximumBlockLength;255    /// Portion of the block weight that is available to all normal transactions.256    type AvailableBlockRatio = AvailableBlockRatio;257    /// Version of the runtime.258    type Version = Version;259 	/// This type is being generated by `construct_runtime!`.260    type PalletInfo = PalletInfo;261    /// What to do if a new account is created.262    type OnNewAccount = ();263    /// What to do if an account is fully reaped from the system.264    type OnKilledAccount = ();265    /// The data to be stored in an account.266    type AccountData = pallet_balances::AccountData<Balance>;267	/// Weight information for the extrinsics of this pallet.268	type SystemWeightInfo = ();269}270271impl pallet_aura::Trait for Runtime {272    type AuthorityId = AuraId;273}274275impl pallet_grandpa::Trait for Runtime {276	type Event = Event;277	type Call = Call;278279	type KeyOwnerProofSystem = ();280281	type KeyOwnerProof =282		<Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, GrandpaId)>>::Proof;283284	type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(285		KeyTypeId,286		GrandpaId,287	)>>::IdentificationTuple;288289	type HandleEquivocation = ();290291	type WeightInfo = ();292}293294parameter_types! {295    pub const MinimumPeriod: u64 = SLOT_DURATION / 2;296}297298impl pallet_timestamp::Trait for Runtime {299	/// A timestamp: milliseconds since the unix epoch.300	type Moment = u64;301	type OnTimestampSet = Aura;302	type MinimumPeriod = MinimumPeriod;303	type WeightInfo = ();304}305306parameter_types! {307    // pub const ExistentialDeposit: u128 = 500;308    pub const ExistentialDeposit: u128 = 0;309	pub const MaxLocks: u32 = 50;310}311312impl pallet_balances::Trait for Runtime {313	type MaxLocks = MaxLocks;314	/// The type for recording an account's balance.315	type Balance = Balance;316	/// The ubiquitous event type.317	type Event = Event;318	type DustRemoval = Treasury;319	type ExistentialDeposit = ExistentialDeposit;320	type AccountStore = System;321	type WeightInfo = ();322}323324pub const MILLICENTS: Balance = 1_000_000_000;325pub const CENTS: Balance = 1_000 * MILLICENTS;326pub const DOLLARS: Balance = 100 * CENTS;327328parameter_types! {329    pub const TombstoneDeposit: Balance = 0;330    pub const RentByteFee: Balance = 4 * MILLICENTS;331    pub const RentDepositOffset: Balance = 1000 * MILLICENTS;332    pub const SurchargeReward: Balance = 150 * MILLICENTS;333}334335impl pallet_contracts::Trait for Runtime {336	type Time = Timestamp;337	type Randomness = RandomnessCollectiveFlip;338	type Currency = Balances;339	type Event = Event;340	type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminer<Runtime>;341	type TrieIdGenerator = pallet_contracts::TrieIdFromParentCounter<Runtime>;342	type RentPayment = ();343	type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;344	type TombstoneDeposit = TombstoneDeposit;345	type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset;346	type RentByteFee = RentByteFee;347	type RentDepositOffset = RentDepositOffset;348	type SurchargeReward = SurchargeReward;349	type MaxDepth = pallet_contracts::DefaultMaxDepth;350	type MaxValueSize = pallet_contracts::DefaultMaxValueSize;351	type WeightPrice = pallet_transaction_payment::Module<Self>;352}353354parameter_types! {355	pub const TransactionByteFee: Balance = 10 * MILLICENTS;356}357358impl pallet_transaction_payment::Trait for Runtime {359    type Currency = pallet_balances::Module<Runtime>;360    type OnTransactionPayment = Treasury;361    type TransactionByteFee = TransactionByteFee;362    type WeightToFee = IdentityFee<Balance>;363    type FeeMultiplierUpdate =  ();364}365366parameter_types! {367	pub const ProposalBond: Permill = Permill::from_percent(5);368	pub const ProposalBondMinimum: Balance = 1 * DOLLARS;369	pub const SpendPeriod: BlockNumber = 5 * MINUTES;370	pub const Burn: Permill = Permill::from_percent(0);371	pub const TipCountdown: BlockNumber = 1 * DAYS;372	pub const TipFindersFee: Percent = Percent::from_percent(20);373	pub const TipReportDepositBase: Balance = 1 * DOLLARS;374	pub const DataDepositPerByte: Balance = 1 * CENTS;375	pub const BountyDepositBase: Balance = 1 * DOLLARS;376	pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;377	pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry");378	pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;379	pub const MaximumReasonLength: u32 = 16384;380	pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);381	pub const BountyValueMinimum: Balance = 5 * DOLLARS;382}383384impl pallet_treasury::Trait for Runtime {385	type ModuleId = TreasuryModuleId;386	type Currency = Balances;387    type ApproveOrigin = EnsureRoot<AccountId>;388	type RejectOrigin = EnsureRoot<AccountId>;389	type Tippers = ValiudatorsOnly<Self>;390	type TipCountdown = TipCountdown;391	type TipFindersFee = TipFindersFee;392	type TipReportDepositBase = TipReportDepositBase;393	type DataDepositPerByte = DataDepositPerByte;394	type Event = Event;395	type OnSlash = ();396	type ProposalBond = ProposalBond;397	type ProposalBondMinimum = ProposalBondMinimum;398	type SpendPeriod = SpendPeriod;399	type Burn = Burn;400	type BountyDepositBase = BountyDepositBase;401	type BountyDepositPayoutDelay = BountyDepositPayoutDelay;402	type BountyUpdatePeriod = BountyUpdatePeriod;403	type BountyCuratorDeposit = BountyCuratorDeposit;404	type BountyValueMinimum = BountyValueMinimum;405	type MaximumReasonLength = MaximumReasonLength;406	type BurnDestination = ();407	type WeightInfo = ();408}409410impl pallet_sudo::Trait for Runtime {411    type Event = Event;412    type Call = Call;413}414415/// Used for the module nft in `./nft.rs`416impl pallet_nft::Trait for Runtime {417    type Event = Event;418    type WeightInfo = nft_weights::WeightInfo;419}420421construct_runtime!(422    pub enum Runtime where423        Block = Block,424        NodeBlock = opaque::Block,425        UncheckedExtrinsic = UncheckedExtrinsic426    {427        System: system::{Module, Call, Config, Storage, Event<T>},428        RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},429        Contracts: pallet_contracts::{Module, Call, Config, Storage, Event<T>},430        Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},431        Aura: pallet_aura::{Module, Config<T>, Inherent},432        Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},433        Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},434        TransactionPayment: pallet_transaction_payment::{Module, Storage},435        Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},436        Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},437        Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},438    }439);440441/// The address format for describing accounts.442pub type Address = AccountId;443/// Block header type as expected by this runtime.444pub type Header = generic::Header<BlockNumber, BlakeTwo256>;445/// Block type as expected by this runtime.446pub type Block = generic::Block<Header, UncheckedExtrinsic>;447/// A Block signed with a Justification448pub type SignedBlock = generic::SignedBlock<Block>;449/// BlockId type as expected by this runtime.450pub type BlockId = generic::BlockId<Block>;451/// The SignedExtension to the basic transaction logic.452pub type SignedExtra = (453    system::CheckSpecVersion<Runtime>,454    system::CheckTxVersion<Runtime>,455    system::CheckGenesis<Runtime>,456    system::CheckEra<Runtime>,457    system::CheckNonce<Runtime>,458    system::CheckWeight<Runtime>,459    pallet_nft::ChargeTransactionPayment<Runtime>,460);461/// Unchecked extrinsic type as expected by this runtime.462pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;463/// Extrinsic type that has already been checked.464pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;465/// Executive: handles dispatch to the various modules.466pub type Executive =467    frame_executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;468469impl_runtime_apis! {470471    impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>472    for Runtime473    {474        fn call(475            origin: AccountId,476            dest: AccountId,477            value: Balance,478            gas_limit: u64,479            input_data: Vec<u8>,480        ) -> ContractExecResult {481			let (exec_result, gas_consumed) =482				Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data);483			match exec_result {484				Ok(v) => ContractExecResult::Success {485					flags: v.flags.bits(),486					data: v.data,487					gas_consumed: gas_consumed,488				},489				Err(_) => ContractExecResult::Error,490			}491        }492493        fn get_storage(494            address: AccountId,495            key: [u8; 32],496        ) -> pallet_contracts_primitives::GetStorageResult {497            Contracts::get_storage(address, key)498        }499500        fn rent_projection(501            address: AccountId,502        ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {503            Contracts::rent_projection(address)504        }505    }506507    impl sp_api::Core<Block> for Runtime {508        fn version() -> RuntimeVersion {509            VERSION510        }511512        fn execute_block(block: Block) {513            Executive::execute_block(block)514        }515516        fn initialize_block(header: &<Block as BlockT>::Header) {517            Executive::initialize_block(header)518        }519    }520521    impl sp_api::Metadata<Block> for Runtime {522        fn metadata() -> OpaqueMetadata {523            Runtime::metadata().into()524        }525    }526527    impl sp_block_builder::BlockBuilder<Block> for Runtime {528        fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {529            Executive::apply_extrinsic(extrinsic)530        }531532        fn finalize_block() -> <Block as BlockT>::Header {533            Executive::finalize_block()534        }535536        fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {537            data.create_extrinsics()538        }539540        fn check_inherents(541            block: Block,542            data: sp_inherents::InherentData,543        ) -> sp_inherents::CheckInherentsResult {544            data.check_extrinsics(&block)545        }546547        fn random_seed() -> <Block as BlockT>::Hash {548            RandomnessCollectiveFlip::random_seed()549        }550    }551552    impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {553        fn validate_transaction(554            source: TransactionSource,555            tx: <Block as BlockT>::Extrinsic,556        ) -> TransactionValidity {557            Executive::validate_transaction(source, tx)558        }559    }560561    impl sp_offchain::OffchainWorkerApi<Block> for Runtime {562        fn offchain_worker(header: &<Block as BlockT>::Header) {563            Executive::offchain_worker(header)564        }565    }566567    impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {568        fn slot_duration() -> u64 {569            Aura::slot_duration()570        }571572        fn authorities() -> Vec<AuraId> {573            Aura::authorities()574        }575    }576577    impl sp_session::SessionKeys<Block> for Runtime {578        fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {579            opaque::SessionKeys::generate(seed)580        }581582        fn decode_session_keys(583            encoded: Vec<u8>,584        ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {585            opaque::SessionKeys::decode_into_raw_public_keys(&encoded)586        }587    }588589	impl fg_primitives::GrandpaApi<Block> for Runtime {590		fn grandpa_authorities() -> GrandpaAuthorityList {591			Grandpa::grandpa_authorities()592		}593594		fn submit_report_equivocation_unsigned_extrinsic(595			_equivocation_proof: fg_primitives::EquivocationProof<596				<Block as BlockT>::Hash,597				NumberFor<Block>,598			>,599			_key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof,600		) -> Option<()> {601			None602		}603604		fn generate_key_ownership_proof(605			_set_id: fg_primitives::SetId,606			_authority_id: GrandpaId,607		) -> Option<fg_primitives::OpaqueKeyOwnershipProof> {608			// NOTE: this is the only implementation possible since we've609			// defined our key owner proof type as a bottom type (i.e. a type610			// with no values).611			None612		}613    }614    615	impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {616		fn account_nonce(account: AccountId) -> Index {617			System::account_nonce(account)618		}619	}620621	impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {622		fn query_info(623			uxt: <Block as BlockT>::Extrinsic,624			len: u32,625		) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {626			TransactionPayment::query_info(uxt, len)627		}628	}629630    #[cfg(feature = "runtime-benchmarks")]631	impl frame_benchmarking::Benchmark<Block> for Runtime {632		fn dispatch_benchmark(633			config: frame_benchmarking::BenchmarkConfig634		) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {635			use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};636637			let whitelist: Vec<TrackedStorageKey> = vec![638				// Alice account639				hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").to_vec().into(),640				// // Total Issuance641				// hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),642				// // Execution Phase643				// hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),644				// // Event Count645				// hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),646				// // System Events647				// hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),648            ];649650			let mut batches = Vec::<BenchmarkBatch>::new();651			let params = (&config, &whitelist);652653			add_benchmark!(params, batches, pallet_nft, Nft);654655			if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }656			Ok(batches)657		}658	}659}
after · runtime/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.78#![cfg_attr(not(feature = "std"), no_std)]9// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.10#![recursion_limit = "256"]1112// Make the WASM binary available.13#[cfg(feature = "std")]14include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1516use pallet_contracts_rpc_runtime_api::ContractExecResult;17use pallet_grandpa::fg_primitives;18use pallet_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};19use sp_api::impl_runtime_apis;20use sp_consensus_aura::sr25519::AuthorityId as AuraId;21use sp_core::{ crypto::KeyTypeId, crypto::Public, OpaqueMetadata };22use sp_runtime::{23    create_runtime_str, generic, impl_opaque_keys,24    traits::{25        Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, 26        IdentityLookup, NumberFor, Saturating, Verify,27    },28    transaction_validity::{TransactionSource, TransactionValidity},29    ApplyExtrinsicResult, MultiSignature,30};31use sp_std::prelude::*;32#[cfg(feature = "std")]33use sp_version::NativeVersion;34use sp_version::RuntimeVersion;3536// A few exports that help ease life for downstream crates.37pub use pallet_balances::Call as BalancesCall;38pub use pallet_contracts::Schedule as ContractsSchedule;39pub use frame_support::{40    construct_runtime,41    dispatch::DispatchResult,42    parameter_types,43    traits::{44        Currency, ExistenceRequirement, Get, KeyOwnerProofSystem, OnUnbalanced, Randomness,45        WithdrawReason, LockIdentifier,46    },47    weights::{48        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},49        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,50        WeightToFeePolynomial,51    },52    StorageValue,53};54#[cfg(any(feature = "std", test))]55pub use sp_runtime::BuildStorage;56use sp_runtime:: { Perbill, Permill, Percent, ModuleId };57use frame_system::{self as system, EnsureRoot };58use sp_std::{marker::PhantomData};5960pub use pallet_timestamp::Call as TimestampCall;6162/// Struct that handles the conversion of Balance -> `u64`. This is used for63/// staking's election calculation.64pub struct CurrencyToVoteHandler;6566impl CurrencyToVoteHandler {67	fn factor() -> Balance {68		(Balances::total_issuance() / u64::max_value() as Balance).max(1)69	}70}7172impl Convert<Balance, u64> for CurrencyToVoteHandler {73	fn convert(x: Balance) -> u64 {74		(x / Self::factor()) as u6475	}76}7778impl Convert<u128, Balance> for CurrencyToVoteHandler {79	fn convert(x: u128) -> Balance {80		x * Self::factor()81	}82}8384/// Re-export a nft pallet85/// TODO: Check this re-export. Is this safe and good style?86extern crate pallet_nft;87pub use pallet_nft::*;8889/// An index to a block.90pub type BlockNumber = u32;9192/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.93pub type Signature = MultiSignature;9495/// Some way of identifying an account on the chain. We intentionally make it equivalent96/// to the public key of our transaction signing scheme.97pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;9899/// The type for looking up accounts. We don't expect more than 4 billion of them, but you100/// never know...101pub type AccountIndex = u32;102103/// Balance of an account.104pub type Balance = u128;105106/// Index of a transaction in the chain.107pub type Index = u32;108109/// A hash of some data used by the chain.110pub type Hash = sp_core::H256;111112/// Digest item type.113pub type DigestItem = generic::DigestItem<Hash>;114115mod nft_weights;116117/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know118/// the specifics of the runtime. They can then be made to be agnostic over specific formats119/// of data like extrinsics, allowing for them to continue syncing the network through upgrades120/// to even the core data structures.121pub mod opaque {122    use super::*;123124    pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;125126    /// Opaque block header type.127    pub type Header = generic::Header<BlockNumber, BlakeTwo256>;128    /// Opaque block type.129    pub type Block = generic::Block<Header, UncheckedExtrinsic>;130    /// Opaque block identifier type.131    pub type BlockId = generic::BlockId<Block>;132133    impl_opaque_keys! {134        pub struct SessionKeys {135            pub aura: Aura,136            pub grandpa: Grandpa,137        }138    }139}140141/// This runtime version.142pub const VERSION: RuntimeVersion = RuntimeVersion {143    spec_name: create_runtime_str!("nft"),144    impl_name: create_runtime_str!("nft"),145    authoring_version: 1,146    spec_version: 2,147    impl_version: 1,148    apis: RUNTIME_API_VERSIONS,149    transaction_version: 1,150};151152pub const MILLISECS_PER_BLOCK: u64 = 6000;153154pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;155156// These time units are defined in number of blocks.157pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);158pub const HOURS: BlockNumber = MINUTES * 60;159pub const DAYS: BlockNumber = HOURS * 24;160161/// The version information used to identify this runtime when compiled natively.162#[cfg(feature = "std")]163pub fn native_version() -> NativeVersion {164    NativeVersion {165        runtime_version: VERSION,166        can_author_with: Default::default(),167    }168}169170/// Provides a membership set with only the configured aura users171pub struct ValiudatorsOnly<Runtime: pallet_aura::Trait>(PhantomData<Runtime>);172impl frame_support::traits::Contains<AccountId> for ValiudatorsOnly<Runtime> {173	fn contains(t: &AccountId) -> bool {174        let arr: [u8; 32] = *t.as_ref();175        let raw_key: Vec<u8> = Vec::from(arr);176177        match pallet_aura::Module::<Runtime>::authorities().iter().find(|auth| auth.to_raw_vec() == raw_key) {178            Some(_) => true,179            None => false,180        }  181	}182	fn sorted_members() -> Vec<AccountId> {183        let mut members: Vec<AccountId> = Vec::new();184        for auth in pallet_aura::Module::<Runtime>::authorities() {185            let mut arr: [u8; 32] = Default::default(); 186            let bor_arr = auth.clone().to_raw_vec();187            let slice = bor_arr.as_slice();188            arr.copy_from_slice(slice);189            members.push(AccountId::from(arr));190        }191        members  192	}193	fn count() -> usize {194        pallet_aura::Module::<Runtime>::authorities().len()195	}196}197198impl frame_support::traits::ContainsLengthBound for ValiudatorsOnly<Runtime> {199	fn min_len() -> usize {200		1201	}202	fn max_len() -> usize {203		100204	}205}206207parameter_types! {208    pub const BlockHashCount: BlockNumber = 2400;209    /// We allow for 2 seconds of compute with a 6 second average block time.210    pub const MaximumBlockWeight: Weight = 2 * WEIGHT_PER_SECOND;211    pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);212    /// Assume 10% of weight for average on_initialize calls.213    pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()214        .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();215    pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;216    pub const Version: RuntimeVersion = VERSION;217}218219impl system::Trait for Runtime {220    /// The basic call filter to use in dispatchable.221    type BaseCallFilter = ();222    /// The identifier used to distinguish between accounts.223    type AccountId = AccountId;224    /// The aggregated dispatch type that is available for extrinsics.225    type Call = Call;226    /// The lookup mechanism to get account ID from whatever is passed in dispatchers.227    type Lookup = IdentityLookup<AccountId>;228    /// The index type for storing how many extrinsics an account has signed.229    type Index = Index;230    /// The index type for blocks.231    type BlockNumber = BlockNumber;232    /// The type for hashing blocks and tries.233    type Hash = Hash;234    /// The hashing algorithm used.235    type Hashing = BlakeTwo256;236    /// The header type.237    type Header = generic::Header<BlockNumber, BlakeTwo256>;238    /// The ubiquitous event type.239    type Event = Event;240    /// The ubiquitous origin type.241    type Origin = Origin;242    /// Maximum number of block number to block hash mappings to keep (oldest pruned first).243    type BlockHashCount = BlockHashCount;244    /// Maximum weight of each block.245    type MaximumBlockWeight = MaximumBlockWeight;246    /// The weight of database operations that the runtime can invoke.247    type DbWeight = RocksDbWeight;248    /// The weight of the overhead invoked on the block import process, independent of the249    /// extrinsics included in that block.250    type BlockExecutionWeight = BlockExecutionWeight;251    /// The base weight of any extrinsic processed by the runtime, independent of the252    /// logic of that extrinsic. (Signature verification, nonce increment, fee, etc...)253    type ExtrinsicBaseWeight = ExtrinsicBaseWeight;254    /// The maximum weight that a single extrinsic of `Normal` dispatch class can have,255    /// idependent of the logic of that extrinsics. (Roughly max block weight - average on256    /// initialize cost).257    type MaximumExtrinsicWeight = MaximumExtrinsicWeight;258    /// Maximum size of all encoded transactions (in bytes) that are allowed in one block.259    type MaximumBlockLength = MaximumBlockLength;260    /// Portion of the block weight that is available to all normal transactions.261    type AvailableBlockRatio = AvailableBlockRatio;262    /// Version of the runtime.263    type Version = Version;264 	/// This type is being generated by `construct_runtime!`.265    type PalletInfo = PalletInfo;266    /// What to do if a new account is created.267    type OnNewAccount = ();268    /// What to do if an account is fully reaped from the system.269    type OnKilledAccount = ();270    /// The data to be stored in an account.271    type AccountData = pallet_balances::AccountData<Balance>;272	/// Weight information for the extrinsics of this pallet.273	type SystemWeightInfo = ();274}275276impl pallet_aura::Trait for Runtime {277    type AuthorityId = AuraId;278}279280impl pallet_grandpa::Trait for Runtime {281	type Event = Event;282	type Call = Call;283284	type KeyOwnerProofSystem = ();285286	type KeyOwnerProof =287		<Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, GrandpaId)>>::Proof;288289	type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(290		KeyTypeId,291		GrandpaId,292	)>>::IdentificationTuple;293294	type HandleEquivocation = ();295296	type WeightInfo = ();297}298299parameter_types! {300    pub const MinimumPeriod: u64 = SLOT_DURATION / 2;301}302303impl pallet_timestamp::Trait for Runtime {304	/// A timestamp: milliseconds since the unix epoch.305	type Moment = u64;306	type OnTimestampSet = Aura;307	type MinimumPeriod = MinimumPeriod;308	type WeightInfo = ();309}310311parameter_types! {312    // pub const ExistentialDeposit: u128 = 500;313    pub const ExistentialDeposit: u128 = 0;314	pub const MaxLocks: u32 = 50;315}316317impl pallet_balances::Trait for Runtime {318	type MaxLocks = MaxLocks;319	/// The type for recording an account's balance.320	type Balance = Balance;321	/// The ubiquitous event type.322	type Event = Event;323	type DustRemoval = Treasury;324	type ExistentialDeposit = ExistentialDeposit;325	type AccountStore = System;326	type WeightInfo = ();327}328329pub const MILLICENTS: Balance = 1_000_000_000;330pub const CENTS: Balance = 1_000 * MILLICENTS;331pub const DOLLARS: Balance = 100 * CENTS;332333parameter_types! {334    pub const TombstoneDeposit: Balance = 0;335    pub const RentByteFee: Balance = 4 * MILLICENTS;336    pub const RentDepositOffset: Balance = 1000 * MILLICENTS;337    pub const SurchargeReward: Balance = 150 * MILLICENTS;338}339340impl pallet_contracts::Trait for Runtime {341	type Time = Timestamp;342	type Randomness = RandomnessCollectiveFlip;343	type Currency = Balances;344	type Event = Event;345	type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminer<Runtime>;346	type TrieIdGenerator = pallet_contracts::TrieIdFromParentCounter<Runtime>;347	type RentPayment = ();348	type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;349	type TombstoneDeposit = TombstoneDeposit;350	type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset;351	type RentByteFee = RentByteFee;352	type RentDepositOffset = RentDepositOffset;353	type SurchargeReward = SurchargeReward;354	type MaxDepth = pallet_contracts::DefaultMaxDepth;355	type MaxValueSize = pallet_contracts::DefaultMaxValueSize;356	type WeightPrice = pallet_transaction_payment::Module<Self>;357}358359parameter_types! {360	pub const TransactionByteFee: Balance = 10 * MILLICENTS;361}362363impl pallet_transaction_payment::Trait for Runtime {364    type Currency = pallet_balances::Module<Runtime>;365    type OnTransactionPayment = Treasury;366    type TransactionByteFee = TransactionByteFee;367    type WeightToFee = IdentityFee<Balance>;368    type FeeMultiplierUpdate =  ();369}370371parameter_types! {372	pub const ProposalBond: Permill = Permill::from_percent(5);373	pub const ProposalBondMinimum: Balance = 1 * DOLLARS;374	pub const SpendPeriod: BlockNumber = 5 * MINUTES;375	pub const Burn: Permill = Permill::from_percent(0);376	pub const TipCountdown: BlockNumber = 1 * DAYS;377	pub const TipFindersFee: Percent = Percent::from_percent(20);378	pub const TipReportDepositBase: Balance = 1 * DOLLARS;379	pub const DataDepositPerByte: Balance = 1 * CENTS;380	pub const BountyDepositBase: Balance = 1 * DOLLARS;381	pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;382	pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry");383	pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;384	pub const MaximumReasonLength: u32 = 16384;385	pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);386	pub const BountyValueMinimum: Balance = 5 * DOLLARS;387}388389impl pallet_treasury::Trait for Runtime {390	type ModuleId = TreasuryModuleId;391	type Currency = Balances;392    type ApproveOrigin = EnsureRoot<AccountId>;393	type RejectOrigin = EnsureRoot<AccountId>;394	type Tippers = ValiudatorsOnly<Self>;395	type TipCountdown = TipCountdown;396	type TipFindersFee = TipFindersFee;397	type TipReportDepositBase = TipReportDepositBase;398	type DataDepositPerByte = DataDepositPerByte;399	type Event = Event;400	type OnSlash = ();401	type ProposalBond = ProposalBond;402	type ProposalBondMinimum = ProposalBondMinimum;403	type SpendPeriod = SpendPeriod;404	type Burn = Burn;405	type BountyDepositBase = BountyDepositBase;406	type BountyDepositPayoutDelay = BountyDepositPayoutDelay;407	type BountyUpdatePeriod = BountyUpdatePeriod;408	type BountyCuratorDeposit = BountyCuratorDeposit;409	type BountyValueMinimum = BountyValueMinimum;410	type MaximumReasonLength = MaximumReasonLength;411	type BurnDestination = ();412	type WeightInfo = ();413}414415impl pallet_sudo::Trait for Runtime {416    type Event = Event;417    type Call = Call;418}419420parameter_types! {421	pub const MinVestedTransfer: Balance = 100 * DOLLARS;422}423424impl pallet_vesting::Trait for Runtime {425	type Event = Event;426	type Currency = Balances;427	type BlockNumberToBalance = ConvertInto;428	type MinVestedTransfer = MinVestedTransfer;429	type WeightInfo = ();430}431432/// Used for the module nft in `./nft.rs`433impl pallet_nft::Trait for Runtime {434    type Event = Event;435    type WeightInfo = nft_weights::WeightInfo;436}437438construct_runtime!(439    pub enum Runtime where440        Block = Block,441        NodeBlock = opaque::Block,442        UncheckedExtrinsic = UncheckedExtrinsic443    {444        System: system::{Module, Call, Config, Storage, Event<T>},445        RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},446        Contracts: pallet_contracts::{Module, Call, Config, Storage, Event<T>},447        Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},448        Aura: pallet_aura::{Module, Config<T>, Inherent},449        Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},450        Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},451        TransactionPayment: pallet_transaction_payment::{Module, Storage},452        Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},453        Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},454        Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},455        Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},456    }457);458459/// The address format for describing accounts.460pub type Address = AccountId;461/// Block header type as expected by this runtime.462pub type Header = generic::Header<BlockNumber, BlakeTwo256>;463/// Block type as expected by this runtime.464pub type Block = generic::Block<Header, UncheckedExtrinsic>;465/// A Block signed with a Justification466pub type SignedBlock = generic::SignedBlock<Block>;467/// BlockId type as expected by this runtime.468pub type BlockId = generic::BlockId<Block>;469/// The SignedExtension to the basic transaction logic.470pub type SignedExtra = (471    system::CheckSpecVersion<Runtime>,472    system::CheckTxVersion<Runtime>,473    system::CheckGenesis<Runtime>,474    system::CheckEra<Runtime>,475    system::CheckNonce<Runtime>,476    system::CheckWeight<Runtime>,477    pallet_nft::ChargeTransactionPayment<Runtime>,478);479/// Unchecked extrinsic type as expected by this runtime.480pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;481/// Extrinsic type that has already been checked.482pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;483/// Executive: handles dispatch to the various modules.484pub type Executive =485    frame_executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;486487impl_runtime_apis! {488489    impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>490    for Runtime491    {492        fn call(493            origin: AccountId,494            dest: AccountId,495            value: Balance,496            gas_limit: u64,497            input_data: Vec<u8>,498        ) -> ContractExecResult {499			let (exec_result, gas_consumed) =500				Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data);501			match exec_result {502				Ok(v) => ContractExecResult::Success {503					flags: v.flags.bits(),504					data: v.data,505					gas_consumed: gas_consumed,506				},507				Err(_) => ContractExecResult::Error,508			}509        }510511        fn get_storage(512            address: AccountId,513            key: [u8; 32],514        ) -> pallet_contracts_primitives::GetStorageResult {515            Contracts::get_storage(address, key)516        }517518        fn rent_projection(519            address: AccountId,520        ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {521            Contracts::rent_projection(address)522        }523    }524525    impl sp_api::Core<Block> for Runtime {526        fn version() -> RuntimeVersion {527            VERSION528        }529530        fn execute_block(block: Block) {531            Executive::execute_block(block)532        }533534        fn initialize_block(header: &<Block as BlockT>::Header) {535            Executive::initialize_block(header)536        }537    }538539    impl sp_api::Metadata<Block> for Runtime {540        fn metadata() -> OpaqueMetadata {541            Runtime::metadata().into()542        }543    }544545    impl sp_block_builder::BlockBuilder<Block> for Runtime {546        fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {547            Executive::apply_extrinsic(extrinsic)548        }549550        fn finalize_block() -> <Block as BlockT>::Header {551            Executive::finalize_block()552        }553554        fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {555            data.create_extrinsics()556        }557558        fn check_inherents(559            block: Block,560            data: sp_inherents::InherentData,561        ) -> sp_inherents::CheckInherentsResult {562            data.check_extrinsics(&block)563        }564565        fn random_seed() -> <Block as BlockT>::Hash {566            RandomnessCollectiveFlip::random_seed()567        }568    }569570    impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {571        fn validate_transaction(572            source: TransactionSource,573            tx: <Block as BlockT>::Extrinsic,574        ) -> TransactionValidity {575            Executive::validate_transaction(source, tx)576        }577    }578579    impl sp_offchain::OffchainWorkerApi<Block> for Runtime {580        fn offchain_worker(header: &<Block as BlockT>::Header) {581            Executive::offchain_worker(header)582        }583    }584585    impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {586        fn slot_duration() -> u64 {587            Aura::slot_duration()588        }589590        fn authorities() -> Vec<AuraId> {591            Aura::authorities()592        }593    }594595    impl sp_session::SessionKeys<Block> for Runtime {596        fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {597            opaque::SessionKeys::generate(seed)598        }599600        fn decode_session_keys(601            encoded: Vec<u8>,602        ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {603            opaque::SessionKeys::decode_into_raw_public_keys(&encoded)604        }605    }606607	impl fg_primitives::GrandpaApi<Block> for Runtime {608		fn grandpa_authorities() -> GrandpaAuthorityList {609			Grandpa::grandpa_authorities()610		}611612		fn submit_report_equivocation_unsigned_extrinsic(613			_equivocation_proof: fg_primitives::EquivocationProof<614				<Block as BlockT>::Hash,615				NumberFor<Block>,616			>,617			_key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof,618		) -> Option<()> {619			None620		}621622		fn generate_key_ownership_proof(623			_set_id: fg_primitives::SetId,624			_authority_id: GrandpaId,625		) -> Option<fg_primitives::OpaqueKeyOwnershipProof> {626			// NOTE: this is the only implementation possible since we've627			// defined our key owner proof type as a bottom type (i.e. a type628			// with no values).629			None630		}631    }632    633	impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {634		fn account_nonce(account: AccountId) -> Index {635			System::account_nonce(account)636		}637	}638639	impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {640		fn query_info(641			uxt: <Block as BlockT>::Extrinsic,642			len: u32,643		) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {644			TransactionPayment::query_info(uxt, len)645		}646	}647648    #[cfg(feature = "runtime-benchmarks")]649	impl frame_benchmarking::Benchmark<Block> for Runtime {650		fn dispatch_benchmark(651			config: frame_benchmarking::BenchmarkConfig652		) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {653			use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};654655			let whitelist: Vec<TrackedStorageKey> = vec![656				// Alice account657				hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").to_vec().into(),658				// // Total Issuance659				// hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),660				// // Execution Phase661				// hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),662				// // Event Count663				// hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),664				// // System Events665				// hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),666            ];667668			let mut batches = Vec::<BenchmarkBatch>::new();669			let params = (&config, &whitelist);670671			add_benchmark!(params, batches, pallet_nft, Nft);672673			if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }674			Ok(batches)675		}676	}677}
modifiedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
 
 pub struct WeightInfo;
deletedtests/READMEdiffbeforeafterboth
--- a/tests/README
+++ /dev/null
@@ -1,12 +0,0 @@
-# Tests
-
-## How to run
-
-1. Run `npm install`.
-2. Setup a test node. You can do it using `docker-compose up -d` in parent directory.
-3. Configure tests with env variables or by editing [configuration file](src/config.ts).
-4. Run `npm run test`.
-
-## Don't run on the same node twice
-
-Some tests fail when ran on the same blockchain node twice. Either always use a new node or purge the existing one with `nft purge-chain --dev`. There is also [a script](../purge-running-node.sh) to purge and restart a node, started with docker-compose.
addedtests/README.mddiffbeforeafterboth
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,9 @@
+# Tests
+
+## How to run
+
+1. Run `npm install`.
+2. Setup a test node. You can do it using `docker-compose up -d` in parent directory.
+3. Optional step - configure tests with env variables or by editing [configuration file](src/config.ts).
+4. Run `npm test`.
+
modifiedtests/src/accounts.tsdiffbeforeafterboth
--- a/tests/src/accounts.ts
+++ b/tests/src/accounts.ts
@@ -1,3 +1,9 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 export const bobsPublicKey = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
 export const alicesPublicKey = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
 export const ferdiesPublicKey = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL';
+export const nullPublicKey = '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM';
\ No newline at end of file
modifiedtests/src/blocks-production.test.tsdiffbeforeafterboth
--- a/tests/src/blocks-production.test.ts
+++ b/tests/src/blocks-production.test.ts
@@ -1,8 +1,13 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import usingApi from "./substrate/substrate-api";
 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, () => {
modifiedtests/src/config.tsdiffbeforeafterboth
--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import process from 'process';
 
 const config = {
modifiedtests/src/connection.test.tsdiffbeforeafterboth
--- a/tests/src/connection.test.ts
+++ b/tests/src/connection.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import usingApi from "./substrate/substrate-api";
 import { WsProvider } from '@polkadot/api';
 import * as chai from 'chai';
@@ -7,7 +12,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 +21,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
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.test.ts
@@ -1,13 +1,22 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+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<BlueprintPromise> {
   return new Promise<BlueprintPromise>(async (resolve, reject) => {
@@ -25,7 +34,6 @@
 
 function deployContract(alice: IKeyringPair, blueprint: BlueprintPromise) : Promise<any> {
   return new Promise<any>(async (resolve, reject) => {
-    const endowment = 1000000000000000n;
     const initValue = true;
 
     const unsub = await blueprint.tx
@@ -39,28 +47,25 @@
   });
 }
 
-function runTransaction(privateKey: IKeyringPair, extrinsic: SubmittableExtrinsic<ApiTypes>) {
-  return new Promise<void>(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);
 
-        if(result.findRecord('system', 'ExtrinsicSuccess')) {
-          resolve();
-        }
-        else {
-          reject('Failed to flip value.');
-        }
-      })
-  });
+  // 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);
+
+  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 +74,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 +90,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 +117,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]);
 
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import { default as usingApi } from "./substrate/substrate-api";
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { assert } from 'chai';
 import { alicesPublicKey } from './accounts';
 import privateKey from './substrate/privateKey';
addedtests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/creditFeesToTreasury.test.ts
@@ -0,0 +1,112 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+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);
+    });
+  });
+
+});
+
deletedtests/src/crefitFeesToTreasury.test.tsdiffbeforeafterboth
--- 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);
-    });
-  });
-
-});
-
addedtests/src/destroyCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/destroyCollection.test.ts
@@ -0,0 +1,87 @@
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
+import { createCollectionExpectSuccess, createCollectionExpectFailure } from "./util/helpers";
+import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import privateKey from './substrate/privateKey';
+import { nullPublicKey } from './accounts'; 
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+function getDestroyResult(events: EventRecord[]): boolean {
+  let success: boolean = false;
+  events.forEach(({ phase, event: { data, method, section } }) => {
+    // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
+    if (method == 'ExtrinsicSuccess') {
+      success = true;
+    }
+  });
+  return success;
+}
+
+async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+    // Run the DestroyCollection transaction
+    const alicePrivateKey = privateKey(senderSeed);
+    const tx = api.tx.nft.destroyCollection(collectionId);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getDestroyResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result).to.be.true;
+    expect(collection).to.be.not.null;
+    expect(collection.Owner).to.be.equal(nullPublicKey);
+  });
+}
+
+async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+    // Run the DestroyCollection transaction
+    const alicePrivateKey = privateKey(senderSeed);
+    const tx = api.tx.nft.destroyCollection(collectionId);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getDestroyResult(events);
+
+    // What to expect
+    expect(result).to.be.false;
+  });
+}
+
+describe('integration test: ext. destroyCollection():', () => {
+  it('NFT collection can be destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+  it('Fungible collection can be destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+  it('ReFungible collection can be destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+});
+
+describe('(!negative test!) integration test: ext. destroyCollection():', () => {
+  it('(!negative test!) Destroy a collection that never existed', async () => {
+    await usingApi(async (api) => {
+      // Find the collection that never existed
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      await destroyCollectionExpectFailure(collectionId);
+    });
+  });
+  it('(!negative test!) Destroy a collection that has already been destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectSuccess(collectionId);
+    await destroyCollectionExpectFailure(collectionId);
+  });
+  it('(!negative test!) Destroy a collection using non-owner account', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectFailure(collectionId, '//Bob');
+    await destroyCollectionExpectSuccess(collectionId, '//Alice');
+  });
+});
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { ApiPromise } from "@polkadot/api";
 import { expect } from "chai";
 import usingApi from "./substrate/substrate-api";
@@ -6,20 +11,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<requiredPallets.length; i++) {
+        expect(getModuleNames(api)).to.include(requiredPallets[i]);
+      }
     });
   });
-  it('Balances pallet is present.', async () => {
+  it('Governance and consensus pallets are present', async () => {
     await usingApi(async api => {
-      expect(getModuleNames(api)).to.include('balances');
+      for (let i=0; i<consensusPallets.length; i++) {
+        expect(getModuleNames(api)).to.include(consensusPallets[i]);
+      }
     });
   });
-  it('Contracts pallet is present.', async () => {
+  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);
     });
   });
 });
modifiedtests/src/substrate/get-balance.tsdiffbeforeafterboth
--- a/tests/src/substrate/get-balance.ts
+++ b/tests/src/substrate/get-balance.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { ApiPromise } from "@polkadot/api";
 import promisifySubstrate from "./promisify-substrate";
 import {AccountInfo} from "@polkadot/types/interfaces/system";
modifiedtests/src/substrate/privateKey.tsdiffbeforeafterboth
--- a/tests/src/substrate/privateKey.ts
+++ b/tests/src/substrate/privateKey.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { Keyring } from "@polkadot/api";
 import { IKeyringPair } from "@polkadot/types/types";
 
modifiedtests/src/substrate/promisify-substrate.tsdiffbeforeafterboth
--- a/tests/src/substrate/promisify-substrate.ts
+++ b/tests/src/substrate/promisify-substrate.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import ApiPromise from "@polkadot/api/promise/Api";
 
 type PromiseType<T> = T extends PromiseLike<infer TInner> ? TInner : T;
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { WsProvider, ApiPromise } from "@polkadot/api";
 import type { AccountId, Address, ApplyExtrinsicResult, DispatchError, DispatchInfo, EventRecord, Extrinsic, ExtrinsicStatus, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
 import { IKeyringPair } from "@polkadot/types/types";
modifiedtests/src/substrate/wait-new-blocks.tsdiffbeforeafterboth
--- a/tests/src/substrate/wait-new-blocks.ts
+++ b/tests/src/substrate/wait-new-blocks.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { ApiPromise } from "@polkadot/api";
 
 export default function waitNewBlocks(api: ApiPromise, blocksCount: number = 1): Promise<void> {
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -1,8 +1,15 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { expect, assert } from "chai";
 import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
 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 +30,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 () {};
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -1,10 +1,18 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 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;
@@ -49,7 +57,8 @@
   return result;
 }
 
-export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string) {
+export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string): Promise<number> {
+  let collectionId: number = 0;
   await usingApi(async (api) => {
     // Get number of collections before the transaction
     const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
@@ -75,7 +84,11 @@
     expect(utf16ToStr(collection.Name)).to.be.equal(name);
     expect(utf16ToStr(collection.Description)).to.be.equal(description);
     expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);
+
+    collectionId = result.collectionId;
   });
+
+  return collectionId;
 }
   
 export async function createCollectionExpectFailure(name: string, description: string, tokenPrefix: string, mode: string) {
@@ -98,3 +111,14 @@
   });
 }
   
+export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
+  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
modifiedtests/src/util/util.tsdiffbeforeafterboth
--- a/tests/src/util/util.ts
+++ b/tests/src/util/util.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 export function strToUTF16(str: string): any {
   let buf: number[] = [];
   for (let i=0, strLen=str.length; i < strLen; i++) {