git.delta.rocks / unique-network / refs/commits / 0954b32db3b2

difftreelog

Merge branch 'develop' into feature/transferfromtests

Greg Zaitsev2021-02-09parents: #4f05952 #2109c86.patch.diff
in: master

43 files changed

added.devcontainer/Dockerfilediffbeforeafterboth
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,14 @@
+FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1
+
+RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \
+    apt-get -y install --no-install-recommends libssl-dev pkg-config libclang-dev clang
+
+USER vscode
+
+RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash && \
+    export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" && \
+    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
+    nvm install v12.20.1 && \
+    rustup toolchain install nightly-2020-10-01 && \
+    rustup default nightly-2020-10-01 && \
+    rustup target add wasm32-unknown-unknown
\ No newline at end of file
added.devcontainer/devcontainer.jsondiffbeforeafterboth
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,18 @@
+{
+	"name": "Rust",
+	"build": {
+		"dockerfile": "Dockerfile"
+	},
+	"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
+	"settings": { 
+		"terminal.integrated.shell.linux": "/bin/bash",
+		"lldb.executable": "/usr/bin/lldb",
+		"files.watcherExclude": {
+			"**/target/**": true
+		}
+	},
+	"extensions": [
+		"matklad.rust-analyzer"
+	],
+	"remoteUser": "vscode"
+}
modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -543,9 +543,9 @@
 
 [[package]]
 name = "bumpalo"
-version = "3.5.0"
+version = "3.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f07aa6688c702439a1be0307b6a94dffe1168569e45b9500c1372bc580740d59"
+checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
 
 [[package]]
 name = "byte-slice-cast"
@@ -985,9 +985,9 @@
 
 [[package]]
 name = "ctor"
-version = "0.1.18"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10bcb9d7dcbf7002aaffbb53eac22906b64cdcc127971dcc387d8eb7c95d5560"
+checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
 dependencies = [
  "quote",
  "syn",
@@ -1032,9 +1032,9 @@
 
 [[package]]
 name = "data-encoding"
-version = "2.3.1"
+version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "993a608597367c6377b258c25d7120740f00ed23a2252b729b1932dd7866f908"
+checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
 
 [[package]]
 name = "derive_more"
@@ -1151,7 +1151,7 @@
  "ed25519",
  "rand 0.7.3",
  "serde",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "zeroize",
 ]
 
@@ -1342,9 +1342,9 @@
 
 [[package]]
 name = "flate2"
-version = "1.0.19"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
+checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
 dependencies = [
  "cfg-if 1.0.0",
  "crc32fast",
@@ -1907,7 +1907,7 @@
  "http 0.2.3",
  "indexmap",
  "slab",
- "tokio 0.2.24",
+ "tokio 0.2.25",
  "tokio-util",
  "tracing",
  "tracing-futures",
@@ -2064,9 +2064,9 @@
 
 [[package]]
 name = "httparse"
-version = "1.3.4"
+version = "1.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
+checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
 
 [[package]]
 name = "httpdate"
@@ -2129,9 +2129,9 @@
  "httparse",
  "httpdate",
  "itoa",
- "pin-project 1.0.4",
+ "pin-project 1.0.5",
  "socket2",
- "tokio 0.2.24",
+ "tokio 0.2.25",
  "tower-service",
  "tracing",
  "want 0.3.0",
@@ -2150,7 +2150,7 @@
  "log",
  "rustls 0.18.1",
  "rustls-native-certs",
- "tokio 0.2.24",
+ "tokio 0.2.25",
  "tokio-rustls",
  "webpki",
 ]
@@ -2553,9 +2553,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.82"
+version = "0.2.85"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
+checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
 
 [[package]]
 name = "libloading"
@@ -2606,7 +2606,7 @@
  "libp2p-yamux",
  "parity-multiaddr",
  "parking_lot 0.11.1",
- "pin-project 1.0.4",
+ "pin-project 1.0.5",
  "smallvec 1.6.1",
  "wasm-timer",
 ]
@@ -2631,13 +2631,13 @@
  "multistream-select",
  "parity-multiaddr",
  "parking_lot 0.11.1",
- "pin-project 1.0.4",
+ "pin-project 1.0.5",
  "prost",
  "prost-build",
  "rand 0.7.3",
  "ring",
  "rw-stream-sink",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "smallvec 1.6.1",
  "thiserror",
  "unsigned-varint 0.6.0",
@@ -2715,7 +2715,7 @@
  "prost-build",
  "rand 0.7.3",
  "regex",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "smallvec 1.6.1",
  "unsigned-varint 0.6.0",
  "wasm-timer",
@@ -2755,7 +2755,7 @@
  "prost",
  "prost-build",
  "rand 0.7.3",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "smallvec 1.6.1",
  "uint",
  "unsigned-varint 0.6.0",
@@ -2817,7 +2817,7 @@
  "prost",
  "prost-build",
  "rand 0.7.3",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "snow",
  "static_assertions",
  "x25519-dalek",
@@ -2864,7 +2864,7 @@
 dependencies = [
  "futures 0.3.12",
  "log",
- "pin-project 1.0.4",
+ "pin-project 1.0.5",
  "rand 0.7.3",
  "salsa20",
  "sha3",
@@ -2892,9 +2892,9 @@
 
 [[package]]
 name = "libp2p-swarm"
-version = "0.27.1"
+version = "0.27.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4f89ebb4d8953bda12623e9871959fe728dea3bf6eae0421dc9c42dc821e488"
+checksum = "7955b973e1fd2bd61ffd43ce261c1223f61f4aacd5bae362a924993f9a25fd98"
 dependencies = [
  "either",
  "futures 0.3.12",
@@ -3074,9 +3074,9 @@
 
 [[package]]
 name = "lru"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3aae342b73d57ad0b8b364bd12584819f2c1fe9114285dfcf8b0722607671635"
+checksum = "fe2382d8ed3918ea4ba70d98d5b74e47a168e0331965f3f17cdbc748bdebc5a3"
 dependencies = [
  "hashbrown",
 ]
@@ -3310,7 +3310,7 @@
  "digest 0.9.0",
  "generic-array 0.14.4",
  "multihash-derive",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "unsigned-varint 0.5.1",
 ]
 
@@ -3343,7 +3343,7 @@
  "bytes 1.0.1",
  "futures 0.3.12",
  "log",
- "pin-project 1.0.4",
+ "pin-project 1.0.5",
  "smallvec 1.6.1",
  "unsigned-varint 0.6.0",
 ]
@@ -4269,11 +4269,11 @@
 
 [[package]]
 name = "pin-project"
-version = "1.0.4"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2"
+checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63"
 dependencies = [
- "pin-project-internal 1.0.4",
+ "pin-project-internal 1.0.5",
 ]
 
 [[package]]
@@ -4289,9 +4289,9 @@
 
 [[package]]
 name = "pin-project-internal"
-version = "1.0.4"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71"
+checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4862,9 +4862,9 @@
 
 [[package]]
 name = "ring"
-version = "0.16.19"
+version = "0.16.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
 dependencies = [
  "cc",
  "libc",
@@ -5114,7 +5114,7 @@
  "structopt",
  "thiserror",
  "tiny-bip39",
- "tokio 0.2.24",
+ "tokio 0.2.25",
 ]
 
 [[package]]
@@ -6037,9 +6037,9 @@
 
 [[package]]
 name = "sha-1"
-version = "0.9.2"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
+checksum = "f4b312c3731e3fe78a185e6b9b911a7aa715b8e31cce117975219aab2acf285d"
 dependencies = [
  "block-buffer 0.9.0",
  "cfg-if 1.0.0",
@@ -6062,9 +6062,9 @@
 
 [[package]]
 name = "sha2"
-version = "0.9.2"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
+checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
 dependencies = [
  "block-buffer 0.9.0",
  "cfg-if 1.0.0",
@@ -6171,7 +6171,7 @@
  "rand_core 0.5.1",
  "ring",
  "rustc_version",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "subtle 2.4.0",
  "x25519-dalek",
 ]
@@ -6200,7 +6200,7 @@
  "httparse",
  "log",
  "rand 0.7.3",
- "sha-1 0.9.2",
+ "sha-1 0.9.3",
 ]
 
 [[package]]
@@ -6428,7 +6428,7 @@
  "schnorrkel",
  "secrecy",
  "serde",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "sp-debug-derive",
  "sp-externalities",
  "sp-runtime-interface",
@@ -6973,7 +6973,7 @@
  "hyper 0.13.9",
  "log",
  "prometheus",
- "tokio 0.2.24",
+ "tokio 0.2.25",
 ]
 
 [[package]]
@@ -7083,9 +7083,9 @@
 
 [[package]]
 name = "thread_local"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915"
+checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
 dependencies = [
  "once_cell",
 ]
@@ -7122,7 +7122,7 @@
  "pbkdf2 0.4.0",
  "rand 0.7.3",
  "rustc-hash",
- "sha2 0.9.2",
+ "sha2 0.9.3",
  "thiserror",
  "unicode-normalization",
  "zeroize",
@@ -7178,9 +7178,9 @@
 
 [[package]]
 name = "tokio"
-version = "0.2.24"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
+checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
 dependencies = [
  "bytes 0.5.6",
  "fnv",
@@ -7302,7 +7302,7 @@
 dependencies = [
  "futures-core",
  "rustls 0.18.1",
- "tokio 0.2.24",
+ "tokio 0.2.25",
  "webpki",
 ]
 
@@ -7412,7 +7412,7 @@
  "futures-sink",
  "log",
  "pin-project-lite 0.1.11",
- "tokio 0.2.24",
+ "tokio 0.2.25",
 ]
 
 [[package]]
@@ -7432,9 +7432,9 @@
 
 [[package]]
 name = "tracing"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
+checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3"
 dependencies = [
  "cfg-if 1.0.0",
  "log",
@@ -7445,9 +7445,9 @@
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.11"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada"
+checksum = "43f080ea7e4107844ef4766459426fa2d5c1ada2e47edba05dc7fa99d9629f47"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -7518,9 +7518,9 @@
 
 [[package]]
 name = "trie-db"
-version = "0.22.2"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc176c377eb24d652c9c69c832c832019011b6106182bf84276c66b66d5c9a6"
+checksum = "ec051edf7f0fc9499a2cb0947652cab2148b9d7f61cee7605e312e9f970dacaf"
 dependencies = [
  "hash-db",
  "hashbrown",
@@ -8032,18 +8032,18 @@
 
 [[package]]
 name = "wast"
-version = "32.0.0"
+version = "33.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c24a3ee360d01d60ed0a0f960ab76a6acce64348cdb0bf8699c2a866fad57c7c"
+checksum = "1d04fe175c7f78214971293e7d8875673804e736092206a3a4544dbc12811c1b"
 dependencies = [
  "leb128",
 ]
 
 [[package]]
 name = "wat"
-version = "1.0.33"
+version = "1.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e8f7f34773fa6318e8897283abf7941c1f250faae4e1a52f82df09c3bad7cce"
+checksum = "7ec9c6ee01ae07a26adadcdfed22c7a97e0b8cbee9c06e0e96076ece5aeb5cfe"
 dependencies = [
  "wast",
 ]
modifiedDockerfilediffbeforeafterboth
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,9 @@
 # ===== BUILD ======
 
-FROM phusion/baseimage:0.10.2 as builder
+FROM phusion/baseimage:18.04-1.0.0 as builder
 LABEL maintainer="gz@usetech.com"
 
-ENV WASM_TOOLCHAIN=nightly-2020-10-01
+ENV WASM_TOOLCHAIN=nightly-2021-01-27
 
 ARG PROFILE=release
 
@@ -37,7 +37,7 @@
 
 # ===== RUN ======
 
-FROM phusion/baseimage:0.10.2
+FROM phusion/baseimage:18.04-1.0.0
 ARG PROFILE=release
 
 COPY --from=builder /nft_parachain/target/$PROFILE/nft /usr/local/bin
modifiedREADME.mddiffbeforeafterboth
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@
 
 ```bash
 rustup toolchain install 1.49.0
-rustup toolchain install nightly-2020-10-01
+rustup toolchain install nightly-2020-01-27
 rustup default nightly-2021-01-27
 ```
 
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/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#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;58pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5960// Structs61// #region6263pub type CollectionId = u32;64pub type TokenId = u32;65pub type DecimalPoints = u8;6667#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]68#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]69pub enum CollectionMode {70    Invalid,71    NFT,72    // decimal points73    Fungible(DecimalPoints),74    // decimal points75    ReFungible(DecimalPoints),76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible(_) => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188}189190impl Default for CollectionLimits {191    fn default() -> CollectionLimits {192        CollectionLimits { 193            account_token_ownership_limit: 10_000_000, 194            token_limit: u32::max_value(),195            sponsored_data_size: u32::max_value(), 196            sponsor_transfer_timeout: 14400 }197    }198}199200#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]201#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]202pub struct ChainLimits {203    pub collection_numbers_limit: u32,204    pub account_token_ownership_limit: u32,205    pub collections_admins_limit: u64,206    pub custom_data_limit: u32,207208    // Timeouts for item types in passed blocks209    pub nft_sponsor_transfer_timeout: u32,210    pub fungible_sponsor_transfer_timeout: u32,211    pub refungible_sponsor_transfer_timeout: u32,212213    // Schema limits214    pub offchain_schema_limit: u32,215    pub variable_on_chain_schema_limit: u32,216    pub const_on_chain_schema_limit: u32,217}218219pub trait WeightInfo {220	fn create_collection() -> Weight;221	fn destroy_collection() -> Weight;222	fn add_to_white_list() -> Weight;223	fn remove_from_white_list() -> Weight;224    fn set_public_access_mode() -> Weight;225    fn set_mint_permission() -> Weight;226    fn change_collection_owner() -> Weight;227    fn add_collection_admin() -> Weight;228    fn remove_collection_admin() -> Weight;229    fn set_collection_sponsor() -> Weight;230    fn confirm_sponsorship() -> Weight;231    fn remove_collection_sponsor() -> Weight;232    fn create_item(s: usize) -> Weight;233    fn burn_item() -> Weight;234    fn transfer() -> Weight;235    fn approve() -> Weight;236    fn transfer_from() -> Weight;237    fn set_offchain_schema() -> Weight;238    fn set_const_on_chain_schema() -> Weight;239    fn set_variable_on_chain_schema() -> Weight;240    fn set_variable_meta_data() -> Weight;241    fn enable_contract_sponsoring() -> Weight;242    fn set_schema_version() -> Weight;243    fn set_chain_limits() -> Weight;244    fn set_contract_sponsoring_rate_limit() -> Weight;245    fn toggle_contract_white_list() -> Weight;246    fn add_to_contract_white_list() -> Weight;247    fn remove_from_contract_white_list() -> Weight;248    fn set_collection_limits() -> Weight;249}250251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253pub struct CreateNftData {254    pub const_data: Vec<u8>,255    pub variable_data: Vec<u8>,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateFungibleData {261    pub value: u128,262}263264#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]265#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]266pub struct CreateReFungibleData {267    pub const_data: Vec<u8>,268    pub variable_data: Vec<u8>,269}270271#[derive(Encode, Decode, Debug, Clone, PartialEq)]272#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]273pub enum CreateItemData {274    NFT(CreateNftData),275    Fungible(CreateFungibleData),276    ReFungible(CreateReFungibleData),277}278279impl CreateItemData {280    pub fn len(&self) -> usize {281        let len = match self {282            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),283            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),284            _ => 0285        };286        287        return len;288    }289}290291impl From<CreateNftData> for CreateItemData {292    fn from(item: CreateNftData) -> Self {293        CreateItemData::NFT(item)294    }295}296297impl From<CreateReFungibleData> for CreateItemData {298    fn from(item: CreateReFungibleData) -> Self {299        CreateItemData::ReFungible(item)300    }301}302303impl From<CreateFungibleData> for CreateItemData {304    fn from(item: CreateFungibleData) -> Self {305        CreateItemData::Fungible(item)306    }307}308309310decl_error! {311	/// Error for non-fungible-token module.312	pub enum Error for Module<T: Config> {313        /// Total collections bound exceeded.314        TotalCollectionsLimitExceeded,315		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.316        CollectionDecimalPointLimitExceeded, 317        /// Collection name can not be longer than 63 char.318        CollectionNameLimitExceeded, 319        /// Collection description can not be longer than 255 char.320        CollectionDescriptionLimitExceeded, 321        /// Token prefix can not be longer than 15 char.322        CollectionTokenPrefixLimitExceeded,323        /// This collection does not exist.324        CollectionNotFound,325        /// Item not exists.326        TokenNotFound,327        /// Admin not found328        AdminNotFound,329        /// Arithmetic calculation overflow.330        NumOverflow,       331        /// Account already has admin role.332        AlreadyAdmin,  333        /// You do not own this collection.334        NoPermission,335        /// This address is not set as sponsor, use setCollectionSponsor first.336        ConfirmUnsetSponsorFail,337        /// Collection is not in mint mode.338        PublicMintingNotAllowed,339        /// Sender parameter and item owner must be equal.340        MustBeTokenOwner,341        /// Item balance not enough.342        TokenValueTooLow,343        /// Size of item is too large.344        NftSizeLimitExceeded,345        /// No approve found346        ApproveNotFound,347        /// Requested value more than approved.348        TokenValueNotEnough,349        /// Only approved addresses can call this method.350        ApproveRequired,351        /// Address is not in white list.352        AddresNotInWhiteList,353        /// Number of collection admins bound exceeded.354        CollectionAdminsLimitExceeded,355        /// Owned tokens by a single address bound exceeded.356        AddressOwnershipLimitExceeded,357        /// Length of items properties must be greater than 0.358        EmptyArgument,359        /// const_data exceeded data limit.360        TokenConstDataLimitExceeded,361        /// variable_data exceeded data limit.362        TokenVariableDataLimitExceeded,363        /// Not NFT item data used to mint in NFT collection.364        NotNftDataUsedToMintNftCollectionToken,365        /// Not Fungible item data used to mint in Fungible collection.366        NotFungibleDataUsedToMintFungibleCollectionToken,367        /// Not Re Fungible item data used to mint in Re Fungible collection.368        NotReFungibleDataUsedToMintReFungibleCollectionToken,369        /// Unexpected collection type.370        UnexpectedCollectionType,371        /// Can't store metadata in fungible tokens.372        CantStoreMetadataInFungibleTokens,373        /// Collection token limit exceeded374        CollectionTokenLimitExceeded,375        /// Account token limit exceeded per collection376        AccountTokenLimitExceeded,377        /// Collection limit bounds per collection exceeded378        CollectionLimitBoundsExceeded,379        /// Schema data size limit bound exceeded380        SchemaDataLimitExceeded381	}382}383384pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {385    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;386387    /// Weight information for extrinsics in this pallet.388	type WeightInfo: WeightInfo;389}390391#[cfg(feature = "runtime-benchmarks")]392mod benchmarking;393394// #endregion395396decl_storage! {397    trait Store for Module<T: Config> as Nft {398399        // Private members400        NextCollectionID: CollectionId;401        CreatedCollectionCount: u32;402        ChainVersion: u64;403        ItemListIndex: map hasher(identity) CollectionId => TokenId;404405        // Chain limits struct406        pub ChainLimit get(fn chain_limit) config(): ChainLimits;407408        // Bound counters409        CollectionCount: u32;410        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;411412        // Basic collections413        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;414        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;415        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;416417        /// Balance owner per collection map418        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;419420        /// second parameter: item id + owner account id + spender account id421        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;422423        /// Item collections424        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;425        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;426        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;427428        /// Index list429        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;430431        /// Tokens transfer baskets432        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;433        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;434        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;435        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;436437        // Contract Sponsorship and Ownership438        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;439        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;440        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;441        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;442        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 443        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 444    }445    add_extra_genesis {446        build(|config: &GenesisConfig<T>| {447            // Modification of storage448            for (_num, _c) in &config.collection {449                <Module<T>>::init_collection(_c);450            }451452            for (_num, _c, _i) in &config.nft_item_id {453                <Module<T>>::init_nft_token(*_c, _i);454            }455456            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {457                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);458            }459460            for (_num, _c, _i) in &config.refungible_item_id {461                <Module<T>>::init_refungible_token(*_c, _i);462            }463        })464    }465}466467decl_event!(468    pub enum Event<T>469    where470        AccountId = <T as system::Config>::AccountId,471    {472        /// New collection was created473        /// 474        /// # Arguments475        /// 476        /// * collection_id: Globally unique identifier of newly created collection.477        /// 478        /// * mode: [CollectionMode] converted into u8.479        /// 480        /// * account_id: Collection owner.481        Created(CollectionId, u8, AccountId),482483        /// New item was created.484        /// 485        /// # Arguments486        /// 487        /// * collection_id: Id of the collection where item was created.488        /// 489        /// * item_id: Id of an item. Unique within the collection.490        ItemCreated(CollectionId, TokenId),491492        /// Collection item was burned.493        /// 494        /// # Arguments495        /// 496        /// collection_id.497        /// 498        /// item_id: Identifier of burned NFT.499        ItemDestroyed(CollectionId, TokenId),500    }501);502503decl_module! {504    pub struct Module<T: Config> for enum Call 505    where 506        origin: T::Origin507    {508        fn deposit_event() = default;509        type Error = Error<T>;510511        fn on_initialize(now: T::BlockNumber) -> Weight {512513            if ChainVersion::get() < 2514            {515                let value = NextCollectionID::get();516                CreatedCollectionCount::put(value);517                ChainVersion::put(2);518            }519520            0521        }522523        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.524        /// 525        /// # Permissions526        /// 527        /// * Anyone.528        /// 529        /// # Arguments530        /// 531        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.532        /// 533        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.534        /// 535        /// * token_prefix: UTF-8 string with token prefix.536        /// 537        /// * mode: [CollectionMode] collection type and type dependent data.538        // returns collection ID539        #[weight = <T as Config>::WeightInfo::create_collection()]540        pub fn create_collection(origin,541                                 collection_name: Vec<u16>,542                                 collection_description: Vec<u16>,543                                 token_prefix: Vec<u8>,544                                 mode: CollectionMode) -> DispatchResult {545546            // Anyone can create a collection547            let who = ensure_signed(origin)?;548549            let decimal_points = match mode {550                CollectionMode::Fungible(points) => points,551                CollectionMode::ReFungible(points) => points,552                _ => 0553            };554555            // bound Total number of collections556            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);557558            // check params559            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);560            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);561            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);562            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);563564            // Generate next collection ID565            let next_id = CreatedCollectionCount::get()566                .checked_add(1)567                .ok_or(Error::<T>::NumOverflow)?;568569            // bound counter570            let total = CollectionCount::get()571                .checked_add(1)572                .ok_or(Error::<T>::NumOverflow)?;573574            CreatedCollectionCount::put(next_id);575            CollectionCount::put(total);576577            // Create new collection578            let new_collection = CollectionType {579                owner: who.clone(),580                name: collection_name,581                mode: mode.clone(),582                mint_mode: false,583                access: AccessMode::Normal,584                description: collection_description,585                decimal_points: decimal_points,586                token_prefix: token_prefix,587                offchain_schema: Vec::new(),588                schema_version: SchemaVersion::ImageURL,589                sponsor: T::AccountId::default(),590                sponsor_confirmed: false,591                variable_on_chain_schema: Vec::new(),592                const_on_chain_schema: Vec::new(),593                limits: CollectionLimits::default(),594            };595596            // Add new collection to map597            <Collection<T>>::insert(next_id, new_collection);598599            // call event600            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));601602            Ok(())603        }604605        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.606        /// 607        /// # Permissions608        /// 609        /// * Collection Owner.610        /// 611        /// # Arguments612        /// 613        /// * collection_id: collection to destroy.614        #[weight = <T as Config>::WeightInfo::destroy_collection()]615        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {616617            let sender = ensure_signed(origin)?;618            Self::check_owner_permissions(collection_id, sender)?;619620            <AddressTokens<T>>::remove_prefix(collection_id);621            <Allowances<T>>::remove_prefix(collection_id);622            <Balance<T>>::remove_prefix(collection_id);623            <ItemListIndex>::remove(collection_id);624            <AdminList<T>>::remove(collection_id);625            <Collection<T>>::remove(collection_id);626            <WhiteList<T>>::remove_prefix(collection_id);627628            <NftItemList<T>>::remove_prefix(collection_id);629            <FungibleItemList<T>>::remove_prefix(collection_id);630            <ReFungibleItemList<T>>::remove_prefix(collection_id);631632            <NftTransferBasket<T>>::remove_prefix(collection_id);633            <FungibleTransferBasket<T>>::remove_prefix(collection_id);634            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);635636            if CollectionCount::get() > 0637            {638                // bound couter639                let total = CollectionCount::get()640                    .checked_sub(1)641                    .ok_or(Error::<T>::NumOverflow)?;642643                CollectionCount::put(total);644            }645646            Ok(())647        }648649        /// Add an address to white list.650        /// 651        /// # Permissions652        /// 653        /// * Collection Owner654        /// * Collection Admin655        /// 656        /// # Arguments657        /// 658        /// * collection_id.659        /// 660        /// * address.661        #[weight = <T as Config>::WeightInfo::add_to_white_list()]662        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{663664            let sender = ensure_signed(origin)?;665            Self::check_owner_or_admin_permissions(collection_id, sender)?;666667            <WhiteList<T>>::insert(collection_id, address, true);668            669            Ok(())670        }671672        /// Remove an address from white list.673        /// 674        /// # Permissions675        /// 676        /// * Collection Owner677        /// * Collection Admin678        /// 679        /// # Arguments680        /// 681        /// * collection_id.682        /// 683        /// * address.684        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]685        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{686687            let sender = ensure_signed(origin)?;688            Self::check_owner_or_admin_permissions(collection_id, sender)?;689690            <WhiteList<T>>::remove(collection_id, address);691692            Ok(())693        }694695        /// Toggle between normal and white list access for the methods with access for `Anyone`.696        /// 697        /// # Permissions698        /// 699        /// * Collection Owner.700        /// 701        /// # Arguments702        /// 703        /// * collection_id.704        /// 705        /// * mode: [AccessMode]706        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]707        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult708        {709            let sender = ensure_signed(origin)?;710711            Self::check_owner_permissions(collection_id, sender)?;712            let mut target_collection = <Collection<T>>::get(collection_id);713            target_collection.access = mode;714            <Collection<T>>::insert(collection_id, target_collection);715716            Ok(())717        }718719        /// Allows Anyone to create tokens if:720        /// * White List is enabled, and721        /// * Address is added to white list, and722        /// * This method was called with True parameter723        /// 724        /// # Permissions725        /// * Collection Owner726        ///727        /// # Arguments728        /// 729        /// * collection_id.730        /// 731        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.732        #[weight = <T as Config>::WeightInfo::set_mint_permission()]733        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult734        {735            let sender = ensure_signed(origin)?;736737            Self::check_owner_permissions(collection_id, sender)?;738            let mut target_collection = <Collection<T>>::get(collection_id);739            target_collection.mint_mode = mint_permission;740            <Collection<T>>::insert(collection_id, target_collection);741742            Ok(())743        }744745        /// Change the owner of the collection.746        /// 747        /// # Permissions748        /// 749        /// * Collection Owner.750        /// 751        /// # Arguments752        /// 753        /// * collection_id.754        /// 755        /// * new_owner.756        #[weight = <T as Config>::WeightInfo::change_collection_owner()]757        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {758759            let sender = ensure_signed(origin)?;760            Self::check_owner_permissions(collection_id, sender)?;761            let mut target_collection = <Collection<T>>::get(collection_id);762            target_collection.owner = new_owner;763            <Collection<T>>::insert(collection_id, target_collection);764765            Ok(())766        }767768        /// Adds an admin of the Collection.769        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 770        /// 771        /// # Permissions772        /// 773        /// * Collection Owner.774        /// * Collection Admin.775        /// 776        /// # Arguments777        /// 778        /// * collection_id: ID of the Collection to add admin for.779        /// 780        /// * new_admin_id: Address of new admin to add.781        #[weight = <T as Config>::WeightInfo::add_collection_admin()]782        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {783784            let sender = ensure_signed(origin)?;785            Self::check_owner_or_admin_permissions(collection_id, sender)?;786            let mut admin_arr: Vec<T::AccountId> = Vec::new();787788            if <AdminList<T>>::contains_key(collection_id)789            {790                admin_arr = <AdminList<T>>::get(collection_id);791                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);792            }793794            // Number of collection admins795            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);796797            admin_arr.push(new_admin_id);798            <AdminList<T>>::insert(collection_id, admin_arr);799800            Ok(())801        }802803        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.804        ///805        /// # Permissions806        /// 807        /// * Collection Owner.808        /// * Collection Admin.809        /// 810        /// # Arguments811        /// 812        /// * collection_id: ID of the Collection to remove admin for.813        /// 814        /// * account_id: Address of admin to remove.815        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]816        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {817818            let sender = ensure_signed(origin)?;819            Self::check_owner_or_admin_permissions(collection_id, sender)?;820            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);821822            let mut admin_arr = <AdminList<T>>::get(collection_id);823            admin_arr.retain(|i| *i != account_id);824            <AdminList<T>>::insert(collection_id, admin_arr);825826            Ok(())827        }828829        /// # Permissions830        /// 831        /// * Collection Owner832        /// 833        /// # Arguments834        /// 835        /// * collection_id.836        /// 837        /// * new_sponsor.838        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]839        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {840841            let sender = ensure_signed(origin)?;842            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);843844            let mut target_collection = <Collection<T>>::get(collection_id);845            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);846847            target_collection.sponsor = new_sponsor;848            target_collection.sponsor_confirmed = false;849            <Collection<T>>::insert(collection_id, target_collection);850851            Ok(())852        }853854        /// # Permissions855        /// 856        /// * Sponsor.857        /// 858        /// # Arguments859        /// 860        /// * collection_id.861        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]862        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {863864            let sender = ensure_signed(origin)?;865            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);866867            let mut target_collection = <Collection<T>>::get(collection_id);868            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);869870            target_collection.sponsor_confirmed = true;871            <Collection<T>>::insert(collection_id, target_collection);872873            Ok(())874        }875876        /// Switch back to pay-per-own-transaction model.877        ///878        /// # Permissions879        ///880        /// * Collection owner.881        /// 882        /// # Arguments883        /// 884        /// * collection_id.885        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]886        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {887888            let sender = ensure_signed(origin)?;889            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);890891            let mut target_collection = <Collection<T>>::get(collection_id);892            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);893894            target_collection.sponsor = T::AccountId::default();895            target_collection.sponsor_confirmed = false;896            <Collection<T>>::insert(collection_id, target_collection);897898            Ok(())899        }900901        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.902        /// 903        /// # Permissions904        /// 905        /// * Collection Owner.906        /// * Collection Admin.907        /// * Anyone if908        ///     * White List is enabled, and909        ///     * Address is added to white list, and910        ///     * MintPermission is enabled (see SetMintPermission method)911        /// 912        /// # Arguments913        /// 914        /// * collection_id: ID of the collection.915        /// 916        /// * owner: Address, initial owner of the NFT.917        ///918        /// * data: Token data to store on chain.919        // #[weight =920        // (130_000_000 as Weight)921        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))922        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))923        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]924925        #[weight = <T as Config>::WeightInfo::create_item(data.len())]926        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {927928            let sender = ensure_signed(origin)?;929930            Self::collection_exists(collection_id)?;931932            let target_collection = <Collection<T>>::get(collection_id);933934            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;935            Self::validate_create_item_args(&target_collection, &data)?;936            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;937938            Ok(())939        }940941        /// This method creates multiple instances of NFT Collection created with CreateCollection method.942        /// 943        /// # Permissions944        /// 945        /// * Collection Owner.946        /// * Collection Admin.947        /// * Anyone if948        ///     * White List is enabled, and949        ///     * Address is added to white list, and950        ///     * MintPermission is enabled (see SetMintPermission method)951        /// 952        /// # Arguments953        /// 954        /// * collection_id: ID of the collection.955        /// 956        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].957        /// 958        /// * owner: Address, initial owner of the NFT.959        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()960                               .map(|data| { data.len() })961                               .sum())]962        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {963964            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);965            let sender = ensure_signed(origin)?;966967            Self::collection_exists(collection_id)?;968            let target_collection = <Collection<T>>::get(collection_id);969970            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;971972            for data in &items_data {973                Self::validate_create_item_args(&target_collection, data)?;974            }975            for data in &items_data {976                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;977            }978979            Ok(())980        }981982        /// Destroys a concrete instance of NFT.983        /// 984        /// # Permissions985        /// 986        /// * Collection Owner.987        /// * Collection Admin.988        /// * Current NFT Owner.989        /// 990        /// # Arguments991        /// 992        /// * collection_id: ID of the collection.993        /// 994        /// * item_id: ID of NFT to burn.995        #[weight = <T as Config>::WeightInfo::burn_item()]996        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {997998            let sender = ensure_signed(origin)?;999            Self::collection_exists(collection_id)?;10001001            // Transfer permissions check1002            let target_collection = <Collection<T>>::get(collection_id);1003            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1004                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1005                Error::<T>::NoPermission);10061007            if target_collection.access == AccessMode::WhiteList {1008                Self::check_white_list(collection_id, &sender)?;1009            }10101011            match target_collection.mode1012            {1013                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1014                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1015                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1016                _ => ()1017            };10181019            // call event1020            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10211022            Ok(())1023        }10241025        /// Change ownership of the token.1026        /// 1027        /// # Permissions1028        /// 1029        /// * Collection Owner1030        /// * Collection Admin1031        /// * Current NFT owner1032        ///1033        /// # Arguments1034        /// 1035        /// * recipient: Address of token recipient.1036        /// 1037        /// * collection_id.1038        /// 1039        /// * item_id: ID of the item1040        ///     * Non-Fungible Mode: Required.1041        ///     * Fungible Mode: Ignored.1042        ///     * Re-Fungible Mode: Required.1043        /// 1044        /// * value: Amount to transfer.1045        ///     * Non-Fungible Mode: Ignored1046        ///     * Fungible Mode: Must specify transferred amount1047        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1048        #[weight = <T as Config>::WeightInfo::transfer()]1049        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1050            let sender = ensure_signed(origin)?;1051            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1052        }10531054        /// Set, change, or remove approved address to transfer the ownership of the NFT.1055        /// 1056        /// # Permissions1057        /// 1058        /// * Collection Owner1059        /// * Collection Admin1060        /// * Current NFT owner1061        /// 1062        /// # Arguments1063        /// 1064        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1065        /// 1066        /// * collection_id.1067        /// 1068        /// * item_id: ID of the item.1069        #[weight = <T as Config>::WeightInfo::approve()]1070        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10711072            let sender = ensure_signed(origin)?;10731074            Self::collection_exists(collection_id)?;1075            Self::token_exists(collection_id, item_id, &sender)?;10761077            // Transfer permissions check1078            let target_collection = <Collection<T>>::get(collection_id);1079            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1080                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1081                Error::<T>::NoPermission);10821083            if target_collection.access == AccessMode::WhiteList {1084                Self::check_white_list(collection_id, &sender)?;1085                Self::check_white_list(collection_id, &spender)?;1086            }10871088            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1089            let mut allowance: u128 = amount;1090            if allowance_exists {1091                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1092            }1093            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10941095            Ok(())1096        }1097        1098        /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1099        /// 1100        /// # Permissions1101        /// * Collection Owner1102        /// * Collection Admin1103        /// * Current NFT owner1104        /// * Address approved by current NFT owner1105        /// 1106        /// # Arguments1107        /// 1108        /// * from: Address that owns token.1109        /// 1110        /// * recipient: Address of token recipient.1111        /// 1112        /// * collection_id.1113        /// 1114        /// * item_id: ID of the item.1115        /// 1116        /// * value: Amount to transfer.1117        #[weight = <T as Config>::WeightInfo::transfer_from()]1118        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11191120            let sender = ensure_signed(origin)?;1121            let mut appoved_transfer = false;11221123            // Check approval1124            let mut approval: u128 = 0;1125            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1126                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1127                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1128                appoved_transfer = true;1129            }11301131            let target_collection = <Collection<T>>::get(collection_id);11321133            // Limits check1134            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11351136            // Transfer permissions check         1137            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1138                Error::<T>::NoPermission);11391140            if target_collection.access == AccessMode::WhiteList {1141                Self::check_white_list(collection_id, &sender)?;1142                Self::check_white_list(collection_id, &recipient)?;1143            }11441145            // Reduce approval by transferred amount or remove if remaining approval drops to 01146            if approval.checked_sub(value).unwrap_or(0) > 0 {1147                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1148            }1149            else {1150                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1151            }11521153            match target_collection.mode1154            {1155                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1156                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1157                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1158                _ => ()1159            };11601161            Ok(())1162        }11631164        // #[weight = 0]1165        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11661167        //     // let no_perm_mes = "You do not have permissions to modify this collection";1168        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1169        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1170        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11711172        //     // // on_nft_received  call11731174        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11751176        //     Ok(())1177        // }11781179        /// Set off-chain data schema.1180        /// 1181        /// # Permissions1182        /// 1183        /// * Collection Owner1184        /// * Collection Admin1185        /// 1186        /// # Arguments1187        /// 1188        /// * collection_id.1189        /// 1190        /// * schema: String representing the offchain data schema.1191        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1192        pub fn set_variable_meta_data (1193            origin,1194            collection_id: CollectionId,1195            item_id: TokenId,1196            data: Vec<u8>1197        ) -> DispatchResult {1198            let sender = ensure_signed(origin)?;1199            1200            Self::collection_exists(collection_id)?;1201            Self::token_exists(collection_id, item_id, &sender)?;12021203            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12041205            // Modify permissions check1206            let target_collection = <Collection<T>>::get(collection_id);1207            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1208                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1209                Error::<T>::NoPermission);12101211            match target_collection.mode1212            {1213                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1214                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1215                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1216                _ => fail!(Error::<T>::UnexpectedCollectionType)1217            };12181219            Ok(())1220        }1221 1222        /// Set schema standard1223        /// ImageURL1224        /// Unique1225        /// 1226        /// # Permissions1227        /// 1228        /// * Collection Owner1229        /// * Collection Admin1230        /// 1231        /// # Arguments1232        /// 1233        /// * collection_id.1234        /// 1235        /// * schema: SchemaVersion: enum1236        #[weight = <T as Config>::WeightInfo::set_schema_version()]1237        pub fn set_schema_version(1238            origin,1239            collection_id: CollectionId,1240            version: SchemaVersion1241        ) -> DispatchResult {1242            let sender = ensure_signed(origin)?;1243            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1244            let mut target_collection = <Collection<T>>::get(collection_id);1245            target_collection.schema_version = version;1246            <Collection<T>>::insert(collection_id, target_collection);12471248            Ok(())1249        }12501251        /// Set off-chain data schema.1252        /// 1253        /// # Permissions1254        /// 1255        /// * Collection Owner1256        /// * Collection Admin1257        /// 1258        /// # Arguments1259        /// 1260        /// * collection_id.1261        /// 1262        /// * schema: String representing the offchain data schema.1263        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1264        pub fn set_offchain_schema(1265            origin,1266            collection_id: CollectionId,1267            schema: Vec<u8>1268        ) -> DispatchResult {1269            let sender = ensure_signed(origin)?;1270            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12711272            // check schema limit1273            ensure!(schema.len() as u32 > ChainLimit::get().offchain_schema_limit, "");12741275            let mut target_collection = <Collection<T>>::get(collection_id);1276            target_collection.offchain_schema = schema;1277            <Collection<T>>::insert(collection_id, target_collection);12781279            Ok(())1280        }12811282        /// Set const on-chain data schema.1283        /// 1284        /// # Permissions1285        /// 1286        /// * Collection Owner1287        /// * Collection Admin1288        /// 1289        /// # Arguments1290        /// 1291        /// * collection_id.1292        /// 1293        /// * schema: String representing the const on-chain data schema.1294        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1295        pub fn set_const_on_chain_schema (1296            origin,1297            collection_id: CollectionId,1298            schema: Vec<u8>1299        ) -> DispatchResult {1300            let sender = ensure_signed(origin)?;1301            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13021303            // check schema limit1304            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13051306            let mut target_collection = <Collection<T>>::get(collection_id);1307            target_collection.const_on_chain_schema = schema;1308            <Collection<T>>::insert(collection_id, target_collection);13091310            Ok(())1311        }13121313        /// Set variable on-chain data schema.1314        /// 1315        /// # Permissions1316        /// 1317        /// * Collection Owner1318        /// * Collection Admin1319        /// 1320        /// # Arguments1321        /// 1322        /// * collection_id.1323        /// 1324        /// * schema: String representing the variable on-chain data schema.1325        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1326        pub fn set_variable_on_chain_schema (1327            origin,1328            collection_id: CollectionId,1329            schema: Vec<u8>1330        ) -> DispatchResult {1331            let sender = ensure_signed(origin)?;1332            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13331334            // check schema limit1335            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13361337            let mut target_collection = <Collection<T>>::get(collection_id);1338            target_collection.variable_on_chain_schema = schema;1339            <Collection<T>>::insert(collection_id, target_collection);13401341            Ok(())1342        }13431344        // Sudo permissions function1345        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1346        pub fn set_chain_limits(1347            origin,1348            limits: ChainLimits1349        ) -> DispatchResult {13501351            #[cfg(not(feature = "runtime-benchmarks"))]1352            ensure_root(origin)?;13531354            <ChainLimit>::put(limits);1355            Ok(())1356        }13571358        /// Enable smart contract self-sponsoring.1359        /// 1360        /// # Permissions1361        /// 1362        /// * Contract Owner1363        /// 1364        /// # Arguments1365        /// 1366        /// * contract address1367        /// * enable flag1368        /// 1369        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1370        pub fn enable_contract_sponsoring(1371            origin,1372            contract_address: T::AccountId,1373            enable: bool1374        ) -> DispatchResult {13751376            let sender = ensure_signed(origin)?;13771378            #[cfg(feature = "runtime-benchmarks")]1379            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13801381            Self::ensure_contract_owned(sender, &contract_address)?;13821383            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1384            Ok(())1385        }13861387        /// Set the rate limit for contract sponsoring to specified number of blocks.1388        /// 1389        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1390        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1391        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1392        /// from contract endowment if there are at least B blocks between such transactions. 1393        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1394        /// 1395        /// # Permissions1396        /// 1397        /// * Contract Owner1398        /// 1399        /// # Arguments1400        /// 1401        /// -`contract_address`: Address of the contract to sponsor1402        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1403        /// 1404        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1405        pub fn set_contract_sponsoring_rate_limit(1406            origin,1407            contract_address: T::AccountId,1408            rate_limit: T::BlockNumber1409        ) -> DispatchResult {1410            let sender = ensure_signed(origin)?;14111412            #[cfg(feature = "runtime-benchmarks")]1413            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14141415            Self::ensure_contract_owned(sender, &contract_address)?;1416            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1417            Ok(())1418        }14191420        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1421        /// 1422        /// # Permissions1423        /// 1424        /// * Address that deployed smart contract.1425        /// 1426        /// # Arguments1427        /// 1428        /// -`contract_address`: Address of the contract.1429        /// 1430        /// - `enable`: .  1431        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1432        pub fn toggle_contract_white_list(1433            origin,1434            contract_address: T::AccountId,1435            enable: bool1436        ) -> DispatchResult {1437            let sender = ensure_signed(origin)?;14381439            #[cfg(feature = "runtime-benchmarks")]1440            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14411442            Self::ensure_contract_owned(sender, &contract_address)?;1443            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1444            Ok(())1445        }1446        1447        /// Add an address to smart contract white list.1448        /// 1449        /// # Permissions1450        /// 1451        /// * Address that deployed smart contract.1452        /// 1453        /// # Arguments1454        /// 1455        /// -`contract_address`: Address of the contract.1456        ///1457        /// -`account_address`: Address to add.1458        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1459        pub fn add_to_contract_white_list(1460            origin,1461            contract_address: T::AccountId,1462            account_address: T::AccountId1463        ) -> DispatchResult {1464            let sender = ensure_signed(origin)?;14651466            #[cfg(feature = "runtime-benchmarks")]1467            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1468            1469            Self::ensure_contract_owned(sender, &contract_address)?;      1470            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1471            Ok(())1472        }14731474        /// Remove an address from smart contract white list.1475        /// 1476        /// # Permissions1477        /// 1478        /// * Address that deployed smart contract.1479        /// 1480        /// # Arguments1481        /// 1482        /// -`contract_address`: Address of the contract.1483        ///1484        /// -`account_address`: Address to remove.1485        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1486        pub fn remove_from_contract_white_list(1487            origin,1488            contract_address: T::AccountId,1489            account_address: T::AccountId1490        ) -> DispatchResult {1491            let sender = ensure_signed(origin)?;14921493            #[cfg(feature = "runtime-benchmarks")]1494            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14951496            Self::ensure_contract_owned(sender, &contract_address)?;1497            <ContractWhiteList<T>>::remove(contract_address, account_address);1498            Ok(())1499        }15001501        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1502        pub fn set_collection_limits(1503            origin,1504            collection_id: u32,1505            limits: CollectionLimits,1506        ) -> DispatchResult {1507            let sender = ensure_signed(origin)?;1508            Self::check_owner_permissions(collection_id, sender.clone())?;1509            let mut target_collection = <Collection<T>>::get(collection_id);1510            let chain_limits = ChainLimit::get();1511            let climits = target_collection.limits;15121513            // collection bounds1514            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1515                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1516                Error::<T>::CollectionLimitBoundsExceeded);15171518            // token_limit   check  prev1519            ensure!(climits.token_limit > limits.token_limit && 1520                limits.token_limit <= chain_limits.account_token_ownership_limit, 1521                Error::<T>::AccountTokenLimitExceeded);15221523            target_collection.limits = limits;1524            <Collection<T>>::insert(collection_id, target_collection);15251526            Ok(())1527        } 1528    }1529}15301531impl<T: Config> Module<T> {15321533    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15341535        let target_collection = <Collection<T>>::get(collection_id);15361537        // Limits check1538        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15391540        // Transfer permissions check1541        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1542            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1543            Error::<T>::NoPermission);15441545        if target_collection.access == AccessMode::WhiteList {1546            Self::check_white_list(collection_id, &sender)?;1547            Self::check_white_list(collection_id, &recipient)?;1548        }15491550        match target_collection.mode1551        {1552            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1553            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1554            CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1555            _ => ()1556        };15571558        Ok(())1559    }156015611562    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15631564        // check token limit and account token limit1565        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1566        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1567        1568        Ok(())1569    }15701571    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15721573        // check token limit and account token limit1574        let total_items: u32 = ItemListIndex::get(collection_id);1575        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1576        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1577        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15781579        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1580            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1581            Self::check_white_list(collection_id, owner)?;1582            Self::check_white_list(collection_id, sender)?;1583        }15841585        Ok(())1586    }15871588    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1589        match target_collection.mode1590        {1591            CollectionMode::NFT => {1592                if let CreateItemData::NFT(data) = data {1593                    // check sizes1594                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1595                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1596                } else {1597                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1598                }1599            },1600            CollectionMode::Fungible(_) => {1601                if let CreateItemData::Fungible(_) = data {1602                } else {1603                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1604                }1605            },1606            CollectionMode::ReFungible(_) => {1607                if let CreateItemData::ReFungible(data) = data {16081609                    // check sizes1610                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1611                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1612                } else {1613                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1614                }1615            },1616            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1617        };16181619        Ok(())1620    }16211622    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1623        match data1624        {1625            CreateItemData::NFT(data) => {1626                let item = NftItemType {1627                    owner,1628                    const_data: data.const_data,1629                    variable_data: data.variable_data1630                };16311632                Self::add_nft_item(collection_id, item)?;1633            },1634            CreateItemData::Fungible(data) => {1635                Self::add_fungible_item(collection_id, &owner, data.value)?;1636            },1637            CreateItemData::ReFungible(data) => {1638                let mut owner_list = Vec::new();1639                let value = (10 as u128).pow(collection.decimal_points as u32);1640                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16411642                let item = ReFungibleItemType {1643                    owner: owner_list,1644                    const_data: data.const_data,1645                    variable_data: data.variable_data1646                };16471648                Self::add_refungible_item(collection_id, item)?;1649            }1650        };16511652        // call event1653        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16541655        Ok(())1656    }16571658    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16591660        // Does new owner already have an account?1661        let mut balance: u128 = 0;1662        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1663            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1664        } 16651666        // Mint 1667        let item = FungibleItemType {1668            value: balance + value1669        };1670        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16711672        // Update balance1673        let new_balance = <Balance<T>>::get(collection_id, owner)1674            .checked_add(value)1675            .ok_or(Error::<T>::NumOverflow)?;1676        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16771678        Ok(())1679    }16801681    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1682        let current_index = <ItemListIndex>::get(collection_id)1683            .checked_add(1)1684            .ok_or(Error::<T>::NumOverflow)?;1685        let itemcopy = item.clone();16861687        let value = item.owner.first().unwrap().fraction;1688        let owner = item.owner.first().unwrap().owner.clone();16891690        Self::add_token_index(collection_id, current_index, &owner)?;16911692        <ItemListIndex>::insert(collection_id, current_index);1693        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16941695        // Update balance1696        let new_balance = <Balance<T>>::get(collection_id, &owner)1697            .checked_add(value)1698            .ok_or(Error::<T>::NumOverflow)?;1699        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17001701        Ok(())1702    }17031704    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1705        let current_index = <ItemListIndex>::get(collection_id)1706            .checked_add(1)1707            .ok_or(Error::<T>::NumOverflow)?;17081709        let item_owner = item.owner.clone();1710        Self::add_token_index(collection_id, current_index, &item.owner)?;17111712        <ItemListIndex>::insert(collection_id, current_index);1713        <NftItemList<T>>::insert(collection_id, current_index, item);17141715        // Update balance1716        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1717            .checked_add(1)1718            .ok_or(Error::<T>::NumOverflow)?;1719        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17201721        Ok(())1722    }17231724    fn burn_refungible_item(1725        collection_id: CollectionId,1726        item_id: TokenId,1727        owner: &T::AccountId,1728    ) -> DispatchResult {1729        ensure!(1730            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1731            Error::<T>::TokenNotFound1732        );1733        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1734        let rft_balance = token1735            .owner1736            .iter()1737            .filter(|&i| i.owner == *owner)1738            .next()1739            .unwrap();1740        Self::remove_token_index(collection_id, item_id, owner)?;17411742        // update balance1743        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1744            .checked_sub(rft_balance.fraction)1745            .ok_or(Error::<T>::NumOverflow)?;1746        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17471748        // Re-create owners list with sender removed1749        let index = token1750            .owner1751            .iter()1752            .position(|i| i.owner == *owner)1753            .unwrap();1754        token.owner.remove(index);1755        let owner_count = token.owner.len();17561757        // Burn the token completely if this was the last (only) owner1758        if owner_count == 0 {1759            <ReFungibleItemList<T>>::remove(collection_id, item_id);1760        }1761        else {1762            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1763        }17641765        Ok(())1766    }17671768    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1769        ensure!(1770            <NftItemList<T>>::contains_key(collection_id, item_id),1771            Error::<T>::TokenNotFound1772        );1773        let item = <NftItemList<T>>::get(collection_id, item_id);1774        Self::remove_token_index(collection_id, item_id, &item.owner)?;17751776        // update balance1777        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1778            .checked_sub(1)1779            .ok_or(Error::<T>::NumOverflow)?;1780        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1781        <NftItemList<T>>::remove(collection_id, item_id);17821783        Ok(())1784    }17851786    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1787        ensure!(1788            <FungibleItemList<T>>::contains_key(collection_id, owner),1789            Error::<T>::TokenNotFound1790        );1791        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1792        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17931794        // update balance1795        let new_balance = <Balance<T>>::get(collection_id, owner)1796            .checked_sub(value)1797            .ok_or(Error::<T>::NumOverflow)?;1798        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17991800        if balance.value - value > 0 {1801            balance.value -= value;1802            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1803        }1804        else {1805            <FungibleItemList<T>>::remove(collection_id, owner);1806        }18071808        Ok(())1809    }18101811    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1812        ensure!(1813            <Collection<T>>::contains_key(collection_id),1814            Error::<T>::CollectionNotFound1815        );1816        Ok(())1817    }18181819    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1820        Self::collection_exists(collection_id)?;18211822        let target_collection = <Collection<T>>::get(collection_id);1823        ensure!(1824            subject == target_collection.owner,1825            Error::<T>::NoPermission1826        );18271828        Ok(())1829    }18301831    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1832        let target_collection = <Collection<T>>::get(collection_id);1833        let mut result: bool = subject == target_collection.owner;1834        let exists = <AdminList<T>>::contains_key(collection_id);18351836        if !result & exists {1837            if <AdminList<T>>::get(collection_id).contains(&subject) {1838                result = true1839            }1840        }18411842        result1843    }18441845    fn check_owner_or_admin_permissions(1846        collection_id: CollectionId,1847        subject: T::AccountId,1848    ) -> DispatchResult {1849        Self::collection_exists(collection_id)?;1850        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18511852        ensure!(1853            result,1854            Error::<T>::NoPermission1855        );1856        Ok(())1857    }18581859    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1860        let target_collection = <Collection<T>>::get(collection_id);18611862        match target_collection.mode {1863            CollectionMode::NFT => {1864                <NftItemList<T>>::get(collection_id, item_id).owner == subject1865            }1866            CollectionMode::Fungible(_) => {1867                <FungibleItemList<T>>::contains_key(collection_id, &subject)1868            }1869            CollectionMode::ReFungible(_) => {1870                <ReFungibleItemList<T>>::get(collection_id, item_id)1871                    .owner1872                    .iter()1873                    .any(|i| i.owner == subject)1874            }1875            CollectionMode::Invalid => false,1876        }1877    }18781879    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1880        let mes = Error::<T>::AddresNotInWhiteList;1881        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18821883        Ok(())1884    }18851886    /// Check if token exists. In case of Fungible, check if there is an entry for 1887    /// the owner in fungible balances double map1888    fn token_exists(1889        collection_id: CollectionId,1890        item_id: TokenId,1891        owner: &T::AccountId1892    ) -> DispatchResult {1893        let target_collection = <Collection<T>>::get(collection_id);1894        let exists = match target_collection.mode1895        {1896            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1897            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1898            CollectionMode::ReFungible(_)  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1899            _ => false1900        };19011902        ensure!(exists == true, Error::<T>::TokenNotFound);1903        Ok(())1904    }19051906    fn transfer_fungible(1907        collection_id: CollectionId,1908        value: u128,1909        owner: &T::AccountId,1910        recipient: &T::AccountId,1911    ) -> DispatchResult {1912        Self::token_exists(collection_id, 0, owner)?;19131914        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1915        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19161917        // Send balance to recipient (updates balanceOf of recipient)1918        Self::add_fungible_item(collection_id, recipient, value)?;19191920        // update balanceOf of sender1921        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19221923        // Reduce or remove sender1924        if balance.value == value {1925            <FungibleItemList<T>>::remove(collection_id, owner);1926        }1927        else {1928            balance.value -= value;1929            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1930        }19311932        Ok(())1933    }19341935    fn transfer_refungible(1936        collection_id: CollectionId,1937        item_id: TokenId,1938        value: u128,1939        owner: T::AccountId,1940        new_owner: T::AccountId,1941    ) -> DispatchResult {1942        Self::token_exists(collection_id, item_id, &owner)?;19431944        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1945        let item = full_item1946            .owner1947            .iter()1948            .filter(|i| i.owner == owner)1949            .next()1950            .ok_or(Error::<T>::NumOverflow)?;1951        let amount = item.fraction;19521953        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19541955        // update balance1956        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1957            .checked_sub(value)1958            .ok_or(Error::<T>::NumOverflow)?;1959        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19601961        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1962            .checked_add(value)1963            .ok_or(Error::<T>::NumOverflow)?;1964        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19651966        let old_owner = item.owner.clone();1967        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19681969        // transfer1970        if amount == value && !new_owner_has_account {1971            // change owner1972            // new owner do not have account1973            let mut new_full_item = full_item.clone();1974            new_full_item1975                .owner1976                .iter_mut()1977                .find(|i| i.owner == owner)1978                .unwrap()1979                .owner = new_owner.clone();1980            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19811982            // update index collection1983            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1984        } else {1985            let mut new_full_item = full_item.clone();1986            new_full_item1987                .owner1988                .iter_mut()1989                .find(|i| i.owner == owner)1990                .unwrap()1991                .fraction -= value;19921993            // separate amount1994            if new_owner_has_account {1995                // new owner has account1996                new_full_item1997                    .owner1998                    .iter_mut()1999                    .find(|i| i.owner == new_owner)2000                    .unwrap()2001                    .fraction += value;2002            } else {2003                // new owner do not have account2004                new_full_item.owner.push(Ownership {2005                    owner: new_owner.clone(),2006                    fraction: value,2007                });2008                Self::add_token_index(collection_id, item_id, &new_owner)?;2009            }20102011            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2012        }20132014        Ok(())2015    }20162017    fn transfer_nft(2018        collection_id: CollectionId,2019        item_id: TokenId,2020        sender: T::AccountId,2021        new_owner: T::AccountId,2022    ) -> DispatchResult {2023        Self::token_exists(collection_id, item_id, &sender)?;20242025        let mut item = <NftItemList<T>>::get(collection_id, item_id);20262027        ensure!(2028            sender == item.owner,2029            Error::<T>::MustBeTokenOwner2030        );20312032        // update balance2033        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2034            .checked_sub(1)2035            .ok_or(Error::<T>::NumOverflow)?;2036        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20372038        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2039            .checked_add(1)2040            .ok_or(Error::<T>::NumOverflow)?;2041        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20422043        // change owner2044        let old_owner = item.owner.clone();2045        item.owner = new_owner.clone();2046        <NftItemList<T>>::insert(collection_id, item_id, item);20472048        // update index collection2049        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20502051        Ok(())2052    }2053    2054    fn set_re_fungible_variable_data(2055        collection_id: CollectionId,2056        item_id: TokenId,2057        data: Vec<u8>2058    ) -> DispatchResult {2059        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20602061        item.variable_data = data;20622063        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20642065        Ok(())2066    }20672068    fn set_nft_variable_data(2069        collection_id: CollectionId,2070        item_id: TokenId,2071        data: Vec<u8>2072    ) -> DispatchResult {2073        let mut item = <NftItemList<T>>::get(collection_id, item_id);2074        2075        item.variable_data = data;20762077        <NftItemList<T>>::insert(collection_id, item_id, item);2078        2079        Ok(())2080    }20812082    fn init_collection(item: &CollectionType<T::AccountId>) {2083        // check params2084        assert!(2085            item.decimal_points <= MAX_DECIMAL_POINTS,2086            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2087        );2088        assert!(2089            item.name.len() <= 64,2090            "Collection name can not be longer than 63 char"2091        );2092        assert!(2093            item.name.len() <= 256,2094            "Collection description can not be longer than 255 char"2095        );2096        assert!(2097            item.token_prefix.len() <= 16,2098            "Token prefix can not be longer than 15 char"2099        );21002101        // Generate next collection ID2102        let next_id = CreatedCollectionCount::get()2103            .checked_add(1)2104            .unwrap();21052106        CreatedCollectionCount::put(next_id);2107    }21082109    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2110        let current_index = <ItemListIndex>::get(collection_id)2111            .checked_add(1)2112            .unwrap();21132114        let item_owner = item.owner.clone();2115        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21162117        <ItemListIndex>::insert(collection_id, current_index);21182119        // Update balance2120        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2121            .checked_add(1)2122            .unwrap();2123        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2124    }21252126    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2127        let current_index = <ItemListIndex>::get(collection_id)2128            .checked_add(1)2129            .unwrap();21302131        Self::add_token_index(collection_id, current_index, owner).unwrap();21322133        <ItemListIndex>::insert(collection_id, current_index);21342135        // Update balance2136        let new_balance = <Balance<T>>::get(collection_id, owner)2137            .checked_add(item.value)2138            .unwrap();2139        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2140    }21412142    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2143        let current_index = <ItemListIndex>::get(collection_id)2144            .checked_add(1)2145            .unwrap();21462147        let value = item.owner.first().unwrap().fraction;2148        let owner = item.owner.first().unwrap().owner.clone();21492150        Self::add_token_index(collection_id, current_index, &owner).unwrap();21512152        <ItemListIndex>::insert(collection_id, current_index);21532154        // Update balance2155        let new_balance = <Balance<T>>::get(collection_id, &owner)2156            .checked_add(value)2157            .unwrap();2158        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2159    }21602161    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21622163        // add to account limit2164        if <AccountItemCount<T>>::contains_key(owner) {21652166            // bound Owned tokens by a single address2167            let count = <AccountItemCount<T>>::get(owner);2168            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21692170            <AccountItemCount<T>>::insert(owner.clone(), count2171                .checked_add(1)2172                .ok_or(Error::<T>::NumOverflow)?);2173        }2174        else {2175            <AccountItemCount<T>>::insert(owner.clone(), 1);2176        }21772178        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2179        if list_exists {2180            let mut list = <AddressTokens<T>>::get(collection_id, owner);2181            let item_contains = list.contains(&item_index.clone());21822183            if !item_contains {2184                list.push(item_index.clone());2185            }21862187            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2188        } else {2189            let mut itm = Vec::new();2190            itm.push(item_index.clone());2191            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2192        }21932194        Ok(())2195    }21962197    fn remove_token_index(2198        collection_id: CollectionId,2199        item_index: TokenId,2200        owner: &T::AccountId,2201    ) -> DispatchResult {22022203        // update counter2204        <AccountItemCount<T>>::insert(owner.clone(), 2205            <AccountItemCount<T>>::get(owner)2206            .checked_sub(1)2207            .ok_or(Error::<T>::NumOverflow)?);220822092210        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2211        if list_exists {2212            let mut list = <AddressTokens<T>>::get(collection_id, owner);2213            let item_contains = list.contains(&item_index.clone());22142215            if item_contains {2216                list.retain(|&item| item != item_index);2217                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2218            }2219        }22202221        Ok(())2222    }22232224    fn move_token_index(2225        collection_id: CollectionId,2226        item_index: TokenId,2227        old_owner: &T::AccountId,2228        new_owner: &T::AccountId,2229    ) -> DispatchResult {2230        Self::remove_token_index(collection_id, item_index, old_owner)?;2231        Self::add_token_index(collection_id, item_index, new_owner)?;22322233        Ok(())2234    }2235    2236    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2237        if <ContractOwner<T>>::contains_key(contract.clone()) {2238            let owner = <ContractOwner<T>>::get(contract);2239            ensure!(account == owner, Error::<T>::NoPermission);2240        } else {2241            fail!(Error::<T>::NoPermission);2242        }22432244        Ok(())2245    }2246}22472248////////////////////////////////////////////////////////////////////////////////////////////////////2249// Economic models2250// #region22512252/// Fee multiplier.2253pub type Multiplier = FixedU128;22542255type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22562257/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2258/// in the queue.2259#[derive(Encode, Decode, Clone, Eq, PartialEq)]2260pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);22612262impl<T: Config + Send + Sync> sp_std::fmt::Debug 2263    for ChargeTransactionPayment<T>2264{2265	#[cfg(feature = "std")]2266	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2267		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2268	}2269	#[cfg(not(feature = "std"))]2270	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2271		Ok(())2272	}2273}22742275impl<T: Config> ChargeTransactionPayment<T>2276where2277    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2278    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2279    T::AccountId: AsRef<[u8]>,2280    T::AccountId: UncheckedFrom<T::Hash>,2281{2282    fn traditional_fee(2283        len: usize,2284        info: &DispatchInfoOf<T::Call>,2285        tip: BalanceOf<T>,2286    ) -> BalanceOf<T>2287    where2288        T::Call: Dispatchable<Info = DispatchInfo>,2289    {2290        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2291    }22922293	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2294        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2295        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2296        let len_saturation = max_block_length as u64 / (len as u64).max(1);2297        let coefficient: BalanceOf<T> = weight_saturation2298            .min(len_saturation)2299            .saturated_into::<BalanceOf<T>>();2300        final_fee2301            .saturating_mul(coefficient)2302            .saturated_into::<TransactionPriority>()2303    }23042305    fn withdraw_fee(2306        &self,2307        who: &T::AccountId,2308        call: &T::Call,2309        info: &DispatchInfoOf<T::Call>,2310        len: usize,2311	) -> Result<2312		(2313			BalanceOf<T>,2314			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2315		),2316		TransactionValidityError,2317	> {2318        let tip = self.0;23192320        // Set fee based on call type. Creating collection costs 1 Unique.2321        // All other transactions have traditional fees so far2322        // let fee = match call.is_sub_type() {2323        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2324        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2325        //                                                 // _ => <BalanceOf<T>>::from(100)2326        // };2327        let fee = Self::traditional_fee(len, info, tip);23282329        // Only mess with balances if fee is not zero.2330        if fee.is_zero() {2331            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2332			.map(|i| (fee, i));2333        }23342335        // Determine who is paying transaction fee based on ecnomic model2336        // Parse call to extract collection ID and access collection sponsor2337        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2338            Some(Call::create_item(collection_id, _owner, _properties)) => {23392340                // sponsor timeout2341                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23422343                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2344                let mut sponsored = true;2345                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2346                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2347                    let limit_time = last_tx_block + limit.into();2348                    if block_number <= limit_time {2349                        sponsored = false;2350                    }2351                }2352                if sponsored {2353                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2354                }23552356                // check free create limit2357                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2358                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2359                   (sponsored)2360                {2361                    <Collection<T>>::get(collection_id).sponsor2362                } else {2363                    T::AccountId::default()2364                }2365            }2366            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2367                2368                let mut sponsor_transfer = false;2369                if <Collection<T>>::get(collection_id).sponsor_confirmed {23702371                    let collection_limits = <Collection<T>>::get(collection_id).limits;2372                    let collection_mode = <Collection<T>>::get(collection_id).mode;2373    2374                    // sponsor timeout2375                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2376                    sponsor_transfer = match collection_mode {2377                        CollectionMode::NFT => {2378    2379                            // get correct limit2380                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2381                                collection_limits.sponsor_transfer_timeout2382                            } else {2383                                ChainLimit::get().nft_sponsor_transfer_timeout2384                            };2385    2386                            let mut sponsored = true;2387                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2388                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2389                                let limit_time = last_tx_block + limit.into();2390                                if block_number <= limit_time {2391                                    sponsored = false;2392                                }2393                            }2394                            if sponsored {2395                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2396                            }23972398                            sponsored2399                        }2400                        CollectionMode::Fungible(_) => {2401    2402                            // get correct limit2403                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2404                                collection_limits.sponsor_transfer_timeout2405                            } else {2406                                ChainLimit::get().fungible_sponsor_transfer_timeout2407                            };2408    2409                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2410                            let mut sponsored = true;2411                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2412                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2413                                let limit_time = last_tx_block + limit.into();2414                                if block_number <= limit_time {2415                                    sponsored = false;2416                                }2417                            }2418                            if sponsored {2419                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2420                            }24212422                            sponsored2423                        }2424                        CollectionMode::ReFungible(_) => {2425    2426                            // get correct limit2427                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2428                                collection_limits.sponsor_transfer_timeout2429                            } else {2430                                ChainLimit::get().refungible_sponsor_transfer_timeout2431                            };2432    2433                            let mut sponsored = true;2434                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2435                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2436                                let limit_time = last_tx_block + limit.into();2437                                if block_number <= limit_time {2438                                    sponsored = false;2439                                }2440                            }2441                            if sponsored {2442                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2443                            }24442445                            sponsored2446                        }2447                        _ => {2448                            false2449                        },2450                    };2451                }24522453                if !sponsor_transfer {2454                    T::AccountId::default()2455                } else {2456                    <Collection<T>>::get(collection_id).sponsor2457                }2458            }24592460            _ => T::AccountId::default(),2461        };24622463        // Sponsor smart contracts2464        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24652466            // On instantiation: set the contract owner2467            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {24682469                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2470                    &who,2471                    code_hash,2472                    salt,2473                );2474                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24752476                T::AccountId::default()2477            },24782479            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2480            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24812482                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24832484                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2485                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2486                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2487                  2488                if !owned_contract && white_list_enabled {2489                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2490                        return Err(InvalidTransaction::Call.into());2491                    }2492                }24932494                let mut sponsor_transfer = false;2495                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2496                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2497                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2498                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2499                    let limit_time = last_tx_block + rate_limit;25002501                    if block_number >= limit_time {2502                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2503                        sponsor_transfer = true;2504                    }2505                } else {2506                    sponsor_transfer = false;2507                }2508               2509                2510                let mut sp = T::AccountId::default();2511                if sponsor_transfer {2512                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2513                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2514                            sp = called_contract;2515                        }2516                    }2517                }25182519                sp2520            },25212522            _ => sponsor,2523        };25242525        let mut who_pays_fee: T::AccountId = sponsor.clone();2526        if sponsor == T::AccountId::default() {2527            who_pays_fee = who.clone();2528        }25292530		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2531			.map(|i| (fee, i))2532    }2533}253425352536impl<T: Config + Send + Sync> SignedExtension2537    for ChargeTransactionPayment<T>2538where2539    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2540    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2541    T::AccountId: AsRef<[u8]>,2542    T::AccountId: UncheckedFrom<T::Hash>,2543{2544    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2545    type AccountId = T::AccountId;2546    type Call = T::Call;2547    type AdditionalSigned = ();2548    type Pre = (2549        // tip2550        BalanceOf<T>,2551        // who pays fee2552        Self::AccountId,2553		// imbalance resulting from withdrawing the fee2554		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2555    );2556    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2557        Ok(())2558    }25592560    fn validate(2561        &self,2562        who: &Self::AccountId,2563        call: &Self::Call,2564        info: &DispatchInfoOf<Self::Call>,2565        len: usize,2566    ) -> TransactionValidity {2567		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2568		Ok(ValidTransaction {2569			priority: Self::get_priority(len, info, fee),2570			..Default::default()2571		})2572    }25732574    fn pre_dispatch(2575        self,2576        who: &Self::AccountId,2577        call: &Self::Call,2578        info: &DispatchInfoOf<Self::Call>,2579        len: usize,2580    ) -> Result<Self::Pre, TransactionValidityError> {2581        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2582        Ok((self.0, who.clone(), imbalance))2583    }25842585    fn post_dispatch(2586        pre: Self::Pre,2587        info: &DispatchInfoOf<Self::Call>,2588        post_info: &PostDispatchInfoOf<Self::Call>,2589        len: usize,2590        _result: &DispatchResult,2591    ) -> Result<(), TransactionValidityError> {2592		let (tip, who, imbalance) = pre;2593		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2594			len as u32,2595			info,2596			post_info,2597			tip,2598		);2599		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2600		Ok(())2601    }2602}26032604// #endregion
after · pallets/nft/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#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188}189190impl Default for CollectionLimits {191    fn default() -> CollectionLimits {192        CollectionLimits { 193            account_token_ownership_limit: 10_000_000, 194            token_limit: u32::max_value(),195            sponsored_data_size: u32::max_value(), 196            sponsor_transfer_timeout: 14400 }197    }198}199200#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]201#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]202pub struct ChainLimits {203    pub collection_numbers_limit: u32,204    pub account_token_ownership_limit: u32,205    pub collections_admins_limit: u64,206    pub custom_data_limit: u32,207208    // Timeouts for item types in passed blocks209    pub nft_sponsor_transfer_timeout: u32,210    pub fungible_sponsor_transfer_timeout: u32,211    pub refungible_sponsor_transfer_timeout: u32,212213    // Schema limits214    pub offchain_schema_limit: u32,215    pub variable_on_chain_schema_limit: u32,216    pub const_on_chain_schema_limit: u32,217}218219pub trait WeightInfo {220	fn create_collection() -> Weight;221	fn destroy_collection() -> Weight;222	fn add_to_white_list() -> Weight;223	fn remove_from_white_list() -> Weight;224    fn set_public_access_mode() -> Weight;225    fn set_mint_permission() -> Weight;226    fn change_collection_owner() -> Weight;227    fn add_collection_admin() -> Weight;228    fn remove_collection_admin() -> Weight;229    fn set_collection_sponsor() -> Weight;230    fn confirm_sponsorship() -> Weight;231    fn remove_collection_sponsor() -> Weight;232    fn create_item(s: usize) -> Weight;233    fn burn_item() -> Weight;234    fn transfer() -> Weight;235    fn approve() -> Weight;236    fn transfer_from() -> Weight;237    fn set_offchain_schema() -> Weight;238    fn set_const_on_chain_schema() -> Weight;239    fn set_variable_on_chain_schema() -> Weight;240    fn set_variable_meta_data() -> Weight;241    fn enable_contract_sponsoring() -> Weight;242    fn set_schema_version() -> Weight;243    fn set_chain_limits() -> Weight;244    fn set_contract_sponsoring_rate_limit() -> Weight;245    fn toggle_contract_white_list() -> Weight;246    fn add_to_contract_white_list() -> Weight;247    fn remove_from_contract_white_list() -> Weight;248    fn set_collection_limits() -> Weight;249}250251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253pub struct CreateNftData {254    pub const_data: Vec<u8>,255    pub variable_data: Vec<u8>,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateFungibleData {261    pub value: u128,262}263264#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]265#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]266pub struct CreateReFungibleData {267    pub const_data: Vec<u8>,268    pub variable_data: Vec<u8>,269    pub pieces: u128,270}271272#[derive(Encode, Decode, Debug, Clone, PartialEq)]273#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]274pub enum CreateItemData {275    NFT(CreateNftData),276    Fungible(CreateFungibleData),277    ReFungible(CreateReFungibleData),278}279280impl CreateItemData {281    pub fn len(&self) -> usize {282        let len = match self {283            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),284            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),285            _ => 0286        };287        288        return len;289    }290}291292impl From<CreateNftData> for CreateItemData {293    fn from(item: CreateNftData) -> Self {294        CreateItemData::NFT(item)295    }296}297298impl From<CreateReFungibleData> for CreateItemData {299    fn from(item: CreateReFungibleData) -> Self {300        CreateItemData::ReFungible(item)301    }302}303304impl From<CreateFungibleData> for CreateItemData {305    fn from(item: CreateFungibleData) -> Self {306        CreateItemData::Fungible(item)307    }308}309310311decl_error! {312	/// Error for non-fungible-token module.313	pub enum Error for Module<T: Config> {314        /// Total collections bound exceeded.315        TotalCollectionsLimitExceeded,316		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.317        CollectionDecimalPointLimitExceeded, 318        /// Collection name can not be longer than 63 char.319        CollectionNameLimitExceeded, 320        /// Collection description can not be longer than 255 char.321        CollectionDescriptionLimitExceeded, 322        /// Token prefix can not be longer than 15 char.323        CollectionTokenPrefixLimitExceeded,324        /// This collection does not exist.325        CollectionNotFound,326        /// Item not exists.327        TokenNotFound,328        /// Admin not found329        AdminNotFound,330        /// Arithmetic calculation overflow.331        NumOverflow,       332        /// Account already has admin role.333        AlreadyAdmin,  334        /// You do not own this collection.335        NoPermission,336        /// This address is not set as sponsor, use setCollectionSponsor first.337        ConfirmUnsetSponsorFail,338        /// Collection is not in mint mode.339        PublicMintingNotAllowed,340        /// Sender parameter and item owner must be equal.341        MustBeTokenOwner,342        /// Item balance not enough.343        TokenValueTooLow,344        /// Size of item is too large.345        NftSizeLimitExceeded,346        /// No approve found347        ApproveNotFound,348        /// Requested value more than approved.349        TokenValueNotEnough,350        /// Only approved addresses can call this method.351        ApproveRequired,352        /// Address is not in white list.353        AddresNotInWhiteList,354        /// Number of collection admins bound exceeded.355        CollectionAdminsLimitExceeded,356        /// Owned tokens by a single address bound exceeded.357        AddressOwnershipLimitExceeded,358        /// Length of items properties must be greater than 0.359        EmptyArgument,360        /// const_data exceeded data limit.361        TokenConstDataLimitExceeded,362        /// variable_data exceeded data limit.363        TokenVariableDataLimitExceeded,364        /// Not NFT item data used to mint in NFT collection.365        NotNftDataUsedToMintNftCollectionToken,366        /// Not Fungible item data used to mint in Fungible collection.367        NotFungibleDataUsedToMintFungibleCollectionToken,368        /// Not Re Fungible item data used to mint in Re Fungible collection.369        NotReFungibleDataUsedToMintReFungibleCollectionToken,370        /// Unexpected collection type.371        UnexpectedCollectionType,372        /// Can't store metadata in fungible tokens.373        CantStoreMetadataInFungibleTokens,374        /// Collection token limit exceeded375        CollectionTokenLimitExceeded,376        /// Account token limit exceeded per collection377        AccountTokenLimitExceeded,378        /// Collection limit bounds per collection exceeded379        CollectionLimitBoundsExceeded,380        /// Schema data size limit bound exceeded381        SchemaDataLimitExceeded,382        /// Maximum refungibility exceeded383        WrongRefungiblePieces384	}385}386387pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {388    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;389390    /// Weight information for extrinsics in this pallet.391	type WeightInfo: WeightInfo;392}393394#[cfg(feature = "runtime-benchmarks")]395mod benchmarking;396397// #endregion398399decl_storage! {400    trait Store for Module<T: Config> as Nft {401402        // Private members403        NextCollectionID: CollectionId;404        CreatedCollectionCount: u32;405        ChainVersion: u64;406        ItemListIndex: map hasher(identity) CollectionId => TokenId;407408        // Chain limits struct409        pub ChainLimit get(fn chain_limit) config(): ChainLimits;410411        // Bound counters412        CollectionCount: u32;413        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;414415        // Basic collections416        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;417        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;418        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;419420        /// Balance owner per collection map421        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;422423        /// second parameter: item id + owner account id + spender account id424        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;425426        /// Item collections427        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;428        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;429        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;430431        /// Index list432        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;433434        /// Tokens transfer baskets435        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;436        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;437        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;438        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;439440        // Contract Sponsorship and Ownership441        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;442        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;443        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;444        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;445        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 446        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 447    }448    add_extra_genesis {449        build(|config: &GenesisConfig<T>| {450            // Modification of storage451            for (_num, _c) in &config.collection {452                <Module<T>>::init_collection(_c);453            }454455            for (_num, _c, _i) in &config.nft_item_id {456                <Module<T>>::init_nft_token(*_c, _i);457            }458459            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {460                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);461            }462463            for (_num, _c, _i) in &config.refungible_item_id {464                <Module<T>>::init_refungible_token(*_c, _i);465            }466        })467    }468}469470decl_event!(471    pub enum Event<T>472    where473        AccountId = <T as system::Config>::AccountId,474    {475        /// New collection was created476        /// 477        /// # Arguments478        /// 479        /// * collection_id: Globally unique identifier of newly created collection.480        /// 481        /// * mode: [CollectionMode] converted into u8.482        /// 483        /// * account_id: Collection owner.484        Created(CollectionId, u8, AccountId),485486        /// New item was created.487        /// 488        /// # Arguments489        /// 490        /// * collection_id: Id of the collection where item was created.491        /// 492        /// * item_id: Id of an item. Unique within the collection.493        ItemCreated(CollectionId, TokenId),494495        /// Collection item was burned.496        /// 497        /// # Arguments498        /// 499        /// collection_id.500        /// 501        /// item_id: Identifier of burned NFT.502        ItemDestroyed(CollectionId, TokenId),503    }504);505506decl_module! {507    pub struct Module<T: Config> for enum Call 508    where 509        origin: T::Origin510    {511        fn deposit_event() = default;512        type Error = Error<T>;513514        fn on_initialize(now: T::BlockNumber) -> Weight {515516            if ChainVersion::get() < 2517            {518                let value = NextCollectionID::get();519                CreatedCollectionCount::put(value);520                ChainVersion::put(2);521            }522523            0524        }525526        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.527        /// 528        /// # Permissions529        /// 530        /// * Anyone.531        /// 532        /// # Arguments533        /// 534        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.535        /// 536        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.537        /// 538        /// * token_prefix: UTF-8 string with token prefix.539        /// 540        /// * mode: [CollectionMode] collection type and type dependent data.541        // returns collection ID542        #[weight = <T as Config>::WeightInfo::create_collection()]543        pub fn create_collection(origin,544                                 collection_name: Vec<u16>,545                                 collection_description: Vec<u16>,546                                 token_prefix: Vec<u8>,547                                 mode: CollectionMode) -> DispatchResult {548549            // Anyone can create a collection550            let who = ensure_signed(origin)?;551552            let decimal_points = match mode {553                CollectionMode::Fungible(points) => points,554                _ => 0555            };556557            // bound Total number of collections558            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);559560            // check params561            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);562            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);563            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);564            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);565566            // Generate next collection ID567            let next_id = CreatedCollectionCount::get()568                .checked_add(1)569                .ok_or(Error::<T>::NumOverflow)?;570571            // bound counter572            let total = CollectionCount::get()573                .checked_add(1)574                .ok_or(Error::<T>::NumOverflow)?;575576            CreatedCollectionCount::put(next_id);577            CollectionCount::put(total);578579            // Create new collection580            let new_collection = CollectionType {581                owner: who.clone(),582                name: collection_name,583                mode: mode.clone(),584                mint_mode: false,585                access: AccessMode::Normal,586                description: collection_description,587                decimal_points: decimal_points,588                token_prefix: token_prefix,589                offchain_schema: Vec::new(),590                schema_version: SchemaVersion::ImageURL,591                sponsor: T::AccountId::default(),592                sponsor_confirmed: false,593                variable_on_chain_schema: Vec::new(),594                const_on_chain_schema: Vec::new(),595                limits: CollectionLimits::default(),596            };597598            // Add new collection to map599            <Collection<T>>::insert(next_id, new_collection);600601            // call event602            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));603604            Ok(())605        }606607        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.608        /// 609        /// # Permissions610        /// 611        /// * Collection Owner.612        /// 613        /// # Arguments614        /// 615        /// * collection_id: collection to destroy.616        #[weight = <T as Config>::WeightInfo::destroy_collection()]617        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {618619            let sender = ensure_signed(origin)?;620            Self::check_owner_permissions(collection_id, sender)?;621622            <AddressTokens<T>>::remove_prefix(collection_id);623            <Allowances<T>>::remove_prefix(collection_id);624            <Balance<T>>::remove_prefix(collection_id);625            <ItemListIndex>::remove(collection_id);626            <AdminList<T>>::remove(collection_id);627            <Collection<T>>::remove(collection_id);628            <WhiteList<T>>::remove_prefix(collection_id);629630            <NftItemList<T>>::remove_prefix(collection_id);631            <FungibleItemList<T>>::remove_prefix(collection_id);632            <ReFungibleItemList<T>>::remove_prefix(collection_id);633634            <NftTransferBasket<T>>::remove_prefix(collection_id);635            <FungibleTransferBasket<T>>::remove_prefix(collection_id);636            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);637638            if CollectionCount::get() > 0639            {640                // bound couter641                let total = CollectionCount::get()642                    .checked_sub(1)643                    .ok_or(Error::<T>::NumOverflow)?;644645                CollectionCount::put(total);646            }647648            Ok(())649        }650651        /// Add an address to white list.652        /// 653        /// # Permissions654        /// 655        /// * Collection Owner656        /// * Collection Admin657        /// 658        /// # Arguments659        /// 660        /// * collection_id.661        /// 662        /// * address.663        #[weight = <T as Config>::WeightInfo::add_to_white_list()]664        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{665666            let sender = ensure_signed(origin)?;667            Self::check_owner_or_admin_permissions(collection_id, sender)?;668669            <WhiteList<T>>::insert(collection_id, address, true);670            671            Ok(())672        }673674        /// Remove an address from white list.675        /// 676        /// # Permissions677        /// 678        /// * Collection Owner679        /// * Collection Admin680        /// 681        /// # Arguments682        /// 683        /// * collection_id.684        /// 685        /// * address.686        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]687        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{688689            let sender = ensure_signed(origin)?;690            Self::check_owner_or_admin_permissions(collection_id, sender)?;691692            <WhiteList<T>>::remove(collection_id, address);693694            Ok(())695        }696697        /// Toggle between normal and white list access for the methods with access for `Anyone`.698        /// 699        /// # Permissions700        /// 701        /// * Collection Owner.702        /// 703        /// # Arguments704        /// 705        /// * collection_id.706        /// 707        /// * mode: [AccessMode]708        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]709        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult710        {711            let sender = ensure_signed(origin)?;712713            Self::check_owner_permissions(collection_id, sender)?;714            let mut target_collection = <Collection<T>>::get(collection_id);715            target_collection.access = mode;716            <Collection<T>>::insert(collection_id, target_collection);717718            Ok(())719        }720721        /// Allows Anyone to create tokens if:722        /// * White List is enabled, and723        /// * Address is added to white list, and724        /// * This method was called with True parameter725        /// 726        /// # Permissions727        /// * Collection Owner728        ///729        /// # Arguments730        /// 731        /// * collection_id.732        /// 733        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.734        #[weight = <T as Config>::WeightInfo::set_mint_permission()]735        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult736        {737            let sender = ensure_signed(origin)?;738739            Self::check_owner_permissions(collection_id, sender)?;740            let mut target_collection = <Collection<T>>::get(collection_id);741            target_collection.mint_mode = mint_permission;742            <Collection<T>>::insert(collection_id, target_collection);743744            Ok(())745        }746747        /// Change the owner of the collection.748        /// 749        /// # Permissions750        /// 751        /// * Collection Owner.752        /// 753        /// # Arguments754        /// 755        /// * collection_id.756        /// 757        /// * new_owner.758        #[weight = <T as Config>::WeightInfo::change_collection_owner()]759        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {760761            let sender = ensure_signed(origin)?;762            Self::check_owner_permissions(collection_id, sender)?;763            let mut target_collection = <Collection<T>>::get(collection_id);764            target_collection.owner = new_owner;765            <Collection<T>>::insert(collection_id, target_collection);766767            Ok(())768        }769770        /// Adds an admin of the Collection.771        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 772        /// 773        /// # Permissions774        /// 775        /// * Collection Owner.776        /// * Collection Admin.777        /// 778        /// # Arguments779        /// 780        /// * collection_id: ID of the Collection to add admin for.781        /// 782        /// * new_admin_id: Address of new admin to add.783        #[weight = <T as Config>::WeightInfo::add_collection_admin()]784        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {785786            let sender = ensure_signed(origin)?;787            Self::check_owner_or_admin_permissions(collection_id, sender)?;788            let mut admin_arr: Vec<T::AccountId> = Vec::new();789790            if <AdminList<T>>::contains_key(collection_id)791            {792                admin_arr = <AdminList<T>>::get(collection_id);793                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);794            }795796            // Number of collection admins797            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);798799            admin_arr.push(new_admin_id);800            <AdminList<T>>::insert(collection_id, admin_arr);801802            Ok(())803        }804805        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.806        ///807        /// # Permissions808        /// 809        /// * Collection Owner.810        /// * Collection Admin.811        /// 812        /// # Arguments813        /// 814        /// * collection_id: ID of the Collection to remove admin for.815        /// 816        /// * account_id: Address of admin to remove.817        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]818        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {819820            let sender = ensure_signed(origin)?;821            Self::check_owner_or_admin_permissions(collection_id, sender)?;822            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);823824            let mut admin_arr = <AdminList<T>>::get(collection_id);825            admin_arr.retain(|i| *i != account_id);826            <AdminList<T>>::insert(collection_id, admin_arr);827828            Ok(())829        }830831        /// # Permissions832        /// 833        /// * Collection Owner834        /// 835        /// # Arguments836        /// 837        /// * collection_id.838        /// 839        /// * new_sponsor.840        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]841        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {842843            let sender = ensure_signed(origin)?;844            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);845846            let mut target_collection = <Collection<T>>::get(collection_id);847            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);848849            target_collection.sponsor = new_sponsor;850            target_collection.sponsor_confirmed = false;851            <Collection<T>>::insert(collection_id, target_collection);852853            Ok(())854        }855856        /// # Permissions857        /// 858        /// * Sponsor.859        /// 860        /// # Arguments861        /// 862        /// * collection_id.863        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]864        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {865866            let sender = ensure_signed(origin)?;867            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);868869            let mut target_collection = <Collection<T>>::get(collection_id);870            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);871872            target_collection.sponsor_confirmed = true;873            <Collection<T>>::insert(collection_id, target_collection);874875            Ok(())876        }877878        /// Switch back to pay-per-own-transaction model.879        ///880        /// # Permissions881        ///882        /// * Collection owner.883        /// 884        /// # Arguments885        /// 886        /// * collection_id.887        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]888        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {889890            let sender = ensure_signed(origin)?;891            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);892893            let mut target_collection = <Collection<T>>::get(collection_id);894            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);895896            target_collection.sponsor = T::AccountId::default();897            target_collection.sponsor_confirmed = false;898            <Collection<T>>::insert(collection_id, target_collection);899900            Ok(())901        }902903        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.904        /// 905        /// # Permissions906        /// 907        /// * Collection Owner.908        /// * Collection Admin.909        /// * Anyone if910        ///     * White List is enabled, and911        ///     * Address is added to white list, and912        ///     * MintPermission is enabled (see SetMintPermission method)913        /// 914        /// # Arguments915        /// 916        /// * collection_id: ID of the collection.917        /// 918        /// * owner: Address, initial owner of the NFT.919        ///920        /// * data: Token data to store on chain.921        // #[weight =922        // (130_000_000 as Weight)923        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))924        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))925        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]926927        #[weight = <T as Config>::WeightInfo::create_item(data.len())]928        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {929930            let sender = ensure_signed(origin)?;931932            Self::collection_exists(collection_id)?;933934            let target_collection = <Collection<T>>::get(collection_id);935936            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;937            Self::validate_create_item_args(&target_collection, &data)?;938            Self::create_item_no_validation(collection_id, owner, data)?;939940            Ok(())941        }942943        /// This method creates multiple instances of NFT Collection created with CreateCollection method.944        /// 945        /// # Permissions946        /// 947        /// * Collection Owner.948        /// * Collection Admin.949        /// * Anyone if950        ///     * White List is enabled, and951        ///     * Address is added to white list, and952        ///     * MintPermission is enabled (see SetMintPermission method)953        /// 954        /// # Arguments955        /// 956        /// * collection_id: ID of the collection.957        /// 958        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].959        /// 960        /// * owner: Address, initial owner of the NFT.961        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()962                               .map(|data| { data.len() })963                               .sum())]964        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {965966            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);967            let sender = ensure_signed(origin)?;968969            Self::collection_exists(collection_id)?;970            let target_collection = <Collection<T>>::get(collection_id);971972            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;973974            for data in &items_data {975                Self::validate_create_item_args(&target_collection, data)?;976            }977            for data in &items_data {978                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;979            }980981            Ok(())982        }983984        /// Destroys a concrete instance of NFT.985        /// 986        /// # Permissions987        /// 988        /// * Collection Owner.989        /// * Collection Admin.990        /// * Current NFT Owner.991        /// 992        /// # Arguments993        /// 994        /// * collection_id: ID of the collection.995        /// 996        /// * item_id: ID of NFT to burn.997        #[weight = <T as Config>::WeightInfo::burn_item()]998        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {9991000            let sender = ensure_signed(origin)?;1001            Self::collection_exists(collection_id)?;10021003            // Transfer permissions check1004            let target_collection = <Collection<T>>::get(collection_id);1005            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1006                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1007                Error::<T>::NoPermission);10081009            if target_collection.access == AccessMode::WhiteList {1010                Self::check_white_list(collection_id, &sender)?;1011            }10121013            match target_collection.mode1014            {1015                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1016                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1017                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1018                _ => ()1019            };10201021            // call event1022            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10231024            Ok(())1025        }10261027        /// Change ownership of the token.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner1032        /// * Collection Admin1033        /// * Current NFT owner1034        ///1035        /// # Arguments1036        /// 1037        /// * recipient: Address of token recipient.1038        /// 1039        /// * collection_id.1040        /// 1041        /// * item_id: ID of the item1042        ///     * Non-Fungible Mode: Required.1043        ///     * Fungible Mode: Ignored.1044        ///     * Re-Fungible Mode: Required.1045        /// 1046        /// * value: Amount to transfer.1047        ///     * Non-Fungible Mode: Ignored1048        ///     * Fungible Mode: Must specify transferred amount1049        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1050        #[weight = <T as Config>::WeightInfo::transfer()]1051        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1052            let sender = ensure_signed(origin)?;1053            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1054        }10551056        /// Set, change, or remove approved address to transfer the ownership of the NFT.1057        /// 1058        /// # Permissions1059        /// 1060        /// * Collection Owner1061        /// * Collection Admin1062        /// * Current NFT owner1063        /// 1064        /// # Arguments1065        /// 1066        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1067        /// 1068        /// * collection_id.1069        /// 1070        /// * item_id: ID of the item.1071        #[weight = <T as Config>::WeightInfo::approve()]1072        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10731074            let sender = ensure_signed(origin)?;10751076            Self::collection_exists(collection_id)?;1077            Self::token_exists(collection_id, item_id, &sender)?;10781079            // Transfer permissions check1080            let target_collection = <Collection<T>>::get(collection_id);1081            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1082                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1083                Error::<T>::NoPermission);10841085            if target_collection.access == AccessMode::WhiteList {1086                Self::check_white_list(collection_id, &sender)?;1087                Self::check_white_list(collection_id, &spender)?;1088            }10891090            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1091            let mut allowance: u128 = amount;1092            if allowance_exists {1093                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1094            }1095            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10961097            Ok(())1098        }1099        1100        /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1101        /// 1102        /// # Permissions1103        /// * Collection Owner1104        /// * Collection Admin1105        /// * Current NFT owner1106        /// * Address approved by current NFT owner1107        /// 1108        /// # Arguments1109        /// 1110        /// * from: Address that owns token.1111        /// 1112        /// * recipient: Address of token recipient.1113        /// 1114        /// * collection_id.1115        /// 1116        /// * item_id: ID of the item.1117        /// 1118        /// * value: Amount to transfer.1119        #[weight = <T as Config>::WeightInfo::transfer_from()]1120        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11211122            let sender = ensure_signed(origin)?;1123            let mut appoved_transfer = false;11241125            // Check approval1126            let mut approval: u128 = 0;1127            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1128                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1129                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1130                appoved_transfer = true;1131            }11321133            let target_collection = <Collection<T>>::get(collection_id);11341135            // Limits check1136            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11371138            // Transfer permissions check         1139            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1140                Error::<T>::NoPermission);11411142            if target_collection.access == AccessMode::WhiteList {1143                Self::check_white_list(collection_id, &sender)?;1144                Self::check_white_list(collection_id, &recipient)?;1145            }11461147            // Reduce approval by transferred amount or remove if remaining approval drops to 01148            if approval.checked_sub(value).unwrap_or(0) > 0 {1149                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1150            }1151            else {1152                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1153            }11541155            match target_collection.mode1156            {1157                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1158                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1159                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1160                _ => ()1161            };11621163            Ok(())1164        }11651166        // #[weight = 0]1167        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11681169        //     // let no_perm_mes = "You do not have permissions to modify this collection";1170        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1171        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1172        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11731174        //     // // on_nft_received  call11751176        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11771178        //     Ok(())1179        // }11801181        /// Set off-chain data schema.1182        /// 1183        /// # Permissions1184        /// 1185        /// * Collection Owner1186        /// * Collection Admin1187        /// 1188        /// # Arguments1189        /// 1190        /// * collection_id.1191        /// 1192        /// * schema: String representing the offchain data schema.1193        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1194        pub fn set_variable_meta_data (1195            origin,1196            collection_id: CollectionId,1197            item_id: TokenId,1198            data: Vec<u8>1199        ) -> DispatchResult {1200            let sender = ensure_signed(origin)?;1201            1202            Self::collection_exists(collection_id)?;1203            Self::token_exists(collection_id, item_id, &sender)?;12041205            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12061207            // Modify permissions check1208            let target_collection = <Collection<T>>::get(collection_id);1209            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1210                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1211                Error::<T>::NoPermission);12121213            match target_collection.mode1214            {1215                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1216                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1217                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1218                _ => fail!(Error::<T>::UnexpectedCollectionType)1219            };12201221            Ok(())1222        }1223 1224        /// Set schema standard1225        /// ImageURL1226        /// Unique1227        /// 1228        /// # Permissions1229        /// 1230        /// * Collection Owner1231        /// * Collection Admin1232        /// 1233        /// # Arguments1234        /// 1235        /// * collection_id.1236        /// 1237        /// * schema: SchemaVersion: enum1238        #[weight = <T as Config>::WeightInfo::set_schema_version()]1239        pub fn set_schema_version(1240            origin,1241            collection_id: CollectionId,1242            version: SchemaVersion1243        ) -> DispatchResult {1244            let sender = ensure_signed(origin)?;1245            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1246            let mut target_collection = <Collection<T>>::get(collection_id);1247            target_collection.schema_version = version;1248            <Collection<T>>::insert(collection_id, target_collection);12491250            Ok(())1251        }12521253        /// Set off-chain data schema.1254        /// 1255        /// # Permissions1256        /// 1257        /// * Collection Owner1258        /// * Collection Admin1259        /// 1260        /// # Arguments1261        /// 1262        /// * collection_id.1263        /// 1264        /// * schema: String representing the offchain data schema.1265        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1266        pub fn set_offchain_schema(1267            origin,1268            collection_id: CollectionId,1269            schema: Vec<u8>1270        ) -> DispatchResult {1271            let sender = ensure_signed(origin)?;1272            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12731274            // check schema limit1275            ensure!(schema.len() as u32 > ChainLimit::get().offchain_schema_limit, "");12761277            let mut target_collection = <Collection<T>>::get(collection_id);1278            target_collection.offchain_schema = schema;1279            <Collection<T>>::insert(collection_id, target_collection);12801281            Ok(())1282        }12831284        /// Set const on-chain data schema.1285        /// 1286        /// # Permissions1287        /// 1288        /// * Collection Owner1289        /// * Collection Admin1290        /// 1291        /// # Arguments1292        /// 1293        /// * collection_id.1294        /// 1295        /// * schema: String representing the const on-chain data schema.1296        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1297        pub fn set_const_on_chain_schema (1298            origin,1299            collection_id: CollectionId,1300            schema: Vec<u8>1301        ) -> DispatchResult {1302            let sender = ensure_signed(origin)?;1303            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13041305            // check schema limit1306            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13071308            let mut target_collection = <Collection<T>>::get(collection_id);1309            target_collection.const_on_chain_schema = schema;1310            <Collection<T>>::insert(collection_id, target_collection);13111312            Ok(())1313        }13141315        /// Set variable on-chain data schema.1316        /// 1317        /// # Permissions1318        /// 1319        /// * Collection Owner1320        /// * Collection Admin1321        /// 1322        /// # Arguments1323        /// 1324        /// * collection_id.1325        /// 1326        /// * schema: String representing the variable on-chain data schema.1327        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1328        pub fn set_variable_on_chain_schema (1329            origin,1330            collection_id: CollectionId,1331            schema: Vec<u8>1332        ) -> DispatchResult {1333            let sender = ensure_signed(origin)?;1334            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13351336            // check schema limit1337            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13381339            let mut target_collection = <Collection<T>>::get(collection_id);1340            target_collection.variable_on_chain_schema = schema;1341            <Collection<T>>::insert(collection_id, target_collection);13421343            Ok(())1344        }13451346        // Sudo permissions function1347        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1348        pub fn set_chain_limits(1349            origin,1350            limits: ChainLimits1351        ) -> DispatchResult {13521353            #[cfg(not(feature = "runtime-benchmarks"))]1354            ensure_root(origin)?;13551356            <ChainLimit>::put(limits);1357            Ok(())1358        }13591360        /// Enable smart contract self-sponsoring.1361        /// 1362        /// # Permissions1363        /// 1364        /// * Contract Owner1365        /// 1366        /// # Arguments1367        /// 1368        /// * contract address1369        /// * enable flag1370        /// 1371        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1372        pub fn enable_contract_sponsoring(1373            origin,1374            contract_address: T::AccountId,1375            enable: bool1376        ) -> DispatchResult {13771378            let sender = ensure_signed(origin)?;13791380            #[cfg(feature = "runtime-benchmarks")]1381            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13821383            Self::ensure_contract_owned(sender, &contract_address)?;13841385            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1386            Ok(())1387        }13881389        /// Set the rate limit for contract sponsoring to specified number of blocks.1390        /// 1391        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1392        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1393        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1394        /// from contract endowment if there are at least B blocks between such transactions. 1395        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1396        /// 1397        /// # Permissions1398        /// 1399        /// * Contract Owner1400        /// 1401        /// # Arguments1402        /// 1403        /// -`contract_address`: Address of the contract to sponsor1404        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1405        /// 1406        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1407        pub fn set_contract_sponsoring_rate_limit(1408            origin,1409            contract_address: T::AccountId,1410            rate_limit: T::BlockNumber1411        ) -> DispatchResult {1412            let sender = ensure_signed(origin)?;14131414            #[cfg(feature = "runtime-benchmarks")]1415            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14161417            Self::ensure_contract_owned(sender, &contract_address)?;1418            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1419            Ok(())1420        }14211422        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1423        /// 1424        /// # Permissions1425        /// 1426        /// * Address that deployed smart contract.1427        /// 1428        /// # Arguments1429        /// 1430        /// -`contract_address`: Address of the contract.1431        /// 1432        /// - `enable`: .  1433        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1434        pub fn toggle_contract_white_list(1435            origin,1436            contract_address: T::AccountId,1437            enable: bool1438        ) -> DispatchResult {1439            let sender = ensure_signed(origin)?;14401441            #[cfg(feature = "runtime-benchmarks")]1442            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14431444            Self::ensure_contract_owned(sender, &contract_address)?;1445            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1446            Ok(())1447        }1448        1449        /// Add an address to smart contract white list.1450        /// 1451        /// # Permissions1452        /// 1453        /// * Address that deployed smart contract.1454        /// 1455        /// # Arguments1456        /// 1457        /// -`contract_address`: Address of the contract.1458        ///1459        /// -`account_address`: Address to add.1460        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1461        pub fn add_to_contract_white_list(1462            origin,1463            contract_address: T::AccountId,1464            account_address: T::AccountId1465        ) -> DispatchResult {1466            let sender = ensure_signed(origin)?;14671468            #[cfg(feature = "runtime-benchmarks")]1469            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1470            1471            Self::ensure_contract_owned(sender, &contract_address)?;      1472            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1473            Ok(())1474        }14751476        /// Remove an address from smart contract white list.1477        /// 1478        /// # Permissions1479        /// 1480        /// * Address that deployed smart contract.1481        /// 1482        /// # Arguments1483        /// 1484        /// -`contract_address`: Address of the contract.1485        ///1486        /// -`account_address`: Address to remove.1487        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1488        pub fn remove_from_contract_white_list(1489            origin,1490            contract_address: T::AccountId,1491            account_address: T::AccountId1492        ) -> DispatchResult {1493            let sender = ensure_signed(origin)?;14941495            #[cfg(feature = "runtime-benchmarks")]1496            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14971498            Self::ensure_contract_owned(sender, &contract_address)?;1499            <ContractWhiteList<T>>::remove(contract_address, account_address);1500            Ok(())1501        }15021503        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1504        pub fn set_collection_limits(1505            origin,1506            collection_id: u32,1507            limits: CollectionLimits,1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;1510            Self::check_owner_permissions(collection_id, sender.clone())?;1511            let mut target_collection = <Collection<T>>::get(collection_id);1512            let chain_limits = ChainLimit::get();1513            let climits = target_collection.limits;15141515            // collection bounds1516            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1517                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1518                Error::<T>::CollectionLimitBoundsExceeded);15191520            // token_limit   check  prev1521            ensure!(climits.token_limit > limits.token_limit && 1522                limits.token_limit <= chain_limits.account_token_ownership_limit, 1523                Error::<T>::AccountTokenLimitExceeded);15241525            target_collection.limits = limits;1526            <Collection<T>>::insert(collection_id, target_collection);15271528            Ok(())1529        } 1530    }1531}15321533impl<T: Config> Module<T> {15341535    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15361537        let target_collection = <Collection<T>>::get(collection_id);15381539        // Limits check1540        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15411542        // Transfer permissions check1543        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1544            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1545            Error::<T>::NoPermission);15461547        if target_collection.access == AccessMode::WhiteList {1548            Self::check_white_list(collection_id, &sender)?;1549            Self::check_white_list(collection_id, &recipient)?;1550        }15511552        match target_collection.mode1553        {1554            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1555            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1556            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1557            _ => ()1558        };15591560        Ok(())1561    }156215631564    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15651566        // check token limit and account token limit1567        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1568        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1569        1570        Ok(())1571    }15721573    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15741575        // check token limit and account token limit1576        let total_items: u32 = ItemListIndex::get(collection_id);1577        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1578        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1579        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15801581        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1582            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1583            Self::check_white_list(collection_id, owner)?;1584            Self::check_white_list(collection_id, sender)?;1585        }15861587        Ok(())1588    }15891590    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1591        match target_collection.mode1592        {1593            CollectionMode::NFT => {1594                if let CreateItemData::NFT(data) = data {1595                    // check sizes1596                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1597                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1598                } else {1599                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1600                }1601            },1602            CollectionMode::Fungible(_) => {1603                if let CreateItemData::Fungible(_) = data {1604                } else {1605                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1606                }1607            },1608            CollectionMode::ReFungible => {1609                if let CreateItemData::ReFungible(data) = data {16101611                    // check sizes1612                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1613                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16141615                    // Check refungibility limits1616                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1617                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1618                } else {1619                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1620                }1621            },1622            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1623        };16241625        Ok(())1626    }16271628    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1629        match data1630        {1631            CreateItemData::NFT(data) => {1632                let item = NftItemType {1633                    owner,1634                    const_data: data.const_data,1635                    variable_data: data.variable_data1636                };16371638                Self::add_nft_item(collection_id, item)?;1639            },1640            CreateItemData::Fungible(data) => {1641                Self::add_fungible_item(collection_id, &owner, data.value)?;1642            },1643            CreateItemData::ReFungible(data) => {1644                let mut owner_list = Vec::new();1645                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16461647                let item = ReFungibleItemType {1648                    owner: owner_list,1649                    const_data: data.const_data,1650                    variable_data: data.variable_data1651                };16521653                Self::add_refungible_item(collection_id, item)?;1654            }1655        };16561657        // call event1658        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16591660        Ok(())1661    }16621663    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16641665        // Does new owner already have an account?1666        let mut balance: u128 = 0;1667        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1668            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1669        } 16701671        // Mint 1672        let item = FungibleItemType {1673            value: balance + value1674        };1675        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16761677        // Update balance1678        let new_balance = <Balance<T>>::get(collection_id, owner)1679            .checked_add(value)1680            .ok_or(Error::<T>::NumOverflow)?;1681        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16821683        Ok(())1684    }16851686    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1687        let current_index = <ItemListIndex>::get(collection_id)1688            .checked_add(1)1689            .ok_or(Error::<T>::NumOverflow)?;1690        let itemcopy = item.clone();16911692        let value = item.owner.first().unwrap().fraction;1693        let owner = item.owner.first().unwrap().owner.clone();16941695        Self::add_token_index(collection_id, current_index, &owner)?;16961697        <ItemListIndex>::insert(collection_id, current_index);1698        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16991700        // Update balance1701        let new_balance = <Balance<T>>::get(collection_id, &owner)1702            .checked_add(value)1703            .ok_or(Error::<T>::NumOverflow)?;1704        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17051706        Ok(())1707    }17081709    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1710        let current_index = <ItemListIndex>::get(collection_id)1711            .checked_add(1)1712            .ok_or(Error::<T>::NumOverflow)?;17131714        let item_owner = item.owner.clone();1715        Self::add_token_index(collection_id, current_index, &item.owner)?;17161717        <ItemListIndex>::insert(collection_id, current_index);1718        <NftItemList<T>>::insert(collection_id, current_index, item);17191720        // Update balance1721        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1722            .checked_add(1)1723            .ok_or(Error::<T>::NumOverflow)?;1724        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17251726        Ok(())1727    }17281729    fn burn_refungible_item(1730        collection_id: CollectionId,1731        item_id: TokenId,1732        owner: &T::AccountId,1733    ) -> DispatchResult {1734        ensure!(1735            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1736            Error::<T>::TokenNotFound1737        );1738        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1739        let rft_balance = token1740            .owner1741            .iter()1742            .filter(|&i| i.owner == *owner)1743            .next()1744            .unwrap();1745        Self::remove_token_index(collection_id, item_id, owner)?;17461747        // update balance1748        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1749            .checked_sub(rft_balance.fraction)1750            .ok_or(Error::<T>::NumOverflow)?;1751        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17521753        // Re-create owners list with sender removed1754        let index = token1755            .owner1756            .iter()1757            .position(|i| i.owner == *owner)1758            .unwrap();1759        token.owner.remove(index);1760        let owner_count = token.owner.len();17611762        // Burn the token completely if this was the last (only) owner1763        if owner_count == 0 {1764            <ReFungibleItemList<T>>::remove(collection_id, item_id);1765        }1766        else {1767            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1768        }17691770        Ok(())1771    }17721773    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1774        ensure!(1775            <NftItemList<T>>::contains_key(collection_id, item_id),1776            Error::<T>::TokenNotFound1777        );1778        let item = <NftItemList<T>>::get(collection_id, item_id);1779        Self::remove_token_index(collection_id, item_id, &item.owner)?;17801781        // update balance1782        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1783            .checked_sub(1)1784            .ok_or(Error::<T>::NumOverflow)?;1785        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1786        <NftItemList<T>>::remove(collection_id, item_id);17871788        Ok(())1789    }17901791    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1792        ensure!(1793            <FungibleItemList<T>>::contains_key(collection_id, owner),1794            Error::<T>::TokenNotFound1795        );1796        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1797        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17981799        // update balance1800        let new_balance = <Balance<T>>::get(collection_id, owner)1801            .checked_sub(value)1802            .ok_or(Error::<T>::NumOverflow)?;1803        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18041805        if balance.value - value > 0 {1806            balance.value -= value;1807            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1808        }1809        else {1810            <FungibleItemList<T>>::remove(collection_id, owner);1811        }18121813        Ok(())1814    }18151816    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1817        ensure!(1818            <Collection<T>>::contains_key(collection_id),1819            Error::<T>::CollectionNotFound1820        );1821        Ok(())1822    }18231824    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1825        Self::collection_exists(collection_id)?;18261827        let target_collection = <Collection<T>>::get(collection_id);1828        ensure!(1829            subject == target_collection.owner,1830            Error::<T>::NoPermission1831        );18321833        Ok(())1834    }18351836    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1837        let target_collection = <Collection<T>>::get(collection_id);1838        let mut result: bool = subject == target_collection.owner;1839        let exists = <AdminList<T>>::contains_key(collection_id);18401841        if !result & exists {1842            if <AdminList<T>>::get(collection_id).contains(&subject) {1843                result = true1844            }1845        }18461847        result1848    }18491850    fn check_owner_or_admin_permissions(1851        collection_id: CollectionId,1852        subject: T::AccountId,1853    ) -> DispatchResult {1854        Self::collection_exists(collection_id)?;1855        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18561857        ensure!(1858            result,1859            Error::<T>::NoPermission1860        );1861        Ok(())1862    }18631864    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1865        let target_collection = <Collection<T>>::get(collection_id);18661867        match target_collection.mode {1868            CollectionMode::NFT => {1869                <NftItemList<T>>::get(collection_id, item_id).owner == subject1870            }1871            CollectionMode::Fungible(_) => {1872                <FungibleItemList<T>>::contains_key(collection_id, &subject)1873            }1874            CollectionMode::ReFungible => {1875                <ReFungibleItemList<T>>::get(collection_id, item_id)1876                    .owner1877                    .iter()1878                    .any(|i| i.owner == subject)1879            }1880            CollectionMode::Invalid => false,1881        }1882    }18831884    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1885        let mes = Error::<T>::AddresNotInWhiteList;1886        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18871888        Ok(())1889    }18901891    /// Check if token exists. In case of Fungible, check if there is an entry for 1892    /// the owner in fungible balances double map1893    fn token_exists(1894        collection_id: CollectionId,1895        item_id: TokenId,1896        owner: &T::AccountId1897    ) -> DispatchResult {1898        let target_collection = <Collection<T>>::get(collection_id);1899        let exists = match target_collection.mode1900        {1901            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1902            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1903            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1904            _ => false1905        };19061907        ensure!(exists == true, Error::<T>::TokenNotFound);1908        Ok(())1909    }19101911    fn transfer_fungible(1912        collection_id: CollectionId,1913        value: u128,1914        owner: &T::AccountId,1915        recipient: &T::AccountId,1916    ) -> DispatchResult {1917        Self::token_exists(collection_id, 0, owner)?;19181919        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1920        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19211922        // Send balance to recipient (updates balanceOf of recipient)1923        Self::add_fungible_item(collection_id, recipient, value)?;19241925        // update balanceOf of sender1926        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19271928        // Reduce or remove sender1929        if balance.value == value {1930            <FungibleItemList<T>>::remove(collection_id, owner);1931        }1932        else {1933            balance.value -= value;1934            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1935        }19361937        Ok(())1938    }19391940    fn transfer_refungible(1941        collection_id: CollectionId,1942        item_id: TokenId,1943        value: u128,1944        owner: T::AccountId,1945        new_owner: T::AccountId,1946    ) -> DispatchResult {1947        Self::token_exists(collection_id, item_id, &owner)?;19481949        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1950        let item = full_item1951            .owner1952            .iter()1953            .filter(|i| i.owner == owner)1954            .next()1955            .ok_or(Error::<T>::NumOverflow)?;1956        let amount = item.fraction;19571958        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19591960        // update balance1961        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1962            .checked_sub(value)1963            .ok_or(Error::<T>::NumOverflow)?;1964        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19651966        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1967            .checked_add(value)1968            .ok_or(Error::<T>::NumOverflow)?;1969        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19701971        let old_owner = item.owner.clone();1972        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19731974        // transfer1975        if amount == value && !new_owner_has_account {1976            // change owner1977            // new owner do not have account1978            let mut new_full_item = full_item.clone();1979            new_full_item1980                .owner1981                .iter_mut()1982                .find(|i| i.owner == owner)1983                .unwrap()1984                .owner = new_owner.clone();1985            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19861987            // update index collection1988            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1989        } else {1990            let mut new_full_item = full_item.clone();1991            new_full_item1992                .owner1993                .iter_mut()1994                .find(|i| i.owner == owner)1995                .unwrap()1996                .fraction -= value;19971998            // separate amount1999            if new_owner_has_account {2000                // new owner has account2001                new_full_item2002                    .owner2003                    .iter_mut()2004                    .find(|i| i.owner == new_owner)2005                    .unwrap()2006                    .fraction += value;2007            } else {2008                // new owner do not have account2009                new_full_item.owner.push(Ownership {2010                    owner: new_owner.clone(),2011                    fraction: value,2012                });2013                Self::add_token_index(collection_id, item_id, &new_owner)?;2014            }20152016            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2017        }20182019        Ok(())2020    }20212022    fn transfer_nft(2023        collection_id: CollectionId,2024        item_id: TokenId,2025        sender: T::AccountId,2026        new_owner: T::AccountId,2027    ) -> DispatchResult {2028        Self::token_exists(collection_id, item_id, &sender)?;20292030        let mut item = <NftItemList<T>>::get(collection_id, item_id);20312032        ensure!(2033            sender == item.owner,2034            Error::<T>::MustBeTokenOwner2035        );20362037        // update balance2038        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2039            .checked_sub(1)2040            .ok_or(Error::<T>::NumOverflow)?;2041        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20422043        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2044            .checked_add(1)2045            .ok_or(Error::<T>::NumOverflow)?;2046        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20472048        // change owner2049        let old_owner = item.owner.clone();2050        item.owner = new_owner.clone();2051        <NftItemList<T>>::insert(collection_id, item_id, item);20522053        // update index collection2054        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20552056        Ok(())2057    }2058    2059    fn set_re_fungible_variable_data(2060        collection_id: CollectionId,2061        item_id: TokenId,2062        data: Vec<u8>2063    ) -> DispatchResult {2064        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20652066        item.variable_data = data;20672068        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20692070        Ok(())2071    }20722073    fn set_nft_variable_data(2074        collection_id: CollectionId,2075        item_id: TokenId,2076        data: Vec<u8>2077    ) -> DispatchResult {2078        let mut item = <NftItemList<T>>::get(collection_id, item_id);2079        2080        item.variable_data = data;20812082        <NftItemList<T>>::insert(collection_id, item_id, item);2083        2084        Ok(())2085    }20862087    fn init_collection(item: &CollectionType<T::AccountId>) {2088        // check params2089        assert!(2090            item.decimal_points <= MAX_DECIMAL_POINTS,2091            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2092        );2093        assert!(2094            item.name.len() <= 64,2095            "Collection name can not be longer than 63 char"2096        );2097        assert!(2098            item.name.len() <= 256,2099            "Collection description can not be longer than 255 char"2100        );2101        assert!(2102            item.token_prefix.len() <= 16,2103            "Token prefix can not be longer than 15 char"2104        );21052106        // Generate next collection ID2107        let next_id = CreatedCollectionCount::get()2108            .checked_add(1)2109            .unwrap();21102111        CreatedCollectionCount::put(next_id);2112    }21132114    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2115        let current_index = <ItemListIndex>::get(collection_id)2116            .checked_add(1)2117            .unwrap();21182119        let item_owner = item.owner.clone();2120        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21212122        <ItemListIndex>::insert(collection_id, current_index);21232124        // Update balance2125        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2126            .checked_add(1)2127            .unwrap();2128        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2129    }21302131    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2132        let current_index = <ItemListIndex>::get(collection_id)2133            .checked_add(1)2134            .unwrap();21352136        Self::add_token_index(collection_id, current_index, owner).unwrap();21372138        <ItemListIndex>::insert(collection_id, current_index);21392140        // Update balance2141        let new_balance = <Balance<T>>::get(collection_id, owner)2142            .checked_add(item.value)2143            .unwrap();2144        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2145    }21462147    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2148        let current_index = <ItemListIndex>::get(collection_id)2149            .checked_add(1)2150            .unwrap();21512152        let value = item.owner.first().unwrap().fraction;2153        let owner = item.owner.first().unwrap().owner.clone();21542155        Self::add_token_index(collection_id, current_index, &owner).unwrap();21562157        <ItemListIndex>::insert(collection_id, current_index);21582159        // Update balance2160        let new_balance = <Balance<T>>::get(collection_id, &owner)2161            .checked_add(value)2162            .unwrap();2163        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2164    }21652166    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21672168        // add to account limit2169        if <AccountItemCount<T>>::contains_key(owner) {21702171            // bound Owned tokens by a single address2172            let count = <AccountItemCount<T>>::get(owner);2173            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21742175            <AccountItemCount<T>>::insert(owner.clone(), count2176                .checked_add(1)2177                .ok_or(Error::<T>::NumOverflow)?);2178        }2179        else {2180            <AccountItemCount<T>>::insert(owner.clone(), 1);2181        }21822183        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2184        if list_exists {2185            let mut list = <AddressTokens<T>>::get(collection_id, owner);2186            let item_contains = list.contains(&item_index.clone());21872188            if !item_contains {2189                list.push(item_index.clone());2190            }21912192            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2193        } else {2194            let mut itm = Vec::new();2195            itm.push(item_index.clone());2196            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2197        }21982199        Ok(())2200    }22012202    fn remove_token_index(2203        collection_id: CollectionId,2204        item_index: TokenId,2205        owner: &T::AccountId,2206    ) -> DispatchResult {22072208        // update counter2209        <AccountItemCount<T>>::insert(owner.clone(), 2210            <AccountItemCount<T>>::get(owner)2211            .checked_sub(1)2212            .ok_or(Error::<T>::NumOverflow)?);221322142215        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2216        if list_exists {2217            let mut list = <AddressTokens<T>>::get(collection_id, owner);2218            let item_contains = list.contains(&item_index.clone());22192220            if item_contains {2221                list.retain(|&item| item != item_index);2222                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2223            }2224        }22252226        Ok(())2227    }22282229    fn move_token_index(2230        collection_id: CollectionId,2231        item_index: TokenId,2232        old_owner: &T::AccountId,2233        new_owner: &T::AccountId,2234    ) -> DispatchResult {2235        Self::remove_token_index(collection_id, item_index, old_owner)?;2236        Self::add_token_index(collection_id, item_index, new_owner)?;22372238        Ok(())2239    }2240    2241    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2242        if <ContractOwner<T>>::contains_key(contract.clone()) {2243            let owner = <ContractOwner<T>>::get(contract);2244            ensure!(account == owner, Error::<T>::NoPermission);2245        } else {2246            fail!(Error::<T>::NoPermission);2247        }22482249        Ok(())2250    }2251}22522253////////////////////////////////////////////////////////////////////////////////////////////////////2254// Economic models2255// #region22562257/// Fee multiplier.2258pub type Multiplier = FixedU128;22592260type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22612262/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2263/// in the queue.2264#[derive(Encode, Decode, Clone, Eq, PartialEq)]2265pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);22662267impl<T: Config + Send + Sync> sp_std::fmt::Debug 2268    for ChargeTransactionPayment<T>2269{2270	#[cfg(feature = "std")]2271	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2272		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2273	}2274	#[cfg(not(feature = "std"))]2275	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2276		Ok(())2277	}2278}22792280impl<T: Config> ChargeTransactionPayment<T>2281where2282    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2283    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2284    T::AccountId: AsRef<[u8]>,2285    T::AccountId: UncheckedFrom<T::Hash>,2286{2287    fn traditional_fee(2288        len: usize,2289        info: &DispatchInfoOf<T::Call>,2290        tip: BalanceOf<T>,2291    ) -> BalanceOf<T>2292    where2293        T::Call: Dispatchable<Info = DispatchInfo>,2294    {2295        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2296    }22972298	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2299        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2300        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2301        let len_saturation = max_block_length as u64 / (len as u64).max(1);2302        let coefficient: BalanceOf<T> = weight_saturation2303            .min(len_saturation)2304            .saturated_into::<BalanceOf<T>>();2305        final_fee2306            .saturating_mul(coefficient)2307            .saturated_into::<TransactionPriority>()2308    }23092310    fn withdraw_fee(2311        &self,2312        who: &T::AccountId,2313        call: &T::Call,2314        info: &DispatchInfoOf<T::Call>,2315        len: usize,2316	) -> Result<2317		(2318			BalanceOf<T>,2319			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2320		),2321		TransactionValidityError,2322	> {2323        let tip = self.0;23242325        // Set fee based on call type. Creating collection costs 1 Unique.2326        // All other transactions have traditional fees so far2327        // let fee = match call.is_sub_type() {2328        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2329        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2330        //                                                 // _ => <BalanceOf<T>>::from(100)2331        // };2332        let fee = Self::traditional_fee(len, info, tip);23332334        // Only mess with balances if fee is not zero.2335        if fee.is_zero() {2336            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2337			.map(|i| (fee, i));2338        }23392340        // Determine who is paying transaction fee based on ecnomic model2341        // Parse call to extract collection ID and access collection sponsor2342        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2343            Some(Call::create_item(collection_id, _owner, _properties)) => {23442345                // sponsor timeout2346                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23472348                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2349                let mut sponsored = true;2350                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2351                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2352                    let limit_time = last_tx_block + limit.into();2353                    if block_number <= limit_time {2354                        sponsored = false;2355                    }2356                }2357                if sponsored {2358                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2359                }23602361                // check free create limit2362                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2363                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2364                   (sponsored)2365                {2366                    <Collection<T>>::get(collection_id).sponsor2367                } else {2368                    T::AccountId::default()2369                }2370            }2371            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2372                2373                let mut sponsor_transfer = false;2374                if <Collection<T>>::get(collection_id).sponsor_confirmed {23752376                    let collection_limits = <Collection<T>>::get(collection_id).limits;2377                    let collection_mode = <Collection<T>>::get(collection_id).mode;2378    2379                    // sponsor timeout2380                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2381                    sponsor_transfer = match collection_mode {2382                        CollectionMode::NFT => {2383    2384                            // get correct limit2385                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2386                                collection_limits.sponsor_transfer_timeout2387                            } else {2388                                ChainLimit::get().nft_sponsor_transfer_timeout2389                            };2390    2391                            let mut sponsored = true;2392                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2393                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2394                                let limit_time = last_tx_block + limit.into();2395                                if block_number <= limit_time {2396                                    sponsored = false;2397                                }2398                            }2399                            if sponsored {2400                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2401                            }24022403                            sponsored2404                        }2405                        CollectionMode::Fungible(_) => {2406    2407                            // get correct limit2408                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2409                                collection_limits.sponsor_transfer_timeout2410                            } else {2411                                ChainLimit::get().fungible_sponsor_transfer_timeout2412                            };2413    2414                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2415                            let mut sponsored = true;2416                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2417                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2418                                let limit_time = last_tx_block + limit.into();2419                                if block_number <= limit_time {2420                                    sponsored = false;2421                                }2422                            }2423                            if sponsored {2424                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2425                            }24262427                            sponsored2428                        }2429                        CollectionMode::ReFungible => {2430    2431                            // get correct limit2432                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2433                                collection_limits.sponsor_transfer_timeout2434                            } else {2435                                ChainLimit::get().refungible_sponsor_transfer_timeout2436                            };2437    2438                            let mut sponsored = true;2439                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2440                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2441                                let limit_time = last_tx_block + limit.into();2442                                if block_number <= limit_time {2443                                    sponsored = false;2444                                }2445                            }2446                            if sponsored {2447                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2448                            }24492450                            sponsored2451                        }2452                        _ => {2453                            false2454                        },2455                    };2456                }24572458                if !sponsor_transfer {2459                    T::AccountId::default()2460                } else {2461                    <Collection<T>>::get(collection_id).sponsor2462                }2463            }24642465            _ => T::AccountId::default(),2466        };24672468        // Sponsor smart contracts2469        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24702471            // On instantiation: set the contract owner2472            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {24732474                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2475                    &who,2476                    code_hash,2477                    salt,2478                );2479                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24802481                T::AccountId::default()2482            },24832484            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2485            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24862487                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24882489                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2490                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2491                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2492                  2493                if !owned_contract && white_list_enabled {2494                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2495                        return Err(InvalidTransaction::Call.into());2496                    }2497                }24982499                let mut sponsor_transfer = false;2500                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2501                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2502                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2503                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2504                    let limit_time = last_tx_block + rate_limit;25052506                    if block_number >= limit_time {2507                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2508                        sponsor_transfer = true;2509                    }2510                } else {2511                    sponsor_transfer = false;2512                }2513               2514                2515                let mut sp = T::AccountId::default();2516                if sponsor_transfer {2517                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2518                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2519                            sp = called_contract;2520                        }2521                    }2522                }25232524                sp2525            },25262527            _ => sponsor,2528        };25292530        let mut who_pays_fee: T::AccountId = sponsor.clone();2531        if sponsor == T::AccountId::default() {2532            who_pays_fee = who.clone();2533        }25342535		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2536			.map(|i| (fee, i))2537    }2538}253925402541impl<T: Config + Send + Sync> SignedExtension2542    for ChargeTransactionPayment<T>2543where2544    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2545    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2546    T::AccountId: AsRef<[u8]>,2547    T::AccountId: UncheckedFrom<T::Hash>,2548{2549    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2550    type AccountId = T::AccountId;2551    type Call = T::Call;2552    type AdditionalSigned = ();2553    type Pre = (2554        // tip2555        BalanceOf<T>,2556        // who pays fee2557        Self::AccountId,2558		// imbalance resulting from withdrawing the fee2559		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2560    );2561    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2562        Ok(())2563    }25642565    fn validate(2566        &self,2567        who: &Self::AccountId,2568        call: &Self::Call,2569        info: &DispatchInfoOf<Self::Call>,2570        len: usize,2571    ) -> TransactionValidity {2572		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2573		Ok(ValidTransaction {2574			priority: Self::get_priority(len, info, fee),2575			..Default::default()2576		})2577    }25782579    fn pre_dispatch(2580        self,2581        who: &Self::AccountId,2582        call: &Self::Call,2583        info: &DispatchInfoOf<Self::Call>,2584        len: usize,2585    ) -> Result<Self::Pre, TransactionValidityError> {2586        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2587        Ok((self.0, who.clone(), imbalance))2588    }25892590    fn post_dispatch(2591        pre: Self::Pre,2592        info: &DispatchInfoOf<Self::Call>,2593        post_info: &PostDispatchInfoOf<Self::Call>,2594        len: usize,2595        _result: &DispatchResult,2596    ) -> Result<(), TransactionValidityError> {2597		let (tip, who, imbalance) = pre;2598		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2599			len as u32,2600			info,2601			post_info,2602			tip,2603		);2604		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2605		Ok(())2606    }2607}26082609// #endregion
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
--- a/pallets/nft/src/mock.rs
+++ b/pallets/nft/src/mock.rs
@@ -1,26 +1,21 @@
 // Creating mock runtime here
 
-use crate::{Module, Trait};
-
-use pallet_contracts::{
-	ContractAddressFor, TrieId, TrieIdGenerator,
-};
+use crate::{Module, Config};
 
 use frame_support::{
     impl_outer_origin, parameter_types,
-    weights::{
-      //  constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},
-        Weight, IdentityFee,
-    },
+    weights::{Weight, IdentityFee},
 };
 use frame_system as system;
-use transaction_payment;
+use system::limits::{BlockLength, BlockWeights};
+use transaction_payment::{self, CurrencyAdapter};
 use sp_core::H256;
 use sp_runtime::{
     testing::Header,
-    traits::{BlakeTwo256, IdentityLookup, Saturating},
+    traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
+use pallet_contracts::WeightInfo;
 pub use pallet_balances;
 
 impl_outer_origin! {
@@ -34,14 +29,12 @@
 pub struct Test;
 parameter_types! {
     pub const BlockHashCount: u64 = 250;
-    pub const MaximumBlockWeight: Weight = 1024;
-    pub const MaximumBlockLength: u32 = 2 * 1024;
-    pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
-    pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()
-    .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();
+	pub TestBlockWeights: BlockWeights = BlockWeights::default();
+	pub TestBlockLength: BlockLength = BlockLength::default();
+	pub SS58Prefix: u8 = 0;
 }
 
-impl system::Trait for Test {
+impl system::Config for Test {
 	type BaseCallFilter = ();
 	type Origin = Origin;
 	type Call = ();
@@ -54,18 +47,15 @@
 	type Header = Header;
 	type Event = ();
 	type BlockHashCount = BlockHashCount;
-	type MaximumBlockWeight = MaximumBlockWeight;
+	type BlockWeights = TestBlockWeights;
 	type DbWeight = ();
-	type BlockExecutionWeight = ();
-	type ExtrinsicBaseWeight = ();
-	type MaximumExtrinsicWeight = MaximumBlockWeight;
-	type MaximumBlockLength = MaximumBlockLength;
-	type AvailableBlockRatio = AvailableBlockRatio;
+	type BlockLength = TestBlockLength;
 	type Version = ();
 	type PalletInfo = ();
 	type AccountData = pallet_balances::AccountData<u64>;
 	type OnNewAccount = ();
 	type OnKilledAccount = ();
+	type SS58Prefix = SS58Prefix;
 	type SystemWeightInfo = ();
 }
 
@@ -75,7 +65,7 @@
 }
 
 type System = frame_system::Module<Test>;
-impl pallet_balances::Trait for Test {
+impl pallet_balances::Config for Test {
     type AccountStore = System;
     type Balance = u64;
     type DustRemoval = ();
@@ -88,9 +78,9 @@
 parameter_types! {
 	pub const TransactionByteFee: u64 = 1;
 }
-impl transaction_payment::Trait for Test {
-	type Currency = pallet_balances::Module<Test>;
-	type OnTransactionPayment = ();
+
+impl transaction_payment::Config for Test {
+	type OnChargeTransaction = CurrencyAdapter<pallet_balances::Module<Test>, ()>;
 	type TransactionByteFee = TransactionByteFee;
 	type WeightToFee = IdentityFee<u64>;
 	type FeeMultiplierUpdate = ();
@@ -100,7 +90,7 @@
 parameter_types! {
 	pub const MinimumPeriod: u64 = 1;
 }
-impl pallet_timestamp::Trait for Test {
+impl pallet_timestamp::Config for Test {
 	type Moment = u64;
 	type OnTimestampSet = ();
 	type MinimumPeriod = MinimumPeriod;
@@ -112,49 +102,45 @@
 
 parameter_types! {
 	pub const TombstoneDeposit: u64 = 1;
-	pub const RentByteFee: u64 = 1;
-	pub const RentDepositOffset: u64 = 1;
+	pub const DepositPerContract: u64 = 1;
+	pub const DepositPerStorageByte: u64 = 1;
+	pub const DepositPerStorageItem: u64 = 1;
+	pub RentFraction: Perbill = Perbill::from_rational_approximation(1u32, 30 * 24 * 60 * 10);
 	pub const SurchargeReward: u64 = 1;
-}
-
-pub struct DummyTrieIdGenerator;
-impl TrieIdGenerator<u64> for DummyTrieIdGenerator {
-	fn trie_id(account_id: &u64) -> TrieId {
-		let new_seed = *account_id + 1;
-		let mut res = vec![];
-		res.extend_from_slice(&new_seed.to_le_bytes());
-		res.extend_from_slice(&account_id.to_le_bytes());
-		res
-	}
-}
-
-pub struct DummyContractAddressFor;
-impl ContractAddressFor<H256, u64> for DummyContractAddressFor {
-	fn contract_address_for(_code_hash: &H256, _data: &[u8], origin: &u64) -> u64 {
-		*origin + 1
-	}
+	pub const SignedClaimHandicap: u32 = 2;
+	pub const MaxDepth: u32 = 32;
+	pub const MaxValueSize: u32 = 16 * 1024;
+	pub DeletionWeightLimit: Weight = Perbill::from_percent(10) *
+		TestBlockWeights::get().max_block;
+	pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (
+		<Test as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -
+		<Test as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)
+	)) / 5) as u32;
 }
 
-impl pallet_contracts::Trait for Test {
+impl pallet_contracts::Config for Test {
 	type Time = Timestamp;
 	type Randomness = Randomness;
 	type Currency = pallet_balances::Module<Test>;
 	type Event = ();
-	type DetermineContractAddress = DummyContractAddressFor;
-	type TrieIdGenerator = DummyTrieIdGenerator;
 	type RentPayment = ();
-	type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;
+	type SignedClaimHandicap = SignedClaimHandicap;
 	type TombstoneDeposit = TombstoneDeposit;
-	type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset;
-	type RentByteFee = RentByteFee;
-	type RentDepositOffset = RentDepositOffset;
+	type DepositPerContract = DepositPerContract;
+	type DepositPerStorageByte = DepositPerStorageByte;
+	type DepositPerStorageItem = DepositPerStorageItem;
+	type RentFraction = RentFraction;
 	type SurchargeReward = SurchargeReward;
-	type MaxDepth = pallet_contracts::DefaultMaxDepth;
-	type MaxValueSize = pallet_contracts::DefaultMaxValueSize;
+	type DeletionWeightLimit = DeletionWeightLimit;
+	type MaxDepth = MaxDepth;
+	type DeletionQueueDepth = DeletionQueueDepth;
+	type MaxValueSize = MaxValueSize;
+	type ChainExtension = ();
 	type WeightPrice = ();
+	type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
 }
 
-impl Trait for Test {
+impl Config for Test {
 	type Event = ();
 	type WeightInfo = ();
 
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -35,7 +35,7 @@
 }
 
 fn default_re_fungible_data () -> CreateReFungibleData {
-    CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] }
+    CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1], pieces: 1023 }
 }
 
 fn create_test_collection_for_owner(mode: &CollectionMode, owner: u64, id: CollectionId) -> CollectionId {
@@ -114,26 +114,6 @@
 }
 
 #[test]
-fn create_re_fungible_collection_fails_with_large_decimal_numbers() {
-    new_test_ext().execute_with(|| {
-        default_limits();
-
-        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
-        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
-        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
-
-        let origin1 = Origin::signed(1);
-        assert_noop!(TemplateModule::create_collection(
-            origin1,
-            col_name1,
-            col_desc1,
-            token_prefix1,
-            CollectionMode::ReFungible(MAX_DECIMAL_POINTS + 1)
-        ), Error::<Test>::CollectionDecimalPointLimitExceeded);
-    });
-}
-
-#[test]
 fn create_nft_item() {
     new_test_ext().execute_with(|| {
         default_limits();
@@ -176,7 +156,7 @@
 fn create_refungible_item() {
     new_test_ext().execute_with(|| {
         default_limits();
-        let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+        let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
         let data = default_re_fungible_data();
         create_test_item(collection_id, &data.clone().into());
@@ -192,7 +172,7 @@
             TemplateModule::refungible_item_id(collection_id, 1).owner[0],
             Ownership {
                 owner: 1,
-                fraction: 1000
+                fraction: 1023
             }
         );
     });
@@ -203,7 +183,7 @@
     new_test_ext().execute_with(|| {
         default_limits();
         
-        create_test_collection(&CollectionMode::ReFungible(3), 1);
+        create_test_collection(&CollectionMode::ReFungible, 1);
 
         let origin1 = Origin::signed(1);
 
@@ -224,7 +204,7 @@
                 item.owner[0],
                 Ownership {
                     owner: 1,
-                    fraction: 1000
+                    fraction: 1023
                 }
             );
         }
@@ -312,7 +292,7 @@
     new_test_ext().execute_with(|| {
         default_limits();
         
-        let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+        let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
         let data = default_re_fungible_data();
         create_test_item(collection_id, &data.clone().into());
@@ -331,23 +311,23 @@
             TemplateModule::refungible_item_id(collection_id, 1).owner[0],
             Ownership {
                 owner: 1,
-                fraction: 1000
+                fraction: 1023
             }
         );
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
+        assert_eq!(TemplateModule::balance_count(1, 1), 1023);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
         // change owner scenario
-        assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));
+        assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1023));
         assert_eq!(
             TemplateModule::refungible_item_id(1, 1).owner[0],
             Ownership {
                 owner: 2,
-                fraction: 1000
+                fraction: 1023
             }
         );
         assert_eq!(TemplateModule::balance_count(1, 1), 0);
-        assert_eq!(TemplateModule::balance_count(1, 2), 1000);
+        assert_eq!(TemplateModule::balance_count(1, 2), 1023);
         // assert_eq!(TemplateModule::address_tokens(1, 1), []);
         assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
 
@@ -357,7 +337,7 @@
             TemplateModule::refungible_item_id(1, 1).owner[0],
             Ownership {
                 owner: 2,
-                fraction: 500
+                fraction: 523
             }
         );
         assert_eq!(
@@ -367,7 +347,7 @@
                 fraction: 500
             }
         );
-        assert_eq!(TemplateModule::balance_count(1, 2), 500);
+        assert_eq!(TemplateModule::balance_count(1, 2), 523);
         assert_eq!(TemplateModule::balance_count(1, 3), 500);
         assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
         assert_eq!(TemplateModule::address_tokens(1, 3), [1]);
@@ -378,7 +358,7 @@
             TemplateModule::refungible_item_id(1, 1).owner[0],
             Ownership {
                 owner: 2,
-                fraction: 300
+                fraction: 323
             }
         );
         assert_eq!(
@@ -388,7 +368,7 @@
                 fraction: 700
             }
         );
-        assert_eq!(TemplateModule::balance_count(1, 2), 300);
+        assert_eq!(TemplateModule::balance_count(1, 2), 323);
         assert_eq!(TemplateModule::balance_count(1, 3), 700);
         assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
         assert_eq!(TemplateModule::address_tokens(1, 3), [1]);
@@ -454,7 +434,7 @@
         assert_ok!(TemplateModule::transfer_from(
             origin2.clone(),
             1,
-            2,
+            3,
             1,
             1,
             1
@@ -508,7 +488,7 @@
             1,
             1
         ));
-        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4);
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4);
     });
 }
 
@@ -517,7 +497,7 @@
     new_test_ext().execute_with(|| {
         default_limits();
         
-        let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+        let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
         
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
@@ -525,7 +505,7 @@
         let data = default_re_fungible_data();
         create_test_item(collection_id, &data.into());
 
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
+        assert_eq!(TemplateModule::balance_count(1, 1), 1023);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
         assert_ok!(TemplateModule::set_mint_permission(
@@ -543,10 +523,8 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
-        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000));
-        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1023));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1023);
 
         assert_ok!(TemplateModule::transfer_from(
             origin2.clone(),
@@ -556,14 +534,14 @@
             1,
             100
         ));
-        assert_eq!(TemplateModule::balance_count(1, 1), 900);
+        assert_eq!(TemplateModule::balance_count(1, 1), 923);
         assert_eq!(TemplateModule::balance_count(1, 3), 100);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
         assert_eq!(TemplateModule::address_tokens(1, 3), [1]);
 
         assert_eq!(
-            TemplateModule::approved(1, (1, 1, 3)),
-            900
+            TemplateModule::approved(1, (1, 1, 2)),
+            923
         );
     });
 }
@@ -618,10 +596,8 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1);
         assert_eq!(TemplateModule::balance_count(1, 3), 4);
 
-        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
-        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1);
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
 
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
         assert_noop!(TemplateModule::transfer_from(
             origin2.clone(),
             1,
@@ -721,7 +697,7 @@
     new_test_ext().execute_with(|| {
         default_limits();
         
-        let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+        let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
         let origin1 = Origin::signed(1);
 
         assert_ok!(TemplateModule::set_mint_permission(
@@ -742,12 +718,12 @@
         create_test_item(collection_id, &data.into());
 
         // check balance (collection with id = 1, user id = 2)
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
+        assert_eq!(TemplateModule::balance_count(1, 1), 1023);
 
         // burn item
-        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000));
+        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1023));
         assert_noop!(
-            TemplateModule::burn_item(origin1.clone(), 1, 1, 1000),
+            TemplateModule::burn_item(origin1.clone(), 1, 1, 1023),
             Error::<Test>::TokenNotFound
         );
 
@@ -811,7 +787,7 @@
         
         let nft_collection_id = create_test_collection(&CollectionMode::NFT, 1);
         let fungible_collection_id = create_test_collection(&CollectionMode::Fungible(3), 2);
-        let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible(3), 3);
+        let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible, 3);
         
         // check balance before
         assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 0);
@@ -830,7 +806,7 @@
         // check balance (collection with id = 1, user id = 1)
         assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);
         assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5);
-        assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);
+        assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1023);
         assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);
         assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5);
         assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);
@@ -1909,7 +1885,7 @@
     new_test_ext().execute_with(|| {
         default_limits();
 
-        let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+        let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
         let origin1 = Origin::signed(1);
 
@@ -1986,7 +1962,7 @@
         }));
 
 
-        let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+        let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
         let origin1 = Origin::signed(1);
 
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -1,27 +1,4 @@
 {
-    "Schedule": {
-      "version": "u32",
-      "put_code_per_byte_cost": "Gas",
-      "grow_mem_cost": "Gas",
-      "regular_op_cost": "Gas",
-      "return_data_per_byte_cost": "Gas",
-      "event_data_per_byte_cost": "Gas",
-      "event_per_topic_cost": "Gas",
-      "event_base_cost": "Gas",
-      "call_base_cost": "Gas",
-      "instantiate_base_cost": "Gas",
-      "dispatch_base_cost": "Gas",
-      "sandbox_data_read_cost": "Gas",
-      "sandbox_data_write_cost": "Gas",
-      "transfer_cost": "Gas",
-      "instantiate_cost": "Gas",
-      "max_event_topics": "u32",
-      "max_stack_height": "u32",
-      "max_memory_pages": "u32",
-      "max_table_size": "u32",
-      "enable_println": "bool",
-      "max_subject_len": "u32"
-    },
     "AccessMode": {
       "_enum": [
         "Normal",
@@ -34,7 +11,7 @@
         "Invalid": null,
         "NFT": null,
         "Fungible": "DecimalPoints",
-        "ReFungible": "DecimalPoints"
+        "ReFungible": null
       }
     },
     "Ownership": {
@@ -84,7 +61,8 @@
     },
     "CreateReFungibleData": {
       "const_data": "Vec<u8>",
-      "variable_data": "Vec<u8>" 
+      "variable_data": "Vec<u8>",
+      "pieces": "u128"
     },
     "CreateItemData": {
       "_enum": {
@@ -102,27 +80,21 @@
     "CollectionId": "u32",
     "TokenId": "u32",
     "ChainLimits": {
-      "collection_numbers_limit": "u32",
-      "account_token_ownership_limit": "u32",
-      "collections_admins_limit": "u64",
-      "custom_data_limit": "u32",
-      "nft_sponsor_timeout": "u32",
-      "fungible_sponsor_timeout": "u32",
-      "refungible_sponsor_timeout": "u32"
+      "CollectionNumbersLimit": "u32",
+      "AccountTokenOwnershipLimit": "u32",
+      "CollectionAdminsLimit": "u64",
+      "CustomDataLimit": "u32",
+      "NftSponsorTimeout": "u32",
+      "FungibleSponsorTimeout": "u32",
+      "RefungibleSponsorTimeout": "u32",
+      "OffchainSchemaLimit": "u32",
+      "VariableOnChainSchemaLimit": "u32",
+      "ConstOnChainSchemaLimit": "u32"
     },
     "CollectionLimits": {
       "AccountTokenOwnershipLimit": "u32",
       "SponsoredMintSize": "u32",
       "TokenLimit": "u32",
       "SponsorTimeout": "u32"
-    },
-    "AccountInfo": "AccountInfoWithProviders",
-    "AccountInfoWithProviders": {
-      "nonce": "Index",
-      "consumers": "RefCount",
-      "providers": "RefCount",
-      "data": "AccountData"
     }
-
-  }
-  
\ No newline at end of file
+}
\ No newline at end of file
addedtests/.vscode/settings.jsondiffbeforeafterboth
--- /dev/null
+++ b/tests/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "mocha.enabled": true
+}
\ No newline at end of file
modifiedtests/README.mddiffbeforeafterboth
--- a/tests/README.md
+++ b/tests/README.md
@@ -2,8 +2,8 @@
 
 ## How to run
 
-1. Run `npm install`.
+1. Run `yarn 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`.
+4. Run `yarn test`.
 
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -22,10 +22,13 @@
     "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts",
     "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
     "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
+    "testSetVariableMetaData": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetaData.test.ts",
     "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
     "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
+    "testRemoveFromWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/removeFromWhiteList.test.ts",
     "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",
     "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+    "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts",
     "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
     "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
     "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
@@ -33,7 +36,10 @@
     "testAddToContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractWhiteList.test.ts",
     "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",
     "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts",
-    "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts"
+    "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts",
+    "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts",
+    "testEnableContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/enableContractSponsoring.test.ts",
+    "testSetContractSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setContractSponsoringRateLimit.test.ts"
   },
   "author": "",
   "license": "SEE LICENSE IN ../LICENSE",
modifiedtests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/addCollectionAdmin.test.ts
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -1,4 +1,9 @@
-import { ApiPromise } from '@polkadot/api';
+//
+// 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 BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
@@ -98,7 +103,7 @@
     });
   });
 
-  it('Add an admin to a collection that has reached the maximum number of admins limit', async () => {
+  it.only('Add an admin to a collection that has reached the maximum number of admins limit', async () => {
     await usingApi(async (api: ApiPromise) => {
       const Alice = privateKey('//Alice');
       const accounts = [
@@ -112,18 +117,18 @@
       ];
       const collectionId = await createCollectionExpectSuccess();
 
-      const chainLimit = await api.query.nft.chainLimit() as unknown as { collections_admins_limit: BN };
-      const chainLimitNumber = chainLimit.collections_admins_limit.toNumber();
-      expect(chainLimitNumber).to.be.equal(5);
+      const chainLimit = await api.query.nft.chainLimit() as unknown as { CollectionAdminsLimit: BN };
+      const chainAdminLimit = chainLimit.CollectionAdminsLimit.toNumber();
+      expect(chainAdminLimit).to.be.equal(5);
 
-      for (let i = 0; i < chainLimitNumber; i++) {
+      for (let i = 0; i < chainAdminLimit; i++) {
         const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, accounts[i]);
         await submitTransactionAsync(Alice, changeAdminTx);
         const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
         expect(adminListAfterAddAdmin).to.be.contains(accounts[i]);
       }
 
-      const tx = api.tx.nft.addCollectionAdmin(collectionId, accounts[chainLimitNumber]);
+      const tx = api.tx.nft.addCollectionAdmin(collectionId, accounts[chainAdminLimit]);
       await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
     });
   });
modifiedtests/src/addToContractWhiteList.test.tsdiffbeforeafterboth
--- a/tests/src/addToContractWhiteList.test.ts
+++ b/tests/src/addToContractWhiteList.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 usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
modifiedtests/src/addToWhiteList.test.tsdiffbeforeafterboth
--- a/tests/src/addToWhiteList.test.ts
+++ b/tests/src/addToWhiteList.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 { IKeyringPair } from '@polkadot/types/types';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
modifiedtests/src/approve.test.tsdiffbeforeafterboth
--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -34,7 +34,7 @@
       await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
       // reFungible
       const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
     });
@@ -56,7 +56,7 @@
       await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);
       // reFungible
       const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);
       await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);
@@ -95,7 +95,7 @@
       await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);
       // reFungible
       const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       await destroyCollectionExpectSuccess(reFungibleCollectionId);
       await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);
     });
@@ -113,7 +113,7 @@
       await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);
       // reFungible
       const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);
     });
   });
@@ -132,7 +132,7 @@
       await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);
       // reFungible
       const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);
     });
modifiedtests/src/burnItem.test.tsdiffbeforeafterboth
--- a/tests/src/burnItem.test.ts
+++ b/tests/src/burnItem.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 { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
 import { Keyring } from "@polkadot/api";
 import { IKeyringPair } from "@polkadot/types/types";
@@ -70,7 +75,7 @@
   });
   it('Burn item in ReFungible collection', async () => {
     const createMode = 'ReFungible';
-    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
     const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
 
     await usingApi(async (api) => {
@@ -91,7 +96,7 @@
 
   it('Burn owned portion of item in ReFungible collection', async () => {
     const createMode = 'ReFungible';
-    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
     const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
 
     await usingApi(async (api) => {
@@ -107,7 +112,7 @@
       const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
       const events2 = await submitTransactionAsync(bob, tx);
       const result2 = getGenericResult(events2);
-  
+
       // Get balances 
       const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
       // console.log(balance);
modifiedtests/src/change-collection-owner.test.tsdiffbeforeafterboth
--- a/tests/src/change-collection-owner.test.ts
+++ b/tests/src/change-collection-owner.test.ts
@@ -1,4 +1,9 @@
-import chai from 'chai';
+//
+// 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 privateKey from './substrate/privateKey';
 import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
modifiedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -115,7 +115,7 @@
   });
 
   it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -247,7 +247,7 @@
   });
 
   it('ReFungible: Sponsoring is rate limited', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.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 usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -28,7 +28,7 @@
     await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
   });
   it('Create new ReFungible collection', async () => {
-    await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+    await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
   });
 });
 
modifiedtests/src/createItem.test.tsdiffbeforeafterboth
--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.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 { default as usingApi } from './substrate/substrate-api';
 import { Keyring } from "@polkadot/api";
 import { IKeyringPair } from "@polkadot/types/types";
@@ -28,7 +33,7 @@
   });
   it('Create new item in ReFungible collection', async () => {
     const createMode = 'ReFungible';
-    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
 });
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -2,68 +2,177 @@
 // 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 BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {
+  createCollectionExpectSuccess,
+  destroyCollectionExpectSuccess,
+  IReFungibleTokenDataType,
+} from './util/helpers';
 
-import { assert } from 'chai';
-import { alicesPublicKey } from './accounts';
-import privateKey from './substrate/privateKey';
-import usingApi from './substrate/substrate-api';
-import waitNewBlocks from './substrate/wait-new-blocks';
+chai.use(chaiAsPromised);
+const expect = chai.expect;
 
-const idCollection = 12;
+interface ITokenDataType {
+  Owner: number[];
+  ConstData: number[];
+  VariableData: number[];
+}
 
-describe.skip('integration test: ext. createMultipleItems():', () => {
-  it('Create two NFT tokens in active NFT collection', async () => {
-    await usingApi(async (api) => {
-      const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (before): ${AitemListIndex}`);
-      const args = ['NFT', 'NFT'];
-      const alicePrivateKey = privateKey('//Alice');
-      const createMultipleItems = await api.tx.nft
-      .createMultipleItems(idCollection, alicesPublicKey, args)
-      .signAndSend(alicePrivateKey);
-      // tslint:disable-next-line: no-unused-expression
-      assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`');
-      console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`);
-      await waitNewBlocks(api);
-      const BitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (after): ${BitemListIndex}`);
-      if (BitemListIndex === AitemListIndex) { assert.fail('Corret token not added in collection!'); }
+describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
+  it('Create  0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexBefore.toNumber()).to.be.equal(0);
+      const Alice = privateKey('//Alice');
+      const args = [{ nft: ['0x31', '0x31'] }, { nft: ['0x32', '0x32'] }, { nft: ['0x33', '0x33'] }];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await submitTransactionAsync(Alice, createMultipleItemsTx);
+      const itemsListIndexAfter = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexAfter.toNumber()).to.be.equal(3);
+      const token1Data = await api.query.nft.nftItemList(collectionId, 1) as unknown as ITokenDataType;
+      const token2Data = await api.query.nft.nftItemList(collectionId, 2) as unknown as ITokenDataType;
+      const token3Data = await api.query.nft.nftItemList(collectionId, 3) as unknown as ITokenDataType;
+
+      expect(token1Data.Owner.toString()).to.be.equal(Alice.address);
+      expect(token2Data.Owner.toString()).to.be.equal(Alice.address);
+      expect(token3Data.Owner.toString()).to.be.equal(Alice.address);
+
+      expect(token1Data.ConstData.toString()).to.be.equal('0x31');
+      expect(token2Data.ConstData.toString()).to.be.equal('0x32');
+      expect(token3Data.ConstData.toString()).to.be.equal('0x33');
+
+      expect(token1Data.VariableData.toString()).to.be.equal('0x31');
+      expect(token2Data.VariableData.toString()).to.be.equal('0x32');
+      expect(token3Data.VariableData.toString()).to.be.equal('0x33');
     });
   });
-  it('(!negative test!) Create two Fungible tokens in active NFT collection', async () => {
-    await usingApi(async (api) => {
-      const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (before): ${AitemListIndex}`);
-      const args = ['Fungible', 'Fungible'];
-      const alicePrivateKey = privateKey('//Alice');
-      const createMultipleItems = await api.tx.nft
-      .createMultipleItems(idCollection, alicesPublicKey, args)
-      .signAndSend(alicePrivateKey);
-      // tslint:disable-next-line: no-unused-expression
-      assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`');
-      console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`);
-      await waitNewBlocks(api);
-      const BitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (after): ${BitemListIndex}`);
-      if (BitemListIndex > AitemListIndex) { assert.fail('Incorrect token added in collection!'); }
+
+  it('Create  0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+      const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexBefore.toNumber()).to.be.equal(0);
+      const Alice = privateKey('//Alice');
+      const args = [
+        {refungible: {const_data: [0x31], variable_data: [0x31], pieces: 1}},
+        {refungible: {const_data: [0x32], variable_data: [0x32], pieces: 1}},
+        {refungible: {const_data: [0x33], variable_data: [0x33], pieces: 1}},
+      ];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await submitTransactionAsync(Alice, createMultipleItemsTx);
+      const itemsListIndexAfter = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexAfter.toNumber()).to.be.equal(3);
+      const token1Data = await api.query.nft.reFungibleItemList(collectionId, 1) as unknown as IReFungibleTokenDataType;
+      const token2Data = await api.query.nft.reFungibleItemList(collectionId, 2) as unknown as IReFungibleTokenDataType;
+      const token3Data = await api.query.nft.reFungibleItemList(collectionId, 3) as unknown as IReFungibleTokenDataType;
+
+      expect(token1Data.Owner[0].Owner.toString()).to.be.equal(Alice.address);
+      expect(token1Data.Owner[0].Fraction.toNumber()).to.be.equal(1);
+
+      expect(token2Data.Owner[0].Owner.toString()).to.be.equal(Alice.address);
+      expect(token2Data.Owner[0].Fraction.toNumber()).to.be.equal(1);
+
+      expect(token3Data.Owner[0].Owner.toString()).to.be.equal(Alice.address);
+      expect(token3Data.Owner[0].Fraction.toNumber()).to.be.equal(1);
+
+      expect(token1Data.ConstData.toString()).to.be.equal('0x31');
+      expect(token2Data.ConstData.toString()).to.be.equal('0x32');
+      expect(token3Data.ConstData.toString()).to.be.equal('0x33');
+
+      expect(token1Data.VariableData.toString()).to.be.equal('0x31');
+      expect(token2Data.VariableData.toString()).to.be.equal('0x32');
+      expect(token3Data.VariableData.toString()).to.be.equal('0x33');
     });
   });
-  it('(!negative test!) Create two ReFungible tokens in active NFT collection', async () => {
-    await usingApi(async (api) => {
-      const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (before): ${AitemListIndex}`);
-      const args = ['ReFungible', 'ReFungible'];
-      const alicePrivateKey = privateKey('//Alice');
-      const createMultipleItems = await api.tx.nft
-      .createMultipleItems(idCollection, alicesPublicKey, args)
-      .signAndSend(alicePrivateKey);
-      // tslint:disable-next-line: no-unused-expression
-      assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`');
-      console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`);
-      await waitNewBlocks(api);
-      const BitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (after): ${BitemListIndex}`);
-      if (BitemListIndex > AitemListIndex) { assert.fail('Incorrect token added in collection!'); }
+});
+
+describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
+  it('Create token with not existing type', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      try {
+        const args = [{ invalid: null }, { invalid: null }, { invalid: null }];
+        const createMultipleItemsTx = await api.tx.nft
+          .createMultipleItems(collectionId, Alice.address, args);
+        await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+    });
+  });
+
+  it('Create token in not existing collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      const Alice = privateKey('//Alice');
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, ['NFT', 'NFT', 'NFT']);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+    });
+  });
+
+  it('Create NFT and Re-fungible tokens that has reached the maximum data limit', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // NFT
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const args = [
+        { nft: ['A'.repeat(2049), 'A'.repeat(2049)] },
+        { nft: ['B'.repeat(2049), 'B'.repeat(2049)] },
+        { nft: ['C'.repeat(2049), 'C'.repeat(2049)] },
+      ];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+
+      // ReFungible
+      const collectionIdReFungible =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+      const argsReFungible = [
+        { ReFungible: ['1'.repeat(2049), '1'.repeat(2049)] },
+        { ReFungible: ['2'.repeat(2049), '2'.repeat(2049)] },
+        { ReFungible: ['3'.repeat(2049), '3'.repeat(2049)] },
+      ];
+      const createMultipleItemsTxFungible = await api.tx.nft
+        .createMultipleItems(collectionIdReFungible, Alice.address, argsReFungible);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTxFungible)).to.be.rejected;
+    });
+  });
+
+  it('Create tokens with different types', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, ['NFT', 'Fungible', 'ReFungible']);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+      // garbage collection :-D
+      await destroyCollectionExpectSuccess(collectionId);
+    });
+  });
+
+  it('Create tokens with different data limits <> maximum data limit', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const args = [
+        { nft: ['A', 'A'] },
+        { nft: ['B', 'B'.repeat(2049)] },
+        { nft: ['C'.repeat(2049), 'C'] },
+      ];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
     });
   });
 });
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -1,12 +1,14 @@
+//
+// 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 { createCollectionExpectSuccess, createCollectionExpectFailure, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers";
-import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
-import privateKey from './substrate/privateKey';
+import { default as usingApi } from "./substrate/substrate-api";
+import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers";
 
 chai.use(chaiAsPromised);
-const expect = chai.expect;
 
 describe('integration test: ext. destroyCollection():', () => {
   it('NFT collection can be destroyed', async () => {
@@ -18,7 +20,7 @@
     await destroyCollectionExpectSuccess(collectionId);
   });
   it('ReFungible collection can be destroyed', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     await destroyCollectionExpectSuccess(collectionId);
   });
 });
addedtests/src/enableContractSponsoring.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/enableContractSponsoring.test.ts
@@ -0,0 +1,89 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import { deployFlipper, getFlipValue, toggleFlipValueExpectSuccess } from './util/contracthelpers';
+import {
+  enableContractSponsoringExpectFailure,
+  enableContractSponsoringExpectSuccess,
+  findUnusedAddress,
+  setContractSponsoringRateLimitExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test enableContractSponsoring', () => {
+  it('ensure tx fee is paid from endowment', async () => {
+    await usingApi(async (api) => {
+      const user = await findUnusedAddress(api);
+
+      const [flipper, deployer] = await deployFlipper(api);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+      await setContractSponsoringRateLimitExpectSuccess(deployer, flipper.address, 1);
+      await toggleFlipValueExpectSuccess(user, flipper);
+
+      expect(await getFlipValue(flipper, deployer)).to.be.false;
+    });
+  });
+
+  it('ensure it can be enabled twice', async () => {
+    await usingApi(async (api) => {
+      const [flipper, deployer] = await deployFlipper(api);
+
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+    });
+  });
+
+  it('ensure it can be disabled twice', async () => {
+    await usingApi(async (api) => {
+      const [flipper, deployer] = await deployFlipper(api);
+
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, false);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, false);
+    });
+  });
+
+  it('ensure it can be re-enabled', async () => {
+    await usingApi(async (api) => {
+      const [flipper, deployer] = await deployFlipper(api);
+
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, false);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+    });
+  });
+
+});
+
+describe('Negative Integration Test enableContractSponsoring', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    alice = privateKey('//Alice');
+  });
+
+  it('fails when called for non-contract address', async () => {
+    await usingApi(async (api) => {
+      const user = await findUnusedAddress(api);
+
+      await enableContractSponsoringExpectFailure(alice, user.address, true);
+    });
+  });
+
+  it('fails when called by non-owning user', async () => {
+    await usingApi(async (api) => {
+      const [flipper, _] = await deployFlipper(api);
+
+      await enableContractSponsoringExpectFailure(alice, flipper.address, true);
+    });
+  });
+});
modifiedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/removeCollectionAdmin.test.ts
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -1,5 +1,9 @@
+//
+// 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 BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import privateKey from './substrate/privateKey';
addedtests/src/removeFromWhiteList.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/removeFromWhiteList.test.ts
@@ -0,0 +1,89 @@
+//
+// 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';
+import {
+  createCollectionExpectSuccess,
+  destroyCollectionExpectSuccess,
+  enableWhiteListExpectSuccess,
+  addToWhiteListExpectSuccess,
+  removeFromWhiteListExpectSuccess,
+  isWhitelisted,
+  findNotExistingCollection,
+  removeFromWhiteListExpectFailure,
+  disableWhiteListExpectSuccess,
+} from './util/helpers';
+import { IKeyringPair } from '@polkadot/types/types';
+import privateKey from './substrate/privateKey';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test removeFromWhiteList', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async (api) => {
+      alice = privateKey('//Alice');
+      bob = privateKey('//Bob');
+    });
+  });
+
+  it('ensure bob is not in whitelist after removal', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      await enableWhiteListExpectSuccess(alice, collectionId);
+      await addToWhiteListExpectSuccess(alice, collectionId, bob.address);
+
+      await removeFromWhiteListExpectSuccess(alice, collectionId, bob.address);
+      expect(await isWhitelisted(collectionId, bob.address)).to.be.false;
+    });
+  });
+
+  it('allows removal from collection with unset whitelist status', async () => {
+    await usingApi(async () => {
+      const collectionWithoutWhitelistId = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId);
+      await addToWhiteListExpectSuccess(alice, collectionWithoutWhitelistId, bob.address);
+      await disableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId);
+
+      await removeFromWhiteListExpectSuccess(alice, collectionWithoutWhitelistId, bob.address);
+    });
+  });
+});
+
+describe('Negative Integration Test removeFromWhiteList', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async (api) => {
+      alice = privateKey('//Alice');
+      bob = privateKey('//Bob');
+    });
+  });
+
+  it('fails on removal from not existing collection', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await findNotExistingCollection(api);
+
+      await removeFromWhiteListExpectFailure(alice, collectionId, bob.address);
+    });
+  });
+
+  it('fails on removal from removed collection', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(alice, collectionId);
+      await addToWhiteListExpectSuccess(alice, collectionId, bob.address);
+      await destroyCollectionExpectSuccess(collectionId);
+
+      await removeFromWhiteListExpectFailure(alice, collectionId, bob.address);
+    });
+  });
+});
modifiedtests/src/setCollectionLimits.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionLimits.test.ts
+++ b/tests/src/setCollectionLimits.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.
+//
+
 // https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
 import { ApiPromise, Keyring } from '@polkadot/api';
 import { IKeyringPair } from '@polkadot/types/types';
modifiedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionSponsor.test.ts
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -9,7 +9,6 @@
 import { createCollectionExpectSuccess, setCollectionSponsorExpectSuccess, destroyCollectionExpectSuccess, setCollectionSponsorExpectFailure } from "./util/helpers";
 import { Keyring } from "@polkadot/api";
 import { IKeyringPair } from "@polkadot/types/types";
-import type { AccountId } from '@polkadot/types/interfaces';
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
@@ -34,7 +33,7 @@
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
   it('Set ReFungible collection sponsor', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible', decimalPoints: 0} });
+    const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible'} });
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
 
addedtests/src/setConstOnChainSchema.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setConstOnChainSchema.test.ts
@@ -0,0 +1,96 @@
+//
+// 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';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {
+  createCollectionExpectSuccess,
+  destroyCollectionExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+let Shema: any;
+let largeShema: any;
+
+before(async () => {
+  await usingApi(async (api) => {
+    const keyring = new Keyring({ type: 'sr25519' });
+    Alice = keyring.addFromUri('//Alice');
+    Bob = keyring.addFromUri('//Bob');
+    Shema = '0x31';
+    largeShema = new Array(4097).fill(0xff);
+
+  });
+});
+describe('Integration Test ext. setConstOnChainSchema()', () => {
+
+  it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
+      await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+      const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema);
+      await submitTransactionAsync(Alice, setShema);
+    });
+  });
+
+  it('Checking collection data using the ConstOnChainSchema parameter', async () => {
+      await usingApi(async (api) => {
+        const collectionId = await createCollectionExpectSuccess();
+        const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema);
+        await submitTransactionAsync(Alice, setShema);
+        const collection: any = (await api.query.nft.collection(collectionId));
+        expect(collection.ConstOnChainSchema.toString()).to.be.eq(Shema);
+
+    });
+  });
+});
+
+describe('Negative Integration Test ext. setConstOnChainSchema()', () => {
+
+  it('Set a non-existent collection', async () => {
+    await usingApi(async (api) => {
+      // tslint:disable-next-line: radix
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema);
+      await expect(submitTransactionExpectFailAsync(Alice, setShema)).to.be.rejected;
+    });
+  });
+
+  it('Set a previously deleted collection', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      await destroyCollectionExpectSuccess(collectionId);
+      const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema);
+      await expect(submitTransactionExpectFailAsync(Alice, setShema)).to.be.rejected;
+    });
+  });
+
+  it('Set invalid data in schema (size too large:> 1024b)', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const setShema = api.tx.nft.setConstOnChainSchema(collectionId, largeShema);
+      await expect(submitTransactionExpectFailAsync(Alice, setShema)).to.be.rejected;
+    });
+  });
+
+  it('Execute method not on behalf of the collection owner', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+      const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema);
+      await expect(submitTransactionExpectFailAsync(Bob, setShema)).to.be.rejected;
+    });
+  });
+
+});
addedtests/src/setContractSponsoringRateLimit.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setContractSponsoringRateLimit.test.ts
@@ -0,0 +1,67 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { IKeyringPair } from '@polkadot/types/types';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import waitNewBlocks from './substrate/wait-new-blocks';
+import { deployFlipper, toggleFlipValueExpectFailure, toggleFlipValueExpectSuccess } from './util/contracthelpers';
+import {
+  enableContractSponsoringExpectSuccess,
+  findUnusedAddress,
+  setContractSponsoringRateLimitExpectFailure,
+  setContractSponsoringRateLimitExpectSuccess,
+} from './util/helpers';
+
+describe('Integration Test setContractSponsoringRateLimit', () => {
+  it('ensure sponsored contract can\'t be called twice without pause for free', async () => {
+    await usingApi(async (api) => {
+      const user = await findUnusedAddress(api);
+
+      const [flipper, deployer] = await deployFlipper(api);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+      await setContractSponsoringRateLimitExpectSuccess(deployer, flipper.address, 10);
+      await toggleFlipValueExpectSuccess(user, flipper);
+      await toggleFlipValueExpectFailure(user, flipper);
+    });
+  });
+
+  it('ensure sponsored contract can be called twice with pause for free', async () => {
+    await usingApi(async (api) => {
+      const user = await findUnusedAddress(api);
+
+      const [flipper, deployer] = await deployFlipper(api);
+      await enableContractSponsoringExpectSuccess(deployer, flipper.address, true);
+      await setContractSponsoringRateLimitExpectSuccess(deployer, flipper.address, 1);
+      await toggleFlipValueExpectSuccess(user, flipper);
+      await waitNewBlocks(api, 1);
+      await toggleFlipValueExpectSuccess(user, flipper);
+    });
+  });
+});
+
+describe('Negative Integration Test setContractSponsoringRateLimit', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    alice = privateKey('//Alice');
+  });
+
+  it('fails when called for non-contract address', async () => {
+    await usingApi(async (api) => {
+      const user = await findUnusedAddress(api);
+
+      await setContractSponsoringRateLimitExpectFailure(alice, user.address, 1);
+    });
+  });
+
+  it('fails when called by non-owning user', async () => {
+    await usingApi(async (api) => {
+      const [flipper, _] = await deployFlipper(api);
+
+      await setContractSponsoringRateLimitExpectFailure(alice, flipper.address, 1);
+    });
+  });
+});
addedtests/src/setMintPermission.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setMintPermission.test.ts
@@ -0,0 +1,103 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { IKeyringPair } from '@polkadot/types/types';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import {
+  addToWhiteListExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectFailure,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  enableWhiteListExpectSuccess,
+  findNotExistingCollection,
+  setMintPermissionExpectFailure,
+  setMintPermissionExpectSuccess,
+} from './util/helpers';
+
+describe('Integration Test setMintPermission', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async () => {
+      alice = privateKey('//Alice');
+      bob = privateKey('//Bob');
+    });
+  });
+
+  it('ensure white-listed non-privileged address can mint tokens', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      await enableWhiteListExpectSuccess(alice, collectionId);
+      await setMintPermissionExpectSuccess(alice, collectionId, true);
+      await addToWhiteListExpectSuccess(alice, collectionId, bob.address);
+
+      await createItemExpectSuccess(bob, collectionId, 'NFT');
+    });
+  });
+
+  it('can be enabled twice', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      await setMintPermissionExpectSuccess(alice, collectionId, true);
+      await setMintPermissionExpectSuccess(alice, collectionId, true);
+    });
+  });
+
+  it('can be disabled twice', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      await setMintPermissionExpectSuccess(alice, collectionId, true);
+      await setMintPermissionExpectSuccess(alice, collectionId, false);
+      await setMintPermissionExpectSuccess(alice, collectionId, false);
+    });
+  });
+});
+
+describe('Negative Integration Test setMintPermission', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async () => {
+      alice = privateKey('//Alice');
+      bob = privateKey('//Bob');
+    });
+  });
+
+  it('fails on not existing collection', async () => {
+    await usingApi(async (api) => {
+      const nonExistingCollection = await findNotExistingCollection(api);
+      await setMintPermissionExpectFailure(alice, nonExistingCollection, true);
+    });
+  });
+
+  it('fails on removed collection', async () => {
+    await usingApi(async () => {
+      const removedCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      await destroyCollectionExpectSuccess(removedCollectionId);
+
+      await setMintPermissionExpectFailure(alice, removedCollectionId, true);
+    });
+  });
+
+  it('fails when not collection owner tries to set mint status', async () => {
+    const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+    await enableWhiteListExpectSuccess(alice, collectionId);
+    await setMintPermissionExpectFailure(bob, collectionId, true);
+  });
+
+  it('ensure non-white-listed non-privileged address can\'t mint tokens', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      await enableWhiteListExpectSuccess(alice, collectionId);
+      await setMintPermissionExpectSuccess(alice, collectionId, true);
+
+      await createItemExpectFailure(bob, collectionId, 'NFT');
+    });
+  });
+});
modifiedtests/src/setPublicAccessMode.test.tsdiffbeforeafterboth
--- a/tests/src/setPublicAccessMode.test.ts
+++ b/tests/src/setPublicAccessMode.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.
+//
+
 // https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion
 import { ApiPromise, Keyring } from '@polkadot/api';
 import { IKeyringPair } from '@polkadot/types/types';
modifiedtests/src/setSchemaVersion.test.tsdiffbeforeafterboth
--- a/tests/src/setSchemaVersion.test.ts
+++ b/tests/src/setSchemaVersion.test.ts
@@ -1,7 +1,11 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 // https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion
 import { ApiPromise, Keyring } from '@polkadot/api';
 import { IKeyringPair } from '@polkadot/types/types';
-import BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import usingApi, {submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
@@ -98,12 +102,16 @@
 
   it('execute setSchemaVersion with not correct schema version', async () => {
     await usingApi(async (api: ApiPromise) => {
+      const consoleError = console.error;
+      console.error = (message: string) => {};
       try {
         tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'Test');
         await submitTransactionAsync(alice, tx);
       } catch (e) {
         // tslint:disable-next-line:no-unused-expression
         expect(e).to.be.exist;
+      } finally {
+        console.error = consoleError;
       }
     });
   });
addedtests/src/setVariableMetaData.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setVariableMetaData.test.ts
@@ -0,0 +1,109 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import {
+  burnItemExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  findNotExistingCollection,
+  setVariableMetaDataExpectFailure,
+  setVariableMetaDataExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test setVariableMetaData', () => {
+  const data = [1, 2, 254, 255];
+
+  let alice: IKeyringPair;
+  let collectionId: number;
+  let tokenId: number;
+  before(async () => {
+    await usingApi(async () => {
+      alice = privateKey('//Alice');
+      collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+    });
+  });
+
+  it('execute setVariableMetaData', async () => {
+    await setVariableMetaDataExpectSuccess(alice, collectionId, tokenId, data);
+  });
+
+  it('verify data was set', async () => {
+    await usingApi(async api => {
+      const item: any = await api.query.nft.nftItemList(collectionId, tokenId);
+
+      expect(Array.from(item.VariableData)).to.deep.equal(Array.from(data));
+    });
+  });
+});
+
+describe('Negative Integration Test setVariableMetaData', () => {
+  let data = [1];
+
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  let validCollectionId: number;
+  let validTokenId: number;
+
+  before(async () => {
+    await usingApi(async () => {
+      alice = privateKey('//Alice');
+      bob = privateKey('//Bob');
+
+      validCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+      validTokenId = await createItemExpectSuccess(alice, validCollectionId, 'NFT');
+    });
+  });
+
+  it('fails on not existing collection id', async () => {
+    await usingApi(async api => {
+      let nonExistingCollectionId = await findNotExistingCollection(api);
+      await setVariableMetaDataExpectFailure(alice, nonExistingCollectionId, 1, data);
+    });
+  });
+  it('fails on removed collection id', async () => {
+    const removedCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+    const removedCollectionTokenId = await createItemExpectSuccess(alice, removedCollectionId, 'NFT');
+
+    await destroyCollectionExpectSuccess(removedCollectionId);
+    await setVariableMetaDataExpectFailure(alice, removedCollectionId, removedCollectionTokenId, data);
+  });
+  it('fails on removed token', async () => {
+    const removedTokenCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+    const removedTokenId = await createItemExpectSuccess(alice, removedTokenCollectionId, 'NFT');
+    await burnItemExpectSuccess(alice, removedTokenCollectionId, removedTokenId);
+
+    await setVariableMetaDataExpectFailure(alice, removedTokenCollectionId, removedTokenId, data);
+  });
+  it('fails on not existing token', async () => {
+    const nonExistingTokenId = validTokenId + 1;
+
+    await setVariableMetaDataExpectFailure(alice, validCollectionId, nonExistingTokenId, data);
+  });
+  it('fails on too long data', async () => {
+    const tooLongData = new Array(4097).fill(0xff);
+
+    await setVariableMetaDataExpectFailure(alice, validCollectionId, validTokenId, tooLongData);
+  });
+  it('fails on fungible token', async () => {
+    const fungibleCollectionId = await createCollectionExpectSuccess({ mode: { type: 'Fungible', decimalPoints: 0 } });
+    const fungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
+
+    await setVariableMetaDataExpectFailure(alice, fungibleCollectionId, fungibleTokenId, data);
+  });
+  it('fails on bad sender', async () => {
+    await setVariableMetaDataExpectFailure(bob, validCollectionId, validTokenId, data);
+  });
+});
addedtests/src/setVariableOnChainSchema.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setVariableOnChainSchema.test.ts
@@ -0,0 +1,96 @@
+//
+// 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';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {
+  createCollectionExpectSuccess,
+  destroyCollectionExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+let Schema: any;
+let largeSchema: any;
+
+before(async () => {
+  await usingApi(async (api) => {
+    const keyring = new Keyring({ type: 'sr25519' });
+    Alice = keyring.addFromUri('//Alice');
+    Bob = keyring.addFromUri('//Bob');
+    Schema = '0x31';
+    largeSchema = new Array(4097).fill(0xff);
+
+  });
+});
+describe('Integration Test ext. setVariableOnChainSchema()', () => {
+
+  it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
+      await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+      const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema);
+      await submitTransactionAsync(Alice, setSchema);
+    });
+  });
+
+  it('Checking collection data using the setVariableOnChainSchema parameter', async () => {
+      await usingApi(async (api) => {
+        const collectionId = await createCollectionExpectSuccess();
+        const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema);
+        await submitTransactionAsync(Alice, setSchema);
+        const collection: any = (await api.query.nft.collection(collectionId));
+        expect(collection.VariableOnChainSchema.toString()).to.be.eq(Schema);
+
+    });
+  });
+});
+
+describe('Negative Integration Test ext. setVariableOnChainSchema()', () => {
+
+  it('Set a non-existent collection', async () => {
+    await usingApi(async (api) => {
+      // tslint:disable-next-line: radix
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema);
+      await expect(submitTransactionExpectFailAsync(Alice, setSchema)).to.be.rejected;
+    });
+  });
+
+  it('Set a previously deleted collection', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      await destroyCollectionExpectSuccess(collectionId);
+      const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema);
+      await expect(submitTransactionExpectFailAsync(Alice, setSchema)).to.be.rejected;
+    });
+  });
+
+  it('Set invalid data in schema (size too large:> 1024b)', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, largeSchema);
+      await expect(submitTransactionExpectFailAsync(Alice, setSchema)).to.be.rejected;
+    });
+  });
+
+  it('Execute method not on behalf of the collection owner', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+      const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema);
+      await expect(submitTransactionExpectFailAsync(Bob, setSchema)).to.be.rejected;
+    });
+  });
+
+});
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -24,12 +24,17 @@
 
   // TODO: Remove, this is temporary: Filter unneeded API output 
   // (Jaco promised it will be removed in the next version)
-  // const consoleLog = console.log;
-  // console.log = (message: string) => {
-  //   if (!(message.includes("API/INIT: Capabilities detected") || message.includes("2021-"))) {
-  //     consoleLog(message);
-  //   }
-  // };
+  const consoleLog = console.log;
+  console.log = (message: string) => {
+    if (message.includes("API/INIT: Capabilities detected") || message.includes("2021-")) {}
+    else if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {}
+    else consoleLog(message);
+  };
+  const consoleErr = console.error;
+  console.error = (message: string) => {
+    if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {}
+    else consoleErr(message);
+  };
 
   try {
     await promisifySubstrate(api, async () => {
@@ -40,7 +45,8 @@
     })();
   } finally {
     await api.disconnect();
-    // console.log = consoleLog;
+    console.log = consoleLog;
+    console.error = consoleErr;
   }
 }
 
@@ -115,11 +121,11 @@
       await transaction.signAndSend(sender, ({ events = [], status }) => {
         const transactionStatus = getTransactionStatus(events, status);
 
-        console.log('transactionStatus', transactionStatus, 'events', events);
+        // console.log('transactionStatus', transactionStatus, 'events', events);
 
-        if (transactionStatus == TransactionStatus.Success) {
+        if (transactionStatus === TransactionStatus.Success) {
           resolve(events);
-        } else if (transactionStatus == TransactionStatus.Fail) {
+        } else if (transactionStatus === TransactionStatus.Fail) {
           reject(events);
         }
       });
modifiedtests/src/toggleContractWhiteList.test.tsdiffbeforeafterboth
--- a/tests/src/toggleContractWhiteList.test.ts
+++ b/tests/src/toggleContractWhiteList.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 usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -63,7 +63,7 @@
     });
   });
 
-  it('Create collection, balance transfers and check balance', async () => {
+  it('User can transfer owned token', async () => {
     await usingApi(async (api) => {
       const Alice = privateKey('//Alice');
       const Bob = privateKey('//Bob');
@@ -77,10 +77,10 @@
       await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
       // reFungible
       const reFungibleCollectionId = await
-        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       await transferExpectSuccess(reFungibleCollectionId,
-        newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
+        newReFungibleTokenId, Alice, Bob, 100, 'ReFungible');
     });
   });
 });
@@ -119,7 +119,7 @@
     await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
     // reFungible
     const reFungibleCollectionId = await
-      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
     await destroyCollectionExpectSuccess(reFungibleCollectionId);
     await transferExpectFail(reFungibleCollectionId,
@@ -134,7 +134,7 @@
     await transferExpectFail(fungibleCollectionId, 2, Alice, Bob, 1, 'Fungible');
     // reFungible
     const reFungibleCollectionId = await
-      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     await transferExpectFail(reFungibleCollectionId,
       2, Alice, Bob, 1, 'ReFungible');
   });
@@ -151,7 +151,7 @@
     await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
     // reFungible
     const reFungibleCollectionId = await
-      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
     await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1);
     await transferExpectFail(reFungibleCollectionId,
@@ -168,7 +168,7 @@
     await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Charlie, Bob, 1, 'Fungible');
     // reFungible
     const reFungibleCollectionId = await
-      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
     await transferExpectFail(reFungibleCollectionId,
       newReFungibleTokenId, Charlie, Bob, 1, 'ReFungible');
modifiedtests/src/transferFrom.test.tsdiffbeforeafterboth
--- a/tests/src/transferFrom.test.ts
+++ b/tests/src/transferFrom.test.ts
@@ -41,11 +41,11 @@
       await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1, 'Fungible');
       // reFungible
       const reFungibleCollectionId = await
-        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
-      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 100);
       await transferFromExpectSuccess(reFungibleCollectionId,
-        newReFungibleTokenId, Bob, Alice, Charlie, 1, 'ReFungible');
+        newReFungibleTokenId, Bob, Alice, Charlie, 100, 'ReFungible');
     });
   });
 });
@@ -110,7 +110,7 @@
       await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1);
       // reFungible
       const reFungibleCollectionId = await
-        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       await transferFromExpectFail(reFungibleCollectionId,
         newReFungibleTokenId, Bob, Alice, Charlie, 1);
@@ -136,7 +136,7 @@
       await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 2);
       // reFungible
       const reFungibleCollectionId = await
-        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
       await transferFromExpectFail(reFungibleCollectionId,
@@ -149,7 +149,7 @@
       const Alice = privateKey('//Alice');
       const Bob = privateKey('//Bob');
       const Charlie = privateKey('//Charlie');
-      const Dave = privateKey('//DAVE');
+      const Dave = privateKey('//Dave');
       // nft
       const nftCollectionId = await createCollectionExpectSuccess();
       const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
@@ -175,7 +175,7 @@
       }
       // reFungible
       const reFungibleCollectionId = await
-        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+        createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
       const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
       try {
         await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Bob);
modifiedtests/src/types.tsdiffbeforeafterboth
--- a/tests/src/types.ts
+++ b/tests/src/types.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 BN from 'bn.js';
 
 export interface ICollectionInterface {
modifiedtests/src/util/contracthelpers.tsdiffbeforeafterboth
--- a/tests/src/util/contracthelpers.ts
+++ b/tests/src/util/contracthelpers.ts
@@ -5,7 +5,7 @@
 
 import chai from "chai";
 import chaiAsPromised from 'chai-as-promised';
-import { submitTransactionAsync } from "../substrate/substrate-api";
+import { submitTransactionAsync, submitTransactionExpectFailAsync } from "../substrate/substrate-api";
 import fs from "fs";
 import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
 import { IKeyringPair } from "@polkadot/types/types";
@@ -14,7 +14,7 @@
 chai.use(chaiAsPromised);
 const expect = chai.expect;
 import { BigNumber } from 'bignumber.js';
-import { findUnusedAddress } from '../util/helpers';
+import { findUnusedAddress, getGenericResult } from '../util/helpers';
 
 const value = 0;
 const gasLimit = '200000000000';
@@ -92,6 +92,19 @@
   return (result.result.asOk.data[0] == 0x00) ? false : true;
 }
 
+export async function toggleFlipValueExpectSuccess(sender: IKeyringPair, contract: Contract) {
+  const tx = contract.tx.flip(value, gasLimit);
+  const events = await submitTransactionAsync(sender, tx);
+  const result = getGenericResult(events);
+
+  expect(result.success).to.be.true;
+}
+
+export async function toggleFlipValueExpectFailure(sender: IKeyringPair, contract: Contract) {
+  const tx = contract.tx.flip(value, gasLimit);
+  await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+}
+
 function instantiateTransferContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
   return new Promise<any>(async (resolve, reject) => {
     const unsub = await blueprint.tx
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -51,7 +51,7 @@
   Value: BN;
 }
 
-interface IReFungibleTokenDataType {
+export interface IReFungibleTokenDataType {
   Owner: IReFungibleOwner[];
   ConstData: number[];
   VariableData: number[];
@@ -124,21 +124,6 @@
 
 interface ReFungible {
   type: 'ReFungible';
-  decimalPoints: number;
-}
-
-interface Nft {
-  type: 'NFT'
-}
-
-interface Fungible {
-  type: 'Fungible',
-  decimalPoints: number
-}
-
-interface ReFungible {
-  type: 'ReFungible',
-  decimalPoints: number
 }
 
 type CollectionMode = Nft | Fungible | ReFungible | Invalid;
@@ -174,7 +159,7 @@
     } else if (mode.type === 'Fungible') {
       modeprm = {fungible: mode.decimalPoints};
     } else if (mode.type === 'ReFungible') {
-      modeprm = {refungible: mode.decimalPoints};
+      modeprm = {refungible: null};
     } else if (mode.type === 'Invalid') {
       modeprm = {invalid: null};
     }
@@ -216,7 +201,7 @@
   } else if (mode.type === 'Fungible') {
     modeprm = {fungible: mode.decimalPoints};
   } else if (mode.type === 'ReFungible') {
-    modeprm = {refungible: mode.decimalPoints};
+    modeprm = {refungible: null};
   } else if (mode.type === 'Invalid') {
     modeprm = {invalid: null};
   }
@@ -253,6 +238,12 @@
   return unused;
 }
 
+export async function findNotExistingCollection(api: ApiPromise): Promise<number> {
+  const totalNumber = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10) as unknown as number;
+  const newCollection: number = totalNumber + 1;
+  return newCollection;
+}
+
 function getDestroyResult(events: EventRecord[]): boolean {
   let success: boolean = false;
   events.forEach(({ phase, event: { data, method, section } }) => {
@@ -379,6 +370,63 @@
   });
 }
 
+export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.true;
+  });
+}
+
+export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.true;
+  });
+}
+
+export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function setVariableMetaDataExpectSuccess(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.true;
+  });
+}
+
+export async function setVariableMetaDataExpectFailure(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));
+    await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+  });
+}
+
 export interface CreateFungibleData extends Struct {
   readonly value: u128;
 }
@@ -440,7 +488,7 @@
     }
     const transferFromTx = await api.tx.nft.transferFrom(
       accountFrom.address, accountTo.address, collectionId, tokenId, value);
-    const events = await submitTransactionAsync(accountFrom, transferFromTx);
+    const events = await submitTransactionAsync(accountApproved, transferFromTx);
     const result = getCreateItemResult(events);
     // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
@@ -558,6 +606,9 @@
     if (createMode === 'Fungible') {
       const createData = {fungible: {value: 10}};
       tx = api.tx.nft.createItem(collectionId, owner, createData);
+    } else if (createMode === 'ReFungible') {
+      const createData = {refungible: {const_data: [], variable_data: [], pieces: 100}};
+      tx = api.tx.nft.createItem(collectionId, owner, createData);
     } else {
       tx = api.tx.nft.createItem(collectionId, owner, createMode);
     }
@@ -583,11 +634,25 @@
   return newItemId;
 }
 
-export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
+export async function createItemExpectFailure(
+  sender: IKeyringPair, collectionId: number, createMode: string, owner: string = sender.address) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.createItem(collectionId, owner, createMode);
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getCreateItemResult(events);
+
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function setPublicAccessModeExpectSuccess(
+  sender: IKeyringPair, collectionId: number,
+  accessMode: 'Normal' | 'WhiteList',
+) {
   await usingApi(async (api) => {
 
     // Run the transaction
-    const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+    const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
@@ -597,15 +662,23 @@
     // What to expect
     // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
-    expect(collection.Access).to.be.equal('WhiteList');
+    expect(collection.Access).to.be.equal(accessMode);
   });
 }
 
-export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {
+export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
+  await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList');
+}
+
+export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
+  await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');
+}
+
+export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {
   await usingApi(async (api) => {
 
     // Run the transaction
-    const tx = api.tx.nft.setMintPermission(collectionId, true);
+    const tx = api.tx.nft.setMintPermission(collectionId, enabled);
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
@@ -613,9 +686,33 @@
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
-    expect(collection.MintMode).to.be.equal(true);
+    expect(collection.MintMode).to.be.equal(enabled);
+  });
+}
+
+export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {
+  await setMintPermissionExpectSuccess(sender, collectionId, true);
+}
+
+export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {
+  await usingApi(async (api) => {
+    // Run the transaction
+    const tx = api.tx.nft.setMintPermission(collectionId, enabled);
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getCreateCollectionResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function isWhitelisted(collectionId: number, address: string) {
+  let whitelisted: boolean = false;
+  await usingApi(async (api) => {
+    whitelisted = (await api.query.nft.whiteList(collectionId, address)).toJSON() as unknown as boolean;
   });
+  return whitelisted;
 }
 
 export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
@@ -640,6 +737,32 @@
   });
 }
 
+export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
+  await usingApi(async (api) => {
+    // Run the transaction
+    const tx = api.tx.nft.removeFromWhiteList(collectionId, address);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    // What to expect
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+  });
+}
+
+export async function removeFromWhiteListExpectFailure(sender: IKeyringPair, collectionId: number, address: string) {
+  await usingApi(async (api) => {
+    // Run the transaction
+    const tx = api.tx.nft.removeFromWhiteList(collectionId, address);
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getGenericResult(events);
+
+    // What to expect
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
 export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)
   : Promise<ICollectionInterface | null> => {
   return await api.query.nft.collection(collectionId) as unknown as ICollectionInterface;