git.delta.rocks / unique-network / refs/commits / 7bcde70a2ca7

difftreelog

NFTPAR-118. Per Collection Limits

str-mv2020-12-11parent: #68e6834.patch.diff
in: master

3 files changed

modifiedREADME.mddiffbeforeafterboth
before · README.md
1![Docker build](https://github.com/usetech-llc/nft_parachain/workflows/Docker%20build/badge.svg)23# NFT Parachain45## Project Description67The NFT Pallet is the core of NFT functionality. Like ERC-721 standard in Ethereum ecosystem, this pallet provides the basement for creating collections of unique non-divisible things, also called Non Fungible Tokens (NFTs), minting NFT of a given Collection, and managing their ownership.89The pallet also enables storing NFT properties. Though (according to ERC-721) NFT properties belong to logic of a concrete application that operates a Collection, so purposefully the NFT Tracking Module does not have any knowledge about properties except their byte size leaving application logic out to be controlled by Smart Contracts.1011The NFT Chain also provides:1213* Smart Contracts Pallet and example smart contract that interacts with NFT Runtime14* ERC-1155 Functionality (currently PoC as Re-Fungible tokens, i.e. items that are still unique, but that can be split between multiple users)15* Variety of economic options for dapp producers to choose from to create freemium games and other ways to attract users. As a step one, we implemented an economic model when a collection sponsor can be set to pay for collection Transfer transactions.1617Wider NFT Ecosystem (most of it was developed during Hackusama):18* [SubstraPunks Game hosted on IPFS](https://github.com/usetech-llc/substrapunks)19* [NFT Wallet and UI](https://uniqueapps.usetech.com/#/nft)20* [NFT Asset for Unity Framework](https://github.com/usetech-llc/nft_unity)2122Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!2324## Hackusama Update2526During the Kusama Hackaphon the following changes were made:27* Enabled Smart Contracts Pallet28* Enabled integration between Smart Contracts and NFT Pallet (required special edition of RC4 Substrate version)29* Fixed misc. bugs in NFT Pallet30* Deployed NFT TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.31* New Features:32  * Re-Fungible Token Mode33  * Off-Chain Schema to store token image URLs34  * Alternative economic model35  * White Lists and Public Mint Permission36* Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and NFT Testnet Blockchain.3738## Application Development3940If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).4142## Building4344Building NFT chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything so that we can keep the builds stable.45461. Install Rust:4748```bash49curl https://sh.rustup.rs -sSf | sh50sudo apt-get install libssl-dev pkg-config libclang-dev clang51```52532. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.54553. Install Rust Toolchain 1.44.0:5657```bash58rustup install 1.44.059```60614. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)62```bash63rustup toolchain list64rustup default 1.44.0-x86_64-unknown-linux-gnu65```66675. Install nightly toolchain and add wasm target for it:6869```bash70rustup toolchain install nightly-2020-05-0171rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu72```73746. Build:75```bash76cargo build77```7879## Run8081You can start a development chain with:8283```bash84cargo run -- --dev85```8687Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.8889If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with testnet units. Give each node a name and expose them so they are listed on the Polkadot [telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.9091We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from the `--node-key` value that we specify below:9293```bash94cargo run -- \95  --base-path /tmp/alice \96  --chain=local \97  --alice \98  --node-key 0000000000000000000000000000000000000000000000000000000000000001 \99  --telemetry-url ws://telemetry.polkadot.io:1024 \100  --validator101```102103In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's bootnode ID on TCP port 30333:104105```bash106cargo run -- \107  --base-path /tmp/bob \108  --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \109  --chain=local \110  --bob \111  --port 30334 \112  --telemetry-url ws://telemetry.polkadot.io:1024 \113  --validator114```115116Additional CLI usage options are available and may be shown by running `cargo run -- --help`.117118## Benchmarks119120First of all, add rust toolchain and make it default.121```bash122rustup target add wasm32-unknown-unknown --toolchain nightly-2020-10-01123```124125Then in "/node/src" run build command below126```bash127cargo +nightly-2020-10-01 build --release --features runtime-benchmarks128```129130Run benchmark131```bash132target/release/nft benchmark --chain dev --pallet "pallet_nft" --extrinsic "*" --repeat 1133```134135## UI custom types136```137{138  "Schedule": {139    "version": "u32",140    "put_code_per_byte_cost": "Gas",141    "grow_mem_cost": "Gas",142    "regular_op_cost": "Gas",143    "return_data_per_byte_cost": "Gas",144    "event_data_per_byte_cost": "Gas",145    "event_per_topic_cost": "Gas",146    "event_base_cost": "Gas",147    "call_base_cost": "Gas",148    "instantiate_base_cost": "Gas",149    "dispatch_base_cost": "Gas",150    "sandbox_data_read_cost": "Gas",151    "sandbox_data_write_cost": "Gas",152    "transfer_cost": "Gas",153    "instantiate_cost": "Gas",154    "max_event_topics": "u32",155    "max_stack_height": "u32",156    "max_memory_pages": "u32",157    "max_table_size": "u32",158    "enable_println": "bool",159    "max_subject_len": "u32"160  },161  "AccessMode": {162    "_enum": [163      "Normal",164      "WhiteList"165    ]166  },167  "CollectionMode": {168    "_enum": {169      "Invalid": null,170      "NFT": null,171      "Fungible": "u32",172      "ReFungible": "u32"173    }174  },175  "Ownership": {176    "Owner": "AccountId",177    "Fraction": "u128"178  },179  "FungibleItemType": {180    "Collection": "u64",181    "Owner": "AccountId",182    "Value": "u128"183  },184  "ReFungibleItemType": {185    "Collection": "u64",186    "Owner": "Vec<Ownership>",187    "Data": "Vec<u8>"188  },189  "NftItemType": {190    "Collection": "u64",191    "Owner": "AccountId",192    "ConstData": "Vec<u8>",193    "VariableData": "Vec<u8>"194  },195  "Ownership": {196    "owner": "AccountId",197    "fraction": "u128"198  },199  "ReFungibleItemType": {200    "Collection": "u64",201    "Owner": "Vec<Ownership<AccountId>>",202    "ConstData": "Vec<u8>",203    "VariableData": "Vec<u8>"204  },205  "CollectionType": {206    "Owner": "AccountId",207    "Mode": "CollectionMode",208    "Access": "AccessMode",209    "DecimalPoints": "u32",210    "Name": "Vec<u16>",211    "Description": "Vec<u16>",212    "TokenPrefix": "Vec<u8>",213    "MintMode": "bool",214    "OffchainSchema": "Vec<u8>",215    "Sponsor": "AccountId",216    "UnconfirmedSponsor": "AccountId",217    "VariableOnChainSchema": "Vec<u8>",218    "ConstOnChainSchema": "Vec<u8>"219  },220  "ApprovePermissions": {221    "Approved": "AccountId",222    "Amount": "u64"223  },224  "RawData": "Vec<u8>",225  "Address": "AccountId",226  "LookupSource": "AccountId",227  "Weight": "u64",228  "CreateNftData": {229    "const_data": "Vec<u8>",230    "variable_data": "Vec<u8>" 231  },232  "CreateFungibleData": {},233  "CreateReFungibleData": {234    "const_data": "Vec<u8>",235    "variable_data": "Vec<u8>" 236  },237  "CreateItemData": {238    "_enum": {239      "NFT": "CreateNftData",240      "Fungible": "CreateFungibleData",241      "ReFungible": "CreateReFungibleData"242    }243  }244}245246```
after · README.md
1![Docker build](https://github.com/usetech-llc/nft_parachain/workflows/Docker%20build/badge.svg)23# NFT Parachain45## Project Description67The NFT Pallet is the core of NFT functionality. Like ERC-721 standard in Ethereum ecosystem, this pallet provides the basement for creating collections of unique non-divisible things, also called Non Fungible Tokens (NFTs), minting NFT of a given Collection, and managing their ownership.89The pallet also enables storing NFT properties. Though (according to ERC-721) NFT properties belong to logic of a concrete application that operates a Collection, so purposefully the NFT Tracking Module does not have any knowledge about properties except their byte size leaving application logic out to be controlled by Smart Contracts.1011The NFT Chain also provides:1213* Smart Contracts Pallet and example smart contract that interacts with NFT Runtime14* ERC-1155 Functionality (currently PoC as Re-Fungible tokens, i.e. items that are still unique, but that can be split between multiple users)15* Variety of economic options for dapp producers to choose from to create freemium games and other ways to attract users. As a step one, we implemented an economic model when a collection sponsor can be set to pay for collection Transfer transactions.1617Wider NFT Ecosystem (most of it was developed during Hackusama):18* [SubstraPunks Game hosted on IPFS](https://github.com/usetech-llc/substrapunks)19* [NFT Wallet and UI](https://uniqueapps.usetech.com/#/nft)20* [NFT Asset for Unity Framework](https://github.com/usetech-llc/nft_unity)2122Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!2324## Hackusama Update2526During the Kusama Hackaphon the following changes were made:27* Enabled Smart Contracts Pallet28* Enabled integration between Smart Contracts and NFT Pallet (required special edition of RC4 Substrate version)29* Fixed misc. bugs in NFT Pallet30* Deployed NFT TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.31* New Features:32  * Re-Fungible Token Mode33  * Off-Chain Schema to store token image URLs34  * Alternative economic model35  * White Lists and Public Mint Permission36* Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and NFT Testnet Blockchain.3738## Application Development3940If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).4142## Building4344Building NFT chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything so that we can keep the builds stable.45461. Install Rust:4748```bash49curl https://sh.rustup.rs -sSf | sh50sudo apt-get install libssl-dev pkg-config libclang-dev clang51```52532. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.54553. Install Rust Toolchain 1.44.0:5657```bash58rustup install 1.44.059```60614. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)62```bash63rustup toolchain list64rustup default 1.44.0-x86_64-unknown-linux-gnu65```66675. Install nightly toolchain and add wasm target for it:6869```bash70rustup toolchain install nightly-2020-05-0171rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu72```73746. Build:75```bash76cargo build77```7879## Run8081You can start a development chain with:8283```bash84cargo run -- --dev85```8687Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.8889If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with testnet units. Give each node a name and expose them so they are listed on the Polkadot [telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.9091We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from the `--node-key` value that we specify below:9293```bash94cargo run -- \95  --base-path /tmp/alice \96  --chain=local \97  --alice \98  --node-key 0000000000000000000000000000000000000000000000000000000000000001 \99  --telemetry-url ws://telemetry.polkadot.io:1024 \100  --validator101```102103In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's bootnode ID on TCP port 30333:104105```bash106cargo run -- \107  --base-path /tmp/bob \108  --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \109  --chain=local \110  --bob \111  --port 30334 \112  --telemetry-url ws://telemetry.polkadot.io:1024 \113  --validator114```115116Additional CLI usage options are available and may be shown by running `cargo run -- --help`.117118## Benchmarks119120First of all, add rust toolchain and make it default.121```bash122rustup target add wasm32-unknown-unknown --toolchain nightly-2020-10-01123```124125Then in "/node/src" run build command below126```bash127cargo +nightly-2020-10-01 build --release --features runtime-benchmarks128```129130Run benchmark131```bash132target/release/nft benchmark --chain dev --pallet "pallet_nft" --extrinsic "*" --repeat 1133```134135## UI custom types136```137{138  "Schedule": {139    "version": "u32",140    "put_code_per_byte_cost": "Gas",141    "grow_mem_cost": "Gas",142    "regular_op_cost": "Gas",143    "return_data_per_byte_cost": "Gas",144    "event_data_per_byte_cost": "Gas",145    "event_per_topic_cost": "Gas",146    "event_base_cost": "Gas",147    "call_base_cost": "Gas",148    "instantiate_base_cost": "Gas",149    "dispatch_base_cost": "Gas",150    "sandbox_data_read_cost": "Gas",151    "sandbox_data_write_cost": "Gas",152    "transfer_cost": "Gas",153    "instantiate_cost": "Gas",154    "max_event_topics": "u32",155    "max_stack_height": "u32",156    "max_memory_pages": "u32",157    "max_table_size": "u32",158    "enable_println": "bool",159    "max_subject_len": "u32"160  },161  "AccessMode": {162    "_enum": [163      "Normal",164      "WhiteList"165    ]166  },167  "CollectionMode": {168    "_enum": {169      "Invalid": null,170      "NFT": null,171      "Fungible": "u32",172      "ReFungible": "u32"173    }174  },175  "Ownership": {176    "Owner": "AccountId",177    "Fraction": "u128"178  },179  "FungibleItemType": {180    "Collection": "u64",181    "Owner": "AccountId",182    "Value": "u128"183  },184  "ReFungibleItemType": {185    "Collection": "u64",186    "Owner": "Vec<Ownership>",187    "Data": "Vec<u8>"188  },189  "NftItemType": {190    "Collection": "u64",191    "Owner": "AccountId",192    "ConstData": "Vec<u8>",193    "VariableData": "Vec<u8>"194  },195  "Ownership": {196    "owner": "AccountId",197    "fraction": "u128"198  },199  "ReFungibleItemType": {200    "Collection": "u64",201    "Owner": "Vec<Ownership<AccountId>>",202    "ConstData": "Vec<u8>",203    "VariableData": "Vec<u8>"204  },205  "CollectionType": {206    "Owner": "AccountId",207    "Mode": "CollectionMode",208    "Access": "AccessMode",209    "DecimalPoints": "u32",210    "Name": "Vec<u16>",211    "Description": "Vec<u16>",212    "TokenPrefix": "Vec<u8>",213    "MintMode": "bool",214    "OffchainSchema": "Vec<u8>",215    "Sponsor": "AccountId",216    "UnconfirmedSponsor": "AccountId",217    "VariableOnChainSchema": "Vec<u8>",218    "ConstOnChainSchema": "Vec<u8>"219  },220  "ApprovePermissions": {221    "Approved": "AccountId",222    "Amount": "u64"223  },224  "RawData": "Vec<u8>",225  "Address": "AccountId",226  "LookupSource": "AccountId",227  "Weight": "u64",228  "CreateNftData": {229    "const_data": "Vec<u8>",230    "variable_data": "Vec<u8>" 231  },232  "CreateFungibleData": {},233  "CreateReFungibleData": {234    "const_data": "Vec<u8>",235    "variable_data": "Vec<u8>" 236  },237  "CreateItemData": {238    "_enum": {239      "NFT": "CreateNftData",240      "Fungible": "CreateFungibleData",241      "ReFungible": "CreateReFungibleData"242    }243  }244  "CollectionLimits": {245    "AccountTokenOwnershipLimit": "u32",246    "SponsoredDataSize": "u32",247    "TokenLimit": "u32",248    "SponsorTransferTimeout": "u32"249  }250}251252```
modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -171,7 +171,8 @@
                     sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     const_on_chain_schema: vec![],
-                    variable_on_chain_schema: vec![]
+					variable_on_chain_schema: vec![],
+					limits: CollectionLimits::default()
                 },
             )],
             nft_item_id: vec![],
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -109,6 +109,7 @@
     pub offchain_schema: Vec<u8>,
     pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
     pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
+    pub limits: CollectionLimits, // Collection private restrictions 
     pub variable_on_chain_schema: Vec<u8>, //
     pub const_on_chain_schema: Vec<u8>, //
 }
@@ -164,6 +165,27 @@
     pub start_block: BlockNumber,
 }
 
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CollectionLimits {
+    pub account_token_ownership_limit: u32,
+    pub sponsored_data_size: u32,
+    pub token_limit: u32,
+
+    // Timeouts for item types in passed blocks
+    pub sponsor_transfer_timeout: u32,
+}
+
+impl Default for CollectionLimits {
+    fn default() -> CollectionLimits {
+        CollectionLimits { 
+            account_token_ownership_limit: 0, 
+            token_limit: 0,
+            sponsored_data_size: 0, 
+            sponsor_transfer_timeout: 0 }
+    }
+}
+
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct ChainLimits {
@@ -321,7 +343,11 @@
         /// Unexpected collection type.
         UnexpectedCollectionType,
         /// Can't store metadata in fungible tokens.
-        CantStoreMetadataInFungibleTokens
+        CantStoreMetadataInFungibleTokens,
+        /// Collection token limit exceeded
+        CollectionTokenLimitExceeded,
+        /// Account token limit exceeded per collection
+        AccountTokenLimitExceeded
 	}
 }
 
@@ -537,6 +563,7 @@
                 unconfirmed_sponsor: T::AccountId::default(),
                 variable_on_chain_schema: Vec::new(),
                 const_on_chain_schema: Vec::new(),
+                limits: CollectionLimits::default(),
             };
 
             // Add new collection to map
@@ -1380,17 +1407,21 @@
             Ok(())
         }
 
-        // #[cfg(feature = "runtime-benchmarks")]
-        // #[weight = 0]
-        // pub fn add_contract_sponsoring_debug(
-        //     origin,
-        //     contract_address: T::AccountId, 
-        //     owner: T::AccountId) -> DispatchResult {
-        //     let sender = ensure_signed(origin)?;
-        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);
-        //     Ok(())
-        // }
-    
+        #[weight = 0]
+        pub fn set_collection_limits(
+            origin,
+            collection_id: u64,
+            limits: CollectionLimits,
+        ) -> DispatchResult {
+            let sender = ensure_signed(origin)?;
+            Self::check_owner_permissions(collection_id, sender.clone())?;
+
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            target_collection.limits = limits;
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        } 
     }
 }
 
@@ -1399,6 +1430,12 @@
     fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {
 
         if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+
+            // check token limit and account token limit
+            let total_items: u64 = ItemListIndex::get(collection_id);
+            let account_items: u32 = <AddressTokens<T>>::get(collection_id, sender.clone()).len() as u32;
+            ensure!(collection.limits.token_limit as u64 > total_items,  Error::<T>::CollectionTokenLimitExceeded);
+            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);
             ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);
             Self::check_white_list(collection_id, owner)?;
             Self::check_white_list(collection_id, sender)?;
@@ -1478,7 +1515,6 @@
                 Self::add_refungible_item(item)?;
             }
         };
-
 
         // call event
         Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
@@ -2211,18 +2247,35 @@
         // Determine who is paying transaction fee based on ecnomic model
         // Parse call to extract collection ID and access collection sponsor
         let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {
-            Some(Call::create_item(collection_id, _properties, _owner)) => {
-                <Collection<T>>::get(collection_id).sponsor
+            Some(Call::create_item(collection_id, _owner, _properties)) => {
+
+                // check free create limit
+                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)
+                {
+                    <Collection<T>>::get(collection_id).sponsor
+                } else {
+                    T::AccountId::default()
+                }
             }
             Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {
+                
+                let _collection_limits = <Collection<T>>::get(collection_id).limits;
                 let _collection_mode = <Collection<T>>::get(collection_id).mode;
 
                 // sponsor timeout
                 let sponsor_transfer = match _collection_mode {
                     CollectionMode::NFT => {
+
+                        // get correct limit
+                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+                            _collection_limits.sponsor_transfer_timeout
+                        } else {
+                            ChainLimit::get().nft_sponsor_transfer_timeout
+                        };
+
                         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();
+                        let limit_time = basket + limit.into();
                         if block_number >= limit_time {
                             <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);
                             true
@@ -2232,12 +2285,20 @@
                         }
                     }
                     CollectionMode::Fungible(_) => {
+
+                        // get correct limit
+                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+                            _collection_limits.sponsor_transfer_timeout
+                        } else {
+                            ChainLimit::get().fungible_sponsor_transfer_timeout
+                        };
+
                         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();
+                            let limit_time = item.start_block + limit.into();
                             if block_number >= limit_time {
                                 basket.retain(|x| x.address == item.address);
                                 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });
@@ -2254,9 +2315,17 @@
                         }
                     }
                     CollectionMode::ReFungible(_) => {
+
+                        // get correct limit
+                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+                            _collection_limits.sponsor_transfer_timeout
+                        } else {
+                            ChainLimit::get().refungible_sponsor_transfer_timeout
+                        };
+
                         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();
+                        let limit_time = basket + limit.into();
                         if block_number >= limit_time {
                             <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);
                             true