difftreelog
refactor make collection limits fields optional
in: master
8 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -105,10 +105,10 @@
Ok(())
}
pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
- Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)
+ Ok(self.limits.owner_can_transfer() && self.is_owner_or_admin(user)?)
}
pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
- Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)
+ Ok(self.limits.owner_can_transfer() && self.is_owner_or_admin(user)?)
}
pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {
self.consume_sload()?;
@@ -405,9 +405,10 @@
collection: CollectionHandle<T>,
sender: &T::CrossAccountId,
) -> DispatchResult {
- if !collection.limits.owner_can_destroy {
- fail!(Error::<T>::NoPermission);
- }
+ ensure!(
+ collection.limits.owner_can_destroy(),
+ <Error<T>>::NoPermission,
+ );
collection.check_is_owner(&sender)?;
let destroyed_collections = <DestroyedCollectionCount<T>>::get()
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -157,8 +157,8 @@
amount: u128,
) -> DispatchResult {
ensure!(
- collection.transfers_enabled,
- <CommonError<T>>::TransferNotAllowed
+ collection.limits.transfers_enabled(),
+ <CommonError<T>>::TransferNotAllowed,
);
if collection.access == AccessMode::WhiteList {
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -43,11 +43,8 @@
let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
let collection_limits = &collection.limits;
- let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- NFT_SPONSOR_TRANSFER_TIMEOUT
- };
+ let limit =
+ collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
let mut sponsor = true;
if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {
@@ -74,11 +71,8 @@
UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
let who = T::CrossAccountId::from_eth(*caller);
let collection_limits = &collection.limits;
- let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
- };
+ let limit = collection_limits
+ .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
let mut sponsored = true;
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -37,8 +37,9 @@
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, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,
- CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
+ 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,
};
use pallet_common::{
account::CrossAccountId, CollectionHandle, IsAdmin, Pallet as PalletCommon,
@@ -188,11 +189,6 @@
// Anyone can create a collection
let who = ensure_signed(origin)?;
-
- let limits = CollectionLimits::<T::BlockNumber> {
- sponsored_data_size: CUSTOM_DATA_LIMIT,
- ..Default::default()
- };
// Create new collection
let new_collection = Collection::<T> {
@@ -208,8 +204,7 @@
sponsorship: SponsorshipState::Disabled,
variable_on_chain_schema: Vec::new(),
const_on_chain_schema: Vec::new(),
- limits,
- transfers_enabled: true,
+ limits: Default::default(),
meta_update_permission: Default::default(),
};
@@ -582,7 +577,7 @@
// =========
- target_collection.transfers_enabled = value;
+ target_collection.limits.transfers_enabled = Some(value);
target_collection.save()
}
@@ -888,30 +883,63 @@
pub fn set_collection_limits(
origin,
collection_id: CollectionId,
- new_limits: CollectionLimits<T::BlockNumber>,
+ new_limit: CollectionLimits,
) -> DispatchResult {
+ let mut new_limit = new_limit;
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
target_collection.check_is_owner(&sender)?;
- let old_limits = &target_collection.limits;
+ let old_limit = &target_collection.limits;
- // collection bounds
- ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&
- new_limits.account_token_ownership_limit.unwrap_or(0) <= MAX_TOKEN_OWNERSHIP &&
- new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,
- Error::<T>::CollectionLimitBoundsExceeded);
+ 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
+ }
+ )*
+ }};
+ }
- // token_limit check prev
- ensure!(old_limits.token_limit >= new_limits.token_limit, <CommonError<T>>::CollectionTokenLimitExceeded);
- ensure!(new_limits.token_limit > 0, <CommonError<T>>::CollectionTokenLimitExceeded);
-
- ensure!(
- (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&
- (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),
- Error::<T>::OwnerPermissionsCantBeReverted,
+ limit_default!(old_limit, new_limit,
+ account_token_ownership_limit => ensure!(
+ new_limit <= MAX_TOKEN_OWNERSHIP,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ sponsor_transfer_timeout(match target_collection.mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ }) => ensure!(
+ new_limit <= MAX_SPONSOR_TIMEOUT,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ sponsored_data_size => ensure!(
+ new_limit <= CUSTOM_DATA_LIMIT,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ token_limit => ensure!(
+ old_limit >= new_limit && new_limit > 0,
+ <CommonError<T>>::CollectionTokenLimitExceeded
+ ),
+ owner_can_transfer => ensure!(
+ old_limit || !new_limit,
+ <Error<T>>::OwnerPermissionsCantBeReverted,
+ ),
+ owner_can_destroy => ensure!(
+ old_limit || !new_limit,
+ <Error<T>>::OwnerPermissionsCantBeReverted,
+ ),
+ sponsored_data_rate_limit => {},
+ transfers_enabled => {},
);
- target_collection.limits = new_limits;
+ target_collection.limits = new_limit;
target_collection.save()
}
pallets/nft/src/sponsorship.rsdiffbeforeafterboth--- a/pallets/nft/src/sponsorship.rs
+++ b/pallets/nft/src/sponsorship.rs
@@ -26,7 +26,13 @@
// sponsor timeout
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let limit = collection.limits.sponsor_transfer_timeout;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match _properties {
+ CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
if CreateItemBasket::<T>::contains_key((collection_id, &who)) {
let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));
let limit_time = last_tx_block + limit.into();
@@ -37,7 +43,7 @@
CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
// check free create limit
- if collection.limits.sponsored_data_size >= (_properties.data_size() as u32) {
+ if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {
collection.sponsorship.sponsor().cloned()
} else {
None
@@ -61,11 +67,8 @@
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 {
- NFT_SPONSOR_TRANSFER_TIMEOUT
- };
+ let limit =
+ collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
let mut sponsored = true;
if NftTransferBasket::<T>::contains_key(collection_id, item_id) {
@@ -83,11 +86,8 @@
}
CollectionMode::Fungible(_) => {
// get correct limit
- let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
- };
+ let limit = collection_limits
+ .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
let mut sponsored = true;
@@ -106,11 +106,8 @@
}
CollectionMode::ReFungible => {
// get correct limit
- let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
- };
+ let limit = collection_limits
+ .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
let mut sponsored = true;
if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
@@ -150,13 +147,13 @@
// Can't sponsor fungible collection, this tx will be rejected
// as invalid
!matches!(collection.mode, CollectionMode::Fungible(_)) &&
- data.len() <= collection.limits.sponsored_data_size as usize
+ data.len() <= collection.limits.sponsored_data_size() as usize
{
- if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {
+ if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
if VariableMetaDataBasket::<T>::get(collection_id, item_id)
- .map(|last_block| block_number - last_block > rate_limit)
+ .map(|last_block| block_number - last_block > rate_limit.into())
.unwrap_or(true)
{
sponsor_metadata_changes = true;
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -164,7 +164,7 @@
.ok_or_else(|| <CommonError<T>>::TokenNotFound)?;
ensure!(
&token_data.owner == sender
- || (collection.limits.owner_can_transfer
+ || (collection.limits.owner_can_transfer()
&& collection.is_owner_or_admin(sender)?),
<CommonError<T>>::NoPermission
);
@@ -215,7 +215,7 @@
token: TokenId,
) -> DispatchResult {
ensure!(
- collection.transfers_enabled,
+ collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed
);
@@ -223,7 +223,8 @@
.ok_or_else(|| <CommonError<T>>::TokenNotFound)?;
ensure!(
&token_data.owner == from
- || (collection.limits.owner_can_transfer && collection.is_owner_or_admin(from)?),
+ || (collection.limits.owner_can_transfer()
+ && collection.is_owner_or_admin(from)?),
<CommonError<T>>::NoPermission
);
@@ -327,7 +328,7 @@
.checked_add(data.len() as u32)
.ok_or(ArithmeticError::Overflow)?;
ensure!(
- tokens_minted < collection.limits.token_limit,
+ tokens_minted < collection.limits.token_limit(),
<CommonError<T>>::CollectionTokenLimitExceeded
);
collection.consume_sstore()?;
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -268,7 +268,7 @@
amount: u128,
) -> DispatchResult {
ensure!(
- collection.transfers_enabled,
+ collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed
);
@@ -404,7 +404,7 @@
.checked_add(data.len() as u32)
.ok_or(ArithmeticError::Overflow)?;
ensure!(
- tokens_minted < collection.limits.token_limit,
+ tokens_minted < collection.limits.token_limit(),
<CommonError<T>>::CollectionTokenLimitExceeded
);
primitives/nft/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12 BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13 dispatch::DispatchResult,14 ensure, fail, parameter_types,15 traits::{16 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17 Randomness, IsSubType, WithdrawReasons,18 },19 weights::{20 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22 WeightToFeePolynomial, DispatchClass,23 },24 StorageValue, transactional,25};26use derivative::Derivative;27use scale_info::TypeInfo;2829pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;30pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;31pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;32pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;3334pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {35 10000036} else {37 1038};39pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40 204841} else {42 1043};44pub const COLLECTION_ADMINS_LIMIT: u64 = 5;45pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {46 100000047} else {48 1049};5051// Timeouts for item types in passed blocks52pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;53pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;54pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;5556// Schema limits57pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;58pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;59pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;6061pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;62pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;63pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;6465/// How much items can be created per single66/// create_many call67pub const MAX_ITEMS_PER_BATCH: u32 = 200;6869parameter_types! {70 pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;71}7273#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]74#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]75pub struct CollectionId(pub u32);76impl EncodeLike<u32> for CollectionId {}77impl EncodeLike<CollectionId> for u32 {}7879#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]80#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]81pub struct TokenId(pub u32);82impl EncodeLike<u32> for TokenId {}83impl EncodeLike<TokenId> for u32 {}8485impl TokenId {86 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {87 self.088 .checked_add(1)89 .ok_or(ArithmeticError::Overflow)90 .map(Self)91 }92}9394impl From<TokenId> for U256 {95 fn from(t: TokenId) -> Self {96 t.0.into()97 }98}99100impl TryFrom<U256> for TokenId {101 type Error = &'static str;102103 fn try_from(value: U256) -> Result<Self, Self::Error> {104 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))105 }106}107108pub struct OverflowError;109impl From<OverflowError> for &'static str {110 fn from(_: OverflowError) -> Self {111 "overflow occured"112 }113}114115pub type DecimalPoints = u8;116117#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]118#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]119pub enum CollectionMode {120 NFT,121 // decimal points122 Fungible(DecimalPoints),123 ReFungible,124}125126impl CollectionMode {127 pub fn id(&self) -> u8 {128 match self {129 CollectionMode::NFT => 1,130 CollectionMode::Fungible(_) => 2,131 CollectionMode::ReFungible => 3,132 }133 }134}135136pub trait SponsoringResolve<AccountId, Call> {137 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;138}139140#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]141#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]142pub enum AccessMode {143 Normal,144 WhiteList,145}146impl Default for AccessMode {147 fn default() -> Self {148 Self::Normal149 }150}151152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]153#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]154pub enum SchemaVersion {155 ImageURL,156 Unique,157}158impl Default for SchemaVersion {159 fn default() -> Self {160 Self::ImageURL161 }162}163164#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]165#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]166pub struct Ownership<AccountId> {167 pub owner: AccountId,168 pub fraction: u128,169}170171#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]172#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]173pub enum SponsorshipState<AccountId> {174 /// The fees are applied to the transaction sender175 Disabled,176 Unconfirmed(AccountId),177 /// Transactions are sponsored by specified account178 Confirmed(AccountId),179}180181impl<AccountId> SponsorshipState<AccountId> {182 pub fn sponsor(&self) -> Option<&AccountId> {183 match self {184 Self::Confirmed(sponsor) => Some(sponsor),185 _ => None,186 }187 }188189 pub fn pending_sponsor(&self) -> Option<&AccountId> {190 match self {191 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),192 _ => None,193 }194 }195196 pub fn confirmed(&self) -> bool {197 matches!(self, Self::Confirmed(_))198 }199}200201impl<T> Default for SponsorshipState<T> {202 fn default() -> Self {203 Self::Disabled204 }205}206207#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]208#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]209pub struct Collection<T: frame_system::Config> {210 pub owner: T::AccountId,211 pub mode: CollectionMode,212 pub access: AccessMode,213 pub name: Vec<u16>, // 64 include null escape char214 pub description: Vec<u16>, // 256 include null escape char215 pub token_prefix: Vec<u8>, // 16 include null escape char216 pub mint_mode: bool,217 pub offchain_schema: Vec<u8>,218 pub schema_version: SchemaVersion,219 pub sponsorship: SponsorshipState<T::AccountId>,220 pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions221 pub variable_on_chain_schema: Vec<u8>, //222 pub const_on_chain_schema: Vec<u8>, //223 pub meta_update_permission: MetaUpdatePermission,224 pub transfers_enabled: bool,225}226227#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]228#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]229pub struct NftItemType<AccountId> {230 pub owner: AccountId,231 pub const_data: Vec<u8>,232 pub variable_data: Vec<u8>,233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]236#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]237pub struct FungibleItemType {238 pub value: u128,239}240241#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct ReFungibleItemType<AccountId> {244 pub owner: Vec<Ownership<AccountId>>,245 pub const_data: Vec<u8>,246 pub variable_data: Vec<u8>,247}248249#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]251pub struct CollectionLimits<BlockNumber: Encode + Decode> {252 pub account_token_ownership_limit: Option<u32>,253 pub sponsored_data_size: u32,254 /// None - setVariableMetadata is not sponsored255 /// Some(v) - setVariableMetadata is sponsored256 /// if there is v block between txs257 pub sponsored_data_rate_limit: Option<BlockNumber>,258 pub token_limit: u32,259260 // Timeouts for item types in passed blocks261 pub sponsor_transfer_timeout: u32,262 pub owner_can_transfer: bool,263 pub owner_can_destroy: bool,264}265266impl<BlockNumber: Encode + Decode> CollectionLimits<BlockNumber> {267 pub fn account_token_ownership_limit(&self) -> u32 {268 self.account_token_ownership_limit269 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)270 .min(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)271 }272}273274impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {275 fn default() -> Self {276 Self {277 account_token_ownership_limit: Some(10_000_000),278 token_limit: u32::max_value(),279 sponsored_data_size: u32::MAX,280 sponsored_data_rate_limit: None,281 sponsor_transfer_timeout: 14400,282 owner_can_transfer: true,283 owner_can_destroy: true,284 }285 }286}287288/// BoundedVec doesn't supports serde289#[cfg(feature = "serde1")]290mod bounded_serde {291 use core::convert::TryFrom;292 use frame_support::{BoundedVec, traits::Get};293 use serde::{294 ser::{self, Serialize},295 de::{self, Deserialize, Error},296 };297 use sp_std::vec::Vec;298299 pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>300 where301 D: ser::Serializer,302 V: Serialize,303 {304 let vec: &Vec<_> = &value;305 vec.serialize(serializer)306 }307308 pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>309 where310 D: de::Deserializer<'de>,311 V: de::Deserialize<'de>,312 S: Get<u32>,313 {314 // TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?315 let vec = <Vec<V>>::deserialize(deserializer)?;316 let len = vec.len();317 TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))318 }319}320321#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]322#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]323#[derivative(Debug)]324pub struct CreateNftData {325 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]326 #[derivative(Debug = "ignore")]327 pub const_data: BoundedVec<u8, CustomDataLimit>,328 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]329 #[derivative(Debug = "ignore")]330 pub variable_data: BoundedVec<u8, CustomDataLimit>,331}332333#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]334#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]335pub struct CreateFungibleData {336 pub value: u128,337}338339#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]340#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]341#[derivative(Debug)]342pub struct CreateReFungibleData {343 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]344 #[derivative(Debug = "ignore")]345 pub const_data: BoundedVec<u8, CustomDataLimit>,346 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]347 #[derivative(Debug = "ignore")]348 pub variable_data: BoundedVec<u8, CustomDataLimit>,349 pub pieces: u128,350}351352#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]353#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]354pub enum MetaUpdatePermission {355 ItemOwner,356 Admin,357 None,358}359360impl Default for MetaUpdatePermission {361 fn default() -> Self {362 Self::ItemOwner363 }364}365366#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]367#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]368pub enum CreateItemData {369 NFT(CreateNftData),370 Fungible(CreateFungibleData),371 ReFungible(CreateReFungibleData),372}373374impl CreateItemData {375 pub fn data_size(&self) -> usize {376 match self {377 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),378 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),379 _ => 0,380 }381 }382}383384impl From<CreateNftData> for CreateItemData {385 fn from(item: CreateNftData) -> Self {386 CreateItemData::NFT(item)387 }388}389390impl From<CreateReFungibleData> for CreateItemData {391 fn from(item: CreateReFungibleData) -> Self {392 CreateItemData::ReFungible(item)393 }394}395396impl From<CreateFungibleData> for CreateItemData {397 fn from(item: CreateFungibleData) -> Self {398 CreateItemData::Fungible(item)399 }400}1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12 BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13 dispatch::DispatchResult,14 ensure, fail, parameter_types,15 traits::{16 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17 Randomness, IsSubType, WithdrawReasons,18 },19 weights::{20 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22 WeightToFeePolynomial, DispatchClass,23 },24 StorageValue, transactional,25};26use derivative::Derivative;27use scale_info::TypeInfo;2829pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;30pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;31pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;32pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;3334pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {35 10000036} else {37 1038};39pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40 204841} else {42 1043};44pub const COLLECTION_ADMINS_LIMIT: u64 = 5;45pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;46pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {47 100000048} else {49 1050};5152// Timeouts for item types in passed blocks53pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;5657// Schema limits58pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;59pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;60pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;6162pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;63pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;64pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;6566/// How much items can be created per single67/// create_many call68pub const MAX_ITEMS_PER_BATCH: u32 = 200;6970parameter_types! {71 pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;72}7374#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]75#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]76pub struct CollectionId(pub u32);77impl EncodeLike<u32> for CollectionId {}78impl EncodeLike<CollectionId> for u32 {}7980#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]81#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]82pub struct TokenId(pub u32);83impl EncodeLike<u32> for TokenId {}84impl EncodeLike<TokenId> for u32 {}8586impl TokenId {87 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {88 self.089 .checked_add(1)90 .ok_or(ArithmeticError::Overflow)91 .map(Self)92 }93}9495impl From<TokenId> for U256 {96 fn from(t: TokenId) -> Self {97 t.0.into()98 }99}100101impl TryFrom<U256> for TokenId {102 type Error = &'static str;103104 fn try_from(value: U256) -> Result<Self, Self::Error> {105 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))106 }107}108109pub struct OverflowError;110impl From<OverflowError> for &'static str {111 fn from(_: OverflowError) -> Self {112 "overflow occured"113 }114}115116pub type DecimalPoints = u8;117118#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]119#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]120pub enum CollectionMode {121 NFT,122 // decimal points123 Fungible(DecimalPoints),124 ReFungible,125}126127impl CollectionMode {128 pub fn id(&self) -> u8 {129 match self {130 CollectionMode::NFT => 1,131 CollectionMode::Fungible(_) => 2,132 CollectionMode::ReFungible => 3,133 }134 }135}136137pub trait SponsoringResolve<AccountId, Call> {138 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;139}140141#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]142#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]143pub enum AccessMode {144 Normal,145 WhiteList,146}147impl Default for AccessMode {148 fn default() -> Self {149 Self::Normal150 }151}152153#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]154#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]155pub enum SchemaVersion {156 ImageURL,157 Unique,158}159impl Default for SchemaVersion {160 fn default() -> Self {161 Self::ImageURL162 }163}164165#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]166#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]167pub struct Ownership<AccountId> {168 pub owner: AccountId,169 pub fraction: u128,170}171172#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]173#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]174pub enum SponsorshipState<AccountId> {175 /// The fees are applied to the transaction sender176 Disabled,177 Unconfirmed(AccountId),178 /// Transactions are sponsored by specified account179 Confirmed(AccountId),180}181182impl<AccountId> SponsorshipState<AccountId> {183 pub fn sponsor(&self) -> Option<&AccountId> {184 match self {185 Self::Confirmed(sponsor) => Some(sponsor),186 _ => None,187 }188 }189190 pub fn pending_sponsor(&self) -> Option<&AccountId> {191 match self {192 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),193 _ => None,194 }195 }196197 pub fn confirmed(&self) -> bool {198 matches!(self, Self::Confirmed(_))199 }200}201202impl<T> Default for SponsorshipState<T> {203 fn default() -> Self {204 Self::Disabled205 }206}207208#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]209#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]210pub struct Collection<T: frame_system::Config> {211 pub owner: T::AccountId,212 pub mode: CollectionMode,213 pub access: AccessMode,214 pub name: Vec<u16>, // 64 include null escape char215 pub description: Vec<u16>, // 256 include null escape char216 pub token_prefix: Vec<u8>, // 16 include null escape char217 pub mint_mode: bool,218 pub offchain_schema: Vec<u8>,219 pub schema_version: SchemaVersion,220 pub sponsorship: SponsorshipState<T::AccountId>,221 pub limits: CollectionLimits, // Collection private restrictions222 pub variable_on_chain_schema: Vec<u8>, //223 pub const_on_chain_schema: Vec<u8>, //224 pub meta_update_permission: MetaUpdatePermission,225}226227#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]228#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]229pub struct NftItemType<AccountId> {230 pub owner: AccountId,231 pub const_data: Vec<u8>,232 pub variable_data: Vec<u8>,233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]236#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]237pub struct FungibleItemType {238 pub value: u128,239}240241#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct ReFungibleItemType<AccountId> {244 pub owner: Vec<Ownership<AccountId>>,245 pub const_data: Vec<u8>,246 pub variable_data: Vec<u8>,247}248249#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]251pub struct CollectionLimits {252 pub account_token_ownership_limit: Option<u32>,253 pub sponsored_data_size: Option<u32>,254 /// None - setVariableMetadata is not sponsored255 /// Some(v) - setVariableMetadata is sponsored256 /// if there is v block between txs257 pub sponsored_data_rate_limit: Option<u32>,258 pub token_limit: Option<u32>,259260 // Timeouts for item types in passed blocks261 pub sponsor_transfer_timeout: Option<u32>,262 pub owner_can_transfer: Option<bool>,263 pub owner_can_destroy: Option<bool>,264 pub transfers_enabled: Option<bool>,265}266267impl CollectionLimits {268 pub fn account_token_ownership_limit(&self) -> u32 {269 self.account_token_ownership_limit270 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)271 .min(MAX_TOKEN_OWNERSHIP)272 }273 pub fn sponsored_data_size(&self) -> u32 {274 self.sponsored_data_size275 .unwrap_or(CUSTOM_DATA_LIMIT)276 .min(CUSTOM_DATA_LIMIT)277 }278 pub fn token_limit(&self) -> u32 {279 self.token_limit280 .unwrap_or(COLLECTION_TOKEN_LIMIT)281 .min(COLLECTION_TOKEN_LIMIT)282 }283 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {284 self.sponsor_transfer_timeout285 .unwrap_or(default)286 .min(MAX_SPONSOR_TIMEOUT)287 }288 pub fn owner_can_transfer(&self) -> bool {289 self.owner_can_transfer.unwrap_or(true)290 }291 pub fn owner_can_destroy(&self) -> bool {292 self.owner_can_destroy.unwrap_or(true)293 }294 pub fn transfers_enabled(&self) -> bool {295 self.transfers_enabled.unwrap_or(true)296 }297 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {298 self.sponsored_data_rate_limit299 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))300 }301}302303/// BoundedVec doesn't supports serde304#[cfg(feature = "serde1")]305mod bounded_serde {306 use core::convert::TryFrom;307 use frame_support::{BoundedVec, traits::Get};308 use serde::{309 ser::{self, Serialize},310 de::{self, Deserialize, Error},311 };312 use sp_std::vec::Vec;313314 pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>315 where316 D: ser::Serializer,317 V: Serialize,318 {319 let vec: &Vec<_> = &value;320 vec.serialize(serializer)321 }322323 pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>324 where325 D: de::Deserializer<'de>,326 V: de::Deserialize<'de>,327 S: Get<u32>,328 {329 // TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?330 let vec = <Vec<V>>::deserialize(deserializer)?;331 let len = vec.len();332 TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))333 }334}335336#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]337#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]338#[derivative(Debug)]339pub struct CreateNftData {340 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]341 #[derivative(Debug = "ignore")]342 pub const_data: BoundedVec<u8, CustomDataLimit>,343 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]344 #[derivative(Debug = "ignore")]345 pub variable_data: BoundedVec<u8, CustomDataLimit>,346}347348#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]349#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]350pub struct CreateFungibleData {351 pub value: u128,352}353354#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]355#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]356#[derivative(Debug)]357pub struct CreateReFungibleData {358 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]359 #[derivative(Debug = "ignore")]360 pub const_data: BoundedVec<u8, CustomDataLimit>,361 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]362 #[derivative(Debug = "ignore")]363 pub variable_data: BoundedVec<u8, CustomDataLimit>,364 pub pieces: u128,365}366367#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]368#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]369pub enum MetaUpdatePermission {370 ItemOwner,371 Admin,372 None,373}374375impl Default for MetaUpdatePermission {376 fn default() -> Self {377 Self::ItemOwner378 }379}380381#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]382#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]383pub enum CreateItemData {384 NFT(CreateNftData),385 Fungible(CreateFungibleData),386 ReFungible(CreateReFungibleData),387}388389impl CreateItemData {390 pub fn data_size(&self) -> usize {391 match self {392 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),393 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),394 _ => 0,395 }396 }397}398399impl From<CreateNftData> for CreateItemData {400 fn from(item: CreateNftData) -> Self {401 CreateItemData::NFT(item)402 }403}404405impl From<CreateReFungibleData> for CreateItemData {406 fn from(item: CreateReFungibleData) -> Self {407 CreateItemData::ReFungible(item)408 }409}410411impl From<CreateFungibleData> for CreateItemData {412 fn from(item: CreateFungibleData) -> Self {413 CreateItemData::Fungible(item)414 }415}