difftreelog
Merge pull request #32 from usetech-llc/feature/nftpar_118
in: master
NFTPAR-118. Per Collection Limits
4 files changed
node/src/chain_spec.rsdiffbeforeafterboth171 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),171 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),172 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),172 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),173 const_on_chain_schema: vec![],173 const_on_chain_schema: vec![],174 variable_on_chain_schema: vec![]174 variable_on_chain_schema: vec![],175 limits: CollectionLimits::default()175 },176 },176 )],177 )],177 nft_item_id: vec![],178 nft_item_id: vec![],pallets/nft/src/lib.rsdiffbeforeafterboth46mod default_weights;46mod default_weights;474748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;48pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;49pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;50pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;495150// Structs52// Structs51// #region53// #region116 pub offchain_schema: Vec<u8>,118 pub offchain_schema: Vec<u8>,117 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender119 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender118 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship120 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship121 pub limits: CollectionLimits, // Collection private restrictions 119 pub variable_on_chain_schema: Vec<u8>, //122 pub variable_on_chain_schema: Vec<u8>, //120 pub const_on_chain_schema: Vec<u8>, //123 pub const_on_chain_schema: Vec<u8>, //121}124}171 pub start_block: BlockNumber,174 pub start_block: BlockNumber,172}175}173176177#[derive(Encode, Decode, Debug, Clone, PartialEq)]178#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]179pub struct CollectionLimits {180 pub account_token_ownership_limit: u32,181 pub sponsored_data_size: u32,182 pub token_limit: u32,183184 // Timeouts for item types in passed blocks185 pub sponsor_transfer_timeout: u32,186}187188impl Default for CollectionLimits {189 fn default() -> CollectionLimits {190 CollectionLimits { 191 account_token_ownership_limit: 10_000_000, 192 token_limit: u32::max_value(),193 sponsored_data_size: u32::max_value(), 194 sponsor_transfer_timeout: 14400 }195 }196}197174#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]198#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]175#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]199#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]176pub struct ChainLimits {200pub struct ChainLimits {328 /// Unexpected collection type.352 /// Unexpected collection type.329 UnexpectedCollectionType,353 UnexpectedCollectionType,330 /// Can't store metadata in fungible tokens.354 /// Can't store metadata in fungible tokens.331 CantStoreMetadataInFungibleTokens355 CantStoreMetadataInFungibleTokens,356 /// Collection token limit exceeded357 CollectionTokenLimitExceeded,358 /// Account token limit exceeded per collection359 AccountTokenLimitExceeded,360 /// Collection limit bounds per collection exceeded361 CollectionLimitBoundsExceeded332 }362 }333}363}334364544 unconfirmed_sponsor: T::AccountId::default(),574 unconfirmed_sponsor: T::AccountId::default(),545 variable_on_chain_schema: Vec::new(),575 variable_on_chain_schema: Vec::new(),546 const_on_chain_schema: Vec::new(),576 const_on_chain_schema: Vec::new(),577 limits: CollectionLimits::default(),547 };578 };548579549 // Add new collection to map580 // Add new collection to map1022 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1053 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {102310541024 let sender = ensure_signed(origin)?;1055 let sender = ensure_signed(origin)?;1056 let target_collection = <Collection<T>>::get(collection_id);102510571058 // Limits check1059 Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10601026 // Transfer permissions check1061 // Transfer permissions check1027 let target_collection = <Collection<T>>::get(collection_id);1028 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1062 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1029 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1063 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1030 Error::<T>::NoPermission);1064 Error::<T>::NoPermission);1135 }1169 }1136 }1170 }113711711138 // Transfer permissions check1139 let target_collection = <Collection<T>>::get(collection_id);1172 let target_collection = <Collection<T>>::get(collection_id);1140 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1141 Error::<T>::NoPermission);114211731174 // Limits check1175 Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11761177 // Transfer permissions check 1178 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1179 Error::<T>::NoPermission);11801143 if target_collection.access == AccessMode::WhiteList {1181 if target_collection.access == AccessMode::WhiteList {1144 Self::check_white_list(collection_id, &sender)?;1182 Self::check_white_list(collection_id, &sender)?;1145 Self::check_white_list(collection_id, &recipient)?;1183 Self::check_white_list(collection_id, &recipient)?;1387 Ok(())1425 Ok(())1388 }1426 }138914271390 // #[cfg(feature = "runtime-benchmarks")]1428 #[weight = 0]1391 // #[weight = 0]1429 pub fn set_collection_limits(1430 origin,1392 // pub fn add_contract_sponsoring_debug(1431 collection_id: u32,1393 // origin,1432 limits: CollectionLimits,1394 // contract_address: T::AccountId, 1433 ) -> DispatchResult {1434 let sender = ensure_signed(origin)?;1435 Self::check_owner_permissions(collection_id, sender.clone())?;1395 // owner: T::AccountId) -> DispatchResult {1436 let mut target_collection = <Collection<T>>::get(collection_id);1437 let chain_limits = ChainLimit::get();1396 // let sender = ensure_signed(origin)?;1438 let climits = target_collection.limits;14391440 // collection bounds1441 ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1442 limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP, 1443 Error::<T>::CollectionLimitBoundsExceeded);14441397 // <ContractOwner<T>>::insert(contract_address.clone(), owner);1445 // token_limit check prev1446 ensure!(climits.token_limit > limits.token_limit && 1447 limits.token_limit <= chain_limits.account_token_ownership_limit, 1448 Error::<T>::AccountTokenLimitExceeded);14491450 target_collection.limits = limits;1451 <Collection<T>>::insert(collection_id, target_collection);14521398 // Ok(())1453 Ok(())1399 // }1454 } 1400 1401 }1455 }1402}1456}140314571404impl<T: Trait> Module<T> {1458impl<T: Trait> Module<T> {140514591460 fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {1461 1462 // check token limit and account token limit1463 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1464 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1465 1466 Ok(())1467 }14681406 fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {1469 fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {140714701471 // check token limit and account token limit1472 let total_items: u32 = ItemListIndex::get(collection_id);1473 let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1474 ensure!(collection.limits.token_limit > total_items, Error::<T>::CollectionTokenLimitExceeded);1475 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);14761408 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1477 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1409 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1478 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1410 Self::check_white_list(collection_id, owner)?;1479 Self::check_white_list(collection_id, owner)?;1486 }1555 }1487 };1556 };1488155714891490 // call event1558 // call event1491 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));1559 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));149215602213 // Determine who is paying transaction fee based on ecnomic model2281 // Determine who is paying transaction fee based on ecnomic model2214 // Parse call to extract collection ID and access collection sponsor2282 // Parse call to extract collection ID and access collection sponsor2215 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2283 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2216 Some(Call::create_item(collection_id, _properties, _owner)) => {2284 Some(Call::create_item(collection_id, _owner, _properties)) => {22852217 <Collection<T>>::get(collection_id).sponsor2286 // check free create limit2287 if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2288 {2289 <Collection<T>>::get(collection_id).sponsor2290 } else {2291 T::AccountId::default()2292 }2218 }2293 }2219 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2294 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2295 2296 let _collection_limits = <Collection<T>>::get(collection_id).limits;2220 let _collection_mode = <Collection<T>>::get(collection_id).mode;2297 let _collection_mode = <Collection<T>>::get(collection_id).mode;222122982222 // sponsor timeout2299 // sponsor timeout2223 let sponsor_transfer = match _collection_mode {2300 let sponsor_transfer = match _collection_mode {2224 CollectionMode::NFT => {2301 CollectionMode::NFT => {23022303 // get correct limit2304 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2305 _collection_limits.sponsor_transfer_timeout2306 } else {2307 ChainLimit::get().nft_sponsor_transfer_timeout2308 };23092225 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2310 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2226 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2311 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2227 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2312 let limit_time = basket + limit.into();2228 if block_number >= limit_time {2313 if block_number >= limit_time {2229 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2314 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2230 true2315 true2234 }2319 }2235 }2320 }2236 CollectionMode::Fungible(_) => {2321 CollectionMode::Fungible(_) => {23222323 // get correct limit2324 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2325 _collection_limits.sponsor_transfer_timeout2326 } else {2327 ChainLimit::get().fungible_sponsor_transfer_timeout2328 };23292237 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2330 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2238 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2331 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2239 if basket.iter().any(|i| i.address == _new_owner.clone())2332 if basket.iter().any(|i| i.address == _new_owner.clone())2240 {2333 {2241 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2334 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2242 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2335 let limit_time = item.start_block + limit.into();2243 if block_number >= limit_time {2336 if block_number >= limit_time {2244 basket.retain(|x| x.address == item.address);2337 basket.retain(|x| x.address == item.address);2245 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2338 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2256 }2349 }2257 }2350 }2258 CollectionMode::ReFungible(_) => {2351 CollectionMode::ReFungible(_) => {23522353 // get correct limit2354 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2355 _collection_limits.sponsor_transfer_timeout2356 } else {2357 ChainLimit::get().refungible_sponsor_transfer_timeout2358 };23592259 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2360 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2260 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2361 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2261 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2362 let limit_time = basket + limit.into();2262 if block_number >= limit_time {2363 if block_number >= limit_time {2263 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2364 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2264 true2365 truepallets/nft/src/tests.rsdiffbeforeafterboth3use crate::mock::*;3use crate::mock::*;4use crate::{AccessMode, ApprovePermissions, CollectionMode,4use crate::{AccessMode, ApprovePermissions, CollectionMode,5 Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,5 Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,6 CollectionId, TokenId, MAX_DECIMAL_POINTS}; //Err6 CollectionId, TokenId, MAX_DECIMAL_POINTS};7use frame_support::{assert_noop, assert_ok};7use frame_support::{assert_noop, assert_ok};8use frame_system::{ RawOrigin };8use frame_system::{ RawOrigin };99runtime_types.jsondiffbeforeafterboth46 "Owner": "AccountId",46 "Owner": "AccountId",47 "Value": "u128"47 "Value": "u128"48 },48 },49 "ReFungibleItemType": {50 "Collection": "CollectionId",51 "Owner": "Vec<Ownership>",52 "Data": "Vec<u8>"53 },54 "NftItemType": {49 "NftItemType": {55 "Collection": "CollectionId",50 "Collection": "CollectionId",56 "Owner": "AccountId",51 "Owner": "AccountId",57 "ConstData": "Vec<u8>",52 "ConstData": "Vec<u8>",58 "VariableData": "Vec<u8>"53 "VariableData": "Vec<u8>"59 },54 },60 "Ownership": {61 "owner": "AccountId",62 "fraction": "u128"63 },64 "ReFungibleItemType": {55 "ReFungibleItemType": {65 "Collection": "CollectionId",56 "Collection": "CollectionId",66 "Owner": "Vec<Ownership<AccountId>>",57 "Owner": "Vec<Ownership<AccountId>>",79 "OffchainSchema": "Vec<u8>",70 "OffchainSchema": "Vec<u8>",80 "Sponsor": "AccountId",71 "Sponsor": "AccountId",81 "UnconfirmedSponsor": "AccountId",72 "UnconfirmedSponsor": "AccountId",73 "Limits": "CollectionLimits",82 "VariableOnChainSchema": "Vec<u8>",74 "VariableOnChainSchema": "Vec<u8>",83 "ConstOnChainSchema": "Vec<u8>"75 "ConstOnChainSchema": "Vec<u8>"84 },76 },117 "account_token_ownership_limit": "u32",109 "account_token_ownership_limit": "u32",118 "collections_admins_limit": "u64",110 "collections_admins_limit": "u64",119 "custom_data_limit": "u32",111 "custom_data_limit": "u32",120 "nft_sponsor_transfer_timeout": "u32",112 "nft_sponsor_timeout": "u32",121 "fungible_sponsor_transfer_timeout": "u32",113 "fungible_sponsor_timeout": "u32",122 "refungible_sponsor_transfer_timeout": "u32"114 "refungible_sponsor_timeout": "u32"123 }115 },116 "CollectionLimits": {117 "AccountTokenOwnershipLimit": "u32",118 "SponsoredMintSize": "u32",119 "TokenLimit": "u32",120 "SponsorTimeout": "u32"121 }124 }122 }125 123