difftreelog
Merge pull request #427 from UniqueNetwork/doc/architectural-changes
in: master
19 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,16 @@
account: CrossAccountId,
at: Option<BlockHash>,
) -> Result<Vec<TokenId>>;
+
+ /// Get tokens contained within a collection.
#[method(name = "unique_collectionTokens")]
fn collection_tokens(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<TokenId>>;
+
+ /// Check if the token exists.
#[method(name = "unique_tokenExists")]
fn token_exists(
&self,
@@ -63,6 +68,7 @@
at: Option<BlockHash>,
) -> Result<bool>;
+ /// Get the token owner.
#[method(name = "unique_tokenOwner")]
fn token_owner(
&self,
@@ -80,6 +86,7 @@
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+ /// Get the topmost token owner in the hierarchy of a possibly nested token.
#[method(name = "unique_topmostTokenOwner")]
fn topmost_token_owner(
&self,
@@ -87,6 +94,8 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+
+ /// Get tokens nested directly into the token.
#[method(name = "unique_tokenChildren")]
fn token_children(
&self,
@@ -95,6 +104,7 @@
at: Option<BlockHash>,
) -> Result<Vec<TokenChild>>;
+ /// Get collection properties, optionally limited to the provided keys.
#[method(name = "unique_collectionProperties")]
fn collection_properties(
&self,
@@ -103,6 +113,7 @@
at: Option<BlockHash>,
) -> Result<Vec<Property>>;
+ /// Get token properties, optionally limited to the provided keys.
#[method(name = "unique_tokenProperties")]
fn token_properties(
&self,
@@ -112,6 +123,7 @@
at: Option<BlockHash>,
) -> Result<Vec<Property>>;
+ /// Get property permissions, optionally limited to the provided keys.
#[method(name = "unique_propertyPermissions")]
fn property_permissions(
&self,
@@ -120,6 +132,7 @@
at: Option<BlockHash>,
) -> Result<Vec<PropertyKeyPermission>>;
+ /// Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT.
#[method(name = "unique_tokenData")]
fn token_data(
&self,
@@ -129,8 +142,11 @@
at: Option<BlockHash>,
) -> Result<TokenData<CrossAccountId>>;
+ /// Get the amount of distinctive tokens present in a collection.
#[method(name = "unique_totalSupply")]
fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;
+
+ /// Get the amount of any user tokens owned by an account.
#[method(name = "unique_accountBalance")]
fn account_balance(
&self,
@@ -138,6 +154,8 @@
account: CrossAccountId,
at: Option<BlockHash>,
) -> Result<u32>;
+
+ /// Get the amount of a specific token owned by an account.
#[method(name = "unique_balance")]
fn balance(
&self,
@@ -146,6 +164,8 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<String>;
+
+ /// Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor.
#[method(name = "unique_allowance")]
fn allowance(
&self,
@@ -156,18 +176,23 @@
at: Option<BlockHash>,
) -> Result<String>;
+ /// Get the list of admin accounts of a collection.
#[method(name = "unique_adminlist")]
fn adminlist(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+
+ /// Get the list of accounts allowed to operate within a collection.
#[method(name = "unique_allowlist")]
fn allowlist(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+
+ /// Check if a user is allowed to operate within a collection.
#[method(name = "unique_allowed")]
fn allowed(
&self,
@@ -175,17 +200,24 @@
user: CrossAccountId,
at: Option<BlockHash>,
) -> Result<bool>;
+
+ /// Get the 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 info by the specified ID.
#[method(name = "unique_collectionById")]
fn collection_by_id(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Option<RpcCollection<AccountId>>>;
+
+ /// Get chain stats about collections.
#[method(name = "unique_collectionStats")]
fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;
+ /// Get the number of blocks until sponsoring a transaction is available.
#[method(name = "unique_nextSponsored")]
fn next_sponsored(
&self,
@@ -195,6 +227,7 @@
at: Option<BlockHash>,
) -> Result<Option<u64>>;
+ /// Get effective collection limits. If not explicitly set, get the chain defaults.
#[method(name = "unique_effectiveCollectionLimits")]
fn effective_collection_limits(
&self,
@@ -202,6 +235,7 @@
at: Option<BlockHash>,
) -> Result<Option<CollectionLimits>>;
+ /// Get the total amount of pieces of an RFT.
#[method(name = "unique_totalPieces")]
fn total_pieces(
&self,
@@ -228,20 +262,20 @@
Theme,
>
{
+ /// Get the latest created collection ID.
#[method(name = "rmrk_lastCollectionIdx")]
- /// Get the latest created collection id
fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;
+ /// Get collection info by ID.
#[method(name = "rmrk_collectionById")]
- /// Get collection by id
fn collection_by_id(
&self,
id: RmrkCollectionId,
at: Option<BlockHash>,
) -> Result<Option<CollectionInfo>>;
+ /// Get NFT info by collection and NFT IDs.
#[method(name = "rmrk_nftById")]
- /// Get NFT by collection id and NFT id
fn nft_by_id(
&self,
collection_id: RmrkCollectionId,
@@ -249,8 +283,8 @@
at: Option<BlockHash>,
) -> Result<Option<NftInfo>>;
+ /// Get tokens owned by an account in a collection.
#[method(name = "rmrk_accountTokens")]
- /// Get tokens owned by an account in a collection
fn account_tokens(
&self,
account_id: AccountId,
@@ -258,8 +292,8 @@
at: Option<BlockHash>,
) -> Result<Vec<RmrkNftId>>;
+ /// Get tokens nested in an NFT - its direct children (not the children's children).
#[method(name = "rmrk_nftChildren")]
- /// Get NFT children
fn nft_children(
&self,
collection_id: RmrkCollectionId,
@@ -267,8 +301,8 @@
at: Option<BlockHash>,
) -> Result<Vec<RmrkNftChild>>;
+ /// Get collection properties, created by the user - not the proxy-specific properties.
#[method(name = "rmrk_collectionProperties")]
- /// Get collection properties
fn collection_properties(
&self,
collection_id: RmrkCollectionId,
@@ -276,8 +310,8 @@
at: Option<BlockHash>,
) -> Result<Vec<PropertyInfo>>;
+ /// Get NFT properties, created by the user - not the proxy-specific properties.
#[method(name = "rmrk_nftProperties")]
- /// Get NFT properties
fn nft_properties(
&self,
collection_id: RmrkCollectionId,
@@ -286,8 +320,8 @@
at: Option<BlockHash>,
) -> Result<Vec<PropertyInfo>>;
+ /// Get data of resources of an NFT.
#[method(name = "rmrk_nftResources")]
- /// Get NFT resources
fn nft_resources(
&self,
collection_id: RmrkCollectionId,
@@ -295,8 +329,8 @@
at: Option<BlockHash>,
) -> Result<Vec<ResourceInfo>>;
+ /// Get the priority of a resource in an NFT.
#[method(name = "rmrk_nftResourcePriority")]
- /// Get NFT resource priority
fn nft_resource_priority(
&self,
collection_id: RmrkCollectionId,
@@ -305,14 +339,15 @@
at: Option<BlockHash>,
) -> Result<Option<u32>>;
+ /// Get base info by its ID.
#[method(name = "rmrk_base")]
- /// Get base info
fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;
+ /// Get all parts of a base.
#[method(name = "rmrk_baseParts")]
- /// Get all Base's parts
fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;
+ /// Get the theme names belonging to a base.
#[method(name = "rmrk_themeNames")]
fn theme_names(
&self,
@@ -320,6 +355,7 @@
at: Option<BlockHash>,
) -> Result<Vec<RmrkThemeName>>;
+ /// Get theme info, including properties, optionally limited to the provided keys.
#[method(name = "rmrk_themes")]
fn theme(
&self,
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -441,7 +441,7 @@
u128,
),
- /// The colletion property has been set.
+ /// The colletion property has been added or edited.
CollectionPropertySet(
/// Id of collection to which property has been set.
CollectionId,
@@ -457,7 +457,7 @@
PropertyKey,
),
- /// The token property has been set.
+ /// The token property has been added or edited.
TokenPropertySet(
/// Identifier of the collection whose token has the property set.
CollectionId,
@@ -477,9 +477,9 @@
PropertyKey,
),
- /// The colletion property permission has been set.
+ /// The token property permission of a collection has been set.
PropertyPermissionSet(
- /// Id of collection to which property permission has been set.
+ /// ID of collection to which property permission has been set.
CollectionId,
/// The property permission that was set.
PropertyKey,
@@ -524,26 +524,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
@@ -558,7 +558,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
@@ -571,7 +571,7 @@
CollectionIsInternal,
}
- /// Storage of the count of created collections.
+ /// Storage of the count of created collections. Essentially contains the last collection ID.
#[pallet::storage]
pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
@@ -600,7 +600,7 @@
OnEmpty = up_data_structs::CollectionProperties,
>;
- /// Storage of collection properties permissions.
+ /// Storage of token property permissions of a collection.
#[pallet::storage]
#[pallet::getter(fn property_permissions)]
pub type CollectionPropertyPermissions<T> = StorageMap<
@@ -610,7 +610,7 @@
QueryKind = ValueQuery,
>;
- /// Storage of collection admins count.
+ /// Storage of the amount of collection admins.
#[pallet::storage]
pub type AdminAmount<T> = StorageMap<
Hasher = Blake2_128Concat,
@@ -619,7 +619,7 @@
QueryKind = ValueQuery,
>;
- /// List of collection admins
+ /// List of collection admins.
#[pallet::storage]
pub type IsAdmin<T: Config> = StorageNMap<
Key = (
@@ -630,7 +630,7 @@
QueryKind = ValueQuery,
>;
- /// Allowlisted collection users
+ /// Allowlisted collection users.
#[pallet::storage]
pub type Allowlist<T: Config> = StorageNMap<
Key = (
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -119,8 +119,7 @@
pub enum Error<T> {
/// Not Fungible item data used to mint in Fungible collection.
NotFungibleDataUsedToMintFungibleCollectionToken,
- /// Not default id passed as TokenId argument.
- /// The default value of TokenId for Fungible collection is 0.
+ /// Fungible tokens hold no ID, and the default value of TokenId for Fungible collection is 0.
FungibleItemsHaveNoId,
/// Tried to set data for fungible item.
FungibleItemsDontHaveData,
@@ -157,7 +156,7 @@
QueryKind = ValueQuery,
>;
- /// Storage for delegated assets.
+ /// Storage for assets delegated to a limited extent to other users.
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (
@@ -172,7 +171,6 @@
/// Wrapper around untyped collection handle, asserting inner collection is of fungible type.
/// Required for interaction with Fungible collections, type safety and implementation [`solidity_interface`][`evm_coder::solidity_interface`].
-
pub struct FungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);
/// Implementation of methods required for dispatching during runtime.
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//! # Nonfungible Pallet18//!19//! The Nonfungible pallet provides functionality for handling nonfungible collections and tokens.20//!21//! - [`Config`]22//! - [`NonfungibleHandle`]23//! - [`Pallet`]24//! - [`CommonWeights`]25//!26//! ## Overview27//!28//! The Nonfungible pallet provides functions for:29//!30//! - NFT collection creation and removal31//! - Minting and burning of NFT tokens32//! - Retrieving account balances33//! - Transfering NFT tokens34//! - Setting and checking allowance for NFT tokens35//! - Setting properties and permissions for NFT collections and tokens36//! - Nesting and unnesting tokens37//!38//! ### Terminology39//!40//! - **NFT token:** Non fungible token.41//!42//! - **NFT Collection:** A collection of NFT tokens. All NFT tokens are part of a collection.43//! Each collection can define it's own properties, properties for it's tokens and set of permissions.44//!45//! - **Balance:** Number of NFT tokens owned by an account46//!47//! - **Allowance:** NFT tokens owned by one account that another account is allowed to make operations on48//!49//! - **Burning:** The process of “deleting” a token from a collection and from50//! an account balance of the owner.51//!52//! - **Nesting:** Setting up parent-child relationship between tokens. Nested tokens are inhereting53//! owner from their parent. There could be multiple levels of nesting. Token couldn't be nested in54//! it's child token i.e. parent-child relationship graph shouldn't have cycles.55//!56//! - **Properties:** Key-Values pairs. Token properties are attached to a token. Collection properties are57//! attached to a collection. Set of permissions could be defined for each property.58//!59//! ### Implementations60//!61//! The Nonfungible pallet provides implementations for the following traits. If these traits provide62//! the functionality that you need, then you can avoid coupling with the Nonfungible pallet.63//!64//! - [`CommonWeightInfo`](pallet_common::CommonWeightInfo): Functions for retrieval of transaction weight65//! - [`CommonCollectionOperations`](pallet_common::CommonCollectionOperations): Functions for dealing66//! with collections67//!68//! ## Interface69//!70//! ### Dispatchable Functions71//!72//! - `init_collection` - Create NFT collection. NFT collection can be configured to allow or deny access for73//! some accounts.74//! - `destroy_collection` - Destroy exising NFT collection. There should be no tokens in the collection.75//! - `burn` - Burn NFT token owned by account.76//! - `transfer` - Transfer NFT token. Transfers should be enabled for NFT collection.77//! Nests the NFT token if it is sent to another token.78//! - `create_item` - Mint NFT token in collection. Sender should have permission to mint tokens.79//! - `set_allowance` - Set allowance for another account.80//! - `set_token_property` - Set token property value.81//! - `delete_token_property` - Remove property from the token.82//! - `set_collection_properties` - Set collection properties.83//! - `delete_collection_properties` - Remove properties from the collection.84//! - `set_property_permission` - Set collection property permission.85//! - `set_token_property_permissions` - Set token property permissions.86//!87//! ## Assumptions88//!89//! * To perform operations on tokens sender should be in collection's allow list if collection access mode is `AllowList`.9091#![cfg_attr(not(feature = "std"), no_std)]9293use erc::ERC721Events;94use evm_coder::ToLog;95use frame_support::{96 BoundedVec, ensure, fail, transactional,97 storage::with_transaction,98 pallet_prelude::DispatchResultWithPostInfo,99 pallet_prelude::Weight,100 weights::{PostDispatchInfo, Pays},101};102use up_data_structs::{103 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,104 mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey,105 PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild,106 AuxPropertyValue,107};108use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};109use pallet_common::{110 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,111 eth::collection_id_to_address,112};113use pallet_structure::{Pallet as PalletStructure, Error as StructureError};114use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};115use sp_core::H160;116use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};117use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap, collections::btree_set::BTreeSet};118use core::ops::Deref;119use codec::{Encode, Decode, MaxEncodedLen};120use scale_info::TypeInfo;121122pub use pallet::*;123use weights::WeightInfo;124#[cfg(feature = "runtime-benchmarks")]125pub mod benchmarking;126pub mod common;127pub mod erc;128pub mod weights;129130pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;131pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;132133#[struct_versioning::versioned(version = 2, upper)]134#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]135pub struct ItemData<CrossAccountId> {136 #[version(..2)]137 pub const_data: BoundedVec<u8, CustomDataLimit>,138139 #[version(..2)]140 pub variable_data: BoundedVec<u8, CustomDataLimit>,141142 pub owner: CrossAccountId,143}144145#[frame_support::pallet]146pub mod pallet {147 use super::*;148 use frame_support::{149 Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion,150 };151 use frame_system::pallet_prelude::*;152 use up_data_structs::{CollectionId, TokenId};153 use super::weights::WeightInfo;154155 #[pallet::error]156 pub enum Error<T> {157 /// Not Nonfungible item data used to mint in Nonfungible collection.158 NotNonfungibleDataUsedToMintFungibleCollectionToken,159 /// Used amount > 1 with NFT160 NonfungibleItemsHaveNoAmount,161 /// Unable to burn NFT with children162 CantBurnNftWithChildren,163 }164165 #[pallet::config]166 pub trait Config:167 frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config168 {169 type WeightInfo: WeightInfo;170 }171172 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);173174 #[pallet::pallet]175 #[pallet::storage_version(STORAGE_VERSION)]176 #[pallet::generate_store(pub(super) trait Store)]177 pub struct Pallet<T>(_);178179 /// Amount of tokens minted for collection.180 #[pallet::storage]181 pub type TokensMinted<T: Config> =182 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;183184 /// Amount of burnt tokens for collection.185 #[pallet::storage]186 pub type TokensBurnt<T: Config> =187 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;188189 /// Custom data serialized to bytes for token.190 #[pallet::storage]191 pub type TokenData<T: Config> = StorageNMap<192 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),193 Value = ItemData<T::CrossAccountId>,194 QueryKind = OptionQuery,195 >;196197 /// Key-Value map stored for token.198 #[pallet::storage]199 #[pallet::getter(fn token_properties)]200 pub type TokenProperties<T: Config> = StorageNMap<201 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),202 Value = Properties,203 QueryKind = ValueQuery,204 OnEmpty = up_data_structs::TokenProperties,205 >;206207 /// Custom data that is serialized to bytes and attached to a token property.208 /// Currently used to store RMRK data.209 #[pallet::storage]210 #[pallet::getter(fn token_aux_property)]211 pub type TokenAuxProperties<T: Config> = StorageNMap<212 Key = (213 Key<Twox64Concat, CollectionId>,214 Key<Twox64Concat, TokenId>,215 Key<Twox64Concat, PropertyScope>,216 Key<Twox64Concat, PropertyKey>,217 ),218 Value = AuxPropertyValue,219 QueryKind = OptionQuery,220 >;221222 /// Used to enumerate tokens owned by account.223 #[pallet::storage]224 pub type Owned<T: Config> = StorageNMap<225 Key = (226 Key<Twox64Concat, CollectionId>,227 Key<Blake2_128Concat, T::CrossAccountId>,228 Key<Twox64Concat, TokenId>,229 ),230 Value = bool,231 QueryKind = ValueQuery,232 >;233234 /// Used to enumerate token's children.235 #[pallet::storage]236 #[pallet::getter(fn token_children)]237 pub type TokenChildren<T: Config> = StorageNMap<238 Key = (239 Key<Twox64Concat, CollectionId>,240 Key<Twox64Concat, TokenId>,241 Key<Twox64Concat, (CollectionId, TokenId)>,242 ),243 Value = bool,244 QueryKind = ValueQuery,245 >;246247 /// Amount of tokens owned by account.248 #[pallet::storage]249 pub type AccountBalance<T: Config> = StorageNMap<250 Key = (251 Key<Twox64Concat, CollectionId>,252 Key<Blake2_128Concat, T::CrossAccountId>,253 ),254 Value = u32,255 QueryKind = ValueQuery,256 >;257258 /// Allowance set by an owner for a spender for a token.259 #[pallet::storage]260 pub type Allowance<T: Config> = StorageNMap<261 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),262 Value = T::CrossAccountId,263 QueryKind = OptionQuery,264 >;265266 #[pallet::hooks]267 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {268 fn on_runtime_upgrade() -> Weight {269 if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {270 let mut had_consts = BTreeSet::new();271 <TokenData<T>>::translate::<ItemDataVersion1<T::CrossAccountId>, _>(272 |(collection, token), v| {273 let mut props = vec![];274 if !v.const_data.is_empty() {275 props.push(Property {276 key: b"_old_constData".to_vec().try_into().unwrap(),277 value: v278 .const_data279 .clone()280 .into_inner()281 .try_into()282 .expect("const too long"),283 });284 had_consts.insert(collection);285 }286 if !v.variable_data.is_empty() {287 props.push(Property {288 key: b"_old_variableData".to_vec().try_into().unwrap(),289 value: v290 .variable_data291 .clone()292 .into_inner()293 .try_into()294 .expect("variable too long"),295 })296 }297 if !props.is_empty() {298 Self::set_scoped_token_properties(299 collection,300 token,301 PropertyScope::None,302 props.into_iter(),303 )304 .expect("existing token data exceeds property storage");305 }306 Some(<ItemDataVersion2<T::CrossAccountId>>::from(v))307 },308 );309 for collection in had_consts {310 <PalletCommon<T>>::set_property_permission_unchecked(311 collection,312 PropertyKeyPermission {313 key: b"_old_constData".to_vec().try_into().unwrap(),314 permission: PropertyPermission {315 mutable: false,316 collection_admin: true,317 token_owner: false,318 },319 },320 )321 .expect("failed to configure permission");322 }323 }324325 0326 }327 }328}329330pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);331impl<T: Config> NonfungibleHandle<T> {332 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {333 Self(inner)334 }335 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {336 self.0337 }338 pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {339 &mut self.0340 }341}342impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {343 fn recorder(&self) -> &SubstrateRecorder<T> {344 self.0.recorder()345 }346 fn into_recorder(self) -> SubstrateRecorder<T> {347 self.0.into_recorder()348 }349}350impl<T: Config> Deref for NonfungibleHandle<T> {351 type Target = pallet_common::CollectionHandle<T>;352353 fn deref(&self) -> &Self::Target {354 &self.0355 }356}357358impl<T: Config> Pallet<T> {359 /// Get number of NFT tokens in collection.360 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {361 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)362 }363364 /// Check that NFT token exists.365 ///366 /// - `token`: Token ID.367 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {368 <TokenData<T>>::contains_key((collection.id, token))369 }370371 /// Set the token property with the scope.372 ///373 /// - `property`: Contains key-value pair.374 pub fn set_scoped_token_property(375 collection_id: CollectionId,376 token_id: TokenId,377 scope: PropertyScope,378 property: Property,379 ) -> DispatchResult {380 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {381 properties.try_scoped_set(scope, property.key, property.value)382 })383 .map_err(<CommonError<T>>::from)?;384385 Ok(())386 }387388 /// Batch operation to set multiple properties with the same scope.389 pub fn set_scoped_token_properties(390 collection_id: CollectionId,391 token_id: TokenId,392 scope: PropertyScope,393 properties: impl Iterator<Item = Property>,394 ) -> DispatchResult {395 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {396 stored_properties.try_scoped_set_from_iter(scope, properties)397 })398 .map_err(<CommonError<T>>::from)?;399400 Ok(())401 }402403 /// Add or edit auxiliary data for the property.404 ///405 /// - `f`: function that adds or edits auxiliary data.406 pub fn try_mutate_token_aux_property<R, E>(407 collection_id: CollectionId,408 token_id: TokenId,409 scope: PropertyScope,410 key: PropertyKey,411 f: impl FnOnce(&mut Option<AuxPropertyValue>) -> Result<R, E>,412 ) -> Result<R, E> {413 <TokenAuxProperties<T>>::try_mutate((collection_id, token_id, scope, key), f)414 }415416 /// Remove auxiliary data for the property.417 pub fn remove_token_aux_property(418 collection_id: CollectionId,419 token_id: TokenId,420 scope: PropertyScope,421 key: PropertyKey,422 ) {423 <TokenAuxProperties<T>>::remove((collection_id, token_id, scope, key));424 }425426 /// Get all auxiliary data in a given scope.427 ///428 /// Returns iterator over Property Key - Data pairs.429 pub fn iterate_token_aux_properties(430 collection_id: CollectionId,431 token_id: TokenId,432 scope: PropertyScope,433 ) -> impl Iterator<Item = (PropertyKey, AuxPropertyValue)> {434 <TokenAuxProperties<T>>::iter_prefix((collection_id, token_id, scope))435 }436437 /// Get ID of the last minted token438 pub fn current_token_id(collection_id: CollectionId) -> TokenId {439 TokenId(<TokensMinted<T>>::get(collection_id))440 }441}442443// unchecked calls skips any permission checks444impl<T: Config> Pallet<T> {445 /// Create NFT collection446 ///447 /// `init_collection` will take non-refundable deposit for collection creation.448 ///449 /// - `data`: Contains settings for collection limits and permissions.450 pub fn init_collection(451 owner: T::CrossAccountId,452 data: CreateCollectionData<T::AccountId>,453 is_external: bool,454 ) -> Result<CollectionId, DispatchError> {455 <PalletCommon<T>>::init_collection(owner, data, is_external)456 }457458 /// Destroy NFT collection459 ///460 /// `destroy_collection` will throw error if collection contains any tokens.461 /// Only owner can destroy collection.462 pub fn destroy_collection(463 collection: NonfungibleHandle<T>,464 sender: &T::CrossAccountId,465 ) -> DispatchResult {466 let id = collection.id;467468 if Self::collection_has_tokens(id) {469 return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());470 }471472 // =========473474 PalletCommon::destroy_collection(collection.0, sender)?;475476 <TokenData<T>>::remove_prefix((id,), None);477 <TokenChildren<T>>::remove_prefix((id,), None);478 <Owned<T>>::remove_prefix((id,), None);479 <TokensMinted<T>>::remove(id);480 <TokensBurnt<T>>::remove(id);481 <Allowance<T>>::remove_prefix((id,), None);482 <AccountBalance<T>>::remove_prefix((id,), None);483 Ok(())484 }485486 /// Burn NFT token487 ///488 /// `burn` removes `token` from the `collection`, from it's owner and from the parent token489 /// if the token is nested.490 /// Only the owner can `burn` the token. The `token` shouldn't have any nested tokens.491 /// Also removes all corresponding properties and auxiliary properties.492 ///493 /// - `token`: Token that should be burned494 /// - `collection`: Collection that contains the token495 pub fn burn(496 collection: &NonfungibleHandle<T>,497 sender: &T::CrossAccountId,498 token: TokenId,499 ) -> DispatchResult {500 let token_data =501 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;502 ensure!(&token_data.owner == sender, <CommonError<T>>::NoPermission);503504 if collection.permissions.access() == AccessMode::AllowList {505 collection.check_allowlist(sender)?;506 }507508 if Self::token_has_children(collection.id, token) {509 return Err(<Error<T>>::CantBurnNftWithChildren.into());510 }511512 let burnt = <TokensBurnt<T>>::get(collection.id)513 .checked_add(1)514 .ok_or(ArithmeticError::Overflow)?;515516 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))517 .checked_sub(1)518 .ok_or(ArithmeticError::Overflow)?;519520 // =========521522 if balance == 0 {523 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));524 } else {525 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);526 }527528 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);529530 <Owned<T>>::remove((collection.id, &token_data.owner, token));531 <TokensBurnt<T>>::insert(collection.id, burnt);532 <TokenData<T>>::remove((collection.id, token));533 <TokenProperties<T>>::remove((collection.id, token));534 <TokenAuxProperties<T>>::remove_prefix((collection.id, token), None);535 let old_spender = <Allowance<T>>::take((collection.id, token));536537 if let Some(old_spender) = old_spender {538 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(539 collection.id,540 token,541 token_data.owner.clone(),542 old_spender,543 0,544 ));545 }546547 <PalletEvm<T>>::deposit_log(548 ERC721Events::Transfer {549 from: *token_data.owner.as_eth(),550 to: H160::default(),551 token_id: token.into(),552 }553 .to_log(collection_id_to_address(collection.id)),554 );555 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(556 collection.id,557 token,558 token_data.owner,559 1,560 ));561 Ok(())562 }563564 /// Same as [`burn`] but burns all the tokens that are nested in the token first565 ///566 /// - `self_budget`: Limit for searching children in depth.567 /// - `breadth_budget`: Limit of breadth of searching children.568 ///569 /// [`burn`]: struct.Pallet.html#method.burn570 #[transactional]571 pub fn burn_recursively(572 collection: &NonfungibleHandle<T>,573 sender: &T::CrossAccountId,574 token: TokenId,575 self_budget: &dyn Budget,576 breadth_budget: &dyn Budget,577 ) -> DispatchResultWithPostInfo {578 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);579580 let current_token_account =581 T::CrossTokenAddressMapping::token_to_address(collection.id, token);582583 let mut weight = 0 as Weight;584585 // This method is transactional, if user in fact doesn't have permissions to remove token -586 // tokens removed here will be restored after rejected transaction587 for ((collection, token), _) in <TokenChildren<T>>::iter_prefix((collection.id, token)) {588 ensure!(breadth_budget.consume(), <StructureError<T>>::BreadthLimit,);589 let PostDispatchInfo { actual_weight, .. } =590 <PalletStructure<T>>::burn_item_recursively(591 current_token_account.clone(),592 collection,593 token,594 self_budget,595 breadth_budget,596 )?;597 if let Some(actual_weight) = actual_weight {598 weight = weight.saturating_add(actual_weight);599 }600 }601602 Self::burn(collection, sender, token)?;603 DispatchResultWithPostInfo::Ok(PostDispatchInfo {604 actual_weight: Some(weight + <SelfWeightOf<T>>::burn_item()),605 pays_fee: Pays::Yes,606 })607 }608609 /// Batch operation to add, edit or remove properties for the token610 ///611 /// All affected properties should have mutable permission and sender should have612 /// permission to edit those properties.613 ///614 /// - `nesting_budget`: Limit for searching parents in depth to check ownership.615 /// - `is_token_create`: Indicates that method is called during token initialization.616 /// Allows to bypass ownership check.617 #[transactional]618 fn modify_token_properties(619 collection: &NonfungibleHandle<T>,620 sender: &T::CrossAccountId,621 token_id: TokenId,622 properties: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,623 is_token_create: bool,624 nesting_budget: &dyn Budget,625 ) -> DispatchResult {626 let mut collection_admin_status = None;627 let mut token_owner_result = None;628629 let mut is_collection_admin =630 || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));631632 let mut is_token_owner = || {633 *token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {634 let is_owned = <PalletStructure<T>>::check_indirectly_owned(635 sender.clone(),636 collection.id,637 token_id,638 None,639 nesting_budget,640 )?;641642 Ok(is_owned)643 })644 };645646 for (key, value) in properties {647 let permission = <PalletCommon<T>>::property_permissions(collection.id)648 .get(&key)649 .cloned()650 .unwrap_or_else(PropertyPermission::none);651652 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))653 .get(&key)654 .is_some();655656 match permission {657 PropertyPermission { mutable: false, .. } if is_property_exists => {658 return Err(<CommonError<T>>::NoPermission.into());659 }660661 PropertyPermission {662 collection_admin,663 token_owner,664 ..665 } => {666 //TODO: investigate threats during public minting.667 if is_token_create && (collection_admin || token_owner) && value.is_some() {668 // Pass669 } else if collection_admin && is_collection_admin() {670 // Pass671 } else if token_owner && is_token_owner()? {672 // Pass673 } else {674 fail!(<CommonError<T>>::NoPermission);675 }676 }677 }678679 match value {680 Some(value) => {681 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {682 properties.try_set(key.clone(), value)683 })684 .map_err(<CommonError<T>>::from)?;685686 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(687 collection.id,688 token_id,689 key,690 ));691 }692 None => {693 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {694 properties.remove(&key)695 })696 .map_err(<CommonError<T>>::from)?;697698 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(699 collection.id,700 token_id,701 key,702 ));703 }704 }705 }706707 Ok(())708 }709710 /// Batch operation to add or edit properties for the token711 ///712 /// Same as [`modify_token_properties`] but doesn't allow to remove properties713 ///714 /// [`modify_token_properties`]: struct.Pallet.html#method.modify_token_properties715 pub fn set_token_properties(716 collection: &NonfungibleHandle<T>,717 sender: &T::CrossAccountId,718 token_id: TokenId,719 properties: impl Iterator<Item = Property>,720 is_token_create: bool,721 nesting_budget: &dyn Budget,722 ) -> DispatchResult {723 Self::modify_token_properties(724 collection,725 sender,726 token_id,727 properties.map(|p| (p.key, Some(p.value))),728 is_token_create,729 nesting_budget,730 )731 }732733 /// Add or edit single property for the token734 ///735 /// Calls [`set_token_properties`] internally736 ///737 /// [`set_token_properties`]: struct.Pallet.html#method.set_token_properties738 pub fn set_token_property(739 collection: &NonfungibleHandle<T>,740 sender: &T::CrossAccountId,741 token_id: TokenId,742 property: Property,743 nesting_budget: &dyn Budget,744 ) -> DispatchResult {745 let is_token_create = false;746747 Self::set_token_properties(748 collection,749 sender,750 token_id,751 [property].into_iter(),752 is_token_create,753 nesting_budget,754 )755 }756757 /// Batch operation to remove properties from the token758 ///759 /// Same as [`modify_token_properties`] but doesn't allow to add or edit properties760 ///761 /// [`modify_token_properties`]: struct.Pallet.html#method.modify_token_properties762 pub fn delete_token_properties(763 collection: &NonfungibleHandle<T>,764 sender: &T::CrossAccountId,765 token_id: TokenId,766 property_keys: impl Iterator<Item = PropertyKey>,767 nesting_budget: &dyn Budget,768 ) -> DispatchResult {769 let is_token_create = false;770771 Self::modify_token_properties(772 collection,773 sender,774 token_id,775 property_keys.into_iter().map(|key| (key, None)),776 is_token_create,777 nesting_budget,778 )779 }780781 /// Remove single property from the token782 ///783 /// Calls [`delete_token_properties`] internally784 ///785 /// [`delete_token_properties`]: struct.Pallet.html#method.delete_token_properties786 pub fn delete_token_property(787 collection: &NonfungibleHandle<T>,788 sender: &T::CrossAccountId,789 token_id: TokenId,790 property_key: PropertyKey,791 nesting_budget: &dyn Budget,792 ) -> DispatchResult {793 Self::delete_token_properties(794 collection,795 sender,796 token_id,797 [property_key].into_iter(),798 nesting_budget,799 )800 }801802 /// Add or edit properties for the collection803 pub fn set_collection_properties(804 collection: &NonfungibleHandle<T>,805 sender: &T::CrossAccountId,806 properties: Vec<Property>,807 ) -> DispatchResult {808 <PalletCommon<T>>::set_collection_properties(collection, sender, properties)809 }810811 /// Remove properties from the collection812 pub fn delete_collection_properties(813 collection: &CollectionHandle<T>,814 sender: &T::CrossAccountId,815 property_keys: Vec<PropertyKey>,816 ) -> DispatchResult {817 <PalletCommon<T>>::delete_collection_properties(collection, sender, property_keys)818 }819820 /// Set property permissions for the token.821 ///822 /// Sender should be the owner or admin of token's collection.823 pub fn set_token_property_permissions(824 collection: &CollectionHandle<T>,825 sender: &T::CrossAccountId,826 property_permissions: Vec<PropertyKeyPermission>,827 ) -> DispatchResult {828 <PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)829 }830831 /// Set property permissions for the collection.832 ///833 /// Sender should be the owner or admin of the collection.834 pub fn set_property_permission(835 collection: &CollectionHandle<T>,836 sender: &T::CrossAccountId,837 permission: PropertyKeyPermission,838 ) -> DispatchResult {839 <PalletCommon<T>>::set_property_permission(collection, sender, permission)840 }841842 /// Transfer NFT token from one account to another.843 ///844 /// `from` account stops being the owner and `to` account becomes the owner of the token.845 /// If `to` is token than `to` becomes owner of the token and the token become nested.846 /// Unnests token from previous parent if it was nested before.847 /// Removes allowance for the token if there was any.848 /// Throws if transfers aren't allowed for collection or if receiver reached token ownership limit.849 ///850 /// - `nesting_budget`: Limit for token nesting depth851 pub fn transfer(852 collection: &NonfungibleHandle<T>,853 from: &T::CrossAccountId,854 to: &T::CrossAccountId,855 token: TokenId,856 nesting_budget: &dyn Budget,857 ) -> DispatchResult {858 ensure!(859 collection.limits.transfers_enabled(),860 <CommonError<T>>::TransferNotAllowed861 );862863 let token_data =864 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;865 ensure!(&token_data.owner == from, <CommonError<T>>::NoPermission);866867 if collection.permissions.access() == AccessMode::AllowList {868 collection.check_allowlist(from)?;869 collection.check_allowlist(to)?;870 }871 <PalletCommon<T>>::ensure_correct_receiver(to)?;872873 let balance_from = <AccountBalance<T>>::get((collection.id, from))874 .checked_sub(1)875 .ok_or(<CommonError<T>>::TokenValueTooLow)?;876 let balance_to = if from != to {877 let balance_to = <AccountBalance<T>>::get((collection.id, to))878 .checked_add(1)879 .ok_or(ArithmeticError::Overflow)?;880881 ensure!(882 balance_to < collection.limits.account_token_ownership_limit(),883 <CommonError<T>>::AccountTokenLimitExceeded,884 );885886 Some(balance_to)887 } else {888 None889 };890891 <PalletStructure<T>>::nest_if_sent_to_token(892 from.clone(),893 to,894 collection.id,895 token,896 nesting_budget,897 )?;898899 // =========900901 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);902903 <TokenData<T>>::insert(904 (collection.id, token),905 ItemData {906 owner: to.clone(),907 ..token_data908 },909 );910911 if let Some(balance_to) = balance_to {912 // from != to913 if balance_from == 0 {914 <AccountBalance<T>>::remove((collection.id, from));915 } else {916 <AccountBalance<T>>::insert((collection.id, from), balance_from);917 }918 <AccountBalance<T>>::insert((collection.id, to), balance_to);919 <Owned<T>>::remove((collection.id, from, token));920 <Owned<T>>::insert((collection.id, to, token), true);921 }922 Self::set_allowance_unchecked(collection, from, token, None, true);923924 <PalletEvm<T>>::deposit_log(925 ERC721Events::Transfer {926 from: *from.as_eth(),927 to: *to.as_eth(),928 token_id: token.into(),929 }930 .to_log(collection_id_to_address(collection.id)),931 );932 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(933 collection.id,934 token,935 from.clone(),936 to.clone(),937 1,938 ));939 Ok(())940 }941942 /// Batch operation to mint multiple NFT tokens.943 ///944 /// The sender should be the owner/admin of the collection or collection should be configured945 /// to allow public minting.946 /// Throws if amount of tokens reached it's limit for the collection or if caller reached947 /// token ownership limit.948 ///949 /// - `data`: Contains list of token properties and users who will become the owners of the950 /// corresponging tokens.951 /// - `nesting_budget`: Limit for token nesting depth952 pub fn create_multiple_items(953 collection: &NonfungibleHandle<T>,954 sender: &T::CrossAccountId,955 data: Vec<CreateItemData<T>>,956 nesting_budget: &dyn Budget,957 ) -> DispatchResult {958 if !collection.is_owner_or_admin(sender) {959 ensure!(960 collection.permissions.mint_mode(),961 <CommonError<T>>::PublicMintingNotAllowed962 );963 collection.check_allowlist(sender)?;964965 for item in data.iter() {966 collection.check_allowlist(&item.owner)?;967 }968 }969970 for data in data.iter() {971 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;972 }973974 let first_token = <TokensMinted<T>>::get(collection.id);975 let tokens_minted = first_token976 .checked_add(data.len() as u32)977 .ok_or(ArithmeticError::Overflow)?;978 ensure!(979 tokens_minted <= collection.limits.token_limit(),980 <CommonError<T>>::CollectionTokenLimitExceeded981 );982983 let mut balances = BTreeMap::new();984 for data in &data {985 let balance = balances986 .entry(&data.owner)987 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));988 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;989990 ensure!(991 *balance <= collection.limits.account_token_ownership_limit(),992 <CommonError<T>>::AccountTokenLimitExceeded,993 );994 }995996 for (i, data) in data.iter().enumerate() {997 let token = TokenId(first_token + i as u32 + 1);998999 <PalletStructure<T>>::check_nesting(1000 sender.clone(),1001 &data.owner,1002 collection.id,1003 token,1004 nesting_budget,1005 )?;1006 }10071008 // =========10091010 with_transaction(|| {1011 for (i, data) in data.iter().enumerate() {1012 let token = first_token + i as u32 + 1;10131014 <TokenData<T>>::insert(1015 (collection.id, token),1016 ItemData {1017 // const_data: data.const_data.clone(),1018 owner: data.owner.clone(),1019 },1020 );10211022 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(1023 &data.owner,1024 collection.id,1025 TokenId(token),1026 );10271028 if let Err(e) = Self::set_token_properties(1029 collection,1030 sender,1031 TokenId(token),1032 data.properties.clone().into_iter(),1033 true,1034 nesting_budget,1035 ) {1036 return TransactionOutcome::Rollback(Err(e));1037 }1038 }1039 TransactionOutcome::Commit(Ok(()))1040 })?;10411042 <TokensMinted<T>>::insert(collection.id, tokens_minted);1043 for (account, balance) in balances {1044 <AccountBalance<T>>::insert((collection.id, account), balance);1045 }1046 for (i, data) in data.into_iter().enumerate() {1047 let token = first_token + i as u32 + 1;1048 <Owned<T>>::insert((collection.id, &data.owner, token), true);10491050 <PalletEvm<T>>::deposit_log(1051 ERC721Events::Transfer {1052 from: H160::default(),1053 to: *data.owner.as_eth(),1054 token_id: token.into(),1055 }1056 .to_log(collection_id_to_address(collection.id)),1057 );1058 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(1059 collection.id,1060 TokenId(token),1061 data.owner.clone(),1062 1,1063 ));1064 }1065 Ok(())1066 }10671068 pub fn set_allowance_unchecked(1069 collection: &NonfungibleHandle<T>,1070 sender: &T::CrossAccountId,1071 token: TokenId,1072 spender: Option<&T::CrossAccountId>,1073 assume_implicit_eth: bool,1074 ) {1075 if let Some(spender) = spender {1076 let old_spender = <Allowance<T>>::get((collection.id, token));1077 <Allowance<T>>::insert((collection.id, token), spender);1078 // In ERC721 there is only one possible approved user of token, so we set1079 // approved user to spender1080 <PalletEvm<T>>::deposit_log(1081 ERC721Events::Approval {1082 owner: *sender.as_eth(),1083 approved: *spender.as_eth(),1084 token_id: token.into(),1085 }1086 .to_log(collection_id_to_address(collection.id)),1087 );1088 // In Unique chain, any token can have any amount of approved users, so we need to1089 // set allowance of old owner to 0, and allowance of new owner to 11090 if old_spender.as_ref() != Some(spender) {1091 if let Some(old_owner) = old_spender {1092 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1093 collection.id,1094 token,1095 sender.clone(),1096 old_owner,1097 0,1098 ));1099 }1100 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1101 collection.id,1102 token,1103 sender.clone(),1104 spender.clone(),1105 1,1106 ));1107 }1108 } else {1109 let old_spender = <Allowance<T>>::take((collection.id, token));1110 if !assume_implicit_eth {1111 // In ERC721 there is only one possible approved user of token, so we set1112 // approved user to zero address1113 <PalletEvm<T>>::deposit_log(1114 ERC721Events::Approval {1115 owner: *sender.as_eth(),1116 approved: H160::default(),1117 token_id: token.into(),1118 }1119 .to_log(collection_id_to_address(collection.id)),1120 );1121 }1122 // In Unique chain, any token can have any amount of approved users, so we need to1123 // set allowance of old owner to 01124 if let Some(old_spender) = old_spender {1125 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1126 collection.id,1127 token,1128 sender.clone(),1129 old_spender,1130 0,1131 ));1132 }1133 }1134 }11351136 /// Set allowance for the spender to `transfer` or `burn` sender's token.1137 ///1138 /// - `token`: Token the spender is allowed to `transfer` or `burn`.1139 pub fn set_allowance(1140 collection: &NonfungibleHandle<T>,1141 sender: &T::CrossAccountId,1142 token: TokenId,1143 spender: Option<&T::CrossAccountId>,1144 ) -> DispatchResult {1145 if collection.permissions.access() == AccessMode::AllowList {1146 collection.check_allowlist(sender)?;1147 if let Some(spender) = spender {1148 collection.check_allowlist(spender)?;1149 }1150 }11511152 if let Some(spender) = spender {1153 <PalletCommon<T>>::ensure_correct_receiver(spender)?;1154 }11551156 let token_data =1157 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;1158 if &token_data.owner != sender {1159 ensure!(1160 collection.ignores_owned_amount(sender),1161 <CommonError<T>>::CantApproveMoreThanOwned1162 );1163 }11641165 // =========11661167 Self::set_allowance_unchecked(collection, sender, token, spender, false);1168 Ok(())1169 }11701171 /// Checks allowance for the spender to use the token.1172 fn check_allowed(1173 collection: &NonfungibleHandle<T>,1174 spender: &T::CrossAccountId,1175 from: &T::CrossAccountId,1176 token: TokenId,1177 nesting_budget: &dyn Budget,1178 ) -> DispatchResult {1179 if spender.conv_eq(from) {1180 return Ok(());1181 }1182 if collection.permissions.access() == AccessMode::AllowList {1183 // `from`, `to` checked in [`transfer`]1184 collection.check_allowlist(spender)?;1185 }11861187 if collection.limits.owner_can_transfer() && collection.is_owner_or_admin(spender) {1188 return Ok(());1189 }11901191 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1192 ensure!(1193 <PalletStructure<T>>::check_indirectly_owned(1194 spender.clone(),1195 source.0,1196 source.1,1197 None,1198 nesting_budget1199 )?,1200 <CommonError<T>>::ApprovedValueTooLow,1201 );1202 return Ok(());1203 }1204 if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {1205 return Ok(());1206 }1207 ensure!(1208 collection.ignores_allowance(spender),1209 <CommonError<T>>::ApprovedValueTooLow1210 );1211 Ok(())1212 }12131214 /// Transfer NFT token from one account to another.1215 ///1216 /// Same as the [`transfer`] but spender doesn't needs to be the owner of the token.1217 /// The owner should set allowance for the spender to transfer token.1218 ///1219 /// [`transfer`]: struct.Pallet.html#method.transfer1220 pub fn transfer_from(1221 collection: &NonfungibleHandle<T>,1222 spender: &T::CrossAccountId,1223 from: &T::CrossAccountId,1224 to: &T::CrossAccountId,1225 token: TokenId,1226 nesting_budget: &dyn Budget,1227 ) -> DispatchResult {1228 Self::check_allowed(collection, spender, from, token, nesting_budget)?;12291230 // =========12311232 // Allowance is reset in [`transfer`]1233 Self::transfer(collection, from, to, token, nesting_budget)1234 }12351236 /// Burn NFT token for `from` account.1237 ///1238 /// Same as the [`burn`] but spender doesn't need to be an owner of the token. The owner should1239 /// set allowance for the spender to burn token.1240 ///1241 /// [`burn`]: struct.Pallet.html#method.burn1242 pub fn burn_from(1243 collection: &NonfungibleHandle<T>,1244 spender: &T::CrossAccountId,1245 from: &T::CrossAccountId,1246 token: TokenId,1247 nesting_budget: &dyn Budget,1248 ) -> DispatchResult {1249 Self::check_allowed(collection, spender, from, token, nesting_budget)?;12501251 // =========12521253 Self::burn(collection, from, token)1254 }12551256 /// Check that `from` token could be nested in `under` token.1257 ///1258 pub fn check_nesting(1259 handle: &NonfungibleHandle<T>,1260 sender: T::CrossAccountId,1261 from: (CollectionId, TokenId),1262 under: TokenId,1263 nesting_budget: &dyn Budget,1264 ) -> DispatchResult {1265 let nesting = handle.permissions.nesting();12661267 #[cfg(not(feature = "runtime-benchmarks"))]1268 let permissive = false;1269 #[cfg(feature = "runtime-benchmarks")]1270 let permissive = nesting.permissive;12711272 if permissive {1273 // Pass1274 } else if nesting.token_owner1275 && <PalletStructure<T>>::check_indirectly_owned(1276 sender.clone(),1277 handle.id,1278 under,1279 Some(from),1280 nesting_budget,1281 )? {1282 // Pass1283 } else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {1284 // Pass1285 } else {1286 fail!(<CommonError<T>>::UserIsNotAllowedToNest);1287 }12881289 if let Some(whitelist) = &nesting.restricted {1290 ensure!(1291 whitelist.contains(&from.0),1292 <CommonError<T>>::SourceCollectionIsNotAllowedToNest1293 );1294 }1295 Ok(())1296 }12971298 fn nest(under: (CollectionId, TokenId), to_nest: (CollectionId, TokenId)) {1299 <TokenChildren<T>>::insert((under.0, under.1, (to_nest.0, to_nest.1)), true);1300 }13011302 fn unnest(under: (CollectionId, TokenId), to_unnest: (CollectionId, TokenId)) {1303 <TokenChildren<T>>::remove((under.0, under.1, to_unnest));1304 }13051306 fn collection_has_tokens(collection_id: CollectionId) -> bool {1307 <TokenData<T>>::iter_prefix((collection_id,))1308 .next()1309 .is_some()1310 }13111312 fn token_has_children(collection_id: CollectionId, token_id: TokenId) -> bool {1313 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1314 .next()1315 .is_some()1316 }13171318 pub fn token_children_ids(collection_id: CollectionId, token_id: TokenId) -> Vec<TokenChild> {1319 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1320 .map(|((child_collection_id, child_id), _)| TokenChild {1321 collection: child_collection_id,1322 token: child_id,1323 })1324 .collect()1325 }13261327 /// Mint single NFT token.1328 ///1329 /// Delegated to [`create_multiple_items`]1330 ///1331 /// [`create_multiple_items`]: struct.Pallet.html#method.create_multiple_items1332 pub fn create_item(1333 collection: &NonfungibleHandle<T>,1334 sender: &T::CrossAccountId,1335 data: CreateItemData<T>,1336 nesting_budget: &dyn Budget,1337 ) -> DispatchResult {1338 Self::create_multiple_items(collection, sender, vec![data], nesting_budget)1339 }1340}1// 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//! # Nonfungible Pallet18//!19//! The Nonfungible pallet provides functionality for handling nonfungible collections and tokens.20//!21//! - [`Config`]22//! - [`NonfungibleHandle`]23//! - [`Pallet`]24//! - [`CommonWeights`](common::CommonWeights)25//!26//! ## Overview27//!28//! The Nonfungible pallet provides functions for:29//!30//! - NFT collection creation and removal31//! - Minting and burning of NFT tokens32//! - Retrieving account balances33//! - Transfering NFT tokens34//! - Setting and checking allowance for NFT tokens35//! - Setting properties and permissions for NFT collections and tokens36//! - Nesting and unnesting tokens37//!38//! ### Terminology39//!40//! - **NFT token:** Non fungible token.41//!42//! - **NFT Collection:** A collection of NFT tokens. All NFT tokens are part of a collection.43//! Each collection can define it's own properties, properties for it's tokens and set of permissions.44//!45//! - **Balance:** Number of NFT tokens owned by an account46//!47//! - **Allowance:** NFT tokens owned by one account that another account is allowed to make operations on48//!49//! - **Burning:** The process of “deleting” a token from a collection and from50//! an account balance of the owner.51//!52//! - **Nesting:** Setting up parent-child relationship between tokens. Nested tokens are inhereting53//! owner from their parent. There could be multiple levels of nesting. Token couldn't be nested in54//! it's child token i.e. parent-child relationship graph shouldn't have cycles.55//!56//! - **Properties:** Key-Values pairs. Token properties are attached to a token. Collection properties are57//! attached to a collection. Set of permissions could be defined for each property.58//!59//! ### Implementations60//!61//! The Nonfungible pallet provides implementations for the following traits. If these traits provide62//! the functionality that you need, then you can avoid coupling with the Nonfungible pallet.63//!64//! - [`CommonWeightInfo`](pallet_common::CommonWeightInfo): Functions for retrieval of transaction weight65//! - [`CommonCollectionOperations`](pallet_common::CommonCollectionOperations): Functions for dealing66//! with collections67//!68//! ## Interface69//!70//! ### Dispatchable Functions71//!72//! - `init_collection` - Create NFT collection. NFT collection can be configured to allow or deny access for73//! some accounts.74//! - `destroy_collection` - Destroy exising NFT collection. There should be no tokens in the collection.75//! - `burn` - Burn NFT token owned by account.76//! - `transfer` - Transfer NFT token. Transfers should be enabled for NFT collection.77//! Nests the NFT token if it is sent to another token.78//! - `create_item` - Mint NFT token in collection. Sender should have permission to mint tokens.79//! - `set_allowance` - Set allowance for another account.80//! - `set_token_property` - Set token property value.81//! - `delete_token_property` - Remove property from the token.82//! - `set_collection_properties` - Set collection properties.83//! - `delete_collection_properties` - Remove properties from the collection.84//! - `set_property_permission` - Set collection property permission.85//! - `set_token_property_permissions` - Set token property permissions.86//!87//! ## Assumptions88//!89//! * To perform operations on tokens sender should be in collection's allow list if collection access mode is `AllowList`.9091#![cfg_attr(not(feature = "std"), no_std)]9293use erc::ERC721Events;94use evm_coder::ToLog;95use frame_support::{96 BoundedVec, ensure, fail, transactional,97 storage::with_transaction,98 pallet_prelude::DispatchResultWithPostInfo,99 pallet_prelude::Weight,100 weights::{PostDispatchInfo, Pays},101};102use up_data_structs::{103 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,104 mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey,105 PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild,106 AuxPropertyValue,107};108use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};109use pallet_common::{110 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,111 eth::collection_id_to_address,112};113use pallet_structure::{Pallet as PalletStructure, Error as StructureError};114use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};115use sp_core::H160;116use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};117use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap, collections::btree_set::BTreeSet};118use core::ops::Deref;119use codec::{Encode, Decode, MaxEncodedLen};120use scale_info::TypeInfo;121122pub use pallet::*;123use weights::WeightInfo;124#[cfg(feature = "runtime-benchmarks")]125pub mod benchmarking;126pub mod common;127pub mod erc;128pub mod weights;129130pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;131pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;132133/// Token data, stored independently from other data used to describe it134/// for the convenience of database access. Notably contains the owner account address.135#[struct_versioning::versioned(version = 2, upper)]136#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]137pub struct ItemData<CrossAccountId> {138 #[version(..2)]139 pub const_data: BoundedVec<u8, CustomDataLimit>,140141 #[version(..2)]142 pub variable_data: BoundedVec<u8, CustomDataLimit>,143144 pub owner: CrossAccountId,145}146147#[frame_support::pallet]148pub mod pallet {149 use super::*;150 use frame_support::{151 Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion,152 };153 use frame_system::pallet_prelude::*;154 use up_data_structs::{CollectionId, TokenId};155 use super::weights::WeightInfo;156157 #[pallet::error]158 pub enum Error<T> {159 /// Not Nonfungible item data used to mint in Nonfungible collection.160 NotNonfungibleDataUsedToMintFungibleCollectionToken,161 /// Used amount > 1 with NFT162 NonfungibleItemsHaveNoAmount,163 /// Unable to burn NFT with children164 CantBurnNftWithChildren,165 }166167 #[pallet::config]168 pub trait Config:169 frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config170 {171 type WeightInfo: WeightInfo;172 }173174 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);175176 #[pallet::pallet]177 #[pallet::storage_version(STORAGE_VERSION)]178 #[pallet::generate_store(pub(super) trait Store)]179 pub struct Pallet<T>(_);180181 /// Total amount of minted tokens in a collection.182 #[pallet::storage]183 pub type TokensMinted<T: Config> =184 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;185186 /// Amount of burnt tokens in a collection.187 #[pallet::storage]188 pub type TokensBurnt<T: Config> =189 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;190191 /// Token data, used to partially describe a token.192 #[pallet::storage]193 pub type TokenData<T: Config> = StorageNMap<194 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),195 Value = ItemData<T::CrossAccountId>,196 QueryKind = OptionQuery,197 >;198199 /// Map of key-value pairs, describing the metadata of a token.200 #[pallet::storage]201 #[pallet::getter(fn token_properties)]202 pub type TokenProperties<T: Config> = StorageNMap<203 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),204 Value = Properties,205 QueryKind = ValueQuery,206 OnEmpty = up_data_structs::TokenProperties,207 >;208209 /// Custom data of a token that is serialized to bytes,210 /// primarily reserved for on-chain operations,211 /// normally obscured from the external users.212 ///213 /// Auxiliary properties are slightly different from214 /// usual [`TokenProperties`] due to an unlimited number215 /// and separately stored and written-to key-value pairs.216 ///217 /// Currently used to store RMRK data.218 #[pallet::storage]219 #[pallet::getter(fn token_aux_property)]220 pub type TokenAuxProperties<T: Config> = StorageNMap<221 Key = (222 Key<Twox64Concat, CollectionId>,223 Key<Twox64Concat, TokenId>,224 Key<Twox64Concat, PropertyScope>,225 Key<Twox64Concat, PropertyKey>,226 ),227 Value = AuxPropertyValue,228 QueryKind = OptionQuery,229 >;230231 /// Used to enumerate tokens owned by account.232 #[pallet::storage]233 pub type Owned<T: Config> = StorageNMap<234 Key = (235 Key<Twox64Concat, CollectionId>,236 Key<Blake2_128Concat, T::CrossAccountId>,237 Key<Twox64Concat, TokenId>,238 ),239 Value = bool,240 QueryKind = ValueQuery,241 >;242243 /// Used to enumerate token's children.244 #[pallet::storage]245 #[pallet::getter(fn token_children)]246 pub type TokenChildren<T: Config> = StorageNMap<247 Key = (248 Key<Twox64Concat, CollectionId>,249 Key<Twox64Concat, TokenId>,250 Key<Twox64Concat, (CollectionId, TokenId)>,251 ),252 Value = bool,253 QueryKind = ValueQuery,254 >;255256 /// Amount of tokens owned by an account in a collection.257 #[pallet::storage]258 pub type AccountBalance<T: Config> = StorageNMap<259 Key = (260 Key<Twox64Concat, CollectionId>,261 Key<Blake2_128Concat, T::CrossAccountId>,262 ),263 Value = u32,264 QueryKind = ValueQuery,265 >;266267 /// Allowance set by a token owner for another user to perform one of certain transactions on a token.268 #[pallet::storage]269 pub type Allowance<T: Config> = StorageNMap<270 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),271 Value = T::CrossAccountId,272 QueryKind = OptionQuery,273 >;274275 /// Upgrade from the old schema to properties.276 #[pallet::hooks]277 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {278 fn on_runtime_upgrade() -> Weight {279 if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {280 let mut had_consts = BTreeSet::new();281 <TokenData<T>>::translate::<ItemDataVersion1<T::CrossAccountId>, _>(282 |(collection, token), v| {283 let mut props = vec![];284 if !v.const_data.is_empty() {285 props.push(Property {286 key: b"_old_constData".to_vec().try_into().unwrap(),287 value: v288 .const_data289 .clone()290 .into_inner()291 .try_into()292 .expect("const too long"),293 });294 had_consts.insert(collection);295 }296 if !v.variable_data.is_empty() {297 props.push(Property {298 key: b"_old_variableData".to_vec().try_into().unwrap(),299 value: v300 .variable_data301 .clone()302 .into_inner()303 .try_into()304 .expect("variable too long"),305 })306 }307 if !props.is_empty() {308 Self::set_scoped_token_properties(309 collection,310 token,311 PropertyScope::None,312 props.into_iter(),313 )314 .expect("existing token data exceeds property storage");315 }316 Some(<ItemDataVersion2<T::CrossAccountId>>::from(v))317 },318 );319 for collection in had_consts {320 <PalletCommon<T>>::set_property_permission_unchecked(321 collection,322 PropertyKeyPermission {323 key: b"_old_constData".to_vec().try_into().unwrap(),324 permission: PropertyPermission {325 mutable: false,326 collection_admin: true,327 token_owner: false,328 },329 },330 )331 .expect("failed to configure permission");332 }333 }334335 0336 }337 }338}339340pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);341impl<T: Config> NonfungibleHandle<T> {342 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {343 Self(inner)344 }345 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {346 self.0347 }348 pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {349 &mut self.0350 }351}352impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {353 fn recorder(&self) -> &SubstrateRecorder<T> {354 self.0.recorder()355 }356 fn into_recorder(self) -> SubstrateRecorder<T> {357 self.0.into_recorder()358 }359}360impl<T: Config> Deref for NonfungibleHandle<T> {361 type Target = pallet_common::CollectionHandle<T>;362363 fn deref(&self) -> &Self::Target {364 &self.0365 }366}367368impl<T: Config> Pallet<T> {369 /// Get number of NFT tokens in collection.370 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {371 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)372 }373374 /// Check that NFT token exists.375 ///376 /// - `token`: Token ID.377 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {378 <TokenData<T>>::contains_key((collection.id, token))379 }380381 /// Set the token property with the scope.382 ///383 /// - `property`: Contains key-value pair.384 pub fn set_scoped_token_property(385 collection_id: CollectionId,386 token_id: TokenId,387 scope: PropertyScope,388 property: Property,389 ) -> DispatchResult {390 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {391 properties.try_scoped_set(scope, property.key, property.value)392 })393 .map_err(<CommonError<T>>::from)?;394395 Ok(())396 }397398 /// Batch operation to set multiple properties with the same scope.399 pub fn set_scoped_token_properties(400 collection_id: CollectionId,401 token_id: TokenId,402 scope: PropertyScope,403 properties: impl Iterator<Item = Property>,404 ) -> DispatchResult {405 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {406 stored_properties.try_scoped_set_from_iter(scope, properties)407 })408 .map_err(<CommonError<T>>::from)?;409410 Ok(())411 }412413 /// Add or edit auxiliary data for the property.414 ///415 /// - `f`: function that adds or edits auxiliary data.416 pub fn try_mutate_token_aux_property<R, E>(417 collection_id: CollectionId,418 token_id: TokenId,419 scope: PropertyScope,420 key: PropertyKey,421 f: impl FnOnce(&mut Option<AuxPropertyValue>) -> Result<R, E>,422 ) -> Result<R, E> {423 <TokenAuxProperties<T>>::try_mutate((collection_id, token_id, scope, key), f)424 }425426 /// Remove auxiliary data for the property.427 pub fn remove_token_aux_property(428 collection_id: CollectionId,429 token_id: TokenId,430 scope: PropertyScope,431 key: PropertyKey,432 ) {433 <TokenAuxProperties<T>>::remove((collection_id, token_id, scope, key));434 }435436 /// Get all auxiliary data in a given scope.437 ///438 /// Returns iterator over Property Key - Data pairs.439 pub fn iterate_token_aux_properties(440 collection_id: CollectionId,441 token_id: TokenId,442 scope: PropertyScope,443 ) -> impl Iterator<Item = (PropertyKey, AuxPropertyValue)> {444 <TokenAuxProperties<T>>::iter_prefix((collection_id, token_id, scope))445 }446447 /// Get ID of the last minted token448 pub fn current_token_id(collection_id: CollectionId) -> TokenId {449 TokenId(<TokensMinted<T>>::get(collection_id))450 }451}452453// unchecked calls skips any permission checks454impl<T: Config> Pallet<T> {455 /// Create NFT collection456 ///457 /// `init_collection` will take non-refundable deposit for collection creation.458 ///459 /// - `data`: Contains settings for collection limits and permissions.460 pub fn init_collection(461 owner: T::CrossAccountId,462 data: CreateCollectionData<T::AccountId>,463 is_external: bool,464 ) -> Result<CollectionId, DispatchError> {465 <PalletCommon<T>>::init_collection(owner, data, is_external)466 }467468 /// Destroy NFT collection469 ///470 /// `destroy_collection` will throw error if collection contains any tokens.471 /// Only owner can destroy collection.472 pub fn destroy_collection(473 collection: NonfungibleHandle<T>,474 sender: &T::CrossAccountId,475 ) -> DispatchResult {476 let id = collection.id;477478 if Self::collection_has_tokens(id) {479 return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());480 }481482 // =========483484 PalletCommon::destroy_collection(collection.0, sender)?;485486 <TokenData<T>>::remove_prefix((id,), None);487 <TokenChildren<T>>::remove_prefix((id,), None);488 <Owned<T>>::remove_prefix((id,), None);489 <TokensMinted<T>>::remove(id);490 <TokensBurnt<T>>::remove(id);491 <Allowance<T>>::remove_prefix((id,), None);492 <AccountBalance<T>>::remove_prefix((id,), None);493 Ok(())494 }495496 /// Burn NFT token497 ///498 /// `burn` removes `token` from the `collection`, from it's owner and from the parent token499 /// if the token is nested.500 /// Only the owner can `burn` the token. The `token` shouldn't have any nested tokens.501 /// Also removes all corresponding properties and auxiliary properties.502 ///503 /// - `token`: Token that should be burned504 /// - `collection`: Collection that contains the token505 pub fn burn(506 collection: &NonfungibleHandle<T>,507 sender: &T::CrossAccountId,508 token: TokenId,509 ) -> DispatchResult {510 let token_data =511 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;512 ensure!(&token_data.owner == sender, <CommonError<T>>::NoPermission);513514 if collection.permissions.access() == AccessMode::AllowList {515 collection.check_allowlist(sender)?;516 }517518 if Self::token_has_children(collection.id, token) {519 return Err(<Error<T>>::CantBurnNftWithChildren.into());520 }521522 let burnt = <TokensBurnt<T>>::get(collection.id)523 .checked_add(1)524 .ok_or(ArithmeticError::Overflow)?;525526 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))527 .checked_sub(1)528 .ok_or(ArithmeticError::Overflow)?;529530 // =========531532 if balance == 0 {533 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));534 } else {535 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);536 }537538 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);539540 <Owned<T>>::remove((collection.id, &token_data.owner, token));541 <TokensBurnt<T>>::insert(collection.id, burnt);542 <TokenData<T>>::remove((collection.id, token));543 <TokenProperties<T>>::remove((collection.id, token));544 <TokenAuxProperties<T>>::remove_prefix((collection.id, token), None);545 let old_spender = <Allowance<T>>::take((collection.id, token));546547 if let Some(old_spender) = old_spender {548 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(549 collection.id,550 token,551 token_data.owner.clone(),552 old_spender,553 0,554 ));555 }556557 <PalletEvm<T>>::deposit_log(558 ERC721Events::Transfer {559 from: *token_data.owner.as_eth(),560 to: H160::default(),561 token_id: token.into(),562 }563 .to_log(collection_id_to_address(collection.id)),564 );565 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(566 collection.id,567 token,568 token_data.owner,569 1,570 ));571 Ok(())572 }573574 /// Same as [`burn`] but burns all the tokens that are nested in the token first575 ///576 /// - `self_budget`: Limit for searching children in depth.577 /// - `breadth_budget`: Limit of breadth of searching children.578 ///579 /// [`burn`]: struct.Pallet.html#method.burn580 #[transactional]581 pub fn burn_recursively(582 collection: &NonfungibleHandle<T>,583 sender: &T::CrossAccountId,584 token: TokenId,585 self_budget: &dyn Budget,586 breadth_budget: &dyn Budget,587 ) -> DispatchResultWithPostInfo {588 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);589590 let current_token_account =591 T::CrossTokenAddressMapping::token_to_address(collection.id, token);592593 let mut weight = 0 as Weight;594595 // This method is transactional, if user in fact doesn't have permissions to remove token -596 // tokens removed here will be restored after rejected transaction597 for ((collection, token), _) in <TokenChildren<T>>::iter_prefix((collection.id, token)) {598 ensure!(breadth_budget.consume(), <StructureError<T>>::BreadthLimit,);599 let PostDispatchInfo { actual_weight, .. } =600 <PalletStructure<T>>::burn_item_recursively(601 current_token_account.clone(),602 collection,603 token,604 self_budget,605 breadth_budget,606 )?;607 if let Some(actual_weight) = actual_weight {608 weight = weight.saturating_add(actual_weight);609 }610 }611612 Self::burn(collection, sender, token)?;613 DispatchResultWithPostInfo::Ok(PostDispatchInfo {614 actual_weight: Some(weight + <SelfWeightOf<T>>::burn_item()),615 pays_fee: Pays::Yes,616 })617 }618619 /// Batch operation to add, edit or remove properties for the token620 ///621 /// All affected properties should have mutable permission and sender should have622 /// permission to edit those properties.623 ///624 /// - `nesting_budget`: Limit for searching parents in depth to check ownership.625 /// - `is_token_create`: Indicates that method is called during token initialization.626 /// Allows to bypass ownership check.627 #[transactional]628 fn modify_token_properties(629 collection: &NonfungibleHandle<T>,630 sender: &T::CrossAccountId,631 token_id: TokenId,632 properties: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,633 is_token_create: bool,634 nesting_budget: &dyn Budget,635 ) -> DispatchResult {636 let mut collection_admin_status = None;637 let mut token_owner_result = None;638639 let mut is_collection_admin =640 || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));641642 let mut is_token_owner = || {643 *token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {644 let is_owned = <PalletStructure<T>>::check_indirectly_owned(645 sender.clone(),646 collection.id,647 token_id,648 None,649 nesting_budget,650 )?;651652 Ok(is_owned)653 })654 };655656 for (key, value) in properties {657 let permission = <PalletCommon<T>>::property_permissions(collection.id)658 .get(&key)659 .cloned()660 .unwrap_or_else(PropertyPermission::none);661662 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))663 .get(&key)664 .is_some();665666 match permission {667 PropertyPermission { mutable: false, .. } if is_property_exists => {668 return Err(<CommonError<T>>::NoPermission.into());669 }670671 PropertyPermission {672 collection_admin,673 token_owner,674 ..675 } => {676 //TODO: investigate threats during public minting.677 if is_token_create && (collection_admin || token_owner) && value.is_some() {678 // Pass679 } else if collection_admin && is_collection_admin() {680 // Pass681 } else if token_owner && is_token_owner()? {682 // Pass683 } else {684 fail!(<CommonError<T>>::NoPermission);685 }686 }687 }688689 match value {690 Some(value) => {691 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {692 properties.try_set(key.clone(), value)693 })694 .map_err(<CommonError<T>>::from)?;695696 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(697 collection.id,698 token_id,699 key,700 ));701 }702 None => {703 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {704 properties.remove(&key)705 })706 .map_err(<CommonError<T>>::from)?;707708 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(709 collection.id,710 token_id,711 key,712 ));713 }714 }715 }716717 Ok(())718 }719720 /// Batch operation to add or edit properties for the token721 ///722 /// Same as [`modify_token_properties`] but doesn't allow to remove properties723 ///724 /// [`modify_token_properties`]: struct.Pallet.html#method.modify_token_properties725 pub fn set_token_properties(726 collection: &NonfungibleHandle<T>,727 sender: &T::CrossAccountId,728 token_id: TokenId,729 properties: impl Iterator<Item = Property>,730 is_token_create: bool,731 nesting_budget: &dyn Budget,732 ) -> DispatchResult {733 Self::modify_token_properties(734 collection,735 sender,736 token_id,737 properties.map(|p| (p.key, Some(p.value))),738 is_token_create,739 nesting_budget,740 )741 }742743 /// Add or edit single property for the token744 ///745 /// Calls [`set_token_properties`] internally746 ///747 /// [`set_token_properties`]: struct.Pallet.html#method.set_token_properties748 pub fn set_token_property(749 collection: &NonfungibleHandle<T>,750 sender: &T::CrossAccountId,751 token_id: TokenId,752 property: Property,753 nesting_budget: &dyn Budget,754 ) -> DispatchResult {755 let is_token_create = false;756757 Self::set_token_properties(758 collection,759 sender,760 token_id,761 [property].into_iter(),762 is_token_create,763 nesting_budget,764 )765 }766767 /// Batch operation to remove properties from the token768 ///769 /// Same as [`modify_token_properties`] but doesn't allow to add or edit properties770 ///771 /// [`modify_token_properties`]: struct.Pallet.html#method.modify_token_properties772 pub fn delete_token_properties(773 collection: &NonfungibleHandle<T>,774 sender: &T::CrossAccountId,775 token_id: TokenId,776 property_keys: impl Iterator<Item = PropertyKey>,777 nesting_budget: &dyn Budget,778 ) -> DispatchResult {779 let is_token_create = false;780781 Self::modify_token_properties(782 collection,783 sender,784 token_id,785 property_keys.into_iter().map(|key| (key, None)),786 is_token_create,787 nesting_budget,788 )789 }790791 /// Remove single property from the token792 ///793 /// Calls [`delete_token_properties`] internally794 ///795 /// [`delete_token_properties`]: struct.Pallet.html#method.delete_token_properties796 pub fn delete_token_property(797 collection: &NonfungibleHandle<T>,798 sender: &T::CrossAccountId,799 token_id: TokenId,800 property_key: PropertyKey,801 nesting_budget: &dyn Budget,802 ) -> DispatchResult {803 Self::delete_token_properties(804 collection,805 sender,806 token_id,807 [property_key].into_iter(),808 nesting_budget,809 )810 }811812 /// Add or edit properties for the collection813 pub fn set_collection_properties(814 collection: &NonfungibleHandle<T>,815 sender: &T::CrossAccountId,816 properties: Vec<Property>,817 ) -> DispatchResult {818 <PalletCommon<T>>::set_collection_properties(collection, sender, properties)819 }820821 /// Remove properties from the collection822 pub fn delete_collection_properties(823 collection: &CollectionHandle<T>,824 sender: &T::CrossAccountId,825 property_keys: Vec<PropertyKey>,826 ) -> DispatchResult {827 <PalletCommon<T>>::delete_collection_properties(collection, sender, property_keys)828 }829830 /// Set property permissions for the token.831 ///832 /// Sender should be the owner or admin of token's collection.833 pub fn set_token_property_permissions(834 collection: &CollectionHandle<T>,835 sender: &T::CrossAccountId,836 property_permissions: Vec<PropertyKeyPermission>,837 ) -> DispatchResult {838 <PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)839 }840841 /// Set property permissions for the collection.842 ///843 /// Sender should be the owner or admin of the collection.844 pub fn set_property_permission(845 collection: &CollectionHandle<T>,846 sender: &T::CrossAccountId,847 permission: PropertyKeyPermission,848 ) -> DispatchResult {849 <PalletCommon<T>>::set_property_permission(collection, sender, permission)850 }851852 /// Transfer NFT token from one account to another.853 ///854 /// `from` account stops being the owner and `to` account becomes the owner of the token.855 /// If `to` is token than `to` becomes owner of the token and the token become nested.856 /// Unnests token from previous parent if it was nested before.857 /// Removes allowance for the token if there was any.858 /// Throws if transfers aren't allowed for collection or if receiver reached token ownership limit.859 ///860 /// - `nesting_budget`: Limit for token nesting depth861 pub fn transfer(862 collection: &NonfungibleHandle<T>,863 from: &T::CrossAccountId,864 to: &T::CrossAccountId,865 token: TokenId,866 nesting_budget: &dyn Budget,867 ) -> DispatchResult {868 ensure!(869 collection.limits.transfers_enabled(),870 <CommonError<T>>::TransferNotAllowed871 );872873 let token_data =874 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;875 ensure!(&token_data.owner == from, <CommonError<T>>::NoPermission);876877 if collection.permissions.access() == AccessMode::AllowList {878 collection.check_allowlist(from)?;879 collection.check_allowlist(to)?;880 }881 <PalletCommon<T>>::ensure_correct_receiver(to)?;882883 let balance_from = <AccountBalance<T>>::get((collection.id, from))884 .checked_sub(1)885 .ok_or(<CommonError<T>>::TokenValueTooLow)?;886 let balance_to = if from != to {887 let balance_to = <AccountBalance<T>>::get((collection.id, to))888 .checked_add(1)889 .ok_or(ArithmeticError::Overflow)?;890891 ensure!(892 balance_to < collection.limits.account_token_ownership_limit(),893 <CommonError<T>>::AccountTokenLimitExceeded,894 );895896 Some(balance_to)897 } else {898 None899 };900901 <PalletStructure<T>>::nest_if_sent_to_token(902 from.clone(),903 to,904 collection.id,905 token,906 nesting_budget,907 )?;908909 // =========910911 <PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);912913 <TokenData<T>>::insert(914 (collection.id, token),915 ItemData {916 owner: to.clone(),917 ..token_data918 },919 );920921 if let Some(balance_to) = balance_to {922 // from != to923 if balance_from == 0 {924 <AccountBalance<T>>::remove((collection.id, from));925 } else {926 <AccountBalance<T>>::insert((collection.id, from), balance_from);927 }928 <AccountBalance<T>>::insert((collection.id, to), balance_to);929 <Owned<T>>::remove((collection.id, from, token));930 <Owned<T>>::insert((collection.id, to, token), true);931 }932 Self::set_allowance_unchecked(collection, from, token, None, true);933934 <PalletEvm<T>>::deposit_log(935 ERC721Events::Transfer {936 from: *from.as_eth(),937 to: *to.as_eth(),938 token_id: token.into(),939 }940 .to_log(collection_id_to_address(collection.id)),941 );942 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(943 collection.id,944 token,945 from.clone(),946 to.clone(),947 1,948 ));949 Ok(())950 }951952 /// Batch operation to mint multiple NFT tokens.953 ///954 /// The sender should be the owner/admin of the collection or collection should be configured955 /// to allow public minting.956 /// Throws if amount of tokens reached it's limit for the collection or if caller reached957 /// token ownership limit.958 ///959 /// - `data`: Contains list of token properties and users who will become the owners of the960 /// corresponging tokens.961 /// - `nesting_budget`: Limit for token nesting depth962 pub fn create_multiple_items(963 collection: &NonfungibleHandle<T>,964 sender: &T::CrossAccountId,965 data: Vec<CreateItemData<T>>,966 nesting_budget: &dyn Budget,967 ) -> DispatchResult {968 if !collection.is_owner_or_admin(sender) {969 ensure!(970 collection.permissions.mint_mode(),971 <CommonError<T>>::PublicMintingNotAllowed972 );973 collection.check_allowlist(sender)?;974975 for item in data.iter() {976 collection.check_allowlist(&item.owner)?;977 }978 }979980 for data in data.iter() {981 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;982 }983984 let first_token = <TokensMinted<T>>::get(collection.id);985 let tokens_minted = first_token986 .checked_add(data.len() as u32)987 .ok_or(ArithmeticError::Overflow)?;988 ensure!(989 tokens_minted <= collection.limits.token_limit(),990 <CommonError<T>>::CollectionTokenLimitExceeded991 );992993 let mut balances = BTreeMap::new();994 for data in &data {995 let balance = balances996 .entry(&data.owner)997 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));998 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;9991000 ensure!(1001 *balance <= collection.limits.account_token_ownership_limit(),1002 <CommonError<T>>::AccountTokenLimitExceeded,1003 );1004 }10051006 for (i, data) in data.iter().enumerate() {1007 let token = TokenId(first_token + i as u32 + 1);10081009 <PalletStructure<T>>::check_nesting(1010 sender.clone(),1011 &data.owner,1012 collection.id,1013 token,1014 nesting_budget,1015 )?;1016 }10171018 // =========10191020 with_transaction(|| {1021 for (i, data) in data.iter().enumerate() {1022 let token = first_token + i as u32 + 1;10231024 <TokenData<T>>::insert(1025 (collection.id, token),1026 ItemData {1027 // const_data: data.const_data.clone(),1028 owner: data.owner.clone(),1029 },1030 );10311032 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(1033 &data.owner,1034 collection.id,1035 TokenId(token),1036 );10371038 if let Err(e) = Self::set_token_properties(1039 collection,1040 sender,1041 TokenId(token),1042 data.properties.clone().into_iter(),1043 true,1044 nesting_budget,1045 ) {1046 return TransactionOutcome::Rollback(Err(e));1047 }1048 }1049 TransactionOutcome::Commit(Ok(()))1050 })?;10511052 <TokensMinted<T>>::insert(collection.id, tokens_minted);1053 for (account, balance) in balances {1054 <AccountBalance<T>>::insert((collection.id, account), balance);1055 }1056 for (i, data) in data.into_iter().enumerate() {1057 let token = first_token + i as u32 + 1;1058 <Owned<T>>::insert((collection.id, &data.owner, token), true);10591060 <PalletEvm<T>>::deposit_log(1061 ERC721Events::Transfer {1062 from: H160::default(),1063 to: *data.owner.as_eth(),1064 token_id: token.into(),1065 }1066 .to_log(collection_id_to_address(collection.id)),1067 );1068 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(1069 collection.id,1070 TokenId(token),1071 data.owner.clone(),1072 1,1073 ));1074 }1075 Ok(())1076 }10771078 pub fn set_allowance_unchecked(1079 collection: &NonfungibleHandle<T>,1080 sender: &T::CrossAccountId,1081 token: TokenId,1082 spender: Option<&T::CrossAccountId>,1083 assume_implicit_eth: bool,1084 ) {1085 if let Some(spender) = spender {1086 let old_spender = <Allowance<T>>::get((collection.id, token));1087 <Allowance<T>>::insert((collection.id, token), spender);1088 // In ERC721 there is only one possible approved user of token, so we set1089 // approved user to spender1090 <PalletEvm<T>>::deposit_log(1091 ERC721Events::Approval {1092 owner: *sender.as_eth(),1093 approved: *spender.as_eth(),1094 token_id: token.into(),1095 }1096 .to_log(collection_id_to_address(collection.id)),1097 );1098 // In Unique chain, any token can have any amount of approved users, so we need to1099 // set allowance of old owner to 0, and allowance of new owner to 11100 if old_spender.as_ref() != Some(spender) {1101 if let Some(old_owner) = old_spender {1102 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1103 collection.id,1104 token,1105 sender.clone(),1106 old_owner,1107 0,1108 ));1109 }1110 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1111 collection.id,1112 token,1113 sender.clone(),1114 spender.clone(),1115 1,1116 ));1117 }1118 } else {1119 let old_spender = <Allowance<T>>::take((collection.id, token));1120 if !assume_implicit_eth {1121 // In ERC721 there is only one possible approved user of token, so we set1122 // approved user to zero address1123 <PalletEvm<T>>::deposit_log(1124 ERC721Events::Approval {1125 owner: *sender.as_eth(),1126 approved: H160::default(),1127 token_id: token.into(),1128 }1129 .to_log(collection_id_to_address(collection.id)),1130 );1131 }1132 // In Unique chain, any token can have any amount of approved users, so we need to1133 // set allowance of old owner to 01134 if let Some(old_spender) = old_spender {1135 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1136 collection.id,1137 token,1138 sender.clone(),1139 old_spender,1140 0,1141 ));1142 }1143 }1144 }11451146 /// Set allowance for the spender to `transfer` or `burn` sender's token.1147 ///1148 /// - `token`: Token the spender is allowed to `transfer` or `burn`.1149 pub fn set_allowance(1150 collection: &NonfungibleHandle<T>,1151 sender: &T::CrossAccountId,1152 token: TokenId,1153 spender: Option<&T::CrossAccountId>,1154 ) -> DispatchResult {1155 if collection.permissions.access() == AccessMode::AllowList {1156 collection.check_allowlist(sender)?;1157 if let Some(spender) = spender {1158 collection.check_allowlist(spender)?;1159 }1160 }11611162 if let Some(spender) = spender {1163 <PalletCommon<T>>::ensure_correct_receiver(spender)?;1164 }11651166 let token_data =1167 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;1168 if &token_data.owner != sender {1169 ensure!(1170 collection.ignores_owned_amount(sender),1171 <CommonError<T>>::CantApproveMoreThanOwned1172 );1173 }11741175 // =========11761177 Self::set_allowance_unchecked(collection, sender, token, spender, false);1178 Ok(())1179 }11801181 /// Checks allowance for the spender to use the token.1182 fn check_allowed(1183 collection: &NonfungibleHandle<T>,1184 spender: &T::CrossAccountId,1185 from: &T::CrossAccountId,1186 token: TokenId,1187 nesting_budget: &dyn Budget,1188 ) -> DispatchResult {1189 if spender.conv_eq(from) {1190 return Ok(());1191 }1192 if collection.permissions.access() == AccessMode::AllowList {1193 // `from`, `to` checked in [`transfer`]1194 collection.check_allowlist(spender)?;1195 }11961197 if collection.limits.owner_can_transfer() && collection.is_owner_or_admin(spender) {1198 return Ok(());1199 }12001201 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1202 ensure!(1203 <PalletStructure<T>>::check_indirectly_owned(1204 spender.clone(),1205 source.0,1206 source.1,1207 None,1208 nesting_budget1209 )?,1210 <CommonError<T>>::ApprovedValueTooLow,1211 );1212 return Ok(());1213 }1214 if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {1215 return Ok(());1216 }1217 ensure!(1218 collection.ignores_allowance(spender),1219 <CommonError<T>>::ApprovedValueTooLow1220 );1221 Ok(())1222 }12231224 /// Transfer NFT token from one account to another.1225 ///1226 /// Same as the [`transfer`] but spender doesn't needs to be the owner of the token.1227 /// The owner should set allowance for the spender to transfer token.1228 ///1229 /// [`transfer`]: struct.Pallet.html#method.transfer1230 pub fn transfer_from(1231 collection: &NonfungibleHandle<T>,1232 spender: &T::CrossAccountId,1233 from: &T::CrossAccountId,1234 to: &T::CrossAccountId,1235 token: TokenId,1236 nesting_budget: &dyn Budget,1237 ) -> DispatchResult {1238 Self::check_allowed(collection, spender, from, token, nesting_budget)?;12391240 // =========12411242 // Allowance is reset in [`transfer`]1243 Self::transfer(collection, from, to, token, nesting_budget)1244 }12451246 /// Burn NFT token for `from` account.1247 ///1248 /// Same as the [`burn`] but spender doesn't need to be an owner of the token. The owner should1249 /// set allowance for the spender to burn token.1250 ///1251 /// [`burn`]: struct.Pallet.html#method.burn1252 pub fn burn_from(1253 collection: &NonfungibleHandle<T>,1254 spender: &T::CrossAccountId,1255 from: &T::CrossAccountId,1256 token: TokenId,1257 nesting_budget: &dyn Budget,1258 ) -> DispatchResult {1259 Self::check_allowed(collection, spender, from, token, nesting_budget)?;12601261 // =========12621263 Self::burn(collection, from, token)1264 }12651266 /// Check that `from` token could be nested in `under` token.1267 ///1268 pub fn check_nesting(1269 handle: &NonfungibleHandle<T>,1270 sender: T::CrossAccountId,1271 from: (CollectionId, TokenId),1272 under: TokenId,1273 nesting_budget: &dyn Budget,1274 ) -> DispatchResult {1275 let nesting = handle.permissions.nesting();12761277 #[cfg(not(feature = "runtime-benchmarks"))]1278 let permissive = false;1279 #[cfg(feature = "runtime-benchmarks")]1280 let permissive = nesting.permissive;12811282 if permissive {1283 // Pass1284 } else if nesting.token_owner1285 && <PalletStructure<T>>::check_indirectly_owned(1286 sender.clone(),1287 handle.id,1288 under,1289 Some(from),1290 nesting_budget,1291 )? {1292 // Pass1293 } else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {1294 // Pass1295 } else {1296 fail!(<CommonError<T>>::UserIsNotAllowedToNest);1297 }12981299 if let Some(whitelist) = &nesting.restricted {1300 ensure!(1301 whitelist.contains(&from.0),1302 <CommonError<T>>::SourceCollectionIsNotAllowedToNest1303 );1304 }1305 Ok(())1306 }13071308 fn nest(under: (CollectionId, TokenId), to_nest: (CollectionId, TokenId)) {1309 <TokenChildren<T>>::insert((under.0, under.1, (to_nest.0, to_nest.1)), true);1310 }13111312 fn unnest(under: (CollectionId, TokenId), to_unnest: (CollectionId, TokenId)) {1313 <TokenChildren<T>>::remove((under.0, under.1, to_unnest));1314 }13151316 fn collection_has_tokens(collection_id: CollectionId) -> bool {1317 <TokenData<T>>::iter_prefix((collection_id,))1318 .next()1319 .is_some()1320 }13211322 fn token_has_children(collection_id: CollectionId, token_id: TokenId) -> bool {1323 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1324 .next()1325 .is_some()1326 }13271328 pub fn token_children_ids(collection_id: CollectionId, token_id: TokenId) -> Vec<TokenChild> {1329 <TokenChildren<T>>::iter_prefix((collection_id, token_id))1330 .map(|((child_collection_id, child_id), _)| TokenChild {1331 collection: child_collection_id,1332 token: child_id,1333 })1334 .collect()1335 }13361337 /// Mint single NFT token.1338 ///1339 /// Delegated to [`create_multiple_items`]1340 ///1341 /// [`create_multiple_items`]: struct.Pallet.html#method.create_multiple_items1342 pub fn create_item(1343 collection: &NonfungibleHandle<T>,1344 sender: &T::CrossAccountId,1345 data: CreateItemData<T>,1346 nesting_budget: &dyn Budget,1347 ) -> DispatchResult {1348 Self::create_multiple_items(collection, sender, vec![data], nesting_budget)1349 }1350}pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -21,7 +21,7 @@
//! - [`Config`]
//! - [`RefungibleHandle`]
//! - [`Pallet`]
-//! - [`CommonWeights`]
+//! - [`CommonWeights`](common::CommonWeights)
//!
//! ## Overview
//!
@@ -119,6 +119,8 @@
pub mod weights;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
+/// Token data, stored independently from other data used to describe it
+/// for the convenience of database access. Notably contains the token metadata.
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
pub struct ItemData {
@@ -143,13 +145,13 @@
pub enum Error<T> {
/// Not Refungible item data used to mint in Refungible collection.
NotRefungibleDataUsedToMintFungibleCollectionToken,
- /// Maximum refungibility exceeded
+ /// Maximum refungibility exceeded.
WrongRefungiblePieces,
- /// Refungible token can't be repartitioned by user who isn't owns all pieces
+ /// Refungible token can't be repartitioned by user who isn't owns all pieces.
RepartitionWhileNotOwningAllPieces,
- /// Refungible token can't nest other tokens
+ /// Refungible token can't nest other tokens.
RefungibleDisallowsNesting,
- /// Setting item properties is not allowed
+ /// Setting item properties is not allowed.
SettingPropertiesNotAllowed,
}
@@ -167,17 +169,17 @@
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
- /// Amount of tokens minted for collection
+ /// 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 burnt tokens for collection
+ /// Amount of tokens burnt in a collection.
#[pallet::storage]
pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
- /// Custom data serialized to bytes for token
+ /// Token data, used to partially describe a token.
#[pallet::storage]
pub type TokenData<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -185,6 +187,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of pieces a refungible token is split into.
#[pallet::storage]
#[pallet::getter(fn token_properties)]
pub type TokenProperties<T: Config> = StorageNMap<
@@ -202,7 +205,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 = (
@@ -214,7 +217,7 @@
QueryKind = ValueQuery,
>;
- /// Amount of tokens owned by account
+ /// Amount of tokens (not pieces) partially owned by an account within a collection.
#[pallet::storage]
pub type AccountBalance<T: Config> = StorageNMap<
Key = (
@@ -226,7 +229,7 @@
QueryKind = ValueQuery,
>;
- /// Amount of token pieces owned by account
+ /// Amount of token pieces owned by account.
#[pallet::storage]
pub type Balance<T: Config> = StorageNMap<
Key = (
@@ -239,7 +242,7 @@
QueryKind = ValueQuery,
>;
- /// Allowance set by an owner for a spender for a token
+ /// Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token.
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (
pallets/scheduler/src/lib.rsdiffbeforeafterboth--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -24,7 +24,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -50,7 +50,7 @@
//! Also possible to book a call with a certain frequency.
//!
//! Key differences from the original pallet:
-//! https://crates.io/crates/pallet-scheduler
+//! <https://crates.io/crates/pallet-scheduler>
//! Schedule Id restricted by 16 bytes. Identificator for booked call.
//! Priority limited by HARD DEADLINE (<= 63). Calls over maximum weight don't include to block.
//! The maximum weight that may be scheduled per block for any dispatchables of less priority than `schedule::HARD_DEADLINE`.
@@ -267,6 +267,7 @@
/// A Scheduler-Runtime interface for finer payment handling.
pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {
+ /// Reserve (lock) the maximum spendings on a call, calculated from its weight and the repetition count.
fn reserve_balance(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -274,7 +275,7 @@
count: u32,
) -> Result<(), DispatchError>;
- /// Unlock centain amount from payer
+ /// Unreserve (unlock) a certain amount from the payer's reserved funds, returning the change.
fn pay_for_call(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -290,6 +291,7 @@
TransactionValidityError,
>;
+ /// Release unspent reserved funds in case of a schedule cancel.
fn cancel_reserve(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -494,9 +496,8 @@
Agenda::<T>::append(wake, Some(s));
}
}
- /// Weight should be 0, because transaction already paid
+ // Total weight should be 0, because the transaction is already paid for
0
- //total_weight
}
}
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -78,19 +78,19 @@
#[pallet::error]
pub enum Error<T> {
- /// While searched for owner, got already checked account
+ /// While nesting, encountered an already checked account, detecting a loop.
OuroborosDetected,
- /// While searched for owner, encountered depth limit
+ /// While nesting, reached the depth limit of nesting, exceeding the provided budget.
DepthLimit,
- /// While iterating over children, encountered breadth limit
+ /// While nesting, reached the breadth limit of nesting, exceeding the provided budget.
BreadthLimit,
- /// While searched for owner, found token owner by not-yet-existing token
+ /// Couldn't find the token owner that is itself a token.
TokenNotFound,
}
#[pallet::event]
pub enum Event<T> {
- /// Executed call on behalf of token
+ /// Executed call on behalf of the token.
Executed(DispatchResult),
}
@@ -126,11 +126,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/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+//! Implementation of CollectionHelpers contract.
+
use core::marker::PhantomData;
use evm_coder::{execution::*, generate_stubgen, solidity_interface, weight, types::*};
use ethereum as _;
@@ -33,7 +35,8 @@
use sp_std::vec::Vec;
use alloc::format;
-struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);
+/// See [`CollectionHelpersCall`]
+pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);
impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {
fn recorder(&self) -> &SubstrateRecorder<T> {
&self.0
@@ -44,8 +47,14 @@
}
}
+/// @title Contract, which allows users to operate with collections
#[solidity_interface(name = "CollectionHelpers", events(CollectionHelpersEvents))]
impl<T: Config + pallet_nonfungible::Config> EvmCollectionHelpers<T> {
+ /// Create an NFT collection
+ /// @param name Name of the collection
+ /// @param description Informative description of the collection
+ /// @param token_prefix Token prefix to represent the collection tokens in UI and user applications
+ /// @return address Address of the newly created collection
#[weight(<SelfWeightOf<T>>::create_collection())]
fn create_nonfungible_collection(
&mut self,
@@ -100,6 +109,9 @@
Ok(address)
}
+ /// Check if a collection exists
+ /// @param collection_address Address of the collection in question
+ /// @return bool Does the collection exist?
fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {
if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {
let collection_id = id;
@@ -110,6 +122,7 @@
}
}
+/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]
pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);
impl<T: Config + pallet_nonfungible::Config> OnMethodCall<T> for CollectionHelpersOnMethodCall<T> {
fn is_reserved(contract: &sp_core::H160) -> bool {
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -14,6 +14,55 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+//! # Unique Pallet
+//!
+//! A pallet governing Unique transactions.
+//!
+//! - [`Config`]
+//! - [`Call`]
+//! - [`Pallet`]
+//!
+//! ## Overview
+//!
+//! The Unique pallet's purpose is to be the primary interface between
+//! external users and the inner structure of the Unique chains.
+//!
+//! It also contains an implementation of [`CollectionHelpers`][`eth`],
+//! an Ethereum contract dealing with collection operations.
+//!
+//! ## Interface
+//!
+//! ### Dispatchables
+//!
+//! - `create_collection` - Create a collection of tokens. **Deprecated**, use `create_collection_ex`.
+//! - `create_collection_ex` - Create a collection of tokens with explicit parameters.
+//! - `destroy_collection` - Destroy a collection if no tokens exist within.
+//! - `add_to_allow_list` - Add an address to allow list.
+//! - `remove_from_allow_list` - Remove an address from allow list.
+//! - `change_collection_owner` - Change the owner of the collection.
+//! - `add_collection_admin` - Add an admin to a collection.
+//! - `remove_collection_admin` - Remove admin of a collection.
+//! - `set_collection_sponsor` - Invite a new collection sponsor.
+//! - `confirm_sponsorship` - Confirm own sponsorship of a collection, becoming the sponsor.
+//! - `remove_collection_sponsor` - Remove a sponsor from a collection.
+//! - `create_item` - Create an item within a collection.
+//! - `create_multiple_items` - Create multiple items within a collection.
+//! - `set_collection_properties` - Add or change collection properties.
+//! - `delete_collection_properties` - Delete specified collection properties.
+//! - `set_token_properties` - Add or change token properties.
+//! - `delete_token_properties` - Delete token properties.
+//! - `set_token_property_permissions` - Add or change token property permissions of a collection.
+//! - `create_multiple_items_ex` - Create multiple items within a collection with explicitly specified initial parameters.
+//! - `set_transfers_enabled_flag` - Completely allow or disallow transfers for a particular collection.
+//! - `burn_item` - Destroy an item.
+//! - `burn_from` - Destroy an item on behalf of the owner as a non-owner account.
+//! - `transfer` - Change ownership of the token.
+//! - `transfer_from` - Change ownership of the token on behalf of the owner as a non-owner account.
+//! - `approve` - Allow a non-permissioned address to transfer or burn an item.
+//! - `set_collection_limits` - Set specific limits of a collection.
+//! - `set_collection_permissions` - Set specific permissions of a collection.
+//! - `repartition` - Re-partition a refungible token, while owning all of its parts.
+
#![recursion_limit = "1024"]
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(
@@ -54,28 +103,35 @@
pub mod weights;
use weights::WeightInfo;
-const NESTING_BUDGET: u32 = 5;
+/// Maximum number of levels of depth in the token nesting tree.
+pub const NESTING_BUDGET: u32 = 5;
decl_error! {
- /// Error for non-fungible-token module.
+ /// Errors for the common Unique transactions.
pub enum Error for Module<T: Config> {
- /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.
+ /// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].
CollectionDecimalPointLimitExceeded,
/// This address is not set as sponsor, use setCollectionSponsor first.
ConfirmUnsetSponsorFail,
/// Length of items properties must be greater than 0.
EmptyArgument,
- /// Repertition is only supported by refungible collection
+ /// Repertition is only supported by refungible collection.
RepartitionCalledOnNonRefungibleCollection,
}
}
+/// Configuration trait of this pallet.
pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {
+ /// Overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
+
+ /// Weight information for common pallet operations.
type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;
+
+ /// Weight info information for extra refungible pallet operations.
type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;
}
@@ -88,80 +144,68 @@
/// Collection sponsor was removed
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
+ /// * collection_id: ID of the affected collection.
CollectionSponsorRemoved(CollectionId),
/// Collection admin was added
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * admin: Admin address.
+ /// * collection_id: ID of the affected collection.
+ /// * admin: Admin address.
CollectionAdminAdded(CollectionId, CrossAccountId),
- /// Collection owned was change
+ /// Collection owned was changed
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * owner: New owner address.
+ /// * collection_id: ID of the affected collection.
+ /// * owner: New owner address.
CollectionOwnedChanged(CollectionId, AccountId),
/// Collection sponsor was set
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * owner: New sponsor address.
+ /// * collection_id: ID of the affected collection.
+ /// * owner: New sponsor address.
CollectionSponsorSet(CollectionId, AccountId),
/// New sponsor was confirm
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * sponsor: New sponsor address.
+ /// * collection_id: ID of the affected collection.
+ /// * sponsor: New sponsor address.
SponsorshipConfirmed(CollectionId, AccountId),
/// Collection admin was removed
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * admin: Admin address.
+ /// * collection_id: ID of the affected collection.
+ /// * admin: Removed admin address.
CollectionAdminRemoved(CollectionId, CrossAccountId),
- /// Address was remove from allow list
+ /// Address was removed from the allow list
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * user: Address.
+ /// * collection_id: ID of the affected collection.
+ /// * user: Address of the removed account.
AllowListAddressRemoved(CollectionId, CrossAccountId),
- /// Address was add to allow list
+ /// Address was added to the allow list
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
- ///
- /// * user: Address.
+ /// * collection_id: ID of the affected collection.
+ /// * user: Address of the added account.
AllowListAddressAdded(CollectionId, CrossAccountId),
- /// Collection limits was set
+ /// Collection limits were set
///
/// # Arguments
- ///
- /// * collection_id: Globally unique collection identifier.
+ /// * collection_id: ID of the affected collection.
CollectionLimitSet(CollectionId),
+ /// Collection permissions were set
+ ///
+ /// # Arguments
+ /// * collection_id: ID of the affected collection.
CollectionPermissionSet(CollectionId),
}
}
@@ -198,7 +242,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,16 +258,20 @@
/// 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>;
}
}
decl_module! {
+ /// Type alias to Pallet, to be used by construct_runtime.
pub struct Module<T: Config> for enum Call
where
origin: T::Origin
@@ -244,30 +292,38 @@
0
}
- /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.
- ///
- /// # Permissions
+ /// Create a collection of tokens.
///
- /// * Anyone.
+ /// Each Token may have multiple properties encoded as an array of bytes
+ /// of certain length. The initial owner of the collection is set
+ /// to the address that signed the transaction and can be changed later.
///
- /// # Arguments
+ /// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.
///
- /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.
+ /// # Permissions
///
- /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.
+ /// * Anyone - becomes the owner of the new collection.
///
- /// * token_prefix: UTF-8 string with token prefix.
+ /// # Arguments
///
- /// * mode: [CollectionMode] collection type and type dependent data.
+ /// * `collection_name`: Wide-character string with collection name
+ /// (limit [`MAX_COLLECTION_NAME_LENGTH`]).
+ /// * `collection_description`: Wide-character string with collection description
+ /// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).
+ /// * `token_prefix`: Byte string containing the token prefix to mark a collection
+ /// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).
+ /// * `mode`: Type of items stored in the collection and type dependent data.
// returns collection ID
#[weight = <SelfWeightOf<T>>::create_collection()]
#[transactional]
- #[deprecated]
- pub fn create_collection(origin,
- collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
- collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
- token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
- mode: CollectionMode) -> DispatchResult {
+ #[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]
+ pub fn create_collection(
+ origin,
+ collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
+ collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
+ token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
+ mode: CollectionMode
+ ) -> DispatchResult {
let data: CreateCollectionData<T::AccountId> = CreateCollectionData {
name: collection_name,
description: collection_description,
@@ -278,9 +334,17 @@
Self::create_collection_ex(origin, data)
}
- /// This method creates a collection
+ /// Create a collection with explicit parameters.
+ ///
+ /// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.
+ ///
+ /// # Permissions
+ ///
+ /// * Anyone - becomes the owner of the new collection.
///
- /// Prefer it to deprecated [`created_collection`] method
+ /// # Arguments
+ ///
+ /// * `data`: Explicit data of a collection used for its creation.
#[weight = <SelfWeightOf<T>>::create_collection()]
#[transactional]
pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
@@ -293,15 +357,15 @@
Ok(())
}
- /// Destroys collection if no tokens within this collection
+ /// Destroy a collection if no tokens exist within.
///
/// # Permissions
///
- /// * Collection Owner.
+ /// * Collection owner
///
/// # Arguments
///
- /// * collection_id: collection to destroy.
+ /// * `collection_id`: Collection to destroy.
#[weight = <SelfWeightOf<T>>::destroy_collection()]
#[transactional]
pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {
@@ -328,14 +392,13 @@
///
/// # Permissions
///
- /// * Collection Owner
- /// * Collection Admin
+ /// * Collection owner
+ /// * Collection admin
///
/// # Arguments
///
- /// * collection_id.
- ///
- /// * address.
+ /// * `collection_id`: ID of the modified collection.
+ /// * `address`: ID of the address to be added to the allowlist.
#[weight = <SelfWeightOf<T>>::add_to_allow_list()]
#[transactional]
pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{
@@ -363,14 +426,13 @@
///
/// # Permissions
///
- /// * Collection Owner
- /// * Collection Admin
+ /// * Collection owner
+ /// * Collection admin
///
/// # Arguments
///
- /// * collection_id.
- ///
- /// * address.
+ /// * `collection_id`: ID of the modified collection.
+ /// * `address`: ID of the address to be removed from the allowlist.
#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]
#[transactional]
pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{
@@ -398,13 +460,12 @@
///
/// # Permissions
///
- /// * Collection Owner.
+ /// * Collection owner
///
/// # Arguments
///
- /// * collection_id.
- ///
- /// * new_owner.
+ /// * `collection_id`: ID of the modified collection.
+ /// * `new_owner`: ID of the account that will become the owner.
#[weight = <SelfWeightOf<T>>::change_collection_owner()]
#[transactional]
pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {
@@ -424,46 +485,51 @@
target_collection.save()
}
- /// 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.
+ /// Add an admin to a 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.
+ /// * `collection_id`: ID of the Collection to add an admin for.
+ /// * `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.
+ /// Remove admin of a 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
///
- /// * collection_id: ID of the Collection to remove admin for.
- ///
- /// * account_id: Address of admin to remove.
+ /// * `collection_id`: ID of the collection to remove the admin for.
+ /// * `account_id`: Address of the admin to remove.
#[weight = <SelfWeightOf<T>>::remove_collection_admin()]
#[transactional]
pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {
@@ -479,15 +545,19 @@
<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 owner
+ /// * Collection admin
///
/// # Arguments
///
- /// * collection_id.
- ///
- /// * new_sponsor.
+ /// * `collection_id`: ID of the modified collection.
+ /// * `new_sponsor`: ID of the account of the sponsor-to-be.
#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]
#[transactional]
pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {
@@ -507,13 +577,19 @@
target_collection.save()
}
+ /// Confirm own sponsorship of a collection, becoming the sponsor.
+ ///
+ /// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].
+ /// Sponsor can pay the fees of a transaction instead of the sender,
+ /// but only within specified limits.
+ ///
/// # Permissions
///
- /// * Sponsor.
+ /// * Sponsor-to-be
///
/// # Arguments
///
- /// * collection_id.
+ /// * `collection_id`: ID of the collection with the pending sponsor.
#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]
#[transactional]
pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {
@@ -534,15 +610,15 @@
target_collection.save()
}
- /// Switch back to pay-per-own-transaction model.
+ /// Remove a collection's a sponsor, making everyone pay for their own transactions.
///
/// # Permissions
///
- /// * Collection owner.
+ /// * Collection owner
///
/// # Arguments
///
- /// * collection_id.
+ /// * `collection_id`: ID of the collection with the sponsor to remove.
#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]
#[transactional]
pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {
@@ -560,24 +636,24 @@
target_collection.save()
}
- /// This method creates a concrete instance of NFT Collection created with CreateCollection method.
+ /// Mint an item within a collection.
+ ///
+ /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
+ /// * Collection owner
+ /// * Collection admin
/// * Anyone if
/// * Allow List is enabled, and
/// * Address is added to allow list, and
- /// * MintPermission is enabled (see SetMintPermission method)
+ /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])
///
/// # Arguments
- ///
- /// * collection_id: ID of the collection.
///
- /// * owner: Address, initial owner of the NFT.
- ///
- /// * data: Token data to store on chain.
+ /// * `collection_id`: ID of the collection to which an item would belong.
+ /// * `owner`: Address of the initial owner of the item.
+ /// * `data`: Token data describing the item to store on chain.
#[weight = T::CommonWeightInfo::create_item()]
#[transactional]
pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {
@@ -587,24 +663,24 @@
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 within a collection.
///
+ /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].
+ ///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
+ /// * Collection owner
+ /// * Collection admin
/// * Anyone if
/// * Allow List is enabled, and
- /// * Address is added to allow list, and
- /// * MintPermission is enabled (see SetMintPermission method)
+ /// * Address is added to the allow list, and
+ /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])
///
/// # Arguments
///
- /// * collection_id: ID of the collection.
- ///
- /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].
- ///
- /// * owner: Address, initial owner of the NFT.
+ /// * `collection_id`: ID of the collection to which the tokens would belong.
+ /// * `owner`: Address of the initial owner of the tokens.
+ /// * `items_data`: Vector of data describing each item to be created.
#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]
#[transactional]
pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {
@@ -615,6 +691,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`: ID of the modified collection.
+ /// * `properties`: 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 +717,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`: ID of the modified collection.
+ /// * `property_keys`: Vector of keys of the properties to be deleted.
+ /// Keys support Latin letters, `-`, `_`, and `.` as symbols.
#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]
#[transactional]
pub fn delete_collection_properties(
@@ -643,6 +743,24 @@
dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))
}
+ /// Add or change token properties according to collection's permissions.
+ /// Currently properties only work with NFTs.
+ ///
+ /// # Permissions
+ ///
+ /// * Depends on collection's token property permissions and specified property mutability:
+ /// * Collection owner
+ /// * Collection admin
+ /// * Token owner
+ ///
+ /// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].
+ ///
+ /// # Arguments
+ ///
+ /// * `collection_id: ID of the collection to which the token belongs.
+ /// * `token_id`: ID of the modified token.
+ /// * `properties`: 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 +777,21 @@
dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))
}
+ /// Delete specified token properties. Currently properties only work with NFTs.
+ ///
+ /// # Permissions
+ ///
+ /// * Depends on collection's token property permissions and specified property mutability:
+ /// * Collection owner
+ /// * Collection admin
+ /// * Token owner
+ ///
+ /// # Arguments
+ ///
+ /// * `collection_id`: ID of the collection to which the token belongs.
+ /// * `token_id`: ID of the modified token.
+ /// * `property_keys`: Vector of keys of the properties to be deleted.
+ /// Keys support Latin letters, `-`, `_`, and `.` as symbols.
#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]
#[transactional]
pub fn delete_token_properties(
@@ -675,6 +808,21 @@
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.
+ ///
+ /// Without a permission for a particular key, a property with that key
+ /// cannot be created in a token.
+ ///
+ /// # Permissions
+ ///
+ /// * Collection owner
+ /// * Collection admin
+ ///
+ /// # Arguments
+ ///
+ /// * `collection_id`: ID of the modified collection.
+ /// * `property_permissions`: 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 +837,21 @@
dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))
}
+ /// Create multiple items within 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 [`set_collection_permissions`][`Pallet::set_collection_permissions`])
+ ///
+ /// # Arguments
+ ///
+ /// * `collection_id`: ID of the collection to which the tokens would belong.
+ /// * `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,17 +861,16 @@
dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))
}
- /// Set transfers_enabled value for particular collection
+ /// Completely allow or disallow transfers for a particular collection.
///
/// # Permissions
///
- /// * Collection Owner.
+ /// * Collection owner
///
/// # Arguments
///
- /// * collection_id: ID of the collection.
- ///
- /// * value: New flag value.
+ /// * `collection_id`: ID of the collection.
+ /// * `value`: New value of the flag, are transfers allowed?
#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]
#[transactional]
pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {
@@ -723,19 +885,22 @@
target_collection.save()
}
- /// Destroys a concrete instance of NFT.
+ /// Destroy an item.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
- /// * Current NFT Owner.
+ /// * Collection owner
+ /// * Collection admin
+ /// * Current item owner
///
/// # Arguments
///
- /// * collection_id: ID of the collection.
- ///
- /// * item_id: ID of NFT to burn.
+ /// * `collection_id`: ID of the collection to which the item belongs.
+ /// * `item_id`: ID of item to burn.
+ /// * `value`: Number of pieces of the item to destroy.
+ /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ /// * Fungible Mode: The desired number of pieces to burn.
+ /// * Re-Fungible Mode: The desired number of pieces to burn.
#[weight = T::CommonWeightInfo::burn_item()]
#[transactional]
pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
@@ -752,22 +917,29 @@
Ok(post_info)
}
- /// Destroys a concrete instance of NFT on behalf of the owner
- /// See also: [`approve`]
+ /// Destroy a token on behalf of the owner as a non-owner account.
+ ///
+ /// See also: [`approve`][`Pallet::approve`].
+ ///
+ /// After this method executes, one approval is removed from the total so that
+ /// the approved address will not be able to transfer this item again from this owner.
///
/// # Permissions
///
- /// * Collection Owner.
- /// * Collection Admin.
- /// * Current NFT Owner.
+ /// * Collection owner
+ /// * Collection admin
+ /// * Current token owner
+ /// * Address approved by current item owner
///
/// # Arguments
///
- /// * collection_id: ID of the collection.
- ///
- /// * item_id: ID of NFT to burn.
- ///
- /// * from: owner of item
+ /// * `from`: The owner of the burning item.
+ /// * `collection_id`: ID of the collection to which the item belongs.
+ /// * `item_id`: ID of item to burn.
+ /// * `value`: Number of pieces to burn.
+ /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ /// * Fungible Mode: The desired number of pieces to burn.
+ /// * Re-Fungible Mode: The desired number of pieces to burn.
#[weight = T::CommonWeightInfo::burn_from()]
#[transactional]
pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
@@ -781,25 +953,23 @@
///
/// # Permissions
///
- /// * Collection Owner
- /// * Collection Admin
- /// * Current NFT owner
+ /// * Collection owner
+ /// * Collection admin
+ /// * Current token owner
///
/// # Arguments
- ///
- /// * recipient: Address of token recipient.
///
- /// * collection_id.
- ///
- /// * item_id: ID of the item
+ /// * `recipient`: Address of token recipient.
+ /// * `collection_id`: ID of the collection the item belongs to.
+ /// * `item_id`: ID of the item.
/// * Non-Fungible Mode: Required.
/// * Fungible Mode: Ignored.
/// * Re-Fungible Mode: Required.
///
- /// * value: Amount to transfer.
- /// * Non-Fungible Mode: Ignored
- /// * Fungible Mode: Must specify transferred amount
- /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)
+ /// * `value`: Amount to transfer.
+ /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ /// * Fungible Mode: The desired number of pieces to transfer.
+ /// * Re-Fungible Mode: The desired number of pieces to transfer.
#[weight = T::CommonWeightInfo::transfer()]
#[transactional]
pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
@@ -809,21 +979,21 @@
dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))
}
- /// Set, change, or remove approved address to transfer the ownership of the NFT.
+ /// Allow a non-permissioned address to transfer or burn an item.
///
/// # Permissions
///
- /// * Collection Owner
- /// * Collection Admin
- /// * Current NFT owner
+ /// * Collection owner
+ /// * Collection admin
+ /// * Current item owner
///
/// # Arguments
///
- /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).
- ///
- /// * collection_id.
- ///
- /// * item_id: ID of the item.
+ /// * `spender`: Account to be approved to make specific transactions on non-owned tokens.
+ /// * `collection_id`: ID of the collection the item belongs to.
+ /// * `item_id`: ID of the item transactions on which are now approved.
+ /// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).
+ /// Set to 0 to revoke the approval.
#[weight = T::CommonWeightInfo::approve()]
#[transactional]
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
@@ -832,25 +1002,30 @@
dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
}
- /// 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.
+ /// Change ownership of an item on behalf of the owner as a non-owner account.
///
- /// # Permissions
- /// * Collection Owner
- /// * Collection Admin
- /// * Current NFT owner
- /// * Address approved by current NFT owner
+ /// See the [`approve`][`Pallet::approve`] method for additional information.
///
- /// # Arguments
+ /// After this method executes, one approval is removed from the total so that
+ /// the approved address will not be able to transfer this item again from this owner.
///
- /// * from: Address that owns token.
+ /// # Permissions
///
- /// * recipient: Address of token recipient.
+ /// * Collection owner
+ /// * Collection admin
+ /// * Current item owner
+ /// * Address approved by current item owner
///
- /// * collection_id.
+ /// # Arguments
///
- /// * item_id: ID of the item.
- ///
- /// * value: Amount to transfer.
+ /// * `from`: Address that currently owns the token.
+ /// * `recipient`: Address of the new token-owner-to-be.
+ /// * `collection_id`: ID of the collection the item.
+ /// * `item_id`: ID of the item to be transferred.
+ /// * `value`: Amount to transfer.
+ /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ /// * Fungible Mode: The desired number of pieces to transfer.
+ /// * Re-Fungible Mode: The desired number of pieces to transfer.
#[weight = T::CommonWeightInfo::transfer_from()]
#[transactional]
pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {
@@ -860,6 +1035,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`: ID of the modified collection.
+ /// * `new_limit`: New limits of the collection. Fields that are not set (None)
+ /// will not overwrite the old ones.
#[weight = <SelfWeightOf<T>>::set_collection_limits()]
#[transactional]
pub fn set_collection_limits(
@@ -882,12 +1069,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`: ID of the modified collection.
+ /// * `new_permission`: New permissions of the collection. Fields that are not set (None)
+ /// will not overwrite the old 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 +1094,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,18 +1103,29 @@
target_collection.save()
}
+ /// Re-partition a refungible token, while owning all of its parts/pieces.
+ ///
+ /// # Permissions
+ ///
+ /// * Token owner (must own every part)
+ ///
+ /// # Arguments
+ ///
+ /// * `collection_id`: ID of the collection the RFT belongs to.
+ /// * `token_id`: ID of the RFT.
+ /// * `amount`: New number of parts/pieces into which the token shall be partitioned.
#[weight = T::RefungibleExtensionsWeightInfo::repartition()]
#[transactional]
pub fn repartition(
origin,
collection_id: CollectionId,
- token: TokenId,
+ token_id: TokenId,
amount: u128,
) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
dispatch_tx::<T, _>(collection_id, |d| {
if let Some(refungible_extensions) = d.refungible_extensions() {
- refungible_extensions.repartition(&sender, token, amount)
+ refungible_extensions.repartition(&sender, token_id, amount)
} else {
fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)
}
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,26 +365,41 @@
pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;
-/// 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
+/// Limits and restrictions of a collection.
+/// All fields are wrapped in `Option`s, where None means chain default.
+///
+/// todo:doc links to chain defaults
+// IMPORTANT: When adding/removing fields from this struct - don't forget to also
+// update clamp_limits() in pallet-common.
#[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. Chain default: [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`]
pub account_token_ownership_limit: Option<u32>,
+ /// Maximum size of data in bytes of a sponsored transaction. Chain default: [`CUSTOM_DATA_LIMIT`]
pub sponsored_data_size: Option<u32>,
/// FIXME should we delete this or repurpose it?
/// None - setVariableMetadata is not sponsored
/// Some(v) - setVariableMetadata is sponsored
/// if there is v block between txs
+ ///
+ /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,
+ /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]
pub token_limit: Option<u32>,
- // Timeouts for item types in passed blocks
+ /// Timeout for sponsoring a token transfer in passed blocks. Chain default:
+ /// either [`NFT_SPONSOR_TRANSFER_TIMEOUT`], [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`], or [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`],
+ /// depending on the collection type.
pub sponsor_transfer_timeout: Option<u32>,
+ /// Timeout for sponsoring an approval in passed blocks. Chain default: [`SPONSOR_APPROVE_TIMEOUT`]
pub sponsor_approve_timeout: Option<u32>,
+ /// Can a token be transferred by the owner. Chain default: `false`
pub owner_can_transfer: Option<bool>,
+ /// Can a token be burned by the owner. Chain default: `true`
pub owner_can_destroy: Option<bool>,
+ /// Can a token be transferred at all. Chain default: `true`
pub transfers_enabled: Option<bool>,
}
@@ -434,7 +452,10 @@
}
}
-// When adding/removing fields from this struct - don't forget to also update clamp_limits
+/// Permissions on certain operations within a collection.
+/// All fields are wrapped in `Option`s, where None means chain default.
+// IMPORTANT: When adding/removing fields from this struct - don't forget to also
+// update clamp_limits() in pallet-common.
#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CollectionPermissions {
@@ -489,6 +510,7 @@
}
}
+/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[derivative(Debug)]
@@ -505,38 +527,49 @@
pub permissive: bool,
}
+/// Enum denominating how often can sponsoring occur if it is enabled.
#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum SponsoringRateLimit {
+ /// Sponsoring is disabled, and the collection sponsor will not pay for transactions
SponsoringDisabled,
+ /// Once per how many blocks can sponsorship of a transaction type occur
Blocks(u32),
}
+/// Data used to describe an NFT at creation.
#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]
#[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,
}
+/// Data used to describe a Fungible token at creation.
#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CreateFungibleData {
+ /// Number of fungible coins minted
pub value: u128,
}
+/// Data used to describe a Refungible token at creation.
#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[derivative(Debug)]
pub struct CreateReFungibleData {
+ /// Immutable metadata of the token
#[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,
+ /// 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,
@@ -550,6 +583,7 @@
None,
}
+/// Enum holding data used for creation of all three item types.
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum CreateItemData {
@@ -558,6 +592,7 @@
ReFungible(CreateReFungibleData),
}
+/// Explicit NFT creation data with meta parameters.
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug)]
pub struct CreateNftExData<CrossAccountId> {
@@ -566,6 +601,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> {
@@ -577,6 +613,7 @@
pub properties: CollectionPropertiesVec,
}
+/// 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> {
@@ -624,6 +661,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/augment-api-errors.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -57,7 +57,7 @@
**/
AddressNotInAllowlist: AugmentedError<ApiType>;
/**
- * Requested value more than approved.
+ * Requested value is more than the approved
**/
ApprovedValueTooLow: AugmentedError<ApiType>;
/**
@@ -113,7 +113,7 @@
**/
EmptyPropertyKey: AugmentedError<ApiType>;
/**
- * Only ASCII letters, digits, and '_', '-' are allowed
+ * Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed
**/
InvalidCharacterInPropertyKey: AugmentedError<ApiType>;
/**
@@ -133,7 +133,7 @@
**/
NoSpaceForProperty: AugmentedError<ApiType>;
/**
- * Not sufficient funds to perform action
+ * Insufficient funds to perform an action
**/
NotSufficientFounds: AugmentedError<ApiType>;
/**
@@ -153,15 +153,15 @@
**/
PublicMintingNotAllowed: AugmentedError<ApiType>;
/**
- * Only tokens from specific collections may nest tokens under this
+ * Only tokens from specific collections may nest tokens under this one
**/
SourceCollectionIsNotAllowedToNest: AugmentedError<ApiType>;
/**
- * Item not exists.
+ * Item does not exist
**/
TokenNotFound: AugmentedError<ApiType>;
/**
- * Item balance not enough.
+ * Item is balance not enough
**/
TokenValueTooLow: AugmentedError<ApiType>;
/**
@@ -173,11 +173,11 @@
**/
TransferNotAllowed: AugmentedError<ApiType>;
/**
- * Target collection doesn't supports this operation
+ * Target collection doesn't support this operation
**/
UnsupportedOperation: AugmentedError<ApiType>;
/**
- * User not passed nesting rule
+ * User does not satisfy the nesting rule
**/
UserIsNotAllowedToNest: AugmentedError<ApiType>;
/**
@@ -285,8 +285,7 @@
**/
FungibleItemsDontHaveData: AugmentedError<ApiType>;
/**
- * Not default id passed as TokenId argument.
- * The default value of TokenId for Fungible collection is 0.
+ * Fungible tokens hold no ID, and the default value of TokenId for Fungible collection is 0.
**/
FungibleItemsHaveNoId: AugmentedError<ApiType>;
/**
@@ -426,19 +425,19 @@
**/
NotRefungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
/**
- * Refungible token can't nest other tokens
+ * Refungible token can't nest other tokens.
**/
RefungibleDisallowsNesting: AugmentedError<ApiType>;
/**
- * Refungible token can't be repartitioned by user who isn't owns all pieces
+ * Refungible token can't be repartitioned by user who isn't owns all pieces.
**/
RepartitionWhileNotOwningAllPieces: AugmentedError<ApiType>;
/**
- * Setting item properties is not allowed
+ * Setting item properties is not allowed.
**/
SettingPropertiesNotAllowed: AugmentedError<ApiType>;
/**
- * Maximum refungibility exceeded
+ * Maximum refungibility exceeded.
**/
WrongRefungiblePieces: AugmentedError<ApiType>;
/**
@@ -509,19 +508,19 @@
};
structure: {
/**
- * While iterating over children, encountered breadth limit
+ * While iterating over children, reached the breadth limit.
**/
BreadthLimit: AugmentedError<ApiType>;
/**
- * While searched for owner, encountered depth limit
+ * While searching for the owner, reached the depth limit.
**/
DepthLimit: AugmentedError<ApiType>;
/**
- * While searched for owner, got already checked account
+ * While searching for the owner, encountered an already checked account, detecting a loop.
**/
OuroborosDetected: AugmentedError<ApiType>;
/**
- * While searched for owner, found token owner by not-yet-existing token
+ * Couldn't find the token owner that is itself a token.
**/
TokenNotFound: AugmentedError<ApiType>;
/**
@@ -597,7 +596,7 @@
};
unique: {
/**
- * Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.
+ * Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].
**/
CollectionDecimalPointLimitExceeded: AugmentedError<ApiType>;
/**
@@ -609,7 +608,7 @@
**/
EmptyArgument: AugmentedError<ApiType>;
/**
- * Repertition is only supported by refungible collection
+ * Repertition is only supported by refungible collection.
**/
RepartitionCalledOnNonRefungibleCollection: AugmentedError<ApiType>;
/**
tests/src/interfaces/augment-api-events.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -75,7 +75,7 @@
**/
CollectionPropertyDeleted: AugmentedEvent<ApiType, [u32, Bytes]>;
/**
- * The colletion property has been set.
+ * The colletion property has been added or edited.
**/
CollectionPropertySet: AugmentedEvent<ApiType, [u32, Bytes]>;
/**
@@ -87,7 +87,7 @@
**/
ItemDestroyed: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
/**
- * The colletion property permission has been set.
+ * The token property permission of a collection has been set.
**/
PropertyPermissionSet: AugmentedEvent<ApiType, [u32, Bytes]>;
/**
@@ -95,7 +95,7 @@
**/
TokenPropertyDeleted: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
/**
- * The token property has been set.
+ * The token property has been added or edited.
**/
TokenPropertySet: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
/**
@@ -407,7 +407,7 @@
};
structure: {
/**
- * Executed call on behalf of token
+ * Executed call on behalf of the token.
**/
Executed: AugmentedEvent<ApiType, [Result<Null, SpRuntimeDispatchError>]>;
/**
@@ -499,90 +499,80 @@
};
unique: {
/**
- * Address was add to allow list
+ * Address was added to the allow list
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * user: Address.
+ * * collection_id: ID of the affected collection.
+ * * user: Address of the added account.
**/
AllowListAddressAdded: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Address was remove from allow list
+ * Address was removed from the allow list
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * user: Address.
+ * * collection_id: ID of the affected collection.
+ * * user: Address of the removed account.
**/
AllowListAddressRemoved: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
* Collection admin was added
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * admin: Admin address.
+ * * collection_id: ID of the affected collection.
+ * * admin: Admin address.
**/
CollectionAdminAdded: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
* Collection admin was removed
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * admin: Admin address.
+ * * collection_id: ID of the affected collection.
+ * * admin: Removed admin address.
**/
CollectionAdminRemoved: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Collection limits was set
+ * Collection limits were set
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
+ * * collection_id: ID of the affected collection.
**/
CollectionLimitSet: AugmentedEvent<ApiType, [u32]>;
/**
- * Collection owned was change
+ * Collection owned was changed
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * owner: New owner address.
+ * * collection_id: ID of the affected collection.
+ * * owner: New owner address.
**/
CollectionOwnedChanged: AugmentedEvent<ApiType, [u32, AccountId32]>;
+ /**
+ * Collection permissions were set
+ *
+ * # Arguments
+ * * collection_id: ID of the affected collection.
+ **/
CollectionPermissionSet: AugmentedEvent<ApiType, [u32]>;
/**
* Collection sponsor was removed
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
+ * * collection_id: ID of the affected collection.
**/
CollectionSponsorRemoved: AugmentedEvent<ApiType, [u32]>;
/**
* Collection sponsor was set
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * owner: New sponsor address.
+ * * collection_id: ID of the affected collection.
+ * * owner: New sponsor address.
**/
CollectionSponsorSet: AugmentedEvent<ApiType, [u32, AccountId32]>;
/**
* New sponsor was confirm
*
* # Arguments
- *
- * * collection_id: Globally unique collection identifier.
- *
- * * sponsor: New sponsor address.
+ * * collection_id: ID of the affected collection.
+ * * sponsor: New sponsor address.
**/
SponsorshipConfirmed: AugmentedEvent<ApiType, [u32, AccountId32]>;
/**
tests/src/interfaces/augment-api-query.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -70,11 +70,11 @@
};
common: {
/**
- * Storage of collection admins count.
+ * Storage of the amount of collection admins.
**/
adminAmount: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
- * Allowlisted collection users
+ * Allowlisted collection users.
**/
allowlist: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
@@ -86,11 +86,11 @@
**/
collectionProperties: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
- * Storage of collection properties permissions.
+ * Storage of token property permissions of a collection.
**/
collectionPropertyPermissions: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<BTreeMap<Bytes, UpDataStructsPropertyPermission>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
- * Storage of the count of created collections.
+ * Storage of the count of created collections. Essentially contains the last collection ID.
**/
createdCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
/**
@@ -102,7 +102,7 @@
**/
dummyStorageValue: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[UpDataStructsCollectionStats, u32, u32, UpDataStructsTokenChild, PhantomTypeUpDataStructs]>>>, []> & QueryableStorageEntry<ApiType, []>;
/**
- * List of collection admins
+ * List of collection admins.
**/
isAdmin: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
@@ -200,7 +200,7 @@
};
fungible: {
/**
- * Storage for delegated assets.
+ * Storage for assets delegated to a limited extent to other users.
**/
allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
@@ -244,11 +244,11 @@
};
nonfungible: {
/**
- * Amount of tokens owned by account.
+ * Amount of tokens owned by an account in a collection.
**/
accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Allowance set by an owner for a spender for a token.
+ * Allowance set by a token owner for another user to perform one of certain transactions on a token.
**/
allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
@@ -256,7 +256,14 @@
**/
owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
/**
- * Custom data that is serialized to bytes and attached to a token property.
+ * Custom data of a token that is serialized to bytes,
+ * primarily reserved for on-chain operations,
+ * normally obscured from the external users.
+ *
+ * Auxiliary properties are slightly different from
+ * usual [`TokenProperties`] due to an unlimited number
+ * and separately stored and written-to key-value pairs.
+ *
* Currently used to store RMRK data.
**/
tokenAuxProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: UpDataStructsPropertyScope | 'None' | 'Rmrk' | number | Uint8Array, arg4: Bytes | string | Uint8Array) => Observable<Option<Bytes>>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry<ApiType, [u32, u32, UpDataStructsPropertyScope, Bytes]>;
@@ -265,19 +272,19 @@
**/
tokenChildren: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable<bool>, [u32, u32, ITuple<[u32, u32]>]> & QueryableStorageEntry<ApiType, [u32, u32, ITuple<[u32, u32]>]>;
/**
- * Custom data serialized to bytes for token.
+ * Token data, used to partially describe a token.
**/
tokenData: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletNonfungibleItemData>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
- * Key-Value map stored for token.
+ * Map of key-value pairs, describing the metadata of a token.
**/
tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
- * Amount of burnt tokens for collection.
+ * Amount of burnt tokens in a collection.
**/
tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
- * Amount of tokens minted for collection.
+ * Total amount of minted tokens in a collection.
**/
tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
@@ -444,32 +451,35 @@
};
refungible: {
/**
- * Amount of tokens owned by account
+ * Amount of tokens (not pieces) partially owned by an account within a collection.
**/
accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Allowance set by an owner for a spender for a token
+ * Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token.
**/
allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg4: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Amount of token pieces owned by account
+ * Amount of token pieces owned by account.
**/
balance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Used to enumerate tokens owned by account
+ * Used to enumerate tokens owned by account.
**/
owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
/**
- * Custom data serialized to bytes for token
+ * Token data, used to partially describe a token.
**/
tokenData: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<PalletRefungibleItemData>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+ /**
+ * Amount of pieces a refungible token is split into.
+ **/
tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
- * Amount of burnt tokens for collection
+ * Amount of tokens burnt in a collection.
**/
tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
- * Amount of tokens minted for collection
+ * Total amount of minted tokens in a collection.
**/
tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
@@ -663,24 +673,33 @@
* TODO: Off chain worker should remove from this map when collection gets removed
**/
createItemBasket: AugmentedQuery<ApiType, (arg: ITuple<[u32, AccountId32]> | [u32 | AnyNumber | Uint8Array, AccountId32 | string | Uint8Array]) => Observable<Option<u32>>, [ITuple<[u32, AccountId32]>]> & QueryableStorageEntry<ApiType, [ITuple<[u32, AccountId32]>]>;
+ /**
+ * Last sponsoring of fungible tokens approval in a collection
+ **/
fungibleApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
/**
* Collection id (controlled?2), owning user (real)
**/
fungibleTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
/**
- * Approval sponsoring
+ * Last sponsoring of NFT approval in a collection
**/
nftApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
* Collection id (controlled?2), token id (controlled?2)
**/
nftTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+ /**
+ * Last sponsoring of RFT approval in a collection
+ **/
refungibleApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, u32, AccountId32]>;
/**
* Collection id (controlled?2), token id (controlled?2)
**/
reFungibleTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, u32, AccountId32]>;
+ /**
+ * Last sponsoring of token property setting // todo:doc rephrase this and the following
+ **/
tokenPropertyBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
* Variable metadata sponsoring
tests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -629,47 +629,47 @@
};
unique: {
/**
- * Get amount of different user tokens
+ * Get the amount of any user tokens owned by an account
**/
accountBalance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
/**
- * Get tokens owned by account
+ * Get tokens owned by an account in a collection
**/
accountTokens: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<u32>>>;
/**
- * Get admin list
+ * Get the list of admin accounts of a collection
**/
adminlist: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
/**
- * Get allowed amount
+ * Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor
**/
allowance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, sender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
/**
- * Check if user is allowed to use collection
+ * Check if a user is allowed to operate within a collection
**/
allowed: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
/**
- * Get allowlist
+ * Get the list of accounts allowed to operate within a collection
**/
allowlist: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
/**
- * Get amount of specific account token
+ * Get the amount of a specific token owned by an account
**/
balance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
/**
- * Get collection by specified id
+ * Get a collection by the specified ID
**/
collectionById: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<UpDataStructsRpcCollection>>>;
/**
- * Get collection properties
+ * Get collection properties, optionally limited to the provided keys
**/
collectionProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsProperty>>>;
/**
- * Get collection stats
+ * Get chain stats about collections
**/
collectionStats: AugmentedRpc<(at?: Hash | string | Uint8Array) => Observable<UpDataStructsCollectionStats>>;
/**
- * Get tokens contained in collection
+ * Get tokens contained within a collection
**/
collectionTokens: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<u32>>>;
/**
@@ -681,15 +681,15 @@
**/
effectiveCollectionLimits: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<UpDataStructsCollectionLimits>>>;
/**
- * Get last token id
+ * Get the last token ID created in a collection
**/
lastTokenId: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
/**
- * Get number of blocks when sponsored transaction is available
+ * Get the number of blocks until sponsoring a transaction is available
**/
nextSponsored: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<u64>>>;
/**
- * Get property permissions
+ * Get property permissions, optionally limited to the provided keys
**/
propertyPermissions: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsPropertyKeyPermission>>>;
/**
@@ -697,15 +697,15 @@
**/
tokenChildren: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsTokenChild>>>;
/**
- * Get token data
+ * Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT
**/
tokenData: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<UpDataStructsTokenData>>;
/**
- * Check if token exists
+ * Check if the token exists
**/
tokenExists: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
/**
- * Get token owner
+ * Get the token owner
**/
tokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>>;
/**
@@ -713,19 +713,19 @@
**/
tokenOwners: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
/**
- * Get token properties
+ * Get token properties, optionally limited to the provided keys
**/
tokenProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsProperty>>>;
/**
- * Get token owner, in case of nested token - find parent recursive
+ * Get the topmost token owner in the hierarchy of a possibly nested token
**/
topmostTokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>>;
/**
- * Get total pieces of token
+ * Get the total amount of pieces of an RFT
**/
totalPieces: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<u128>>>;
/**
- * Get amount of unique collection tokens
+ * Get the amount of distinctive tokens present in a collection
**/
totalSupply: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
/**
tests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -730,87 +730,99 @@
};
unique: {
/**
- * 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.
+ * Add an admin to a 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.
+ * * `collection_id`: ID of the Collection to add an admin for.
+ * * `new_admin`: Address of new admin to add.
**/
- addCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newAdminId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+ addCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newAdmin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
* Add an address to allow list.
*
* # Permissions
*
- * * Collection Owner
- * * Collection Admin
+ * * Collection owner
+ * * Collection admin
*
* # Arguments
*
- * * collection_id.
- *
- * * address.
+ * * `collection_id`: ID of the modified collection.
+ * * `address`: ID of the address to be added to the allowlist.
**/
addToAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Set, change, or remove approved address to transfer the ownership of the NFT.
+ * Allow a non-permissioned address to transfer or burn an item.
*
* # Permissions
*
- * * Collection Owner
- * * Collection Admin
- * * Current NFT owner
+ * * Collection owner
+ * * Collection admin
+ * * Current item owner
*
* # Arguments
- *
- * * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).
*
- * * collection_id.
- *
- * * item_id: ID of the item.
+ * * `spender`: Account to be approved to make specific transactions on non-owned tokens.
+ * * `collection_id`: ID of the collection the item belongs to.
+ * * `item_id`: ID of the item transactions on which are now approved.
+ * * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).
+ * Set to 0 to revoke the approval.
**/
approve: AugmentedSubmittable<(spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
/**
- * Destroys a concrete instance of NFT on behalf of the owner
- * See also: [`approve`]
+ * Destroy a token on behalf of the owner as a non-owner account.
+ *
+ * See also: [`approve`][`Pallet::approve`].
+ *
+ * After this method executes, one approval is removed from the total so that
+ * the approved address will not be able to transfer this item again from this owner.
*
* # Permissions
*
- * * Collection Owner.
- * * Collection Admin.
- * * Current NFT Owner.
+ * * Collection owner
+ * * Collection admin
+ * * Current token owner
+ * * Address approved by current item owner
*
* # Arguments
*
- * * collection_id: ID of the collection.
- *
- * * item_id: ID of NFT to burn.
- *
- * * from: owner of item
+ * * `from`: The owner of the burning item.
+ * * `collection_id`: ID of the collection to which the item belongs.
+ * * `item_id`: ID of item to burn.
+ * * `value`: Number of pieces to burn.
+ * * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ * * Fungible Mode: The desired number of pieces to burn.
+ * * Re-Fungible Mode: The desired number of pieces to burn.
**/
burnFrom: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32, u128]>;
/**
- * Destroys a concrete instance of NFT.
+ * Destroy an item.
*
* # Permissions
*
- * * Collection Owner.
- * * Collection Admin.
- * * Current NFT Owner.
+ * * Collection owner
+ * * Collection admin
+ * * Current item owner
*
* # Arguments
*
- * * collection_id: ID of the collection.
- *
- * * item_id: ID of NFT to burn.
+ * * `collection_id`: ID of the collection to which the item belongs.
+ * * `item_id`: ID of item to burn.
+ * * `value`: Number of pieces of the item to destroy.
+ * * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ * * Fungible Mode: The desired number of pieces to burn.
+ * * Re-Fungible Mode: The desired number of pieces to burn.
**/
burnItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
/**
@@ -818,131 +830,200 @@
*
* # Permissions
*
- * * Collection Owner.
+ * * Collection owner
*
* # Arguments
*
- * * collection_id.
- *
- * * new_owner.
+ * * `collection_id`: ID of the modified collection.
+ * * `new_owner`: ID of the account that will become the owner.
**/
changeCollectionOwner: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newOwner: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, AccountId32]>;
/**
+ * Confirm own sponsorship of a collection, becoming the sponsor.
+ *
+ * An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].
+ * Sponsor can pay the fees of a transaction instead of the sender,
+ * but only within specified limits.
+ *
* # Permissions
*
- * * Sponsor.
+ * * Sponsor-to-be
*
* # Arguments
*
- * * collection_id.
+ * * `collection_id`: ID of the collection with the pending sponsor.
**/
confirmSponsorship: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
/**
- * This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.
+ * Create a collection of tokens.
+ *
+ * Each Token may have multiple properties encoded as an array of bytes
+ * of certain length. The initial owner of the collection is set
+ * to the address that signed the transaction and can be changed later.
+ *
+ * Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.
*
* # Permissions
*
- * * Anyone.
+ * * Anyone - becomes the owner of the new collection.
*
* # Arguments
*
- * * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.
+ * * `collection_name`: Wide-character string with collection name
+ * (limit [`MAX_COLLECTION_NAME_LENGTH`]).
+ * * `collection_description`: Wide-character string with collection description
+ * (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).
+ * * `token_prefix`: Byte string containing the token prefix to mark a collection
+ * to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).
+ * * `mode`: Type of items stored in the collection and type dependent data.
+ **/
+ createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
+ /**
+ * Create a collection with explicit parameters.
*
- * * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.
+ * Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.
+ *
+ * # Permissions
*
- * * token_prefix: UTF-8 string with token prefix.
+ * * Anyone - becomes the owner of the new collection.
*
- * * mode: [CollectionMode] collection type and type dependent data.
- **/
- createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
- /**
- * This method creates a collection
+ * # Arguments
*
- * Prefer it to deprecated [`created_collection`] method
+ * * `data`: Explicit data of a collection used for its creation.
**/
createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; pendingSponsor?: any; limits?: any; permissions?: any; tokenPropertyPermissions?: any; properties?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [UpDataStructsCreateCollectionData]>;
/**
- * This method creates a concrete instance of NFT Collection created with CreateCollection method.
+ * Mint an item within a collection.
*
+ * A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].
+ *
* # Permissions
*
- * * Collection Owner.
- * * Collection Admin.
+ * * Collection owner
+ * * Collection admin
* * Anyone if
* * Allow List is enabled, and
* * Address is added to allow list, and
- * * MintPermission is enabled (see SetMintPermission method)
+ * * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])
*
* # Arguments
*
- * * collection_id: ID of the collection.
+ * * `collection_id`: ID of the collection to which an item would belong.
+ * * `owner`: Address of the initial owner of the item.
+ * * `data`: Token data describing the item to store on chain.
+ **/
+ createItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, data: UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCreateItemData]>;
+ /**
+ * Create multiple items within a collection.
*
- * * owner: Address, initial owner of the NFT.
+ * A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].
+ *
+ * # Permissions
+ *
+ * * Collection owner
+ * * Collection admin
+ * * Anyone if
+ * * Allow List is enabled, and
+ * * Address is added to the allow list, and
+ * * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])
*
- * * data: Token data to store on chain.
+ * # Arguments
+ *
+ * * `collection_id`: ID of the collection to which the tokens would belong.
+ * * `owner`: Address of the initial owner of the tokens.
+ * * `items_data`: Vector of data describing each item to be created.
**/
- createItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, data: UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCreateItemData]>;
+ createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec<UpDataStructsCreateItemData> | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, Vec<UpDataStructsCreateItemData>]>;
/**
- * This method creates multiple items in a collection created with CreateCollection method.
+ * Create multiple items within a collection with explicitly specified initial parameters.
*
* # Permissions
*
- * * Collection Owner.
- * * Collection Admin.
+ * * Collection owner
+ * * Collection admin
* * Anyone if
* * Allow List is enabled, and
* * Address is added to allow list, and
- * * MintPermission is enabled (see SetMintPermission method)
+ * * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])
*
* # Arguments
*
- * * collection_id: ID of the collection.
+ * * `collection_id`: ID of the collection to which the tokens would belong.
+ * * `data`: Explicit item creation data.
+ **/
+ createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCreateItemExData]>;
+ /**
+ * Delete specified collection properties.
*
- * * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].
+ * # Permissions
+ *
+ * * Collection Owner
+ * * Collection Admin
+ *
+ * # Arguments
*
- * * owner: Address, initial owner of the NFT.
+ * * `collection_id`: ID of the modified collection.
+ * * `property_keys`: Vector of keys of the properties to be deleted.
+ * Keys support Latin letters, `-`, `_`, and `.` as symbols.
**/
- createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec<UpDataStructsCreateItemData> | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, Vec<UpDataStructsCreateItemData>]>;
- createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCreateItemExData]>;
deleteCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<Bytes>]>;
+ /**
+ * Delete specified token properties. Currently properties only work with NFTs.
+ *
+ * # Permissions
+ *
+ * * Depends on collection's token property permissions and specified property mutability:
+ * * Collection owner
+ * * Collection admin
+ * * Token owner
+ *
+ * # Arguments
+ *
+ * * `collection_id`: ID of the collection to which the token belongs.
+ * * `token_id`: ID of the modified token.
+ * * `property_keys`: Vector of keys of the properties to be deleted.
+ * Keys support Latin letters, `-`, `_`, and `.` as symbols.
+ **/
deleteTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, u32, Vec<Bytes>]>;
/**
- * Destroys collection if no tokens within this collection
+ * Destroy a collection if no tokens exist within.
*
* # Permissions
*
- * * Collection Owner.
+ * * Collection owner
*
* # Arguments
*
- * * collection_id: collection to destroy.
+ * * `collection_id`: Collection to destroy.
**/
destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
/**
- * 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.
+ * Remove admin of a 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
*
- * * collection_id: ID of the Collection to remove admin for.
- *
- * * account_id: Address of admin to remove.
+ * * `collection_id`: ID of the collection to remove the admin for.
+ * * `account_id`: Address of the admin to remove.
**/
removeCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, accountId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Switch back to pay-per-own-transaction model.
+ * Remove a collection's a sponsor, making everyone pay for their own transactions.
*
* # Permissions
*
- * * Collection owner.
+ * * Collection owner
*
* # Arguments
*
- * * collection_id.
+ * * `collection_id`: ID of the collection with the sponsor to remove.
**/
removeCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
/**
@@ -950,46 +1031,140 @@
*
* # Permissions
*
- * * Collection Owner
- * * Collection Admin
+ * * Collection owner
+ * * Collection admin
*
* # Arguments
*
- * * collection_id.
+ * * `collection_id`: ID of the modified collection.
+ * * `address`: ID of the address to be removed from the allowlist.
+ **/
+ removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+ /**
+ * Re-partition a refungible token, while owning all of its parts/pieces.
+ *
+ * # Permissions
+ *
+ * * Token owner (must own every part)
+ *
+ * # Arguments
+ *
+ * * `collection_id`: ID of the collection the RFT belongs to.
+ * * `token_id`: ID of the RFT.
+ * * `amount`: New number of parts/pieces into which the token shall be partitioned.
+ **/
+ repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
+ /**
+ * Set specific limits of a collection. Empty, or None fields mean chain default.
+ *
+ * # Permissions
*
- * * address.
+ * * Collection owner
+ * * Collection admin
+ *
+ * # Arguments
+ *
+ * * `collection_id`: ID of the modified collection.
+ * * `new_limit`: New limits of the collection. Fields that are not set (None)
+ * will not overwrite the old ones.
**/
- removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
- repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, token: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
setCollectionLimits: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionLimits | { accountTokenOwnershipLimit?: any; sponsoredDataSize?: any; sponsoredDataRateLimit?: any; tokenLimit?: any; sponsorTransferTimeout?: any; sponsorApproveTimeout?: any; ownerCanTransfer?: any; ownerCanDestroy?: any; transfersEnabled?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionLimits]>;
- setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionPermissions]>;
- setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsProperty>]>;
/**
+ * Set specific permissions of a collection. Empty, or None fields mean chain default.
+ *
* # Permissions
*
- * * Collection Owner
+ * * Collection owner
+ * * Collection admin
*
* # Arguments
*
- * * collection_id.
+ * * `collection_id`: ID of the modified collection.
+ * * `new_permission`: New permissions of the collection. Fields that are not set (None)
+ * will not overwrite the old ones.
+ **/
+ setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newPermission: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionPermissions]>;
+ /**
+ * Add or change collection properties.
+ *
+ * # Permissions
+ *
+ * * Collection owner
+ * * Collection admin
*
- * * new_sponsor.
+ * # Arguments
+ *
+ * * `collection_id`: ID of the modified collection.
+ * * `properties`: Vector of key-value pairs stored as the collection's metadata.
+ * Keys support Latin letters, `-`, `_`, and `.` as symbols.
**/
+ setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsProperty>]>;
+ /**
+ * Set (invite) a new collection sponsor.
+ *
+ * If successful, confirmation from the sponsor-to-be will be pending.
+ *
+ * # Permissions
+ *
+ * * Collection owner
+ * * Collection admin
+ *
+ * # Arguments
+ *
+ * * `collection_id`: ID of the modified collection.
+ * * `new_sponsor`: ID of the account of the sponsor-to-be.
+ **/
setCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newSponsor: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, AccountId32]>;
+ /**
+ * Add or change token properties according to collection's permissions.
+ * Currently properties only work with NFTs.
+ *
+ * # Permissions
+ *
+ * * Depends on collection's token property permissions and specified property mutability:
+ * * Collection owner
+ * * Collection admin
+ * * Token owner
+ *
+ * See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].
+ *
+ * # Arguments
+ *
+ * * `collection_id: ID of the collection to which the token belongs.
+ * * `token_id`: ID of the modified token.
+ * * `properties`: Vector of key-value pairs stored as the token's metadata.
+ * Keys support Latin letters, `-`, `_`, and `.` as symbols.
+ **/
setTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, u32, Vec<UpDataStructsProperty>]>;
- setTokenPropertyPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyPermissions: Vec<UpDataStructsPropertyKeyPermission> | (UpDataStructsPropertyKeyPermission | { key?: any; permission?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsPropertyKeyPermission>]>;
/**
- * Set transfers_enabled value for particular collection
+ * Add or change token property permissions of a collection.
+ *
+ * Without a permission for a particular key, a property with that key
+ * cannot be created in a token.
*
* # Permissions
*
- * * Collection Owner.
+ * * Collection owner
+ * * Collection admin
*
* # Arguments
*
- * * collection_id: ID of the collection.
+ * * `collection_id`: ID of the modified collection.
+ * * `property_permissions`: Vector of permissions for property keys.
+ * Keys support Latin letters, `-`, `_`, and `.` as symbols.
+ **/
+ setTokenPropertyPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyPermissions: Vec<UpDataStructsPropertyKeyPermission> | (UpDataStructsPropertyKeyPermission | { key?: any; permission?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsPropertyKeyPermission>]>;
+ /**
+ * Completely allow or disallow transfers for a particular collection.
*
- * * value: New flag value.
+ * # Permissions
+ *
+ * * Collection owner
+ *
+ * # Arguments
+ *
+ * * `collection_id`: ID of the collection.
+ * * `value`: New value of the flag, are transfers allowed?
**/
setTransfersEnabledFlag: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, value: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, bool]>;
/**
@@ -997,47 +1172,50 @@
*
* # Permissions
*
- * * Collection Owner
- * * Collection Admin
- * * Current NFT owner
+ * * Collection owner
+ * * Collection admin
+ * * Current token owner
*
* # Arguments
*
- * * recipient: Address of token recipient.
- *
- * * collection_id.
- *
- * * item_id: ID of the item
+ * * `recipient`: Address of token recipient.
+ * * `collection_id`: ID of the collection the item belongs to.
+ * * `item_id`: ID of the item.
* * Non-Fungible Mode: Required.
* * Fungible Mode: Ignored.
* * Re-Fungible Mode: Required.
*
- * * value: Amount to transfer.
- * * Non-Fungible Mode: Ignored
- * * Fungible Mode: Must specify transferred amount
- * * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)
+ * * `value`: Amount to transfer.
+ * * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ * * Fungible Mode: The desired number of pieces to transfer.
+ * * Re-Fungible Mode: The desired number of pieces to transfer.
**/
transfer: AugmentedSubmittable<(recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
/**
- * 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
- * * Address approved by current NFT owner
+ * Change ownership of an item on behalf of the owner as a non-owner account.
*
- * # Arguments
+ * See the [`approve`][`Pallet::approve`] method for additional information.
*
- * * from: Address that owns token.
+ * After this method executes, one approval is removed from the total so that
+ * the approved address will not be able to transfer this item again from this owner.
*
- * * recipient: Address of token recipient.
+ * # Permissions
*
- * * collection_id.
+ * * Collection owner
+ * * Collection admin
+ * * Current item owner
+ * * Address approved by current item owner
*
- * * item_id: ID of the item.
+ * # Arguments
*
- * * value: Amount to transfer.
+ * * `from`: Address that currently owns the token.
+ * * `recipient`: Address of the new token-owner-to-be.
+ * * `collection_id`: ID of the collection the item.
+ * * `item_id`: ID of the item to be transferred.
+ * * `value`: Amount to transfer.
+ * * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.
+ * * Fungible Mode: The desired number of pieces to transfer.
+ * * Re-Fungible Mode: The desired number of pieces to transfer.
**/
transferFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
/**
tests/src/interfaces/default/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -1672,7 +1672,7 @@
readonly isAddCollectionAdmin: boolean;
readonly asAddCollectionAdmin: {
readonly collectionId: u32;
- readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr;
+ readonly newAdmin: PalletEvmAccountBasicCrossAccountIdRepr;
} & Struct;
readonly isRemoveCollectionAdmin: boolean;
readonly asRemoveCollectionAdmin: {
@@ -1784,12 +1784,12 @@
readonly isSetCollectionPermissions: boolean;
readonly asSetCollectionPermissions: {
readonly collectionId: u32;
- readonly newLimit: UpDataStructsCollectionPermissions;
+ readonly newPermission: UpDataStructsCollectionPermissions;
} & Struct;
readonly isRepartition: boolean;
readonly asRepartition: {
readonly collectionId: u32;
- readonly token: u32;
+ readonly tokenId: u32;
readonly amount: u128;
} & Struct;
readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition';
tests/src/interfaces/lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1269,7 +1269,7 @@
},
add_collection_admin: {
collectionId: 'u32',
- newAdminId: 'PalletEvmAccountBasicCrossAccountIdRepr',
+ newAdmin: 'PalletEvmAccountBasicCrossAccountIdRepr',
},
remove_collection_admin: {
collectionId: 'u32',
@@ -1361,11 +1361,11 @@
},
set_collection_permissions: {
collectionId: 'u32',
- newLimit: 'UpDataStructsCollectionPermissions',
+ newPermission: 'UpDataStructsCollectionPermissions',
},
repartition: {
collectionId: 'u32',
- token: 'u32',
+ tokenId: 'u32',
amount: 'u128'
}
}
tests/src/interfaces/types-lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1385,7 +1385,7 @@
readonly isAddCollectionAdmin: boolean;
readonly asAddCollectionAdmin: {
readonly collectionId: u32;
- readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr;
+ readonly newAdmin: PalletEvmAccountBasicCrossAccountIdRepr;
} & Struct;
readonly isRemoveCollectionAdmin: boolean;
readonly asRemoveCollectionAdmin: {
@@ -1497,12 +1497,12 @@
readonly isSetCollectionPermissions: boolean;
readonly asSetCollectionPermissions: {
readonly collectionId: u32;
- readonly newLimit: UpDataStructsCollectionPermissions;
+ readonly newPermission: UpDataStructsCollectionPermissions;
} & Struct;
readonly isRepartition: boolean;
readonly asRepartition: {
readonly collectionId: u32;
- readonly token: u32;
+ readonly tokenId: u32;
readonly amount: u128;
} & Struct;
readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition';
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -37,49 +37,143 @@
export default {
types: {},
rpc: {
- adminlist: fun('Get admin list', [collectionParam], 'Vec<PalletEvmAccountBasicCrossAccountIdRepr>'),
- allowlist: fun('Get allowlist', [collectionParam], 'Vec<PalletEvmAccountBasicCrossAccountIdRepr>'),
+ accountTokens: fun(
+ 'Get tokens owned by an account in a collection',
+ [collectionParam, crossAccountParam()],
+ 'Vec<u32>',
+ ),
+ collectionTokens: fun(
+ 'Get tokens contained within a collection',
+ [collectionParam],
+ 'Vec<u32>',
+ ),
+ tokenExists: fun(
+ 'Check if the token exists',
+ [collectionParam, tokenParam],
+ 'bool',
+ ),
- accountTokens: fun('Get tokens owned by account', [collectionParam, crossAccountParam()], 'Vec<u32>'),
- collectionTokens: fun('Get tokens contained in collection', [collectionParam], 'Vec<u32>'),
+ tokenOwner: fun(
+ 'Get the token owner',
+ [collectionParam, tokenParam],
+ `Option<${CROSS_ACCOUNT_ID_TYPE}>`,
+ ),
+ topmostTokenOwner: fun(
+ 'Get the topmost token owner in the hierarchy of a possibly nested token',
+ [collectionParam, tokenParam],
+ `Option<${CROSS_ACCOUNT_ID_TYPE}>`,
+ ),
+ tokenOwners: fun(
+ 'Returns 10 tokens owners in no particular order',
+ [collectionParam, tokenParam],
+ `Vec<${CROSS_ACCOUNT_ID_TYPE}>`,
+ ),
+ tokenChildren: fun(
+ 'Get tokens nested directly into the token',
+ [collectionParam, tokenParam],
+ 'Vec<UpDataStructsTokenChild>',
+ ),
- lastTokenId: fun('Get last token id', [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'),
- allowance: fun('Get allowed amount', [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], 'u128'),
- tokenOwner: fun('Get token owner', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
- tokenOwners: fun('Returns 10 tokens owners in no particular order', [collectionParam, tokenParam], `Vec<${CROSS_ACCOUNT_ID_TYPE}>`),
- topmostTokenOwner: fun('Get token owner, in case of nested token - find parent recursive', [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>'),
collectionProperties: fun(
- 'Get collection properties',
+ 'Get collection properties, optionally limited to the provided keys',
[collectionParam, propertyKeysParam],
'Vec<UpDataStructsProperty>',
),
tokenProperties: fun(
- 'Get token properties',
+ 'Get token properties, optionally limited to the provided keys',
[collectionParam, tokenParam, propertyKeysParam],
'Vec<UpDataStructsProperty>',
),
propertyPermissions: fun(
- 'Get property permissions',
+ 'Get property permissions, optionally limited to the provided keys',
[collectionParam, propertyKeysParam],
'Vec<UpDataStructsPropertyKeyPermission>',
),
+
+ constMetadata: fun(
+ 'Get token constant metadata',
+ [collectionParam, tokenParam],
+ 'Vec<u8>',
+ ),
+ variableMetadata: fun(
+ 'Get token variable metadata',
+ [collectionParam, tokenParam],
+ 'Vec<u8>',
+ ),
+
tokenData: fun(
- 'Get token data',
+ 'Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT',
[collectionParam, tokenParam, propertyKeysParam],
'UpDataStructsTokenData',
),
- tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),
- 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>'),
- effectiveCollectionLimits: fun('Get effective collection limits', [collectionParam], 'Option<UpDataStructsCollectionLimits>'),
- totalPieces: fun('Get total pieces of token', [collectionParam, tokenParam], 'Option<u128>'),
+ totalSupply: fun(
+ 'Get the amount of distinctive tokens present in a collection',
+ [collectionParam],
+ 'u32',
+ ),
+
+ accountBalance: fun(
+ 'Get the amount of any user tokens owned by an account',
+ [collectionParam, crossAccountParam()],
+ 'u32',
+ ),
+ balance: fun(
+ 'Get the amount of a specific token owned by an account',
+ [collectionParam, crossAccountParam(), tokenParam],
+ 'u128',
+ ),
+ allowance: fun(
+ 'Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor',
+ [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam],
+ 'u128',
+ ),
+
+ adminlist: fun(
+ 'Get the list of admin accounts of a collection',
+ [collectionParam],
+ 'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
+ ),
+ allowlist: fun(
+ 'Get the list of accounts allowed to operate within a collection',
+ [collectionParam],
+ 'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
+ ),
+ allowed: fun(
+ 'Check if a user is allowed to operate within a collection',
+ [collectionParam, crossAccountParam()],
+ 'bool',
+ ),
+
+ lastTokenId: fun(
+ 'Get the last token ID created in a collection',
+ [collectionParam],
+ 'u32',
+ ),
+ collectionById: fun(
+ 'Get a collection by the specified ID',
+ [collectionParam],
+ 'Option<UpDataStructsRpcCollection>',
+ ),
+ collectionStats: fun(
+ 'Get chain stats about collections',
+ [],
+ 'UpDataStructsCollectionStats',
+ ),
+
+ nextSponsored: fun(
+ 'Get the number of blocks until sponsoring a transaction is available',
+ [collectionParam, crossAccountParam(), tokenParam],
+ 'Option<u64>',
+ ),
+ effectiveCollectionLimits: fun(
+ 'Get effective collection limits',
+ [collectionParam],
+ 'Option<UpDataStructsCollectionLimits>',
+ ),
+ totalPieces: fun(
+ 'Get the total amount of pieces of an RFT',
+ [collectionParam, tokenParam],
+ 'Option<u128>',
+ ),
},
};