--- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -73,13 +73,6 @@ token: TokenId, at: Option, ) -> Result>; - #[rpc(name = "unique_constMetadata")] - fn const_metadata( - &self, - collection: CollectionId, - token: TokenId, - at: Option, - ) -> Result>; #[rpc(name = "unique_collectionProperties")] fn collection_properties( @@ -418,9 +411,6 @@ ); pass_method!( topmost_token_owner(collection: CollectionId, token: TokenId) -> Option, unique_api - ); - pass_method!( - const_metadata(collection: CollectionId, token: TokenId) -> Vec, unique_api ); pass_method!(total_supply(collection: CollectionId) -> u32, unique_api); pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api); --- a/pallets/common/src/benchmarking.rs +++ b/pallets/common/src/benchmarking.rs @@ -86,8 +86,6 @@ let name = create_u16_data::(); let description = create_u16_data::(); let token_prefix = create_data::(); - let offchain_schema = create_data::(); - let const_on_chain_schema = create_data::(); handler( owner, CreateCollectionData { @@ -95,8 +93,6 @@ name, description, token_prefix, - offchain_schema, - const_on_chain_schema, ..Default::default() }, ) --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -55,7 +55,6 @@ SponsoringRateLimit, budget::Budget, COLLECTION_FIELD_LIMIT, - CollectionField, PhantomType, Property, Properties, @@ -77,6 +76,8 @@ RmrkPartType, RmrkTheme, RmrkNftChild, + CollectionPermissions, + SchemaVersion, }; pub use pallet::*; @@ -433,17 +434,6 @@ Hasher = Blake2_128Concat, Key = CollectionId, Value = PropertiesPermissionMap, - QueryKind = ValueQuery, - >; - - /// Large variable-size collection fields are extracted here - #[pallet::storage] - pub type CollectionData = StorageNMap< - Key = ( - Key, - Key, - ), - Value = BoundedVec>, QueryKind = ValueQuery, >; @@ -505,19 +495,37 @@ if StorageVersion::get::>() < StorageVersion::new(1) { use up_data_structs::{CollectionVersion1, CollectionVersion2}; >::translate::, _>(|id, v| { - Self::set_field_raw( - id, - CollectionField::OffchainSchema, - v.offchain_schema.clone().into_inner(), - ) - .expect("data has lower bounds than field"); - Self::set_field_raw( + let mut props = Vec::new(); + if !v.offchain_schema.is_empty() { + props.push(Property { + key: b"_old_offchainSchema".to_vec().try_into().unwrap(), + value: v.offchain_schema.clone().into_inner().try_into().expect("offchain schema too big"), + }); + } + if !v.variable_on_chain_schema.is_empty() { + props.push(Property { + key: b"_old_variableOnChainSchema".to_vec().try_into().unwrap(), + value: v.variable_on_chain_schema.clone().into_inner().try_into().expect("offchain schema too big"), + }); + } + if !v.const_on_chain_schema.is_empty() { + props.push(Property { + key: b"_old_constOnChainSchema".to_vec().try_into().unwrap(), + value: v.const_on_chain_schema.clone().into_inner().try_into().expect("offchain schema too big"), + }); + } + props.push(Property { + key: b"_old_schemaVersion".to_vec().try_into().unwrap(), + value: match v.schema_version { + SchemaVersion::ImageURL => b"ImageUrl".as_slice(), + SchemaVersion::Unique => b"Unique".as_slice(), + }.to_vec().try_into().unwrap(), + }); + Self::set_scoped_collection_properties( id, - CollectionField::ConstOnChainSchema, - v.const_on_chain_schema.clone().into_inner(), - ) - .expect("data has lower bounds than field"); - + PropertyScope::None, + props.into_iter(), + ).expect("existing data larger than properties"); Some(CollectionVersion2::from(v)) }); } @@ -587,7 +595,6 @@ owner_can_transfer: Some(limits.owner_can_transfer()), owner_can_destroy: Some(limits.owner_can_destroy()), transfers_enabled: Some(limits.transfers_enabled()), - nesting_rule: Some(limits.nesting_rule().clone()), }; Some(effective_limits) @@ -599,12 +606,10 @@ description, owner, mode, - access, token_prefix, - mint_mode, - schema_version, sponsorship, limits, + permissions, } = >::get(collection)?; let token_property_permissions = >::get(collection) @@ -628,28 +633,45 @@ description: description.into_inner(), owner, mode, - access, token_prefix: token_prefix.into_inner(), - mint_mode, - schema_version, sponsorship, limits, - offchain_schema: >::get(( - collection, - CollectionField::OffchainSchema, - )) - .into_inner(), - const_on_chain_schema: >::get(( - collection, - CollectionField::ConstOnChainSchema, - )) - .into_inner(), + permissions, token_property_permissions, properties, }) } } +macro_rules! limit_default { + ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{ + $( + if let Some($new) = $new.$field { + let $old = $old.$field($($arg)?); + let _ = $new; + let _ = $old; + $check + } else { + $new.$field = $old.$field + } + )* + }}; +} +macro_rules! limit_default_clone { + ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{ + $( + if let Some($new) = $new.$field.clone() { + let $old = $old.$field($($arg)?); + let _ = $new; + let _ = $old; + $check + } else { + $new.$field = $old.$field.clone() + } + )* + }}; +} + impl Pallet { pub fn init_collection( owner: T::AccountId, @@ -681,11 +703,8 @@ owner: owner.clone(), name: data.name, mode: data.mode.clone(), - mint_mode: false, - access: data.access.unwrap_or_default(), description: data.description, token_prefix: data.token_prefix, - schema_version: data.schema_version.unwrap_or_default(), sponsorship: data .pending_sponsor .map(SponsorshipState::Unconfirmed) @@ -694,6 +713,10 @@ .limits .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits)) .unwrap_or_else(|| Ok(CollectionLimits::default()))?, + permissions: data + .permissions + .map(|permissions| Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)) + .unwrap_or_else(|| Ok(CollectionPermissions::default()))?, }; let mut collection_properties = up_data_structs::CollectionProperties::get(); @@ -732,18 +755,6 @@ >::put(created_count); >::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone())); >::insert(id, collection); - Self::set_field_raw( - id, - CollectionField::OffchainSchema, - data.offchain_schema.into_inner(), - ) - .expect("data has lower bounds than field"); - Self::set_field_raw( - id, - CollectionField::ConstOnChainSchema, - data.const_on_chain_schema.into_inner(), - ) - .expect("data has lower bounds than field"); Ok(id) } @@ -766,7 +777,6 @@ >::put(destroyed_collections); >::remove(collection.id); - >::remove_prefix((collection.id,), None); >::remove(collection.id); >::remove_prefix((collection.id,), None); >::remove_prefix((collection.id,), None); @@ -866,6 +876,18 @@ Ok(()) } + // For migrations + pub fn set_property_permission_unchecked( + collection: CollectionId, + property_permission: PropertyKeyPermission, + ) -> DispatchResult { + >::try_mutate(collection, |permissions| { + permissions.try_set(property_permission.key, property_permission.permission) + }) + .map_err(>::from)?; + Ok(()) + } + pub fn set_property_permission( collection: &CollectionHandle, sender: &T::CrossAccountId, @@ -989,35 +1011,6 @@ Ok(key_permissions) } - fn set_field_raw( - collection_id: CollectionId, - field: CollectionField, - value: Vec, - ) -> DispatchResult { - if !value.is_empty() { - >::insert( - (collection_id, field), - BoundedVec::try_from(value).map_err(|_| >::CollectionFieldSizeExceeded)?, - ) - } else { - >::remove((collection_id, field)); - } - Ok(()) - } - - pub fn set_field( - collection: &CollectionHandle, - sender: &T::CrossAccountId, - field: CollectionField, - value: Vec, - ) -> DispatchResult { - collection.check_is_owner_or_admin(sender)?; - - // ========= - - Self::set_field_raw(collection.id, field, value) - } - pub fn toggle_allowlist( collection: &CollectionHandle, sender: &T::CrossAccountId, @@ -1077,21 +1070,6 @@ old_limit: &CollectionLimits, mut new_limit: CollectionLimits, ) -> Result { - macro_rules! limit_default { - ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{ - $( - if let Some($new) = $new.$field { - let $old = $old.$field($($arg)?); - let _ = $new; - let _ = $old; - $check - } else { - $new.$field = $old.$field - } - )* - }}; - } - limit_default!(old_limit, new_limit, account_token_ownership_limit => ensure!( new_limit <= MAX_TOKEN_OWNERSHIP, @@ -1126,6 +1104,15 @@ ); Ok(new_limit) } + pub fn clamp_permissions( + mode: CollectionMode, + old_limit: &CollectionPermissions, + mut new_limit: CollectionPermissions, + ) -> Result { + limit_default_clone!(old_limit, new_limit, + ); + Ok(new_limit) + } } #[macro_export] @@ -1253,7 +1240,6 @@ fn last_token_id(&self) -> TokenId; fn token_owner(&self, token: TokenId) -> Option; - fn const_metadata(&self, token: TokenId) -> Vec; fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option; fn token_properties(&self, token_id: TokenId, keys: Option>) -> Vec; /// Amount of unique collection tokens --- a/pallets/fungible/src/common.rs +++ b/pallets/fungible/src/common.rs @@ -51,27 +51,27 @@ >::burn_item() } - fn set_collection_properties(amount: u32) -> Weight { + fn set_collection_properties(_amount: u32) -> Weight { // Error 0 } - fn delete_collection_properties(amount: u32) -> Weight { + fn delete_collection_properties(_amount: u32) -> Weight { // Error 0 } - fn set_token_properties(amount: u32) -> Weight { + fn set_token_properties(_amount: u32) -> Weight { // Error 0 } - fn delete_token_properties(amount: u32) -> Weight { + fn delete_token_properties(_amount: u32) -> Weight { // Error 0 } - fn set_property_permissions(amount: u32) -> Weight { + fn set_property_permissions(_amount: u32) -> Weight { // Error 0 } @@ -320,9 +320,6 @@ fn token_owner(&self, _token: TokenId) -> Option { None - } - fn const_metadata(&self, _token: TokenId) -> Vec { - Vec::new() } fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option { --- a/pallets/fungible/src/lib.rs +++ b/pallets/fungible/src/lib.rs @@ -168,7 +168,7 @@ .checked_sub(amount) .ok_or(>::TokenValueTooLow)?; - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(owner)?; } @@ -210,7 +210,7 @@ >::TransferNotAllowed, ); - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(from)?; collection.check_allowlist(to)?; } @@ -280,7 +280,7 @@ ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { ensure!( - collection.mint_mode, + collection.permissions.mint_mode(), >::PublicMintingNotAllowed ); collection.check_allowlist(sender)?; @@ -380,7 +380,7 @@ spender: &T::CrossAccountId, amount: u128, ) -> DispatchResult { - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(owner)?; collection.check_allowlist(spender)?; } @@ -408,7 +408,7 @@ if spender.conv_eq(from) { return Ok(None); } - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { // `from`, `to` checked in [`transfer`] collection.check_allowlist(spender)?; } --- a/pallets/nonfungible/src/benchmarking.rs +++ b/pallets/nonfungible/src/benchmarking.rs @@ -29,9 +29,7 @@ const SEED: u32 = 1; fn create_max_item_data(owner: T::CrossAccountId) -> CreateItemData { - let const_data = create_data::(); CreateItemData:: { - const_data, owner, properties: Default::default(), } --- a/pallets/nonfungible/src/common.rs +++ b/pallets/nonfungible/src/common.rs @@ -116,7 +116,6 @@ ) -> Result, DispatchError> { match data { up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData:: { - const_data: data.const_data, properties: data.properties, owner: to.clone(), }), @@ -376,12 +375,6 @@ fn token_owner(&self, token: TokenId) -> Option { >::get((self.id, token)).map(|t| t.owner) - } - fn const_metadata(&self, token: TokenId) -> Vec { - >::get((self.id, token)) - .map(|t| t.const_data) - .unwrap_or_default() - .into_inner() } fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option { --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -109,15 +109,6 @@ } } -fn error_unsupported_schema_version() -> Error { - alloc::format!( - "Unsupported schema version! Support only {:?}", - SchemaVersion::ImageURL - ) - .as_str() - .into() -} - #[derive(ToLog)] pub enum ERC721Events { Transfer { @@ -167,16 +158,10 @@ /// Returns token's const_metadata #[solidity(rename_selector = "tokenURI")] fn token_uri(&self, token_id: uint256) -> Result { - if !matches!(self.schema_version, SchemaVersion::ImageURL) { - return Err(error_unsupported_schema_version()); - } - self.consume_store_reads(1)?; - let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; + let _token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; Ok(string::from_utf8_lossy( - &>::get((self.id, token_id)) - .ok_or("token not found")? - .const_data, + todo!() ) .into()) } @@ -344,7 +329,6 @@ self, &caller, CreateItemData:: { - const_data: BoundedVec::default(), properties: BoundedVec::default(), owner: to, }, @@ -366,10 +350,6 @@ token_id: uint256, token_uri: string, ) -> Result { - if !matches!(self.schema_version, SchemaVersion::ImageURL) { - return Err(error_unsupported_schema_version()); - } - let caller = T::CrossAccountId::from_eth(caller); let to = T::CrossAccountId::from_eth(to); let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?; @@ -385,13 +365,12 @@ return Err("item id should be next".into()); } + todo!("token uri"); + >::create_item( self, &caller, CreateItemData:: { - const_data: Vec::::from(token_uri) - .try_into() - .map_err(|_| "token uri is too long")?, properties: BoundedVec::default(), owner: to, }, @@ -477,7 +456,6 @@ } let data = (0..total_tokens) .map(|_| CreateItemData:: { - const_data: BoundedVec::default(), properties: BoundedVec::default(), owner: to.clone(), }) @@ -496,10 +474,6 @@ to: address, tokens: Vec<(uint256, string)>, ) -> Result { - if !matches!(self.schema_version, SchemaVersion::ImageURL) { - return Err(error_unsupported_schema_version()); - } - let caller = T::CrossAccountId::from_eth(caller); let to = T::CrossAccountId::from_eth(to); let mut expected_index = >::get(self.id) @@ -517,10 +491,8 @@ } expected_index = expected_index.checked_add(1).ok_or("item id overflow")?; + todo!("token uri"); data.push(CreateItemData:: { - const_data: Vec::::from(token_uri) - .try_into() - .map_err(|_| "token uri is too long")?, properties: BoundedVec::default(), owner: to.clone(), }); --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -33,7 +33,7 @@ use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use sp_core::H160; use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; -use sp_std::{vec::Vec, vec}; +use sp_std::{vec::Vec, vec, collections::btree_set::BTreeSet}; use core::ops::Deref; use sp_std::collections::btree_map::BTreeMap; use codec::{Encode, Decode, MaxEncodedLen}; @@ -52,6 +52,7 @@ #[struct_versioning::versioned(version = 2, upper)] #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] pub struct ItemData { + #[version(..2)] pub const_data: BoundedVec, #[version(..2)] @@ -148,9 +149,45 @@ impl Hooks> for Pallet { fn on_runtime_upgrade() -> Weight { if StorageVersion::get::>() < StorageVersion::new(1) { - >::translate_values::, _>(|v| { + let mut had_consts = BTreeSet::new(); + >::translate::, _>(|(collection, token), v| { + let mut props = vec![]; + if !v.const_data.is_empty() { + props.push(Property { + key: b"_old_constData".to_vec().try_into().unwrap(), + value: v.const_data.clone().into_inner().try_into().expect("const too long"), + }); + had_consts.insert(collection); + } + if !v.variable_data.is_empty() { + props.push(Property { + key: b"_old_variableData".to_vec().try_into().unwrap(), + value: v.variable_data.clone().into_inner().try_into().expect("variable too long"), + }) + } + if !props.is_empty() { + Self::set_scoped_token_properties( + collection, + token, + PropertyScope::None, + props.into_iter(), + ).expect("existing token data exceeds property storage"); + } Some(>::from(v)) - }) + }); + for collection in had_consts { + >::set_property_permission_unchecked( + collection, + PropertyKeyPermission { + key: b"_old_constData".to_vec().try_into().unwrap(), + permission: PropertyPermission { + mutable: false, + collection_admin: true, + token_owner: false, + }, + } + ).expect("failed to configure permission"); + } } 0 @@ -267,7 +304,7 @@ >::NoPermission ); - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(sender)?; } @@ -493,7 +530,7 @@ >::NoPermission ); - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(from)?; collection.check_allowlist(to)?; } @@ -579,7 +616,7 @@ ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { ensure!( - collection.mint_mode, + collection.permissions.mint_mode(), >::PublicMintingNotAllowed ); collection.check_allowlist(sender)?; @@ -639,7 +676,7 @@ >::insert( (collection.id, token), ItemData { - const_data: data.const_data.clone(), + // const_data: data.const_data.clone(), owner: data.owner.clone(), }, ); @@ -756,7 +793,7 @@ token: TokenId, spender: Option<&T::CrossAccountId>, ) -> DispatchResult { - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(sender)?; if let Some(spender) = spender { collection.check_allowlist(spender)?; @@ -791,7 +828,7 @@ if spender.conv_eq(from) { return Ok(()); } - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { // `from`, `to` checked in [`transfer`] collection.check_allowlist(spender)?; } @@ -875,7 +912,7 @@ ); Ok(()) } - match handle.limits.nesting_rule() { + match handle.permissions.nesting() { NestingRule::Disabled => fail!(>::NestingIsDisabled), NestingRule::Owner => { ensure_sender_allowed::(handle.id, under, from, sender, nesting_budget)? --- a/pallets/proxy-rmrk-core/src/lib.rs +++ b/pallets/proxy-rmrk-core/src/lib.rs @@ -403,10 +403,8 @@ nft_type: NftType, properties: impl Iterator ) -> Result { + todo!("store nft type"); let data = CreateNftExData { - const_data: nft_type.encode() - .try_into() - .map_err(|_| >::NftTypeEncodeError)?, properties: BoundedVec::default(), owner: owner.clone(), }; @@ -528,13 +526,8 @@ Ok(nft_property) } - pub fn get_nft_type(collection_id: CollectionId, token_id: TokenId) -> Result { - let token_data = >::get((collection_id, token_id)) - .ok_or(>::NoAvailableNftId)?; - - let mut const_data = token_data.const_data.as_slice(); - - NftType::decode(&mut const_data).map_err(|_| >::NoAvailableNftId.into()) + pub fn get_nft_type(_collection_id: CollectionId, _token_id: TokenId) -> Result { + todo!("should get it from properties?") } pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult { --- a/pallets/refungible/src/common.rs +++ b/pallets/refungible/src/common.rs @@ -336,11 +336,6 @@ fn token_owner(&self, _token: TokenId) -> Option { None } - fn const_metadata(&self, token: TokenId) -> Vec { - >::get((self.id, token)) - .const_data - .into_inner() - } fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option { None --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -321,7 +321,7 @@ >::TransferNotAllowed ); - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(from)?; collection.check_allowlist(to)?; } @@ -424,7 +424,7 @@ ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { ensure!( - collection.mint_mode, + collection.permissions.mint_mode(), >::PublicMintingNotAllowed ); collection.check_allowlist(sender)?; @@ -566,7 +566,7 @@ token: TokenId, amount: u128, ) -> DispatchResult { - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(sender)?; collection.check_allowlist(spender)?; } @@ -598,7 +598,7 @@ if spender.conv_eq(from) { return Ok(None); } - if collection.access == AccessMode::AllowList { + if collection.permissions.access() == AccessMode::AllowList { // `from`, `to` checked in [`transfer`] collection.check_allowlist(spender)?; } --- a/pallets/unique/src/benchmarking.rs +++ b/pallets/unique/src/benchmarking.rs @@ -130,26 +130,6 @@ let collection = create_nft_collection::(caller.clone())?; }: _(RawOrigin::Signed(caller.clone()), collection, false) - set_offchain_schema { - let b in 0..OFFCHAIN_SCHEMA_LIMIT; - - let caller: T::AccountId = account("caller", 0, SEED); - let collection = create_nft_collection::(caller.clone())?; - let data = create_var_data(b); - }: set_offchain_schema(RawOrigin::Signed(caller.clone()), collection, data) - - set_const_on_chain_schema { - let b in 0..CONST_ON_CHAIN_SCHEMA_LIMIT; - - let caller: T::AccountId = account("caller", 0, SEED); - let collection = create_nft_collection::(caller.clone())?; - let data = create_var_data(b); - }: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data) - - set_schema_version { - let caller: T::AccountId = account("caller", 0, SEED); - let collection = create_nft_collection::(caller.clone())?; - }: set_schema_version(RawOrigin::Signed(caller.clone()), collection, SchemaVersion::Unique) set_collection_limits{ let caller: T::AccountId = account("caller", 0, SEED); --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -35,10 +35,10 @@ use frame_system::{self as system, ensure_signed}; use sp_runtime::{sp_std::prelude::Vec}; use up_data_structs::{ - CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, + MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, - CollectionLimits, CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, - CreateCollectionData, CreateItemExData, budget, CollectionField, Property, PropertyKey, + CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState, + CreateCollectionData, CreateItemExData, budget, Property, PropertyKey, PropertyKeyPermission, }; use pallet_evm::account::CrossAccountId; @@ -162,6 +162,8 @@ /// * collection_id: Globally unique collection identifier. CollectionLimitSet(CollectionId), + CollectionPermissionSet(CollectionId), + /// Mint permission was set /// /// # Arguments @@ -417,67 +419,6 @@ )); Ok(()) - } - - /// Toggle between normal and allow list access for the methods with access for `Anyone`. - /// - /// # Permissions - /// - /// * Collection Owner. - /// - /// # Arguments - /// - /// * collection_id. - /// - /// * mode: [AccessMode] - #[weight = >::set_public_access_mode()] - #[transactional] - pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult - { - let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); - - let mut target_collection = >::try_get(collection_id)?; - target_collection.check_is_owner(&sender)?; - - target_collection.access = mode.clone(); - - >::deposit_event(Event::::PublicAccessModeSet( - collection_id, - mode - )); - - target_collection.save() - } - - /// Allows Anyone to create tokens if: - /// * Allow List is enabled, and - /// * Address is added to allow list, and - /// * This method was called with True parameter - /// - /// # Permissions - /// * Collection Owner - /// - /// # Arguments - /// - /// * collection_id. - /// - /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above. - #[weight = >::set_mint_permission()] - #[transactional] - pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult - { - let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); - - let mut target_collection = >::try_get(collection_id)?; - target_collection.check_is_owner(&sender)?; - - target_collection.mint_mode = mint_permission; - - >::deposit_event(Event::::MintPermissionSet( - collection_id - )); - - target_collection.save() } /// Change the owner of the collection. @@ -941,118 +882,42 @@ dispatch_call::(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget)) } - /// Set schema standard - /// ImageURL - /// Unique - /// - /// # Permissions - /// - /// * Collection Owner - /// * Collection Admin - /// - /// # Arguments - /// - /// * collection_id. - /// - /// * schema: SchemaVersion: enum - #[weight = >::set_schema_version()] + #[weight = >::set_collection_limits()] #[transactional] - pub fn set_schema_version( + pub fn set_collection_limits( origin, collection_id: CollectionId, - version: SchemaVersion + new_limit: CollectionLimits, ) -> DispatchResult { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); let mut target_collection = >::try_get(collection_id)?; - target_collection.check_is_owner_or_admin(&sender)?; - target_collection.schema_version = version; - - >::deposit_event(Event::::SchemaVersionSet( - collection_id - )); - - target_collection.save() - } - - /// Set off-chain data schema. - /// - /// # Permissions - /// - /// * Collection Owner - /// * Collection Admin - /// - /// # Arguments - /// - /// * collection_id. - /// - /// * schema: String representing the offchain data schema. - #[weight = >::set_offchain_schema(schema.len() as u32)] - #[transactional] - pub fn set_offchain_schema( - origin, - collection_id: CollectionId, - schema: BoundedVec>, - ) -> DispatchResult { - let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); - let collection = >::try_get(collection_id)?; + target_collection.check_is_owner(&sender)?; + let old_limit = &target_collection.limits; - // ========= + target_collection.limits = >::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?; - >::set_field(&collection, &sender, CollectionField::OffchainSchema, schema.into_inner())?; - - >::deposit_event(Event::::OffchainSchemaSet( + >::deposit_event(Event::::CollectionLimitSet( collection_id )); - Ok(()) - } - /// Set const on-chain data schema. - /// - /// # Permissions - /// - /// * Collection Owner - /// * Collection Admin - /// - /// # Arguments - /// - /// * collection_id. - /// - /// * schema: String representing the const on-chain data schema. - #[weight = >::set_const_on_chain_schema(schema.len() as u32)] - #[transactional] - pub fn set_const_on_chain_schema ( - origin, - collection_id: CollectionId, - schema: BoundedVec> - ) -> DispatchResult { - let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); - let collection = >::try_get(collection_id)?; - - // ========= - - >::set_field(&collection, &sender, CollectionField::ConstOnChainSchema, schema.into_inner())?; - - >::deposit_event(Event::::ConstOnChainSchemaSet( - collection_id - )); - Ok(()) + target_collection.save() } #[weight = >::set_collection_limits()] #[transactional] - pub fn set_collection_limits( + pub fn set_collection_permissions( origin, collection_id: CollectionId, - new_limit: CollectionLimits, + new_limit: CollectionPermissions, ) -> DispatchResult { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); let mut target_collection = >::try_get(collection_id)?; target_collection.check_is_owner(&sender)?; - let old_limit = &target_collection.limits; + let old_limit = &target_collection.permissions; - target_collection.limits = >::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?; + target_collection.permissions = >::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?; - >::deposit_event(Event::::CollectionLimitSet( + >::deposit_event(Event::::CollectionPermissionSet( collection_id )); --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -186,7 +186,6 @@ #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct TokenData { - pub const_data: Vec, pub properties: Vec, pub owner: Option, } @@ -223,7 +222,7 @@ fn resolve(who: &AccountId, call: &Call) -> Option; } -#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] +#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum AccessMode { Normal, @@ -296,22 +295,26 @@ pub struct Collection { pub owner: AccountId, pub mode: CollectionMode, + #[version(..2)] pub access: AccessMode, pub name: BoundedVec>, pub description: BoundedVec>, pub token_prefix: BoundedVec>, + + #[version(..2)] pub mint_mode: bool, #[version(..2)] pub offchain_schema: BoundedVec>, + #[version(..2)] pub schema_version: SchemaVersion, pub sponsorship: SponsorshipState, - #[version(..2)] - pub limits: CollectionLimitsVersion1, // Collection private restrictions - #[version(2.., upper(limits.into()))] - pub limits: CollectionLimitsVersion2, + pub limits: CollectionLimits, + + #[version(2.., upper(Default::default()))] + pub permissions: CollectionPermissions, #[version(..2)] pub variable_on_chain_schema: BoundedVec>, @@ -329,27 +332,16 @@ pub struct RpcCollection { pub owner: AccountId, pub mode: CollectionMode, - pub access: AccessMode, pub name: Vec, pub description: Vec, pub token_prefix: Vec, - pub mint_mode: bool, - pub offchain_schema: Vec, - pub schema_version: SchemaVersion, pub sponsorship: SponsorshipState, pub limits: CollectionLimits, - pub const_on_chain_schema: Vec, + pub permissions: CollectionPermissions, pub token_property_permissions: Vec, pub properties: Vec, } -#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] -pub enum CollectionField { - ConstOnChainSchema, - OffchainSchema, -} - #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)] #[derivative(Debug, Default(bound = ""))] pub struct CreateCollectionData { @@ -359,11 +351,9 @@ pub name: BoundedVec>, pub description: BoundedVec>, pub token_prefix: BoundedVec>, - pub offchain_schema: BoundedVec>, - pub schema_version: Option, pub pending_sponsor: Option, pub limits: Option, - pub const_on_chain_schema: BoundedVec>, + pub permissions: Option, pub token_property_permissions: CollectionPropertiesPermissionsVec, pub properties: CollectionPropertiesVec, } @@ -375,7 +365,6 @@ BoundedVec>; /// All fields are wrapped in `Option`s, where None means chain default -#[struct_versioning::versioned(version = 2, upper)] #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct CollectionLimits { @@ -395,9 +384,6 @@ pub owner_can_transfer: Option, pub owner_can_destroy: Option, pub transfers_enabled: Option, - - #[version(2.., upper(None))] - pub nesting_rule: Option, } impl CollectionLimits { @@ -444,9 +430,26 @@ SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)), } } - pub fn nesting_rule(&self) -> &NestingRule { +} + +#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct CollectionPermissions { + pub access: Option, + pub mint_mode: Option, + pub nesting: Option, +} + +impl CollectionPermissions { + pub fn access(&self) -> AccessMode { + self.access.unwrap_or(AccessMode::Normal) + } + pub fn mint_mode(&self) -> bool { + self.mint_mode.unwrap_or(false) + } + pub fn nesting(&self) -> &NestingRule { static DEFAULT: NestingRule = NestingRule::Disabled; - self.nesting_rule.as_ref().unwrap_or(&DEFAULT) + self.nesting.as_ref().unwrap_or(&DEFAULT) } } @@ -520,8 +523,6 @@ #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug)] pub struct CreateNftExData { - #[derivative(Debug(format_with = "bounded::vec_debug"))] - pub const_data: BoundedVec, #[derivative(Debug(format_with = "bounded::vec_debug"))] pub properties: CollectionPropertiesVec, pub owner: CrossAccountId, --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -41,7 +41,6 @@ fn token_owner(collection: CollectionId, token: TokenId) -> Result>; fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result>; - fn const_metadata(collection: CollectionId, token: TokenId) -> Result>; fn collection_properties(collection: CollectionId, properties: Option>>) -> Result>; --- a/runtime/common/src/runtime_apis.rs +++ b/runtime/common/src/runtime_apis.rs @@ -29,9 +29,6 @@ Ok(Some(>::find_topmost_owner(collection, token, &budget)?)) } - fn const_metadata(collection: CollectionId, token: TokenId) -> Result, DispatchError> { - dispatch_unique_runtime!(collection.const_metadata(token)) - } fn collection_properties( collection: CollectionId, @@ -73,7 +70,6 @@ keys: Option>> ) -> Result, DispatchError> { let token_data = TokenData { - const_data: Self::const_metadata(collection, token_id)?, properties: Self::token_properties(collection, token_id, keys)?, owner: Self::token_owner(collection, token_id)? }; --- a/runtime/tests/src/tests.rs +++ b/runtime/tests/src/tests.rs @@ -2399,28 +2399,6 @@ // #endregion #[test] -fn set_const_on_chain_schema() { - new_test_ext().execute_with(|| { - let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1)); - - let origin1 = Origin::signed(1); - assert_ok!(Unique::set_const_on_chain_schema( - origin1, - collection_id, - b"test const on chain schema".to_vec().try_into().unwrap() - )); - - assert_eq!( - >::get(( - collection_id, - CollectionField::ConstOnChainSchema - )), - b"test const on chain schema".to_vec() - ); - }); -} - -#[test] fn collection_transfer_flag_works() { new_test_ext().execute_with(|| { let origin1 = Origin::signed(1);