Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit b14fdf5

Browse files
Non-fungible token traits (#8993)
* Non-fungible token traits * Docs * Fixes * Implement non-fungible trait for Uniques * Update frame/uniques/src/impl_nonfungibles.rs Co-authored-by: Shawn Tabrizi <[email protected]> * Update frame/uniques/src/impl_nonfungibles.rs Co-authored-by: Shawn Tabrizi <[email protected]> Co-authored-by: Shawn Tabrizi <[email protected]>
1 parent 48aea1b commit b14fdf5

File tree

10 files changed

+663
-62
lines changed

10 files changed

+663
-62
lines changed

frame/support/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub use self::hash::{
7676
pub use self::storage::{
7777
StorageValue, StorageMap, StorageDoubleMap, StorageNMap, StoragePrefixedMap,
7878
IterableStorageMap, IterableStorageDoubleMap, IterableStorageNMap, migration,
79-
bounded_vec::BoundedVec, weak_bounded_vec::WeakBoundedVec,
79+
bounded_vec::{BoundedVec, BoundedSlice}, weak_bounded_vec::WeakBoundedVec,
8080
};
8181
pub use self::dispatch::{Parameter, Callable};
8282
pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable};

frame/support/src/storage/bounded_vec.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
2121
use sp_std::prelude::*;
2222
use sp_std::{convert::TryFrom, fmt, marker::PhantomData};
23-
use codec::{Encode, Decode};
23+
use codec::{Encode, Decode, EncodeLike};
2424
use core::{
2525
ops::{Deref, Index, IndexMut},
2626
slice::SliceIndex,
@@ -40,6 +40,33 @@ use crate::{
4040
#[derive(Encode)]
4141
pub struct BoundedVec<T, S>(Vec<T>, PhantomData<S>);
4242

43+
/// A bounded slice.
44+
///
45+
/// Similar to a `BoundedVec`, but not owned and cannot be decoded.
46+
#[derive(Encode)]
47+
pub struct BoundedSlice<'a, T, S>(&'a [T], PhantomData<S>);
48+
49+
// `BoundedSlice`s encode to something which will always decode into a `BoundedVec` or a `Vec`.
50+
impl<'a, T: Encode + Decode, S: Get<u32>> EncodeLike<BoundedVec<T, S>> for BoundedSlice<'a, T, S> {}
51+
impl<'a, T: Encode + Decode, S: Get<u32>> EncodeLike<Vec<T>> for BoundedSlice<'a, T, S> {}
52+
53+
impl<'a, T, S: Get<u32>> TryFrom<&'a [T]> for BoundedSlice<'a, T, S> {
54+
type Error = ();
55+
fn try_from(t: &'a [T]) -> Result<Self, Self::Error> {
56+
if t.len() < S::get() as usize {
57+
Ok(BoundedSlice(t, PhantomData))
58+
} else {
59+
Err(())
60+
}
61+
}
62+
}
63+
64+
impl<'a, T, S> From<BoundedSlice<'a, T, S>> for &'a [T] {
65+
fn from(t: BoundedSlice<'a, T, S>) -> Self {
66+
t.0
67+
}
68+
}
69+
4370
impl<T: Decode, S: Get<u32>> Decode for BoundedVec<T, S> {
4471
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
4572
let inner = Vec::<T>::decode(input)?;
@@ -54,6 +81,9 @@ impl<T: Decode, S: Get<u32>> Decode for BoundedVec<T, S> {
5481
}
5582
}
5683

84+
// `BoundedVec`s encode to something which will always decode as a `Vec`.
85+
impl<T: Encode + Decode, S: Get<u32>> EncodeLike<Vec<T>> for BoundedVec<T, S> {}
86+
5787
impl<T, S> BoundedVec<T, S> {
5888
/// Create `Self` from `t` without any checks.
5989
fn unchecked_from(t: Vec<T>) -> Self {

frame/support/src/traits/tokens.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub mod fungible;
2121
pub mod fungibles;
2222
pub mod currency;
2323
pub mod imbalance;
24+
pub mod nonfungible;
25+
pub mod nonfungibles;
2426
mod misc;
2527
pub use misc::{
2628
WithdrawConsequence, DepositConsequence, ExistenceRequirement, BalanceStatus, WithdrawReasons,
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// This file is part of Substrate.
2+
3+
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
//! Traits for dealing with a single non-fungible asset class.
19+
//!
20+
//! This assumes a single level namespace identified by `Inspect::InstanceId`, and could
21+
//! reasonably be implemented by pallets which wants to expose a single collection of NFT-like
22+
//! objects.
23+
//!
24+
//! For an NFT API which has dual-level namespacing, the traits in `nonfungibles` are better to
25+
//! use.
26+
27+
use codec::{Encode, Decode};
28+
use sp_std::prelude::*;
29+
use sp_runtime::TokenError;
30+
use crate::dispatch::DispatchResult;
31+
use crate::traits::Get;
32+
use super::nonfungibles;
33+
34+
/// Trait for providing an interface to a read-only NFT-like set of asset instances.
35+
pub trait Inspect<AccountId> {
36+
/// Type for identifying an asset instance.
37+
type InstanceId;
38+
39+
/// Returns the owner of asset `instance`, or `None` if the asset doesn't exist or has no
40+
/// owner.
41+
fn owner(instance: &Self::InstanceId) -> Option<AccountId>;
42+
43+
/// Returns the attribute value of `instance` corresponding to `key`.
44+
///
45+
/// By default this is `None`; no attributes are defined.
46+
fn attribute(_instance: &Self::InstanceId, _key: &[u8]) -> Option<Vec<u8>> { None }
47+
48+
/// Returns the strongly-typed attribute value of `instance` corresponding to `key`.
49+
///
50+
/// By default this just attempts to use `attribute`.
51+
fn typed_attribute<K: Encode, V: Decode>(instance: &Self::InstanceId, key: &K) -> Option<V> {
52+
key.using_encoded(|d| Self::attribute(instance, d))
53+
.and_then(|v| V::decode(&mut &v[..]).ok())
54+
}
55+
56+
/// Returns `true` if the asset `instance` may be transferred.
57+
///
58+
/// Default implementation is that all assets are transferable.
59+
fn can_transfer(_instance: &Self::InstanceId) -> bool { true }
60+
}
61+
62+
/// Interface for enumerating assets in existence or owned by a given account over a collection
63+
/// of NFTs.
64+
///
65+
/// WARNING: These may be a heavy operations. Do not use when execution time is limited.
66+
pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
67+
/// Returns the instances of an asset `class` in existence.
68+
fn instances() -> Vec<Self::InstanceId>;
69+
70+
/// Returns the asset instances of all classes owned by `who`.
71+
fn owned(who: &AccountId) -> Vec<Self::InstanceId>;
72+
}
73+
74+
/// Trait for providing an interface for NFT-like assets which may be minted, burned and/or have
75+
/// attributes set on them.
76+
pub trait Mutate<AccountId>: Inspect<AccountId> {
77+
/// Mint some asset `instance` to be owned by `who`.
78+
///
79+
/// By default, this is not a supported operation.
80+
fn mint_into(_instance: &Self::InstanceId, _who: &AccountId) -> DispatchResult {
81+
Err(TokenError::Unsupported.into())
82+
}
83+
84+
/// Burn some asset `instance`.
85+
///
86+
/// By default, this is not a supported operation.
87+
fn burn_from(_instance: &Self::InstanceId) -> DispatchResult {
88+
Err(TokenError::Unsupported.into())
89+
}
90+
91+
/// Set attribute `value` of asset `instance`'s `key`.
92+
///
93+
/// By default, this is not a supported operation.
94+
fn set_attribute(_instance: &Self::InstanceId, _key: &[u8], _value: &[u8]) -> DispatchResult {
95+
Err(TokenError::Unsupported.into())
96+
}
97+
98+
/// Attempt to set the strongly-typed attribute `value` of `instance`'s `key`.
99+
///
100+
/// By default this just attempts to use `set_attribute`.
101+
fn set_typed_attribute<K: Encode, V: Encode>(
102+
instance: &Self::InstanceId,
103+
key: &K,
104+
value: &V,
105+
) -> DispatchResult {
106+
key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(instance, k, v)))
107+
}
108+
}
109+
110+
/// Trait for providing a non-fungible set of assets which can only be transferred.
111+
pub trait Transfer<AccountId>: Inspect<AccountId> {
112+
/// Transfer asset `instance` into `destination` account.
113+
fn transfer(instance: &Self::InstanceId, destination: &AccountId) -> DispatchResult;
114+
}
115+
116+
/// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying
117+
/// a single item.
118+
pub struct ItemOf<
119+
F: nonfungibles::Inspect<AccountId>,
120+
A: Get<<F as nonfungibles::Inspect<AccountId>>::ClassId>,
121+
AccountId,
122+
>(
123+
sp_std::marker::PhantomData<(F, A, AccountId)>
124+
);
125+
126+
impl<
127+
F: nonfungibles::Inspect<AccountId>,
128+
A: Get<<F as nonfungibles::Inspect<AccountId>>::ClassId>,
129+
AccountId,
130+
> Inspect<AccountId> for ItemOf<F, A, AccountId> {
131+
type InstanceId = <F as nonfungibles::Inspect<AccountId>>::InstanceId;
132+
fn owner(instance: &Self::InstanceId) -> Option<AccountId> {
133+
<F as nonfungibles::Inspect<AccountId>>::owner(&A::get(), instance)
134+
}
135+
fn attribute(instance: &Self::InstanceId, key: &[u8]) -> Option<Vec<u8>> {
136+
<F as nonfungibles::Inspect<AccountId>>::attribute(&A::get(), instance, key)
137+
}
138+
fn typed_attribute<K: Encode, V: Decode>(instance: &Self::InstanceId, key: &K) -> Option<V> {
139+
<F as nonfungibles::Inspect<AccountId>>::typed_attribute(&A::get(), instance, key)
140+
}
141+
fn can_transfer(instance: &Self::InstanceId) -> bool {
142+
<F as nonfungibles::Inspect<AccountId>>::can_transfer(&A::get(), instance)
143+
}
144+
}
145+
146+
impl<
147+
F: nonfungibles::InspectEnumerable<AccountId>,
148+
A: Get<<F as nonfungibles::Inspect<AccountId>>::ClassId>,
149+
AccountId,
150+
> InspectEnumerable<AccountId> for ItemOf<F, A, AccountId> {
151+
fn instances() -> Vec<Self::InstanceId> {
152+
<F as nonfungibles::InspectEnumerable<AccountId>>::instances(&A::get())
153+
}
154+
fn owned(who: &AccountId) -> Vec<Self::InstanceId> {
155+
<F as nonfungibles::InspectEnumerable<AccountId>>::owned_in_class(&A::get(), who)
156+
}
157+
}
158+
159+
impl<
160+
F: nonfungibles::Mutate<AccountId>,
161+
A: Get<<F as nonfungibles::Inspect<AccountId>>::ClassId>,
162+
AccountId,
163+
> Mutate<AccountId> for ItemOf<F, A, AccountId> {
164+
fn mint_into(instance: &Self::InstanceId, who: &AccountId) -> DispatchResult {
165+
<F as nonfungibles::Mutate<AccountId>>::mint_into(&A::get(), instance, who)
166+
}
167+
fn burn_from(instance: &Self::InstanceId) -> DispatchResult {
168+
<F as nonfungibles::Mutate<AccountId>>::burn_from(&A::get(), instance)
169+
}
170+
fn set_attribute(instance: &Self::InstanceId, key: &[u8], value: &[u8]) -> DispatchResult {
171+
<F as nonfungibles::Mutate<AccountId>>::set_attribute(&A::get(), instance, key, value)
172+
}
173+
fn set_typed_attribute<K: Encode, V: Encode>(
174+
instance: &Self::InstanceId,
175+
key: &K,
176+
value: &V,
177+
) -> DispatchResult {
178+
<F as nonfungibles::Mutate<AccountId>>::set_typed_attribute(&A::get(), instance, key, value)
179+
}
180+
}
181+
182+
impl<
183+
F: nonfungibles::Transfer<AccountId>,
184+
A: Get<<F as nonfungibles::Inspect<AccountId>>::ClassId>,
185+
AccountId,
186+
> Transfer<AccountId> for ItemOf<F, A, AccountId> {
187+
fn transfer(instance: &Self::InstanceId, destination: &AccountId) -> DispatchResult {
188+
<F as nonfungibles::Transfer<AccountId>>::transfer(&A::get(), instance, destination)
189+
}
190+
}

0 commit comments

Comments
 (0)