difftreelog
doc: architectural changes
in: master
10 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -42,6 +42,7 @@
#[rpc(server)]
#[async_trait]
pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {
+ /// Get tokens owned by account
#[method(name = "unique_accountTokens")]
fn account_tokens(
&self,
@@ -49,12 +50,14 @@
account: CrossAccountId,
at: Option<BlockHash>,
) -> Result<Vec<TokenId>>;
+ /// Get tokens contained in collection
#[method(name = "unique_collectionTokens")]
fn collection_tokens(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<TokenId>>;
+ /// Check if token exists
#[method(name = "unique_tokenExists")]
fn token_exists(
&self,
@@ -62,7 +65,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<bool>;
-
+ /// Get token owner
#[method(name = "unique_tokenOwner")]
fn token_owner(
&self,
@@ -70,6 +73,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+ /// Get token owner, in case of nested token - find the parent recursively
#[method(name = "unique_topmostTokenOwner")]
fn topmost_token_owner(
&self,
@@ -77,6 +81,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+ /// Get tokens nested directly into the token
#[method(name = "unique_tokenChildren")]
fn token_children(
&self,
@@ -84,7 +89,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Vec<TokenChild>>;
-
+ /// Get collection properties
#[method(name = "unique_collectionProperties")]
fn collection_properties(
&self,
@@ -92,7 +97,7 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<Vec<Property>>;
-
+ /// Get token properties
#[method(name = "unique_tokenProperties")]
fn token_properties(
&self,
@@ -101,7 +106,7 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<Vec<Property>>;
-
+ /// Get property permissions
#[method(name = "unique_propertyPermissions")]
fn property_permissions(
&self,
@@ -109,7 +114,7 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<Vec<PropertyKeyPermission>>;
-
+ /// Get token data
#[method(name = "unique_tokenData")]
fn token_data(
&self,
@@ -118,9 +123,10 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<TokenData<CrossAccountId>>;
-
+ /// Get amount of unique collection tokens
#[method(name = "unique_totalSupply")]
fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;
+ /// Get owned amount of any user tokens
#[method(name = "unique_accountBalance")]
fn account_balance(
&self,
@@ -128,6 +134,7 @@
account: CrossAccountId,
at: Option<BlockHash>,
) -> Result<u32>;
+ /// Get owned amount of specific account token
#[method(name = "unique_balance")]
fn balance(
&self,
@@ -136,6 +143,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<String>;
+ /// Get allowed amount
#[method(name = "unique_allowance")]
fn allowance(
&self,
@@ -145,19 +153,21 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<String>;
-
+ /// Get admin list
#[method(name = "unique_adminlist")]
fn adminlist(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+ /// Get allowlist
#[method(name = "unique_allowlist")]
fn allowlist(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+ /// Check if user is allowed to use collection
#[method(name = "unique_allowed")]
fn allowed(
&self,
@@ -165,17 +175,20 @@
user: CrossAccountId,
at: Option<BlockHash>,
) -> Result<bool>;
+ /// Get last token ID created in a collection
#[method(name = "unique_lastTokenId")]
fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;
+ /// Get collection by specified ID
#[method(name = "unique_collectionById")]
fn collection_by_id(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Option<RpcCollection<AccountId>>>;
+ /// Get collection stats
#[method(name = "unique_collectionStats")]
fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;
-
+ /// Get number of blocks when sponsored transaction is available
#[method(name = "unique_nextSponsored")]
fn next_sponsored(
&self,
@@ -184,14 +197,14 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<u64>>;
-
+ /// Get effective collection limits
#[method(name = "unique_effectiveCollectionLimits")]
fn effective_collection_limits(
&self,
collection_id: CollectionId,
at: Option<BlockHash>,
) -> Result<Option<CollectionLimits>>;
-
+ /// Get total pieces of token
#[method(name = "unique_totalPieces")]
fn total_pieces(
&self,
@@ -304,6 +317,7 @@
fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;
#[method(name = "rmrk_themeNames")]
+ /// Get Base's theme names
fn theme_names(
&self,
base_id: RmrkBaseId,
@@ -311,6 +325,7 @@
) -> Result<Vec<RmrkThemeName>>;
#[method(name = "rmrk_themes")]
+ /// Get Theme info -- name, properties, and inherit flag
fn theme(
&self,
base_id: RmrkBaseId,
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -299,46 +299,48 @@
///
/// # Arguments
///
- /// * collection_id: Globally unique identifier of collection.
+ /// * collection_id: Globally unique identifier of collection that has been destroyed.
CollectionDestroyed(CollectionId),
/// New item was created.
///
/// # Arguments
///
- /// * collection_id: Id of the collection where item was created.
+ /// * collection_id: ID of the collection where the item was created.
///
- /// * item_id: Id of an item. Unique within the collection.
+ /// * item_id: ID of the item. Unique within the collection.
///
- /// * recipient: Owner of newly created item
+ /// * recipient: Owner of the newly created item.
///
- /// * amount: Always 1 for NFT
+ /// * amount: The amount of tokens that were created (always 1 for NFT).
ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),
/// Collection item was burned.
///
/// # Arguments
///
- /// * collection_id.
+ /// * collection_id: Identifier of the collection to which the burned NFT belonged.
///
/// * item_id: Identifier of burned NFT.
///
- /// * owner: which user has destroyed its tokens
+ /// * owner: Which user has destroyed their tokens.
///
- /// * amount: Always 1 for NFT
+ /// * amount: The amount of tokens that were destroyed (always 1 for NFT).
ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),
- /// Item was transferred
+ /// Item was transferred.
+ ///
+ /// # Arguments
///
- /// * collection_id: Id of collection to which item is belong
+ /// * collection_id: ID of the collection to which the item belongs.
///
- /// * item_id: Id of an item
+ /// * item_id: ID of the item trasnferred.
///
- /// * sender: Original owner of item
+ /// * sender: Original owner of the item.
///
- /// * recipient: New owner of item
+ /// * recipient: New owner of the item.
///
- /// * amount: Always 1 for NFT
+ /// * amount: The amount of tokens that were transferred (always 1 for NFT).
Transfer(
CollectionId,
TokenId,
@@ -347,6 +349,10 @@
u128,
),
+ /// Sponsoring allowance was approved.
+ ///
+ /// # Arguments
+ ///
/// * collection_id
///
/// * item_id
@@ -364,14 +370,53 @@
u128,
),
+ /// Collection property was added or edited.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose property was just set.
+ ///
+ /// * property_key: Key of the property that was just set.
CollectionPropertySet(CollectionId, PropertyKey),
+ /// Collection property was deleted.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose property was just deleted.
+ ///
+ /// * property_key: Key of the property that was just deleted.
CollectionPropertyDeleted(CollectionId, PropertyKey),
+ /// Item property was added or edited.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose token's property was just set.
+ ///
+ /// * item_id: ID of the item, whose property was just set.
+ ///
+ /// * property_key: Key of the property that was just set.
TokenPropertySet(CollectionId, TokenId, PropertyKey),
+ /// Item property was deleted.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose token's property was just deleted.
+ ///
+ /// * item_id: ID of the item, whose property was just deleted.
+ ///
+ /// * property_key: Key of the property that was just deleted.
TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),
+ /// Token property permission was added or updated for a collection.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose permissions were just set/updated.
+ ///
+ /// * property_key: Key of the property of the set/updated permission.
PropertyPermissionSet(CollectionId, PropertyKey),
}
@@ -413,26 +458,26 @@
/// Metadata flag frozen
MetadataFlagFrozen,
- /// Item not exists.
+ /// Item does not exist
TokenNotFound,
- /// Item balance not enough.
+ /// Item is balance not enough
TokenValueTooLow,
- /// Requested value more than approved.
+ /// Requested value is more than the approved
ApprovedValueTooLow,
/// Tried to approve more than owned
CantApproveMoreThanOwned,
/// Can't transfer tokens to ethereum zero address
AddressIsZero,
- /// Target collection doesn't supports this operation
+ /// Target collection doesn't support this operation
UnsupportedOperation,
- /// Not sufficient funds to perform action
+ /// Insufficient funds to perform an action
NotSufficientFounds,
- /// User not passed nesting rule
+ /// User does not satisfy the nesting rule
UserIsNotAllowedToNest,
- /// Only tokens from specific collections may nest tokens under this
+ /// Only tokens from specific collections may nest tokens under this one
SourceCollectionIsNotAllowedToNest,
/// Tried to store more data than allowed in collection field
@@ -447,7 +492,7 @@
/// Property key is too long
PropertyKeyIsTooLong,
- /// Only ASCII letters, digits, and '_', '-' are allowed
+ /// Only ASCII letters, digits, and symbols '_', '-', and '.' are allowed
InvalidCharacterInPropertyKey,
/// Empty property keys are forbidden
@@ -460,8 +505,11 @@
CollectionIsInternal,
}
+ /// The number of created collections. Essentially contains the last collection ID.
#[pallet::storage]
pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+
+ /// The number of destroyed collections
#[pallet::storage]
pub type DestroyedCollectionCount<T> =
StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
@@ -486,6 +534,7 @@
OnEmpty = up_data_structs::CollectionProperties,
>;
+ /// Token permissions of a collection
#[pallet::storage]
#[pallet::getter(fn property_permissions)]
pub type CollectionPropertyPermissions<T> = StorageMap<
@@ -495,6 +544,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of collection admins
#[pallet::storage]
pub type AdminAmount<T> = StorageMap<
Hasher = Blake2_128Concat,
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -44,6 +44,7 @@
pub mod erc;
pub mod weights;
+/// todo:doc?
pub type CreateItemData<T> = (<T as pallet_evm::account::Config>::CrossAccountId, u128);
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
@@ -78,10 +79,12 @@
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
+ /// Total amount of fungible tokens inside a collection.
#[pallet::storage]
pub type TotalSupply<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;
+ /// Amount of tokens owned by an account inside a collection.
#[pallet::storage]
pub type Balance<T: Config> = StorageNMap<
Key = (
@@ -92,6 +95,7 @@
QueryKind = ValueQuery,
>;
+ /// todo:doc
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (
pallets/nonfungible/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use erc::ERC721Events;20use evm_coder::ToLog;21use frame_support::{22 BoundedVec, ensure, fail, transactional,23 storage::with_transaction,24 pallet_prelude::DispatchResultWithPostInfo,25 pallet_prelude::Weight,26 weights::{PostDispatchInfo, Pays},27};28use up_data_structs::{29 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,30 mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey,31 PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild,32 AuxPropertyValue,33};34use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};35use pallet_common::{36 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,37 eth::collection_id_to_address,38};39use pallet_structure::{Pallet as PalletStructure, Error as StructureError};40use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};41use sp_core::H160;42use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};43use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap, collections::btree_set::BTreeSet};44use core::ops::Deref;45use codec::{Encode, Decode, MaxEncodedLen};46use scale_info::TypeInfo;4748pub use pallet::*;49use weights::WeightInfo;50#[cfg(feature = "runtime-benchmarks")]51pub mod benchmarking;52pub mod common;53pub mod erc;54pub mod weights;5556pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;57pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;5859/// Token data, stored independently from other data used to describe it.60/// Notably contains the owner account address.61#[struct_versioning::versioned(version = 2, upper)]62#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]63pub struct ItemData<CrossAccountId> {64 #[version(..2)]65 pub const_data: BoundedVec<u8, CustomDataLimit>,6667 #[version(..2)]68 pub variable_data: BoundedVec<u8, CustomDataLimit>,6970 pub owner: CrossAccountId,71}7273#[frame_support::pallet]74pub mod pallet {75 use super::*;76 use frame_support::{77 Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion,78 };79 use frame_system::pallet_prelude::*;80 use up_data_structs::{CollectionId, TokenId};81 use super::weights::WeightInfo;8283 #[pallet::error]84 pub enum Error<T> {85 /// Not Nonfungible item data used to mint in Nonfungible collection.86 NotNonfungibleDataUsedToMintFungibleCollectionToken,87 /// Used amount > 1 with NFT88 NonfungibleItemsHaveNoAmount,89 /// Unable to burn NFT with children90 CantBurnNftWithChildren,91 }9293 #[pallet::config]94 pub trait Config:95 frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config96 {97 type WeightInfo: WeightInfo;98 }99100 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);101102 #[pallet::pallet]103 #[pallet::storage_version(STORAGE_VERSION)]104 #[pallet::generate_store(pub(super) trait Store)]105 pub struct Pallet<T>(_);106107 /// Total amount of minted tokens in a collection.108 #[pallet::storage]109 pub type TokensMinted<T: Config> =110 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;111112 /// Amount of burnt tokens in a collection.113 #[pallet::storage]114 pub type TokensBurnt<T: Config> =115 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;116117 /// Token data, used to partially describe a token.118 #[pallet::storage]119 pub type TokenData<T: Config> = StorageNMap<120 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),121 Value = ItemData<T::CrossAccountId>,122 QueryKind = OptionQuery,123 >;124125 /// Key-value pairs, describing the metadata of a token.126 #[pallet::storage]127 #[pallet::getter(fn token_properties)]128 pub type TokenProperties<T: Config> = StorageNMap<129 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),130 Value = Properties,131 QueryKind = ValueQuery,132 OnEmpty = up_data_structs::TokenProperties,133 >;134135 /// Scoped, auxiliary properties of a token, primarily used for on-chain operations.136 #[pallet::storage]137 #[pallet::getter(fn token_aux_property)]138 pub type TokenAuxProperties<T: Config> = StorageNMap<139 Key = (140 Key<Twox64Concat, CollectionId>,141 Key<Twox64Concat, TokenId>,142 Key<Twox64Concat, PropertyScope>,143 Key<Twox64Concat, PropertyKey>,144 ),145 Value = AuxPropertyValue,146 QueryKind = OptionQuery,147 >;148149 /// Used to enumerate tokens owned by account.150 #[pallet::storage]151 pub type Owned<T: Config> = StorageNMap<152 Key = (153 Key<Twox64Concat, CollectionId>,154 Key<Blake2_128Concat, T::CrossAccountId>,155 Key<Twox64Concat, TokenId>,156 ),157 Value = bool,158 QueryKind = ValueQuery,159 >;160161 /// Used to enumerate token's children.162 #[pallet::storage]163 #[pallet::getter(fn token_children)]164 pub type TokenChildren<T: Config> = StorageNMap<165 Key = (166 Key<Twox64Concat, CollectionId>,167 Key<Twox64Concat, TokenId>,168 Key<Twox64Concat, (CollectionId, TokenId)>,169 ),170 Value = bool,171 QueryKind = ValueQuery,172 >;173174 /// Amount of tokens owned in a collection.s175 #[pallet::storage]176 pub type AccountBalance<T: Config> = StorageNMap<177 Key = (178 Key<Twox64Concat, CollectionId>,179 Key<Blake2_128Concat, T::CrossAccountId>,180 ),181 Value = u32,182 QueryKind = ValueQuery,183 >;184185 /// todo doc186 #[pallet::storage]187 pub type Allowance<T: Config> = StorageNMap<188 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),189 Value = T::CrossAccountId,190 QueryKind = OptionQuery,191 >;192193 /// Upgrade from the old schema to properties.194 #[pallet::hooks]195 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {196 fn on_runtime_upgrade() -> Weight {197 if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {198 let mut had_consts = BTreeSet::new();199 <TokenData<T>>::translate::<ItemDataVersion1<T::CrossAccountId>, _>(200 |(collection, token), v| {201 let mut props = vec![];202 if !v.const_data.is_empty() {203 props.push(Property {204 key: b"_old_constData".to_vec().try_into().unwrap(),205 value: v206 .const_data207 .clone()208 .into_inner()209 .try_into()210 .expect("const too long"),211 });212 had_consts.insert(collection);213 }214 if !v.variable_data.is_empty() {215 props.push(Property {216 key: b"_old_variableData".to_vec().try_into().unwrap(),217 value: v218 .variable_data219 .clone()220 .into_inner()221 .try_into()222 .expect("variable too long"),223 })224 }225 if !props.is_empty() {226 Self::set_scoped_token_properties(227 collection,228 token,229 PropertyScope::None,230 props.into_iter(),231 )232 .expect("existing token data exceeds property storage");233 }234 Some(<ItemDataVersion2<T::CrossAccountId>>::from(v))235 },236 );237 for collection in had_consts {238 <PalletCommon<T>>::set_property_permission_unchecked(239 collection,240 PropertyKeyPermission {241 key: b"_old_constData".to_vec().try_into().unwrap(),242 permission: PropertyPermission {243 mutable: false,244 collection_admin: true,245 token_owner: false,246 },247 },248 )249 .expect("failed to configure permission");250 }251 }252253 0254 }255 }256}257258pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);259impl<T: Config> NonfungibleHandle<T> {260 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {261 Self(inner)262 }263 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {264 self.0265 }266 pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {267 &mut self.0268 }269}270impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {271 fn recorder(&self) -> &SubstrateRecorder<T> {272 self.0.recorder()273 }274 fn into_recorder(self) -> SubstrateRecorder<T> {275 self.0.into_recorder()276 }277}278impl<T: Config> Deref for NonfungibleHandle<T> {279 type Target = pallet_common::CollectionHandle<T>;280281 fn deref(&self) -> &Self::Target {282 &self.0283 }284}285286impl<T: Config> Pallet<T> {287 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {288 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)289 }290 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {291 <TokenData<T>>::contains_key((collection.id, token))292 }293294 pub fn set_scoped_token_property(295 collection_id: CollectionId,296 token_id: TokenId,297 scope: PropertyScope,298 property: Property,299 ) -> DispatchResult {300 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {301 properties.try_scoped_set(scope, property.key, property.value)302 })303 .map_err(<CommonError<T>>::from)?;304305 Ok(())306 }307308 pub fn set_scoped_token_properties(309 collection_id: CollectionId,310 token_id: TokenId,311 scope: PropertyScope,312 properties: impl Iterator<Item = Property>,313 ) -> DispatchResult {314 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {315 stored_properties.try_scoped_set_from_iter(scope, properties)316 })317 .map_err(<CommonError<T>>::from)?;318319 Ok(())320 }321322 pub fn try_mutate_token_aux_property<R, E>(323 collection_id: CollectionId,324 token_id: TokenId,325 scope: PropertyScope,326 key: PropertyKey,327 f: impl FnOnce(&mut Option<AuxPropertyValue>) -> Result<R, E>,328 ) -> Result<R, E> {329 <TokenAuxProperties<T>>::try_mutate((collection_id, token_id, scope, key), f)330 }331332 pub fn remove_token_aux_property(333 collection_id: CollectionId,334 token_id: TokenId,335 scope: PropertyScope,336 key: PropertyKey,337 ) {338 <TokenAuxProperties<T>>::remove((collection_id, token_id, scope, key));339 }340341 pub fn iterate_token_aux_properties(342 collection_id: CollectionId,343 token_id: TokenId,344 scope: PropertyScope,345 ) -> impl Iterator<Item = (PropertyKey, AuxPropertyValue)> {346 <TokenAuxProperties<T>>::iter_prefix((collection_id, token_id, scope))347 }348349 pub fn current_token_id(collection_id: CollectionId) -> TokenId {350 TokenId(<TokensMinted<T>>::get(collection_id))351 }352}353354// unchecked calls skips any permission checks355impl<T: Config> Pallet<T> {356 pub fn init_collection(357 owner: T::CrossAccountId,358 data: CreateCollectionData<T::AccountId>,359 is_external: bool,360 ) -> Result<CollectionId, DispatchError> {361 <PalletCommon<T>>::init_collection(owner, data, is_external)362 }363 pub fn destroy_collection(364 collection: NonfungibleHandle<T>,365 sender: &T::CrossAccountId,366 ) -> DispatchResult {367 let id = collection.id;368369 if Self::collection_has_tokens(id) {370 return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());371 }372373 // =========374375 PalletCommon::destroy_collection(collection.0, sender)?;376377 <TokenData<T>>::remove_prefix((id,), None);378 <TokenChildren<T>>::remove_prefix((id,), None);379 <Owned<T>>::remove_prefix((id,), None);380 <TokensMinted<T>>::remove(id);381 <TokensBurnt<T>>::remove(id);382 <Allowance<T>>::remove_prefix((id,), None);383 <AccountBalance<T>>::remove_prefix((id,), None);384 Ok(())385 }386387 pub fn burn(388 collection: &NonfungibleHandle<T>,389 sender: &T::CrossAccountId,390 token: TokenId,391 ) -> DispatchResult {392 let token_data =393 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;394 ensure!(&token_data.owner == sender, <CommonError<T>>::NoPermission);395396 if collection.permissions.access() == AccessMode::AllowList {397 collection.check_allowlist(sender)?;398 }399400 if Self::token_has_children(collection.id, token) {401 return Err(<Error<T>>::CantBurnNftWithChildren.into());402 }403404 let burnt = <TokensBurnt<T>>::get(collection.id)405 .checked_add(1)406 .ok_or(ArithmeticError::Overflow)?;407408 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))409 .checked_sub(1)410 .ok_or(ArithmeticError::Overflow)?;411412 // =========413414 if balance == 0 {415 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));416 } else {417 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);418 }419420 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);421422 <Owned<T>>::remove((collection.id, &token_data.owner, token));423 <TokensBurnt<T>>::insert(collection.id, burnt);424 <TokenData<T>>::remove((collection.id, token));425 <TokenProperties<T>>::remove((collection.id, token));426 <TokenAuxProperties<T>>::remove_prefix((collection.id, token), None);427 let old_spender = <Allowance<T>>::take((collection.id, token));428429 if let Some(old_spender) = old_spender {430 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(431 collection.id,432 token,433 token_data.owner.clone(),434 old_spender,435 0,436 ));437 }438439 <PalletEvm<T>>::deposit_log(440 ERC721Events::Transfer {441 from: *token_data.owner.as_eth(),442 to: H160::default(),443 token_id: token.into(),444 }445 .to_log(collection_id_to_address(collection.id)),446 );447 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(448 collection.id,449 token,450 token_data.owner,451 1,452 ));453 Ok(())454 }455456 #[transactional]457 pub fn burn_recursively(458 collection: &NonfungibleHandle<T>,459 sender: &T::CrossAccountId,460 token: TokenId,461 self_budget: &dyn Budget,462 breadth_budget: &dyn Budget,463 ) -> DispatchResultWithPostInfo {464 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);465466 let current_token_account =467 T::CrossTokenAddressMapping::token_to_address(collection.id, token);468469 let mut weight = 0 as Weight;470471 // This method is transactional, if user in fact doesn't have permissions to remove token -472 // tokens removed here will be restored after rejected transaction473 for ((collection, token), _) in <TokenChildren<T>>::iter_prefix((collection.id, token)) {474 ensure!(breadth_budget.consume(), <StructureError<T>>::BreadthLimit,);475 let PostDispatchInfo { actual_weight, .. } =476 <PalletStructure<T>>::burn_item_recursively(477 current_token_account.clone(),478 collection,479 token,480 self_budget,481 breadth_budget,482 )?;483 if let Some(actual_weight) = actual_weight {484 weight = weight.saturating_add(actual_weight);485 }486 }487488 Self::burn(collection, sender, token)?;489 DispatchResultWithPostInfo::Ok(PostDispatchInfo {490 actual_weight: Some(weight + <SelfWeightOf<T>>::burn_item()),491 pays_fee: Pays::Yes,492 })493 }494495 #[transactional]496 fn modify_token_properties(497 collection: &NonfungibleHandle<T>,498 sender: &T::CrossAccountId,499 token_id: TokenId,500 properties: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,501 is_token_create: bool,502 nesting_budget: &dyn Budget,503 ) -> DispatchResult {504 let mut collection_admin_status = None;505 let mut token_owner_result = None;506507 let mut is_collection_admin =508 || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));509510 let mut is_token_owner = || {511 *token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {512 let is_owned = <PalletStructure<T>>::check_indirectly_owned(513 sender.clone(),514 collection.id,515 token_id,516 None,517 nesting_budget,518 )?;519520 Ok(is_owned)521 })522 };523524 for (key, value) in properties {525 let permission = <PalletCommon<T>>::property_permissions(collection.id)526 .get(&key)527 .cloned()528 .unwrap_or_else(PropertyPermission::none);529530 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))531 .get(&key)532 .is_some();533534 match permission {535 PropertyPermission { mutable: false, .. } if is_property_exists => {536 return Err(<CommonError<T>>::NoPermission.into());537 }538539 PropertyPermission {540 collection_admin,541 token_owner,542 ..543 } => {544 //TODO: investigate threats during public minting.545 if is_token_create && (collection_admin || token_owner) && value.is_some() {546 // Pass547 } else if collection_admin && is_collection_admin() {548 // Pass549 } else if token_owner && is_token_owner()? {550 // Pass551 } else {552 fail!(<CommonError<T>>::NoPermission);553 }554 }555 }556557 match value {558 Some(value) => {559 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {560 properties.try_set(key.clone(), value)561 })562 .map_err(<CommonError<T>>::from)?;563564 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(565 collection.id,566 token_id,567 key,568 ));569 }570 None => {571 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {572 properties.remove(&key)573 })574 .map_err(<CommonError<T>>::from)?;575576 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(577 collection.id,578 token_id,579 key,580 ));581 }582 }583 }584585 Ok(())586 }587588 pub fn set_token_properties(589 collection: &NonfungibleHandle<T>,590 sender: &T::CrossAccountId,591 token_id: TokenId,592 properties: impl Iterator<Item = Property>,593 is_token_create: bool,594 nesting_budget: &dyn Budget,595 ) -> DispatchResult {596 Self::modify_token_properties(597 collection,598 sender,599 token_id,600 properties.map(|p| (p.key, Some(p.value))),601 is_token_create,602 nesting_budget,603 )604 }605606 pub fn set_token_property(607 collection: &NonfungibleHandle<T>,608 sender: &T::CrossAccountId,609 token_id: TokenId,610 property: Property,611 nesting_budget: &dyn Budget,612 ) -> DispatchResult {613 let is_token_create = false;614615 Self::set_token_properties(616 collection,617 sender,618 token_id,619 [property].into_iter(),620 is_token_create,621 nesting_budget,622 )623 }624625 pub fn delete_token_properties(626 collection: &NonfungibleHandle<T>,627 sender: &T::CrossAccountId,628 token_id: TokenId,629 property_keys: impl Iterator<Item = PropertyKey>,630 nesting_budget: &dyn Budget,631 ) -> DispatchResult {632 let is_token_create = false;633634 Self::modify_token_properties(635 collection,636 sender,637 token_id,638 property_keys.into_iter().map(|key| (key, None)),639 is_token_create,640 nesting_budget,641 )642 }643644 pub fn delete_token_property(645 collection: &NonfungibleHandle<T>,646 sender: &T::CrossAccountId,647 token_id: TokenId,648 property_key: PropertyKey,649 nesting_budget: &dyn Budget,650 ) -> DispatchResult {651 Self::delete_token_properties(652 collection,653 sender,654 token_id,655 [property_key].into_iter(),656 nesting_budget,657 )658 }659660 pub fn set_collection_properties(661 collection: &NonfungibleHandle<T>,662 sender: &T::CrossAccountId,663 properties: Vec<Property>,664 ) -> DispatchResult {665 <PalletCommon<T>>::set_collection_properties(collection, sender, properties)666 }667668 pub fn delete_collection_properties(669 collection: &CollectionHandle<T>,670 sender: &T::CrossAccountId,671 property_keys: Vec<PropertyKey>,672 ) -> DispatchResult {673 <PalletCommon<T>>::delete_collection_properties(collection, sender, property_keys)674 }675676 pub fn set_token_property_permissions(677 collection: &CollectionHandle<T>,678 sender: &T::CrossAccountId,679 property_permissions: Vec<PropertyKeyPermission>,680 ) -> DispatchResult {681 <PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)682 }683684 pub fn set_property_permission(685 collection: &CollectionHandle<T>,686 sender: &T::CrossAccountId,687 permission: PropertyKeyPermission,688 ) -> DispatchResult {689 <PalletCommon<T>>::set_property_permission(collection, sender, permission)690 }691692 pub fn transfer(693 collection: &NonfungibleHandle<T>,694 from: &T::CrossAccountId,695 to: &T::CrossAccountId,696 token: TokenId,697 nesting_budget: &dyn Budget,698 ) -> DispatchResult {699 ensure!(700 collection.limits.transfers_enabled(),701 <CommonError<T>>::TransferNotAllowed702 );703704 let token_data =705 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;706 ensure!(&token_data.owner == from, <CommonError<T>>::NoPermission);707708 if collection.permissions.access() == AccessMode::AllowList {709 collection.check_allowlist(from)?;710 collection.check_allowlist(to)?;711 }712 <PalletCommon<T>>::ensure_correct_receiver(to)?;713714 let balance_from = <AccountBalance<T>>::get((collection.id, from))715 .checked_sub(1)716 .ok_or(<CommonError<T>>::TokenValueTooLow)?;717 let balance_to = if from != to {718 let balance_to = <AccountBalance<T>>::get((collection.id, to))719 .checked_add(1)720 .ok_or(ArithmeticError::Overflow)?;721722 ensure!(723 balance_to < collection.limits.account_token_ownership_limit(),724 <CommonError<T>>::AccountTokenLimitExceeded,725 );726727 Some(balance_to)728 } else {729 None730 };731732 <PalletStructure<T>>::nest_if_sent_to_token(733 from.clone(),734 to,735 collection.id,736 token,737 nesting_budget,738 )?;739740 // =========741742 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);743744 <TokenData<T>>::insert(745 (collection.id, token),746 ItemData {747 owner: to.clone(),748 ..token_data749 },750 );751752 if let Some(balance_to) = balance_to {753 // from != to754 if balance_from == 0 {755 <AccountBalance<T>>::remove((collection.id, from));756 } else {757 <AccountBalance<T>>::insert((collection.id, from), balance_from);758 }759 <AccountBalance<T>>::insert((collection.id, to), balance_to);760 <Owned<T>>::remove((collection.id, from, token));761 <Owned<T>>::insert((collection.id, to, token), true);762 }763 Self::set_allowance_unchecked(collection, from, token, None, true);764765 <PalletEvm<T>>::deposit_log(766 ERC721Events::Transfer {767 from: *from.as_eth(),768 to: *to.as_eth(),769 token_id: token.into(),770 }771 .to_log(collection_id_to_address(collection.id)),772 );773 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(774 collection.id,775 token,776 from.clone(),777 to.clone(),778 1,779 ));780 Ok(())781 }782783 pub fn create_multiple_items(784 collection: &NonfungibleHandle<T>,785 sender: &T::CrossAccountId,786 data: Vec<CreateItemData<T>>,787 nesting_budget: &dyn Budget,788 ) -> DispatchResult {789 if !collection.is_owner_or_admin(sender) {790 ensure!(791 collection.permissions.mint_mode(),792 <CommonError<T>>::PublicMintingNotAllowed793 );794 collection.check_allowlist(sender)?;795796 for item in data.iter() {797 collection.check_allowlist(&item.owner)?;798 }799 }800801 for data in data.iter() {802 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;803 }804805 let first_token = <TokensMinted<T>>::get(collection.id);806 let tokens_minted = first_token807 .checked_add(data.len() as u32)808 .ok_or(ArithmeticError::Overflow)?;809 ensure!(810 tokens_minted <= collection.limits.token_limit(),811 <CommonError<T>>::CollectionTokenLimitExceeded812 );813814 let mut balances = BTreeMap::new();815 for data in &data {816 let balance = balances817 .entry(&data.owner)818 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));819 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;820821 ensure!(822 *balance <= collection.limits.account_token_ownership_limit(),823 <CommonError<T>>::AccountTokenLimitExceeded,824 );825 }826827 for (i, data) in data.iter().enumerate() {828 let token = TokenId(first_token + i as u32 + 1);829830 <PalletStructure<T>>::check_nesting(831 sender.clone(),832 &data.owner,833 collection.id,834 token,835 nesting_budget,836 )?;837 }838839 // =========840841 with_transaction(|| {842 for (i, data) in data.iter().enumerate() {843 let token = first_token + i as u32 + 1;844845 <TokenData<T>>::insert(846 (collection.id, token),847 ItemData {848 // const_data: data.const_data.clone(),849 owner: data.owner.clone(),850 },851 );852853 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(854 &data.owner,855 collection.id,856 TokenId(token),857 );858859 if let Err(e) = Self::set_token_properties(860 collection,861 sender,862 TokenId(token),863 data.properties.clone().into_iter(),864 true,865 nesting_budget,866 ) {867 return TransactionOutcome::Rollback(Err(e));868 }869 }870 TransactionOutcome::Commit(Ok(()))871 })?;872873 <TokensMinted<T>>::insert(collection.id, tokens_minted);874 for (account, balance) in balances {875 <AccountBalance<T>>::insert((collection.id, account), balance);876 }877 for (i, data) in data.into_iter().enumerate() {878 let token = first_token + i as u32 + 1;879 <Owned<T>>::insert((collection.id, &data.owner, token), true);880881 <PalletEvm<T>>::deposit_log(882 ERC721Events::Transfer {883 from: H160::default(),884 to: *data.owner.as_eth(),885 token_id: token.into(),886 }887 .to_log(collection_id_to_address(collection.id)),888 );889 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(890 collection.id,891 TokenId(token),892 data.owner.clone(),893 1,894 ));895 }896 Ok(())897 }898899 pub fn set_allowance_unchecked(900 collection: &NonfungibleHandle<T>,901 sender: &T::CrossAccountId,902 token: TokenId,903 spender: Option<&T::CrossAccountId>,904 assume_implicit_eth: bool,905 ) {906 if let Some(spender) = spender {907 let old_spender = <Allowance<T>>::get((collection.id, token));908 <Allowance<T>>::insert((collection.id, token), spender);909 // In ERC721 there is only one possible approved user of token, so we set910 // approved user to spender911 <PalletEvm<T>>::deposit_log(912 ERC721Events::Approval {913 owner: *sender.as_eth(),914 approved: *spender.as_eth(),915 token_id: token.into(),916 }917 .to_log(collection_id_to_address(collection.id)),918 );919 // In Unique chain, any token can have any amount of approved users, so we need to920 // set allowance of old owner to 0, and allowance of new owner to 1921 if old_spender.as_ref() != Some(spender) {922 if let Some(old_owner) = old_spender {923 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(924 collection.id,925 token,926 sender.clone(),927 old_owner,928 0,929 ));930 }931 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(932 collection.id,933 token,934 sender.clone(),935 spender.clone(),936 1,937 ));938 }939 } else {940 let old_spender = <Allowance<T>>::take((collection.id, token));941 if !assume_implicit_eth {942 // In ERC721 there is only one possible approved user of token, so we set943 // approved user to zero address944 <PalletEvm<T>>::deposit_log(945 ERC721Events::Approval {946 owner: *sender.as_eth(),947 approved: H160::default(),948 token_id: token.into(),949 }950 .to_log(collection_id_to_address(collection.id)),951 );952 }953 // In Unique chain, any token can have any amount of approved users, so we need to954 // set allowance of old owner to 0955 if let Some(old_spender) = old_spender {956 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(957 collection.id,958 token,959 sender.clone(),960 old_spender,961 0,962 ));963 }964 }965 }966967 pub fn set_allowance(968 collection: &NonfungibleHandle<T>,969 sender: &T::CrossAccountId,970 token: TokenId,971 spender: Option<&T::CrossAccountId>,972 ) -> DispatchResult {973 if collection.permissions.access() == AccessMode::AllowList {974 collection.check_allowlist(sender)?;975 if let Some(spender) = spender {976 collection.check_allowlist(spender)?;977 }978 }979980 if let Some(spender) = spender {981 <PalletCommon<T>>::ensure_correct_receiver(spender)?;982 }983984 let token_data =985 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;986 if &token_data.owner != sender {987 ensure!(988 collection.ignores_owned_amount(sender),989 <CommonError<T>>::CantApproveMoreThanOwned990 );991 }992993 // =========994995 Self::set_allowance_unchecked(collection, sender, token, spender, false);996 Ok(())997 }998999 fn check_allowed(1000 collection: &NonfungibleHandle<T>,1001 spender: &T::CrossAccountId,1002 from: &T::CrossAccountId,1003 token: TokenId,1004 nesting_budget: &dyn Budget,1005 ) -> DispatchResult {1006 if spender.conv_eq(from) {1007 return Ok(());1008 }1009 if collection.permissions.access() == AccessMode::AllowList {1010 // `from`, `to` checked in [`transfer`]1011 collection.check_allowlist(spender)?;1012 }10131014 if collection.limits.owner_can_transfer() && collection.is_owner_or_admin(spender) {1015 return Ok(());1016 }10171018 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1019 ensure!(1020 <PalletStructure<T>>::check_indirectly_owned(1021 spender.clone(),1022 source.0,1023 source.1,1024 None,1025 nesting_budget1026 )?,1027 <CommonError<T>>::ApprovedValueTooLow,1028 );1029 return Ok(());1030 }1031 if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {1032 return Ok(());1033 }1034 ensure!(1035 collection.ignores_allowance(spender),1036 <CommonError<T>>::ApprovedValueTooLow1037 );1038 Ok(())1039 }10401041 pub fn transfer_from(1042 collection: &NonfungibleHandle<T>,1043 spender: &T::CrossAccountId,1044 from: &T::CrossAccountId,1045 to: &T::CrossAccountId,1046 token: TokenId,1047 nesting_budget: &dyn Budget,1048 ) -> DispatchResult {1049 Self::check_allowed(collection, spender, from, token, nesting_budget)?;10501051 // =========10521053 // Allowance is reset in [`transfer`]1054 Self::transfer(collection, from, to, token, nesting_budget)1055 }10561057 pub fn burn_from(1058 collection: &NonfungibleHandle<T>,1059 spender: &T::CrossAccountId,1060 from: &T::CrossAccountId,1061 token: TokenId,1062 nesting_budget: &dyn Budget,1063 ) -> DispatchResult {1064 Self::check_allowed(collection, spender, from, token, nesting_budget)?;10651066 // =========10671068 Self::burn(collection, from, token)1069 }10701071 pub fn check_nesting(1072 handle: &NonfungibleHandle<T>,1073 sender: T::CrossAccountId,1074 from: (CollectionId, TokenId),1075 under: TokenId,1076 nesting_budget: &dyn Budget,1077 ) -> DispatchResult {1078 let nesting = handle.permissions.nesting();10791080 #[cfg(not(feature = "runtime-benchmarks"))]1081 let permissive = false;1082 #[cfg(feature = "runtime-benchmarks")]1083 let permissive = nesting.permissive;10841085 if permissive {1086 // Pass1087 } else if nesting.token_owner1088 && <PalletStructure<T>>::check_indirectly_owned(1089 sender.clone(),1090 handle.id,1091 under,1092 Some(from),1093 nesting_budget,1094 )? {1095 // Pass1096 } else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {1097 // Pass1098 } else {1099 fail!(<CommonError<T>>::UserIsNotAllowedToNest);1100 }11011102 if let Some(whitelist) = &nesting.restricted {1103 ensure!(1104 whitelist.contains(&from.0),1105 <CommonError<T>>::SourceCollectionIsNotAllowedToNest1106 );1107 }1108 Ok(())1109 }11101111 fn nest(under: (CollectionId, TokenId), to_nest: (CollectionId, TokenId)) {1112 <TokenChildren<T>>::insert((under.0, under.1, (to_nest.0, to_nest.1)), true);1113 }11141115 fn unnest(under: (CollectionId, TokenId), to_unnest: (CollectionId, TokenId)) {1116 <TokenChildren<T>>::remove((under.0, under.1, to_unnest));1117 }11181119 fn collection_has_tokens(collection_id: CollectionId) -> bool {1120 <TokenData<T>>::iter_prefix((collection_id,))1121 .next()1122 .is_some()1123 }11241125 fn token_has_children(collection_id: CollectionId, token_id: TokenId) -> bool {1126 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1127 .next()1128 .is_some()1129 }11301131 pub fn token_children_ids(collection_id: CollectionId, token_id: TokenId) -> Vec<TokenChild> {1132 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1133 .map(|((child_collection_id, child_id), _)| TokenChild {1134 collection: child_collection_id,1135 token: child_id,1136 })1137 .collect()1138 }11391140 /// Delegated to `create_multiple_items`1141 pub fn create_item(1142 collection: &NonfungibleHandle<T>,1143 sender: &T::CrossAccountId,1144 data: CreateItemData<T>,1145 nesting_budget: &dyn Budget,1146 ) -> DispatchResult {1147 Self::create_multiple_items(collection, sender, vec![data], nesting_budget)1148 }1149}pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -38,6 +38,8 @@
pub mod weights;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
+/// Token data, stored independently from other data used to describe it.
+/// Notably contains the token metadata.
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
pub struct ItemData {
@@ -86,13 +88,17 @@
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
+ /// Total amount of minted tokens in a collection.
#[pallet::storage]
pub type TokensMinted<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+
+ /// Amount of tokens burnt in a collection.
#[pallet::storage]
pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+ /// Token data, used to partially describe a token.
#[pallet::storage]
pub type TokenData<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -100,6 +106,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of pieces a refungible token is split into.
#[pallet::storage]
pub type TotalSupply<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -107,7 +114,7 @@
QueryKind = ValueQuery,
>;
- /// Used to enumerate tokens owned by account
+ /// Used to enumerate tokens owned by account.
#[pallet::storage]
pub type Owned<T: Config> = StorageNMap<
Key = (
@@ -119,6 +126,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of tokens (not pieces) partially owned by an account within a collection.
#[pallet::storage]
pub type AccountBalance<T: Config> = StorageNMap<
Key = (
@@ -130,6 +138,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of pieces of a token owned by an account.
#[pallet::storage]
pub type Balance<T: Config> = StorageNMap<
Key = (
@@ -142,6 +151,7 @@
QueryKind = ValueQuery,
>;
+ /// todo:doc
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (
@@ -248,7 +258,7 @@
// TODO: ERC721 transfer event
Ok(())
}
-
+
pub fn burn(
collection: &RefungibleHandle<T>,
owner: &T::CrossAccountId,
@@ -595,6 +605,7 @@
Ok(())
}
+ /// todo:doc oh look, a precedent. not pub, too. but it has an unclear use-case.
/// Returns allowance, which should be set after transaction
fn check_allowed(
collection: &RefungibleHandle<T>,
pallets/scheduler/src/lib.rsdiffbeforeafterboth--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -258,6 +258,7 @@
/// A Scheduler-Runtime interface for finer payment handling.
pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {
+ /// Reserve the maximum spendings on a call.
fn reserve_balance(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -265,6 +266,7 @@
count: u32,
) -> Result<(), DispatchError>;
+ /// Pay for call dispatch (un-reserve) from the reserved funds, returning the change.
fn pay_for_call(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -280,6 +282,7 @@
TransactionValidityError,
>;
+ /// Release reserved funds.
fn cancel_reserve(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -25,19 +25,19 @@
#[pallet::error]
pub enum Error<T> {
- /// While searched for owner, got already checked account
+ /// While searching for the owner, encountered an already checked account, detecting a loop.
OuroborosDetected,
- /// While searched for owner, encountered depth limit
+ /// While searching for the owner, reached the depth limit.
DepthLimit,
- /// While iterating over children, encountered breadth limit
+ /// While iterating over children, reached the breadth limit.
BreadthLimit,
- /// While searched for owner, found token owner by not-yet-existing token
+ /// Couldn't find the token owner that is a token. Perhaps, it does not yet exist. todo:doc? rephrase?
TokenNotFound,
}
#[pallet::event]
pub enum Event<T> {
- /// Executed call on behalf of token
+ /// Executed call on behalf of the token.
Executed(DispatchResult),
}
@@ -73,11 +73,11 @@
#[derive(PartialEq)]
pub enum Parent<CrossAccountId> {
- /// Token owned by normal account
+ /// Token owned by a normal account.
User(CrossAccountId),
- /// Passed token not found
+ /// Could not find the token provided as the owner.
TokenNotFound,
- /// Token owner is another token (target token still may not exist)
+ /// Token owner is another token (still, the target token may not exist).
Token(CollectionId, TokenId),
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -101,7 +101,7 @@
/// * admin: Admin address.
CollectionAdminAdded(CollectionId, CrossAccountId),
- /// Collection owned was change
+ /// Collection owned was changed
///
/// # Arguments
///
@@ -137,7 +137,7 @@
/// * admin: Admin address.
CollectionAdminRemoved(CollectionId, CrossAccountId),
- /// Address was remove from allow list
+ /// Address was removed from the allow list
///
/// # Arguments
///
@@ -146,7 +146,7 @@
/// * user: Address.
AllowListAddressRemoved(CollectionId, CrossAccountId),
- /// Address was add to allow list
+ /// Address was added to the allow list
///
/// # Arguments
///
@@ -155,13 +155,18 @@
/// * user: Address.
AllowListAddressAdded(CollectionId, CrossAccountId),
- /// Collection limits was set
+ /// Collection limits were set
///
/// # Arguments
///
/// * collection_id: Globally unique collection identifier.
CollectionLimitSet(CollectionId),
+ /// Collection permissions were set
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: Globally unique collection identifier.
CollectionPermissionSet(CollectionId),
}
}
@@ -198,7 +203,7 @@
ChainVersion: u64;
//#endregion
- //#region Tokens transfer rate limit baskets
+ //#region Tokens transfer sponosoring rate limit baskets
/// (Collection id (controlled?2), who created (real))
/// TODO: Off chain worker should remove from this map when collection gets removed
pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;
@@ -214,11 +219,14 @@
/// Collection id (controlled?2), token id (controlled?2)
#[deprecated]
pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+ /// Last sponsoring of token property setting // todo:doc rephrase this and the following
pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
- /// Approval sponsoring
+ /// Last sponsoring of NFT approval in a collection
pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+ /// Last sponsoring of fungible tokens approval in a collection
pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
+ /// Last sponsoring of RFT approval in a collection
pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
}
}
@@ -278,9 +286,16 @@
Self::create_collection_ex(origin, data)
}
- /// This method creates a collection
+ /// Create a collection with explicit parameters.
+ /// Prefer it to the deprecated [`created_collection`] method.
+ ///
+ /// # Permissions
+ ///
+ /// * Anyone.
///
- /// Prefer it to deprecated [`created_collection`] method
+ /// # Arguments
+ ///
+ /// * data: explicit create-collection data.
#[weight = <SelfWeightOf<T>>::create_collection()]
#[transactional]
pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
@@ -293,11 +308,11 @@
Ok(())
}
- /// Destroys collection if no tokens within this collection
+ /// Destroy the collection if no tokens exist within.
///
/// # Permissions
///
- /// * Collection Owner.
+ /// * Collection Owner
///
/// # Arguments
///
@@ -398,7 +413,7 @@
///
/// # Permissions
///
- /// * Collection Owner.
+ /// * Collection Owner
///
/// # Arguments
///
@@ -424,40 +439,40 @@
target_collection.save()
}
- /// Adds an admin of the Collection.
+ /// Adds an admin of the collection.
/// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
+ /// * Collection Owner
+ /// * Collection Admin
///
/// # Arguments
///
/// * collection_id: ID of the Collection to add admin for.
///
- /// * new_admin_id: Address of new admin to add.
+ /// * new_admin: Address of new admin to add.
#[weight = <SelfWeightOf<T>>::add_collection_admin()]
#[transactional]
- pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {
+ pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin: T::CrossAccountId) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
collection.check_is_internal()?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(
collection_id,
- new_admin_id.clone()
+ new_admin.clone()
));
- <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)
+ <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, 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.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
+ /// * Collection Owner
+ /// * Collection Admin
///
/// # Arguments
///
@@ -479,9 +494,12 @@
<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)
}
+ /// Set (invite) a new collection sponsor. If successful, confirmation from the sponsor-to-be will be pending.
+ ///
/// # Permissions
///
/// * Collection Owner
+ /// * Collection Admin
///
/// # Arguments
///
@@ -507,9 +525,11 @@
target_collection.save()
}
+ /// Confirm own sponsorship of a collection.
+ ///
/// # Permissions
///
- /// * Sponsor.
+ /// * The sponsor to-be
///
/// # Arguments
///
@@ -538,7 +558,7 @@
///
/// # Permissions
///
- /// * Collection owner.
+ /// * Collection Owner
///
/// # Arguments
///
@@ -560,12 +580,12 @@
target_collection.save()
}
- /// This method creates a concrete instance of NFT Collection created with CreateCollection method.
+ /// Create a concrete instance of NFT Collection created with CreateCollection method.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
+ /// * Collection Owner
+ /// * Collection Admin
/// * Anyone if
/// * Allow List is enabled, and
/// * Address is added to allow list, and
@@ -587,12 +607,12 @@
dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))
}
- /// This method creates multiple items in a collection created with CreateCollection method.
+ /// Create multiple items in a collection created with CreateCollection method.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
+ /// * Collection Owner
+ /// * Collection Admin
/// * Anyone if
/// * Allow List is enabled, and
/// * Address is added to allow list, and
@@ -615,6 +635,18 @@
dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))
}
+ /// Add or change collection properties.
+ ///
+ /// # Permissions
+ ///
+ /// * Collection Owner
+ /// * Collection Admin
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * properties: a vector of key-value pairs stored as the collection's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.
#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]
#[transactional]
pub fn set_collection_properties(
@@ -629,6 +661,18 @@
dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))
}
+ /// Delete specified collection properties.
+ ///
+ /// # Permissions
+ ///
+ /// * Collection Owner
+ /// * Collection Admin
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * property_keys: a vector of keys of the properties to be deleted.
#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]
#[transactional]
pub fn delete_collection_properties(
@@ -643,6 +687,22 @@
dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))
}
+ /// Add or change token properties according to collection's permissions.
+ ///
+ /// # Permissions
+ ///
+ /// * Depends on collection's token property permissions and specified property mutability:
+ /// * Collection Owner
+ /// * Collection Admin
+ /// * Token Owner
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * token_id.
+ ///
+ /// * properties: a vector of key-value pairs stored as the token's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.
#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]
#[transactional]
pub fn set_token_properties(
@@ -659,6 +719,22 @@
dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))
}
+ /// Delete specified token properties.
+ ///
+ /// # Permissions
+ ///
+ /// * Depends on collection's token property permissions and specified property mutability:
+ /// * Collection Owner
+ /// * Collection Admin
+ /// * Token Owner
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * token_id.
+ ///
+ /// * property_keys: a vector of keys of the properties to be deleted.
#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]
#[transactional]
pub fn delete_token_properties(
@@ -675,6 +751,18 @@
dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))
}
+ /// Add or change token property permissions of a collection.
+ ///
+ /// # Permissions
+ ///
+ /// * Collection Owner
+ /// * Collection Admin
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * property_permissions: a vector of permissions for property keys. Keys support Latin letters, '-', '_', and '.' as symbols.
#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]
#[transactional]
pub fn set_token_property_permissions(
@@ -689,6 +777,22 @@
dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))
}
+ /// Create multiple items inside a collection with explicitly specified initial parameters.
+ ///
+ /// # Permissions
+ ///
+ /// * Collection Owner
+ /// * Collection Admin
+ /// * Anyone if
+ /// * Allow List is enabled, and
+ /// * Address is added to allow list, and
+ /// * MintPermission is enabled (see SetMintPermission method)
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection.
+ ///
+ /// * data: explicit item creation data.
#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]
#[transactional]
pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {
@@ -698,11 +802,11 @@
dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))
}
- /// Set transfers_enabled value for particular collection
+ /// Set transfers_enabled value for particular collection.
///
/// # Permissions
///
- /// * Collection Owner.
+ /// * Collection Owner
///
/// # Arguments
///
@@ -723,13 +827,13 @@
target_collection.save()
}
- /// Destroys a concrete instance of NFT.
+ /// Destroy a concrete instance of NFT.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
- /// * Current NFT Owner.
+ /// * Collection Owner
+ /// * Collection Admin
+ /// * Current NFT Owner
///
/// # Arguments
///
@@ -752,7 +856,7 @@
Ok(post_info)
}
- /// Destroys a concrete instance of NFT on behalf of the owner
+ /// Destroy a concrete instance of NFT on behalf of the owner.
/// See also: [`approve`]
///
/// # Permissions
@@ -835,6 +939,7 @@
/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
///
/// # Permissions
+ ///
/// * Collection Owner
/// * Collection Admin
/// * Current NFT owner
@@ -860,6 +965,18 @@
dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))
}
+ /// Set specific limits of a collection. Empty, or None fields mean chain default.
+ ///.
+ /// # Permissions
+ ///
+ /// * Collection Owner
+ /// * Collection Admin
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * new_limit: The new limits of the collection. They will overwrite the current ones.
#[weight = <SelfWeightOf<T>>::set_collection_limits()]
#[transactional]
pub fn set_collection_limits(
@@ -882,12 +999,24 @@
target_collection.save()
}
+ /// Set specific permissions of a collection. Empty, or None fields mean chain default.
+ ///
+ /// # Permissions
+ ///
+ /// * Collection Owner
+ /// * Collection Admin
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * new_permission: The new permissions of the collection. They will overwrite the current ones.
#[weight = <SelfWeightOf<T>>::set_collection_limits()]
#[transactional]
pub fn set_collection_permissions(
origin,
collection_id: CollectionId,
- new_limit: CollectionPermissions,
+ new_permission: CollectionPermissions,
) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
@@ -895,7 +1024,7 @@
target_collection.check_is_owner_or_admin(&sender)?;
let old_limit = &target_collection.permissions;
- target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;
+ target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(
collection_id
@@ -904,6 +1033,19 @@
target_collection.save()
}
+ /// Re-partition a refungible token, while owning all of its parts.
+ ///
+ /// # Permissions
+ ///
+ /// * Token Owner (must own every part)
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * token: the ID of the RFT.
+ ///
+ /// * amount: The new number of parts into which the token shall be partitioned.
#[weight = T::RefungibleExtensionsWeightInfo::repartition()]
#[transactional]
pub fn repartition(
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -197,7 +197,6 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum CollectionMode {
NFT,
- // decimal points
Fungible(DecimalPoints),
ReFungible,
}
@@ -252,12 +251,14 @@
pub enum SponsorshipState<AccountId> {
/// The fees are applied to the transaction sender
Disabled,
+ /// Pending confirmation from a sponsor-to-be
Unconfirmed(AccountId),
/// Transactions are sponsored by specified account
Confirmed(AccountId),
}
impl<AccountId> SponsorshipState<AccountId> {
+ /// Get the acting sponsor account, if present
pub fn sponsor(&self) -> Option<&AccountId> {
match self {
Self::Confirmed(sponsor) => Some(sponsor),
@@ -265,6 +266,7 @@
}
}
+ /// Get the sponsor account currently pending confirmation, if present
pub fn pending_sponsor(&self) -> Option<&AccountId> {
match self {
Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),
@@ -272,6 +274,7 @@
}
}
+ /// Is sponsorship set and acting
pub fn confirmed(&self) -> bool {
matches!(self, Self::Confirmed(_))
}
@@ -283,7 +286,7 @@
}
}
-/// Used in storage
+/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version)
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
pub struct Collection<AccountId> {
@@ -324,7 +327,7 @@
pub meta_update_permission: MetaUpdatePermission,
}
-/// Used in RPC calls
+/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version)
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct RpcCollection<AccountId> {
@@ -362,12 +365,15 @@
pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;
-/// All fields are wrapped in `Option`s, where None means chain default
+/// Limits and restrictions of a collection.
+/// All fields are wrapped in `Option`s, where None means chain default.
// When adding/removing fields from this struct - don't forget to also update clamp_limits
#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CollectionLimits {
+ /// Maximum number of owned tokens per account
pub account_token_ownership_limit: Option<u32>,
+ /// Maximum size of data of a sponsored transaction
pub sponsored_data_size: Option<u32>,
/// FIXME should we delete this or repurpose it?
@@ -375,13 +381,18 @@
/// Some(v) - setVariableMetadata is sponsored
/// if there is v block between txs
pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,
+ /// Maximum amount of tokens inside the collection
pub token_limit: Option<u32>,
- // Timeouts for item types in passed blocks
+ /// Timeout for sponsoring a token transfer in passed blocks
pub sponsor_transfer_timeout: Option<u32>,
+ /// Timeout for sponsoring an approval in passed blocks
pub sponsor_approve_timeout: Option<u32>,
+ /// Can a token be transferred by the owner
pub owner_can_transfer: Option<bool>,
+ /// Can a token be burned by the owner
pub owner_can_destroy: Option<bool>,
+ /// Can a token be transferred at all
pub transfers_enabled: Option<bool>,
}
@@ -509,6 +520,7 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum SponsoringRateLimit {
SponsoringDisabled,
+ /// Once per how many blocks can sponsorship of a transaction type occur
Blocks(u32),
}
@@ -516,6 +528,7 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[derivative(Debug)]
pub struct CreateNftData {
+ /// Key-value pairs used to describe the token as metadata
#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
#[derivative(Debug(format_with = "bounded::vec_debug"))]
pub properties: CollectionPropertiesVec,
@@ -524,6 +537,7 @@
#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CreateFungibleData {
+ /// Number of fungible tokens minted
pub value: u128,
}
@@ -534,6 +548,7 @@
#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
#[derivative(Debug(format_with = "bounded::vec_debug"))]
pub const_data: BoundedVec<u8, CustomDataLimit>,
+ /// Number of pieces the RFT is split into
pub pieces: u128,
}
@@ -553,6 +568,7 @@
ReFungible(CreateReFungibleData),
}
+/// Explicit NFT creation data with meta parameters
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug)]
pub struct CreateNftExData<CrossAccountId> {
@@ -561,6 +577,7 @@
pub owner: CrossAccountId,
}
+/// Explicit RFT creation data with meta parameters
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
pub struct CreateRefungibleExData<CrossAccountId> {
@@ -570,6 +587,7 @@
pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
}
+/// Explicit item creation data with meta parameters, namely the owner
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
pub enum CreateItemExData<CrossAccountId> {
@@ -617,6 +635,7 @@
}
}
+/// Token's address, dictated by its collection and token IDs
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
// todo possibly rename to be used generally as an address pair
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -43,13 +43,13 @@
accountTokens: fun('Get tokens owned by account', [collectionParam, crossAccountParam()], 'Vec<u32>'),
collectionTokens: fun('Get tokens contained in collection', [collectionParam], 'Vec<u32>'),
- lastTokenId: fun('Get last token id', [collectionParam], 'u32'),
+ lastTokenId: fun('Get last token ID created in a collection', [collectionParam], 'u32'),
totalSupply: fun('Get amount of unique collection tokens', [collectionParam], 'u32'),
- accountBalance: fun('Get amount of different user tokens', [collectionParam, crossAccountParam()], 'u32'),
- balance: fun('Get amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
+ accountBalance: fun('Get owned amount of any user tokens', [collectionParam, crossAccountParam()], 'u32'),
+ balance: fun('Get owned amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
allowance: fun('Get allowed amount', [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], 'u128'),
tokenOwner: fun('Get token owner', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
- topmostTokenOwner: fun('Get token owner, in case of nested token - find parent recursive', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
+ topmostTokenOwner: fun('Get token owner, in case of nested token - find the parent recursively', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
tokenChildren: fun('Get tokens nested directly into the token', [collectionParam, tokenParam], 'Vec<UpDataStructsTokenChild>'),
constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),
variableMetadata: fun('Get token variable metadata', [collectionParam, tokenParam], 'Vec<u8>'),
@@ -74,7 +74,7 @@
'UpDataStructsTokenData',
),
tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),
- collectionById: fun('Get collection by specified id', [collectionParam], 'Option<UpDataStructsRpcCollection>'),
+ collectionById: fun('Get collection by specified ID', [collectionParam], 'Option<UpDataStructsRpcCollection>'),
collectionStats: fun('Get collection stats', [], 'UpDataStructsCollectionStats'),
allowed: fun('Check if user is allowed to use collection', [collectionParam, crossAccountParam()], 'bool'),
nextSponsored: fun('Get number of blocks when sponsored transaction is available', [collectionParam, crossAccountParam(), tokenParam], 'Option<u64>'),