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

difftreelog

Merge branch 'develop' into feature/CORE-162

Igor Kozyrev2021-09-01parents: #d8da38d #4d7077f.patch.diff
in: master

96 files changed

modified.github/workflows/node_build_test.ymldiffbeforeafterboth
--- a/.github/workflows/node_build_test.yml
+++ b/.github/workflows/node_build_test.yml
@@ -31,11 +31,11 @@
           username: ${{ secrets.SERVER_USERNAME }}
           key: ${{ secrets.KEY }}
           port: ${{ secrets.SERVER_PORT }}
-          command_timeout: 240m
+          command_timeout: 300m
           script: |
             eval $(ssh-agent -s)
-            ssh-add /home/polkadot/.ssh/git_hub
-            git clone git@github.com:usetech-llc/nft_private.git
+            ssh-add /home/devops/.ssh/git_hub
+            git clone git@github.com:UniqueNetwork/nft_private.git
             cd nft_private
             git checkout develop
             # git pull --all
added.maintain/frame-weight-template.hbsdiffbeforeafterboth
--- /dev/null
+++ b/.maintain/frame-weight-template.hbs
@@ -0,0 +1,95 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for {{pallet}}
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}
+//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: {{cmd.repeat}}, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`
+//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}}
+
+// Executed Command:
+{{#each args as |arg|~}}
+// {{arg}}
+{{/each}}
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for {{pallet}}.
+pub trait WeightInfo {
+	{{~#each benchmarks as |benchmark|}}
+	fn {{benchmark.name~}}
+	(
+		{{~#each benchmark.components as |c| ~}}
+		{{c.name}}: u32, {{/each~}}
+	) -> Weight;
+	{{~/each}}
+}
+
+/// Weights for {{pallet}} using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	{{~#each benchmarks as |benchmark|}}
+	{{~#each benchmark.comments as |comment|}}
+	// {{comment}}
+	{{~/each}}
+	fn {{benchmark.name~}}
+	(
+		{{~#each benchmark.components as |c| ~}}
+		{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
+	) -> Weight {
+		({{underscore benchmark.base_weight}} as Weight)
+			{{~#each benchmark.component_weight as |cw|}}
+			// Standard Error: {{underscore cw.error}}
+			.saturating_add(({{underscore cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight))
+			{{~/each}}
+			{{~#if (ne benchmark.base_reads "0")}}
+			.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight))
+			{{~/if}}
+			{{~#each benchmark.component_reads as |cr|}}
+			.saturating_add(T::DbWeight::get().reads(({{cr.slope}} as Weight).saturating_mul({{cr.name}} as Weight)))
+			{{~/each}}
+			{{~#if (ne benchmark.base_writes "0")}}
+			.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight))
+			{{~/if}}
+			{{~#each benchmark.component_writes as |cw|}}
+			.saturating_add(T::DbWeight::get().writes(({{cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight)))
+			{{~/each}}
+	}
+	{{~/each}}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	{{~#each benchmarks as |benchmark|}}
+	{{~#each benchmark.comments as |comment|}}
+	// {{comment}}
+	{{~/each}}
+	fn {{benchmark.name~}}
+	(
+		{{~#each benchmark.components as |c| ~}}
+		{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
+	) -> Weight {
+		({{underscore benchmark.base_weight}} as Weight)
+			{{~#each benchmark.component_weight as |cw|}}
+			// Standard Error: {{underscore cw.error}}
+			.saturating_add(({{underscore cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight))
+			{{~/each}}
+			{{~#if (ne benchmark.base_reads "0")}}
+			.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}} as Weight))
+			{{~/if}}
+			{{~#each benchmark.component_reads as |cr|}}
+			.saturating_add(RocksDbWeight::get().reads(({{cr.slope}} as Weight).saturating_mul({{cr.name}} as Weight)))
+			{{~/each}}
+			{{~#if (ne benchmark.base_writes "0")}}
+			.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}} as Weight))
+			{{~/if}}
+			{{~#each benchmark.component_writes as |cw|}}
+			.saturating_add(RocksDbWeight::get().writes(({{cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight)))
+			{{~/each}}
+	}
+	{{~/each}}
+}
\ No newline at end of file
added.maintain/scripts/compile_stub.shdiffbeforeafterboth
--- /dev/null
+++ b/.maintain/scripts/compile_stub.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -eu
+
+dir=$PWD
+
+tmp=$(mktemp -d)
+cd $tmp
+cp $dir/$INPUT input.sol
+solcjs --optimize --bin input.sol
+
+mv input_sol_$(basename $OUTPUT .raw).bin out.bin
+xxd -r -p out.bin out.raw
+
+mv out.raw $dir/$OUTPUT
\ No newline at end of file
added.maintain/scripts/generate_api.shdiffbeforeafterboth
--- /dev/null
+++ b/.maintain/scripts/generate_api.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -eu
+
+tmp=$(mktemp)
+cargo test --package $PACKAGE -- $NAME --exact --nocapture --ignored | tee $tmp
+raw=$(mktemp --suffix .sol)
+sed -n '/=== SNIP START ===/, /=== SNIP END ===/{ /=== SNIP START ===/! { /=== SNIP END ===/! p } }' $tmp > $raw
+formatted=$(mktemp)
+prettier --use-tabs $raw > $formatted
+solhint --fix $formatted
+
+mv $formatted $OUTPUT
\ No newline at end of file
modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -704,6 +704,117 @@
 ]
 
 [[package]]
+name = "bp-header-chain"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "finality-grandpa",
+ "frame-support",
+ "parity-scale-codec",
+ "serde",
+ "sp-core",
+ "sp-finality-grandpa",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
+name = "bp-messages"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "bitvec 0.20.4",
+ "bp-runtime",
+ "frame-support",
+ "frame-system",
+ "impl-trait-for-tuples",
+ "parity-scale-codec",
+ "serde",
+ "sp-std",
+]
+
+[[package]]
+name = "bp-polkadot-core"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "bp-messages",
+ "bp-runtime",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "sp-api",
+ "sp-core",
+ "sp-runtime",
+ "sp-std",
+ "sp-version",
+]
+
+[[package]]
+name = "bp-rococo"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "bp-messages",
+ "bp-polkadot-core",
+ "bp-runtime",
+ "frame-support",
+ "parity-scale-codec",
+ "smallvec 1.6.1",
+ "sp-api",
+ "sp-runtime",
+ "sp-std",
+ "sp-version",
+]
+
+[[package]]
+name = "bp-runtime"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "frame-support",
+ "hash-db",
+ "num-traits",
+ "parity-scale-codec",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-state-machine",
+ "sp-std",
+ "sp-trie",
+]
+
+[[package]]
+name = "bp-test-utils"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "bp-header-chain",
+ "ed25519-dalek",
+ "finality-grandpa",
+ "parity-scale-codec",
+ "sp-application-crypto",
+ "sp-finality-grandpa",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
+name = "bp-wococo"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "bp-messages",
+ "bp-polkadot-core",
+ "bp-rococo",
+ "bp-runtime",
+ "parity-scale-codec",
+ "sp-api",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "bs58"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3779,6 +3890,91 @@
 ]
 
 [[package]]
+name = "kusama-runtime"
+version = "0.9.8"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "beefy-primitives",
+ "bitvec 0.20.4",
+ "frame-benchmarking",
+ "frame-election-provider-support",
+ "frame-executive",
+ "frame-support",
+ "frame-system",
+ "frame-system-benchmarking",
+ "frame-system-rpc-runtime-api",
+ "frame-try-runtime",
+ "hex-literal",
+ "log",
+ "max-encoded-len",
+ "pallet-authority-discovery",
+ "pallet-authorship",
+ "pallet-babe",
+ "pallet-balances",
+ "pallet-bounties",
+ "pallet-collective",
+ "pallet-democracy",
+ "pallet-election-provider-multi-phase",
+ "pallet-elections-phragmen",
+ "pallet-gilt",
+ "pallet-grandpa",
+ "pallet-identity",
+ "pallet-im-online",
+ "pallet-indices",
+ "pallet-membership",
+ "pallet-mmr-primitives",
+ "pallet-multisig",
+ "pallet-nicks",
+ "pallet-offences",
+ "pallet-offences-benchmarking",
+ "pallet-proxy",
+ "pallet-recovery",
+ "pallet-scheduler 3.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8)",
+ "pallet-session",
+ "pallet-session-benchmarking",
+ "pallet-society",
+ "pallet-staking",
+ "pallet-staking-reward-fn",
+ "pallet-timestamp",
+ "pallet-tips",
+ "pallet-transaction-payment",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "pallet-treasury",
+ "pallet-utility",
+ "pallet-vesting",
+ "pallet-xcm",
+ "parity-scale-codec",
+ "polkadot-primitives",
+ "polkadot-runtime-common",
+ "polkadot-runtime-parachains",
+ "rustc-hex",
+ "serde",
+ "serde_derive",
+ "smallvec 1.6.1",
+ "sp-api",
+ "sp-arithmetic",
+ "sp-authority-discovery",
+ "sp-block-builder",
+ "sp-consensus-babe",
+ "sp-core",
+ "sp-inherents",
+ "sp-io",
+ "sp-npos-elections",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-session",
+ "sp-staking",
+ "sp-std",
+ "sp-transaction-pool",
+ "sp-version",
+ "static_assertions",
+ "substrate-wasm-builder 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8)",
+ "xcm",
+ "xcm-builder",
+ "xcm-executor",
+]
+
+[[package]]
 name = "kv-log-macro"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4935,12 +5131,15 @@
 name = "nft-data-structs"
 version = "0.9.0"
 dependencies = [
+ "derivative",
  "frame-support",
  "frame-system",
+ "max-encoded-len",
  "parity-scale-codec",
  "serde",
  "sp-core",
  "sp-runtime",
+ "sp-std",
 ]
 
 [[package]]
@@ -4999,6 +5198,7 @@
  "cumulus-primitives-core",
  "cumulus-primitives-timestamp",
  "cumulus-primitives-utility",
+ "derivative",
  "fp-rpc",
  "frame-benchmarking",
  "frame-executive",
@@ -5007,6 +5207,7 @@
  "frame-system-benchmarking",
  "frame-system-rpc-runtime-api",
  "hex-literal",
+ "max-encoded-len",
  "nft-data-structs",
  "pallet-aura",
  "pallet-balances",
@@ -5014,6 +5215,7 @@
  "pallet-evm",
  "pallet-evm-coder-substrate",
  "pallet-evm-contract-helpers",
+ "pallet-evm-migration",
  "pallet-evm-transaction-payment",
  "pallet-inflation",
  "pallet-nft",
@@ -5316,6 +5518,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-treasury",
@@ -5325,10 +5528,32 @@
 ]
 
 [[package]]
+name = "pallet-bridge-grandpa"
+version = "0.1.0"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "bp-header-chain",
+ "bp-runtime",
+ "bp-test-utils",
+ "finality-grandpa",
+ "frame-support",
+ "frame-system",
+ "log",
+ "num-traits",
+ "parity-scale-codec",
+ "serde",
+ "sp-finality-grandpa",
+ "sp-runtime",
+ "sp-std",
+ "sp-trie",
+]
+
+[[package]]
 name = "pallet-collective"
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "log",
@@ -5418,11 +5643,13 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
  "log",
  "parity-scale-codec",
+ "rand 0.7.3",
  "sp-arithmetic",
  "sp-core",
  "sp-io",
@@ -5437,6 +5664,7 @@
 version = "4.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "log",
@@ -5537,6 +5765,22 @@
 ]
 
 [[package]]
+name = "pallet-evm-migration"
+version = "0.1.0"
+dependencies = [
+ "fp-evm",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "pallet-evm",
+ "parity-scale-codec",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "pallet-evm-transaction-payment"
 version = "0.1.0"
 dependencies = [
@@ -5554,6 +5798,20 @@
 ]
 
 [[package]]
+name = "pallet-gilt"
+version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "sp-arithmetic",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "pallet-grandpa"
 version = "3.1.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
@@ -5595,6 +5853,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "log",
@@ -5613,6 +5872,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "parity-scale-codec",
@@ -5712,6 +5972,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "parity-scale-codec",
@@ -5817,10 +6078,33 @@
 ]
 
 [[package]]
+name = "pallet-offences-benchmarking"
+version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+dependencies = [
+ "frame-benchmarking",
+ "frame-election-provider-support",
+ "frame-support",
+ "frame-system",
+ "pallet-babe",
+ "pallet-balances",
+ "pallet-grandpa",
+ "pallet-im-online",
+ "pallet-offences",
+ "pallet-session",
+ "pallet-staking",
+ "parity-scale-codec",
+ "sp-runtime",
+ "sp-staking",
+ "sp-std",
+]
+
+[[package]]
 name = "pallet-proxy"
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "max-encoded-len",
@@ -5845,36 +6129,50 @@
 ]
 
 [[package]]
+name = "pallet-recovery"
+version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+dependencies = [
+ "enumflags2",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "pallet-scheduler"
 version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
  "log",
  "parity-scale-codec",
- "serde",
- "sp-core",
  "sp-io",
  "sp-runtime",
  "sp-std",
- "substrate-test-utils",
- "up-sponsorship",
 ]
 
 [[package]]
 name = "pallet-scheduler"
 version = "3.0.0"
-source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
  "log",
  "parity-scale-codec",
+ "serde",
+ "sp-core",
  "sp-io",
  "sp-runtime",
  "sp-std",
+ "substrate-test-utils",
+ "up-sponsorship",
 ]
 
 [[package]]
@@ -5898,10 +6196,40 @@
 ]
 
 [[package]]
+name = "pallet-session-benchmarking"
+version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "pallet-session",
+ "pallet-staking",
+ "rand 0.7.3",
+ "sp-runtime",
+ "sp-session",
+ "sp-std",
+]
+
+[[package]]
+name = "pallet-society"
+version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "rand_chacha 0.2.2",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "pallet-staking"
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
@@ -5910,6 +6238,7 @@
  "pallet-session",
  "parity-scale-codec",
  "paste",
+ "rand_chacha 0.2.2",
  "serde",
  "sp-application-crypto",
  "sp-io",
@@ -5931,6 +6260,15 @@
 ]
 
 [[package]]
+name = "pallet-staking-reward-fn"
+version = "3.0.0"
+source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+dependencies = [
+ "log",
+ "sp-arithmetic",
+]
+
+[[package]]
 name = "pallet-sudo"
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
@@ -5966,6 +6304,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-treasury",
@@ -6024,6 +6363,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "impl-trait-for-tuples",
@@ -6039,6 +6379,7 @@
 version = "3.0.0"
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "parity-scale-codec",
@@ -6054,6 +6395,7 @@
 source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
  "enumflags2",
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "parity-scale-codec",
@@ -6069,6 +6411,7 @@
  "frame-support",
  "frame-system",
  "parity-scale-codec",
+ "serde",
  "sp-runtime",
  "sp-std",
  "xcm",
@@ -7144,12 +7487,15 @@
 dependencies = [
  "beefy-primitives",
  "bitvec 0.20.4",
+ "frame-benchmarking",
  "frame-election-provider-support",
  "frame-executive",
  "frame-support",
  "frame-system",
+ "frame-system-benchmarking",
  "frame-system-rpc-runtime-api",
  "frame-try-runtime",
+ "hex-literal",
  "log",
  "max-encoded-len",
  "pallet-authority-discovery",
@@ -7170,9 +7516,11 @@
  "pallet-multisig",
  "pallet-nicks",
  "pallet-offences",
+ "pallet-offences-benchmarking",
  "pallet-proxy",
  "pallet-scheduler 3.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8)",
  "pallet-session",
+ "pallet-session-benchmarking",
  "pallet-staking",
  "pallet-staking-reward-curve",
  "pallet-timestamp",
@@ -7215,12 +7563,14 @@
 dependencies = [
  "beefy-primitives",
  "bitvec 0.20.4",
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "impl-trait-for-tuples",
  "libsecp256k1 0.3.5",
  "log",
  "pallet-authorship",
+ "pallet-babe",
  "pallet-balances",
  "pallet-beefy",
  "pallet-election-provider-multi-phase",
@@ -7258,8 +7608,10 @@
 dependencies = [
  "bitvec 0.20.4",
  "derive_more",
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
+ "libsecp256k1 0.3.5",
  "log",
  "pallet-authority-discovery",
  "pallet-authorship",
@@ -7299,6 +7651,7 @@
  "frame-system-rpc-runtime-api",
  "futures 0.3.16",
  "hex-literal",
+ "kusama-runtime",
  "kvdb",
  "kvdb-rocksdb",
  "pallet-babe",
@@ -7334,6 +7687,7 @@
  "polkadot-runtime",
  "polkadot-runtime-parachains",
  "polkadot-statement-distribution",
+ "rococo-runtime",
  "sc-authority-discovery",
  "sc-basic-authorship",
  "sc-block-builder",
@@ -7375,6 +7729,7 @@
  "substrate-prometheus-endpoint",
  "thiserror",
  "tracing",
+ "westend-runtime",
 ]
 
 [[package]]
@@ -8139,6 +8494,73 @@
 ]
 
 [[package]]
+name = "rococo-runtime"
+version = "0.9.8"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "beefy-primitives",
+ "bp-rococo",
+ "bp-wococo",
+ "frame-executive",
+ "frame-support",
+ "frame-system",
+ "frame-system-rpc-runtime-api",
+ "hex-literal",
+ "log",
+ "max-encoded-len",
+ "pallet-authority-discovery",
+ "pallet-authorship",
+ "pallet-babe",
+ "pallet-balances",
+ "pallet-beefy",
+ "pallet-bridge-grandpa",
+ "pallet-collective",
+ "pallet-grandpa",
+ "pallet-im-online",
+ "pallet-indices",
+ "pallet-membership",
+ "pallet-mmr",
+ "pallet-mmr-primitives",
+ "pallet-offences",
+ "pallet-proxy",
+ "pallet-session",
+ "pallet-staking",
+ "pallet-staking-reward-curve",
+ "pallet-sudo",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "pallet-utility",
+ "pallet-xcm",
+ "parity-scale-codec",
+ "polkadot-parachain",
+ "polkadot-primitives",
+ "polkadot-runtime-common",
+ "polkadot-runtime-parachains",
+ "serde",
+ "serde_derive",
+ "smallvec 1.6.1",
+ "sp-api",
+ "sp-authority-discovery",
+ "sp-block-builder",
+ "sp-consensus-babe",
+ "sp-core",
+ "sp-inherents",
+ "sp-io",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-session",
+ "sp-staking",
+ "sp-std",
+ "sp-transaction-pool",
+ "sp-version",
+ "substrate-wasm-builder 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8)",
+ "xcm",
+ "xcm-builder",
+ "xcm-executor",
+]
+
+[[package]]
 name = "rpassword"
 version = "5.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -10690,13 +11112,13 @@
 [[package]]
 name = "substrate-wasm-builder"
 version = "4.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93a3d51ad6abbc408b03ea962062bfcc959b438a318d7d4bedd181e1effd0610"
+source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
 dependencies = [
  "ansi_term 0.12.1",
  "atty",
  "build-helper",
- "cargo_metadata 0.12.3",
+ "cargo_metadata 0.13.1",
+ "sp-maybe-compressed-blob",
  "tempfile",
  "toml",
  "walkdir",
@@ -10706,13 +11128,13 @@
 [[package]]
 name = "substrate-wasm-builder"
 version = "4.0.0"
-source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8#74101dc21cfffb4c2d014fcc28edc166d5ca1b16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93a3d51ad6abbc408b03ea962062bfcc959b438a318d7d4bedd181e1effd0610"
 dependencies = [
  "ansi_term 0.12.1",
  "atty",
  "build-helper",
- "cargo_metadata 0.13.1",
- "sp-maybe-compressed-blob",
+ "cargo_metadata 0.12.3",
  "tempfile",
  "toml",
  "walkdir",
@@ -12039,6 +12461,89 @@
 ]
 
 [[package]]
+name = "westend-runtime"
+version = "0.9.8"
+source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.8#3a10ee63c0b5703a1c802db3438ab7e01344a8ce"
+dependencies = [
+ "beefy-primitives",
+ "bitvec 0.20.4",
+ "frame-benchmarking",
+ "frame-election-provider-support",
+ "frame-executive",
+ "frame-support",
+ "frame-system",
+ "frame-system-benchmarking",
+ "frame-system-rpc-runtime-api",
+ "frame-try-runtime",
+ "hex-literal",
+ "log",
+ "max-encoded-len",
+ "pallet-authority-discovery",
+ "pallet-authorship",
+ "pallet-babe",
+ "pallet-balances",
+ "pallet-collective",
+ "pallet-democracy",
+ "pallet-election-provider-multi-phase",
+ "pallet-elections-phragmen",
+ "pallet-grandpa",
+ "pallet-identity",
+ "pallet-im-online",
+ "pallet-indices",
+ "pallet-membership",
+ "pallet-mmr-primitives",
+ "pallet-multisig",
+ "pallet-nicks",
+ "pallet-offences",
+ "pallet-offences-benchmarking",
+ "pallet-proxy",
+ "pallet-recovery",
+ "pallet-scheduler 3.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8)",
+ "pallet-session",
+ "pallet-session-benchmarking",
+ "pallet-society",
+ "pallet-staking",
+ "pallet-staking-reward-curve",
+ "pallet-sudo",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "pallet-treasury",
+ "pallet-utility",
+ "pallet-vesting",
+ "pallet-xcm",
+ "parity-scale-codec",
+ "polkadot-parachain",
+ "polkadot-primitives",
+ "polkadot-runtime-common",
+ "polkadot-runtime-parachains",
+ "rustc-hex",
+ "serde",
+ "serde_derive",
+ "smallvec 1.6.1",
+ "sp-api",
+ "sp-authority-discovery",
+ "sp-block-builder",
+ "sp-consensus-babe",
+ "sp-core",
+ "sp-inherents",
+ "sp-io",
+ "sp-npos-elections",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-session",
+ "sp-staking",
+ "sp-std",
+ "sp-transaction-pool",
+ "sp-version",
+ "static_assertions",
+ "substrate-wasm-builder 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.8)",
+ "xcm",
+ "xcm-builder",
+ "xcm-executor",
+]
+
+[[package]]
 name = "which"
 version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
addedMakefilediffbeforeafterboth
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,43 @@
+.PHONY: _eth_codegen
+_eth_codegen:
+
+.PHONY: regenerate_solidity
+regenerate_solidity:
+	PACKAGE=pallet-nft NAME=eth::erc::fungible_iface OUTPUT=./tests/src/eth/api/UniqueFungible.sol ./.maintain/scripts/generate_api.sh
+	PACKAGE=pallet-nft NAME=eth::erc::nft_iface OUTPUT=./tests/src/eth/api/UniqueNFT.sol ./.maintain/scripts/generate_api.sh
+	PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_iface OUTPUT=./tests/src/eth/api/ContractHelpers.sol ./.maintain/scripts/generate_api.sh
+
+	PACKAGE=pallet-nft NAME=eth::erc::fungible_impl OUTPUT=./pallets/nft/src/eth/stubs/UniqueFungible.sol ./.maintain/scripts/generate_api.sh
+	PACKAGE=pallet-nft NAME=eth::erc::nft_impl OUTPUT=./pallets/nft/src/eth/stubs/UniqueNFT.sol ./.maintain/scripts/generate_api.sh
+	PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_impl OUTPUT=./pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol ./.maintain/scripts/generate_api.sh
+
+NFT_EVM_STUBS=./pallets/nft/src/eth/stubs
+CONTRACT_HELPERS_STUBS=./pallets/evm-contract-helpers/src/stubs/
+
+$(NFT_EVM_STUBS)/UniqueFungible.raw: $(NFT_EVM_STUBS)/UniqueFungible.sol
+	INPUT=$< OUTPUT=$@ ./.maintain/scripts/compile_stub.sh
+$(NFT_EVM_STUBS)/UniqueNFT.raw: $(NFT_EVM_STUBS)/UniqueNFT.sol
+	INPUT=$< OUTPUT=$@ ./.maintain/scripts/compile_stub.sh
+$(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw: $(CONTRACT_HELPERS_STUBS)/ContractHelpers.sol
+	INPUT=$< OUTPUT=$@ ./.maintain/scripts/compile_stub.sh
+
+evm_stubs: $(NFT_EVM_STUBS)/UniqueFungible.raw $(NFT_EVM_STUBS)/UniqueNFT.raw $(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw
+
+.PHONY: _bench
+_bench:
+	cargo run --release --features runtime-benchmarks -- \
+	benchmark --pallet pallet-$(PALLET) \
+	--wasm-execution compiled --extrinsic '*' \
+	--template .maintain/frame-weight-template.hbs --steps=50 --repeat=20 \
+	--output=./pallets/$(PALLET)/src/weights.rs
+
+.PHONY: bench-evm-migration
+bench-evm-migration:
+	make _bench PALLET=evm-migration
+
+.PHONY: bench-nft
+bench-nft:
+	make _bench PALLET=nft
+
+.PHONY: bench
+bench: bench-evm-migration bench-nft
modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -1,16 +1,16 @@
 #![allow(dead_code)]
 
 use quote::quote;
-use darling::FromMeta;
+use darling::{FromMeta, ToTokens};
 use inflector::cases;
 use std::fmt::Write;
 use syn::{
-	FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path,
-	ReturnType, Type, spanned::Spanned,
+	Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,
+	NestedMeta, PatType, Path, PathArguments, ReturnType, Type, spanned::Spanned,
 };
 
 use crate::{
-	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,
+	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,
 	parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,
 	snake_ident_to_screaming,
 };
@@ -73,6 +73,20 @@
 			}
 		}
 	}
+
+	fn expand_generator(&self) -> proc_macro2::TokenStream {
+		let pascal_call_name = &self.pascal_call_name;
+		quote! {
+			#pascal_call_name::generate_solidity_interface(tc, is_impl);
+		}
+	}
+
+	fn expand_event_generator(&self) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		quote! {
+			#name::generate_solidity_interface(tc, is_impl);
+		}
+	}
 }
 
 #[derive(Default)]
@@ -107,29 +121,183 @@
 	rename_selector: Option<String>,
 }
 
+enum AbiType {
+	// type
+	Plain(Ident),
+	// (type1,type2)
+	Tuple(Vec<AbiType>),
+	// type[]
+	Vec(Box<AbiType>),
+	// type[20]
+	Array(Box<AbiType>, usize),
+}
+impl AbiType {
+	fn try_from(value: &Type) -> syn::Result<Self> {
+		let value = Self::try_maybe_special_from(value)?;
+		if value.is_special() {
+			return Err(syn::Error::new(value.span(), "unexpected special type"));
+		}
+		Ok(value)
+	}
+	fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {
+		match value {
+			Type::Array(arr) => {
+				let wrapped = AbiType::try_from(&arr.elem)?;
+				match &arr.len {
+					Expr::Lit(l) => match &l.lit {
+						Lit::Int(i) => {
+							let num = i.base10_parse::<usize>()?;
+							Ok(AbiType::Array(Box::new(wrapped), num as usize))
+						}
+						_ => Err(syn::Error::new(arr.len.span(), "should be int literal")),
+					},
+					_ => Err(syn::Error::new(arr.len.span(), "should be literal")),
+				}
+			}
+			Type::Path(_) => {
+				let path = parse_path(value)?;
+				let segment = parse_path_segment(path)?;
+				if segment.ident == "Vec" {
+					let args = match &segment.arguments {
+						PathArguments::AngleBracketed(e) => e,
+						_ => {
+							return Err(syn::Error::new(
+								segment.arguments.span(),
+								"missing Vec generic",
+							))
+						}
+					};
+					let args = &args.args;
+					if args.len() != 1 {
+						return Err(syn::Error::new(
+							args.span(),
+							"expected only one generic for vec",
+						));
+					}
+					let arg = args.first().unwrap();
+
+					let ty = match arg {
+						GenericArgument::Type(ty) => ty,
+						_ => {
+							return Err(syn::Error::new(
+								arg.span(),
+								"expected first generic to be type",
+							))
+						}
+					};
+
+					let wrapped = AbiType::try_from(ty)?;
+					Ok(Self::Vec(Box::new(wrapped)))
+				} else {
+					if !segment.arguments.is_empty() {
+						return Err(syn::Error::new(
+							segment.arguments.span(),
+							"unexpected generic arguments for non-vec type",
+						));
+					}
+					Ok(Self::Plain(segment.ident.clone()))
+				}
+			}
+			Type::Tuple(t) => {
+				let mut out = Vec::with_capacity(t.elems.len());
+				for el in t.elems.iter() {
+					out.push(AbiType::try_from(el)?)
+				}
+				Ok(Self::Tuple(out))
+			}
+			_ => Err(syn::Error::new(
+				value.span(),
+				"unexpected type, only arrays, plain types and tuples are supported",
+			)),
+		}
+	}
+	fn is_value(&self) -> bool {
+		match self {
+			Self::Plain(v) if v == "value" => true,
+			_ => false,
+		}
+	}
+	fn is_caller(&self) -> bool {
+		match self {
+			Self::Plain(v) if v == "caller" => true,
+			_ => false,
+		}
+	}
+	fn is_special(&self) -> bool {
+		self.is_caller() || self.is_value()
+	}
+	fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {
+		match self {
+			AbiType::Plain(t) => {
+				write!(buf, "{}", t)
+			}
+			AbiType::Tuple(t) => {
+				write!(buf, "(")?;
+				for (i, t) in t.iter().enumerate() {
+					if i != 0 {
+						write!(buf, ",")?;
+					}
+					t.selector_ty_buf(buf)?;
+				}
+				write!(buf, ")")
+			}
+			AbiType::Vec(v) => {
+				v.selector_ty_buf(buf)?;
+				write!(buf, "[]")
+			}
+			AbiType::Array(v, len) => {
+				v.selector_ty_buf(buf)?;
+				write!(buf, "[{}]", len)
+			}
+		}
+	}
+	fn selector_ty(&self) -> String {
+		let mut out = String::new();
+		self.selector_ty_buf(&mut out).expect("no fmt error");
+		out
+	}
+}
+impl ToTokens for AbiType {
+	fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+		match self {
+			AbiType::Plain(t) => tokens.extend(quote! {#t}),
+			AbiType::Tuple(t) => {
+				tokens.extend(quote! {(
+					#(#t),*
+				)});
+			}
+			AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),
+			AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),
+		}
+	}
+}
+
 struct MethodArg {
 	name: Ident,
-	ty: Ident,
+	camel_name: String,
+	ty: AbiType,
 }
 impl MethodArg {
 	fn try_from(value: &PatType) -> syn::Result<Self> {
+		let name = parse_ident_from_pat(&value.pat)?.clone();
 		Ok(Self {
-			name: parse_ident_from_pat(&value.pat)?.clone(),
-			ty: parse_ident_from_type(&value.ty, false)?.clone(),
+			camel_name: cases::camelcase::to_camel_case(&name.to_string()),
+			name,
+			ty: AbiType::try_maybe_special_from(&value.ty)?,
 		})
 	}
 	fn is_value(&self) -> bool {
-		self.ty == "value"
+		self.ty.is_value()
 	}
 	fn is_caller(&self) -> bool {
-		self.ty == "caller"
+		self.ty.is_caller()
 	}
 	fn is_special(&self) -> bool {
-		self.is_value() || self.is_caller()
+		self.ty.is_special()
 	}
-	fn selector_ty(&self) -> &Ident {
+	fn selector_ty(&self) -> String {
 		assert!(!self.is_special());
-		&self.ty
+		self.ty.selector_ty()
 	}
 
 	fn expand_call_def(&self) -> proc_macro2::TokenStream {
@@ -168,10 +336,10 @@
 	}
 
 	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
-		let name = &self.name.to_string();
+		let camel_name = &self.camel_name.to_string();
 		let ty = &self.ty;
 		quote! {
-			<NamedArgument<#ty>>::new(#name)
+			<NamedArgument<#ty>>::new(#camel_name)
 		}
 	}
 }
@@ -397,10 +565,16 @@
 		};
 		let result = &self.result;
 
-		let args = self.args.iter().map(MethodArg::expand_solidity_argument);
+		let args = self
+			.args
+			.iter()
+			.filter(|a| !a.is_special())
+			.map(MethodArg::expand_solidity_argument);
+		let selector = format!("{} {:0>8x}", self.selector_str, self.selector);
 
 		quote! {
 			SolidityFunction {
+				selector: #selector,
 				name: #camel_name,
 				mutability: #mutability,
 				args: (
@@ -475,6 +649,24 @@
 		let call_variants_this = self.methods.iter().map(Method::expand_variant_call);
 		let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);
 
+		// TODO: Inline inline_is
+		let solidity_is = self
+			.info
+			.is
+			.0
+			.iter()
+			.chain(self.info.inline_is.0.iter())
+			.map(|is| is.name.to_string());
+		let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());
+		let solidity_generators = self
+			.info
+			.is
+			.0
+			.iter()
+			.chain(self.info.inline_is.0.iter())
+			.map(Is::expand_generator);
+		let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);
+
 		// let methods = self.methods.iter().map(Method::solidity_def);
 
 		quote! {
@@ -505,18 +697,40 @@
 						)*
 					)
 				}
-				pub fn generate_solidity_interface() -> string {
+				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
 					use evm_coder::solidity::*;
 					use core::fmt::Write;
 					let interface = SolidityInterface {
 						name: #solidity_name,
+						is: &["Dummy", #(
+							#solidity_is,
+						)* #(
+							#solidity_events_is,
+						)* ],
 						functions: (#(
 							#solidity_functions,
 						)*),
 					};
+					if is_impl {
+						tc.collect("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\n".into());
+					} else {
+						tc.collect("// Common stubs holder\ninterface Dummy {\n}\n".into());
+					}
+					#(
+						#solidity_generators
+					)*
+					#(
+						#solidity_event_generators
+					)*
+
 					let mut out = string::new();
-					let _ = interface.format(&mut out);
-					out
+					// In solidity interface usage (is) should be preceeded by interface definition
+					// This comment helps to sort it in a set
+					if #solidity_name.starts_with("Inline") {
+						out.push_str("// Inline\n");
+					}
+					let _ = interface.format(is_impl, &mut out, tc);
+					tc.collect(out);
 				}
 			}
 			impl ::evm_coder::Call for #call_name {
modifiedcrates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/to_log.rs
+++ b/crates/evm-coder-macros/src/to_log.rs
@@ -1,3 +1,4 @@
+use inflector::cases;
 use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};
 use std::fmt::Write;
 use quote::quote;
@@ -6,6 +7,7 @@
 
 struct EventField {
 	name: Ident,
+	camel_name: String,
 	ty: Ident,
 	indexed: bool,
 }
@@ -24,10 +26,19 @@
 		}
 		Ok(Self {
 			name: name.to_owned(),
+			camel_name: cases::camelcase::to_camel_case(&name.to_string()),
 			ty: ty.to_owned(),
 			indexed,
 		})
 	}
+	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
+		let camel_name = &self.camel_name;
+		let ty = &self.ty;
+		let indexed = self.indexed;
+		quote! {
+			<SolidityEventArgument<#ty>>::new(#indexed, #camel_name)
+		}
+	}
 }
 
 struct Event {
@@ -116,6 +127,21 @@
 			)*];
 		}
 	}
+
+	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
+		let name = self.name.to_string();
+		let args = self.fields.iter().map(EventField::expand_solidity_argument);
+		quote! {
+			SolidityEvent {
+				name: #name,
+				args: (
+					#(
+						#args,
+					)*
+				),
+			}
+		}
+	}
 }
 
 pub struct Events {
@@ -144,12 +170,30 @@
 
 		let consts = self.events.iter().map(Event::expand_consts);
 		let serializers = self.events.iter().map(Event::expand_serializers);
+		let solidity_name = self.name.to_string();
+		let solidity_functions = self.events.iter().map(Event::expand_solidity_function);
 
 		quote! {
 			impl #name {
 				#(
 					#consts
 				)*
+
+				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
+					use evm_coder::solidity::*;
+					use core::fmt::Write;
+					let interface = SolidityInterface {
+						name: #solidity_name,
+						is: &[],
+						functions: (#(
+							#solidity_functions,
+						)*),
+					};
+					let mut out = string::new();
+					out.push_str("// Inline\n");
+					let _ = interface.format(is_impl, &mut out, tc);
+					tc.collect(out);
+				}
 			}
 
 			#[automatically_derived]
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -19,11 +19,16 @@
 #[derive(Clone)]
 pub struct AbiReader<'i> {
 	buf: &'i [u8],
+	subresult_offset: usize,
 	offset: usize,
 }
 impl<'i> AbiReader<'i> {
 	pub fn new(buf: &'i [u8]) -> Self {
-		Self { buf, offset: 0 }
+		Self {
+			buf,
+			subresult_offset: 0,
+			offset: 0,
+		}
 	}
 	pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> {
 		if buf.len() < 4 {
@@ -32,11 +37,18 @@
 		let mut method_id = [0; 4];
 		method_id.copy_from_slice(&buf[0..4]);
 
-		Ok((u32::from_be_bytes(method_id), Self { buf, offset: 4 }))
+		Ok((
+			u32::from_be_bytes(method_id),
+			Self {
+				buf,
+				subresult_offset: 4,
+				offset: 4,
+			},
+		))
 	}
 
 	fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {
-		if self.buf.len() - self.offset < 32 {
+		if self.buf.len() - self.offset < ABI_ALIGNMENT {
 			return Err(Error::Error(ExitError::OutOfOffset));
 		}
 		let mut block = [0; S];
@@ -79,6 +91,9 @@
 		}
 		Ok(subresult.buf[subresult.offset..subresult.offset + length].into())
 	}
+	pub fn string(&mut self) -> Result<string> {
+		string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
+	}
 
 	pub fn uint32(&mut self) -> Result<u32> {
 		Ok(u32::from_be_bytes(self.read_padleft()?))
@@ -103,9 +118,13 @@
 
 	fn subresult(&mut self) -> Result<AbiReader<'i>> {
 		let offset = self.read_usize()?;
+		if offset + self.subresult_offset > self.buf.len() {
+			return Err(Error::Error(ExitError::InvalidRange));
+		}
 		Ok(AbiReader {
 			buf: self.buf,
-			offset: offset + self.offset,
+			subresult_offset: offset + self.subresult_offset,
+			offset: offset + self.subresult_offset,
 		})
 	}
 
@@ -192,11 +211,15 @@
 		self.memory(value.as_bytes())
 	}
 
+	pub fn bytes(&mut self, value: &[u8]) {
+		self.memory(value)
+	}
+
 	pub fn finish(mut self) -> Vec<u8> {
 		for (static_offset, part) in self.dynamic_part {
 			let part_offset = self.static_part.len();
 
-			let encoded_dynamic_offset = usize::to_be_bytes(part_offset - static_offset);
+			let encoded_dynamic_offset = usize::to_be_bytes(part_offset);
 			self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()
 				..static_offset + ABI_ALIGNMENT]
 				.copy_from_slice(&encoded_dynamic_offset);
@@ -226,6 +249,60 @@
 impl_abi_readable!(H160, address);
 impl_abi_readable!(Vec<u8>, bytes);
 impl_abi_readable!(bool, bool);
+impl_abi_readable!(string, string);
+
+mod sealed {
+	/// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead
+	pub trait CanBePlacedInVec {}
+}
+
+impl sealed::CanBePlacedInVec for U256 {}
+impl sealed::CanBePlacedInVec for string {}
+impl sealed::CanBePlacedInVec for H160 {}
+
+impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>
+where
+	Self: AbiRead<R>,
+{
+	fn abi_read(&mut self) -> Result<Vec<R>> {
+		let mut sub = self.subresult()?;
+		let size = sub.read_usize()?;
+		sub.subresult_offset = sub.offset;
+		let mut out = Vec::with_capacity(size);
+		for _ in 0..size {
+			out.push(<Self as AbiRead<R>>::abi_read(&mut sub)?);
+		}
+		Ok(out)
+	}
+}
+
+macro_rules! impl_tuples {
+	($($ident:ident)+) => {
+		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
+		impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>
+		where
+			$(Self: AbiRead<$ident>),+
+		{
+			fn abi_read(&mut self) -> Result<($($ident,)+)> {
+				let mut subresult = self.subresult()?;
+				Ok((
+					$(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+
+				))
+			}
+		}
+	};
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
 
 pub trait AbiWrite {
 	fn abi_write(&self, writer: &mut AbiWriter);
@@ -253,6 +330,11 @@
 		writer.string(self)
 	}
 }
+impl AbiWrite for &Vec<u8> {
+	fn abi_write(&self, writer: &mut AbiWriter) {
+		writer.bytes(self)
+	}
+}
 
 impl AbiWrite for () {
 	fn abi_write(&self, _writer: &mut AbiWriter) {}
@@ -285,3 +367,103 @@
 		writer
 	}}
 }
+
+#[cfg(test)]
+pub mod test {
+	use crate::{
+		abi::AbiRead,
+		types::{string, uint256},
+	};
+
+	use super::{AbiReader, AbiWriter};
+	use hex_literal::hex;
+
+	#[test]
+	fn dynamic_after_static() {
+		let mut encoder = AbiWriter::new();
+		encoder.bool(&true);
+		encoder.string("test");
+		let encoded = encoder.finish();
+
+		let mut encoder = AbiWriter::new();
+		encoder.bool(&true);
+		// Offset to subresult
+		encoder.uint32(&(32 * 2));
+		// Len of "test"
+		encoder.uint32(&4);
+		encoder.write_padright(&[b't', b'e', b's', b't']);
+		let alternative_encoded = encoder.finish();
+
+		assert_eq!(encoded, alternative_encoded);
+
+		let mut decoder = AbiReader::new(&encoded);
+		assert_eq!(decoder.bool().unwrap(), true);
+		assert_eq!(decoder.string().unwrap(), "test");
+	}
+
+	#[test]
+	fn mint_sample() {
+		let (call, mut decoder) = AbiReader::new_call(&hex!(
+			"
+				50bb4e7f
+				000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374
+				0000000000000000000000000000000000000000000000000000000000000001
+				0000000000000000000000000000000000000000000000000000000000000060
+				0000000000000000000000000000000000000000000000000000000000000008
+				5465737420555249000000000000000000000000000000000000000000000000
+			"
+		))
+		.unwrap();
+		assert_eq!(call, 0x50bb4e7f);
+		assert_eq!(
+			format!("{:?}", decoder.address().unwrap()),
+			"0xad2c0954693c2b5404b7e50967d3481bea432374"
+		);
+		assert_eq!(decoder.uint32().unwrap(), 1);
+		assert_eq!(decoder.string().unwrap(), "Test URI");
+	}
+
+	#[test]
+	fn mint_bulk() {
+		let (call, mut decoder) = AbiReader::new_call(&hex!(
+			"
+				36543006
+				00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address
+				0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]
+				0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
+
+				0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
+				00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
+				0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
+
+				0000000000000000000000000000000000000000000000000000000000000001 // first token id?   					#60
+				0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+				000000000000000000000000000000000000000000000000000000000000000a // size of string
+				5465737420555249203000000000000000000000000000000000000000000000 // string
+
+				000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11?			#e0
+				0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+				000000000000000000000000000000000000000000000000000000000000000a // size of string
+				5465737420555249203100000000000000000000000000000000000000000000 // string
+
+				000000000000000000000000000000000000000000000000000000000000000c // third token id?  Why ==12?			#160
+				0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+				000000000000000000000000000000000000000000000000000000000000000a // size of string
+				5465737420555249203200000000000000000000000000000000000000000000 // string
+			"
+		))
+		.unwrap();
+		assert_eq!(call, 0x36543006);
+		let _ = decoder.address().unwrap();
+		let data =
+			<AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();
+		assert_eq!(
+			data,
+			vec![
+				(1.into(), "Test URI 0".to_string()),
+				(11.into(), "Test URI 1".to_string()),
+				(12.into(), "Test URI 2".to_string())
+			]
+		);
+	}
+}
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -61,6 +61,29 @@
 	fn call(&mut self, call: types::Msg<C>) -> execution::Result<AbiWriter>;
 }
 
+#[macro_export]
+macro_rules! generate_stubgen {
+	($name:ident, $decl:ident, $is_impl:literal) => {
+		#[test]
+		#[ignore]
+		fn $name() {
+			use evm_coder::solidity::TypeCollector;
+			let mut out = TypeCollector::new();
+			$decl::generate_solidity_interface(&mut out, $is_impl);
+			println!("=== SNIP START ===");
+			println!("// SPDX-License-Identifier: OTHER");
+			println!("// This code is automatically generated");
+			println!();
+			println!("pragma solidity >=0.8.0 <0.9.0;");
+			println!();
+			for b in out.finish() {
+				println!("{}", b);
+			}
+			println!("=== SNIP END ===");
+		}
+	};
+}
+
 #[cfg(test)]
 mod tests {
 	use super::*;
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -1,40 +1,104 @@
 #[cfg(not(feature = "std"))]
-use alloc::{string::String};
-use core::{fmt, marker::PhantomData};
+use alloc::{
+	string::String,
+	vec::Vec,
+	collections::{BTreeSet, BTreeMap},
+	format,
+};
+#[cfg(feature = "std")]
+use std::collections::{BTreeSet, BTreeMap};
+use core::{
+	fmt::{self, Write},
+	marker::PhantomData,
+	cell::{Cell, RefCell},
+};
 use impl_trait_for_tuples::impl_for_tuples;
 use crate::types::*;
 
+#[derive(Default)]
+pub struct TypeCollector {
+	structs: RefCell<BTreeSet<string>>,
+	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
+	id: Cell<usize>,
+}
+impl TypeCollector {
+	pub fn new() -> Self {
+		Self::default()
+	}
+	pub fn collect(&self, item: string) {
+		self.structs.borrow_mut().insert(item);
+	}
+	pub fn next_id(&self) -> usize {
+		let v = self.id.get();
+		self.id.set(v + 1);
+		v
+	}
+	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {
+		let names = T::names(self);
+		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {
+			return format!("Tuple{}", id);
+		}
+		let id = self.next_id();
+		let mut str = String::new();
+		writeln!(str, "// Anonymous struct").unwrap();
+		writeln!(str, "struct Tuple{} {{", id).unwrap();
+		for (i, name) in names.iter().enumerate() {
+			writeln!(str, "\t{} field_{};", name, i).unwrap();
+		}
+		writeln!(str, "}}").unwrap();
+		self.collect(str);
+		self.anonymous.borrow_mut().insert(names, id);
+		format!("Tuple{}", id)
+	}
+	pub fn finish(self) -> BTreeSet<string> {
+		self.structs.into_inner()
+	}
+}
+
 pub trait SolidityTypeName: 'static {
-	fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result;
+	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+	fn is_simple() -> bool;
+	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
 	fn is_void() -> bool {
 		false
 	}
 }
-
 macro_rules! solidity_type_name {
-    ($($ty:ident => $name:expr),* $(,)?) => {
+    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {
         $(
             impl SolidityTypeName for $ty {
-                fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
+                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
                     write!(writer, $name)
                 }
+				fn is_simple() -> bool {
+					$simple
+				}
+				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
+					write!(writer, $default)
+				}
             }
         )*
     };
 }
 
 solidity_type_name! {
-	uint8 => "uint8",
-	uint32 => "uint32",
-	uint128 => "uint128",
-	uint256 => "uint256",
-	address => "address",
-	string => "memory string",
-	bytes => "memory bytes",
-	bool => "bool",
+	uint8 => "uint8" true = "0",
+	uint32 => "uint32" true = "0",
+	uint128 => "uint128" true = "0",
+	uint256 => "uint256" true = "0",
+	address => "address" true = "0x0000000000000000000000000000000000000000",
+	string => "string" false = "\"\"",
+	bytes => "bytes" false = "hex\"\"",
+	bool => "bool" true = "false",
 }
 impl SolidityTypeName for void {
-	fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {
+	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+		Ok(())
+	}
+	fn is_simple() -> bool {
+		true
+	}
+	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
 		Ok(())
 	}
 	fn is_void() -> bool {
@@ -42,8 +106,88 @@
 	}
 }
 
+mod sealed {
+	pub trait CanBePlacedInVec {}
+}
+
+impl sealed::CanBePlacedInVec for uint256 {}
+impl sealed::CanBePlacedInVec for string {}
+impl sealed::CanBePlacedInVec for address {}
+
+impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {
+	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+		T::solidity_name(writer, tc)?;
+		write!(writer, "[]")
+	}
+	fn is_simple() -> bool {
+		false
+	}
+	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+		write!(writer, "[]")
+	}
+}
+
+pub trait SolidityTupleType {
+	fn names(tc: &TypeCollector) -> Vec<String>;
+	fn len() -> usize;
+}
+
+macro_rules! count {
+    () => (0usize);
+    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
+}
+
+macro_rules! impl_tuples {
+	($($ident:ident)+) => {
+		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
+		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {
+			fn names(tc: &TypeCollector) -> Vec<string> {
+				let mut collected = Vec::with_capacity(Self::len());
+				$({
+					let mut out = string::new();
+					$ident::solidity_name(&mut out, tc).expect("no fmt error");
+					collected.push(out);
+				})*;
+				collected
+			}
+
+			fn len() -> usize {
+				count!($($ident)*)
+			}
+		}
+		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {
+			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+				write!(writer, "{}", tc.collect_tuple::<Self>())
+			}
+			fn is_simple() -> bool {
+				false
+			}
+			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+				write!(writer, "{}(", tc.collect_tuple::<Self>())?;
+				$(
+					<$ident>::solidity_default(writer, tc)?;
+				)*
+				write!(writer, ")")
+			}
+		}
+	};
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
+
 pub trait SolidityArguments {
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
 	fn is_empty(&self) -> bool {
 		self.len() == 0
 	}
@@ -54,13 +198,23 @@
 pub struct UnnamedArgument<T>(PhantomData<*const T>);
 
 impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
 		if !T::is_void() {
-			T::solidity_name(writer)
+			T::solidity_name(writer, tc)?;
+			if !T::is_simple() {
+				write!(writer, " memory")?;
+			}
+			Ok(())
 		} else {
 			Ok(())
 		}
 	}
+	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+		Ok(())
+	}
+	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+		T::solidity_default(writer, tc)
+	}
 	fn len(&self) -> usize {
 		if T::is_void() {
 			0
@@ -79,14 +233,58 @@
 }
 
 impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
 		if !T::is_void() {
-			T::solidity_name(writer)?;
+			T::solidity_name(writer, tc)?;
+			if !T::is_simple() {
+				write!(writer, " memory")?;
+			}
 			write!(writer, " {}", self.0)
 		} else {
 			Ok(())
 		}
 	}
+	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+		writeln!(writer, "\t\t{};", self.0)
+	}
+	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+		T::solidity_default(writer, tc)
+	}
+	fn len(&self) -> usize {
+		if T::is_void() {
+			0
+		} else {
+			1
+		}
+	}
+}
+
+pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);
+
+impl<T> SolidityEventArgument<T> {
+	pub fn new(indexed: bool, name: &'static str) -> Self {
+		Self(indexed, name, Default::default())
+	}
+}
+
+impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {
+	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+		if !T::is_void() {
+			T::solidity_name(writer, tc)?;
+			if self.0 {
+				write!(writer, " indexed")?;
+			}
+			write!(writer, " {}", self.1)
+		} else {
+			Ok(())
+		}
+	}
+	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+		writeln!(writer, "\t\t{};", self.1)
+	}
+	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+		T::solidity_default(writer, tc)
+	}
 	fn len(&self) -> usize {
 		if T::is_void() {
 			0
@@ -97,7 +295,13 @@
 }
 
 impl SolidityArguments for () {
-	fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+		Ok(())
+	}
+	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+		Ok(())
+	}
+	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
 		Ok(())
 	}
 	fn len(&self) -> usize {
@@ -109,7 +313,7 @@
 impl SolidityArguments for Tuple {
 	for_tuples!( where #( Tuple: SolidityArguments ),* );
 
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
 		let mut first = true;
 		for_tuples!( #(
             if !Tuple.is_empty() {
@@ -117,18 +321,53 @@
                     write!(writer, ", ")?;
                 }
                 first = false;
-                Tuple.solidity_name(writer)?;
+                Tuple.solidity_name(writer, tc)?;
             }
         )* );
 		Ok(())
 	}
+	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+		for_tuples!( #(
+            Tuple.solidity_get(writer)?;
+        )* );
+		Ok(())
+	}
+	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+		if self.is_empty() {
+			Ok(())
+		} else if self.len() == 1 {
+			for_tuples!( #(
+				Tuple.solidity_default(writer, tc)?;
+			)* );
+			Ok(())
+		} else {
+			write!(writer, "(")?;
+			let mut first = true;
+			for_tuples!( #(
+				if !Tuple.is_empty() {
+					if !first {
+						write!(writer, ", ")?;
+					}
+					first = false;
+					Tuple.solidity_default(writer, tc)?;
+				}
+			)* );
+			write!(writer, ")")?;
+			Ok(())
+		}
+	}
 	fn len(&self) -> usize {
 		for_tuples!( #( Tuple.len() )+* )
 	}
 }
 
 pub trait SolidityFunctions {
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+	fn solidity_name(
+		&self,
+		is_impl: bool,
+		writer: &mut impl fmt::Write,
+		tc: &TypeCollector,
+	) -> fmt::Result;
 }
 
 pub enum SolidityMutability {
@@ -137,16 +376,28 @@
 	Mutable,
 }
 pub struct SolidityFunction<A, R> {
+	pub selector: &'static str,
 	pub name: &'static str,
 	pub args: A,
 	pub result: R,
 	pub mutability: SolidityMutability,
 }
 impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
-		write!(writer, "function {}(", self.name)?;
-		self.args.solidity_name(writer)?;
-		write!(writer, ") external")?;
+	fn solidity_name(
+		&self,
+		is_impl: bool,
+		writer: &mut impl fmt::Write,
+		tc: &TypeCollector,
+	) -> fmt::Result {
+		writeln!(writer, "\t// Selector: {}", self.selector)?;
+		write!(writer, "\tfunction {}(", self.name)?;
+		self.args.solidity_name(writer, tc)?;
+		write!(writer, ")")?;
+		if is_impl {
+			write!(writer, " public")?;
+		} else {
+			write!(writer, " external")?;
+		}
 		match &self.mutability {
 			SolidityMutability::Pure => write!(writer, " pure")?,
 			SolidityMutability::View => write!(writer, " view")?,
@@ -154,10 +405,28 @@
 		}
 		if !self.result.is_empty() {
 			write!(writer, " returns (")?;
-			self.result.solidity_name(writer)?;
+			self.result.solidity_name(writer, tc)?;
 			write!(writer, ")")?;
 		}
-		writeln!(writer, ";")
+		if is_impl {
+			writeln!(writer, " {{")?;
+			writeln!(writer, "\t\trequire(false, stub_error);")?;
+			self.args.solidity_get(writer)?;
+			match &self.mutability {
+				SolidityMutability::Pure => {}
+				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,
+				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,
+			}
+			if !self.result.is_empty() {
+				write!(writer, "\t\treturn ")?;
+				self.result.solidity_default(writer, tc)?;
+				writeln!(writer, ";")?;
+			}
+			writeln!(writer, "\t}}")?;
+		} else {
+			writeln!(writer, ";")?;
+		}
+		Ok(())
 	}
 }
 
@@ -165,10 +434,15 @@
 impl SolidityFunctions for Tuple {
 	for_tuples!( where #( Tuple: SolidityFunctions ),* );
 
-	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+	fn solidity_name(
+		&self,
+		is_impl: bool,
+		writer: &mut impl fmt::Write,
+		tc: &TypeCollector,
+	) -> fmt::Result {
 		let mut first = false;
 		for_tuples!( #(
-            Tuple.solidity_name(writer)?;
+            Tuple.solidity_name(is_impl, writer, tc)?;
         )* );
 		Ok(())
 	}
@@ -176,14 +450,53 @@
 
 pub struct SolidityInterface<F: SolidityFunctions> {
 	pub name: &'static str,
+	pub is: &'static [&'static str],
 	pub functions: F,
 }
 
 impl<F: SolidityFunctions> SolidityInterface<F> {
-	pub fn format(&self, out: &mut impl fmt::Write) -> fmt::Result {
-		writeln!(out, "interface {} {{", self.name)?;
-		self.functions.solidity_name(out)?;
+	pub fn format(
+		&self,
+		is_impl: bool,
+		out: &mut impl fmt::Write,
+		tc: &TypeCollector,
+	) -> fmt::Result {
+		if is_impl {
+			write!(out, "contract ")?;
+		} else {
+			write!(out, "interface ")?;
+		}
+		write!(out, "{}", self.name)?;
+		if !self.is.is_empty() {
+			write!(out, " is")?;
+			for (i, n) in self.is.iter().enumerate() {
+				if i != 0 {
+					write!(out, ",")?;
+				}
+				write!(out, " {}", n)?;
+			}
+		}
+		writeln!(out, " {{")?;
+		self.functions.solidity_name(is_impl, out, tc)?;
 		writeln!(out, "}}")?;
 		Ok(())
 	}
 }
+
+pub struct SolidityEvent<A> {
+	pub name: &'static str,
+	pub args: A,
+}
+
+impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
+	fn solidity_name(
+		&self,
+		_is_impl: bool,
+		writer: &mut impl fmt::Write,
+		tc: &TypeCollector,
+	) -> fmt::Result {
+		write!(writer, "\tevent {}(", self.name)?;
+		self.args.solidity_name(writer, tc)?;
+		writeln!(writer, ");")
+	}
+}
modifiedcrates/evm-coder/tests/a.rsdiffbeforeafterboth
--- a/crates/evm-coder/tests/a.rs
+++ b/crates/evm-coder/tests/a.rs
@@ -2,6 +2,7 @@
 
 use evm_coder::{solidity_interface, types::*, ToLog, execution::Result};
 use evm_coder_macros::solidity;
+use std as sp_std;
 
 struct Impls;
 
modifiednode/cli/Cargo.tomldiffbeforeafterboth
--- a/node/cli/Cargo.toml
+++ b/node/cli/Cargo.toml
@@ -317,4 +317,7 @@
 
 [features]
 default = []
-runtime-benchmarks = ['nft-runtime/runtime-benchmarks']
+runtime-benchmarks = [
+    'nft-runtime/runtime-benchmarks',
+    'polkadot-service/runtime-benchmarks',
+]
modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -202,18 +202,6 @@
 			nft_item_id: vec![],
 			fungible_item_id: vec![],
 			refungible_item_id: vec![],
-			chain_limit: ChainLimits {
-				collection_numbers_limit: 100000,
-				account_token_ownership_limit: 1000000,
-				collections_admins_limit: 5,
-				custom_data_limit: 2048,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-				const_on_chain_schema_limit: 1024,
-			},
 		},
 		parachain_info: nft_runtime::ParachainInfoConfig { parachain_id: id },
 		aura: nft_runtime::AuraConfig {
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -1,5 +1,5 @@
 use core::marker::PhantomData;
-use evm_coder::{abi::AbiWriter, execution::Result, solidity_interface, types::*};
+use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
 use pallet_evm_coder_substrate::SubstrateRecorder;
 use pallet_evm::{ExitReason, ExitRevert, OnCreate, OnMethodCall, PrecompileOutput};
 use sp_core::H160;
@@ -14,76 +14,76 @@
 
 #[solidity_interface(name = "ContractHelpers")]
 impl<T: Config> ContractHelpers<T> {
-	fn contract_owner(&self, contract: address) -> Result<address> {
+	fn contract_owner(&self, contract_address: address) -> Result<address> {
 		self.0.consume_sload()?;
-		Ok(<Owner<T>>::get(contract))
+		Ok(<Owner<T>>::get(contract_address))
 	}
 
-	fn sponsoring_enabled(&self, contract: address) -> Result<bool> {
+	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
 		self.0.consume_sload()?;
-		Ok(<SelfSponsoring<T>>::get(contract))
+		Ok(<SelfSponsoring<T>>::get(contract_address))
 	}
 
 	fn toggle_sponsoring(
 		&mut self,
 		caller: caller,
-		contract: address,
+		contract_address: address,
 		enabled: bool,
 	) -> Result<void> {
 		self.0.consume_sload()?;
-		<Pallet<T>>::ensure_owner(contract, caller)?;
+		<Pallet<T>>::ensure_owner(contract_address, caller)?;
 		self.0.consume_sstore()?;
-		<Pallet<T>>::toggle_sponsoring(contract, enabled);
+		<Pallet<T>>::toggle_sponsoring(contract_address, enabled);
 		Ok(())
 	}
 
 	fn set_sponsoring_rate_limit(
 		&mut self,
 		caller: caller,
-		contract: address,
+		contract_address: address,
 		rate_limit: uint32,
 	) -> Result<void> {
 		self.0.consume_sload()?;
-		<Pallet<T>>::ensure_owner(contract, caller)?;
+		<Pallet<T>>::ensure_owner(contract_address, caller)?;
 		self.0.consume_sstore()?;
-		<Pallet<T>>::set_sponsoring_rate_limit(contract, rate_limit.into());
+		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());
 		Ok(())
 	}
 
-	fn allowed(&self, contract: address, user: address) -> Result<bool> {
+	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
 		self.0.consume_sload()?;
-		Ok(<Pallet<T>>::allowed(contract, user, true))
+		Ok(<Pallet<T>>::allowed(contract_address, user, true))
 	}
 
-	fn allowlist_enabled(&self, contract: address) -> Result<bool> {
+	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
 		self.0.consume_sload()?;
-		Ok(<AllowlistEnabled<T>>::get(contract))
+		Ok(<AllowlistEnabled<T>>::get(contract_address))
 	}
 
 	fn toggle_allowlist(
 		&mut self,
 		caller: caller,
-		contract: address,
+		contract_address: address,
 		enabled: bool,
 	) -> Result<void> {
 		self.0.consume_sload()?;
-		<Pallet<T>>::ensure_owner(contract, caller)?;
+		<Pallet<T>>::ensure_owner(contract_address, caller)?;
 		self.0.consume_sstore()?;
-		<Pallet<T>>::toggle_allowlist(contract, enabled);
+		<Pallet<T>>::toggle_allowlist(contract_address, enabled);
 		Ok(())
 	}
 
 	fn toggle_allowed(
 		&mut self,
 		caller: caller,
-		contract: address,
+		contract_address: address,
 		user: address,
 		allowed: bool,
 	) -> Result<void> {
 		self.0.consume_sload()?;
-		<Pallet<T>>::ensure_owner(contract, caller)?;
+		<Pallet<T>>::ensure_owner(contract_address, caller)?;
 		self.0.consume_sstore()?;
-		<Pallet<T>>::toggle_allowed(contract, user, allowed);
+		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);
 		Ok(())
 	}
 }
@@ -130,7 +130,7 @@
 
 	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {
 		(contract == &T::ContractAddress::get())
-			.then(|| include_bytes!("./stubs/ContractHelpers.bin").to_vec())
+			.then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())
 	}
 }
 
@@ -162,3 +162,6 @@
 		None
 	}
 }
+
+generate_stubgen!(contract_helpers_impl, ContractHelpersCall, true);
+generate_stubgen!(contract_helpers_iface, ContractHelpersCall, false);
deletedpallets/evm-contract-helpers/src/stubs/ContractHelpers.bindiffbeforeafterboth

binary blob — no preview

addedpallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -1,40 +1,100 @@
-contract ContractHelpers {
-    uint8 _dummmy = 0;
-    address _dummy_addr = 0x0000000000000000000000000000000000000000;
-	string stub_error = "this contract does not exists, contract helpers are implemented on substrate chain side";
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+contract Dummy {
+	uint8 dummy;
+	string stub_error = "this contract is implemented in native";
+}
+
+contract ContractHelpers is Dummy {
+	// Selector: contractOwner(address) 5152b14c
+	function contractOwner(address contractAddress)
+		public
+		view
+		returns (address)
+	{
+		require(false, stub_error);
+		contractAddress;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	// Selector: sponsoringEnabled(address) 6027dc61
+	function sponsoringEnabled(address contractAddress)
+		public
+		view
+		returns (bool)
+	{
+		require(false, stub_error);
+		contractAddress;
+		dummy;
+		return false;
+	}
+
+	// Selector: toggleSponsoring(address,bool) fcac6d86
+	function toggleSponsoring(address contractAddress, bool enabled) public {
+		require(false, stub_error);
+		contractAddress;
+		enabled;
+		dummy = 0;
+	}
+
+	// Selector: setSponsoringRateLimit(address,uint32) 77b6c908
+	function setSponsoringRateLimit(address contractAddress, uint32 rateLimit)
+		public
+	{
+		require(false, stub_error);
+		contractAddress;
+		rateLimit;
+		dummy = 0;
+	}
+
+	// Selector: allowed(address,address) 5c658165
+	function allowed(address contractAddress, address user)
+		public
+		view
+		returns (bool)
+	{
+		require(false, stub_error);
+		contractAddress;
+		user;
+		dummy;
+		return false;
+	}
+
+	// Selector: allowlistEnabled(address) c772ef6c
+	function allowlistEnabled(address contractAddress)
+		public
+		view
+		returns (bool)
+	{
+		require(false, stub_error);
+		contractAddress;
+		dummy;
+		return false;
+	}
 
-    function contractOwner(address contract_address) public view returns (address) {
-        require(false, stub_error);
-        contract_address;
-        return _dummy_addr;
-    }
-    
-    function sponsoringEnabled(address contract_address) public view returns (bool) {
-        require(false, stub_error);
-        contract_address;
-        _dummmy;
-        return false;
-    }
-    
-    function toggleSponsoring(address contract_address, bool enabled) public {
-        require(false, stub_error);
-        contract_address;
-        enabled;
-        _dummmy = 0;
-    }
-    
-    function toggleAllowlist(address contract_address, bool enabled) public {
-        require(false, stub_error);
-        contract_address;
-        enabled;
-        _dummmy = 0;
-    }
-    
-    function toggleAllowed(address contract_address, address user, bool allowed) public {
-        require(false, stub_error);
-        contract_address;
-        user;
-        allowed;
-        _dummmy = 0;
-    }
-}
\ No newline at end of file
+	// Selector: toggleAllowlist(address,bool) 36de20f5
+	function toggleAllowlist(address contractAddress, bool enabled) public {
+		require(false, stub_error);
+		contractAddress;
+		enabled;
+		dummy = 0;
+	}
+
+	// Selector: toggleAllowed(address,address,bool) 4706cc1c
+	function toggleAllowed(
+		address contractAddress,
+		address user,
+		bool allowed
+	) public {
+		require(false, stub_error);
+		contractAddress;
+		user;
+		allowed;
+		dummy = 0;
+	}
+}
addedpallets/evm-migration/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-migration/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+name = "pallet-evm-migration"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+frame-benchmarking = { default-features = false, version = '3.0.0', optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-io = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-core = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+pallet-evm = { default-features = false, version = "5.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+fp-evm = { default-features = false, version = "2.0.0", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[features]
+default = ["std", "runtime-benchmarks"]
+std = [
+    "frame-support/std",
+    "frame-system/std",
+    "frame-benchmarking/std",
+    "sp-runtime/std",
+    "sp-std/std",
+    "sp-io/std",
+    "sp-core/std",
+    "pallet-evm/std",
+    "fp-evm/std",
+]
+runtime-benchmarks = ["frame-benchmarking"]
addedpallets/evm-migration/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-migration/src/benchmarking.rs
@@ -0,0 +1,30 @@
+use super::{Call, Config, Pallet};
+use frame_benchmarking::benchmarks;
+use frame_system::RawOrigin;
+use sp_core::{H160, H256};
+use sp_std::vec::Vec;
+
+benchmarks! {
+	begin {
+	}: _(RawOrigin::Root, H160::default())
+
+	set_data {
+		let b in 0..80;
+		let address = H160::from_low_u64_be(b as u64);
+		let mut data = Vec::new();
+		for i in 0..b {
+			data.push((
+				H256::from_low_u64_be(i as u64),
+				H256::from_low_u64_be(i as u64),
+			));
+		}
+		<Pallet<T>>::begin(RawOrigin::Root.into(), address)?;
+	}: _(RawOrigin::Root, address, data)
+
+	finish {
+		let b in 0..80;
+		let address = H160::from_low_u64_be(b as u64);
+		let data: Vec<u8> = (0..b as u8).collect();
+		<Pallet<T>>::begin(RawOrigin::Root.into(), address)?;
+	}: _(RawOrigin::Root, address, data)
+}
addedpallets/evm-migration/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-migration/src/lib.rs
@@ -0,0 +1,111 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+#[cfg(feature = "runtime-benchmarks")]
+pub mod benchmarking;
+pub mod weights;
+
+#[frame_support::pallet]
+pub mod pallet {
+	use frame_support::{pallet_prelude::*, transactional};
+	use frame_system::pallet_prelude::*;
+	use sp_core::{H160, H256};
+	use sp_std::vec::Vec;
+	use super::weights::WeightInfo;
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config + pallet_evm::Config {
+		type WeightInfo: WeightInfo;
+	}
+
+	type SelfWeightOf<T> = <T as Config>::WeightInfo;
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	pub struct Pallet<T>(_);
+
+	#[pallet::error]
+	pub enum Error<T> {
+		AccountNotEmpty,
+		AccountIsNotMigrating,
+	}
+
+	#[pallet::storage]
+	pub(super) type MigrationPending<T: Config> =
+		StorageMap<Hasher = Twox64Concat, Key = H160, Value = bool, QueryKind = ValueQuery>;
+
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
+		#[pallet::weight(<SelfWeightOf<T>>::begin())]
+		pub fn begin(origin: OriginFor<T>, address: H160) -> DispatchResult {
+			ensure_root(origin)?;
+			ensure!(
+				<pallet_evm::Pallet<T>>::is_account_empty(&address)
+					&& !<MigrationPending<T>>::get(&address),
+				<Error<T>>::AccountNotEmpty,
+			);
+
+			<MigrationPending<T>>::insert(address, true);
+			Ok(())
+		}
+
+		#[pallet::weight(<SelfWeightOf<T>>::set_data(data.len() as u32))]
+		pub fn set_data(
+			origin: OriginFor<T>,
+			address: H160,
+			data: Vec<(H256, H256)>,
+		) -> DispatchResult {
+			use frame_support::StorageDoubleMap;
+			ensure_root(origin)?;
+			ensure!(
+				<MigrationPending<T>>::get(&address),
+				<Error<T>>::AccountIsNotMigrating,
+			);
+
+			for (k, v) in data {
+				pallet_evm::AccountStorages::insert(&address, k, v);
+			}
+			Ok(())
+		}
+
+		#[pallet::weight(<SelfWeightOf<T>>::finish(code.len() as u32))]
+		#[transactional]
+		pub fn finish(origin: OriginFor<T>, address: H160, code: Vec<u8>) -> DispatchResult {
+			use frame_support::StorageMap;
+			ensure_root(origin)?;
+			ensure!(
+				<MigrationPending<T>>::get(&address),
+				<Error<T>>::AccountIsNotMigrating,
+			);
+
+			pallet_evm::AccountCodes::insert(&address, code);
+			<MigrationPending<T>>::remove(address);
+			Ok(())
+		}
+	}
+
+	pub struct OnMethodCall<T>(PhantomData<T>);
+	impl<T: Config> pallet_evm::OnMethodCall<T> for OnMethodCall<T> {
+		fn is_reserved(contract: &H160) -> bool {
+			<MigrationPending<T>>::get(&contract)
+		}
+
+		fn is_used(_contract: &H160) -> bool {
+			false
+		}
+
+		fn call(
+			_source: &H160,
+			_arget: &H160,
+			_gas_left: u64,
+			_input: &[u8],
+			_value: sp_core::U256,
+		) -> Option<pallet_evm::PrecompileOutput> {
+			None
+		}
+
+		fn get_code(_contract: &H160) -> Option<Vec<u8>> {
+			None
+		}
+	}
+}
addedpallets/evm-migration/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-migration/src/weights.rs
@@ -0,0 +1,80 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_evm_migration
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
+//! DATE: 2021-08-12, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128
+
+// Executed Command:
+// target/release/nft
+// benchmark
+// --pallet
+// pallet-evm-migration
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=20
+// --output=./pallets/evm-migration/src/weights.rs
+
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_evm_migration.
+pub trait WeightInfo {
+	fn begin() -> Weight;
+	fn set_data(b: u32, ) -> Weight;
+	fn finish(b: u32, ) -> Weight;
+}
+
+/// Weights for pallet_evm_migration using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	fn begin() -> Weight {
+		(6_210_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(3 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_data(b: u32, ) -> Weight {
+		(2_926_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((649_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	fn finish(_b: u32, ) -> Weight {
+		(4_309_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	fn begin() -> Weight {
+		(6_210_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(3 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_data(b: u32, ) -> Weight {
+		(2_926_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((649_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	fn finish(_b: u32, ) -> Weight {
+		(4_309_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+	}
+}
\ No newline at end of file
deletedpallets/nft-transaction-payment/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nft-transaction-payment/src/benchmarking.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#![cfg(feature = "runtime-benchmarks")]
-
-use super::*;
-
-use sp_std::prelude::*;
-use frame_system::RawOrigin;
-use frame_benchmarking::{benchmarks};
-use frame_support::traits::OnInitialize;
-
-benchmarks! {
-
-	// on_initialize {
-		// let block1: T::BlockNumber = T::BlockNumber::from(1u32);
-		// let block2: T::BlockNumber = T::BlockNumber::from(2u32);
-		// Inflation::<T>::on_initialize(block1); // Create Treasury account
-	// }: { Inflation::<T>::on_initialize(block2); } // Benchmark deposit_into_existing path
-
-}
modifiedpallets/nft-transaction-payment/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft-transaction-payment/src/lib.rs
+++ b/pallets/nft-transaction-payment/src/lib.rs
@@ -11,9 +11,6 @@
 #[cfg(feature = "std")]
 pub use serde::*;
 
-#[cfg(feature = "runtime-benchmarks")]
-mod benchmarking;
-
 use frame_support::{decl_module, decl_storage};
 use sp_std::prelude::*;
 use up_sponsorship::SponsorshipHandler;
modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -41,6 +41,7 @@
     'evm-coder/std',
     'pallet-evm-coder-substrate/std',
 ]
+limit-testing = ["nft-data-structs/limit-testing"]
 
 ################################################################################
 # Substrate Dependencies
modifiedpallets/nft/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -1,419 +1,342 @@
 #![cfg(feature = "runtime-benchmarks")]
 
 use super::*;
-use crate::Module as Nft;
-
-use sp_std::prelude::*;
+use crate::Pallet;
 use frame_system::RawOrigin;
-use frame_benchmarking::{benchmarks, account, whitelisted_caller}; // , TrackedStorageKey,
+use frame_benchmarking::{benchmarks, account};
+use nft_data_structs::*;
+use core::convert::TryInto;
+use sp_runtime::DispatchError;
 
 const SEED: u32 = 1;
-/*
+
+fn create_data(size: usize) -> Vec<u8> {
+	(0..size).map(|v| (v & 0xff) as u8).collect()
+}
+fn create_u16_data(size: usize) -> Vec<u16> {
+	(0..size).map(|v| (v & 0xffff) as u16).collect()
+}
+
 fn default_nft_data() -> CreateItemData {
-	CreateItemData::NFT(CreateNftData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+	CreateItemData::NFT(CreateNftData {
+		const_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+		variable_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+	})
 }
 
-fn default_fungible_data () -> CreateItemData {
-	CreateItemData::Fungible(CreateFungibleData { })
+fn default_fungible_data() -> CreateItemData {
+	CreateItemData::Fungible(CreateFungibleData { value: 1000 })
 }
 
-fn default_re_fungible_data () -> CreateItemData {
-	CreateItemData::ReFungible(CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+fn default_re_fungible_data() -> CreateItemData {
+	CreateItemData::ReFungible(CreateReFungibleData {
+		const_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+		variable_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+		pieces: 1000,
+	})
+}
+
+fn create_collection_helper<T: Config>(
+	owner: T::AccountId,
+	mode: CollectionMode,
+) -> Result<CollectionId, DispatchError> {
+	T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
+	let col_name = create_u16_data(MAX_COLLECTION_NAME_LENGTH)
+		.try_into()
+		.unwrap();
+	let col_desc = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH)
+		.try_into()
+		.unwrap();
+	let token_prefix = create_data(MAX_TOKEN_PREFIX_LENGTH).try_into().unwrap();
+	<Pallet<T>>::create_collection(
+		RawOrigin::Signed(owner).into(),
+		col_name,
+		col_desc,
+		token_prefix,
+		mode,
+	)?;
+	Ok(CreatedCollectionCount::get())
+}
+fn create_nft_collection<T: Config>(owner: T::AccountId) -> Result<CollectionId, DispatchError> {
+	create_collection_helper::<T>(owner, CollectionMode::NFT)
+}
+fn create_fungible_collection<T: Config>(
+	owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
+	create_collection_helper::<T>(owner, CollectionMode::Fungible(0))
+}
+fn create_refungible_collection<T: Config>(
+	owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
+	create_collection_helper::<T>(owner, CollectionMode::ReFungible)
 }
-*/
 
 benchmarks! {
 
 	create_collection {
-		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 col_name: Vec<u16> = create_u16_data(MAX_COLLECTION_NAME_LENGTH);
+		let col_desc: Vec<u16> = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH);
+		let token_prefix: Vec<u8> = create_data(MAX_TOKEN_PREFIX_LENGTH);
 		let mode: CollectionMode = CollectionMode::NFT;
 		let caller: T::AccountId = account("caller", 0, SEED);
-	}: _(RawOrigin::Signed(caller.clone()), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode)
-/*
+		T::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+	}: _(RawOrigin::Signed(caller.clone()), col_name.clone(), col_desc.clone(), token_prefix.clone(), mode)
 	verify {
-		assert_eq!(Nft::<T>::collection_id(2).owner, caller);
+		assert_eq!(<Pallet<T>>::collection_id(2).unwrap().owner, caller);
 	}
+
 	destroy_collection {
-		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())?;
-	}: _(RawOrigin::Signed(caller.clone()), 2)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection)
 
 	add_to_white_list {
-		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());
+		let caller: T::AccountId = account("caller", 0, SEED);
 		let whitelist_account: T::AccountId = account("admin", 0, SEED);
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: add_to_white_list(RawOrigin::Signed(caller.clone()), 2, whitelist_account)
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(whitelist_account))
 
 	remove_from_white_list {
-		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());
+		let caller: T::AccountId = account("caller", 0, SEED);
 		let whitelist_account: T::AccountId = account("admin", 0, SEED);
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		Nft::<T>::add_to_white_list(RawOrigin::Signed(caller.clone()).into(), 2, whitelist_account.clone())?;
-	}: remove_from_white_list(RawOrigin::Signed(caller.clone()), 2, whitelist_account)
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		<Pallet<T>>::add_to_white_list(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(whitelist_account.clone()))?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(whitelist_account))
 
 	set_public_access_mode {
-		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_public_access_mode(RawOrigin::Signed(caller.clone()), 2, AccessMode::WhiteList)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, AccessMode::WhiteList)
 
 	set_mint_permission {
-		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_mint_permission(RawOrigin::Signed(caller.clone()), 2, true)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, true)
 
 	change_collection_owner {
-		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 caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let new_owner: T::AccountId = account("admin", 0, SEED);
-	}: change_collection_owner(RawOrigin::Signed(caller.clone()), 2, new_owner)
+	}: _(RawOrigin::Signed(caller.clone()), collection, new_owner)
 
 	add_collection_admin {
-		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 caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let new_admin: T::AccountId = account("admin", 0, SEED);
-	}: add_collection_admin(RawOrigin::Signed(caller.clone()), 2, new_admin)
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(new_admin))
 
 	remove_collection_admin {
-		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 caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let new_admin: T::AccountId = account("admin", 0, SEED);
-		Nft::<T>::add_collection_admin(RawOrigin::Signed(caller.clone()).into(), 2, new_admin.clone())?;
-	}: remove_collection_admin(RawOrigin::Signed(caller.clone()), 2, new_admin)
+		<Pallet<T>>::add_collection_admin(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(new_admin.clone()))?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(new_admin))
 
 	set_collection_sponsor {
-		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_collection_sponsor(RawOrigin::Signed(caller.clone()), 2, caller.clone())
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, caller.clone())
 
 	confirm_sponsorship {
-		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())?;
-		Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
-	}: confirm_sponsorship(RawOrigin::Signed(caller.clone()), 2)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		<Pallet<T>>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection)
 
 	remove_collection_sponsor {
-		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())?;
-		Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
-		Nft::<T>::confirm_sponsorship(RawOrigin::Signed(caller.clone()).into(), 2)?;
-	}: remove_collection_sponsor(RawOrigin::Signed(caller.clone()), 2)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		<Pallet<T>>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
+		<Pallet<T>>::confirm_sponsorship(RawOrigin::Signed(caller.clone()).into(), 2)?;
+	}: _(RawOrigin::Signed(caller.clone()), collection)
 
 	// nft item
 	create_item_nft {
-		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 data = default_nft_data();
+		let b in 0..(CUSTOM_DATA_LIMIT * 2);
 
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = CreateItemData::NFT(CreateNftData {
+			const_data: create_data(b.min(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+			variable_data: create_data(b.saturating_sub(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+		});
+	}: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
-	#[extra]
-	create_item_nft_large {
-		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());
-		let mut nft_data = CreateNftData {
-			const_data: vec![],
-			variable_data: vec![]
-		};
-		for i in 0..1998 {
-			nft_data.const_data.push(10);
-			nft_data.variable_data.push(10);
-		}
-		let data = CreateItemData::NFT(nft_data);
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+	create_multiple_items_nft {
+		// TODO: Take item data size into account. As create_item_nft bench shows, this parameter has no effect on execution time,
+		// but it may if we increase CUSTOM_DATA_LIMIT
+		let b in 1..1000;
 
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = (0..b).map(|_| default_nft_data()).collect();
+	}: create_multiple_items(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
 	// fungible item
 	create_item_fungible {
-		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::Fungible(3);
-		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 data = default_fungible_data();
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
+		let data = CreateItemData::Fungible(CreateFungibleData {
+			value: 1000,
+		});
+	}: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
+
+	create_multiple_items_fungible {
+		let b in 1..1000;
 
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
+		let data = (0..b).map(|_| default_fungible_data()).collect();
+	}: create_multiple_items(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
 	// refungible item
 	create_item_refungible {
-		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::ReFungible(3);
-		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 data = default_re_fungible_data();
+		let b in 0..(CUSTOM_DATA_LIMIT * 2);
 
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
+		let data = CreateItemData::ReFungible(CreateReFungibleData {
+			const_data: create_data(b.min(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+			variable_data: create_data(b.saturating_sub(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+			pieces: 1000,
+		});
+	}: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
-	burn_item {
-		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 data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+	create_multiple_items_refungible {
+		// TODO: Take item data size into account. As create_item_nft bench shows, this parameter has no effect on execution time,
+		// but it may if we increase CUSTOM_DATA_LIMIT
+		let b in 1..1000;
 
-	}: burn_item(RawOrigin::Signed(caller.clone()), 2, 1)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
+		let data = (0..b).map(|_| default_re_fungible_data()).collect();
+	}: create_multiple_items(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
+	burn_item_nft {
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = default_nft_data();
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: burn_item(RawOrigin::Signed(caller.clone()), collection, 1, 1)
+
 	transfer_nft {
-		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 = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
-	}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
 	transfer_fungible {
-		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::Fungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
-	}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
 	transfer_refungible {
-		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::ReFungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_re_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
-	}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
+	set_transfers_enabled_flag {
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, false)
 
-	approve {
-		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::ReFungible(3);
+	approve_nft {
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_re_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
-	}: approve(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1)
+		let data = default_nft_data();
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: approve(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
 	// Nft
 	transfer_from_nft {
-		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 = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-		Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
-	}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		<Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+	}: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
 
 	// Fungible
 	transfer_from_fungible {
-		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::Fungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-		Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
-	}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		<Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+	}: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
 
 	// ReFungible
 	transfer_from_refungible {
-		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::ReFungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		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 data = default_re_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-		Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
-	}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
-
-	enable_contract_sponsoring {
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-
-	}: enable_contract_sponsoring(RawOrigin::Signed(caller.clone()), caller.clone(), true)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		<Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+	}: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
 
 	set_offchain_schema {
-		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::ReFungible(3);
-		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 b in 0..OFFCHAIN_SCHEMA_LIMIT;
 
-	}: set_offchain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = create_data(b as usize);
+	}: set_offchain_schema(RawOrigin::Signed(caller.clone()), collection, data)
 
 	set_const_on_chain_schema {
-		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::ReFungible(3);
-		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_const_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+		let b in 0..CONST_ON_CHAIN_SCHEMA_LIMIT;
 
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = create_data(b as usize);
+	}: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data)
+
 	set_variable_on_chain_schema {
-		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::ReFungible(3);
-		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_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+		let b in 0..VARIABLE_ON_CHAIN_SCHEMA_LIMIT;
 
-	set_variable_meta_data {
-		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 data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = create_data(b as usize);
+	}: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, data)
 
-	}: set_variable_meta_data(RawOrigin::Signed(caller.clone()), 2, 1, [1, 2, 3].to_vec())
+	set_variable_meta_data_nft {
+		let b in 0..CUSTOM_DATA_LIMIT;
 
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = default_nft_data();
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		let data = create_data(b as usize);
+	}: set_variable_meta_data(RawOrigin::Signed(caller.clone()), collection, 1, data)
+
 	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())?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.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 caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 
 		let cl = CollectionLimits {
 			account_token_ownership_limit: 0,
 			sponsored_data_size: 0,
-			token_limit: 0,
-			sponsor_transfer_timeout: 0
+			token_limit: 1,
+			sponsor_transfer_timeout: 0,
+			owner_can_destroy: true,
+			owner_can_transfer: true,
+			sponsored_data_rate_limit: None,
 		};
-
 	}: 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)
-*/
 }
deletedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
-
-impl crate::WeightInfo for () {
-	fn create_collection() -> Weight {
-		70_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn destroy_collection() -> Weight {
-		90_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn add_to_white_list() -> Weight {
-		30_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_from_white_list() -> Weight {
-		35_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_public_access_mode() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_mint_permission() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn change_collection_owner() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn add_collection_admin() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_admin() -> Weight {
-		50_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_collection_sponsor() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn confirm_sponsorship() -> Weight {
-		22_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_sponsor() -> Weight {
-		24_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn create_item(s: usize) -> Weight {
-		130_000_000_u64
-			.saturating_add(2135_u64.saturating_mul(s as Weight).saturating_mul(500_u64)) // 500 is temporary multiplier, fee for storage
-			.saturating_add(DbWeight::get().reads(10_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn burn_item() -> Weight {
-		170_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn transfer() -> Weight {
-		125_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn approve() -> Weight {
-		45_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn transfer_from() -> Weight {
-		150_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn set_offchain_schema() -> Weight {
-		33_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_const_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_meta_data() -> Weight {
-		17_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn enable_contract_sponsoring() -> Weight {
-		13_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_schema_version() -> Weight {
-		8_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_chain_limits() -> Weight {
-		1_300_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_contract_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_variable_meta_data_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn toggle_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn add_to_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn remove_from_contract_white_list() -> Weight {
-		3_200_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_collection_limits() -> Weight {
-		8_900_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-}
modifiedpallets/nft/src/eth/erc.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/erc.rs
+++ b/pallets/nft/src/eth/erc.rs
@@ -1,12 +1,16 @@
 use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
-use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};
+use evm_coder::{ToLog, execution::Result, generate_stubgen, solidity, solidity_interface, types::*};
+use nft_data_structs::{CreateItemData, CreateNftData};
 use core::convert::TryInto;
-use alloc::format;
-use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};
-use frame_support::storage::StorageDoubleMap;
+use crate::{
+	Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,
+	ItemListIndex,
+};
+use frame_support::storage::{StorageMap, StorageDoubleMap};
 use pallet_evm::AddressMapping;
+use pallet_evm_coder_substrate::dispatch_to_evm;
 use super::account::CrossAccountId;
-use sp_std::vec::Vec;
+use sp_std::{vec, vec::Vec};
 
 #[solidity_interface(name = "ERC165")]
 impl<T: Config> CollectionHandle<T> {
@@ -42,9 +46,15 @@
 
 #[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]
 impl<T: Config> CollectionHandle<T> {
+	#[solidity(rename_selector = "tokenURI")]
 	fn token_uri(&self, token_id: uint256) -> Result<string> {
-		// TODO: We should standartize url prefix, maybe via offchain schema?
-		Ok(format!("unique.network/{}/{}", self.id, token_id))
+		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
+		Ok(string::from_utf8_lossy(
+			&<NftItemList<T>>::get(self.id, token_id)
+				.ok_or("token not found")?
+				.const_data,
+		)
+		.into())
 	}
 }
 
@@ -178,6 +188,93 @@
 	}
 }
 
+#[solidity_interface(name = "ERC721Burnable")]
+impl<T: Config> CollectionHandle<T> {
+	fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
+
+		<Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;
+		Ok(())
+	}
+}
+
+#[derive(ToLog)]
+pub enum ERC721MintableEvents {
+	#[allow(dead_code)]
+	MintingFinished {},
+}
+
+#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
+impl<T: Config> CollectionHandle<T> {
+	fn minting_finished(&self) -> Result<bool> {
+		Ok(false)
+	}
+
+	fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let to = T::CrossAccountId::from_eth(to);
+		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
+		if <ItemListIndex>::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?
+			!= token_id
+		{
+			return Err("item id should be next".into());
+		}
+
+		<Module<T>>::create_item_internal(
+			&caller,
+			&self,
+			&to,
+			CreateItemData::NFT(CreateNftData {
+				const_data: vec![].try_into().unwrap(),
+				variable_data: vec![].try_into().unwrap(),
+			}),
+		)
+		.map_err(|_| "mint error")?;
+		Ok(true)
+	}
+
+	#[solidity(rename_selector = "mintWithTokenURI")]
+	fn mint_with_token_uri(
+		&mut self,
+		caller: caller,
+		to: address,
+		token_id: uint256,
+		token_uri: string,
+	) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let to = T::CrossAccountId::from_eth(to);
+		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
+		if <ItemListIndex>::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?
+			!= token_id
+		{
+			return Err("item id should be next".into());
+		}
+
+		<Module<T>>::create_item_internal(
+			&caller,
+			&self,
+			&to,
+			CreateItemData::NFT(CreateNftData {
+				const_data: Vec::<u8>::from(token_uri)
+					.try_into()
+					.map_err(|_| "token uri is too long")?,
+				variable_data: vec![].try_into().unwrap(),
+			}),
+		)
+		.map_err(|_| "mint error")?;
+		Ok(true)
+	}
+
+	fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
+		Err("not implementable".into())
+	}
+}
+
 #[solidity_interface(name = "ERC721UniqueExtensions")]
 impl<T: Config> CollectionHandle<T> {
 	#[solidity(rename_selector = "transfer")]
@@ -196,6 +293,97 @@
 			.map_err(|_| "transfer error")?;
 		Ok(())
 	}
+
+	fn next_token_id(&self) -> Result<uint256> {
+		Ok(ItemListIndex::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?
+			.into())
+	}
+
+	fn set_variable_metadata(
+		&mut self,
+		caller: caller,
+		token_id: uint256,
+		data: bytes,
+	) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let token_id = token_id.try_into().map_err(|_| "token id overflow")?;
+
+		<Module<T>>::set_variable_meta_data_internal(&caller, self, token_id, data)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+
+	fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {
+		let token_id = token_id.try_into().map_err(|_| "token id overflow")?;
+
+		<Module<T>>::get_variable_metadata(self, token_id).map_err(dispatch_to_evm::<T>)
+	}
+
+	fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let to = T::CrossAccountId::from_eth(to);
+		let mut expected_index = <ItemListIndex>::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?;
+
+		let total_tokens = token_ids.len();
+		for id in token_ids.into_iter() {
+			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;
+			if id != expected_index {
+				return Err("item id should be next".into());
+			}
+			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
+		}
+
+		let data = (0..total_tokens)
+			.map(|_| {
+				CreateItemData::NFT(CreateNftData {
+					const_data: vec![].try_into().unwrap(),
+					variable_data: vec![].try_into().unwrap(),
+				})
+			})
+			.collect();
+
+		<Module<T>>::create_multiple_items_internal(&caller, self, &to, data)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
+
+	#[solidity(rename_selector = "mintBulkWithTokenURI")]
+	fn mint_bulk_with_token_uri(
+		&mut self,
+		caller: caller,
+		to: address,
+		tokens: Vec<(uint256, string)>,
+	) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let to = T::CrossAccountId::from_eth(to);
+		let mut expected_index = <ItemListIndex>::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?;
+
+		let mut data = Vec::with_capacity(tokens.len());
+		for (id, token_uri) in tokens {
+			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;
+			if id != expected_index {
+				panic!("item id should be next ({}) but got {}", expected_index, id);
+			}
+			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
+
+			data.push(CreateItemData::NFT(CreateNftData {
+				const_data: Vec::<u8>::from(token_uri)
+					.try_into()
+					.map_err(|_| "token uri is too long")?,
+				variable_data: vec![].try_into().unwrap(),
+			}));
+		}
+
+		<Module<T>>::create_multiple_items_internal(&caller, self, &to, data)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
 }
 
 #[solidity_interface(
@@ -205,7 +393,9 @@
 		ERC721,
 		ERC721Metadata,
 		ERC721Enumerable,
-		ERC721UniqueExtensions
+		ERC721UniqueExtensions,
+		ERC721Mintable,
+		ERC721Burnable,
 	)
 )]
 impl<T: Config> CollectionHandle<T> {}
@@ -297,3 +487,10 @@
 
 #[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
 impl<T: Config> CollectionHandle<T> {}
+
+// Not a tests, but code generators
+generate_stubgen!(nft_impl, UniqueNFTCall, true);
+generate_stubgen!(nft_iface, UniqueNFTCall, false);
+
+generate_stubgen!(fungible_impl, UniqueFungibleCall, true);
+generate_stubgen!(fungible_iface, UniqueFungibleCall, false);
modifiedpallets/nft/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/mod.rs
+++ b/pallets/nft/src/eth/mod.rs
@@ -96,10 +96,14 @@
 			.and_then(<CollectionById<T>>::get)
 			.map(|collection| {
 				match collection.mode {
-					CollectionMode::NFT => include_bytes!("stubs/ERC721.bin") as &[u8],
-					CollectionMode::Fungible(_) => include_bytes!("stubs/ERC20.bin") as &[u8],
-					CollectionMode::ReFungible => include_bytes!("stubs/ERC1633.bin") as &[u8],
-					CollectionMode::Invalid => include_bytes!("stubs/Invalid.bin") as &[u8],
+					CollectionMode::NFT => include_bytes!("stubs/UniqueNFT.raw") as &[u8],
+					CollectionMode::Fungible(_) => {
+						include_bytes!("stubs/UniqueFungible.raw") as &[u8]
+					}
+					CollectionMode::ReFungible => {
+						include_bytes!("stubs/UniqueRefungible.raw") as &[u8]
+					}
+					CollectionMode::Invalid => include_bytes!("stubs/UniqueInvalid.raw") as &[u8],
 				}
 				.to_owned()
 			})
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -1,12 +1,12 @@
 //! Implements EVM sponsoring logic via OnChargeEVMTransaction
 
 use crate::{
-	ChainLimit, Collection, CollectionById, Config, FungibleTransferBasket, NftTransferBasket,
+	Collection, CollectionById, Config, FungibleTransferBasket, NftTransferBasket,
 	eth::{account::EvmBackwardsAddressMapping, map_eth_to_id},
 };
 use evm_coder::{Call, abi::AbiReader};
 use frame_support::{
-	storage::{StorageMap, StorageDoubleMap, StorageValue},
+	storage::{StorageMap, StorageDoubleMap},
 };
 use sp_core::H160;
 use sp_std::prelude::*;
@@ -17,6 +17,7 @@
 };
 use core::convert::TryInto;
 use core::marker::PhantomData;
+use nft_data_structs::{NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
 
 struct AnyError;
 
@@ -43,7 +44,7 @@
 					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
 						collection_limits.sponsor_transfer_timeout
 					} else {
-						ChainLimit::get().nft_sponsor_transfer_timeout
+						NFT_SPONSOR_TRANSFER_TIMEOUT
 					};
 
 					let mut sponsor = true;
@@ -74,7 +75,7 @@
 					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
 						collection_limits.sponsor_transfer_timeout
 					} else {
-						ChainLimit::get().fungible_sponsor_transfer_timeout
+						FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
 					};
 
 					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
deletedpallets/nft/src/eth/stubs/ERC1633.bindiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/ERC1633.bin
+++ /dev/null
@@ -1 +0,0 @@
-TODO
\ No newline at end of file
deletedpallets/nft/src/eth/stubs/ERC20.bindiffbeforeafterboth

binary blob — no preview

deletedpallets/nft/src/eth/stubs/ERC20.soldiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/ERC20.sol
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: OTHER
-
-pragma solidity >=0.8.0 <0.9.0;
-
-contract ERC20 {
-	uint8 _dummy = 0;
-	string stub_error = "this contract does not exists, code for collections is implemented at pallet side";
-
-	// 0x18160ddd
-	function totalSupply() external view returns (uint256) {
-		require(false, stub_error);
-		_dummy;
-		return 0;
-	}
-
-	// 0x70a08231
-	function balanceOf(address account) external view returns (uint256) {
-		require(false, stub_error);
-		account;
-		_dummy;
-		return 0;
-	}
-
-	// 0xa9059cbb
-	function transfer(address recipient, uint256 amount) external returns (bool) {
-		require(false, stub_error);
-		recipient;
-		amount;
-		_dummy = 0;
-		return false;
-	}
-
-	// 0xdd62ed3e
-	function allowance(address owner, address spender) external view returns (uint256) {
-		require(false, stub_error);
-		owner;
-		spender;
-		return _dummy;
-	}
-
-	// 0x095ea7b3
-	function approve(address spender, uint256 amount) external returns (bool) {
-		require(false, stub_error);
-		spender;
-		amount;
-		_dummy = 0;
-		return false;
-	}
-
-	// 0x23b872dd
-	function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
-		require(false, stub_error);
-		sender;
-		recipient;
-		amount;
-		_dummy = 0;
-		return false;
-	}
-
-	// While ERC165 is not required by spec of ERC20, better implement it
-	// 0x01ffc9a7
-	function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
-		return 
-			// ERC20
-			interfaceID == 0x36372b07 || 
-			// ERC165
-			interfaceID == 0x01ffc9a7;
-	}
-}
\ No newline at end of file
deletedpallets/nft/src/eth/stubs/ERC721.bindiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/ERC721.bin
+++ /dev/null
@@ -1 +0,0 @@
-0x608060405260008060006101000a81548160ff021916908360ff16021790555060008060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060405180608001604052806051815260200162000cd6605191396001908051906020019061008f9291906100a2565b5034801561009c57600080fd5b506101a6565b8280546100ae90610145565b90600052602060002090601f0160209004810192826100d05760008555610117565b82601f106100e957805160ff1916838001178555610117565b82800160010185558215610117579182015b828111156101165782518255916020019190600101906100fb565b5b5090506101249190610128565b5090565b5b80821115610141576000816000905550600101610129565b5090565b6000600282049050600182168061015d57607f821691505b6020821081141561017157610170610177565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b610b2080620001b66000396000f3fe6080604052600436106100915760003560e01c80636352211e116100595780636352211e1461016457806370a08231146101a1578063a22cb465146101de578063b88d4fde14610207578063e985e9c51461022357610091565b806301ffc9a714610096578063081812fc146100d3578063095ea7b31461011057806323b872dd1461012c57806342842e0e14610148575b600080fd5b3480156100a257600080fd5b506100bd60048036038101906100b89190610821565b610260565b6040516100ca919061093b565b60405180910390f35b3480156100df57600080fd5b506100fa60048036038101906100f5919061084a565b6102c2565b6040516101079190610920565b60405180910390f35b61012a600480360381019061012591906107e5565b610333565b005b610146600480360381019061014191906106da565b61037d565b005b610162600480360381019061015d91906106da565b6103c8565b005b34801561017057600080fd5b5061018b6004803603810190610186919061084a565b610413565b6040516101989190610920565b60405180910390f35b3480156101ad57600080fd5b506101c860048036038101906101c39190610675565b610484565b6040516101d59190610978565b60405180910390f35b3480156101ea57600080fd5b50610205600480360381019061020091906107a9565b6104d4565b005b610221600480360381019061021c9190610729565b610539565b005b34801561022f57600080fd5b5061024a6004803603810190610245919061069e565b610586565b604051610257919061093b565b60405180910390f35b60006380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806102bb57506301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b600080600190610308576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102ff9190610956565b60405180910390fd5b50600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000600190610378576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036f9190610956565b60405180910390fd5b505050565b60006001906103c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b99190610956565b60405180910390fd5b50505050565b600060019061040d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104049190610956565b60405180910390fd5b50505050565b600080600190610459576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104509190610956565b60405180910390fd5b50600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000806001906104ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c19190610956565b60405180910390fd5b5060009050919050565b6000600190610519576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105109190610956565b60405180910390fd5b5060008060006101000a81548160ff021916908360ff1602179055505050565b600060019061057e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105759190610956565b60405180910390fd5b505050505050565b6000806001906105cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105c39190610956565b60405180910390fd5b506000905092915050565b6000813590506105e681610a8e565b92915050565b6000813590506105fb81610aa5565b92915050565b60008135905061061081610abc565b92915050565b60008083601f84011261062857600080fd5b8235905067ffffffffffffffff81111561064157600080fd5b60208301915083600182028301111561065957600080fd5b9250929050565b60008135905061066f81610ad3565b92915050565b60006020828403121561068757600080fd5b6000610695848285016105d7565b91505092915050565b600080604083850312156106b157600080fd5b60006106bf858286016105d7565b92505060206106d0858286016105d7565b9150509250929050565b6000806000606084860312156106ef57600080fd5b60006106fd868287016105d7565b935050602061070e868287016105d7565b925050604061071f86828701610660565b9150509250925092565b60008060008060006080868803121561074157600080fd5b600061074f888289016105d7565b9550506020610760888289016105d7565b945050604061077188828901610660565b935050606086013567ffffffffffffffff81111561078e57600080fd5b61079a88828901610616565b92509250509295509295909350565b600080604083850312156107bc57600080fd5b60006107ca858286016105d7565b92505060206107db858286016105ec565b9150509250929050565b600080604083850312156107f857600080fd5b6000610806858286016105d7565b925050602061081785828601610660565b9150509250929050565b60006020828403121561083357600080fd5b600061084184828501610601565b91505092915050565b60006020828403121561085c57600080fd5b600061086a84828501610660565b91505092915050565b61087c816109b9565b82525050565b61088b816109cb565b82525050565b6000815461089e81610a2d565b6108a881866109a8565b945060018216600081146108c357600181146108d557610908565b60ff1983168652602086019350610908565b6108de85610993565b60005b83811015610900578154818901526001820191506020810190506108e1565b808801955050505b50505092915050565b61091a81610a23565b82525050565b60006020820190506109356000830184610873565b92915050565b60006020820190506109506000830184610882565b92915050565b600060208201905081810360008301526109708184610891565b905092915050565b600060208201905061098d6000830184610911565b92915050565b60008190508160005260206000209050919050565b600082825260208201905092915050565b60006109c482610a03565b9050919050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006002820490506001821680610a4557607f821691505b60208210811415610a5957610a58610a5f565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b610a97816109b9565b8114610aa257600080fd5b50565b610aae816109cb565b8114610ab957600080fd5b50565b610ac5816109d7565b8114610ad057600080fd5b50565b610adc81610a23565b8114610ae757600080fd5b5056fea26469706673582212206e53fcc41e6b23f5bd49a262462ecf5ff647f480649658ee8b67c91a8f733ba864736f6c634300080100337468697320636f6e747261637420646f6573206e6f74206578697374732c20636f646520666f7220636f6c6c656374696f6e7320697320696d706c656d656e7465642061742070616c6c65742073696465
\ No newline at end of file
deletedpallets/nft/src/eth/stubs/ERC721.soldiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/ERC721.sol
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: OTHER
-
-pragma solidity >=0.8.0 <0.9.0;
-
-contract ERC721 {
-    uint8 _dummy = 0;
-    address _dummy_addr = 0x0000000000000000000000000000000000000000;
-    string _dummy_string = "";
-    string stub_error =
-        "this contract does not exists, code for collections is implemented at pallet side";
-
-    event Transfer(
-        address indexed from,
-        address indexed to,
-        uint256 indexed tokenId
-    );
-
-    event Approval(
-        address indexed owner,
-        address indexed approved,
-        uint256 indexed tokenId
-    );
-
-    event ApprovalForAll(
-        address indexed owner,
-        address indexed operator,
-        bool approved
-    );
-
-	// 0x18160ddd
-    function totalSupply() external view returns (uint256) {
-        require(false, stub_error);
-        return 0;
-    }
-
-    function name() external view returns (string memory res_name) {
-        require(false, stub_error);
-        res_name = _dummy_string;
-    }
-
-    function symbol() external view returns (string memory res_symbol) {
-        require(false, stub_error);
-        res_symbol = _dummy_string;
-    }
-
-    function tokenURI(uint256 tokenId) external view returns (string memory) {
-        require(false, stub_error);
-        tokenId;
-        return _dummy_string;
-    }
-
-    function tokenByIndex(uint256 index) external view returns (uint256) {
-        require(false, stub_error);
-        index;
-		return 0;
-    }
-
-    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) {
-        require(false, stub_error);
-		owner;
-		index;
-		return 0;
-    }
-
-    // 0x70a08231
-    function balanceOf(address owner) external view returns (uint256) {
-        require(false, stub_error);
-        owner;
-        return 0;
-    }
-
-    // 0x6352211e
-    function ownerOf(uint256 tokenId) external view returns (address) {
-        require(false, stub_error);
-        tokenId;
-        return _dummy_addr;
-    }
-
-    // 0xb88d4fde
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId,
-        bytes calldata data
-    ) external payable {
-        require(false, stub_error);
-        from;
-        to;
-        tokenId;
-        data;
-    }
-
-    // 0x42842e0e
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) external payable {
-        require(false, stub_error);
-        from;
-        to;
-        tokenId;
-    }
-
-    // 0x23b872dd
-    function transferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) external payable {
-        require(false, stub_error);
-        from;
-        to;
-        tokenId;
-    }
-
-    // 0x095ea7b3
-    function approve(address approved, uint256 tokenId) external payable {
-        require(false, stub_error);
-        approved;
-        tokenId;
-    }
-
-    // 0xa22cb465
-    function setApprovalForAll(address operator, bool approved) external {
-        require(false, stub_error);
-        operator;
-        approved;
-        _dummy = 0;
-    }
-
-    // 0x081812fc
-    function getApproved(uint256 tokenId) external view returns (address) {
-        require(false, stub_error);
-        tokenId;
-        return _dummy_addr;
-    }
-
-    // 0xe985e9c5
-    function isApprovedForAll(address owner, address operator)
-        external
-        view
-        returns (bool)
-    {
-        require(false, stub_error);
-        owner;
-        operator;
-        return false;
-    }
-
-    // 0x01ffc9a7
-    function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
-        return
-            // ERC721
-            interfaceID == 0x80ac58cd ||
-            // ERC721Metadata
-            interfaceID == 0x5b5e139f ||
-            // ERC721Enumerable
-            interfaceID == 0x780e9d63 ||
-            // ERC165
-            interfaceID == 0x01ffc9a7;
-    }
-}
deletedpallets/nft/src/eth/stubs/Invalid.bindiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/Invalid.bin
+++ /dev/null
@@ -1 +0,0 @@
-TODO
\ No newline at end of file
addedpallets/nft/src/eth/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

addedpallets/nft/src/eth/stubs/UniqueFungible.soldiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft/src/eth/stubs/UniqueFungible.sol
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+contract Dummy {
+	uint8 dummy;
+	string stub_error = "this contract is implemented in native";
+}
+
+// Inline
+contract ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+// Inline
+contract InlineNameSymbol is Dummy {
+	// Selector: name() 06fdde03
+	function name() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	// Selector: symbol() 95d89b41
+	function symbol() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+}
+
+// Inline
+contract InlineTotalSupply is Dummy {
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+}
+
+contract ERC165 is Dummy {
+	// Selector: supportsInterface(bytes4) 01ffc9a7
+	function supportsInterface(uint32 interfaceId) public view returns (bool) {
+		require(false, stub_error);
+		interfaceId;
+		dummy;
+		return false;
+	}
+}
+
+contract ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
+	// Selector: decimals() 313ce567
+	function decimals() public view returns (uint8) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	// Selector: balanceOf(address) 70a08231
+	function balanceOf(address owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: transferFrom(address,address,uint256) 23b872dd
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) public returns (bool) {
+		require(false, stub_error);
+		from;
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: approve(address,uint256) 095ea7b3
+	function approve(address spender, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		spender;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: allowance(address,address) dd62ed3e
+	function allowance(address owner, address spender)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		spender;
+		dummy;
+		return 0;
+	}
+}
+
+contract UniqueFungible is Dummy, ERC165, ERC20 {}
addedpallets/nft/src/eth/stubs/UniqueInvalid.rawdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft/src/eth/stubs/UniqueInvalid.raw
@@ -0,0 +1 @@
+TODO
\ No newline at end of file
addedpallets/nft/src/eth/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

addedpallets/nft/src/eth/stubs/UniqueNFT.soldiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft/src/eth/stubs/UniqueNFT.sol
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Anonymous struct
+struct Tuple0 {
+	uint256 field_0;
+	string field_1;
+}
+
+// Common stubs holder
+contract Dummy {
+	uint8 dummy;
+	string stub_error = "this contract is implemented in native";
+}
+
+// Inline
+contract ERC721Events {
+	event Transfer(
+		address indexed from,
+		address indexed to,
+		uint256 indexed tokenId
+	);
+	event Approval(
+		address indexed owner,
+		address indexed approved,
+		uint256 indexed tokenId
+	);
+	event ApprovalForAll(
+		address indexed owner,
+		address indexed operator,
+		bool approved
+	);
+}
+
+// Inline
+contract ERC721MintableEvents {
+	event MintingFinished();
+}
+
+// Inline
+contract InlineNameSymbol is Dummy {
+	// Selector: name() 06fdde03
+	function name() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	// Selector: symbol() 95d89b41
+	function symbol() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+}
+
+// Inline
+contract InlineTotalSupply is Dummy {
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+}
+
+contract ERC165 is Dummy {
+	// Selector: supportsInterface(bytes4) 01ffc9a7
+	function supportsInterface(uint32 interfaceId) public view returns (bool) {
+		require(false, stub_error);
+		interfaceId;
+		dummy;
+		return false;
+	}
+}
+
+contract ERC721 is Dummy, ERC165, ERC721Events {
+	// Selector: balanceOf(address) 70a08231
+	function balanceOf(address owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
+	// Selector: ownerOf(uint256) 6352211e
+	function ownerOf(uint256 tokenId) public view returns (address) {
+		require(false, stub_error);
+		tokenId;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
+	function safeTransferFromWithData(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) public {
+		require(false, stub_error);
+		from;
+		to;
+		tokenId;
+		data;
+		dummy = 0;
+	}
+
+	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) public {
+		require(false, stub_error);
+		from;
+		to;
+		tokenId;
+		dummy = 0;
+	}
+
+	// Selector: transferFrom(address,address,uint256) 23b872dd
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) public {
+		require(false, stub_error);
+		from;
+		to;
+		tokenId;
+		dummy = 0;
+	}
+
+	// Selector: approve(address,uint256) 095ea7b3
+	function approve(address approved, uint256 tokenId) public {
+		require(false, stub_error);
+		approved;
+		tokenId;
+		dummy = 0;
+	}
+
+	// Selector: setApprovalForAll(address,bool) a22cb465
+	function setApprovalForAll(address operator, bool approved) public {
+		require(false, stub_error);
+		operator;
+		approved;
+		dummy = 0;
+	}
+
+	// Selector: getApproved(uint256) 081812fc
+	function getApproved(uint256 tokenId) public view returns (address) {
+		require(false, stub_error);
+		tokenId;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	// Selector: isApprovedForAll(address,address) e985e9c5
+	function isApprovedForAll(address owner, address operator)
+		public
+		view
+		returns (address)
+	{
+		require(false, stub_error);
+		owner;
+		operator;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+}
+
+contract ERC721Burnable is Dummy {
+	// Selector: burn(uint256) 42966c68
+	function burn(uint256 tokenId) public {
+		require(false, stub_error);
+		tokenId;
+		dummy = 0;
+	}
+}
+
+contract ERC721Enumerable is Dummy, InlineTotalSupply {
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) public view returns (uint256) {
+		require(false, stub_error);
+		index;
+		dummy;
+		return 0;
+	}
+
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		index;
+		dummy;
+		return 0;
+	}
+}
+
+contract ERC721Metadata is Dummy, InlineNameSymbol {
+	// Selector: tokenURI(uint256) c87b56dd
+	function tokenURI(uint256 tokenId) public view returns (string memory) {
+		require(false, stub_error);
+		tokenId;
+		dummy;
+		return "";
+	}
+}
+
+contract ERC721Mintable is Dummy, ERC721MintableEvents {
+	// Selector: mintingFinished() 05d2035b
+	function mintingFinished() public view returns (bool) {
+		require(false, stub_error);
+		dummy;
+		return false;
+	}
+
+	// Selector: mint(address,uint256) 40c10f19
+	function mint(address to, uint256 tokenId) public returns (bool) {
+		require(false, stub_error);
+		to;
+		tokenId;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
+	function mintWithTokenURI(
+		address to,
+		uint256 tokenId,
+		string memory tokenUri
+	) public returns (bool) {
+		require(false, stub_error);
+		to;
+		tokenId;
+		tokenUri;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: finishMinting() 7d64bcb4
+	function finishMinting() public returns (bool) {
+		require(false, stub_error);
+		dummy = 0;
+		return false;
+	}
+}
+
+contract ERC721UniqueExtensions is Dummy {
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 tokenId) public {
+		require(false, stub_error);
+		to;
+		tokenId;
+		dummy = 0;
+	}
+
+	// Selector: nextTokenId() 75794a3c
+	function nextTokenId() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	// Selector: setVariableMetadata(uint256,bytes) d4eac26d
+	function setVariableMetadata(uint256 tokenId, bytes memory data) public {
+		require(false, stub_error);
+		tokenId;
+		data;
+		dummy = 0;
+	}
+
+	// Selector: getVariableMetadata(uint256) e6c5ce6f
+	function getVariableMetadata(uint256 tokenId)
+		public
+		view
+		returns (bytes memory)
+	{
+		require(false, stub_error);
+		tokenId;
+		dummy;
+		return hex"";
+	}
+
+	// Selector: mintBulk(address,uint256[]) 44a9945e
+	function mintBulk(address to, uint256[] memory tokenIds)
+		public
+		returns (bool)
+	{
+		require(false, stub_error);
+		to;
+		tokenIds;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
+	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+		public
+		returns (bool)
+	{
+		require(false, stub_error);
+		to;
+		tokens;
+		dummy = 0;
+		return false;
+	}
+}
+
+contract UniqueNFT is
+	Dummy,
+	ERC165,
+	ERC721,
+	ERC721Metadata,
+	ERC721Enumerable,
+	ERC721UniqueExtensions,
+	ERC721Mintable,
+	ERC721Burnable
+{}
addedpallets/nft/src/eth/stubs/UniqueRefungible.rawdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft/src/eth/stubs/UniqueRefungible.raw
@@ -0,0 +1 @@
+TODO
\ No newline at end of file
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9	clippy::too_many_arguments,10	clippy::unnecessary_mut_passed,11	clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19	construct_runtime, decl_event, decl_module, decl_storage, decl_error,20	dispatch::DispatchResult,21	ensure, fail, parameter_types,22	traits::{23		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24		Randomness, IsSubType, WithdrawReasons,25	},26	weights::{27		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29		WeightToFeePolynomial, DispatchClass,30	},31	StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,42	CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,43	FungibleItemType, ReFungibleItemType,44};4546#[cfg(test)]47mod mock;4849#[cfg(test)]50mod tests;5152mod default_weights;53mod eth;54mod sponsorship;55pub use sponsorship::NftSponsorshipHandler;56pub use eth::sponsoring::NftEthSponsorshipHandler;5758pub use eth::NftErcSupport;59pub use eth::account::*;60use eth::erc::{ERC20Events, ERC721Events};6162#[cfg(feature = "runtime-benchmarks")]63mod benchmarking;6465pub trait WeightInfo {66	fn create_collection() -> Weight;67	fn destroy_collection() -> Weight;68	fn add_to_white_list() -> Weight;69	fn remove_from_white_list() -> Weight;70	fn set_public_access_mode() -> Weight;71	fn set_mint_permission() -> Weight;72	fn change_collection_owner() -> Weight;73	fn add_collection_admin() -> Weight;74	fn remove_collection_admin() -> Weight;75	fn set_collection_sponsor() -> Weight;76	fn confirm_sponsorship() -> Weight;77	fn remove_collection_sponsor() -> Weight;78	fn create_item(s: usize) -> Weight;79	fn burn_item() -> Weight;80	fn transfer() -> Weight;81	fn approve() -> Weight;82	fn transfer_from() -> Weight;83	fn set_offchain_schema() -> Weight;84	fn set_const_on_chain_schema() -> Weight;85	fn set_variable_on_chain_schema() -> Weight;86	fn set_variable_meta_data() -> Weight;87	fn enable_contract_sponsoring() -> Weight;88	fn set_schema_version() -> Weight;89	fn set_chain_limits() -> Weight;90	fn set_contract_sponsoring_rate_limit() -> Weight;91	fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;92	fn toggle_contract_white_list() -> Weight;93	fn add_to_contract_white_list() -> Weight;94	fn remove_from_contract_white_list() -> Weight;95	fn set_collection_limits() -> Weight;96}9798decl_error! {99	/// Error for non-fungible-token module.100	pub enum Error for Module<T: Config> {101		/// Total collections bound exceeded.102		TotalCollectionsLimitExceeded,103		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.104		CollectionDecimalPointLimitExceeded,105		/// Collection name can not be longer than 63 char.106		CollectionNameLimitExceeded,107		/// Collection description can not be longer than 255 char.108		CollectionDescriptionLimitExceeded,109		/// Token prefix can not be longer than 15 char.110		CollectionTokenPrefixLimitExceeded,111		/// This collection does not exist.112		CollectionNotFound,113		/// Item not exists.114		TokenNotFound,115		/// Admin not found116		AdminNotFound,117		/// Arithmetic calculation overflow.118		NumOverflow,119		/// Account already has admin role.120		AlreadyAdmin,121		/// You do not own this collection.122		NoPermission,123		/// This address is not set as sponsor, use setCollectionSponsor first.124		ConfirmUnsetSponsorFail,125		/// Collection is not in mint mode.126		PublicMintingNotAllowed,127		/// Sender parameter and item owner must be equal.128		MustBeTokenOwner,129		/// Item balance not enough.130		TokenValueTooLow,131		/// Size of item is too large.132		NftSizeLimitExceeded,133		/// No approve found134		ApproveNotFound,135		/// Requested value more than approved.136		TokenValueNotEnough,137		/// Only approved addresses can call this method.138		ApproveRequired,139		/// Address is not in white list.140		AddresNotInWhiteList,141		/// Number of collection admins bound exceeded.142		CollectionAdminsLimitExceeded,143		/// Owned tokens by a single address bound exceeded.144		AddressOwnershipLimitExceeded,145		/// Length of items properties must be greater than 0.146		EmptyArgument,147		/// const_data exceeded data limit.148		TokenConstDataLimitExceeded,149		/// variable_data exceeded data limit.150		TokenVariableDataLimitExceeded,151		/// Not NFT item data used to mint in NFT collection.152		NotNftDataUsedToMintNftCollectionToken,153		/// Not Fungible item data used to mint in Fungible collection.154		NotFungibleDataUsedToMintFungibleCollectionToken,155		/// Not Re Fungible item data used to mint in Re Fungible collection.156		NotReFungibleDataUsedToMintReFungibleCollectionToken,157		/// Unexpected collection type.158		UnexpectedCollectionType,159		/// Can't store metadata in fungible tokens.160		CantStoreMetadataInFungibleTokens,161		/// Collection token limit exceeded162		CollectionTokenLimitExceeded,163		/// Account token limit exceeded per collection164		AccountTokenLimitExceeded,165		/// Collection limit bounds per collection exceeded166		CollectionLimitBoundsExceeded,167		/// Tried to enable permissions which are only permitted to be disabled168		OwnerPermissionsCantBeReverted,169		/// Schema data size limit bound exceeded170		SchemaDataLimitExceeded,171		/// Maximum refungibility exceeded172		WrongRefungiblePieces,173		/// createRefungible should be called with one owner174		BadCreateRefungibleCall,175		/// Gas limit exceeded176		OutOfGas,177		/// Collection settings not allowing items transferring178		TransferNotAllowed,179	}180}181182#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]183pub struct CollectionHandle<T: Config> {184	pub id: CollectionId,185	collection: Collection<T>,186	recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,187}188impl<T: Config> CollectionHandle<T> {189	pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {190		<CollectionById<T>>::get(id).map(|collection| Self {191			id,192			collection,193			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(194				eth::collection_id_to_address(id),195				gas_limit,196			),197		})198	}199	pub fn get(id: CollectionId) -> Option<Self> {200		Self::get_with_gas_limit(id, u64::MAX)201	}202	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {203		self.recorder.log_sub(log)204	}205	fn consume_gas(&self, gas: u64) -> DispatchResult {206		self.recorder.consume_gas_sub(gas)207	}208	pub fn submit_logs(self) -> DispatchResult {209		self.recorder.submit_logs()210	}211	pub fn save(self) -> DispatchResult {212		self.recorder.submit_logs()?;213		<CollectionById<T>>::insert(self.id, self.collection);214		Ok(())215	}216}217impl<T: Config> Deref for CollectionHandle<T> {218	type Target = Collection<T>;219220	fn deref(&self) -> &Self::Target {221		&self.collection222	}223}224225impl<T: Config> DerefMut for CollectionHandle<T> {226	fn deref_mut(&mut self) -> &mut Self::Target {227		&mut self.collection228	}229}230231pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {232	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;233234	/// Weight information for extrinsics in this pallet.235	type WeightInfo: WeightInfo;236237	type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;238	type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;239240	type CrossAccountId: CrossAccountId<Self::AccountId>;241	type Currency: Currency<Self::AccountId>;242	type CollectionCreationPrice: Get<243		<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,244	>;245	type TreasuryAccountId: Get<Self::AccountId>;246}247248// # Used definitions249//250// ## User control levels251//252// chain-controlled - key is uncontrolled by user253//                    i.e autoincrementing index254//                    can use non-cryptographic hash255// real - key is controlled by user256//        but it is hard to generate enough colliding values, i.e owner of signed txs257//        can use non-cryptographic hash258// controlled - key is completly controlled by users259//              i.e maps with mutable keys260//              should use cryptographic hash261//262// ## User control level downgrade reasons263//264// ?1 - chain-controlled -> controlled265//      collections/tokens can be destroyed, resulting in massive holes266// ?2 - chain-controlled -> controlled267//      same as ?1, but can be only added, resulting in easier exploitation268// ?3 - real -> controlled269//      no confirmation required, so addresses can be easily generated270decl_storage! {271	trait Store for Module<T: Config> as Nft {272273		//#region Private members274		/// Id of next collection275		CreatedCollectionCount: u32;276		/// Used for migrations277		ChainVersion: u64;278		/// Id of last collection token279		/// Collection id (controlled?1)280		ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;281		//#endregion282283		//#region Chain limits struct284		pub ChainLimit get(fn chain_limit) config(): ChainLimits;285		//#endregion286287		//#region Bound counters288		/// Amount of collections destroyed, used for total amount tracking with289		/// CreatedCollectionCount290		DestroyedCollectionCount: u32;291		/// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)292		/// Account id (real)293		pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;294		//#endregion295296		//#region Basic collections297		/// Collection info298		/// Collection id (controlled?1)299		pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;300		/// List of collection admins301		/// Collection id (controlled?2)302		pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;303		/// Whitelisted collection users304		/// Collection id (controlled?2), user id (controlled?3)305		pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;306		//#endregion307308		/// How many of collection items user have309		/// Collection id (controlled?2), account id (real)310		pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;311312		/// Amount of items which spender can transfer out of owners account (via transferFrom)313		/// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))314		/// TODO: Off chain worker should remove from this map when token gets removed315		pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;316317		//#region Item collections318		/// Collection id (controlled?2), token id (controlled?1)319		pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;320		/// Collection id (controlled?2), owner (controlled?2)321		pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;322		/// Collection id (controlled?2), token id (controlled?1)323		pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;324		//#endregion325326		//#region Index list327		/// Collection id (controlled?2), tokens owner (controlled?2)328		pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;329		//#endregion330331		//#region Tokens transfer rate limit baskets332		/// (Collection id (controlled?2), who created (real))333		/// TODO: Off chain worker should remove from this map when collection gets removed334		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;335		/// Collection id (controlled?2), token id (controlled?2)336		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;337		/// Collection id (controlled?2), owning user (real)338		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;339		/// Collection id (controlled?2), token id (controlled?2)340		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;341		//#endregion342343		/// Variable metadata sponsoring344		/// Collection id (controlled?2), token id (controlled?2)345		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;346	}347	add_extra_genesis {348		build(|config: &GenesisConfig<T>| {349			// Modification of storage350			for (_num, _c) in &config.collection_id {351				<Module<T>>::init_collection(_c);352			}353354			for (_num, _c, _i) in &config.nft_item_id {355				<Module<T>>::init_nft_token(*_c, _i);356			}357358			for (collection_id, account_id, fungible_item) in &config.fungible_item_id {359				<Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);360			}361362			for (_num, _c, _i) in &config.refungible_item_id {363				<Module<T>>::init_refungible_token(*_c, _i);364			}365		})366	}367}368369decl_event!(370	pub enum Event<T>371	where372		AccountId = <T as frame_system::Config>::AccountId,373		CrossAccountId = <T as Config>::CrossAccountId,374	{375		/// New collection was created376		///377		/// # Arguments378		///379		/// * collection_id: Globally unique identifier of newly created collection.380		///381		/// * mode: [CollectionMode] converted into u8.382		///383		/// * account_id: Collection owner.384		CollectionCreated(CollectionId, u8, AccountId),385386		/// New item was created.387		///388		/// # Arguments389		///390		/// * collection_id: Id of the collection where item was created.391		///392		/// * item_id: Id of an item. Unique within the collection.393		///394		/// * recipient: Owner of newly created item395		ItemCreated(CollectionId, TokenId, CrossAccountId),396397		/// Collection item was burned.398		///399		/// # Arguments400		///401		/// collection_id.402		///403		/// item_id: Identifier of burned NFT.404		ItemDestroyed(CollectionId, TokenId),405406		/// Item was transferred407		///408		/// * collection_id: Id of collection to which item is belong409		///410		/// * item_id: Id of an item411		///412		/// * sender: Original owner of item413		///414		/// * recipient: New owner of item415		///416		/// * amount: Always 1 for NFT417		Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),418419		/// * collection_id420		///421		/// * item_id422		///423		/// * sender424		///425		/// * spender426		///427		/// * amount428		Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),429	}430);431432decl_module! {433	pub struct Module<T: Config> for enum Call434	where435		origin: T::Origin436	{437		fn deposit_event() = default;438		type Error = Error<T>;439440		fn on_initialize(_now: T::BlockNumber) -> Weight {441			0442		}443444		/// 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.445		///446		/// # Permissions447		///448		/// * Anyone.449		///450		/// # Arguments451		///452		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.453		///454		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.455		///456		/// * token_prefix: UTF-8 string with token prefix.457		///458		/// * mode: [CollectionMode] collection type and type dependent data.459		// returns collection ID460		#[weight = <T as Config>::WeightInfo::create_collection()]461		#[transactional]462		pub fn create_collection(origin,463								 collection_name: Vec<u16>,464								 collection_description: Vec<u16>,465								 token_prefix: Vec<u8>,466								 mode: CollectionMode) -> DispatchResult {467468			// Anyone can create a collection469			let who = ensure_signed(origin)?;470471			// Take a (non-refundable) deposit of collection creation472			let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();473			imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(474				&T::TreasuryAccountId::get(),475				T::CollectionCreationPrice::get(),476			));477			<T as Config>::Currency::settle(478				&who,479				imbalance,480				WithdrawReasons::TRANSFER,481				ExistenceRequirement::KeepAlive,482			).map_err(|_| Error::<T>::NoPermission)?;483484			let decimal_points = match mode {485				CollectionMode::Fungible(points) => points,486				_ => 0487			};488489			let chain_limit = ChainLimit::get();490491			let created_count = CreatedCollectionCount::get();492			let destroyed_count = DestroyedCollectionCount::get();493494			// bound Total number of collections495			ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);496497			// check params498			ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);499			ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);500			ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);501			ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);502503			// Generate next collection ID504			let next_id = created_count505				.checked_add(1)506				.ok_or(Error::<T>::NumOverflow)?;507508			CreatedCollectionCount::put(next_id);509510			let limits = CollectionLimits {511				sponsored_data_size: chain_limit.custom_data_limit,512				..Default::default()513			};514515			// Create new collection516			let new_collection = Collection {517				owner: who.clone(),518				name: collection_name,519				mode: mode.clone(),520				mint_mode: false,521				access: AccessMode::Normal,522				description: collection_description,523				decimal_points,524				token_prefix,525				offchain_schema: Vec::new(),526				schema_version: SchemaVersion::ImageURL,527				sponsorship: SponsorshipState::Disabled,528				variable_on_chain_schema: Vec::new(),529				const_on_chain_schema: Vec::new(),530				limits,531				transfers_enabled: true,532			};533534			// Add new collection to map535			<CollectionById<T>>::insert(next_id, new_collection);536537			// call event538			Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));539540			Ok(())541		}542543		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.544		///545		/// # Permissions546		///547		/// * Collection Owner.548		///549		/// # Arguments550		///551		/// * collection_id: collection to destroy.552		#[weight = <T as Config>::WeightInfo::destroy_collection()]553		#[transactional]554		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {555556			let sender = ensure_signed(origin)?;557			let collection = Self::get_collection(collection_id)?;558			Self::check_owner_permissions(&collection, &sender)?;559			if !collection.limits.owner_can_destroy {560				fail!(Error::<T>::NoPermission);561			}562563			<AddressTokens<T>>::remove_prefix(collection_id, None);564			<Allowances<T>>::remove_prefix(collection_id, None);565			<Balance<T>>::remove_prefix(collection_id, None);566			<ItemListIndex>::remove(collection_id);567			<AdminList<T>>::remove(collection_id);568			<CollectionById<T>>::remove(collection_id);569			<WhiteList<T>>::remove_prefix(collection_id, None);570571			<NftItemList<T>>::remove_prefix(collection_id, None);572			<FungibleItemList<T>>::remove_prefix(collection_id, None);573			<ReFungibleItemList<T>>::remove_prefix(collection_id, None);574575			<NftTransferBasket<T>>::remove_prefix(collection_id, None);576			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);577			<ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);578579			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);580581			DestroyedCollectionCount::put(DestroyedCollectionCount::get()582				.checked_add(1)583				.ok_or(Error::<T>::NumOverflow)?);584585			Ok(())586		}587588		/// Add an address to white list.589		///590		/// # Permissions591		///592		/// * Collection Owner593		/// * Collection Admin594		///595		/// # Arguments596		///597		/// * collection_id.598		///599		/// * address.600		#[weight = <T as Config>::WeightInfo::add_to_white_list()]601		#[transactional]602		pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{603604			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);605			let collection = Self::get_collection(collection_id)?;606607			Self::toggle_white_list_internal(608				&sender,609				&collection,610				&address,611				true,612			)?;613614			Ok(())615		}616617		/// Remove an address from white list.618		///619		/// # Permissions620		///621		/// * Collection Owner622		/// * Collection Admin623		///624		/// # Arguments625		///626		/// * collection_id.627		///628		/// * address.629		#[weight = <T as Config>::WeightInfo::remove_from_white_list()]630		#[transactional]631		pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{632633			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);634			let collection = Self::get_collection(collection_id)?;635636			Self::toggle_white_list_internal(637				&sender,638				&collection,639				&address,640				false,641			)?;642643			Ok(())644		}645646		/// Toggle between normal and white list access for the methods with access for `Anyone`.647		///648		/// # Permissions649		///650		/// * Collection Owner.651		///652		/// # Arguments653		///654		/// * collection_id.655		///656		/// * mode: [AccessMode]657		#[weight = <T as Config>::WeightInfo::set_public_access_mode()]658		#[transactional]659		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult660		{661			let sender = ensure_signed(origin)?;662663			let mut target_collection = Self::get_collection(collection_id)?;664			Self::check_owner_permissions(&target_collection, &sender)?;665			target_collection.access = mode;666			target_collection.save()667		}668669		/// Allows Anyone to create tokens if:670		/// * White List is enabled, and671		/// * Address is added to white list, and672		/// * This method was called with True parameter673		///674		/// # Permissions675		/// * Collection Owner676		///677		/// # Arguments678		///679		/// * collection_id.680		///681		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.682		#[weight = <T as Config>::WeightInfo::set_mint_permission()]683		#[transactional]684		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult685		{686			let sender = ensure_signed(origin)?;687688			let mut target_collection = Self::get_collection(collection_id)?;689			Self::check_owner_permissions(&target_collection, &sender)?;690			target_collection.mint_mode = mint_permission;691			target_collection.save()692		}693694		/// Change the owner of the collection.695		///696		/// # Permissions697		///698		/// * Collection Owner.699		///700		/// # Arguments701		///702		/// * collection_id.703		///704		/// * new_owner.705		#[weight = <T as Config>::WeightInfo::change_collection_owner()]706		#[transactional]707		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {708709			let sender = ensure_signed(origin)?;710			let mut target_collection = Self::get_collection(collection_id)?;711			Self::check_owner_permissions(&target_collection, &sender)?;712			target_collection.owner = new_owner;713			target_collection.save()714		}715716		/// Adds an admin of the Collection.717		/// 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.718		///719		/// # Permissions720		///721		/// * Collection Owner.722		/// * Collection Admin.723		///724		/// # Arguments725		///726		/// * collection_id: ID of the Collection to add admin for.727		///728		/// * new_admin_id: Address of new admin to add.729		#[weight = <T as Config>::WeightInfo::add_collection_admin()]730		#[transactional]731		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {732			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);733			let collection = Self::get_collection(collection_id)?;734			Self::check_owner_or_admin_permissions(&collection, &sender)?;735			let mut admin_arr = <AdminList<T>>::get(collection_id);736737			match admin_arr.binary_search(&new_admin_id) {738				Ok(_) => {},739				Err(idx) => {740					let limits = ChainLimit::get();741					ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);742					admin_arr.insert(idx, new_admin_id);743					<AdminList<T>>::insert(collection_id, admin_arr);744				}745			}746			Ok(())747		}748749		/// 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.750		///751		/// # Permissions752		///753		/// * Collection Owner.754		/// * Collection Admin.755		///756		/// # Arguments757		///758		/// * collection_id: ID of the Collection to remove admin for.759		///760		/// * account_id: Address of admin to remove.761		#[weight = <T as Config>::WeightInfo::remove_collection_admin()]762		#[transactional]763		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {764			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);765			let collection = Self::get_collection(collection_id)?;766			Self::check_owner_or_admin_permissions(&collection, &sender)?;767			let mut admin_arr = <AdminList<T>>::get(collection_id);768769			if let Ok(idx) = admin_arr.binary_search(&account_id) {770				admin_arr.remove(idx);771				<AdminList<T>>::insert(collection_id, admin_arr);772			}773			Ok(())774		}775776		/// # Permissions777		///778		/// * Collection Owner779		///780		/// # Arguments781		///782		/// * collection_id.783		///784		/// * new_sponsor.785		#[weight = <T as Config>::WeightInfo::set_collection_sponsor()]786		#[transactional]787		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {788			let sender = ensure_signed(origin)?;789			let mut target_collection = Self::get_collection(collection_id)?;790			Self::check_owner_permissions(&target_collection, &sender)?;791792			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);793			target_collection.save()794		}795796		/// # Permissions797		///798		/// * Sponsor.799		///800		/// # Arguments801		///802		/// * collection_id.803		#[weight = <T as Config>::WeightInfo::confirm_sponsorship()]804		#[transactional]805		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {806			let sender = ensure_signed(origin)?;807808			let mut target_collection = Self::get_collection(collection_id)?;809			ensure!(810				target_collection.sponsorship.pending_sponsor() == Some(&sender),811				Error::<T>::ConfirmUnsetSponsorFail812			);813814			target_collection.sponsorship = SponsorshipState::Confirmed(sender);815			target_collection.save()816		}817818		/// Switch back to pay-per-own-transaction model.819		///820		/// # Permissions821		///822		/// * Collection owner.823		///824		/// # Arguments825		///826		/// * collection_id.827		#[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]828		#[transactional]829		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {830			let sender = ensure_signed(origin)?;831832			let mut target_collection = Self::get_collection(collection_id)?;833			Self::check_owner_permissions(&target_collection, &sender)?;834835			target_collection.sponsorship = SponsorshipState::Disabled;836			target_collection.save()837		}838839		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.840		///841		/// # Permissions842		///843		/// * Collection Owner.844		/// * Collection Admin.845		/// * Anyone if846		///     * White List is enabled, and847		///     * Address is added to white list, and848		///     * MintPermission is enabled (see SetMintPermission method)849		///850		/// # Arguments851		///852		/// * collection_id: ID of the collection.853		///854		/// * owner: Address, initial owner of the NFT.855		///856		/// * data: Token data to store on chain.857		// #[weight =858		// (130_000_000 as Weight)859		// .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))860		// .saturating_add(RocksDbWeight::get().reads(10 as Weight))861		// .saturating_add(RocksDbWeight::get().writes(8 as Weight))]862863		#[weight = <T as Config>::WeightInfo::create_item(data.data_size())]864		#[transactional]865		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {866			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);867			let collection = Self::get_collection(collection_id)?;868869			Self::create_item_internal(&sender, &collection, &owner, data)?;870871			collection.submit_logs()872		}873874		/// This method creates multiple items in a collection created with CreateCollection method.875		///876		/// # Permissions877		///878		/// * Collection Owner.879		/// * Collection Admin.880		/// * Anyone if881		///     * White List is enabled, and882		///     * Address is added to white list, and883		///     * MintPermission is enabled (see SetMintPermission method)884		///885		/// # Arguments886		///887		/// * collection_id: ID of the collection.888		///889		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].890		///891		/// * owner: Address, initial owner of the NFT.892		#[weight = <T as Config>::WeightInfo::create_item(items_data.iter()893							   .map(|data| { data.data_size() })894							   .sum())]895		#[transactional]896		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {897898			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);899			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);900			let collection = Self::get_collection(collection_id)?;901902			Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;903904			collection.submit_logs()905		}906907		// TODO! transaction weight908909		/// Set transfers_enabled value for particular collection910		///911		/// # Permissions912		///913		/// * Collection Owner.914		///915		/// # Arguments916		///917		/// * collection_id: ID of the collection.918		///919		/// * value: New flag value.920		#[weight = <T as Config>::WeightInfo::burn_item()]921		#[transactional]922		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {923924			let sender = ensure_signed(origin)?;925			let mut target_collection = Self::get_collection(collection_id)?;926927			Self::check_owner_permissions(&target_collection, &sender)?;928929			target_collection.transfers_enabled = value;930			target_collection.save()931		}932933		/// Destroys a concrete instance of NFT.934		///935		/// # Permissions936		///937		/// * Collection Owner.938		/// * Collection Admin.939		/// * Current NFT Owner.940		///941		/// # Arguments942		///943		/// * collection_id: ID of the collection.944		///945		/// * item_id: ID of NFT to burn.946		#[weight = <T as Config>::WeightInfo::burn_item()]947		#[transactional]948		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {949950			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);951			let target_collection = Self::get_collection(collection_id)?;952953			Self::burn_item_internal(&sender, &target_collection, item_id, value)?;954955			target_collection.submit_logs()956		}957958		/// Change ownership of the token.959		///960		/// # Permissions961		///962		/// * Collection Owner963		/// * Collection Admin964		/// * Current NFT owner965		///966		/// # Arguments967		///968		/// * recipient: Address of token recipient.969		///970		/// * collection_id.971		///972		/// * item_id: ID of the item973		///     * Non-Fungible Mode: Required.974		///     * Fungible Mode: Ignored.975		///     * Re-Fungible Mode: Required.976		///977		/// * value: Amount to transfer.978		///     * Non-Fungible Mode: Ignored979		///     * Fungible Mode: Must specify transferred amount980		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)981		#[weight = <T as Config>::WeightInfo::transfer()]982		#[transactional]983		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {984			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);985			let collection = Self::get_collection(collection_id)?;986987			Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;988989			collection.submit_logs()990		}991992		/// Set, change, or remove approved address to transfer the ownership of the NFT.993		///994		/// # Permissions995		///996		/// * Collection Owner997		/// * Collection Admin998		/// * Current NFT owner999		///1000		/// # Arguments1001		///1002		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1003		///1004		/// * collection_id.1005		///1006		/// * item_id: ID of the item.1007		#[weight = <T as Config>::WeightInfo::approve()]1008		#[transactional]1009		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1010			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1011			let collection = Self::get_collection(collection_id)?;10121013			Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10141015			collection.submit_logs()1016		}10171018		/// 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.1019		///1020		/// # Permissions1021		/// * Collection Owner1022		/// * Collection Admin1023		/// * Current NFT owner1024		/// * Address approved by current NFT owner1025		///1026		/// # Arguments1027		///1028		/// * from: Address that owns token.1029		///1030		/// * recipient: Address of token recipient.1031		///1032		/// * collection_id.1033		///1034		/// * item_id: ID of the item.1035		///1036		/// * value: Amount to transfer.1037		#[weight = <T as Config>::WeightInfo::transfer_from()]1038		#[transactional]1039		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1040			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1041			let collection = Self::get_collection(collection_id)?;10421043			Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10441045			collection.submit_logs()1046		}1047		// #[weight = 0]1048		//     // let no_perm_mes = "You do not have permissions to modify this collection";1049		//     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1050		//     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1051		//     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10521053		//     // // on_nft_received  call10541055		//     // Self::transfer(origin, collection_id, item_id, new_owner)?;10561057		//     Ok(())1058		// }10591060		/// Set off-chain data schema.1061		///1062		/// # Permissions1063		///1064		/// * Collection Owner1065		/// * Collection Admin1066		///1067		/// # Arguments1068		///1069		/// * collection_id.1070		///1071		/// * schema: String representing the offchain data schema.1072		#[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1073		#[transactional]1074		pub fn set_variable_meta_data (1075			origin,1076			collection_id: CollectionId,1077			item_id: TokenId,1078			data: Vec<u8>1079		) -> DispatchResult {1080			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10811082			let collection = Self::get_collection(collection_id)?;10831084			Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10851086			Ok(())1087		}10881089		/// Set schema standard1090		/// ImageURL1091		/// Unique1092		///1093		/// # Permissions1094		///1095		/// * Collection Owner1096		/// * Collection Admin1097		///1098		/// # Arguments1099		///1100		/// * collection_id.1101		///1102		/// * schema: SchemaVersion: enum1103		#[weight = <T as Config>::WeightInfo::set_schema_version()]1104		#[transactional]1105		pub fn set_schema_version(1106			origin,1107			collection_id: CollectionId,1108			version: SchemaVersion1109		) -> DispatchResult {1110			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1111			let mut target_collection = Self::get_collection(collection_id)?;1112			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1113			target_collection.schema_version = version;1114			target_collection.save()1115		}11161117		/// Set off-chain data schema.1118		///1119		/// # Permissions1120		///1121		/// * Collection Owner1122		/// * Collection Admin1123		///1124		/// # Arguments1125		///1126		/// * collection_id.1127		///1128		/// * schema: String representing the offchain data schema.1129		#[weight = <T as Config>::WeightInfo::set_offchain_schema()]1130		#[transactional]1131		pub fn set_offchain_schema(1132			origin,1133			collection_id: CollectionId,1134			schema: Vec<u8>1135		) -> DispatchResult {1136			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1137			let mut target_collection = Self::get_collection(collection_id)?;1138			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11391140			// check schema limit1141			ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11421143			target_collection.offchain_schema = schema;1144			target_collection.save()1145		}11461147		/// Set const on-chain data schema.1148		///1149		/// # Permissions1150		///1151		/// * Collection Owner1152		/// * Collection Admin1153		///1154		/// # Arguments1155		///1156		/// * collection_id.1157		///1158		/// * schema: String representing the const on-chain data schema.1159		#[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1160		#[transactional]1161		pub fn set_const_on_chain_schema (1162			origin,1163			collection_id: CollectionId,1164			schema: Vec<u8>1165		) -> DispatchResult {1166			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1167			let mut target_collection = Self::get_collection(collection_id)?;1168			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11691170			// check schema limit1171			ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11721173			target_collection.const_on_chain_schema = schema;1174			target_collection.save()1175		}11761177		/// Set variable on-chain data schema.1178		///1179		/// # Permissions1180		///1181		/// * Collection Owner1182		/// * Collection Admin1183		///1184		/// # Arguments1185		///1186		/// * collection_id.1187		///1188		/// * schema: String representing the variable on-chain data schema.1189		#[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1190		#[transactional]1191		pub fn set_variable_on_chain_schema (1192			origin,1193			collection_id: CollectionId,1194			schema: Vec<u8>1195		) -> DispatchResult {1196			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1197			let mut target_collection = Self::get_collection(collection_id)?;1198			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11991200			// check schema limit1201			ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12021203			target_collection.variable_on_chain_schema = schema;1204			target_collection.save()1205		}12061207		// Sudo permissions function1208		#[weight = <T as Config>::WeightInfo::set_chain_limits()]1209		#[transactional]1210		pub fn set_chain_limits(1211			origin,1212			limits: ChainLimits1213		) -> DispatchResult {12141215			#[cfg(not(feature = "runtime-benchmarks"))]1216			ensure_root(origin)?;12171218			<ChainLimit>::put(limits);1219			Ok(())1220		}12211222		#[weight = <T as Config>::WeightInfo::set_collection_limits()]1223		#[transactional]1224		pub fn set_collection_limits(1225			origin,1226			collection_id: u32,1227			new_limits: CollectionLimits<T::BlockNumber>,1228		) -> DispatchResult {1229			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1230			let mut target_collection = Self::get_collection(collection_id)?;1231			Self::check_owner_permissions(&target_collection, sender.as_sub())?;1232			let old_limits = &target_collection.limits;1233			let chain_limits = ChainLimit::get();12341235			// collection bounds1236			ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1237				new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1238				new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1239				Error::<T>::CollectionLimitBoundsExceeded);12401241			// token_limit   check  prev1242			ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1243			ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12441245			ensure!(1246				(old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1247				(old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1248				Error::<T>::OwnerPermissionsCantBeReverted,1249			);12501251			target_collection.limits = new_limits;12521253			target_collection.save()1254		}1255	}1256}12571258impl<T: Config> Module<T> {1259	pub fn create_item_internal(1260		sender: &T::CrossAccountId,1261		collection: &CollectionHandle<T>,1262		owner: &T::CrossAccountId,1263		data: CreateItemData,1264	) -> DispatchResult {1265		Self::can_create_items_in_collection(collection, sender, owner, 1)?;1266		Self::validate_create_item_args(collection, &data)?;1267		Self::create_item_no_validation(collection, owner, data)?;12681269		Ok(())1270	}12711272	pub fn transfer_internal(1273		sender: &T::CrossAccountId,1274		recipient: &T::CrossAccountId,1275		target_collection: &CollectionHandle<T>,1276		item_id: TokenId,1277		value: u128,1278	) -> DispatchResult {1279		target_collection.consume_gas(2000000)?;1280		// Limits check1281		Self::is_correct_transfer(target_collection, recipient)?;12821283		// Transfer permissions check1284		ensure!(1285			Self::is_item_owner(sender, target_collection, item_id)1286				|| Self::is_owner_or_admin_permissions(target_collection, sender),1287			Error::<T>::NoPermission1288		);12891290		if target_collection.access == AccessMode::WhiteList {1291			Self::check_white_list(target_collection, sender)?;1292			Self::check_white_list(target_collection, recipient)?;1293		}12941295		match target_collection.mode {1296			CollectionMode::NFT => Self::transfer_nft(1297				target_collection,1298				item_id,1299				sender.clone(),1300				recipient.clone(),1301			)?,1302			CollectionMode::Fungible(_) => {1303				Self::transfer_fungible(target_collection, value, sender, recipient)?1304			}1305			CollectionMode::ReFungible => Self::transfer_refungible(1306				target_collection,1307				item_id,1308				value,1309				sender.clone(),1310				recipient.clone(),1311			)?,1312			_ => (),1313		};13141315		Self::deposit_event(RawEvent::Transfer(1316			target_collection.id,1317			item_id,1318			sender.clone(),1319			recipient.clone(),1320			value,1321		));13221323		Ok(())1324	}13251326	pub fn approve_internal(1327		sender: &T::CrossAccountId,1328		spender: &T::CrossAccountId,1329		collection: &CollectionHandle<T>,1330		item_id: TokenId,1331		amount: u128,1332	) -> DispatchResult {1333		collection.consume_gas(2000000)?;1334		Self::token_exists(collection, item_id)?;13351336		// Transfer permissions check1337		let bypasses_limits = collection.limits.owner_can_transfer1338			&& Self::is_owner_or_admin_permissions(collection, sender);13391340		let allowance_limit = if bypasses_limits {1341			None1342		} else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1343			Some(amount)1344		} else {1345			fail!(Error::<T>::NoPermission);1346		};13471348		if collection.access == AccessMode::WhiteList {1349			Self::check_white_list(collection, sender)?;1350			Self::check_white_list(collection, spender)?;1351		}13521353		let allowance: u128 = amount1354			.checked_add(<Allowances<T>>::get(1355				collection.id,1356				(item_id, sender.as_sub(), spender.as_sub()),1357			))1358			.ok_or(Error::<T>::NumOverflow)?;1359		if let Some(limit) = allowance_limit {1360			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1361		}1362		<Allowances<T>>::insert(1363			collection.id,1364			(item_id, sender.as_sub(), spender.as_sub()),1365			allowance,1366		);13671368		if matches!(collection.mode, CollectionMode::NFT) {1369			// TODO: NFT: only one owner may exist for token in ERC7211370			collection.log(ERC721Events::Approval {1371				owner: *sender.as_eth(),1372				approved: *spender.as_eth(),1373				token_id: item_id.into(),1374			})?;1375		}13761377		if matches!(collection.mode, CollectionMode::Fungible(_)) {1378			// TODO: NFT: only one owner may exist for token in ERC201379			collection.log(ERC20Events::Approval {1380				owner: *sender.as_eth(),1381				spender: *spender.as_eth(),1382				value: allowance.into(),1383			})?;1384		}13851386		Self::deposit_event(RawEvent::Approved(1387			collection.id,1388			item_id,1389			sender.clone(),1390			spender.clone(),1391			allowance,1392		));1393		Ok(())1394	}13951396	pub fn transfer_from_internal(1397		sender: &T::CrossAccountId,1398		from: &T::CrossAccountId,1399		recipient: &T::CrossAccountId,1400		collection: &CollectionHandle<T>,1401		item_id: TokenId,1402		amount: u128,1403	) -> DispatchResult {1404		collection.consume_gas(2000000)?;1405		// Check approval1406		let approval: u128 =1407			<Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));14081409		// Limits check1410		Self::is_correct_transfer(collection, recipient)?;14111412		// Transfer permissions check1413		ensure!(1414			approval >= amount1415				|| (collection.limits.owner_can_transfer1416					&& Self::is_owner_or_admin_permissions(collection, sender)),1417			Error::<T>::NoPermission1418		);14191420		if collection.access == AccessMode::WhiteList {1421			Self::check_white_list(collection, sender)?;1422			Self::check_white_list(collection, recipient)?;1423		}14241425		// Reduce approval by transferred amount or remove if remaining approval drops to 01426		let allowance = approval.saturating_sub(amount);1427		if allowance > 0 {1428			<Allowances<T>>::insert(1429				collection.id,1430				(item_id, from.as_sub(), sender.as_sub()),1431				allowance,1432			);1433		} else {1434			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1435		}14361437		match collection.mode {1438			CollectionMode::NFT => {1439				Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1440			}1441			CollectionMode::Fungible(_) => {1442				Self::transfer_fungible(collection, amount, from, recipient)?1443			}1444			CollectionMode::ReFungible => Self::transfer_refungible(1445				collection,1446				item_id,1447				amount,1448				from.clone(),1449				recipient.clone(),1450			)?,1451			_ => (),1452		};14531454		if matches!(collection.mode, CollectionMode::Fungible(_)) {1455			collection.log(ERC20Events::Approval {1456				owner: *from.as_eth(),1457				spender: *sender.as_eth(),1458				value: allowance.into(),1459			})?;1460		}14611462		Ok(())1463	}14641465	pub fn set_variable_meta_data_internal(1466		sender: &T::CrossAccountId,1467		collection: &CollectionHandle<T>,1468		item_id: TokenId,1469		data: Vec<u8>,1470	) -> DispatchResult {1471		Self::token_exists(collection, item_id)?;14721473		ensure!(1474			ChainLimit::get().custom_data_limit >= data.len() as u32,1475			Error::<T>::TokenVariableDataLimitExceeded1476		);14771478		// Modify permissions check1479		ensure!(1480			Self::is_item_owner(sender, collection, item_id)1481				|| Self::is_owner_or_admin_permissions(collection, sender),1482			Error::<T>::NoPermission1483		);14841485		match collection.mode {1486			CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1487			CollectionMode::ReFungible => {1488				Self::set_re_fungible_variable_data(collection, item_id, data)?1489			}1490			CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1491			_ => fail!(Error::<T>::UnexpectedCollectionType),1492		};14931494		Ok(())1495	}14961497	pub fn create_multiple_items_internal(1498		sender: &T::CrossAccountId,1499		collection: &CollectionHandle<T>,1500		owner: &T::CrossAccountId,1501		items_data: Vec<CreateItemData>,1502	) -> DispatchResult {1503		Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;15041505		for data in &items_data {1506			Self::validate_create_item_args(collection, data)?;1507		}1508		for data in &items_data {1509			Self::create_item_no_validation(collection, owner, data.clone())?;1510		}15111512		Ok(())1513	}15141515	pub fn burn_item_internal(1516		sender: &T::CrossAccountId,1517		collection: &CollectionHandle<T>,1518		item_id: TokenId,1519		value: u128,1520	) -> DispatchResult {1521		ensure!(1522			Self::is_item_owner(sender, collection, item_id)1523				|| (collection.limits.owner_can_transfer1524					&& Self::is_owner_or_admin_permissions(collection, sender)),1525			Error::<T>::NoPermission1526		);15271528		if collection.access == AccessMode::WhiteList {1529			Self::check_white_list(collection, sender)?;1530		}15311532		match collection.mode {1533			CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1534			CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1535			CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1536			_ => (),1537		};15381539		Ok(())1540	}15411542	pub fn toggle_white_list_internal(1543		sender: &T::CrossAccountId,1544		collection: &CollectionHandle<T>,1545		address: &T::CrossAccountId,1546		whitelisted: bool,1547	) -> DispatchResult {1548		Self::check_owner_or_admin_permissions(collection, sender)?;15491550		if whitelisted {1551			<WhiteList<T>>::insert(collection.id, address.as_sub(), true);1552		} else {1553			<WhiteList<T>>::remove(collection.id, address.as_sub());1554		}15551556		Ok(())1557	}15581559	fn is_correct_transfer(1560		collection: &CollectionHandle<T>,1561		recipient: &T::CrossAccountId,1562	) -> DispatchResult {1563		let collection_id = collection.id;15641565		// check token limit and account token limit1566		let account_items: u32 =1567			<AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1568		ensure!(1569			collection.limits.account_token_ownership_limit > account_items,1570			Error::<T>::AccountTokenLimitExceeded1571		);15721573		// preliminary transfer check1574		ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15751576		Ok(())1577	}15781579	fn can_create_items_in_collection(1580		collection: &CollectionHandle<T>,1581		sender: &T::CrossAccountId,1582		owner: &T::CrossAccountId,1583		amount: u32,1584	) -> DispatchResult {1585		let collection_id = collection.id;15861587		// check token limit and account token limit1588		let total_items: u32 = ItemListIndex::get(collection_id)1589			.checked_add(amount)1590			.ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1591		let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1592			as u32)1593			.checked_add(amount)1594			.ok_or(Error::<T>::AccountTokenLimitExceeded)?;1595		ensure!(1596			collection.limits.token_limit >= total_items,1597			Error::<T>::CollectionTokenLimitExceeded1598		);1599		ensure!(1600			collection.limits.account_token_ownership_limit >= account_items,1601			Error::<T>::AccountTokenLimitExceeded1602		);16031604		if !Self::is_owner_or_admin_permissions(collection, sender) {1605			ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1606			Self::check_white_list(collection, owner)?;1607			Self::check_white_list(collection, sender)?;1608		}16091610		Ok(())1611	}16121613	fn validate_create_item_args(1614		target_collection: &CollectionHandle<T>,1615		data: &CreateItemData,1616	) -> DispatchResult {1617		match target_collection.mode {1618			CollectionMode::NFT => {1619				if let CreateItemData::NFT(data) = data {1620					// check sizes1621					ensure!(1622						ChainLimit::get().custom_data_limit >= data.const_data.len() as u32,1623						Error::<T>::TokenConstDataLimitExceeded1624					);1625					ensure!(1626						ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32,1627						Error::<T>::TokenVariableDataLimitExceeded1628					);1629				} else {1630					fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1631				}1632			}1633			CollectionMode::Fungible(_) => {1634				if let CreateItemData::Fungible(_) = data {1635				} else {1636					fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1637				}1638			}1639			CollectionMode::ReFungible => {1640				if let CreateItemData::ReFungible(data) = data {1641					// check sizes1642					ensure!(1643						ChainLimit::get().custom_data_limit >= data.const_data.len() as u32,1644						Error::<T>::TokenConstDataLimitExceeded1645					);1646					ensure!(1647						ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32,1648						Error::<T>::TokenVariableDataLimitExceeded1649					);16501651					// Check refungibility limits1652					ensure!(1653						data.pieces <= MAX_REFUNGIBLE_PIECES,1654						Error::<T>::WrongRefungiblePieces1655					);1656					ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1657				} else {1658					fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1659				}1660			}1661			_ => {1662				fail!(Error::<T>::UnexpectedCollectionType);1663			}1664		};16651666		Ok(())1667	}16681669	fn create_item_no_validation(1670		collection: &CollectionHandle<T>,1671		owner: &T::CrossAccountId,1672		data: CreateItemData,1673	) -> DispatchResult {1674		match data {1675			CreateItemData::NFT(data) => {1676				let item = NftItemType {1677					owner: owner.clone(),1678					const_data: data.const_data,1679					variable_data: data.variable_data,1680				};16811682				Self::add_nft_item(collection, item)?;1683			}1684			CreateItemData::Fungible(data) => {1685				Self::add_fungible_item(collection, owner, data.value)?;1686			}1687			CreateItemData::ReFungible(data) => {1688				let owner_list = vec![Ownership {1689					owner: owner.clone(),1690					fraction: data.pieces,1691				}];16921693				let item = ReFungibleItemType {1694					owner: owner_list,1695					const_data: data.const_data,1696					variable_data: data.variable_data,1697				};16981699				Self::add_refungible_item(collection, item)?;1700			}1701		};17021703		Ok(())1704	}17051706	fn add_fungible_item(1707		collection: &CollectionHandle<T>,1708		owner: &T::CrossAccountId,1709		value: u128,1710	) -> DispatchResult {1711		let collection_id = collection.id;17121713		// Does new owner already have an account?1714		let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17151716		// Mint1717		let item = FungibleItemType {1718			value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1719		};1720		<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17211722		// Update balance1723		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1724			.checked_add(value)1725			.ok_or(Error::<T>::NumOverflow)?;1726		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17271728		Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1729		Ok(())1730	}17311732	fn add_refungible_item(1733		collection: &CollectionHandle<T>,1734		item: ReFungibleItemType<T::CrossAccountId>,1735	) -> DispatchResult {1736		let collection_id = collection.id;17371738		let current_index = <ItemListIndex>::get(collection_id)1739			.checked_add(1)1740			.ok_or(Error::<T>::NumOverflow)?;1741		let itemcopy = item.clone();17421743		ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1744		let item_owner = item.owner.first().expect("only one owner is defined");17451746		let value = item_owner.fraction;1747		let owner = item_owner.owner.clone();17481749		Self::add_token_index(collection_id, current_index, &owner)?;17501751		<ItemListIndex>::insert(collection_id, current_index);1752		<ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17531754		// Update balance1755		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1756			.checked_add(value)1757			.ok_or(Error::<T>::NumOverflow)?;1758		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17591760		Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1761		Ok(())1762	}17631764	fn add_nft_item(1765		collection: &CollectionHandle<T>,1766		item: NftItemType<T::CrossAccountId>,1767	) -> DispatchResult {1768		let collection_id = collection.id;17691770		let current_index = <ItemListIndex>::get(collection_id)1771			.checked_add(1)1772			.ok_or(Error::<T>::NumOverflow)?;17731774		let item_owner = item.owner.clone();1775		Self::add_token_index(collection_id, current_index, &item.owner)?;17761777		<ItemListIndex>::insert(collection_id, current_index);1778		<NftItemList<T>>::insert(collection_id, current_index, item);17791780		// Update balance1781		let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1782			.checked_add(1)1783			.ok_or(Error::<T>::NumOverflow)?;1784		<Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17851786		collection.log(ERC721Events::Transfer {1787			from: H160::default(),1788			to: *item_owner.as_eth(),1789			token_id: current_index.into(),1790		})?;1791		Self::deposit_event(RawEvent::ItemCreated(1792			collection_id,1793			current_index,1794			item_owner,1795		));1796		Ok(())1797	}17981799	fn burn_refungible_item(1800		collection: &CollectionHandle<T>,1801		item_id: TokenId,1802		owner: &T::CrossAccountId,1803	) -> DispatchResult {1804		let collection_id = collection.id;18051806		let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1807			.ok_or(Error::<T>::TokenNotFound)?;1808		let rft_balance = token1809			.owner1810			.iter()1811			.find(|&i| i.owner == *owner)1812			.ok_or(Error::<T>::TokenNotFound)?;1813		Self::remove_token_index(collection_id, item_id, owner)?;18141815		// update balance1816		let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1817			.checked_sub(rft_balance.fraction)1818			.ok_or(Error::<T>::NumOverflow)?;1819		<Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18201821		// Re-create owners list with sender removed1822		let index = token1823			.owner1824			.iter()1825			.position(|i| i.owner == *owner)1826			.expect("owned item is exists");1827		token.owner.remove(index);1828		let owner_count = token.owner.len();18291830		// Burn the token completely if this was the last (only) owner1831		if owner_count == 0 {1832			<ReFungibleItemList<T>>::remove(collection_id, item_id);1833			<VariableMetaDataBasket<T>>::remove(collection_id, item_id);1834		} else {1835			<ReFungibleItemList<T>>::insert(collection_id, item_id, token);1836		}18371838		Ok(())1839	}18401841	fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1842		let collection_id = collection.id;18431844		let item =1845			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1846		Self::remove_token_index(collection_id, item_id, &item.owner)?;18471848		// update balance1849		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1850			.checked_sub(1)1851			.ok_or(Error::<T>::NumOverflow)?;1852		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1853		<NftItemList<T>>::remove(collection_id, item_id);1854		<VariableMetaDataBasket<T>>::remove(collection_id, item_id);18551856		Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1857		Ok(())1858	}18591860	fn burn_fungible_item(1861		owner: &T::CrossAccountId,1862		collection: &CollectionHandle<T>,1863		value: u128,1864	) -> DispatchResult {1865		let collection_id = collection.id;18661867		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1868		ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18691870		// update balance1871		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1872			.checked_sub(value)1873			.ok_or(Error::<T>::NumOverflow)?;1874		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18751876		if balance.value - value > 0 {1877			balance.value -= value;1878			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1879		} else {1880			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1881		}18821883		collection.log(ERC20Events::Transfer {1884			from: *owner.as_eth(),1885			to: H160::default(),1886			value: value.into(),1887		})?;1888		Ok(())1889	}18901891	pub fn get_collection(1892		collection_id: CollectionId,1893	) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1894		Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1895	}18961897	fn check_owner_permissions(1898		target_collection: &CollectionHandle<T>,1899		subject: &T::AccountId,1900	) -> DispatchResult {1901		ensure!(1902			*subject == target_collection.owner,1903			Error::<T>::NoPermission1904		);19051906		Ok(())1907	}19081909	fn is_owner_or_admin_permissions(1910		collection: &CollectionHandle<T>,1911		subject: &T::CrossAccountId,1912	) -> bool {1913		*subject.as_sub() == collection.owner1914			|| <AdminList<T>>::get(collection.id).contains(subject)1915	}19161917	fn check_owner_or_admin_permissions(1918		collection: &CollectionHandle<T>,1919		subject: &T::CrossAccountId,1920	) -> DispatchResult {1921		ensure!(1922			Self::is_owner_or_admin_permissions(collection, subject),1923			Error::<T>::NoPermission1924		);19251926		Ok(())1927	}19281929	fn owned_amount(1930		subject: &T::CrossAccountId,1931		target_collection: &CollectionHandle<T>,1932		item_id: TokenId,1933	) -> Option<u128> {1934		let collection_id = target_collection.id;19351936		match target_collection.mode {1937			CollectionMode::NFT => {1938				(<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1939			}1940			CollectionMode::Fungible(_) => {1941				Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1942			}1943			CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1944				.owner1945				.iter()1946				.find(|i| i.owner == *subject)1947				.map(|i| i.fraction),1948			CollectionMode::Invalid => None,1949		}1950	}19511952	fn is_item_owner(1953		subject: &T::CrossAccountId,1954		target_collection: &CollectionHandle<T>,1955		item_id: TokenId,1956	) -> bool {1957		match target_collection.mode {1958			CollectionMode::Fungible(_) => true,1959			_ => Self::owned_amount(subject, target_collection, item_id).is_some(),1960		}1961	}19621963	fn check_white_list(1964		collection: &CollectionHandle<T>,1965		address: &T::CrossAccountId,1966	) -> DispatchResult {1967		let collection_id = collection.id;19681969		let mes = Error::<T>::AddresNotInWhiteList;1970		ensure!(1971			<WhiteList<T>>::contains_key(collection_id, address.as_sub()),1972			mes1973		);19741975		Ok(())1976	}19771978	/// Check if token exists. In case of Fungible, check if there is an entry for1979	/// the owner in fungible balances double map1980	fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1981		let collection_id = target_collection.id;1982		let exists = match target_collection.mode {1983			CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1984			CollectionMode::Fungible(_) => true,1985			CollectionMode::ReFungible => {1986				<ReFungibleItemList<T>>::contains_key(collection_id, item_id)1987			}1988			_ => false,1989		};19901991		ensure!(exists, Error::<T>::TokenNotFound);1992		Ok(())1993	}19941995	fn transfer_fungible(1996		collection: &CollectionHandle<T>,1997		value: u128,1998		owner: &T::CrossAccountId,1999		recipient: &T::CrossAccountId,2000	) -> DispatchResult {2001		let collection_id = collection.id;20022003		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2004		ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20052006		// Send balance to recipient (updates balanceOf of recipient)2007		Self::add_fungible_item(collection, recipient, value)?;20082009		// update balanceOf of sender2010		<Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);20112012		// Reduce or remove sender2013		if balance.value == value {2014			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());2015		} else {2016			balance.value -= value;2017			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2018		}20192020		collection.log(ERC20Events::Transfer {2021			from: *owner.as_eth(),2022			to: *recipient.as_eth(),2023			value: value.into(),2024		})?;2025		Self::deposit_event(RawEvent::Transfer(2026			collection.id,2027			1,2028			owner.clone(),2029			recipient.clone(),2030			value,2031		));20322033		Ok(())2034	}20352036	fn transfer_refungible(2037		collection: &CollectionHandle<T>,2038		item_id: TokenId,2039		value: u128,2040		owner: T::CrossAccountId,2041		new_owner: T::CrossAccountId,2042	) -> DispatchResult {2043		let collection_id = collection.id;2044		let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2045			.ok_or(Error::<T>::TokenNotFound)?;20462047		let item = full_item2048			.owner2049			.iter()2050			.find(|i| i.owner == owner)2051			.ok_or(Error::<T>::TokenNotFound)?;2052		let amount = item.fraction;20532054		ensure!(amount >= value, Error::<T>::TokenValueTooLow);20552056		// update balance2057		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2058			.checked_sub(value)2059			.ok_or(Error::<T>::NumOverflow)?;2060		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20612062		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2063			.checked_add(value)2064			.ok_or(Error::<T>::NumOverflow)?;2065		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20662067		let old_owner = item.owner.clone();2068		let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20692070		let mut new_full_item = full_item.clone();2071		// transfer2072		if amount == value && !new_owner_has_account {2073			// change owner2074			// new owner do not have account2075			new_full_item2076				.owner2077				.iter_mut()2078				.find(|i| i.owner == owner)2079				.expect("old owner does present in refungible")2080				.owner = new_owner.clone();2081			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20822083			// update index collection2084			Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2085		} else {2086			new_full_item2087				.owner2088				.iter_mut()2089				.find(|i| i.owner == owner)2090				.expect("old owner does present in refungible")2091				.fraction -= value;20922093			// separate amount2094			if new_owner_has_account {2095				// new owner has account2096				new_full_item2097					.owner2098					.iter_mut()2099					.find(|i| i.owner == new_owner)2100					.expect("new owner has account")2101					.fraction += value;2102			} else {2103				// new owner do not have account2104				new_full_item.owner.push(Ownership {2105					owner: new_owner.clone(),2106					fraction: value,2107				});2108				Self::add_token_index(collection_id, item_id, &new_owner)?;2109			}21102111			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2112		}21132114		Self::deposit_event(RawEvent::Transfer(2115			collection.id,2116			item_id,2117			owner,2118			new_owner,2119			amount,2120		));21212122		Ok(())2123	}21242125	fn transfer_nft(2126		collection: &CollectionHandle<T>,2127		item_id: TokenId,2128		sender: T::CrossAccountId,2129		new_owner: T::CrossAccountId,2130	) -> DispatchResult {2131		let collection_id = collection.id;2132		let mut item =2133			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21342135		ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21362137		// update balance2138		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2139			.checked_sub(1)2140			.ok_or(Error::<T>::NumOverflow)?;2141		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21422143		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2144			.checked_add(1)2145			.ok_or(Error::<T>::NumOverflow)?;2146		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21472148		// change owner2149		let old_owner = item.owner.clone();2150		item.owner = new_owner.clone();2151		<NftItemList<T>>::insert(collection_id, item_id, item);21522153		// update index collection2154		Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21552156		collection.log(ERC721Events::Transfer {2157			from: *sender.as_eth(),2158			to: *new_owner.as_eth(),2159			token_id: item_id.into(),2160		})?;2161		Self::deposit_event(RawEvent::Transfer(2162			collection.id,2163			item_id,2164			sender,2165			new_owner,2166			1,2167		));21682169		Ok(())2170	}21712172	fn set_re_fungible_variable_data(2173		collection: &CollectionHandle<T>,2174		item_id: TokenId,2175		data: Vec<u8>,2176	) -> DispatchResult {2177		let collection_id = collection.id;2178		let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2179			.ok_or(Error::<T>::TokenNotFound)?;21802181		item.variable_data = data;21822183		<ReFungibleItemList<T>>::insert(collection_id, item_id, item);21842185		Ok(())2186	}21872188	fn set_nft_variable_data(2189		collection: &CollectionHandle<T>,2190		item_id: TokenId,2191		data: Vec<u8>,2192	) -> DispatchResult {2193		let collection_id = collection.id;2194		let mut item =2195			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21962197		item.variable_data = data;21982199		<NftItemList<T>>::insert(collection_id, item_id, item);22002201		Ok(())2202	}22032204	#[allow(dead_code)]2205	fn init_collection(item: &Collection<T>) {2206		// check params2207		assert!(2208			item.decimal_points <= MAX_DECIMAL_POINTS,2209			"decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2210		);2211		assert!(2212			item.name.len() <= 64,2213			"Collection name can not be longer than 63 char"2214		);2215		assert!(2216			item.name.len() <= 256,2217			"Collection description can not be longer than 255 char"2218		);2219		assert!(2220			item.token_prefix.len() <= 16,2221			"Token prefix can not be longer than 15 char"2222		);22232224		// Generate next collection ID2225		let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22262227		CreatedCollectionCount::put(next_id);2228	}22292230	#[allow(dead_code)]2231	fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2232		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22332234		Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22352236		<ItemListIndex>::insert(collection_id, current_index);22372238		// Update balance2239		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2240			.checked_add(1)2241			.unwrap();2242		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2243	}22442245	#[allow(dead_code)]2246	fn init_fungible_token(2247		collection_id: CollectionId,2248		owner: &T::CrossAccountId,2249		item: &FungibleItemType,2250	) {2251		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22522253		Self::add_token_index(collection_id, current_index, owner).unwrap();22542255		<ItemListIndex>::insert(collection_id, current_index);22562257		// Update balance2258		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2259			.checked_add(item.value)2260			.unwrap();2261		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2262	}22632264	#[allow(dead_code)]2265	fn init_refungible_token(2266		collection_id: CollectionId,2267		item: &ReFungibleItemType<T::CrossAccountId>,2268	) {2269		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22702271		let value = item.owner.first().unwrap().fraction;2272		let owner = item.owner.first().unwrap().owner.clone();22732274		Self::add_token_index(collection_id, current_index, &owner).unwrap();22752276		<ItemListIndex>::insert(collection_id, current_index);22772278		// Update balance2279		let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2280			.checked_add(value)2281			.unwrap();2282		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2283	}22842285	fn add_token_index(2286		collection_id: CollectionId,2287		item_index: TokenId,2288		owner: &T::CrossAccountId,2289	) -> DispatchResult {2290		// add to account limit2291		if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2292			// bound Owned tokens by a single address2293			let count = <AccountItemCount<T>>::get(owner.as_sub());2294			ensure!(2295				count < ChainLimit::get().account_token_ownership_limit,2296				Error::<T>::AddressOwnershipLimitExceeded2297			);22982299			<AccountItemCount<T>>::insert(2300				owner.as_sub(),2301				count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2302			);2303		} else {2304			<AccountItemCount<T>>::insert(owner.as_sub(), 1);2305		}23062307		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2308		if list_exists {2309			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2310			let item_contains = list.contains(&item_index.clone());23112312			if !item_contains {2313				list.push(item_index);2314			}23152316			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2317		} else {2318			let itm = vec![item_index];2319			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2320		}23212322		Ok(())2323	}23242325	fn remove_token_index(2326		collection_id: CollectionId,2327		item_index: TokenId,2328		owner: &T::CrossAccountId,2329	) -> DispatchResult {2330		// update counter2331		<AccountItemCount<T>>::insert(2332			owner.as_sub(),2333			<AccountItemCount<T>>::get(owner.as_sub())2334				.checked_sub(1)2335				.ok_or(Error::<T>::NumOverflow)?,2336		);23372338		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2339		if list_exists {2340			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2341			let item_contains = list.contains(&item_index.clone());23422343			if item_contains {2344				list.retain(|&item| item != item_index);2345				<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2346			}2347		}23482349		Ok(())2350	}23512352	fn move_token_index(2353		collection_id: CollectionId,2354		item_index: TokenId,2355		old_owner: &T::CrossAccountId,2356		new_owner: &T::CrossAccountId,2357	) -> DispatchResult {2358		Self::remove_token_index(collection_id, item_index, old_owner)?;2359		Self::add_token_index(collection_id, item_index, new_owner)?;23602361		Ok(())2362	}2363}23642365sp_api::decl_runtime_apis! {2366	pub trait NftApi {2367		/// Used for ethereum integration2368		fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2369	}2370}
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
--- a/pallets/nft/src/mock.rs
+++ b/pallets/nft/src/mock.rs
@@ -129,8 +129,11 @@
 	fn as_sub(&self) -> &u64 {
 		&self.0
 	}
-	fn from_eth(_eth: sp_core::H160) -> Self {
-		unimplemented!()
+	fn from_eth(eth: sp_core::H160) -> Self {
+		let mut sub_raw = [0; 8];
+		sub_raw.copy_from_slice(&eth.0[0..8]);
+		let sub = u64::from_be_bytes(sub_raw);
+		Self(sub, eth)
 	}
 	fn as_eth(&self) -> &sp_core::H160 {
 		&self.1
modifiedpallets/nft/src/sponsorship.rsdiffbeforeafterboth
--- a/pallets/nft/src/sponsorship.rs
+++ b/pallets/nft/src/sponsorship.rs
@@ -1,15 +1,18 @@
 use crate::{
 	Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket,
-	ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, ChainLimit,
-	CreateItemData, CollectionMode,
+	ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, CreateItemData,
+	CollectionMode,
 };
 use core::marker::PhantomData;
 use up_sponsorship::SponsorshipHandler;
 use frame_support::{
-	traits::IsSubType,
-	storage::{StorageMap, StorageDoubleMap, StorageValue},
+	traits::{IsSubType},
+	storage::{StorageMap, StorageDoubleMap},
+};
+use nft_data_structs::{
+	TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
 };
-use nft_data_structs::{TokenId, CollectionId};
 
 pub struct NftSponsorshipHandler<T>(PhantomData<T>);
 impl<T: Config> NftSponsorshipHandler<T> {
@@ -47,7 +50,6 @@
 		item_id: &TokenId,
 	) -> Option<T::AccountId> {
 		let collection = CollectionById::<T>::get(collection_id)?;
-		let limits = ChainLimit::get();
 
 		let mut sponsor_transfer = false;
 		if collection.sponsorship.confirmed() {
@@ -62,7 +64,7 @@
 					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
 						collection_limits.sponsor_transfer_timeout
 					} else {
-						limits.nft_sponsor_transfer_timeout
+						NFT_SPONSOR_TRANSFER_TIMEOUT
 					};
 
 					let mut sponsored = true;
@@ -84,7 +86,7 @@
 					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
 						collection_limits.sponsor_transfer_timeout
 					} else {
-						limits.fungible_sponsor_transfer_timeout
+						FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
 					};
 
 					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
@@ -107,7 +109,7 @@
 					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
 						collection_limits.sponsor_transfer_timeout
 					} else {
-						limits.refungible_sponsor_transfer_timeout
+						REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
 					};
 
 					let mut sponsored = true;
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,40 +1,18 @@
 // Tests to be written here
 use super::*;
 use crate::mock::*;
-use crate::{AccessMode, CollectionMode, Ownership, ChainLimits, CreateItemData};
+use crate::{AccessMode, CollectionMode, Ownership, CreateItemData};
 use nft_data_structs::{
 	CreateNftData, CreateFungibleData, CreateReFungibleData, CollectionId, TokenId,
 	MAX_DECIMAL_POINTS,
 };
 use frame_support::{assert_noop, assert_ok};
-use frame_system::{RawOrigin};
-
-fn default_collection_numbers_limit() -> u32 {
-	10
-}
-
-fn default_limits() {
-	assert_ok!(TemplateModule::set_chain_limits(
-		RawOrigin::Root.into(),
-		ChainLimits {
-			collection_numbers_limit: default_collection_numbers_limit(),
-			account_token_ownership_limit: 10,
-			collections_admins_limit: 5,
-			custom_data_limit: 2048,
-			nft_sponsor_transfer_timeout: 15,
-			fungible_sponsor_transfer_timeout: 15,
-			refungible_sponsor_transfer_timeout: 15,
-			const_on_chain_schema_limit: 1024,
-			offchain_schema_limit: 1024,
-			variable_on_chain_schema_limit: 1024,
-		}
-	));
-}
+use sp_std::convert::TryInto;
 
 fn default_nft_data() -> CreateNftData {
 	CreateNftData {
-		const_data: vec![1, 2, 3],
-		variable_data: vec![3, 2, 1],
+		const_data: vec![1, 2, 3].try_into().unwrap(),
+		variable_data: vec![3, 2, 1].try_into().unwrap(),
 	}
 }
 
@@ -44,8 +22,8 @@
 
 fn default_re_fungible_data() -> CreateReFungibleData {
 	CreateReFungibleData {
-		const_data: vec![1, 2, 3],
-		variable_data: vec![3, 2, 1],
+		const_data: vec![1, 2, 3].try_into().unwrap(),
+		variable_data: vec![3, 2, 1].try_into().unwrap(),
 		pieces: 1023,
 	}
 }
@@ -112,7 +90,6 @@
 #[test]
 fn set_version_schema() {
 	new_test_ext().execute_with(|| {
-		default_limits();
 		let origin1 = Origin::signed(1);
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
@@ -133,8 +110,6 @@
 #[test]
 fn create_fungible_collection_fails_with_large_decimal_numbers() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
 		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
 		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
@@ -156,14 +131,13 @@
 #[test]
 fn create_nft_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
 		create_test_item(collection_id, &data.clone().into());
 		let item = TemplateModule::nft_item_id(collection_id, 1).unwrap();
-		assert_eq!(item.const_data, data.const_data);
-		assert_eq!(item.variable_data, data.variable_data);
+		assert_eq!(item.const_data, data.const_data.into_inner());
+		assert_eq!(item.variable_data, data.variable_data.into_inner());
 	});
 }
 
@@ -172,8 +146,6 @@
 #[test]
 fn create_nft_multiple_items() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -190,10 +162,10 @@
 				.map(|d| { d.into() })
 				.collect()
 		));
-		for (index, data) in items_data.iter().enumerate() {
+		for (index, data) in items_data.into_iter().enumerate() {
 			let item = TemplateModule::nft_item_id(1, (index + 1) as TokenId).unwrap();
-			assert_eq!(item.const_data.to_vec(), data.const_data);
-			assert_eq!(item.variable_data.to_vec(), data.variable_data);
+			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+			assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
 		}
 	});
 }
@@ -201,14 +173,13 @@
 #[test]
 fn create_refungible_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
 		let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
 		let data = default_re_fungible_data();
 		create_test_item(collection_id, &data.clone().into());
 		let item = TemplateModule::refungible_item_id(collection_id, 1).unwrap();
-		assert_eq!(item.const_data, data.const_data);
-		assert_eq!(item.variable_data, data.variable_data);
+		assert_eq!(item.const_data, data.const_data.into_inner());
+		assert_eq!(item.variable_data, data.variable_data.into_inner());
 		assert_eq!(
 			item.owner[0],
 			Ownership {
@@ -222,8 +193,6 @@
 #[test]
 fn create_multiple_refungible_items() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		create_test_collection(&CollectionMode::ReFungible, 1);
 
 		let origin1 = Origin::signed(1);
@@ -244,10 +213,10 @@
 				.map(|d| { d.into() })
 				.collect()
 		));
-		for (index, data) in items_data.iter().enumerate() {
+		for (index, data) in items_data.into_iter().enumerate() {
 			let item = TemplateModule::refungible_item_id(1, (index + 1) as TokenId).unwrap();
-			assert_eq!(item.const_data.to_vec(), data.const_data);
-			assert_eq!(item.variable_data.to_vec(), data.variable_data);
+			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+			assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
 			assert_eq!(
 				item.owner[0],
 				Ownership {
@@ -262,8 +231,6 @@
 #[test]
 fn create_fungible_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
 
 		let data = default_fungible_data();
@@ -302,8 +269,6 @@
 #[test]
 fn transfer_fungible_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
 
 		let origin1 = Origin::signed(1);
@@ -344,8 +309,6 @@
 #[test]
 fn transfer_refungible_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
 		let data = default_re_fungible_data();
@@ -355,8 +318,8 @@
 		let origin2 = Origin::signed(2);
 		{
 			let item = TemplateModule::refungible_item_id(collection_id, 1).unwrap();
-			assert_eq!(item.const_data, data.const_data);
-			assert_eq!(item.variable_data, data.variable_data);
+			assert_eq!(item.const_data, data.const_data.into_inner());
+			assert_eq!(item.variable_data, data.variable_data.into_inner());
 			assert_eq!(
 				item.owner[0],
 				Ownership {
@@ -441,8 +404,6 @@
 #[test]
 fn transfer_nft_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
@@ -464,8 +425,6 @@
 #[test]
 fn nft_approve_and_transfer_from() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
@@ -503,8 +462,6 @@
 #[test]
 fn nft_approve_and_transfer_from_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -514,8 +471,8 @@
 		create_test_item(collection_id, &data.clone().into());
 
 		assert_eq!(
-			TemplateModule::nft_item_id(1, 1).unwrap().const_data,
-			data.const_data
+			&TemplateModule::nft_item_id(1, 1).unwrap().const_data,
+			&data.const_data.into_inner()
 		);
 		assert_eq!(TemplateModule::balance_count(1, 1), 1);
 		assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -573,8 +530,6 @@
 #[test]
 fn refungible_approve_and_transfer_from() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
 		let origin1 = Origin::signed(1);
@@ -636,8 +591,6 @@
 #[test]
 fn fungible_approve_and_transfer_from() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
 
 		let data = default_fungible_data();
@@ -710,8 +663,6 @@
 #[test]
 fn change_collection_owner() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -730,8 +681,6 @@
 #[test]
 fn destroy_collection() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -742,8 +691,6 @@
 #[test]
 fn burn_nft_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -773,8 +720,6 @@
 #[test]
 fn burn_fungible_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
 
 		let origin1 = Origin::signed(1);
@@ -804,8 +749,6 @@
 #[test]
 fn burn_refungible_item() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 		let origin1 = Origin::signed(1);
 
@@ -851,8 +794,6 @@
 #[test]
 fn add_collection_admin() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
 		create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);
 		create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);
@@ -879,8 +820,6 @@
 #[test]
 fn remove_collection_admin() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
 		create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);
 		create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);
@@ -916,8 +855,6 @@
 #[test]
 fn balance_of() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let nft_collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let fungible_collection_id = create_test_collection(&CollectionMode::Fungible(3), 2);
 		let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible, 3);
@@ -969,8 +906,6 @@
 #[test]
 fn approve() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
@@ -987,8 +922,6 @@
 #[test]
 fn transfer_from() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 		let origin2 = Origin::signed(2);
@@ -1051,8 +984,6 @@
 #[test]
 fn owner_can_add_address_to_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1068,8 +999,6 @@
 #[test]
 fn admin_can_add_address_to_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 		let origin2 = Origin::signed(2);
@@ -1091,8 +1020,6 @@
 #[test]
 fn nonprivileged_user_cannot_add_address_to_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin2 = Origin::signed(2);
@@ -1106,8 +1033,6 @@
 #[test]
 fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let origin1 = Origin::signed(1);
 
 		assert_noop!(
@@ -1120,8 +1045,6 @@
 #[test]
 fn nobody_can_add_address_to_white_list_of_deleted_collection() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1140,8 +1063,6 @@
 #[test]
 fn address_is_already_added_to_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 
@@ -1162,8 +1083,6 @@
 #[test]
 fn owner_can_remove_address_from_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1184,8 +1103,6 @@
 #[test]
 fn admin_can_remove_address_from_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 		let origin2 = Origin::signed(2);
@@ -1213,8 +1130,6 @@
 #[test]
 fn nonprivileged_user_cannot_remove_address_from_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 		let origin2 = Origin::signed(2);
@@ -1235,7 +1150,6 @@
 #[test]
 fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {
 	new_test_ext().execute_with(|| {
-		default_limits();
 		let origin1 = Origin::signed(1);
 
 		assert_noop!(
@@ -1248,8 +1162,6 @@
 #[test]
 fn nobody_can_remove_address_from_white_list_of_deleted_collection() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 		let origin2 = Origin::signed(2);
@@ -1272,8 +1184,6 @@
 #[test]
 fn address_is_already_removed_from_white_list() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 
@@ -1300,8 +1210,6 @@
 #[test]
 fn white_list_test_1() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1330,8 +1238,6 @@
 #[test]
 fn white_list_test_2() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 
@@ -1381,8 +1287,6 @@
 #[test]
 fn white_list_test_3() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1411,8 +1315,6 @@
 #[test]
 fn white_list_test_4() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1463,8 +1365,6 @@
 #[test]
 fn white_list_test_5() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1488,8 +1388,6 @@
 #[test]
 fn white_list_test_6() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1516,8 +1414,6 @@
 #[test]
 fn white_list_test_7() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
@@ -1548,8 +1444,6 @@
 #[test]
 fn white_list_test_8() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
@@ -1598,8 +1492,6 @@
 #[test]
 fn white_list_test_9() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 		let origin1 = Origin::signed(1);
 
@@ -1623,8 +1515,6 @@
 #[test]
 fn white_list_test_10() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1660,8 +1550,6 @@
 #[test]
 fn white_list_test_11() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1694,8 +1582,6 @@
 #[test]
 fn white_list_test_12() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1723,8 +1609,6 @@
 #[test]
 fn white_list_test_13() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1749,8 +1633,6 @@
 #[test]
 fn white_list_test_14() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1786,8 +1668,6 @@
 #[test]
 fn white_list_test_15() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1815,8 +1695,6 @@
 #[test]
 fn white_list_test_16() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1851,8 +1729,6 @@
 #[test]
 fn total_number_collections_bound() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		create_test_collection(&CollectionMode::NFT, 1);
 	});
 }
@@ -1861,11 +1737,9 @@
 #[test]
 fn total_number_collections_bound_neg() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let origin1 = Origin::signed(1);
 
-		for i in 0..default_collection_numbers_limit() {
+		for i in 0..COLLECTION_NUMBER_LIMIT {
 			create_test_collection(&CollectionMode::NFT, i + 1);
 		}
 
@@ -1891,8 +1765,6 @@
 #[test]
 fn owned_tokens_bound() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let data = default_nft_data();
@@ -1905,28 +1777,16 @@
 #[test]
 fn owned_tokens_bound_neg() {
 	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 1,
-				collections_admins_limit: 5,
-				custom_data_limit: 2048,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
+
+		for _ in 0..ACCOUNT_TOKEN_OWNERSHIP_LIMIT {
+			let data = default_nft_data();
+			create_test_item(collection_id, &data.clone().into());
+		}
+
 		let data = default_nft_data();
-		create_test_item(collection_id, &data.clone().into());
-
 		assert_noop!(
 			TemplateModule::create_item(origin1, 1, account(1), data.into()),
 			Error::<Test>::AddressOwnershipLimitExceeded
@@ -1938,22 +1798,6 @@
 #[test]
 fn collection_admins_bound() {
 	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 2,
-				custom_data_limit: 2048,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -1975,184 +1819,32 @@
 #[test]
 fn collection_admins_bound_neg() {
 	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 1,
-				collections_admins_limit: 1,
-				custom_data_limit: 2048,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
 
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
+		for i in 0..COLLECTION_ADMINS_LIMIT {
+			assert_ok!(TemplateModule::add_collection_admin(
+				origin1.clone(),
+				collection_id,
+				account(2 + i)
+			));
+		}
 		assert_noop!(
-			TemplateModule::add_collection_admin(origin1, collection_id, account(3)),
+			TemplateModule::add_collection_admin(
+				origin1,
+				collection_id,
+				account(3 + COLLECTION_ADMINS_LIMIT)
+			),
 			Error::<Test>::CollectionAdminsLimitExceeded
-		);
-	});
-}
-
-// NFT custom data size. Negative test const_data.
-#[test]
-fn custom_data_size_nft_const_data_bound_neg() {
-	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 5,
-				custom_data_limit: 2,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
-		let origin1 = Origin::signed(1);
-		let too_big_const_data = CreateItemData::NFT(CreateNftData {
-			const_data: vec![1, 2, 3, 4],
-			variable_data: vec![],
-		});
-
-		assert_noop!(
-			TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
-			Error::<Test>::TokenConstDataLimitExceeded
-		);
-	});
-}
-
-// NFT custom data size. Negative test variable_data.
-#[test]
-fn custom_data_size_nft_variable_data_bound_neg() {
-	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 5,
-				custom_data_limit: 2,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
-		let origin1 = Origin::signed(1);
-		let too_big_const_data = CreateItemData::NFT(CreateNftData {
-			const_data: vec![],
-			variable_data: vec![1, 2, 3, 4],
-		});
-
-		assert_noop!(
-			TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
-			Error::<Test>::TokenVariableDataLimitExceeded
-		);
-	});
-}
-
-// Re fungible custom data size. Negative test const_data.
-#[test]
-fn custom_data_size_re_fungible_const_data_bound_neg() {
-	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 5,
-				custom_data_limit: 2,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
-		let origin1 = Origin::signed(1);
-		let too_big_const_data = CreateItemData::NFT(CreateNftData {
-			const_data: vec![1, 2, 3, 4],
-			variable_data: vec![],
-		});
-
-		assert_noop!(
-			TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
-			Error::<Test>::TokenConstDataLimitExceeded
 		);
 	});
 }
-
-// Re fungible custom data size. Negative test variable_data.
-#[test]
-fn custom_data_size_re_fungible_variable_data_bound_neg() {
-	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: 10,
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 5,
-				custom_data_limit: 2,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
-		let origin1 = Origin::signed(1);
-		let too_big_const_data = CreateItemData::NFT(CreateNftData {
-			const_data: vec![],
-			variable_data: vec![1, 2, 3, 4],
-		});
-
-		assert_noop!(
-			TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
-			Error::<Test>::TokenVariableDataLimitExceeded
-		);
-	});
-}
 // #endregion
 
 #[test]
 fn set_const_on_chain_schema() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -2180,8 +1872,6 @@
 #[test]
 fn set_variable_on_chain_schema() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -2209,8 +1899,6 @@
 #[test]
 fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -2218,7 +1906,7 @@
 		let data = default_nft_data();
 		create_test_item(1, &data.into());
 
-		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		let variable_data = b"test data".to_vec();
 		assert_ok!(TemplateModule::set_variable_meta_data(
 			origin1,
 			collection_id,
@@ -2238,8 +1926,6 @@
 #[test]
 fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
 		let origin1 = Origin::signed(1);
@@ -2247,7 +1933,7 @@
 		let data = default_re_fungible_data();
 		create_test_item(1, &data.into());
 
-		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		let variable_data = b"test data".to_vec();
 		assert_ok!(TemplateModule::set_variable_meta_data(
 			origin1,
 			collection_id,
@@ -2267,8 +1953,6 @@
 #[test]
 fn set_variable_meta_data_on_fungible_token_fails() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
 
 		let origin1 = Origin::signed(1);
@@ -2276,7 +1960,7 @@
 		let data = default_fungible_data();
 		create_test_item(1, &data.into());
 
-		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		let variable_data = b"test data".to_vec();
 		assert_noop!(
 			TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data),
 			Error::<Test>::CantStoreMetadataInFungibleTokens
@@ -2287,22 +1971,6 @@
 #[test]
 fn set_variable_meta_data_on_nft_token_fails_for_big_data() {
 	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: default_collection_numbers_limit(),
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 5,
-				custom_data_limit: 10,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
 
 		let origin1 = Origin::signed(1);
@@ -2321,22 +1989,6 @@
 #[test]
 fn set_variable_meta_data_on_re_fungible_token_fails_for_big_data() {
 	new_test_ext().execute_with(|| {
-		assert_ok!(TemplateModule::set_chain_limits(
-			RawOrigin::Root.into(),
-			ChainLimits {
-				collection_numbers_limit: default_collection_numbers_limit(),
-				account_token_ownership_limit: 10,
-				collections_admins_limit: 5,
-				custom_data_limit: 10,
-				nft_sponsor_transfer_timeout: 15,
-				fungible_sponsor_transfer_timeout: 15,
-				refungible_sponsor_transfer_timeout: 15,
-				const_on_chain_schema_limit: 1024,
-				offchain_schema_limit: 1024,
-				variable_on_chain_schema_limit: 1024,
-			}
-		));
-
 		let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
 
 		let origin1 = Origin::signed(1);
@@ -2355,8 +2007,6 @@
 #[test]
 fn collection_transfer_flag_works() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let origin1 = Origin::signed(1);
 
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -2382,8 +2032,6 @@
 #[test]
 fn collection_transfer_flag_works_neg() {
 	new_test_ext().execute_with(|| {
-		default_limits();
-
 		let origin1 = Origin::signed(1);
 
 		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
addedpallets/nft/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft/src/weights.rs
@@ -0,0 +1,424 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_nft
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
+//! DATE: 2021-08-31, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128
+
+// Executed Command:
+// target/release/nft
+// benchmark
+// --pallet
+// pallet-nft
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=20
+// --output=./pallets/nft/src/weights.rs
+
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_nft.
+pub trait WeightInfo {
+	fn create_collection() -> Weight;
+	fn destroy_collection() -> Weight;
+	fn add_to_white_list() -> Weight;
+	fn remove_from_white_list() -> Weight;
+	fn set_public_access_mode() -> Weight;
+	fn set_mint_permission() -> Weight;
+	fn change_collection_owner() -> Weight;
+	fn add_collection_admin() -> Weight;
+	fn remove_collection_admin() -> Weight;
+	fn set_collection_sponsor() -> Weight;
+	fn confirm_sponsorship() -> Weight;
+	fn remove_collection_sponsor() -> Weight;
+	fn create_item_nft(b: u32, ) -> Weight;
+	fn create_multiple_items_nft(b: u32, ) -> Weight;
+	fn create_item_fungible() -> Weight;
+	fn create_multiple_items_fungible(b: u32, ) -> Weight;
+	fn create_item_refungible(b: u32, ) -> Weight;
+	fn create_multiple_items_refungible(b: u32, ) -> Weight;
+	fn burn_item_nft() -> Weight;
+	fn transfer_nft() -> Weight;
+	fn transfer_fungible() -> Weight;
+	fn transfer_refungible() -> Weight;
+	fn set_transfers_enabled_flag() -> Weight;
+	fn approve_nft() -> Weight;
+	fn transfer_from_nft() -> Weight;
+	fn transfer_from_fungible() -> Weight;
+	fn transfer_from_refungible() -> Weight;
+	fn set_offchain_schema(b: u32, ) -> Weight;
+	fn set_const_on_chain_schema(b: u32, ) -> Weight;
+	fn set_variable_on_chain_schema(b: u32, ) -> Weight;
+	fn set_variable_meta_data_nft(b: u32, ) -> Weight;
+	fn set_schema_version() -> Weight;
+	fn set_collection_limits() -> Weight;
+}
+
+/// Weights for pallet_nft using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	fn create_collection() -> Weight {
+		(25_851_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(8 as Weight))
+			.saturating_add(T::DbWeight::get().writes(6 as Weight))
+	}
+	fn destroy_collection() -> Weight {
+		(28_737_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn add_to_white_list() -> Weight {
+		(6_237_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn remove_from_white_list() -> Weight {
+		(6_252_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_public_access_mode() -> Weight {
+		(6_691_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_mint_permission() -> Weight {
+		(6_630_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn change_collection_owner() -> Weight {
+		(6_521_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn add_collection_admin() -> Weight {
+		(8_057_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_admin() -> Weight {
+		(8_307_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_sponsor() -> Weight {
+		(6_484_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn confirm_sponsorship() -> Weight {
+		(6_530_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_sponsor() -> Weight {
+		(6_733_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn create_item_nft(_b: u32, ) -> Weight {
+		(167_909_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(8 as Weight))
+	}
+	fn create_multiple_items_nft(b: u32, ) -> Weight {
+		(336_830_000 as Weight)
+			// Standard Error: 42_000
+			.saturating_add((11_627_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	fn create_item_fungible() -> Weight {
+		(24_123_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn create_multiple_items_fungible(b: u32, ) -> Weight {
+		(48_227_000 as Weight)
+			// Standard Error: 13_000
+			.saturating_add((2_918_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn create_item_refungible(_b: u32, ) -> Weight {
+		(26_293_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn create_multiple_items_refungible(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 16_000
+			.saturating_add((8_374_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(6 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	fn burn_item_nft() -> Weight {
+		(32_237_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_nft() -> Weight {
+		(192_578_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(14 as Weight))
+			.saturating_add(T::DbWeight::get().writes(10 as Weight))
+	}
+	fn transfer_fungible() -> Weight {
+		(170_749_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_refungible() -> Weight {
+		(35_949_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(10 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn set_transfers_enabled_flag() -> Weight {
+		(6_376_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn approve_nft() -> Weight {
+		(169_825_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn transfer_from_nft() -> Weight {
+		(197_912_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(15 as Weight))
+			.saturating_add(T::DbWeight::get().writes(11 as Weight))
+	}
+	fn transfer_from_fungible() -> Weight {
+		(183_789_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(12 as Weight))
+			.saturating_add(T::DbWeight::get().writes(8 as Weight))
+	}
+	fn transfer_from_refungible() -> Weight {
+		(37_149_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(8 as Weight))
+	}
+	fn set_offchain_schema(_b: u32, ) -> Weight {
+		(6_435_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_const_on_chain_schema(_b: u32, ) -> Weight {
+		(6_646_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_on_chain_schema(_b: u32, ) -> Weight {
+		(6_542_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_meta_data_nft(_b: u32, ) -> Weight {
+		(14_697_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_schema_version() -> Weight {
+		(6_566_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_limits() -> Weight {
+		(6_349_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	fn create_collection() -> Weight {
+		(25_851_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(8 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(6 as Weight))
+	}
+	fn destroy_collection() -> Weight {
+		(28_737_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn add_to_white_list() -> Weight {
+		(6_237_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn remove_from_white_list() -> Weight {
+		(6_252_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_public_access_mode() -> Weight {
+		(6_691_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_mint_permission() -> Weight {
+		(6_630_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn change_collection_owner() -> Weight {
+		(6_521_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn add_collection_admin() -> Weight {
+		(8_057_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_admin() -> Weight {
+		(8_307_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_sponsor() -> Weight {
+		(6_484_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn confirm_sponsorship() -> Weight {
+		(6_530_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_sponsor() -> Weight {
+		(6_733_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn create_item_nft(b: u32, ) -> Weight {
+		(167_180_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((10_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(8 as Weight))
+	}
+	fn create_multiple_items_nft(b: u32, ) -> Weight {
+		(336_830_000 as Weight)
+			// Standard Error: 42_000
+			.saturating_add((11_627_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	fn create_item_fungible() -> Weight {
+		(24_123_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn create_multiple_items_fungible(b: u32, ) -> Weight {
+		(13_217_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((2_971_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn create_item_refungible(_b: u32, ) -> Weight {
+		(26_293_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn create_multiple_items_refungible(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 16_000
+			.saturating_add((8_374_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(6 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	fn burn_item_nft() -> Weight {
+		(32_237_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_nft() -> Weight {
+		(192_578_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(14 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(10 as Weight))
+	}
+	fn transfer_fungible() -> Weight {
+		(170_749_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_refungible() -> Weight {
+		(35_949_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(10 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn set_transfers_enabled_flag() -> Weight {
+		(6_376_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn approve_nft() -> Weight {
+		(169_825_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn transfer_from_nft() -> Weight {
+		(197_912_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(15 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(11 as Weight))
+	}
+	fn transfer_from_fungible() -> Weight {
+		(183_789_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(12 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(8 as Weight))
+	}
+	fn transfer_from_refungible() -> Weight {
+		(37_149_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(8 as Weight))
+	}
+	fn set_offchain_schema(_b: u32, ) -> Weight {
+		(6_435_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_const_on_chain_schema(_b: u32, ) -> Weight {
+		(6_646_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_on_chain_schema(_b: u32, ) -> Weight {
+		(6_542_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_meta_data_nft(_b: u32, ) -> Weight {
+		(14_697_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_schema_version() -> Weight {
+		(6_566_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_limits() -> Weight {
+		(6_349_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+}
\ No newline at end of file
modifiedprimitives/nft/Cargo.tomldiffbeforeafterboth
--- a/primitives/nft/Cargo.toml
+++ b/primitives/nft/Cargo.toml
@@ -9,20 +9,28 @@
 version = '0.9.0'
 
 [dependencies]
-codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ['derive'] }
-serde = { version = "1.0.119", features = ['derive'], default-features = false }
+codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false, features = ['derive'] }
+serde = { version = "1.0.119", features = ['derive'], default-features = false, optional = true }
+max-encoded-len = { default-features = false, features = ['derive'], version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
 frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
 frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
 sp-core = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-std = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
 sp-runtime = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+derivative = "2.2.0"
 
 [features]
 default = ["std"]
 std = [
+  "serde1",
   "serde/std",
   "codec/std",
+  "max-encoded-len/std",
   "frame-system/std",
   "frame-support/std",
   "sp-runtime/std",
   "sp-core/std",
-]
\ No newline at end of file
+  "sp-std/std",
+]
+serde1 = ["serde"]
+limit-testing = []
\ No newline at end of file
modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -1,11 +1,13 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
+#[cfg(feature = "serde")]
 pub use serde::{Serialize, Deserialize};
 
 use sp_runtime::sp_std::prelude::Vec;
 use codec::{Decode, Encode};
+use max_encoded_len::MaxEncodedLen;
 pub use frame_support::{
-	construct_runtime, decl_event, decl_module, decl_storage, decl_error,
+	BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,
 	dispatch::DispatchResult,
 	ensure, fail, parameter_types,
 	traits::{
@@ -19,18 +21,58 @@
 	},
 	StorageValue, transactional,
 };
+use derivative::Derivative;
 
 pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;
 pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;
 pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;
 pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;
 
+pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {
+	100000
+} else {
+	10
+};
+pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {
+	2048
+} else {
+	10
+};
+pub const COLLECTION_ADMINS_LIMIT: u64 = 5;
+pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {
+	1000000
+} else {
+	10
+};
+
+// Timeouts for item types in passed blocks
+pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+
+// Schema limits
+pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;
+pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
+pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
+
+pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;
+pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;
+pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;
+
+/// How much items can be created per single
+/// create_many call
+pub const MAX_ITEMS_PER_BATCH: u32 = 200;
+
+parameter_types! {
+	pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;
+}
+
 pub type CollectionId = u32;
 pub type TokenId = u32;
 pub type DecimalPoints = u8;
 
 #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum CollectionMode {
 	Invalid,
 	NFT,
@@ -61,7 +103,7 @@
 }
 
 #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum AccessMode {
 	Normal,
 	WhiteList,
@@ -73,7 +115,7 @@
 }
 
 #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum SchemaVersion {
 	ImageURL,
 	Unique,
@@ -85,14 +127,14 @@
 }
 
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct Ownership<AccountId> {
 	pub owner: AccountId,
 	pub fraction: u128,
 }
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum SponsorshipState<AccountId> {
 	/// The fees are applied to the transaction sender
 	Disabled,
@@ -128,7 +170,7 @@
 }
 
 #[derive(Encode, Decode, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct Collection<T: frame_system::Config> {
 	pub owner: T::AccountId,
 	pub mode: CollectionMode,
@@ -148,7 +190,7 @@
 }
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct NftItemType<AccountId> {
 	pub owner: AccountId,
 	pub const_data: Vec<u8>,
@@ -156,13 +198,13 @@
 }
 
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct FungibleItemType {
 	pub value: u128,
 }
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct ReFungibleItemType<AccountId> {
 	pub owner: Vec<Ownership<AccountId>>,
 	pub const_data: Vec<u8>,
@@ -170,7 +212,7 @@
 }
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct CollectionLimits<BlockNumber: Encode + Decode> {
 	pub account_token_ownership_limit: u32,
 	pub sponsored_data_size: u32,
@@ -200,48 +242,72 @@
 	}
 }
 
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct ChainLimits {
-	pub collection_numbers_limit: u32,
-	pub account_token_ownership_limit: u32,
-	pub collections_admins_limit: u64,
-	pub custom_data_limit: u32,
+/// BoundedVec doesn't supports serde
+#[cfg(feature = "serde1")]
+mod bounded_serde {
+	use core::convert::TryFrom;
+	use frame_support::{BoundedVec, traits::Get};
+	use serde::{
+		ser::{self, Serialize},
+		de::{self, Deserialize, Error},
+	};
+	use sp_std::vec::Vec;
 
-	// Timeouts for item types in passed blocks
-	pub nft_sponsor_transfer_timeout: u32,
-	pub fungible_sponsor_transfer_timeout: u32,
-	pub refungible_sponsor_transfer_timeout: u32,
+	pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>
+	where
+		D: ser::Serializer,
+		V: Serialize,
+	{
+		let vec: &Vec<_> = &value;
+		vec.serialize(serializer)
+	}
 
-	// Schema limits
-	pub offchain_schema_limit: u32,
-	pub variable_on_chain_schema_limit: u32,
-	pub const_on_chain_schema_limit: u32,
+	pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>
+	where
+		D: de::Deserializer<'de>,
+		V: de::Deserialize<'de>,
+		S: Get<u32>,
+	{
+		// TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?
+		let vec = <Vec<V>>::deserialize(deserializer)?;
+		let len = vec.len();
+		TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
+	}
 }
 
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
+#[derivative(Debug)]
 pub struct CreateNftData {
-	pub const_data: Vec<u8>,
-	pub variable_data: Vec<u8>,
+	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+	#[derivative(Debug = "ignore")]
+	pub const_data: BoundedVec<u8, CustomDataLimit>,
+	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+	#[derivative(Debug = "ignore")]
+	pub variable_data: BoundedVec<u8, CustomDataLimit>,
 }
 
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct CreateFungibleData {
 	pub value: u128,
 }
 
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
+#[derivative(Debug)]
 pub struct CreateReFungibleData {
-	pub const_data: Vec<u8>,
-	pub variable_data: Vec<u8>,
+	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+	#[derivative(Debug = "ignore")]
+	pub const_data: BoundedVec<u8, CustomDataLimit>,
+	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+	#[derivative(Debug = "ignore")]
+	pub variable_data: BoundedVec<u8, CustomDataLimit>,
 	pub pieces: u128,
 }
 
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum CreateItemData {
 	NFT(CreateNftData),
 	Fungible(CreateFungibleData),
modifiedruntime/Cargo.tomldiffbeforeafterboth
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -23,14 +23,18 @@
     'frame-support/runtime-benchmarks',
     'frame-system-benchmarking',
     'frame-system/runtime-benchmarks',
+    'pallet-evm-migration/runtime-benchmarks',
     'pallet-balances/runtime-benchmarks',
     'pallet-timestamp/runtime-benchmarks',
     'pallet-nft/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
+    'pallet-xcm/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
+    'xcm-builder/runtime-benchmarks',
 ]
 std = [
     'codec/std',
+    'max-encoded-len/std',
     'cumulus-pallet-aura-ext/std',
     'cumulus-pallet-parachain-system/std',
     'cumulus-pallet-xcm/std',
@@ -55,6 +59,7 @@
     'pallet-treasury/std',
     'pallet-vesting/std',
     'pallet-evm/std',
+    'pallet-evm-migration/std',
     'pallet-evm-contract-helpers/std',
     'pallet-evm-transaction-payment/std',
     'pallet-evm-coder-substrate/std',
@@ -84,6 +89,10 @@
     'xcm-builder/std',
     'xcm-executor/std',
 ]
+limit-testing = [
+    'pallet-nft/limit-testing',
+    'nft-data-structs/limit-testing',
+]
 
 ################################################################################
 # Substrate Dependencies
@@ -378,6 +387,8 @@
 # local dependencies
 
 [dependencies]
+max-encoded-len = { default-features = false, features = ['derive'], version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+derivative = "2.2.0"
 pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
 pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
 nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }
@@ -385,6 +396,7 @@
 # pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
 pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }
 pallet-nft-charge-transaction = { path = '../pallets/nft-charge-transaction', default-features = false, version = '3.0.0' }
+pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }
 pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }
 pallet-evm-transaction-payment = { path = '../pallets/evm-transaction-payment', default-features = false }
 pallet-evm-coder-substrate = { default-features = false, path = "../pallets/evm-coder-substrate" }
modifiedruntime/src/chain_extension.rsdiffbeforeafterboth
--- a/runtime/src/chain_extension.rs
+++ b/runtime/src/chain_extension.rs
@@ -6,6 +6,8 @@
 //
 
 use codec::{Decode, Encode};
+use max_encoded_len::MaxEncodedLen;
+use derivative::Derivative;
 
 pub use pallet_contracts::chain_extension::RetVal;
 use pallet_contracts::chain_extension::{
@@ -19,61 +21,63 @@
 pub use pallet_nft::*;
 use pallet_nft::CrossAccountId;
 use nft_data_structs::*;
-
-use crate::Vec;
 
 /// Create item parameters
-#[derive(Debug, PartialEq, Encode, Decode)]
-pub struct NFTExtCreateItem<E: Ext> {
-	pub owner: <E::T as SysConfig>::AccountId,
+#[derive(Debug, PartialEq, Encode, Decode, MaxEncodedLen)]
+pub struct NFTExtCreateItem<AccountId> {
+	pub owner: AccountId,
 	pub collection_id: u32,
 	pub data: CreateItemData,
 }
 
 /// Transfer parameters
-#[derive(Debug, PartialEq, Encode, Decode)]
-pub struct NFTExtTransfer<E: Ext> {
-	pub recipient: <E::T as SysConfig>::AccountId,
+#[derive(Debug, PartialEq, Encode, Decode, MaxEncodedLen)]
+pub struct NFTExtTransfer<AccountId> {
+	pub recipient: AccountId,
 	pub collection_id: u32,
 	pub token_id: u32,
 	pub amount: u128,
 }
 
-#[derive(Debug, PartialEq, Encode, Decode)]
-pub struct NFTExtCreateMultipleItems<E: Ext> {
-	pub owner: <E::T as SysConfig>::AccountId,
+#[derive(Derivative, PartialEq, Encode, Decode, MaxEncodedLen)]
+#[derivative(Debug)]
+pub struct NFTExtCreateMultipleItems<AccountId> {
+	pub owner: AccountId,
 	pub collection_id: u32,
-	pub data: Vec<CreateItemData>,
+	#[derivative(Debug = "ignore")]
+	pub data: BoundedVec<CreateItemData, MaxItemsPerBatch>,
 }
 
-#[derive(Debug, PartialEq, Encode, Decode)]
-pub struct NFTExtApprove<E: Ext> {
-	pub spender: <E::T as SysConfig>::AccountId,
+#[derive(Debug, PartialEq, Encode, Decode, MaxEncodedLen)]
+pub struct NFTExtApprove<AccountId> {
+	pub spender: AccountId,
 	pub collection_id: u32,
 	pub item_id: u32,
 	pub amount: u128,
 }
 
-#[derive(Debug, PartialEq, Encode, Decode)]
-pub struct NFTExtTransferFrom<E: Ext> {
-	pub owner: <E::T as SysConfig>::AccountId,
-	pub recipient: <E::T as SysConfig>::AccountId,
+#[derive(Debug, PartialEq, Encode, Decode, MaxEncodedLen)]
+pub struct NFTExtTransferFrom<AccountId> {
+	pub owner: AccountId,
+	pub recipient: AccountId,
 	pub collection_id: u32,
 	pub item_id: u32,
 	pub amount: u128,
 }
 
-#[derive(Debug, PartialEq, Encode, Decode)]
+#[derive(Derivative, PartialEq, Encode, Decode, MaxEncodedLen)]
+#[derivative(Debug)]
 pub struct NFTExtSetVariableMetaData {
 	pub collection_id: u32,
 	pub item_id: u32,
-	pub data: Vec<u8>,
+	#[derivative(Debug = "ignore")]
+	pub data: BoundedVec<u8, MaxDataSize>,
 }
 
-#[derive(Debug, PartialEq, Encode, Decode)]
-pub struct NFTExtToggleWhiteList<E: Ext> {
+#[derive(Debug, PartialEq, Encode, Decode, MaxEncodedLen)]
+pub struct NFTExtToggleWhiteList<AccountId> {
 	pub collection_id: u32,
-	pub address: <E::T as SysConfig>::AccountId,
+	pub address: AccountId,
 	pub whitelisted: bool,
 }
 
@@ -82,6 +86,8 @@
 
 pub type NftWeightInfoOf<C> = <C as pallet_nft::Config>::WeightInfo;
 
+pub type AccountIdOf<C> = <C as SysConfig>::AccountId;
+
 impl<C: Config + pallet_contracts::Config> ChainExtension<C> for NFTExtension {
 	fn call<E: Ext>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal, DispatchError>
 	where
@@ -93,7 +99,7 @@
 		match func_id {
 			0 => {
 				let mut env = env.buf_in_buf_out();
-				let input: NFTExtTransfer<E> = env.read_as()?;
+				let input: NFTExtTransfer<AccountIdOf<C>> = env.read_as()?;
 				env.charge_weight(NftWeightInfoOf::<C>::transfer())?;
 
 				let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
@@ -106,13 +112,13 @@
 					input.amount,
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			1 => {
 				// Create Item
 				let mut env = env.buf_in_buf_out();
-				let input: NFTExtCreateItem<E> = env.read_as()?;
+				let input: NFTExtCreateItem<AccountIdOf<C>> = env.read_as()?;
 				env.charge_weight(NftWeightInfoOf::<C>::create_item(input.data.data_size()))?;
 
 				let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
@@ -124,13 +130,13 @@
 					input.data,
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			2 => {
 				// Create multiple items
 				let mut env = env.buf_in_buf_out();
-				let input: NFTExtCreateMultipleItems<E> = env.read_as()?;
+				let input: NFTExtCreateMultipleItems<AccountIdOf<C>> = env.read_as()?;
 				env.charge_weight(NftWeightInfoOf::<C>::create_item(
 					input.data.iter().map(|i| i.data_size()).sum(),
 				))?;
@@ -141,16 +147,16 @@
 					&C::CrossAccountId::from_sub(env.ext().address().clone()),
 					&collection,
 					&C::CrossAccountId::from_sub(input.owner),
-					input.data,
+					input.data.into_inner(),
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			3 => {
 				// Approve
 				let mut env = env.buf_in_buf_out();
-				let input: NFTExtApprove<E> = env.read_as()?;
+				let input: NFTExtApprove<AccountIdOf<C>> = env.read_as()?;
 				env.charge_weight(NftWeightInfoOf::<C>::approve())?;
 
 				let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
@@ -163,13 +169,13 @@
 					input.amount,
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			4 => {
 				// Transfer from
 				let mut env = env.buf_in_buf_out();
-				let input: NFTExtTransferFrom<E> = env.read_as()?;
+				let input: NFTExtTransferFrom<AccountIdOf<C>> = env.read_as()?;
 				env.charge_weight(NftWeightInfoOf::<C>::transfer_from())?;
 
 				let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
@@ -183,7 +189,7 @@
 					input.amount,
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			5 => {
@@ -198,16 +204,16 @@
 					&C::CrossAccountId::from_sub(env.ext().address().clone()),
 					&collection,
 					input.item_id,
-					input.data,
+					input.data.into_inner(),
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			6 => {
 				// Toggle whitelist
 				let mut env = env.buf_in_buf_out();
-				let input: NFTExtToggleWhiteList<E> = env.read_as()?;
+				let input: NFTExtToggleWhiteList<AccountIdOf<C>> = env.read_as()?;
 				env.charge_weight(NftWeightInfoOf::<C>::add_to_white_list())?;
 
 				let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
@@ -219,7 +225,7 @@
 					input.whitelisted,
 				)?;
 
-				pallet_nft::Module::<C>::submit_logs(collection)?;
+				collection.submit_logs()?;
 				Ok(RetVal::Converging(0))
 			}
 			_ => Err(DispatchError::Other("unknown chain_extension func_id")),
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -72,7 +72,6 @@
 use sp_runtime::{
 	traits::{Dispatchable},
 };
-// use pallet_contracts::chain_extension::UncheckedFrom;
 
 // pub use pallet_timestamp::Call as TimestampCall;
 pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
@@ -119,8 +118,6 @@
 
 /// Digest item type.
 pub type DigestItem = generic::DigestItem<Hash>;
-
-mod nft_weights;
 
 /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
 /// the specifics of the runtime. They can then be made to be agnostic over specific formats
@@ -253,6 +250,7 @@
 	type Currency = Balances;
 	type Event = Event;
 	type OnMethodCall = (
+		pallet_evm_migration::OnMethodCall<Self>,
 		pallet_nft::NftErcSupport<Self>,
 		pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
 	);
@@ -264,6 +262,10 @@
 	type FindAuthor = EthereumFindAuthor<Aura>;
 }
 
+impl pallet_evm_migration::Config for Runtime {
+	type WeightInfo = pallet_evm_migration::weights::SubstrateWeight<Self>;
+}
+
 pub struct EthereumFindAuthor<F>(core::marker::PhantomData<F>);
 impl<F: FindAuthor<u32>> FindAuthor<H160> for EthereumFindAuthor<F> {
 	fn find_author<'a, I>(digests: I) -> Option<H160>
@@ -419,7 +421,7 @@
 	type DepositPerStorageItem = DepositPerStorageItem;
 	type RentFraction = RentFraction;
 	type SurchargeReward = SurchargeReward;
-	type WeightPrice = pallet_transaction_payment::Module<Self>;
+	type WeightPrice = pallet_transaction_payment::Pallet<Self>;
 	type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
 	type ChainExtension = NFTExtension;
 	type DeletionQueueDepth = DeletionQueueDepth;
@@ -691,7 +693,7 @@
 /// Used for the pallet nft in `./nft.rs`
 impl pallet_nft::Config for Runtime {
 	type Event = Event;
-	type WeightInfo = nft_weights::WeightInfo;
+	type WeightInfo = pallet_nft::weights::SubstrateWeight<Self>;
 
 	type EvmBackwardsAddressMapping = pallet_nft::MapBackwardsAddressTruncated;
 	type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
@@ -820,6 +822,7 @@
 		EvmCoderSubstrate: pallet_evm_coder_substrate::{Pallet, Storage} = 150,
 		EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151,
 		EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152,
+		EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153,
 	}
 );
 
@@ -1181,6 +1184,7 @@
 			let mut batches = Vec::<BenchmarkBatch>::new();
 			let params = (&config, &whitelist);
 
+			add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);
 			add_benchmark!(params, batches, pallet_nft, Nft);
 			add_benchmark!(params, batches, pallet_inflation, Inflation);
 
deletedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ /dev/null
@@ -1,161 +0,0 @@
-//
-// 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;
-impl pallet_nft::WeightInfo for WeightInfo {
-	fn create_collection() -> Weight {
-		70_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn destroy_collection() -> Weight {
-		90_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn add_to_white_list() -> Weight {
-		30_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_from_white_list() -> Weight {
-		35_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_public_access_mode() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_mint_permission() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn change_collection_owner() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn add_collection_admin() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_admin() -> Weight {
-		50_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_collection_sponsor() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn confirm_sponsorship() -> Weight {
-		22_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_sponsor() -> Weight {
-		24_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn create_item(s: usize) -> Weight {
-		130_000_000_u64
-			.saturating_add(2135_u64.saturating_mul(s as Weight).saturating_mul(500_u64)) // 500 is temparary multiplier, fee for storage
-			.saturating_add(DbWeight::get().reads(10_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn burn_item() -> Weight {
-		170_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn transfer() -> Weight {
-		125_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn approve() -> Weight {
-		45_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn transfer_from() -> Weight {
-		150_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn set_offchain_schema() -> Weight {
-		33_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_const_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_meta_data() -> Weight {
-		17_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn enable_contract_sponsoring() -> Weight {
-		13_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_schema_version() -> Weight {
-		8_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_chain_limits() -> Weight {
-		1_300_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_contract_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_variable_meta_data_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn toggle_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn add_to_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn remove_from_contract_white_list() -> Weight {
-		3_200_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_collection_limits() -> Weight {
-		8_900_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-}
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -4,9 +4,9 @@
   "description": "Substrate Nft tests",
   "main": "",
   "devDependencies": {
-    "@polkadot/dev": "0.62.43",
-    "@polkadot/ts": "0.3.89",
-    "@polkadot/typegen": "5.0.1",
+    "@polkadot/dev": "0.62.60",
+    "@polkadot/ts": "0.4.4",
+    "@polkadot/typegen": "5.5.1",
     "@types/chai": "^4.2.17",
     "@types/chai-as-promised": "^7.1.3",
     "@types/mocha": "^8.2.2",
@@ -23,9 +23,9 @@
   "scripts": {
     "lint": "eslint --ext .ts,.js src/",
     "fix": "eslint --ext .ts,.js src/ --fix",
-    "test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts ./**/eth/**/*.test.ts",
-    "testEth": "mocha --timeout 9999999 -r ts-node/register ./**/eth/**/*.test.ts",
-    "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts",
+    "test": "mocha --timeout 9999999 -r ts-node/register './**/*.test.ts'",
+    "testEth": "mocha --timeout 9999999 -r ts-node/register './**/eth/**/*.test.ts'",
+    "load": "mocha --timeout 9999999 -r ts-node/register './**/*.load.ts'",
     "loadTransfer": "ts-node src/transfer.nload.ts",
     "testCollision": "mocha --timeout 9999999 -r ts-node/register ./src/collision-tests/*.test.ts",
     "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
@@ -66,8 +66,9 @@
   "license": "SEE LICENSE IN ../LICENSE",
   "homepage": "",
   "dependencies": {
-    "@polkadot/api": "5.0.1",
-    "@polkadot/api-contract": "5.0.1",
+    "@polkadot/api": "5.5.1",
+    "@polkadot/api-contract": "5.5.1",
+    "@polkadot/util-crypto": "^7.2.1",
     "bignumber.js": "^9.0.1",
     "chai-as-promised": "^7.1.1",
     "solc": "^0.8.6",
@@ -84,4 +85,4 @@
       "artifacts"
     ]
   }
-}
\ No newline at end of file
+}
modifiedtests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/addCollectionAdmin.test.ts
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -117,8 +117,7 @@
       ];
       const collectionId = await createCollectionExpectSuccess();
 
-      const chainLimit = await api.query.nft.chainLimit() as unknown as { CollectionAdminsLimit: BN };
-      const chainAdminLimit = chainLimit.CollectionAdminsLimit.toNumber();
+      const chainAdminLimit = (api.consts.nft.collectionAdminsLimit as any).toNumber();
       expect(chainAdminLimit).to.be.equal(5);
 
       for (let i = 0; i < chainAdminLimit; i++) {
modifiedtests/src/addToWhiteList.test.tsdiffbeforeafterboth
--- a/tests/src/addToWhiteList.test.ts
+++ b/tests/src/addToWhiteList.test.ts
@@ -17,6 +17,7 @@
   enableWhiteListExpectSuccess,
   normalizeAccountId,
   addCollectionAdminExpectSuccess,
+  addToWhiteListExpectFail,
 } from './util/helpers';
 
 chai.use(chaiAsPromised);
@@ -98,6 +99,11 @@
     });
   });
 
+  it('Negative. Add to the white list by regular user', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await addToWhiteListExpectFail(Bob, collectionId, Charlie.address);
+  });
+
   it('Execute the extrinsic with parameters: Collection ID and address to add to the white list', async () => {
     const collectionId = await createCollectionExpectSuccess();
     await addCollectionAdminExpectSuccess(Alice, collectionId, Bob);
modifiedtests/src/change-collection-owner.test.tsdiffbeforeafterboth
--- a/tests/src/change-collection-owner.test.ts
+++ b/tests/src/change-collection-owner.test.ts
@@ -15,6 +15,13 @@
   enableWhiteListExpectSuccess,
   setMintPermissionExpectSuccess,
   destroyCollectionExpectSuccess,
+  setCollectionSponsorExpectFailure,
+  confirmSponsorshipExpectFailure,
+  removeCollectionSponsorExpectFailure,
+  enableWhiteListExpectFail,
+  setMintPermissionExpectFailure,
+  destroyCollectionExpectFailure,
+  setPublicAccessModeExpectSuccess,
 } from './util/helpers';
 
 chai.use(chaiAsPromised);
@@ -53,7 +60,7 @@
       await submitTransactionAsync(alice, changeOwnerTx);
 
       const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);
-      await expect(submitTransactionAsync(alice, badChangeOwnerTx)).to.be.rejected;
+      await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;
 
       const collectionAfterOwnerChange: any = (await api.query.nft.collectionById(collectionId)).toJSON();
       expect(collectionAfterOwnerChange.Owner).to.be.deep.eq(bob.address);
@@ -97,11 +104,37 @@
       );
       await submitTransactionAsync(bob, tx1);
 
+      await setPublicAccessModeExpectSuccess(bob, collectionId, 'WhiteList');
       await enableWhiteListExpectSuccess(bob, collectionId);
       await setMintPermissionExpectSuccess(bob, collectionId, true);
       await destroyCollectionExpectSuccess(collectionId, '//Bob');
     });
   });
+
+  it('New collectionOwner has access to changeCollectionOwner', async () => {
+    await usingApi(async api => {
+      const collectionId = await createCollectionExpectSuccess();
+      const alice = privateKey('//Alice');
+      const bob = privateKey('//Bob');
+      const charlie = privateKey('//Charlie');
+  
+      const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();
+      expect(collection.Owner).to.be.deep.eq(alice.address);
+  
+      const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
+      await submitTransactionAsync(alice, changeOwnerTx);
+  
+      const collectionAfterOwnerChange: any = (await api.query.nft.collectionById(collectionId)).toJSON();
+      expect(collectionAfterOwnerChange.Owner).to.be.deep.eq(bob.address);
+
+      const changeOwnerTx2 = api.tx.nft.changeCollectionOwner(collectionId, charlie.address);
+      await submitTransactionAsync(bob, changeOwnerTx2);
+  
+      // ownership lost
+      const collectionAfterOwnerChange2: any = (await api.query.nft.collectionById(collectionId)).toJSON();
+      expect(collectionAfterOwnerChange2.Owner).to.be.deep.eq(charlie.address);
+    });
+  });
 });
 
 describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
@@ -169,14 +202,14 @@
       await submitTransactionAsync(alice, changeOwnerTx);
 
       const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);
-      await expect(submitTransactionAsync(alice, badChangeOwnerTx)).to.be.rejected;
+      await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;
 
       const collectionAfterOwnerChange: any = (await api.query.nft.collectionById(collectionId)).toJSON();
       expect(collectionAfterOwnerChange.Owner).to.be.deep.eq(bob.address);
 
-      await expect(setCollectionSponsorExpectSuccess(collectionId, charlie.address, '//Alice')).to.be.rejected;
-      await expect(confirmSponsorshipExpectSuccess(collectionId, '//Alice')).to.be.rejected;
-      await expect(removeCollectionSponsorExpectSuccess(collectionId, '//Alice')).to.be.rejected;
+      await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Alice');
+      await confirmSponsorshipExpectFailure(collectionId, '//Alice');
+      await removeCollectionSponsorExpectFailure(collectionId, '//Alice');
 
       const collectionLimits = {
         AccountTokenOwnershipLimit: 1,
@@ -190,11 +223,11 @@
         collectionId,
         collectionLimits,
       );
-      await expect(submitTransactionAsync(alice, tx1)).to.be.rejected;
+      await expect(submitTransactionExpectFailAsync(alice, tx1)).to.be.rejected;
 
-      await expect(enableWhiteListExpectSuccess(alice, collectionId)).to.be.rejected;
-      await expect(setMintPermissionExpectSuccess(alice, collectionId, true)).to.be.rejected;
-      await expect(destroyCollectionExpectSuccess(collectionId, '//Alice')).to.be.rejected;
+      await enableWhiteListExpectFail(alice, collectionId);
+      await setMintPermissionExpectFailure(alice, collectionId, true);
+      await destroyCollectionExpectFailure(collectionId, '//Alice');
     });
   });
 });
modifiedtests/src/collision-tests/adminLimitsOff.test.tsdiffbeforeafterboth
--- a/tests/src/collision-tests/adminLimitsOff.test.ts
+++ b/tests/src/collision-tests/adminLimitsOff.test.ts
@@ -34,8 +34,7 @@
     await usingApi(async (api) => {
       const collectionId = await createCollectionExpectSuccess();
 
-      const chainLimit = await api.query.nft.chainLimit() as unknown as { CollectionAdminsLimit: BN };
-      const chainAdminLimit = chainLimit.CollectionAdminsLimit.toNumber();
+      const chainAdminLimit = (api.consts.nft.collectionAdminsLimit as any).toNumber();
       expect(chainAdminLimit).to.be.equal(5);
 
       const changeAdminTx1 = api.tx.nft.addCollectionAdmin(collectionId, Eve.address);
addedtests/src/eth/api/ContractHelpers.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+interface Dummy {
+
+}
+
+interface ContractHelpers is Dummy {
+	// Selector: contractOwner(address) 5152b14c
+	function contractOwner(address contractAddress)
+		external
+		view
+		returns (address);
+
+	// Selector: sponsoringEnabled(address) 6027dc61
+	function sponsoringEnabled(address contractAddress)
+		external
+		view
+		returns (bool);
+
+	// Selector: toggleSponsoring(address,bool) fcac6d86
+	function toggleSponsoring(address contractAddress, bool enabled) external;
+
+	// Selector: setSponsoringRateLimit(address,uint32) 77b6c908
+	function setSponsoringRateLimit(address contractAddress, uint32 rateLimit)
+		external;
+
+	// Selector: allowed(address,address) 5c658165
+	function allowed(address contractAddress, address user)
+		external
+		view
+		returns (bool);
+
+	// Selector: allowlistEnabled(address) c772ef6c
+	function allowlistEnabled(address contractAddress)
+		external
+		view
+		returns (bool);
+
+	// Selector: toggleAllowlist(address,bool) 36de20f5
+	function toggleAllowlist(address contractAddress, bool enabled) external;
+
+	// Selector: toggleAllowed(address,address,bool) 4706cc1c
+	function toggleAllowed(
+		address contractAddress,
+		address user,
+		bool allowed
+	) external;
+}
addedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+interface Dummy {
+
+}
+
+// Inline
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+// Inline
+interface InlineNameSymbol is Dummy {
+	// Selector: name() 06fdde03
+	function name() external view returns (string memory);
+
+	// Selector: symbol() 95d89b41
+	function symbol() external view returns (string memory);
+}
+
+// Inline
+interface InlineTotalSupply is Dummy {
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() external view returns (uint256);
+}
+
+interface ERC165 is Dummy {
+	// Selector: supportsInterface(bytes4) 01ffc9a7
+	function supportsInterface(uint32 interfaceId) external view returns (bool);
+}
+
+interface ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
+	// Selector: decimals() 313ce567
+	function decimals() external view returns (uint8);
+
+	// Selector: balanceOf(address) 70a08231
+	function balanceOf(address owner) external view returns (uint256);
+
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 amount) external returns (bool);
+
+	// Selector: transferFrom(address,address,uint256) 23b872dd
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) external returns (bool);
+
+	// Selector: approve(address,uint256) 095ea7b3
+	function approve(address spender, uint256 amount) external returns (bool);
+
+	// Selector: allowance(address,address) dd62ed3e
+	function allowance(address owner, address spender)
+		external
+		view
+		returns (uint256);
+}
+
+interface UniqueFungible is Dummy, ERC165, ERC20 {}
addedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Anonymous struct
+struct Tuple0 {
+	uint256 field_0;
+	string field_1;
+}
+
+// Common stubs holder
+interface Dummy {
+
+}
+
+// Inline
+interface ERC721Events {
+	event Transfer(
+		address indexed from,
+		address indexed to,
+		uint256 indexed tokenId
+	);
+	event Approval(
+		address indexed owner,
+		address indexed approved,
+		uint256 indexed tokenId
+	);
+	event ApprovalForAll(
+		address indexed owner,
+		address indexed operator,
+		bool approved
+	);
+}
+
+// Inline
+interface ERC721MintableEvents {
+	event MintingFinished();
+}
+
+// Inline
+interface InlineNameSymbol is Dummy {
+	// Selector: name() 06fdde03
+	function name() external view returns (string memory);
+
+	// Selector: symbol() 95d89b41
+	function symbol() external view returns (string memory);
+}
+
+// Inline
+interface InlineTotalSupply is Dummy {
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() external view returns (uint256);
+}
+
+interface ERC165 is Dummy {
+	// Selector: supportsInterface(bytes4) 01ffc9a7
+	function supportsInterface(uint32 interfaceId) external view returns (bool);
+}
+
+interface ERC721 is Dummy, ERC165, ERC721Events {
+	// Selector: balanceOf(address) 70a08231
+	function balanceOf(address owner) external view returns (uint256);
+
+	// Selector: ownerOf(uint256) 6352211e
+	function ownerOf(uint256 tokenId) external view returns (address);
+
+	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
+	function safeTransferFromWithData(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) external;
+
+	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
+
+	// Selector: transferFrom(address,address,uint256) 23b872dd
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
+
+	// Selector: approve(address,uint256) 095ea7b3
+	function approve(address approved, uint256 tokenId) external;
+
+	// Selector: setApprovalForAll(address,bool) a22cb465
+	function setApprovalForAll(address operator, bool approved) external;
+
+	// Selector: getApproved(uint256) 081812fc
+	function getApproved(uint256 tokenId) external view returns (address);
+
+	// Selector: isApprovedForAll(address,address) e985e9c5
+	function isApprovedForAll(address owner, address operator)
+		external
+		view
+		returns (address);
+}
+
+interface ERC721Burnable is Dummy {
+	// Selector: burn(uint256) 42966c68
+	function burn(uint256 tokenId) external;
+}
+
+interface ERC721Enumerable is Dummy, InlineTotalSupply {
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) external view returns (uint256);
+
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		external
+		view
+		returns (uint256);
+}
+
+interface ERC721Metadata is Dummy, InlineNameSymbol {
+	// Selector: tokenURI(uint256) c87b56dd
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
+
+interface ERC721Mintable is Dummy, ERC721MintableEvents {
+	// Selector: mintingFinished() 05d2035b
+	function mintingFinished() external view returns (bool);
+
+	// Selector: mint(address,uint256) 40c10f19
+	function mint(address to, uint256 tokenId) external returns (bool);
+
+	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
+	function mintWithTokenURI(
+		address to,
+		uint256 tokenId,
+		string memory tokenUri
+	) external returns (bool);
+
+	// Selector: finishMinting() 7d64bcb4
+	function finishMinting() external returns (bool);
+}
+
+interface ERC721UniqueExtensions is Dummy {
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 tokenId) external;
+
+	// Selector: nextTokenId() 75794a3c
+	function nextTokenId() external view returns (uint256);
+
+	// Selector: setVariableMetadata(uint256,bytes) d4eac26d
+	function setVariableMetadata(uint256 tokenId, bytes memory data) external;
+
+	// Selector: getVariableMetadata(uint256) e6c5ce6f
+	function getVariableMetadata(uint256 tokenId)
+		external
+		view
+		returns (bytes memory);
+
+	// Selector: mintBulk(address,uint256[]) 44a9945e
+	function mintBulk(address to, uint256[] memory tokenIds)
+		external
+		returns (bool);
+
+	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
+	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+		external
+		returns (bool);
+}
+
+interface UniqueNFT is
+	Dummy,
+	ERC165,
+	ERC721,
+	ERC721Metadata,
+	ERC721Enumerable,
+	ERC721UniqueExtensions,
+	ERC721Mintable,
+	ERC721Burnable
+{}
addedtests/src/eth/base.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/base.test.ts
@@ -0,0 +1,22 @@
+
+import { createEthAccount, createEthAccountWithBalance, deployFlipper, ethBalanceViaSub, GAS_ARGS, itWeb3, recordEthFee } from './util/helpers';
+import { expect } from 'chai';
+import { UNIQUE } from '../util/helpers';
+
+describe('Contract calls', () => {
+  itWeb3('Call of simple contract fee is less than 0.2 UNQ', async ({ web3, api }) => {
+    const deployer = await createEthAccountWithBalance(api, web3);
+    const flipper = await deployFlipper(web3 as any, deployer);
+
+    const cost = await recordEthFee(api, deployer, () => flipper.methods.flip().send({from: deployer}));
+    expect(cost < BigInt(0.2 * Number(UNIQUE))).to.be.true;
+  });
+
+  itWeb3('Balance transfer fee is less than 0.2 UNQ', async ({ web3, api }) => {
+    const userA = await createEthAccountWithBalance(api, web3);
+    const userB = createEthAccount(web3);
+
+    const cost = await recordEthFee(api, userA, () => web3.eth.sendTransaction({ from: userA, to: userB, value: '1000000', ...GAS_ARGS }));
+    expect(cost - await ethBalanceViaSub(api, userB) < BigInt(0.2 * Number(UNIQUE))).to.be.true;
+  });
+});
\ No newline at end of file
addedtests/src/eth/crossTransfer.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/crossTransfer.test.ts
@@ -0,0 +1,88 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import privateKey from '../substrate/privateKey';
+import { createCollectionExpectSuccess, 
+  createFungibleItemExpectSuccess, 
+  transferExpectSuccess, 
+  transferFromExpectSuccess, 
+  createItemExpectSuccess } from '../util/helpers';
+import { collectionIdToAddress, 
+  createEthAccountWithBalance, 
+  subToEth,
+  GAS_ARGS, itWeb3 } from './util/helpers';
+import fungibleAbi from './fungibleAbi.json';
+import nonFungibleAbi from './nonFungibleAbi.json';
+
+describe('Token transfer between substrate address and EVM address. Fungible', () => {
+  itWeb3('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async () => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { substrate: alice.address });
+    await transferExpectSuccess(collection, 0, alice, {ethereum: subToEth(charlie.address)} , 200, 'Fungible');
+    await transferFromExpectSuccess(collection, 0, alice, {ethereum: subToEth(charlie.address)}, charlie, 50, 'Fungible');
+    await transferExpectSuccess(collection, 0, charlie, bob, 50, 'Fungible');
+  });
+
+  itWeb3('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const bobProxy = await createEthAccountWithBalance(api, web3);
+    const aliceProxy = await createEthAccountWithBalance(api, web3);
+
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, alice.address);
+    await transferExpectSuccess(collection, 0, alice, { ethereum: aliceProxy } , 200, 'Fungible');
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: aliceProxy, ...GAS_ARGS});
+
+    await contract.methods.transfer(bobProxy, 50).send({ from: aliceProxy });
+    await transferFromExpectSuccess(collection, 0, alice, {ethereum: bobProxy}, bob, 50, 'Fungible');
+    await transferExpectSuccess(collection, 0, bob, alice, 50, 'Fungible');
+  });
+});
+
+describe('Token transfer between substrate address and EVM address. NFT', () => {
+  itWeb3('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async () => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { substrate: alice.address });
+    await transferExpectSuccess(collection, tokenId, alice, { ethereum: subToEth(charlie.address) }, 1, 'NFT');
+    await transferFromExpectSuccess(collection, tokenId, alice, {ethereum: subToEth(charlie.address)}, charlie, 1, 'NFT');
+    await transferExpectSuccess(collection, tokenId, charlie, bob, 1, 'NFT');
+  });
+
+  itWeb3('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    const bobProxy = await createEthAccountWithBalance(api, web3);
+    const aliceProxy = await createEthAccountWithBalance(api, web3);
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { substrate: alice.address });
+    await transferExpectSuccess(collection, tokenId, alice, { ethereum: aliceProxy } , 1, 'NFT');
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: aliceProxy, ...GAS_ARGS});
+    await contract.methods.transfer(bobProxy, 1).send({ from: aliceProxy });
+    await transferFromExpectSuccess(collection, tokenId, alice, {ethereum: bobProxy}, bob, 1, 'NFT');
+    await transferExpectSuccess(collection, tokenId, bob, charlie, 1, 'NFT');
+  });
+});
\ No newline at end of file
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -4,8 +4,8 @@
 //
 
 import privateKey from '../substrate/privateKey';
-import { approveExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess } from '../util/helpers';
-import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEvents, subToEth, transferBalanceToEth } from './util/helpers';
+import { approveExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE } from '../util/helpers';
+import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth } from './util/helpers';
 import fungibleAbi from './fungibleAbi.json';
 import { expect } from 'chai';
 
@@ -192,6 +192,64 @@
   });
 });
 
+describe('Fungible: Fees', () => {
+  itWeb3('approve() call fee is less than 0.2UNQ', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+    const spender = createEthAccount(web3);
+
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(fungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+
+    const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({ from: owner }));
+    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+  });
+
+  itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: {type: 'Fungible', decimalPoints: 0},
+    });
+    const alice = privateKey('//Alice');
+  
+    const owner = await createEthAccountWithBalance(api, web3);
+    const spender = await createEthAccountWithBalance(api, web3);
+
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(fungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+
+    await contract.methods.approve(spender, 100).send({ from: owner });
+
+    const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({ from: spender }));
+    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+  });
+
+  itWeb3('transfer() call fee is less than 0.2UNQ', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+    const receiver = createEthAccount(web3);
+
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(fungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+
+    const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({ from: owner }));
+    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+  });
+});
+
 describe('Fungible: Substrate calls', () => {
   itWeb3('Events emitted for approve()', async ({ web3 }) => {
     const collection = await createCollectionExpectSuccess({
modifiedtests/src/eth/helpersSmoke.test.tsdiffbeforeafterboth
--- a/tests/src/eth/helpersSmoke.test.ts
+++ b/tests/src/eth/helpersSmoke.test.ts
@@ -1,26 +1,24 @@
 import { expect } from 'chai';
 import waitNewBlocks from '../substrate/wait-new-blocks';
-import { createEthAccountWithBalance, deployFlipper, itWeb3, usingWeb3Http, contractHelpers } from './util/helpers';
+import { createEthAccountWithBalance, deployFlipper, itWeb3, contractHelpers } from './util/helpers';
 
-itWeb3('Contract owner is recorded', async ({ api, web3 }) => {
-  await usingWeb3Http(async web3Http => {
-    const owner = await createEthAccountWithBalance(api, web3Http);
+describe('Helpers sanity check', () => {
+  itWeb3('Contract owner is recorded', async ({ api, web3 }) => {
+    const owner = await createEthAccountWithBalance(api, web3);
 
-    const flipper = await deployFlipper(web3Http, owner);
+    const flipper = await deployFlipper(web3, owner);
     await waitNewBlocks(api, 1);
 
     expect(await contractHelpers(web3, owner).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner);
   });
-});
 
-itWeb3('Flipper is working', async({api}) => {
-  await usingWeb3Http(async web3Http => {
-    const owner = await createEthAccountWithBalance(api, web3Http);
-    const flipper = await deployFlipper(web3Http, owner);
+  itWeb3('Flipper is working', async ({ api, web3 }) => {
+    const owner = await createEthAccountWithBalance(api, web3);
+    const flipper = await deployFlipper(web3, owner);
     await waitNewBlocks(api, 1);
 
     expect(await flipper.methods.getValue().call()).to.be.false;
-    await flipper.methods.flip().send({from: owner});
+    await flipper.methods.flip().send({ from: owner });
     await waitNewBlocks(api, 1);
     expect(await flipper.methods.getValue().call()).to.be.true;
   });
addedtests/src/eth/marketplace/MarketPlaceKSM.abidiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlaceKSM.abi
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_escrow","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asksbySeller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"balanceKSM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"escrowBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"offers","outputs":[{"internalType":"uint256","name":"idNFT","type":"uint256"},{"internalType":"uint256","name":"currencyCode","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"address","name":"idCollection","type":"address"},{"internalType":"address","name":"userAddr","type":"address"},{"internalType":"uint8","name":"flagActive","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"uint8","name":"_active","type":"uint8"}],"name":"setAsk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newEscrow","type":"address"}],"name":"setEscrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newEscrow","type":"address"}],"name":"setowner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedtests/src/eth/marketplace/MarketPlaceKSM.bindiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlaceKSM.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b50604051610e27380380610e2783398101604081905261002f9161005d565b600480546001600160a01b039092166001600160a01b0319928316179055600580549091163317905561008d565b60006020828403121561006f57600080fd5b81516001600160a01b038116811461008657600080fd5b9392505050565b610d8b8061009c6000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806399f2d4eb1161007157806399f2d4eb1461018e5780639df55f52146101a1578063c10c35461461010f578063cce7ec13146101b4578063db87db52146101c7578063e22d4f7d146101fa57600080fd5b80630ad58d2f146100ae57806310a5ff17146100d6578063592bd7051461010f5780638a72ea6a146101245780638dbdbe6d1461017b575b600080fd5b6100c16100bc366004610c4d565b610225565b60405190151581526020015b60405180910390f35b6101016100e4366004610bd8565b600160209081526000928352604080842090915290825290205481565b6040519081526020016100cd565b61012261011d366004610b97565b6102d8565b005b610137610132366004610c04565b610345565b6040805197885260208801969096529486019390935260608501919091526001600160a01b0390811660808501521660a083015260ff1660c082015260e0016100cd565b610122610189366004610c4d565b6103a4565b61010161019c366004610bd8565b610449565b6101226101af366004610c86565b61047a565b6101226101c2366004610bd8565b610984565b6101016101d5366004610c1d565b6001600160a01b03166000908152600160209081526040808320938352929052205490565b610101610208366004610bd8565b600260209081526000928352604080842090915290825290205481565b6004546000906001600160a01b031633146102795760405162461bcd60e51b815260206004820152600f60248201526e27b7363c9032b9b1b937bb9031b0b760891b60448201526064015b60405180910390fd5b6001600160a01b03821660009081526001602090815260408083208684529091529020546102a8908590610cfa565b6001600160a01b039290921660009081526001602081815260408084209684529590529390209190915550919050565b6005546001600160a01b031633146103235760405162461bcd60e51b815260206004820152600e60248201526d27b7363c9037bbb732b91031b0b760911b6044820152606401610270565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6000818154811061035557600080fd5b6000918252602090912060069091020180546001820154600283015460038401546004850154600590950154939550919390926001600160a01b0391821691811690600160a01b900460ff1687565b6004546001600160a01b031633146103f05760405162461bcd60e51b815260206004820152600f60248201526e27b7363c9032b9b1b937bb9031b0b760891b6044820152606401610270565b6001600160a01b038116600090815260016020908152604080832085845290915290205461041f908490610ce2565b6001600160a01b039091166000908152600160209081526040808320948352939052919091205550565b6003602052816000526040600020818154811061046557600080fd5b90600052602060002001600091509150505481565b6040516331a9108f60e11b81526004810183905233906001600160a01b03851690636352211e9060240160206040518083038186803b1580156104bc57600080fd5b505afa1580156104d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f49190610bbb565b6001600160a01b0316146105425760405162461bcd60e51b81526020600482015260156024820152742737ba103934b3b43a103a37b5b2b71037bbb732b960591b6044820152606401610270565b6001600160a01b0383166000908152600260209081526040808320858452909152812054905415806105ac575060006001600160a01b03166000828154811061058d5761058d610d27565b60009182526020909120600460069092020101546001600160a01b0316145b15610793576040805160e0810182528481526020810187815291810188815242606083019081526001600160a01b03808916608085019081523360a0860190815260ff808a1660c088019081526000805460018181018355828052995160069091027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56381019190915599517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5648b015596517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5658a015594517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56689015591517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e567880180549185166001600160a01b0319909216919091179055517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56890960180549351909116600160a01b026001600160a81b031990931695909116949094171790925590546107399190610cfa565b6001600160a01b038516600090815260026020908152604080832087845282528083209390935533825260039052908120905461077890600190610cfa565b81546001810183556000928352602090922090910155610916565b6040805160e0810182526001600160a01b03861660009081526002602090815283822087835290529182205482549192839290919081106107d6576107d6610d27565b60009182526020808320600690920290910154835282810189905260408084018b90524260608501526001600160a01b0389168352600282528083208884529091528120548154608090930192811061083157610831610d27565b60009182526020808320600460069093020191909101546001600160a01b039081168452338483015260ff871660409485015288168252600281528282208783529052908120548154811061088857610888610d27565b60009182526020918290208351600690920201908155908201516001820155604082015160028201556060820151600382015560808201516004820180546001600160a01b039283166001600160a01b031990911617905560a08301516005909201805460c09094015160ff16600160a01b026001600160a81b031990941692909116919091179190911790555b6040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b038516906323b872dd90606401600060405180830381600087803b15801561096457600080fd5b505af1158015610978573d6000803e3d6000fd5b50505050505050505050565b6001600160a01b0382166000908152600260209081526040808320848452909152812054815482919081106109bb576109bb610d27565b600091825260208083206040805160e0810182526006949094029091018054845260018082015485850190815260028301548685018190526003840154606088015260048401546001600160a01b03908116608089015260059094015493841660a0880152600160a01b90930460ff1660c08701523387529084528286209051865290925290922054909250610a519190610cfa565b33600090815260016020818152604080842086830180518652908352818520959095558581015160a08701516001600160a01b031685529282528084209451845293905291902054610aa39190610ce2565b60a08201516001600160a01b0390811660009081526001602090815260408083208287015184528252808320949094559186168152600282528281208582529091529081205481548291908110610afc57610afc610d27565b60009182526020909120600560069092020101805460ff92909216600160a01b0260ff60a01b199092169190911790556040516323b872dd60e01b8152306004820152336024820152604481018390526001600160a01b038416906323b872dd90606401600060405180830381600087803b158015610b7a57600080fd5b505af1158015610b8e573d6000803e3d6000fd5b50505050505050565b600060208284031215610ba957600080fd5b8135610bb481610d3d565b9392505050565b600060208284031215610bcd57600080fd5b8151610bb481610d3d565b60008060408385031215610beb57600080fd5b8235610bf681610d3d565b946020939093013593505050565b600060208284031215610c1657600080fd5b5035919050565b60008060408385031215610c3057600080fd5b823591506020830135610c4281610d3d565b809150509250929050565b600080600060608486031215610c6257600080fd5b83359250602084013591506040840135610c7b81610d3d565b809150509250925092565b600080600080600060a08688031215610c9e57600080fd5b85359450602086013593506040860135610cb781610d3d565b925060608601359150608086013560ff81168114610cd457600080fd5b809150509295509295909350565b60008219821115610cf557610cf5610d11565b500190565b600082821015610d0c57610d0c610d11565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0381168114610d5257600080fd5b5056fea2646970667358221220d098ae4e0cc7bdbb7de1061c03402a667836f903122c1c606ca2c65db1ab78e964736f6c63430008070033
\ No newline at end of file
addedtests/src/eth/marketplace/MarketPlaceKSM.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlaceKSM.sol
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier:  Apache License
+pragma solidity >=0.8.0;
+
+interface IERC721 {
+    event Transfer(
+        address indexed from,
+        address indexed to,
+        uint256 indexed tokenId
+    );
+    event Approval(
+        address indexed owner,
+        address indexed approved,
+        uint256 indexed tokenId
+    );
+
+    function ownerOf(uint256 tokenId) external view returns (address owner);
+
+    function transferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external;
+
+    function debug(string memory value) external;
+}
+
+interface IERC20 {
+    event Transfer(address indexed from, address indexed to, uint256 value);
+    event Approval(
+        address indexed owner,
+        address indexed spender,
+        uint256 value
+    );
+
+    function transfer(address recipient, uint256 amount)
+        external
+        returns (bool);
+
+    function transferFrom(
+        address sender,
+        address recipient,
+        uint256 amount
+    ) external returns (bool);
+}
+
+contract MarketPlaceKSM {
+    struct Offer {
+        uint256 idNFT;
+        uint256 currencyCode;
+        uint256 price;
+        uint256 time;
+        address idCollection;
+        address userAddr;
+        uint8 flagActive;
+    }
+    Offer[] public offers;
+
+    mapping(address => mapping(uint256 => uint256)) public balanceKSM; //  [userAddr] => [KSMs]
+    mapping(address => mapping(uint256 => uint256)) public asks; // [buyer][idCollection][idNFT] => idOffer
+
+    mapping(address => uint256[]) public asksbySeller; // [addressSeller] =>idOffer
+
+    address escrow;
+    address owner;
+
+    constructor(address _escrow) {
+        escrow = _escrow;
+        owner = msg.sender;
+    }
+
+    function setowner(address _newEscrow) public onlyOwner {
+        escrow = _newEscrow;
+    }
+
+    function setEscrow(address _newEscrow) public onlyOwner {
+        escrow = _newEscrow;
+    }
+
+    modifier onlyEscrow() {
+        require(msg.sender == escrow, "Only escrow can");
+        _;
+    }
+
+    modifier onlyOwner() {
+        require(msg.sender == owner, "Only owner can");
+        _;
+    }
+
+    /**
+     * Make bids (offers) to sell NFTs
+     */
+    function setAsk(
+        uint256 _price,
+        uint256 _currencyCode,
+        address _idCollection,
+        uint256 _idNFT,
+        uint8 _active
+    ) public {
+        require(
+            IERC721(_idCollection).ownerOf(_idNFT) == msg.sender,
+            "Not right token owner"
+        );
+        uint256 offerID = asks[_idCollection][_idNFT];
+        if (offers.length == 0 || offers[offerID].idCollection == address(0)) {
+            offers.push(
+                Offer(
+                    _idNFT,
+                    _currencyCode,
+                    _price,
+                    block.timestamp,
+                    _idCollection,
+                    msg.sender,
+                    _active
+                )
+            );
+            asks[_idCollection][_idNFT] = offers.length - 1;
+            asksbySeller[msg.sender].push(offers.length - 1);
+        }
+        //edit existing offer
+        else {
+            offers[asks[_idCollection][_idNFT]] = Offer(
+                offers[asks[_idCollection][_idNFT]].idNFT,
+                _currencyCode,
+                _price,
+                block.timestamp,
+                offers[asks[_idCollection][_idNFT]].idCollection,
+                msg.sender,
+                _active
+            );
+        }
+
+        IERC721(_idCollection).transferFrom(msg.sender, address(this), _idNFT);
+    }
+
+    function deposit(
+        uint256 _amount,
+        uint256 _currencyCode,
+        address _sender
+    ) public onlyEscrow {
+        balanceKSM[_sender][_currencyCode] =
+            balanceKSM[_sender][_currencyCode] +
+            _amount;
+    }
+
+    function buy(address _idCollection, uint256 _idNFT) public {
+        Offer memory offer = offers[asks[_idCollection][_idNFT]];
+        // 1. reduce balance
+        balanceKSM[msg.sender][offer.currencyCode] =
+            balanceKSM[msg.sender][offer.currencyCode] -
+            offer.price;
+        // 2. increase balance
+        balanceKSM[offer.userAddr][offer.currencyCode] =
+            balanceKSM[offer.userAddr][offer.currencyCode] +
+            offer.price;
+        // 3. close offer
+        offers[asks[_idCollection][_idNFT]].flagActive = 0;
+        // 4. transfer NFT to buyer
+        IERC721(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
+    }
+
+    function withdraw(
+        uint256 _amount,
+        uint256 _currencyCode,
+        address _sender
+    ) public onlyEscrow returns (bool) {
+        balanceKSM[_sender][_currencyCode] =
+            balanceKSM[_sender][_currencyCode] -
+            _amount;
+        return true;
+    }
+
+    function escrowBalance(uint256 _currencyCode, address _sender)
+        public
+        view
+        returns (uint256)
+    {
+        return balanceKSM[_sender][_currencyCode];
+    }
+}
addedtests/src/eth/marketplace/MarketPlaceUNQ.abidiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlaceUNQ.abi
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asksbySeller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"offers","outputs":[{"internalType":"uint256","name":"idNFT","type":"uint256"},{"internalType":"address","name":"currencyCode","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"address","name":"idCollection","type":"address"},{"internalType":"address","name":"userAddr","type":"address"},{"internalType":"uint8","name":"flagActive","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"uint8","name":"_active","type":"uint8"}],"name":"setAsk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setowner","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedtests/src/eth/marketplace/MarketPlaceUNQ.bindiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlaceUNQ.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b50600380546001600160a01b03191633179055610f2d806100326000396000f3fe6080604052600436106100705760003560e01c806399f2d4eb1161004e57806399f2d4eb14610120578063b3ffb7601461014e578063cce7ec1314610161578063e22d4f7d1461017457600080fd5b8063592bd705146100755780637b1d6001146100975780638a72ea6a146100b7575b600080fd5b34801561008157600080fd5b50610095610090366004610d2b565b6101ac565b005b3480156100a357600080fd5b506100956100b2366004610e1b565b61021e565b3480156100c357600080fd5b506100d76100d2366004610e02565b61073d565b604080519788526001600160a01b03968716602089015287019490945260608601929092528316608085015290911660a083015260ff1660c082015260e0015b60405180910390f35b34801561012c57600080fd5b5061014061013b366004610d6c565b6107a0565b604051908152602001610117565b61009561015c366004610d98565b6107d1565b61009561016f366004610d6c565b610b0b565b34801561018057600080fd5b5061014061018f366004610d6c565b600160209081526000928352604080842090915290825290205481565b6003546001600160a01b031633146101fc5760405162461bcd60e51b815260206004820152600e60248201526d27b7363c9037bbb732b91031b0b760911b60448201526064015b60405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6040516331a9108f60e11b81526004810183905233906001600160a01b03851690636352211e9060240160206040518083038186803b15801561026057600080fd5b505afa158015610274573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102989190610d4f565b6001600160a01b0316146102e65760405162461bcd60e51b81526020600482015260156024820152742737ba103934b3b43a103a37b5b2b71037bbb732b960591b60448201526064016101f3565b6001600160a01b038316600090815260016020908152604080832085845290915281205490541580610350575060006001600160a01b03166000828154811061033157610331610ec9565b60009182526020909120600460069092020101546001600160a01b0316145b15610542576040805160e0810182528481526001600160a01b03878116602083019081529282018981524260608401908152888316608085019081523360a0860190815260ff89811660c08801908152600080546001808201835582805299517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56360069092029182015599517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5648b0180546001600160a01b0319908116928b1692909217905596517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5658b015594517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5668a015592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5678901805490961690871617909455517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56890960180549151969094166001600160a81b031990911617600160a01b959092169490940217905590546104e99190610ea4565b6001600160a01b0385166000908152600160208181526040808420888552825280842094909455338352600290529181209054909161052791610ea4565b815460018101835560009283526020909220909101556106d3565b6040805160e0810182526001600160a01b038616600090815260016020908152838220878352905291822054825491928392909190811061058557610585610ec9565b6000918252602080832060069092029091015483526001600160a01b03808a168483015260408085018c9052426060860152908916835260018252808320888452909152812054815460809093019281106105e2576105e2610ec9565b60009182526020808320600460069093020191909101546001600160a01b039081168452338483015260ff871660409485015288168252600181528282208783529052908120548154811061063957610639610ec9565b60009182526020918290208351600690920201908155908201516001820180546001600160a01b039283166001600160a01b031991821617909155604084015160028401556060840151600384015560808401516004840180549184169190921617905560a08301516005909201805460c09094015160ff16600160a01b026001600160a81b031990941692909116919091179190911790555b6040516323b872dd60e01b81526001600160a01b038516906323b872dd9061070390339030908890600401610e80565b600060405180830381600087803b15801561071d57600080fd5b505af1158015610731573d6000803e3d6000fd5b50505050505050505050565b6000818154811061074d57600080fd5b60009182526020909120600690910201805460018201546002830154600384015460048501546005909501549395506001600160a01b03928316949193909291821691811690600160a01b900460ff1687565b600260205281600052604060002081815481106107bc57600080fd5b90600052602060002001600091509150505481565b6001600160a01b03841660009081526001602090815260408083208684529091528120548154829190811061080857610808610ec9565b60009182526020918290206040805160e0810182526006939093029091018054835260018101546001600160a01b03908116948401949094526002810154918301829052600381015460608401526004810154841660808401526005015492831660a0830152600160a01b90920460ff1660c08201529150821480156108a35750826001600160a01b031681602001516001600160a01b0316145b6109245760405162461bcd60e51b815260206004820152604660248201527f4e6f7420726967687420616d6f756e74206f722063757272656e63792073656e60448201527f742c206861766520746f20626520657175616c2063757272656e637920616e6460648201526520707269636560d01b608482015260a4016101f3565b80602001516001600160a01b03166323b872dd333084604001516040518463ffffffff1660e01b815260040161095c93929190610e80565b602060405180830381600087803b15801561097657600080fd5b505af115801561098a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ae9190610de0565b50602081015160a0820151604080840151905163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb90604401602060405180830381600087803b158015610a0957600080fd5b505af1158015610a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a419190610de0565b506001600160a01b038516600090815260016020908152604080832087845290915281205481548291908110610a7957610a79610ec9565b906000526020600020906006020160050160146101000a81548160ff021916908360ff160217905550846001600160a01b03166323b872dd3033876040518463ffffffff1660e01b8152600401610ad293929190610e80565b600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b505050505050505050565b6001600160a01b038216600090815260016020908152604080832084845290915281205481548291908110610b4257610b42610ec9565b60009182526020918290206040805160e0810182526006939093029091018054835260018101546001600160a01b03908116948401949094526002810154918301829052600381015460608401526004810154841660808401526005015492831660a0830152600160a01b90920460ff1660c082015291503414610c225760405162461bcd60e51b815260206004820152603160248201527f4e6f7420726967687420616d6f756e7420554e512073656e742c206861766520604482015270746f20626520657175616c20707269636560781b60648201526084016101f3565b8060a001516001600160a01b03166108fc82604001519081150290604051600060405180830381858888f19350505050158015610c63573d6000803e3d6000fd5b506001600160a01b038316600090815260016020908152604080832085845290915281205481548291908110610c9b57610c9b610ec9565b906000526020600020906006020160050160146101000a81548160ff021916908360ff160217905550826001600160a01b03166323b872dd3033856040518463ffffffff1660e01b8152600401610cf493929190610e80565b600060405180830381600087803b158015610d0e57600080fd5b505af1158015610d22573d6000803e3d6000fd5b50505050505050565b600060208284031215610d3d57600080fd5b8135610d4881610edf565b9392505050565b600060208284031215610d6157600080fd5b8151610d4881610edf565b60008060408385031215610d7f57600080fd5b8235610d8a81610edf565b946020939093013593505050565b60008060008060808587031215610dae57600080fd5b8435610db981610edf565b9350602085013592506040850135610dd081610edf565b9396929550929360600135925050565b600060208284031215610df257600080fd5b81518015158114610d4857600080fd5b600060208284031215610e1457600080fd5b5035919050565b600080600080600060a08688031215610e3357600080fd5b853594506020860135610e4581610edf565b93506040860135610e5581610edf565b925060608601359150608086013560ff81168114610e7257600080fd5b809150509295509295909350565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600082821015610ec457634e487b7160e01b600052601160045260246000fd5b500390565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0381168114610ef457600080fd5b5056fea2646970667358221220fbf33eb1e471c428b86ad6d948b8699fd08a127b923d5fe27ec1e1ec5f59627564736f6c63430008070033
\ No newline at end of file
addedtests/src/eth/marketplace/MarketPlaceUNQ.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlaceUNQ.sol
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier:  Apache License
+pragma solidity >=0.8.0;
+
+interface IERC721 {
+    event Transfer(
+        address indexed from,
+        address indexed to,
+        uint256 indexed tokenId
+    );
+    event Approval(
+        address indexed owner,
+        address indexed approved,
+        uint256 indexed tokenId
+    );
+
+    function ownerOf(uint256 tokenId) external view returns (address owner);
+
+    function transferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external;
+
+    function debug(string memory value) external;
+}
+
+interface IERC20 {
+    event Transfer(address indexed from, address indexed to, uint256 value);
+    event Approval(
+        address indexed owner,
+        address indexed spender,
+        uint256 value
+    );
+
+    function transfer(address recipient, uint256 amount)
+        external
+        returns (bool);
+
+    function transferFrom(
+        address sender,
+        address recipient,
+        uint256 amount
+    ) external returns (bool);
+}
+
+contract MarketPlaceUNQ {
+    struct Offer {
+        uint256 idNFT;
+        address currencyCode; //address of currency  token, = address(0) for UNQ
+        uint256 price;
+        uint256 time;
+        address idCollection;
+        address userAddr;
+        uint8 flagActive;
+    }
+    Offer[] public offers;
+
+    mapping(address => mapping(uint256 => uint256)) public asks; // [idCollection][idNFT] => idOffer
+
+    mapping(address => uint256[]) public asksbySeller; // [addressSeller] =>idOffer
+
+    address owner;
+
+    constructor() {
+        owner = msg.sender;
+    }
+
+    function setowner(address _newOwner) public onlyOwner {
+        owner = _newOwner;
+    }
+
+    modifier onlyOwner() {
+        require(msg.sender == owner, "Only owner can");
+        _;
+    }
+
+    /**
+     * Make bids (offers) to sell NFTs
+     */
+    function setAsk(
+        uint256 _price,
+        address _currencyCode,
+        address _idCollection,
+        uint256 _idNFT,
+        uint8 _active
+    ) public {
+        require(
+            IERC721(_idCollection).ownerOf(_idNFT) == msg.sender,
+            "Not right token owner"
+        );
+        uint256 offerID = asks[_idCollection][_idNFT];
+        if (offers.length == 0 || offers[offerID].idCollection == address(0)) {
+            offers.push(
+                Offer(
+                    _idNFT,
+                    _currencyCode,
+                    _price,
+                    block.timestamp,
+                    _idCollection,
+                    msg.sender,
+                    _active
+                )
+            );
+            asks[_idCollection][_idNFT] = offers.length - 1;
+            asksbySeller[msg.sender].push(offers.length - 1);
+        }
+        //edit existing offer
+        else {
+            offers[asks[_idCollection][_idNFT]] = Offer(
+                offers[asks[_idCollection][_idNFT]].idNFT,
+                _currencyCode,
+                _price,
+                block.timestamp,
+                offers[asks[_idCollection][_idNFT]].idCollection,
+                msg.sender,
+                _active
+            );
+        }
+
+        IERC721(_idCollection).transferFrom(msg.sender, address(this), _idNFT);
+    }
+
+    function buy(address _idCollection, uint256 _idNFT) public payable {
+        //buing for UNQ like as ethers
+        Offer memory offer = offers[asks[_idCollection][_idNFT]];
+        //1. check sent amount and send to seller
+        require(
+            msg.value == offer.price,
+            "Not right amount UNQ sent, have to be equal price"
+        );
+        payable(offer.userAddr).transfer(offer.price);
+        // 2. close offer
+        offers[asks[_idCollection][_idNFT]].flagActive = 0;
+        // 3. transfer NFT to buyer
+        IERC721(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
+    }
+
+    function buy(
+        address _idCollection,
+        uint256 _idNFT,
+        address _currencyCode,
+        uint256 _amount
+    ) public payable {
+        Offer memory offer = offers[asks[_idCollection][_idNFT]];
+        //1. check sent amount and transfer from buyer to seller
+        require(
+            offer.price == _amount && offer.currencyCode == _currencyCode,
+            "Not right amount or currency sent, have to be equal currency and price"
+        );
+        // !!! transfer have to be approved to marketplace!
+        IERC20(offer.currencyCode).transferFrom(
+            msg.sender,
+            address(this),
+            offer.price
+        );
+        //to not disclojure buyer's address
+        IERC20(offer.currencyCode).transfer(offer.userAddr, offer.price);
+        // 2. close offer
+        offers[asks[_idCollection][_idNFT]].flagActive = 0;
+        // 3. transfer NFT to buyer
+        IERC721(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
+    }
+}
addedtests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/marketplace/marketplace.test.ts
@@ -0,0 +1,198 @@
+import { readFile } from 'fs/promises';
+import { getBalanceSingle, transferBalanceExpectSuccess } from '../../substrate/get-balance';
+import privateKey from '../../substrate/privateKey';
+import { createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, queryNftOwner, transferExpectSuccess, transferFromExpectSuccess } from '../../util/helpers';
+import { collectionIdToAddress, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase } from '../util/helpers';
+import { evmToAddress } from '@polkadot/util-crypto';
+import nonFungibleAbi from '../nonFungibleAbi.json';
+import fungibleAbi from '../fungibleAbi.json';
+import waitNewBlocks from '../../substrate/wait-new-blocks';
+import { expect } from 'chai';
+
+const PRICE = 2000n;
+
+describe('Matcher contract usage', () => {
+  itWeb3('With UNQ', async ({api, web3}) => {
+    const matcherOwner = await createEthAccountWithBalance(api, web3);
+    const Matcher = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {
+      from: matcherOwner,
+      ...GAS_ARGS,
+    });
+    const matcher = await Matcher.deploy({ data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString() }).send({ from: matcherOwner });
+
+    const alice = privateKey('//Alice');
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), { from: matcherOwner });
+
+    const seller = privateKey('//Bob');
+  
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
+
+    // To transfer item to matcher it first needs to be transfered to EVM account of bob
+    await transferExpectSuccess(collectionId, tokenId, seller, {ethereum: subToEth(seller.address)});
+    // Fees will be paid from EVM account, so we should have some balance here
+    await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);
+    await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);
+    await waitNewBlocks(api, 1);
+
+    // Token is owned by seller initially
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(seller.address) });
+
+    // Ask
+    {
+      await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));
+      await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, '0x0000000000000000000000000000000000000000', evmCollection.options.address, tokenId, 1));
+    }
+      
+    // Token is transferred to matcher
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: matcher.options.address.toLowerCase() });
+
+    // Buy
+    {
+      const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);
+      // There is two functions named 'buy', so we should provide full signature
+      await executeEthTxOnSub(api, alice, matcher, m => m['buy(address,uint256)'](evmCollection.options.address, tokenId), { value: PRICE });
+      expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);
+    }
+
+    // Token is transferred to evm account of alice
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(alice.address) });
+
+  
+    // Transfer token to substrate side of alice
+    await transferFromExpectSuccess(collectionId, tokenId, alice, { ethereum: subToEth(alice.address) }, { substrate: alice.address });
+    
+    // Token is transferred to substrate account of alice, seller received funds
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ substrate: alice.address });
+  });
+
+  itWeb3('With custom ERC20', async ({api, web3}) => {
+    const matcherOwner = await createEthAccountWithBalance(api, web3);
+    const Matcher = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {
+      from: matcherOwner,
+      ...GAS_ARGS,
+    });
+    const matcher = await Matcher.deploy({ data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString() }).send({ from: matcherOwner });
+
+    const alice = privateKey('//Alice');
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), { from: matcherOwner });
+
+    const fungibleId = await createCollectionExpectSuccess({ mode: { type: 'Fungible', decimalPoints: 0 } });
+    const evmFungible = new web3.eth.Contract(fungibleAbi as any, collectionIdToAddress(fungibleId), { from: matcherOwner, ...GAS_ARGS });
+    await createFungibleItemExpectSuccess(alice, fungibleId, { Value: PRICE }, { ethereum: subToEth(alice.address) });
+
+    const seller = privateKey('//Bob');
+  
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
+
+    // To transfer item to matcher it first needs to be transfered to EVM account of bob
+    await transferExpectSuccess(collectionId, tokenId, seller, {ethereum: subToEth(seller.address)});
+    // Fees will be paid from EVM account, so we should have some balance here
+    await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);
+    await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);
+    await waitNewBlocks(api, 1);
+
+    // Token is owned by seller initially
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(seller.address) });
+
+    // Ask
+    {
+      await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));
+      await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, evmFungible.options.address, evmCollection.options.address, tokenId, 1));
+    }
+      
+    // Token is transferred to matcher
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: matcher.options.address.toLowerCase() });
+
+    // Buy
+    {
+      const sellerBalanceBeforePurchase = BigInt(await evmFungible.methods.balanceOf(subToEth(seller.address)).call());
+      const buyerBalanceBeforePurchase = BigInt(await evmFungible.methods.balanceOf(subToEth(alice.address)).call());
+
+      await executeEthTxOnSub(api, alice, evmFungible, m => m.approve(matcher.options.address, PRICE));
+      // There is two functions named 'buy', so we should provide full signature
+      await executeEthTxOnSub(api, alice, matcher, m =>
+        m['buy(address,uint256,address,uint256)'](evmCollection.options.address, tokenId, evmFungible.options.address, PRICE));
+
+      // Approved price is removed from buyer balance, and added to seller
+      expect(BigInt(await evmFungible.methods.balanceOf(subToEth(seller.address)).call()) - sellerBalanceBeforePurchase === PRICE);
+      expect(buyerBalanceBeforePurchase - BigInt(await evmFungible.methods.balanceOf(subToEth(alice.address)).call()) === PRICE);
+    }
+
+    // Token is transferred to evm account of alice
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(alice.address) });
+
+  
+    // Transfer token to substrate side of alice
+    await transferFromExpectSuccess(collectionId, tokenId, alice, { ethereum: subToEth(alice.address) }, { substrate: alice.address });
+    
+    // Token is transferred to substrate account of alice
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ substrate: alice.address });
+  });
+
+  itWeb3('With escrow', async ({ api, web3 }) => {
+    const matcherOwner = await createEthAccountWithBalance(api, web3);
+    const escrow = await createEthAccountWithBalance(api, web3);
+    const Matcher = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceKSM.abi`)).toString()), undefined, {
+      from: matcherOwner,
+      ...GAS_ARGS,
+    });
+    const matcher = await Matcher.deploy({ data: (await readFile(`${__dirname}/MarketPlaceKSM.bin`)).toString(), arguments: [escrow] }).send({ from: matcherOwner });
+    
+    const ksmToken = 11;
+
+    const alice = privateKey('//Alice');
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), { from: matcherOwner });
+
+    const seller = privateKey('//Bob');
+  
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
+
+    // To transfer item to matcher it first needs to be transfered to EVM account of bob
+    await transferExpectSuccess(collectionId, tokenId, seller, {ethereum: subToEth(seller.address)});
+    // Fees will be paid from EVM account, so we should have some balance here
+    await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);
+    await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);
+    await waitNewBlocks(api, 1);
+
+    // Token is owned by seller initially
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(seller.address) });
+
+    // Ask
+    {
+      await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));
+      await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, ksmToken, evmCollection.options.address, tokenId, 1));
+    }
+      
+    // Token is transferred to matcher
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: matcher.options.address.toLowerCase() });
+
+    // Give buyer KSM
+    await matcher.methods.deposit(PRICE, ksmToken, subToEth(alice.address)).send({ from: escrow });
+    await waitNewBlocks(api, 1);
+  
+    // Buy
+    {
+      expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal('0');
+      expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal(PRICE.toString());
+
+      await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId));
+      await waitNewBlocks(api, 1);
+
+      // Price is removed from buyer balance, and added to seller
+      expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal('0');
+      expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal(PRICE.toString());
+    }
+
+    // Token is transferred to evm account of alice
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(alice.address) });
+  
+    // Transfer token to substrate side of alice
+    await transferFromExpectSuccess(collectionId, tokenId, alice, { ethereum: subToEth(alice.address) }, { substrate: alice.address });
+    
+    // Token is transferred to substrate account of alice, seller received funds
+    expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ substrate: alice.address });
+  });
+});
addedtests/src/eth/migration.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/migration.test.ts
@@ -0,0 +1,82 @@
+import { expect } from 'chai';
+import privateKey from '../substrate/privateKey';
+import { submitTransactionAsync } from '../substrate/substrate-api';
+import { createEthAccountWithBalance, GAS_ARGS, itWeb3 } from './util/helpers';
+
+describe('EVM Migrations', () => {
+  itWeb3('Deploy contract saved state', async ({ web3, api }) => {
+    /*
+      contract StatefulContract {
+        uint counter;
+        mapping (uint => uint) kv;
+
+        function inc() public {
+          counter = counter + 1;
+        }
+        function counterValue() public view returns (uint) {
+          return counter;
+        }
+
+        function set(uint key, uint value) public {
+          kv[key] = value;
+        }
+
+        function get(uint key) public view returns (uint) {
+          return kv[key];
+        }
+      }
+    */
+    const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6';
+    const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033';
+    const DATA = [
+      // counter = 10
+      ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'],
+      // kv = {1: 1, 2: 2, 3: 3, 4: 4},
+      ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'],
+      ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'],
+      ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'],
+      ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'],
+    ];
+
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+
+    await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.begin(ADDRESS)));
+    await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.setData(ADDRESS, DATA)));
+    await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.finish(ADDRESS, CODE)));
+
+    const contract = new web3.eth.Contract([
+      {
+        inputs: [],
+        name: 'counterValue',
+        outputs: [{
+          internalType: 'uint256',
+          name: '',
+          type: 'uint256',
+        }],
+        stateMutability: 'view',
+        type: 'function',
+      },
+      {
+        inputs: [{
+          internalType: 'uint256',
+          name: 'key',
+          type: 'uint256',
+        }],
+        name: 'get',
+        outputs: [{
+          internalType: 'uint256',
+          name: '',
+          type: 'uint256',
+        }],
+        stateMutability: 'view',
+        type: 'function',
+      },
+    ], ADDRESS, { from: caller, ...GAS_ARGS });
+    
+    expect(await contract.methods.counterValue().call()).to.be.equal('10');
+    for (let i = 1; i <= 4; i++) {
+      expect(await contract.methods.get(i).call()).to.be.equal(i.toString());
+    }
+  });
+});
\ No newline at end of file
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -4,11 +4,12 @@
 //
 
 import privateKey from '../substrate/privateKey';
-import { approveExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess } from '../util/helpers';
-import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEvents, subToEth, transferBalanceToEth } from './util/helpers';
+import { approveExpectSuccess, burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, setVariableMetaDataExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE } from '../util/helpers';
+import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth } from './util/helpers';
 import nonFungibleAbi from './nonFungibleAbi.json';
 import { expect } from 'chai';
 import waitNewBlocks from '../substrate/wait-new-blocks';
+import { submitTransactionAsync } from '../substrate/substrate-api';
 
 describe('NFT: Information getting', () => {
   itWeb3('totalSupply', async ({ api, web3 }) => {
@@ -64,6 +65,141 @@
 });
 
 describe('NFT: Plain calls', () => {
+  itWeb3('Can perform mint()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const caller = await createEthAccountWithBalance(api, web3);
+    const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: caller });
+    await submitTransactionAsync(alice, changeAdminTx);
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintWithTokenURI(
+        receiver,
+        nextTokenId,
+        'Test URI',
+      ).send({from: caller});
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: nextTokenId,
+          },
+        },
+      ]);
+
+      await waitNewBlocks(api, 1);
+      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+    }
+  });
+  itWeb3('Can perform mintBulk()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const caller = await createEthAccountWithBalance(api, web3);
+    const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: caller });
+    await submitTransactionAsync(alice, changeAdminTx);
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintBulkWithTokenURI(
+        receiver,
+        [
+          [nextTokenId, 'Test URI 0'],
+          [+nextTokenId + 1, 'Test URI 1'],
+          [+nextTokenId + 2, 'Test URI 2'],
+        ],
+      ).send({ from: caller });
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: nextTokenId,
+          },
+        },
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: String(+nextTokenId + 1),
+          },
+        },
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: String(+nextTokenId + 2),
+          },
+        },
+      ]);
+
+      await waitNewBlocks(api, 1);
+      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
+      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
+      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
+    }
+  });
+
+  itWeb3('Can perform burn()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: {type: 'NFT'},
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+    
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+
+    {
+      const result = await contract.methods.burn(tokenId).send({ from: owner });
+      const events = normalizeEvents(result.events);
+      
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: owner,
+            to: '0x0000000000000000000000000000000000000000',
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+  });
+
   itWeb3('Can perform approve()', async ({ web3, api }) => {
     const collection = await createCollectionExpectSuccess({
       mode: { type: 'NFT' },
@@ -190,9 +326,156 @@
       expect(+balance).to.equal(1);
     }
   });
+
+  itWeb3('Can perform getVariableMetadata', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+
+    const item = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+    await setVariableMetaDataExpectSuccess(alice, collection, item, [1, 2, 3]);
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+    
+    expect(await contract.methods.getVariableMetadata(item).call()).to.be.equal('0x010203');
+  });
+
+  itWeb3('Can perform setVariableMetadata', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+
+    const item = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+    
+    expect(await contract.methods.setVariableMetadata(item, '0x010203').send({ from: owner }));
+    await waitNewBlocks(api, 1);
+    expect(await contract.methods.getVariableMetadata(item).call()).to.be.equal('0x010203');
+  });
+});
+
+describe('NFT: Fees', () => {
+  itWeb3('approve() call fee is less than 0.2UNQ', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+    const spender = createEthAccount(web3);
+
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+
+    const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, tokenId).send({ from: owner }));
+    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+  });
+
+  itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+  
+    const owner = await createEthAccountWithBalance(api, web3);
+    const spender = await createEthAccountWithBalance(api, web3);
+
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+
+    await contract.methods.approve(spender, tokenId).send({ from: owner });
+
+    const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({ from: spender }));
+    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+  });
+
+  itWeb3('transfer() call fee is less than 0.2UNQ', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const owner = await createEthAccountWithBalance(api, web3);
+    const receiver = createEthAccount(web3);
+
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, { from: owner, ...GAS_ARGS });
+
+    const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, tokenId).send({ from: owner }));
+    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+  });
 });
 
 describe('NFT: Substrate calls', () => {
+  itWeb3('Events emitted for mint()', async ({ web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+
+    let tokenId: number;
+    const events = await recordEvents(contract, async () => {
+      tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
+    });
+
+    expect(events).to.be.deep.equal([
+      {
+        address,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: subToEth(alice.address),
+          tokenId: tokenId!.toString(),
+        },
+      },
+    ]);
+  });
+
+  itWeb3('Events emitted for burn()', async ({ web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const address = collectionIdToAddress(collection);
+    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
+    const events = await recordEvents(contract, async () => {
+      await burnItemExpectSuccess(alice, collection, tokenId);
+    });
+
+    expect(events).to.be.deep.equal([
+      {
+        address,
+        event: 'Transfer',
+        args: {
+          from: subToEth(alice.address),
+          to: '0x0000000000000000000000000000000000000000',
+          tokenId: tokenId.toString(),
+        },
+      },
+    ]);
+  });
+
   itWeb3('Events emitted for approve()', async ({ web3 }) => {
     const collection = await createCollectionExpectSuccess({
       mode: { type: 'NFT' },
@@ -284,4 +567,4 @@
       },
     ]);
   });
-});
\ No newline at end of file
+});
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -51,6 +51,12 @@
     },
     {
         "anonymous": false,
+        "inputs": [],
+        "name": "MintingFinished",
+        "type": "event"
+    },
+    {
+        "anonymous": false,
         "inputs": [
             {
                 "indexed": true,
@@ -89,7 +95,7 @@
         ],
         "name": "approve",
         "outputs": [],
-        "stateMutability": "payable",
+        "stateMutability": "nonpayable",
         "type": "function"
     },
     {
@@ -119,6 +125,32 @@
                 "type": "uint256"
             }
         ],
+        "name": "burn",
+        "outputs": [],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [],
+        "name": "finishMinting",
+        "outputs": [
+            {
+                "internalType": "bool",
+                "name": "",
+                "type": "bool"
+            }
+        ],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
+                "internalType": "uint256",
+                "name": "tokenId",
+                "type": "uint256"
+            }
+        ],
         "name": "getApproved",
         "outputs": [
             {
@@ -133,6 +165,25 @@
     {
         "inputs": [
             {
+                "internalType": "uint256",
+                "name": "tokenId",
+                "type": "uint256"
+            }
+        ],
+        "name": "getVariableMetadata",
+        "outputs": [
+            {
+                "internalType": "bytes",
+                "name": "",
+                "type": "bytes"
+            }
+        ],
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
                 "internalType": "address",
                 "name": "owner",
                 "type": "address"
@@ -146,11 +197,137 @@
         "name": "isApprovedForAll",
         "outputs": [
             {
+                "internalType": "address",
+                "name": "",
+                "type": "address"
+            }
+        ],
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
+                "internalType": "address",
+                "name": "to",
+                "type": "address"
+            },
+            {
+                "internalType": "uint256",
+                "name": "tokenId",
+                "type": "uint256"
+            }
+        ],
+        "name": "mint",
+        "outputs": [
+            {
+                "internalType": "bool",
+                "name": "",
+                "type": "bool"
+            }
+        ],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
+                "internalType": "address",
+                "name": "to",
+                "type": "address"
+            },
+            {
+                "internalType": "uint256[]",
+                "name": "tokenIds",
+                "type": "uint256[]"
+            }
+        ],
+        "name": "mintBulk",
+        "outputs": [
+            {
+                "internalType": "bool",
+                "name": "",
+                "type": "bool"
+            }
+        ],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
+                "internalType": "address",
+                "name": "to",
+                "type": "address"
+            },
+            {
+                "components": [
+                    {
+                        "internalType": "uint256",
+                        "name": "field_0",
+                        "type": "uint256"
+                    },
+                    {
+                        "internalType": "string",
+                        "name": "field_1",
+                        "type": "string"
+                    }
+                ],
+                "internalType": "struct Tuple0[]",
+                "name": "tokens",
+                "type": "tuple[]"
+            }
+        ],
+        "name": "mintBulkWithTokenURI",
+        "outputs": [
+            {
+                "internalType": "bool",
+                "name": "",
+                "type": "bool"
+            }
+        ],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
+                "internalType": "address",
+                "name": "to",
+                "type": "address"
+            },
+            {
+                "internalType": "uint256",
+                "name": "tokenId",
+                "type": "uint256"
+            },
+            {
+                "internalType": "string",
+                "name": "tokenUri",
+                "type": "string"
+            }
+        ],
+        "name": "mintWithTokenURI",
+        "outputs": [
+            {
                 "internalType": "bool",
                 "name": "",
                 "type": "bool"
             }
         ],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [],
+        "name": "mintingFinished",
+        "outputs": [
+            {
+                "internalType": "bool",
+                "name": "",
+                "type": "bool"
+            }
+        ],
         "stateMutability": "view",
         "type": "function"
     },
@@ -160,7 +337,7 @@
         "outputs": [
             {
                 "internalType": "string",
-                "name": "res_name",
+                "name": "",
                 "type": "string"
             }
         ],
@@ -168,6 +345,19 @@
         "type": "function"
     },
     {
+        "inputs": [],
+        "name": "nextTokenId",
+        "outputs": [
+            {
+                "internalType": "uint256",
+                "name": "",
+                "type": "uint256"
+            }
+        ],
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
         "inputs": [
             {
                 "internalType": "uint256",
@@ -206,7 +396,7 @@
         ],
         "name": "safeTransferFrom",
         "outputs": [],
-        "stateMutability": "payable",
+        "stateMutability": "nonpayable",
         "type": "function"
     },
     {
@@ -232,9 +422,9 @@
                 "type": "bytes"
             }
         ],
-        "name": "safeTransferFrom",
+        "name": "safeTransferFromWithData",
         "outputs": [],
-        "stateMutability": "payable",
+        "stateMutability": "nonpayable",
         "type": "function"
     },
     {
@@ -258,9 +448,27 @@
     {
         "inputs": [
             {
-                "internalType": "bytes4",
-                "name": "interfaceID",
-                "type": "bytes4"
+                "internalType": "uint256",
+                "name": "tokenId",
+                "type": "uint256"
+            },
+            {
+                "internalType": "bytes",
+                "name": "data",
+                "type": "bytes"
+            }
+        ],
+        "name": "setVariableMetadata",
+        "outputs": [],
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "inputs": [
+            {
+                "internalType": "uint32",
+                "name": "interfaceId",
+                "type": "uint32"
             }
         ],
         "name": "supportsInterface",
@@ -271,7 +479,7 @@
                 "type": "bool"
             }
         ],
-        "stateMutability": "pure",
+        "stateMutability": "view",
         "type": "function"
     },
     {
@@ -280,7 +488,7 @@
         "outputs": [
             {
                 "internalType": "string",
-                "name": "res_symbol",
+                "name": "",
                 "type": "string"
             }
         ],
@@ -364,11 +572,6 @@
     },
     {
         "inputs": [
-            {
-                "internalType": "address",
-                "name": "from",
-                "type": "address"
-            },
             {
                 "internalType": "address",
                 "name": "to",
@@ -380,15 +583,20 @@
                 "type": "uint256"
             }
         ],
-        "name": "transferFrom",
+        "name": "transfer",
         "outputs": [],
-        "stateMutability": "payable",
+        "stateMutability": "nonpayable",
         "type": "function"
     },
     {
         "inputs": [
             {
                 "internalType": "address",
+                "name": "from",
+                "type": "address"
+            },
+            {
+                "internalType": "address",
                 "name": "to",
                 "type": "address"
             },
@@ -398,9 +606,9 @@
                 "type": "uint256"
             }
         ],
-        "name": "transfer",
+        "name": "transferFrom",
         "outputs": [],
-        "stateMutability": "payable",
+        "stateMutability": "nonpayable",
         "type": "function"
     }
-]
\ No newline at end of file
+]
addedtests/src/eth/payable.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/payable.test.ts
@@ -0,0 +1,98 @@
+import { expect } from 'chai';
+import privateKey from '../substrate/privateKey';
+import { submitTransactionAsync } from '../substrate/substrate-api';
+import waitNewBlocks from '../substrate/wait-new-blocks';
+import { createEthAccountWithBalance, deployCollector, GAS_ARGS, itWeb3, subToEth } from './util/helpers';
+import {evmToAddress} from '@polkadot/util-crypto';
+import { getGenericResult } from '../util/helpers';
+import { getBalanceSingle, transferBalanceExpectSuccess } from '../substrate/get-balance';
+
+describe('EVM payable contracts', ()=>{
+  itWeb3('Evm contract can receive wei from eth account', async ({api, web3}) => {
+    const deployer = await createEthAccountWithBalance(api, web3);
+    const contract = await deployCollector(web3, deployer);
+
+    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', ...GAS_ARGS});
+    await waitNewBlocks(api, 1);
+
+    expect(await contract.methods.getCollected().call()).to.be.equal('10000');
+  });
+
+  itWeb3('Evm contract can receive wei from substrate account', async ({api, web3}) => {
+    const deployer = await createEthAccountWithBalance(api, web3);
+    const contract = await deployCollector(web3, deployer);
+    const alice = privateKey('//Alice');
+
+    // Transaction fee/value will be payed from subToEth(sender) evm balance,
+    // which is backed by evmToAddress(subToEth(sender)) substrate balance
+    await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), '1000000000000');
+
+    {
+      const tx = api.tx.evm.call(
+        subToEth(alice.address),
+        contract.options.address,
+        contract.methods.giveMoney().encodeABI(),
+        '10000',
+        GAS_ARGS.gas,
+        GAS_ARGS.gasPrice,
+        null,
+      );
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+      expect(result.success).to.be.true;
+    }
+
+    expect(await contract.methods.getCollected().call()).to.be.equal('10000');
+  });
+
+  // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible
+  itWeb3('Wei sent directly to backing storage of evm contract balance is unaccounted', async({api, web3}) => {
+    const deployer = await createEthAccountWithBalance(api, web3);
+    const contract = await deployCollector(web3, deployer);
+    const alice = privateKey('//Alice');
+
+    await transferBalanceExpectSuccess(api, alice, evmToAddress(contract.options.address), '10000');
+  
+    expect(await contract.methods.getUnaccounted().call()).to.be.equal('10000');
+  });
+
+  itWeb3('Balance can be retrieved from evm contract', async({api, web3}) => {
+    const FEE_BALANCE = 10n ** 18n;
+    const CONTRACT_BALANCE = 10n ** 14n;
+
+    const deployer = await createEthAccountWithBalance(api, web3);
+    const contract = await deployCollector(web3, deployer);
+    const alice = privateKey('//Alice');
+
+    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), ...GAS_ARGS});
+    await waitNewBlocks(api, 1);
+
+    const receiver = privateKey(`//Receiver${Date.now()}`);
+
+    // First receive balance on eth balance of bob
+    {
+      const ethReceiver = subToEth(receiver.address);
+      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');
+      await contract.methods.withdraw(ethReceiver).send({from: deployer});
+      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());
+    }
+
+    // Some balance is required to pay fee for evm.withdraw call
+    await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());
+
+    // Withdraw balance from eth to substrate
+    {
+      const initialReceiverBalance = await getBalanceSingle(api, receiver.address);
+      const tx = api.tx.evm.withdraw(
+        subToEth(receiver.address),
+        CONTRACT_BALANCE.toString(),
+      );
+      const events = await submitTransactionAsync(receiver, tx);
+      const result = getGenericResult(events);
+      expect(result.success).to.be.true;
+      const finalReceiverBalance = await getBalanceSingle(api, receiver.address);
+
+      expect(finalReceiverBalance > initialReceiverBalance).to.be.true;
+    }
+  });
+});
\ No newline at end of file
addedtests/src/eth/proxy/UniqueFungibleProxy.abidiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/UniqueFungibleProxy.abi
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"interfaceId","type":"uint32"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedtests/src/eth/proxy/UniqueFungibleProxy.bindiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/UniqueFungibleProxy.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b5060405161098038038061098083398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6108ed806100936000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806370a082311161006657806370a082311461012757806395d89b411461013a578063a9059cbb14610142578063dd62ed3e14610155578063f4f4b5001461016857600080fd5b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100e457806323b872dd146100fa578063313ce5671461010d575b600080fd5b6100ab61017b565b6040516100b8919061083e565b60405180910390f35b6100d46100cf3660046106e3565b610200565b60405190151581526020016100b8565b6100ec61028f565b6040519081526020016100b8565b6100d46101083660046106a7565b610316565b6101156103ad565b60405160ff90911681526020016100b8565b6100ec610135366004610659565b610434565b6100ab6104b8565b6100d46101503660046106e3565b6104fc565b6100ec610163366004610674565b610536565b6100d46101763660046107f5565b6105bc565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde039260048082019391829003018186803b1580156101bf57600080fd5b505afa1580156101d3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101fb919081019061072f565b905090565b6000805460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b3906044015b602060405180830381600087803b15801561025057600080fd5b505af1158015610264573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061070d565b9392505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156102de57600080fd5b505afa1580156102f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb91906107dc565b600080546040516323b872dd60e01b81526001600160a01b038681166004830152858116602483015260448201859052909116906323b872dd90606401602060405180830381600087803b15801561036d57600080fd5b505af1158015610381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a5919061070d565b949350505050565b60008060009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103fc57600080fd5b505afa158015610410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb919061081b565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b15801561047a57600080fd5b505afa15801561048e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b291906107dc565b92915050565b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b419260048082019391829003018186803b1580156101bf57600080fd5b6000805460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb90604401610236565b60008054604051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529091169063dd62ed3e9060440160206040518083038186803b15801561058457600080fd5b505afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028891906107dc565b6000805460405162f4f4b560e81b815263ffffffff841660048201526001600160a01b039091169063f4f4b5009060240160206040518083038186803b15801561060557600080fd5b505afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b2919061070d565b80356001600160a01b038116811461065457600080fd5b919050565b60006020828403121561066b57600080fd5b6102888261063d565b6000806040838503121561068757600080fd5b6106908361063d565b915061069e6020840161063d565b90509250929050565b6000806000606084860312156106bc57600080fd5b6106c58461063d565b92506106d36020850161063d565b9150604084013590509250925092565b600080604083850312156106f657600080fd5b6106ff8361063d565b946020939093013593505050565b60006020828403121561071f57600080fd5b8151801515811461028857600080fd5b60006020828403121561074157600080fd5b815167ffffffffffffffff8082111561075957600080fd5b818401915084601f83011261076d57600080fd5b81518181111561077f5761077f6108a1565b604051601f8201601f19908116603f011681019083821181831017156107a7576107a76108a1565b816040528281528760208487010111156107c057600080fd5b6107d1836020830160208801610871565b979650505050505050565b6000602082840312156107ee57600080fd5b5051919050565b60006020828403121561080757600080fd5b813563ffffffff8116811461028857600080fd5b60006020828403121561082d57600080fd5b815160ff8116811461028857600080fd5b602081526000825180602084015261085d816040850160208701610871565b601f01601f19169190910160400192915050565b60005b8381101561088c578181015183820152602001610874565b8381111561089b576000848401525b50505050565b634e487b7160e01b600052604160045260246000fdfea2646970667358221220c05e4cb7ab439b86c0b6ac8a84eec6a230b592fa63d1ab2e3a7f1474c27338f364736f6c63430008070033
\ No newline at end of file
addedtests/src/eth/proxy/UniqueFungibleProxy.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/UniqueFungibleProxy.sol
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: OTHER
+
+pragma solidity >=0.8.0 <0.9.0;
+
+import "../api/UniqueFungible.sol";
+
+contract UniqueFungibleProxy is UniqueFungible {
+    UniqueFungible proxied;
+
+    constructor(address _proxied) UniqueFungible() {
+        proxied = UniqueFungible(_proxied);
+    }
+
+    function name() external view override returns (string memory) {
+        return proxied.name();
+    }
+
+    function symbol() external view override returns (string memory) {
+        return proxied.symbol();
+    }
+
+    function totalSupply() external view override returns (uint256) {
+        return proxied.totalSupply();
+    }
+
+    function supportsInterface(uint32 interfaceId)
+        external
+        view
+        override
+        returns (bool)
+    {
+        return proxied.supportsInterface(interfaceId);
+    }
+
+    function decimals() external view override returns (uint8) {
+        return proxied.decimals();
+    }
+
+    function balanceOf(address owner) external view override returns (uint256) {
+        return proxied.balanceOf(owner);
+    }
+
+    function transfer(address to, uint256 amount)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.transfer(to, amount);
+    }
+
+    function transferFrom(
+        address from,
+        address to,
+        uint256 amount
+    ) external override returns (bool) {
+        return proxied.transferFrom(from, to, amount);
+    }
+
+    function approve(address spender, uint256 amount)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.approve(spender, amount);
+    }
+
+    function allowance(address owner, address spender)
+        external
+        view
+        override
+        returns (uint256)
+    {
+        return proxied.allowance(owner, spender);
+    }
+}
addedtests/src/eth/proxy/UniqueNFTProxy.abidiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/UniqueNFTProxy.abi
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[],"name":"MintingFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"approved","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finishMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getVariableMetadata","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintBulk","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"field_0","type":"uint256"},{"internalType":"string","name":"field_1","type":"string"}],"internalType":"struct Tuple0[]","name":"tokens","type":"tuple[]"}],"name":"mintBulkWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"tokenUri","type":"string"}],"name":"mintWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintingFinished","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFromWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setVariableMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"interfaceId","type":"uint32"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedtests/src/eth/proxy/UniqueNFTProxy.bindiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/UniqueNFTProxy.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b5060405161168d38038061168d83398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6115fa806100936000396000f3fe608060405234801561001057600080fd5b50600436106101a95760003560e01c806350bb4e7f116100f9578063a22cb46511610097578063d4eac26d11610071578063d4eac26d1461036b578063e6c5ce6f1461037e578063e985e9c514610391578063f4f4b500146103a457600080fd5b8063a22cb46514610332578063a9059cbb14610345578063c87b56dd1461035857600080fd5b806370a08231116100d357806370a082311461030757806375794a3c1461031a5780637d64bcb41461032257806395d89b411461032a57600080fd5b806350bb4e7f146102ce57806360a11672146102e15780636352211e146102f457600080fd5b80632f745c591161016657806342842e0e1161014057806342842e0e1461028257806342966c681461029557806344a9945e146102a85780634f6ccce7146102bb57600080fd5b80632f745c5914610249578063365430061461025c57806340c10f191461026f57600080fd5b806305d2035b146101ae57806306fdde03146101cb578063081812fc146101e0578063095ea7b31461020b57806318160ddd1461022057806323b872dd14610236575b600080fd5b6101b66103b7565b60405190151581526020015b60405180910390f35b6101d3610443565b6040516101c2919061148a565b6101f36101ee366004611277565b6104c3565b6040516001600160a01b0390911681526020016101c2565b61021e61021936600461118c565b610547565b005b6102286105b2565b6040519081526020016101c2565b61021e610244366004610eff565b610639565b61022861025736600461118c565b6106ad565b6101b661026a366004610fac565b610739565b6101b661027d36600461118c565b6107be565b61021e610290366004610eff565b6107f8565b61021e6102a3366004611277565b610839565b6101b66102b63660046110b1565b61089a565b6102286102c9366004611277565b6108cd565b6101b66102dc3660046111b8565b61094b565b61021e6102ef366004610f40565b6109da565b6101f3610302366004611277565b610a49565b610228610315366004610e8c565b610a7b565b610228610aae565b6101b6610afd565b6101d3610b62565b61021e61034036600461115e565b610ba6565b61021e61035336600461118c565b610be0565b6101d3610366366004611277565b610c19565b61021e6103793660046112a9565b610c9b565b6101d361038c366004611277565b610ccd565b6101f361039f366004610ec6565b610cff565b6101b66103b23660046112f0565b610d85565b60008060009054906101000a90046001600160a01b03166001600160a01b03166305d2035b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561040657600080fd5b505afa15801561041a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043e9190611211565b905090565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde039260048082019391829003018186803b15801561048757600080fd5b505afa15801561049b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261043e919081019061122e565b6000805460405163020604bf60e21b8152600481018490526001600160a01b039091169063081812fc906024015b60206040518083038186803b15801561050957600080fd5b505afa15801561051d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105419190610ea9565b92915050565b60005460405163095ea7b360e01b81526001600160a01b038481166004830152602482018490529091169063095ea7b3906044015b600060405180830381600087803b15801561059657600080fd5b505af11580156105aa573d6000803e3d6000fd5b505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561060157600080fd5b505afa158015610615573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043e9190611290565b6000546040516323b872dd60e01b81526001600160a01b038581166004830152848116602483015260448201849052909116906323b872dd906064015b600060405180830381600087803b15801561069057600080fd5b505af11580156106a4573d6000803e3d6000fd5b50505050505050565b60008054604051632f745c5960e01b81526001600160a01b0385811660048301526024820185905290911690632f745c599060440160206040518083038186803b1580156106fa57600080fd5b505afa15801561070e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107329190611290565b9392505050565b60008054604051631b2a180360e11b81526001600160a01b039091169063365430069061076c908690869060040161137f565b602060405180830381600087803b15801561078657600080fd5b505af115801561079a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107329190611211565b600080546040516340c10f1960e01b81526001600160a01b03858116600483015260248201859052909116906340c10f199060440161076c565b600054604051632142170760e11b81526001600160a01b038581166004830152848116602483015260448201849052909116906342842e0e90606401610676565b600054604051630852cd8d60e31b8152600481018390526001600160a01b03909116906342966c6890602401600060405180830381600087803b15801561087f57600080fd5b505af1158015610893573d6000803e3d6000fd5b5050505050565b60008054604051632254ca2f60e11b81526001600160a01b03909116906344a9945e9061076c9086908690600401611404565b60008054604051634f6ccce760e01b8152600481018490526001600160a01b0390911690634f6ccce7906024015b60206040518083038186803b15801561091357600080fd5b505afa158015610927573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105419190611290565b600080546040516350bb4e7f60e01b81526001600160a01b03909116906350bb4e7f906109809087908790879060040161145a565b602060405180830381600087803b15801561099a57600080fd5b505af11580156109ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d29190611211565b949350505050565b6000546040516330508b3960e11b81526001600160a01b03909116906360a1167290610a10908790879087908790600401611342565b600060405180830381600087803b158015610a2a57600080fd5b505af1158015610a3e573d6000803e3d6000fd5b505050505b50505050565b600080546040516331a9108f60e11b8152600481018490526001600160a01b0390911690636352211e906024016104f1565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a08231906024016108fb565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375794a3c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561060157600080fd5b60008060009054906101000a90046001600160a01b03166001600160a01b0316637d64bcb46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610b4e57600080fd5b505af115801561041a573d6000803e3d6000fd5b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b419260048082019391829003018186803b15801561048757600080fd5b60005460405163a22cb46560e01b81526001600160a01b03848116600483015283151560248301529091169063a22cb4659060440161057c565b60005460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb9060440161057c565b60005460405163c87b56dd60e01b8152600481018390526060916001600160a01b03169063c87b56dd906024015b60006040518083038186803b158015610c5f57600080fd5b505afa158015610c73573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610541919081019061122e565b60005460405163d4eac26d60e01b81526001600160a01b039091169063d4eac26d9061057c908590859060040161149d565b60005460405163e6c5ce6f60e01b8152600481018390526060916001600160a01b03169063e6c5ce6f90602401610c47565b6000805460405163e985e9c560e01b81526001600160a01b03858116600483015284811660248301529091169063e985e9c59060440160206040518083038186803b158015610d4d57600080fd5b505afa158015610d61573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107329190610ea9565b6000805460405162f4f4b560e81b815263ffffffff841660048201526001600160a01b039091169063f4f4b5009060240160206040518083038186803b158015610dce57600080fd5b505afa158015610de2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105419190611211565b6000610e19610e1484611534565b6114df565b9050828152838383011115610e2d57600080fd5b61073283602083018461155c565b600082601f830112610e4c57600080fd5b8135610e5a610e1482611534565b818152846020838601011115610e6f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215610e9e57600080fd5b81356107328161159e565b600060208284031215610ebb57600080fd5b81516107328161159e565b60008060408385031215610ed957600080fd5b8235610ee48161159e565b91506020830135610ef48161159e565b809150509250929050565b600080600060608486031215610f1457600080fd5b8335610f1f8161159e565b92506020840135610f2f8161159e565b929592945050506040919091013590565b60008060008060808587031215610f5657600080fd5b8435610f618161159e565b93506020850135610f718161159e565b925060408501359150606085013567ffffffffffffffff811115610f9457600080fd5b610fa087828801610e3b565b91505092959194509250565b60008060408385031215610fbf57600080fd5b8235610fca8161159e565b915060208381013567ffffffffffffffff80821115610fe857600080fd5b818601915086601f830112610ffc57600080fd5b813561100a610e1482611510565b8082825285820191508585018a878560051b880101111561102a57600080fd5b60005b848110156110a05781358681111561104457600080fd5b87016040818e03601f1901121561105a57600080fd5b6110626114b6565b89820135815260408201358881111561107a57600080fd5b6110888f8c83860101610e3b565b828c015250855250928701929087019060010161102d565b50979a909950975050505050505050565b600080604083850312156110c457600080fd5b82356110cf8161159e565b915060208381013567ffffffffffffffff8111156110ec57600080fd5b8401601f810186136110fd57600080fd5b803561110b610e1482611510565b80828252848201915084840189868560051b870101111561112b57600080fd5b600094505b8385101561114e578035835260019490940193918501918501611130565b5080955050505050509250929050565b6000806040838503121561117157600080fd5b823561117c8161159e565b91506020830135610ef4816115b6565b6000806040838503121561119f57600080fd5b82356111aa8161159e565b946020939093013593505050565b6000806000606084860312156111cd57600080fd5b83356111d88161159e565b925060208401359150604084013567ffffffffffffffff8111156111fb57600080fd5b61120786828701610e3b565b9150509250925092565b60006020828403121561122357600080fd5b8151610732816115b6565b60006020828403121561124057600080fd5b815167ffffffffffffffff81111561125757600080fd5b8201601f8101841361126857600080fd5b6109d284825160208401610e06565b60006020828403121561128957600080fd5b5035919050565b6000602082840312156112a257600080fd5b5051919050565b600080604083850312156112bc57600080fd5b82359150602083013567ffffffffffffffff8111156112da57600080fd5b6112e685828601610e3b565b9150509250929050565b60006020828403121561130257600080fd5b813563ffffffff8116811461073257600080fd5b6000815180845261132e81602086016020860161155c565b601f01601f19169290920160200192915050565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061137590830184611316565b9695505050505050565b6001600160a01b0383168152604060208083018290528351828401819052600092916060600583901b860181019290860190878301865b828110156113f557888603605f190184528151805187528501518587018890526113e288880182611316565b96505092840192908401906001016113b6565b50939998505050505050505050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561144d57845183529383019391830191600101611431565b5090979650505050505050565b60018060a01b03841681528260208201526060604082015260006114816060830184611316565b95945050505050565b6020815260006107326020830184611316565b8281526040602082015260006109d26040830184611316565b6040805190810167ffffffffffffffff811182821017156114d9576114d9611588565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561150857611508611588565b604052919050565b600067ffffffffffffffff82111561152a5761152a611588565b5060051b60200190565b600067ffffffffffffffff82111561154e5761154e611588565b50601f01601f191660200190565b60005b8381101561157757818101518382015260200161155f565b83811115610a435750506000910152565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146115b357600080fd5b50565b80151581146115b357600080fdfea2646970667358221220669a75a3efcdc6b60606caa5c7e41cab1d727e89a03fd231d1cda27f4de159a064736f6c63430008070033
\ No newline at end of file
addedtests/src/eth/proxy/UniqueNFTProxy.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/UniqueNFTProxy.sol
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: OTHER
+
+pragma solidity >=0.8.0 <0.9.0;
+
+import "../api/UniqueNFT.sol";
+
+contract UniqueNFTProxy is UniqueNFT {
+    UniqueNFT proxied;
+
+    constructor(address _proxied) UniqueNFT() {
+        proxied = UniqueNFT(_proxied);
+    }
+
+    function name() external view override returns (string memory) {
+        return proxied.name();
+    }
+
+    function symbol() external view override returns (string memory) {
+        return proxied.symbol();
+    }
+
+    function totalSupply() external view override returns (uint256) {
+        return proxied.totalSupply();
+    }
+
+    function balanceOf(address owner) external view override returns (uint256) {
+        return proxied.balanceOf(owner);
+    }
+
+    function ownerOf(uint256 tokenId) external view override returns (address) {
+        return proxied.ownerOf(tokenId);
+    }
+
+    function safeTransferFromWithData(
+        address from,
+        address to,
+        uint256 tokenId,
+        bytes memory data
+    ) external override {
+        return proxied.safeTransferFromWithData(from, to, tokenId, data);
+    }
+
+    function safeTransferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external override {
+        return proxied.safeTransferFrom(from, to, tokenId);
+    }
+
+    function transferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external override {
+        return proxied.transferFrom(from, to, tokenId);
+    }
+
+    function approve(address approved, uint256 tokenId) external override {
+        return proxied.approve(approved, tokenId);
+    }
+
+    function setApprovalForAll(address operator, bool approved)
+        external
+        override
+    {
+        return proxied.setApprovalForAll(operator, approved);
+    }
+
+    function getApproved(uint256 tokenId)
+        external
+        view
+        override
+        returns (address)
+    {
+        return proxied.getApproved(tokenId);
+    }
+
+    function isApprovedForAll(address owner, address operator)
+        external
+        view
+        override
+        returns (address)
+    {
+        return proxied.isApprovedForAll(owner, operator);
+    }
+
+    function burn(uint256 tokenId) external override {
+        return proxied.burn(tokenId);
+    }
+
+    function tokenByIndex(uint256 index)
+        external
+        view
+        override
+        returns (uint256)
+    {
+        return proxied.tokenByIndex(index);
+    }
+
+    function tokenOfOwnerByIndex(address owner, uint256 index)
+        external
+        view
+        override
+        returns (uint256)
+    {
+        return proxied.tokenOfOwnerByIndex(owner, index);
+    }
+
+    function tokenURI(uint256 tokenId)
+        external
+        view
+        override
+        returns (string memory)
+    {
+        return proxied.tokenURI(tokenId);
+    }
+
+    function mintingFinished() external view override returns (bool) {
+        return proxied.mintingFinished();
+    }
+
+    function mint(address to, uint256 tokenId)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.mint(to, tokenId);
+    }
+
+    function mintWithTokenURI(
+        address to,
+        uint256 tokenId,
+        string memory tokenUri
+    ) external override returns (bool) {
+        return proxied.mintWithTokenURI(to, tokenId, tokenUri);
+    }
+
+    function finishMinting() external override returns (bool) {
+        return proxied.finishMinting();
+    }
+
+    function transfer(address to, uint256 tokenId) external override {
+        return proxied.transfer(to, tokenId);
+    }
+
+    function nextTokenId() external view override returns (uint256) {
+        return proxied.nextTokenId();
+    }
+
+    function supportsInterface(uint32 interfaceId)
+        external
+        view
+        override
+        returns (bool)
+    {
+        return proxied.supportsInterface(interfaceId);
+    }
+
+    function setVariableMetadata(uint256 tokenId, bytes memory data)
+        external
+        override
+    {
+        return proxied.setVariableMetadata(tokenId, data);
+    }
+
+    function getVariableMetadata(uint256 tokenId)
+        external
+        view
+        override
+        returns (bytes memory)
+    {
+        return proxied.getVariableMetadata(tokenId);
+    }
+
+    function mintBulk(address to, uint256[] memory tokenIds)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.mintBulk(to, tokenIds);
+    }
+
+    function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.mintBulkWithTokenURI(to, tokens);
+    }
+}
addedtests/src/eth/proxy/fungibleProxy.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/fungibleProxy.test.ts
@@ -0,0 +1,194 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import privateKey from '../../substrate/privateKey';
+import { createCollectionExpectSuccess, createFungibleItemExpectSuccess } from '../../util/helpers';
+import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents } from '../util/helpers';
+import fungibleAbi from '../fungibleAbi.json';
+import { expect } from 'chai';
+import { ApiPromise } from '@polkadot/api';
+import Web3 from 'web3';
+import { readFile } from 'fs/promises';
+
+async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any) {
+  // Proxy owner has no special privilegies, we don't need to reuse them
+  const owner = await createEthAccountWithBalance(api, web3);
+  const Proxy = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/UniqueFungibleProxy.abi`)).toString()), undefined, {
+    from: owner,
+    ...GAS_ARGS,
+  });
+  const proxy = await Proxy.deploy({ data: (await readFile(`${__dirname}/UniqueFungibleProxy.bin`)).toString(), arguments: [wrapped.options.address] }).send({ from: owner });
+  return proxy;
+}
+
+describe('Fungible (Via EVM proxy): Information getting', () => {
+  itWeb3('totalSupply', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+  
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { substrate: alice.address });
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const totalSupply = await contract.methods.totalSupply().call();
+
+    // FIXME: always equals to 0, because this method is not implemented
+    expect(totalSupply).to.equal('0');
+  });
+
+  itWeb3('balanceOf', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: caller });
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const balance = await contract.methods.balanceOf(caller).call();
+
+    expect(balance).to.equal('200');
+  });
+});
+
+describe('Fungible (Via EVM proxy): Plain calls', () => {
+  itWeb3('Can perform approve()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const spender = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: contract.options.address });
+
+    {
+      const result = await contract.methods.approve(spender, 100).send({from: caller});
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Approval',
+          args: {
+            owner: contract.options.address,
+            spender,
+            value: '100',
+          },
+        },
+      ]);
+    }
+
+    {
+      const allowance = await contract.methods.allowance(contract.options.address, spender).call();
+      expect(+allowance).to.equal(100);
+    }
+  });
+
+  itWeb3('Can perform transferFrom()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const owner = await createEthAccountWithBalance(api, web3);
+
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: owner });
+
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const evmCollection = new web3.eth.Contract(fungibleAbi as any, address, { from: caller, ...GAS_ARGS });
+    const contract = await proxyWrap(api, web3, evmCollection);
+
+    await evmCollection.methods.approve(contract.options.address, 100).send({from: owner});
+
+    {
+      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: caller});
+      const events = normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: owner,
+            to: receiver,
+            value: '49',
+          },
+        },
+        {
+          address,
+          event: 'Approval',
+          args: {
+            owner,
+            spender: contract.options.address,
+            value: '51',
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(49);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(owner).call();
+      expect(+balance).to.equal(151);
+    }
+  });
+
+  itWeb3('Can perform transfer()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      name: 'token name',
+      mode: { type: 'Fungible', decimalPoints: 0 },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const receiver = await createEthAccountWithBalance(api, web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    await createFungibleItemExpectSuccess(alice, collection, { Value: 200n }, { ethereum: contract.options.address });
+
+    {
+      const result = await contract.methods.transfer(receiver, 50).send({ from: caller});
+      const events = normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: contract.options.address,
+            to: receiver,
+            value: '50',
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(contract.options.address).call();
+      expect(+balance).to.equal(150);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(50);
+    }
+  });
+});
addedtests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/proxy/nonFungibleProxy.test.ts
@@ -0,0 +1,362 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import privateKey from '../../substrate/privateKey';
+import { createCollectionExpectSuccess, createItemExpectSuccess, setVariableMetaDataExpectSuccess } from '../../util/helpers';
+import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents } from '../util/helpers';
+import nonFungibleAbi from '../nonFungibleAbi.json';
+import { expect } from 'chai';
+import waitNewBlocks from '../../substrate/wait-new-blocks';
+import { submitTransactionAsync } from '../../substrate/substrate-api';
+import Web3 from 'web3';
+import { readFile } from 'fs/promises';
+import { ApiPromise } from '@polkadot/api';
+
+async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any) {
+  // Proxy owner has no special privilegies, we don't need to reuse them
+  const owner = await createEthAccountWithBalance(api, web3);
+  const Proxy = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/UniqueNFTProxy.abi`)).toString()), undefined, {
+    from: owner,
+    ...GAS_ARGS,
+  });
+  const proxy = await Proxy.deploy({ data: (await readFile(`${__dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address] }).send({ from: owner });
+  return proxy;
+}
+
+describe('NFT (Via EVM proxy): Information getting', () => {
+  itWeb3('totalSupply', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+
+    await createItemExpectSuccess(alice, collection, 'NFT', { substrate: alice.address });
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const totalSupply = await contract.methods.totalSupply().call();
+
+    // FIXME: always equals to 0, because this method is not implemented
+    expect(totalSupply).to.equal('0');
+  });
+
+  itWeb3('balanceOf', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const caller = await createEthAccountWithBalance(api, web3);
+    await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: caller });
+    await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: caller });
+    await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: caller });
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const balance = await contract.methods.balanceOf(caller).call();
+
+    expect(balance).to.equal('3');
+  });
+
+  itWeb3('ownerOf', async ({ api, web3 }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const caller = await createEthAccountWithBalance(api, web3);
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: caller });
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const owner = await contract.methods.ownerOf(tokenId).call();
+
+    expect(owner).to.equal(caller);
+  });
+});
+
+describe('NFT (Via EVM proxy): Plain calls', () => {
+  itWeb3('Can perform mint()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, { from: caller, ...GAS_ARGS }));
+
+    const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: contract.options.address });
+    await submitTransactionAsync(alice, changeAdminTx);
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintWithTokenURI(
+        receiver,
+        nextTokenId,
+        'Test URI',
+      ).send({ from: caller });
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: nextTokenId,
+          },
+        },
+      ]);
+
+      await waitNewBlocks(api, 1);
+      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+    }
+  });
+  itWeb3('Can perform mintBulk()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+
+    const caller = await createEthAccountWithBalance(api, web3);
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, { from: caller, ...GAS_ARGS }));
+    const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: contract.options.address });
+    await submitTransactionAsync(alice, changeAdminTx);
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintBulkWithTokenURI(
+        receiver,
+        [
+          [nextTokenId, 'Test URI 0'],
+          [+nextTokenId + 1, 'Test URI 1'],
+          [+nextTokenId + 2, 'Test URI 2'],
+        ],
+      ).send({ from: caller });
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: nextTokenId,
+          },
+        },
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: String(+nextTokenId + 1),
+          },
+        },
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: String(+nextTokenId + 2),
+          },
+        },
+      ]);
+
+      await waitNewBlocks(api, 1);
+      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
+      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
+      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
+    }
+  });
+
+  itWeb3('Can perform burn()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: {type: 'NFT'},
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: contract.options.address });
+
+    const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: contract.options.address });
+    await submitTransactionAsync(alice, changeAdminTx);
+
+    {
+      const result = await contract.methods.burn(tokenId).send({ from: caller });
+      const events = normalizeEvents(result.events);
+      
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: contract.options.address,
+            to: '0x0000000000000000000000000000000000000000',
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+  });
+
+  itWeb3('Can perform approve()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const spender = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address));
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: contract.options.address });
+
+    {
+      const result = await contract.methods.approve(spender, tokenId).send({ from: caller, gas: '0x1000000', gasPrice: '0x01' });
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Approval',
+          args: {
+            owner: contract.options.address,
+            approved: spender,
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+  });
+
+  itWeb3('Can perform transferFrom()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const owner = await createEthAccountWithBalance(api, web3);
+
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, address, { from: caller, ...GAS_ARGS });
+    const contract = await proxyWrap(api, web3, evmCollection);
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
+
+    await evmCollection.methods.approve(contract.options.address, tokenId).send({ from: owner });
+
+    {
+      const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({ from: caller });
+      const events = normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: owner,
+            to: receiver,
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(contract.options.address).call();
+      expect(+balance).to.equal(0);
+    }
+  });
+
+  itWeb3('Can perform transfer()', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+    const receiver = createEthAccount(web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: contract.options.address });
+
+    {
+      const result = await contract.methods.transfer(receiver, tokenId).send({ from: caller });
+      await waitNewBlocks(api, 1);
+      const events = normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: contract.options.address,
+            to: receiver,
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(contract.options.address).call();
+      expect(+balance).to.equal(0);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+  });
+
+  itWeb3('Can perform getVariableMetadata', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, { from: caller, ...GAS_ARGS }));
+    const item = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: contract.options.address });
+    await setVariableMetaDataExpectSuccess(alice, collection, item, [1, 2, 3]);
+    
+    expect(await contract.methods.getVariableMetadata(item).call()).to.be.equal('0x010203');
+  });
+
+  itWeb3('Can perform setVariableMetadata', async ({ web3, api }) => {
+    const collection = await createCollectionExpectSuccess({
+      mode: { type: 'NFT' },
+    });
+    const alice = privateKey('//Alice');
+    const caller = await createEthAccountWithBalance(api, web3);
+
+    const address = collectionIdToAddress(collection);
+    const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, { from: caller, ...GAS_ARGS }));
+    const item = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: contract.options.address });
+    
+    expect(await contract.methods.setVariableMetadata(item, '0x010203').send({ from: caller }));
+    await waitNewBlocks(api, 1);
+    expect(await contract.methods.getVariableMetadata(item).call()).to.be.equal('0x010203');
+  });
+});
modifiedtests/src/eth/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/eth/util/helpers.ts
+++ b/tests/src/eth/util/helpers.ts
@@ -17,6 +17,8 @@
 import config from '../../config';
 import privateKey from '../../substrate/privateKey';
 import contractHelpersAbi from './contractHelpersAbi.json';
+import getBalance from '../../substrate/get-balance';
+import waitNewBlocks from '../../substrate/wait-new-blocks';
 
 export const GAS_ARGS = { gas: 0x1000000, gasPrice: '0x01' };
 
@@ -37,12 +39,12 @@
   }
 }
 
-type Web3HttpMarker = {web3Http: true};
-
-export async function usingWeb3Http<T>(cb: (web3: Web3 & Web3HttpMarker) => Promise<T> | T): Promise<T> {
+/**
+ * @deprecated Web3 update solved issue with deployment over ws provider
+ */
+export async function usingWeb3Http<T>(cb: (web3: Web3) => Promise<T> | T): Promise<T> {
   const provider = new Web3.providers.HttpProvider(config.frontierUrl);
-  const web3: Web3 & Web3HttpMarker = new Web3(provider) as any;
-  web3.web3Http = true;
+  const web3: Web3 = new Web3(provider);
 
   return await cb(web3);
 }
@@ -141,12 +143,15 @@
   return normalizeEvents(out);
 }
 
-export function subToEth(eth: string): string {
+export function subToEthLowercase(eth: string): string {
   const bytes = addressToEvm(eth);
-  const string = '0x' + Buffer.from(bytes).toString('hex');
-  return Web3.utils.toChecksumAddress(string);
+  return '0x' + Buffer.from(bytes).toString('hex');
 }
 
+export function subToEth(eth: string): string {
+  return Web3.utils.toChecksumAddress(subToEthLowercase(eth));
+}
+
 export function compileContract(name: string, src: string) {
   const out = JSON.parse(solc.compile(JSON.stringify({
     language: 'Solidity',
@@ -175,7 +180,7 @@
   };
 }
 
-export async function deployFlipper(web3: Web3 & Web3HttpMarker, deployer: string) {
+export async function deployFlipper(web3: Web3, deployer: string) {
   const compiled = compileContract('Flipper', `
     contract Flipper {
       bool value = false;
@@ -197,16 +202,27 @@
   return flipper;
 }
 
-export async function deployCollector(web3: Web3 & Web3HttpMarker, deployer: string) {
+export async function deployCollector(web3: Web3, deployer: string) {
   const compiled = compileContract('Collector', `
     contract Collector {
       uint256 collected;
+      fallback() external payable {
+        giveMoney();
+      }
       function giveMoney() public payable {
         collected += msg.value;
       }
       function getCollected() public view returns (uint256) {
         return collected;
       }
+      function getUnaccounted() public view returns (uint256) {
+        return address(this).balance - collected;
+      }
+
+      function withdraw(address payable target) public {
+        target.transfer(collected);
+        collected = 0;
+      }
     }
   `);
   const Collector = new web3.eth.Contract(compiled.abi, undefined, {
@@ -221,4 +237,36 @@
 
 export function contractHelpers(web3: Web3, caller: string) {
   return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});
-}
\ No newline at end of file
+}
+
+export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, { value = 0 }: {value?: bigint | number} = { }) {
+  const tx = api.tx.evm.call(
+    subToEth(from.address),
+    to.options.address,
+    mkTx(to.methods).encodeABI(),
+    value,
+    GAS_ARGS.gas,
+    GAS_ARGS.gasPrice,
+    null,
+  );
+  const events = await submitTransactionAsync(from, tx);
+  expect(events.find(({ event: {section, method}})=>section === 'evm' && method === 'Executed')).to.be.not.undefined;
+}
+
+export async function ethBalanceViaSub(api: ApiPromise, address: string): Promise<bigint> {
+  return (await getBalance(api, [evmToAddress(address)]))[0];
+}
+
+export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {
+  const before = await ethBalanceViaSub(api, user);
+
+  await call();
+  await waitNewBlocks(api, 1);
+
+  const after = await ethBalanceViaSub(api, user);
+
+  // Can't use .to.be.less, because chai doesn't supports bigint
+  expect(after < before).to.be.true;
+
+  return before - after;
+}
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -23,6 +23,10 @@
   'parachainsystem',
   'parachaininfo',
   'evm',
+  'evmcodersubstrate',
+  'evmcontracthelpers',
+  'evmmigration',
+  'evmtransactionpayment',
   'ethereum',
   'xcmpqueue',
   'polkadotxcm',
@@ -59,7 +63,7 @@
   });
   it('No extra pallets are included', async () => {
     await usingApi(async api => {
-      expect(getModuleNames(api).length).to.be.equal(requiredPallets.length + consensusPallets.length);
+      expect(getModuleNames(api).sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort());
     });
   });
 });
modifiedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/removeCollectionAdmin.test.ts
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -37,6 +37,33 @@
     });
   });
 
+  it('Remove collection admin by admin.', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//Charlie');
+      const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();
+      expect(collection.Owner).to.be.deep.eq(Alice.address);
+      // first - add collection admin Bob
+      const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(Bob.address));
+      await submitTransactionAsync(Alice, addAdminTx);
+
+      const addAdminTx2 = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(Charlie.address));
+      await submitTransactionAsync(Alice, addAdminTx2);
+
+      const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId)).toJSON();
+      expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(Bob.address));
+
+      // then remove bob from admins of collection
+      const removeAdminTx = api.tx.nft.removeCollectionAdmin(collectionId, normalizeAccountId(Bob.address));
+      await submitTransactionAsync(Charlie, removeAdminTx);
+
+      const adminListAfterRemoveAdmin: any = (await api.query.nft.adminList(collectionId)).toJSON;
+      expect(adminListAfterRemoveAdmin).not.to.be.deep.contains(normalizeAccountId(Bob.address));
+    });
+  });
+
   it('Remove admin from collection that has no admins', async () => {
     await usingApi(async (api: ApiPromise) => {
       const Alice = privateKey('//Alice');
modifiedtests/src/removeCollectionSponsor.test.tsdiffbeforeafterboth
--- a/tests/src/removeCollectionSponsor.test.ts
+++ b/tests/src/removeCollectionSponsor.test.ts
@@ -112,6 +112,12 @@
     await removeCollectionSponsorExpectFailure(collectionId, '//Bob');
   });
 
+  it('(!negative test!) Remove sponsor for a collection by regular user', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await removeCollectionSponsorExpectFailure(collectionId, '//Bob');
+  });
+
   it('(!negative test!) Remove sponsor in a destroyed collection', async () => {
     const collectionId = await createCollectionExpectSuccess();
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
modifiedtests/src/removeFromWhiteList.test.tsdiffbeforeafterboth
--- a/tests/src/removeFromWhiteList.test.ts
+++ b/tests/src/removeFromWhiteList.test.ts
@@ -124,4 +124,13 @@
       await removeFromWhiteListExpectSuccess(bob, collectionWithoutWhitelistId, normalizeAccountId(charlie.address));
     });
   });
+
+  it('Regular user can`t remove from whitelist', async () => {
+    await usingApi(async () => {
+      const collectionWithoutWhitelistId = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId);
+      await addToWhiteListExpectSuccess(alice, collectionWithoutWhitelistId, charlie.address);
+      await removeFromWhiteListExpectFailure(bob, collectionWithoutWhitelistId, normalizeAccountId(charlie.address));
+    });
+  });
 });
\ No newline at end of file
modifiedtests/src/setChainLimits.test.tsdiffbeforeafterboth
--- a/tests/src/setChainLimits.test.ts
+++ b/tests/src/setChainLimits.test.ts
@@ -13,7 +13,7 @@
   IChainLimits,
 } from './util/helpers';
 
-describe('Negative Integration Test setChainLimits', () => {
+describe.skip('Negative Integration Test setChainLimits', () => {
   let alice: IKeyringPair;
   let bob: IKeyringPair;
   let dave: IKeyringPair;
modifiedtests/src/setPublicAccessMode.test.tsdiffbeforeafterboth
--- a/tests/src/setPublicAccessMode.test.ts
+++ b/tests/src/setPublicAccessMode.test.ts
@@ -101,7 +101,7 @@
       Bob = privateKey('//Bob');
     });
   });
-  it('Set the collection that has been deleted', async () => {
+  it('setPublicAccessMode by collection admin', async () => {
     await usingApi(async (api: ApiPromise) => {
       // tslint:disable-next-line: no-bitwise
       const collectionId = await createCollectionExpectSuccess();
modifiedtests/src/setSchemaVersion.test.tsdiffbeforeafterboth
--- a/tests/src/setSchemaVersion.test.ts
+++ b/tests/src/setSchemaVersion.test.ts
@@ -24,6 +24,7 @@
 
 let alice: IKeyringPair;
 let bob: IKeyringPair;
+let charlie: IKeyringPair;
 let collectionIdForTesting: number;
 
 /*
@@ -117,6 +118,7 @@
     await usingApi(async () => {
       const keyring = new Keyring({ type: 'sr25519' });
       alice = keyring.addFromUri('//Alice');
+      charlie = keyring.addFromUri('//Charlie');
     });
   });
   it('execute setSchemaVersion for not exists collection', async () => {
@@ -127,7 +129,6 @@
       await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
     });
   });
-
   it('execute setSchemaVersion with not correct schema version', async () => {
     await usingApi(async (api: ApiPromise) => {
       const consoleError = console.error;
@@ -143,7 +144,6 @@
       }
     });
   });
-
   it('execute setSchemaVersion for deleted collection', async () => {
     await usingApi(async (api: ApiPromise) => {
       await destroyCollectionExpectSuccess(collectionIdForTesting);
@@ -151,4 +151,10 @@
       await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
     });
   });
+  it('Regular user can`t execute setSchemaVersion with image url and unique ', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'Unique');
+      await expect(submitTransactionAsync(charlie, tx)).to.be.rejected;
+    });
+  });
 });
modifiedtests/src/substrate/get-balance.tsdiffbeforeafterboth
--- a/tests/src/substrate/get-balance.ts
+++ b/tests/src/substrate/get-balance.ts
@@ -6,9 +6,24 @@
 import { ApiPromise } from '@polkadot/api';
 import {AccountInfo} from '@polkadot/types/interfaces/system';
 import promisifySubstrate from './promisify-substrate';
+import { IKeyringPair } from '@polkadot/types/types';
+import { submitTransactionAsync } from './substrate-api';
+import { getGenericResult } from '../util/helpers';
+import { expect } from 'chai';
 
 export default async function getBalance(api: ApiPromise, accounts: string[]): Promise<Array<bigint>> {
   const balance = promisifySubstrate(api, (acc: string[]) => api.query.system.account.multi(acc));
   const responce = await balance(accounts) as unknown as AccountInfo[];
   return responce.map((r) => r.data.free.toBigInt().valueOf());
 }
+
+export async function getBalanceSingle(api: ApiPromise, account: string): Promise<bigint> {
+  return (await getBalance(api, [account]))[0];
+}
+
+export async function transferBalanceExpectSuccess(api: ApiPromise, from: IKeyringPair, to: string, amount: bigint | string) {
+  const tx = api.tx.balances.transfer(to, amount);
+  const events = await submitTransactionAsync(from, tx);
+  const result = getGenericResult(events);
+  expect(result.success).to.be.true;
+}
\ No newline at end of file
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -60,7 +60,7 @@
         // tslint:disable-next-line:no-unused-expression
         expect(result.success).to.be.false;
       };
-      expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');
+      await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');
     });
   });
 
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -53,6 +53,11 @@
 
 export const U128_MAX = (1n << 128n) - 1n;
 
+const MICROUNIQUE = 1_000_000_000n;
+const MILLIUNIQUE = 1_000n * MICROUNIQUE;
+const CENTIUNIQUE = 10n * MILLIUNIQUE;
+export const UNIQUE = 100n * CENTIUNIQUE;
+
 type GenericResult = {
   success: boolean,
 };
@@ -1006,10 +1011,31 @@
   });
 }
 
+export async function setPublicAccessModeExpectFail(
+  sender: IKeyringPair, collectionId: number,
+  accessMode: 'Normal' | 'WhiteList',
+) {
+  await usingApi(async (api) => {
+
+    // Run the transaction
+    const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getGenericResult(events);
+
+    // What to expect
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
 export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
   await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList');
 }
 
+export async function enableWhiteListExpectFail(sender: IKeyringPair, collectionId: number) {
+  await setPublicAccessModeExpectFail(sender, collectionId, 'WhiteList');
+}
+
 export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
   await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');
 }
@@ -1088,6 +1114,19 @@
   });
 }
 
+export async function addToWhiteListExpectFail(sender: IKeyringPair, collectionId: number, address: string | AccountId) {
+  await usingApi(async (api) => {
+    // Run the transaction
+    const tx = api.tx.nft.addToWhiteList(collectionId, normalizeAccountId(address));
+    const events = await expect(submitTransactionAsync(sender, tx)).to.be.rejected;
+    const result = getGenericResult(events);
+
+    // What to expect
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
 export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {
   await usingApi(async (api) => {
     // Run the transaction
@@ -1129,3 +1168,7 @@
     return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;
   });
 }
+
+export async function queryNftOwner(api: ApiPromise, collectionId: number, tokenId: number): Promise<CrossAccountId> {
+  return normalizeAccountId((await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON().Owner);
+}
\ No newline at end of file
modifiedtests/yarn.lockdiffbeforeafterboth
--- a/tests/yarn.lock
+++ b/tests/yarn.lock
@@ -2,10 +2,10 @@
 # yarn lockfile v1
 
 
-"@babel/cli@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.14.5.tgz#9551b194f02360729de6060785bbdcce52c69f0a"
-  integrity sha512-poegjhRvXHWO0EAsnYajwYZuqcz7gyfxwfaecUESxDujrqOivf3zrjFbub8IJkrqEaz3fvJWh001EzxBub54fg==
+"@babel/cli@^7.14.8":
+  version "7.14.8"
+  resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.14.8.tgz#fac73c0e2328a8af9fd3560c06b096bfa3730933"
+  integrity sha512-lcy6Lymft9Rpfqmrqdd4oTDdUx9ZwaAhAfywVrHG4771Pa6PPT0danJ1kDHBXYqh4HHSmIdA+nlmfxfxSDPtBg==
   dependencies:
     commander "^4.0.1"
     convert-source-map "^1.1.0"
@@ -42,7 +42,12 @@
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08"
   integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==
 
-"@babel/core@^7.1.0", "@babel/core@^7.14.6", "@babel/core@^7.7.2", "@babel/core@^7.7.5":
+"@babel/compat-data@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176"
+  integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==
+
+"@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5":
   version "7.14.6"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab"
   integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==
@@ -63,6 +68,27 @@
     semver "^6.3.0"
     source-map "^0.5.0"
 
+"@babel/core@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.0.tgz#749e57c68778b73ad8082775561f67f5196aafa8"
+  integrity sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==
+  dependencies:
+    "@babel/code-frame" "^7.14.5"
+    "@babel/generator" "^7.15.0"
+    "@babel/helper-compilation-targets" "^7.15.0"
+    "@babel/helper-module-transforms" "^7.15.0"
+    "@babel/helpers" "^7.14.8"
+    "@babel/parser" "^7.15.0"
+    "@babel/template" "^7.14.5"
+    "@babel/traverse" "^7.15.0"
+    "@babel/types" "^7.15.0"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.2"
+    json5 "^2.1.2"
+    semver "^6.3.0"
+    source-map "^0.5.0"
+
 "@babel/generator@^7.14.5", "@babel/generator@^7.7.2":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785"
@@ -72,6 +98,15 @@
     jsesc "^2.5.1"
     source-map "^0.5.0"
 
+"@babel/generator@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15"
+  integrity sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==
+  dependencies:
+    "@babel/types" "^7.15.0"
+    jsesc "^2.5.1"
+    source-map "^0.5.0"
+
 "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61"
@@ -97,7 +132,17 @@
     browserslist "^4.16.6"
     semver "^6.3.0"
 
-"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6":
+"@babel/helper-compilation-targets@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz#973df8cbd025515f3ff25db0c05efc704fa79818"
+  integrity sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==
+  dependencies:
+    "@babel/compat-data" "^7.15.0"
+    "@babel/helper-validator-option" "^7.14.5"
+    browserslist "^4.16.6"
+    semver "^6.3.0"
+
+"@babel/helper-create-class-features-plugin@^7.14.5":
   version "7.14.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542"
   integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg==
@@ -109,6 +154,18 @@
     "@babel/helper-replace-supers" "^7.14.5"
     "@babel/helper-split-export-declaration" "^7.14.5"
 
+"@babel/helper-create-class-features-plugin@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.0.tgz#c9a137a4d137b2d0e2c649acf536d7ba1a76c0f7"
+  integrity sha512-MdmDXgvTIi4heDVX/e9EFfeGpugqm9fobBVg/iioE8kueXrOHdRDe36FAY7SnE9xXLVeYCoJR/gdrBEIHRC83Q==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.14.5"
+    "@babel/helper-function-name" "^7.14.5"
+    "@babel/helper-member-expression-to-functions" "^7.15.0"
+    "@babel/helper-optimise-call-expression" "^7.14.5"
+    "@babel/helper-replace-supers" "^7.15.0"
+    "@babel/helper-split-export-declaration" "^7.14.5"
+
 "@babel/helper-create-regexp-features-plugin@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4"
@@ -168,6 +225,13 @@
   dependencies:
     "@babel/types" "^7.14.5"
 
+"@babel/helper-member-expression-to-functions@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz#0ddaf5299c8179f27f37327936553e9bba60990b"
+  integrity sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==
+  dependencies:
+    "@babel/types" "^7.15.0"
+
 "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
@@ -189,6 +253,20 @@
     "@babel/traverse" "^7.14.5"
     "@babel/types" "^7.14.5"
 
+"@babel/helper-module-transforms@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz#679275581ea056373eddbe360e1419ef23783b08"
+  integrity sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.14.5"
+    "@babel/helper-replace-supers" "^7.15.0"
+    "@babel/helper-simple-access" "^7.14.8"
+    "@babel/helper-split-export-declaration" "^7.14.5"
+    "@babel/helper-validator-identifier" "^7.14.9"
+    "@babel/template" "^7.14.5"
+    "@babel/traverse" "^7.15.0"
+    "@babel/types" "^7.15.0"
+
 "@babel/helper-optimise-call-expression@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c"
@@ -220,6 +298,16 @@
     "@babel/traverse" "^7.14.5"
     "@babel/types" "^7.14.5"
 
+"@babel/helper-replace-supers@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz#ace07708f5bf746bf2e6ba99572cce79b5d4e7f4"
+  integrity sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.15.0"
+    "@babel/helper-optimise-call-expression" "^7.14.5"
+    "@babel/traverse" "^7.15.0"
+    "@babel/types" "^7.15.0"
+
 "@babel/helper-simple-access@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4"
@@ -227,6 +315,13 @@
   dependencies:
     "@babel/types" "^7.14.5"
 
+"@babel/helper-simple-access@^7.14.8":
+  version "7.14.8"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924"
+  integrity sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==
+  dependencies:
+    "@babel/types" "^7.14.8"
+
 "@babel/helper-skip-transparent-expression-wrappers@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4"
@@ -246,6 +341,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8"
   integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==
 
+"@babel/helper-validator-identifier@^7.14.9":
+  version "7.14.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48"
+  integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==
+
 "@babel/helper-validator-option@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
@@ -270,6 +370,15 @@
     "@babel/traverse" "^7.14.5"
     "@babel/types" "^7.14.5"
 
+"@babel/helpers@^7.14.8":
+  version "7.15.3"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.3.tgz#c96838b752b95dcd525b4e741ed40bb1dc2a1357"
+  integrity sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==
+  dependencies:
+    "@babel/template" "^7.14.5"
+    "@babel/traverse" "^7.15.0"
+    "@babel/types" "^7.15.0"
+
 "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
@@ -284,6 +393,11 @@
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.6.tgz#d85cc68ca3cac84eae384c06f032921f5227f4b2"
   integrity sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==
 
+"@babel/parser@^7.15.0":
+  version "7.15.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862"
+  integrity sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==
+
 "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e"
@@ -293,10 +407,10 @@
     "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5"
     "@babel/plugin-proposal-optional-chaining" "^7.14.5"
 
-"@babel/plugin-proposal-async-generator-functions@^7.14.7":
-  version "7.14.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz#784a48c3d8ed073f65adcf30b57bcbf6c8119ace"
-  integrity sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==
+"@babel/plugin-proposal-async-generator-functions@^7.14.9":
+  version "7.14.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.9.tgz#7028dc4fa21dc199bbacf98b39bab1267d0eaf9a"
+  integrity sha512-d1lnh+ZnKrFKwtTYdw320+sQWCTwgkB9fmUhNXRADA4akR6wLjaruSGnIEUjpt9HCOwTr4ynFTKu19b7rFRpmw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
     "@babel/helper-remap-async-to-generator" "^7.14.5"
@@ -577,10 +691,10 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-transform-classes@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf"
-  integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA==
+"@babel/plugin-transform-classes@^7.14.9":
+  version "7.14.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.9.tgz#2a391ffb1e5292710b00f2e2c210e1435e7d449f"
+  integrity sha512-NfZpTcxU3foGWbl4wxmZ35mTsYJy8oQocbeIMoDAGGFarAmSQlL+LWMkDx/tj6pNotpbX3rltIA4dprgAPOq5A==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.14.5"
     "@babel/helper-function-name" "^7.14.5"
@@ -665,14 +779,14 @@
     "@babel/helper-plugin-utils" "^7.14.5"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-commonjs@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97"
-  integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A==
+"@babel/plugin-transform-modules-commonjs@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.0.tgz#3305896e5835f953b5cdb363acd9e8c2219a5281"
+  integrity sha512-3H/R9s8cXcOGE8kgMlmjYYC9nqr5ELiPkJn4q0mypBrjhYQoc+5/Maq69vV4xRPWnkzZuwJPf5rArxpB/35Cig==
   dependencies:
-    "@babel/helper-module-transforms" "^7.14.5"
+    "@babel/helper-module-transforms" "^7.15.0"
     "@babel/helper-plugin-utils" "^7.14.5"
-    "@babel/helper-simple-access" "^7.14.5"
+    "@babel/helper-simple-access" "^7.14.8"
     babel-plugin-dynamic-import-node "^2.3.3"
 
 "@babel/plugin-transform-modules-systemjs@^7.14.5":
@@ -694,10 +808,10 @@
     "@babel/helper-module-transforms" "^7.14.5"
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-transform-named-capturing-groups-regex@^7.14.7":
-  version "7.14.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz#60c06892acf9df231e256c24464bfecb0908fd4e"
-  integrity sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg==
+"@babel/plugin-transform-named-capturing-groups-regex@^7.14.9":
+  version "7.14.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.9.tgz#c68f5c5d12d2ebaba3762e57c2c4f6347a46e7b2"
+  integrity sha512-l666wCVYO75mlAtGFfyFwnWmIXQm3kSH0C3IRnJqWcZbWkoihyAdDhFm2ZWaxWTqvBvhVFfJjMRQ0ez4oN1yYA==
   dependencies:
     "@babel/helper-create-regexp-features-plugin" "^7.14.5"
 
@@ -777,10 +891,10 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-transform-runtime@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz#30491dad49c6059f8f8fa5ee8896a0089e987523"
-  integrity sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg==
+"@babel/plugin-transform-runtime@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.0.tgz#d3aa650d11678ca76ce294071fda53d7804183b3"
+  integrity sha512-sfHYkLGjhzWTq6xsuQ01oEsUYjkHRux9fW1iUA68dC7Qd8BS1Unq4aZ8itmQp95zUzIcyR2EbNMTzAicFj+guw==
   dependencies:
     "@babel/helper-module-imports" "^7.14.5"
     "@babel/helper-plugin-utils" "^7.14.5"
@@ -825,12 +939,12 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-transform-typescript@^7.14.5":
-  version "7.14.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c"
-  integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA==
+"@babel/plugin-transform-typescript@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.15.0.tgz#553f230b9d5385018716586fc48db10dd228eb7e"
+  integrity sha512-WIIEazmngMEEHDaPTx0IZY48SaAmjVWe3TRSX7cmJXn0bEv9midFzAjxiruOWYIVf5iQ10vFx7ASDpgEO08L5w==
   dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.14.6"
+    "@babel/helper-create-class-features-plugin" "^7.15.0"
     "@babel/helper-plugin-utils" "^7.14.5"
     "@babel/plugin-syntax-typescript" "^7.14.5"
 
@@ -849,17 +963,17 @@
     "@babel/helper-create-regexp-features-plugin" "^7.14.5"
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/preset-env@^7.14.7":
-  version "7.14.7"
-  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.7.tgz#5c70b22d4c2d893b03d8c886a5c17422502b932a"
-  integrity sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==
+"@babel/preset-env@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.15.0.tgz#e2165bf16594c9c05e52517a194bf6187d6fe464"
+  integrity sha512-FhEpCNFCcWW3iZLg0L2NPE9UerdtsCR6ZcsGHUX6Om6kbCQeL5QZDqFDmeNHC6/fy6UH3jEge7K4qG5uC9In0Q==
   dependencies:
-    "@babel/compat-data" "^7.14.7"
-    "@babel/helper-compilation-targets" "^7.14.5"
+    "@babel/compat-data" "^7.15.0"
+    "@babel/helper-compilation-targets" "^7.15.0"
     "@babel/helper-plugin-utils" "^7.14.5"
     "@babel/helper-validator-option" "^7.14.5"
     "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.14.5"
-    "@babel/plugin-proposal-async-generator-functions" "^7.14.7"
+    "@babel/plugin-proposal-async-generator-functions" "^7.14.9"
     "@babel/plugin-proposal-class-properties" "^7.14.5"
     "@babel/plugin-proposal-class-static-block" "^7.14.5"
     "@babel/plugin-proposal-dynamic-import" "^7.14.5"
@@ -892,7 +1006,7 @@
     "@babel/plugin-transform-async-to-generator" "^7.14.5"
     "@babel/plugin-transform-block-scoped-functions" "^7.14.5"
     "@babel/plugin-transform-block-scoping" "^7.14.5"
-    "@babel/plugin-transform-classes" "^7.14.5"
+    "@babel/plugin-transform-classes" "^7.14.9"
     "@babel/plugin-transform-computed-properties" "^7.14.5"
     "@babel/plugin-transform-destructuring" "^7.14.7"
     "@babel/plugin-transform-dotall-regex" "^7.14.5"
@@ -903,10 +1017,10 @@
     "@babel/plugin-transform-literals" "^7.14.5"
     "@babel/plugin-transform-member-expression-literals" "^7.14.5"
     "@babel/plugin-transform-modules-amd" "^7.14.5"
-    "@babel/plugin-transform-modules-commonjs" "^7.14.5"
+    "@babel/plugin-transform-modules-commonjs" "^7.15.0"
     "@babel/plugin-transform-modules-systemjs" "^7.14.5"
     "@babel/plugin-transform-modules-umd" "^7.14.5"
-    "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.7"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.9"
     "@babel/plugin-transform-new-target" "^7.14.5"
     "@babel/plugin-transform-object-super" "^7.14.5"
     "@babel/plugin-transform-parameters" "^7.14.5"
@@ -921,11 +1035,11 @@
     "@babel/plugin-transform-unicode-escapes" "^7.14.5"
     "@babel/plugin-transform-unicode-regex" "^7.14.5"
     "@babel/preset-modules" "^0.1.4"
-    "@babel/types" "^7.14.5"
+    "@babel/types" "^7.15.0"
     babel-plugin-polyfill-corejs2 "^0.2.2"
     babel-plugin-polyfill-corejs3 "^0.2.2"
     babel-plugin-polyfill-regenerator "^0.2.2"
-    core-js-compat "^3.15.0"
+    core-js-compat "^3.16.0"
     semver "^6.3.0"
 
 "@babel/preset-modules@^0.1.4":
@@ -951,19 +1065,19 @@
     "@babel/plugin-transform-react-jsx-development" "^7.14.5"
     "@babel/plugin-transform-react-pure-annotations" "^7.14.5"
 
-"@babel/preset-typescript@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz#aa98de119cf9852b79511f19e7f44a2d379bcce0"
-  integrity sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw==
+"@babel/preset-typescript@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.15.0.tgz#e8fca638a1a0f64f14e1119f7fe4500277840945"
+  integrity sha512-lt0Y/8V3y06Wq/8H/u0WakrqciZ7Fz7mwPDHWUJAXlABL5hiUG42BNlRXiELNjeWjO5rWmnNKlx+yzJvxezHow==
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
     "@babel/helper-validator-option" "^7.14.5"
-    "@babel/plugin-transform-typescript" "^7.14.5"
+    "@babel/plugin-transform-typescript" "^7.15.0"
 
-"@babel/register@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233"
-  integrity sha512-TjJpGz/aDjFGWsItRBQMOFTrmTI9tr79CHOK+KIvLeCkbxuOAk2M5QHjvruIMGoo9OuccMh5euplPzc5FjAKGg==
+"@babel/register@^7.15.3":
+  version "7.15.3"
+  resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.15.3.tgz#6b40a549e06ec06c885b2ec42c3dd711f55fe752"
+  integrity sha512-mj4IY1ZJkorClxKTImccn4T81+UKTo4Ux0+OFSV9hME1ooqS9UV+pJ6BjD0qXPK4T3XW/KNa79XByjeEMZz+fw==
   dependencies:
     clone-deep "^4.0.1"
     find-cache-dir "^2.0.0"
@@ -978,6 +1092,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.15.3":
+  version "7.15.3"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
+  integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/template@^7.14.5", "@babel/template@^7.3.3":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
@@ -1002,6 +1123,21 @@
     debug "^4.1.0"
     globals "^11.1.0"
 
+"@babel/traverse@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.0.tgz#4cca838fd1b2a03283c1f38e141f639d60b3fc98"
+  integrity sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==
+  dependencies:
+    "@babel/code-frame" "^7.14.5"
+    "@babel/generator" "^7.15.0"
+    "@babel/helper-function-name" "^7.14.5"
+    "@babel/helper-hoist-variables" "^7.14.5"
+    "@babel/helper-split-export-declaration" "^7.14.5"
+    "@babel/parser" "^7.15.0"
+    "@babel/types" "^7.15.0"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
 "@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff"
@@ -1010,26 +1146,19 @@
     "@babel/helper-validator-identifier" "^7.14.5"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.14.8", "@babel/types@^7.15.0":
+  version "7.15.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd"
+  integrity sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.14.9"
+    to-fast-properties "^2.0.0"
+
 "@bcoe/v8-coverage@^0.2.3":
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
   integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
 
-"@eslint/eslintrc@^0.4.2":
-  version "0.4.2"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179"
-  integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==
-  dependencies:
-    ajv "^6.12.4"
-    debug "^4.1.1"
-    espree "^7.3.0"
-    globals "^13.9.0"
-    ignore "^4.0.6"
-    import-fresh "^3.2.1"
-    js-yaml "^3.13.1"
-    minimatch "^3.0.4"
-    strip-json-comments "^3.1.1"
-
 "@eslint/eslintrc@^0.4.3":
   version "0.4.3"
   resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
@@ -1251,94 +1380,94 @@
   resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
   integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
 
-"@jest/console@^27.0.2":
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.2.tgz#b8eeff8f21ac51d224c851e1729d2630c18631e6"
-  integrity sha512-/zYigssuHLImGeMAACkjI4VLAiiJznHgAl3xnFT19iWyct2LhrH3KXOjHRmxBGTkiPLZKKAJAgaPpiU9EZ9K+w==
+"@jest/console@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.6.tgz#3eb72ea80897495c3d73dd97aab7f26770e2260f"
+  integrity sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     chalk "^4.0.0"
-    jest-message-util "^27.0.2"
-    jest-util "^27.0.2"
+    jest-message-util "^27.0.6"
+    jest-util "^27.0.6"
     slash "^3.0.0"
 
-"@jest/core@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.5.tgz#59e9e69e7374d65dbb22e3fc1bd52e80991eae72"
-  integrity sha512-g73//jF0VwsOIrWUC9Cqg03lU3QoAMFxVjsm6n6yNmwZcQPN/o8w+gLWODw5VfKNFZT38otXHWxc6b8eGDUpEA==
+"@jest/core@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.6.tgz#c5f642727a0b3bf0f37c4b46c675372d0978d4a1"
+  integrity sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow==
   dependencies:
-    "@jest/console" "^27.0.2"
-    "@jest/reporters" "^27.0.5"
-    "@jest/test-result" "^27.0.2"
-    "@jest/transform" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/console" "^27.0.6"
+    "@jest/reporters" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/transform" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     ansi-escapes "^4.2.1"
     chalk "^4.0.0"
     emittery "^0.8.1"
     exit "^0.1.2"
     graceful-fs "^4.2.4"
-    jest-changed-files "^27.0.2"
-    jest-config "^27.0.5"
-    jest-haste-map "^27.0.5"
-    jest-message-util "^27.0.2"
-    jest-regex-util "^27.0.1"
-    jest-resolve "^27.0.5"
-    jest-resolve-dependencies "^27.0.5"
-    jest-runner "^27.0.5"
-    jest-runtime "^27.0.5"
-    jest-snapshot "^27.0.5"
-    jest-util "^27.0.2"
-    jest-validate "^27.0.2"
-    jest-watcher "^27.0.2"
+    jest-changed-files "^27.0.6"
+    jest-config "^27.0.6"
+    jest-haste-map "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-regex-util "^27.0.6"
+    jest-resolve "^27.0.6"
+    jest-resolve-dependencies "^27.0.6"
+    jest-runner "^27.0.6"
+    jest-runtime "^27.0.6"
+    jest-snapshot "^27.0.6"
+    jest-util "^27.0.6"
+    jest-validate "^27.0.6"
+    jest-watcher "^27.0.6"
     micromatch "^4.0.4"
     p-each-series "^2.1.0"
     rimraf "^3.0.0"
     slash "^3.0.0"
     strip-ansi "^6.0.0"
 
-"@jest/environment@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.5.tgz#a294ad4acda2e250f789fb98dc667aad33d3adc9"
-  integrity sha512-IAkJPOT7bqn0GiX5LPio6/e1YpcmLbrd8O5EFYpAOZ6V+9xJDsXjdgN2vgv9WOKIs/uA1kf5WeD96HhlBYO+FA==
+"@jest/environment@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.6.tgz#ee293fe996db01d7d663b8108fa0e1ff436219d2"
+  integrity sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==
   dependencies:
-    "@jest/fake-timers" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/fake-timers" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
-    jest-mock "^27.0.3"
+    jest-mock "^27.0.6"
 
-"@jest/fake-timers@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.5.tgz#304d5aedadf4c75cff3696995460b39d6c6e72f6"
-  integrity sha512-d6Tyf7iDoKqeUdwUKrOBV/GvEZRF67m7lpuWI0+SCD9D3aaejiOQZxAOxwH2EH/W18gnfYaBPLi0VeTGBHtQBg==
+"@jest/fake-timers@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.6.tgz#cbad52f3fe6abe30e7acb8cd5fa3466b9588e3df"
+  integrity sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     "@sinonjs/fake-timers" "^7.0.2"
     "@types/node" "*"
-    jest-message-util "^27.0.2"
-    jest-mock "^27.0.3"
-    jest-util "^27.0.2"
+    jest-message-util "^27.0.6"
+    jest-mock "^27.0.6"
+    jest-util "^27.0.6"
 
-"@jest/globals@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.5.tgz#f63b8bfa6ea3716f8df50f6a604b5c15b36ffd20"
-  integrity sha512-qqKyjDXUaZwDuccpbMMKCCMBftvrbXzigtIsikAH/9ca+kaae8InP2MDf+Y/PdCSMuAsSpHS6q6M25irBBUh+Q==
+"@jest/globals@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.6.tgz#48e3903f99a4650673d8657334d13c9caf0e8f82"
+  integrity sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==
   dependencies:
-    "@jest/environment" "^27.0.5"
-    "@jest/types" "^27.0.2"
-    expect "^27.0.2"
+    "@jest/environment" "^27.0.6"
+    "@jest/types" "^27.0.6"
+    expect "^27.0.6"
 
-"@jest/reporters@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.5.tgz#cd730b77d9667b8ff700ad66d4edc293bb09716a"
-  integrity sha512-4uNg5+0eIfRafnpgu3jCZws3NNcFzhu5JdRd1mKQ4/53+vkIqwB6vfZ4gn5BdGqOaLtYhlOsPaL5ATkKzyBrJw==
+"@jest/reporters@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.6.tgz#91e7f2d98c002ad5df94d5b5167c1eb0b9fd5b00"
+  integrity sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA==
   dependencies:
     "@bcoe/v8-coverage" "^0.2.3"
-    "@jest/console" "^27.0.2"
-    "@jest/test-result" "^27.0.2"
-    "@jest/transform" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/console" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/transform" "^27.0.6"
+    "@jest/types" "^27.0.6"
     chalk "^4.0.0"
     collect-v8-coverage "^1.0.0"
     exit "^0.1.2"
@@ -1349,70 +1478,70 @@
     istanbul-lib-report "^3.0.0"
     istanbul-lib-source-maps "^4.0.0"
     istanbul-reports "^3.0.2"
-    jest-haste-map "^27.0.5"
-    jest-resolve "^27.0.5"
-    jest-util "^27.0.2"
-    jest-worker "^27.0.2"
+    jest-haste-map "^27.0.6"
+    jest-resolve "^27.0.6"
+    jest-util "^27.0.6"
+    jest-worker "^27.0.6"
     slash "^3.0.0"
     source-map "^0.6.0"
     string-length "^4.0.1"
     terminal-link "^2.0.0"
     v8-to-istanbul "^8.0.0"
 
-"@jest/source-map@^27.0.1":
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.1.tgz#2afbf73ddbaddcb920a8e62d0238a0a9e0a8d3e4"
-  integrity sha512-yMgkF0f+6WJtDMdDYNavmqvbHtiSpwRN2U/W+6uztgfqgkq/PXdKPqjBTUF1RD/feth4rH5N3NW0T5+wIuln1A==
+"@jest/source-map@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.6.tgz#be9e9b93565d49b0548b86e232092491fb60551f"
+  integrity sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==
   dependencies:
     callsites "^3.0.0"
     graceful-fs "^4.2.4"
     source-map "^0.6.0"
 
-"@jest/test-result@^27.0.2":
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.2.tgz#0451049e32ceb609b636004ccc27c8fa22263f10"
-  integrity sha512-gcdWwL3yP5VaIadzwQtbZyZMgpmes8ryBAJp70tuxghiA8qL4imJyZex+i+USQH2H4jeLVVszhwntgdQ97fccA==
+"@jest/test-result@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.6.tgz#3fa42015a14e4fdede6acd042ce98c7f36627051"
+  integrity sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==
   dependencies:
-    "@jest/console" "^27.0.2"
-    "@jest/types" "^27.0.2"
+    "@jest/console" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/istanbul-lib-coverage" "^2.0.0"
     collect-v8-coverage "^1.0.0"
 
-"@jest/test-sequencer@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.5.tgz#c58b21db49afc36c0e3921d7ddf1fb7954abfded"
-  integrity sha512-opztnGs+cXzZ5txFG2+omBaV5ge/0yuJNKbhE3DREMiXE0YxBuzyEa6pNv3kk2JuucIlH2Xvgmn9kEEHSNt/SA==
+"@jest/test-sequencer@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz#80a913ed7a1130545b1cd777ff2735dd3af5d34b"
+  integrity sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA==
   dependencies:
-    "@jest/test-result" "^27.0.2"
+    "@jest/test-result" "^27.0.6"
     graceful-fs "^4.2.4"
-    jest-haste-map "^27.0.5"
-    jest-runtime "^27.0.5"
+    jest-haste-map "^27.0.6"
+    jest-runtime "^27.0.6"
 
-"@jest/transform@^27.0.5":
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.5.tgz#2dcb78953708af713941ac845b06078bc74ed873"
-  integrity sha512-lBD6OwKXSc6JJECBNk4mVxtSVuJSBsQrJ9WCBisfJs7EZuYq4K6vM9HmoB7hmPiLIDGeyaerw3feBV/bC4z8tg==
+"@jest/transform@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.6.tgz#189ad7107413208f7600f4719f81dd2f7278cc95"
+  integrity sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==
   dependencies:
     "@babel/core" "^7.1.0"
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     babel-plugin-istanbul "^6.0.0"
     chalk "^4.0.0"
     convert-source-map "^1.4.0"
     fast-json-stable-stringify "^2.0.0"
     graceful-fs "^4.2.4"
-    jest-haste-map "^27.0.5"
-    jest-regex-util "^27.0.1"
-    jest-util "^27.0.2"
+    jest-haste-map "^27.0.6"
+    jest-regex-util "^27.0.6"
+    jest-util "^27.0.6"
     micromatch "^4.0.4"
     pirates "^4.0.1"
     slash "^3.0.0"
     source-map "^0.6.1"
     write-file-atomic "^3.0.0"
 
-"@jest/types@^27.0.2":
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.2.tgz#e153d6c46bda0f2589f0702b071f9898c7bbd37e"
-  integrity sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==
+"@jest/types@^27.0.6":
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b"
+  integrity sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==
   dependencies:
     "@types/istanbul-lib-coverage" "^2.0.0"
     "@types/istanbul-reports" "^3.0.0"
@@ -1559,54 +1688,54 @@
   dependencies:
     "@octokit/openapi-types" "^7.3.2"
 
-"@polkadot/api-contract@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-5.0.1.tgz#520a7b3cd990a76374b79e12eca5bf629cc565a1"
-  integrity sha512-qZ2wnXHDyU2c1/V9GKpbcZKBfVua4YFsU/LHKevZLkJfnGFBgNRdwAuKgVe5h2FCt2W2/pt618WgxG0UDWwjcw==
+"@polkadot/api-contract@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-5.5.1.tgz#4cdd0d6f4352050c58464d5958bdb779770bd2dc"
+  integrity sha512-/1O1AnpCu+LM2EKhRY83r36blG8KOr0JCVFeSfT0u52tM4wMdLlUy1XV/XTZayuCucdJ6I0pjUudCljm92aiGw==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/api" "5.0.1"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/util" "^7.0.1"
-    rxjs "^7.2.0"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/api" "5.5.1"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/util" "^7.2.1"
+    rxjs "^7.3.0"
 
-"@polkadot/api-derive@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-5.0.1.tgz#08064c10ed159826ffd07013dcdde1d8b63186a0"
-  integrity sha512-JZpH1JVLu3PvX4+A71iDLtNr6LL103dAFou61DxyJF4obyTmS2lzigG3xXqUFShiPDb19ywxQpsE4gAOP6emuQ==
+"@polkadot/api-derive@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-5.5.1.tgz#6fdba748d90024f2fcdeb7827d178ff8d0ad308e"
+  integrity sha512-dkpl3CnroBYAlLx571KoyjI72TqRweWI61z7tzNeR8qwniNyWDEILTErUfzy5jYAO7XrZpW1Gn4WMMH+kEcqZQ==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/api" "5.0.1"
-    "@polkadot/rpc-core" "5.0.1"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/util" "^7.0.1"
-    "@polkadot/util-crypto" "^7.0.1"
-    rxjs "^7.2.0"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/api" "5.5.1"
+    "@polkadot/rpc-core" "5.5.1"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/util" "^7.2.1"
+    "@polkadot/util-crypto" "^7.2.1"
+    rxjs "^7.3.0"
 
-"@polkadot/api@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-5.0.1.tgz#9607b53009322f9264f7dcc8705c466bd33aa516"
-  integrity sha512-5JDpM2Fjc80gHBju1B/rMBGDfAvY8UiU4XVlivJRk+mTVD3OTwbtTro4nmwJOub05xQCJvD/bnCuxG8eFSoq+Q==
+"@polkadot/api@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-5.5.1.tgz#0298c6d883c2264a68ae93a3b59b98263aed53d3"
+  integrity sha512-GS/MoRc7NB61jz7TzwX0WAA3JS7DSj3xH4ac39LcuPHXu0VMQw6LgT/5KIzYxTx+79Iwth62bKelW/Mgk23wUg==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/api-derive" "5.0.1"
-    "@polkadot/keyring" "^7.0.1"
-    "@polkadot/rpc-core" "5.0.1"
-    "@polkadot/rpc-provider" "5.0.1"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/types-known" "5.0.1"
-    "@polkadot/util" "^7.0.1"
-    "@polkadot/util-crypto" "^7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/api-derive" "5.5.1"
+    "@polkadot/keyring" "^7.2.1"
+    "@polkadot/rpc-core" "5.5.1"
+    "@polkadot/rpc-provider" "5.5.1"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/types-known" "5.5.1"
+    "@polkadot/util" "^7.2.1"
+    "@polkadot/util-crypto" "^7.2.1"
     eventemitter3 "^4.0.7"
-    rxjs "^7.2.0"
+    rxjs "^7.3.0"
 
-"@polkadot/dev@0.62.43":
-  version "0.62.43"
-  resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.62.43.tgz#567591bf3c38dded4b4c1f3ec8bf3d3198a23b6d"
-  integrity sha512-ZSgYUbC6A+WtRSY5nq7yeGYW+wv3g8cds2zndL4GeClIdVHksgEW6+ch3Hx6LMkE3y4q9Kjvs50oHeemBDut4Q==
+"@polkadot/dev@0.62.60":
+  version "0.62.60"
+  resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.62.60.tgz#450007c189a8433d8627bcc1fc01d2ffb1becfc3"
+  integrity sha512-VHZ4d/hhRSNFe0RXp3L/ZcAk1t2JQ1/lsznONAz4I9SZ2pn5kAQrWRF6eukgfQscrlqCH5njDAxwgXe6ODNVRw==
   dependencies:
-    "@babel/cli" "^7.14.5"
-    "@babel/core" "^7.14.6"
+    "@babel/cli" "^7.14.8"
+    "@babel/core" "^7.15.0"
     "@babel/plugin-proposal-class-properties" "^7.14.5"
     "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5"
     "@babel/plugin-proposal-numeric-separator" "^7.14.5"
@@ -1618,28 +1747,34 @@
     "@babel/plugin-syntax-import-meta" "^7.10.4"
     "@babel/plugin-syntax-top-level-await" "^7.14.5"
     "@babel/plugin-transform-regenerator" "^7.14.5"
-    "@babel/plugin-transform-runtime" "^7.14.5"
-    "@babel/preset-env" "^7.14.7"
+    "@babel/plugin-transform-runtime" "^7.15.0"
+    "@babel/preset-env" "^7.15.0"
     "@babel/preset-react" "^7.14.5"
-    "@babel/preset-typescript" "^7.14.5"
-    "@babel/register" "^7.14.5"
-    "@babel/runtime" "^7.14.6"
+    "@babel/preset-typescript" "^7.15.0"
+    "@babel/register" "^7.15.3"
+    "@babel/runtime" "^7.15.3"
+    "@rollup/plugin-alias" "^3.1.5"
+    "@rollup/plugin-commonjs" "^19.0.2"
+    "@rollup/plugin-inject" "^4.0.2"
+    "@rollup/plugin-json" "^4.1.0"
+    "@rollup/plugin-node-resolve" "^13.0.4"
     "@rushstack/eslint-patch" "^1.0.6"
-    "@typescript-eslint/eslint-plugin" "4.28.0"
-    "@typescript-eslint/parser" "4.28.0"
+    "@typescript-eslint/eslint-plugin" "4.29.2"
+    "@typescript-eslint/parser" "4.29.2"
     "@vue/component-compiler-utils" "^3.2.2"
-    babel-jest "^27.0.5"
+    babel-jest "^27.0.6"
     babel-plugin-module-extension-resolver "^1.0.0-rc.2"
     babel-plugin-module-resolver "^4.1.0"
-    babel-plugin-styled-components "^1.12.0"
-    browserslist "^4.16.6"
-    chalk "^4.1.1"
-    coveralls "^3.1.0"
-    eslint "^7.29.0"
+    babel-plugin-styled-components "^1.13.2"
+    browserslist "^4.16.7"
+    chalk "^4.1.2"
+    coveralls "^3.1.1"
+    eslint "^7.32.0"
     eslint-config-standard "^16.0.3"
-    eslint-import-resolver-node "^0.3.4"
+    eslint-import-resolver-node "^0.3.6"
     eslint-plugin-header "^3.1.1"
-    eslint-plugin-import "^2.23.4"
+    eslint-plugin-import "^2.24.0"
+    eslint-plugin-import-newlines "^1.1.4"
     eslint-plugin-node "^11.1.0"
     eslint-plugin-promise "^5.1.0"
     eslint-plugin-react "^7.24.0"
@@ -1651,118 +1786,129 @@
     gh-release "^6.0.0"
     glob "^7.1.7"
     glob2base "^0.0.12"
-    jest "^27.0.5"
-    jest-cli "^27.0.5"
-    jest-config "^27.0.5"
-    jest-haste-map "^27.0.5"
-    jest-resolve "^27.0.5"
+    jest "^27.0.6"
+    jest-cli "^27.0.6"
+    jest-config "^27.0.6"
+    jest-haste-map "^27.0.6"
+    jest-resolve "^27.0.6"
     madge "^4.0.2"
     minimatch "^3.0.4"
     mkdirp "^1.0.4"
-    prettier "^2.3.1"
+    prettier "^2.3.2"
     rimraf "^3.0.2"
-    typescript "^4.3.4"
-    yargs "^17.0.1"
+    rollup "^2.56.2"
+    typescript "^4.3.5"
+    yargs "^17.1.1"
 
-"@polkadot/keyring@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-7.0.1.tgz#666e903661b98279dc16d512be69f5ace4b58d8d"
-  integrity sha512-eSvG8Q4gUTRDFWj2lqTY/9NekGP8dtp+W6WmouKh0DDwHRawaVeDaq6UJYQv6XoBG1i+ZGPvErRQeGMPOn/mUQ==
+"@polkadot/keyring@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-7.2.1.tgz#5ed8e6c0edc61e3dd99ee647a227b9e3d955e8e6"
+  integrity sha512-WmiTsHKELX16uZWLvebDBckZIAXeJFfbcOM6m/VbMOjSV5C6xIKqiV3232Mn8ZuPKgsOf25Q78/IwJW1Dq53Qg==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/util" "7.0.1"
-    "@polkadot/util-crypto" "7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/util" "7.2.1"
+    "@polkadot/util-crypto" "7.2.1"
 
-"@polkadot/networks@7.0.1", "@polkadot/networks@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-7.0.1.tgz#e07c4b88e25711433e76d24fce4c7273c25dd38b"
-  integrity sha512-bJSvI7UgEpxmBKS8TMh+I1mfmCMwhClGdSs29kwU+K61IjBTKTt3yQJ/SflYIQV7QftGbz3oMfSkGbQbRHZqvQ==
+"@polkadot/networks@7.2.1", "@polkadot/networks@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-7.2.1.tgz#20c8d81fba4b48162bf360759d8d54c55d30128b"
+  integrity sha512-YX8oQ7QQ2oq3YowwOiv/C82l849V0ZEzpR26YrPgKSXbYFbasho3Akf0zalndZJZV1Bb8EiOkzGoJ3ffogSPxA==
   dependencies:
-    "@babel/runtime" "^7.14.6"
+    "@babel/runtime" "^7.15.3"
 
-"@polkadot/rpc-core@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-5.0.1.tgz#8460287532fe61c31505564df53e92ef6feba875"
-  integrity sha512-JMNOVQijjyJZNu9B8CJwIrQzGYzAp03uCBSbqYfzWBFnYVLKh7JmvOlkLnODM8uUYq0gVN4BaDUSPc39GpELAQ==
+"@polkadot/rpc-core@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-5.5.1.tgz#4ce0646becabe7736dfb81eb902e5933158646c8"
+  integrity sha512-hP7a55iSpgZVqxAIpK+v63eV/nD14Tm7C1rUmfKIS6gGJFJf+sQbTmp6d7+fuKxvYfFqBrFLU8IraOhLOQ5W3Q==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/rpc-provider" "5.0.1"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/util" "^7.0.1"
-    rxjs "^7.2.0"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/rpc-provider" "5.5.1"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/util" "^7.2.1"
+    rxjs "^7.3.0"
 
-"@polkadot/rpc-provider@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-5.0.1.tgz#ef022a123eb9073634b59c6e0f6e1705e96066a5"
-  integrity sha512-t+VKhMtQfQVgkZDqYnP/44KlBDmcCVo1/MvJ+DoNd7RUWUIBJt3v71G5gDSNeGMTyvxn0KK0qL4j+Nqr6c4FUQ==
+"@polkadot/rpc-provider@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-5.5.1.tgz#633f4a48605623092fb9017433f2b3cd70cd96bc"
+  integrity sha512-wOCKeeyUa7Dw3nxKkQntnfOO471icdzqT2V7bwloBOo+G2MX8nHImO0mW3QMfJygn4qoARF1PBo1PLbDUEDgog==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/util" "^7.0.1"
-    "@polkadot/util-crypto" "^7.0.1"
-    "@polkadot/x-fetch" "^7.0.1"
-    "@polkadot/x-global" "^7.0.1"
-    "@polkadot/x-ws" "^7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/util" "^7.2.1"
+    "@polkadot/util-crypto" "^7.2.1"
+    "@polkadot/x-fetch" "^7.2.1"
+    "@polkadot/x-global" "^7.2.1"
+    "@polkadot/x-ws" "^7.2.1"
     eventemitter3 "^4.0.7"
 
-"@polkadot/ts@0.3.89":
-  version "0.3.89"
-  resolved "https://registry.yarnpkg.com/@polkadot/ts/-/ts-0.3.89.tgz#c7a704ea284d04fcf4d581f5df156d5945480033"
-  integrity sha512-GC0H8wmVKebkieN2MHScjDDonzigIzkjl1Q4V1OhoRcfQbeZZ7vijeiVwP8Hw3wIw4GLKxxXeDrkKPWl/bcaHw==
+"@polkadot/ts@0.4.4":
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/@polkadot/ts/-/ts-0.4.4.tgz#e86aa47c2bcbc70ac8385b31014c81927c4b0a88"
+  integrity sha512-lzB8lg8GfdJlA7RdeoOJVFopecN4i++JndbUs6jW7AgRz+joeXQIIRomVgCNE52nW1uWpXMELnlvEP812v7sVw==
   dependencies:
-    "@types/chrome" "^0.0.144"
+    "@types/chrome" "^0.0.145"
 
-"@polkadot/typegen@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/typegen/-/typegen-5.0.1.tgz#718b517f4f1578441911096603577bd0a2a968e0"
-  integrity sha512-iFLJoWgIkn+J6MDw3AUWveP9qxVn1C+VeLJbpZ21St5WyeE148Tml0BmYnKLSXlaPMhZEwB+/IV3jpQ35dH4bw==
+"@polkadot/typegen@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/typegen/-/typegen-5.5.1.tgz#b31010716a142290d12f48ead6166851599055d0"
+  integrity sha512-ua55CVqT3+Y5fX9stpYUO+UhiJJ1RDTZ6vM2/Lndmsuda4lHQnUDrCnMmxKhM5OcyIlJlY5mF9dyO0kl5mTm+w==
   dependencies:
-    "@babel/core" "^7.14.6"
-    "@babel/register" "^7.14.5"
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/api" "5.0.1"
-    "@polkadot/rpc-provider" "5.0.1"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/util" "^7.0.1"
+    "@babel/core" "^7.15.0"
+    "@babel/register" "^7.15.3"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/api" "5.5.1"
+    "@polkadot/rpc-provider" "5.5.1"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/types-support" "5.5.1"
+    "@polkadot/util" "^7.2.1"
     handlebars "^4.7.7"
     websocket "^1.0.34"
-    yargs "^17.0.1"
+    yargs "^17.1.0"
 
-"@polkadot/types-known@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-5.0.1.tgz#21feb327fc4323733bf027c8d874a2aa3014b21f"
-  integrity sha512-AIhPlN4r14ZW4wdwHZD2nIe1DE61ZO9PsyrCyAU3ysl6Cw6TI+txDCN3aS/8XYuC7wDLEgLB9vJv2sVWdCzqJg==
+"@polkadot/types-known@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-5.5.1.tgz#b00b0d45cbd07b4e0c3199f8ba00d10a1bd3f63d"
+  integrity sha512-bxBRmZ0a3lwEyWkWOKqmDJfpNKh3cp9xo6IidrQU2S5OPMjFFercB+HwJjkNE1cMtShwBYTvDheUImNkdm+FXA==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/networks" "^7.0.1"
-    "@polkadot/types" "5.0.1"
-    "@polkadot/util" "^7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/networks" "^7.2.1"
+    "@polkadot/types" "5.5.1"
+    "@polkadot/util" "^7.2.1"
 
-"@polkadot/types@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-5.0.1.tgz#2a4e23e452f999eeae175b595470df0e426a930d"
-  integrity sha512-aN6JKeF7ZYi5irYAaUoDqth6qlOlB15C5vhlDOojEorYLfRs/R+GCrO+lPSs+bKmSxh7BSRh500ikI/xD4nx5A==
+"@polkadot/types-support@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-5.5.1.tgz#15556c2f31e79f0a6a821c7723f702757cb89462"
+  integrity sha512-57i1SdK8B+miGTAlDNdvbBuN6FguTnwzv2UPE2Zv3iQznTSZBkQZN16tIK/yMkQfhtO4ZzPcAnnSPZMncqh/Mg==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/util" "^7.0.1"
-    "@polkadot/util-crypto" "^7.0.1"
-    rxjs "^7.2.0"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/util" "^7.2.1"
+
+"@polkadot/types@5.5.1":
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-5.5.1.tgz#057e8f0fc2369c0741c0f9b0224418e8f25a6938"
+  integrity sha512-+Cm7Y6D/98WqL8ofONyZrVvE2CxzK3/z18bATIQIWhG2w9ir9PdWaFMZ3fLCRw2Ggaq88AknguK6kXeEPcKPrA==
+  dependencies:
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/util" "^7.2.1"
+    "@polkadot/util-crypto" "^7.2.1"
+    rxjs "^7.3.0"
 
-"@polkadot/util-crypto@7.0.1", "@polkadot/util-crypto@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-7.0.1.tgz#03109dc11323dad174fb2214d395855495def16a"
-  integrity sha512-dbvdsICoyOVw/K45RmHOP7wXE/7vj+NzEKGcKbiDt39nglHm6g2BTJ947PwwyNusTTAx82Q2iJ9vIZ1Kl0xG+g==
+"@polkadot/util-crypto@7.2.1", "@polkadot/util-crypto@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-7.2.1.tgz#7f1bbf031dac75090699083fc9e825c22e5d5f61"
+  integrity sha512-X3iGba/1JTL/0MNzMNEIlO9DNyKlwFV839jfGLDKhPbCuDmWp0NdQjF3mBmbvNwkXvn07WmhE7g3q9n5iTzqvQ==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/networks" "7.0.1"
-    "@polkadot/util" "7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/networks" "7.2.1"
+    "@polkadot/util" "7.2.1"
     "@polkadot/wasm-crypto" "^4.1.2"
-    "@polkadot/x-randomvalues" "7.0.1"
+    "@polkadot/x-randomvalues" "7.2.1"
     base-x "^3.0.8"
     base64-js "^1.5.1"
     blakejs "^1.1.1"
     bn.js "^4.11.9"
     create-hash "^1.2.0"
+    ed2curve "^0.3.0"
     elliptic "^6.5.4"
     hash.js "^1.1.7"
     js-sha3 "^0.8.0"
@@ -1770,14 +1916,14 @@
     tweetnacl "^1.0.3"
     xxhashjs "^0.2.2"
 
-"@polkadot/util@7.0.1", "@polkadot/util@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-7.0.1.tgz#79afd40473016876f51d65ebb9900a20108fe0a4"
-  integrity sha512-EtQlZL6ok0Ep+zRz2QHMUoJo/b3kFHVN2qqyD2+9sdqg0FGLmkzNFM+K6dasCMLXieJ1l0HoFsQppSo/leUeaA==
+"@polkadot/util@7.2.1", "@polkadot/util@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-7.2.1.tgz#abcad49f884534ff042c37480f63b9750c69341d"
+  integrity sha512-GilFg3i5dmu0H6dHEyh5bUw3yywmnFpEHfxFmKghL1ABDEr4qD0d/XAJ9UrzLFCBKbdTZsR0MDjgjVI2N84J1A==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/x-textdecoder" "7.0.1"
-    "@polkadot/x-textencoder" "7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/x-textdecoder" "7.2.1"
+    "@polkadot/x-textencoder" "7.2.1"
     "@types/bn.js" "^4.11.6"
     bn.js "^4.11.9"
     camelcase "^5.3.1"
@@ -1806,57 +1952,114 @@
     "@polkadot/wasm-crypto-asmjs" "^4.1.2"
     "@polkadot/wasm-crypto-wasm" "^4.1.2"
 
-"@polkadot/x-fetch@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-7.0.1.tgz#2db6fa19f4f4d9b2f4cf50ba78bf3aa947d7b982"
-  integrity sha512-9R38FjtlJcvdpEA7tVGmTmH4aiBCTABuLJdVSn3cYkgWfxDHeFMqjdFzTJ6Asa5cY0Ds3ZKsh9uccTQBQzV/HQ==
+"@polkadot/x-fetch@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-7.2.1.tgz#c9bee0316d31cd150b2cb6646ccdb77c6dc3d42d"
+  integrity sha512-osdZNPfrB50d7tfjVs4QRjfsb6xqC09JEeYzbUl24hUXPwtkQE8/379jayu1usPe9/JI2wKYGscdf/nRl4pBkA==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/x-global" "7.0.1"
-    "@types/node-fetch" "^2.5.11"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/x-global" "7.2.1"
+    "@types/node-fetch" "^2.5.12"
     node-fetch "^2.6.1"
 
-"@polkadot/x-global@7.0.1", "@polkadot/x-global@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-7.0.1.tgz#44fb248d3aaea557753318327149772969e96bff"
-  integrity sha512-gVVACSdRhHYRJejLEAL0mM9BZfY8N50VT2+15A7ALD1tVqwS4tz3P9vRW3Go7ZjfyAc83aEmh0PiQ8Nm1R+2Cg==
+"@polkadot/x-global@7.2.1", "@polkadot/x-global@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-7.2.1.tgz#32207936b7f939a21da608f82ca20535f9148cda"
+  integrity sha512-VNW+76TxEPqvBy3XMNV05mJRPRGZcYh3k5HjW4+asYeFunMahH4zjmCulhtD9SRI/TqdfHTiqDOqKNKe2xJcVg==
   dependencies:
-    "@babel/runtime" "^7.14.6"
+    "@babel/runtime" "^7.15.3"
 
-"@polkadot/x-randomvalues@7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-7.0.1.tgz#32036ae5d48645a062f6a1c3ebbf227236c806b8"
-  integrity sha512-UNoIFaz1xJPozruT+lo8BTeT8A3NM3PgWuru7Vs8OsIz0Phkg7lUWlpHu9PZHyQCyKlUryvkOA692IlVlNYy2Q==
+"@polkadot/x-randomvalues@7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-7.2.1.tgz#708b7a54bd90ec091ab54e125d8b52e0853ea86b"
+  integrity sha512-B4sjwX+gFweZ1YM1Cg/S9hAEx9E/gV/vqLW89PJB6+hyvsPS9eiVvfVpaOsohc7AgmuINm/bSQbNZvtC+BbbKw==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/x-global" "7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/x-global" "7.2.1"
 
-"@polkadot/x-textdecoder@7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-7.0.1.tgz#bb9bba94b2eb1612dd35c299f43ab74515db74d9"
-  integrity sha512-CFRnpI0cp1h2N1+ec551BLVLwV6OHG6Gj62EYcIOXR+o/SX/6MXm3Qcehm2YvfTKqktyIUSWmTwbWjGjuqPrpA==
+"@polkadot/x-textdecoder@7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-7.2.1.tgz#c52074dba9943a12583f3f8672a49399f10e00f3"
+  integrity sha512-yXSZ0P/D/8HT8gbkdTjw/1AKZIVbX3+mIfiDiN3VqUBzruV7ak5hA+D01I0woBGDqxWISoLQFtGrxPAQ8pwAcg==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/x-global" "7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/x-global" "7.2.1"
 
-"@polkadot/x-textencoder@7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-7.0.1.tgz#181d403c5dc1d94fd1e2147fd1c5c528d30d8805"
-  integrity sha512-m+QL1HNiu5GMz6cfr/udSA6fUTv3RyIybJb7v43EQCxqlj/L0J3cUHapFd6tqH9PElD6jPkH1pXcgYN8e7dWTQ==
+"@polkadot/x-textencoder@7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-7.2.1.tgz#2326b3b7f9a5e445d7e560c438effbe800e2b1f6"
+  integrity sha512-1aqfxmfKSOWeOxmGBmk+RYrpqGtWywS6t0y/R3FI+k+s8NfIfGdcjMcupKq7khPh92PvVGkur+CnM/y6chn4XA==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/x-global" "7.0.1"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/x-global" "7.2.1"
 
-"@polkadot/x-ws@^7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-7.0.1.tgz#8c22a61c0dd9b82865c7631e22ac147c2b73b118"
-  integrity sha512-VUn/6sCJUpvW9WhUK+DKo1uDrw4yO84twRcy5JSzvSiBTaSplhU9Q4qGFl2Atr3WIzAYYx1jQSm/j6AhPRji1w==
+"@polkadot/x-ws@^7.2.1":
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-7.2.1.tgz#5971ab630911cdecd0a46db6fb899e9086954e58"
+  integrity sha512-sYnOF0qNdMuGFiRGWAtpkQQYIP44JFzGywap0CskhNEyCc+zDBi4l/ta3qHjeGta+h9rdVjDeYk2J86EsKlkSw==
   dependencies:
-    "@babel/runtime" "^7.14.6"
-    "@polkadot/x-global" "7.0.1"
-    "@types/websocket" "^1.0.3"
+    "@babel/runtime" "^7.15.3"
+    "@polkadot/x-global" "7.2.1"
+    "@types/websocket" "^1.0.4"
     websocket "^1.0.34"
 
+"@rollup/plugin-alias@^3.1.5":
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-3.1.5.tgz#73356a3a1eab2e1e2fd952f9f53cd89fc740d952"
+  integrity sha512-yzUaSvCC/LJPbl9rnzX3HN7vy0tq7EzHoEiQl1ofh4n5r2Rd5bj/+zcJgaGA76xbw95/JjWQyvHg9rOJp2y0oQ==
+  dependencies:
+    slash "^3.0.0"
+
+"@rollup/plugin-commonjs@^19.0.2":
+  version "19.0.2"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.2.tgz#1ccc3d63878d1bc9846f8969f09dd3b3e4ecc244"
+  integrity sha512-gBjarfqlC7qs0AutpRW/hrFNm+cd2/QKxhwyFa+srbg1oX7rDsEU3l+W7LAUhsAp9mPJMAkXDhLbQaVwEaE8bA==
+  dependencies:
+    "@rollup/pluginutils" "^3.1.0"
+    commondir "^1.0.1"
+    estree-walker "^2.0.1"
+    glob "^7.1.6"
+    is-reference "^1.2.1"
+    magic-string "^0.25.7"
+    resolve "^1.17.0"
+
+"@rollup/plugin-inject@^4.0.2":
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.2.tgz#55b21bb244a07675f7fdde577db929c82fc17395"
+  integrity sha512-TSLMA8waJ7Dmgmoc8JfPnwUwVZgLjjIAM6MqeIFqPO2ODK36JqE0Cf2F54UTgCUuW8da93Mvoj75a6KAVWgylw==
+  dependencies:
+    "@rollup/pluginutils" "^3.0.4"
+    estree-walker "^1.0.1"
+    magic-string "^0.25.5"
+
+"@rollup/plugin-json@^4.1.0":
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3"
+  integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==
+  dependencies:
+    "@rollup/pluginutils" "^3.0.8"
+
+"@rollup/plugin-node-resolve@^13.0.4":
+  version "13.0.4"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.4.tgz#b10222f4145a019740acb7738402130d848660c0"
+  integrity sha512-eYq4TFy40O8hjeDs+sIxEH/jc9lyuI2k9DM557WN6rO5OpnC2qXMBNj4IKH1oHrnAazL49C5p0tgP0/VpqJ+/w==
+  dependencies:
+    "@rollup/pluginutils" "^3.1.0"
+    "@types/resolve" "1.17.1"
+    builtin-modules "^3.1.0"
+    deepmerge "^4.2.2"
+    is-module "^1.0.0"
+    resolve "^1.19.0"
+
+"@rollup/pluginutils@^3.0.4", "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
+  integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
+  dependencies:
+    "@types/estree" "0.0.39"
+    estree-walker "^1.0.1"
+    picomatch "^2.2.2"
+
 "@rushstack/eslint-patch@^1.0.6":
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.6.tgz#023d72a5c4531b4ce204528971700a78a85a0c50"
@@ -1945,14 +2148,24 @@
   resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.18.tgz#0c8e298dbff8205e2266606c1ea5fbdba29b46e4"
   integrity sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ==
 
-"@types/chrome@^0.0.144":
-  version "0.0.144"
-  resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.144.tgz#7dd9188e355aa17e3ad397f50b5cd3ad12caf788"
-  integrity sha512-BgoiO7/KP9hRNrCR2Wq+aKWT5Dh9bTofuWaRtcqPcj8YKhZojQgb6sSdIqvds2C+eO63BwaR9KHVMYYgZdGGBg==
+"@types/chrome@^0.0.145":
+  version "0.0.145"
+  resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.145.tgz#6c53ae0af5f25350b07bfd24cf459b5fe65cd9b8"
+  integrity sha512-vLvTMmfc8mvwOZzkmn2UwlWSNu0t0txBkyuIv8NgihRkvFCe6XJX65YZAgAP/RdBit3enhU2GTxCr+prn4uZmA==
   dependencies:
     "@types/filesystem" "*"
     "@types/har-format" "*"
 
+"@types/estree@*":
+  version "0.0.50"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
+  integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
+
+"@types/estree@0.0.39":
+  version "0.0.39"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
+  integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
+
 "@types/filesystem@*":
   version "0.0.30"
   resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.30.tgz#a7373a2edf34d13e298baf7ee1101f738b2efb7e"
@@ -2011,10 +2224,10 @@
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.2.tgz#91daa226eb8c2ff261e6a8cbf8c7304641e095e0"
   integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==
 
-"@types/node-fetch@^2.5.11":
-  version "2.5.11"
-  resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.11.tgz#ce22a2e65fc8999f4dbdb7ddbbcf187d755169e4"
-  integrity sha512-2upCKaqVZETDRb8A2VTaRymqFBEgH8u6yr96b/u3+1uQEPDRo3mJLEiPk7vdXBHRtjwkjqzFYMJXrt0Z9QsYjQ==
+"@types/node-fetch@^2.5.12":
+  version "2.5.12"
+  resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66"
+  integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==
   dependencies:
     "@types/node" "*"
     form-data "^3.0.0"
@@ -2046,6 +2259,13 @@
   resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.0.tgz#2e8332cc7363f887d32ec5496b207d26ba8052bb"
   integrity sha512-hkc1DATxFLQo4VxPDpMH1gCkPpBbpOoJ/4nhuXw4n63/0R6bCpQECj4+K226UJ4JO/eJQz+1mC2I7JsWanAdQw==
 
+"@types/resolve@1.17.1":
+  version "1.17.1"
+  resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
+  integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/secp256k1@^4.0.1":
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.2.tgz#20c29a87149d980f64464e56539bf4810fdb5d1d"
@@ -2058,10 +2278,10 @@
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
   integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
 
-"@types/websocket@^1.0.3":
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.3.tgz#49e09f939afd0ccdee4f7108d4712ec9feb0f153"
-  integrity sha512-ZdoTSwmDsKR7l1I8fpfQtmTI/hUwlOvE3q0iyJsp4tXU0MkdrYowimDzwxjhQvxU4qjhHLd3a6ig0OXRbLgIdw==
+"@types/websocket@^1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.4.tgz#1dc497280d8049a5450854dd698ee7e6ea9e60b8"
+  integrity sha512-qn1LkcFEKK8RPp459jkjzsfpbsx36BBt3oC3pITYtkoBw/aVX+EZFa5j3ThCRTNpLFvIMr5dSTD4RaMdilIOpA==
   dependencies:
     "@types/node" "*"
 
@@ -2077,13 +2297,13 @@
   dependencies:
     "@types/yargs-parser" "*"
 
-"@typescript-eslint/eslint-plugin@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz#1a66f03b264844387beb7dc85e1f1d403bd1803f"
-  integrity sha512-KcF6p3zWhf1f8xO84tuBailV5cN92vhS+VT7UJsPzGBm9VnQqfI9AsiMUFUCYHTYPg1uCCo+HyiDnpDuvkAMfQ==
+"@typescript-eslint/eslint-plugin@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.2.tgz#f54dc0a32b8f61c6024ab8755da05363b733838d"
+  integrity sha512-x4EMgn4BTfVd9+Z+r+6rmWxoAzBaapt4QFqE+d8L8sUtYZYLDTK6VG/y/SMMWA5t1/BVU5Kf+20rX4PtWzUYZg==
   dependencies:
-    "@typescript-eslint/experimental-utils" "4.28.0"
-    "@typescript-eslint/scope-manager" "4.28.0"
+    "@typescript-eslint/experimental-utils" "4.29.2"
+    "@typescript-eslint/scope-manager" "4.29.2"
     debug "^4.3.1"
     functional-red-black-tree "^1.0.1"
     regexpp "^3.1.0"
@@ -2103,18 +2323,6 @@
     semver "^7.3.5"
     tsutils "^3.21.0"
 
-"@typescript-eslint/experimental-utils@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz#13167ed991320684bdc23588135ae62115b30ee0"
-  integrity sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ==
-  dependencies:
-    "@types/json-schema" "^7.0.7"
-    "@typescript-eslint/scope-manager" "4.28.0"
-    "@typescript-eslint/types" "4.28.0"
-    "@typescript-eslint/typescript-estree" "4.28.0"
-    eslint-scope "^5.1.1"
-    eslint-utils "^3.0.0"
-
 "@typescript-eslint/experimental-utils@4.28.5":
   version "4.28.5"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz#66c28bef115b417cf9d80812a713e0e46bb42a64"
@@ -2127,14 +2335,26 @@
     eslint-scope "^5.1.1"
     eslint-utils "^3.0.0"
 
-"@typescript-eslint/parser@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.0.tgz#2404c16751a28616ef3abab77c8e51d680a12caa"
-  integrity sha512-7x4D22oPY8fDaOCvkuXtYYTQ6mTMmkivwEzS+7iml9F9VkHGbbZ3x4fHRwxAb5KeuSkLqfnYjs46tGx2Nour4A==
+"@typescript-eslint/experimental-utils@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.2.tgz#5f67fb5c5757ef2cb3be64817468ba35c9d4e3b7"
+  integrity sha512-P6mn4pqObhftBBPAv4GQtEK7Yos1fz/MlpT7+YjH9fTxZcALbiiPKuSIfYP/j13CeOjfq8/fr9Thr2glM9ub7A==
+  dependencies:
+    "@types/json-schema" "^7.0.7"
+    "@typescript-eslint/scope-manager" "4.29.2"
+    "@typescript-eslint/types" "4.29.2"
+    "@typescript-eslint/typescript-estree" "4.29.2"
+    eslint-scope "^5.1.1"
+    eslint-utils "^3.0.0"
+
+"@typescript-eslint/parser@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.29.2.tgz#1c7744f4c27aeb74610c955d3dce9250e95c370a"
+  integrity sha512-WQ6BPf+lNuwteUuyk1jD/aHKqMQ9jrdCn7Gxt9vvBnzbpj7aWEf+aZsJ1zvTjx5zFxGCt000lsbD9tQPEL8u6g==
   dependencies:
-    "@typescript-eslint/scope-manager" "4.28.0"
-    "@typescript-eslint/types" "4.28.0"
-    "@typescript-eslint/typescript-estree" "4.28.0"
+    "@typescript-eslint/scope-manager" "4.29.2"
+    "@typescript-eslint/types" "4.29.2"
+    "@typescript-eslint/typescript-estree" "4.29.2"
     debug "^4.3.1"
 
 "@typescript-eslint/parser@^4.28.5":
@@ -2147,14 +2367,6 @@
     "@typescript-eslint/typescript-estree" "4.28.5"
     debug "^4.3.1"
 
-"@typescript-eslint/scope-manager@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz#6a3009d2ab64a30fc8a1e257a1a320067f36a0ce"
-  integrity sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==
-  dependencies:
-    "@typescript-eslint/types" "4.28.0"
-    "@typescript-eslint/visitor-keys" "4.28.0"
-
 "@typescript-eslint/scope-manager@4.28.5":
   version "4.28.5"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz#3a1b70c50c1535ac33322786ea99ebe403d3b923"
@@ -2163,33 +2375,28 @@
     "@typescript-eslint/types" "4.28.5"
     "@typescript-eslint/visitor-keys" "4.28.5"
 
+"@typescript-eslint/scope-manager@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.2.tgz#442b0f029d981fa402942715b1718ac7fcd5aa1b"
+  integrity sha512-mfHmvlQxmfkU8D55CkZO2sQOueTxLqGvzV+mG6S/6fIunDiD2ouwsAoiYCZYDDK73QCibYjIZmGhpvKwAB5BOA==
+  dependencies:
+    "@typescript-eslint/types" "4.29.2"
+    "@typescript-eslint/visitor-keys" "4.29.2"
+
 "@typescript-eslint/types@4.27.0":
   version "4.27.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.27.0.tgz#712b408519ed699baff69086bc59cd2fc13df8d8"
   integrity sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==
 
-"@typescript-eslint/types@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.0.tgz#a33504e1ce7ac51fc39035f5fe6f15079d4dafb0"
-  integrity sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==
-
 "@typescript-eslint/types@4.28.5":
   version "4.28.5"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.5.tgz#d33edf8e429f0c0930a7c3d44e9b010354c422e9"
   integrity sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA==
 
-"@typescript-eslint/typescript-estree@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz#e66d4e5aa2ede66fec8af434898fe61af10c71cf"
-  integrity sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==
-  dependencies:
-    "@typescript-eslint/types" "4.28.0"
-    "@typescript-eslint/visitor-keys" "4.28.0"
-    debug "^4.3.1"
-    globby "^11.0.3"
-    is-glob "^4.0.1"
-    semver "^7.3.5"
-    tsutils "^3.21.0"
+"@typescript-eslint/types@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.2.tgz#fc0489c6b89773f99109fb0aa0aaddff21f52fcd"
+  integrity sha512-K6ApnEXId+WTGxqnda8z4LhNMa/pZmbTFkDxEBLQAbhLZL50DjeY0VIDCml/0Y3FlcbqXZrABqrcKxq+n0LwzQ==
 
 "@typescript-eslint/typescript-estree@4.28.5":
   version "4.28.5"
@@ -2204,6 +2411,19 @@
     semver "^7.3.5"
     tsutils "^3.21.0"
 
+"@typescript-eslint/typescript-estree@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.2.tgz#a0ea8b98b274adbb2577100ba545ddf8bf7dc219"
+  integrity sha512-TJ0/hEnYxapYn9SGn3dCnETO0r+MjaxtlWZ2xU+EvytF0g4CqTpZL48SqSNn2hXsPolnewF30pdzR9a5Lj3DNg==
+  dependencies:
+    "@typescript-eslint/types" "4.29.2"
+    "@typescript-eslint/visitor-keys" "4.29.2"
+    debug "^4.3.1"
+    globby "^11.0.3"
+    is-glob "^4.0.1"
+    semver "^7.3.5"
+    tsutils "^3.21.0"
+
 "@typescript-eslint/typescript-estree@^4.8.2":
   version "4.27.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz#189a7b9f1d0717d5cccdcc17247692dedf7a09da"
@@ -2225,14 +2445,6 @@
     "@typescript-eslint/types" "4.27.0"
     eslint-visitor-keys "^2.0.0"
 
-"@typescript-eslint/visitor-keys@4.28.0":
-  version "4.28.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz#255c67c966ec294104169a6939d96f91c8a89434"
-  integrity sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==
-  dependencies:
-    "@typescript-eslint/types" "4.28.0"
-    eslint-visitor-keys "^2.0.0"
-
 "@typescript-eslint/visitor-keys@4.28.5":
   version "4.28.5"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz#ffee2c602762ed6893405ee7c1144d9cc0a29675"
@@ -2241,6 +2453,14 @@
     "@typescript-eslint/types" "4.28.5"
     eslint-visitor-keys "^2.0.0"
 
+"@typescript-eslint/visitor-keys@4.29.2":
+  version "4.29.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.2.tgz#d2da7341f3519486f50655159f4e5ecdcb2cd1df"
+  integrity sha512-bDgJLQ86oWHJoZ1ai4TZdgXzJxsea3Ee9u9wsTAvjChdj2WLcVsgWYAPeY7RQMn16tKrlQaBnpKv7KBfs4EQag==
+  dependencies:
+    "@typescript-eslint/types" "4.29.2"
+    eslint-visitor-keys "^2.0.0"
+
 "@ungap/promise-all-settled@1.1.2":
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
@@ -2606,16 +2826,16 @@
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
   integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
 
-babel-jest@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.5.tgz#cd34c033ada05d1362211e5152391fd7a88080c8"
-  integrity sha512-bTMAbpCX7ldtfbca2llYLeSFsDM257aspyAOpsdrdSrBqoLkWCy4HPYTXtXWaSLgFPjrJGACL65rzzr4RFGadw==
+babel-jest@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.6.tgz#e99c6e0577da2655118e3608b68761a5a69bd0d8"
+  integrity sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA==
   dependencies:
-    "@jest/transform" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/transform" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/babel__core" "^7.1.14"
     babel-plugin-istanbul "^6.0.0"
-    babel-preset-jest "^27.0.1"
+    babel-preset-jest "^27.0.6"
     chalk "^4.0.0"
     graceful-fs "^4.2.4"
     slash "^3.0.0"
@@ -2638,10 +2858,10 @@
     istanbul-lib-instrument "^4.0.0"
     test-exclude "^6.0.0"
 
-babel-plugin-jest-hoist@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.1.tgz#a6d10e484c93abff0f4e95f437dad26e5736ea11"
-  integrity sha512-sqBF0owAcCDBVEDtxqfYr2F36eSHdx7lAVGyYuOBRnKdD6gzcy0I0XrAYCZgOA3CRrLhmR+Uae9nogPzmAtOfQ==
+babel-plugin-jest-hoist@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz#f7c6b3d764af21cb4a2a1ab6870117dbde15b456"
+  integrity sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw==
   dependencies:
     "@babel/template" "^7.3.3"
     "@babel/types" "^7.3.3"
@@ -2688,10 +2908,10 @@
   dependencies:
     "@babel/helper-define-polyfill-provider" "^0.2.2"
 
-babel-plugin-styled-components@^1.12.0:
-  version "1.12.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz#1dec1676512177de6b827211e9eda5a30db4f9b9"
-  integrity sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==
+babel-plugin-styled-components@^1.13.2:
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.2.tgz#ebe0e6deff51d7f93fceda1819e9b96aeb88278d"
+  integrity sha512-Vb1R3d4g+MUfPQPVDMCGjm3cDocJEUTR7Xq7QS95JWWeksN1wdFRYpD2kulDgI3Huuaf1CZd+NK4KQmqUFh5dA==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.0.0"
     "@babel/helper-module-imports" "^7.0.0"
@@ -2721,12 +2941,12 @@
     "@babel/plugin-syntax-optional-chaining" "^7.8.3"
     "@babel/plugin-syntax-top-level-await" "^7.8.3"
 
-babel-preset-jest@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.1.tgz#7a50c75d16647c23a2cf5158d5bb9eb206b10e20"
-  integrity sha512-nIBIqCEpuiyhvjQs2mVNwTxQQa2xk70p9Dd/0obQGBf8FBzbnI8QhQKzLsWMN2i6q+5B0OcWDtrboBX5gmOLyA==
+babel-preset-jest@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz#909ef08e9f24a4679768be2f60a3df0856843f9d"
+  integrity sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw==
   dependencies:
-    babel-plugin-jest-hoist "^27.0.1"
+    babel-plugin-jest-hoist "^27.0.6"
     babel-preset-current-node-syntax "^1.0.0"
 
 balanced-match@^1.0.0:
@@ -2966,6 +3186,17 @@
     escalade "^3.1.1"
     node-releases "^1.1.71"
 
+browserslist@^4.16.7:
+  version "4.16.7"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.7.tgz#108b0d1ef33c4af1b587c54f390e7041178e4335"
+  integrity sha512-7I4qVwqZltJ7j37wObBe3SoTz+nS8APaNcrBOlgoirb6/HbEU2XxW/LpUDTCngM6iauwFqmRTuOMfyKnFGY5JA==
+  dependencies:
+    caniuse-lite "^1.0.30001248"
+    colorette "^1.2.2"
+    electron-to-chromium "^1.3.793"
+    escalade "^3.1.1"
+    node-releases "^1.1.73"
+
 bs58@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
@@ -3024,6 +3255,11 @@
   resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
   integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
 
+builtin-modules@^3.1.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
+  integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==
+
 bytes@3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
@@ -3085,6 +3321,11 @@
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001238.tgz#e6a8b45455c5de601718736d0242feef0ecdda15"
   integrity sha512-bZGam2MxEt7YNsa2VwshqWQMwrYs5tR5WZQRYSuFxsBQunWjBuXhN4cS9nV5FFb1Z9y+DoQcQ0COyQbv6A+CKw==
 
+caniuse-lite@^1.0.30001248:
+  version "1.0.30001251"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz#6853a606ec50893115db660f82c094d18f096d85"
+  integrity sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A==
+
 caseless@~0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -3134,6 +3375,14 @@
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
 
+chalk@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 changelog-parser@^2.0.0:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/changelog-parser/-/changelog-parser-2.8.0.tgz#c14293e3e8fab797913c722de965480198650108"
@@ -3472,12 +3721,12 @@
     browserslist "^4.16.6"
     semver "7.0.0"
 
-core-js-compat@^3.15.0:
-  version "3.15.1"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.1.tgz#1afe233716d37ee021956ef097594071b2b585a7"
-  integrity sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==
+core-js-compat@^3.16.0:
+  version "3.16.1"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.16.1.tgz#c44b7caa2dcb94b673a98f27eee1c8312f55bc2d"
+  integrity sha512-NHXQXvRbd4nxp9TEmooTJLUf94ySUG6+DSsscBpTftN1lQLQ4LjnWvc7AoIo4UjDsFF3hB8Uh5LLCRRdaiT5MQ==
   dependencies:
-    browserslist "^4.16.6"
+    browserslist "^4.16.7"
     semver "7.0.0"
 
 core-util-is@1.0.2, core-util-is@~1.0.0:
@@ -3493,10 +3742,10 @@
     object-assign "^4"
     vary "^1"
 
-coveralls@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.0.tgz#13c754d5e7a2dd8b44fe5269e21ca394fb4d615b"
-  integrity sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==
+coveralls@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.1.tgz#f5d4431d8b5ae69c5079c8f8ca00d64ac77cf081"
+  integrity sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==
   dependencies:
     js-yaml "^3.13.1"
     lcov-parse "^1.0.0"
@@ -3883,10 +4132,10 @@
     node-source-walk "^4.2.0"
     typescript "^3.9.7"
 
-diff-sequences@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.1.tgz#9c9801d52ed5f576ff0a20e3022a13ee6e297e7c"
-  integrity sha512-XPLijkfJUh/PIBnfkcSHgvD6tlYixmcMAn3osTk6jt+H0v/mgURto1XUiD9DKuGX5NDoVS6dSlA23gd9FUaCFg==
+diff-sequences@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723"
+  integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==
 
 diff@5.0.0:
   version "5.0.0"
@@ -3970,6 +4219,13 @@
     jsbn "~0.1.0"
     safer-buffer "^2.1.0"
 
+ed2curve@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/ed2curve/-/ed2curve-0.3.0.tgz#322b575152a45305429d546b071823a93129a05d"
+  integrity sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ==
+  dependencies:
+    tweetnacl "1.x.x"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -3980,6 +4236,11 @@
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09"
   integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==
 
+electron-to-chromium@^1.3.793:
+  version "1.3.807"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.807.tgz#c2eb803f4f094869b1a24151184ffbbdbf688b1f"
+  integrity sha512-p8uxxg2a23zRsvQ2uwA/OOI+O4BQxzaR7YKMIGGGQCpYmkFX2CVF5f0/hxLMV7yCr7nnJViCwHLhPfs52rIYCA==
+
 elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4:
   version "6.5.4"
   resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
@@ -4151,18 +4412,18 @@
   resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz#6c8761e544e96c531ff92642eeb87842b8488516"
   integrity sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==
 
-eslint-import-resolver-node@^0.3.4:
-  version "0.3.4"
-  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
-  integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==
+eslint-import-resolver-node@^0.3.5, eslint-import-resolver-node@^0.3.6:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
+  integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
   dependencies:
-    debug "^2.6.9"
-    resolve "^1.13.1"
+    debug "^3.2.7"
+    resolve "^1.20.0"
 
-eslint-module-utils@^2.6.1:
-  version "2.6.1"
-  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233"
-  integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==
+eslint-module-utils@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz#94e5540dd15fe1522e8ffa3ec8db3b7fa7e7a534"
+  integrity sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==
   dependencies:
     debug "^3.2.7"
     pkg-dir "^2.0.0"
@@ -4180,17 +4441,22 @@
   resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6"
   integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==
 
-eslint-plugin-import@^2.23.4:
-  version "2.23.4"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97"
-  integrity sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==
+eslint-plugin-import-newlines@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.1.4.tgz#d69d03fe512b2f54bc781d1dfc51a4ad99df7a52"
+  integrity sha512-GCIM+524XQOFcEPinEyrvktQHkQq+k+kYCwbRrIioGBVGnk3RGDFWv5BPqBQCDci6SNZCVgIOi3/FmtDetbxvA==
+
+eslint-plugin-import@^2.24.0:
+  version "2.24.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.0.tgz#697ffd263e24da5e84e03b282f5fb62251777177"
+  integrity sha512-Kc6xqT9hiYi2cgybOc0I2vC9OgAYga5o/rAFinam/yF/t5uBqxQbauNPMC6fgb640T/89P0gFoO27FOilJ/Cqg==
   dependencies:
     array-includes "^3.1.3"
     array.prototype.flat "^1.2.4"
     debug "^2.6.9"
     doctrine "^2.1.0"
-    eslint-import-resolver-node "^0.3.4"
-    eslint-module-utils "^2.6.1"
+    eslint-import-resolver-node "^0.3.5"
+    eslint-module-utils "^2.6.2"
     find-up "^2.0.0"
     has "^1.0.3"
     is-core-module "^2.4.0"
@@ -4285,13 +4551,14 @@
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
   integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
 
-eslint@^7.29.0:
-  version "7.29.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.29.0.tgz#ee2a7648f2e729485e4d0bd6383ec1deabc8b3c0"
-  integrity sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==
+eslint@^7.31.0:
+  version "7.31.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca"
+  integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA==
   dependencies:
     "@babel/code-frame" "7.12.11"
-    "@eslint/eslintrc" "^0.4.2"
+    "@eslint/eslintrc" "^0.4.3"
+    "@humanwhocodes/config-array" "^0.5.0"
     ajv "^6.10.0"
     chalk "^4.0.0"
     cross-spawn "^7.0.2"
@@ -4330,10 +4597,10 @@
     text-table "^0.2.0"
     v8-compile-cache "^2.0.3"
 
-eslint@^7.31.0:
-  version "7.31.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca"
-  integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA==
+eslint@^7.32.0:
+  version "7.32.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
+  integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
   dependencies:
     "@babel/code-frame" "7.12.11"
     "@eslint/eslintrc" "^0.4.3"
@@ -4414,6 +4681,16 @@
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
   integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
 
+estree-walker@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
+  integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
+
+estree-walker@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+  integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
 esutils@^2.0.2:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@@ -4574,17 +4851,17 @@
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-expect@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.2.tgz#e66ca3a4c9592f1c019fa1d46459a9d2084f3422"
-  integrity sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w==
+expect@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.6.tgz#a4d74fbe27222c718fff68ef49d78e26a8fd4c05"
+  integrity sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     ansi-styles "^5.0.0"
-    jest-get-type "^27.0.1"
-    jest-matcher-utils "^27.0.2"
-    jest-message-util "^27.0.2"
-    jest-regex-util "^27.0.1"
+    jest-get-type "^27.0.6"
+    jest-matcher-utils "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-regex-util "^27.0.6"
 
 express@^4.14.0:
   version "4.17.1"
@@ -5857,6 +6134,11 @@
   resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
   integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
 
+is-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
+  integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
+
 is-negative-zero@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
@@ -5931,6 +6213,13 @@
   resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
   integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
 
+is-reference@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
+  integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
+  dependencies:
+    "@types/estree" "*"
+
 is-regex@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
@@ -6088,226 +6377,226 @@
     has-to-string-tag-x "^1.2.0"
     is-object "^1.0.1"
 
-jest-changed-files@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.2.tgz#997253042b4a032950fc5f56abf3c5d1f8560801"
-  integrity sha512-eMeb1Pn7w7x3wue5/vF73LPCJ7DKQuC9wQUR5ebP9hDPpk5hzcT/3Hmz3Q5BOFpR3tgbmaWhJcMTVgC8Z1NuMw==
+jest-changed-files@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.6.tgz#bed6183fcdea8a285482e3b50a9a7712d49a7a8b"
+  integrity sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     execa "^5.0.0"
     throat "^6.0.1"
 
-jest-circus@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.5.tgz#b5e327f1d6857c8485126f8e364aefa4378debaa"
-  integrity sha512-p5rO90o1RTh8LPOG6l0Fc9qgp5YGv+8M5CFixhMh7gGHtGSobD1AxX9cjFZujILgY8t30QZ7WVvxlnuG31r8TA==
+jest-circus@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.6.tgz#dd4df17c4697db6a2c232aaad4e9cec666926668"
+  integrity sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q==
   dependencies:
-    "@jest/environment" "^27.0.5"
-    "@jest/test-result" "^27.0.2"
-    "@jest/types" "^27.0.2"
+    "@jest/environment" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     chalk "^4.0.0"
     co "^4.6.0"
     dedent "^0.7.0"
-    expect "^27.0.2"
+    expect "^27.0.6"
     is-generator-fn "^2.0.0"
-    jest-each "^27.0.2"
-    jest-matcher-utils "^27.0.2"
-    jest-message-util "^27.0.2"
-    jest-runtime "^27.0.5"
-    jest-snapshot "^27.0.5"
-    jest-util "^27.0.2"
-    pretty-format "^27.0.2"
+    jest-each "^27.0.6"
+    jest-matcher-utils "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-runtime "^27.0.6"
+    jest-snapshot "^27.0.6"
+    jest-util "^27.0.6"
+    pretty-format "^27.0.6"
     slash "^3.0.0"
     stack-utils "^2.0.3"
     throat "^6.0.1"
 
-jest-cli@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.5.tgz#f359ba042624cffb96b713010a94bffb7498a37c"
-  integrity sha512-kZqY020QFOFQKVE2knFHirTBElw3/Q0kUbDc3nMfy/x+RQ7zUY89SUuzpHHJoSX1kX7Lq569ncvjNqU3Td/FCA==
+jest-cli@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.6.tgz#d021e5f4d86d6a212450d4c7b86cb219f1e6864f"
+  integrity sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg==
   dependencies:
-    "@jest/core" "^27.0.5"
-    "@jest/test-result" "^27.0.2"
-    "@jest/types" "^27.0.2"
+    "@jest/core" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/types" "^27.0.6"
     chalk "^4.0.0"
     exit "^0.1.2"
     graceful-fs "^4.2.4"
     import-local "^3.0.2"
-    jest-config "^27.0.5"
-    jest-util "^27.0.2"
-    jest-validate "^27.0.2"
+    jest-config "^27.0.6"
+    jest-util "^27.0.6"
+    jest-validate "^27.0.6"
     prompts "^2.0.1"
     yargs "^16.0.3"
 
-jest-config@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.5.tgz#683da3b0d8237675c29c817f6e3aba1481028e19"
-  integrity sha512-zCUIXag7QIXKEVN4kUKbDBDi9Q53dV5o3eNhGqe+5zAbt1vLs4VE3ceWaYrOub0L4Y7E9pGfM84TX/0ARcE+Qw==
+jest-config@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.6.tgz#119fb10f149ba63d9c50621baa4f1f179500277f"
+  integrity sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w==
   dependencies:
     "@babel/core" "^7.1.0"
-    "@jest/test-sequencer" "^27.0.5"
-    "@jest/types" "^27.0.2"
-    babel-jest "^27.0.5"
+    "@jest/test-sequencer" "^27.0.6"
+    "@jest/types" "^27.0.6"
+    babel-jest "^27.0.6"
     chalk "^4.0.0"
     deepmerge "^4.2.2"
     glob "^7.1.1"
     graceful-fs "^4.2.4"
     is-ci "^3.0.0"
-    jest-circus "^27.0.5"
-    jest-environment-jsdom "^27.0.5"
-    jest-environment-node "^27.0.5"
-    jest-get-type "^27.0.1"
-    jest-jasmine2 "^27.0.5"
-    jest-regex-util "^27.0.1"
-    jest-resolve "^27.0.5"
-    jest-runner "^27.0.5"
-    jest-util "^27.0.2"
-    jest-validate "^27.0.2"
+    jest-circus "^27.0.6"
+    jest-environment-jsdom "^27.0.6"
+    jest-environment-node "^27.0.6"
+    jest-get-type "^27.0.6"
+    jest-jasmine2 "^27.0.6"
+    jest-regex-util "^27.0.6"
+    jest-resolve "^27.0.6"
+    jest-runner "^27.0.6"
+    jest-util "^27.0.6"
+    jest-validate "^27.0.6"
     micromatch "^4.0.4"
-    pretty-format "^27.0.2"
+    pretty-format "^27.0.6"
 
-jest-diff@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.2.tgz#f315b87cee5dc134cf42c2708ab27375cc3f5a7e"
-  integrity sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw==
+jest-diff@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.6.tgz#4a7a19ee6f04ad70e0e3388f35829394a44c7b5e"
+  integrity sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==
   dependencies:
     chalk "^4.0.0"
-    diff-sequences "^27.0.1"
-    jest-get-type "^27.0.1"
-    pretty-format "^27.0.2"
+    diff-sequences "^27.0.6"
+    jest-get-type "^27.0.6"
+    pretty-format "^27.0.6"
 
-jest-docblock@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.1.tgz#bd9752819b49fa4fab1a50b73eb58c653b962e8b"
-  integrity sha512-TA4+21s3oebURc7VgFV4r7ltdIJ5rtBH1E3Tbovcg7AV+oLfD5DcJ2V2vJ5zFA9sL5CFd/d2D6IpsAeSheEdrA==
+jest-docblock@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3"
+  integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==
   dependencies:
     detect-newline "^3.0.0"
 
-jest-each@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.2.tgz#865ddb4367476ced752167926b656fa0dcecd8c7"
-  integrity sha512-OLMBZBZ6JkoXgUenDtseFRWA43wVl2BwmZYIWQws7eS7pqsIvePqj/jJmEnfq91ALk3LNphgwNK/PRFBYi7ITQ==
+jest-each@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.6.tgz#cee117071b04060158dc8d9a66dc50ad40ef453b"
+  integrity sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     chalk "^4.0.0"
-    jest-get-type "^27.0.1"
-    jest-util "^27.0.2"
-    pretty-format "^27.0.2"
+    jest-get-type "^27.0.6"
+    jest-util "^27.0.6"
+    pretty-format "^27.0.6"
 
-jest-environment-jsdom@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.5.tgz#c36771977cf4490a9216a70473b39161d193c212"
-  integrity sha512-ToWhViIoTl5738oRaajTMgYhdQL73UWPoV4GqHGk2DPhs+olv8OLq5KoQW8Yf+HtRao52XLqPWvl46dPI88PdA==
+jest-environment-jsdom@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz#f66426c4c9950807d0a9f209c590ce544f73291f"
+  integrity sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw==
   dependencies:
-    "@jest/environment" "^27.0.5"
-    "@jest/fake-timers" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/environment" "^27.0.6"
+    "@jest/fake-timers" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
-    jest-mock "^27.0.3"
-    jest-util "^27.0.2"
+    jest-mock "^27.0.6"
+    jest-util "^27.0.6"
     jsdom "^16.6.0"
 
-jest-environment-node@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.5.tgz#b7238fc2b61ef2fb9563a3b7653a95fa009a6a54"
-  integrity sha512-47qqScV/WMVz5OKF5TWpAeQ1neZKqM3ySwNveEnLyd+yaE/KT6lSMx/0SOx60+ZUcVxPiESYS+Kt2JS9y4PpkQ==
+jest-environment-node@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.6.tgz#a6699b7ceb52e8d68138b9808b0c404e505f3e07"
+  integrity sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w==
   dependencies:
-    "@jest/environment" "^27.0.5"
-    "@jest/fake-timers" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/environment" "^27.0.6"
+    "@jest/fake-timers" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
-    jest-mock "^27.0.3"
-    jest-util "^27.0.2"
+    jest-mock "^27.0.6"
+    jest-util "^27.0.6"
 
-jest-get-type@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.1.tgz#34951e2b08c8801eb28559d7eb732b04bbcf7815"
-  integrity sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg==
+jest-get-type@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe"
+  integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==
 
-jest-haste-map@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.5.tgz#2e1e55073b5328410a2c0d74b334e513d71f3470"
-  integrity sha512-3LFryGSHxwPFHzKIs6W0BGA2xr6g1MvzSjR3h3D8K8Uqy4vbRm/grpGHzbPtIbOPLC6wFoViRrNEmd116QWSkw==
+jest-haste-map@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.6.tgz#4683a4e68f6ecaa74231679dca237279562c8dc7"
+  integrity sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     "@types/graceful-fs" "^4.1.2"
     "@types/node" "*"
     anymatch "^3.0.3"
     fb-watchman "^2.0.0"
     graceful-fs "^4.2.4"
-    jest-regex-util "^27.0.1"
-    jest-serializer "^27.0.1"
-    jest-util "^27.0.2"
-    jest-worker "^27.0.2"
+    jest-regex-util "^27.0.6"
+    jest-serializer "^27.0.6"
+    jest-util "^27.0.6"
+    jest-worker "^27.0.6"
     micromatch "^4.0.4"
     walker "^1.0.7"
   optionalDependencies:
     fsevents "^2.3.2"
 
-jest-jasmine2@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.5.tgz#8a6eb2a685cdec3af13881145c77553e4e197776"
-  integrity sha512-m3TojR19sFmTn79QoaGy1nOHBcLvtLso6Zh7u+gYxZWGcza4rRPVqwk1hciA5ZOWWZIJOukAcore8JRX992FaA==
+jest-jasmine2@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz#fd509a9ed3d92bd6edb68a779f4738b100655b37"
+  integrity sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA==
   dependencies:
     "@babel/traverse" "^7.1.0"
-    "@jest/environment" "^27.0.5"
-    "@jest/source-map" "^27.0.1"
-    "@jest/test-result" "^27.0.2"
-    "@jest/types" "^27.0.2"
+    "@jest/environment" "^27.0.6"
+    "@jest/source-map" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     chalk "^4.0.0"
     co "^4.6.0"
-    expect "^27.0.2"
+    expect "^27.0.6"
     is-generator-fn "^2.0.0"
-    jest-each "^27.0.2"
-    jest-matcher-utils "^27.0.2"
-    jest-message-util "^27.0.2"
-    jest-runtime "^27.0.5"
-    jest-snapshot "^27.0.5"
-    jest-util "^27.0.2"
-    pretty-format "^27.0.2"
+    jest-each "^27.0.6"
+    jest-matcher-utils "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-runtime "^27.0.6"
+    jest-snapshot "^27.0.6"
+    jest-util "^27.0.6"
+    pretty-format "^27.0.6"
     throat "^6.0.1"
 
-jest-leak-detector@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.2.tgz#ce19aa9dbcf7a72a9d58907a970427506f624e69"
-  integrity sha512-TZA3DmCOfe8YZFIMD1GxFqXUkQnIoOGQyy4hFCA2mlHtnAaf+FeOMxi0fZmfB41ZL+QbFG6BVaZF5IeFIVy53Q==
+jest-leak-detector@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz#545854275f85450d4ef4b8fe305ca2a26450450f"
+  integrity sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ==
   dependencies:
-    jest-get-type "^27.0.1"
-    pretty-format "^27.0.2"
+    jest-get-type "^27.0.6"
+    pretty-format "^27.0.6"
 
-jest-matcher-utils@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz#f14c060605a95a466cdc759acc546c6f4cbfc4f0"
-  integrity sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA==
+jest-matcher-utils@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz#2a8da1e86c620b39459f4352eaa255f0d43e39a9"
+  integrity sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==
   dependencies:
     chalk "^4.0.0"
-    jest-diff "^27.0.2"
-    jest-get-type "^27.0.1"
-    pretty-format "^27.0.2"
+    jest-diff "^27.0.6"
+    jest-get-type "^27.0.6"
+    pretty-format "^27.0.6"
 
-jest-message-util@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.2.tgz#181c9b67dff504d8f4ad15cba10d8b80f272048c"
-  integrity sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw==
+jest-message-util@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.6.tgz#158bcdf4785706492d164a39abca6a14da5ab8b5"
+  integrity sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==
   dependencies:
     "@babel/code-frame" "^7.12.13"
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     "@types/stack-utils" "^2.0.0"
     chalk "^4.0.0"
     graceful-fs "^4.2.4"
     micromatch "^4.0.4"
-    pretty-format "^27.0.2"
+    pretty-format "^27.0.6"
     slash "^3.0.0"
     stack-utils "^2.0.3"
 
-jest-mock@^27.0.3:
-  version "27.0.3"
-  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.3.tgz#5591844f9192b3335c0dca38e8e45ed297d4d23d"
-  integrity sha512-O5FZn5XDzEp+Xg28mUz4ovVcdwBBPfAhW9+zJLO0Efn2qNbYcDaJvSlRiQ6BCZUCVOJjALicuJQI9mRFjv1o9Q==
+jest-mock@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.6.tgz#0efdd40851398307ba16778728f6d34d583e3467"
+  integrity sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
 
 jest-pnp-resolver@^1.2.2:
@@ -6315,76 +6604,76 @@
   resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
   integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
 
-jest-regex-util@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.1.tgz#69d4b1bf5b690faa3490113c47486ed85dd45b68"
-  integrity sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ==
+jest-regex-util@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5"
+  integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==
 
-jest-resolve-dependencies@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.5.tgz#819ccdddd909c65acddb063aac3a49e4ba1ed569"
-  integrity sha512-xUj2dPoEEd59P+nuih4XwNa4nJ/zRd/g4rMvjHrZPEBWeWRq/aJnnM6mug+B+Nx+ILXGtfWHzQvh7TqNV/WbuA==
+jest-resolve-dependencies@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz#3e619e0ef391c3ecfcf6ef4056207a3d2be3269f"
+  integrity sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA==
   dependencies:
-    "@jest/types" "^27.0.2"
-    jest-regex-util "^27.0.1"
-    jest-snapshot "^27.0.5"
+    "@jest/types" "^27.0.6"
+    jest-regex-util "^27.0.6"
+    jest-snapshot "^27.0.6"
 
-jest-resolve@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.5.tgz#937535a5b481ad58e7121eaea46d1424a1e0c507"
-  integrity sha512-Md65pngRh8cRuWVdWznXBB5eDt391OJpdBaJMxfjfuXCvOhM3qQBtLMCMTykhuUKiBMmy5BhqCW7AVOKmPrW+Q==
+jest-resolve@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.6.tgz#e90f436dd4f8fbf53f58a91c42344864f8e55bff"
+  integrity sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     chalk "^4.0.0"
     escalade "^3.1.1"
     graceful-fs "^4.2.4"
     jest-pnp-resolver "^1.2.2"
-    jest-util "^27.0.2"
-    jest-validate "^27.0.2"
+    jest-util "^27.0.6"
+    jest-validate "^27.0.6"
     resolve "^1.20.0"
     slash "^3.0.0"
 
-jest-runner@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.5.tgz#b6fdc587e1a5056339205914294555c554efc08a"
-  integrity sha512-HNhOtrhfKPArcECgBTcWOc+8OSL8GoFoa7RsHGnfZR1C1dFohxy9eLtpYBS+koybAHlJLZzNCx2Y/Ic3iEtJpQ==
+jest-runner@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.6.tgz#1325f45055539222bbc7256a6976e993ad2f9520"
+  integrity sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ==
   dependencies:
-    "@jest/console" "^27.0.2"
-    "@jest/environment" "^27.0.5"
-    "@jest/test-result" "^27.0.2"
-    "@jest/transform" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/console" "^27.0.6"
+    "@jest/environment" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/transform" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     chalk "^4.0.0"
     emittery "^0.8.1"
     exit "^0.1.2"
     graceful-fs "^4.2.4"
-    jest-docblock "^27.0.1"
-    jest-environment-jsdom "^27.0.5"
-    jest-environment-node "^27.0.5"
-    jest-haste-map "^27.0.5"
-    jest-leak-detector "^27.0.2"
-    jest-message-util "^27.0.2"
-    jest-resolve "^27.0.5"
-    jest-runtime "^27.0.5"
-    jest-util "^27.0.2"
-    jest-worker "^27.0.2"
+    jest-docblock "^27.0.6"
+    jest-environment-jsdom "^27.0.6"
+    jest-environment-node "^27.0.6"
+    jest-haste-map "^27.0.6"
+    jest-leak-detector "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-resolve "^27.0.6"
+    jest-runtime "^27.0.6"
+    jest-util "^27.0.6"
+    jest-worker "^27.0.6"
     source-map-support "^0.5.6"
     throat "^6.0.1"
 
-jest-runtime@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.5.tgz#cd5d1aa9754d30ddf9f13038b3cb7b95b46f552d"
-  integrity sha512-V/w/+VasowPESbmhXn5AsBGPfb35T7jZPGZybYTHxZdP7Gwaa+A0EXE6rx30DshHKA98lVCODbCO8KZpEW3hiQ==
+jest-runtime@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.6.tgz#45877cfcd386afdd4f317def551fc369794c27c9"
+  integrity sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==
   dependencies:
-    "@jest/console" "^27.0.2"
-    "@jest/environment" "^27.0.5"
-    "@jest/fake-timers" "^27.0.5"
-    "@jest/globals" "^27.0.5"
-    "@jest/source-map" "^27.0.1"
-    "@jest/test-result" "^27.0.2"
-    "@jest/transform" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/console" "^27.0.6"
+    "@jest/environment" "^27.0.6"
+    "@jest/fake-timers" "^27.0.6"
+    "@jest/globals" "^27.0.6"
+    "@jest/source-map" "^27.0.6"
+    "@jest/test-result" "^27.0.6"
+    "@jest/transform" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/yargs" "^16.0.0"
     chalk "^4.0.0"
     cjs-module-lexer "^1.0.0"
@@ -6392,30 +6681,30 @@
     exit "^0.1.2"
     glob "^7.1.3"
     graceful-fs "^4.2.4"
-    jest-haste-map "^27.0.5"
-    jest-message-util "^27.0.2"
-    jest-mock "^27.0.3"
-    jest-regex-util "^27.0.1"
-    jest-resolve "^27.0.5"
-    jest-snapshot "^27.0.5"
-    jest-util "^27.0.2"
-    jest-validate "^27.0.2"
+    jest-haste-map "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-mock "^27.0.6"
+    jest-regex-util "^27.0.6"
+    jest-resolve "^27.0.6"
+    jest-snapshot "^27.0.6"
+    jest-util "^27.0.6"
+    jest-validate "^27.0.6"
     slash "^3.0.0"
     strip-bom "^4.0.0"
     yargs "^16.0.3"
 
-jest-serializer@^27.0.1:
-  version "27.0.1"
-  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.1.tgz#2464d04dcc33fb71dc80b7c82e3c5e8a08cb1020"
-  integrity sha512-svy//5IH6bfQvAbkAEg1s7xhhgHTtXu0li0I2fdKHDsLP2P2MOiscPQIENQep8oU2g2B3jqLyxKKzotZOz4CwQ==
+jest-serializer@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.6.tgz#93a6c74e0132b81a2d54623251c46c498bb5bec1"
+  integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==
   dependencies:
     "@types/node" "*"
     graceful-fs "^4.2.4"
 
-jest-snapshot@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.5.tgz#6e3b9e8e193685372baff771ba34af631fe4d4d5"
-  integrity sha512-H1yFYdgnL1vXvDqMrnDStH6yHFdMEuzYQYc71SnC/IJnuuhW6J16w8GWG1P+qGd3Ag3sQHjbRr0TcwEo/vGS+g==
+jest-snapshot@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.6.tgz#f4e6b208bd2e92e888344d78f0f650bcff05a4bf"
+  integrity sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==
   dependencies:
     "@babel/core" "^7.7.2"
     "@babel/generator" "^7.7.2"
@@ -6423,79 +6712,79 @@
     "@babel/plugin-syntax-typescript" "^7.7.2"
     "@babel/traverse" "^7.7.2"
     "@babel/types" "^7.0.0"
-    "@jest/transform" "^27.0.5"
-    "@jest/types" "^27.0.2"
+    "@jest/transform" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/babel__traverse" "^7.0.4"
     "@types/prettier" "^2.1.5"
     babel-preset-current-node-syntax "^1.0.0"
     chalk "^4.0.0"
-    expect "^27.0.2"
+    expect "^27.0.6"
     graceful-fs "^4.2.4"
-    jest-diff "^27.0.2"
-    jest-get-type "^27.0.1"
-    jest-haste-map "^27.0.5"
-    jest-matcher-utils "^27.0.2"
-    jest-message-util "^27.0.2"
-    jest-resolve "^27.0.5"
-    jest-util "^27.0.2"
+    jest-diff "^27.0.6"
+    jest-get-type "^27.0.6"
+    jest-haste-map "^27.0.6"
+    jest-matcher-utils "^27.0.6"
+    jest-message-util "^27.0.6"
+    jest-resolve "^27.0.6"
+    jest-util "^27.0.6"
     natural-compare "^1.4.0"
-    pretty-format "^27.0.2"
+    pretty-format "^27.0.6"
     semver "^7.3.2"
 
-jest-util@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.2.tgz#fc2c7ace3c75ae561cf1e5fdb643bf685a5be7c7"
-  integrity sha512-1d9uH3a00OFGGWSibpNYr+jojZ6AckOMCXV2Z4K3YXDnzpkAaXQyIpY14FOJPiUmil7CD+A6Qs+lnnh6ctRbIA==
+jest-util@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.6.tgz#e8e04eec159de2f4d5f57f795df9cdc091e50297"
+  integrity sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     chalk "^4.0.0"
     graceful-fs "^4.2.4"
     is-ci "^3.0.0"
     picomatch "^2.2.3"
 
-jest-validate@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.2.tgz#7fe2c100089449cd5cbb47a5b0b6cb7cda5beee5"
-  integrity sha512-UgBF6/oVu1ofd1XbaSotXKihi8nZhg0Prm8twQ9uCuAfo59vlxCXMPI/RKmrZEVgi3Nd9dS0I8A0wzWU48pOvg==
+jest-validate@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.6.tgz#930a527c7a951927df269f43b2dc23262457e2a6"
+  integrity sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     camelcase "^6.2.0"
     chalk "^4.0.0"
-    jest-get-type "^27.0.1"
+    jest-get-type "^27.0.6"
     leven "^3.1.0"
-    pretty-format "^27.0.2"
+    pretty-format "^27.0.6"
 
-jest-watcher@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.2.tgz#dab5f9443e2d7f52597186480731a8c6335c5deb"
-  integrity sha512-8nuf0PGuTxWj/Ytfw5fyvNn/R80iXY8QhIT0ofyImUvdnoaBdT6kob0GmhXR+wO+ALYVnh8bQxN4Tjfez0JgkA==
+jest-watcher@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.6.tgz#89526f7f9edf1eac4e4be989bcb6dec6b8878d9c"
+  integrity sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ==
   dependencies:
-    "@jest/test-result" "^27.0.2"
-    "@jest/types" "^27.0.2"
+    "@jest/test-result" "^27.0.6"
+    "@jest/types" "^27.0.6"
     "@types/node" "*"
     ansi-escapes "^4.2.1"
     chalk "^4.0.0"
-    jest-util "^27.0.2"
+    jest-util "^27.0.6"
     string-length "^4.0.1"
 
-jest-worker@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.2.tgz#4ebeb56cef48b3e7514552f80d0d80c0129f0b05"
-  integrity sha512-EoBdilOTTyOgmHXtw/cPc+ZrCA0KJMrkXzkrPGNwLmnvvlN1nj7MPrxpT7m+otSv2e1TLaVffzDnE/LB14zJMg==
+jest-worker@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed"
+  integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==
   dependencies:
     "@types/node" "*"
     merge-stream "^2.0.0"
     supports-color "^8.0.0"
 
-jest@^27.0.5:
-  version "27.0.5"
-  resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.5.tgz#141825e105514a834cc8d6e44670509e8d74c5f2"
-  integrity sha512-4NlVMS29gE+JOZvgmSAsz3eOjkSsHqjTajlIsah/4MVSmKvf3zFP/TvgcLoWe2UVHiE9KF741sReqhF0p4mqbQ==
+jest@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.6.tgz#10517b2a628f0409087fbf473db44777d7a04505"
+  integrity sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==
   dependencies:
-    "@jest/core" "^27.0.5"
+    "@jest/core" "^27.0.6"
     import-local "^3.0.2"
-    jest-cli "^27.0.5"
+    jest-cli "^27.0.6"
 
 js-sha3@0.5.7, js-sha3@^0.5.7:
   version "0.5.7"
@@ -6931,6 +7220,13 @@
     typescript "^3.9.5"
     walkdir "^0.4.1"
 
+magic-string@^0.25.5, magic-string@^0.25.7:
+  version "0.25.7"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
+  integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
+  dependencies:
+    sourcemap-codec "^1.4.4"
+
 make-dir@^2.0.0, make-dir@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@@ -7366,6 +7662,11 @@
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20"
   integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==
 
+node-releases@^1.1.73:
+  version "1.1.74"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.74.tgz#e5866488080ebaa70a93b91144ccde06f3c3463e"
+  integrity sha512-caJBVempXZPepZoZAPCWRTNxYQ+xtG/KAi4ozTA5A+nJ7IU+kLQCbqaUjb5Rwy14M9upBWiQ4NutcmW04LJSRw==
+
 node-source-walk@^4.0.0, node-source-walk@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.2.0.tgz#c2efe731ea8ba9c03c562aa0a9d984e54f27bc2c"
@@ -7812,7 +8113,7 @@
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
   integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
 
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
   integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
@@ -7994,17 +8295,17 @@
   resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
   integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
 
-prettier@^2.3.1:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6"
-  integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==
+prettier@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d"
+  integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==
 
-pretty-format@^27.0.2:
-  version "27.0.2"
-  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.2.tgz#9283ff8c4f581b186b2d4da461617143dca478a4"
-  integrity sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig==
+pretty-format@^27.0.6:
+  version "27.0.6"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f"
+  integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==
   dependencies:
-    "@jest/types" "^27.0.2"
+    "@jest/types" "^27.0.6"
     ansi-regex "^5.0.0"
     ansi-styles "^5.0.0"
     react-is "^17.0.1"
@@ -8454,7 +8755,7 @@
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
   integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
 
-resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2:
+resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2:
   version "1.20.0"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
   integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@@ -8524,6 +8825,13 @@
   dependencies:
     bn.js "^4.11.1"
 
+rollup@^2.56.2:
+  version "2.56.2"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.56.2.tgz#a045ff3f6af53ee009b5f5016ca3da0329e5470f"
+  integrity sha512-s8H00ZsRi29M2/lGdm1u8DJpJ9ML8SUOpVVBd33XNeEeL3NVaTiUcSBHzBdF3eAyR0l7VSpsuoVUGrRHq7aPwQ==
+  optionalDependencies:
+    fsevents "~2.3.2"
+
 run-async@^2.4.0:
   version "2.4.1"
   resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@@ -8543,10 +8851,10 @@
   dependencies:
     tslib "^1.9.0"
 
-rxjs@^7.2.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.2.0.tgz#5cd12409639e9514a71c9f5f9192b2c4ae94de31"
-  integrity sha512-aX8w9OpKrQmiPKfT1bqETtUr9JygIz6GZ+gql8v7CijClsP0laoFUdKzxFAoWuRdSlOdU2+crss+cMf+cqMTnw==
+rxjs@^7.3.0:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6"
+  integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==
   dependencies:
     tslib "~2.1.0"
 
@@ -8894,6 +9202,11 @@
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
   integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
 
+sourcemap-codec@^1.4.4:
+  version "1.4.8"
+  resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+  integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
 spdx-correct@^3.0.0:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
@@ -9453,16 +9766,16 @@
   dependencies:
     safe-buffer "^5.0.1"
 
+tweetnacl@1.x.x, tweetnacl@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
+  integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
+
 tweetnacl@^0.14.3, tweetnacl@~0.14.0:
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
   integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
-tweetnacl@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
-  integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
-
 type-check@^0.4.0, type-check@~0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -9527,11 +9840,16 @@
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
   integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
 
-typescript@^4.2.4, typescript@^4.3.4:
+typescript@^4.2.4:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc"
   integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==
 
+typescript@^4.3.5:
+  version "4.3.5"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
+  integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
+
 uglify-js@^3.1.4:
   version "3.13.9"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.9.tgz#4d8d21dcd497f29cfd8e9378b9df123ad025999b"
@@ -10339,7 +10657,7 @@
     y18n "^5.0.5"
     yargs-parser "^20.2.2"
 
-yargs@^17.0.0, yargs@^17.0.1:
+yargs@^17.0.0:
   version "17.0.1"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
   integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==
@@ -10352,6 +10670,19 @@
     y18n "^5.0.5"
     yargs-parser "^20.2.2"
 
+yargs@^17.1.0, yargs@^17.1.1:
+  version "17.1.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.1.1.tgz#c2a8091564bdb196f7c0a67c1d12e5b85b8067ba"
+  integrity sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==
+  dependencies:
+    cliui "^7.0.2"
+    escalade "^3.1.1"
+    get-caller-file "^2.0.5"
+    require-directory "^2.1.1"
+    string-width "^4.2.0"
+    y18n "^5.0.5"
+    yargs-parser "^20.2.2"
+
 yn@3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"