--- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,26 @@ ] [[package]] +name = "bondrewd" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1660fac8d3acced44dac64453fafedf5aab2de196b932c727e63e4ae42d1cc" +dependencies = [ + "bondrewd-derive", +] + +[[package]] +name = "bondrewd-derive" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723da0dee1eef38edc021b0793f892bdc024500c6a5b0727a2efe16f0e0a6977" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "bounded-vec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5892,6 +5912,7 @@ name = "pallet-foreign-assets" version = "0.1.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "hex", @@ -6600,7 +6621,7 @@ [[package]] name = "pallet-unique" -version = "0.1.4" +version = "0.2.0" dependencies = [ "ethereum", "evm-coder", @@ -12634,6 +12655,7 @@ name = "up-data-structs" version = "0.2.2" dependencies = [ + "bondrewd", "derivative", "frame-support", "frame-system", --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -70,6 +70,7 @@ COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, + CollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH, @@ -252,9 +253,9 @@ } /// Checks that the collection was created with, and must be operated upon through **Unique API**. - /// Now check only the `external_collection` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error. + /// Now check only the `external` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error. pub fn check_is_internal(&self) -> DispatchResult { - if self.external_collection { + if self.flags.external { return Err(>::CollectionIsExternal)?; } @@ -262,9 +263,9 @@ } /// Checks that the collection was created with, and must be operated upon through an **assimilated API**. - /// Now check only the `external_collection` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error. + /// Now check only the `external` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error. pub fn check_is_external(&self) -> DispatchResult { - if !self.external_collection { + if !self.flags.external { return Err(>::CollectionIsInternal)?; } @@ -345,7 +346,7 @@ use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion}; use frame_system::pallet_prelude::*; use frame_support::traits::Currency; - use up_data_structs::{TokenId, mapping::TokenAddressMapping}; + use up_data_structs::{TokenId, mapping::TokenAddressMapping, CollectionFlags}; use scale_info::TypeInfo; use weights::WeightInfo; @@ -792,7 +793,7 @@ sponsorship, limits, permissions, - external_collection, + flags, } = >::get(collection)?; let token_property_permissions = >::get(collection) @@ -822,7 +823,8 @@ permissions, token_property_permissions, properties, - read_only: external_collection, + read_only: flags.external, + foreign: flags.foreign, }) } } @@ -861,11 +863,11 @@ /// /// * `owner` - The owner of the collection. /// * `data` - Description of the created collection. - /// * `is_external` - Marks that collection managet by not "Unique network". + /// * `flags` - Extra flags to store. pub fn init_collection( owner: T::CrossAccountId, data: CreateCollectionData, - is_external: bool, + flags: CollectionFlags, ) -> Result { { ensure!( @@ -909,7 +911,7 @@ Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions) }) .unwrap_or_else(|| Ok(CollectionPermissions::default()))?, - external_collection: is_external, + flags, }; let mut collection_properties = up_data_structs::CollectionProperties::get(); --- a/pallets/fungible/src/lib.rs +++ b/pallets/fungible/src/lib.rs @@ -83,8 +83,8 @@ use frame_support::{ensure}; use pallet_evm::account::CrossAccountId; use up_data_structs::{ - AccessMode, CollectionId, TokenId, CreateCollectionData, mapping::TokenAddressMapping, - budget::Budget, + AccessMode, CollectionId, CollectionFlags, TokenId, CreateCollectionData, + mapping::TokenAddressMapping, budget::Budget, }; use pallet_common::{ Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, @@ -167,11 +167,6 @@ Value = u128, QueryKind = ValueQuery, >; - - /// Foreign collection flag - #[pallet::storage] - pub type ForeignCollection = - StorageMap; } /// Wrapper around untyped collection handle, asserting inner collection is of fungible type. @@ -217,7 +212,7 @@ owner: T::CrossAccountId, data: CreateCollectionData, ) -> Result { - >::init_collection(owner, data, false) + >::init_collection(owner, data, CollectionFlags::default()) } /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise. @@ -225,8 +220,14 @@ owner: T::CrossAccountId, data: CreateCollectionData, ) -> Result { - let id = >::init_collection(owner, data, false)?; - >::insert(id, true); + let id = >::init_collection( + owner, + data, + CollectionFlags { + foreign: true, + ..Default::default() + }, + )?; Ok(id) } @@ -245,7 +246,6 @@ PalletCommon::destroy_collection(collection.0, sender)?; - >::remove(id); >::remove(id); let _ = >::clear_prefix((id,), u32::MAX, None); let _ = >::clear_prefix((id,), u32::MAX, None); @@ -274,10 +274,7 @@ .ok_or(>::TokenValueTooLow)?; // Foreign collection check - ensure!( - !>::get(collection.id), - >::NoPermission - ); + ensure!(!collection.flags.foreign, >::NoPermission); if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(owner)?; @@ -502,10 +499,7 @@ nesting_budget: &dyn Budget, ) -> DispatchResult { // Foreign collection check - ensure!( - !>::get(collection.id), - >::NoPermission - ); + ensure!(!collection.flags.foreign, >::NoPermission); if !collection.is_owner_or_admin(sender) { ensure!( --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -100,10 +100,10 @@ weights::{PostDispatchInfo, Pays}, }; use up_data_structs::{ - AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData, - mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey, - PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild, - AuxPropertyValue, + AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData, + CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, + PropertyKey, PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, + TokenChild, AuxPropertyValue, }; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use pallet_common::{ @@ -408,7 +408,14 @@ data: CreateCollectionData, is_external: bool, ) -> Result { - >::init_collection(owner, data, is_external) + >::init_collection( + owner, + data, + CollectionFlags { + external: is_external, + ..Default::default() + }, + ) } /// Destroy NFT collection --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -112,10 +112,10 @@ use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap}; use up_data_structs::{ - AccessMode, budget::Budget, CollectionId, CollectionMode, CollectionPropertiesVec, - CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH, - MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission, - PropertyScope, PropertyValue, TokenId, TrySetProperty, + AccessMode, budget::Budget, CollectionId, CollectionMode, CollectionFlags, + CollectionPropertiesVec, CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, + MAX_ITEMS_PER_BATCH, MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, + PropertyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty, }; use frame_support::BoundedBTreeMap; use derivative::Derivative; @@ -375,7 +375,7 @@ owner: T::CrossAccountId, data: CreateCollectionData, ) -> Result { - >::init_collection(owner, data, false) + >::init_collection(owner, data, CollectionFlags::default()) } /// Destroy RFT collection --- a/primitives/data-structs/Cargo.toml +++ b/primitives/data-structs/Cargo.toml @@ -27,6 +27,7 @@ struct-versioning = { path = "../../crates/struct-versioning" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } rmrk-traits = { default-features = false, path = "../rmrk-traits" } +bondrewd = { version = "0.1.14", features = ["derive"], default-features = false } [features] default = ["std"] --- /dev/null +++ b/primitives/data-structs/src/bondrewd_codec.rs @@ -0,0 +1,31 @@ +//! Integration between bondrewd and parity scale codec +//! Maybe we can move it to scale-codec itself in future? + +#[macro_export] +macro_rules! bondrewd_codec { + ($T:ty) => { + impl Encode for $T { + fn encode_to(&self, dest: &mut O) { + dest.write(&self.into_bytes()) + } + } + impl codec::Decode for $T { + fn decode(from: &mut I) -> Result { + let mut bytes = [0; Self::BYTE_SIZE]; + from.read(&mut bytes)?; + Ok(Self::from_bytes(bytes)) + } + } + impl MaxEncodedLen for $T { + fn max_encoded_len() -> usize { + Self::BYTE_SIZE + } + } + impl TypeInfo for $T { + type Identity = [u8; Self::BYTE_SIZE]; + fn type_info() -> scale_info::Type { + <[u8; Self::BYTE_SIZE] as TypeInfo>::type_info() + } + } + }; +} --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -36,6 +36,7 @@ use sp_core::U256; use sp_runtime::{ArithmeticError, sp_std::prelude::Vec, Permill}; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use bondrewd::Bitfields; use frame_support::{BoundedVec, traits::ConstU32}; use derivative::Derivative; use scale_info::TypeInfo; @@ -54,6 +55,7 @@ FixedPart as RmrkFixedPart, SlotPart as RmrkSlotPart, }; +mod bondrewd_codec; mod bounded; pub mod budget; pub mod mapping; @@ -357,6 +359,21 @@ pub type CollectionDescription = BoundedVec>; pub type CollectionTokenPrefix = BoundedVec>; +#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)] +#[bondrewd(enforce_bytes = 1)] +pub struct CollectionFlags { + /// Tokens in foreign collections can be transferred, but not burnt + #[bondrewd(bits = "0..1")] + pub foreign: bool, + /// External collections can't be managed using `unique` api + #[bondrewd(bits = "7..8")] + pub external: bool, + + #[bondrewd(reserve, bits = "1..7")] + pub reserved: u8, +} +bondrewd_codec!(CollectionFlags); + /// Base structure for represent collection. /// /// Used to provide basic functionality for all types of collections. @@ -404,9 +421,8 @@ #[version(2.., upper(Default::default()))] pub permissions: CollectionPermissions, - /// Marks that this collection is not "unique", and managed from external. - #[version(2.., upper(false))] - pub external_collection: bool, + #[version(2.., upper(Default::default()))] + pub flags: CollectionFlags, #[version(..2)] pub variable_on_chain_schema: BoundedVec>, @@ -454,6 +470,9 @@ /// Is collection read only. pub read_only: bool, + + /// Is collection is foreign. + pub foreign: bool, } /// Data used for create collection. --- a/primitives/data-structs/src/migration.rs +++ b/primitives/data-structs/src/migration.rs @@ -38,3 +38,26 @@ test_to_option(SponsoringRateLimit::SponsoringDisabled); test_to_option(SponsoringRateLimit::Blocks(10)); } + +#[test] +fn collection_flags_have_same_encoding_as_bool() { + use crate::CollectionFlags; + use codec::Encode; + + assert_eq!( + true.encode(), + CollectionFlags { + external: true, + ..Default::default() + } + .encode() + ); + assert_eq!( + false.encode(), + CollectionFlags { + external: false, + ..Default::default() + } + .encode() + ); +}