Skip to content

Commit

Permalink
Add traits for managing Memberships and implement for non-fungible
Browse files Browse the repository at this point in the history
  • Loading branch information
olanod committed Dec 21, 2023
1 parent 1b05e74 commit 2a4eb07
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 63 deletions.
24 changes: 11 additions & 13 deletions substrate/frame/nfts/src/impl_nonfungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,9 @@ impl<T: Config<I>, I: 'static> Destroy<<T as SystemConfig>::AccountId> for Palle
}
}

impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig> for Pallet<T, I> {
impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId> for Pallet<T, I> {
type ItemConfig = ItemConfig;

fn mint_into(
collection: &Self::CollectionId,
item: &Self::ItemId,
Expand Down Expand Up @@ -257,7 +259,7 @@ impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig
Self::do_burn(*collection, *item, |d| {
if let Some(check_owner) = maybe_check_owner {
if &d.owner != check_owner {
return Err(Error::<T, I>::NoPermission.into())
return Err(Error::<T, I>::NoPermission.into());
}
}
Ok(())
Expand Down Expand Up @@ -288,7 +290,7 @@ impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig
) -> DispatchResult {
key.using_encoded(|k| {
value.using_encoded(|v| {
<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(collection, item, k, v)
<Self as Mutate<T::AccountId>>::set_attribute(collection, item, k, v)
})
})
}
Expand All @@ -315,9 +317,7 @@ impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig
) -> DispatchResult {
key.using_encoded(|k| {
value.using_encoded(|v| {
<Self as Mutate<T::AccountId, ItemConfig>>::set_collection_attribute(
collection, k, v,
)
<Self as Mutate<T::AccountId>>::set_collection_attribute(collection, k, v)
})
})
}
Expand Down Expand Up @@ -368,9 +368,7 @@ impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig
item: &Self::ItemId,
key: &K,
) -> DispatchResult {
key.using_encoded(|k| {
<Self as Mutate<T::AccountId, ItemConfig>>::clear_attribute(collection, item, k)
})
key.using_encoded(|k| <Self as Mutate<T::AccountId>>::clear_attribute(collection, item, k))
}

fn clear_collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> DispatchResult {
Expand All @@ -388,7 +386,7 @@ impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig
key: &K,
) -> DispatchResult {
key.using_encoded(|k| {
<Self as Mutate<T::AccountId, ItemConfig>>::clear_collection_attribute(collection, k)
<Self as Mutate<T::AccountId>>::clear_collection_attribute(collection, k)
})
}

Expand Down Expand Up @@ -422,10 +420,10 @@ impl<T: Config<I>, I: 'static> Transfer<T::AccountId> for Pallet<T, I> {
Self::has_system_attribute(&collection, &item, PalletAttributes::TransferDisabled)?;
// Can't lock the item twice
if transfer_disabled {
return Err(Error::<T, I>::ItemLocked.into())
return Err(Error::<T, I>::ItemLocked.into());
}

<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(
<Self as Mutate<T::AccountId>>::set_attribute(
collection,
item,
&PalletAttributes::<Self::CollectionId>::TransferDisabled.encode(),
Expand All @@ -434,7 +432,7 @@ impl<T: Config<I>, I: 'static> Transfer<T::AccountId> for Pallet<T, I> {
}

fn enable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult {
<Self as Mutate<T::AccountId, ItemConfig>>::clear_attribute(
<Self as Mutate<T::AccountId>>::clear_attribute(
collection,
item,
&PalletAttributes::<Self::CollectionId>::TransferDisabled.encode(),
Expand Down
34 changes: 15 additions & 19 deletions substrate/frame/nfts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ fn set_collection_system_attributes_should_work() {
let attribute_key = [0u8];
let attribute_value = [0u8];

assert_ok!(<Nfts as Mutate<AccountIdOf<Test>, ItemConfig>>::set_collection_attribute(
assert_ok!(<Nfts as Mutate<AccountIdOf<Test>>>::set_collection_attribute(
&collection_id,
&attribute_key,
&attribute_value
Expand All @@ -1021,13 +1021,11 @@ fn set_collection_system_attributes_should_work() {
struct TypedAttributeValue(u32);
let typed_attribute_value = TypedAttributeValue(42);

assert_ok!(
<Nfts as Mutate<AccountIdOf<Test>, ItemConfig>>::set_typed_collection_attribute(
&collection_id,
&typed_attribute_key,
&typed_attribute_value
)
);
assert_ok!(<Nfts as Mutate<AccountIdOf<Test>>>::set_typed_collection_attribute(
&collection_id,
&typed_attribute_key,
&typed_attribute_value
));

assert_eq!(
<Nfts as Inspect<AccountIdOf<Test>>>::typed_system_attribute(
Expand Down Expand Up @@ -1384,7 +1382,7 @@ fn validate_deposit_required_setting() {
bvec![2],
bvec![0],
));
assert_ok!(<Nfts as Mutate<<Test as SystemConfig>::AccountId, ItemConfig>>::set_attribute(
assert_ok!(<Nfts as Mutate<<Test as SystemConfig>::AccountId>>::set_attribute(
&0,
&0,
&[3],
Expand All @@ -1403,13 +1401,11 @@ fn validate_deposit_required_setting() {
assert_eq!(Balances::reserved_balance(account(2)), 3);
assert_eq!(Balances::reserved_balance(account(3)), 3);

assert_ok!(
<Nfts as Mutate<<Test as SystemConfig>::AccountId, ItemConfig>>::clear_attribute(
&0,
&0,
&[3],
)
);
assert_ok!(<Nfts as Mutate<<Test as SystemConfig>::AccountId>>::clear_attribute(
&0,
&0,
&[3],
));
assert_eq!(
attributes(0),
vec![
Expand Down Expand Up @@ -3078,9 +3074,9 @@ fn collection_locking_should_work() {

let stored_config = CollectionConfigOf::<Test>::get(collection_id).unwrap();
let full_lock_config = collection_config_from_disabled_settings(
CollectionSetting::TransferableItems |
CollectionSetting::UnlockedMetadata |
CollectionSetting::UnlockedAttributes,
CollectionSetting::TransferableItems
| CollectionSetting::UnlockedMetadata
| CollectionSetting::UnlockedAttributes,
);
assert_eq!(stored_config, full_lock_config);
});
Expand Down
8 changes: 4 additions & 4 deletions substrate/frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ pub use tokens::{
};

mod members;
#[allow(deprecated)]
pub use members::{AllowAll, DenyAll, Filter};
pub use members::{
AsContains, ChangeMembers, Contains, ContainsLengthBound, ContainsPair, Equals, Everything,
EverythingBut, FromContainsPair, InitializeMembers, InsideBoth, IsInVec, Nothing,
membership, AsContains, ChangeMembers, Contains, ContainsLengthBound, ContainsPair, Equals,
Everything, EverythingBut, FromContainsPair, InitializeMembers, InsideBoth, IsInVec, Nothing,
RankedMembers, SortedMembers, TheseExcept,
};
#[allow(deprecated)]
pub use members::{AllowAll, DenyAll, Filter};

mod validation;
pub use validation::{
Expand Down
175 changes: 175 additions & 0 deletions substrate/frame/support/src/traits/members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,178 @@ impl<T: Clone + Ord> ChangeMembers<T> for () {
fn set_members_sorted(_: &[T], _: &[T]) {}
fn set_prime(_: Option<T>) {}
}

pub mod membership {
use super::*;
use crate::traits::tokens::nonfungible_v2 as nonfungible;
use crate::traits::Get;
use crate::Parameter;
use core::num::NonZeroU8;
use sp_arithmetic::traits::Bounded;
use sp_runtime::traits::BlockNumberProvider;

/// Access data associated to a unique membership
pub trait Inspect<AccountId> {
type MembershipId: Parameter;
type MembershipInfo: Membership<Id = Self::MembershipId>;
type MembershipsIter: Iterator<Item = Self::MembershipId>;

/// Retrieve membership data that is expected to belong to member
fn get_membership(
id: impl Into<Self::MembershipId>,
member: &AccountId,
) -> Option<Self::MembershipInfo>;

/// Retrieve all memberships belonging to member
fn account_memberships(member: &AccountId) -> Self::MembershipsIter;

/// Check membership is owned by the given account
fn has_membership(id: impl Into<Self::MembershipId>, member: &AccountId) -> bool {
Self::get_membership(id, member).is_some()
}
}

/// Change data related to a unique membership
pub trait Mutate<AccountId>: Inspect<AccountId> {
/// Update the membership possibly changing its owner
fn update(
id: impl Into<Self::MembershipId>,
membership: Self::MembershipInfo,
maybe_member: Option<AccountId>,
) -> DispatchResult;
}

/// A unique membership
pub trait Membership: codec::Decode + codec::Encode {
type Id: Parameter;

fn new(id: Self::Id) -> Self;

fn id(&self) -> Self::Id;
}

/// A membership with a rating system
pub trait WithRank<Rank = GenericRank>: Membership
where
Rank: Eq + Ord,
{
fn rank(&self) -> Rank;
fn set_rank(&mut self, rank: impl Into<Rank>);
}

/// A generic rank in the range 0 to 100
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Ord,
PartialEq,
PartialOrd,
codec::Decode,
codec::Encode,
codec::MaxEncodedLen,
scale_info::TypeInfo,
)]
pub struct GenericRank(u8);
impl GenericRank {
pub const MIN: Self = GenericRank(0);
pub const MAX: Self = GenericRank(100);
pub const ADMIN: Self = Self::MAX;

pub fn set(&mut self, n: u8) {
*self = Self(n.min(Self::MAX.0))
}
pub fn promote_by(&mut self, n: NonZeroU8) {
*self = Self(self.0.saturating_add(n.get()).min(Self::MAX.0))
}
pub fn demote_by(&mut self, n: NonZeroU8) {
*self = Self(self.0.saturating_sub(n.get()).max(Self::MIN.0))
}
}
impl From<u8> for GenericRank {
fn from(value: u8) -> Self {
Self(value)
}
}

/// A membership that can expire
pub trait WithLifetime: Membership {
type BlockProvider: BlockNumberProvider;

fn extend_validity(&mut self, _until: BlockNumberFor<Self>) {}

fn valid_until(&self) -> BlockNumberFor<Self> {
Bounded::max_value()
}

fn is_valid(&self) -> bool {
let now = Self::BlockProvider::current_block_number();
self.valid_until() < now
}
}
type BlockNumberFor<T> =
<<T as WithLifetime>::BlockProvider as BlockNumberProvider>::BlockNumber;

/// Adapter to auto implement Membership traits for anything that implements nonfungible.
pub struct NonFungibleAdpter<T, M, Attr>(PhantomData<(T, M, Attr)>);

impl<T, M, AccountId, Attr> Inspect<AccountId> for NonFungibleAdpter<T, M, Attr>
where
T: nonfungible::Inspect<AccountId>
+ nonfungible::InspectEnumerable<
AccountId,
OwnedIterator = crate::storage::KeyPrefixIterator<
<T as nonfungible::Inspect<AccountId>>::ItemId,
>,
>,
M: Membership<Id = T::ItemId>,
AccountId: Eq,
Attr: Get<&'static [u8; 10]>,
{
type MembershipId = T::ItemId;
type MembershipInfo = M;
type MembershipsIter = crate::storage::KeyPrefixIterator<T::ItemId>;

fn get_membership(
id: impl Into<Self::MembershipId>,
member: &AccountId,
) -> Option<Self::MembershipInfo> {
let id = id.into();
T::owner(&id).and_then(|o| member.eq(&o).then_some(()))?;
T::typed_system_attribute(&id, Attr::get())
}

fn account_memberships(member: &AccountId) -> Self::MembershipsIter {
T::owned(member)
}
}

impl<T, M, AccountId, Attr> Mutate<AccountId> for NonFungibleAdpter<T, M, Attr>
where
T: nonfungible::Inspect<AccountId>
+ nonfungible::InspectEnumerable<
AccountId,
OwnedIterator = crate::storage::KeyPrefixIterator<
<T as nonfungible::Inspect<AccountId>>::ItemId,
>,
> + nonfungible::Mutate<AccountId>
+ nonfungible::Transfer<AccountId>,
M: Membership<Id = T::ItemId>,
AccountId: Eq,
Attr: Get<&'static [u8; 10]>,
{
fn update(
id: impl Into<Self::MembershipId>,
membership: Self::MembershipInfo,
maybe_member: Option<AccountId>,
) -> DispatchResult {
let id = id.into();
if let Some(new_owner) = maybe_member {
T::transfer(&id, &new_owner)?;
}
T::set_typed_attribute(&id, Attr::get(), &membership)
}
}
}
Loading

0 comments on commit 2a4eb07

Please sign in to comment.