difftreelog
NFTPAR-118. Per Collection Limits
in: master
3 files changed
README.mddiffbeforeafterboth--- a/README.md
+++ b/README.md
@@ -241,6 +241,12 @@
"ReFungible": "CreateReFungibleData"
}
}
+ "CollectionLimits": {
+ "AccountTokenOwnershipLimit": "u32",
+ "SponsoredDataSize": "u32",
+ "TokenLimit": "u32",
+ "SponsorTransferTimeout": "u32"
+ }
}
```
\ No newline at end of file
node/src/chain_spec.rsdiffbeforeafterboth171 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),171 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),172 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),172 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),173 const_on_chain_schema: vec![],173 const_on_chain_schema: vec![],174 variable_on_chain_schema: vec![]174 variable_on_chain_schema: vec![],175 limits: CollectionLimits::default()175 },176 },176 )],177 )],177 nft_item_id: vec![],178 nft_item_id: vec![],pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -109,6 +109,7 @@
pub offchain_schema: Vec<u8>,
pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
+ pub limits: CollectionLimits, // Collection private restrictions
pub variable_on_chain_schema: Vec<u8>, //
pub const_on_chain_schema: Vec<u8>, //
}
@@ -164,6 +165,27 @@
pub start_block: BlockNumber,
}
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CollectionLimits {
+ pub account_token_ownership_limit: u32,
+ pub sponsored_data_size: u32,
+ pub token_limit: u32,
+
+ // Timeouts for item types in passed blocks
+ pub sponsor_transfer_timeout: u32,
+}
+
+impl Default for CollectionLimits {
+ fn default() -> CollectionLimits {
+ CollectionLimits {
+ account_token_ownership_limit: 0,
+ token_limit: 0,
+ sponsored_data_size: 0,
+ sponsor_transfer_timeout: 0 }
+ }
+}
+
#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ChainLimits {
@@ -321,7 +343,11 @@
/// Unexpected collection type.
UnexpectedCollectionType,
/// Can't store metadata in fungible tokens.
- CantStoreMetadataInFungibleTokens
+ CantStoreMetadataInFungibleTokens,
+ /// Collection token limit exceeded
+ CollectionTokenLimitExceeded,
+ /// Account token limit exceeded per collection
+ AccountTokenLimitExceeded
}
}
@@ -537,6 +563,7 @@
unconfirmed_sponsor: T::AccountId::default(),
variable_on_chain_schema: Vec::new(),
const_on_chain_schema: Vec::new(),
+ limits: CollectionLimits::default(),
};
// Add new collection to map
@@ -1380,17 +1407,21 @@
Ok(())
}
- // #[cfg(feature = "runtime-benchmarks")]
- // #[weight = 0]
- // pub fn add_contract_sponsoring_debug(
- // origin,
- // contract_address: T::AccountId,
- // owner: T::AccountId) -> DispatchResult {
- // let sender = ensure_signed(origin)?;
- // <ContractOwner<T>>::insert(contract_address.clone(), owner);
- // Ok(())
- // }
-
+ #[weight = 0]
+ pub fn set_collection_limits(
+ origin,
+ collection_id: u64,
+ limits: CollectionLimits,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ Self::check_owner_permissions(collection_id, sender.clone())?;
+
+ let mut target_collection = <Collection<T>>::get(collection_id);
+ target_collection.limits = limits;
+ <Collection<T>>::insert(collection_id, target_collection);
+
+ Ok(())
+ }
}
}
@@ -1399,6 +1430,12 @@
fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {
if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+
+ // check token limit and account token limit
+ let total_items: u64 = ItemListIndex::get(collection_id);
+ let account_items: u32 = <AddressTokens<T>>::get(collection_id, sender.clone()).len() as u32;
+ ensure!(collection.limits.token_limit as u64 > total_items, Error::<T>::CollectionTokenLimitExceeded);
+ ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);
ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);
Self::check_white_list(collection_id, owner)?;
Self::check_white_list(collection_id, sender)?;
@@ -1478,7 +1515,6 @@
Self::add_refungible_item(item)?;
}
};
-
// call event
Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
@@ -2211,18 +2247,35 @@
// Determine who is paying transaction fee based on ecnomic model
// Parse call to extract collection ID and access collection sponsor
let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {
- Some(Call::create_item(collection_id, _properties, _owner)) => {
- <Collection<T>>::get(collection_id).sponsor
+ Some(Call::create_item(collection_id, _owner, _properties)) => {
+
+ // check free create limit
+ if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)
+ {
+ <Collection<T>>::get(collection_id).sponsor
+ } else {
+ T::AccountId::default()
+ }
}
Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {
+
+ let _collection_limits = <Collection<T>>::get(collection_id).limits;
let _collection_mode = <Collection<T>>::get(collection_id).mode;
// sponsor timeout
let sponsor_transfer = match _collection_mode {
CollectionMode::NFT => {
+
+ // get correct limit
+ let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+ _collection_limits.sponsor_transfer_timeout
+ } else {
+ ChainLimit::get().nft_sponsor_transfer_timeout
+ };
+
let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);
let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
- let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();
+ let limit_time = basket + limit.into();
if block_number >= limit_time {
<NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);
true
@@ -2232,12 +2285,20 @@
}
}
CollectionMode::Fungible(_) => {
+
+ // get correct limit
+ let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+ _collection_limits.sponsor_transfer_timeout
+ } else {
+ ChainLimit::get().fungible_sponsor_transfer_timeout
+ };
+
let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);
let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
if basket.iter().any(|i| i.address == _new_owner.clone())
{
let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();
- let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();
+ let limit_time = item.start_block + limit.into();
if block_number >= limit_time {
basket.retain(|x| x.address == item.address);
basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });
@@ -2254,9 +2315,17 @@
}
}
CollectionMode::ReFungible(_) => {
+
+ // get correct limit
+ let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+ _collection_limits.sponsor_transfer_timeout
+ } else {
+ ChainLimit::get().refungible_sponsor_transfer_timeout
+ };
+
let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);
let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
- let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();
+ let limit_time = basket + limit.into();
if block_number >= limit_time {
<ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);
true