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

difftreelog

Merge pull request #32 from usetech-llc/feature/nftpar_118

Greg Zaitsev2020-12-15parents: #c313f34 #2b0cd0f.patch.diff
in: master
NFTPAR-118. Per Collection Limits

4 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
171 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![],
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
46mod default_weights;46mod default_weights;
4747
48pub 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;
4951
50// Structs52// Structs
51// #region53// #region
116 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 sender
118 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship120 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
121 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}
173176
177#[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,
183
184 // Timeouts for item types in passed blocks
185 pub sponsor_transfer_timeout: u32,
186}
187
188impl 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}
197
174#[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 exceeded
357 CollectionTokenLimitExceeded,
358 /// Account token limit exceeded per collection
359 AccountTokenLimitExceeded,
360 /// Collection limit bounds per collection exceeded
361 CollectionLimitBoundsExceeded
332 }362 }
333}363}
334364
544 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 };
548579
549 // Add new collection to map580 // Add new collection to map
1022 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 {
10231054
1024 let sender = ensure_signed(origin)?;1055 let sender = ensure_signed(origin)?;
1056 let target_collection = <Collection<T>>::get(collection_id);
10251057
1058 // Limits check
1059 Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;
1060
1026 // Transfer permissions check1061 // Transfer permissions check
1027 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 }
11371171
1138 // Transfer permissions check
1139 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);
11421173
1174 // Limits check
1175 Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;
1176
1177 // Transfer permissions check
1178 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
1179 Error::<T>::NoPermission);
1180
1143 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 }
13891427
1390 // #[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;
1439
1440 // collection bounds
1441 ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&
1442 limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,
1443 Error::<T>::CollectionLimitBoundsExceeded);
1444
1397 // <ContractOwner<T>>::insert(contract_address.clone(), owner);1445 // token_limit check prev
1446 ensure!(climits.token_limit > limits.token_limit &&
1447 limits.token_limit <= chain_limits.account_token_ownership_limit,
1448 Error::<T>::AccountTokenLimitExceeded);
1449
1450 target_collection.limits = limits;
1451 <Collection<T>>::insert(collection_id, target_collection);
1452
1398 // Ok(())1453 Ok(())
1399 // }1454 }
1400
1401 }1455 }
1402}1456}
14031457
1404impl<T: Trait> Module<T> {1458impl<T: Trait> Module<T> {
14051459
1460 fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {
1461
1462 // check token limit and account token limit
1463 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 }
1468
1406 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 {
14071470
1471 // check token limit and account token limit
1472 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);
1476
1408 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 };
14881557
1489
1490 // call event1558 // call event
1491 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));1559 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
14921560
2213 // Determine who is paying transaction fee based on ecnomic model2281 // Determine who is paying transaction fee based on ecnomic model
2214 // Parse call to extract collection ID and access collection sponsor2282 // Parse call to extract collection ID and access collection sponsor
2215 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)) => {
2285
2217 <Collection<T>>::get(collection_id).sponsor2286 // check free create limit
2287 if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)
2288 {
2289 <Collection<T>>::get(collection_id).sponsor
2290 } 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;
22212298
2222 // sponsor timeout2299 // sponsor timeout
2223 let sponsor_transfer = match _collection_mode {2300 let sponsor_transfer = match _collection_mode {
2224 CollectionMode::NFT => {2301 CollectionMode::NFT => {
2302
2303 // get correct limit
2304 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2305 _collection_limits.sponsor_transfer_timeout
2306 } else {
2307 ChainLimit::get().nft_sponsor_transfer_timeout
2308 };
2309
2225 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 true
2234 }2319 }
2235 }2320 }
2236 CollectionMode::Fungible(_) => {2321 CollectionMode::Fungible(_) => {
2322
2323 // get correct limit
2324 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2325 _collection_limits.sponsor_transfer_timeout
2326 } else {
2327 ChainLimit::get().fungible_sponsor_transfer_timeout
2328 };
2329
2237 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(_) => {
2352
2353 // get correct limit
2354 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2355 _collection_limits.sponsor_transfer_timeout
2356 } else {
2357 ChainLimit::get().refungible_sponsor_transfer_timeout
2358 };
2359
2259 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 true
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
3use 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 };
99
modifiedruntime_types.jsondiffbeforeafterboth
46 "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