difftreelog
doc: architectural changes
in: master
10 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -42,6 +42,7 @@
#[rpc(server)]
#[async_trait]
pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {
+ /// Get tokens owned by account
#[method(name = "unique_accountTokens")]
fn account_tokens(
&self,
@@ -49,12 +50,14 @@
account: CrossAccountId,
at: Option<BlockHash>,
) -> Result<Vec<TokenId>>;
+ /// Get tokens contained in collection
#[method(name = "unique_collectionTokens")]
fn collection_tokens(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<TokenId>>;
+ /// Check if token exists
#[method(name = "unique_tokenExists")]
fn token_exists(
&self,
@@ -62,7 +65,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<bool>;
-
+ /// Get token owner
#[method(name = "unique_tokenOwner")]
fn token_owner(
&self,
@@ -70,6 +73,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+ /// Get token owner, in case of nested token - find the parent recursively
#[method(name = "unique_topmostTokenOwner")]
fn topmost_token_owner(
&self,
@@ -77,6 +81,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+ /// Get tokens nested directly into the token
#[method(name = "unique_tokenChildren")]
fn token_children(
&self,
@@ -84,7 +89,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Vec<TokenChild>>;
-
+ /// Get collection properties
#[method(name = "unique_collectionProperties")]
fn collection_properties(
&self,
@@ -92,7 +97,7 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<Vec<Property>>;
-
+ /// Get token properties
#[method(name = "unique_tokenProperties")]
fn token_properties(
&self,
@@ -101,7 +106,7 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<Vec<Property>>;
-
+ /// Get property permissions
#[method(name = "unique_propertyPermissions")]
fn property_permissions(
&self,
@@ -109,7 +114,7 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<Vec<PropertyKeyPermission>>;
-
+ /// Get token data
#[method(name = "unique_tokenData")]
fn token_data(
&self,
@@ -118,9 +123,10 @@
keys: Option<Vec<String>>,
at: Option<BlockHash>,
) -> Result<TokenData<CrossAccountId>>;
-
+ /// Get amount of unique collection tokens
#[method(name = "unique_totalSupply")]
fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;
+ /// Get owned amount of any user tokens
#[method(name = "unique_accountBalance")]
fn account_balance(
&self,
@@ -128,6 +134,7 @@
account: CrossAccountId,
at: Option<BlockHash>,
) -> Result<u32>;
+ /// Get owned amount of specific account token
#[method(name = "unique_balance")]
fn balance(
&self,
@@ -136,6 +143,7 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<String>;
+ /// Get allowed amount
#[method(name = "unique_allowance")]
fn allowance(
&self,
@@ -145,19 +153,21 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<String>;
-
+ /// Get admin list
#[method(name = "unique_adminlist")]
fn adminlist(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+ /// Get allowlist
#[method(name = "unique_allowlist")]
fn allowlist(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+ /// Check if user is allowed to use collection
#[method(name = "unique_allowed")]
fn allowed(
&self,
@@ -165,17 +175,20 @@
user: CrossAccountId,
at: Option<BlockHash>,
) -> Result<bool>;
+ /// Get last token ID created in a collection
#[method(name = "unique_lastTokenId")]
fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;
+ /// Get collection by specified ID
#[method(name = "unique_collectionById")]
fn collection_by_id(
&self,
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Option<RpcCollection<AccountId>>>;
+ /// Get collection stats
#[method(name = "unique_collectionStats")]
fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;
-
+ /// Get number of blocks when sponsored transaction is available
#[method(name = "unique_nextSponsored")]
fn next_sponsored(
&self,
@@ -184,14 +197,14 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<u64>>;
-
+ /// Get effective collection limits
#[method(name = "unique_effectiveCollectionLimits")]
fn effective_collection_limits(
&self,
collection_id: CollectionId,
at: Option<BlockHash>,
) -> Result<Option<CollectionLimits>>;
-
+ /// Get total pieces of token
#[method(name = "unique_totalPieces")]
fn total_pieces(
&self,
@@ -304,6 +317,7 @@
fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;
#[method(name = "rmrk_themeNames")]
+ /// Get Base's theme names
fn theme_names(
&self,
base_id: RmrkBaseId,
@@ -311,6 +325,7 @@
) -> Result<Vec<RmrkThemeName>>;
#[method(name = "rmrk_themes")]
+ /// Get Theme info -- name, properties, and inherit flag
fn theme(
&self,
base_id: RmrkBaseId,
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -299,46 +299,48 @@
///
/// # Arguments
///
- /// * collection_id: Globally unique identifier of collection.
+ /// * collection_id: Globally unique identifier of collection that has been destroyed.
CollectionDestroyed(CollectionId),
/// New item was created.
///
/// # Arguments
///
- /// * collection_id: Id of the collection where item was created.
+ /// * collection_id: ID of the collection where the item was created.
///
- /// * item_id: Id of an item. Unique within the collection.
+ /// * item_id: ID of the item. Unique within the collection.
///
- /// * recipient: Owner of newly created item
+ /// * recipient: Owner of the newly created item.
///
- /// * amount: Always 1 for NFT
+ /// * amount: The amount of tokens that were created (always 1 for NFT).
ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),
/// Collection item was burned.
///
/// # Arguments
///
- /// * collection_id.
+ /// * collection_id: Identifier of the collection to which the burned NFT belonged.
///
/// * item_id: Identifier of burned NFT.
///
- /// * owner: which user has destroyed its tokens
+ /// * owner: Which user has destroyed their tokens.
///
- /// * amount: Always 1 for NFT
+ /// * amount: The amount of tokens that were destroyed (always 1 for NFT).
ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),
- /// Item was transferred
+ /// Item was transferred.
+ ///
+ /// # Arguments
///
- /// * collection_id: Id of collection to which item is belong
+ /// * collection_id: ID of the collection to which the item belongs.
///
- /// * item_id: Id of an item
+ /// * item_id: ID of the item trasnferred.
///
- /// * sender: Original owner of item
+ /// * sender: Original owner of the item.
///
- /// * recipient: New owner of item
+ /// * recipient: New owner of the item.
///
- /// * amount: Always 1 for NFT
+ /// * amount: The amount of tokens that were transferred (always 1 for NFT).
Transfer(
CollectionId,
TokenId,
@@ -347,6 +349,10 @@
u128,
),
+ /// Sponsoring allowance was approved.
+ ///
+ /// # Arguments
+ ///
/// * collection_id
///
/// * item_id
@@ -364,14 +370,53 @@
u128,
),
+ /// Collection property was added or edited.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose property was just set.
+ ///
+ /// * property_key: Key of the property that was just set.
CollectionPropertySet(CollectionId, PropertyKey),
+ /// Collection property was deleted.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose property was just deleted.
+ ///
+ /// * property_key: Key of the property that was just deleted.
CollectionPropertyDeleted(CollectionId, PropertyKey),
+ /// Item property was added or edited.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose token's property was just set.
+ ///
+ /// * item_id: ID of the item, whose property was just set.
+ ///
+ /// * property_key: Key of the property that was just set.
TokenPropertySet(CollectionId, TokenId, PropertyKey),
+ /// Item property was deleted.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose token's property was just deleted.
+ ///
+ /// * item_id: ID of the item, whose property was just deleted.
+ ///
+ /// * property_key: Key of the property that was just deleted.
TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),
+ /// Token property permission was added or updated for a collection.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: ID of the collection, whose permissions were just set/updated.
+ ///
+ /// * property_key: Key of the property of the set/updated permission.
PropertyPermissionSet(CollectionId, PropertyKey),
}
@@ -413,26 +458,26 @@
/// Metadata flag frozen
MetadataFlagFrozen,
- /// Item not exists.
+ /// Item does not exist
TokenNotFound,
- /// Item balance not enough.
+ /// Item is balance not enough
TokenValueTooLow,
- /// Requested value more than approved.
+ /// Requested value is more than the approved
ApprovedValueTooLow,
/// Tried to approve more than owned
CantApproveMoreThanOwned,
/// Can't transfer tokens to ethereum zero address
AddressIsZero,
- /// Target collection doesn't supports this operation
+ /// Target collection doesn't support this operation
UnsupportedOperation,
- /// Not sufficient funds to perform action
+ /// Insufficient funds to perform an action
NotSufficientFounds,
- /// User not passed nesting rule
+ /// User does not satisfy the nesting rule
UserIsNotAllowedToNest,
- /// Only tokens from specific collections may nest tokens under this
+ /// Only tokens from specific collections may nest tokens under this one
SourceCollectionIsNotAllowedToNest,
/// Tried to store more data than allowed in collection field
@@ -447,7 +492,7 @@
/// Property key is too long
PropertyKeyIsTooLong,
- /// Only ASCII letters, digits, and '_', '-' are allowed
+ /// Only ASCII letters, digits, and symbols '_', '-', and '.' are allowed
InvalidCharacterInPropertyKey,
/// Empty property keys are forbidden
@@ -460,8 +505,11 @@
CollectionIsInternal,
}
+ /// The number of created collections. Essentially contains the last collection ID.
#[pallet::storage]
pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+
+ /// The number of destroyed collections
#[pallet::storage]
pub type DestroyedCollectionCount<T> =
StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
@@ -486,6 +534,7 @@
OnEmpty = up_data_structs::CollectionProperties,
>;
+ /// Token permissions of a collection
#[pallet::storage]
#[pallet::getter(fn property_permissions)]
pub type CollectionPropertyPermissions<T> = StorageMap<
@@ -495,6 +544,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of collection admins
#[pallet::storage]
pub type AdminAmount<T> = StorageMap<
Hasher = Blake2_128Concat,
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -44,6 +44,7 @@
pub mod erc;
pub mod weights;
+/// todo:doc?
pub type CreateItemData<T> = (<T as pallet_evm::account::Config>::CrossAccountId, u128);
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
@@ -78,10 +79,12 @@
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
+ /// Total amount of fungible tokens inside a collection.
#[pallet::storage]
pub type TotalSupply<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;
+ /// Amount of tokens owned by an account inside a collection.
#[pallet::storage]
pub type Balance<T: Config> = StorageNMap<
Key = (
@@ -92,6 +95,7 @@
QueryKind = ValueQuery,
>;
+ /// todo:doc
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -56,6 +56,8 @@
pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
+/// Token data, stored independently from other data used to describe it.
+/// Notably contains the owner account address.
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
pub struct ItemData<CrossAccountId> {
@@ -102,13 +104,17 @@
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
+ /// Total amount of minted tokens in a collection.
#[pallet::storage]
pub type TokensMinted<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+
+ /// Amount of burnt tokens in a collection.
#[pallet::storage]
pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+ /// Token data, used to partially describe a token.
#[pallet::storage]
pub type TokenData<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -116,6 +122,7 @@
QueryKind = OptionQuery,
>;
+ /// Key-value pairs, describing the metadata of a token.
#[pallet::storage]
#[pallet::getter(fn token_properties)]
pub type TokenProperties<T: Config> = StorageNMap<
@@ -125,6 +132,7 @@
OnEmpty = up_data_structs::TokenProperties,
>;
+ /// Scoped, auxiliary properties of a token, primarily used for on-chain operations.
#[pallet::storage]
#[pallet::getter(fn token_aux_property)]
pub type TokenAuxProperties<T: Config> = StorageNMap<
@@ -138,7 +146,7 @@
QueryKind = OptionQuery,
>;
- /// Used to enumerate tokens owned by account
+ /// Used to enumerate tokens owned by account.
#[pallet::storage]
pub type Owned<T: Config> = StorageNMap<
Key = (
@@ -150,7 +158,7 @@
QueryKind = ValueQuery,
>;
- /// Used to enumerate token's children
+ /// Used to enumerate token's children.
#[pallet::storage]
#[pallet::getter(fn token_children)]
pub type TokenChildren<T: Config> = StorageNMap<
@@ -163,6 +171,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of tokens owned in a collection.s
#[pallet::storage]
pub type AccountBalance<T: Config> = StorageNMap<
Key = (
@@ -173,6 +182,7 @@
QueryKind = ValueQuery,
>;
+ /// todo doc
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -180,6 +190,7 @@
QueryKind = OptionQuery,
>;
+ /// Upgrade from the old schema to properties.
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_runtime_upgrade() -> Weight {
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -38,6 +38,8 @@
pub mod weights;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
+/// Token data, stored independently from other data used to describe it.
+/// Notably contains the token metadata.
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
pub struct ItemData {
@@ -86,13 +88,17 @@
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
+ /// Total amount of minted tokens in a collection.
#[pallet::storage]
pub type TokensMinted<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+
+ /// Amount of tokens burnt in a collection.
#[pallet::storage]
pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
+ /// Token data, used to partially describe a token.
#[pallet::storage]
pub type TokenData<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -100,6 +106,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of pieces a refungible token is split into.
#[pallet::storage]
pub type TotalSupply<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
@@ -107,7 +114,7 @@
QueryKind = ValueQuery,
>;
- /// Used to enumerate tokens owned by account
+ /// Used to enumerate tokens owned by account.
#[pallet::storage]
pub type Owned<T: Config> = StorageNMap<
Key = (
@@ -119,6 +126,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of tokens (not pieces) partially owned by an account within a collection.
#[pallet::storage]
pub type AccountBalance<T: Config> = StorageNMap<
Key = (
@@ -130,6 +138,7 @@
QueryKind = ValueQuery,
>;
+ /// Amount of pieces of a token owned by an account.
#[pallet::storage]
pub type Balance<T: Config> = StorageNMap<
Key = (
@@ -142,6 +151,7 @@
QueryKind = ValueQuery,
>;
+ /// todo:doc
#[pallet::storage]
pub type Allowance<T: Config> = StorageNMap<
Key = (
@@ -248,7 +258,7 @@
// TODO: ERC721 transfer event
Ok(())
}
-
+
pub fn burn(
collection: &RefungibleHandle<T>,
owner: &T::CrossAccountId,
@@ -595,6 +605,7 @@
Ok(())
}
+ /// todo:doc oh look, a precedent. not pub, too. but it has an unclear use-case.
/// Returns allowance, which should be set after transaction
fn check_allowed(
collection: &RefungibleHandle<T>,
pallets/scheduler/src/lib.rsdiffbeforeafterboth--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -258,6 +258,7 @@
/// A Scheduler-Runtime interface for finer payment handling.
pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {
+ /// Reserve the maximum spendings on a call.
fn reserve_balance(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -265,6 +266,7 @@
count: u32,
) -> Result<(), DispatchError>;
+ /// Pay for call dispatch (un-reserve) from the reserved funds, returning the change.
fn pay_for_call(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
@@ -280,6 +282,7 @@
TransactionValidityError,
>;
+ /// Release reserved funds.
fn cancel_reserve(
id: ScheduledId,
sponsor: <T as frame_system::Config>::AccountId,
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -25,19 +25,19 @@
#[pallet::error]
pub enum Error<T> {
- /// While searched for owner, got already checked account
+ /// While searching for the owner, encountered an already checked account, detecting a loop.
OuroborosDetected,
- /// While searched for owner, encountered depth limit
+ /// While searching for the owner, reached the depth limit.
DepthLimit,
- /// While iterating over children, encountered breadth limit
+ /// While iterating over children, reached the breadth limit.
BreadthLimit,
- /// While searched for owner, found token owner by not-yet-existing token
+ /// Couldn't find the token owner that is a token. Perhaps, it does not yet exist. todo:doc? rephrase?
TokenNotFound,
}
#[pallet::event]
pub enum Event<T> {
- /// Executed call on behalf of token
+ /// Executed call on behalf of the token.
Executed(DispatchResult),
}
@@ -73,11 +73,11 @@
#[derive(PartialEq)]
pub enum Parent<CrossAccountId> {
- /// Token owned by normal account
+ /// Token owned by a normal account.
User(CrossAccountId),
- /// Passed token not found
+ /// Could not find the token provided as the owner.
TokenNotFound,
- /// Token owner is another token (target token still may not exist)
+ /// Token owner is another token (still, the target token may not exist).
Token(CollectionId, TokenId),
}
pallets/unique/src/lib.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#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20 clippy::too_many_arguments,21 clippy::unnecessary_mut_passed,22 clippy::unused_unit23)]2425extern crate alloc;2627use frame_support::{28 decl_module, decl_storage, decl_error, decl_event,29 dispatch::DispatchResult,30 ensure, fail,31 weights::{Weight},32 transactional,33 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},34 BoundedVec,35};36use scale_info::TypeInfo;37use frame_system::{self as system, ensure_signed};38use sp_runtime::{sp_std::prelude::Vec};39use up_data_structs::{40 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,41 CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,42 SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,43 PropertyKeyPermission,44};45use pallet_evm::account::CrossAccountId;46use pallet_common::{47 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,48 dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,49};50pub mod eth;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;54pub mod weights;55use weights::WeightInfo;5657const NESTING_BUDGET: u32 = 5;5859decl_error! {60 /// Error for non-fungible-token module.61 pub enum Error for Module<T: Config> {62 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.63 CollectionDecimalPointLimitExceeded,64 /// This address is not set as sponsor, use setCollectionSponsor first.65 ConfirmUnsetSponsorFail,66 /// Length of items properties must be greater than 0.67 EmptyArgument,68 /// Repertition is only supported by refungible collection69 RepartitionCalledOnNonRefungibleCollection,70 }71}7273pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {74 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7576 /// Weight information for extrinsics in this pallet.77 type WeightInfo: WeightInfo;78 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;79 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;80}8182decl_event! {83 pub enum Event<T>84 where85 <T as frame_system::Config>::AccountId,86 <T as pallet_evm::account::Config>::CrossAccountId,87 {88 /// Collection sponsor was removed89 ///90 /// # Arguments91 ///92 /// * collection_id: Globally unique collection identifier.93 CollectionSponsorRemoved(CollectionId),9495 /// Collection admin was added96 ///97 /// # Arguments98 ///99 /// * collection_id: Globally unique collection identifier.100 ///101 /// * admin: Admin address.102 CollectionAdminAdded(CollectionId, CrossAccountId),103104 /// Collection owned was change105 ///106 /// # Arguments107 ///108 /// * collection_id: Globally unique collection identifier.109 ///110 /// * owner: New owner address.111 CollectionOwnedChanged(CollectionId, AccountId),112113 /// Collection sponsor was set114 ///115 /// # Arguments116 ///117 /// * collection_id: Globally unique collection identifier.118 ///119 /// * owner: New sponsor address.120 CollectionSponsorSet(CollectionId, AccountId),121122 /// New sponsor was confirm123 ///124 /// # Arguments125 ///126 /// * collection_id: Globally unique collection identifier.127 ///128 /// * sponsor: New sponsor address.129 SponsorshipConfirmed(CollectionId, AccountId),130131 /// Collection admin was removed132 ///133 /// # Arguments134 ///135 /// * collection_id: Globally unique collection identifier.136 ///137 /// * admin: Admin address.138 CollectionAdminRemoved(CollectionId, CrossAccountId),139140 /// Address was remove from allow list141 ///142 /// # Arguments143 ///144 /// * collection_id: Globally unique collection identifier.145 ///146 /// * user: Address.147 AllowListAddressRemoved(CollectionId, CrossAccountId),148149 /// Address was add to allow list150 ///151 /// # Arguments152 ///153 /// * collection_id: Globally unique collection identifier.154 ///155 /// * user: Address.156 AllowListAddressAdded(CollectionId, CrossAccountId),157158 /// Collection limits was set159 ///160 /// # Arguments161 ///162 /// * collection_id: Globally unique collection identifier.163 CollectionLimitSet(CollectionId),164165 CollectionPermissionSet(CollectionId),166 }167}168169type SelfWeightOf<T> = <T as Config>::WeightInfo;170171// # Used definitions172//173// ## User control levels174//175// chain-controlled - key is uncontrolled by user176// i.e autoincrementing index177// can use non-cryptographic hash178// real - key is controlled by user179// but it is hard to generate enough colliding values, i.e owner of signed txs180// can use non-cryptographic hash181// controlled - key is completly controlled by users182// i.e maps with mutable keys183// should use cryptographic hash184//185// ## User control level downgrade reasons186//187// ?1 - chain-controlled -> controlled188// collections/tokens can be destroyed, resulting in massive holes189// ?2 - chain-controlled -> controlled190// same as ?1, but can be only added, resulting in easier exploitation191// ?3 - real -> controlled192// no confirmation required, so addresses can be easily generated193decl_storage! {194 trait Store for Module<T: Config> as Unique {195196 //#region Private members197 /// Used for migrations198 ChainVersion: u64;199 //#endregion200201 //#region Tokens transfer rate limit baskets202 /// (Collection id (controlled?2), who created (real))203 /// TODO: Off chain worker should remove from this map when collection gets removed204 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;205 /// Collection id (controlled?2), token id (controlled?2)206 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;207 /// Collection id (controlled?2), owning user (real)208 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;209 /// Collection id (controlled?2), token id (controlled?2)210 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;211 //#endregion212213 /// Variable metadata sponsoring214 /// Collection id (controlled?2), token id (controlled?2)215 #[deprecated]216 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;217 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;218219 /// Approval sponsoring220 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;221 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;222 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>;223 }224}225226decl_module! {227 pub struct Module<T: Config> for enum Call228 where229 origin: T::Origin230 {231 type Error = Error<T>;232233 fn deposit_event() = default;234235 fn on_initialize(_now: T::BlockNumber) -> Weight {236 0237 }238239 fn on_runtime_upgrade() -> Weight {240 let limit = None;241242 <VariableMetaDataBasket<T>>::remove_all(limit);243244 0245 }246247 /// 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.248 ///249 /// # Permissions250 ///251 /// * Anyone.252 ///253 /// # Arguments254 ///255 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.256 ///257 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.258 ///259 /// * token_prefix: UTF-8 string with token prefix.260 ///261 /// * mode: [CollectionMode] collection type and type dependent data.262 // returns collection ID263 #[weight = <SelfWeightOf<T>>::create_collection()]264 #[transactional]265 #[deprecated]266 pub fn create_collection(origin,267 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,268 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,269 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,270 mode: CollectionMode) -> DispatchResult {271 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {272 name: collection_name,273 description: collection_description,274 token_prefix,275 mode,276 ..Default::default()277 };278 Self::create_collection_ex(origin, data)279 }280281 /// This method creates a collection282 ///283 /// Prefer it to deprecated [`created_collection`] method284 #[weight = <SelfWeightOf<T>>::create_collection()]285 #[transactional]286 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {287 let sender = ensure_signed(origin)?;288289 // =========290291 T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;292293 Ok(())294 }295296 /// Destroys collection if no tokens within this collection297 ///298 /// # Permissions299 ///300 /// * Collection Owner.301 ///302 /// # Arguments303 ///304 /// * collection_id: collection to destroy.305 #[weight = <SelfWeightOf<T>>::destroy_collection()]306 #[transactional]307 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {308 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);309 let collection = <CollectionHandle<T>>::try_get(collection_id)?;310 collection.check_is_internal()?;311312 // =========313314 T::CollectionDispatch::destroy(sender, collection)?;315316 <NftTransferBasket<T>>::remove_prefix(collection_id, None);317 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);318 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);319320 <NftApproveBasket<T>>::remove_prefix(collection_id, None);321 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);322 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);323324 Ok(())325 }326327 /// Add an address to allow list.328 ///329 /// # Permissions330 ///331 /// * Collection Owner332 /// * Collection Admin333 ///334 /// # Arguments335 ///336 /// * collection_id.337 ///338 /// * address.339 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]340 #[transactional]341 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{342343 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);344 let collection = <CollectionHandle<T>>::try_get(collection_id)?;345 collection.check_is_internal()?;346347 <PalletCommon<T>>::toggle_allowlist(348 &collection,349 &sender,350 &address,351 true,352 )?;353354 Self::deposit_event(Event::<T>::AllowListAddressAdded(355 collection_id,356 address357 ));358359 Ok(())360 }361362 /// Remove an address from allow list.363 ///364 /// # Permissions365 ///366 /// * Collection Owner367 /// * Collection Admin368 ///369 /// # Arguments370 ///371 /// * collection_id.372 ///373 /// * address.374 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]375 #[transactional]376 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{377378 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);379 let collection = <CollectionHandle<T>>::try_get(collection_id)?;380 collection.check_is_internal()?;381382 <PalletCommon<T>>::toggle_allowlist(383 &collection,384 &sender,385 &address,386 false,387 )?;388389 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(390 collection_id,391 address392 ));393394 Ok(())395 }396397 /// Change the owner of the collection.398 ///399 /// # Permissions400 ///401 /// * Collection Owner.402 ///403 /// # Arguments404 ///405 /// * collection_id.406 ///407 /// * new_owner.408 #[weight = <SelfWeightOf<T>>::change_collection_owner()]409 #[transactional]410 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {411412 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);413414 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;415 target_collection.check_is_internal()?;416 target_collection.check_is_owner(&sender)?;417418 target_collection.owner = new_owner.clone();419 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(420 collection_id,421 new_owner422 ));423424 target_collection.save()425 }426427 /// Adds an admin of the Collection.428 /// 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.429 ///430 /// # Permissions431 ///432 /// * Collection Owner.433 /// * Collection Admin.434 ///435 /// # Arguments436 ///437 /// * collection_id: ID of the Collection to add admin for.438 ///439 /// * new_admin_id: Address of new admin to add.440 #[weight = <SelfWeightOf<T>>::add_collection_admin()]441 #[transactional]442 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {443 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);444 let collection = <CollectionHandle<T>>::try_get(collection_id)?;445 collection.check_is_internal()?;446447 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(448 collection_id,449 new_admin_id.clone()450 ));451452 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)453 }454455 /// 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.456 ///457 /// # Permissions458 ///459 /// * Collection Owner.460 /// * Collection Admin.461 ///462 /// # Arguments463 ///464 /// * collection_id: ID of the Collection to remove admin for.465 ///466 /// * account_id: Address of admin to remove.467 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]468 #[transactional]469 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {470 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);471 let collection = <CollectionHandle<T>>::try_get(collection_id)?;472 collection.check_is_internal()?;473474 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(475 collection_id,476 account_id.clone()477 ));478479 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)480 }481482 /// # Permissions483 ///484 /// * Collection Owner485 ///486 /// # Arguments487 ///488 /// * collection_id.489 ///490 /// * new_sponsor.491 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]492 #[transactional]493 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {494 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);495496 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;497 target_collection.check_is_owner_or_admin(&sender)?;498 target_collection.check_is_internal()?;499500 target_collection.set_sponsor(new_sponsor.clone())?;501502 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(503 collection_id,504 new_sponsor505 ));506507 target_collection.save()508 }509510 /// # Permissions511 ///512 /// * Sponsor.513 ///514 /// # Arguments515 ///516 /// * collection_id.517 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]518 #[transactional]519 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {520 let sender = ensure_signed(origin)?;521522 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;523 target_collection.check_is_internal()?;524 ensure!(525 target_collection.confirm_sponsorship(&sender)?,526 Error::<T>::ConfirmUnsetSponsorFail527 );528529 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(530 collection_id,531 sender532 ));533534 target_collection.save()535 }536537 /// Switch back to pay-per-own-transaction model.538 ///539 /// # Permissions540 ///541 /// * Collection owner.542 ///543 /// # Arguments544 ///545 /// * collection_id.546 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]547 #[transactional]548 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {549 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);550551 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;552 target_collection.check_is_internal()?;553 target_collection.check_is_owner(&sender)?;554555 target_collection.sponsorship = SponsorshipState::Disabled;556557 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(558 collection_id559 ));560 target_collection.save()561 }562563 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.564 ///565 /// # Permissions566 ///567 /// * Collection Owner.568 /// * Collection Admin.569 /// * Anyone if570 /// * Allow List is enabled, and571 /// * Address is added to allow list, and572 /// * MintPermission is enabled (see SetMintPermission method)573 ///574 /// # Arguments575 ///576 /// * collection_id: ID of the collection.577 ///578 /// * owner: Address, initial owner of the NFT.579 ///580 /// * data: Token data to store on chain.581 #[weight = T::CommonWeightInfo::create_item()]582 #[transactional]583 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {584 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);585 let budget = budget::Value::new(NESTING_BUDGET);586587 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))588 }589590 /// This method creates multiple items in a collection created with CreateCollection method.591 ///592 /// # Permissions593 ///594 /// * Collection Owner.595 /// * Collection Admin.596 /// * Anyone if597 /// * Allow List is enabled, and598 /// * Address is added to allow list, and599 /// * MintPermission is enabled (see SetMintPermission method)600 ///601 /// # Arguments602 ///603 /// * collection_id: ID of the collection.604 ///605 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].606 ///607 /// * owner: Address, initial owner of the NFT.608 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]609 #[transactional]610 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {611 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);612 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);613 let budget = budget::Value::new(NESTING_BUDGET);614615 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))616 }617618 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]619 #[transactional]620 pub fn set_collection_properties(621 origin,622 collection_id: CollectionId,623 properties: Vec<Property>624 ) -> DispatchResultWithPostInfo {625 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);626627 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);628629 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))630 }631632 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]633 #[transactional]634 pub fn delete_collection_properties(635 origin,636 collection_id: CollectionId,637 property_keys: Vec<PropertyKey>,638 ) -> DispatchResultWithPostInfo {639 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);640641 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);642643 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))644 }645646 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]647 #[transactional]648 pub fn set_token_properties(649 origin,650 collection_id: CollectionId,651 token_id: TokenId,652 properties: Vec<Property>653 ) -> DispatchResultWithPostInfo {654 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);655656 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);657 let budget = budget::Value::new(NESTING_BUDGET);658659 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))660 }661662 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]663 #[transactional]664 pub fn delete_token_properties(665 origin,666 collection_id: CollectionId,667 token_id: TokenId,668 property_keys: Vec<PropertyKey>669 ) -> DispatchResultWithPostInfo {670 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);671672 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);673 let budget = budget::Value::new(NESTING_BUDGET);674675 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))676 }677678 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]679 #[transactional]680 pub fn set_token_property_permissions(681 origin,682 collection_id: CollectionId,683 property_permissions: Vec<PropertyKeyPermission>,684 ) -> DispatchResultWithPostInfo {685 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);686687 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);688689 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))690 }691692 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]693 #[transactional]694 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {695 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696 let budget = budget::Value::new(NESTING_BUDGET);697698 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))699 }700701 /// Set transfers_enabled value for particular collection702 ///703 /// # Permissions704 ///705 /// * Collection Owner.706 ///707 /// # Arguments708 ///709 /// * collection_id: ID of the collection.710 ///711 /// * value: New flag value.712 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]713 #[transactional]714 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {715 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);716 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;717 target_collection.check_is_internal()?;718 target_collection.check_is_owner(&sender)?;719720 // =========721722 target_collection.limits.transfers_enabled = Some(value);723 target_collection.save()724 }725726 /// Destroys a concrete instance of NFT.727 ///728 /// # Permissions729 ///730 /// * Collection Owner.731 /// * Collection Admin.732 /// * Current NFT Owner.733 ///734 /// # Arguments735 ///736 /// * collection_id: ID of the collection.737 ///738 /// * item_id: ID of NFT to burn.739 #[weight = T::CommonWeightInfo::burn_item()]740 #[transactional]741 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {742 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);743744 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;745 if value == 1 {746 <NftTransferBasket<T>>::remove(collection_id, item_id);747 <NftApproveBasket<T>>::remove(collection_id, item_id);748 }749 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?750 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());751 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));752 Ok(post_info)753 }754755 /// Destroys a concrete instance of NFT on behalf of the owner756 /// See also: [`approve`]757 ///758 /// # Permissions759 ///760 /// * Collection Owner.761 /// * Collection Admin.762 /// * Current NFT Owner.763 ///764 /// # Arguments765 ///766 /// * collection_id: ID of the collection.767 ///768 /// * item_id: ID of NFT to burn.769 ///770 /// * from: owner of item771 #[weight = T::CommonWeightInfo::burn_from()]772 #[transactional]773 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {774 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);775 let budget = budget::Value::new(NESTING_BUDGET);776777 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))778 }779780 /// Change ownership of the token.781 ///782 /// # Permissions783 ///784 /// * Collection Owner785 /// * Collection Admin786 /// * Current NFT owner787 ///788 /// # Arguments789 ///790 /// * recipient: Address of token recipient.791 ///792 /// * collection_id.793 ///794 /// * item_id: ID of the item795 /// * Non-Fungible Mode: Required.796 /// * Fungible Mode: Ignored.797 /// * Re-Fungible Mode: Required.798 ///799 /// * value: Amount to transfer.800 /// * Non-Fungible Mode: Ignored801 /// * Fungible Mode: Must specify transferred amount802 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)803 #[weight = T::CommonWeightInfo::transfer()]804 #[transactional]805 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {806 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);807 let budget = budget::Value::new(NESTING_BUDGET);808809 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))810 }811812 /// Set, change, or remove approved address to transfer the ownership of the NFT.813 ///814 /// # Permissions815 ///816 /// * Collection Owner817 /// * Collection Admin818 /// * Current NFT owner819 ///820 /// # Arguments821 ///822 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).823 ///824 /// * collection_id.825 ///826 /// * item_id: ID of the item.827 #[weight = T::CommonWeightInfo::approve()]828 #[transactional]829 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {830 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);831832 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))833 }834835 /// 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.836 ///837 /// # Permissions838 /// * Collection Owner839 /// * Collection Admin840 /// * Current NFT owner841 /// * Address approved by current NFT owner842 ///843 /// # Arguments844 ///845 /// * from: Address that owns token.846 ///847 /// * recipient: Address of token recipient.848 ///849 /// * collection_id.850 ///851 /// * item_id: ID of the item.852 ///853 /// * value: Amount to transfer.854 #[weight = T::CommonWeightInfo::transfer_from()]855 #[transactional]856 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {857 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858 let budget = budget::Value::new(NESTING_BUDGET);859860 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))861 }862863 #[weight = <SelfWeightOf<T>>::set_collection_limits()]864 #[transactional]865 pub fn set_collection_limits(866 origin,867 collection_id: CollectionId,868 new_limit: CollectionLimits,869 ) -> DispatchResult {870 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);871 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;872 target_collection.check_is_internal()?;873 target_collection.check_is_owner_or_admin(&sender)?;874 let old_limit = &target_collection.limits;875876 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;877878 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(879 collection_id880 ));881882 target_collection.save()883 }884885 #[weight = <SelfWeightOf<T>>::set_collection_limits()]886 #[transactional]887 pub fn set_collection_permissions(888 origin,889 collection_id: CollectionId,890 new_limit: CollectionPermissions,891 ) -> DispatchResult {892 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);893 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;894 target_collection.check_is_internal()?;895 target_collection.check_is_owner_or_admin(&sender)?;896 let old_limit = &target_collection.permissions;897898 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;899900 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(901 collection_id902 ));903904 target_collection.save()905 }906907 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]908 #[transactional]909 pub fn repartition(910 origin,911 collection_id: CollectionId,912 token: TokenId,913 amount: u128,914 ) -> DispatchResultWithPostInfo {915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916 dispatch_tx::<T, _>(collection_id, |d| {917 if let Some(refungible_extensions) = d.refungible_extensions() {918 refungible_extensions.repartition(&sender, token, amount)919 } else {920 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)921 }922 })923 }924 }925}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#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20 clippy::too_many_arguments,21 clippy::unnecessary_mut_passed,22 clippy::unused_unit23)]2425extern crate alloc;2627use frame_support::{28 decl_module, decl_storage, decl_error, decl_event,29 dispatch::DispatchResult,30 ensure, fail,31 weights::{Weight},32 transactional,33 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},34 BoundedVec,35};36use scale_info::TypeInfo;37use frame_system::{self as system, ensure_signed};38use sp_runtime::{sp_std::prelude::Vec};39use up_data_structs::{40 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,41 CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,42 SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,43 PropertyKeyPermission,44};45use pallet_evm::account::CrossAccountId;46use pallet_common::{47 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,48 dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,49};50pub mod eth;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;54pub mod weights;55use weights::WeightInfo;5657const NESTING_BUDGET: u32 = 5;5859decl_error! {60 /// Error for non-fungible-token module.61 pub enum Error for Module<T: Config> {62 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.63 CollectionDecimalPointLimitExceeded,64 /// This address is not set as sponsor, use setCollectionSponsor first.65 ConfirmUnsetSponsorFail,66 /// Length of items properties must be greater than 0.67 EmptyArgument,68 /// Repertition is only supported by refungible collection69 RepartitionCalledOnNonRefungibleCollection,70 }71}7273pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {74 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7576 /// Weight information for extrinsics in this pallet.77 type WeightInfo: WeightInfo;78 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;79 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;80}8182decl_event! {83 pub enum Event<T>84 where85 <T as frame_system::Config>::AccountId,86 <T as pallet_evm::account::Config>::CrossAccountId,87 {88 /// Collection sponsor was removed89 ///90 /// # Arguments91 ///92 /// * collection_id: Globally unique collection identifier.93 CollectionSponsorRemoved(CollectionId),9495 /// Collection admin was added96 ///97 /// # Arguments98 ///99 /// * collection_id: Globally unique collection identifier.100 ///101 /// * admin: Admin address.102 CollectionAdminAdded(CollectionId, CrossAccountId),103104 /// Collection owned was changed105 ///106 /// # Arguments107 ///108 /// * collection_id: Globally unique collection identifier.109 ///110 /// * owner: New owner address.111 CollectionOwnedChanged(CollectionId, AccountId),112113 /// Collection sponsor was set114 ///115 /// # Arguments116 ///117 /// * collection_id: Globally unique collection identifier.118 ///119 /// * owner: New sponsor address.120 CollectionSponsorSet(CollectionId, AccountId),121122 /// New sponsor was confirm123 ///124 /// # Arguments125 ///126 /// * collection_id: Globally unique collection identifier.127 ///128 /// * sponsor: New sponsor address.129 SponsorshipConfirmed(CollectionId, AccountId),130131 /// Collection admin was removed132 ///133 /// # Arguments134 ///135 /// * collection_id: Globally unique collection identifier.136 ///137 /// * admin: Admin address.138 CollectionAdminRemoved(CollectionId, CrossAccountId),139140 /// Address was removed from the allow list141 ///142 /// # Arguments143 ///144 /// * collection_id: Globally unique collection identifier.145 ///146 /// * user: Address.147 AllowListAddressRemoved(CollectionId, CrossAccountId),148149 /// Address was added to the allow list150 ///151 /// # Arguments152 ///153 /// * collection_id: Globally unique collection identifier.154 ///155 /// * user: Address.156 AllowListAddressAdded(CollectionId, CrossAccountId),157158 /// Collection limits were set159 ///160 /// # Arguments161 ///162 /// * collection_id: Globally unique collection identifier.163 CollectionLimitSet(CollectionId),164165 /// Collection permissions were set166 /// 167 /// # Arguments168 /// 169 /// * collection_id: Globally unique collection identifier.170 CollectionPermissionSet(CollectionId),171 }172}173174type SelfWeightOf<T> = <T as Config>::WeightInfo;175176// # Used definitions177//178// ## User control levels179//180// chain-controlled - key is uncontrolled by user181// i.e autoincrementing index182// can use non-cryptographic hash183// real - key is controlled by user184// but it is hard to generate enough colliding values, i.e owner of signed txs185// can use non-cryptographic hash186// controlled - key is completly controlled by users187// i.e maps with mutable keys188// should use cryptographic hash189//190// ## User control level downgrade reasons191//192// ?1 - chain-controlled -> controlled193// collections/tokens can be destroyed, resulting in massive holes194// ?2 - chain-controlled -> controlled195// same as ?1, but can be only added, resulting in easier exploitation196// ?3 - real -> controlled197// no confirmation required, so addresses can be easily generated198decl_storage! {199 trait Store for Module<T: Config> as Unique {200201 //#region Private members202 /// Used for migrations203 ChainVersion: u64;204 //#endregion205206 //#region Tokens transfer sponosoring rate limit baskets207 /// (Collection id (controlled?2), who created (real))208 /// TODO: Off chain worker should remove from this map when collection gets removed209 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;210 /// Collection id (controlled?2), token id (controlled?2)211 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;212 /// Collection id (controlled?2), owning user (real)213 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;214 /// Collection id (controlled?2), token id (controlled?2)215 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;216 //#endregion217218 /// Variable metadata sponsoring219 /// Collection id (controlled?2), token id (controlled?2)220 #[deprecated]221 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;222 /// Last sponsoring of token property setting // todo:doc rephrase this and the following 223 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;224225 /// Last sponsoring of NFT approval in a collection226 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;227 /// Last sponsoring of fungible tokens approval in a collection228 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;229 /// Last sponsoring of RFT approval in a collection230 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>;231 }232}233234decl_module! {235 pub struct Module<T: Config> for enum Call236 where237 origin: T::Origin238 {239 type Error = Error<T>;240241 fn deposit_event() = default;242243 fn on_initialize(_now: T::BlockNumber) -> Weight {244 0245 }246247 fn on_runtime_upgrade() -> Weight {248 let limit = None;249250 <VariableMetaDataBasket<T>>::remove_all(limit);251252 0253 }254255 /// 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.256 ///257 /// # Permissions258 ///259 /// * Anyone.260 ///261 /// # Arguments262 ///263 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.264 ///265 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.266 ///267 /// * token_prefix: UTF-8 string with token prefix.268 ///269 /// * mode: [CollectionMode] collection type and type dependent data.270 // returns collection ID271 #[weight = <SelfWeightOf<T>>::create_collection()]272 #[transactional]273 #[deprecated]274 pub fn create_collection(origin,275 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,276 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,277 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,278 mode: CollectionMode) -> DispatchResult {279 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {280 name: collection_name,281 description: collection_description,282 token_prefix,283 mode,284 ..Default::default()285 };286 Self::create_collection_ex(origin, data)287 }288289 /// Create a collection with explicit parameters.290 /// Prefer it to the deprecated [`created_collection`] method.291 ///292 /// # Permissions293 ///294 /// * Anyone.295 ///296 /// # Arguments297 /// 298 /// * data: explicit create-collection data.299 #[weight = <SelfWeightOf<T>>::create_collection()]300 #[transactional]301 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {302 let sender = ensure_signed(origin)?;303304 // =========305306 T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;307308 Ok(())309 }310311 /// Destroy the collection if no tokens exist within.312 ///313 /// # Permissions314 ///315 /// * Collection Owner316 ///317 /// # Arguments318 ///319 /// * collection_id: collection to destroy.320 #[weight = <SelfWeightOf<T>>::destroy_collection()]321 #[transactional]322 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {323 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);324 let collection = <CollectionHandle<T>>::try_get(collection_id)?;325 collection.check_is_internal()?;326327 // =========328329 T::CollectionDispatch::destroy(sender, collection)?;330331 <NftTransferBasket<T>>::remove_prefix(collection_id, None);332 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);333 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);334335 <NftApproveBasket<T>>::remove_prefix(collection_id, None);336 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);337 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);338339 Ok(())340 }341342 /// Add an address to allow list.343 ///344 /// # Permissions345 ///346 /// * Collection Owner347 /// * Collection Admin348 ///349 /// # Arguments350 ///351 /// * collection_id.352 ///353 /// * address.354 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]355 #[transactional]356 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{357358 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);359 let collection = <CollectionHandle<T>>::try_get(collection_id)?;360 collection.check_is_internal()?;361362 <PalletCommon<T>>::toggle_allowlist(363 &collection,364 &sender,365 &address,366 true,367 )?;368369 Self::deposit_event(Event::<T>::AllowListAddressAdded(370 collection_id,371 address372 ));373374 Ok(())375 }376377 /// Remove an address from allow list.378 ///379 /// # Permissions380 ///381 /// * Collection Owner382 /// * Collection Admin383 ///384 /// # Arguments385 ///386 /// * collection_id.387 ///388 /// * address.389 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]390 #[transactional]391 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{392393 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);394 let collection = <CollectionHandle<T>>::try_get(collection_id)?;395 collection.check_is_internal()?;396397 <PalletCommon<T>>::toggle_allowlist(398 &collection,399 &sender,400 &address,401 false,402 )?;403404 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(405 collection_id,406 address407 ));408409 Ok(())410 }411412 /// Change the owner of the collection.413 ///414 /// # Permissions415 ///416 /// * Collection Owner417 ///418 /// # Arguments419 ///420 /// * collection_id.421 ///422 /// * new_owner.423 #[weight = <SelfWeightOf<T>>::change_collection_owner()]424 #[transactional]425 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {426427 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);428429 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;430 target_collection.check_is_internal()?;431 target_collection.check_is_owner(&sender)?;432433 target_collection.owner = new_owner.clone();434 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(435 collection_id,436 new_owner437 ));438439 target_collection.save()440 }441442 /// Adds an admin of the collection.443 /// 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.444 ///445 /// # Permissions446 ///447 /// * Collection Owner448 /// * Collection Admin449 ///450 /// # Arguments451 ///452 /// * collection_id: ID of the Collection to add admin for.453 ///454 /// * new_admin: Address of new admin to add.455 #[weight = <SelfWeightOf<T>>::add_collection_admin()]456 #[transactional]457 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin: T::CrossAccountId) -> DispatchResult {458 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);459 let collection = <CollectionHandle<T>>::try_get(collection_id)?;460 collection.check_is_internal()?;461462 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(463 collection_id,464 new_admin.clone()465 ));466467 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, true)468 }469470 /// 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.471 ///472 /// # Permissions473 ///474 /// * Collection Owner475 /// * Collection Admin476 ///477 /// # Arguments478 ///479 /// * collection_id: ID of the Collection to remove admin for.480 ///481 /// * account_id: Address of admin to remove.482 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]483 #[transactional]484 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {485 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);486 let collection = <CollectionHandle<T>>::try_get(collection_id)?;487 collection.check_is_internal()?;488489 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(490 collection_id,491 account_id.clone()492 ));493494 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)495 }496497 /// Set (invite) a new collection sponsor. If successful, confirmation from the sponsor-to-be will be pending.498 /// 499 /// # Permissions500 ///501 /// * Collection Owner502 /// * Collection Admin503 ///504 /// # Arguments505 ///506 /// * collection_id.507 ///508 /// * new_sponsor.509 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]510 #[transactional]511 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {512 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);513514 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;515 target_collection.check_is_owner_or_admin(&sender)?;516 target_collection.check_is_internal()?;517518 target_collection.set_sponsor(new_sponsor.clone())?;519520 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(521 collection_id,522 new_sponsor523 ));524525 target_collection.save()526 }527528 /// Confirm own sponsorship of a collection.529 /// 530 /// # Permissions531 ///532 /// * The sponsor to-be533 ///534 /// # Arguments535 ///536 /// * collection_id.537 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]538 #[transactional]539 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {540 let sender = ensure_signed(origin)?;541542 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;543 target_collection.check_is_internal()?;544 ensure!(545 target_collection.confirm_sponsorship(&sender)?,546 Error::<T>::ConfirmUnsetSponsorFail547 );548549 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(550 collection_id,551 sender552 ));553554 target_collection.save()555 }556557 /// Switch back to pay-per-own-transaction model.558 ///559 /// # Permissions560 ///561 /// * Collection Owner562 ///563 /// # Arguments564 ///565 /// * collection_id.566 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]567 #[transactional]568 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {569 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);570571 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;572 target_collection.check_is_internal()?;573 target_collection.check_is_owner(&sender)?;574575 target_collection.sponsorship = SponsorshipState::Disabled;576577 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(578 collection_id579 ));580 target_collection.save()581 }582583 /// Create a concrete instance of NFT Collection created with CreateCollection method.584 ///585 /// # Permissions586 ///587 /// * Collection Owner588 /// * Collection Admin589 /// * Anyone if590 /// * Allow List is enabled, and591 /// * Address is added to allow list, and592 /// * MintPermission is enabled (see SetMintPermission method)593 ///594 /// # Arguments595 ///596 /// * collection_id: ID of the collection.597 ///598 /// * owner: Address, initial owner of the NFT.599 ///600 /// * data: Token data to store on chain.601 #[weight = T::CommonWeightInfo::create_item()]602 #[transactional]603 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {604 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);605 let budget = budget::Value::new(NESTING_BUDGET);606607 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))608 }609610 /// Create multiple items in a collection created with CreateCollection method.611 ///612 /// # Permissions613 ///614 /// * Collection Owner615 /// * Collection Admin616 /// * Anyone if617 /// * Allow List is enabled, and618 /// * Address is added to allow list, and619 /// * MintPermission is enabled (see SetMintPermission method)620 ///621 /// # Arguments622 ///623 /// * collection_id: ID of the collection.624 ///625 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].626 ///627 /// * owner: Address, initial owner of the NFT.628 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]629 #[transactional]630 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {631 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);632 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);633 let budget = budget::Value::new(NESTING_BUDGET);634635 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))636 }637638 /// Add or change collection properties.639 ///640 /// # Permissions641 ///642 /// * Collection Owner643 /// * Collection Admin644 ///645 /// # Arguments646 ///647 /// * collection_id.648 ///649 /// * properties: a vector of key-value pairs stored as the collection's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.650 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]651 #[transactional]652 pub fn set_collection_properties(653 origin,654 collection_id: CollectionId,655 properties: Vec<Property>656 ) -> DispatchResultWithPostInfo {657 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);658659 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);660661 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))662 }663664 /// Delete specified collection properties.665 ///666 /// # Permissions667 ///668 /// * Collection Owner669 /// * Collection Admin670 ///671 /// # Arguments672 ///673 /// * collection_id.674 ///675 /// * property_keys: a vector of keys of the properties to be deleted.676 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]677 #[transactional]678 pub fn delete_collection_properties(679 origin,680 collection_id: CollectionId,681 property_keys: Vec<PropertyKey>,682 ) -> DispatchResultWithPostInfo {683 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);684685 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);686687 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))688 }689690 /// Add or change token properties according to collection's permissions.691 ///692 /// # Permissions693 ///694 /// * Depends on collection's token property permissions and specified property mutability:695 /// * Collection Owner696 /// * Collection Admin697 /// * Token Owner698 ///699 /// # Arguments700 ///701 /// * collection_id.702 /// 703 /// * token_id.704 ///705 /// * properties: a vector of key-value pairs stored as the token's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.706 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]707 #[transactional]708 pub fn set_token_properties(709 origin,710 collection_id: CollectionId,711 token_id: TokenId,712 properties: Vec<Property>713 ) -> DispatchResultWithPostInfo {714 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);715716 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);717 let budget = budget::Value::new(NESTING_BUDGET);718719 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))720 }721722 /// Delete specified token properties.723 ///724 /// # Permissions725 ///726 /// * Depends on collection's token property permissions and specified property mutability:727 /// * Collection Owner728 /// * Collection Admin729 /// * Token Owner730 ///731 /// # Arguments732 ///733 /// * collection_id.734 ///735 /// * token_id.736 ///737 /// * property_keys: a vector of keys of the properties to be deleted.738 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]739 #[transactional]740 pub fn delete_token_properties(741 origin,742 collection_id: CollectionId,743 token_id: TokenId,744 property_keys: Vec<PropertyKey>745 ) -> DispatchResultWithPostInfo {746 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);747748 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);749 let budget = budget::Value::new(NESTING_BUDGET);750751 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))752 }753754 /// Add or change token property permissions of a collection.755 ///756 /// # Permissions757 ///758 /// * Collection Owner759 /// * Collection Admin760 ///761 /// # Arguments762 ///763 /// * collection_id.764 ///765 /// * property_permissions: a vector of permissions for property keys. Keys support Latin letters, '-', '_', and '.' as symbols.766 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]767 #[transactional]768 pub fn set_token_property_permissions(769 origin,770 collection_id: CollectionId,771 property_permissions: Vec<PropertyKeyPermission>,772 ) -> DispatchResultWithPostInfo {773 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);774775 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);776777 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))778 }779780 /// Create multiple items inside a collection with explicitly specified initial parameters.781 /// 782 /// # Permissions783 ///784 /// * Collection Owner785 /// * Collection Admin786 /// * Anyone if787 /// * Allow List is enabled, and788 /// * Address is added to allow list, and789 /// * MintPermission is enabled (see SetMintPermission method)790 ///791 /// # Arguments792 ///793 /// * collection_id: ID of the collection.794 ///795 /// * data: explicit item creation data. 796 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]797 #[transactional]798 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {799 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);800 let budget = budget::Value::new(NESTING_BUDGET);801802 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))803 }804805 /// Set transfers_enabled value for particular collection.806 ///807 /// # Permissions808 ///809 /// * Collection Owner810 ///811 /// # Arguments812 ///813 /// * collection_id: ID of the collection.814 ///815 /// * value: New flag value.816 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]817 #[transactional]818 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {819 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);820 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;821 target_collection.check_is_internal()?;822 target_collection.check_is_owner(&sender)?;823824 // =========825826 target_collection.limits.transfers_enabled = Some(value);827 target_collection.save()828 }829830 /// Destroy a concrete instance of NFT.831 ///832 /// # Permissions833 ///834 /// * Collection Owner835 /// * Collection Admin836 /// * Current NFT Owner837 ///838 /// # Arguments839 ///840 /// * collection_id: ID of the collection.841 ///842 /// * item_id: ID of NFT to burn.843 #[weight = T::CommonWeightInfo::burn_item()]844 #[transactional]845 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {846 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);847848 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;849 if value == 1 {850 <NftTransferBasket<T>>::remove(collection_id, item_id);851 <NftApproveBasket<T>>::remove(collection_id, item_id);852 }853 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?854 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());855 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));856 Ok(post_info)857 }858859 /// Destroy a concrete instance of NFT on behalf of the owner.860 /// See also: [`approve`]861 ///862 /// # Permissions863 ///864 /// * Collection Owner.865 /// * Collection Admin.866 /// * Current NFT Owner.867 ///868 /// # Arguments869 ///870 /// * collection_id: ID of the collection.871 ///872 /// * item_id: ID of NFT to burn.873 ///874 /// * from: owner of item875 #[weight = T::CommonWeightInfo::burn_from()]876 #[transactional]877 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {878 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);879 let budget = budget::Value::new(NESTING_BUDGET);880881 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))882 }883884 /// Change ownership of the token.885 ///886 /// # Permissions887 ///888 /// * Collection Owner889 /// * Collection Admin890 /// * Current NFT owner891 ///892 /// # Arguments893 ///894 /// * recipient: Address of token recipient.895 ///896 /// * collection_id.897 ///898 /// * item_id: ID of the item899 /// * Non-Fungible Mode: Required.900 /// * Fungible Mode: Ignored.901 /// * Re-Fungible Mode: Required.902 ///903 /// * value: Amount to transfer.904 /// * Non-Fungible Mode: Ignored905 /// * Fungible Mode: Must specify transferred amount906 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)907 #[weight = T::CommonWeightInfo::transfer()]908 #[transactional]909 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {910 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);911 let budget = budget::Value::new(NESTING_BUDGET);912913 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))914 }915916 /// Set, change, or remove approved address to transfer the ownership of the NFT.917 ///918 /// # Permissions919 ///920 /// * Collection Owner921 /// * Collection Admin922 /// * Current NFT owner923 ///924 /// # Arguments925 ///926 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).927 ///928 /// * collection_id.929 ///930 /// * item_id: ID of the item.931 #[weight = T::CommonWeightInfo::approve()]932 #[transactional]933 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {934 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);935936 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))937 }938939 /// 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.940 ///941 /// # Permissions942 /// 943 /// * Collection Owner944 /// * Collection Admin945 /// * Current NFT owner946 /// * Address approved by current NFT owner947 ///948 /// # Arguments949 ///950 /// * from: Address that owns token.951 ///952 /// * recipient: Address of token recipient.953 ///954 /// * collection_id.955 ///956 /// * item_id: ID of the item.957 ///958 /// * value: Amount to transfer.959 #[weight = T::CommonWeightInfo::transfer_from()]960 #[transactional]961 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {962 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);963 let budget = budget::Value::new(NESTING_BUDGET);964965 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))966 }967968 /// Set specific limits of a collection. Empty, or None fields mean chain default.969 ///.970 /// # Permissions971 /// 972 /// * Collection Owner973 /// * Collection Admin974 ///975 /// # Arguments976 ///977 /// * collection_id.978 ///979 /// * new_limit: The new limits of the collection. They will overwrite the current ones.980 #[weight = <SelfWeightOf<T>>::set_collection_limits()]981 #[transactional]982 pub fn set_collection_limits(983 origin,984 collection_id: CollectionId,985 new_limit: CollectionLimits,986 ) -> DispatchResult {987 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);988 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;989 target_collection.check_is_internal()?;990 target_collection.check_is_owner_or_admin(&sender)?;991 let old_limit = &target_collection.limits;992993 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;994995 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(996 collection_id997 ));998999 target_collection.save()1000 }10011002 /// Set specific permissions of a collection. Empty, or None fields mean chain default.1003 ///1004 /// # Permissions1005 /// 1006 /// * Collection Owner1007 /// * Collection Admin1008 ///1009 /// # Arguments1010 ///1011 /// * collection_id.1012 ///1013 /// * new_permission: The new permissions of the collection. They will overwrite the current ones.1014 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1015 #[transactional]1016 pub fn set_collection_permissions(1017 origin,1018 collection_id: CollectionId,1019 new_permission: CollectionPermissions,1020 ) -> DispatchResult {1021 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1022 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1023 target_collection.check_is_internal()?;1024 target_collection.check_is_owner_or_admin(&sender)?;1025 let old_limit = &target_collection.permissions;10261027 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10281029 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1030 collection_id1031 ));10321033 target_collection.save()1034 }10351036 /// Re-partition a refungible token, while owning all of its parts.1037 ///1038 /// # Permissions1039 /// 1040 /// * Token Owner (must own every part)1041 ///1042 /// # Arguments1043 ///1044 /// * collection_id.1045 /// 1046 /// * token: the ID of the RFT. 1047 ///1048 /// * amount: The new number of parts into which the token shall be partitioned.1049 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]1050 #[transactional]1051 pub fn repartition(1052 origin,1053 collection_id: CollectionId,1054 token: TokenId,1055 amount: u128,1056 ) -> DispatchResultWithPostInfo {1057 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1058 dispatch_tx::<T, _>(collection_id, |d| {1059 if let Some(refungible_extensions) = d.refungible_extensions() {1060 refungible_extensions.repartition(&sender, token, amount)1061 } else {1062 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1063 }1064 })1065 }1066 }1067}primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -197,7 +197,6 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum CollectionMode {
NFT,
- // decimal points
Fungible(DecimalPoints),
ReFungible,
}
@@ -252,12 +251,14 @@
pub enum SponsorshipState<AccountId> {
/// The fees are applied to the transaction sender
Disabled,
+ /// Pending confirmation from a sponsor-to-be
Unconfirmed(AccountId),
/// Transactions are sponsored by specified account
Confirmed(AccountId),
}
impl<AccountId> SponsorshipState<AccountId> {
+ /// Get the acting sponsor account, if present
pub fn sponsor(&self) -> Option<&AccountId> {
match self {
Self::Confirmed(sponsor) => Some(sponsor),
@@ -265,6 +266,7 @@
}
}
+ /// Get the sponsor account currently pending confirmation, if present
pub fn pending_sponsor(&self) -> Option<&AccountId> {
match self {
Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),
@@ -272,6 +274,7 @@
}
}
+ /// Is sponsorship set and acting
pub fn confirmed(&self) -> bool {
matches!(self, Self::Confirmed(_))
}
@@ -283,7 +286,7 @@
}
}
-/// Used in storage
+/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version)
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
pub struct Collection<AccountId> {
@@ -324,7 +327,7 @@
pub meta_update_permission: MetaUpdatePermission,
}
-/// Used in RPC calls
+/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version)
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct RpcCollection<AccountId> {
@@ -362,12 +365,15 @@
pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;
-/// All fields are wrapped in `Option`s, where None means chain default
+/// Limits and restrictions of a collection.
+/// All fields are wrapped in `Option`s, where None means chain default.
// When adding/removing fields from this struct - don't forget to also update clamp_limits
#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CollectionLimits {
+ /// Maximum number of owned tokens per account
pub account_token_ownership_limit: Option<u32>,
+ /// Maximum size of data of a sponsored transaction
pub sponsored_data_size: Option<u32>,
/// FIXME should we delete this or repurpose it?
@@ -375,13 +381,18 @@
/// Some(v) - setVariableMetadata is sponsored
/// if there is v block between txs
pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,
+ /// Maximum amount of tokens inside the collection
pub token_limit: Option<u32>,
- // Timeouts for item types in passed blocks
+ /// Timeout for sponsoring a token transfer in passed blocks
pub sponsor_transfer_timeout: Option<u32>,
+ /// Timeout for sponsoring an approval in passed blocks
pub sponsor_approve_timeout: Option<u32>,
+ /// Can a token be transferred by the owner
pub owner_can_transfer: Option<bool>,
+ /// Can a token be burned by the owner
pub owner_can_destroy: Option<bool>,
+ /// Can a token be transferred at all
pub transfers_enabled: Option<bool>,
}
@@ -509,6 +520,7 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum SponsoringRateLimit {
SponsoringDisabled,
+ /// Once per how many blocks can sponsorship of a transaction type occur
Blocks(u32),
}
@@ -516,6 +528,7 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[derivative(Debug)]
pub struct CreateNftData {
+ /// Key-value pairs used to describe the token as metadata
#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
#[derivative(Debug(format_with = "bounded::vec_debug"))]
pub properties: CollectionPropertiesVec,
@@ -524,6 +537,7 @@
#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CreateFungibleData {
+ /// Number of fungible tokens minted
pub value: u128,
}
@@ -534,6 +548,7 @@
#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
#[derivative(Debug(format_with = "bounded::vec_debug"))]
pub const_data: BoundedVec<u8, CustomDataLimit>,
+ /// Number of pieces the RFT is split into
pub pieces: u128,
}
@@ -553,6 +568,7 @@
ReFungible(CreateReFungibleData),
}
+/// Explicit NFT creation data with meta parameters
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug)]
pub struct CreateNftExData<CrossAccountId> {
@@ -561,6 +577,7 @@
pub owner: CrossAccountId,
}
+/// Explicit RFT creation data with meta parameters
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
pub struct CreateRefungibleExData<CrossAccountId> {
@@ -570,6 +587,7 @@
pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
}
+/// Explicit item creation data with meta parameters, namely the owner
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
pub enum CreateItemExData<CrossAccountId> {
@@ -617,6 +635,7 @@
}
}
+/// Token's address, dictated by its collection and token IDs
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
// todo possibly rename to be used generally as an address pair
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -43,13 +43,13 @@
accountTokens: fun('Get tokens owned by account', [collectionParam, crossAccountParam()], 'Vec<u32>'),
collectionTokens: fun('Get tokens contained in collection', [collectionParam], 'Vec<u32>'),
- lastTokenId: fun('Get last token id', [collectionParam], 'u32'),
+ lastTokenId: fun('Get last token ID created in a collection', [collectionParam], 'u32'),
totalSupply: fun('Get amount of unique collection tokens', [collectionParam], 'u32'),
- accountBalance: fun('Get amount of different user tokens', [collectionParam, crossAccountParam()], 'u32'),
- balance: fun('Get amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
+ accountBalance: fun('Get owned amount of any user tokens', [collectionParam, crossAccountParam()], 'u32'),
+ balance: fun('Get owned amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
allowance: fun('Get allowed amount', [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], 'u128'),
tokenOwner: fun('Get token owner', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
- topmostTokenOwner: fun('Get token owner, in case of nested token - find parent recursive', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
+ topmostTokenOwner: fun('Get token owner, in case of nested token - find the parent recursively', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
tokenChildren: fun('Get tokens nested directly into the token', [collectionParam, tokenParam], 'Vec<UpDataStructsTokenChild>'),
constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),
variableMetadata: fun('Get token variable metadata', [collectionParam, tokenParam], 'Vec<u8>'),
@@ -74,7 +74,7 @@
'UpDataStructsTokenData',
),
tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),
- collectionById: fun('Get collection by specified id', [collectionParam], 'Option<UpDataStructsRpcCollection>'),
+ collectionById: fun('Get collection by specified ID', [collectionParam], 'Option<UpDataStructsRpcCollection>'),
collectionStats: fun('Get collection stats', [], 'UpDataStructsCollectionStats'),
allowed: fun('Check if user is allowed to use collection', [collectionParam, crossAccountParam()], 'bool'),
nextSponsored: fun('Get number of blocks when sponsored transaction is available', [collectionParam, crossAccountParam(), tokenParam], 'Option<u64>'),