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

difftreelog

Merge remote-tracking branch 'origin/develop' into develop

kpozdnikin2020-12-29parents: #ac63732 #3e2b774.patch.diff
in: master
# Conflicts:
#	tests/src/contracts.test.ts

49 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -12,11 +12,11 @@
 
 [[package]]
 name = "addr2line"
-version = "0.13.0"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
+checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423"
 dependencies = [
- "gimli 0.22.0",
+ "gimli 0.23.0",
 ]
 
 [[package]]
@@ -55,7 +55,7 @@
  "aes",
  "block-cipher",
  "ghash",
- "subtle 2.3.0",
+ "subtle 2.4.0",
 ]
 
 [[package]]
@@ -95,10 +95,16 @@
 checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
 
 [[package]]
+name = "ahash"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
+
+[[package]]
 name = "aho-corasick"
-version = "0.7.14"
+version = "0.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
 dependencies = [
  "memchr",
 ]
@@ -134,9 +140,9 @@
 
 [[package]]
 name = "anyhow"
-version = "1.0.33"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
+checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
 
 [[package]]
 name = "approx"
@@ -149,9 +155,9 @@
 
 [[package]]
 name = "arc-swap"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
+checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8"
 
 [[package]]
 name = "arrayref"
@@ -170,9 +176,9 @@
 
 [[package]]
 name = "arrayvec"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
 [[package]]
 name = "asn1_der"
@@ -206,36 +212,36 @@
 
 [[package]]
 name = "async-executor"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801"
+checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146"
 dependencies = [
  "async-task",
  "concurrent-queue",
  "fastrand",
  "futures-lite",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
  "vec-arena",
 ]
 
 [[package]]
 name = "async-global-executor"
-version = "1.4.0"
+version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0659b83a146398616883aa5199cdd1b055ec088a83c9a338eab3534f33f0622e"
+checksum = "73079b49cd26b8fd5a15f68fc7707fc78698dc2a3d61430f2a7a9430230dfa04"
 dependencies = [
  "async-executor",
  "async-io",
  "futures-lite",
  "num_cpus",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
 ]
 
 [[package]]
 name = "async-io"
-version = "1.1.10"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e"
+checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd"
 dependencies = [
  "concurrent-queue",
  "fastrand",
@@ -243,7 +249,7 @@
  "libc",
  "log",
  "nb-connect",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
  "parking",
  "polling",
  "vec-arena",
@@ -261,16 +267,34 @@
 ]
 
 [[package]]
+name = "async-process"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8cea09c1fb10a317d1b5af8024eeba256d6554763e85ecd90ff8df31c7bbda"
+dependencies = [
+ "async-io",
+ "blocking",
+ "cfg-if 0.1.10",
+ "event-listener",
+ "futures-lite",
+ "once_cell 1.5.2",
+ "signal-hook",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "async-std"
-version = "1.6.5"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9fa76751505e8df1c7a77762f60486f60c71bbd9b8557f4da6ad47d083732ed"
+checksum = "8f9f84f1280a2b436a2c77c2582602732b6c2f4321d5494d6e799e6c367859a8"
 dependencies = [
+ "async-channel",
  "async-global-executor",
  "async-io",
  "async-mutex",
+ "async-process",
  "blocking",
- "crossbeam-utils",
+ "crossbeam-utils 0.8.1",
  "futures-channel",
  "futures-core",
  "futures-io",
@@ -280,8 +304,8 @@
  "log",
  "memchr",
  "num_cpus",
- "once_cell 1.4.1",
- "pin-project-lite",
+ "once_cell 1.5.2",
+ "pin-project-lite 0.2.0",
  "pin-utils",
  "slab",
  "wasm-bindgen-futures",
@@ -289,9 +313,9 @@
 
 [[package]]
 name = "async-task"
-version = "4.0.2"
+version = "4.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ab27c1aa62945039e44edaeee1dc23c74cc0c303dd5fe0fb462a184f1c3a518"
+checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
 
 [[package]]
 name = "async-tls"
@@ -299,7 +323,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "df097e3f506bec0e1a24f06bb3c962c228f36671de841ff579cb99f371772634"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "rustls",
  "webpki",
  "webpki-roots 0.19.0",
@@ -307,9 +331,9 @@
 
 [[package]]
 name = "async-trait"
-version = "0.1.41"
+version = "0.1.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0"
+checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -353,15 +377,15 @@
 
 [[package]]
 name = "backtrace"
-version = "0.3.53"
+version = "0.3.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e"
+checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
 dependencies = [
  "addr2line",
  "cfg-if 1.0.0",
  "libc",
  "miniz_oxide",
- "object 0.21.1",
+ "object 0.22.0",
  "rustc-demangle",
 ]
 
@@ -384,6 +408,12 @@
 checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
 
 [[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
+[[package]]
 name = "bincode"
 version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -456,15 +486,13 @@
 
 [[package]]
 name = "blake2"
-version = "0.9.0"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84ce5b6108f8e154604bd4eb76a2f726066c3464d5a552a4229262a18c9bb471"
+checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4"
 dependencies = [
- "byte-tools",
- "byteorder 1.3.4",
  "crypto-mac 0.8.0",
  "digest 0.9.0",
- "opaque-debug 0.2.3",
+ "opaque-debug 0.3.0",
 ]
 
 [[package]]
@@ -479,23 +507,23 @@
 
 [[package]]
 name = "blake2b_simd"
-version = "0.5.10"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
+checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
 dependencies = [
  "arrayref",
- "arrayvec 0.5.1",
+ "arrayvec 0.5.2",
  "constant_time_eq",
 ]
 
 [[package]]
 name = "blake2s_simd"
-version = "0.5.10"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab9e07352b829279624ceb7c64adb4f585dacdb81d35cafae81139ccd617cf44"
+checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2"
 dependencies = [
  "arrayref",
- "arrayvec 0.5.1",
+ "arrayvec 0.5.2",
  "constant_time_eq",
 ]
 
@@ -556,7 +584,7 @@
  "atomic-waker",
  "fastrand",
  "futures-lite",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
 ]
 
 [[package]]
@@ -566,10 +594,16 @@
 checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb"
 
 [[package]]
+name = "bs58"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
+
+[[package]]
 name = "bstr"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
+checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
 dependencies = [
  "memchr",
 ]
@@ -635,9 +669,9 @@
 
 [[package]]
 name = "cc"
-version = "1.0.61"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
+checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
 dependencies = [
  "jobserver",
 ]
@@ -669,7 +703,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "244fbce0d47e97e8ef2f63b81d5e05882cb518c68531eb33194990d7b7e85845"
 dependencies = [
- "stream-cipher 0.7.1",
+ "stream-cipher",
  "zeroize",
 ]
 
@@ -682,7 +716,7 @@
  "aead",
  "chacha20",
  "poly1305",
- "stream-cipher 0.7.1",
+ "stream-cipher",
  "zeroize",
 ]
 
@@ -730,15 +764,6 @@
 version = "0.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "cloudabi"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467"
 dependencies = [
  "bitflags",
 ]
@@ -754,9 +779,9 @@
 
 [[package]]
 name = "const-random"
-version = "0.1.11"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02dc82c12dc2ee6e1ded861cf7d582b46f66f796d1b6c93fa28b911ead95da02"
+checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
 dependencies = [
  "const-random-macro",
  "proc-macro-hack",
@@ -764,15 +789,23 @@
 
 [[package]]
 name = "const-random-macro"
-version = "0.1.11"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc757bbb9544aa296c2ae00c679e81f886b37e28e59097defe0cf524306f6685"
+checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
 dependencies = [
  "getrandom 0.2.0",
+ "lazy_static",
  "proc-macro-hack",
+ "tiny-keccak",
 ]
 
 [[package]]
+name = "const_fn"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
+
+[[package]]
 name = "constant_time_eq"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -801,6 +834,12 @@
 checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
 
 [[package]]
+name = "cpuid-bool"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
+
+[[package]]
 name = "cranelift-bforest"
 version = "0.66.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -824,7 +863,7 @@
  "log",
  "regalloc",
  "serde",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "target-lexicon",
  "thiserror",
 ]
@@ -862,7 +901,7 @@
 dependencies = [
  "cranelift-codegen",
  "log",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "target-lexicon",
 ]
 
@@ -894,21 +933,21 @@
 
 [[package]]
 name = "crc32fast"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.4.4"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
+checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
 dependencies = [
- "crossbeam-utils",
- "maybe-uninit",
+ "cfg-if 1.0.0",
+ "crossbeam-utils 0.8.1",
 ]
 
 [[package]]
@@ -917,12 +956,23 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
 dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
+ "crossbeam-epoch 0.8.2",
+ "crossbeam-utils 0.7.2",
  "maybe-uninit",
 ]
 
 [[package]]
+name = "crossbeam-deque"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
+dependencies = [
+ "cfg-if 1.0.0",
+ "crossbeam-epoch 0.9.1",
+ "crossbeam-utils 0.8.1",
+]
+
+[[package]]
 name = "crossbeam-epoch"
 version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -930,10 +980,24 @@
 dependencies = [
  "autocfg 1.0.1",
  "cfg-if 0.1.10",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "lazy_static",
  "maybe-uninit",
- "memoffset",
+ "memoffset 0.5.6",
+ "scopeguard 1.1.0",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
+dependencies = [
+ "cfg-if 1.0.0",
+ "const_fn",
+ "crossbeam-utils 0.8.1",
+ "lazy_static",
+ "memoffset 0.6.1",
  "scopeguard 1.1.0",
 ]
 
@@ -944,7 +1008,7 @@
 checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
 dependencies = [
  "cfg-if 0.1.10",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "maybe-uninit",
 ]
 
@@ -960,6 +1024,17 @@
 ]
 
 [[package]]
+name = "crossbeam-utils"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
+dependencies = [
+ "autocfg 1.0.1",
+ "cfg-if 1.0.0",
+ "lazy_static",
+]
+
+[[package]]
 name = "crunchy"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -982,7 +1057,7 @@
 checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
 dependencies = [
  "generic-array 0.14.4",
- "subtle 2.3.0",
+ "subtle 2.4.0",
 ]
 
 [[package]]
@@ -1013,7 +1088,7 @@
  "byteorder 1.3.4",
  "digest 0.8.1",
  "rand_core 0.5.1",
- "subtle 2.3.0",
+ "subtle 2.4.0",
  "zeroize",
 ]
 
@@ -1026,15 +1101,15 @@
  "byteorder 1.3.4",
  "digest 0.9.0",
  "rand_core 0.5.1",
- "subtle 2.3.0",
+ "subtle 2.4.0",
  "zeroize",
 ]
 
 [[package]]
 name = "data-encoding"
-version = "2.3.0"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4d0e2d24e5ee3b23a01de38eefdcd978907890701f08ffffd4cb457ca4ee8d6"
+checksum = "993a608597367c6377b258c25d7120740f00ed23a2252b729b1932dd7866f908"
 
 [[package]]
 name = "derive_more"
@@ -1119,9 +1194,9 @@
 
 [[package]]
 name = "dyn-clone"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c53dc3a653e0f64081026e4bf048d48fec9fce90c66e8326ca7292df0ff2d82"
+checksum = "d55796afa1b20c2945ca8eabfc421839f2b766619209f1ede813cf2484f31804"
 
 [[package]]
 name = "ed25519"
@@ -1142,7 +1217,7 @@
  "ed25519",
  "rand 0.7.3",
  "serde",
- "sha2 0.9.1",
+ "sha2 0.9.2",
  "zeroize",
 ]
 
@@ -1153,6 +1228,26 @@
 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
 [[package]]
+name = "enumflags2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0"
+dependencies = [
+ "enumflags2_derive",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "env_logger"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1182,9 +1277,9 @@
 
 [[package]]
 name = "errno"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01"
+checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe"
 dependencies = [
  "errno-dragonfly",
  "libc",
@@ -1213,7 +1308,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
 ]
 
 [[package]]
@@ -1285,7 +1380,7 @@
 checksum = "8feb87a63249689640ac9c011742c33139204e3c134293d3054022276869133b"
 dependencies = [
  "either",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 2.0.2",
  "log",
  "num-traits",
@@ -1313,11 +1408,11 @@
 
 [[package]]
 name = "flate2"
-version = "1.0.18"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee"
+checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "crc32fast",
  "libc",
  "libz-sys",
@@ -1325,6 +1420,22 @@
 ]
 
 [[package]]
+name = "flexi_logger"
+version = "0.15.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aaab3caedb4149800f91e8e4899f29cd9ddf3b569b04c365ca9334f92f7542bf"
+dependencies = [
+ "atty",
+ "chrono",
+ "glob",
+ "lazy_static",
+ "log",
+ "regex",
+ "thiserror",
+ "yansi",
+]
+
+[[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1333,15 +1444,25 @@
 [[package]]
 name = "fork-tree"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
 ]
 
 [[package]]
+name = "form_urlencoded"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
+dependencies = [
+ "matches",
+ "percent-encoding 2.1.0",
+]
+
+[[package]]
 name = "frame-benchmarking"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -1359,7 +1480,7 @@
 [[package]]
 name = "frame-benchmarking-cli"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-benchmarking",
  "parity-scale-codec",
@@ -1377,7 +1498,7 @@
 [[package]]
 name = "frame-executive"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -1393,7 +1514,7 @@
 [[package]]
 name = "frame-metadata"
 version = "12.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "serde",
@@ -1404,18 +1525,18 @@
 [[package]]
 name = "frame-support"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "bitmask",
  "frame-metadata",
  "frame-support-procedural",
  "impl-trait-for-tuples",
  "log",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
  "parity-scale-codec",
  "paste",
  "serde",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "sp-arithmetic",
  "sp-core",
  "sp-inherents",
@@ -1429,7 +1550,7 @@
 [[package]]
 name = "frame-support-procedural"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support-procedural-tools",
  "proc-macro2",
@@ -1440,7 +1561,7 @@
 [[package]]
 name = "frame-support-procedural-tools"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support-procedural-tools-derive",
  "proc-macro-crate",
@@ -1452,7 +1573,7 @@
 [[package]]
 name = "frame-support-procedural-tools-derive"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1462,7 +1583,7 @@
 [[package]]
 name = "frame-system"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "impl-trait-for-tuples",
@@ -1478,7 +1599,7 @@
 [[package]]
 name = "frame-system-benchmarking"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -1492,7 +1613,7 @@
 [[package]]
 name = "frame-system-rpc-runtime-api"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-api",
@@ -1500,9 +1621,9 @@
 
 [[package]]
 name = "fs-swap"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb"
+checksum = "5839fda247e24ca4919c87c71dd5ca658f1f39e4f06829f80e3f15c3bafcfc2c"
 dependencies = [
  "lazy_static",
  "libc",
@@ -1540,9 +1661,9 @@
 
 [[package]]
 name = "futures"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e"
+checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1555,9 +1676,9 @@
 
 [[package]]
 name = "futures-channel"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74"
+checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1574,9 +1695,9 @@
 
 [[package]]
 name = "futures-core"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b"
+checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"
 
 [[package]]
 name = "futures-core-preview"
@@ -1601,20 +1722,20 @@
 checksum = "fdcef58a173af8148b182684c9f2d5250875adbcaff7b5794073894f9d8634a9"
 dependencies = [
  "futures 0.1.30",
- "futures 0.3.6",
+ "futures 0.3.8",
  "lazy_static",
  "log",
  "parking_lot 0.9.0",
- "pin-project",
+ "pin-project 0.4.27",
  "serde",
  "serde_json",
 ]
 
 [[package]]
 name = "futures-executor"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab"
+checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1624,30 +1745,30 @@
 
 [[package]]
 name = "futures-io"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c"
+checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
 
 [[package]]
 name = "futures-lite"
-version = "1.11.1"
+version = "1.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "381a7ad57b1bad34693f63f6f377e1abded7a9c85c9d3eb6771e11c60aaadab9"
+checksum = "5e6c079abfac3ab269e2927ec048dabc89d009ebfdda6b8ee86624f30c689658"
 dependencies = [
  "fastrand",
  "futures-core",
  "futures-io",
  "memchr",
  "parking",
- "pin-project-lite",
+ "pin-project-lite 0.1.11",
  "waker-fn",
 ]
 
 [[package]]
 name = "futures-macro"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b"
+checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -1657,17 +1778,17 @@
 
 [[package]]
 name = "futures-sink"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd"
+checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d"
 
 [[package]]
 name = "futures-task"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94"
+checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d"
 dependencies = [
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
 ]
 
 [[package]]
@@ -1684,9 +1805,9 @@
 
 [[package]]
 name = "futures-util"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34"
+checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2"
 dependencies = [
  "futures 0.1.30",
  "futures-channel",
@@ -1696,7 +1817,7 @@
  "futures-sink",
  "futures-task",
  "memchr",
- "pin-project",
+ "pin-project 1.0.2",
  "pin-utils",
  "proc-macro-hack",
  "proc-macro-nested",
@@ -1722,9 +1843,9 @@
 checksum = "ce54d63f8b0c75023ed920d46fd71d0cbbb830b0ee012726b5b4f506fb6dea5b"
 dependencies = [
  "bytes 0.5.6",
- "futures 0.3.6",
+ "futures 0.3.8",
  "memchr",
- "pin-project",
+ "pin-project 0.4.27",
 ]
 
 [[package]]
@@ -1734,6 +1855,19 @@
 checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
 
 [[package]]
+name = "generator"
+version = "0.6.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc"
+dependencies = [
+ "cc",
+ "libc",
+ "log",
+ "rustc_version",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "generic-array"
 version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1818,9 +1952,9 @@
 
 [[package]]
 name = "gimli"
-version = "0.22.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
+checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
 
 [[package]]
 name = "glob"
@@ -1830,9 +1964,9 @@
 
 [[package]]
 name = "globset"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120"
+checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a"
 dependencies = [
  "aho-corasick",
  "bstr",
@@ -1874,21 +2008,22 @@
 
 [[package]]
 name = "h2"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
+checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
 dependencies = [
  "bytes 0.5.6",
  "fnv",
  "futures-core",
  "futures-sink",
  "futures-util",
- "http 0.2.1",
+ "http 0.2.2",
  "indexmap",
  "slab",
- "tokio 0.2.22",
+ "tokio 0.2.24",
  "tokio-util",
  "tracing",
+ "tracing-futures",
 ]
 
 [[package]]
@@ -1941,6 +2076,9 @@
 version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+dependencies = [
+ "ahash 0.4.7",
+]
 
 [[package]]
 name = "heck"
@@ -2012,9 +2150,9 @@
 
 [[package]]
 name = "http"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
+checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26"
 dependencies = [
  "bytes 0.5.6",
  "fnv",
@@ -2040,7 +2178,7 @@
 checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
 dependencies = [
  "bytes 0.5.6",
- "http 0.2.1",
+ "http 0.2.2",
 ]
 
 [[package]]
@@ -2096,23 +2234,23 @@
 
 [[package]]
 name = "hyper"
-version = "0.13.8"
+version = "0.13.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835"
+checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf"
 dependencies = [
  "bytes 0.5.6",
  "futures-channel",
  "futures-core",
  "futures-util",
- "h2 0.2.6",
- "http 0.2.1",
+ "h2 0.2.7",
+ "http 0.2.2",
  "http-body 0.3.1",
  "httparse",
  "httpdate",
  "itoa",
- "pin-project",
+ "pin-project 1.0.2",
  "socket2",
- "tokio 0.2.22",
+ "tokio 0.2.24",
  "tower-service",
  "tracing",
  "want 0.3.0",
@@ -2127,11 +2265,11 @@
  "bytes 0.5.6",
  "ct-logs",
  "futures-util",
- "hyper 0.13.8",
+ "hyper 0.13.9",
  "log",
  "rustls",
  "rustls-native-certs",
- "tokio 0.2.22",
+ "tokio 0.2.24",
  "tokio-rustls",
  "webpki",
 ]
@@ -2189,9 +2327,9 @@
 
 [[package]]
 name = "indexmap"
-version = "1.6.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
+checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
 dependencies = [
  "autocfg 1.0.1",
  "hashbrown 0.9.1",
@@ -2200,11 +2338,11 @@
 
 [[package]]
 name = "instant"
-version = "0.1.7"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66"
+checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -2222,7 +2360,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "64fa110ec7b8f493f416eed552740d10e7030ad5f63b2308f82c9608ec2df275"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 2.0.2",
 ]
 
@@ -2282,9 +2420,9 @@
 
 [[package]]
 name = "js-sys"
-version = "0.3.45"
+version = "0.3.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
+checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -2443,7 +2581,7 @@
 checksum = "0315ef2f688e33844400b31f11c263f2b3dc21d8b9355c6891c5f185fae43f9a"
 dependencies = [
  "parity-util-mem",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
 ]
 
 [[package]]
@@ -2472,7 +2610,7 @@
  "parking_lot 0.10.2",
  "regex",
  "rocksdb",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
 ]
 
 [[package]]
@@ -2495,9 +2633,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.79"
+version = "0.2.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
+checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
 
 [[package]]
 name = "libloading"
@@ -2523,7 +2661,7 @@
 dependencies = [
  "atomic",
  "bytes 0.5.6",
- "futures 0.3.6",
+ "futures 0.3.8",
  "lazy_static",
  "libp2p-core",
  "libp2p-core-derive",
@@ -2549,8 +2687,8 @@
  "multihash",
  "parity-multiaddr",
  "parking_lot 0.10.2",
- "pin-project",
- "smallvec 1.4.2",
+ "pin-project 0.4.27",
+ "smallvec 1.5.1",
  "wasm-timer",
 ]
 
@@ -2561,11 +2699,11 @@
 checksum = "52f13ba8c7df0768af2eb391696d562c7de88cc3a35122531aaa6a7d77754d25"
 dependencies = [
  "asn1_der",
- "bs58",
+ "bs58 0.3.1",
  "ed25519-dalek",
  "either",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "lazy_static",
  "libsecp256k1",
@@ -2574,14 +2712,14 @@
  "multistream-select",
  "parity-multiaddr",
  "parking_lot 0.10.2",
- "pin-project",
+ "pin-project 0.4.27",
  "prost",
  "prost-build",
  "rand 0.7.3",
  "ring",
  "rw-stream-sink",
  "sha2 0.8.2",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "thiserror",
  "unsigned-varint 0.4.0",
  "void",
@@ -2605,7 +2743,7 @@
 checksum = "74029ae187f35f4b8ddf26b9779a68b340045d708528a103917cdca49a296db5"
 dependencies = [
  "flate2",
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
 ]
 
@@ -2615,7 +2753,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7cf319822e08dd65c8e060d2354e9f952895bbc433f5706c75ed010c152aee5e"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "log",
 ]
@@ -2628,13 +2766,13 @@
 dependencies = [
  "cuckoofilter",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "libp2p-swarm",
  "prost",
  "prost-build",
  "rand 0.7.3",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
 ]
 
 [[package]]
@@ -2647,7 +2785,7 @@
  "byteorder 1.3.4",
  "bytes 0.5.6",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures_codec",
  "hex_fmt",
  "libp2p-core",
@@ -2658,7 +2796,7 @@
  "prost-build",
  "rand 0.7.3",
  "sha2 0.8.2",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "unsigned-varint 0.4.0",
  "wasm-timer",
 ]
@@ -2669,13 +2807,13 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56396ee63aa9164eacf40c2c5d2bda8c4133c2f57e1b0425d51d3a4e362583b1"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "libp2p-swarm",
  "log",
  "prost",
  "prost-build",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "wasm-timer",
 ]
 
@@ -2685,11 +2823,11 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cc7fa9047f8b8f544278a35c2d9d45d3b2c1785f2d86d4e1629d6edf97be3955"
 dependencies = [
- "arrayvec 0.5.1",
+ "arrayvec 0.5.2",
  "bytes 0.5.6",
  "either",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures_codec",
  "libp2p-core",
  "libp2p-swarm",
@@ -2699,7 +2837,7 @@
  "prost-build",
  "rand 0.7.3",
  "sha2 0.8.2",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "uint",
  "unsigned-varint 0.4.0",
  "void",
@@ -2716,14 +2854,14 @@
  "data-encoding",
  "dns-parser",
  "either",
- "futures 0.3.6",
+ "futures 0.3.8",
  "lazy_static",
  "libp2p-core",
  "libp2p-swarm",
  "log",
  "net2",
  "rand 0.7.3",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "void",
  "wasm-timer",
 ]
@@ -2736,7 +2874,7 @@
 dependencies = [
  "bytes 0.5.6",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures_codec",
  "libp2p-core",
  "log",
@@ -2752,7 +2890,7 @@
 dependencies = [
  "bytes 0.5.6",
  "curve25519-dalek 2.1.0",
- "futures 0.3.6",
+ "futures 0.3.8",
  "lazy_static",
  "libp2p-core",
  "log",
@@ -2772,7 +2910,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ad063c21dfcea4518ac9e8bd4119d33a5b26c41e674f602f41f05617a368a5c8"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "libp2p-swarm",
  "log",
@@ -2788,7 +2926,7 @@
 checksum = "903a12e99c72dbebefea258de887982adeacc7025baa1ceb10b7fa9928f54791"
 dependencies = [
  "bytes 0.5.6",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures_codec",
  "libp2p-core",
  "log",
@@ -2801,16 +2939,16 @@
 
 [[package]]
 name = "libp2p-pnet"
-version = "0.19.1"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d0db10e139d22d7af0b23ed7949449ec86262798aa0fd01595abdbcb02dc87"
+checksum = "96b3c2d5d26a9500e959a0e19743897239a6c4be78dadf99b70414301a70c006"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "log",
- "pin-project",
+ "pin-project 0.4.27",
  "rand 0.7.3",
  "salsa20",
- "sha3 0.8.2",
+ "sha3",
 ]
 
 [[package]]
@@ -2821,14 +2959,14 @@
 dependencies = [
  "async-trait",
  "bytes 0.5.6",
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "libp2p-swarm",
  "log",
- "lru 0.6.0",
+ "lru 0.6.2",
  "minicbor",
  "rand 0.7.3",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "unsigned-varint 0.5.1",
  "wasm-timer",
 ]
@@ -2840,11 +2978,11 @@
 checksum = "7193e444210132237b81b755ec7fe53f1c4bd2f53cf719729b94c0c72eb6eaa1"
 dependencies = [
  "either",
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "log",
  "rand 0.7.3",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "void",
  "wasm-timer",
 ]
@@ -2856,7 +2994,7 @@
 checksum = "44f42ec130d7a37a7e47bf4398026b7ad9185c08ed26972e2720f8b94112796f"
 dependencies = [
  "async-std",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "get_if_addrs",
  "ipnet",
@@ -2872,7 +3010,7 @@
 checksum = "dea7acb0a034f70d7db94c300eba3f65c0f6298820105624088a9609c9974d77"
 dependencies = [
  "async-std",
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "log",
 ]
@@ -2883,7 +3021,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34c1faac6f92c21fbe155417957863ea822fba9e9fd5eb24c0912336a100e63f"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "js-sys",
  "libp2p-core",
  "parity-send-wrapper",
@@ -2899,14 +3037,14 @@
 dependencies = [
  "async-tls",
  "either",
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
  "log",
  "quicksink",
  "rustls",
  "rw-stream-sink",
  "soketto",
- "url 2.1.1",
+ "url 2.2.0",
  "webpki",
  "webpki-roots 0.18.0",
 ]
@@ -2917,9 +3055,9 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "781d9b9f043dcdabc40640807125368596b849fd4d96cdca2dcf052fdf6f33fd"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p-core",
- "parking_lot 0.11.0",
+ "parking_lot 0.11.1",
  "thiserror",
  "yamux",
 ]
@@ -2948,7 +3086,7 @@
  "hmac-drbg",
  "rand 0.7.3",
  "sha2 0.8.2",
- "subtle 2.3.0",
+ "subtle 2.4.0",
  "typenum",
 ]
 
@@ -3009,9 +3147,9 @@
 
 [[package]]
 name = "lock_api"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
+checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
 dependencies = [
  "scopeguard 1.1.0",
 ]
@@ -3026,6 +3164,19 @@
 ]
 
 [[package]]
+name = "loom"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed"
+dependencies = [
+ "cfg-if 0.1.10",
+ "generator",
+ "scoped-tls",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
 name = "lru"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3036,11 +3187,11 @@
 
 [[package]]
 name = "lru"
-version = "0.6.0"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "111b945ac72ec09eb7bc62a0fbdc3cc6e80555a7245f52a69d3921a75b53b153"
+checksum = "3abe07af102235a56ac9a6dd904aab1e05483e2e8afdfffec3598be55b1b7606"
 dependencies = [
- "hashbrown 0.8.2",
+ "hashbrown 0.9.1",
 ]
 
 [[package]]
@@ -3075,9 +3226,9 @@
 
 [[package]]
 name = "matrixmultiply"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f"
+checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1"
 dependencies = [
  "rawpointer",
 ]
@@ -3090,9 +3241,9 @@
 
 [[package]]
 name = "memchr"
-version = "2.3.3"
+version = "2.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
 
 [[package]]
 name = "memmap"
@@ -3114,6 +3265,15 @@
 ]
 
 [[package]]
+name = "memoffset"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
+dependencies = [
+ "autocfg 1.0.1",
+]
+
+[[package]]
 name = "memory-db"
 version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3174,9 +3334,9 @@
 
 [[package]]
 name = "mio"
-version = "0.6.22"
+version = "0.6.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
+checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
 dependencies = [
  "cfg-if 0.1.10",
  "fuchsia-zircon",
@@ -3185,7 +3345,7 @@
  "kernel32-sys",
  "libc",
  "log",
- "miow 0.2.1",
+ "miow 0.2.2",
  "net2",
  "slab",
  "winapi 0.2.8",
@@ -3211,7 +3371,7 @@
 dependencies = [
  "log",
  "mio",
- "miow 0.3.5",
+ "miow 0.3.6",
  "winapi 0.3.9",
 ]
 
@@ -3228,9 +3388,9 @@
 
 [[package]]
 name = "miow"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
 dependencies = [
  "kernel32-sys",
  "net2",
@@ -3240,9 +3400,9 @@
 
 [[package]]
 name = "miow"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
+checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
 dependencies = [
  "socket2",
  "winapi 0.3.9",
@@ -3263,9 +3423,9 @@
  "blake2b_simd",
  "blake2s_simd",
  "digest 0.9.0",
- "sha-1 0.9.1",
- "sha2 0.9.1",
- "sha3 0.9.1",
+ "sha-1 0.9.2",
+ "sha2 0.9.2",
+ "sha3",
  "unsigned-varint 0.5.1",
 ]
 
@@ -3277,16 +3437,16 @@
 
 [[package]]
 name = "multistream-select"
-version = "0.8.2"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9157e87afbc2ef0d84cc0345423d715f445edde00141c93721c162de35a05e5"
+checksum = "93faf2e41f9ee62fb01680ed48f3cc26652352327aa2e59869070358f6b7dd75"
 dependencies = [
  "bytes 0.5.6",
- "futures 0.3.6",
+ "futures 0.3.8",
  "log",
- "pin-project",
- "smallvec 1.4.2",
- "unsigned-varint 0.4.0",
+ "pin-project 1.0.2",
+ "smallvec 1.5.1",
+ "unsigned-varint 0.5.1",
 ]
 
 [[package]]
@@ -3327,9 +3487,9 @@
 
 [[package]]
 name = "net2"
-version = "0.2.35"
+version = "0.2.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
+checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -3340,9 +3500,10 @@
 name = "nft"
 version = "2.0.0"
 dependencies = [
+ "flexi_logger",
  "frame-benchmarking",
  "frame-benchmarking-cli",
- "futures 0.3.6",
+ "futures 0.3.8",
  "jsonrpc-core",
  "log",
  "nft-runtime",
@@ -3361,6 +3522,8 @@
  "sc-rpc-api",
  "sc-service",
  "sc-transaction-pool",
+ "serde",
+ "serde_json",
  "sp-api",
  "sp-block-builder",
  "sp-blockchain",
@@ -3400,6 +3563,7 @@
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-treasury",
+ "pallet-vesting",
  "parity-scale-codec",
  "serde",
  "sp-api",
@@ -3474,9 +3638,9 @@
 
 [[package]]
 name = "num-integer"
-version = "0.1.43"
+version = "0.1.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
+checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
 dependencies = [
  "autocfg 1.0.1",
  "num-traits",
@@ -3496,9 +3660,9 @@
 
 [[package]]
 name = "num-traits"
-version = "0.2.12"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
 dependencies = [
  "autocfg 1.0.1",
  "libm",
@@ -3533,9 +3697,9 @@
 
 [[package]]
 name = "object"
-version = "0.21.1"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
+checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
 
 [[package]]
 name = "once_cell"
@@ -3548,11 +3712,11 @@
 
 [[package]]
 name = "once_cell"
-version = "1.4.1"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
+checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
 dependencies = [
- "parking_lot 0.11.0",
+ "parking_lot 0.11.1",
 ]
 
 [[package]]
@@ -3585,7 +3749,7 @@
 [[package]]
 name = "pallet-aura"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3604,7 +3768,7 @@
 [[package]]
 name = "pallet-authorship"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3619,7 +3783,7 @@
 [[package]]
 name = "pallet-balances"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -3633,7 +3797,7 @@
 [[package]]
 name = "pallet-contracts"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "bitflags",
  "frame-support",
@@ -3654,7 +3818,7 @@
 [[package]]
 name = "pallet-contracts-primitives"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-runtime",
@@ -3664,7 +3828,7 @@
 [[package]]
 name = "pallet-contracts-rpc"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "jsonrpc-core",
  "jsonrpc-core-client",
@@ -3683,7 +3847,7 @@
 [[package]]
 name = "pallet-contracts-rpc-runtime-api"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "pallet-contracts-primitives",
  "parity-scale-codec",
@@ -3695,7 +3859,7 @@
 [[package]]
 name = "pallet-finality-tracker"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3711,7 +3875,7 @@
 [[package]]
 name = "pallet-grandpa"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -3754,7 +3918,7 @@
 [[package]]
 name = "pallet-randomness-collective-flip"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3767,7 +3931,7 @@
 [[package]]
 name = "pallet-session"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3787,7 +3951,7 @@
 [[package]]
 name = "pallet-sudo"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3801,7 +3965,7 @@
 [[package]]
 name = "pallet-timestamp"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -3819,14 +3983,14 @@
 [[package]]
 name = "pallet-transaction-payment"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
  "pallet-transaction-payment-rpc-runtime-api",
  "parity-scale-codec",
  "serde",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "sp-core",
  "sp-io",
  "sp-runtime",
@@ -3836,7 +4000,7 @@
 [[package]]
 name = "pallet-transaction-payment-rpc"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "jsonrpc-core",
  "jsonrpc-core-client",
@@ -3854,7 +4018,7 @@
 [[package]]
 name = "pallet-transaction-payment-rpc-runtime-api"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "parity-scale-codec",
@@ -3867,7 +4031,7 @@
 [[package]]
 name = "pallet-treasury"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -3879,6 +4043,20 @@
 ]
 
 [[package]]
+name = "pallet-vesting"
+version = "2.0.0"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
+dependencies = [
+ "enumflags2",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "serde",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "parity-db"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3894,20 +4072,20 @@
 
 [[package]]
 name = "parity-multiaddr"
-version = "0.9.2"
+version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2165a93382a93de55868dcbfa11e4a8f99676a9164eee6a2b4a9479ad319c257"
+checksum = "43244a26dc1ddd3097216bb12eaa6cf8a07b060c72718d9ebd60fd297d6401df"
 dependencies = [
  "arrayref",
- "bs58",
+ "bs58 0.4.0",
  "byteorder 1.3.4",
  "data-encoding",
  "multihash",
  "percent-encoding 2.1.0",
  "serde",
  "static_assertions",
- "unsigned-varint 0.4.0",
- "url 2.1.1",
+ "unsigned-varint 0.5.1",
+ "url 2.2.0",
 ]
 
 [[package]]
@@ -3916,7 +4094,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c740e5fbcb6847058b40ac7e5574766c6388f585e184d769910fe0d3a2ca861"
 dependencies = [
- "arrayvec 0.5.1",
+ "arrayvec 0.5.2",
  "bitvec",
  "byte-slice-cast",
  "parity-scale-codec-derive",
@@ -3952,7 +4130,7 @@
  "libc",
  "log",
  "mio-named-pipes",
- "miow 0.3.5",
+ "miow 0.3.6",
  "rand 0.7.3",
  "tokio 0.1.22",
  "tokio-named-pipes",
@@ -3972,7 +4150,7 @@
  "parity-util-mem-derive",
  "parking_lot 0.10.2",
  "primitive-types",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "winapi 0.3.9",
 ]
 
@@ -4008,7 +4186,7 @@
  "rand 0.7.3",
  "sha-1 0.8.2",
  "slab",
- "url 2.1.1",
+ "url 2.2.0",
 ]
 
 [[package]]
@@ -4050,13 +4228,13 @@
 
 [[package]]
 name = "parking_lot"
-version = "0.11.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
+checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
 dependencies = [
  "instant",
- "lock_api 0.4.1",
- "parking_lot_core 0.8.0",
+ "lock_api 0.4.2",
+ "parking_lot_core 0.8.1",
 ]
 
 [[package]]
@@ -4079,7 +4257,7 @@
 checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
 dependencies = [
  "cfg-if 0.1.10",
- "cloudabi 0.0.3",
+ "cloudabi",
  "libc",
  "redox_syscall",
  "rustc_version",
@@ -4094,25 +4272,24 @@
 checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
 dependencies = [
  "cfg-if 0.1.10",
- "cloudabi 0.0.3",
+ "cloudabi",
  "libc",
  "redox_syscall",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.0"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
+checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0"
 dependencies = [
- "cfg-if 0.1.10",
- "cloudabi 0.1.0",
+ "cfg-if 1.0.0",
  "instant",
  "libc",
  "redox_syscall",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "winapi 0.3.9",
 ]
 
@@ -4180,7 +4357,16 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
 dependencies = [
- "pin-project-internal",
+ "pin-project-internal 0.4.27",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7"
+dependencies = [
+ "pin-project-internal 1.0.2",
 ]
 
 [[package]]
@@ -4195,10 +4381,27 @@
 ]
 
 [[package]]
+name = "pin-project-internal"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"
+
+[[package]]
 name = "pin-project-lite"
-version = "0.1.10"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95"
+checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
 
 [[package]]
 name = "pin-utils"
@@ -4220,9 +4423,9 @@
 
 [[package]]
 name = "polling"
-version = "2.0.1"
+version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab773feb154f12c49ffcfd66ab8bdcf9a1843f950db48b0d8be9d4393783b058"
+checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -4233,34 +4436,35 @@
 
 [[package]]
 name = "poly1305"
-version = "0.6.1"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ce46de8e53ee414ca4d02bfefac75d8c12fba948b76622a40b4be34dfce980"
+checksum = "4b7456bc1ad2d4cf82b3a016be4c2ac48daf11bf990c1603ebd447fe6f30fca8"
 dependencies = [
+ "cpuid-bool 0.2.0",
  "universal-hash",
 ]
 
 [[package]]
 name = "polyval"
-version = "0.4.1"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5884790f1ce3553ad55fec37b5aaac5882e0e845a2612df744d6c85c9bf046c"
+checksum = "b4fd92d8e0c06d08525d2e2643cc2b5c80c69ae8eb12c18272d501cd7079ccc0"
 dependencies = [
- "cfg-if 0.1.10",
+ "cpuid-bool 0.2.0",
  "universal-hash",
 ]
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
+checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
 
 [[package]]
 name = "primitive-types"
-version = "0.7.2"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8"
+checksum = "7dd39dcacf71411ba488570da7bbc89b717225e46478b30ba99b92db6b149809"
 dependencies = [
  "fixed-hash",
  "impl-codec",
@@ -4303,9 +4507,9 @@
 
 [[package]]
 name = "proc-macro-hack"
-version = "0.5.18"
+version = "0.5.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
+checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 
 [[package]]
 name = "proc-macro-nested"
@@ -4331,7 +4535,7 @@
  "cfg-if 0.1.10",
  "fnv",
  "lazy_static",
- "parking_lot 0.11.0",
+ "parking_lot 0.11.1",
  "regex",
  "thiserror",
 ]
@@ -4412,7 +4616,7 @@
 dependencies = [
  "futures-core",
  "futures-sink",
- "pin-project-lite",
+ "pin-project-lite 0.1.11",
 ]
 
 [[package]]
@@ -4459,7 +4663,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
 dependencies = [
- "cloudabi 0.0.3",
+ "cloudabi",
  "fuchsia-cprng",
  "libc",
  "rand_core 0.3.1",
@@ -4587,7 +4791,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
 dependencies = [
- "cloudabi 0.0.3",
+ "cloudabi",
  "fuchsia-cprng",
  "libc",
  "rand_core 0.4.2",
@@ -4642,25 +4846,25 @@
 
 [[package]]
 name = "rayon"
-version = "1.4.1"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032"
+checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
 dependencies = [
  "autocfg 1.0.1",
- "crossbeam-deque",
+ "crossbeam-deque 0.8.0",
  "either",
  "rayon-core",
 ]
 
 [[package]]
 name = "rayon-core"
-version = "1.8.1"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
+checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
 dependencies = [
  "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-utils",
+ "crossbeam-deque 0.8.0",
+ "crossbeam-utils 0.8.1",
  "lazy_static",
  "num_cpus",
 ]
@@ -4693,18 +4897,18 @@
 
 [[package]]
 name = "ref-cast"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "745c1787167ddae5569661d5ffb8b25ae5fedbf46717eaa92d652221cec72623"
+checksum = "e17626b2f4bcf35b84bf379072a66e28cfe5c3c6ae58b38e4914bb8891dabece"
 dependencies = [
  "ref-cast-impl",
 ]
 
 [[package]]
 name = "ref-cast-impl"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d21b475ab879ef0e315ad99067fa25778c3b0377f57f1b00207448dac1a3144"
+checksum = "0c523ccaed8ac4b0288948849a350b37d3035827413c458b6a40ddb614bb4f72"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4719,14 +4923,14 @@
 dependencies = [
  "log",
  "rustc-hash",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
 ]
 
 [[package]]
 name = "regex"
-version = "1.4.1"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
+checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -4746,9 +4950,9 @@
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.20"
+version = "0.6.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
+checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
 
 [[package]]
 name = "region"
@@ -4779,13 +4983,13 @@
 
 [[package]]
 name = "ring"
-version = "0.16.15"
+version = "0.16.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4"
+checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226"
 dependencies = [
  "cc",
  "libc",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
  "spin",
  "untrusted",
  "web-sys",
@@ -4814,21 +5018,21 @@
 
 [[package]]
 name = "rust-argon2"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
+checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
 dependencies = [
- "base64 0.12.3",
+ "base64 0.13.0",
  "blake2b_simd",
  "constant_time_eq",
- "crossbeam-utils",
+ "crossbeam-utils 0.8.1",
 ]
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.17"
+version = "0.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2610b7f643d18c87dff3b489950269617e6601a51f1f05aa5daefee36f64f0b"
+checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
 
 [[package]]
 name = "rustc-hash"
@@ -4882,8 +5086,8 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020"
 dependencies = [
- "futures 0.3.6",
- "pin-project",
+ "futures 0.3.8",
+ "pin-project 0.4.27",
  "static_assertions",
 ]
 
@@ -4904,30 +5108,19 @@
 
 [[package]]
 name = "salsa20"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2324b0e8c3bb9a586a571fdb3136f70e7e2c748de00a78043f86e0cff91f91fe"
-dependencies = [
- "byteorder 1.3.4",
- "salsa20-core",
- "stream-cipher 0.3.2",
-]
-
-[[package]]
-name = "salsa20-core"
-version = "0.2.3"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fe6cc1b9f5a5867853ade63099de70f042f7679e408d1ffe52821c9248e6e69"
+checksum = "c7f47b10fa80f6969bbbd9c8e7cc998f082979d402a9e10579e2303a87955395"
 dependencies = [
- "stream-cipher 0.3.2",
+ "stream-cipher",
 ]
 
 [[package]]
 name = "sc-basic-authorship"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "log",
  "parity-scale-codec",
@@ -4949,7 +5142,7 @@
 [[package]]
 name = "sc-block-builder"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sc-client-api",
@@ -4966,7 +5159,7 @@
 [[package]]
 name = "sc-chain-spec"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "impl-trait-for-tuples",
  "parity-scale-codec",
@@ -4983,7 +5176,7 @@
 [[package]]
 name = "sc-chain-spec-derive"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -4994,7 +5187,7 @@
 [[package]]
 name = "sc-cli"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "ansi_term 0.12.1",
  "atty",
@@ -5002,7 +5195,8 @@
  "chrono",
  "derive_more",
  "fdlimit",
- "futures 0.3.6",
+ "flexi_logger",
+ "futures 0.3.8",
  "hex",
  "lazy_static",
  "libp2p",
@@ -5034,7 +5228,7 @@
  "structopt",
  "substrate-prometheus-endpoint",
  "time",
- "tokio 0.2.22",
+ "tokio 0.2.24",
  "tracing",
  "tracing-log",
  "tracing-subscriber",
@@ -5043,11 +5237,11 @@
 [[package]]
 name = "sc-client-api"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "hash-db",
  "hex-literal",
  "kvdb",
@@ -5079,7 +5273,7 @@
 [[package]]
 name = "sc-client-db"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "blake2-rfc",
  "hash-db",
@@ -5109,7 +5303,7 @@
 [[package]]
 name = "sc-consensus"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "sc-client-api",
  "sp-blockchain",
@@ -5120,10 +5314,10 @@
 [[package]]
 name = "sc-consensus-aura"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "log",
  "parity-scale-codec",
@@ -5151,9 +5345,9 @@
 [[package]]
 name = "sc-consensus-slots"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "log",
  "parity-scale-codec",
@@ -5174,7 +5368,7 @@
 [[package]]
 name = "sc-executor"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "lazy_static",
@@ -5202,7 +5396,7 @@
 [[package]]
 name = "sc-executor-common"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "log",
@@ -5219,7 +5413,7 @@
 [[package]]
 name = "sc-executor-wasmi"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "log",
  "parity-scale-codec",
@@ -5234,7 +5428,7 @@
 [[package]]
 name = "sc-executor-wasmtime"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "log",
  "parity-scale-codec",
@@ -5252,17 +5446,17 @@
 [[package]]
 name = "sc-finality-grandpa"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "finality-grandpa",
  "fork-tree",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "log",
  "parity-scale-codec",
  "parking_lot 0.10.2",
- "pin-project",
+ "pin-project 0.4.27",
  "rand 0.7.3",
  "sc-block-builder",
  "sc-client-api",
@@ -5289,10 +5483,10 @@
 [[package]]
 name = "sc-informant"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "ansi_term 0.12.1",
- "futures 0.3.6",
+ "futures 0.3.8",
  "log",
  "parity-util-mem",
  "sc-client-api",
@@ -5307,7 +5501,7 @@
 [[package]]
 name = "sc-keystore"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "hex",
@@ -5317,13 +5511,13 @@
  "serde_json",
  "sp-application-crypto",
  "sp-core",
- "subtle 2.3.0",
+ "subtle 2.4.0",
 ]
 
 [[package]]
 name = "sc-light"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "hash-db",
  "lazy_static",
@@ -5342,19 +5536,19 @@
 [[package]]
 name = "sc-network"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "async-std",
  "async-trait",
  "bitflags",
- "bs58",
+ "bs58 0.3.1",
  "bytes 0.5.6",
  "derive_more",
  "either",
  "erased-serde",
  "fnv",
  "fork-tree",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "futures_codec",
  "hex",
@@ -5367,7 +5561,7 @@
  "nohash-hasher",
  "parity-scale-codec",
  "parking_lot 0.10.2",
- "pin-project",
+ "pin-project 0.4.27",
  "prost",
  "prost-build",
  "rand 0.7.3",
@@ -5396,9 +5590,9 @@
 [[package]]
 name = "sc-network-gossip"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "libp2p",
  "log",
@@ -5411,13 +5605,13 @@
 [[package]]
 name = "sc-offchain"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "bytes 0.5.6",
  "fnv",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
- "hyper 0.13.8",
+ "hyper 0.13.9",
  "hyper-rustls",
  "log",
  "num_cpus",
@@ -5438,9 +5632,9 @@
 [[package]]
 name = "sc-peerset"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "libp2p",
  "log",
  "serde_json",
@@ -5451,7 +5645,7 @@
 [[package]]
 name = "sc-proposer-metrics"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "log",
  "substrate-prometheus-endpoint",
@@ -5460,9 +5654,9 @@
 [[package]]
 name = "sc-rpc"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "hash-db",
  "jsonrpc-core",
  "jsonrpc-pubsub",
@@ -5492,10 +5686,10 @@
 [[package]]
 name = "sc-rpc-api"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
- "futures 0.3.6",
+ "futures 0.3.8",
  "jsonrpc-core",
  "jsonrpc-core-client",
  "jsonrpc-derive",
@@ -5516,7 +5710,7 @@
 [[package]]
 name = "sc-rpc-server"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "futures 0.1.30",
  "jsonrpc-core",
@@ -5534,13 +5728,13 @@
 [[package]]
 name = "sc-service"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "directories",
  "exit-future",
  "futures 0.1.30",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "hash-db",
  "jsonrpc-core",
@@ -5550,7 +5744,7 @@
  "parity-scale-codec",
  "parity-util-mem",
  "parking_lot 0.10.2",
- "pin-project",
+ "pin-project 0.4.27",
  "rand 0.7.3",
  "sc-block-builder",
  "sc-chain-spec",
@@ -5596,7 +5790,7 @@
 [[package]]
 name = "sc-state-db"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "log",
  "parity-scale-codec",
@@ -5610,14 +5804,14 @@
 [[package]]
 name = "sc-telemetry"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "libp2p",
  "log",
  "parking_lot 0.10.2",
- "pin-project",
+ "pin-project 0.4.27",
  "rand 0.7.3",
  "serde",
  "slog",
@@ -5631,7 +5825,7 @@
 [[package]]
 name = "sc-tracing"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "erased-serde",
  "log",
@@ -5650,10 +5844,10 @@
 [[package]]
 name = "sc-transaction-graph"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
- "futures 0.3.6",
+ "futures 0.3.8",
  "linked-hash-map",
  "log",
  "parity-util-mem",
@@ -5671,10 +5865,10 @@
 [[package]]
 name = "sc-transaction-pool"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-diagnose",
  "intervalier",
  "log",
@@ -5711,14 +5905,14 @@
 checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862"
 dependencies = [
  "arrayref",
- "arrayvec 0.5.1",
+ "arrayvec 0.5.2",
  "curve25519-dalek 2.1.0",
  "getrandom 0.1.15",
  "merlin",
  "rand 0.7.3",
  "rand_core 0.5.1",
  "sha2 0.8.2",
- "subtle 2.3.0",
+ "subtle 2.4.0",
  "zeroize",
 ]
 
@@ -5751,9 +5945,9 @@
 
 [[package]]
 name = "scroll_derive"
-version = "0.10.3"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dfde5d1531034db129e95c76ac857e2baecea3443579d493d02224950b0fb6d"
+checksum = "b12bd20b94c7cdfda8c7ba9b92ad0d9a56e3fa018c25fca83b51aa664c9b4c0d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5819,18 +6013,18 @@
 
 [[package]]
 name = "serde"
-version = "1.0.117"
+version = "1.0.118"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
+checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.117"
+version = "1.0.118"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
+checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5839,9 +6033,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.59"
+version = "1.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
+checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
 dependencies = [
  "itoa",
  "ryu",
@@ -5862,13 +6056,13 @@
 
 [[package]]
 name = "sha-1"
-version = "0.9.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
+checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
 dependencies = [
  "block-buffer 0.9.0",
- "cfg-if 0.1.10",
- "cpuid-bool",
+ "cfg-if 1.0.0",
+ "cpuid-bool 0.1.2",
  "digest 0.9.0",
  "opaque-debug 0.3.0",
 ]
@@ -5887,28 +6081,15 @@
 
 [[package]]
 name = "sha2"
-version = "0.9.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
+checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
 dependencies = [
  "block-buffer 0.9.0",
- "cfg-if 0.1.10",
- "cpuid-bool",
+ "cfg-if 1.0.0",
+ "cpuid-bool 0.1.2",
  "digest 0.9.0",
  "opaque-debug 0.3.0",
-]
-
-[[package]]
-name = "sha3"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf"
-dependencies = [
- "block-buffer 0.7.3",
- "byte-tools",
- "digest 0.8.1",
- "keccak",
- "opaque-debug 0.2.3",
 ]
 
 [[package]]
@@ -5925,11 +6106,12 @@
 
 [[package]]
 name = "sharded-slab"
-version = "0.0.9"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e"
+checksum = "7b4921be914e16899a80adefb821f8ddb7974e3f1250223575a44ed994882127"
 dependencies = [
  "lazy_static",
+ "loom",
 ]
 
 [[package]]
@@ -5939,12 +6121,21 @@
 checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
 
 [[package]]
+name = "signal-hook"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
 name = "signal-hook-registry"
-version = "1.2.1"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
+checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab"
 dependencies = [
- "arc-swap",
  "libc",
 ]
 
@@ -5962,9 +6153,9 @@
 
 [[package]]
 name = "slog"
-version = "2.5.2"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99"
+checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
 dependencies = [
  "erased-serde",
 ]
@@ -6015,9 +6206,9 @@
 
 [[package]]
 name = "smallvec"
-version = "1.4.2"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
+checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
 
 [[package]]
 name = "snow"
@@ -6032,20 +6223,19 @@
  "rand_core 0.5.1",
  "ring",
  "rustc_version",
- "sha2 0.9.1",
- "subtle 2.3.0",
+ "sha2 0.9.2",
+ "subtle 2.4.0",
  "x25519-dalek 1.1.0",
 ]
 
 [[package]]
 name = "socket2"
-version = "0.3.15"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
+checksum = "97e0e9fd577458a4f61fb91fcb559ea2afecc54c934119421f9f5d3d5b1a1057"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "libc",
- "redox_syscall",
  "winapi 0.3.9",
 ]
 
@@ -6058,17 +6248,17 @@
  "base64 0.12.3",
  "bytes 0.5.6",
  "flate2",
- "futures 0.3.6",
+ "futures 0.3.8",
  "httparse",
  "log",
  "rand 0.7.3",
- "sha-1 0.9.1",
+ "sha-1 0.9.2",
 ]
 
 [[package]]
 name = "sp-allocator"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "log",
@@ -6080,7 +6270,7 @@
 [[package]]
 name = "sp-api"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "hash-db",
  "parity-scale-codec",
@@ -6095,7 +6285,7 @@
 [[package]]
 name = "sp-api-proc-macro"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "blake2-rfc",
  "proc-macro-crate",
@@ -6107,7 +6297,7 @@
 [[package]]
 name = "sp-application-crypto"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "serde",
@@ -6119,7 +6309,7 @@
 [[package]]
 name = "sp-arithmetic"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "integer-sqrt",
  "num-traits",
@@ -6132,7 +6322,7 @@
 [[package]]
 name = "sp-authorship"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-inherents",
@@ -6143,7 +6333,7 @@
 [[package]]
 name = "sp-block-builder"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-api",
@@ -6155,7 +6345,7 @@
 [[package]]
 name = "sp-blockchain"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "log",
@@ -6172,7 +6362,7 @@
 [[package]]
 name = "sp-chain-spec"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "serde",
  "serde_json",
@@ -6181,10 +6371,10 @@
 [[package]]
 name = "sp-consensus"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-timer 3.0.2",
  "libp2p",
  "log",
@@ -6207,7 +6397,7 @@
 [[package]]
 name = "sp-consensus-aura"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-api",
@@ -6221,7 +6411,7 @@
 [[package]]
 name = "sp-consensus-slots"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-runtime",
@@ -6230,7 +6420,7 @@
 [[package]]
 name = "sp-core"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "base58",
  "blake2-rfc",
@@ -6238,7 +6428,7 @@
  "derive_more",
  "dyn-clonable",
  "ed25519-dalek",
- "futures 0.3.6",
+ "futures 0.3.8",
  "hash-db",
  "hash256-std-hasher",
  "hex",
@@ -6274,7 +6464,7 @@
 [[package]]
 name = "sp-database"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "kvdb",
  "parking_lot 0.10.2",
@@ -6283,7 +6473,7 @@
 [[package]]
 name = "sp-debug-derive"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6293,7 +6483,7 @@
 [[package]]
 name = "sp-externalities"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "environmental",
  "parity-scale-codec",
@@ -6304,7 +6494,7 @@
 [[package]]
 name = "sp-finality-grandpa"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "finality-grandpa",
  "log",
@@ -6320,7 +6510,7 @@
 [[package]]
 name = "sp-finality-tracker"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-inherents",
@@ -6330,7 +6520,7 @@
 [[package]]
 name = "sp-inherents"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
  "parity-scale-codec",
@@ -6342,9 +6532,9 @@
 [[package]]
 name = "sp-io"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "hash-db",
  "libsecp256k1",
  "log",
@@ -6365,7 +6555,7 @@
 [[package]]
 name = "sp-keyring"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "lazy_static",
  "sp-core",
@@ -6376,7 +6566,7 @@
 [[package]]
 name = "sp-offchain"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "sp-api",
  "sp-core",
@@ -6386,7 +6576,7 @@
 [[package]]
 name = "sp-panic-handler"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "backtrace",
  "log",
@@ -6395,7 +6585,7 @@
 [[package]]
 name = "sp-rpc"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "serde",
  "sp-core",
@@ -6404,7 +6594,7 @@
 [[package]]
 name = "sp-runtime"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "either",
  "hash256-std-hasher",
@@ -6426,7 +6616,7 @@
 [[package]]
 name = "sp-runtime-interface"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "primitive-types",
@@ -6442,7 +6632,7 @@
 [[package]]
 name = "sp-runtime-interface-proc-macro"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "Inflector",
  "proc-macro-crate",
@@ -6454,7 +6644,7 @@
 [[package]]
 name = "sp-sandbox"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-core",
@@ -6467,7 +6657,7 @@
 [[package]]
 name = "sp-serializer"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "serde",
  "serde_json",
@@ -6476,7 +6666,7 @@
 [[package]]
 name = "sp-session"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-api",
@@ -6489,7 +6679,7 @@
 [[package]]
 name = "sp-staking"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "parity-scale-codec",
  "sp-runtime",
@@ -6499,7 +6689,7 @@
 [[package]]
 name = "sp-state-machine"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "hash-db",
  "log",
@@ -6507,7 +6697,7 @@
  "parity-scale-codec",
  "parking_lot 0.10.2",
  "rand 0.7.3",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "sp-core",
  "sp-externalities",
  "sp-panic-handler",
@@ -6520,12 +6710,12 @@
 [[package]]
 name = "sp-std"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 
 [[package]]
 name = "sp-storage"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "impl-serde",
  "parity-scale-codec",
@@ -6538,7 +6728,7 @@
 [[package]]
 name = "sp-timestamp"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "impl-trait-for-tuples",
  "parity-scale-codec",
@@ -6552,7 +6742,7 @@
 [[package]]
 name = "sp-tracing"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "log",
  "parity-scale-codec",
@@ -6565,10 +6755,10 @@
 [[package]]
 name = "sp-transaction-pool"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "derive_more",
- "futures 0.3.6",
+ "futures 0.3.8",
  "log",
  "parity-scale-codec",
  "serde",
@@ -6580,7 +6770,7 @@
 [[package]]
 name = "sp-trie"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "hash-db",
  "memory-db",
@@ -6594,9 +6784,9 @@
 [[package]]
 name = "sp-utils"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "futures-core",
  "futures-timer 3.0.2",
  "lazy_static",
@@ -6606,7 +6796,7 @@
 [[package]]
 name = "sp-version"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "impl-serde",
  "parity-scale-codec",
@@ -6618,7 +6808,7 @@
 [[package]]
 name = "sp-wasm-interface"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "impl-trait-for-tuples",
  "parity-scale-codec",
@@ -6651,15 +6841,6 @@
 checksum = "10102ac8d55e35db2b3fafc26f81ba8647da2e15879ab686a67e6d19af2685e8"
 dependencies = [
  "rand 0.5.6",
-]
-
-[[package]]
-name = "stream-cipher"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c"
-dependencies = [
- "generic-array 0.12.3",
 ]
 
 [[package]]
@@ -6689,9 +6870,9 @@
 
 [[package]]
 name = "structopt"
-version = "0.3.20"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "126d630294ec449fae0b16f964e35bf3c74f940da9dca17ee9b905f7b3112eb8"
+checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c"
 dependencies = [
  "clap",
  "lazy_static",
@@ -6700,9 +6881,9 @@
 
 [[package]]
 name = "structopt-derive"
-version = "0.4.13"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65e51c492f9e23a220534971ff5afc14037289de430e3c83f9daf6a1b6ae91e8"
+checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90"
 dependencies = [
  "heck",
  "proc-macro-error",
@@ -6748,7 +6929,7 @@
 [[package]]
 name = "substrate-build-script-utils"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "platforms",
 ]
@@ -6756,10 +6937,10 @@
 [[package]]
 name = "substrate-frame-rpc-system"
 version = "2.0.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "frame-system-rpc-runtime-api",
- "futures 0.3.6",
+ "futures 0.3.8",
  "jsonrpc-core",
  "jsonrpc-core-client",
  "jsonrpc-derive",
@@ -6779,15 +6960,15 @@
 [[package]]
 name = "substrate-prometheus-endpoint"
 version = "0.8.0"
-source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#a200cdb93c6af5763b9c7bf313fa708764ac88ca"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
 dependencies = [
  "async-std",
  "derive_more",
  "futures-util",
- "hyper 0.13.8",
+ "hyper 0.13.9",
  "log",
  "prometheus",
- "tokio 0.2.22",
+ "tokio 0.2.24",
 ]
 
 [[package]]
@@ -6804,15 +6985,15 @@
 
 [[package]]
 name = "subtle"
-version = "2.3.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
+checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
 
 [[package]]
 name = "syn"
-version = "1.0.44"
+version = "1.0.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
+checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6859,9 +7040,9 @@
 
 [[package]]
 name = "termcolor"
-version = "1.1.0"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 dependencies = [
  "winapi-util",
 ]
@@ -6877,18 +7058,18 @@
 
 [[package]]
 name = "thiserror"
-version = "1.0.21"
+version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
+checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.21"
+version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
+checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6932,7 +7113,7 @@
 dependencies = [
  "failure",
  "hmac",
- "once_cell 1.4.1",
+ "once_cell 1.5.2",
  "pbkdf2",
  "rand 0.7.3",
  "rustc-hash",
@@ -6951,9 +7132,18 @@
 
 [[package]]
 name = "tinyvec"
-version = "0.3.4"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
@@ -6981,9 +7171,9 @@
 
 [[package]]
 name = "tokio"
-version = "0.2.22"
+version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
+checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
 dependencies = [
  "bytes 0.5.6",
  "fnv",
@@ -6995,7 +7185,7 @@
  "mio",
  "mio-uds",
  "num_cpus",
- "pin-project-lite",
+ "pin-project-lite 0.1.11",
  "signal-hook-registry",
  "slab",
  "winapi 0.3.9",
@@ -7039,7 +7229,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
 dependencies = [
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "futures 0.1.30",
 ]
 
@@ -7095,7 +7285,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
 dependencies = [
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "futures 0.1.30",
  "lazy_static",
  "log",
@@ -7116,7 +7306,7 @@
 dependencies = [
  "futures-core",
  "rustls",
- "tokio 0.2.22",
+ "tokio 0.2.24",
  "webpki",
 ]
 
@@ -7170,9 +7360,9 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89"
 dependencies = [
- "crossbeam-deque",
+ "crossbeam-deque 0.7.3",
  "crossbeam-queue",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "futures 0.1.30",
  "lazy_static",
  "log",
@@ -7187,7 +7377,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
 dependencies = [
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "futures 0.1.30",
  "slab",
  "tokio-executor 0.1.10",
@@ -7236,8 +7426,8 @@
  "futures-core",
  "futures-sink",
  "log",
- "pin-project-lite",
- "tokio 0.2.22",
+ "pin-project-lite 0.1.11",
+ "tokio 0.2.24",
 ]
 
 [[package]]
@@ -7257,13 +7447,13 @@
 
 [[package]]
 name = "tracing"
-version = "0.1.21"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
+checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "log",
- "pin-project-lite",
+ "pin-project-lite 0.2.0",
  "tracing-attributes",
  "tracing-core",
 ]
@@ -7289,6 +7479,16 @@
 ]
 
 [[package]]
+name = "tracing-futures"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
+dependencies = [
+ "pin-project 0.4.27",
+ "tracing",
+]
+
+[[package]]
 name = "tracing-log"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7311,9 +7511,9 @@
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.2.13"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ef0a5e15477aa303afbfac3a44cba9b6430fdaad52423b1e6c0dbbe28c3eedd"
+checksum = "a1fa8f0c8f4c594e4fc9debc1990deab13238077271ba84dd853d54902ee3401"
 dependencies = [
  "ansi_term 0.12.1",
  "chrono",
@@ -7323,7 +7523,7 @@
  "serde",
  "serde_json",
  "sharded-slab",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "thread_local",
  "tracing",
  "tracing-core",
@@ -7341,7 +7541,7 @@
  "hashbrown 0.8.2",
  "log",
  "rustc-hex",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
 ]
 
 [[package]]
@@ -7408,18 +7608,18 @@
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.13"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
+checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.6.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
 
 [[package]]
 name = "unicode-width"
@@ -7440,7 +7640,7 @@
 checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
 dependencies = [
  "generic-array 0.14.4",
- "subtle 2.3.0",
+ "subtle 2.4.0",
 ]
 
 [[package]]
@@ -7484,10 +7684,11 @@
 
 [[package]]
 name = "url"
-version = "2.1.1"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
+checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
 dependencies = [
+ "form_urlencoded",
  "idna 0.2.0",
  "matches",
  "percent-encoding 2.1.0",
@@ -7495,9 +7696,9 @@
 
 [[package]]
 name = "vcpkg"
-version = "0.2.10"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
+checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
 
 [[package]]
 name = "vec-arena"
@@ -7564,19 +7765,19 @@
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
+checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
+checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
 dependencies = [
  "bumpalo",
  "lazy_static",
@@ -7589,11 +7790,11 @@
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.18"
+version = "0.4.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
+checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "js-sys",
  "wasm-bindgen",
  "web-sys",
@@ -7601,9 +7802,9 @@
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
+checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -7611,9 +7812,9 @@
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
+checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -7624,9 +7825,9 @@
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
+checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
 
 [[package]]
 name = "wasm-timer"
@@ -7634,9 +7835,9 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "js-sys",
- "parking_lot 0.11.0",
+ "parking_lot 0.11.1",
  "pin-utils",
  "wasm-bindgen",
  "wasm-bindgen-futures",
@@ -7692,7 +7893,7 @@
  "log",
  "region",
  "rustc-demangle",
- "smallvec 1.4.2",
+ "smallvec 1.5.1",
  "target-lexicon",
  "wasmparser 0.59.0",
  "wasmtime-environ",
@@ -7825,7 +8026,7 @@
  "lazy_static",
  "libc",
  "log",
- "memoffset",
+ "memoffset 0.5.6",
  "more-asserts",
  "region",
  "thiserror",
@@ -7835,27 +8036,27 @@
 
 [[package]]
 name = "wast"
-version = "26.0.0"
+version = "30.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54b3678343bfdf1768c16308a4b53823b70f7bb148d3d38225123d4306ec731"
+checksum = "9b79907b22f740634810e882d8d1d9d0f9563095a8ab94e786e370242bff5cd2"
 dependencies = [
  "leb128",
 ]
 
 [[package]]
 name = "wat"
-version = "1.0.27"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b2dccbce4d0e14875091846e110a2369267b18ddd0d6423479b88dad914d71"
+checksum = "a8279a02835bf12e61ed2b3c3cbc6ecf9918762fd97e036917c11a09ec20ca44"
 dependencies = [
  "wast",
 ]
 
 [[package]]
 name = "web-sys"
-version = "0.3.45"
+version = "0.3.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
+checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -7863,9 +8064,9 @@
 
 [[package]]
 name = "webpki"
-version = "0.21.3"
+version = "0.21.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
+checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
 dependencies = [
  "ring",
  "untrusted",
@@ -7891,9 +8092,9 @@
 
 [[package]]
 name = "wepoll-sys"
-version = "3.0.0"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "142bc2cba3fe88be1a8fcb55c727fa4cd5b0cf2d7438722792e22f26f04bc1e0"
+checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
 dependencies = [
  "cc",
 ]
@@ -7988,19 +8189,25 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9aeb8c4043cac71c3c299dff107171c220d179492350ea198e109a414981b83c"
 dependencies = [
- "futures 0.3.6",
+ "futures 0.3.8",
  "log",
  "nohash-hasher",
- "parking_lot 0.11.0",
+ "parking_lot 0.11.1",
  "rand 0.7.3",
  "static_assertions",
 ]
 
 [[package]]
+name = "yansi"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
+
+[[package]]
 name = "zeroize"
-version = "1.1.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a"
+checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
 dependencies = [
  "zeroize_derive",
 ]
@@ -8019,18 +8226,18 @@
 
 [[package]]
 name = "zstd"
-version = "0.5.3+zstd.1.4.5"
+version = "0.5.4+zstd.1.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8"
+checksum = "69996ebdb1ba8b1517f61387a883857818a66c8a295f487b1ffd8fd9d2c82910"
 dependencies = [
  "zstd-safe",
 ]
 
 [[package]]
 name = "zstd-safe"
-version = "2.0.5+zstd.1.4.5"
+version = "2.0.6+zstd.1.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055"
+checksum = "98aa931fb69ecee256d44589d19754e61851ae4769bf963b385119b1cc37a49e"
 dependencies = [
  "libc",
  "zstd-sys",
@@ -8038,9 +8245,9 @@
 
 [[package]]
 name = "zstd-sys"
-version = "1.4.17+zstd.1.4.5"
+version = "1.4.18+zstd.1.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
+checksum = "a1e6e8778706838f43f771d80d37787cb2fe06dafe89dd3aebaf6721b9eaec81"
 dependencies = [
  "cc",
  "glob",
modifiedLICENSEdiffbeforeafterboth
--- a/LICENSE
+++ b/LICENSE
@@ -1,24 +1,15 @@
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
+USETECH PROFESSIONAL CONFIDENTIAL
+__________________
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
+ [2019] - [2020] UseTech Professional LTD. 
+ All Rights Reserved.
 
-For more information, please refer to <http://unlicense.org>
+NOTICE:  All information contained herein is, and remains
+the property of UseTech Professional LTD. and its suppliers,
+if any. The intellectual and technical concepts contained
+herein are proprietary to UseTech Professional LTD.
+and its suppliers and may be covered by U.S. and Foreign Patents,
+patents in process, and are protected by trade secret or copyright law.
+Dissemination of this information or reproduction of this material
+is strictly forbidden unless prior written permission is obtained
+from UseTech Professional LTD..
modifiedREADME.mddiffbeforeafterboth
--- a/README.md
+++ b/README.md
@@ -52,28 +52,27 @@
 
 2. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.
 
-3. Install Rust Toolchain 1.44.0:
+3. Install Toolchain and make it default:
 
 ```bash
-rustup install 1.44.0
+rustup toolchain install nightly-2020-10-01
+rustup default nightly-2020-10-01
 ```
 
-4. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)
+4. Add wasm target for default toolchain:
+
 ```bash
-rustup toolchain list
-rustup default 1.44.0-x86_64-unknown-linux-gnu
+rustup target add wasm32-unknown-unknown
 ```
 
-5. Install nightly toolchain and add wasm target for it:
-
+5. Build:
 ```bash
-rustup toolchain install nightly-2020-05-01
-rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu
+cargo build
 ```
 
-6. Build:
+optionally, build in release:
 ```bash
-cargo build
+cargo build --release
 ```
 
 ## Run
@@ -134,4 +133,8 @@
 
 ## UI custom types
 
-Moved to [runtime_types.json](./runtime_types.json).
\ No newline at end of file
+Moved to [runtime_types.json](./runtime_types.json).
+
+## Running Integration Tests
+
+See [tests/README.md](./tests/README.md).
\ No newline at end of file
addeddoc/builders_walk_through.mddiffbeforeafterboth
--- /dev/null
+++ b/doc/builders_walk_through.md
@@ -0,0 +1,244 @@
+# Builders Program Walk-Through
+
+This document walks through current state of Uniquch Chain as of end of October 2020.
+
+## Unique Chain Source code
+
+Unique chain is a substrate based chain, which mainly consists of NFT pallet (developed by Unique Network) and Smart Contracts pallet.
+
+As of now, it is based off Substrate 2.0 release and supports Ink! 3.0 smart contracts.
+
+[Unique Chain](https://github.com/usetech-llc/nft_private)
+
+### Unique TestNet
+
+The TestNet public node is avaiable at wss://unique.usetech.com. It connects to the standard AppsUI (works best with our version of AppsUI, see below, but connection can be still tested with Polkadot.js.org):
+
+1. Open the [Apps UI](https://polkadot.js.org/apps/#)
+2. Click on the network icon in the top left corner
+3. Scroll down the list and input `wss://unique.usetech.com` under "custom endpoint"
+4. After connection is established, copy these [UI Custom Types](https://github.com/usetech-llc/nft_parachain#ui-custom-types) and paste them in [Developer Settings](https://polkadot.js.org/apps/#/settings/developer). Hit "Save" button.
+5. Now the NFT node is connected with AppsUI. 
+6. Go to [Chain State](https://polkadot.js.org/apps/#/chainstate) and verify that NFT pallet is visible and you can read information from it.
+7. For example, select nft -> collection, and enter collection ID 4 (for SubstraPunks) and click "+" button. You will see the collection properties such as UTF-16 encoded collection name and description, UTF-8 encoded token prefix, the size of custom data, etc.
+
+#### Getting some Unique Tokens
+
+1. Install Polkadot{.js} extension if you don't have it already installed. Here are the links for [Chrome](https://chrome.google.com/webstore/detail/polkadot%7Bjs%7D-extension/mopnmbcafieddcagagdcbnhejhlodfdd) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/polkadot-js-extension/) extensions.
+2. Create an address
+3. Use Unique Faucet Telegram bot: @UniqueFaucetBot
+
+### Smart Contracts Pallet
+
+1. Open [Contracts](https://polkadot.js.org/apps/#/contracts) tab. It only appears in the UI when Smart Contracts pallet is included in the runtime.
+2. Additionally, we can find an existing smart contract. Click on "Add an existing contract" link.
+3. Input this contract address: `5GdNqKMv4Sszq3SRd3TkXNa6a9ct4D3nXvtTWTFR7rTyccVJ`
+4. Input any contract name, for example `Claim Substrapunks`
+5. Download and drag-and-drop this [metadata.json](https://github.com/usetech-llc/substrapunks/releases/download/v1.0.2/metadata.json) file into contract ABI field.
+6. Click Save. The contract appears in the page. If the contract did not exist at that address, the UI would display an error message: "Unable to find deployed contract code at the specified address".
+
+### Integration Between Smart Contracts and NFT Pallet
+
+#### TestNet
+
+This is one of the most technically challenging parts. The pre-RC4 versions of Substrate did not function properly when we tried to use `ext_dispatch_call` to dispatch a runtime call to NFT pallet from Contracts pallet, and RC4 had the `ext_dispatch_call` already removed to "free space" for some friendlier way of interaction between pallets, that was not yet implemented. Thanks to Alexander Theißen who reverted the removal of `ext_dispatch_call` and created a special branch based on RC4 for us!
+
+The smart contract source code exists in this [repository folder](https://github.com/usetech-llc/nft_parachain/tree/master/smart_contract/ink-types-node-runtime), but the best way to test how the interaction between smart contracts and NFT pallet works is to see it in action using the [SubstraPunks Game Example](https://ipfs-gateway.usetech.com/ipns/QmaMtDqE9nhMX9RQLTpaCboqg7bqkb6Gi67iCKMe8NDpCE/) that uses smart contract to claim free characters. The complete demo of this game will come later, so please bare with us and let's put this item demonstration for a bit later.
+
+#### MainNet
+
+The Ink! 3.0 Chain Extensions will allow calling pallets from smart contracts with env() object in Ink!, as soon as they are ready. Our code is prewired for this and waiting for Chain Extensions to release.
+
+### Fungibility and Re-Fungibility support
+
+Fungible and Re-Fungible modes are fully implemented and ready for QA as of now. Fungible mode allows creation of ERC-20-like tokens, and Re-Fungible allows sharing NFT tokens between multiple addresses.
+
+#### Re-Fungibility
+
+Re-fungible feature is best demonstrated in action using our NFT wallet. First, you need to have some Unique and Re-Fungible tokens. [This section](#getting-some-unique-tokens) tells how to get them.
+
+1. Open the [NFT Wallet](https://uniqueapps.usetech.com/#/nft)
+2. Search for collection called "Artwork". Search can be done either by collection name or collection ID (which is 2).
+3. Click on "+ Add collection" in search results. The collection will appear under "My collections"
+4. Expand the "Artwork" collection and see that you own a token with a partial balance
+5. Transfer the token to some other address: Click "Transfer token" and enter address and the amount. Amount should be entered as decimal fraction. For example "0.01" to transfer 1/100th part of the token. 
+6. Observe that your balance decreased by the amount you entered.
+
+#### Fungibility
+
+Fungible tokens can be created manually by calling extrinsics:
+1. Open [Extrinsics page in UI](https://uniqueapps.usetech.com/#/extrinsics)
+2. Select nft - createCollection
+3. In the CollectionMode select Fungible, enter decimal points number and click "Submit Transaction"
+4. Switch to [Explorer](https://uniqueapps.usetech.com/#/explorer) and find the block where the transaction was mined.
+5. Find nft.Created Event, which will contain the ID of the Fungible token you just created
+6. In order to mint a token, use nft - createItem extrinsic. Enter the collection ID (that you found in the previous step) and submit transaction. One token will be minted. (We also support Batch minting for minting different amounts, etc).
+
+#### Re-fungibility The Hard Way
+
+Also, the Re-Fungible support can be demonstrated using the standard UI features without NFT wallet.
+
+1. Open [Chain State](https://uniqueapps.usetech.com/#/chainstate).
+2. Select "nft" - "reFungbleItemList"
+3. Enter parameters: 2 and 1, click "+" button
+4. Observe the following data structure returned:
+
+```
+{
+  Collection: 2,
+  Owner: [
+    {
+      owner: 5FNujvbtMyKoJC2zTrfGVaQek7jhfR1L558BMhogfFfD7veH,
+      fraction: 3,000
+    },
+    {
+      owner: 5D73wtH5pqN99auP4b6KQRQAbketaSj4StkBJxACPBUAUdiq,
+      fraction: 3,000
+    },
+    {
+      owner: 5FZeTmbZQZsJcyEevjGVK1HHkcKfWBYxWpbgEffQ2M1SqAnP,
+      fraction: 2,000
+    },
+    {
+      owner: 5EnzEXBuxFHdymceAAtstym8FETQqH4inx29XJSP6uHaCUiP,
+      fraction: 1,000
+    },
+    {
+      owner: 5GU6iHnc3qTMaufmKYzHUpDmVN2CgzA2JMcGFQLcKNDbE7c6,
+      fraction: 1,000
+    }
+  ],
+  Data: 
+}
+```
+
+The Owner field contains a list of owners with the fractions they own. The fractions are represented as fixed point decimals. The number of decimal points in this fraction is determined by collection properties. For example, in the Artwork collection this property is equal to 4. The owned fraction is then determined as `fraction` divided by 10^DecimalPoints, i.e. 10,000 in this case.
+
+5. Staying in Chain State, select "nft" - "colection"
+6. Input "2" and click "+"
+7. Observe the following structure returned. It tells that collection is ReFungible and DecimalPoints field is equal to 4.
+
+```
+{
+  Owner: 5FNujvbtMyKoJC2zTrfGVaQek7jhfR1L558BMhogfFfD7veH,
+  Mode: {
+    ReFungible: [
+      0,
+      4
+    ]
+  },
+  Access: 0,
+  DecimalPoints: 4,
+  Name: [
+...
+```
+
+### Off-Chain Schema to store token image URLs
+
+This feature is best demonstrated in action using our NFT wallet. First, you need to have some Unique and Re-Fungible tokens. [This section](#getting-some-unique-tokens) tells how to get them.
+
+1. Open the [NFT Wallet](https://uniqueapps.usetech.com/#/nft)
+2. Search for collection called "Artwork". Search can be done either by collection name or collection ID (which is 2).
+3. Expand "Artwork" collection
+4. Click on the image to zoom in. 
+5. Right-click on the image and select "Open Image in New Tab"
+6. Note the image URL:
+```
+https://ipfs-gateway.usetech.com/ipfs/QmUSv64cUmL2m44QYkUFWmH89qykC8VLPFwjhpeAScjejS/image1.jpg
+``` 
+7. Open [Chain State](https://uniqueapps.usetech.com/#/chainstate).
+8. Select "nft" - "colection"
+9. Input "2" and click "+"
+10. Scroll down:
+```
+  ],
+  TokenPrefix: ARTu0000,
+  CustomDataSize: 0,
+  OffchainSchema: https://ipfs-gateway.usetech.com/ipfs/QmUSv64cUmL2m44QYkUFWmH89qykC8VLPFwjhpeAScjejS/image{id}.jpg,
+  Sponsor: 5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM,
+  UnconfirmedSponsor: 5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM
+}
+```
+11. Observe the field called `OffchainSchema`. This field defines the URL schema that is used to contruct token URLs based on the token ID. In this particular example, the {id} template variable is replaced with token id 1, which results in the proper image URL.
+
+### New economic models
+
+This feature is best demonstrated when you own a SubstraPunk character. Please proceed with the demonstration, and then come back after you claim one.
+
+1. Create a new address in Polkadot{.js} extension. We need an address that has zero balance in Unique tokens. Let's call it "ZERO BALANCE" address.
+2. Open the [NFT Wallet](https://uniqueapps.usetech.com/#/nft)
+3. Search for collection called "SubstraPunks" (partial search also works, so you can just type "punk" and hit Enter)
+4. Add collection
+5. Expand substrapunks collection and transfer your PNK token to your "ZERO BALANCE" address.
+6. Now select your "ZERO BALANCE" address in the drop-down list and repeat searching and adding the collection for this address.
+7. Transfer token back to your main address. Observe that despite the zero balance, the transfer is successful.
+
+### White Lists and Public Mint Permission
+
+We did not complete these features in time by Hackusama deadline, but because they are important for security of the network, we completed them after the deadline anyway. They can be seen in branch [feature/white_list](https://github.com/usetech-llc/nft_parachain/tree/feature/white_list). Here are the permalinks to essential functions:
+
+[white_lists](https://github.com/usetech-llc/nft_parachain/blob/b7c59f0085ed2bc1922e937adf68ef4174a8ba36/pallets/nft/src/lib.rs#L659)
+
+[mint_permission](https://github.com/usetech-llc/nft_parachain/blob/b7c59f0085ed2bc1922e937adf68ef4174a8ba36/pallets/nft/src/lib.rs#L373)
+
+## NFT Wallet
+
+[Project Description](https://github.com/usetech-llc/apps/blob/master/README.md)
+
+All features of the NFT Wallet were already demonstrated previously:
+
+* Enables adding favorite collections
+* Shows tokens owned
+* Allows NFT and Re-Fungible transfers
+* Shows Re-Fungible Balances
+* Shows Token Images
+
+## Our version of AppsUI
+
+The UI was also mainly demonstrated. We only need to show some configuration that happens behind the scenes:
+
+### Loads appropriate custom API types
+1. Open the [NFT Wallet Developer Settings](https://uniqueapps.usetech.com/#/settings/developer)
+2. Observe that Custom UI types are already in place
+
+### Defaults to NFT TestNet
+
+1. Open the [NFT Wallet](https://uniqueapps.usetech.com/)
+2. Observe that Unique Network icon appears in the left top corner without a need to specify the network
+3. Click on Unique Network icon and see that `NFT Testnet` is in the network list
+
+## Unity API and SDK PoC
+
+[Project Description](https://github.com/usetech-llc/nft_unity/blob/master/README.md)
+
+In order to see Unity Asset in action, please follow the instructions in this README file:
+
+[Demonstration](https://github.com/usetech-llc/nft_unity/blob/master/src/DemoApplication/readme.md)
+
+## SubstraPunks Game and Marketplace
+
+[Project Description](https://github.com/usetech-llc/substrapunks/blob/master/README.md)
+
+First, you will need some Unique balance. [This section](#getting-some-unique-tokens) tells how to get the TestNet currency.
+
+This game initially was meant as a demonstration, but all Punks were quickly claimed, and we had to develop the marketplace to keep demonstrating capability of it. [Here](https://github.com/usetech-llc/nft_parachain/blob/master/doc/hackusama_walk_through.md#substrapunks-game) is the original description.
+
+Here is how one can test Marketplace:
+
+1. Open [Substrapunks Marketplace](https://ipfs-gateway.usetech.com/ipns/QmaMtDqE9nhMX9RQLTpaCboqg7bqkb6Gi67iCKMe8NDpCE/)
+2. Contact us in [Unique Telegram channel](https://t.me/Uniquechain), so we can place a test NFT for you buy on the marketplace.
+3. Refresh the marketplace page and click on the NFT to buy (the one we placed)
+4. Go through buying process
+5. Let us know to exchange KSM back for the NFT (if you would not like to keep the punk of course :) )
+
+### How Marketplace works
+
+Marketplace is a PoC and it consists a PoC bridge between Kusama and Unique and a Matcher smart contract.
+
+#### Kusama Bridge
+
+The bridge works mainly as an escrow that locks NFTs and KSM allowing users to trustlessly exchange their assets.
+
+#### Matcher Smart Contract
+
+The matcher smart contract allows placing punks on sale and buying. It operates assets that are locked in the bridge and releases them once the trade happens or the offer is cancelled.
modifiednode/Cargo.tomldiffbeforeafterboth
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -58,6 +58,9 @@
 substrate-frame-rpc-system = {version = '2.0.0', git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi'}
 pallet-contracts-rpc = {version = '0.8.0', git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi'} 
 
+serde = { version = "1.0.102", features = ["derive"] }
+serde_json = "1.0.41"
+
 [features]
 default = []
 runtime-benchmarks = ['nft-runtime/runtime-benchmarks']
modifiednode/build.rsdiffbeforeafterboth
--- a/node/build.rs
+++ b/node/build.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
 
 fn main() {
modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 // use nft_runtime::{
 //     AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
 //     SystemConfig, WASM_BINARY,
@@ -9,6 +14,7 @@
 use sp_core::{sr25519, Pair, Public};
 use sp_finality_grandpa::AuthorityId as GrandpaId;
 use sp_runtime::traits::{IdentifyAccount, Verify};
+use serde_json::map::Map;
 
 // Note this is the URL for the telemetry server
 //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
@@ -41,6 +47,11 @@
 pub fn development_config() -> Result<ChainSpec, String> {
 	let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;
 
+	let mut properties = Map::new();
+	properties.insert("tokenSymbol".into(), "UniqueTest".into());
+	properties.insert("tokenDecimals".into(), 15.into());
+	properties.insert("ss58Format".into(), 42.into()); // Generic Substrate wildcard (SS58 checksum preimage)
+
 	Ok(ChainSpec::from_genesis(
 		// Name
 		"Development",
@@ -71,7 +82,7 @@
 		// Protocol ID
 		None,
 		// Properties
-		None,
+		Some(properties),
 		// Extensions
 		None,
 	))
@@ -132,6 +143,11 @@
     endowed_accounts: Vec<AccountId>,
     enable_println: bool,
 ) -> GenesisConfig {
+
+	let vested_accounts = vec![
+		get_account_id_from_seed::<sr25519::Public>("Bob"),
+	];
+
     GenesisConfig {
         system: Some(SystemConfig {
             code: wasm_binary.to_vec(),
@@ -154,7 +170,14 @@
                 .collect(),
 		}),
 		pallet_treasury: Some(Default::default()),
-        pallet_sudo: Some(SudoConfig { key: root_key }),
+		pallet_sudo: Some(SudoConfig { key: root_key }),
+		pallet_vesting: Some(VestingConfig {
+            vesting: vested_accounts
+                .iter()
+                .cloned()
+                .map(|k| (k, 1000, 100, 1 << 98))
+                .collect(),
+        }),
         pallet_nft: Some(NftConfig {
             collection: vec![(
                 1,
@@ -170,7 +193,7 @@
 					offchain_schema: vec![],
 					schema_version: SchemaVersion::default(),
                     sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
-                    unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
+                    sponsor_confirmed: true,
                     const_on_chain_schema: vec![],
 					variable_on_chain_schema: vec![],
 					limits: CollectionLimits::default()
@@ -180,8 +203,8 @@
             fungible_item_id: vec![],
             refungible_item_id: vec![],
             chain_limit: ChainLimits {
-                collection_numbers_limit: 10,
-                account_token_ownership_limit: 10,
+                collection_numbers_limit: 100000,
+                account_token_ownership_limit: 1000000,
                 collections_admins_limit: 5,
                 custom_data_limit: 2048,
                 nft_sponsor_transfer_timeout: 15,
modifiednode/src/main.rsdiffbeforeafterboth
--- a/node/src/main.rs
+++ b/node/src/main.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 //! Substrate Node Template CLI library.
 #![warn(missing_docs)]
 
modifiednode/src/service.rsdiffbeforeafterboth
--- a/node/src/service.rs
+++ b/node/src/service.rs
@@ -1,5 +1,10 @@
 //! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
 
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 use std::sync::Arc;
 use std::time::Duration;
 use sc_client_api::{ExecutorProvider, RemoteBackend};
modifiedpallets/nft/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -355,4 +355,66 @@
             Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
 
         }: set_variable_meta_data(RawOrigin::Signed(caller.clone()), 2, 1, [1, 2, 3].to_vec())
+
+        set_schema_version {
+            let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
+            let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
+            let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+            let mode: CollectionMode = CollectionMode::NFT;
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+            Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+        }: set_schema_version(RawOrigin::Signed(caller.clone()), 2, SchemaVersion::Unique)
+
+        set_chain_limits {
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+            let limits = ChainLimits { 
+                collection_numbers_limit: 0,
+                account_token_ownership_limit: 0,
+                collections_admins_limit: 0,
+                custom_data_limit: 0,
+                nft_sponsor_transfer_timeout: 0,
+                fungible_sponsor_transfer_timeout: 0,
+                refungible_sponsor_transfer_timeout: 0
+            };
+        }: set_chain_limits(RawOrigin::Signed(caller.clone()), limits)
+
+        set_contract_sponsoring_rate_limit {
+            let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
+            let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
+            let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+            let mode: CollectionMode = CollectionMode::NFT;
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+            Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+            let block_number: T::BlockNumber = 0.into();   
+        }: set_contract_sponsoring_rate_limit(RawOrigin::Signed(caller.clone()), caller.clone(), block_number)
+
+        set_collection_limits{
+            let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
+            let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
+            let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+            let mode: CollectionMode = CollectionMode::NFT;
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+            Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+     
+            let cl = CollectionLimits {
+                account_token_ownership_limit: 0,
+                sponsored_data_size: 0,
+                token_limit: 0,
+                sponsor_transfer_timeout: 0
+            };
+
+        }: set_collection_limits(RawOrigin::Signed(caller.clone()), 2, cl)
+
+        add_to_contract_white_list{
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+        }: add_to_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), caller.clone())
+
+        remove_from_contract_white_list{
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+            Nft::<T>::add_to_contract_white_list(RawOrigin::Signed(caller.clone()).into(), caller.clone(), caller.clone())?;
+        }: remove_from_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), caller.clone())
+
+        toggle_contract_white_list{
+            let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+        }: toggle_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), true)
 }
\ No newline at end of file
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -107,19 +107,44 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
-    // fn set_chain_limits() -> Weight {
-    //     (0 as Weight)
-    //         .saturating_add(DbWeight::get().reads(1 as Weight))
-    //         .saturating_add(DbWeight::get().writes(1 as Weight))
-    // }
     fn enable_contract_sponsoring() -> Weight {
         (13_000_000 as Weight)
             .saturating_add(DbWeight::get().reads(1 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
-    // fn set_contract_sponsoring_rate_limit() -> Weight {
-    //     (0 as Weight)
-    //         .saturating_add(DbWeight::get().reads(1 as Weight))
-    //         .saturating_add(DbWeight::get().writes(1 as Weight))
-    // }
+    fn set_schema_version() -> Weight {
+        (8_500_000 as Weight)
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
+    fn set_chain_limits() -> Weight {
+        (1_300_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
+    fn set_contract_sponsoring_rate_limit() -> Weight {
+        (3_500_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }    
+    fn toggle_contract_white_list() -> Weight {
+        (3_000_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }    
+    fn add_to_contract_white_list() -> Weight {
+        (3_000_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }    
+    fn remove_from_contract_white_list() -> Weight {
+        (3_200_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }
+    fn set_collection_limits() -> Weight {
+        (8_900_000 as Weight)
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
 }
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1#![recursion_limit = "1024"]23#![cfg_attr(not(feature = "std"), no_std)]45#[cfg(feature = "std")]6pub use std::*;78#[cfg(feature = "std")]9pub use serde::*;1011use codec::{Decode, Encode};12pub use frame_support::{13    construct_runtime, decl_event, decl_module, decl_storage, decl_error,14    dispatch::DispatchResult,15    ensure, fail, parameter_types,16    traits::{17        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,18        Randomness, WithdrawReason,19    },20    weights::{21        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},22        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,23        WeightToFeePolynomial,24    },25    IsSubType, StorageValue,26};2728use frame_system::{self as system, ensure_signed, ensure_root};29use sp_runtime::sp_std::prelude::Vec;30use sp_runtime::{31    traits::{32        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,33    },34    transaction_validity::{35        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,36    },37    FixedPointOperand, FixedU128,38};39use pallet_contracts::ContractAddressFor;40use sp_runtime::traits::StaticLookup;4142#[cfg(test)]43mod mock;4445#[cfg(test)]46mod tests;4748mod default_weights;4950pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;51pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;52pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5354// Structs55// #region5657pub type CollectionId = u32;58pub type TokenId = u32;59pub type DecimalPoints = u8;6061#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]62#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]63pub enum CollectionMode {64    Invalid,65    NFT,66    // decimal points67    Fungible(DecimalPoints),68    // decimal points69    ReFungible(DecimalPoints),70}7172impl Into<u8> for CollectionMode {73    fn into(self) -> u8 {74        match self {75            CollectionMode::Invalid => 0,76            CollectionMode::NFT => 1,77            CollectionMode::Fungible(_) => 2,78            CollectionMode::ReFungible(_) => 3,79        }80    }81}8283#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]84#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]85pub enum AccessMode {86    Normal,87    WhiteList,88}89impl Default for AccessMode {90    fn default() -> Self {91        Self::Normal92    }93}9495impl Default for CollectionMode {96    fn default() -> Self {97        Self::Invalid98    }99}100101#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum SchemaVersion {104    ImageURL,105    Unique,106}107impl Default for SchemaVersion {108    fn default() -> Self {109        Self::ImageURL110    }111}112113#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]114#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]115pub struct Ownership<AccountId> {116    pub owner: AccountId,117    pub fraction: u128,118}119120#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct CollectionType<AccountId> {123    pub owner: AccountId,124    pub mode: CollectionMode,125    pub access: AccessMode,126    pub decimal_points: DecimalPoints,127    pub name: Vec<u16>,        // 64 include null escape char128    pub description: Vec<u16>, // 256 include null escape char129    pub token_prefix: Vec<u8>, // 16 include null escape char130    pub mint_mode: bool,131    pub offchain_schema: Vec<u8>,132    pub schema_version: SchemaVersion,133    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender134    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship135    pub limits: CollectionLimits, // Collection private restrictions 136    pub variable_on_chain_schema: Vec<u8>, //137    pub const_on_chain_schema: Vec<u8>, //138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct NftItemType<AccountId> {143    pub collection: CollectionId,144    pub owner: AccountId,145    pub const_data: Vec<u8>,146    pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct FungibleItemType<AccountId> {152    pub collection: CollectionId,153    pub owner: AccountId,154    pub value: u128,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct ReFungibleItemType<AccountId> {160    pub collection: CollectionId,161    pub owner: Vec<Ownership<AccountId>>,162    pub const_data: Vec<u8>,163    pub variable_data: Vec<u8>,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct ApprovePermissions<AccountId> {169    pub approved: AccountId,170    pub amount: u128,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct VestingItem<AccountId, Moment> {176    pub sender: AccountId,177    pub recipient: AccountId,178    pub collection_id: CollectionId,179    pub item_id: TokenId,180    pub amount: u64,181    pub vesting_date: Moment,182}183184#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]185#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]186pub struct BasketItem<AccountId, BlockNumber> {187    pub address: AccountId,188    pub start_block: BlockNumber,189}190191#[derive(Encode, Decode, Debug, Clone, PartialEq)]192#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]193pub struct CollectionLimits {194    pub account_token_ownership_limit: u32,195    pub sponsored_data_size: u32,196    pub token_limit: u32,197198    // Timeouts for item types in passed blocks199    pub sponsor_transfer_timeout: u32,200}201202impl Default for CollectionLimits {203    fn default() -> CollectionLimits {204        CollectionLimits { 205            account_token_ownership_limit: 10_000_000, 206            token_limit: u32::max_value(),207            sponsored_data_size: u32::max_value(), 208            sponsor_transfer_timeout: 14400 }209    }210}211212#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214pub struct ChainLimits {215    pub collection_numbers_limit: u32,216    pub account_token_ownership_limit: u32,217    pub collections_admins_limit: u64,218    pub custom_data_limit: u32,219220    // Timeouts for item types in passed blocks221    pub nft_sponsor_transfer_timeout: u32,222    pub fungible_sponsor_transfer_timeout: u32,223    pub refungible_sponsor_transfer_timeout: u32,224}225226pub trait WeightInfo {227	fn create_collection() -> Weight;228	fn destroy_collection() -> Weight;229	fn add_to_white_list() -> Weight;230	fn remove_from_white_list() -> Weight;231    fn set_public_access_mode() -> Weight;232    fn set_mint_permission() -> Weight;233    fn change_collection_owner() -> Weight;234    fn add_collection_admin() -> Weight;235    fn remove_collection_admin() -> Weight;236    fn set_collection_sponsor() -> Weight;237    fn confirm_sponsorship() -> Weight;238    fn remove_collection_sponsor() -> Weight;239    fn create_item(s: usize) -> Weight;240    fn burn_item() -> Weight;241    fn transfer() -> Weight;242    fn approve() -> Weight;243    fn transfer_from() -> Weight;244    fn set_offchain_schema() -> Weight;245    fn set_const_on_chain_schema() -> Weight;246    fn set_variable_on_chain_schema() -> Weight;247    fn set_variable_meta_data() -> Weight;248    fn enable_contract_sponsoring() -> 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}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateReFungibleData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub enum CreateItemData {273    NFT(CreateNftData),274    Fungible(CreateFungibleData),275    ReFungible(CreateReFungibleData),276}277278impl CreateItemData {279    pub fn len(&self) -> usize {280        let len = match self {281            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),282            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),283            _ => 0284        };285        286        return len;287    }288}289290impl From<CreateNftData> for CreateItemData {291    fn from(item: CreateNftData) -> Self {292        CreateItemData::NFT(item)293    }294}295296impl From<CreateReFungibleData> for CreateItemData {297    fn from(item: CreateReFungibleData) -> Self {298        CreateItemData::ReFungible(item)299    }300}301302impl From<CreateFungibleData> for CreateItemData {303    fn from(item: CreateFungibleData) -> Self {304        CreateItemData::Fungible(item)305    }306}307308309decl_error! {310	/// Error for non-fungible-token module.311	pub enum Error for Module<T: Trait> {312        /// Total collections bound exceeded.313        TotalCollectionsLimitExceeded,314		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.315        CollectionDecimalPointLimitExceeded, 316        /// Collection name can not be longer than 63 char.317        CollectionNameLimitExceeded, 318        /// Collection description can not be longer than 255 char.319        CollectionDescriptionLimitExceeded, 320        /// Token prefix can not be longer than 15 char.321        CollectionTokenPrefixLimitExceeded,322        /// This collection does not exist.323        CollectionNotFound,324        /// Item not exists.325        TokenNotFound,326        /// Arithmetic calculation overflow.327        NumOverflow,       328        /// Account already has admin role.329        AlreadyAdmin,  330        /// You do not own this collection.331        NoPermission,332        /// This address is not set as sponsor, use setCollectionSponsor first.333        ConfirmUnsetSponsorFail,334        /// Collection is not in mint mode.335        PublicMintingNotAllowed,336        /// Sender parameter and item owner must be equal.337        MustBeTokenOwner,338        /// Item balance not enough.339        TokenValueTooLow,340        /// Size of item is too large.341        NftSizeLimitExceeded,342        /// No approve found343        ApproveNotFound,344        /// Requested value more than approved.345        TokenValueNotEnough,346        /// Only approved addresses can call this method.347        ApproveRequired,348        /// Address is not in white list.349        AddresNotInWhiteList,350        /// Number of collection admins bound exceeded.351        CollectionAdminsLimitExceeded,352        /// Owned tokens by a single address bound exceeded.353        AddressOwnershipLimitExceeded,354        /// Length of items properties must be greater than 0.355        EmptyArgument,356        /// const_data exceeded data limit.357        TokenConstDataLimitExceeded,358        /// variable_data exceeded data limit.359        TokenVariableDataLimitExceeded,360        /// Not NFT item data used to mint in NFT collection.361        NotNftDataUsedToMintNftCollectionToken,362        /// Not Fungible item data used to mint in Fungible collection.363        NotFungibleDataUsedToMintFungibleCollectionToken,364        /// Not Re Fungible item data used to mint in Re Fungible collection.365        NotReFungibleDataUsedToMintReFungibleCollectionToken,366        /// Unexpected collection type.367        UnexpectedCollectionType,368        /// Can't store metadata in fungible tokens.369        CantStoreMetadataInFungibleTokens,370        /// Collection token limit exceeded371        CollectionTokenLimitExceeded,372        /// Account token limit exceeded per collection373        AccountTokenLimitExceeded,374        /// Collection limit bounds per collection exceeded375        CollectionLimitBoundsExceeded376	}377}378379pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {380    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;381382    /// Weight information for extrinsics in this pallet.383	type WeightInfo: WeightInfo;384}385386#[cfg(feature = "runtime-benchmarks")]387mod benchmarking;388389// #endregion390391decl_storage! {392    trait Store for Module<T: Trait> as Nft {393394        // Private members395        NextCollectionID: CollectionId;396        CreatedCollectionCount: u32;397        ChainVersion: u64;398        ItemListIndex: map hasher(identity) CollectionId => TokenId;399400        // Chain limits struct401        pub ChainLimit get(fn chain_limit) config(): ChainLimits;402403        // Bound counters404        CollectionCount: u32;405        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;406407        // Basic collections408        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;409        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;410        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;411412        /// Balance owner per collection map413        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;414415        /// second parameter: item id + owner account id416        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;417418        /// Item collections419        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;420        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;421        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;422423        /// Index list424        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;425426        /// Tokens transfer baskets427        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;428        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;429        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;430431        // Contract Sponsorship and Ownership432        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;433        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;434        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;435        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;436    }437    add_extra_genesis {438        build(|config: &GenesisConfig<T>| {439            // Modification of storage440            for (_num, _c) in &config.collection {441                <Module<T>>::init_collection(_c);442            }443444            for (_num, _q, _i) in &config.nft_item_id {445                <Module<T>>::init_nft_token(_i);446            }447448            for (_num, _q, _i) in &config.fungible_item_id {449                <Module<T>>::init_fungible_token(_i);450            }451452            for (_num, _q, _i) in &config.refungible_item_id {453                <Module<T>>::init_refungible_token(_i);454            }455        })456    }457}458459decl_event!(460    pub enum Event<T>461    where462        AccountId = <T as system::Trait>::AccountId,463    {464        /// New collection was created465        /// 466        /// # Arguments467        /// 468        /// * collection_id: Globally unique identifier of newly created collection.469        /// 470        /// * mode: [CollectionMode] converted into u8.471        /// 472        /// * account_id: Collection owner.473        Created(CollectionId, u8, AccountId),474475        /// New item was created.476        /// 477        /// # Arguments478        /// 479        /// * collection_id: Id of the collection where item was created.480        /// 481        /// * item_id: Id of an item. Unique within the collection.482        ItemCreated(CollectionId, TokenId),483484        /// Collection item was burned.485        /// 486        /// # Arguments487        /// 488        /// collection_id.489        /// 490        /// item_id: Identifier of burned NFT.491        ItemDestroyed(CollectionId, TokenId),492    }493);494495decl_module! {496    pub struct Module<T: Trait> for enum Call where origin: T::Origin {497498        fn deposit_event() = default;499        type Error = Error<T>;500501        fn on_initialize(now: T::BlockNumber) -> Weight {502503            if ChainVersion::get() < 2504            {505                let value = NextCollectionID::get();506                CreatedCollectionCount::put(value);507                ChainVersion::put(2);508            }509510            0511        }512513        /// 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.514        /// 515        /// # Permissions516        /// 517        /// * Anyone.518        /// 519        /// # Arguments520        /// 521        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.522        /// 523        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.524        /// 525        /// * token_prefix: UTF-8 string with token prefix.526        /// 527        /// * mode: [CollectionMode] collection type and type dependent data.528        // returns collection ID529        #[weight = T::WeightInfo::create_collection()]530        pub fn create_collection(origin,531                                 collection_name: Vec<u16>,532                                 collection_description: Vec<u16>,533                                 token_prefix: Vec<u8>,534                                 mode: CollectionMode) -> DispatchResult {535536            // Anyone can create a collection537            let who = ensure_signed(origin)?;538539            let decimal_points = match mode {540                CollectionMode::Fungible(points) => points,541                CollectionMode::ReFungible(points) => points,542                _ => 0543            };544545            // bound Total number of collections546            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);547548            // check params549            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);550551            let mut name = collection_name.to_vec();552            name.push(0);553            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);554555            let mut description = collection_description.to_vec();556            description.push(0);557            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);558559            let mut prefix = token_prefix.to_vec();560            prefix.push(0);561            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);562563            // Generate next collection ID564            let next_id = CreatedCollectionCount::get()565                .checked_add(1)566                .ok_or(Error::<T>::NumOverflow)?;567568            // bound counter569            let total = CollectionCount::get()570                .checked_add(1)571                .ok_or(Error::<T>::NumOverflow)?;572573            CreatedCollectionCount::put(next_id);574            CollectionCount::put(total);575576            // Create new collection577            let new_collection = CollectionType {578                owner: who.clone(),579                name: name,580                mode: mode.clone(),581                mint_mode: false,582                access: AccessMode::Normal,583                description: description,584                decimal_points: decimal_points,585                token_prefix: prefix,586                offchain_schema: Vec::new(),587                schema_version: SchemaVersion::ImageURL,588                sponsor: T::AccountId::default(),589                unconfirmed_sponsor: T::AccountId::default(),590                variable_on_chain_schema: Vec::new(),591                const_on_chain_schema: Vec::new(),592                limits: CollectionLimits::default(),593            };594595            // Add new collection to map596            <Collection<T>>::insert(next_id, new_collection);597598            // call event599            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));600601            Ok(())602        }603604        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.605        /// 606        /// # Permissions607        /// 608        /// * Collection Owner.609        /// 610        /// # Arguments611        /// 612        /// * collection_id: collection to destroy.613        #[weight = T::WeightInfo::destroy_collection()]614        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {615616            let sender = ensure_signed(origin)?;617            Self::check_owner_permissions(collection_id, sender)?;618619            <AddressTokens<T>>::remove_prefix(collection_id);620            <ApprovedList<T>>::remove_prefix(collection_id);621            <Balance<T>>::remove_prefix(collection_id);622            <ItemListIndex>::remove(collection_id);623            <AdminList<T>>::remove(collection_id);624            <Collection<T>>::remove(collection_id);625            <WhiteList<T>>::remove(collection_id);626627            <NftItemList<T>>::remove_prefix(collection_id);628            <FungibleItemList<T>>::remove_prefix(collection_id);629            <ReFungibleItemList<T>>::remove_prefix(collection_id);630631            <NftTransferBasket<T>>::remove_prefix(collection_id);632            <FungibleTransferBasket<T>>::remove_prefix(collection_id);633            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);634635            if CollectionCount::get() > 0636            {637                // bound couter638                let total = CollectionCount::get()639                    .checked_sub(1)640                    .ok_or(Error::<T>::NumOverflow)?;641642                CollectionCount::put(total);643            }644645            Ok(())646        }647648        /// Add an address to white list.649        /// 650        /// # Permissions651        /// 652        /// * Collection Owner653        /// * Collection Admin654        /// 655        /// # Arguments656        /// 657        /// * collection_id.658        /// 659        /// * address.660        #[weight = T::WeightInfo::add_to_white_list()]661        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{662663            let sender = ensure_signed(origin)?;664            Self::check_owner_or_admin_permissions(collection_id, sender)?;665666            let mut white_list_collection: Vec<T::AccountId>;667            if <WhiteList<T>>::contains_key(collection_id) {668                white_list_collection = <WhiteList<T>>::get(collection_id);669                if !white_list_collection.contains(&address.clone())670                {671                    white_list_collection.push(address.clone());672                }673            }674            else {675                white_list_collection = Vec::new();676                white_list_collection.push(address.clone());677            }678679            <WhiteList<T>>::insert(collection_id, white_list_collection);680            Ok(())681        }682683        /// Remove an address from white list.684        /// 685        /// # Permissions686        /// 687        /// * Collection Owner688        /// * Collection Admin689        /// 690        /// # Arguments691        /// 692        /// * collection_id.693        /// 694        /// * address.695        #[weight = T::WeightInfo::remove_from_white_list()]696        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{697698            let sender = ensure_signed(origin)?;699            Self::check_owner_or_admin_permissions(collection_id, sender)?;700701            if <WhiteList<T>>::contains_key(collection_id) {702                let mut white_list_collection = <WhiteList<T>>::get(collection_id);703                if white_list_collection.contains(&address.clone())704                {705                    white_list_collection.retain(|i| *i != address.clone());706                    <WhiteList<T>>::insert(collection_id, white_list_collection);707                }708            }709710            Ok(())711        }712713        /// Toggle between normal and white list access for the methods with access for `Anyone`.714        /// 715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// 719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * mode: [AccessMode]724        #[weight = T::WeightInfo::set_public_access_mode()]725        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult726        {727            let sender = ensure_signed(origin)?;728729            Self::check_owner_permissions(collection_id, sender)?;730            let mut target_collection = <Collection<T>>::get(collection_id);731            target_collection.access = mode;732            <Collection<T>>::insert(collection_id, target_collection);733734            Ok(())735        }736737        /// Allows Anyone to create tokens if:738        /// * White List is enabled, and739        /// * Address is added to white list, and740        /// * This method was called with True parameter741        /// 742        /// # Permissions743        /// * Collection Owner744        ///745        /// # Arguments746        /// 747        /// * collection_id.748        /// 749        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.750        #[weight = T::WeightInfo::set_mint_permission()]751        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult752        {753            let sender = ensure_signed(origin)?;754755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id);757            target_collection.mint_mode = mint_permission;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Change the owner of the collection.764        /// 765        /// # Permissions766        /// 767        /// * Collection Owner.768        /// 769        /// # Arguments770        /// 771        /// * collection_id.772        /// 773        /// * new_owner.774        #[weight = T::WeightInfo::change_collection_owner()]775        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {776777            let sender = ensure_signed(origin)?;778            Self::check_owner_permissions(collection_id, sender)?;779            let mut target_collection = <Collection<T>>::get(collection_id);780            target_collection.owner = new_owner;781            <Collection<T>>::insert(collection_id, target_collection);782783            Ok(())784        }785786        /// Adds an admin of the Collection.787        /// 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. 788        /// 789        /// # Permissions790        /// 791        /// * Collection Owner.792        /// * Collection Admin.793        /// 794        /// # Arguments795        /// 796        /// * collection_id: ID of the Collection to add admin for.797        /// 798        /// * new_admin_id: Address of new admin to add.799        #[weight = T::WeightInfo::add_collection_admin()]800        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {801802            let sender = ensure_signed(origin)?;803            Self::check_owner_or_admin_permissions(collection_id, sender)?;804            let mut admin_arr: Vec<T::AccountId> = Vec::new();805806            if <AdminList<T>>::contains_key(collection_id)807            {808                admin_arr = <AdminList<T>>::get(collection_id);809                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);810            }811812            // Number of collection admins813            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);814815            admin_arr.push(new_admin_id);816            <AdminList<T>>::insert(collection_id, admin_arr);817818            Ok(())819        }820821        /// 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.822        ///823        /// # Permissions824        /// 825        /// * Collection Owner.826        /// * Collection Admin.827        /// 828        /// # Arguments829        /// 830        /// * collection_id: ID of the Collection to remove admin for.831        /// 832        /// * account_id: Address of admin to remove.833        #[weight = T::WeightInfo::remove_collection_admin()]834        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {835836            let sender = ensure_signed(origin)?;837            Self::check_owner_or_admin_permissions(collection_id, sender)?;838839            if <AdminList<T>>::contains_key(collection_id)840            {841                let mut admin_arr = <AdminList<T>>::get(collection_id);842                admin_arr.retain(|i| *i != account_id);843                <AdminList<T>>::insert(collection_id, admin_arr);844            }845846            Ok(())847        }848849        /// # Permissions850        /// 851        /// * Collection Owner852        /// 853        /// # Arguments854        /// 855        /// * collection_id.856        /// 857        /// * new_sponsor.858        #[weight = T::WeightInfo::set_collection_sponsor()]859        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {860861            let sender = ensure_signed(origin)?;862            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);863864            let mut target_collection = <Collection<T>>::get(collection_id);865            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);866867            target_collection.unconfirmed_sponsor = new_sponsor;868            <Collection<T>>::insert(collection_id, target_collection);869870            Ok(())871        }872873        /// # Permissions874        /// 875        /// * Sponsor.876        /// 877        /// # Arguments878        /// 879        /// * collection_id.880        #[weight = T::WeightInfo::confirm_sponsorship()]881        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {882883            let sender = ensure_signed(origin)?;884            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);885886            let mut target_collection = <Collection<T>>::get(collection_id);887            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);888889            target_collection.sponsor = target_collection.unconfirmed_sponsor;890            target_collection.unconfirmed_sponsor = T::AccountId::default();891            <Collection<T>>::insert(collection_id, target_collection);892893            Ok(())894        }895896        /// Switch back to pay-per-own-transaction model.897        ///898        /// # Permissions899        ///900        /// * Collection owner.901        /// 902        /// # Arguments903        /// 904        /// * collection_id.905        #[weight = T::WeightInfo::remove_collection_sponsor()]906        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {907908            let sender = ensure_signed(origin)?;909            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);910911            let mut target_collection = <Collection<T>>::get(collection_id);912            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);913914            target_collection.sponsor = T::AccountId::default();915            <Collection<T>>::insert(collection_id, target_collection);916917            Ok(())918        }919920        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.921        /// 922        /// # Permissions923        /// 924        /// * Collection Owner.925        /// * Collection Admin.926        /// * Anyone if927        ///     * White List is enabled, and928        ///     * Address is added to white list, and929        ///     * MintPermission is enabled (see SetMintPermission method)930        /// 931        /// # Arguments932        /// 933        /// * collection_id: ID of the collection.934        /// 935        /// * owner: Address, initial owner of the NFT.936        ///937        /// * data: Token data to store on chain.938        // #[weight =939        // (130_000_000 as Weight)940        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))941        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))942        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]943944        #[weight = T::WeightInfo::create_item(data.len())]945        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {946947            let sender = ensure_signed(origin)?;948949            Self::collection_exists(collection_id)?;950951            let target_collection = <Collection<T>>::get(collection_id);952953            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;954            Self::validate_create_item_args(&target_collection, &data)?;955            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;956957            Ok(())958        }959960        /// This method creates multiple instances of NFT Collection created with CreateCollection method.961        /// 962        /// # Permissions963        /// 964        /// * Collection Owner.965        /// * Collection Admin.966        /// * Anyone if967        ///     * White List is enabled, and968        ///     * Address is added to white list, and969        ///     * MintPermission is enabled (see SetMintPermission method)970        /// 971        /// # Arguments972        /// 973        /// * collection_id: ID of the collection.974        /// 975        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].976        /// 977        /// * owner: Address, initial owner of the NFT.978        #[weight = T::WeightInfo::create_item(items_data.into_iter()979                               .map(|data| { data.len() })980                               .sum())]981        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {982983            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);984            let sender = ensure_signed(origin)?;985986            Self::collection_exists(collection_id)?;987            let target_collection = <Collection<T>>::get(collection_id);988989            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;990991            for data in &items_data {992                Self::validate_create_item_args(&target_collection, data)?;993            }994            for data in &items_data {995                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;996            }997998            Ok(())999        }10001001        /// Destroys a concrete instance of NFT.1002        /// 1003        /// # Permissions1004        /// 1005        /// * Collection Owner.1006        /// * Collection Admin.1007        /// * Current NFT Owner.1008        /// 1009        /// # Arguments1010        /// 1011        /// * collection_id: ID of the collection.1012        /// 1013        /// * item_id: ID of NFT to burn.1014        #[weight = T::WeightInfo::burn_item()]1015        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;1018            Self::collection_exists(collection_id)?;10191020            // Transfer permissions check1021            let target_collection = <Collection<T>>::get(collection_id);1022            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1023                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1024                Error::<T>::NoPermission);10251026            if target_collection.access == AccessMode::WhiteList {1027                Self::check_white_list(collection_id, &sender)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1033                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1034                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1035                _ => ()1036            };10371038            // call event1039            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10401041            Ok(())1042        }10431044        /// Change ownership of the token.1045        /// 1046        /// # Permissions1047        /// 1048        /// * Collection Owner1049        /// * Collection Admin1050        /// * Current NFT owner1051        ///1052        /// # Arguments1053        /// 1054        /// * recipient: Address of token recipient.1055        /// 1056        /// * collection_id.1057        /// 1058        /// * item_id: ID of the item1059        ///     * Non-Fungible Mode: Required.1060        ///     * Fungible Mode: Ignored.1061        ///     * Re-Fungible Mode: Required.1062        /// 1063        /// * value: Amount to transfer.1064        ///     * Non-Fungible Mode: Ignored1065        ///     * Fungible Mode: Must specify transferred amount1066        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1067        #[weight = T::WeightInfo::transfer()]1068        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10691070            let sender = ensure_signed(origin)?;1071            let target_collection = <Collection<T>>::get(collection_id);10721073            // Limits check1074            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10751076            // Transfer permissions check1077            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1078                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1079                Error::<T>::NoPermission);10801081            if target_collection.access == AccessMode::WhiteList {1082                Self::check_white_list(collection_id, &sender)?;1083                Self::check_white_list(collection_id, &recipient)?;1084            }10851086            match target_collection.mode1087            {1088                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1089                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1090                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1091                _ => ()1092            };10931094            Ok(())1095        }10961097        /// Set, change, or remove approved address to transfer the ownership of the NFT.1098        /// 1099        /// # Permissions1100        /// 1101        /// * Collection Owner1102        /// * Collection Admin1103        /// * Current NFT owner1104        /// 1105        /// # Arguments1106        /// 1107        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1108        /// 1109        /// * collection_id.1110        /// 1111        /// * item_id: ID of the item.1112        #[weight = T::WeightInfo::approve()]1113        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11141115            let sender = ensure_signed(origin)?;11161117            // Transfer permissions check1118            let target_collection = <Collection<T>>::get(collection_id);1119            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1120                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1121                Error::<T>::NoPermission);11221123            if target_collection.access == AccessMode::WhiteList {1124                Self::check_white_list(collection_id, &sender)?;1125                Self::check_white_list(collection_id, &approved)?;1126            }11271128            // amount param stub1129            let amount = 100000000;11301131            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1132            if list_exists {11331134                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1135                let item_contains = list.iter().any(|i| i.approved == approved);11361137                if !item_contains {1138                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1139                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1140                }1141            } else {11421143                let mut list = Vec::new();1144                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1145                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1146            }11471148            Ok(())1149        }1150        1151        /// 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.1152        /// 1153        /// # Permissions1154        /// * Collection Owner1155        /// * Collection Admin1156        /// * Current NFT owner1157        /// * Address approved by current NFT owner1158        /// 1159        /// # Arguments1160        /// 1161        /// * from: Address that owns token.1162        /// 1163        /// * recipient: Address of token recipient.1164        /// 1165        /// * collection_id.1166        /// 1167        /// * item_id: ID of the item.1168        /// 1169        /// * value: Amount to transfer.1170        #[weight = T::WeightInfo::transfer_from()]1171        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11721173            let sender = ensure_signed(origin)?;1174            let mut appoved_transfer = false;11751176            // Check approve1177            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1178                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1179                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1180                if opt_item.is_some()1181                {1182                    appoved_transfer = true;1183                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1184                }1185            }11861187            let target_collection = <Collection<T>>::get(collection_id);11881189            // Limits check1190            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11911192            // Transfer permissions check         1193            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1194            Error::<T>::NoPermission);11951196            if target_collection.access == AccessMode::WhiteList {1197                Self::check_white_list(collection_id, &sender)?;1198                Self::check_white_list(collection_id, &recipient)?;1199            }12001201            // remove approve1202            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1203                .into_iter().filter(|i| i.approved != sender.clone()).collect();1204            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);120512061207            match target_collection.mode1208            {1209                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1210                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1211                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1212                _ => ()1213            };12141215            Ok(())1216        }12171218        #[weight = 0]1219        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12201221            // let no_perm_mes = "You do not have permissions to modify this collection";1222            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1223            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1224            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12251226            // // on_nft_received  call12271228            // Self::transfer(origin, collection_id, item_id, new_owner)?;12291230            Ok(())1231        }12321233        /// Set off-chain data schema.1234        /// 1235        /// # Permissions1236        /// 1237        /// * Collection Owner1238        /// * Collection Admin1239        /// 1240        /// # Arguments1241        /// 1242        /// * collection_id.1243        /// 1244        /// * schema: String representing the offchain data schema.1245        #[weight = T::WeightInfo::set_variable_meta_data()]1246        pub fn set_variable_meta_data (1247            origin,1248            collection_id: CollectionId,1249            item_id: TokenId,1250            data: Vec<u8>1251        ) -> DispatchResult {1252            let sender = ensure_signed(origin)?;1253            1254            Self::collection_exists(collection_id)?;1255            1256            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12571258            // Modify permissions check1259            let target_collection = <Collection<T>>::get(collection_id);1260            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1261                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1262                Error::<T>::NoPermission);12631264            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12651266            match target_collection.mode1267            {1268                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1269                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1270                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1271                _ => fail!(Error::<T>::UnexpectedCollectionType)1272            };12731274            Ok(())1275        }1276 1277        /// Set schema standard1278        /// ImageURL1279        /// Unique1280        /// 1281        /// # Permissions1282        /// 1283        /// * Collection Owner1284        /// * Collection Admin1285        /// 1286        /// # Arguments1287        /// 1288        /// * collection_id.1289        /// 1290        /// * schema: SchemaVersion: enum1291        #[weight = 0]1292        pub fn set_schema_version(1293            origin,1294            collection_id: CollectionId,1295            version: SchemaVersion1296        ) -> DispatchResult {1297            let sender = ensure_signed(origin)?;1298            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1299            let mut target_collection = <Collection<T>>::get(collection_id);1300            target_collection.schema_version = version;1301            <Collection<T>>::insert(collection_id, target_collection);13021303            Ok(())1304        }13051306        /// Set off-chain data schema.1307        /// 1308        /// # Permissions1309        /// 1310        /// * Collection Owner1311        /// * Collection Admin1312        /// 1313        /// # Arguments1314        /// 1315        /// * collection_id.1316        /// 1317        /// * schema: String representing the offchain data schema.1318        #[weight = T::WeightInfo::set_offchain_schema()]1319        pub fn set_offchain_schema(1320            origin,1321            collection_id: CollectionId,1322            schema: Vec<u8>1323        ) -> DispatchResult {1324            let sender = ensure_signed(origin)?;1325            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13261327            let mut target_collection = <Collection<T>>::get(collection_id);1328            target_collection.offchain_schema = schema;1329            <Collection<T>>::insert(collection_id, target_collection);13301331            Ok(())1332        }13331334        /// Set const on-chain data schema.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Collection Owner1339        /// * Collection Admin1340        /// 1341        /// # Arguments1342        /// 1343        /// * collection_id.1344        /// 1345        /// * schema: String representing the const on-chain data schema.1346        #[weight = T::WeightInfo::set_const_on_chain_schema()]1347        pub fn set_const_on_chain_schema (1348            origin,1349            collection_id: CollectionId,1350            schema: Vec<u8>1351        ) -> DispatchResult {1352            let sender = ensure_signed(origin)?;1353            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13541355            let mut target_collection = <Collection<T>>::get(collection_id);1356            target_collection.const_on_chain_schema = schema;1357            <Collection<T>>::insert(collection_id, target_collection);13581359            Ok(())1360        }13611362        /// Set variable on-chain data schema.1363        /// 1364        /// # Permissions1365        /// 1366        /// * Collection Owner1367        /// * Collection Admin1368        /// 1369        /// # Arguments1370        /// 1371        /// * collection_id.1372        /// 1373        /// * schema: String representing the variable on-chain data schema.1374        #[weight = T::WeightInfo::set_const_on_chain_schema()]1375        pub fn set_variable_on_chain_schema (1376            origin,1377            collection_id: CollectionId,1378            schema: Vec<u8>1379        ) -> DispatchResult {1380            let sender = ensure_signed(origin)?;1381            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13821383            let mut target_collection = <Collection<T>>::get(collection_id);1384            target_collection.variable_on_chain_schema = schema;1385            <Collection<T>>::insert(collection_id, target_collection);13861387            Ok(())1388        }13891390        // Sudo permissions function1391        #[weight = 0]1392        pub fn set_chain_limits(1393            origin,1394            limits: ChainLimits1395        ) -> DispatchResult {1396            ensure_root(origin)?;1397            <ChainLimit>::put(limits);1398            Ok(())1399        }14001401        /// Enable smart contract self-sponsoring.1402        /// 1403        /// # Permissions1404        /// 1405        /// * Contract Owner1406        /// 1407        /// # Arguments1408        /// 1409        /// * contract address1410        /// * enable flag1411        /// 1412        #[weight = T::WeightInfo::enable_contract_sponsoring()]1413        pub fn enable_contract_sponsoring(1414            origin,1415            contract_address: T::AccountId,1416            enable: bool1417        ) -> DispatchResult {14181419            let sender = ensure_signed(origin)?;14201421            #[cfg(feature = "runtime-benchmarks")]1422            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14231424            let mut is_owner = false;1425            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1426                let owner = <ContractOwner<T>>::get(&contract_address);1427                is_owner = sender == owner;1428            }1429            ensure!(is_owner, Error::<T>::NoPermission);14301431            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1432            Ok(())1433        }14341435        /// Set the rate limit for contract sponsoring to specified number of blocks.1436        /// 1437        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1438        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1439        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1440        /// from contract endowment if there are at least B blocks between such transactions. 1441        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1442        /// 1443        /// # Permissions1444        /// 1445        /// * Contract Owner1446        /// 1447        /// # Arguments1448        /// 1449        /// -`contract_address`: Address of the contract to sponsor1450        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1451        /// 1452        #[weight = 0]1453        pub fn set_contract_sponsoring_rate_limit(1454            origin,1455            contract_address: T::AccountId,1456            rate_limit: T::BlockNumber1457        ) -> DispatchResult {1458            let sender = ensure_signed(origin)?;1459            let mut is_owner = false;1460            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1461                let owner = <ContractOwner<T>>::get(&contract_address);1462                is_owner = sender == owner;1463            }1464            ensure!(is_owner, Error::<T>::NoPermission);14651466            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1467            Ok(())1468        }14691470        #[weight = 0]1471        pub fn set_collection_limits(1472            origin,1473            collection_id: u32,1474            limits: CollectionLimits,1475        ) -> DispatchResult {1476            let sender = ensure_signed(origin)?;1477            Self::check_owner_permissions(collection_id, sender.clone())?;1478            let mut target_collection = <Collection<T>>::get(collection_id);1479            let chain_limits = ChainLimit::get();1480            let climits = target_collection.limits;14811482            // collection bounds1483            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1484                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1485                Error::<T>::CollectionLimitBoundsExceeded);14861487            // token_limit   check  prev1488            ensure!(climits.token_limit > limits.token_limit && 1489                limits.token_limit <= chain_limits.account_token_ownership_limit, 1490                Error::<T>::AccountTokenLimitExceeded);14911492            target_collection.limits = limits;1493            <Collection<T>>::insert(collection_id, target_collection);14941495            Ok(())1496        } 1497    }1498}14991500impl<T: Trait> Module<T> {15011502    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15031504        // check token limit and account token limit1505        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1506        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1507        1508        Ok(())1509    }15101511    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15121513        // check token limit and account token limit1514        let total_items: u32 = ItemListIndex::get(collection_id);1515        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1516        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1517        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15181519        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1520            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1521            Self::check_white_list(collection_id, owner)?;1522            Self::check_white_list(collection_id, sender)?;1523        }15241525        Ok(())1526    }15271528    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1529        match target_collection.mode1530        {1531            CollectionMode::NFT => {1532                if let CreateItemData::NFT(data) = data {1533                    // check sizes1534                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1535                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1536                } else {1537                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1538                }1539            },1540            CollectionMode::Fungible(_) => {1541                if let CreateItemData::Fungible(_) = data {1542                } else {1543                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1544                }1545            },1546            CollectionMode::ReFungible(_) => {1547                if let CreateItemData::ReFungible(data) = data {15481549                    // check sizes1550                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1551                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1552                } else {1553                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1554                }1555            },1556            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1557        };15581559        Ok(())1560    }15611562    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1563        match data1564        {1565            CreateItemData::NFT(data) => {1566                let item = NftItemType {1567                    collection: collection_id,1568                    owner,1569                    const_data: data.const_data,1570                    variable_data: data.variable_data1571                };15721573                Self::add_nft_item(item)?;1574            },1575            CreateItemData::Fungible(_) => {1576                let item = FungibleItemType {1577                    collection: collection_id,1578                    owner,1579                    value: (10 as u128).pow(collection.decimal_points as u32)1580                };15811582                Self::add_fungible_item(item)?;1583            },1584            CreateItemData::ReFungible(data) => {1585                let mut owner_list = Vec::new();1586                let value = (10 as u128).pow(collection.decimal_points as u32);1587                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15881589                let item = ReFungibleItemType {1590                    collection: collection_id,1591                    owner: owner_list,1592                    const_data: data.const_data,1593                    variable_data: data.variable_data1594                };15951596                Self::add_refungible_item(item)?;1597            }1598        };15991600        // call event1601        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16021603        Ok(())1604    }16051606    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1607        let current_index = <ItemListIndex>::get(item.collection)1608            .checked_add(1)1609            .ok_or(Error::<T>::NumOverflow)?;1610        let itemcopy = item.clone();1611        let owner = item.owner.clone();16121613        Self::add_token_index(item.collection, current_index, owner.clone())?;16141615        <ItemListIndex>::insert(item.collection, current_index);1616        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16171618        // Add current block1619        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1620        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1621        1622        // Update balance1623        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1624            .checked_add(item.value)1625            .ok_or(Error::<T>::NumOverflow)?;1626        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16271628        Ok(())1629    }16301631    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1632        let current_index = <ItemListIndex>::get(item.collection)1633            .checked_add(1)1634            .ok_or(Error::<T>::NumOverflow)?;1635        let itemcopy = item.clone();16361637        let value = item.owner.first().unwrap().fraction;1638        let owner = item.owner.first().unwrap().owner.clone();16391640        Self::add_token_index(item.collection, current_index, owner.clone())?;16411642        <ItemListIndex>::insert(item.collection, current_index);1643        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16441645        // Add current block1646        let block_number: T::BlockNumber = 0.into();1647        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16481649        // Update balance1650        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1651            .checked_add(value)1652            .ok_or(Error::<T>::NumOverflow)?;1653        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16541655        Ok(())1656    }16571658    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1659        let current_index = <ItemListIndex>::get(item.collection)1660            .checked_add(1)1661            .ok_or(Error::<T>::NumOverflow)?;16621663        let item_owner = item.owner.clone();1664        let collection_id = item.collection.clone();1665        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16661667        <ItemListIndex>::insert(collection_id, current_index);1668        <NftItemList<T>>::insert(collection_id, current_index, item);16691670        // Add current block1671        let block_number: T::BlockNumber = 0.into();1672        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16731674        // Update balance1675        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1676            .checked_add(1)1677            .ok_or(Error::<T>::NumOverflow)?;1678        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16791680        Ok(())1681    }16821683    fn burn_refungible_item(1684        collection_id: CollectionId,1685        item_id: TokenId,1686        owner: T::AccountId,1687    ) -> DispatchResult {1688        ensure!(1689            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1690            Error::<T>::TokenNotFound1691        );1692        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1693        let item = collection1694            .owner1695            .iter()1696            .filter(|&i| i.owner == owner)1697            .next()1698            .unwrap();1699        Self::remove_token_index(collection_id, item_id, owner.clone())?;17001701        // remove approve list1702        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17031704        // update balance1705        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1706            .checked_sub(item.fraction)1707            .ok_or(Error::<T>::NumOverflow)?;1708        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17091710        <ReFungibleItemList<T>>::remove(collection_id, item_id);17111712        Ok(())1713    }17141715    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1716        ensure!(1717            <NftItemList<T>>::contains_key(collection_id, item_id),1718            Error::<T>::TokenNotFound1719        );1720        let item = <NftItemList<T>>::get(collection_id, item_id);1721        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17221723        // remove approve list1724        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17251726        // update balance1727        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1728            .checked_sub(1)1729            .ok_or(Error::<T>::NumOverflow)?;1730        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1731        <NftItemList<T>>::remove(collection_id, item_id);17321733        Ok(())1734    }17351736    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1737        ensure!(1738            <FungibleItemList<T>>::contains_key(collection_id, item_id),1739            Error::<T>::TokenNotFound1740        );1741        let item = <FungibleItemList<T>>::get(collection_id, item_id);1742        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17431744        // remove approve list1745        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17461747        // update balance1748        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1749            .checked_sub(item.value)1750            .ok_or(Error::<T>::NumOverflow)?;1751        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17521753        <FungibleItemList<T>>::remove(collection_id, item_id);17541755        Ok(())1756    }17571758    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1759        ensure!(1760            <Collection<T>>::contains_key(collection_id),1761            Error::<T>::CollectionNotFound1762        );1763        Ok(())1764    }17651766    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1767        Self::collection_exists(collection_id)?;17681769        let target_collection = <Collection<T>>::get(collection_id);1770        ensure!(1771            subject == target_collection.owner,1772            Error::<T>::NoPermission1773        );17741775        Ok(())1776    }17771778    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1779        let target_collection = <Collection<T>>::get(collection_id);1780        let mut result: bool = subject == target_collection.owner;1781        let exists = <AdminList<T>>::contains_key(collection_id);17821783        if !result & exists {1784            if <AdminList<T>>::get(collection_id).contains(&subject) {1785                result = true1786            }1787        }17881789        result1790    }17911792    fn check_owner_or_admin_permissions(1793        collection_id: CollectionId,1794        subject: T::AccountId,1795    ) -> DispatchResult {1796        Self::collection_exists(collection_id)?;1797        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17981799        ensure!(1800            result,1801            Error::<T>::NoPermission1802        );1803        Ok(())1804    }18051806    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1807        let target_collection = <Collection<T>>::get(collection_id);18081809        match target_collection.mode {1810            CollectionMode::NFT => {1811                <NftItemList<T>>::get(collection_id, item_id).owner == subject1812            }1813            CollectionMode::Fungible(_) => {1814                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1815            }1816            CollectionMode::ReFungible(_) => {1817                <ReFungibleItemList<T>>::get(collection_id, item_id)1818                    .owner1819                    .iter()1820                    .any(|i| i.owner == subject)1821            }1822            CollectionMode::Invalid => false,1823        }1824    }18251826    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1827        let mes = Error::<T>::AddresNotInWhiteList;1828        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1829        let wl = <WhiteList<T>>::get(collection_id);1830        ensure!(wl.contains(address), mes);18311832        Ok(())1833    }18341835    fn transfer_fungible(1836        collection_id: CollectionId,1837        item_id: TokenId,1838        value: u128,1839        owner: T::AccountId,1840        new_owner: T::AccountId,1841    ) -> DispatchResult {1842        ensure!(1843            <FungibleItemList<T>>::contains_key(collection_id, item_id),1844            Error::<T>::TokenNotFound1845        );18461847        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1848        let amount = full_item.value;18491850        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18511852        // update balance1853        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1854            .checked_sub(value)1855            .ok_or(Error::<T>::NumOverflow)?;1856        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18571858        let mut new_owner_account_id = 0;1859        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1860        if new_owner_items.len() > 0 {1861            new_owner_account_id = new_owner_items[0];1862        }18631864        // transfer1865        if amount == value && new_owner_account_id == 0 {1866            // change owner1867            // new owner do not have account1868            let mut new_full_item = full_item.clone();1869            new_full_item.owner = new_owner.clone();1870            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18711872            // update balance1873            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1874                .checked_add(value)1875                .ok_or(Error::<T>::NumOverflow)?;1876            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18771878            // update index collection1879            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1880        } else {1881            let mut new_full_item = full_item.clone();1882            new_full_item.value -= value;18831884            // separate amount1885            if new_owner_account_id > 0 {1886                // new owner has account1887                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1888                item.value += value;18891890                // update balance1891                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1892                    .checked_add(value)1893                    .ok_or(Error::<T>::NumOverflow)?;1894                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18951896                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1897            } else {1898                // new owner do not have account1899                let item = FungibleItemType {1900                    collection: collection_id,1901                    owner: new_owner.clone(),1902                    value1903                };19041905                Self::add_fungible_item(item)?;1906            }19071908            if amount == value {1909                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19101911                // remove approve list1912                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1913                <FungibleItemList<T>>::remove(collection_id, item_id);1914            }19151916            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1917        }19181919        Ok(())1920    }19211922    fn transfer_refungible(1923        collection_id: CollectionId,1924        item_id: TokenId,1925        value: u128,1926        owner: T::AccountId,1927        new_owner: T::AccountId,1928    ) -> DispatchResult {1929        ensure!(1930            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1931            Error::<T>::TokenNotFound1932        );19331934        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1935        let item = full_item1936            .owner1937            .iter()1938            .filter(|i| i.owner == owner)1939            .next()1940            .ok_or(Error::<T>::NumOverflow)?;1941        let amount = item.fraction;19421943        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19441945        // update balance1946        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1947            .checked_sub(value)1948            .ok_or(Error::<T>::NumOverflow)?;1949        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19501951        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1952            .checked_add(value)1953            .ok_or(Error::<T>::NumOverflow)?;1954        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19551956        let old_owner = item.owner.clone();1957        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19581959        // transfer1960        if amount == value && !new_owner_has_account {1961            // change owner1962            // new owner do not have account1963            let mut new_full_item = full_item.clone();1964            new_full_item1965                .owner1966                .iter_mut()1967                .find(|i| i.owner == owner)1968                .unwrap()1969                .owner = new_owner.clone();1970            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19711972            // update index collection1973            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1974        } else {1975            let mut new_full_item = full_item.clone();1976            new_full_item1977                .owner1978                .iter_mut()1979                .find(|i| i.owner == owner)1980                .unwrap()1981                .fraction -= value;19821983            // separate amount1984            if new_owner_has_account {1985                // new owner has account1986                new_full_item1987                    .owner1988                    .iter_mut()1989                    .find(|i| i.owner == new_owner)1990                    .unwrap()1991                    .fraction += value;1992            } else {1993                // new owner do not have account1994                new_full_item.owner.push(Ownership {1995                    owner: new_owner.clone(),1996                    fraction: value,1997                });1998                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1999            }20002001            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2002        }20032004        Ok(())2005    }20062007    fn transfer_nft(2008        collection_id: CollectionId,2009        item_id: TokenId,2010        sender: T::AccountId,2011        new_owner: T::AccountId,2012    ) -> DispatchResult {2013        ensure!(2014            <NftItemList<T>>::contains_key(collection_id, item_id),2015            Error::<T>::TokenNotFound2016        );20172018        let mut item = <NftItemList<T>>::get(collection_id, item_id);20192020        ensure!(2021            sender == item.owner,2022            Error::<T>::MustBeTokenOwner2023        );20242025        // update balance2026        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2027            .checked_sub(1)2028            .ok_or(Error::<T>::NumOverflow)?;2029        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20302031        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2032            .checked_add(1)2033            .ok_or(Error::<T>::NumOverflow)?;2034        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20352036        // change owner2037        let old_owner = item.owner.clone();2038        item.owner = new_owner.clone();2039        <NftItemList<T>>::insert(collection_id, item_id, item);20402041        // update index collection2042        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20432044        // reset approved list2045        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2046        Ok(())2047    }2048    2049    fn item_exists(2050        collection_id: CollectionId,2051        item_id: TokenId,2052        mode: &CollectionMode2053    ) -> DispatchResult {2054        match mode {2055            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2056            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2057            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2058            _ => ()2059        };2060        2061        Ok(())2062    }20632064    fn set_re_fungible_variable_data(2065        collection_id: CollectionId,2066        item_id: TokenId,2067        data: Vec<u8>2068    ) -> DispatchResult {2069        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20702071        item.variable_data = data;20722073        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20742075        Ok(())2076    }20772078    fn set_nft_variable_data(2079        collection_id: CollectionId,2080        item_id: TokenId,2081        data: Vec<u8>2082    ) -> DispatchResult {2083        let mut item = <NftItemList<T>>::get(collection_id, item_id);2084        2085        item.variable_data = data;20862087        <NftItemList<T>>::insert(collection_id, item_id, item);2088        2089        Ok(())2090    }20912092    fn init_collection(item: &CollectionType<T::AccountId>) {2093        // check params2094        assert!(2095            item.decimal_points <= MAX_DECIMAL_POINTS,2096            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2097        );2098        assert!(2099            item.name.len() <= 64,2100            "Collection name can not be longer than 63 char"2101        );2102        assert!(2103            item.name.len() <= 256,2104            "Collection description can not be longer than 255 char"2105        );2106        assert!(2107            item.token_prefix.len() <= 16,2108            "Token prefix can not be longer than 15 char"2109        );21102111        // Generate next collection ID2112        let next_id = CreatedCollectionCount::get()2113            .checked_add(1)2114            .unwrap();21152116        CreatedCollectionCount::put(next_id);2117    }21182119    fn init_nft_token(item: &NftItemType<T::AccountId>) {2120        let current_index = <ItemListIndex>::get(item.collection)2121            .checked_add(1)2122            .unwrap();21232124        let item_owner = item.owner.clone();2125        let collection_id = item.collection.clone();2126        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21272128        <ItemListIndex>::insert(collection_id, current_index);21292130        // Update balance2131        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2132            .checked_add(1)2133            .unwrap();2134        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2135    }21362137    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2138        let current_index = <ItemListIndex>::get(item.collection)2139            .checked_add(1)2140            .unwrap();2141        let owner = item.owner.clone();21422143        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21442145        <ItemListIndex>::insert(item.collection, current_index);21462147        // Update balance2148        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2149            .checked_add(item.value)2150            .unwrap();2151        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2152    }21532154    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2155        let current_index = <ItemListIndex>::get(item.collection)2156            .checked_add(1)2157            .unwrap();21582159        let value = item.owner.first().unwrap().fraction;2160        let owner = item.owner.first().unwrap().owner.clone();21612162        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21632164        <ItemListIndex>::insert(item.collection, current_index);21652166        // Update balance2167        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2168            .checked_add(value)2169            .unwrap();2170        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2171    }21722173    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21742175        // add to account limit2176        if <AccountItemCount<T>>::contains_key(owner.clone()) {21772178            // bound Owned tokens by a single address2179            let count = <AccountItemCount<T>>::get(owner.clone());2180            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21812182            <AccountItemCount<T>>::insert(owner.clone(), count2183                .checked_add(1)2184                .ok_or(Error::<T>::NumOverflow)?);2185        }2186        else {2187            <AccountItemCount<T>>::insert(owner.clone(), 1);2188        }21892190        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2191        if list_exists {2192            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2193            let item_contains = list.contains(&item_index.clone());21942195            if !item_contains {2196                list.push(item_index.clone());2197            }21982199            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2200        } else {2201            let mut itm = Vec::new();2202            itm.push(item_index.clone());2203            <AddressTokens<T>>::insert(collection_id, owner, itm);2204            2205        }22062207        Ok(())2208    }22092210    fn remove_token_index(2211        collection_id: CollectionId,2212        item_index: TokenId,2213        owner: T::AccountId,2214    ) -> DispatchResult {22152216        // update counter2217        <AccountItemCount<T>>::insert(owner.clone(), 2218            <AccountItemCount<T>>::get(owner.clone())2219            .checked_sub(1)2220            .ok_or(Error::<T>::NumOverflow)?);222122222223        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2224        if list_exists {2225            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2226            let item_contains = list.contains(&item_index.clone());22272228            if item_contains {2229                list.retain(|&item| item != item_index);2230                <AddressTokens<T>>::insert(collection_id, owner, list);2231            }2232        }22332234        Ok(())2235    }22362237    fn move_token_index(2238        collection_id: CollectionId,2239        item_index: TokenId,2240        old_owner: T::AccountId,2241        new_owner: T::AccountId,2242    ) -> DispatchResult {2243        Self::remove_token_index(collection_id, item_index, old_owner)?;2244        Self::add_token_index(collection_id, item_index, new_owner)?;22452246        Ok(())2247    }2248}22492250////////////////////////////////////////////////////////////////////////////////////////////////////2251// Economic models2252// #region22532254/// Fee multiplier.2255pub type Multiplier = FixedU128;22562257type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2258    <T as system::Trait>::AccountId,2259>>::Balance;2260type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2261    <T as system::Trait>::AccountId,2262>>::NegativeImbalance;22632264/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2265/// in the queue.2266#[derive(Encode, Decode, Clone, Eq, PartialEq)]2267pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2268    #[codec(compact)] BalanceOf<T>2269);22702271impl<T: Trait + Send + Sync> sp_std::fmt::Debug2272    for ChargeTransactionPayment<T>2273{2274    #[cfg(feature = "std")]2275    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2276        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2277    }2278    #[cfg(not(feature = "std"))]2279    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2280        Ok(())2281    }2282}22832284impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2285where2286    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2287    BalanceOf<T>: Send + Sync + FixedPointOperand,2288{2289    /// utility constructor. Used only in client/factory code.2290    pub fn from(fee: BalanceOf<T>) -> Self {2291        Self(fee)2292    }22932294    pub fn traditional_fee(2295        len: usize,2296        info: &DispatchInfoOf<T::Call>,2297        tip: BalanceOf<T>,2298    ) -> BalanceOf<T>2299    where2300        T::Call: Dispatchable<Info = DispatchInfo>,2301    {2302        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)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<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2312        let tip = self.0;23132314        // Set fee based on call type. Creating collection costs 1 Unique.2315        // All other transactions have traditional fees so far2316        // let fee = match call.is_sub_type() {2317        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2318        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2319        //                                                 // _ => <BalanceOf<T>>::from(100)2320        // };2321        let fee = Self::traditional_fee(len, info, tip);23222323        // Determine who is paying transaction fee based on ecnomic model2324        // Parse call to extract collection ID and access collection sponsor2325        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2326            Some(Call::create_item(collection_id, _owner, _properties)) => {23272328                // check free create limit2329                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2330                {2331                    <Collection<T>>::get(collection_id).sponsor2332                } else {2333                    T::AccountId::default()2334                }2335            }2336            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2337                2338                let _collection_limits = <Collection<T>>::get(collection_id).limits;2339                let _collection_mode = <Collection<T>>::get(collection_id).mode;23402341                // sponsor timeout2342                let sponsor_transfer = match _collection_mode {2343                    CollectionMode::NFT => {23442345                        // get correct limit2346                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2347                            _collection_limits.sponsor_transfer_timeout2348                        } else {2349                            ChainLimit::get().nft_sponsor_transfer_timeout2350                        };23512352                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2353                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2354                        let limit_time = basket + limit.into();2355                        if block_number >= limit_time {2356                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2357                            true2358                        }2359                        else {2360                            false2361                        }2362                    }2363                    CollectionMode::Fungible(_) => {23642365                        // get correct limit2366                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2367                            _collection_limits.sponsor_transfer_timeout2368                        } else {2369                            ChainLimit::get().fungible_sponsor_transfer_timeout2370                        };23712372                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2373                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2374                        if basket.iter().any(|i| i.address == _new_owner.clone())2375                        {2376                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2377                            let limit_time = item.start_block + limit.into();2378                            if block_number >= limit_time {2379                                basket.retain(|x| x.address == item.address);2380                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2381                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2382                                true2383                            }2384                            else {2385                                false2386                            }2387                        }2388                        else {2389                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2390                            true2391                        }2392                    }2393                    CollectionMode::ReFungible(_) => {23942395                        // get correct limit2396                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2397                            _collection_limits.sponsor_transfer_timeout2398                        } else {2399                            ChainLimit::get().refungible_sponsor_transfer_timeout2400                        };24012402                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2403                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2404                        let limit_time = basket + limit.into();2405                        if block_number >= limit_time {2406                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2407                            true2408                        } else {2409                            false2410                        }2411                    }2412                    _ => {2413                        false2414                    },2415                };24162417                if !sponsor_transfer {2418                    T::AccountId::default()2419                } else {2420                    <Collection<T>>::get(collection_id).sponsor2421                }2422            }24232424            _ => T::AccountId::default(),2425        };24262427        // Sponsor smart contracts2428        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24292430            // On instantiation: set the contract owner2431            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24322433                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2434                    code_hash,2435                    &data,2436                    &who,2437                );2438                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24392440                T::AccountId::default()2441            },24422443            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2444            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24452446                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24472448                let mut sponsor_transfer = false;2449                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2450                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2451                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2452                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2453                    let limit_time = last_tx_block + rate_limit;24542455                    if block_number >= limit_time {2456                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2457                        sponsor_transfer = true;2458                    }2459                } else {2460                    sponsor_transfer = false;2461                }2462               2463                2464                let mut sp = T::AccountId::default();2465                if sponsor_transfer {2466                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2467                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2468                            sp = called_contract;2469                        }2470                    }2471                }24722473                sp2474            },24752476            _ => sponsor,2477        };24782479        let mut who_pays_fee: T::AccountId = sponsor.clone();2480        if sponsor == T::AccountId::default() {2481            who_pays_fee = who.clone();2482        }24832484        // Only mess with balances if fee is not zero.2485        if fee.is_zero() {2486            return Ok((fee, None));2487        }24882489        match <T as transaction_payment::Trait>::Currency::withdraw(2490            &who_pays_fee,2491            fee,2492            if tip.is_zero() {2493                WithdrawReason::TransactionPayment.into()2494            } else {2495                WithdrawReason::TransactionPayment | WithdrawReason::Tip2496            },2497            ExistenceRequirement::KeepAlive,2498        ) {2499            Ok(imbalance) => Ok((fee, Some(imbalance))),2500            Err(_) => Err(InvalidTransaction::Payment.into()),2501        }2502    }2503}250425052506impl<T: Trait + Send + Sync> SignedExtension2507    for ChargeTransactionPayment<T>2508where2509    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2510    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2511{2512    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2513    type AccountId = T::AccountId;2514    type Call = T::Call;2515    type AdditionalSigned = ();2516    type Pre = (2517        BalanceOf<T>,2518        Self::AccountId,2519        Option<NegativeImbalanceOf<T>>,2520        BalanceOf<T>,2521    );2522    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2523        Ok(())2524    }25252526    fn validate(2527        &self,2528        _who: &Self::AccountId,2529        _call: &Self::Call,2530        _info: &DispatchInfoOf<Self::Call>,2531        _len: usize,2532    ) -> TransactionValidity {2533        Ok(ValidTransaction::default())2534    }25352536    fn pre_dispatch(2537        self,2538        who: &Self::AccountId,2539        call: &Self::Call,2540        info: &DispatchInfoOf<Self::Call>,2541        len: usize,2542    ) -> Result<Self::Pre, TransactionValidityError> {2543        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2544        Ok((self.0, who.clone(), imbalance, fee))2545    }25462547    fn post_dispatch(2548        pre: Self::Pre,2549        info: &DispatchInfoOf<Self::Call>,2550        post_info: &PostDispatchInfoOf<Self::Call>,2551        len: usize,2552        _result: &DispatchResult,2553    ) -> Result<(), TransactionValidityError> {2554        let (tip, who, imbalance, fee) = pre;2555        if let Some(payed) = imbalance {2556            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2557                len as u32, info, post_info, tip,2558            );2559            let refund = fee.saturating_sub(actual_fee);2560            let actual_payment =2561                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2562                    &who, refund,2563                ) {2564                    Ok(refund_imbalance) => {2565                        // The refund cannot be larger than the up front payed max weight.2566                        // `PostDispatchInfo::calc_unspent` guards against such a case.2567                        match payed.offset(refund_imbalance) {2568                            Ok(actual_payment) => actual_payment,2569                            Err(_) => return Err(InvalidTransaction::Payment.into()),2570                        }2571                    }2572                    // We do not recreate the account using the refund. The up front payment2573                    // is gone in that case.2574                    Err(_) => payed,2575                };2576            let imbalances = actual_payment.split(tip);2577            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2578                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2579            );2580        }2581        Ok(())2582    }2583}25842585// #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, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, 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 pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211}212213pub trait WeightInfo {214	fn create_collection() -> Weight;215	fn destroy_collection() -> Weight;216	fn add_to_white_list() -> Weight;217	fn remove_from_white_list() -> Weight;218    fn set_public_access_mode() -> Weight;219    fn set_mint_permission() -> Weight;220    fn change_collection_owner() -> Weight;221    fn add_collection_admin() -> Weight;222    fn remove_collection_admin() -> Weight;223    fn set_collection_sponsor() -> Weight;224    fn confirm_sponsorship() -> Weight;225    fn remove_collection_sponsor() -> Weight;226    fn create_item(s: usize) -> Weight;227    fn burn_item() -> Weight;228    fn transfer() -> Weight;229    fn approve() -> Weight;230    fn transfer_from() -> Weight;231    fn set_offchain_schema() -> Weight;232    fn set_const_on_chain_schema() -> Weight;233    fn set_variable_on_chain_schema() -> Weight;234    fn set_variable_meta_data() -> Weight;235    fn enable_contract_sponsoring() -> Weight;236    fn set_schema_version() -> Weight;237    fn set_chain_limits() -> Weight;238    fn set_contract_sponsoring_rate_limit() -> Weight;239    fn toggle_contract_white_list() -> Weight;240    fn add_to_contract_white_list() -> Weight;241    fn remove_from_contract_white_list() -> Weight;242    fn set_collection_limits() -> Weight;243}244245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247pub struct CreateNftData {248    pub const_data: Vec<u8>,249    pub variable_data: Vec<u8>,250}251252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]254pub struct CreateFungibleData {255    pub value: u128,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateReFungibleData {261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265#[derive(Encode, Decode, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub enum CreateItemData {268    NFT(CreateNftData),269    Fungible(CreateFungibleData),270    ReFungible(CreateReFungibleData),271}272273impl CreateItemData {274    pub fn len(&self) -> usize {275        let len = match self {276            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),277            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),278            _ => 0279        };280        281        return len;282    }283}284285impl From<CreateNftData> for CreateItemData {286    fn from(item: CreateNftData) -> Self {287        CreateItemData::NFT(item)288    }289}290291impl From<CreateReFungibleData> for CreateItemData {292    fn from(item: CreateReFungibleData) -> Self {293        CreateItemData::ReFungible(item)294    }295}296297impl From<CreateFungibleData> for CreateItemData {298    fn from(item: CreateFungibleData) -> Self {299        CreateItemData::Fungible(item)300    }301}302303304decl_error! {305	/// Error for non-fungible-token module.306	pub enum Error for Module<T: Trait> {307        /// Total collections bound exceeded.308        TotalCollectionsLimitExceeded,309		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.310        CollectionDecimalPointLimitExceeded, 311        /// Collection name can not be longer than 63 char.312        CollectionNameLimitExceeded, 313        /// Collection description can not be longer than 255 char.314        CollectionDescriptionLimitExceeded, 315        /// Token prefix can not be longer than 15 char.316        CollectionTokenPrefixLimitExceeded,317        /// This collection does not exist.318        CollectionNotFound,319        /// Item not exists.320        TokenNotFound,321        /// Arithmetic calculation overflow.322        NumOverflow,       323        /// Account already has admin role.324        AlreadyAdmin,  325        /// You do not own this collection.326        NoPermission,327        /// This address is not set as sponsor, use setCollectionSponsor first.328        ConfirmUnsetSponsorFail,329        /// Collection is not in mint mode.330        PublicMintingNotAllowed,331        /// Sender parameter and item owner must be equal.332        MustBeTokenOwner,333        /// Item balance not enough.334        TokenValueTooLow,335        /// Size of item is too large.336        NftSizeLimitExceeded,337        /// No approve found338        ApproveNotFound,339        /// Requested value more than approved.340        TokenValueNotEnough,341        /// Only approved addresses can call this method.342        ApproveRequired,343        /// Address is not in white list.344        AddresNotInWhiteList,345        /// Number of collection admins bound exceeded.346        CollectionAdminsLimitExceeded,347        /// Owned tokens by a single address bound exceeded.348        AddressOwnershipLimitExceeded,349        /// Length of items properties must be greater than 0.350        EmptyArgument,351        /// const_data exceeded data limit.352        TokenConstDataLimitExceeded,353        /// variable_data exceeded data limit.354        TokenVariableDataLimitExceeded,355        /// Not NFT item data used to mint in NFT collection.356        NotNftDataUsedToMintNftCollectionToken,357        /// Not Fungible item data used to mint in Fungible collection.358        NotFungibleDataUsedToMintFungibleCollectionToken,359        /// Not Re Fungible item data used to mint in Re Fungible collection.360        NotReFungibleDataUsedToMintReFungibleCollectionToken,361        /// Unexpected collection type.362        UnexpectedCollectionType,363        /// Can't store metadata in fungible tokens.364        CantStoreMetadataInFungibleTokens,365        /// Collection token limit exceeded366        CollectionTokenLimitExceeded,367        /// Account token limit exceeded per collection368        AccountTokenLimitExceeded,369        /// Collection limit bounds per collection exceeded370        CollectionLimitBoundsExceeded371	}372}373374pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {375    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;376377    /// Weight information for extrinsics in this pallet.378	type WeightInfo: WeightInfo;379}380381#[cfg(feature = "runtime-benchmarks")]382mod benchmarking;383384// #endregion385386decl_storage! {387    trait Store for Module<T: Trait> as Nft {388389        // Private members390        NextCollectionID: CollectionId;391        CreatedCollectionCount: u32;392        ChainVersion: u64;393        ItemListIndex: map hasher(identity) CollectionId => TokenId;394395        // Chain limits struct396        pub ChainLimit get(fn chain_limit) config(): ChainLimits;397398        // Bound counters399        CollectionCount: u32;400        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;401402        // Basic collections403        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;404        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;405        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;406407        /// Balance owner per collection map408        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;409410        /// second parameter: item id + owner account id + spender account id411        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;412413        /// Item collections414        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;415        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;416        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;417418        /// Index list419        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;420421        /// Tokens transfer baskets422        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;423        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;424        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;425426        // Contract Sponsorship and Ownership427        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;428        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;429        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;430        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;431        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 432        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 433    }434    add_extra_genesis {435        build(|config: &GenesisConfig<T>| {436            // Modification of storage437            for (_num, _c) in &config.collection {438                <Module<T>>::init_collection(_c);439            }440441            for (_num, _c, _i) in &config.nft_item_id {442                <Module<T>>::init_nft_token(*_c, _i);443            }444445            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {446                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);447            }448449            for (_num, _c, _i) in &config.refungible_item_id {450                <Module<T>>::init_refungible_token(*_c, _i);451            }452        })453    }454}455456decl_event!(457    pub enum Event<T>458    where459        AccountId = <T as system::Trait>::AccountId,460    {461        /// New collection was created462        /// 463        /// # Arguments464        /// 465        /// * collection_id: Globally unique identifier of newly created collection.466        /// 467        /// * mode: [CollectionMode] converted into u8.468        /// 469        /// * account_id: Collection owner.470        Created(CollectionId, u8, AccountId),471472        /// New item was created.473        /// 474        /// # Arguments475        /// 476        /// * collection_id: Id of the collection where item was created.477        /// 478        /// * item_id: Id of an item. Unique within the collection.479        ItemCreated(CollectionId, TokenId),480481        /// Collection item was burned.482        /// 483        /// # Arguments484        /// 485        /// collection_id.486        /// 487        /// item_id: Identifier of burned NFT.488        ItemDestroyed(CollectionId, TokenId),489    }490);491492decl_module! {493    pub struct Module<T: Trait> for enum Call where origin: T::Origin {494495        fn deposit_event() = default;496        type Error = Error<T>;497498        fn on_initialize(now: T::BlockNumber) -> Weight {499500            if ChainVersion::get() < 2501            {502                let value = NextCollectionID::get();503                CreatedCollectionCount::put(value);504                ChainVersion::put(2);505            }506507            0508        }509510        /// 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.511        /// 512        /// # Permissions513        /// 514        /// * Anyone.515        /// 516        /// # Arguments517        /// 518        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.519        /// 520        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.521        /// 522        /// * token_prefix: UTF-8 string with token prefix.523        /// 524        /// * mode: [CollectionMode] collection type and type dependent data.525        // returns collection ID526        #[weight = T::WeightInfo::create_collection()]527        pub fn create_collection(origin,528                                 collection_name: Vec<u16>,529                                 collection_description: Vec<u16>,530                                 token_prefix: Vec<u8>,531                                 mode: CollectionMode) -> DispatchResult {532533            // Anyone can create a collection534            let who = ensure_signed(origin)?;535536            let decimal_points = match mode {537                CollectionMode::Fungible(points) => points,538                CollectionMode::ReFungible(points) => points,539                _ => 0540            };541542            // bound Total number of collections543            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);544545            // check params546            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);547548            let mut name = collection_name.to_vec();549            name.push(0);550            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);551552            let mut description = collection_description.to_vec();553            description.push(0);554            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);555556            let mut prefix = token_prefix.to_vec();557            prefix.push(0);558            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);559560            // Generate next collection ID561            let next_id = CreatedCollectionCount::get()562                .checked_add(1)563                .ok_or(Error::<T>::NumOverflow)?;564565            // bound counter566            let total = CollectionCount::get()567                .checked_add(1)568                .ok_or(Error::<T>::NumOverflow)?;569570            CreatedCollectionCount::put(next_id);571            CollectionCount::put(total);572573            // Create new collection574            let new_collection = CollectionType {575                owner: who.clone(),576                name: name,577                mode: mode.clone(),578                mint_mode: false,579                access: AccessMode::Normal,580                description: description,581                decimal_points: decimal_points,582                token_prefix: prefix,583                offchain_schema: Vec::new(),584                schema_version: SchemaVersion::ImageURL,585                sponsor: T::AccountId::default(),586                sponsor_confirmed: false,587                variable_on_chain_schema: Vec::new(),588                const_on_chain_schema: Vec::new(),589                limits: CollectionLimits::default(),590            };591592            // Add new collection to map593            <Collection<T>>::insert(next_id, new_collection);594595            // call event596            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));597598            Ok(())599        }600601        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.602        /// 603        /// # Permissions604        /// 605        /// * Collection Owner.606        /// 607        /// # Arguments608        /// 609        /// * collection_id: collection to destroy.610        #[weight = T::WeightInfo::destroy_collection()]611        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {612613            let sender = ensure_signed(origin)?;614            Self::check_owner_permissions(collection_id, sender)?;615616            <AddressTokens<T>>::remove_prefix(collection_id);617            <Allowances<T>>::remove_prefix(collection_id);618            <Balance<T>>::remove_prefix(collection_id);619            <ItemListIndex>::remove(collection_id);620            <AdminList<T>>::remove(collection_id);621            <Collection<T>>::remove(collection_id);622            <WhiteList<T>>::remove_prefix(collection_id);623624            <NftItemList<T>>::remove_prefix(collection_id);625            <FungibleItemList<T>>::remove_prefix(collection_id);626            <ReFungibleItemList<T>>::remove_prefix(collection_id);627628            <NftTransferBasket<T>>::remove_prefix(collection_id);629            <FungibleTransferBasket<T>>::remove_prefix(collection_id);630            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);631632            if CollectionCount::get() > 0633            {634                // bound couter635                let total = CollectionCount::get()636                    .checked_sub(1)637                    .ok_or(Error::<T>::NumOverflow)?;638639                CollectionCount::put(total);640            }641642            Ok(())643        }644645        /// Add an address to white list.646        /// 647        /// # Permissions648        /// 649        /// * Collection Owner650        /// * Collection Admin651        /// 652        /// # Arguments653        /// 654        /// * collection_id.655        /// 656        /// * address.657        #[weight = T::WeightInfo::add_to_white_list()]658        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{659660            let sender = ensure_signed(origin)?;661            Self::check_owner_or_admin_permissions(collection_id, sender)?;662663            <WhiteList<T>>::insert(collection_id, address, true);664            665            Ok(())666        }667668        /// Remove an address from white list.669        /// 670        /// # Permissions671        /// 672        /// * Collection Owner673        /// * Collection Admin674        /// 675        /// # Arguments676        /// 677        /// * collection_id.678        /// 679        /// * address.680        #[weight = T::WeightInfo::remove_from_white_list()]681        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{682683            let sender = ensure_signed(origin)?;684            Self::check_owner_or_admin_permissions(collection_id, sender)?;685686            <WhiteList<T>>::remove(collection_id, address);687688            Ok(())689        }690691        /// Toggle between normal and white list access for the methods with access for `Anyone`.692        /// 693        /// # Permissions694        /// 695        /// * Collection Owner.696        /// 697        /// # Arguments698        /// 699        /// * collection_id.700        /// 701        /// * mode: [AccessMode]702        #[weight = T::WeightInfo::set_public_access_mode()]703        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult704        {705            let sender = ensure_signed(origin)?;706707            Self::check_owner_permissions(collection_id, sender)?;708            let mut target_collection = <Collection<T>>::get(collection_id);709            target_collection.access = mode;710            <Collection<T>>::insert(collection_id, target_collection);711712            Ok(())713        }714715        /// Allows Anyone to create tokens if:716        /// * White List is enabled, and717        /// * Address is added to white list, and718        /// * This method was called with True parameter719        /// 720        /// # Permissions721        /// * Collection Owner722        ///723        /// # Arguments724        /// 725        /// * collection_id.726        /// 727        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.728        #[weight = T::WeightInfo::set_mint_permission()]729        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult730        {731            let sender = ensure_signed(origin)?;732733            Self::check_owner_permissions(collection_id, sender)?;734            let mut target_collection = <Collection<T>>::get(collection_id);735            target_collection.mint_mode = mint_permission;736            <Collection<T>>::insert(collection_id, target_collection);737738            Ok(())739        }740741        /// Change the owner of the collection.742        /// 743        /// # Permissions744        /// 745        /// * Collection Owner.746        /// 747        /// # Arguments748        /// 749        /// * collection_id.750        /// 751        /// * new_owner.752        #[weight = T::WeightInfo::change_collection_owner()]753        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {754755            let sender = ensure_signed(origin)?;756            Self::check_owner_permissions(collection_id, sender)?;757            let mut target_collection = <Collection<T>>::get(collection_id);758            target_collection.owner = new_owner;759            <Collection<T>>::insert(collection_id, target_collection);760761            Ok(())762        }763764        /// Adds an admin of the Collection.765        /// 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. 766        /// 767        /// # Permissions768        /// 769        /// * Collection Owner.770        /// * Collection Admin.771        /// 772        /// # Arguments773        /// 774        /// * collection_id: ID of the Collection to add admin for.775        /// 776        /// * new_admin_id: Address of new admin to add.777        #[weight = T::WeightInfo::add_collection_admin()]778        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {779780            let sender = ensure_signed(origin)?;781            Self::check_owner_or_admin_permissions(collection_id, sender)?;782            let mut admin_arr: Vec<T::AccountId> = Vec::new();783784            if <AdminList<T>>::contains_key(collection_id)785            {786                admin_arr = <AdminList<T>>::get(collection_id);787                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);788            }789790            // Number of collection admins791            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);792793            admin_arr.push(new_admin_id);794            <AdminList<T>>::insert(collection_id, admin_arr);795796            Ok(())797        }798799        /// 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.800        ///801        /// # Permissions802        /// 803        /// * Collection Owner.804        /// * Collection Admin.805        /// 806        /// # Arguments807        /// 808        /// * collection_id: ID of the Collection to remove admin for.809        /// 810        /// * account_id: Address of admin to remove.811        #[weight = T::WeightInfo::remove_collection_admin()]812        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {813814            let sender = ensure_signed(origin)?;815            Self::check_owner_or_admin_permissions(collection_id, sender)?;816817            if <AdminList<T>>::contains_key(collection_id)818            {819                let mut admin_arr = <AdminList<T>>::get(collection_id);820                admin_arr.retain(|i| *i != account_id);821                <AdminList<T>>::insert(collection_id, admin_arr);822            }823824            Ok(())825        }826827        /// # Permissions828        /// 829        /// * Collection Owner830        /// 831        /// # Arguments832        /// 833        /// * collection_id.834        /// 835        /// * new_sponsor.836        #[weight = T::WeightInfo::set_collection_sponsor()]837        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {838839            let sender = ensure_signed(origin)?;840            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);841842            let mut target_collection = <Collection<T>>::get(collection_id);843            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);844845            target_collection.sponsor = new_sponsor;846            target_collection.sponsor_confirmed = false;847            <Collection<T>>::insert(collection_id, target_collection);848849            Ok(())850        }851852        /// # Permissions853        /// 854        /// * Sponsor.855        /// 856        /// # Arguments857        /// 858        /// * collection_id.859        #[weight = T::WeightInfo::confirm_sponsorship()]860        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {861862            let sender = ensure_signed(origin)?;863            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);864865            let mut target_collection = <Collection<T>>::get(collection_id);866            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);867868            target_collection.sponsor_confirmed = true;869            <Collection<T>>::insert(collection_id, target_collection);870871            Ok(())872        }873874        /// Switch back to pay-per-own-transaction model.875        ///876        /// # Permissions877        ///878        /// * Collection owner.879        /// 880        /// # Arguments881        /// 882        /// * collection_id.883        #[weight = T::WeightInfo::remove_collection_sponsor()]884        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {885886            let sender = ensure_signed(origin)?;887            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);888889            let mut target_collection = <Collection<T>>::get(collection_id);890            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);891892            target_collection.sponsor = T::AccountId::default();893            target_collection.sponsor_confirmed = false;894            <Collection<T>>::insert(collection_id, target_collection);895896            Ok(())897        }898899        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.900        /// 901        /// # Permissions902        /// 903        /// * Collection Owner.904        /// * Collection Admin.905        /// * Anyone if906        ///     * White List is enabled, and907        ///     * Address is added to white list, and908        ///     * MintPermission is enabled (see SetMintPermission method)909        /// 910        /// # Arguments911        /// 912        /// * collection_id: ID of the collection.913        /// 914        /// * owner: Address, initial owner of the NFT.915        ///916        /// * data: Token data to store on chain.917        // #[weight =918        // (130_000_000 as Weight)919        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))920        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))921        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]922923        #[weight = T::WeightInfo::create_item(data.len())]924        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {925926            let sender = ensure_signed(origin)?;927928            Self::collection_exists(collection_id)?;929930            let target_collection = <Collection<T>>::get(collection_id);931932            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;933            Self::validate_create_item_args(&target_collection, &data)?;934            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;935936            Ok(())937        }938939        /// This method creates multiple instances of NFT Collection created with CreateCollection method.940        /// 941        /// # Permissions942        /// 943        /// * Collection Owner.944        /// * Collection Admin.945        /// * Anyone if946        ///     * White List is enabled, and947        ///     * Address is added to white list, and948        ///     * MintPermission is enabled (see SetMintPermission method)949        /// 950        /// # Arguments951        /// 952        /// * collection_id: ID of the collection.953        /// 954        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].955        /// 956        /// * owner: Address, initial owner of the NFT.957        #[weight = T::WeightInfo::create_item(items_data.into_iter()958                               .map(|data| { data.len() })959                               .sum())]960        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {961962            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);963            let sender = ensure_signed(origin)?;964965            Self::collection_exists(collection_id)?;966            let target_collection = <Collection<T>>::get(collection_id);967968            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;969970            for data in &items_data {971                Self::validate_create_item_args(&target_collection, data)?;972            }973            for data in &items_data {974                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;975            }976977            Ok(())978        }979980        /// Destroys a concrete instance of NFT.981        /// 982        /// # Permissions983        /// 984        /// * Collection Owner.985        /// * Collection Admin.986        /// * Current NFT Owner.987        /// 988        /// # Arguments989        /// 990        /// * collection_id: ID of the collection.991        /// 992        /// * item_id: ID of NFT to burn.993        #[weight = T::WeightInfo::burn_item()]994        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {995996            let sender = ensure_signed(origin)?;997            Self::collection_exists(collection_id)?;998999            // Transfer permissions check1000            let target_collection = <Collection<T>>::get(collection_id);1001            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1002                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1003                Error::<T>::NoPermission);10041005            if target_collection.access == AccessMode::WhiteList {1006                Self::check_white_list(collection_id, &sender)?;1007            }10081009            match target_collection.mode1010            {1011                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1012                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1013                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1014                _ => ()1015            };10161017            // call event1018            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10191020            Ok(())1021        }10221023        /// Change ownership of the token.1024        /// 1025        /// # Permissions1026        /// 1027        /// * Collection Owner1028        /// * Collection Admin1029        /// * Current NFT owner1030        ///1031        /// # Arguments1032        /// 1033        /// * recipient: Address of token recipient.1034        /// 1035        /// * collection_id.1036        /// 1037        /// * item_id: ID of the item1038        ///     * Non-Fungible Mode: Required.1039        ///     * Fungible Mode: Ignored.1040        ///     * Re-Fungible Mode: Required.1041        /// 1042        /// * value: Amount to transfer.1043        ///     * Non-Fungible Mode: Ignored1044        ///     * Fungible Mode: Must specify transferred amount1045        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1046        #[weight = T::WeightInfo::transfer()]1047        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10481049            let sender = ensure_signed(origin)?;1050            let target_collection = <Collection<T>>::get(collection_id);10511052            // Limits check1053            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10541055            // Transfer permissions check1056            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1057                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1058                Error::<T>::NoPermission);10591060            if target_collection.access == AccessMode::WhiteList {1061                Self::check_white_list(collection_id, &sender)?;1062                Self::check_white_list(collection_id, &recipient)?;1063            }10641065            match target_collection.mode1066            {1067                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1068                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1069                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1070                _ => ()1071            };10721073            Ok(())1074        }10751076        /// Set, change, or remove approved address to transfer the ownership of the NFT.1077        /// 1078        /// # Permissions1079        /// 1080        /// * Collection Owner1081        /// * Collection Admin1082        /// * Current NFT owner1083        /// 1084        /// # Arguments1085        /// 1086        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1087        /// 1088        /// * collection_id.1089        /// 1090        /// * item_id: ID of the item.1091        #[weight = T::WeightInfo::approve()]1092        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10931094            let sender = ensure_signed(origin)?;10951096            // Transfer permissions check1097            let target_collection = <Collection<T>>::get(collection_id);1098            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1099                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1100                Error::<T>::NoPermission);11011102            if target_collection.access == AccessMode::WhiteList {1103                Self::check_white_list(collection_id, &sender)?;1104                Self::check_white_list(collection_id, &spender)?;1105            }11061107            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1108            let mut allowance: u128 = amount;1109            if allowance_exists {1110                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1111            }1112            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11131114            Ok(())1115        }1116        1117        /// 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.1118        /// 1119        /// # Permissions1120        /// * Collection Owner1121        /// * Collection Admin1122        /// * Current NFT owner1123        /// * Address approved by current NFT owner1124        /// 1125        /// # Arguments1126        /// 1127        /// * from: Address that owns token.1128        /// 1129        /// * recipient: Address of token recipient.1130        /// 1131        /// * collection_id.1132        /// 1133        /// * item_id: ID of the item.1134        /// 1135        /// * value: Amount to transfer.1136        #[weight = T::WeightInfo::transfer_from()]1137        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11381139            let sender = ensure_signed(origin)?;1140            let mut appoved_transfer = false;11411142            // Check approval1143            let mut approval: u128 = 0;1144            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1145                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1146                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1147                appoved_transfer = true;1148            }11491150            let target_collection = <Collection<T>>::get(collection_id);11511152            // Limits check1153            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11541155            // Transfer permissions check         1156            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1157                Error::<T>::NoPermission);11581159            if target_collection.access == AccessMode::WhiteList {1160                Self::check_white_list(collection_id, &sender)?;1161                Self::check_white_list(collection_id, &recipient)?;1162            }11631164            // Reduce approval by transferred amount or remove if remaining approval drops to 01165            if approval - value > 0 {1166                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1167            }1168            else {1169                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1170            }11711172            match target_collection.mode1173            {1174                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1175                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1176                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1177                _ => ()1178            };11791180            Ok(())1181        }11821183        #[weight = 0]1184        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11851186            // let no_perm_mes = "You do not have permissions to modify this collection";1187            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1188            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1189            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11901191            // // on_nft_received  call11921193            // Self::transfer(origin, collection_id, item_id, new_owner)?;11941195            Ok(())1196        }11971198        /// Set off-chain data schema.1199        /// 1200        /// # Permissions1201        /// 1202        /// * Collection Owner1203        /// * Collection Admin1204        /// 1205        /// # Arguments1206        /// 1207        /// * collection_id.1208        /// 1209        /// * schema: String representing the offchain data schema.1210        #[weight = T::WeightInfo::set_variable_meta_data()]1211        pub fn set_variable_meta_data (1212            origin,1213            collection_id: CollectionId,1214            item_id: TokenId,1215            data: Vec<u8>1216        ) -> DispatchResult {1217            let sender = ensure_signed(origin)?;1218            1219            Self::collection_exists(collection_id)?;1220            1221            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12221223            // Modify permissions check1224            let target_collection = <Collection<T>>::get(collection_id);1225            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1226                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1227                Error::<T>::NoPermission);12281229            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12301231            match target_collection.mode1232            {1233                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1234                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1235                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1236                _ => fail!(Error::<T>::UnexpectedCollectionType)1237            };12381239            Ok(())1240        }1241 1242        /// Set schema standard1243        /// ImageURL1244        /// Unique1245        /// 1246        /// # Permissions1247        /// 1248        /// * Collection Owner1249        /// * Collection Admin1250        /// 1251        /// # Arguments1252        /// 1253        /// * collection_id.1254        /// 1255        /// * schema: SchemaVersion: enum1256        #[weight = T::WeightInfo::set_schema_version()]1257        pub fn set_schema_version(1258            origin,1259            collection_id: CollectionId,1260            version: SchemaVersion1261        ) -> DispatchResult {1262            let sender = ensure_signed(origin)?;1263            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1264            let mut target_collection = <Collection<T>>::get(collection_id);1265            target_collection.schema_version = version;1266            <Collection<T>>::insert(collection_id, target_collection);12671268            Ok(())1269        }12701271        /// Set off-chain data schema.1272        /// 1273        /// # Permissions1274        /// 1275        /// * Collection Owner1276        /// * Collection Admin1277        /// 1278        /// # Arguments1279        /// 1280        /// * collection_id.1281        /// 1282        /// * schema: String representing the offchain data schema.1283        #[weight = T::WeightInfo::set_offchain_schema()]1284        pub fn set_offchain_schema(1285            origin,1286            collection_id: CollectionId,1287            schema: Vec<u8>1288        ) -> DispatchResult {1289            let sender = ensure_signed(origin)?;1290            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12911292            let mut target_collection = <Collection<T>>::get(collection_id);1293            target_collection.offchain_schema = schema;1294            <Collection<T>>::insert(collection_id, target_collection);12951296            Ok(())1297        }12981299        /// Set const on-chain data schema.1300        /// 1301        /// # Permissions1302        /// 1303        /// * Collection Owner1304        /// * Collection Admin1305        /// 1306        /// # Arguments1307        /// 1308        /// * collection_id.1309        /// 1310        /// * schema: String representing the const on-chain data schema.1311        #[weight = T::WeightInfo::set_const_on_chain_schema()]1312        pub fn set_const_on_chain_schema (1313            origin,1314            collection_id: CollectionId,1315            schema: Vec<u8>1316        ) -> DispatchResult {1317            let sender = ensure_signed(origin)?;1318            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13191320            let mut target_collection = <Collection<T>>::get(collection_id);1321            target_collection.const_on_chain_schema = schema;1322            <Collection<T>>::insert(collection_id, target_collection);13231324            Ok(())1325        }13261327        /// Set variable on-chain data schema.1328        /// 1329        /// # Permissions1330        /// 1331        /// * Collection Owner1332        /// * Collection Admin1333        /// 1334        /// # Arguments1335        /// 1336        /// * collection_id.1337        /// 1338        /// * schema: String representing the variable on-chain data schema.1339        #[weight = T::WeightInfo::set_const_on_chain_schema()]1340        pub fn set_variable_on_chain_schema (1341            origin,1342            collection_id: CollectionId,1343            schema: Vec<u8>1344        ) -> DispatchResult {1345            let sender = ensure_signed(origin)?;1346            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13471348            let mut target_collection = <Collection<T>>::get(collection_id);1349            target_collection.variable_on_chain_schema = schema;1350            <Collection<T>>::insert(collection_id, target_collection);13511352            Ok(())1353        }13541355        // Sudo permissions function1356        #[weight = T::WeightInfo::set_chain_limits()]1357        pub fn set_chain_limits(1358            origin,1359            limits: ChainLimits1360        ) -> DispatchResult {13611362            #[cfg(not(feature = "runtime-benchmarks"))]1363            ensure_root(origin)?;13641365            <ChainLimit>::put(limits);1366            Ok(())1367        }13681369        /// Enable smart contract self-sponsoring.1370        /// 1371        /// # Permissions1372        /// 1373        /// * Contract Owner1374        /// 1375        /// # Arguments1376        /// 1377        /// * contract address1378        /// * enable flag1379        /// 1380        #[weight = T::WeightInfo::enable_contract_sponsoring()]1381        pub fn enable_contract_sponsoring(1382            origin,1383            contract_address: T::AccountId,1384            enable: bool1385        ) -> DispatchResult {13861387            let sender = ensure_signed(origin)?;13881389            #[cfg(feature = "runtime-benchmarks")]1390            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13911392            Self::ensure_contract_owned(sender, &contract_address)?;13931394            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1395            Ok(())1396        }13971398        /// Set the rate limit for contract sponsoring to specified number of blocks.1399        /// 1400        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1401        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1402        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1403        /// from contract endowment if there are at least B blocks between such transactions. 1404        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1405        /// 1406        /// # Permissions1407        /// 1408        /// * Contract Owner1409        /// 1410        /// # Arguments1411        /// 1412        /// -`contract_address`: Address of the contract to sponsor1413        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1414        /// 1415        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1416        pub fn set_contract_sponsoring_rate_limit(1417            origin,1418            contract_address: T::AccountId,1419            rate_limit: T::BlockNumber1420        ) -> DispatchResult {1421            let sender = ensure_signed(origin)?;14221423            #[cfg(feature = "runtime-benchmarks")]1424            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14251426            Self::ensure_contract_owned(sender, &contract_address)?;1427            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1428            Ok(())1429        }14301431        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1432        /// 1433        /// # Permissions1434        /// 1435        /// * Address that deployed smart contract.1436        /// 1437        /// # Arguments1438        /// 1439        /// -`contract_address`: Address of the contract.1440        /// 1441        /// - `enable`: .  1442        #[weight = T::WeightInfo::toggle_contract_white_list()]1443        pub fn toggle_contract_white_list(1444            origin,1445            contract_address: T::AccountId,1446            enable: bool1447        ) -> DispatchResult {1448            let sender = ensure_signed(origin)?;14491450            #[cfg(feature = "runtime-benchmarks")]1451            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14521453            Self::ensure_contract_owned(sender, &contract_address)?;1454            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1455            Ok(())1456        }1457        1458        /// Add an address to smart contract white list.1459        /// 1460        /// # Permissions1461        /// 1462        /// * Address that deployed smart contract.1463        /// 1464        /// # Arguments1465        /// 1466        /// -`contract_address`: Address of the contract.1467        ///1468        /// -`account_address`: Address to add.1469        #[weight = T::WeightInfo::add_to_contract_white_list()]1470        pub fn add_to_contract_white_list(1471            origin,1472            contract_address: T::AccountId,1473            account_address: T::AccountId1474        ) -> DispatchResult {1475            let sender = ensure_signed(origin)?;14761477            #[cfg(feature = "runtime-benchmarks")]1478            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1479            1480            Self::ensure_contract_owned(sender, &contract_address)?;      1481            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1482            Ok(())1483        }14841485        /// Remove an address from smart contract white list.1486        /// 1487        /// # Permissions1488        /// 1489        /// * Address that deployed smart contract.1490        /// 1491        /// # Arguments1492        /// 1493        /// -`contract_address`: Address of the contract.1494        ///1495        /// -`account_address`: Address to remove.1496        #[weight = T::WeightInfo::remove_from_contract_white_list()]1497        pub fn remove_from_contract_white_list(1498            origin,1499            contract_address: T::AccountId,1500            account_address: T::AccountId1501        ) -> DispatchResult {1502            let sender = ensure_signed(origin)?;15031504            #[cfg(feature = "runtime-benchmarks")]1505            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15061507            Self::ensure_contract_owned(sender, &contract_address)?;1508            <ContractWhiteList<T>>::remove(contract_address, account_address);1509            Ok(())1510        }15111512        #[weight = T::WeightInfo::set_collection_limits()]1513        pub fn set_collection_limits(1514            origin,1515            collection_id: u32,1516            limits: CollectionLimits,1517        ) -> DispatchResult {1518            let sender = ensure_signed(origin)?;1519            Self::check_owner_permissions(collection_id, sender.clone())?;1520            let mut target_collection = <Collection<T>>::get(collection_id);1521            let chain_limits = ChainLimit::get();1522            let climits = target_collection.limits;15231524            // collection bounds1525            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1526                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1527                Error::<T>::CollectionLimitBoundsExceeded);15281529            // token_limit   check  prev1530            ensure!(climits.token_limit > limits.token_limit && 1531                limits.token_limit <= chain_limits.account_token_ownership_limit, 1532                Error::<T>::AccountTokenLimitExceeded);15331534            target_collection.limits = limits;1535            <Collection<T>>::insert(collection_id, target_collection);15361537            Ok(())1538        } 1539    }1540}15411542impl<T: Trait> Module<T> {15431544    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15451546        // check token limit and account token limit1547        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1548        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1549        1550        Ok(())1551    }15521553    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15541555        // check token limit and account token limit1556        let total_items: u32 = ItemListIndex::get(collection_id);1557        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1558        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1559        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15601561        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1562            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1563            Self::check_white_list(collection_id, owner)?;1564            Self::check_white_list(collection_id, sender)?;1565        }15661567        Ok(())1568    }15691570    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1571        match target_collection.mode1572        {1573            CollectionMode::NFT => {1574                if let CreateItemData::NFT(data) = data {1575                    // check sizes1576                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1577                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1578                } else {1579                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1580                }1581            },1582            CollectionMode::Fungible(_) => {1583                if let CreateItemData::Fungible(_) = data {1584                } else {1585                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1586                }1587            },1588            CollectionMode::ReFungible(_) => {1589                if let CreateItemData::ReFungible(data) = data {15901591                    // check sizes1592                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1593                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1594                } else {1595                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1596                }1597            },1598            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1599        };16001601        Ok(())1602    }16031604    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1605        match data1606        {1607            CreateItemData::NFT(data) => {1608                let item = NftItemType {1609                    owner,1610                    const_data: data.const_data,1611                    variable_data: data.variable_data1612                };16131614                Self::add_nft_item(collection_id, item)?;1615            },1616            CreateItemData::Fungible(data) => {1617                Self::add_fungible_item(collection_id, &owner, data.value)?;1618            },1619            CreateItemData::ReFungible(data) => {1620                let mut owner_list = Vec::new();1621                let value = (10 as u128).pow(collection.decimal_points as u32);1622                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16231624                let item = ReFungibleItemType {1625                    owner: owner_list,1626                    const_data: data.const_data,1627                    variable_data: data.variable_data1628                };16291630                Self::add_refungible_item(collection_id, item)?;1631            }1632        };16331634        // call event1635        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16361637        Ok(())1638    }16391640    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16411642        // Does new owner already have an account?1643        let mut balance: u128 = 0;1644        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1645            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1646        } 16471648        // Mint 1649        let item = FungibleItemType {1650            value: balance + value1651        };1652        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16531654        // Update balance1655        let new_balance = <Balance<T>>::get(collection_id, owner)1656            .checked_add(value)1657            .ok_or(Error::<T>::NumOverflow)?;1658        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16591660        Ok(())1661    }16621663    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1664        let current_index = <ItemListIndex>::get(collection_id)1665            .checked_add(1)1666            .ok_or(Error::<T>::NumOverflow)?;1667        let itemcopy = item.clone();16681669        let value = item.owner.first().unwrap().fraction;1670        let owner = item.owner.first().unwrap().owner.clone();16711672        Self::add_token_index(collection_id, current_index, owner.clone())?;16731674        <ItemListIndex>::insert(collection_id, current_index);1675        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16761677        // Update balance1678        let new_balance = <Balance<T>>::get(collection_id, owner.clone())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_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1687        let current_index = <ItemListIndex>::get(collection_id)1688            .checked_add(1)1689            .ok_or(Error::<T>::NumOverflow)?;16901691        let item_owner = item.owner.clone();1692        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16931694        <ItemListIndex>::insert(collection_id, current_index);1695        <NftItemList<T>>::insert(collection_id, current_index, item);16961697        // Update balance1698        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1699            .checked_add(1)1700            .ok_or(Error::<T>::NumOverflow)?;1701        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17021703        Ok(())1704    }17051706    fn burn_refungible_item(1707        collection_id: CollectionId,1708        item_id: TokenId,1709        owner: T::AccountId,1710    ) -> DispatchResult {1711        ensure!(1712            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1713            Error::<T>::TokenNotFound1714        );1715        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1716        let item = collection1717            .owner1718            .iter()1719            .filter(|&i| i.owner == owner)1720            .next()1721            .unwrap();1722        Self::remove_token_index(collection_id, item_id, owner.clone())?;17231724        // update balance1725        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1726            .checked_sub(item.fraction)1727            .ok_or(Error::<T>::NumOverflow)?;1728        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17291730        <ReFungibleItemList<T>>::remove(collection_id, item_id);17311732        Ok(())1733    }17341735    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1736        ensure!(1737            <NftItemList<T>>::contains_key(collection_id, item_id),1738            Error::<T>::TokenNotFound1739        );1740        let item = <NftItemList<T>>::get(collection_id, item_id);1741        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17421743        // update balance1744        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1745            .checked_sub(1)1746            .ok_or(Error::<T>::NumOverflow)?;1747        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1748        <NftItemList<T>>::remove(collection_id, item_id);17491750        Ok(())1751    }17521753    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1754        ensure!(1755            <FungibleItemList<T>>::contains_key(collection_id, owner),1756            Error::<T>::TokenNotFound1757        );1758        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1759        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17601761        // update balance1762        let new_balance = <Balance<T>>::get(collection_id, owner)1763            .checked_sub(value)1764            .ok_or(Error::<T>::NumOverflow)?;1765        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17661767        if balance.value - value > 0 {1768            balance.value -= value;1769            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1770        }1771        else {1772            <FungibleItemList<T>>::remove(collection_id, owner);1773        }17741775        Ok(())1776    }17771778    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1779        ensure!(1780            <Collection<T>>::contains_key(collection_id),1781            Error::<T>::CollectionNotFound1782        );1783        Ok(())1784    }17851786    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1787        Self::collection_exists(collection_id)?;17881789        let target_collection = <Collection<T>>::get(collection_id);1790        ensure!(1791            subject == target_collection.owner,1792            Error::<T>::NoPermission1793        );17941795        Ok(())1796    }17971798    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1799        let target_collection = <Collection<T>>::get(collection_id);1800        let mut result: bool = subject == target_collection.owner;1801        let exists = <AdminList<T>>::contains_key(collection_id);18021803        if !result & exists {1804            if <AdminList<T>>::get(collection_id).contains(&subject) {1805                result = true1806            }1807        }18081809        result1810    }18111812    fn check_owner_or_admin_permissions(1813        collection_id: CollectionId,1814        subject: T::AccountId,1815    ) -> DispatchResult {1816        Self::collection_exists(collection_id)?;1817        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18181819        ensure!(1820            result,1821            Error::<T>::NoPermission1822        );1823        Ok(())1824    }18251826    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1827        let target_collection = <Collection<T>>::get(collection_id);18281829        match target_collection.mode {1830            CollectionMode::NFT => {1831                <NftItemList<T>>::get(collection_id, item_id).owner == subject1832            }1833            CollectionMode::Fungible(_) => {1834                <FungibleItemList<T>>::contains_key(collection_id, &subject)1835            }1836            CollectionMode::ReFungible(_) => {1837                <ReFungibleItemList<T>>::get(collection_id, item_id)1838                    .owner1839                    .iter()1840                    .any(|i| i.owner == subject)1841            }1842            CollectionMode::Invalid => false,1843        }1844    }18451846    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1847        let mes = Error::<T>::AddresNotInWhiteList;1848        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18491850        Ok(())1851    }18521853    fn transfer_fungible(1854        collection_id: CollectionId,1855        value: u128,1856        owner: &T::AccountId,1857        recipient: &T::AccountId,1858    ) -> DispatchResult {1859        ensure!(1860            <FungibleItemList<T>>::contains_key(collection_id, owner),1861            Error::<T>::TokenNotFound1862        );18631864        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1865        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18661867        // Send balance to recipient (updates balanceOf of recipient)1868        Self::add_fungible_item(collection_id, recipient, value)?;18691870        // update balanceOf of sender1871        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18721873        // Reduce or remove sender1874        if balance.value == value {1875            <FungibleItemList<T>>::remove(collection_id, owner);1876        }1877        else {1878            balance.value -= value;1879            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1880        }18811882        Ok(())1883    }18841885    fn transfer_refungible(1886        collection_id: CollectionId,1887        item_id: TokenId,1888        value: u128,1889        owner: T::AccountId,1890        new_owner: T::AccountId,1891    ) -> DispatchResult {1892        ensure!(1893            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1894            Error::<T>::TokenNotFound1895        );18961897        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1898        let item = full_item1899            .owner1900            .iter()1901            .filter(|i| i.owner == owner)1902            .next()1903            .ok_or(Error::<T>::NumOverflow)?;1904        let amount = item.fraction;19051906        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19071908        // update balance1909        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1910            .checked_sub(value)1911            .ok_or(Error::<T>::NumOverflow)?;1912        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19131914        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1915            .checked_add(value)1916            .ok_or(Error::<T>::NumOverflow)?;1917        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19181919        let old_owner = item.owner.clone();1920        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19211922        // transfer1923        if amount == value && !new_owner_has_account {1924            // change owner1925            // new owner do not have account1926            let mut new_full_item = full_item.clone();1927            new_full_item1928                .owner1929                .iter_mut()1930                .find(|i| i.owner == owner)1931                .unwrap()1932                .owner = new_owner.clone();1933            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19341935            // update index collection1936            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1937        } else {1938            let mut new_full_item = full_item.clone();1939            new_full_item1940                .owner1941                .iter_mut()1942                .find(|i| i.owner == owner)1943                .unwrap()1944                .fraction -= value;19451946            // separate amount1947            if new_owner_has_account {1948                // new owner has account1949                new_full_item1950                    .owner1951                    .iter_mut()1952                    .find(|i| i.owner == new_owner)1953                    .unwrap()1954                    .fraction += value;1955            } else {1956                // new owner do not have account1957                new_full_item.owner.push(Ownership {1958                    owner: new_owner.clone(),1959                    fraction: value,1960                });1961                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1962            }19631964            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1965        }19661967        Ok(())1968    }19691970    fn transfer_nft(1971        collection_id: CollectionId,1972        item_id: TokenId,1973        sender: T::AccountId,1974        new_owner: T::AccountId,1975    ) -> DispatchResult {1976        ensure!(1977            <NftItemList<T>>::contains_key(collection_id, item_id),1978            Error::<T>::TokenNotFound1979        );19801981        let mut item = <NftItemList<T>>::get(collection_id, item_id);19821983        ensure!(1984            sender == item.owner,1985            Error::<T>::MustBeTokenOwner1986        );19871988        // update balance1989        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1990            .checked_sub(1)1991            .ok_or(Error::<T>::NumOverflow)?;1992        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19931994        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1995            .checked_add(1)1996            .ok_or(Error::<T>::NumOverflow)?;1997        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19981999        // change owner2000        let old_owner = item.owner.clone();2001        item.owner = new_owner.clone();2002        <NftItemList<T>>::insert(collection_id, item_id, item);20032004        // update index collection2005        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20062007        Ok(())2008    }2009    2010    fn item_exists(2011        collection_id: CollectionId,2012        item_id: TokenId,2013        mode: &CollectionMode2014    ) -> DispatchResult {2015        match mode {2016            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2017            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2018            _ => ()2019        };2020        2021        Ok(())2022    }20232024    fn set_re_fungible_variable_data(2025        collection_id: CollectionId,2026        item_id: TokenId,2027        data: Vec<u8>2028    ) -> DispatchResult {2029        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20302031        item.variable_data = data;20322033        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20342035        Ok(())2036    }20372038    fn set_nft_variable_data(2039        collection_id: CollectionId,2040        item_id: TokenId,2041        data: Vec<u8>2042    ) -> DispatchResult {2043        let mut item = <NftItemList<T>>::get(collection_id, item_id);2044        2045        item.variable_data = data;20462047        <NftItemList<T>>::insert(collection_id, item_id, item);2048        2049        Ok(())2050    }20512052    fn init_collection(item: &CollectionType<T::AccountId>) {2053        // check params2054        assert!(2055            item.decimal_points <= MAX_DECIMAL_POINTS,2056            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2057        );2058        assert!(2059            item.name.len() <= 64,2060            "Collection name can not be longer than 63 char"2061        );2062        assert!(2063            item.name.len() <= 256,2064            "Collection description can not be longer than 255 char"2065        );2066        assert!(2067            item.token_prefix.len() <= 16,2068            "Token prefix can not be longer than 15 char"2069        );20702071        // Generate next collection ID2072        let next_id = CreatedCollectionCount::get()2073            .checked_add(1)2074            .unwrap();20752076        CreatedCollectionCount::put(next_id);2077    }20782079    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2080        let current_index = <ItemListIndex>::get(collection_id)2081            .checked_add(1)2082            .unwrap();20832084        let item_owner = item.owner.clone();2085        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20862087        <ItemListIndex>::insert(collection_id, current_index);20882089        // Update balance2090        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2091            .checked_add(1)2092            .unwrap();2093        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2094    }20952096    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2097        let current_index = <ItemListIndex>::get(collection_id)2098            .checked_add(1)2099            .unwrap();21002101        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();21022103        <ItemListIndex>::insert(collection_id, current_index);21042105        // Update balance2106        let new_balance = <Balance<T>>::get(collection_id, owner)2107            .checked_add(item.value)2108            .unwrap();2109        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2110    }21112112    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2113        let current_index = <ItemListIndex>::get(collection_id)2114            .checked_add(1)2115            .unwrap();21162117        let value = item.owner.first().unwrap().fraction;2118        let owner = item.owner.first().unwrap().owner.clone();21192120        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21212122        <ItemListIndex>::insert(collection_id, current_index);21232124        // Update balance2125        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2126            .checked_add(value)2127            .unwrap();2128        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2129    }21302131    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21322133        // add to account limit2134        if <AccountItemCount<T>>::contains_key(owner.clone()) {21352136            // bound Owned tokens by a single address2137            let count = <AccountItemCount<T>>::get(owner.clone());2138            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21392140            <AccountItemCount<T>>::insert(owner.clone(), count2141                .checked_add(1)2142                .ok_or(Error::<T>::NumOverflow)?);2143        }2144        else {2145            <AccountItemCount<T>>::insert(owner.clone(), 1);2146        }21472148        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2149        if list_exists {2150            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2151            let item_contains = list.contains(&item_index.clone());21522153            if !item_contains {2154                list.push(item_index.clone());2155            }21562157            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2158        } else {2159            let mut itm = Vec::new();2160            itm.push(item_index.clone());2161            <AddressTokens<T>>::insert(collection_id, owner, itm);2162            2163        }21642165        Ok(())2166    }21672168    fn remove_token_index(2169        collection_id: CollectionId,2170        item_index: TokenId,2171        owner: T::AccountId,2172    ) -> DispatchResult {21732174        // update counter2175        <AccountItemCount<T>>::insert(owner.clone(), 2176            <AccountItemCount<T>>::get(owner.clone())2177            .checked_sub(1)2178            .ok_or(Error::<T>::NumOverflow)?);217921802181        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2182        if list_exists {2183            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2184            let item_contains = list.contains(&item_index.clone());21852186            if item_contains {2187                list.retain(|&item| item != item_index);2188                <AddressTokens<T>>::insert(collection_id, owner, list);2189            }2190        }21912192        Ok(())2193    }21942195    fn move_token_index(2196        collection_id: CollectionId,2197        item_index: TokenId,2198        old_owner: T::AccountId,2199        new_owner: T::AccountId,2200    ) -> DispatchResult {2201        Self::remove_token_index(collection_id, item_index, old_owner)?;2202        Self::add_token_index(collection_id, item_index, new_owner)?;22032204        Ok(())2205    }2206    2207    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2208        if <ContractOwner<T>>::contains_key(contract.clone()) {2209            let owner = <ContractOwner<T>>::get(contract);2210            ensure!(account == owner, Error::<T>::NoPermission);2211        } else {2212            fail!(Error::<T>::NoPermission);2213        }22142215        Ok(())2216    }2217}22182219////////////////////////////////////////////////////////////////////////////////////////////////////2220// Economic models2221// #region22222223/// Fee multiplier.2224pub type Multiplier = FixedU128;22252226type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2227    <T as system::Trait>::AccountId,2228>>::Balance;2229type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2230    <T as system::Trait>::AccountId,2231>>::NegativeImbalance;22322233/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2234/// in the queue.2235#[derive(Encode, Decode, Clone, Eq, PartialEq)]2236pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2237    #[codec(compact)] BalanceOf<T>2238);22392240impl<T: Trait + Send + Sync> sp_std::fmt::Debug2241    for ChargeTransactionPayment<T>2242{2243    #[cfg(feature = "std")]2244    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2245        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2246    }2247    #[cfg(not(feature = "std"))]2248    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2249        Ok(())2250    }2251}22522253impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2254where2255    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2256    BalanceOf<T>: Send + Sync + FixedPointOperand,2257{2258    /// utility constructor. Used only in client/factory code.2259    pub fn from(fee: BalanceOf<T>) -> Self {2260        Self(fee)2261    }22622263    pub fn traditional_fee(2264        len: usize,2265        info: &DispatchInfoOf<T::Call>,2266        tip: BalanceOf<T>,2267    ) -> BalanceOf<T>2268    where2269        T::Call: Dispatchable<Info = DispatchInfo>,2270    {2271        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2272    }22732274	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2275		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2276		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2277		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2278		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2279	}22802281    fn withdraw_fee(2282        &self,2283        who: &T::AccountId,2284        call: &T::Call,2285        info: &DispatchInfoOf<T::Call>,2286        len: usize,2287    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2288        let tip = self.0;22892290        // Set fee based on call type. Creating collection costs 1 Unique.2291        // All other transactions have traditional fees so far2292        // let fee = match call.is_sub_type() {2293        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2294        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2295        //                                                 // _ => <BalanceOf<T>>::from(100)2296        // };2297        let fee = Self::traditional_fee(len, info, tip);22982299        // Only mess with balances if fee is not zero.2300        if fee.is_zero() {2301            return Ok((fee, None));2302        }23032304        // Determine who is paying transaction fee based on ecnomic model2305        // Parse call to extract collection ID and access collection sponsor2306        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2307            Some(Call::create_item(collection_id, _owner, _properties)) => {23082309                // check free create limit2310                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2311                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2312                {2313                    <Collection<T>>::get(collection_id).sponsor2314                } else {2315                    T::AccountId::default()2316                }2317            }2318            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2319                2320                let mut sponsor_transfer = false;2321                if <Collection<T>>::get(collection_id).sponsor_confirmed {23222323                    let collection_limits = <Collection<T>>::get(collection_id).limits;2324                    let collection_mode = <Collection<T>>::get(collection_id).mode;2325    2326                    // sponsor timeout2327                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2328                    sponsor_transfer = match collection_mode {2329                        CollectionMode::NFT => {2330    2331                            // get correct limit2332                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2333                                collection_limits.sponsor_transfer_timeout2334                            } else {2335                                ChainLimit::get().nft_sponsor_transfer_timeout2336                            };2337    2338                            let mut sponsored = true;2339                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2340                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2341                                let limit_time = last_tx_block + limit.into();2342                                if block_number <= limit_time {2343                                    sponsored = false;2344                                }2345                            }2346                            if sponsored {2347                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2348                            }23492350                            sponsored2351                        }2352                        CollectionMode::Fungible(_) => {2353    2354                            // get correct limit2355                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2356                                collection_limits.sponsor_transfer_timeout2357                            } else {2358                                ChainLimit::get().fungible_sponsor_transfer_timeout2359                            };2360    2361                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2362                            let mut sponsored = true;2363                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2364                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2365                                let limit_time = last_tx_block + limit.into();2366                                if block_number <= limit_time {2367                                    sponsored = false;2368                                }2369                            }2370                            if sponsored {2371                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2372                            }23732374                            sponsored2375                        }2376                        CollectionMode::ReFungible(_) => {2377    2378                            // get correct limit2379                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2380                                collection_limits.sponsor_transfer_timeout2381                            } else {2382                                ChainLimit::get().refungible_sponsor_transfer_timeout2383                            };2384    2385                            let mut sponsored = true;2386                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2387                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2388                                let limit_time = last_tx_block + limit.into();2389                                if block_number <= limit_time {2390                                    sponsored = false;2391                                }2392                            }2393                            if sponsored {2394                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2395                            }23962397                            sponsored2398                        }2399                        _ => {2400                            false2401                        },2402                    };2403                }24042405                if !sponsor_transfer {2406                    T::AccountId::default()2407                } else {2408                    <Collection<T>>::get(collection_id).sponsor2409                }2410            }24112412            _ => T::AccountId::default(),2413        };24142415        // Sponsor smart contracts2416        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24172418            // On instantiation: set the contract owner2419            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24202421                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2422                    code_hash,2423                    &data,2424                    &who,2425                );2426                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24272428                T::AccountId::default()2429            },24302431            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2432            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24332434                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24352436                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2437                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2438                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2439                  2440                if !owned_contract && white_list_enabled {2441                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2442                        return Err(InvalidTransaction::Call.into());2443                    }2444                }24452446                let mut sponsor_transfer = false;2447                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2448                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2449                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2450                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2451                    let limit_time = last_tx_block + rate_limit;24522453                    if block_number >= limit_time {2454                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2455                        sponsor_transfer = true;2456                    }2457                } else {2458                    sponsor_transfer = false;2459                }2460               2461                2462                let mut sp = T::AccountId::default();2463                if sponsor_transfer {2464                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2465                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2466                            sp = called_contract;2467                        }2468                    }2469                }24702471                sp2472            },24732474            _ => sponsor,2475        };24762477        let mut who_pays_fee: T::AccountId = sponsor.clone();2478        if sponsor == T::AccountId::default() {2479            who_pays_fee = who.clone();2480        }24812482        match <T as transaction_payment::Trait>::Currency::withdraw(2483            &who_pays_fee,2484            fee,2485            if tip.is_zero() {2486                WithdrawReason::TransactionPayment.into()2487            } else {2488                WithdrawReason::TransactionPayment | WithdrawReason::Tip2489            },2490            ExistenceRequirement::KeepAlive,2491        ) {2492            Ok(imbalance) => Ok((fee, Some(imbalance))),2493            Err(_) => Err(InvalidTransaction::Payment.into()),2494        }2495    }2496}249724982499impl<T: Trait + Send + Sync> SignedExtension2500    for ChargeTransactionPayment<T>2501where2502    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2503    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2504{2505    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2506    type AccountId = T::AccountId;2507    type Call = T::Call;2508    type AdditionalSigned = ();2509    type Pre = (2510        BalanceOf<T>,2511        Self::AccountId,2512        Option<NegativeImbalanceOf<T>>,2513        BalanceOf<T>,2514    );2515    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2516        Ok(())2517    }25182519    fn validate(2520        &self,2521        who: &Self::AccountId,2522        call: &Self::Call,2523        info: &DispatchInfoOf<Self::Call>,2524        len: usize,2525    ) -> TransactionValidity {2526		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2527		Ok(ValidTransaction {2528			priority: Self::get_priority(len, info, fee),2529			..Default::default()2530		})2531    }25322533    fn pre_dispatch(2534        self,2535        who: &Self::AccountId,2536        call: &Self::Call,2537        info: &DispatchInfoOf<Self::Call>,2538        len: usize,2539    ) -> Result<Self::Pre, TransactionValidityError> {2540        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2541        Ok((self.0, who.clone(), imbalance, fee))2542    }25432544    fn post_dispatch(2545        pre: Self::Pre,2546        info: &DispatchInfoOf<Self::Call>,2547        post_info: &PostDispatchInfoOf<Self::Call>,2548        len: usize,2549        _result: &DispatchResult,2550    ) -> Result<(), TransactionValidityError> {2551        let (tip, who, imbalance, fee) = pre;2552        if let Some(payed) = imbalance {2553            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2554                len as u32, info, post_info, tip,2555            );2556            let refund = fee.saturating_sub(actual_fee);2557            let actual_payment =2558                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2559                    &who, refund,2560                ) {2561                    Ok(refund_imbalance) => {2562                        // The refund cannot be larger than the up front payed max weight.2563                        // `PostDispatchInfo::calc_unspent` guards against such a case.2564                        match payed.offset(refund_imbalance) {2565                            Ok(actual_payment) => actual_payment,2566                            Err(_) => return Err(InvalidTransaction::Payment.into()),2567                        }2568                    }2569                    // We do not recreate the account using the refund. The up front payment2570                    // is gone in that case.2571                    Err(_) => payed,2572                };2573            let imbalances = actual_payment.split(tip);2574            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2575                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2576            );2577        }2578        Ok(())2579    }2580}25812582// #endregion
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -960,7 +960,7 @@
 
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
-        assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
+        assert_eq!(TemplateModule::white_list(collection_id, 2), true);
     });
 }
 
@@ -975,7 +975,7 @@
 
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
         assert_ok!(TemplateModule::add_to_white_list(origin2.clone(), collection_id, 3));
-        assert_eq!(TemplateModule::white_list(collection_id)[0], 3);
+        assert_eq!(TemplateModule::white_list(collection_id, 3), true);
     });
 }
 
@@ -1035,8 +1035,7 @@
         
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
-        assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
-        assert_eq!(TemplateModule::white_list(collection_id).len(), 1);
+        assert_eq!(TemplateModule::white_list(collection_id, 2), true);
     });
 }
 
@@ -1054,7 +1053,7 @@
             collection_id,
             2
         ));
-        assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
+        assert_eq!(TemplateModule::white_list(collection_id, 2), false);
     });
 }
 
@@ -1075,7 +1074,7 @@
             collection_id,
             3
         ));
-        assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
+        assert_eq!(TemplateModule::white_list(collection_id, 3), false);
     });
 }
 
@@ -1093,7 +1092,7 @@
             TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),
             Error::<Test>::NoPermission
         );
-        assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
+        assert_eq!(TemplateModule::white_list(collection_id, 2), true);
     });
 }
 
@@ -1125,7 +1124,7 @@
             TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),
             Error::<Test>::CollectionNotFound
         );
-        assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
+        assert_eq!(TemplateModule::white_list(collection_id, 2), false);
     });
 }
 
@@ -1149,7 +1148,7 @@
             collection_id,
             2
         ));
-        assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
+        assert_eq!(TemplateModule::white_list(collection_id, 2), false);
     });
 }
 
modifiedruntime/Cargo.tomldiffbeforeafterboth
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -45,6 +45,8 @@
 pallet-timestamp = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 pallet-transaction-payment = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 pallet-transaction-payment-rpc-runtime-api = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
+pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
+pallet-vesting = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 sp-api = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 sp-block-builder = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 sp-consensus-aura = { default-features = false, version = '0.8.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
@@ -56,8 +58,6 @@
 sp-std = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 sp-transaction-pool = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 sp-version = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
-
-pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
 
 [features]
 default = ['std']
@@ -90,6 +90,9 @@
     'pallet-timestamp/std',
     'pallet-transaction-payment/std',
     'pallet-transaction-payment-rpc-runtime-api/std',
+    'pallet-treasury/std',
+    'pallet-vesting/std',
+
     'pallet-nft/std',
     'sp-api/std',
     'sp-block-builder/std',
@@ -103,5 +106,4 @@
     'sp-transaction-pool/std',
     'sp-version/std',
 
-    'pallet-treasury/std',
 ]
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.
 
 #![cfg_attr(not(feature = "std"), no_std)]
@@ -17,7 +22,7 @@
 use sp_runtime::{
     create_runtime_str, generic, impl_opaque_keys,
     traits::{
-        Convert, BlakeTwo256, Block as BlockT, IdentifyAccount, 
+        Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, 
         IdentityLookup, NumberFor, Saturating, Verify,
     },
     transaction_validity::{TransactionSource, TransactionValidity},
@@ -315,7 +320,7 @@
 	type Balance = Balance;
 	/// The ubiquitous event type.
 	type Event = Event;
-	type DustRemoval = ();
+	type DustRemoval = Treasury;
 	type ExistentialDeposit = ExistentialDeposit;
 	type AccountStore = System;
 	type WeightInfo = ();
@@ -357,7 +362,7 @@
 
 impl pallet_transaction_payment::Trait for Runtime {
     type Currency = pallet_balances::Module<Runtime>;
-    type OnTransactionPayment = ();
+    type OnTransactionPayment = Treasury;
     type TransactionByteFee = TransactionByteFee;
     type WeightToFee = IdentityFee<Balance>;
     type FeeMultiplierUpdate =  ();
@@ -412,6 +417,18 @@
     type Call = Call;
 }
 
+parameter_types! {
+	pub const MinVestedTransfer: Balance = 100 * DOLLARS;
+}
+
+impl pallet_vesting::Trait for Runtime {
+	type Event = Event;
+	type Currency = Balances;
+	type BlockNumberToBalance = ConvertInto;
+	type MinVestedTransfer = MinVestedTransfer;
+	type WeightInfo = ();
+}
+
 /// Used for the module nft in `./nft.rs`
 impl pallet_nft::Trait for Runtime {
     type Event = Event;
@@ -435,6 +452,7 @@
         Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
         Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},
         Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},
+        Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},
     }
 );
 
modifiedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
 
 pub struct WeightInfo;
@@ -108,19 +113,44 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
-    // fn set_chain_limits() -> Weight {
-    //     (0 as Weight)
-    //         .saturating_add(DbWeight::get().reads(1 as Weight))
-    //         .saturating_add(DbWeight::get().writes(1 as Weight))
-    // }
     fn enable_contract_sponsoring() -> Weight {
         (13_000_000 as Weight)
             .saturating_add(DbWeight::get().reads(1 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
-    // fn set_contract_sponsoring_rate_limit() -> Weight {
-    //     (0 as Weight)
-    //         .saturating_add(DbWeight::get().reads(1 as Weight))
-    //         .saturating_add(DbWeight::get().writes(1 as Weight))
-    // }
+    fn set_schema_version() -> Weight {
+        (8_500_000 as Weight)
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
+    fn set_chain_limits() -> Weight {
+        (1_300_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
+    fn set_contract_sponsoring_rate_limit() -> Weight {
+        (3_500_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }    
+    fn toggle_contract_white_list() -> Weight {
+        (3_000_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }    
+    fn add_to_contract_white_list() -> Weight {
+        (3_000_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }    
+    fn remove_from_contract_white_list() -> Weight {
+        (3_200_000 as Weight)
+            .saturating_add(DbWeight::get().reads(0 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }
+    fn set_collection_limits() -> Weight {
+        (8_900_000 as Weight)
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
 }
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -42,18 +42,14 @@
       "Fraction": "u128"
     },
     "FungibleItemType": {
-      "Collection": "CollectionId",
-      "Owner": "AccountId",
       "Value": "u128"
     },
     "NftItemType": {
-      "Collection": "CollectionId",
       "Owner": "AccountId",
       "ConstData": "Vec<u8>",
       "VariableData": "Vec<u8>"
     },
     "ReFungibleItemType": {
-      "Collection": "CollectionId",
       "Owner": "Vec<Ownership<AccountId>>",
       "ConstData": "Vec<u8>",
       "VariableData": "Vec<u8>"
@@ -70,14 +66,10 @@
       "OffchainSchema": "Vec<u8>",
       "SchemaVersion": "SchemaVersion",
       "Sponsor": "AccountId",
-      "UnconfirmedSponsor": "AccountId",
+      "SponsorConfirmed": "bool",
       "Limits": "CollectionLimits",
       "VariableOnChainSchema": "Vec<u8>",
       "ConstOnChainSchema": "Vec<u8>"
-    },
-    "ApprovePermissions": {
-      "Approved": "AccountId",
-      "Amount": "u128"
     },
     "RawData": "Vec<u8>",
     "Address": "AccountId",
@@ -87,7 +79,9 @@
       "const_data": "Vec<u8>",
       "variable_data": "Vec<u8>" 
     },
-    "CreateFungibleData": {},
+    "CreateFungibleData": {
+      "value": "u128"
+    },
     "CreateReFungibleData": {
       "const_data": "Vec<u8>",
       "variable_data": "Vec<u8>" 
@@ -107,10 +101,6 @@
     },
     "CollectionId": "u32",
     "TokenId": "u32",
-    "BasketItem": {
-      "Address": "AccountId",
-      "start_block": "BlockNumber"
-    },
     "ChainLimits": {
       "collection_numbers_limit": "u32",
       "account_token_ownership_limit": "u32",
deletedtests/READMEdiffbeforeafterboth
--- a/tests/README
+++ /dev/null
@@ -1,12 +0,0 @@
-# Tests
-
-## How to run
-
-1. Run `npm install`.
-2. Setup a test node. You can do it using `docker-compose up -d` in parent directory.
-3. Configure tests with env variables or by editing [configuration file](src/config.ts).
-4. Run `npm run test`.
-
-## Don't run on the same node twice
-
-Some tests fail when ran on the same blockchain node twice. Either always use a new node or purge the existing one with `nft purge-chain --dev`. There is also [a script](../purge-running-node.sh) to purge and restart a node, started with docker-compose.
addedtests/README.mddiffbeforeafterboth
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,9 @@
+# Tests
+
+## How to run
+
+1. Run `npm install`.
+2. Setup a test node. You can do it using `docker-compose up -d` in parent directory.
+3. Optional step - configure tests with env variables or by editing [configuration file](src/config.ts).
+4. Run `npm test`.
+
addedtests/loadtester-src/.gitignorediffbeforeafterboth
--- /dev/null
+++ b/tests/loadtester-src/.gitignore
@@ -0,0 +1,9 @@
+# Ignore build artifacts from the local tests sub-crate.
+/target/
+
+# Ignore backup files creates by cargo fmt.
+**/*.rs.bk
+
+# Remove Cargo.lock when creating an executable, leave it for libraries
+# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
+Cargo.lock
\ No newline at end of file
addedtests/loadtester-src/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/tests/loadtester-src/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+name = "loadtester"
+version = "0.1.0"
+authors = ["[your_name] <[your_email]>"]
+edition = "2018"
+
+[dependencies]
+ink_primitives = { version = "3.0.0-rc2", default-features = false }
+ink_metadata = { version = "3.0.0-rc2", default-features = false, features = ["derive"], optional = true }
+ink_env = { version = "3.0.0-rc2", default-features = false }
+ink_storage = { version = "3.0.0-rc2", default-features = false }
+ink_lang = { version = "3.0.0-rc2", default-features = false }
+ink_prelude = { version = "3.0.0-rc2", default-features = false }
+
+scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
+scale-info = { version = "0.4.1", default-features = false, features = ["derive"], optional = true }
+
+[lib]
+name = "loadtester"
+path = "lib.rs"
+crate-type = [
+	# Used for normal contract Wasm blobs.
+	"cdylib",
+]
+
+[features]
+default = ["std"]
+std = [
+    "ink_metadata/std",
+    "ink_env/std",
+    "ink_storage/std",
+    "ink_primitives/std",
+    "scale/std",
+    "scale-info/std",
+]
+ink-as-dependency = []
addedtests/loadtester-src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/tests/loadtester-src/lib.rs
@@ -0,0 +1,53 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use ink_lang as ink;
+
+#[ink::contract]
+mod loadtester {
+    use ink_storage::collections::Vec as InkVec;
+
+    #[ink(storage)]
+    pub struct LoadTester {
+        vector: InkVec<u64>,
+    }
+
+    impl LoadTester {
+        #[ink(constructor)]
+        pub fn new() -> Self {
+            Self {
+                vector: InkVec::new(),
+            }
+        }
+
+        #[ink(message)]
+        pub fn bloat(&mut self, count: u64){
+            for i in 1..count+1 {
+                self.vector.push(i);
+            }
+        }
+
+        #[ink(message)]
+        pub fn get(&self) -> u128 {
+            let mut sum: u128 = 0;
+            for num in self.vector.iter() {
+                sum += *num as u128;
+            }
+            sum
+        }
+    }
+
+    #[cfg(test)]
+    mod tests {
+       
+        use super::*;
+
+        #[test]
+        fn it_works() {
+            let mut lt = LoadTester::new();
+            lt.bloat(4);
+            assert_eq!(lt.get(), [1,2,3,4]);
+            lt.bloat(3);
+            assert_eq!(lt.get(), [1,2,3,4,1,2,3]);
+        }
+    }
+}
modifiedtests/package-lock.jsondiffbeforeafterboth
--- a/tests/package-lock.json
+++ b/tests/package-lock.json
@@ -13,13 +13,12 @@
         "@polkadot/api-contract": "^2.3.1",
         "@polkadot/types": "^2.3.1",
         "@polkadot/util": "^3.4.1",
-        "@types/bn.js": "^4.11.6",
+        "bignumber.js": "^9.0.0",
         "chai-as-promised": "^7.1.1"
       },
       "devDependencies": {
         "@polkadot/dev": "^0.52.11",
         "@polkadot/ts": "^0.3.41",
-        "@types/bn.js": "^4.11.6",
         "@types/chai": "^4.2.12",
         "@types/chai-as-promised": "^7.1.3",
         "@types/mocha": "^8.0.3",
@@ -7114,6 +7113,14 @@
         "node": "*"
       }
     },
+    "node_modules/bignumber.js": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+      "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/binary-extensions": {
       "version": "1.13.1",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
@@ -34960,6 +34967,11 @@
       "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
       "dev": true
     },
+    "bignumber.js": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+      "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+    },
     "binary-extensions": {
       "version": "1.13.1",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -6,7 +6,6 @@
   "devDependencies": {
     "@polkadot/dev": "^0.52.11",
     "@polkadot/ts": "^0.3.41",
-    "@types/bn.js": "^4.11.6",
     "@types/chai": "^4.2.12",
     "@types/chai-as-promised": "^7.1.3",
     "@types/mocha": "^8.0.3",
@@ -17,7 +16,8 @@
     "typescript": "^3.9.7"
   },
   "scripts": {
-    "test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts"
+    "test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts",
+    "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts"
   },
   "author": "",
   "license": "Apache 2.0",
@@ -27,7 +27,7 @@
     "@polkadot/api-contract": "^2.3.1",
     "@polkadot/types": "^2.3.1",
     "@polkadot/util": "^3.4.1",
-    "@types/bn.js": "^4.11.6",
+    "bignumber.js": "^9.0.0",
     "chai-as-promised": "^7.1.1"
   },
   "standard": {
modifiedtests/src/accounts.tsdiffbeforeafterboth
--- a/tests/src/accounts.ts
+++ b/tests/src/accounts.ts
@@ -1,2 +1,9 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 export const bobsPublicKey = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
 export const alicesPublicKey = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
+export const ferdiesPublicKey = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL';
+export const nullPublicKey = '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM';
\ No newline at end of file
modifiedtests/src/blocks-production.test.tsdiffbeforeafterboth
--- a/tests/src/blocks-production.test.ts
+++ b/tests/src/blocks-production.test.ts
@@ -1,8 +1,13 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import usingApi from "./substrate/substrate-api";
 import promisifySubstrate from "./substrate/promisify-substrate";
 import { expect } from "chai";
 
-describe('Blocks Production', () => {
+describe('Blocks Production smoke test', () => {
   it('Node produces new blocks', async () => {
     await usingApi(async api => {
       const blocksPromise = promisifySubstrate(api, () => {
modifiedtests/src/config.tsdiffbeforeafterboth
--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import process from 'process';
 
 const config = {
addedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/confirmSponsorship.test.ts
@@ -0,0 +1,350 @@
+//
+// 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, 
+  setCollectionSponsorExpectSuccess, 
+  destroyCollectionExpectSuccess, 
+  setCollectionSponsorExpectFailure,
+  confirmSponsorshipExpectSuccess,
+  confirmSponsorshipExpectFailure,
+  createItemExpectSuccess,
+  findUnusedAddress,
+  getGenericResult,
+  enableWhiteListExpectSuccess,
+  enablePublicMintingExpectSuccess,
+  addToWhiteListExpectSuccess,
+} from "./util/helpers";
+import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
+import type { AccountId } from '@polkadot/types/interfaces';
+import { BigNumber } from 'bignumber.js';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+let charlie: IKeyringPair;
+
+describe('integration test: ext. confirmSponsorship():', () => {
+
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+      charlie = keyring.addFromUri(`//Charlie`);
+    });
+  });
+
+  it('Confirm collection sponsorship', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+  });
+  it('Add sponsor to a collection after the same sponsor was already added and confirmed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+  });
+  it('Add new sponsor to a collection after another sponsor was already added and confirmed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+    await setCollectionSponsorExpectSuccess(collectionId, charlie.address);
+  });
+
+  it('NFT: Transfer fees are paid by the sponsor after confirmation', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for unused address
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', zeroBalance.address);
+
+      // Transfer this tokens from unused address to Alice
+      const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 0);
+      const events = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result = getGenericResult(events);
+
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      expect(result.success).to.be.true;
+      expect(BsponsorBalance.lt(AsponsorBalance)).to.be.true;
+    });
+
+  });
+
+  it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for unused address
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);
+
+      // Transfer this tokens from unused address to Alice
+      const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);
+      const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result1 = getGenericResult(events1);
+
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      expect(result1.success).to.be.true;
+      expect(BsponsorBalance.lt(AsponsorBalance)).to.be.true;
+    });
+  });
+
+  it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for unused address
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);
+
+      // Transfer this tokens from unused address to Alice
+      const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);
+      const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result1 = getGenericResult(events1);
+
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      expect(result1.success).to.be.true;
+      expect(BsponsorBalance.lt(AsponsorBalance)).to.be.true;
+    });
+  });
+
+  it('CreateItem fees are paid by the sponsor after confirmation', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    // Enable collection white list 
+    await enableWhiteListExpectSuccess(alice, collectionId);
+
+    // Enable public minting
+    await enablePublicMintingExpectSuccess(alice, collectionId);
+
+    // Create Item 
+    await usingApi(async (api) => {
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Add zeroBalance address to white list
+      await addToWhiteListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+      // Mint token using unused address as signer
+      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      expect(BsponsorBalance.lt(AsponsorBalance)).to.be.true;
+    });
+  });
+
+  it('NFT: Sponsoring is rate limited', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for alice
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+      // Transfer this token from Alice to unused address and back
+      // Alice to Zero gets sponsored
+      const aliceToZero = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 0);
+      const events1 = await submitTransactionAsync(alice, aliceToZero);
+      const result1 = getGenericResult(events1);
+
+      // Second transfer should fail
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+      const zeroToAlice = api.tx.nft.transfer(alice.address, collectionId, itemId, 0);
+      const badTransaction = async function () { 
+        console.log = function () {};
+        console.error = function () {};
+        await submitTransactionAsync(zeroBalance, zeroToAlice);
+        delete console.log;
+        delete console.error;
+      };
+      await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Try again after Zero gets some balance - now it should succeed
+      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
+      await submitTransactionAsync(alice, balancetx);
+      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result2 = getGenericResult(events2);
+
+      expect(result1.success).to.be.true;
+      expect(result2.success).to.be.true;
+      expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;
+    });
+  });
+
+  it('Fungible: Sponsoring is rate limited', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for unused address
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);
+
+      // Transfer this tokens in parts from unused address to Alice
+      const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);
+      const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result1 = getGenericResult(events1);
+
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      const badTransaction = async function () { 
+        console.log = function () {};
+        console.error = function () {};
+        await submitTransactionAsync(zeroBalance, zeroToAlice);
+        delete console.log;
+        delete console.error;
+      };
+
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Try again after Zero gets some balance - now it should succeed
+      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
+      await submitTransactionAsync(alice, balancetx);
+      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result2 = getGenericResult(events2);
+
+      expect(result1.success).to.be.true;
+      expect(result2.success).to.be.true;
+      expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;
+    });
+  });
+
+  it('ReFungible: Sponsoring is rate limited', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for alice
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);
+
+      // Transfer this token from Alice to unused address and back
+      // Alice to Zero gets sponsored
+      const aliceToZero = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);
+      const events1 = await submitTransactionAsync(alice, aliceToZero);
+      const result1 = getGenericResult(events1);
+
+      // Second transfer should fail
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+      const zeroToAlice = api.tx.nft.transfer(alice.address, collectionId, itemId, 1);
+      const badTransaction = async function () { 
+        console.log = function () {};
+        console.error = function () {};
+        await submitTransactionAsync(zeroBalance, zeroToAlice);
+        delete console.log;
+        delete console.error;
+      };
+      await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Try again after Zero gets some balance - now it should succeed
+      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
+      await submitTransactionAsync(alice, balancetx);
+      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
+      const result2 = getGenericResult(events2);
+
+      expect(result1.success).to.be.true;
+      expect(result2.success).to.be.true;
+      expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;
+    });
+  });
+
+});
+
+describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+      charlie = keyring.addFromUri(`//Charlie`);
+    });
+  });
+
+  it('(!negative test!) Confirm sponsorship for a collection that never existed', async () => {
+    // Find the collection that never existed
+    const collectionId = 0;
+    await usingApi(async (api) => {
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+    });
+
+    await confirmSponsorshipExpectFailure(collectionId, '//Bob');
+  });
+
+  it('(!negative test!) Confirm sponsorship using a non-sponsor address', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+
+    await usingApi(async (api) => {
+      const transfer = api.tx.balances.transfer(charlie.address, 1e15);
+      await submitTransactionAsync(alice, transfer);
+    });
+
+    await confirmSponsorshipExpectFailure(collectionId, '//Charlie');
+  });
+
+  it('(!negative test!) Confirm sponsorship using owner address', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectFailure(collectionId, '//Alice');
+  });
+
+  it('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await confirmSponsorshipExpectFailure(collectionId, '//Bob');
+  });
+    
+  it('(!negative test!) Confirm sponsorship in a collection that was destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectSuccess(collectionId);
+    await confirmSponsorshipExpectFailure(collectionId, '//Bob');
+  });
+});
modifiedtests/src/connection.test.tsdiffbeforeafterboth
--- a/tests/src/connection.test.ts
+++ b/tests/src/connection.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import usingApi from "./substrate/substrate-api";
 import { WsProvider } from '@polkadot/api';
 import * as chai from 'chai';
@@ -7,7 +12,7 @@
 
 const expect = chai.expect;
 
-describe('Connection', () => {
+describe('Connection smoke test', () => {
   it('Connection can be established', async () => {
     await usingApi(async api => {
       const health = await api.rpc.system.health();
@@ -16,11 +21,17 @@
   });
 
   it('Cannot connect to 255.255.255.255', async () => {
+    console.log = function () {};
+    console.error = function () {};
+
     const neverConnectProvider = new WsProvider('ws://255.255.255.255:9944');
     await expect((async () => {
       await usingApi(async api => {
         const health = await api.rpc.system.health();
       }, { provider: neverConnectProvider });
     })()).to.be.eventually.rejected;
+
+    delete console.log;
+    delete console.error;
   });
 });
\ No newline at end of file
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.test.ts
@@ -1,17 +1,25 @@
-import { expect } from "chai";
-import usingApi from "./substrate/substrate-api";
+import chai from "chai";
+import chaiAsPromised from 'chai-as-promised';
+import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
 import fs from "fs";
-import { Abi, BlueprintPromise, CodePromise, ContractPromise } from "@polkadot/api-contract";
+import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
 import { IKeyringPair } from "@polkadot/types/types";
-import { Keyring } from "@polkadot/api";
+import { ApiPromise, Keyring } from "@polkadot/api";
 import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types";
+import privateKey from "./substrate/privateKey";
 
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from './util/helpers'
+
 const value = 0;
 const gasLimit = 3000n * 1000000n;
+const endowment = `1000000000000000`;
 const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';
 
-function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<BlueprintPromise> {
-  return new Promise<BlueprintPromise>(async (resolve, reject) => {
+function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<Blueprint> {
+  return new Promise<Blueprint>(async (resolve, reject) => {
     const unsub = await code
       .createBlueprint()
       .signAndSend(alice, (result) => {
@@ -24,7 +32,7 @@
   });
 }
 
-function deployContract(alice: IKeyringPair, blueprint: BlueprintPromise) : Promise<any> {
+function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
   return new Promise<any>(async (resolve, reject) => {
     const endowment = 1000000000000000n;
     const initValue = true;
@@ -40,57 +48,127 @@
   });
 }
 
-function runTransaction(privateKey: IKeyringPair, extrinsic: SubmittableExtrinsic<ApiTypes>) {
-  return new Promise<void>(async (resolve, reject) => {
-    extrinsic.signAndSend(privateKey, async result => {
-        if(!result.isInBlock) {
-          return;
-        }
+async function prepareDeployer(api: ApiPromise) {
+  // Find unused address
+  const deployer = await findUnusedAddress(api);
 
-        if(result.findRecord('system', 'ExtrinsicSuccess')) {
-          resolve();
-        }
-        else {
-          reject('Failed to flip value.');
-        }
-      })
-  });
+  // Transfer balance to it
+  const keyring = new Keyring({ type: 'sr25519' });
+  const alice = keyring.addFromUri(`//Alice`);
+  let amount = new BigNumber(endowment);
+  amount = amount.plus(1e15);
+  const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
+  await submitTransactionAsync(alice, tx);
+
+  return deployer;
 }
 
+async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> {
+  const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
+  const abi = new Abi(metadata);
+
+  const deployer = await prepareDeployer(api);
+
+  const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
+
+  const code = new CodePromise(api, abi, wasm);
+
+  const blueprint = await deployBlueprint(deployer, code);
+  const contract = (await deployContract(deployer, blueprint))['contract'] as Contract;
+
+  const initialGetResponse = await getFlipValue(contract, deployer);
+  expect(initialGetResponse).to.be.true;
+
+  return [contract, deployer];
+}
+
+async function getFlipValue(contract: Contract, deployer: IKeyringPair) {
+  const result = await contract.query.get(deployer.address, value, gasLimit);
+
+  if(!result.result.isSuccess) {
+    throw `Failed to get flipper value`;
+  }
+  return (result.result.asSuccess.data[0] == 0x00) ? false : true;
+}
+
 describe('Contracts', () => {
   it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {
     await usingApi(async api => {
-      const keyring = new Keyring({ type: 'sr25519' });
-      const alice = keyring.addFromUri("//Alice");
-      
-      const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
-      
-      const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
-      const abi = new Abi(metadata);
+      const [contract, deployer] = await deployFlipper(api);
+      const initialGetResponse = await getFlipValue(contract, deployer);
 
-      const code = new CodePromise(api, abi, wasm);
+      const bob = privateKey("//Bob");
+      const flip = contract.exec('flip', value, gasLimit);
+      await submitTransactionAsync(bob, flip);
 
-      const blueprint = await deployBlueprint(alice, code);
-      const contract = (await deployContract(alice, blueprint))['contract'];
+      const afterFlipGetResponse = await getFlipValue(contract, deployer);
+      expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.');
+    });
+  });
 
-      const getFlipValue = async () => {
-        const result = await contract.query.get(alice.address, value, gasLimit);
+  it(`Whitelisted account can call contract.`, async () => {
+    await usingApi(async api => {
+      const bob = privateKey("//Bob");
 
-        if(!result.result.isSuccess) {
-          throw `Failed to get flipper value`;
-        }
-        return (result.result.asSuccess.data[0] == 0x00) ? false : true;
-      }
+      const [contract, deployer] = await deployFlipper(api);
+      const consoleError = console.error;
+      console.error = (...data: any[]) => {
+      };
 
-      const initialGetResponse = await getFlipValue();
-      expect(initialGetResponse).to.be.true;
+      let expectedFlipValue = await getFlipValue(contract, deployer);
 
       const flip = contract.exec('flip', value, gasLimit);
-      await runTransaction(alice, flip);
+      await submitTransactionAsync(bob, flip);
+      expectedFlipValue = !expectedFlipValue;
+      const afterFlip = await getFlipValue(contract,deployer);
+      expect(afterFlip).to.be.eq(expectedFlipValue, `Anyone can call new contract.`);
 
-      const afterFlipGetResponse = await getFlipValue();
+      const deployerCanFlip = async () => {
+        expectedFlipValue = !expectedFlipValue;
+        const deployerFlip = contract.exec('flip', value, gasLimit);
+        await submitTransactionAsync(deployer, deployerFlip);
+        const aliceFlip1Response = await getFlipValue(contract, deployer);
+        expect(aliceFlip1Response).to.be.eq(expectedFlipValue, `Deployer always can flip.`);
+      };
+      await deployerCanFlip();
 
-      expect(afterFlipGetResponse).to.be.false;
+      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+      const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx);
+      const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit);
+      await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected;
+      const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer);
+      expect(flipValueAfterEnableWhiteList).to.be.eq(expectedFlipValue, `Enabling whitelist doesn't make it possible to call contract for everyone.`);
+
+      await deployerCanFlip();
+
+      const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+      const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx);
+      const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit);
+      await submitTransactionAsync(bob, flipWithWhitelistedBob);
+      expectedFlipValue = !expectedFlipValue;
+      const flipAfterWhiteListed = await getFlipValue(contract,deployer);
+      expect(flipAfterWhiteListed).to.be.eq(expectedFlipValue, `Bob was whitelisted, now he can flip.`);
+
+      await deployerCanFlip();
+
+      const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address);
+      const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx);
+      const bobRemoved = contract.exec('flip', value, gasLimit);
+      await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected;
+      const afterBobRemoved = await getFlipValue(contract, deployer);
+      expect(afterBobRemoved).to.be.eq(expectedFlipValue, `Bob can't call contract, now when he is removeed from white list.`);
+
+      await deployerCanFlip();
+
+      const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false);
+      const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx);
+      const whiteListDisabledFlip = contract.exec('flip', value, gasLimit);
+      await submitTransactionAsync(bob, whiteListDisabledFlip);
+      expectedFlipValue = !expectedFlipValue;
+      const afterWhiteListDisabled = await getFlipValue(contract,deployer);
+      expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`);
+
+      console.error = consoleError;
     });
   });
 
@@ -123,7 +201,7 @@
       // const bob = new GenericAccountId(api.registry, bobsPublicKey);
 
       // const transfer = contractInstance.exec('balance_transfer', 0, 1000000000000n, [bob, new u128(api.registry, 1000000)]);
-      // await runTransaction(alicesPrivateKey, transfer);
+      // await submitTransactionAsync(alicesPrivateKey, transfer);
 
       // const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
 
addedtests/src/createCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/createCollection.test.ts
@@ -0,0 +1,72 @@
+//
+// 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, createCollectionExpectFailure } from "./util/helpers";
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('integration test: ext. createCollection():', () => {
+  it('Create new NFT collection', async () => {
+    await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+  });
+  it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => {
+    await createCollectionExpectSuccess(
+      'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD',
+      '1', '1', 'NFT');
+  });
+  it('Create new NFT collection whith collection_description of maximum length (256 bytes)', async () => {
+    await createCollectionExpectSuccess(
+      'A', 
+      'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJabcdef',
+      '1', 'NFT');
+  });
+  it('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async () => {
+    await createCollectionExpectSuccess(
+      '1', 
+      '1',
+      'ABCDEFGHIJABCDEF', 'NFT');
+  });
+  it('Create new Fungible collection', async () => {
+    await createCollectionExpectSuccess('1', '1', '1', 'Fungible');
+  });
+  it('Create new ReFungible collection', async () => {
+    await createCollectionExpectSuccess('1', '1', '1', 'ReFungible');
+  });
+});
+
+describe('(!negative test!) integration test: ext. createCollection():', () => {
+  it('(!negative test!) create new NFT collection whith incorrect data (mode)', async () => {
+    await usingApi(async (api) => {
+      const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+
+      const badTransaction = async function () { 
+        await createCollectionExpectSuccess('1', '1', '1', 'BadMode');
+      };
+      expect(badTransaction()).to.be.rejected;
+
+      const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+      expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Incorrect collection created.');
+    });
+  });
+  it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => {
+    await createCollectionExpectFailure(
+      'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDE', 
+      '1', '1', 'NFT');
+  });
+  it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => {
+    await createCollectionExpectFailure('1',
+      'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJabcdefg',
+      '1', 'NFT');
+  });
+  it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
+    await createCollectionExpectFailure('1', '1', 
+    'ABCDEFGHIJABCDEFG',
+    'NFT');
+  });
+});
addedtests/src/createItem.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/createItem.test.ts
@@ -0,0 +1,34 @@
+import { default as usingApi } from './substrate/substrate-api';
+import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
+import { 
+  createCollectionExpectSuccess, 
+  createItemExpectSuccess
+} from './util/helpers';
+
+let alice: IKeyringPair;
+
+describe('integration test: ext. createItem():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+    });
+  });
+
+  it('Create new item in NFT collection', async () => {
+    const createMode = 'NFT';
+    const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode);
+    await createItemExpectSuccess(alice, newCollectionID, createMode);
+  });
+  it('Create new item in Fungible collection', async () => {
+    const createMode = 'Fungible';
+    const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode);
+    await createItemExpectSuccess(alice, newCollectionID, createMode);
+  });
+  it('Create new item in ReFungible collection', async () => {
+    const createMode = 'ReFungible';
+    const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode);
+    await createItemExpectSuccess(alice, newCollectionID, createMode);
+  });
+});
addedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/createMultipleItems.test.ts
@@ -0,0 +1,69 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { assert } from 'chai';
+import { alicesPublicKey } from './accounts';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import waitNewBlocks from './substrate/wait-new-blocks';
+
+const idCollection = 12;
+
+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!'); }
+    });
+  });
+  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('(!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!'); }
+    });
+  });
+});
addedtests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/creditFeesToTreasury.test.ts
@@ -0,0 +1,112 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
+import { alicesPublicKey, bobsPublicKey } from "./accounts";
+import privateKey from "./substrate/privateKey";
+import { BigNumber } from 'bignumber.js';
+import { createCollectionExpectSuccess, getGenericResult } from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const Treasury = "5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z";
+const saneMinimumFee = 0.0001;
+const saneMaximumFee = 0.01;
+
+describe('integration test: Fees must be credited to Treasury:', () => {
+  it('Total issuance does not change', async () => {
+    await usingApi(async (api) => {
+      const totalBefore = new BigNumber((await api.query.balances.totalIssuance()).toString());
+
+      const alicePrivateKey = privateKey('//Alice');
+      const amount = new BigNumber(1);
+      const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed());
+
+      const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer));
+
+      const totalAfter = new BigNumber((await api.query.balances.totalIssuance()).toString());
+
+      expect(result.success).to.be.true;
+      expect(totalAfter.toFixed()).to.be.equal(totalBefore.toFixed());
+    });
+  });
+
+  it('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async () => {
+    await usingApi(async (api) => {
+      const alicePrivateKey = privateKey('//Alice');
+      const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+
+      const amount = new BigNumber(1);
+      const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed());
+      const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer));
+
+      const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+      const fee = aliceBalanceBefore.minus(aliceBalanceAfter).minus(amount);
+      const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+      expect(result.success).to.be.true;
+      expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
+    });
+  });
+
+  it('Treasury balance increased by failed tx fee', async () => {
+    await usingApi(async (api) => {
+      const bobPrivateKey = privateKey('//Bob');
+      const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
+
+      const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0);
+      const result = getGenericResult(await submitTransactionAsync(bobPrivateKey, badTx));
+
+      const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
+      const fee = bobBalanceBefore.minus(bobBalanceAfter);
+      const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+      expect(result.success).to.be.false;
+      expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
+    });
+  });
+
+  it('NFT Transactions also send fees to Treasury', async () => {
+    await usingApi(async (api) => {
+      const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+
+      await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+
+      const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+      const fee = aliceBalanceBefore.minus(aliceBalanceAfter);
+      const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+      expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
+    });
+  });
+
+  it('Fees are sane', async () => {
+    await usingApi(async (api) => {
+      const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+
+      await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+
+      const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+      const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+      const fee = aliceBalanceBefore.minus(aliceBalanceAfter);
+      const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+      expect(fee.dividedBy(1e15).toNumber()).to.be.lessThan(0.01);
+      expect(fee.dividedBy(1e15).toNumber()).to.be.greaterThan(0.0001);
+    });
+  });
+
+});
+
addedtests/src/destroyCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/destroyCollection.test.ts
@@ -0,0 +1,44 @@
+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';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('integration test: ext. destroyCollection():', () => {
+  it('NFT collection can be destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+  it('Fungible collection can be destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+  it('ReFungible collection can be destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+});
+
+describe('(!negative test!) integration test: ext. destroyCollection():', () => {
+  it('(!negative test!) Destroy a collection that never existed', async () => {
+    await usingApi(async (api) => {
+      // Find the collection that never existed
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      await destroyCollectionExpectFailure(collectionId);
+    });
+  });
+  it('(!negative test!) Destroy a collection that has already been destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectSuccess(collectionId);
+    await destroyCollectionExpectFailure(collectionId);
+  });
+  it('(!negative test!) Destroy a collection using non-owner account', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectFailure(collectionId, '//Bob');
+    await destroyCollectionExpectSuccess(collectionId, '//Alice');
+  });
+});
addedtests/src/load_test_sc/loadtester.wasmdiffbeforeafterboth

binary blob — no preview

addedtests/src/load_test_sc/metadata.jsondiffbeforeafterboth
--- /dev/null
+++ b/tests/src/load_test_sc/metadata.json
@@ -0,0 +1,125 @@
+{
+  "metadataVersion": "0.1.0",
+  "source": {
+    "hash": "0x168cc3cba9657ad3950fb506e568751f99b90fb097685107f6101675662a8303",
+    "language": "ink! 3.0.0-rc2",
+    "compiler": "rustc 1.49.0-nightly"
+  },
+  "contract": {
+    "name": "loadtester",
+    "version": "0.1.0",
+    "authors": [
+      "[your_name] <[your_email]>"
+    ]
+  },
+  "spec": {
+    "constructors": [
+      {
+        "args": [],
+        "docs": [],
+        "name": [
+          "new"
+        ],
+        "selector": "0xd183512b"
+      }
+    ],
+    "docs": [],
+    "events": [],
+    "messages": [
+      {
+        "args": [
+          {
+            "name": "count",
+            "type": {
+              "displayName": [
+                "u64"
+              ],
+              "type": 2
+            }
+          }
+        ],
+        "docs": [],
+        "mutates": true,
+        "name": [
+          "bloat"
+        ],
+        "payable": false,
+        "returnType": null,
+        "selector": "0x49891c2a"
+      },
+      {
+        "args": [],
+        "docs": [],
+        "mutates": false,
+        "name": [
+          "get"
+        ],
+        "payable": false,
+        "returnType": {
+          "displayName": [
+            "u128"
+          ],
+          "type": 3
+        },
+        "selector": "0x1e5ca456"
+      }
+    ]
+  },
+  "storage": {
+    "struct": {
+      "fields": [
+        {
+          "layout": {
+            "struct": {
+              "fields": [
+                {
+                  "layout": {
+                    "cell": {
+                      "key": "0x0000000000000000000000000000000000000000000000000000000000000000",
+                      "ty": 1
+                    }
+                  },
+                  "name": "len"
+                },
+                {
+                  "layout": {
+                    "array": {
+                      "cellsPerElem": 1,
+                      "layout": {
+                        "cell": {
+                          "key": "0x0000000001000000000000000000000000000000000000000000000000000000",
+                          "ty": 2
+                        }
+                      },
+                      "len": 4294967295,
+                      "offset": "0x0100000000000000000000000000000000000000000000000000000000000000"
+                    }
+                  },
+                  "name": "elems"
+                }
+              ]
+            }
+          },
+          "name": "vector"
+        }
+      ]
+    }
+  },
+  "types": [
+    {
+      "def": {
+        "primitive": "u32"
+      }
+    },
+    {
+      "def": {
+        "primitive": "u64"
+      }
+    },
+    {
+      "def": {
+        "primitive": "u128"
+      }
+    }
+  ]
+}
\ No newline at end of file
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { ApiPromise } from "@polkadot/api";
 import { expect } from "chai";
 import usingApi from "./substrate/substrate-api";
@@ -6,20 +11,34 @@
   return api.runtimeMetadata.asLatest.modules.map(m => m.name.toString().toLowerCase());
 }
 
-describe('Pallet presence.', () => {
-  it('NFT pallet is present.', async () => {
+// Pallets that must always be present
+const requiredPallets = [
+  'nft', 'balances', 'contracts', 'randomnesscollectiveflip', 'system', 'timestamp', 'transactionpayment', 'treasury', 'vesting'
+];
+
+// Pallets that depend on consensus and governance configuration
+const consensusPallets = [
+  'sudo', 'grandpa', 'aura'
+];
+
+describe('Pallet presence', () => {
+  it('Required pallets are present', async () => {
     await usingApi(async api => {
-      expect(getModuleNames(api)).to.include('nft');
+      for (let i=0; i<requiredPallets.length; i++) {
+        expect(getModuleNames(api)).to.include(requiredPallets[i]);
+      }
     });
   });
-  it('Balances pallet is present.', async () => {
+  it('Governance and consensus pallets are present', async () => {
     await usingApi(async api => {
-      expect(getModuleNames(api)).to.include('balances');
+      for (let i=0; i<consensusPallets.length; i++) {
+        expect(getModuleNames(api)).to.include(consensusPallets[i]);
+      }
     });
   });
-  it('Contracts pallet is present.', async () => {
+  it('No extra pallets are included', async () => {
     await usingApi(async api => {
-      expect(getModuleNames(api)).to.include('contracts');
+      expect(getModuleNames(api).length).to.be.equal(requiredPallets.length + consensusPallets.length);
     });
   });
 });
addedtests/src/removeCollectionSponsor.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/removeCollectionSponsor.test.ts
@@ -0,0 +1,141 @@
+//
+// 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, 
+  setCollectionSponsorExpectSuccess, 
+  destroyCollectionExpectSuccess, 
+  setCollectionSponsorExpectFailure,
+  confirmSponsorshipExpectSuccess,
+  confirmSponsorshipExpectFailure,
+  createItemExpectSuccess,
+  findUnusedAddress,
+  getGenericResult,
+  enableWhiteListExpectSuccess,
+  enablePublicMintingExpectSuccess,
+  addToWhiteListExpectSuccess,
+  removeCollectionSponsorExpectSuccess,
+  removeCollectionSponsorExpectFailure,
+} from "./util/helpers";
+import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
+import type { AccountId } from '@polkadot/types/interfaces';
+import { BigNumber } from 'bignumber.js';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+let charlie: IKeyringPair;
+
+describe('integration test: ext. removeCollectionSponsor():', () => {
+
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+      charlie = keyring.addFromUri(`//Charlie`);
+    });
+  });
+
+  it('Remove NFT collection sponsor stops sponsorship', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+    await removeCollectionSponsorExpectSuccess(collectionId);
+
+    await usingApi(async (api) => {
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Mint token for unused address
+      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', zeroBalance.address);
+
+      // Transfer this tokens from unused address to Alice - should fail
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+      const zeroToAlice = api.tx.nft.transfer(alice.address, collectionId, itemId, 0);
+      const badTransaction = async function () { 
+        console.log = function () {};
+        console.error = function () {};
+        await submitTransactionAsync(zeroBalance, zeroToAlice);
+        delete console.log;
+        delete console.error;
+      };
+      await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;
+    });
+  });
+
+  it('Remove a sponsor after it was already removed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+    await removeCollectionSponsorExpectSuccess(collectionId);
+    await removeCollectionSponsorExpectSuccess(collectionId);
+  });
+
+  it('Remove sponsor in a collection that never had the sponsor set', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await removeCollectionSponsorExpectSuccess(collectionId);
+  });
+
+  it('Remove sponsor for a collection that had the sponsor set, but not confirmed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await removeCollectionSponsorExpectSuccess(collectionId);
+  });
+
+});
+
+describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+      charlie = keyring.addFromUri(`//Charlie`);
+    });
+  });
+
+  it('(!negative test!) Remove sponsor for a collection that never existed', async () => {
+    // Find the collection that never existed
+    const collectionId = 0;
+    await usingApi(async (api) => {
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+    });
+
+    await removeCollectionSponsorExpectFailure(collectionId);
+  });
+
+  it('(!negative test!) Remove sponsor in a destroyed collection', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await destroyCollectionExpectSuccess(collectionId);
+    await removeCollectionSponsorExpectFailure(collectionId);
+  });
+
+  it('Set - remove - confirm: fails', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await removeCollectionSponsorExpectSuccess(collectionId);
+    await confirmSponsorshipExpectFailure(collectionId, '//Bob');
+  });
+
+  it('Set - confirm - remove - confirm: Sponsor cannot come back', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+    await removeCollectionSponsorExpectSuccess(collectionId);
+    await confirmSponsorshipExpectFailure(collectionId, '//Bob');
+  });
+
+});
addedtests/src/rpc.load.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/rpc.load.ts
@@ -0,0 +1,145 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { expect, assert } from "chai";
+import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
+import { IKeyringPair } from "@polkadot/types/types";
+import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
+import { ApiPromise, Keyring } from "@polkadot/api";
+import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types";
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from './util/helpers'
+import fs from "fs";
+import privateKey from "./substrate/privateKey";
+
+const value = 0;
+const gasLimit = 500000n * 1000000n;
+const endowment = `1000000000000000`;
+
+
+function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<Blueprint> {
+  return new Promise<Blueprint>(async (resolve, reject) => {
+    const unsub = await code
+      .createBlueprint()
+      .signAndSend(alice, (result) => {
+        if (result.status.isInBlock || result.status.isFinalized) {
+          // here we have an additional field in the result, containing the blueprint
+          resolve(result.blueprint);
+          unsub();
+        }
+      })
+  });
+}
+
+function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
+  return new Promise<any>(async (resolve, reject) => {
+    const unsub = await blueprint.tx
+    .new(endowment, gasLimit)
+    .signAndSend(alice, (result) => {
+      if (result.status.isInBlock || result.status.isFinalized) {
+        unsub();
+        resolve(result);
+      }
+    });    
+  });
+}
+
+async function prepareDeployer(api: ApiPromise) {
+  // Find unused address
+  const deployer = await findUnusedAddress(api);
+
+  // Transfer balance to it
+  const keyring = new Keyring({ type: 'sr25519' });
+  const alice = keyring.addFromUri(`//Alice`);
+  let amount = new BigNumber(endowment);
+  amount = amount.plus(1e15);
+  const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
+  await submitTransactionAsync(alice, tx);
+
+  return deployer;
+}
+
+async function deployLoadTester(api: ApiPromise): Promise<[Contract, IKeyringPair]> {
+  const metadata = JSON.parse(fs.readFileSync('./src/load_test_sc/metadata.json').toString('utf-8'));
+  const abi = new Abi(metadata);
+
+  const deployer = await prepareDeployer(api);
+
+  const wasm = fs.readFileSync('./src/load_test_sc/loadtester.wasm');
+
+  const code = new CodePromise(api, abi, wasm);
+
+  const blueprint = await deployBlueprint(deployer, code);
+  const contract = (await deployContract(deployer, blueprint))['contract'] as Contract;
+
+  return [contract, deployer];
+}
+
+async function getScData(contract: Contract, deployer: IKeyringPair) {
+  const result = await contract.query.get(deployer.address, value, gasLimit);
+
+  if(!result.result.isSuccess) {
+    throw `Failed to get value`;
+  }
+  return result.result.asSuccess.data;
+}
+
+
+describe('RPC Tests', () => {
+  it('Simple RPC Load Test', async () => {
+    await usingApi(async api => {
+      let count = 0;
+      let hrTime = process.hrtime();
+      let microsec1 = hrTime[0] * 1000000 + hrTime[1] / 1000;
+      let rate = 0;
+      const checkPoint = 1000;
+      while (true) {
+        await api.rpc.system.chain();
+        count++;
+        process.stdout.write(`RPC reads: ${count} times at rate ${rate} r/s            \r`);
+    
+        if (count % checkPoint == 0) {
+          hrTime = process.hrtime();
+          let microsec2 = hrTime[0] * 1000000 + hrTime[1] / 1000;
+          rate = 1000000*checkPoint/(microsec2 - microsec1);
+          microsec1 = microsec2;
+        }
+      }
+    });
+  });
+
+  it.only('Smart Contract RPC Load Test', async () => {
+    await usingApi(async api => {
+
+      // Deploy smart contract
+      const [contract, deployer] = await deployLoadTester(api);
+
+      // Fill smart contract up with data
+      const bob = privateKey("//Bob");
+      const tx = contract.tx.bloat(value, gasLimit, 200);
+      await submitTransactionAsync(bob, tx);
+
+      // Run load test
+      let count = 0;
+      let hrTime = process.hrtime();
+      let microsec1 = hrTime[0] * 1000000 + hrTime[1] / 1000;
+      let rate = 0;
+      const checkPoint = 10;
+      while (true) {
+        await getScData(contract, deployer);
+        count++;
+        process.stdout.write(`SC reads: ${count} times at rate ${rate} r/s            \r`);
+    
+        if (count % checkPoint == 0) {
+          hrTime = process.hrtime();
+          let microsec2 = hrTime[0] * 1000000 + hrTime[1] / 1000;
+          rate = 1000000*checkPoint/(microsec2 - microsec1);
+          microsec1 = microsec2;
+        }
+      }
+    });
+  });
+
+});
addedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -0,0 +1,82 @@
+//
+// 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, 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;
+
+let bob: IKeyringPair;
+
+describe('integration test: ext. setCollectionSponsor():', () => {
+
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      bob = keyring.addFromUri(`//Bob`);
+    });
+  });
+
+  it('Set NFT collection sponsor', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+  });
+  it('Set Fungible collection sponsor', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+  });
+  it('Set ReFungible collection sponsor', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+  });
+
+  it('Set the same sponsor repeatedly', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+  });
+  it('Replace collection sponsor', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+
+    const keyring = new Keyring({ type: 'sr25519' });
+    const charlie = keyring.addFromUri(`//Charlie`);
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await setCollectionSponsorExpectSuccess(collectionId, charlie.address);
+  });
+});
+
+describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      bob = keyring.addFromUri(`//Bob`);
+    });
+  });
+
+  it('(!negative test!) Add sponsor with a non-owner', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await setCollectionSponsorExpectFailure(collectionId, bob.address, '//Bob');
+  });
+  it('(!negative test!) Add sponsor to a collection that never existed', async () => {
+    // Find the collection that never existed
+    const collectionId = 0;
+    await usingApi(async (api) => {
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+    });
+
+    await setCollectionSponsorExpectFailure(collectionId, bob.address);
+  });
+  it('(!negative test!) Add sponsor to a collection that was destroyed', async () => {
+    const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+    await destroyCollectionExpectSuccess(collectionId);
+    await setCollectionSponsorExpectFailure(collectionId, bob.address);
+  });
+});
modifiedtests/src/substrate/get-balance.tsdiffbeforeafterboth
--- a/tests/src/substrate/get-balance.ts
+++ b/tests/src/substrate/get-balance.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { ApiPromise } from "@polkadot/api";
 import promisifySubstrate from "./promisify-substrate";
 import {AccountInfo} from "@polkadot/types/interfaces/system";
modifiedtests/src/substrate/privateKey.tsdiffbeforeafterboth
--- a/tests/src/substrate/privateKey.ts
+++ b/tests/src/substrate/privateKey.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { Keyring } from "@polkadot/api";
 import { IKeyringPair } from "@polkadot/types/types";
 
modifiedtests/src/substrate/promisify-substrate.tsdiffbeforeafterboth
--- a/tests/src/substrate/promisify-substrate.ts
+++ b/tests/src/substrate/promisify-substrate.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import ApiPromise from "@polkadot/api/promise/Api";
 
 type PromiseType<T> = T extends PromiseLike<infer TInner> ? TInner : T;
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -1,7 +1,15 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { WsProvider, ApiPromise } from "@polkadot/api";
+import type { AccountId, Address, ApplyExtrinsicResult, DispatchError, DispatchInfo, EventRecord, Extrinsic, ExtrinsicStatus, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
+import { IKeyringPair } from "@polkadot/types/types";
+
 import config from "../config";
 import promisifySubstrate from "./promisify-substrate";
-import { ApiOptions } from "@polkadot/api/types";
+import { ApiOptions, SubmittableExtrinsic, ApiTypes } from "@polkadot/api/types";
 import rtt from "../../../runtime_types.json";
 
 function defaultApiOptions(): ApiOptions {
@@ -23,4 +31,50 @@
   } finally {
     await api.disconnect();
   }
+}
+
+export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
+  return new Promise(async function(resolve, reject) {
+    try {
+      await transaction.signAndSend(sender, ({ events = [], status }) => {
+        if (status.isReady) {
+          // nothing to do
+          // console.log(`Current tx status is Ready`);
+        } else if (status.isBroadcast) {
+          // nothing to do
+          // console.log(`Current tx status is Broadcast`);
+        } else if (status.isInBlock || status.isFinalized) {
+          resolve(events);
+        } else {
+          console.log(`Something went wrong with transaction. Status: ${status}`);
+          reject("Transaction failed");
+        }
+      });
+    } catch (e) {
+      console.log("Error: ", e);
+      reject(e);
+    }
+  });
+}
+
+export function submitTransactionExpectFailAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
+  return new Promise(async function(resolve, reject) {
+    try {
+      await transaction.signAndSend(sender, ({ events = [], status }) => {
+        if (status.isReady) {
+          // nothing to do
+          // console.log(`Current tx status is Ready`);
+        } else if (status.isBroadcast) {
+          // nothing to do
+          // console.log(`Current tx status is Broadcast`);
+        } else if (status.isInBlock || status.isFinalized) {
+          resolve(events);
+        } else {
+          reject("Transaction failed");
+        }
+      });
+    } catch (e) {
+      reject(e);
+    }
+  });
 }
\ No newline at end of file
modifiedtests/src/substrate/wait-new-blocks.tsdiffbeforeafterboth
--- a/tests/src/substrate/wait-new-blocks.ts
+++ b/tests/src/substrate/wait-new-blocks.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
 import { ApiPromise } from "@polkadot/api";
 
 export default function waitNewBlocks(api: ApiPromise, blocksCount: number = 1): Promise<void> {
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -1,10 +1,15 @@
-import { expect } from "chai";
-import usingApi from "./substrate/substrate-api";
-import promisifySubstrate from "./substrate/promisify-substrate";
-import waitNewBlocks from "./substrate/wait-new-blocks";
-import { alicesPublicKey, bobsPublicKey } from "./accounts";
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { expect, assert } from "chai";
+import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
+import { alicesPublicKey, bobsPublicKey, ferdiesPublicKey } from "./accounts";
 import privateKey from "./substrate/privateKey";
 import getBalance from "./substrate/get-balance";
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from './util/helpers'
 
 describe('Transfer', () => {
   it('Balance transfers', async () => {
@@ -14,15 +19,31 @@
       const alicePrivateKey = privateKey('//Alice');
       
       const transfer = api.tx.balances.transfer(bobsPublicKey, 1n);
-      
-      await promisifySubstrate(api, () => transfer.signAndSend(alicePrivateKey))();
+      const result = await submitTransactionAsync(alicePrivateKey, transfer);
 
-      await waitNewBlocks(api);
-  
       const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
 
       expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;
       expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;
     });
   });
+
+  it('Inability to pay fees error message is correct', async () => {
+    await usingApi(async api => {
+      // Find unused address
+      const pk = await findUnusedAddress(api);
+
+      console.log = function () {};
+      console.error = function () {};
+  
+      const badTransfer = api.tx.balances.transfer(bobsPublicKey, 1n);
+      const badTransaction = async function () { 
+        const result = await submitTransactionAsync(pk, badTransfer);
+      };
+      await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+
+      delete console.log;
+      delete console.error;
+    });
+  });
 });
addedtests/src/util/helpers.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/util/helpers.ts
@@ -0,0 +1,398 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import { ApiPromise, Keyring } from "@polkadot/api";
+import { default as usingApi, submitTransactionAsync } from "../substrate/substrate-api";
+import privateKey from '../substrate/privateKey';
+import { alicesPublicKey, nullPublicKey } from "../accounts";
+import { strToUTF16, utf16ToStr, hexToStr } from '../util/util';
+import { IKeyringPair } from "@polkadot/types/types";
+import { BigNumber } from 'bignumber.js';
+import { Struct, Enum } from '@polkadot/types/codec';
+import { u128 } from '@polkadot/types/primitive';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+type GenericResult = {
+  success: boolean,
+};
+
+type CreateCollectionResult = {
+  success: boolean,
+  collectionId: number
+};
+
+type CreateItemResult = {
+  success: boolean,
+  collectionId: number,
+  itemId: number
+};
+
+export function getGenericResult(events: EventRecord[]): GenericResult {
+  let result: GenericResult = {
+    success: false
+  }
+  events.forEach(({ phase, event: { data, method, section } }) => {
+    // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
+    if (method == 'ExtrinsicSuccess') {
+      result.success = true;
+    }
+  });
+  return result;
+}
+
+function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {
+  let success = false;
+  let collectionId: number = 0;
+  events.forEach(({ phase, event: { data, method, section } }) => {
+    // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
+    if (method == 'ExtrinsicSuccess') {
+      success = true;
+    } else if ((section == 'nft') && (method == 'Created')) {
+      collectionId = parseInt(data[0].toString());
+    }
+  });
+  let result: CreateCollectionResult = {
+    success,
+    collectionId
+  }
+  return result;
+}
+
+function getCreateItemResult(events: EventRecord[]): CreateItemResult {
+  let success = false;
+  let collectionId: number = 0;
+  let itemId: number = 0;
+  events.forEach(({ phase, event: { data, method, section } }) => {
+    // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
+    if (method == 'ExtrinsicSuccess') {
+      success = true;
+    } else if ((section == 'nft') && (method == 'ItemCreated')) {
+      collectionId = parseInt(data[0].toString());
+      itemId = parseInt(data[1].toString());
+    }
+  });
+  let result: CreateItemResult = {
+    success,
+    collectionId,
+    itemId
+  }
+  return result;
+}
+
+export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string): Promise<number> {
+  let collectionId: number = 0;
+  await usingApi(async (api) => {
+    // Get number of collections before the transaction
+    const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+
+    // Run the CreateCollection transaction
+    const alicePrivateKey = privateKey('//Alice');
+    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getCreateCollectionResult(events);
+
+    // Get number of collections after the transaction
+    const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(result.collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(result.collectionId).to.be.equal(BcollectionCount);
+    expect(collection).to.be.not.null;
+    expect(BcollectionCount).to.be.equal(AcollectionCount+1, 'Error: NFT collection NOT created.');
+    expect(collection.Owner).to.be.equal(alicesPublicKey);
+    expect(utf16ToStr(collection.Name)).to.be.equal(name);
+    expect(utf16ToStr(collection.Description)).to.be.equal(description);
+    expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);
+
+    collectionId = result.collectionId;
+  });
+
+  return collectionId;
+}
+  
+export async function createCollectionExpectFailure(name: string, description: string, tokenPrefix: string, mode: string) {
+  await usingApi(async (api) => {
+    // Get number of collections before the transaction
+    const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+
+    // Run the CreateCollection transaction
+    const alicePrivateKey = privateKey('//Alice');
+    const tx = api.tx.nft.createCollection(name, description, tokenPrefix, mode);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getCreateCollectionResult(events);
+
+    // Get number of collections after the transaction
+    const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+
+    // What to expect
+    expect(result.success).to.be.false;
+    expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');
+  });
+}
+  
+export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
+  let bal = new BigNumber(0);
+  let unused;
+  do {
+    const randomSeed = 'seed' +  Math.floor(Math.random() * Math.floor(10000));
+    const keyring = new Keyring({ type: 'sr25519' });
+    unused = keyring.addFromUri(`//${randomSeed}`);
+    bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());
+  } while (bal.toFixed() != '0');
+  return unused; 
+}
+
+function getDestroyResult(events: EventRecord[]): boolean {
+  let success: boolean = false;
+  events.forEach(({ phase, event: { data, method, section } }) => {
+    // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
+    if (method == 'ExtrinsicSuccess') {
+      success = true;
+    }
+  });
+  return success;
+}
+
+export async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+    // Run the DestroyCollection transaction
+    const alicePrivateKey = privateKey(senderSeed);
+    const tx = api.tx.nft.destroyCollection(collectionId);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getDestroyResult(events);
+
+    // What to expect
+    expect(result).to.be.false;
+  });
+}
+
+export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+    // Run the DestroyCollection transaction
+    const alicePrivateKey = privateKey(senderSeed);
+    const tx = api.tx.nft.destroyCollection(collectionId);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getDestroyResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result).to.be.true;
+    expect(collection).to.be.not.null;
+    expect(collection.Owner).to.be.equal(nullPublicKey);
+  });
+}
+
+export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const alicePrivateKey = privateKey('//Alice');
+    const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getGenericResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(collection.Sponsor.toString()).to.be.equal(sponsor.toString());
+    expect(collection.SponsorConfirmed).to.be.false;
+  });
+}
+
+export async function removeCollectionSponsorExpectSuccess(collectionId: number) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const alicePrivateKey = privateKey('//Alice');
+    const tx = api.tx.nft.removeCollectionSponsor(collectionId);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getGenericResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(collection.Sponsor).to.be.equal(nullPublicKey);
+    expect(collection.SponsorConfirmed).to.be.false;
+  });
+}
+
+export async function removeCollectionSponsorExpectFailure(collectionId: number) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const alicePrivateKey = privateKey('//Alice');
+    const tx = api.tx.nft.removeCollectionSponsor(collectionId);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getGenericResult(events);
+
+    // What to expect
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const alicePrivateKey = privateKey(senderSeed);
+    const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getGenericResult(events);
+
+    // What to expect
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const sender = privateKey(senderSeed);
+    const tx = api.tx.nft.confirmSponsorship(collectionId);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(collection.Sponsor).to.be.equal(sender.address);
+    expect(collection.SponsorConfirmed).to.be.true;
+  });
+}
+
+export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const sender = privateKey(senderSeed);
+    const tx = api.tx.nft.confirmSponsorship(collectionId);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    // What to expect
+    expect(result.success).to.be.false;
+  });
+}
+
+export interface CreateFungibleData extends Struct {
+  readonly value: u128;
+};
+
+export interface CreateReFungibleData extends Struct {};
+export interface CreateNftData extends Struct {};
+
+export interface CreateItemData extends Enum {
+  NFT: CreateNftData,
+  Fungible: CreateFungibleData,
+  ReFungible: CreateReFungibleData
+};
+
+export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
+  let newItemId: number = 0;
+  await usingApi(async (api) => {
+    const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
+    const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();    
+    const AItemBalance = new BigNumber(Aitem.Value);
+
+    if (owner === '') owner = sender.address;
+
+    let tx;
+    if (createMode == 'Fungible') {
+      let createData = {fungible: {value: 10}};
+      tx = api.tx.nft.createItem(collectionId, owner, createData);
+    }
+    else {
+      tx = api.tx.nft.createItem(collectionId, owner, createMode);
+    }
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getCreateItemResult(events);
+
+    const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
+    const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();    
+    const BItemBalance = new BigNumber(Bitem.Value);
+
+    // What to expect
+    expect(result.success).to.be.true;
+    if (createMode == 'Fungible') {
+      expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);
+    }
+    else {
+      expect(BItemCount).to.be.equal(AItemCount+1);
+    }
+    expect(collectionId).to.be.equal(result.collectionId);
+    expect(BItemCount).to.be.equal(result.itemId);
+    newItemId = result.itemId;
+  });
+  return newItemId;
+}
+
+export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(collection.Access).to.be.equal('WhiteList');
+  });
+}
+
+export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const tx = api.tx.nft.setMintPermission(collectionId, true);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(collection.MintMode).to.be.equal(true);
+  });
+}
+
+export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const tx = api.tx.nft.addToWhiteList(collectionId, address);
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    // Get the collection 
+    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+    // What to expect
+    expect(result.success).to.be.true;
+    expect(collection.MintMode).to.be.equal(true);
+  });
+}
+
addedtests/src/util/util.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/util/util.ts
@@ -0,0 +1,35 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+export function strToUTF16(str: string): any {
+  let buf: number[] = [];
+  for (let i=0, strLen=str.length; i < strLen; i++) {
+    buf.push(str.charCodeAt(i));
+  }
+  return buf;
+}
+
+export function utf16ToStr(buf: number[]): string {
+  let str: string = "";
+  for (let i=0, strLen=buf.length; i < strLen; i++) {
+    if (buf[i] != 0) str += String.fromCharCode(buf[i]);
+    else break;
+  }
+  return str;
+}
+
+export function hexToStr(buf: string): string {
+  let str: string = "";
+  let hexStart = buf.indexOf("0x");
+  if (hexStart < 0) hexStart = 0;
+  else hexStart = 2;  
+  for (let i=hexStart, strLen=buf.length; i < strLen; i+=2) {
+    let ch = buf[i] + buf[i+1];
+    let num = parseInt(ch, 16);
+    if (num != 0) str += String.fromCharCode(num);
+    else break;
+  }
+  return str;
+}