diff --git a/pallets/rmrk-core/src/functions.rs b/pallets/rmrk-core/src/functions.rs index 93e2032a..ec0ee9a7 100644 --- a/pallets/rmrk-core/src/functions.rs +++ b/pallets/rmrk-core/src/functions.rs @@ -1,5 +1,191 @@ use super::*; +impl Collection, T::AccountId> for Pallet { + fn issuer(collection_id: CollectionId) -> Option { + None + } + fn collection_create( + issuer: T::AccountId, + metadata: StringLimitOf, + max: u32, + symbol: StringLimitOf, + ) -> Result { + let collection = CollectionInfo { issuer: issuer.clone(), metadata, max, symbol }; + let collection_id = + >::try_mutate(|n| -> Result { + let id = *n; + ensure!(id != CollectionId::max_value(), Error::::NoAvailableCollectionId); + *n += 1; + Ok(id) + })?; + Collections::::insert(collection_id, collection); + Ok(collection_id) + } + + fn collection_burn(issuer: T::AccountId, collection_id: CollectionId) -> DispatchResult { + ensure!( + NFTs::::iter_prefix_values(collection_id).count() == 0, + Error::::CollectionNotEmpty + ); + Collections::::remove(collection_id); + Ok(()) + } + + fn collection_change_issuer( + collection_id: CollectionId, + new_issuer: T::AccountId, + ) -> Result<(T::AccountId, CollectionId), DispatchError> { + ensure!(Collections::::contains_key(collection_id), Error::::NoAvailableCollectionId); + + Collections::::try_mutate_exists(collection_id, |collection| -> DispatchResult { + if let Some(col) = collection { + col.issuer = new_issuer.clone(); + } + Ok(()) + })?; + + Ok((new_issuer, collection_id)) + } + + fn collection_lock(collection_id: CollectionId) -> Result { + Collections::::try_mutate_exists(collection_id, |collection| -> DispatchResult { + let collection = collection.as_mut().ok_or(Error::::CollectionUnknown)?; + let currently_minted = NFTs::::iter_prefix_values(collection_id).count(); + collection.max = currently_minted.try_into().unwrap(); + Ok(()) + })?; + Ok(collection_id) + } +} + +impl Nft> for Pallet { + type MaxRecursions = T::MaxRecursions; + + fn nft_mint( + sender: T::AccountId, + owner: T::AccountId, + collection_id: CollectionId, + recipient: Option, + royalty: Option, + metadata: StringLimitOf, + ) -> sp_std::result::Result<(CollectionId, NftId), DispatchError> { + let nft_id = Self::get_next_nft_id(collection_id)?; + + let collection = Self::collections(collection_id).ok_or(Error::::CollectionUnknown)?; + + let nfts_minted = NFTs::::iter_prefix_values(collection_id).count(); + let max: u32 = collection.max.try_into().unwrap(); + + ensure!( + nfts_minted < max.try_into().unwrap() || max == 0, + Error::::CollectionFullOrLocked + ); + + let recipient = recipient.unwrap_or(owner.clone()); + let royalty = royalty.unwrap_or(Permill::default()); + + let rootowner = owner.clone(); + let owner_as_maybe_account = AccountIdOrCollectionNftTuple::AccountId(owner.clone()); + + let nft = + NftInfo { owner: owner_as_maybe_account, rootowner, recipient, royalty, metadata }; + + NFTs::::insert(collection_id, nft_id, nft); + NftsByOwner::::append(owner, (collection_id, nft_id)); + + Ok((collection_id, nft_id)) + } + + fn nft_burn( + collection_id: CollectionId, + nft_id: NftId, + max_recursions: u32, + ) -> sp_std::result::Result<(CollectionId, NftId), DispatchError> { + ensure!(max_recursions > 0, Error::::TooManyRecursions); + NFTs::::remove(collection_id, nft_id); + if let Some(kids) = Children::::take(collection_id, nft_id) { + for (child_collection_id, child_nft_id) in kids { + Self::nft_burn( + child_collection_id, + child_nft_id, + max_recursions - 1, + )?; + } + } + Ok((collection_id, nft_id)) + } + + fn nft_send( + sender: T::AccountId, + collection_id: CollectionId, + nft_id: NftId, + new_owner: AccountIdOrCollectionNftTuple, + max_recursions: u32, + ) -> sp_std::result::Result<(CollectionId, NftId), DispatchError> { + let mut sending_nft = + NFTs::::get(collection_id, nft_id).ok_or(Error::::NoAvailableNftId)?; + ensure!(&sending_nft.rootowner == &sender, Error::::NoPermission); + + match new_owner.clone() { + AccountIdOrCollectionNftTuple::AccountId(account_id) => { + // Remove previous parental relationship + if let AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) = + sending_nft.owner + { + if let Some(mut kids) = Children::::take(cid, nid) { + kids.retain(|&kid| kid != (collection_id, nft_id)); + Children::::insert(cid, nid, kids); + } + } + sending_nft.rootowner = account_id.clone(); + }, + AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) => { + let recipient_nft = NFTs::::get(cid, nid).ok_or(Error::::NoAvailableNftId)?; + // Check if sending NFT is already a child of recipient NFT + ensure!( + !Pallet::::is_x_descendent_of_y(cid, nid, collection_id, nft_id), + Error::::CannotSendToDescendent + ); + + // Remove parent if exists: first we only care if the owner is a non-AccountId) + if let AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) = + sending_nft.owner + { + // second we only care if the parent has children (it should) + if let Some(mut kids) = Children::::take(cid, nid) { + // third we only "retain" the other children + kids.retain(|&kid| kid != (collection_id, nft_id)); + Children::::insert(cid, nid, kids); + } + } + if sending_nft.rootowner != recipient_nft.rootowner { + // sending_nft.rootowner = recipient_nft.rootowner + sending_nft.rootowner = recipient_nft.rootowner.clone(); + + Pallet::::recursive_update_rootowner( + collection_id, + nft_id, + recipient_nft.rootowner, + max_recursions, + )?; + } + match Children::::take(cid, nid) { + None => Children::::insert(cid, nid, vec![(collection_id, nft_id)]), + Some(mut kids) => { + kids.push((collection_id, nft_id)); + Children::::insert(cid, nid, kids); + }, + } + }, + }; + sending_nft.owner = new_owner.clone(); + + NFTs::::insert(collection_id, nft_id, sending_nft); + + Ok((collection_id, nft_id)) + } +} + impl Pallet { pub fn is_x_descendent_of_y( child_collection_id: CollectionId, @@ -61,10 +247,40 @@ impl Pallet { ensure!(max_recursions > 0, Error::::TooManyRecursions); NFTs::::remove(collection_id, nft_id); if let Some(kids) = Children::::take(collection_id, nft_id) { - for child in kids { - Pallet::::recursive_burn(child.0, child.1, max_recursions - 1)?; + for (child_collection_id, child_nft_id) in kids { + Pallet::::recursive_burn(child_collection_id, child_nft_id, max_recursions - 1)?; } } Ok(()) } + + pub fn to_bounded_string(name: Vec) -> Result, Error> { + name.try_into().map_err(|_| Error::::TooLong) + } + + pub fn to_optional_bounded_string( + name: Option>, + ) -> Result>, Error> { + Ok(match name { + Some(n) => Some(Self::to_bounded_string(n)?), + None => None, + }) + } + + pub fn get_next_nft_id(collection_id: CollectionId) -> Result> { + NextNftId::::try_mutate(collection_id, |id| { + let current_id = *id; + *id = id.checked_add(1).ok_or(Error::::NoAvailableNftId)?; + Ok(current_id) + }) + } + + pub fn get_next_resource_id() -> Result> { + NextResourceId::::try_mutate(|id| { + let current_id = *id; + *id = id.checked_add(1).ok_or(Error::::NoAvailableCollectionId)?; + Ok(current_id) + }) + } + } diff --git a/pallets/rmrk-core/src/lib.rs b/pallets/rmrk-core/src/lib.rs index 26716d6f..f2229a7a 100644 --- a/pallets/rmrk-core/src/lib.rs +++ b/pallets/rmrk-core/src/lib.rs @@ -20,9 +20,11 @@ use sp_runtime::{ }; use sp_std::{convert::TryInto, vec, vec::Vec}; -use types::{AccountIdOrCollectionNftTuple, ClassInfo, InstanceInfo, ResourceInfo}; +use types::{ClassInfo, ResourceInfo}; -use rmrk_traits::{primitives::*, Collection, CollectionInfo}; +use rmrk_traits::{ + primitives::*, AccountIdOrCollectionNftTuple, Collection, CollectionInfo, Nft, NftInfo, +}; use sp_std::result::Result; mod functions; @@ -33,14 +35,9 @@ mod mock; #[cfg(test)] mod tests; -// pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; - -pub type InstanceInfoOf = InstanceInfo< +pub type InstanceInfoOf = NftInfo< ::AccountId, BoundedVec::StringLimit>, - CollectionId, - NftId, >; pub type ResourceOf = ResourceInfo::StringLimit>>; @@ -69,7 +66,6 @@ pub mod pallet { type MaxRecursions: Get; } - /// Next available NFT ID. #[pallet::storage] #[pallet::getter(fn next_nft_id)] pub type NextNftId = StorageMap<_, Twox64Concat, CollectionId, NftId, ValueQuery>; @@ -166,12 +162,7 @@ pub mod pallet { NftMinted(T::AccountId, CollectionId, NftId), NFTBurned(T::AccountId, NftId), CollectionDestroyed(T::AccountId, CollectionId), - NFTSent( - T::AccountId, - AccountIdOrCollectionNftTuple, - CollectionId, - NftId, - ), + NFTSent(T::AccountId, AccountIdOrCollectionNftTuple, CollectionId, NftId), IssuerChanged(T::AccountId, T::AccountId, CollectionId), PropertySet( CollectionId, @@ -230,8 +221,8 @@ pub mod pallet { origin: OriginFor, owner: T::AccountId, collection_id: CollectionId, - recipient: T::AccountId, - royalty: Permill, + recipient: Option, + royalty: Option, metadata: BoundedVec, ) -> DispatchResult { let sender = match T::ProtocolOrigin::try_origin(origin) { @@ -239,45 +230,21 @@ pub mod pallet { Err(origin) => Some(ensure_signed(origin)?), }; - let collection = - Self::collections(collection_id).ok_or(Error::::CollectionUnknown)?; - - let nfts_minted = NFTs::::iter_prefix_values(collection_id).count(); - let max: u32 = collection.max.try_into().unwrap(); - - ensure!( - // Probably a better way to do "max == 0" - nfts_minted < max.try_into().unwrap() || max == max - max, - Error::::CollectionFullOrLocked - ); - - let nft_id: NftId = Self::get_next_nft_id(collection_id)?; - - // let metadata_bounded = Self::to_bounded_string(metadata)?; - // if let Some(r) = royalty { - // ensure!(r < 1000, Error::::NotInRange); - // } - - // pallet_uniques::Pallet::::do_mint( - // collection_id, - // nft_id, - // sender.clone().unwrap_or_default(), - // |_details| Ok(()), - // )?; - - let rootowner = owner.clone(); - let owner_as_maybe_account = AccountIdOrCollectionNftTuple::AccountId(owner.clone()); - - let nft = InstanceInfo { - owner: owner_as_maybe_account, - rootowner, + let (collection_id, nft_id) = Self::nft_mint( + sender.clone().unwrap_or_default(), + owner, + collection_id, recipient, royalty, metadata, - }; + )?; - NFTs::::insert(collection_id, nft_id, nft); - NftsByOwner::::append(owner, (collection_id, nft_id)); + pallet_uniques::Pallet::::do_mint( + collection_id, + nft_id, + sender.clone().unwrap_or_default(), + |_details| Ok(()), + )?; Self::deposit_event(Event::NftMinted( sender.unwrap_or_default(), @@ -302,30 +269,23 @@ pub mod pallet { Err(origin) => Some(ensure_signed(origin)?), }; - // let metadata_bounded = Self::to_bounded_string(metadata)?; - // let symbol_bounded = Self::to_bounded_string(symbol)?; - // let id_bounded = Self::to_bounded_string(id)?; - // let collection_id = Self::get_next_collection_id()?; - let max = max.unwrap_or_default(); let collection_id = - , T::AccountId>>::create_collection( + Self::collection_create(sender.clone().unwrap_or_default(), metadata, max, symbol)?; + + pallet_uniques::Pallet::::do_create_class( + collection_id, + sender.clone().unwrap_or_default(), + sender.clone().unwrap_or_default(), + T::ClassDeposit::get(), + false, + pallet_uniques::Event::Created( + collection_id, + sender.clone().unwrap_or_default(), sender.clone().unwrap_or_default(), - metadata, - max, - symbol, - )?; - - // Collections::::insert( - // collection_id, - // ClassInfo { - // issuer: sender.clone().unwrap_or_default(), - // metadata, - // max, - // symbol, - // }, - // ); + ), + )?; Self::deposit_event(Event::CollectionCreated( sender.clone().unwrap_or_default(), @@ -343,19 +303,12 @@ pub mod pallet { nft_id: NftId, ) -> DispatchResult { let sender = ensure_signed(origin.clone())?; + let max_recursions = T::MaxRecursions::get(); + let (_collection_id, nft_id) = Self::nft_burn(collection_id, nft_id, max_recursions)?; - // NFTs::::remove(collection_id, nft_id); - // if let Some(kids) = Children::::take(collection_id, nft_id) { - // for child in kids { - // Pallet::::burn_nft(origin.clone(), child.0, child.1)?; - // } - // } - - // pallet_uniques::Pallet::::do_burn(collection_id, nft_id, |_, _| { - // Ok(()) - // })?; - - Pallet::::recursive_burn(collection_id, nft_id, T::MaxRecursions::get())?; + pallet_uniques::Pallet::::do_burn(collection_id, nft_id, |_, _| { + Ok(()) + })?; Self::deposit_event(Event::NFTBurned(sender, nft_id)); Ok(()) @@ -373,9 +326,16 @@ pub mod pallet { Err(origin) => Some(ensure_signed(origin)?), }; - , T::AccountId>>::burn_collection( - sender.clone().unwrap_or_default(), + Self::collection_burn(sender.clone().unwrap_or_default(), collection_id)?; + + let witness = pallet_uniques::Pallet::::get_destroy_witness(&collection_id) + .ok_or(Error::::NoWitness)?; + ensure!(witness.instances == 0u32, Error::::CollectionNotEmpty); + + pallet_uniques::Pallet::::do_destroy_class( collection_id, + witness, + sender.clone(), )?; Self::deposit_event(Event::CollectionDestroyed( @@ -392,91 +352,24 @@ pub mod pallet { origin: OriginFor, collection_id: CollectionId, nft_id: NftId, - new_owner: AccountIdOrCollectionNftTuple, + new_owner: AccountIdOrCollectionNftTuple, ) -> DispatchResult { let sender = match T::ProtocolOrigin::try_origin(origin) { Ok(_) => None, Err(origin) => Some(ensure_signed(origin)?), - }; - - let mut sending_nft = - NFTs::::get(collection_id, nft_id).ok_or(Error::::NoAvailableNftId)?; - ensure!( - sending_nft.rootowner == sender.clone().unwrap_or_default(), - Error::::NoPermission - ); - - match new_owner.clone() { - AccountIdOrCollectionNftTuple::AccountId(account_id) => { - // Remove previous parental relationship - if let AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) = - sending_nft.owner - { - if let Some(mut kids) = Children::::take(cid, nid) { - kids.retain(|&kid| kid != (collection_id, nft_id)); - Children::::insert(cid, nid, kids); - } - } - sending_nft.rootowner = account_id.clone(); - - // Pallet::::recursive_update_rootowner( - // collection_id, - // nft_id, - // account_id.clone(), - // T::MaxRecursions::get(), - // )?; - }, - AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) => { - let recipient_nft = - NFTs::::get(cid, nid).ok_or(Error::::NoAvailableNftId)?; - // Check if sending NFT is already a child of recipient NFT - ensure!( - !Pallet::::is_x_descendent_of_y(cid, nid, collection_id, nft_id), - Error::::CannotSendToDescendent - ); - - // Remove parent if exists: first we only care if the owner is a non-AccountId) - if let AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) = - sending_nft.owner - { - // second we only care if the parent has children (it should) - if let Some(mut kids) = Children::::take(cid, nid) { - // third we only "retain" the other children - kids.retain(|&kid| kid != (collection_id, nft_id)); - Children::::insert(cid, nid, kids); - } - } - if sending_nft.rootowner != recipient_nft.rootowner { - // sending_nft.rootowner = recipient_nft.rootowner - sending_nft.rootowner = recipient_nft.rootowner.clone(); - - Pallet::::recursive_update_rootowner( - collection_id, - nft_id, - recipient_nft.rootowner, - T::MaxRecursions::get(), - )?; - } - match Children::::take(cid, nid) { - None => Children::::insert(cid, nid, vec![(collection_id, nft_id)]), - Some(mut kids) => { - kids.push((collection_id, nft_id)); - Children::::insert(cid, nid, kids); - }, - } - }, - }; - sending_nft.owner = new_owner.clone(); - - NFTs::::remove(collection_id, nft_id); - NFTs::::insert(collection_id, nft_id, sending_nft); + } + .unwrap_or_default(); - Self::deposit_event(Event::NFTSent( - sender.unwrap_or_default(), - new_owner, + let max_recursions = T::MaxRecursions::get(); + Self::nft_send( + sender.clone(), collection_id, nft_id, - )); + new_owner.clone(), + max_recursions, + )?; + + Self::deposit_event(Event::NFTSent(sender, new_owner, collection_id, nft_id)); Ok(()) } @@ -499,10 +392,8 @@ pub mod pallet { Error::::NoAvailableCollectionId ); - let (new_issuer, collection_id) = , - T::AccountId, - >>::change_issuer(collection_id, new_issuer)?; + let (new_issuer, collection_id) = + Self::collection_change_issuer(collection_id, new_issuer)?; Self::deposit_event(Event::IssuerChanged( sender.unwrap_or_default(), @@ -557,10 +448,7 @@ pub mod pallet { Err(origin) => Some(ensure_signed(origin)?), }; - let collection_id = - , T::AccountId>>::lock_collection( - collection_id, - )?; + let collection_id = Self::collection_lock(collection_id)?; Self::deposit_event(Event::CollectionLocked(sender.unwrap_or_default(), collection_id)); Ok(()) @@ -672,96 +560,4 @@ pub mod pallet { Ok(()) } } - - impl Pallet { - pub fn to_bounded_string( - name: Vec, - ) -> Result, Error> { - name.try_into().map_err(|_| Error::::TooLong) - } - pub fn to_optional_bounded_string( - name: Option>, - ) -> Result>, Error> { - match name { - Some(n) => { - let bounded_string = Self::to_bounded_string(n)?; - return Ok(Some(bounded_string)) - }, - None => return Ok(None), - } - } - - pub fn get_next_nft_id(collection_id: CollectionId) -> Result> { - NextNftId::::try_mutate(collection_id, |id| { - let current_id = *id; - *id = id.checked_add(1).ok_or(Error::::NoAvailableNftId)?; - Ok(current_id) - }) - } - pub fn get_next_resource_id() -> Result> { - NextResourceId::::try_mutate(|id| { - let current_id = *id; - *id = id.checked_add(1).ok_or(Error::::NoAvailableCollectionId)?; - Ok(current_id) - }) - } - } -} - -impl Collection, T::AccountId> for Pallet { - fn issuer(collection_id: CollectionId) -> Option { - None - } - fn create_collection( - issuer: T::AccountId, - metadata: StringLimitOf, - max: u32, - symbol: StringLimitOf, - ) -> Result { - let collection = CollectionInfo { issuer: issuer.clone(), metadata, max, symbol }; - let collection_id = - >::try_mutate(|n| -> Result { - let id = *n; - ensure!(id != CollectionId::max_value(), Error::::NoAvailableCollectionId); - *n += 1; - Ok(id) - })?; - Collections::::insert(collection_id, collection); - Ok(collection_id) - } - - fn burn_collection(issuer: T::AccountId, collection_id: CollectionId) -> DispatchResult { - ensure!( - NFTs::::iter_prefix_values(collection_id).count() == 0, - Error::::CollectionNotEmpty - ); - Collections::::remove(collection_id); - Ok(()) - } - - fn change_issuer( - collection_id: CollectionId, - new_issuer: T::AccountId, - ) -> Result<(T::AccountId, CollectionId), DispatchError> { - ensure!(Collections::::contains_key(collection_id), Error::::NoAvailableCollectionId); - - Collections::::try_mutate_exists(collection_id, |collection| -> DispatchResult { - if let Some(col) = collection { - col.issuer = new_issuer.clone(); - } - Ok(()) - })?; - - Ok((new_issuer, collection_id)) - } - - fn lock_collection(collection_id: CollectionId) -> Result { - Collections::::try_mutate_exists(collection_id, |collection| -> DispatchResult { - let collection = collection.as_mut().ok_or(Error::::CollectionUnknown)?; - let currently_minted = NFTs::::iter_prefix_values(collection_id).count(); - collection.max = currently_minted.try_into().unwrap(); - Ok(()) - })?; - Ok(collection_id) - } } diff --git a/pallets/rmrk-core/src/tests.rs b/pallets/rmrk-core/src/tests.rs index 51b81b62..d39ea934 100644 --- a/pallets/rmrk-core/src/tests.rs +++ b/pallets/rmrk-core/src/tests.rs @@ -70,24 +70,24 @@ fn mint_nft_works() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(20.525), + Some(ALICE), + Some(Permill::from_float(20.525)), bvec![0u8; 20] )); assert_ok!(RMRKCore::mint_nft( Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(20.525), + Some(ALICE), + Some(Permill::from_float(20.525)), bvec![0u8; 20] )); assert_ok!(RMRKCore::mint_nft( Origin::signed(BOB), BOB, COLLECTION_ID_0, - CHARLIE, - Permill::from_float(20.525), + Some(CHARLIE), + Some(Permill::from_float(20.525)), bvec![0u8; 20] )); assert_noop!( @@ -95,8 +95,8 @@ fn mint_nft_works() { Origin::signed(ALICE), ALICE, NOT_EXISTING_CLASS_ID, - CHARLIE, - Permill::from_float(20.525), + Some(CHARLIE), + Some(Permill::from_float(20.525)), bvec![0u8; 20] ), Error::::CollectionUnknown @@ -113,8 +113,8 @@ fn send_nft_to_minted_nft_works() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(1.525), + Some(ALICE), + Some(Permill::from_float(1.525)), bvec![0u8; 20] )); // Alice mints NFT (0, 1) [will be the child] @@ -122,8 +122,8 @@ fn send_nft_to_minted_nft_works() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(1.525), + Some(ALICE), + Some(Permill::from_float(1.525)), nft_metadata )); // Alice sends NFT (0, 0) [parent] to Bob @@ -203,8 +203,8 @@ fn send_two_nfts_to_same_nft_creates_two_children() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints NFT (0, 1) @@ -212,8 +212,8 @@ fn send_two_nfts_to_same_nft_creates_two_children() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints NFT (0, 2) @@ -221,8 +221,8 @@ fn send_two_nfts_to_same_nft_creates_two_children() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), nft_metadata )); @@ -255,8 +255,8 @@ fn send_nft_removes_existing_parent() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints NFT (0, 1) @@ -264,8 +264,8 @@ fn send_nft_removes_existing_parent() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints NFT (0, 2) @@ -273,8 +273,8 @@ fn send_nft_removes_existing_parent() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints NFT (0, 3) @@ -282,8 +282,8 @@ fn send_nft_removes_existing_parent() { Origin::signed(ALICE), ALICE, 0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), nft_metadata )); @@ -340,12 +340,12 @@ fn burn_nft_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); assert_ok!(RMRKCore::burn_nft(Origin::signed(ALICE), COLLECTION_ID_0, NFT_ID_0)); - assert_eq!(RMRKCore::nfts(COLLECTION_ID_0, NFT_ID_0), None); + assert!(RMRKCore::nfts(COLLECTION_ID_0, NFT_ID_0).is_none()); }); } @@ -358,8 +358,8 @@ fn burn_nft_with_great_grandchildren_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints (0, 1) @@ -367,8 +367,8 @@ fn burn_nft_with_great_grandchildren_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints (0, 2) @@ -376,8 +376,8 @@ fn burn_nft_with_great_grandchildren_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints (0, 3) @@ -385,8 +385,8 @@ fn burn_nft_with_great_grandchildren_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice sends NFT (0, 1) to NFT (0, 0) @@ -415,7 +415,7 @@ fn burn_nft_with_great_grandchildren_works() { // Burn great-grandfather assert_ok!(RMRKCore::burn_nft(Origin::signed(ALICE), COLLECTION_ID_0, NFT_ID_0)); // Child is dead - assert_eq!(RMRKCore::nfts(COLLECTION_ID_0, 3), None); + assert!(RMRKCore::nfts(COLLECTION_ID_0, 3).is_none()) }); } @@ -428,8 +428,8 @@ fn send_to_grandchild_fails() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints (0, 1) @@ -437,8 +437,8 @@ fn send_to_grandchild_fails() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice mints (0, 2) @@ -446,8 +446,8 @@ fn send_to_grandchild_fails() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Alice sends NFT (0, 1) to NFT (0, 0) @@ -486,8 +486,8 @@ fn destroy_collection_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(1.525), + Some(ALICE), + Some(Permill::from_float(1.525)), bvec![0u8; 20] )); assert_noop!( @@ -508,8 +508,8 @@ fn mint_beyond_collection_max_fails() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); } @@ -518,8 +518,8 @@ fn mint_beyond_collection_max_fails() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] ), Error::::CollectionFullOrLocked @@ -536,8 +536,8 @@ fn lock_collection_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); } @@ -547,8 +547,8 @@ fn lock_collection_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] ), Error::::CollectionFullOrLocked @@ -580,8 +580,8 @@ fn create_resource_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); // Add resource works @@ -609,8 +609,8 @@ fn create_empty_resource_fails() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(1.525), + Some(ALICE), + Some(Permill::from_float(1.525)), bvec![0u8; 20] )); assert_noop!( @@ -640,8 +640,8 @@ fn set_property_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(1.525), + Some(ALICE), + Some(Permill::from_float(1.525)), bvec![0u8; 20] )); assert_ok!(RMRKCore::set_property( @@ -662,8 +662,8 @@ fn set_priority_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); assert_ok!(RMRKCore::set_priority( @@ -687,8 +687,8 @@ fn add_resource_pending_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); assert_ok!(RMRKCore::add_resource( @@ -714,8 +714,8 @@ fn accept_resource_works() { Origin::signed(ALICE), ALICE, COLLECTION_ID_0, - ALICE, - Permill::from_float(0.0), + Some(ALICE), + Some(Permill::from_float(0.0)), bvec![0u8; 20] )); assert_ok!(RMRKCore::add_resource( diff --git a/pallets/rmrk-core/src/types.rs b/pallets/rmrk-core/src/types.rs index 110437b5..63e9daa9 100644 --- a/pallets/rmrk-core/src/types.rs +++ b/pallets/rmrk-core/src/types.rs @@ -1,17 +1,11 @@ use frame_support::pallet_prelude::*; use sp_runtime::Permill; +use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use scale_info::TypeInfo; - -#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub enum AccountIdOrCollectionNftTuple { - AccountId(AccountId), - CollectionAndNftTuple(CollectionId, NftId), -} +use rmrk_traits::{primitives::*, AccountIdOrCollectionNftTuple}; #[derive(Encode, Decode, Eq, Copy, PartialEq, Clone, RuntimeDebug, TypeInfo)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -24,12 +18,11 @@ pub struct ClassInfo { } #[derive(Encode, Decode, Eq, Copy, PartialEq, Clone, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct InstanceInfo { +pub struct InstanceInfo { /// The rootowner of the account, must be an account pub rootowner: AccountId, /// The owner of the NFT, can be either an Account or a tuple (CollectionId, NftId) - pub owner: AccountIdOrCollectionNftTuple, + pub owner: AccountIdOrCollectionNftTuple, /// The user account which receives the royalty pub recipient: AccountId, /// Royalty in per mille (1/1000) diff --git a/traits/src/collection.rs b/traits/src/collection.rs index 4b6109df..e7212e56 100644 --- a/traits/src/collection.rs +++ b/traits/src/collection.rs @@ -1,9 +1,10 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{DispatchError, DispatchResult, RuntimeDebug}; -use sp_std::{cmp::Eq, result}; +use sp_std::cmp::Eq; use crate::primitives::*; +use sp_std::result::Result; /// Collection info. #[cfg_attr(feature = "std", derive(PartialEq, Eq))] @@ -19,41 +20,17 @@ pub struct CollectionInfo { /// Abstraction over a Collection system. #[allow(clippy::upper_case_acronyms)] pub trait Collection { - // fn collection_two_info( - // id: Self::CollectionTwoId, - // ) -> Option>; fn issuer(collection_id: CollectionId) -> Option; - fn create_collection( + fn collection_create( issuer: AccountId, metadata: BoundedString, max: u32, symbol: BoundedString, - ) -> sp_std::result::Result; - fn burn_collection(issuer: AccountId, collection_id: CollectionId) -> DispatchResult; - fn change_issuer( + ) -> Result; + fn collection_burn(issuer: AccountId, collection_id: CollectionId) -> DispatchResult; + fn collection_change_issuer( collection_id: CollectionId, new_issuer: AccountId, - ) -> sp_std::result::Result<(AccountId, CollectionId), DispatchError>; - fn lock_collection( - collection_id: CollectionId, - ) -> sp_std::result::Result; -} - -// #[derive(Encode, Decode, Eq, Copy, PartialEq, Clone, RuntimeDebug, TypeInfo)] -// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -// pub struct ClassInfo { -// /// Arbitrary data about a class, e.g. IPFS hash -// pub issuer: AccountId, -// } - -/// Auction info. -#[cfg_attr(feature = "std", derive(PartialEq, Eq))] -#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct AuctionInfo { - /// Current bidder and bid price. - pub bid: Option<(AccountId, Balance)>, - /// Define which block this auction will be started. - pub start: BlockNumber, - /// Define which block this auction will be ended. - pub end: Option, + ) -> Result<(AccountId, CollectionId), DispatchError>; + fn collection_lock(collection_id: CollectionId) -> Result; } diff --git a/traits/src/lib.rs b/traits/src/lib.rs index a3b34460..7c71df10 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -1,99 +1,13 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod collection; +pub mod nft; pub use collection::{Collection, CollectionInfo}; +pub use nft::{AccountIdOrCollectionNftTuple, Nft, NftInfo}; pub mod primitives { pub type CollectionId = u32; pub type ResourceId = u32; pub type NftId = u32; } - -// use codec::{Decode, Encode}; -// use impl_trait_for_tuples::impl_for_tuples; -// use sp_runtime::{DispatchResult, RuntimeDebug}; -// use sp_std::{ -// cmp::{Eq, PartialEq}, -// prelude::Vec, -// }; - -// #[cfg(feature = "std")] -// use serde::{Deserialize, Serialize}; - -// pub use auction::{Auction, AuctionHandler, AuctionInfo, OnNewBidResult}; -// pub use currency::{ -// BalanceStatus, BasicCurrency, BasicCurrencyExtended, BasicLockableCurrency, -// BasicReservableCurrency, LockIdentifier, MultiCurrency, MultiCurrencyExtended, -// MultiLockableCurrency, MultiReservableCurrency, OnDust, -// }; -// pub use data_provider::{DataFeeder, DataProvider, DataProviderExtended}; -// pub use get_by_key::GetByKey; -// pub use multi_asset::ConcreteFungibleAsset; -// pub use nft::NFT; -// pub use price::{DefaultPriceProvider, PriceProvider}; -// pub use rewards::RewardHandler; -// use scale_info::TypeInfo; -// pub use xcm_transfer::XcmTransfer; - -// pub mod arithmetic; -// pub mod auction; -// pub mod currency; -// pub mod data_provider; -// pub mod get_by_key; -// pub mod location; -// pub mod multi_asset; -// pub mod nft; -// pub mod price; -// pub mod rewards; -// pub mod xcm_transfer; - -// /// New data handler -// #[impl_trait_for_tuples::impl_for_tuples(30)] -// pub trait OnNewData { -// /// New data is available -// fn on_new_data(who: &AccountId, key: &Key, value: &Value); -// } - -// /// Combine data provided by operators -// pub trait CombineData { -// /// Combine data provided by operators -// fn combine_data( -// key: &Key, -// values: Vec, -// prev_value: Option, -// ) -> Option; -// } - -// /// Indicate if should change a value -// #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -// pub enum Change { -// /// No change. -// NoChange, -// /// Changed to new value. -// NewValue(Value), -// } - -// #[derive(Encode, Decode, RuntimeDebug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] -// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -// pub struct TimestampedValue { -// pub value: Value, -// pub timestamp: Moment, -// } - -// #[impl_for_tuples(30)] -// pub trait Happened { -// fn happened(t: &T); -// } - -// pub trait Handler { -// fn handle(t: &T) -> DispatchResult; -// } - -// #[impl_for_tuples(30)] -// impl Handler for Tuple { -// fn handle(t: &T) -> DispatchResult { -// for_tuples!( #( Tuple::handle(t); )* ); -// Ok(()) -// } -// } diff --git a/traits/src/nft.rs b/traits/src/nft.rs new file mode 100644 index 00000000..982310ae --- /dev/null +++ b/traits/src/nft.rs @@ -0,0 +1,63 @@ +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::{DispatchError, RuntimeDebug}; +use sp_std::cmp::Eq; + +use frame_support::pallet_prelude::*; +use sp_runtime::Permill; + +use crate::primitives::*; +use sp_std::result::Result; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum AccountIdOrCollectionNftTuple { + AccountId(AccountId), + CollectionAndNftTuple(CollectionId, NftId), +} + +/// Nft info. +#[cfg_attr(feature = "std", derive(PartialEq, Eq))] +#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct NftInfo { + /// The rootowner of the account, must be an account + pub rootowner: AccountId, + /// The owner of the NFT, can be either an Account or a tuple (CollectionId, NftId) + pub owner: AccountIdOrCollectionNftTuple, + /// The user account which receives the royalty + pub recipient: AccountId, + /// Royalty in per mille (1/1000) + pub royalty: Permill, + /// Arbitrary data about an instance, e.g. IPFS hash + pub metadata: BoundedString, +} + +/// Abstraction over a Nft system. +#[allow(clippy::upper_case_acronyms)] +pub trait Nft { + type MaxRecursions: Get; + + fn nft_mint( + sender: AccountId, + owner: AccountId, + collection_id: CollectionId, + recipient: Option, + royalty: Option, + metadata: BoundedString, + ) -> Result<(CollectionId, NftId), DispatchError>; + fn nft_burn( + collection_id: CollectionId, + nft_id: NftId, + max_recursions: u32, + ) -> Result<(CollectionId, NftId), DispatchError>; + fn nft_send( + sender: AccountId, + collection_id: CollectionId, + nft_id: NftId, + new_owner: AccountIdOrCollectionNftTuple, + max_recursions: u32, + ) -> Result<(CollectionId, NftId), DispatchError>; +}