From ae0e886a66645fb2445148166e92229e9feb0b22 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 04 Nov 2021 14:25:28 +0000 Subject: [PATCH] feat: track and limit admin amount --- --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -11,7 +11,8 @@ use nft_data_structs::{ COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH, - MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, WithdrawReasons, + COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, + WithdrawReasons, }; pub use pallet::*; use sp_core::H160; @@ -165,6 +166,13 @@ #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); + #[pallet::extra_constants] + impl Pallet { + pub fn collection_admins_limit() -> u32 { + COLLECTION_ADMINS_LIMIT + } + } + #[pallet::event] #[pallet::generate_deposit(pub fn deposit_event)] pub enum Event { @@ -265,6 +273,8 @@ TotalCollectionsLimitExceeded, /// variable_data exceeded data limit. TokenVariableDataLimitExceeded, + /// Exceeded max admin amount + CollectionAdminAmountExceeded, /// Collection settings not allowing items transferring TransferNotAllowed, @@ -305,6 +315,14 @@ QueryKind = OptionQuery, >; + #[pallet::storage] + pub type AdminAmount = StorageMap< + Hasher = Blake2_128Concat, + Key = CollectionId, + Value = u32, + QueryKind = ValueQuery, + >; + /// List of collection admins #[pallet::storage] pub type IsAdmin = StorageNMap< @@ -419,6 +437,7 @@ >::put(destroyed_collections); >::remove(collection.id); + >::remove(collection.id); >::remove_prefix((collection.id,), None); >::remove_prefix((collection.id,), None); Ok(()) @@ -435,9 +454,44 @@ // ========= if allowed { - >::insert((collection.id, user.as_sub()), true); + >::insert((collection.id, user), true); + } else { + >::remove((collection.id, user)); + } + + Ok(()) + } + + pub fn toggle_admin( + collection: &CollectionHandle, + sender: &T::CrossAccountId, + user: &T::CrossAccountId, + admin: bool, + ) -> DispatchResult { + collection.check_is_owner_or_admin(&sender)?; + + let was_admin = >::get((collection.id, user)); + if was_admin == admin { + return Ok(()); + } + let amount = >::get(collection.id); + + if admin { + let amount = amount + .checked_add(1) + .ok_or(>::CollectionAdminAmountExceeded)?; + ensure!( + amount <= Self::collection_admins_limit(), + >::CollectionAdminAmountExceeded, + ); + + // ========= + + >::insert(collection.id, amount); + >::insert((collection.id, user), true); } else { - >::remove((collection.id, user.as_sub())); + >::insert(collection.id, amount.saturating_sub(1)); + >::remove((collection.id, user)); } Ok(()) --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -36,8 +36,8 @@ use sp_runtime::{sp_std::prelude::Vec}; use nft_data_structs::{ MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT, - VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT, - OFFCHAIN_SCHEMA_LIMIT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, + VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT, + FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, NFT_SPONSOR_TRANSFER_TIMEOUT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission, }; @@ -156,7 +156,6 @@ where origin: T::Origin { - const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT; type Error = Error; fn on_initialize(_now: T::BlockNumber) -> Weight { @@ -406,12 +405,9 @@ #[transactional] pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); - let collection = >::try_get(collection_id)?; - collection.check_is_owner_or_admin(&sender)?; - >::insert((collection_id, new_admin_id.as_sub()), true); - Ok(()) + >::toggle_admin(&collection, &sender, &new_admin_id, true) } /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin. @@ -430,12 +426,9 @@ #[transactional] pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); - let collection = >::try_get(collection_id)?; - collection.check_is_owner_or_admin(&sender)?; - >::remove((collection_id, account_id.as_sub())); - Ok(()) + >::toggle_admin(&collection, &sender, &account_id, false) } /// # Permissions --- a/primitives/nft/src/lib.rs +++ b/primitives/nft/src/lib.rs @@ -41,7 +41,7 @@ } else { 10 }; -pub const COLLECTION_ADMINS_LIMIT: u64 = 5; +pub const COLLECTION_ADMINS_LIMIT: u32 = 5; pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX; pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 1000000 -- gitstuff