diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs index a878a4910f769..117fc15764060 100644 --- a/frame/uniques/src/functions.rs +++ b/frame/uniques/src/functions.rs @@ -18,7 +18,7 @@ //! Various pieces of common functionality. use super::*; -use frame_support::{ensure, traits::Get}; +use frame_support::{ensure, traits::Get, BoundedVec}; use sp_runtime::{DispatchError, DispatchResult}; impl, I: 'static> Pallet { @@ -142,4 +142,47 @@ impl, I: 'static> Pallet { Self::deposit_event(Event::Burned(class, instance, owner)); Ok(()) } + + pub(super) fn do_set_attribute( + class: T::ClassId, + maybe_instance: Option, + maybe_check_owner: &Option, + key: BoundedVec, + value: BoundedVec, + with_details: impl FnOnce(&ClassDetailsFor) -> DispatchResult, + ) -> DispatchResult { + let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + with_details(&class_details)?; + + let maybe_is_frozen = match maybe_instance { + None => ClassMetadataOf::::get(class).map(|v| v.is_frozen), + Some(instance) => InstanceMetadataOf::::get(class, instance).map(|v| v.is_frozen), + }; + ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); + + let attribute = Attribute::::get((class, maybe_instance, &key)); + if attribute.is_none() { + class_details.attributes.saturating_inc(); + } + let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); + class_details.total_deposit.saturating_reduce(old_deposit); + let mut deposit = Zero::zero(); + if !class_details.free_holding && maybe_check_owner.is_some() { + deposit = T::DepositPerByte::get() + .saturating_mul((key.len().saturating_add(value.len()) as u32).into()) + .saturating_add(T::AttributeDepositBase::get()); + } + class_details.total_deposit.saturating_accrue(deposit); + if deposit > old_deposit { + T::Currency::reserve(&class_details.owner, deposit.saturating_sub(old_deposit))?; + } else if deposit < old_deposit { + T::Currency::unreserve(&class_details.owner, old_deposit.saturating_sub(deposit)); + } + + Attribute::::insert((&class, maybe_instance, &key), (&value, deposit)); + Class::::insert(class, &class_details); + + Self::deposit_event(Event::AttributeSet(class, maybe_instance, key, value)); + Ok(()) + } } diff --git a/frame/uniques/src/impl_nonfungibles.rs b/frame/uniques/src/impl_nonfungibles.rs index c5d5c6089f865..9ccf60c5d2fae 100644 --- a/frame/uniques/src/impl_nonfungibles.rs +++ b/frame/uniques/src/impl_nonfungibles.rs @@ -26,7 +26,7 @@ use frame_support::{ BoundedSlice, }; use sp_runtime::DispatchResult; -use sp_std::convert::TryFrom; +use sp_std::convert::{TryFrom, TryInto}; impl, I: 'static> Inspect<::AccountId> for Pallet { type InstanceId = T::InstanceId; @@ -118,6 +118,43 @@ impl, I: 'static> Mutate<::AccountId> for Pallet fn burn_from(class: &Self::ClassId, instance: &Self::InstanceId) -> DispatchResult { Self::do_burn(class.clone(), instance.clone(), |_, _| Ok(())) } + + fn set_attribute( + class: &Self::ClassId, + instance: &Self::InstanceId, + key: &[u8], + value: &[u8], + ) -> DispatchResult { + let bounded_key = key.to_vec().try_into().map_err(|_| Error::::KeyUpperBoundExceeded)?; + let bounded_value = value.to_vec().try_into().map_err(|_| Error::::ValueUpperBoundExceeded)?; + + Self::do_set_attribute( + class.clone(), + Some(instance.clone()), + &None, + bounded_key, + bounded_value, + |_| Ok(()), + ) + } + + fn set_class_attribute( + class: &Self::ClassId, + key: &[u8], + value: &[u8], + ) -> DispatchResult { + let bounded_key = key.to_vec().try_into().map_err(|_| Error::::KeyUpperBoundExceeded)?; + let bounded_value = value.to_vec().try_into().map_err(|_| Error::::ValueUpperBoundExceeded)?; + + Self::do_set_attribute( + class.clone(), + None, + &None, + bounded_key, + bounded_value, + |_| Ok(()), + ) + } } impl, I: 'static> Transfer for Pallet { diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index ee052486b03aa..bd574d2391f25 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -277,6 +277,10 @@ pub mod pallet { NoDelegate, /// No approval exists that would allow the transfer. Unapproved, + /// Attribute key upper bound exceeded. + KeyUpperBoundExceeded, + /// Attribute value upper bound exceeded. + ValueUpperBoundExceeded, } #[pallet::hooks] @@ -919,52 +923,34 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::weight(T::WeightInfo::set_attribute())] - pub fn set_attribute( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - maybe_instance: Option, - key: BoundedVec, - value: BoundedVec, - ) -> DispatchResult { - let maybe_check_owner = T::ForceOrigin::try_origin(origin) - .map(|_| None) - .or_else(|origin| ensure_signed(origin).map(Some))?; - - let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &class_details.owner, Error::::NoPermission); - } - let maybe_is_frozen = match maybe_instance { - None => ClassMetadataOf::::get(class).map(|v| v.is_frozen), - Some(instance) => - InstanceMetadataOf::::get(class, instance).map(|v| v.is_frozen), - }; - ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); - - let attribute = Attribute::::get((class, maybe_instance, &key)); - if attribute.is_none() { - class_details.attributes.saturating_inc(); - } - let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); - class_details.total_deposit.saturating_reduce(old_deposit); - let mut deposit = Zero::zero(); - if !class_details.free_holding && maybe_check_owner.is_some() { - deposit = T::DepositPerByte::get() - .saturating_mul(((key.len() + value.len()) as u32).into()) - .saturating_add(T::AttributeDepositBase::get()); - } - class_details.total_deposit.saturating_accrue(deposit); - if deposit > old_deposit { - T::Currency::reserve(&class_details.owner, deposit - old_deposit)?; - } else if deposit < old_deposit { - T::Currency::unreserve(&class_details.owner, old_deposit - deposit); - } - - Attribute::::insert((&class, maybe_instance, &key), (&value, deposit)); - Class::::insert(class, &class_details); - Self::deposit_event(Event::AttributeSet(class, maybe_instance, key, value)); - Ok(()) - } + pub fn set_attribute( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + maybe_instance: Option, + key: BoundedVec, + value: BoundedVec, + ) -> DispatchResult { + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; + + Self::do_set_attribute( + class, + maybe_instance, + &maybe_check_owner, + key, + value, + |class_details| { + if let Some(check_owner) = &maybe_check_owner { + ensure!( + check_owner == &class_details.owner, + Error::::NoPermission + ); + } + Ok(()) + }, + ) + } /// Set an attribute for an asset class or instance. ///