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

difftreelog

Add properties to RFT pallet

Trubnikov Sergey2022-06-23parent: #b62fa17.patch.diff
in: master

1 file changed

modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
8787
88#![cfg_attr(not(feature = "std"), no_std)]88#![cfg_attr(not(feature = "std"), no_std)]
8989
90use frame_support::{ensure, BoundedVec};90use frame_support::{ensure, BoundedVec, transactional};
91use up_data_structs::{91use up_data_structs::{
92 AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,92 AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
93 CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping, budget::Budget,93 CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping, budget::Budget,
94 Property, PropertyScope, TrySetProperty, PropertyKey, PropertyPermission
94};95};
95use pallet_evm::account::CrossAccountId;96use pallet_evm::account::CrossAccountId;
96use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};97use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, CommonCollectionOperations as _};
97use pallet_structure::Pallet as PalletStructure;98use pallet_structure::Pallet as PalletStructure;
98use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};99use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
99use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};100use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
175 QueryKind = ValueQuery,176 QueryKind = ValueQuery,
176 >;177 >;
178
179 #[pallet::storage]
180 #[pallet::getter(fn token_properties)]
181 pub type TokenProperties<T: Config> = StorageNMap<
182 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
183 Value = up_data_structs::Properties,
184 QueryKind = ValueQuery,
185 OnEmpty = up_data_structs::TokenProperties,
186 >;
177187
178 /// Total amount of pieces for token188 /// Total amount of pieces for token
179 #[pallet::storage]189 #[pallet::storage]
279 <TotalSupply<T>>::contains_key((collection.id, token))289 <TotalSupply<T>>::contains_key((collection.id, token))
280 }290 }
291
292 pub fn set_scoped_token_property(
293 collection_id: CollectionId,
294 token_id: TokenId,
295 scope: PropertyScope,
296 property: Property,
297 ) -> DispatchResult {
298 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {
299 properties.try_scoped_set(scope, property.key, property.value)
300 })
301 .map_err(<CommonError<T>>::from)?;
302
303 Ok(())
304 }
305
306 pub fn set_scoped_token_properties(
307 collection_id: CollectionId,
308 token_id: TokenId,
309 scope: PropertyScope,
310 properties: impl Iterator<Item = Property>,
311 ) -> DispatchResult {
312 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {
313 stored_properties.try_scoped_set_from_iter(scope, properties)
314 })
315 .map_err(<CommonError<T>>::from)?;
316
317 Ok(())
318 }
281}319}
282320
283// unchecked calls skips any permission checks321// unchecked calls skips any permission checks
339377
340 <TokensBurnt<T>>::insert(collection.id, burnt);378 <TokensBurnt<T>>::insert(collection.id, burnt);
341 <TokenData<T>>::remove((collection.id, token_id));379 <TokenData<T>>::remove((collection.id, token_id));
380 <TokenProperties<T>>::remove((collection.id, token_id));
342 <TotalSupply<T>>::remove((collection.id, token_id));381 <TotalSupply<T>>::remove((collection.id, token_id));
343 <Balance<T>>::remove_prefix((collection.id, token_id), None);382 <Balance<T>>::remove_prefix((collection.id, token_id), None);
344 <Allowance<T>>::remove_prefix((collection.id, token_id), None);383 <Allowance<T>>::remove_prefix((collection.id, token_id), None);
427 Ok(())466 Ok(())
428 }467 }
468
469 pub fn set_token_property(
470 collection: &RefungibleHandle<T>,
471 sender: &T::CrossAccountId,
472 token_id: TokenId,
473 property: Property,
474 is_token_create: bool,
475 ) -> DispatchResult {
476 Self::check_token_change_permission(
477 collection,
478 sender,
479 token_id,
480 &property.key,
481 is_token_create,
482 )?;
483
484 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
485 let property = property.clone();
486 properties.try_set(property.key, property.value)
487 })
488 .map_err(<CommonError<T>>::from)?;
489
490 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(
491 collection.id,
492 token_id,
493 property.key,
494 ));
495
496 Ok(())
497 }
498
499 #[transactional]
500 pub fn set_token_properties(
501 collection: &RefungibleHandle<T>,
502 sender: &T::CrossAccountId,
503 token_id: TokenId,
504 properties: Vec<Property>,
505 is_token_create: bool,
506 ) -> DispatchResult {
507 for property in properties {
508 Self::set_token_property(collection, sender, token_id, property, is_token_create)?;
509 }
510
511 Ok(())
512 }
513
514 pub fn delete_token_property(
515 collection: &RefungibleHandle<T>,
516 sender: &T::CrossAccountId,
517 token_id: TokenId,
518 property_key: PropertyKey,
519 ) -> DispatchResult {
520 Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?;
521
522 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
523 properties.remove(&property_key)
524 })
525 .map_err(<CommonError<T>>::from)?;
526
527 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(
528 collection.id,
529 token_id,
530 property_key,
531 ));
532
533 Ok(())
534 }
535
536 fn check_token_change_permission(
537 collection: &RefungibleHandle<T>,
538 sender: &T::CrossAccountId,
539 token_id: TokenId,
540 property_key: &PropertyKey,
541 is_token_create: bool,
542 ) -> DispatchResult {
543 let permission = <PalletCommon<T>>::property_permissions(collection.id)
544 .get(property_key)
545 .cloned()
546 .unwrap_or_else(PropertyPermission::none);
547
548 // Not "try_fold" because total count of pieces is limited by 'MAX_REFUNGIBLE_PIECES'.
549 let total_pieces: u128 = <Balance<T>>::iter_prefix((collection.id, token_id,)).fold(0, |total, piece| total + piece.1);
550 let balance = collection.balance(sender.clone(), token_id);
551
552 let check_token_owner = || -> DispatchResult {
553 ensure!(balance == total_pieces, <CommonError<T>>::NoPermission);
554 Ok(())
555 };
556
557 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))
558 .get(property_key)
559 .is_some();
560
561 match permission {
562 PropertyPermission { mutable: false, .. } if is_property_exists => {
563 Err(<CommonError<T>>::NoPermission.into())
564 }
565
566 PropertyPermission {
567 collection_admin,
568 token_owner,
569 ..
570 } => {
571 //TODO: investigate threats during public minting.
572 if is_token_create && (collection_admin || token_owner) {
573 return Ok(());
574 }
575
576 let mut check_result = Err(<CommonError<T>>::NoPermission.into());
577
578 if collection_admin {
579 check_result = collection.check_is_owner_or_admin(sender);
580 }
581
582 if token_owner {
583 check_result.or_else(|_| check_token_owner())
584 } else {
585 check_result
586 }
587 }
588 }
589 }
590
591 #[transactional]
592 pub fn delete_token_properties(
593 collection: &RefungibleHandle<T>,
594 sender: &T::CrossAccountId,
595 token_id: TokenId,
596 property_keys: Vec<PropertyKey>,
597 ) -> DispatchResult {
598 for key in property_keys {
599 Self::delete_token_property(collection, sender, token_id, key)?;
600 }
601
602 Ok(())
603 }
429604
430 /// Transfer RFT token pieces from one account to another.605 /// Transfer RFT token pieces from one account to another.
431 ///606 ///