// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Traits for dealing with a single non-fungible collection of items. //! //! This assumes a single level namespace identified by `Inspect::ItemId`, and could //! reasonably be implemented by pallets which wants to expose a single collection of NFT-like //! objects. //! //! For an NFT API which has dual-level namespacing, the traits in `nonfungibles` are better to //! use. use super::nonfungibles; use crate::{dispatch::DispatchResult, traits::Get}; use alloc::vec::Vec; use codec::{Decode, Encode}; use pezsp_runtime::TokenError; /// Trait for providing an interface to a read-only NFT-like set of items. pub trait Inspect { /// Type for identifying an item. type ItemId; /// Returns the owner of `item`, or `None` if the item doesn't exist or has no /// owner. fn owner(item: &Self::ItemId) -> Option; /// Returns the attribute value of `item` corresponding to `key`. /// /// By default this is `None`; no attributes are defined. fn attribute(_item: &Self::ItemId, _key: &[u8]) -> Option> { None } /// Returns the strongly-typed attribute value of `item` corresponding to `key`. /// /// By default this just attempts to use `attribute`. fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { key.using_encoded(|d| Self::attribute(item, d)) .and_then(|v| V::decode(&mut &v[..]).ok()) } /// Returns `true` if the `item` may be transferred. /// /// Default implementation is that all items are transferable. fn can_transfer(_item: &Self::ItemId) -> bool { true } } /// Interface for enumerating items in existence or owned by a given account over a collection /// of NFTs. pub trait InspectEnumerable: Inspect { /// The iterator type for [`Self::items`]. type ItemsIterator: Iterator; /// The iterator type for [`Self::owned`]. type OwnedIterator: Iterator; /// Returns an iterator of the items within a `collection` in existence. fn items() -> Self::ItemsIterator; /// Returns an iterator of the items of all collections owned by `who`. fn owned(who: &AccountId) -> Self::OwnedIterator; } /// Trait for providing an interface for NFT-like items which may be minted, burned and/or have /// attributes set on them. pub trait Mutate: Inspect { /// Mint some `item` to be owned by `who`. /// /// By default, this is not a supported operation. fn mint_into(_item: &Self::ItemId, _who: &AccountId) -> DispatchResult { Err(TokenError::Unsupported.into()) } /// Burn some `item`. /// /// By default, this is not a supported operation. fn burn(_item: &Self::ItemId, _maybe_check_owner: Option<&AccountId>) -> DispatchResult { Err(TokenError::Unsupported.into()) } /// Set attribute `value` of `item`'s `key`. /// /// By default, this is not a supported operation. fn set_attribute(_item: &Self::ItemId, _key: &[u8], _value: &[u8]) -> DispatchResult { Err(TokenError::Unsupported.into()) } /// Attempt to set the strongly-typed attribute `value` of `item`'s `key`. /// /// By default this just attempts to use `set_attribute`. fn set_typed_attribute( item: &Self::ItemId, key: &K, value: &V, ) -> DispatchResult { key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(item, k, v))) } } /// Trait for providing a non-fungible set of items which can only be transferred. pub trait Transfer: Inspect { /// Transfer `item` into `destination` account. fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult; } /// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying /// a single item. pub struct ItemOf< F: nonfungibles::Inspect, A: Get<>::CollectionId>, AccountId, >(core::marker::PhantomData<(F, A, AccountId)>); impl< F: nonfungibles::Inspect, A: Get<>::CollectionId>, AccountId, > Inspect for ItemOf { type ItemId = >::ItemId; fn owner(item: &Self::ItemId) -> Option { >::owner(&A::get(), item) } fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { >::attribute(&A::get(), item, key) } fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { >::typed_attribute(&A::get(), item, key) } fn can_transfer(item: &Self::ItemId) -> bool { >::can_transfer(&A::get(), item) } } impl< F: nonfungibles::InspectEnumerable, A: Get<>::CollectionId>, AccountId, > InspectEnumerable for ItemOf { type ItemsIterator = >::ItemsIterator; type OwnedIterator = >::OwnedInCollectionIterator; fn items() -> Self::ItemsIterator { >::items(&A::get()) } fn owned(who: &AccountId) -> Self::OwnedIterator { >::owned_in_collection(&A::get(), who) } } impl< F: nonfungibles::Mutate, A: Get<>::CollectionId>, AccountId, > Mutate for ItemOf { fn mint_into(item: &Self::ItemId, who: &AccountId) -> DispatchResult { >::mint_into(&A::get(), item, who) } fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult { >::burn(&A::get(), item, maybe_check_owner) } fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult { >::set_attribute(&A::get(), item, key, value) } fn set_typed_attribute( item: &Self::ItemId, key: &K, value: &V, ) -> DispatchResult { >::set_typed_attribute(&A::get(), item, key, value) } } impl< F: nonfungibles::Transfer, A: Get<>::CollectionId>, AccountId, > Transfer for ItemOf { fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult { >::transfer(&A::get(), item, destination) } }