diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index c090536b3da63..30d3842224223 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1081,10 +1081,10 @@ impl_runtime_apis! { fn system_attribute( collection: u32, - item: u32, + item: Option, key: Vec, ) -> Option> { - >::system_attribute(&collection, &item, &key) + >::system_attribute(&collection, item.as_ref(), &key) } fn collection_attribute(collection: u32, key: Vec) -> Option> { diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index f4c8a5940a3ca..a2d100e1f8b5f 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2630,10 +2630,10 @@ impl_runtime_apis! { fn system_attribute( collection: u32, - item: u32, + item: Option, key: Vec, ) -> Option> { - >::system_attribute(&collection, &item, &key) + >::system_attribute(&collection, item.as_ref(), &key) } fn collection_attribute(collection: u32, key: Vec) -> Option> { diff --git a/substrate/frame/nfts/runtime-api/src/lib.rs b/substrate/frame/nfts/runtime-api/src/lib.rs index cf2d444b42f8c..77535c64069cc 100644 --- a/substrate/frame/nfts/runtime-api/src/lib.rs +++ b/substrate/frame/nfts/runtime-api/src/lib.rs @@ -48,7 +48,7 @@ sp_api::decl_runtime_apis! { fn system_attribute( collection: CollectionId, - item: ItemId, + item: Option, key: Vec, ) -> Option>; diff --git a/substrate/frame/nfts/src/impl_nonfungibles.rs b/substrate/frame/nfts/src/impl_nonfungibles.rs index 4a6b70eb99732..ee7f42cfc689c 100644 --- a/substrate/frame/nfts/src/impl_nonfungibles.rs +++ b/substrate/frame/nfts/src/impl_nonfungibles.rs @@ -79,17 +79,19 @@ impl, I: 'static> Inspect<::AccountId> for Palle Attribute::::get((collection, Some(item), namespace, key)).map(|a| a.0.into()) } - /// Returns the system attribute value of `item` of `collection` corresponding to `key`. + /// Returns the system attribute value of `item` of `collection` corresponding to `key` if + /// `item` is `Some`. Otherwise, returns the system attribute value of `collection` + /// corresponding to `key`. /// /// By default this is `None`; no attributes are defined. fn system_attribute( collection: &Self::CollectionId, - item: &Self::ItemId, + item: Option<&Self::ItemId>, key: &[u8], ) -> Option> { let namespace = AttributeNamespace::Pallet; let key = BoundedSlice::<_, _>::try_from(key).ok()?; - Attribute::::get((collection, Some(item), namespace, key)).map(|a| a.0.into()) + Attribute::::get((collection, item, namespace, key)).map(|a| a.0.into()) } /// Returns the attribute value of `item` of `collection` corresponding to `key`. diff --git a/substrate/frame/nfts/src/tests.rs b/substrate/frame/nfts/src/tests.rs index a82fcca015121..aeebf51b7c78a 100644 --- a/substrate/frame/nfts/src/tests.rs +++ b/substrate/frame/nfts/src/tests.rs @@ -22,7 +22,7 @@ use enumflags2::BitFlags; use frame_support::{ assert_noop, assert_ok, traits::{ - tokens::nonfungibles_v2::{Create, Destroy, Mutate}, + tokens::nonfungibles_v2::{Create, Destroy, Inspect, Mutate}, Currency, Get, }, }; @@ -982,6 +982,86 @@ fn set_collection_owner_attributes_should_work() { }); } +#[test] +fn set_collection_system_attributes_should_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&account(1), 100); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + collection_config_with_all_settings_enabled() + )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 0, account(1), None)); + + let collection_id = 0; + let attribute_key = [0u8]; + let attribute_value = [0u8]; + + assert_ok!(, ItemConfig>>::set_collection_attribute( + &collection_id, + &attribute_key, + &attribute_value + )); + + assert_eq!(attributes(0), vec![(None, AttributeNamespace::Pallet, bvec![0], bvec![0])]); + + assert_eq!( + >>::system_attribute( + &collection_id, + None, + &attribute_key + ), + Some(attribute_value.to_vec()) + ); + + // test typed system attribute + let typed_attribute_key = [0u8; 32]; + #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] + struct TypedAttributeValue(u32); + let typed_attribute_value = TypedAttributeValue(42); + + assert_ok!( + , ItemConfig>>::set_typed_collection_attribute( + &collection_id, + &typed_attribute_key, + &typed_attribute_value + ) + ); + + assert_eq!( + >>::typed_system_attribute( + &collection_id, + None, + &typed_attribute_key + ), + Some(typed_attribute_value) + ); + + // check storage + assert_eq!( + attributes(collection_id), + [ + (None, AttributeNamespace::Pallet, bvec![0], bvec![0]), + ( + None, + AttributeNamespace::Pallet, + bvec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + ], + bvec![42, 0, 0, 0] + ) + ] + ); + + assert_ok!(Nfts::burn(RuntimeOrigin::root(), collection_id, 0)); + let w = Nfts::get_destroy_witness(&0).unwrap(); + assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), collection_id, w)); + assert_eq!(attributes(collection_id), vec![]); + }) +} + #[test] fn set_item_owner_attributes_should_work() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/support/src/traits/tokens/nonfungible_v2.rs b/substrate/frame/support/src/traits/tokens/nonfungible_v2.rs index 788a4d25e810e..05f76e2859d2e 100644 --- a/substrate/frame/support/src/traits/tokens/nonfungible_v2.rs +++ b/substrate/frame/support/src/traits/tokens/nonfungible_v2.rs @@ -226,7 +226,7 @@ impl< >::custom_attribute(account, &A::get(), item, key) } fn system_attribute(item: &Self::ItemId, key: &[u8]) -> Option> { - >::system_attribute(&A::get(), item, key) + >::system_attribute(&A::get(), Some(item), key) } fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { >::typed_attribute(&A::get(), item, key) @@ -244,7 +244,7 @@ impl< ) } fn typed_system_attribute(item: &Self::ItemId, key: &K) -> Option { - >::typed_system_attribute(&A::get(), item, key) + >::typed_system_attribute(&A::get(), Some(item), key) } fn can_transfer(item: &Self::ItemId) -> bool { >::can_transfer(&A::get(), item) diff --git a/substrate/frame/support/src/traits/tokens/nonfungibles_v2.rs b/substrate/frame/support/src/traits/tokens/nonfungibles_v2.rs index 868afbdf7eee2..c0209b6d5123d 100644 --- a/substrate/frame/support/src/traits/tokens/nonfungibles_v2.rs +++ b/substrate/frame/support/src/traits/tokens/nonfungibles_v2.rs @@ -75,12 +75,14 @@ pub trait Inspect { None } - /// Returns the system attribute value of `item` of `collection` corresponding to `key`. + /// Returns the system attribute value of `item` of `collection` corresponding to `key` if + /// `item` is `Some`. Otherwise, returns the system attribute value of `collection` + /// corresponding to `key`. /// /// By default this is `None`; no attributes are defined. fn system_attribute( _collection: &Self::CollectionId, - _item: &Self::ItemId, + _item: Option<&Self::ItemId>, _key: &[u8], ) -> Option> { None @@ -113,13 +115,14 @@ pub trait Inspect { .and_then(|v| V::decode(&mut &v[..]).ok()) } - /// Returns the strongly-typed system attribute value of `item` of `collection` corresponding to - /// `key`. + /// Returns the strongly-typed system attribute value of `item` corresponding to `key` if + /// `item` is `Some`. Otherwise, returns the strongly-typed system attribute value of + /// `collection` corresponding to `key`. /// /// By default this just attempts to use `system_attribute`. fn typed_system_attribute( collection: &Self::CollectionId, - item: &Self::ItemId, + item: Option<&Self::ItemId>, key: &K, ) -> Option { key.using_encoded(|d| Self::system_attribute(collection, item, d))