Skip to content

Commit

Permalink
Nfts attribute read interface (paritytech#13349)
Browse files Browse the repository at this point in the history
* feat: add custom and system attributes to Inspect

* feat: add nfts runtime api

* fix: pass std feature to runtime api

* fix: api copyright

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

---------

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
  • Loading branch information
2 people authored and nathanwhit committed Jul 19, 2023
1 parent 0560254 commit 4c2a129
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 30 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ members = [
"frame/proxy",
"frame/message-queue",
"frame/nfts",
"frame/nfts/runtime-api",
"frame/nomination-pools",
"frame/nomination-pools/fuzzer",
"frame/nomination-pools/benchmarking",
Expand Down
2 changes: 2 additions & 0 deletions bin/node/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pallet-message-queue = { version = "7.0.0-dev", default-features = false, path =
pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../../frame/merkle-mountain-range" }
pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" }
pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts" }
pallet-nfts-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts/runtime-api" }
pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"}
pallet-nomination-pools-benchmarking = { version = "1.0.0", default-features = false, optional = true, path = "../../../frame/nomination-pools/benchmarking" }
pallet-nomination-pools-runtime-api = { version = "1.0.0-dev", default-features = false, path = "../../../frame/nomination-pools/runtime-api" }
Expand Down Expand Up @@ -203,6 +204,7 @@ std = [
"pallet-recovery/std",
"pallet-uniques/std",
"pallet-nfts/std",
"pallet-nfts-runtime-api/std",
"pallet-vesting/std",
"log/std",
"frame-try-runtime?/std",
Expand Down
52 changes: 48 additions & 4 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ use frame_support::{
pallet_prelude::Get,
parameter_types,
traits::{
fungible::ItemOf, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32,
Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter,
KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote,
WithdrawReasons,
fungible::ItemOf, tokens::nonfungibles_v2::Inspect, AsEnsureOriginWithArg, ConstBool,
ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything,
Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced,
U128CurrencyToVote, WithdrawReasons,
},
weights::{
constants::{
Expand Down Expand Up @@ -2187,6 +2187,50 @@ impl_runtime_apis! {
}
}

impl pallet_nfts_runtime_api::NftsApi<Block, AccountId, u32, u32> for Runtime {
fn owner(collection: u32, item: u32) -> Option<AccountId> {
<Nfts as Inspect<AccountId>>::owner(&collection, &item)
}

fn collection_owner(collection: u32) -> Option<AccountId> {
<Nfts as Inspect<AccountId>>::collection_owner(&collection)
}

fn attribute(
collection: u32,
item: u32,
key: Vec<u8>,
) -> Option<Vec<u8>> {
<Nfts as Inspect<AccountId>>::attribute(&collection, &item, &key)
}

fn custom_attribute(
account: AccountId,
collection: u32,
item: u32,
key: Vec<u8>,
) -> Option<Vec<u8>> {
<Nfts as Inspect<AccountId>>::custom_attribute(
&account,
&collection,
&item,
&key,
)
}

fn system_attribute(
collection: u32,
item: u32,
key: Vec<u8>,
) -> Option<Vec<u8>> {
<Nfts as Inspect<AccountId>>::system_attribute(&collection, &item, &key)
}

fn collection_attribute(collection: u32, key: Vec<u8>) -> Option<Vec<u8>> {
<Nfts as Inspect<AccountId>>::collection_attribute(&collection, &key)
}
}

impl pallet_mmr::primitives::MmrApi<
Block,
mmr::Hash,
Expand Down
28 changes: 28 additions & 0 deletions frame/nfts/runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "pallet-nfts-runtime-api"
version = "4.0.0-dev"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "Runtime API for the FRAME NFTs pallet."
readme = "README.md"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" }
pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../../nfts" }
sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" }

[features]
default = ["std"]
std = [
"codec/std",
"frame-support/std",
"pallet-nfts/std",
"sp-api/std",
]
3 changes: 3 additions & 0 deletions frame/nfts/runtime-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RPC runtime API for the FRAME NFTs pallet.

License: Apache-2.0
57 changes: 57 additions & 0 deletions frame/nfts/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This file is part of Substrate.

// Copyright (C) 2023 Parity Technologies (UK) Ltd.
// 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.

//! Runtime API definition for the FRAME NFTs pallet.

#![cfg_attr(not(feature = "std"), no_std)]

use codec::{Decode, Encode};
use frame_support::dispatch::Vec;

sp_api::decl_runtime_apis! {
pub trait NftsApi<AccountId, CollectionId, ItemId>
where
AccountId: Encode + Decode,
CollectionId: Encode,
ItemId: Encode,
{
fn owner(collection: CollectionId, item: ItemId) -> Option<AccountId>;

fn collection_owner(collection: CollectionId) -> Option<AccountId>;

fn attribute(
collection: CollectionId,
item: ItemId,
key: Vec<u8>,
) -> Option<Vec<u8>>;

fn custom_attribute(
account: AccountId,
collection: CollectionId,
item: ItemId,
key: Vec<u8>,
) -> Option<Vec<u8>>;

fn system_attribute(
collection: CollectionId,
item: ItemId,
key: Vec<u8>,
) -> Option<Vec<u8>>;

fn collection_attribute(collection: CollectionId, key: Vec<u8>) -> Option<Vec<u8>>;
}
}
32 changes: 31 additions & 1 deletion frame/nfts/src/impl_nonfungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,48 @@ impl<T: Config<I>, I: 'static> Inspect<<T as SystemConfig>::AccountId> for Palle
fn attribute(
collection: &Self::CollectionId,
item: &Self::ItemId,
namespace: &AttributeNamespace<<T as SystemConfig>::AccountId>,
key: &[u8],
) -> Option<Vec<u8>> {
if key.is_empty() {
// We make the empty key map to the item metadata value.
ItemMetadataOf::<T, I>::get(collection, item).map(|m| m.data.into())
} else {
let namespace = AttributeNamespace::CollectionOwner;
let key = BoundedSlice::<_, _>::try_from(key).ok()?;
Attribute::<T, I>::get((collection, Some(item), namespace, key)).map(|a| a.0.into())
}
}

/// Returns the custom attribute value of `item` of `collection` corresponding to `key`.
///
/// By default this is `None`; no attributes are defined.
fn custom_attribute(
account: &T::AccountId,
collection: &Self::CollectionId,
item: &Self::ItemId,
key: &[u8],
) -> Option<Vec<u8>> {
let namespace = Account::<T, I>::get((account, collection, item))
.map(|_| AttributeNamespace::ItemOwner)
.unwrap_or_else(|| AttributeNamespace::Account(account.clone()));

let key = BoundedSlice::<_, _>::try_from(key).ok()?;
Attribute::<T, I>::get((collection, Some(item), namespace, key)).map(|a| a.0.into())
}

/// Returns the system attribute value of `item` of `collection` corresponding to `key`.
///
/// By default this is `None`; no attributes are defined.
fn system_attribute(
collection: &Self::CollectionId,
item: &Self::ItemId,
key: &[u8],
) -> Option<Vec<u8>> {
let namespace = AttributeNamespace::Pallet;
let key = BoundedSlice::<_, _>::try_from(key).ok()?;
Attribute::<T, I>::get((collection, Some(item), namespace, key)).map(|a| a.0.into())
}

/// Returns the attribute value of `item` of `collection` corresponding to `key`.
///
/// When `key` is empty, we return the item metadata value.
Expand Down
76 changes: 58 additions & 18 deletions frame/support/src/traits/tokens/nonfungible_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
//! use.

use super::nonfungibles_v2 as nonfungibles;
use crate::{
dispatch::DispatchResult,
traits::{tokens::misc::AttributeNamespace, Get},
};
use crate::{dispatch::DispatchResult, traits::Get};
use codec::{Decode, Encode};
use sp_runtime::TokenError;
use sp_std::prelude::*;
Expand All @@ -45,23 +42,53 @@ pub trait Inspect<AccountId> {
/// Returns the attribute value of `item` corresponding to `key`.
///
/// By default this is `None`; no attributes are defined.
fn attribute(
fn attribute(_item: &Self::ItemId, _key: &[u8]) -> Option<Vec<u8>> {
None
}

/// Returns the custom attribute value of `item` corresponding to `key`.
///
/// By default this is `None`; no attributes are defined.
fn custom_attribute(
_account: &AccountId,
_item: &Self::ItemId,
_namespace: &AttributeNamespace<AccountId>,
_key: &[u8],
) -> Option<Vec<u8>> {
None
}

/// Returns the system attribute value of `item` corresponding to `key`.
///
/// By default this is `None`; no attributes are defined.
fn system_attribute(_item: &Self::ItemId, _key: &[u8]) -> Option<Vec<u8>> {
None
}

/// Returns the strongly-typed attribute value of `item` corresponding to `key`.
///
/// By default this just attempts to use `attribute`.
fn typed_attribute<K: Encode, V: Decode>(
fn typed_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
key.using_encoded(|d| Self::attribute(item, d))
.and_then(|v| V::decode(&mut &v[..]).ok())
}

/// Returns the strongly-typed custom attribute value of `item` corresponding to `key`.
///
/// By default this just attempts to use `custom_attribute`.
fn typed_custom_attribute<K: Encode, V: Decode>(
account: &AccountId,
item: &Self::ItemId,
namespace: &AttributeNamespace<AccountId>,
key: &K,
) -> Option<V> {
key.using_encoded(|d| Self::attribute(item, namespace, d))
key.using_encoded(|d| Self::custom_attribute(account, item, d))
.and_then(|v| V::decode(&mut &v[..]).ok())
}

/// Returns the strongly-typed system attribute value of `item` corresponding to `key`.
///
/// By default this just attempts to use `system_attribute`.
fn typed_system_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
key.using_encoded(|d| Self::system_attribute(item, d))
.and_then(|v| V::decode(&mut &v[..]).ok())
}

Expand Down Expand Up @@ -167,19 +194,32 @@ impl<
fn owner(item: &Self::ItemId) -> Option<AccountId> {
<F as nonfungibles::Inspect<AccountId>>::owner(&A::get(), item)
}
fn attribute(
item: &Self::ItemId,
namespace: &AttributeNamespace<AccountId>,
key: &[u8],
) -> Option<Vec<u8>> {
<F as nonfungibles::Inspect<AccountId>>::attribute(&A::get(), item, namespace, key)
fn attribute(item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
<F as nonfungibles::Inspect<AccountId>>::attribute(&A::get(), item, key)
}
fn custom_attribute(account: &AccountId, item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
<F as nonfungibles::Inspect<AccountId>>::custom_attribute(account, &A::get(), item, key)
}
fn typed_attribute<K: Encode, V: Decode>(
fn system_attribute(item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
<F as nonfungibles::Inspect<AccountId>>::system_attribute(&A::get(), item, key)
}
fn typed_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
<F as nonfungibles::Inspect<AccountId>>::typed_attribute(&A::get(), item, key)
}
fn typed_custom_attribute<K: Encode, V: Decode>(
account: &AccountId,
item: &Self::ItemId,
namespace: &AttributeNamespace<AccountId>,
key: &K,
) -> Option<V> {
<F as nonfungibles::Inspect<AccountId>>::typed_attribute(&A::get(), item, namespace, key)
<F as nonfungibles::Inspect<AccountId>>::typed_custom_attribute(
account,
&A::get(),
item,
key,
)
}
fn typed_system_attribute<K: Encode, V: Decode>(item: &Self::ItemId, key: &K) -> Option<V> {
<F as nonfungibles::Inspect<AccountId>>::typed_system_attribute(&A::get(), item, key)
}
fn can_transfer(item: &Self::ItemId) -> bool {
<F as nonfungibles::Inspect<AccountId>>::can_transfer(&A::get(), item)
Expand Down
Loading

0 comments on commit 4c2a129

Please sign in to comment.