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;3233pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {34 10_000_00035} else {36 1037};38pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {39 100_00040} else {41 1042};43pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {44 204845} else {46 1047};48pub const COLLECTION_ADMINS_LIMIT: u32 = 5;49pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;50pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {51 1_000_00052} else {53 1054};555657pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;58pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;59pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;606162pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;63pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;64pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;6566pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;67pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;68pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;69707172pub const MAX_ITEMS_PER_BATCH: u32 = 200;7374parameter_types! {75 pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;76}7778#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]79#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]80pub struct CollectionId(pub u32);81impl EncodeLike<u32> for CollectionId {}82impl EncodeLike<CollectionId> for u32 {}8384#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default, TypeInfo)]85#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]86pub struct TokenId(pub u32);87impl EncodeLike<u32> for TokenId {}88impl EncodeLike<TokenId> for u32 {}8990impl TokenId {91 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {92 self.093 .checked_add(1)94 .ok_or(ArithmeticError::Overflow)95 .map(Self)96 }97}9899impl From<TokenId> for U256 {100 fn from(t: TokenId) -> Self {101 t.0.into()102 }103}104105impl TryFrom<U256> for TokenId {106 type Error = &'static str;107108 fn try_from(value: U256) -> Result<Self, Self::Error> {109 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))110 }111}112113pub struct OverflowError;114impl From<OverflowError> for &'static str {115 fn from(_: OverflowError) -> Self {116 "overflow occured"117 }118}119120pub type DecimalPoints = u8;121122#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]123#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]124pub enum CollectionMode {125 NFT,126 127 Fungible(DecimalPoints),128 ReFungible,129}130131impl CollectionMode {132 pub fn id(&self) -> u8 {133 match self {134 CollectionMode::NFT => 1,135 CollectionMode::Fungible(_) => 2,136 CollectionMode::ReFungible => 3,137 }138 }139}140141pub trait SponsoringResolve<AccountId, Call> {142 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;143}144145#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]146#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]147pub enum AccessMode {148 Normal,149 AllowList,150}151impl Default for AccessMode {152 fn default() -> Self {153 Self::Normal154 }155}156157#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo)]158#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]159pub enum SchemaVersion {160 ImageURL,161 Unique,162}163impl Default for SchemaVersion {164 fn default() -> Self {165 Self::ImageURL166 }167}168169#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]170#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]171pub struct Ownership<AccountId> {172 pub owner: AccountId,173 pub fraction: u128,174}175176#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]177#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]178pub enum SponsorshipState<AccountId> {179 180 Disabled,181 Unconfirmed(AccountId),182 183 Confirmed(AccountId),184}185186impl<AccountId> SponsorshipState<AccountId> {187 pub fn sponsor(&self) -> Option<&AccountId> {188 match self {189 Self::Confirmed(sponsor) => Some(sponsor),190 _ => None,191 }192 }193194 pub fn pending_sponsor(&self) -> Option<&AccountId> {195 match self {196 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),197 _ => None,198 }199 }200201 pub fn confirmed(&self) -> bool {202 matches!(self, Self::Confirmed(_))203 }204}205206impl<T> Default for SponsorshipState<T> {207 fn default() -> Self {208 Self::Disabled209 }210}211212#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]213#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]214pub struct Collection<T: frame_system::Config> {215 pub owner: T::AccountId,216 pub mode: CollectionMode,217 pub access: AccessMode,218 pub name: Vec<u16>, 219 pub description: Vec<u16>, 220 pub token_prefix: Vec<u8>, 221 pub mint_mode: bool,222 pub offchain_schema: Vec<u8>,223 pub schema_version: SchemaVersion,224 pub sponsorship: SponsorshipState<T::AccountId>,225 pub limits: CollectionLimits, 226 pub variable_on_chain_schema: Vec<u8>, 227 pub const_on_chain_schema: Vec<u8>, 228 pub meta_update_permission: MetaUpdatePermission,229}230231#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]232#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]233pub struct NftItemType<AccountId> {234 pub owner: AccountId,235 pub const_data: Vec<u8>,236 pub variable_data: Vec<u8>,237}238239#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]240#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]241pub struct FungibleItemType {242 pub value: u128,243}244245#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]246#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]247pub struct ReFungibleItemType<AccountId> {248 pub owner: Vec<Ownership<AccountId>>,249 pub const_data: Vec<u8>,250 pub variable_data: Vec<u8>,251}252253#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]254#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]255pub struct CollectionLimits {256 pub account_token_ownership_limit: Option<u32>,257 pub sponsored_data_size: Option<u32>,258 259 260 261 pub sponsored_data_rate_limit: Option<u32>,262 pub token_limit: Option<u32>,263264 265 pub sponsor_transfer_timeout: Option<u32>,266 pub owner_can_transfer: Option<bool>,267 pub owner_can_destroy: Option<bool>,268 pub transfers_enabled: Option<bool>,269}270271impl CollectionLimits {272 pub fn account_token_ownership_limit(&self) -> u32 {273 self.account_token_ownership_limit274 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)275 .min(MAX_TOKEN_OWNERSHIP)276 }277 pub fn sponsored_data_size(&self) -> u32 {278 self.sponsored_data_size279 .unwrap_or(CUSTOM_DATA_LIMIT)280 .min(CUSTOM_DATA_LIMIT)281 }282 pub fn token_limit(&self) -> u32 {283 self.token_limit284 .unwrap_or(COLLECTION_TOKEN_LIMIT)285 .min(COLLECTION_TOKEN_LIMIT)286 }287 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {288 self.sponsor_transfer_timeout289 .unwrap_or(default)290 .min(MAX_SPONSOR_TIMEOUT)291 }292 pub fn owner_can_transfer(&self) -> bool {293 self.owner_can_transfer.unwrap_or(true)294 }295 pub fn owner_can_destroy(&self) -> bool {296 self.owner_can_destroy.unwrap_or(true)297 }298 pub fn transfers_enabled(&self) -> bool {299 self.transfers_enabled.unwrap_or(true)300 }301 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {302 self.sponsored_data_rate_limit303 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))304 }305}306307308#[cfg(feature = "serde1")]309mod bounded_serde {310 use core::convert::TryFrom;311 use frame_support::{BoundedVec, traits::Get};312 use serde::{313 ser::{self, Serialize},314 de::{self, Deserialize, Error},315 };316 use sp_std::vec::Vec;317318 pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>319 where320 D: ser::Serializer,321 V: Serialize,322 {323 let vec: &Vec<_> = &value;324 vec.serialize(serializer)325 }326327 pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>328 where329 D: de::Deserializer<'de>,330 V: de::Deserialize<'de>,331 S: Get<u32>,332 {333 334 let vec = <Vec<V>>::deserialize(deserializer)?;335 let len = vec.len();336 TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))337 }338}339340#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]341#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]342#[derivative(Debug)]343pub struct CreateNftData {344 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]345 #[derivative(Debug = "ignore")]346 pub const_data: BoundedVec<u8, CustomDataLimit>,347 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]348 #[derivative(Debug = "ignore")]349 pub variable_data: BoundedVec<u8, CustomDataLimit>,350}351352#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]353#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]354pub struct CreateFungibleData {355 pub value: u128,356}357358#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]359#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]360#[derivative(Debug)]361pub struct CreateReFungibleData {362 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]363 #[derivative(Debug = "ignore")]364 pub const_data: BoundedVec<u8, CustomDataLimit>,365 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]366 #[derivative(Debug = "ignore")]367 pub variable_data: BoundedVec<u8, CustomDataLimit>,368 pub pieces: u128,369}370371#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]372#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]373pub enum MetaUpdatePermission {374 ItemOwner,375 Admin,376 None,377}378379impl Default for MetaUpdatePermission {380 fn default() -> Self {381 Self::ItemOwner382 }383}384385#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]386#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]387pub enum CreateItemData {388 NFT(CreateNftData),389 Fungible(CreateFungibleData),390 ReFungible(CreateReFungibleData),391}392393impl CreateItemData {394 pub fn data_size(&self) -> usize {395 match self {396 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),397 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),398 _ => 0,399 }400 }401}402403impl From<CreateNftData> for CreateItemData {404 fn from(item: CreateNftData) -> Self {405 CreateItemData::NFT(item)406 }407}408409impl From<CreateReFungibleData> for CreateItemData {410 fn from(item: CreateReFungibleData) -> Self {411 CreateItemData::ReFungible(item)412 }413}414415impl From<CreateFungibleData> for CreateItemData {416 fn from(item: CreateFungibleData) -> Self {417 CreateItemData::Fungible(item)418 }419}