difftreelog
Add properties to RFT pallet
in: master
1 file changed
pallets/refungible/src/lib.rsdiffbeforeafterboth878788#![cfg_attr(not(feature = "std"), no_std)]88#![cfg_attr(not(feature = "std"), no_std)]898990use 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, PropertyPermission94};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 >;178179 #[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 >;177187178 /// Total amount of pieces for token188 /// Total amount of pieces for token179 #[pallet::storage]189 #[pallet::storage]279 <TotalSupply<T>>::contains_key((collection.id, token))289 <TotalSupply<T>>::contains_key((collection.id, token))280 }290 }291292 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)?;302303 Ok(())304 }305306 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)?;316317 Ok(())318 }281}319}282320283// unchecked calls skips any permission checks321// unchecked calls skips any permission checks339377340 <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 }468469 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 )?;483484 <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)?;489490 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(491 collection.id,492 token_id,493 property.key,494 ));495496 Ok(())497 }498499 #[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 }510511 Ok(())512 }513514 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)?;521522 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {523 properties.remove(&property_key)524 })525 .map_err(<CommonError<T>>::from)?;526527 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(528 collection.id,529 token_id,530 property_key,531 ));532533 Ok(())534 }535536 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);547548 // 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);551552 let check_token_owner = || -> DispatchResult {553 ensure!(balance == total_pieces, <CommonError<T>>::NoPermission);554 Ok(())555 };556557 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))558 .get(property_key)559 .is_some();560561 match permission {562 PropertyPermission { mutable: false, .. } if is_property_exists => {563 Err(<CommonError<T>>::NoPermission.into())564 }565566 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 }575576 let mut check_result = Err(<CommonError<T>>::NoPermission.into());577578 if collection_admin {579 check_result = collection.check_is_owner_or_admin(sender);580 }581582 if token_owner {583 check_result.or_else(|_| check_token_owner())584 } else {585 check_result586 }587 }588 }589 }590591 #[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 }601602 Ok(())603 }429604430 /// Transfer RFT token pieces from one account to another.605 /// Transfer RFT token pieces from one account to another.431 ///606 ///