git.delta.rocks / unique-network / refs/commits / 0dbd5ddfd265

difftreelog

Merge pull request #9 from usetech-llc/feature_limits_nft_78_85_105

Greg Zaitsev2020-10-13parents: #eb6665e #d339ca3.patch.diff
in: master
Transfer rate limit + chain bounds

4 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
135 mode: CollectionMode::NFT(50),137 mode: CollectionMode::NFT(50),
136 access: AccessMode::Normal,138 access: AccessMode::Normal,
137 decimal_points: 0,139 decimal_points: 0,
138 name: vec!(),140 name: vec![],
139 description: vec!(),141 description: vec![],
140 token_prefix: vec!(),142 token_prefix: vec![],
141 custom_data_size: 50,143 custom_data_size: 50,
142 mint_mode: false,144 mint_mode: false,
143 offchain_schema: vec!(),145 offchain_schema: vec![],
144 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),146 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
145 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),147 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
146 })],148 },
149 )],
147 nft_item_id: vec!(),150 nft_item_id: vec![],
148 fungible_item_id: vec!(),151 fungible_item_id: vec![],
149 refungible_item_id: vec!(),152 refungible_item_id: vec![],
153 chain_limit: ChainLimits {
154 collection_numbers_limit: 10,
155 account_token_ownership_limit: 10,
156 collections_admins_limit: 5,
157 custom_data_limit: 2048,
158 nft_sponsor_transfer_timeout: 15,
159 fungible_sponsor_transfer_timeout: 15,
160 refungible_sponsor_transfer_timeout: 15,
161 },
150 }),162 }),
151 contracts: Some(ContractsConfig {163 contracts: Some(ContractsConfig {
152 current_schedule: ContractsSchedule {164 current_schedule: ContractsSchedule {
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -1,6 +1,9 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 #[cfg(feature = "std")]
+pub use std::*;
+
+#[cfg(feature = "std")]
 pub use serde::*;
 
 use codec::{Decode, Encode};
@@ -20,7 +23,7 @@
     IsSubType, StorageValue,
 };
 
-use frame_system::{self as system, ensure_signed};
+use frame_system::{self as system, ensure_signed, ensure_root};
 use sp_runtime::sp_std::prelude::Vec;
 use sp_runtime::{
     traits::{
@@ -157,6 +160,27 @@
     pub vesting_date: Moment,
 }
 
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct BasketItem<AccountId, BlockNumber> {
+    pub address: AccountId,
+    pub start_block: BlockNumber,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct ChainLimits {
+    pub collection_numbers_limit: u64,
+    pub account_token_ownership_limit: u64,
+    pub collections_admins_limit: u64,
+    pub custom_data_limit: u32,
+
+    // 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 trait Trait: system::Trait {
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 }
@@ -172,6 +196,14 @@
         ChainVersion: u64;
         ItemListIndex: map hasher(blake2_128_concat) u64 => u64;
 
+        // Chain limits struct
+        pub ChainLimit get(fn chain_limit) config(): ChainLimits;
+
+        // Bound counters
+        CollectionCount: u64;
+        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;
+
+        // Basic collections
         pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;
         pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;
         pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;
@@ -190,13 +222,18 @@
         /// Index list
         pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
 
+        /// Tokens transfer baskets
+        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;
+        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;
+        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;
+
         // Sponsorship
         pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;
         pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;
     }
     add_extra_genesis {
         build(|config: &GenesisConfig<T>| {
-			// Modification of storage
+            // Modification of storage
             for (_num, _c) in &config.collection {
                 <Module<T>>::init_collection(_c);
             }
@@ -212,7 +249,7 @@
             for (_num, _q, _i) in &config.refungible_item_id {
                 <Module<T>>::init_refungible_token(_i);
             }
-		})
+        })
     }
 }
 
@@ -295,8 +332,18 @@
             // Anyone can create a collection
             let who = ensure_signed(origin)?;
             let custom_data_size = match mode {
-                CollectionMode::NFT(size) => size,
-                CollectionMode::ReFungible(size, _) => size,
+                CollectionMode::NFT(size) => {
+
+                    // bound Custom data size
+                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");
+                    size
+                },
+                CollectionMode::ReFungible(size, _) => {
+
+                    // bound Custom data size
+                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");
+                    size
+                },
                 _ => 0
             };
 
@@ -306,6 +353,9 @@
                 _ => 0
             };
 
+            // bound Total number of collections
+            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");
+
             // check params
             ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");
 
@@ -326,7 +376,13 @@
                 .checked_add(1)
                 .expect("collection id error");
 
+            // bound counter
+            let total = CollectionCount::get()
+                .checked_add(1)
+                .expect("collection counter error");
+
             CreatedCollectionCount::put(next_id);
+            CollectionCount::put(total);
 
             // Create new collection
             let new_collection = CollectionType {
@@ -368,7 +424,6 @@
             let sender = ensure_signed(origin)?;
             Self::check_owner_permissions(collection_id, sender)?;
 
-            // TODO Items remove
             <AddressTokens<T>>::remove_prefix(collection_id);
             <ApprovedList<T>>::remove_prefix(collection_id);
             <Balance<T>>::remove_prefix(collection_id);
@@ -377,6 +432,24 @@
             <Collection<T>>::remove(collection_id);
             <WhiteList<T>>::remove(collection_id);
 
+            <NftItemList<T>>::remove_prefix(collection_id);
+            <FungibleItemList<T>>::remove_prefix(collection_id);
+            <ReFungibleItemList<T>>::remove_prefix(collection_id);
+
+            <NftTransferBasket<T>>::remove_prefix(collection_id);
+            <FungibleTransferBasket<T>>::remove_prefix(collection_id);
+            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);
+
+            if CollectionCount::get() > 0
+            {
+                // bound couter
+                let total = CollectionCount::get()
+                    .checked_sub(1)
+                    .expect("collection counter error");
+
+                CollectionCount::put(total);
+            }
+
             Ok(())
         }
 
@@ -544,6 +617,9 @@
                 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");
             }
 
+            // Number of collection admins
+            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");
+
             admin_arr.push(new_admin_id);
             <AdminList<T>>::insert(collection_id, admin_arr);
 
@@ -758,8 +834,8 @@
 
             // Transfer permissions check
             let target_collection = <Collection<T>>::get(collection_id);
-            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 
-                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
+                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
                 "Only item owner, collection owner and admins can modify item");
 
             if target_collection.access == AccessMode::WhiteList {
@@ -810,8 +886,8 @@
 
             // Transfer permissions check
             let target_collection = <Collection<T>>::get(collection_id);
-            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 
-                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
+                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
                 "Only item owner, collection owner and admins can modify item");
 
             if target_collection.access == AccessMode::WhiteList {
@@ -852,8 +928,8 @@
 
             // Transfer permissions check
             let target_collection = <Collection<T>>::get(collection_id);
-            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 
-                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
+                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
                 "Only item owner, collection owner and admins can approve");
 
             if target_collection.access == AccessMode::WhiteList {
@@ -919,7 +995,7 @@
 
             // Transfer permissions check
             let target_collection = <Collection<T>>::get(collection_id);
-            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
                 "Only item owner, collection owner and admins can modify items");
 
             if target_collection.access == AccessMode::WhiteList {
@@ -987,6 +1063,17 @@
 
             Ok(())
         }
+
+        // Sudo permissions function
+        #[weight = 0]
+        pub fn set_chain_limits(
+            origin,
+            limits: ChainLimits
+        ) -> DispatchResult {
+            ensure_root(origin)?;
+            <ChainLimit>::put(limits);
+            Ok(())
+        }        
     }
 }
 
@@ -1004,6 +1091,10 @@
         <ItemListIndex>::insert(item.collection, current_index);
         <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);
 
+        // Add current block
+        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();
+        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);
+        
         // Update balance
         let new_balance = <Balance<T>>::get(item.collection, owner.clone())
             .checked_add(value)
@@ -1027,6 +1118,10 @@
         <ItemListIndex>::insert(item.collection, current_index);
         <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);
 
+        // Add current block
+        let block_number: T::BlockNumber = 0.into();
+        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);
+
         // Update balance
         let new_balance = <Balance<T>>::get(item.collection, owner.clone())
             .checked_add(value)
@@ -1048,6 +1143,10 @@
         <ItemListIndex>::insert(collection_id, current_index);
         <NftItemList<T>>::insert(collection_id, current_index, item);
 
+        // Add current block
+        let block_number: T::BlockNumber = 0.into();
+        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);
+
         // Update balance
         let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())
             .checked_add(1)
@@ -1057,7 +1156,11 @@
         Ok(())
     }
 
-    fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {
+    fn burn_refungible_item(
+        collection_id: u64,
+        item_id: u64,
+        owner: T::AccountId,
+    ) -> DispatchResult {
         ensure!(
             <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
             "Item does not exists"
@@ -1149,7 +1252,6 @@
     }
 
     fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {
-
         let target_collection = <Collection<T>>::get(collection_id);
         let mut result: bool = subject == target_collection.owner;
         let exists = <AdminList<T>>::contains_key(collection_id);
@@ -1163,12 +1265,17 @@
         result
     }
 
-    fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {
-        
+    fn check_owner_or_admin_permissions(
+        collection_id: u64,
+        subject: T::AccountId,
+    ) -> DispatchResult {
         Self::collection_exists(collection_id)?;
         let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());
 
-        ensure!(result, "You do not have permissions to modify this collection");
+        ensure!(
+            result,
+            "You do not have permissions to modify this collection"
+        );
         Ok(())
     }
 
@@ -1193,7 +1300,6 @@
     }
 
     fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {
-
         let mes = "Address is not in white list";
         ensure!(<WhiteList<T>>::contains_key(collection_id), mes);
         let wl = <WhiteList<T>>::get(collection_id);
@@ -1209,7 +1315,6 @@
         owner: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
-
         ensure!(
             <FungibleItemList<T>>::contains_key(collection_id, item_id),
             "Item not exists"
@@ -1299,7 +1404,6 @@
         owner: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
-
         ensure!(
             <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
             "Item not exists"
@@ -1385,7 +1489,6 @@
         sender: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
-    
         ensure!(
             <NftItemList<T>>::contains_key(collection_id, item_id),
             "Item not exists"
@@ -1422,24 +1525,34 @@
         Ok(())
     }
 
-    fn init_collection(item: &CollectionType<T::AccountId>){
+    fn init_collection(item: &CollectionType<T::AccountId>) {
+        // check params
+        assert!(
+            item.decimal_points <= 4,
+            "decimal_points parameter must be lower than 4"
+        );
+        assert!(
+            item.name.len() <= 64,
+            "Collection name can not be longer than 63 char"
+        );
+        assert!(
+            item.name.len() <= 256,
+            "Collection description can not be longer than 255 char"
+        );
+        assert!(
+            item.token_prefix.len() <= 16,
+            "Token prefix can not be longer than 15 char"
+        );
 
-                // check params
-                assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");
-                assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");
-                assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");
-                assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");
-    
-                // Generate next collection ID
-                let next_id = CreatedCollectionCount::get()
-                    .checked_add(1)
-                    .expect("collection id error");
-    
-                CreatedCollectionCount::put(next_id);  
-    }
+        // Generate next collection ID
+        let next_id = CreatedCollectionCount::get()
+            .checked_add(1)
+            .expect("collection id error");
 
-    fn init_nft_token(item: &NftItemType<T::AccountId>){
+        CreatedCollectionCount::put(next_id);
+    }
 
+    fn init_nft_token(item: &NftItemType<T::AccountId>) {
         let current_index = <ItemListIndex>::get(item.collection)
             .checked_add(1)
             .expect("Item list index id error");
@@ -1457,8 +1570,7 @@
         <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);
     }
 
-    fn init_fungible_token(item: &FungibleItemType<T::AccountId>){
-
+    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {
         let current_index = <ItemListIndex>::get(item.collection)
             .checked_add(1)
             .expect("Item list index id error");
@@ -1476,8 +1588,7 @@
         <Balance<T>>::insert(item.collection, owner.clone(), new_balance);
     }
 
-    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){
-
+    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {
         let current_index = <ItemListIndex>::get(item.collection)
             .checked_add(1)
             .expect("Item list index id error");
@@ -1497,6 +1608,21 @@
     }
 
     fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {
+
+        // add to account limit
+        if <AccountItemCount<T>>::contains_key(owner.clone()) {
+
+            // bound Owned tokens by a single address
+            let count = <AccountItemCount<T>>::get(owner.clone());
+            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");
+
+            <AccountItemCount<T>>::insert(owner.clone(), 
+                count.checked_add(1).unwrap());
+        }
+        else {
+            <AccountItemCount<T>>::insert(owner.clone(), 1);
+        }
+
         let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
         if list_exists {
             let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());
@@ -1511,6 +1637,7 @@
             let mut itm = Vec::new();
             itm.push(item_index.clone());
             <AddressTokens<T>>::insert(collection_id, owner, itm);
+            
         }
 
         Ok(())
@@ -1521,6 +1648,12 @@
         item_index: u64,
         owner: T::AccountId,
     ) -> DispatchResult {
+
+        // update counter
+        <AccountItemCount<T>>::insert(owner.clone(), 
+            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());
+
+
         let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
         if list_exists {
             let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());
@@ -1628,7 +1761,65 @@
                 <Collection<T>>::get(collection_id).sponsor
             }
             Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {
-                <Collection<T>>::get(collection_id).sponsor
+                let _collection_mode = <Collection<T>>::get(collection_id).mode;
+
+                // sponsor timeout
+                let sponsor_transfer = match _collection_mode {
+                    CollectionMode::NFT(_) => {
+                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);
+                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
+                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();
+                        if block_number >= limit_time {
+                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);
+                            true
+                        }
+                        else {
+                            false
+                        }
+                    }
+                    CollectionMode::Fungible(_) => {
+                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);
+                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
+                        if basket.iter().any(|i| i.address == _new_owner.clone())
+                        {
+                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();
+                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();
+                            if block_number >= limit_time {
+                                basket.retain(|x| x.address == item.address);
+                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });
+                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);
+                                true
+                            }
+                            else {
+                                false
+                            }
+                        }
+                        else {
+                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});
+                            true
+                        }
+                    }
+                    CollectionMode::ReFungible(_, _) => {
+                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);
+                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
+                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();
+                        if block_number >= limit_time {
+                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);
+                            true
+                        } else {
+                            false
+                        }
+                    }
+                    _ => {
+                        false
+                    },
+                };
+
+                if !sponsor_transfer {
+                    T::AccountId::default()
+                } else {
+                    <Collection<T>>::get(collection_id).sponsor
+                }
             }
 
             _ => T::AccountId::default(),
@@ -1745,4 +1936,4 @@
         Ok(())
     }
 }
-// #endregion
\ No newline at end of file
+// #endregion
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
--- a/pallets/nft/src/mock.rs
+++ b/pallets/nft/src/mock.rs
@@ -65,6 +65,7 @@
 }
 pub type TemplateModule = Module<Test>;
 
+
 // This function basically just builds a genesis storage key/value store according to
 // our desired mockup.
 pub fn new_test_ext() -> sp_io::TestExternalities {
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,7 +1,8 @@
 // Tests to be written here
 use crate::mock::*;
-use crate::{ApprovePermissions, CollectionMode, AccessMode, Ownership};
+use crate::{AccessMode, ApprovePermissions, CollectionMode, Ownership, ChainLimits};
 use frame_support::{assert_noop, assert_ok};
+use frame_system::{ RawOrigin };
 
 // Use cases tests region
 // #region
@@ -13,6 +14,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -41,6 +52,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -79,6 +100,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::Fungible(3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -109,6 +140,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::Fungible(3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -167,6 +208,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -264,6 +315,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -302,6 +363,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -323,8 +394,16 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
@@ -362,6 +441,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -393,8 +482,16 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1000);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
@@ -444,6 +541,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::Fungible(3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -465,8 +572,16 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1000);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
@@ -532,6 +647,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -557,6 +682,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -577,6 +712,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -617,6 +762,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::Fungible(3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -655,6 +810,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::ReFungible(200, 3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -664,9 +829,17 @@
             token_prefix1.clone(),
             mode
         ));
-        
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
 
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
@@ -704,6 +877,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         let origin3 = Origin::signed(3);
@@ -754,6 +937,16 @@
         let origin2 = Origin::signed(2);
         let origin3 = Origin::signed(3);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -809,6 +1002,16 @@
 
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -881,6 +1084,16 @@
         let nft_mode: CollectionMode = CollectionMode::NFT(2000);
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -915,6 +1128,16 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -937,8 +1160,16 @@
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
 
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
@@ -972,6 +1203,16 @@
         let mode: CollectionMode = CollectionMode::NFT(2000);
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -995,6 +1236,16 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1019,6 +1270,16 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1027,16 +1288,32 @@
             mode
         ));
 
-        assert_noop!(TemplateModule::add_to_white_list(origin2.clone(), 1, 3), "You do not have permissions to modify this collection");
+        assert_noop!(
+            TemplateModule::add_to_white_list(origin2.clone(), 1, 3),
+            "You do not have permissions to modify this collection"
+        );
     });
 }
 
 #[test]
 fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {
     new_test_ext().execute_with(|| {
+        let origin1 = Origin::signed(1);
+
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
 
-        let origin1 = Origin::signed(1);
-        assert_noop!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+        assert_noop!(
+            TemplateModule::add_to_white_list(origin1.clone(), 1, 2),
+            "This collection does not exist"
+        );
     });
 }
 
@@ -1049,6 +1326,16 @@
         let mode: CollectionMode = CollectionMode::NFT(2000);
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1058,7 +1345,10 @@
         ));
 
         assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
-        assert_noop!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+        assert_noop!(
+            TemplateModule::add_to_white_list(origin1.clone(), 1, 2),
+            "This collection does not exist"
+        );
     });
 }
 
@@ -1066,13 +1356,22 @@
 #[test]
 fn address_is_already_added_to_white_list() {
     new_test_ext().execute_with(|| {
-
         let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
         let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1091,13 +1390,22 @@
 #[test]
 fn owner_can_remove_address_from_white_list() {
     new_test_ext().execute_with(|| {
-        
         let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
         let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1107,7 +1415,11 @@
         ));
 
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
-        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::remove_from_white_list(
+            origin1.clone(),
+            1,
+            2
+        ));
         assert_eq!(TemplateModule::white_list(1).len(), 0);
     });
 }
@@ -1115,7 +1427,6 @@
 #[test]
 fn admin_can_remove_address_from_white_list() {
     new_test_ext().execute_with(|| {
-
         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();
@@ -1123,6 +1434,16 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1134,7 +1455,11 @@
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
 
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
-        assert_ok!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 3));
+        assert_ok!(TemplateModule::remove_from_white_list(
+            origin2.clone(),
+            1,
+            3
+        ));
         assert_eq!(TemplateModule::white_list(1).len(), 0);
     });
 }
@@ -1142,7 +1467,6 @@
 #[test]
 fn nonprivileged_user_cannot_remove_address_from_white_list() {
     new_test_ext().execute_with(|| {
-
         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();
@@ -1150,6 +1474,16 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1159,7 +1493,10 @@
         ));
 
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
-        assert_noop!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 2), "You do not have permissions to modify this collection");
+        assert_noop!(
+            TemplateModule::remove_from_white_list(origin2.clone(), 1, 2),
+            "You do not have permissions to modify this collection"
+        );
         assert_eq!(TemplateModule::white_list(1)[0], 2);
     });
 }
@@ -1167,16 +1504,28 @@
 #[test]
 fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {
     new_test_ext().execute_with(|| {
+        let origin1 = Origin::signed(1);
+
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
 
-        let origin1 = Origin::signed(1);
-        assert_noop!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+        assert_noop!(
+            TemplateModule::remove_from_white_list(origin1.clone(), 1, 2),
+            "This collection does not exist"
+        );
     });
 }
 
 #[test]
 fn nobody_can_remove_address_from_white_list_of_deleted_collection() {
     new_test_ext().execute_with(|| {
-
         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();
@@ -1184,6 +1533,16 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1194,7 +1553,10 @@
 
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
-        assert_noop!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 2), "This collection does not exist");
+        assert_noop!(
+            TemplateModule::remove_from_white_list(origin2.clone(), 1, 2),
+            "This collection does not exist"
+        );
         assert_eq!(TemplateModule::white_list(1).len(), 0);
     });
 }
@@ -1209,6 +1571,16 @@
         let mode: CollectionMode = CollectionMode::NFT(2000);
         let origin1 = Origin::signed(1);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -1218,8 +1590,16 @@
         ));
 
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
-        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
-        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::remove_from_white_list(
+            origin1.clone(),
+            1,
+            2
+        ));
+        assert_ok!(TemplateModule::remove_from_white_list(
+            origin1.clone(),
+            1,
+            2
+        ));
         assert_eq!(TemplateModule::white_list(1).len(), 0);
     });
 }
@@ -1233,6 +1613,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1250,16 +1640,17 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
-
-        assert_noop!(TemplateModule::transfer(
+        assert_ok!(TemplateModule::set_public_access_mode(
             origin1.clone(),
-            3,
             1,
-            1,
-            1
-        ), "Address is not in white list");
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        assert_noop!(
+            TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1271,6 +1662,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1288,24 +1689,28 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-
-        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 1));
 
-        assert_noop!(TemplateModule::transfer_from(
+        assert_ok!(TemplateModule::remove_from_white_list(
             origin1.clone(),
-            1,
-            3,
             1,
-            1,
             1
-        ), "Address is not in white list");
+        ));
+
+        assert_noop!(
+            TemplateModule::transfer_from(origin1.clone(), 1, 3, 1, 1, 1),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1318,6 +1723,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1335,16 +1750,17 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
-
-        assert_noop!(TemplateModule::transfer(
+        assert_ok!(TemplateModule::set_public_access_mode(
             origin1.clone(),
-            3,
             1,
-            1,
-            1
-        ), "Address is not in white list");
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+
+        assert_noop!(
+            TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1356,6 +1772,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1373,7 +1799,11 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
@@ -1381,16 +1811,16 @@
         assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
 
-        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
-
-        assert_noop!(TemplateModule::transfer_from(
+        assert_ok!(TemplateModule::remove_from_white_list(
             origin1.clone(),
-            1,
-            3,
             1,
-            1,
-            1
-        ), "Address is not in white list");
+            2
+        ));
+
+        assert_noop!(
+            TemplateModule::transfer_from(origin1.clone(), 1, 3, 1, 1, 1),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1403,6 +1833,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1420,8 +1860,15 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_noop!(TemplateModule::burn_item(origin1.clone(), 1, 1), "Address is not in white list");
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_noop!(
+            TemplateModule::burn_item(origin1.clone(), 1, 1),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1434,6 +1881,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1442,10 +1899,17 @@
             token_prefix1.clone(),
             mode
         ));
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
 
         // do approve
-        assert_noop!(TemplateModule::approve(origin1.clone(), 1, 1, 1), "Address is not in white list");
+        assert_noop!(
+            TemplateModule::approve(origin1.clone(), 1, 1, 1),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1459,6 +1923,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1476,17 +1950,15 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
-        assert_ok!(TemplateModule::transfer(
-            origin1.clone(),
-            2,
-            1,
-            1,
-            1
-        ));
+        assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1));
     });
 }
 
@@ -1498,6 +1970,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1515,7 +1997,11 @@
             1
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
@@ -1543,6 +2029,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1552,8 +2048,16 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            false
+        ));
 
         assert_ok!(TemplateModule::create_item(
             origin1.clone(),
@@ -1573,6 +2077,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -1583,8 +2097,16 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            false
+        ));
 
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
 
@@ -1606,6 +2128,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -1616,16 +2148,22 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            false
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
-        assert_noop!(TemplateModule::create_item(
-            origin2.clone(),
-            1,
-            [1, 2, 3].to_vec(),
-            2
-        ), "Collection is not in mint mode");
+        assert_noop!(
+            TemplateModule::create_item(origin2.clone(), 1, [1, 2, 3].to_vec(), 2),
+            "Collection is not in mint mode"
+        );
     });
 }
 
@@ -1638,6 +2176,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -1648,15 +2196,21 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            false
+        ));
 
-        assert_noop!(TemplateModule::create_item(
-            origin2.clone(),
-            1,
-            [1, 2, 3].to_vec(),
-            2
-        ), "Collection is not in mint mode");
+        assert_noop!(
+            TemplateModule::create_item(origin2.clone(), 1, [1, 2, 3].to_vec(), 2),
+            "Collection is not in mint mode"
+        );
     });
 }
 
@@ -1669,6 +2223,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
@@ -1678,8 +2242,16 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
 
         assert_ok!(TemplateModule::create_item(
             origin1.clone(),
@@ -1699,6 +2271,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -1709,8 +2291,16 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
 
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
 
@@ -1732,6 +2322,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -1742,15 +2342,21 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
 
-        assert_noop!(TemplateModule::create_item(
-            origin2.clone(),
-            1,
-            [1, 2, 3].to_vec(),
-            2
-        ), "Address is not in white list");
+        assert_noop!(
+            TemplateModule::create_item(origin2.clone(), 1, [1, 2, 3].to_vec(), 2),
+            "Address is not in white list"
+        );
     });
 }
 
@@ -1763,6 +2369,16 @@
         let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
         let mode: CollectionMode = CollectionMode::NFT(2000);
 
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
@@ -1773,8 +2389,16 @@
             mode
         ));
 
-        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
-        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        assert_ok!(TemplateModule::set_public_access_mode(
+            origin1.clone(),
+            1,
+            AccessMode::WhiteList
+        ));
+        assert_ok!(TemplateModule::set_mint_permission(
+            origin1.clone(),
+            1,
+            true
+        ));
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
         assert_ok!(TemplateModule::create_item(
@@ -1786,4 +2410,290 @@
     });
 }
 
-// #endregion
\ No newline at end of file
+// Total number of collections. Positive test
+#[test]
+fn total_number_collections_bound() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+    });
+}
+
+// Total number of collections. Negotive test
+#[test]
+fn total_number_collections_bound_neg() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
+        let origin1 = Origin::signed(1);
+
+        for _ in 0..10 {
+
+            assert_ok!(TemplateModule::create_collection(
+                origin1.clone(),
+                col_name1.clone(),
+                col_desc1.clone(),
+                token_prefix1.clone(),
+                mode.clone()
+            ));
+        }
+
+        // 11-th collection in chain. Expects error
+        assert_noop!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode.clone()
+        ), "Total collections bound exceeded");
+    });
+}
+
+// Owned tokens by a single address. Positive test
+#[test]
+fn owned_tokens_bound() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+    });
+}
+
+// Owned tokens by a single address. Negotive test
+#[test]
+fn owned_tokens_bound_neg() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_noop!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ), "Owned tokens by a single address bound exceeded");
+    });
+}
+
+// Number of collection admins. Positive test
+#[test]
+fn collection_admins_bound() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 3));
+    });
+}
+
+// Number of collection admins. Negotive test
+#[test]
+fn collection_admins_bound_neg() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+        assert_noop!(TemplateModule::add_collection_admin(origin1.clone(), 1, 3), "Number of collection admins bound exceeded");
+    });
+}
+
+// Custom data size. Positive test
+#[test]
+fn custom_data_size_bound() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+    });
+}
+
+// Custom data size. Negotive test
+#[test]
+fn custom_data_size_bound_neg() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        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: 200,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        assert_noop!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ), "Custom data size bound exceeded");
+    });
+}
+// #endregion