difftreelog
Merge branch 'develop' into feature/NFTPAR-240
in: master
33 files changed
Cargo.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"
LICENSEdiffbeforeafterboth1This is free and unencumbered software released into the public domain.1USETECH PROFESSIONAL CONFIDENTIAL2__________________233Anyone is free to copy, modify, publish, use, compile, sell, or4 [2019] - [2020] UseTech Professional LTD. 4distribute this software, either in source code form or as a compiled5 All Rights Reserved.5binary, for any purpose, commercial or non-commercial, and by any6means.768In jurisdictions that recognize copyright laws, the author or authors7NOTICE: All information contained herein is, and remains9of this software dedicate any and all copyright interest in the8the property of UseTech Professional LTD. and its suppliers,10software to the public domain. We make this dedication for the benefit9if any. The intellectual and technical concepts contained11of the public at large and to the detriment of our heirs and12successors. We intend this dedication to be an overt act of10herein are proprietary to UseTech Professional LTD.13relinquishment in perpetuity of all present and future rights to this11and its suppliers and may be covered by U.S. and Foreign Patents,14software under copyright law.1516THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF12patents in process, and are protected by trade secret or copyright law.18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.19IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR13Dissemination of this information or reproduction of this material20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,14is strictly forbidden unless prior written permission is obtained21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR15from UseTech Professional LTD..22OTHER DEALINGS IN THE SOFTWARE.2324For more information, please refer to <http://unlicense.org>2516README.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
node/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']
node/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() {
node/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,
node/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)]
node/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};
pallets/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)]
runtime/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',
]
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/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.
+//
+
//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
@@ -17,7 +22,7 @@
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{
- Convert, BlakeTwo256, Block as BlockT, IdentifyAccount,
+ Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount,
IdentityLookup, NumberFor, Saturating, Verify,
},
transaction_validity::{TransactionSource, TransactionValidity},
@@ -412,6 +417,18 @@
type Call = Call;
}
+parameter_types! {
+ pub const MinVestedTransfer: Balance = 100 * DOLLARS;
+}
+
+impl pallet_vesting::Trait for Runtime {
+ type Event = Event;
+ type Currency = Balances;
+ type BlockNumberToBalance = ConvertInto;
+ type MinVestedTransfer = MinVestedTransfer;
+ type WeightInfo = ();
+}
+
/// Used for the module nft in `./nft.rs`
impl pallet_nft::Trait for Runtime {
type Event = Event;
@@ -435,6 +452,7 @@
Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},
Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},
+ Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},
}
);
runtime/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;
tests/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.
tests/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`.
+
tests/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
tests/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, () => {
tests/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 = {
tests/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
tests/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]);
tests/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";
tests/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';
tests/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);
+ });
+ });
+
+});
+
tests/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);
- });
- });
-
-});
-
tests/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');
+ });
+});
tests/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);
});
});
});
tests/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";
tests/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";
tests/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;
tests/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";
tests/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> {
tests/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 () {};
tests/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
tests/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++) {