Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 222 additions & 1 deletion pallets/rmrk-core/src/functions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,194 @@
use super::*;

impl<T: Config> Collection<StringLimitOf<T>, T::AccountId> for Pallet<T> {
fn issuer(collection_id: CollectionId) -> Option<T::AccountId> {
None
}
fn create_collection(
issuer: T::AccountId,
metadata: StringLimitOf<T>,
max: u32,
symbol: StringLimitOf<T>,
) -> Result<CollectionId, DispatchError> {
let collection = CollectionInfo { issuer: issuer.clone(), metadata, max, symbol };
let collection_id =
<CollectionIndex<T>>::try_mutate(|n| -> Result<CollectionId, DispatchError> {
let id = *n;
ensure!(id != CollectionId::max_value(), Error::<T>::NoAvailableCollectionId);
*n += 1;
Ok(id)
})?;
Collections::<T>::insert(collection_id, collection);
Ok(collection_id)
}

fn burn_collection(issuer: T::AccountId, collection_id: CollectionId) -> DispatchResult {
ensure!(
NFTs::<T>::iter_prefix_values(collection_id).count() == 0,
Error::<T>::CollectionNotEmpty
);
Collections::<T>::remove(collection_id);
Ok(())
}

fn change_issuer(
collection_id: CollectionId,
new_issuer: T::AccountId,
) -> Result<(T::AccountId, CollectionId), DispatchError> {
ensure!(Collections::<T>::contains_key(collection_id), Error::<T>::NoAvailableCollectionId);

Collections::<T>::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<CollectionId, DispatchError> {
Collections::<T>::try_mutate_exists(collection_id, |collection| -> DispatchResult {
let collection = collection.as_mut().ok_or(Error::<T>::CollectionUnknown)?;
let currently_minted = NFTs::<T>::iter_prefix_values(collection_id).count();
collection.max = currently_minted.try_into().unwrap();
Ok(())
})?;
Ok(collection_id)
}
}

impl<T: Config> Nft<T::AccountId, StringLimitOf<T>> for Pallet<T> {
type CollectionId = CollectionId;
type NftId = NftId;
type MaxRecursions = T::MaxRecursions;

fn mint_nft(
sender: T::AccountId,
owner: T::AccountId,
collection_id: CollectionId,
recipient: Option<T::AccountId>,
royalty: Option<Permill>,
metadata: StringLimitOf<T>,
) -> sp_std::result::Result<(Self::CollectionId, Self::NftId), DispatchError> {
let nft_id = Self::get_next_nft_id(collection_id)?;

let collection = Self::collections(collection_id).ok_or(Error::<T>::CollectionUnknown)?;

let nfts_minted = NFTs::<T>::iter_prefix_values(collection_id).count();
let max: u32 = collection.max.try_into().unwrap();

ensure!(
nfts_minted < max.try_into().unwrap() || max == 0,
Error::<T>::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::<T>::insert(collection_id, nft_id, nft);
NftsByOwner::<T>::append(owner, (collection_id, nft_id));

Ok((collection_id, nft_id))
}

fn burn_nft(
collection_id: CollectionId,
nft_id: NftId,
max_recursions: u32,
) -> sp_std::result::Result<(CollectionId, NftId), DispatchError> {
ensure!(max_recursions > 0, Error::<T>::TooManyRecursions);
NFTs::<T>::remove(collection_id, nft_id);
if let Some(kids) = Children::<T>::take(collection_id, nft_id) {
for child in kids {
<Self as Nft<T::AccountId, StringLimitOf<T>>>::burn_nft(
child.0,
child.1,
max_recursions - 1,
)?;
}
}
Ok((collection_id, nft_id))
}

fn send(
sender: T::AccountId,
collection_id: CollectionId,
nft_id: NftId,
new_owner: AccountIdOrCollectionNftTuple<T::AccountId, CollectionId, NftId>,
max_recursions: u32,
) -> sp_std::result::Result<(CollectionId, NftId), DispatchError> {
let mut sending_nft =
NFTs::<T>::get(collection_id, nft_id).ok_or(Error::<T>::NoAvailableNftId)?;
ensure!(sending_nft.rootowner == sender.clone(), Error::<T>::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::<T>::take(cid, nid) {
kids.retain(|&kid| kid != (collection_id, nft_id));
Children::<T>::insert(cid, nid, kids);
}
}
sending_nft.rootowner = account_id.clone();
}
AccountIdOrCollectionNftTuple::CollectionAndNftTuple(cid, nid) => {
let recipient_nft = NFTs::<T>::get(cid, nid).ok_or(Error::<T>::NoAvailableNftId)?;
// Check if sending NFT is already a child of recipient NFT
ensure!(
!Pallet::<T>::is_x_descendent_of_y(cid, nid, collection_id, nft_id),
Error::<T>::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::<T>::take(cid, nid) {
// third we only "retain" the other children
kids.retain(|&kid| kid != (collection_id, nft_id));
Children::<T>::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::<T>::recursive_update_rootowner(
collection_id,
nft_id,
recipient_nft.rootowner,
max_recursions,
)?;
}
match Children::<T>::take(cid, nid) {
None => Children::<T>::insert(cid, nid, vec![(collection_id, nft_id)]),
Some(mut kids) => {
kids.push((collection_id, nft_id));
Children::<T>::insert(cid, nid, kids);
}
}
}
};
sending_nft.owner = new_owner.clone();

NFTs::<T>::remove(collection_id, nft_id);
NFTs::<T>::insert(collection_id, nft_id, sending_nft);

Ok((collection_id, nft_id))
}
}

impl<T: Config> Pallet<T> {
pub fn is_x_descendent_of_y(
child_collection_id: CollectionId,
Expand All @@ -11,7 +200,7 @@ impl<T: Config> Pallet<T> {
if let Some(children) = Children::<T>::get(parent_collection_id, parent_nft_id) {
for child in children {
if child == (child_collection_id, child_nft_id) {
return true
return true;
} else {
if Pallet::<T>::is_x_descendent_of_y(
child_collection_id,
Expand Down Expand Up @@ -67,4 +256,36 @@ impl<T: Config> Pallet<T> {
}
Ok(())
}

pub fn to_bounded_string(name: Vec<u8>) -> Result<BoundedVec<u8, T::StringLimit>, Error<T>> {
name.try_into().map_err(|_| Error::<T>::TooLong)
}

pub fn to_optional_bounded_string(
name: Option<Vec<u8>>,
) -> Result<Option<BoundedVec<u8, T::StringLimit>>, Error<T>> {
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<NftId, Error<T>> {
NextNftId::<T>::try_mutate(collection_id, |id| {
let current_id = *id;
*id = id.checked_add(1).ok_or(Error::<T>::NoAvailableNftId)?;
Ok(current_id)
})
}

pub fn get_next_resource_id() -> Result<ResourceId, Error<T>> {
NextResourceId::<T>::try_mutate(|id| {
let current_id = *id;
*id = id.checked_add(1).ok_or(Error::<T>::NoAvailableCollectionId)?;
Ok(current_id)
})
}
}
Loading