mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 02:17:58 +00:00
[NFTs] Offchain mint (#13158)
* Allow to mint with the pre-signed signatures * Another try * WIP: test encoder * Fix the deposits * Refactoring + tests + benchmarks * Add sp-core/runtime-benchmarks * Remove sp-core from dev deps * Enable full_crypto for benchmarks * Typo * Fix * Update frame/nfts/src/mock.rs Co-authored-by: Squirrel <gilescope@gmail.com> * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Add docs * Add attributes into the pre-signed object & track the deposit owner for attributes * Update docs * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts * Add the number of attributes provided to weights * Apply suggestions * Remove dead code * Remove Copy * Fix docs * Update frame/nfts/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/nfts/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> --------- Co-authored-by: Squirrel <gilescope@gmail.com> Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
@@ -26,6 +26,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
namespace: AttributeNamespace<T::AccountId>,
|
||||
key: BoundedVec<u8, T::KeyLimit>,
|
||||
value: BoundedVec<u8, T::ValueLimit>,
|
||||
depositor: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
ensure!(
|
||||
Self::is_pallet_feature_enabled(PalletFeature::Attributes),
|
||||
@@ -66,7 +67,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
}
|
||||
|
||||
let attribute = Attribute::<T, I>::get((collection, maybe_item, &namespace, &key));
|
||||
if attribute.is_none() {
|
||||
let attribute_exists = attribute.is_some();
|
||||
if !attribute_exists {
|
||||
collection_details.attributes.saturating_inc();
|
||||
}
|
||||
|
||||
@@ -74,6 +76,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
attribute.map_or(AttributeDeposit { account: None, amount: Zero::zero() }, |m| m.1);
|
||||
|
||||
let mut deposit = Zero::zero();
|
||||
// disabled DepositRequired setting only affects the CollectionOwner namespace
|
||||
if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) ||
|
||||
namespace != AttributeNamespace::CollectionOwner
|
||||
{
|
||||
@@ -82,33 +85,50 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
.saturating_add(T::AttributeDepositBase::get());
|
||||
}
|
||||
|
||||
let is_collection_owner_namespace = namespace == AttributeNamespace::CollectionOwner;
|
||||
let is_depositor_collection_owner =
|
||||
is_collection_owner_namespace && collection_details.owner == depositor;
|
||||
|
||||
// NOTE: in the CollectionOwner namespace if the depositor is `None` that means the deposit
|
||||
// was paid by the collection's owner.
|
||||
let old_depositor =
|
||||
if is_collection_owner_namespace && old_deposit.account.is_none() && attribute_exists {
|
||||
Some(collection_details.owner.clone())
|
||||
} else {
|
||||
old_deposit.account
|
||||
};
|
||||
let depositor_has_changed = old_depositor != Some(depositor.clone());
|
||||
|
||||
// NOTE: when we transfer an item, we don't move attributes in the ItemOwner namespace.
|
||||
// When the new owner updates the same attribute, we will update the depositor record
|
||||
// and return the deposit to the previous owner.
|
||||
if old_deposit.account.is_some() && old_deposit.account != Some(origin.clone()) {
|
||||
T::Currency::unreserve(&old_deposit.account.unwrap(), old_deposit.amount);
|
||||
T::Currency::reserve(&origin, deposit)?;
|
||||
if depositor_has_changed {
|
||||
if let Some(old_depositor) = old_depositor {
|
||||
T::Currency::unreserve(&old_depositor, old_deposit.amount);
|
||||
}
|
||||
T::Currency::reserve(&depositor, deposit)?;
|
||||
} else if deposit > old_deposit.amount {
|
||||
T::Currency::reserve(&origin, deposit - old_deposit.amount)?;
|
||||
T::Currency::reserve(&depositor, deposit - old_deposit.amount)?;
|
||||
} else if deposit < old_deposit.amount {
|
||||
T::Currency::unreserve(&origin, old_deposit.amount - deposit);
|
||||
T::Currency::unreserve(&depositor, old_deposit.amount - deposit);
|
||||
}
|
||||
|
||||
// NOTE: we don't track the depositor in the CollectionOwner namespace as it's always a
|
||||
// collection's owner. This simplifies the collection's transfer to another owner.
|
||||
let deposit_owner = match namespace {
|
||||
AttributeNamespace::CollectionOwner => {
|
||||
collection_details.owner_deposit.saturating_accrue(deposit);
|
||||
if is_depositor_collection_owner {
|
||||
if !depositor_has_changed {
|
||||
collection_details.owner_deposit.saturating_reduce(old_deposit.amount);
|
||||
None
|
||||
},
|
||||
_ => Some(origin),
|
||||
};
|
||||
}
|
||||
collection_details.owner_deposit.saturating_accrue(deposit);
|
||||
}
|
||||
|
||||
let new_deposit_owner = match is_depositor_collection_owner {
|
||||
true => None,
|
||||
false => Some(depositor),
|
||||
};
|
||||
Attribute::<T, I>::insert(
|
||||
(&collection, maybe_item, &namespace, &key),
|
||||
(&value, AttributeDeposit { account: deposit_owner, amount: deposit }),
|
||||
(&value, AttributeDeposit { account: new_deposit_owner, amount: deposit }),
|
||||
);
|
||||
|
||||
Collection::<T, I>::insert(collection, &collection_details);
|
||||
Self::deposit_event(Event::AttributeSet { collection, maybe_item, key, value, namespace });
|
||||
Ok(())
|
||||
@@ -188,10 +208,21 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
// NOTE: if the item was previously burned, the ItemConfigOf record
|
||||
// might not exist. In that case, we allow to clear the attribute.
|
||||
let maybe_is_locked = Self::get_item_config(&collection, &item)
|
||||
.map_or(false, |c| {
|
||||
c.has_disabled_setting(ItemSetting::UnlockedAttributes)
|
||||
.map_or(None, |c| {
|
||||
Some(c.has_disabled_setting(ItemSetting::UnlockedAttributes))
|
||||
});
|
||||
ensure!(!maybe_is_locked, Error::<T, I>::LockedItemAttributes);
|
||||
match maybe_is_locked {
|
||||
Some(is_locked) => {
|
||||
// when item exists, then only the collection's owner can clear that
|
||||
// attribute
|
||||
ensure!(
|
||||
check_owner == &collection_details.owner,
|
||||
Error::<T, I>::NoPermission
|
||||
);
|
||||
ensure!(!is_locked, Error::<T, I>::LockedItemAttributes);
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
},
|
||||
},
|
||||
_ => (),
|
||||
@@ -199,16 +230,16 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
}
|
||||
|
||||
collection_details.attributes.saturating_dec();
|
||||
match namespace {
|
||||
AttributeNamespace::CollectionOwner => {
|
||||
|
||||
match deposit.account {
|
||||
Some(deposit_account) => {
|
||||
T::Currency::unreserve(&deposit_account, deposit.amount);
|
||||
},
|
||||
None if namespace == AttributeNamespace::CollectionOwner => {
|
||||
collection_details.owner_deposit.saturating_reduce(deposit.amount);
|
||||
T::Currency::unreserve(&collection_details.owner, deposit.amount);
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
|
||||
if let Some(deposit_account) = deposit.account {
|
||||
T::Currency::unreserve(&deposit_account, deposit.amount);
|
||||
}
|
||||
|
||||
Collection::<T, I>::insert(collection, &collection_details);
|
||||
|
||||
@@ -85,6 +85,62 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn do_mint_pre_signed(
|
||||
mint_to: T::AccountId,
|
||||
mint_data: PreSignedMintOf<T, I>,
|
||||
signer: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
let PreSignedMint { collection, item, attributes, metadata, deadline, only_account } =
|
||||
mint_data;
|
||||
let metadata = Self::construct_metadata(metadata)?;
|
||||
|
||||
ensure!(
|
||||
attributes.len() <= T::MaxAttributesPerCall::get() as usize,
|
||||
Error::<T, I>::MaxAttributesLimitReached
|
||||
);
|
||||
if let Some(account) = only_account {
|
||||
ensure!(account == mint_to, Error::<T, I>::WrongOrigin);
|
||||
}
|
||||
|
||||
let now = frame_system::Pallet::<T>::block_number();
|
||||
ensure!(deadline >= now, Error::<T, I>::DeadlineExpired);
|
||||
|
||||
let collection_details =
|
||||
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
|
||||
ensure!(collection_details.owner == signer, Error::<T, I>::NoPermission);
|
||||
|
||||
let item_config = ItemConfig { settings: Self::get_default_item_settings(&collection)? };
|
||||
Self::do_mint(
|
||||
collection,
|
||||
item,
|
||||
Some(mint_to.clone()),
|
||||
mint_to.clone(),
|
||||
item_config,
|
||||
|_, _| Ok(()),
|
||||
)?;
|
||||
for (key, value) in attributes {
|
||||
Self::do_set_attribute(
|
||||
collection_details.owner.clone(),
|
||||
collection,
|
||||
Some(item),
|
||||
AttributeNamespace::CollectionOwner,
|
||||
Self::construct_attribute_key(key)?,
|
||||
Self::construct_attribute_value(value)?,
|
||||
mint_to.clone(),
|
||||
)?;
|
||||
}
|
||||
if !metadata.len().is_zero() {
|
||||
Self::do_set_item_metadata(
|
||||
Some(collection_details.owner.clone()),
|
||||
collection,
|
||||
item,
|
||||
metadata,
|
||||
Some(mint_to.clone()),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_burn(
|
||||
collection: T::CollectionId,
|
||||
item: T::ItemId,
|
||||
|
||||
@@ -60,14 +60,16 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
.saturating_add(T::MetadataDepositBase::get());
|
||||
}
|
||||
|
||||
// the previous deposit was taken from the item's owner
|
||||
if old_deposit.account.is_some() && maybe_depositor.is_none() {
|
||||
T::Currency::unreserve(&old_deposit.account.unwrap(), old_deposit.amount);
|
||||
T::Currency::reserve(&collection_details.owner, deposit)?;
|
||||
let depositor = maybe_depositor.clone().unwrap_or(collection_details.owner.clone());
|
||||
let old_depositor = old_deposit.account.unwrap_or(collection_details.owner.clone());
|
||||
|
||||
if depositor != old_depositor {
|
||||
T::Currency::unreserve(&old_depositor, old_deposit.amount);
|
||||
T::Currency::reserve(&depositor, deposit)?;
|
||||
} else if deposit > old_deposit.amount {
|
||||
T::Currency::reserve(&collection_details.owner, deposit - old_deposit.amount)?;
|
||||
T::Currency::reserve(&depositor, deposit - old_deposit.amount)?;
|
||||
} else if deposit < old_deposit.amount {
|
||||
T::Currency::unreserve(&collection_details.owner, old_deposit.amount - deposit);
|
||||
T::Currency::unreserve(&depositor, old_deposit.amount - deposit);
|
||||
}
|
||||
|
||||
if maybe_depositor.is_none() {
|
||||
@@ -191,4 +193,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// A helper method to construct metadata.
|
||||
pub fn construct_metadata(
|
||||
metadata: Vec<u8>,
|
||||
) -> Result<BoundedVec<u8, T::StringLimit>, DispatchError> {
|
||||
Ok(BoundedVec::try_from(metadata).map_err(|_| Error::<T, I>::IncorrectMetadata)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,13 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub(crate) fn get_default_item_settings(
|
||||
collection_id: &T::CollectionId,
|
||||
) -> Result<ItemSettings, DispatchError> {
|
||||
let collection_config = Self::get_collection_config(collection_id)?;
|
||||
Ok(collection_config.mint_settings.default_item_settings)
|
||||
}
|
||||
|
||||
pub(crate) fn is_pallet_feature_enabled(feature: PalletFeature) -> bool {
|
||||
let features = T::Features::get();
|
||||
return features.is_enabled(feature)
|
||||
|
||||
Reference in New Issue
Block a user