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#[struct_versioning::versioned(version = 2, upper)]60#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]61pub struct ItemData<CrossAccountId> {62 #[version(..2)]63 pub const_data: BoundedVec<u8, CustomDataLimit>,6465 #[version(..2)]66 pub variable_data: BoundedVec<u8, CustomDataLimit>,6768 pub owner: CrossAccountId,69}7071#[frame_support::pallet]72pub mod pallet {73 use super::*;74 use frame_support::{75 Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion,76 };77 use frame_system::pallet_prelude::*;78 use up_data_structs::{CollectionId, TokenId};79 use super::weights::WeightInfo;8081 #[pallet::error]82 pub enum Error<T> {83 /// Not Nonfungible item data used to mint in Nonfungible collection.84 NotNonfungibleDataUsedToMintFungibleCollectionToken,85 /// Used amount > 1 with NFT86 NonfungibleItemsHaveNoAmount,87 /// Unable to burn NFT with children88 CantBurnNftWithChildren,89 }9091 #[pallet::config]92 pub trait Config:93 frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config94 {95 type WeightInfo: WeightInfo;96 }9798 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);99100 #[pallet::pallet]101 #[pallet::storage_version(STORAGE_VERSION)]102 #[pallet::generate_store(pub(super) trait Store)]103 pub struct Pallet<T>(_);104105 #[pallet::storage]106 pub type TokensMinted<T: Config> =107 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;108 #[pallet::storage]109 pub type TokensBurnt<T: Config> =110 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;111112 #[pallet::storage]113 pub type TokenData<T: Config> = StorageNMap<114 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),115 Value = ItemData<T::CrossAccountId>,116 QueryKind = OptionQuery,117 >;118119 #[pallet::storage]120 #[pallet::getter(fn token_properties)]121 pub type TokenProperties<T: Config> = StorageNMap<122 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),123 Value = Properties,124 QueryKind = ValueQuery,125 OnEmpty = up_data_structs::TokenProperties,126 >;127128 #[pallet::storage]129 #[pallet::getter(fn token_aux_property)]130 pub type TokenAuxProperties<T: Config> = StorageNMap<131 Key = (132 Key<Twox64Concat, CollectionId>,133 Key<Twox64Concat, TokenId>,134 Key<Twox64Concat, PropertyScope>,135 Key<Twox64Concat, PropertyKey>,136 ),137 Value = AuxPropertyValue,138 QueryKind = OptionQuery,139 >;140141 /// Used to enumerate tokens owned by account142 #[pallet::storage]143 pub type Owned<T: Config> = StorageNMap<144 Key = (145 Key<Twox64Concat, CollectionId>,146 Key<Blake2_128Concat, T::CrossAccountId>,147 Key<Twox64Concat, TokenId>,148 ),149 Value = bool,150 QueryKind = ValueQuery,151 >;152153 /// Used to enumerate token's children154 #[pallet::storage]155 #[pallet::getter(fn token_children)]156 pub type TokenChildren<T: Config> = StorageNMap<157 Key = (158 Key<Twox64Concat, CollectionId>,159 Key<Twox64Concat, TokenId>,160 Key<Twox64Concat, (CollectionId, TokenId)>,161 ),162 Value = bool,163 QueryKind = ValueQuery,164 >;165166 #[pallet::storage]167 pub type AccountBalance<T: Config> = StorageNMap<168 Key = (169 Key<Twox64Concat, CollectionId>,170 Key<Blake2_128Concat, T::CrossAccountId>,171 ),172 Value = u32,173 QueryKind = ValueQuery,174 >;175176 #[pallet::storage]177 pub type Allowance<T: Config> = StorageNMap<178 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),179 Value = T::CrossAccountId,180 QueryKind = OptionQuery,181 >;182183 #[pallet::hooks]184 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {185 fn on_runtime_upgrade() -> Weight {186 if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {187 let mut had_consts = BTreeSet::new();188 <TokenData<T>>::translate::<ItemDataVersion1<T::CrossAccountId>, _>(189 |(collection, token), v| {190 let mut props = vec![];191 if !v.const_data.is_empty() {192 props.push(Property {193 key: b"_old_constData".to_vec().try_into().unwrap(),194 value: v195 .const_data196 .clone()197 .into_inner()198 .try_into()199 .expect("const too long"),200 });201 had_consts.insert(collection);202 }203 if !v.variable_data.is_empty() {204 props.push(Property {205 key: b"_old_variableData".to_vec().try_into().unwrap(),206 value: v207 .variable_data208 .clone()209 .into_inner()210 .try_into()211 .expect("variable too long"),212 })213 }214 if !props.is_empty() {215 Self::set_scoped_token_properties(216 collection,217 token,218 PropertyScope::None,219 props.into_iter(),220 )221 .expect("existing token data exceeds property storage");222 }223 Some(<ItemDataVersion2<T::CrossAccountId>>::from(v))224 },225 );226 for collection in had_consts {227 <PalletCommon<T>>::set_property_permission_unchecked(228 collection,229 PropertyKeyPermission {230 key: b"_old_constData".to_vec().try_into().unwrap(),231 permission: PropertyPermission {232 mutable: false,233 collection_admin: true,234 token_owner: false,235 },236 },237 )238 .expect("failed to configure permission");239 }240 }241242 0243 }244 }245}246247pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);248impl<T: Config> NonfungibleHandle<T> {249 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {250 Self(inner)251 }252 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {253 self.0254 }255 pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {256 &mut self.0257 }258}259impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {260 fn recorder(&self) -> &SubstrateRecorder<T> {261 self.0.recorder()262 }263 fn into_recorder(self) -> SubstrateRecorder<T> {264 self.0.into_recorder()265 }266}267impl<T: Config> Deref for NonfungibleHandle<T> {268 type Target = pallet_common::CollectionHandle<T>;269270 fn deref(&self) -> &Self::Target {271 &self.0272 }273}274275impl<T: Config> Pallet<T> {276 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {277 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)278 }279 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {280 <TokenData<T>>::contains_key((collection.id, token))281 }282283 pub fn set_scoped_token_property(284 collection_id: CollectionId,285 token_id: TokenId,286 scope: PropertyScope,287 property: Property,288 ) -> DispatchResult {289 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {290 properties.try_scoped_set(scope, property.key, property.value)291 })292 .map_err(<CommonError<T>>::from)?;293294 Ok(())295 }296297 pub fn set_scoped_token_properties(298 collection_id: CollectionId,299 token_id: TokenId,300 scope: PropertyScope,301 properties: impl Iterator<Item = Property>,302 ) -> DispatchResult {303 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {304 stored_properties.try_scoped_set_from_iter(scope, properties)305 })306 .map_err(<CommonError<T>>::from)?;307308 Ok(())309 }310311 pub fn try_mutate_token_aux_property<R, E>(312 collection_id: CollectionId,313 token_id: TokenId,314 scope: PropertyScope,315 key: PropertyKey,316 f: impl FnOnce(&mut Option<AuxPropertyValue>) -> Result<R, E>,317 ) -> Result<R, E> {318 <TokenAuxProperties<T>>::try_mutate((collection_id, token_id, scope, key), f)319 }320321 pub fn remove_token_aux_property(322 collection_id: CollectionId,323 token_id: TokenId,324 scope: PropertyScope,325 key: PropertyKey,326 ) {327 <TokenAuxProperties<T>>::remove((collection_id, token_id, scope, key));328 }329330 pub fn iterate_token_aux_properties(331 collection_id: CollectionId,332 token_id: TokenId,333 scope: PropertyScope,334 ) -> impl Iterator<Item = (PropertyKey, AuxPropertyValue)> {335 <TokenAuxProperties<T>>::iter_prefix((collection_id, token_id, scope))336 }337338 pub fn current_token_id(collection_id: CollectionId) -> TokenId {339 TokenId(<TokensMinted<T>>::get(collection_id))340 }341}342343// unchecked calls skips any permission checks344impl<T: Config> Pallet<T> {345 pub fn init_collection(346 owner: T::CrossAccountId,347 data: CreateCollectionData<T::AccountId>,348 is_external: bool,349 ) -> Result<CollectionId, DispatchError> {350 <PalletCommon<T>>::init_collection(owner, data, is_external)351 }352 pub fn destroy_collection(353 collection: NonfungibleHandle<T>,354 sender: &T::CrossAccountId,355 ) -> DispatchResult {356 let id = collection.id;357358 if Self::collection_has_tokens(id) {359 return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());360 }361362 // =========363364 PalletCommon::destroy_collection(collection.0, sender)?;365366 <TokenData<T>>::remove_prefix((id,), None);367 <TokenChildren<T>>::remove_prefix((id,), None);368 <Owned<T>>::remove_prefix((id,), None);369 <TokensMinted<T>>::remove(id);370 <TokensBurnt<T>>::remove(id);371 <Allowance<T>>::remove_prefix((id,), None);372 <AccountBalance<T>>::remove_prefix((id,), None);373 Ok(())374 }375376 pub fn burn(377 collection: &NonfungibleHandle<T>,378 sender: &T::CrossAccountId,379 token: TokenId,380 ) -> DispatchResult {381 let token_data =382 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;383 ensure!(&token_data.owner == sender, <CommonError<T>>::NoPermission);384385 if collection.permissions.access() == AccessMode::AllowList {386 collection.check_allowlist(sender)?;387 }388389 if Self::token_has_children(collection.id, token) {390 return Err(<Error<T>>::CantBurnNftWithChildren.into());391 }392393 let burnt = <TokensBurnt<T>>::get(collection.id)394 .checked_add(1)395 .ok_or(ArithmeticError::Overflow)?;396397 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))398 .checked_sub(1)399 .ok_or(ArithmeticError::Overflow)?;400401 // =========402403 if balance == 0 {404 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));405 } else {406 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);407 }408409 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);410411 <Owned<T>>::remove((collection.id, &token_data.owner, token));412 <TokensBurnt<T>>::insert(collection.id, burnt);413 <TokenData<T>>::remove((collection.id, token));414 <TokenProperties<T>>::remove((collection.id, token));415 <TokenAuxProperties<T>>::remove_prefix((collection.id, token), None);416 let old_spender = <Allowance<T>>::take((collection.id, token));417418 if let Some(old_spender) = old_spender {419 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(420 collection.id,421 token,422 token_data.owner.clone(),423 old_spender,424 0,425 ));426 }427428 <PalletEvm<T>>::deposit_log(429 ERC721Events::Transfer {430 from: *token_data.owner.as_eth(),431 to: H160::default(),432 token_id: token.into(),433 }434 .to_log(collection_id_to_address(collection.id)),435 );436 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(437 collection.id,438 token,439 token_data.owner,440 1,441 ));442 Ok(())443 }444445 #[transactional]446 pub fn burn_recursively(447 collection: &NonfungibleHandle<T>,448 sender: &T::CrossAccountId,449 token: TokenId,450 self_budget: &dyn Budget,451 breadth_budget: &dyn Budget,452 ) -> DispatchResultWithPostInfo {453 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);454455 let current_token_account =456 T::CrossTokenAddressMapping::token_to_address(collection.id, token);457458 let mut weight = 0 as Weight;459460 // This method is transactional, if user in fact doesn't have permissions to remove token -461 // tokens removed here will be restored after rejected transaction462 for ((collection, token), _) in <TokenChildren<T>>::iter_prefix((collection.id, token)) {463 ensure!(breadth_budget.consume(), <StructureError<T>>::BreadthLimit,);464 let PostDispatchInfo { actual_weight, .. } =465 <PalletStructure<T>>::burn_item_recursively(466 current_token_account.clone(),467 collection,468 token,469 self_budget,470 breadth_budget,471 )?;472 if let Some(actual_weight) = actual_weight {473 weight = weight.saturating_add(actual_weight);474 }475 }476477 Self::burn(collection, sender, token)?;478 DispatchResultWithPostInfo::Ok(PostDispatchInfo {479 actual_weight: Some(weight + <SelfWeightOf<T>>::burn_item()),480 pays_fee: Pays::Yes,481 })482 }483484 #[transactional]485 fn modify_token_properties(486 collection: &NonfungibleHandle<T>,487 sender: &T::CrossAccountId,488 token_id: TokenId,489 properties: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,490 is_token_create: bool,491 nesting_budget: &dyn Budget,492 ) -> DispatchResult {493 let mut collection_admin_status = None;494 let mut token_owner_result = None;495496 let mut is_collection_admin =497 || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));498499 let mut is_token_owner = || {500 *token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {501 let is_owned = <PalletStructure<T>>::check_indirectly_owned(502 sender.clone(),503 collection.id,504 token_id,505 None,506 nesting_budget,507 )?;508509 Ok(is_owned)510 })511 };512513 for (key, value) in properties {514 let permission = <PalletCommon<T>>::property_permissions(collection.id)515 .get(&key)516 .cloned()517 .unwrap_or_else(PropertyPermission::none);518519 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))520 .get(&key)521 .is_some();522523 match permission {524 PropertyPermission { mutable: false, .. } if is_property_exists => {525 return Err(<CommonError<T>>::NoPermission.into());526 }527528 PropertyPermission {529 collection_admin,530 token_owner,531 ..532 } => {533 //TODO: investigate threats during public minting.534 if is_token_create && (collection_admin || token_owner) && value.is_some() {535 // Pass536 } else if collection_admin && is_collection_admin() {537 // Pass538 } else if token_owner && is_token_owner()? {539 // Pass540 } else {541 fail!(<CommonError<T>>::NoPermission);542 }543 }544 }545546 match value {547 Some(value) => {548 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {549 properties.try_set(key.clone(), value)550 })551 .map_err(<CommonError<T>>::from)?;552553 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(554 collection.id,555 token_id,556 key,557 ));558 }559 None => {560 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {561 properties.remove(&key)562 })563 .map_err(<CommonError<T>>::from)?;564565 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(566 collection.id,567 token_id,568 key,569 ));570 }571 }572 }573574 Ok(())575 }576577 pub fn set_token_properties(578 collection: &NonfungibleHandle<T>,579 sender: &T::CrossAccountId,580 token_id: TokenId,581 properties: impl Iterator<Item = Property>,582 is_token_create: bool,583 nesting_budget: &dyn Budget,584 ) -> DispatchResult {585 Self::modify_token_properties(586 collection,587 sender,588 token_id,589 properties.map(|p| (p.key, Some(p.value))),590 is_token_create,591 nesting_budget,592 )593 }594595 pub fn set_token_property(596 collection: &NonfungibleHandle<T>,597 sender: &T::CrossAccountId,598 token_id: TokenId,599 property: Property,600 nesting_budget: &dyn Budget,601 ) -> DispatchResult {602 let is_token_create = false;603604 Self::set_token_properties(605 collection,606 sender,607 token_id,608 [property].into_iter(),609 is_token_create,610 nesting_budget,611 )612 }613614 pub fn delete_token_properties(615 collection: &NonfungibleHandle<T>,616 sender: &T::CrossAccountId,617 token_id: TokenId,618 property_keys: impl Iterator<Item = PropertyKey>,619 nesting_budget: &dyn Budget,620 ) -> DispatchResult {621 let is_token_create = false;622623 Self::modify_token_properties(624 collection,625 sender,626 token_id,627 property_keys.into_iter().map(|key| (key, None)),628 is_token_create,629 nesting_budget,630 )631 }632633 pub fn delete_token_property(634 collection: &NonfungibleHandle<T>,635 sender: &T::CrossAccountId,636 token_id: TokenId,637 property_key: PropertyKey,638 nesting_budget: &dyn Budget,639 ) -> DispatchResult {640 Self::delete_token_properties(641 collection,642 sender,643 token_id,644 [property_key].into_iter(),645 nesting_budget,646 )647 }648649 pub fn set_collection_properties(650 collection: &NonfungibleHandle<T>,651 sender: &T::CrossAccountId,652 properties: Vec<Property>,653 ) -> DispatchResult {654 <PalletCommon<T>>::set_collection_properties(collection, sender, properties)655 }656657 pub fn delete_collection_properties(658 collection: &CollectionHandle<T>,659 sender: &T::CrossAccountId,660 property_keys: Vec<PropertyKey>,661 ) -> DispatchResult {662 <PalletCommon<T>>::delete_collection_properties(collection, sender, property_keys)663 }664665 pub fn set_token_property_permissions(666 collection: &CollectionHandle<T>,667 sender: &T::CrossAccountId,668 property_permissions: Vec<PropertyKeyPermission>,669 ) -> DispatchResult {670 <PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)671 }672673 pub fn set_property_permission(674 collection: &CollectionHandle<T>,675 sender: &T::CrossAccountId,676 permission: PropertyKeyPermission,677 ) -> DispatchResult {678 <PalletCommon<T>>::set_property_permission(collection, sender, permission)679 }680681 pub fn transfer(682 collection: &NonfungibleHandle<T>,683 from: &T::CrossAccountId,684 to: &T::CrossAccountId,685 token: TokenId,686 nesting_budget: &dyn Budget,687 ) -> DispatchResult {688 ensure!(689 collection.limits.transfers_enabled(),690 <CommonError<T>>::TransferNotAllowed691 );692693 let token_data =694 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;695 ensure!(&token_data.owner == from, <CommonError<T>>::NoPermission);696697 if collection.permissions.access() == AccessMode::AllowList {698 collection.check_allowlist(from)?;699 collection.check_allowlist(to)?;700 }701 <PalletCommon<T>>::ensure_correct_receiver(to)?;702703 let balance_from = <AccountBalance<T>>::get((collection.id, from))704 .checked_sub(1)705 .ok_or(<CommonError<T>>::TokenValueTooLow)?;706 let balance_to = if from != to {707 let balance_to = <AccountBalance<T>>::get((collection.id, to))708 .checked_add(1)709 .ok_or(ArithmeticError::Overflow)?;710711 ensure!(712 balance_to < collection.limits.account_token_ownership_limit(),713 <CommonError<T>>::AccountTokenLimitExceeded,714 );715716 Some(balance_to)717 } else {718 None719 };720721 <PalletStructure<T>>::nest_if_sent_to_token(722 from.clone(),723 to,724 collection.id,725 token,726 nesting_budget,727 )?;728729 // =========730731 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);732733 <TokenData<T>>::insert(734 (collection.id, token),735 ItemData {736 owner: to.clone(),737 ..token_data738 },739 );740741 if let Some(balance_to) = balance_to {742 // from != to743 if balance_from == 0 {744 <AccountBalance<T>>::remove((collection.id, from));745 } else {746 <AccountBalance<T>>::insert((collection.id, from), balance_from);747 }748 <AccountBalance<T>>::insert((collection.id, to), balance_to);749 <Owned<T>>::remove((collection.id, from, token));750 <Owned<T>>::insert((collection.id, to, token), true);751 }752 Self::set_allowance_unchecked(collection, from, token, None, true);753754 <PalletEvm<T>>::deposit_log(755 ERC721Events::Transfer {756 from: *from.as_eth(),757 to: *to.as_eth(),758 token_id: token.into(),759 }760 .to_log(collection_id_to_address(collection.id)),761 );762 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(763 collection.id,764 token,765 from.clone(),766 to.clone(),767 1,768 ));769 Ok(())770 }771772 pub fn create_multiple_items(773 collection: &NonfungibleHandle<T>,774 sender: &T::CrossAccountId,775 data: Vec<CreateItemData<T>>,776 nesting_budget: &dyn Budget,777 ) -> DispatchResult {778 if !collection.is_owner_or_admin(sender) {779 ensure!(780 collection.permissions.mint_mode(),781 <CommonError<T>>::PublicMintingNotAllowed782 );783 collection.check_allowlist(sender)?;784785 for item in data.iter() {786 collection.check_allowlist(&item.owner)?;787 }788 }789790 for data in data.iter() {791 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;792 }793794 let first_token = <TokensMinted<T>>::get(collection.id);795 let tokens_minted = first_token796 .checked_add(data.len() as u32)797 .ok_or(ArithmeticError::Overflow)?;798 ensure!(799 tokens_minted <= collection.limits.token_limit(),800 <CommonError<T>>::CollectionTokenLimitExceeded801 );802803 let mut balances = BTreeMap::new();804 for data in &data {805 let balance = balances806 .entry(&data.owner)807 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));808 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;809810 ensure!(811 *balance <= collection.limits.account_token_ownership_limit(),812 <CommonError<T>>::AccountTokenLimitExceeded,813 );814 }815816 for (i, data) in data.iter().enumerate() {817 let token = TokenId(first_token + i as u32 + 1);818819 <PalletStructure<T>>::check_nesting(820 sender.clone(),821 &data.owner,822 collection.id,823 token,824 nesting_budget,825 )?;826 }827828 // =========829830 with_transaction(|| {831 for (i, data) in data.iter().enumerate() {832 let token = first_token + i as u32 + 1;833834 <TokenData<T>>::insert(835 (collection.id, token),836 ItemData {837 // const_data: data.const_data.clone(),838 owner: data.owner.clone(),839 },840 );841842 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(843 &data.owner,844 collection.id,845 TokenId(token),846 );847848 if let Err(e) = Self::set_token_properties(849 collection,850 sender,851 TokenId(token),852 data.properties.clone().into_iter(),853 true,854 nesting_budget,855 ) {856 return TransactionOutcome::Rollback(Err(e));857 }858 }859 TransactionOutcome::Commit(Ok(()))860 })?;861862 <TokensMinted<T>>::insert(collection.id, tokens_minted);863 for (account, balance) in balances {864 <AccountBalance<T>>::insert((collection.id, account), balance);865 }866 for (i, data) in data.into_iter().enumerate() {867 let token = first_token + i as u32 + 1;868 <Owned<T>>::insert((collection.id, &data.owner, token), true);869870 <PalletEvm<T>>::deposit_log(871 ERC721Events::Transfer {872 from: H160::default(),873 to: *data.owner.as_eth(),874 token_id: token.into(),875 }876 .to_log(collection_id_to_address(collection.id)),877 );878 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(879 collection.id,880 TokenId(token),881 data.owner.clone(),882 1,883 ));884 }885 Ok(())886 }887888 pub fn set_allowance_unchecked(889 collection: &NonfungibleHandle<T>,890 sender: &T::CrossAccountId,891 token: TokenId,892 spender: Option<&T::CrossAccountId>,893 assume_implicit_eth: bool,894 ) {895 if let Some(spender) = spender {896 let old_spender = <Allowance<T>>::get((collection.id, token));897 <Allowance<T>>::insert((collection.id, token), spender);898 // In ERC721 there is only one possible approved user of token, so we set899 // approved user to spender900 <PalletEvm<T>>::deposit_log(901 ERC721Events::Approval {902 owner: *sender.as_eth(),903 approved: *spender.as_eth(),904 token_id: token.into(),905 }906 .to_log(collection_id_to_address(collection.id)),907 );908 // In Unique chain, any token can have any amount of approved users, so we need to909 // set allowance of old owner to 0, and allowance of new owner to 1910 if old_spender.as_ref() != Some(spender) {911 if let Some(old_owner) = old_spender {912 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(913 collection.id,914 token,915 sender.clone(),916 old_owner,917 0,918 ));919 }920 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(921 collection.id,922 token,923 sender.clone(),924 spender.clone(),925 1,926 ));927 }928 } else {929 let old_spender = <Allowance<T>>::take((collection.id, token));930 if !assume_implicit_eth {931 // In ERC721 there is only one possible approved user of token, so we set932 // approved user to zero address933 <PalletEvm<T>>::deposit_log(934 ERC721Events::Approval {935 owner: *sender.as_eth(),936 approved: H160::default(),937 token_id: token.into(),938 }939 .to_log(collection_id_to_address(collection.id)),940 );941 }942 // In Unique chain, any token can have any amount of approved users, so we need to943 // set allowance of old owner to 0944 if let Some(old_spender) = old_spender {945 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(946 collection.id,947 token,948 sender.clone(),949 old_spender,950 0,951 ));952 }953 }954 }955956 pub fn set_allowance(957 collection: &NonfungibleHandle<T>,958 sender: &T::CrossAccountId,959 token: TokenId,960 spender: Option<&T::CrossAccountId>,961 ) -> DispatchResult {962 if collection.permissions.access() == AccessMode::AllowList {963 collection.check_allowlist(sender)?;964 if let Some(spender) = spender {965 collection.check_allowlist(spender)?;966 }967 }968969 if let Some(spender) = spender {970 <PalletCommon<T>>::ensure_correct_receiver(spender)?;971 }972973 let token_data =974 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;975 if &token_data.owner != sender {976 ensure!(977 collection.ignores_owned_amount(sender),978 <CommonError<T>>::CantApproveMoreThanOwned979 );980 }981982 // =========983984 Self::set_allowance_unchecked(collection, sender, token, spender, false);985 Ok(())986 }987988 fn check_allowed(989 collection: &NonfungibleHandle<T>,990 spender: &T::CrossAccountId,991 from: &T::CrossAccountId,992 token: TokenId,993 nesting_budget: &dyn Budget,994 ) -> DispatchResult {995 if spender.conv_eq(from) {996 return Ok(());997 }998 if collection.permissions.access() == AccessMode::AllowList {999 // `from`, `to` checked in [`transfer`]1000 collection.check_allowlist(spender)?;1001 }10021003 if collection.limits.owner_can_transfer() && collection.is_owner_or_admin(spender) {1004 return Ok(());1005 }10061007 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1008 ensure!(1009 <PalletStructure<T>>::check_indirectly_owned(1010 spender.clone(),1011 source.0,1012 source.1,1013 None,1014 nesting_budget1015 )?,1016 <CommonError<T>>::ApprovedValueTooLow,1017 );1018 return Ok(());1019 }1020 if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {1021 return Ok(());1022 }1023 ensure!(1024 collection.ignores_allowance(spender),1025 <CommonError<T>>::ApprovedValueTooLow1026 );1027 Ok(())1028 }10291030 pub fn transfer_from(1031 collection: &NonfungibleHandle<T>,1032 spender: &T::CrossAccountId,1033 from: &T::CrossAccountId,1034 to: &T::CrossAccountId,1035 token: TokenId,1036 nesting_budget: &dyn Budget,1037 ) -> DispatchResult {1038 Self::check_allowed(collection, spender, from, token, nesting_budget)?;10391040 // =========10411042 // Allowance is reset in [`transfer`]1043 Self::transfer(collection, from, to, token, nesting_budget)1044 }10451046 pub fn burn_from(1047 collection: &NonfungibleHandle<T>,1048 spender: &T::CrossAccountId,1049 from: &T::CrossAccountId,1050 token: TokenId,1051 nesting_budget: &dyn Budget,1052 ) -> DispatchResult {1053 Self::check_allowed(collection, spender, from, token, nesting_budget)?;10541055 // =========10561057 Self::burn(collection, from, token)1058 }10591060 pub fn check_nesting(1061 handle: &NonfungibleHandle<T>,1062 sender: T::CrossAccountId,1063 from: (CollectionId, TokenId),1064 under: TokenId,1065 nesting_budget: &dyn Budget,1066 ) -> DispatchResult {1067 let nesting = handle.permissions.nesting();10681069 #[cfg(not(feature = "runtime-benchmarks"))]1070 let permissive = false;1071 #[cfg(feature = "runtime-benchmarks")]1072 let permissive = nesting.permissive;10731074 if permissive {1075 // Pass1076 } else if nesting.token_owner1077 && <PalletStructure<T>>::check_indirectly_owned(1078 sender.clone(),1079 handle.id,1080 under,1081 Some(from),1082 nesting_budget,1083 )? {1084 // Pass1085 } else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {1086 // Pass1087 } else {1088 fail!(<CommonError<T>>::UserIsNotAllowedToNest);1089 }10901091 if let Some(whitelist) = &nesting.restricted {1092 ensure!(1093 whitelist.contains(&from.0),1094 <CommonError<T>>::SourceCollectionIsNotAllowedToNest1095 );1096 }1097 Ok(())1098 }10991100 fn nest(under: (CollectionId, TokenId), to_nest: (CollectionId, TokenId)) {1101 <TokenChildren<T>>::insert((under.0, under.1, (to_nest.0, to_nest.1)), true);1102 }11031104 fn unnest(under: (CollectionId, TokenId), to_unnest: (CollectionId, TokenId)) {1105 <TokenChildren<T>>::remove((under.0, under.1, to_unnest));1106 }11071108 fn collection_has_tokens(collection_id: CollectionId) -> bool {1109 <TokenData<T>>::iter_prefix((collection_id,))1110 .next()1111 .is_some()1112 }11131114 fn token_has_children(collection_id: CollectionId, token_id: TokenId) -> bool {1115 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1116 .next()1117 .is_some()1118 }11191120 pub fn token_children_ids(collection_id: CollectionId, token_id: TokenId) -> Vec<TokenChild> {1121 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1122 .map(|((child_collection_id, child_id), _)| TokenChild {1123 collection: child_collection_id,1124 token: child_id,1125 })1126 .collect()1127 }11281129 /// Delegated to `create_multiple_items`1130 pub fn create_item(1131 collection: &NonfungibleHandle<T>,1132 sender: &T::CrossAccountId,1133 data: CreateItemData<T>,1134 nesting_budget: &dyn Budget,1135 ) -> DispatchResult {1136 Self::create_multiple_items(collection, sender, vec![data], nesting_budget)1137 }1138}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>'),