difftreelog
refactor use rpcs instead of api.query.common
in: master
33 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -3,7 +3,7 @@
use codec::Decode;
use jsonrpc_core::{Error as RpcError, ErrorCode, Result};
use jsonrpc_derive::rpc;
-use nft_data_structs::{CollectionId, TokenId};
+use nft_data_structs::{Collection, CollectionId, CollectionStats, TokenId};
use sp_api::{BlockId, BlockT, ProvideRuntimeApi};
use sp_blockchain::HeaderBackend;
use up_rpc::NftApi as NftRuntimeApi;
@@ -86,8 +86,23 @@
collection: CollectionId,
at: Option<BlockHash>,
) -> Result<Vec<CrossAccountId>>;
+ #[rpc(name = "nft_allowed")]
+ fn allowed(
+ &self,
+ collection: CollectionId,
+ user: CrossAccountId,
+ at: Option<BlockHash>,
+ ) -> Result<bool>;
#[rpc(name = "nft_lastTokenId")]
fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;
+ #[rpc(name = "nft_collectionById")]
+ fn collection_by_id(
+ &self,
+ collection: CollectionId,
+ at: Option<BlockHash>,
+ ) -> Result<Option<Collection<AccountId>>>;
+ #[rpc(name = "nft_collectionStats")]
+ fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;
}
pub struct Nft<C, P> {
@@ -160,5 +175,8 @@
pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>);
pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>);
+ pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool);
pass_method!(last_token_id(collection: CollectionId) -> TokenId);
+ pass_method!(collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>>);
+ pass_method!(collection_stats() -> CollectionStats);
}
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -19,7 +19,7 @@
pub fn create_collection_raw<T: Config, R>(
owner: T::AccountId,
mode: CollectionMode,
- handler: impl FnOnce(Collection<T>) -> Result<CollectionId, DispatchError>,
+ handler: impl FnOnce(Collection<T::AccountId>) -> Result<CollectionId, DispatchError>,
cast: impl FnOnce(CollectionHandle<T>) -> R,
) -> Result<R, DispatchError> {
T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -12,7 +12,7 @@
COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,
- WithdrawReasons,
+ WithdrawReasons, CollectionStats,
};
pub use pallet::*;
use sp_core::H160;
@@ -26,7 +26,7 @@
#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
pub struct CollectionHandle<T: Config> {
pub id: CollectionId,
- collection: Collection<T>,
+ collection: Collection<T::AccountId>,
pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
}
impl<T: Config> CollectionHandle<T> {
@@ -78,7 +78,7 @@
}
}
impl<T: Config> Deref for CollectionHandle<T> {
- type Target = Collection<T>;
+ type Target = Collection<T::AccountId>;
fn deref(&self) -> &Self::Target {
&self.collection
@@ -311,7 +311,7 @@
pub type CollectionById<T> = StorageMap<
Hasher = Blake2_128Concat,
Key = CollectionId,
- Value = Collection<T>,
+ Value = Collection<<T as frame_system::Config>::AccountId>,
QueryKind = OptionQuery,
>;
@@ -344,6 +344,10 @@
Value = bool,
QueryKind = ValueQuery,
>;
+
+ /// Not used by code, exists only to provide some types to metadata
+ #[pallet::storage]
+ pub type DummyStorageValue<T> = StorageValue<Value = (CollectionStats, CollectionId, TokenId), QueryKind = OptionQuery>;
}
impl<T: Config> Pallet<T> {
@@ -355,10 +359,32 @@
);
Ok(())
}
+ pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {
+ <IsAdmin<T>>::iter_prefix((collection,))
+ .map(|(a, _)| a)
+ .collect()
+ }
+ pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {
+ <Allowlist<T>>::iter_prefix((collection,))
+ .map(|(a, _)| a)
+ .collect()
+ }
+ pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {
+ <Allowlist<T>>::get((collection, user))
+ }
+ pub fn collection_stats() -> CollectionStats {
+ let created = <CreatedCollectionCount<T>>::get();
+ let destroyed = <DestroyedCollectionCount<T>>::get();
+ CollectionStats {
+ created: created.0,
+ destroyed: destroyed.0,
+ alive: created.0 - destroyed.0,
+ }
+ }
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {
+ pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
{
ensure!(
data.name.len() <= MAX_COLLECTION_NAME_LENGTH,
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -91,8 +91,8 @@
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {
- PalletCommon::init_collection(data)
+ pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(data)
}
pub fn destroy_collection(
collection: FungibleHandle<T>,
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -25,7 +25,7 @@
fn try_sponsor<T: Config>(
caller: &H160,
collection_id: CollectionId,
- collection: &Collection<T>,
+ collection: &Collection<T::AccountId>,
call: &[u8],
) -> Result<(), AnyError> {
let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;
@@ -109,7 +109,7 @@
if !collection.sponsorship.confirmed() {
return None;
}
- if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {
+ if try_sponsor::<T>(who, collection_id, &collection, &call.1).is_ok() {
return collection
.sponsorship
.sponsor()
pallets/nft/src/lib.rsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,24 IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32 pallet_prelude::DispatchResultWithPostInfo,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use nft_data_structs::{38 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,39 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,40 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,41 NFT_SPONSOR_TRANSFER_TIMEOUT, AccessMode, Collection, CreateItemData, CollectionLimits,42 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,43};44use pallet_common::{45 account::CrossAccountId, CollectionHandle, IsAdmin, Pallet as PalletCommon,46 Error as CommonError, CommonWeightInfo, Allowlist,47};48use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};49use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};50use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};5152#[cfg(test)]53mod mock;5455#[cfg(test)]56mod tests;5758mod eth;59mod sponsorship;60pub use sponsorship::NftSponsorshipHandler;61pub use eth::sponsoring::NftEthSponsorshipHandler;6263pub use eth::NftErcSupport;6465pub mod common;66use common::CommonWeights;67pub mod dispatch;68use dispatch::dispatch_call;6970#[cfg(feature = "runtime-benchmarks")]71mod benchmarking;72pub mod weights;73use weights::WeightInfo;7475decl_error! {76 /// Error for non-fungible-token module.77 pub enum Error for Module<T: Config> {78 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.79 CollectionDecimalPointLimitExceeded,80 /// This address is not set as sponsor, use setCollectionSponsor first.81 ConfirmUnsetSponsorFail,82 /// Length of items properties must be greater than 0.83 EmptyArgument,84 /// Collection limit bounds per collection exceeded85 CollectionLimitBoundsExceeded,86 /// Tried to enable permissions which are only permitted to be disabled87 OwnerPermissionsCantBeReverted,88 }89}90pub trait Config:91 system::Config92 + pallet_evm_coder_substrate::Config93 + pallet_common::Config94 + pallet_nonfungible::Config95 + pallet_refungible::Config96 + pallet_fungible::Config97 + Sized98 + TypeInfo99{100 /// Weight information for extrinsics in this pallet.101 type WeightInfo: WeightInfo;102}103104type SelfWeightOf<T> = <T as Config>::WeightInfo;105106// # Used definitions107//108// ## User control levels109//110// chain-controlled - key is uncontrolled by user111// i.e autoincrementing index112// can use non-cryptographic hash113// real - key is controlled by user114// but it is hard to generate enough colliding values, i.e owner of signed txs115// can use non-cryptographic hash116// controlled - key is completly controlled by users117// i.e maps with mutable keys118// should use cryptographic hash119//120// ## User control level downgrade reasons121//122// ?1 - chain-controlled -> controlled123// collections/tokens can be destroyed, resulting in massive holes124// ?2 - chain-controlled -> controlled125// same as ?1, but can be only added, resulting in easier exploitation126// ?3 - real -> controlled127// no confirmation required, so addresses can be easily generated128decl_storage! {129 trait Store for Module<T: Config> as Nft {130131 //#region Private members132 /// Used for migrations133 ChainVersion: u64;134 //#endregion135136 //#region Tokens transfer rate limit baskets137 /// (Collection id (controlled?2), who created (real))138 /// TODO: Off chain worker should remove from this map when collection gets removed139 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;140 /// Collection id (controlled?2), token id (controlled?2)141 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;142 /// Collection id (controlled?2), owning user (real)143 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;144 /// Collection id (controlled?2), token id (controlled?2)145 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;146 //#endregion147148 /// Variable metadata sponsoring149 /// Collection id (controlled?2), token id (controlled?2)150 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;151 }152}153154decl_module! {155 pub struct Module<T: Config> for enum Call156 where157 origin: T::Origin158 {159 type Error = Error<T>;160161 fn on_initialize(_now: T::BlockNumber) -> Weight {162 0163 }164165 /// 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 and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.166 ///167 /// # Permissions168 ///169 /// * Anyone.170 ///171 /// # Arguments172 ///173 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.174 ///175 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.176 ///177 /// * token_prefix: UTF-8 string with token prefix.178 ///179 /// * mode: [CollectionMode] collection type and type dependent data.180 // returns collection ID181 #[weight = <SelfWeightOf<T>>::create_collection()]182 #[transactional]183 pub fn create_collection(origin,184 collection_name: Vec<u16>,185 collection_description: Vec<u16>,186 token_prefix: Vec<u8>,187 mode: CollectionMode) -> DispatchResult {188189 // Anyone can create a collection190 let who = ensure_signed(origin)?;191192 // Create new collection193 let new_collection = Collection::<T> {194 owner: who.clone(),195 name: collection_name,196 mode: mode.clone(),197 mint_mode: false,198 access: AccessMode::Normal,199 description: collection_description,200 token_prefix,201 offchain_schema: Vec::new(),202 schema_version: SchemaVersion::ImageURL,203 sponsorship: SponsorshipState::Disabled,204 variable_on_chain_schema: Vec::new(),205 const_on_chain_schema: Vec::new(),206 limits: Default::default(),207 meta_update_permission: Default::default(),208 };209210 let _id = match mode {211 CollectionMode::NFT => {PalletNonfungible::init_collection(new_collection)?},212 CollectionMode::Fungible(decimal_points) => {213 // check params214 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);215 PalletFungible::init_collection(new_collection)?216 }217 CollectionMode::ReFungible => {218 PalletRefungible::init_collection(new_collection)?219 }220 };221222 Ok(())223 }224225 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.226 ///227 /// # Permissions228 ///229 /// * Collection Owner.230 ///231 /// # Arguments232 ///233 /// * collection_id: collection to destroy.234 #[weight = <SelfWeightOf<T>>::destroy_collection()]235 #[transactional]236 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {237 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);238239 let collection = <CollectionHandle<T>>::try_get(collection_id)?;240 collection.check_is_owner(&sender)?;241242 // =========243244 match collection.mode {245 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,246 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,247 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,248 }249250 <NftTransferBasket<T>>::remove_prefix(collection_id, None);251 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);252 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);253254 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);255256 Ok(())257 }258259 /// Add an address to allow list.260 ///261 /// # Permissions262 ///263 /// * Collection Owner264 /// * Collection Admin265 ///266 /// # Arguments267 ///268 /// * collection_id.269 ///270 /// * address.271 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]272 #[transactional]273 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{274275 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);276 let collection = <CollectionHandle<T>>::try_get(collection_id)?;277278 <PalletCommon<T>>::toggle_allowlist(279 &collection,280 &sender,281 &address,282 true,283 )?;284285 Ok(())286 }287288 /// Remove an address from allow list.289 ///290 /// # Permissions291 ///292 /// * Collection Owner293 /// * Collection Admin294 ///295 /// # Arguments296 ///297 /// * collection_id.298 ///299 /// * address.300 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]301 #[transactional]302 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{303304 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);305 let collection = <CollectionHandle<T>>::try_get(collection_id)?;306307 <PalletCommon<T>>::toggle_allowlist(308 &collection,309 &sender,310 &address,311 false,312 )?;313314 Ok(())315 }316317 /// Toggle between normal and allow list access for the methods with access for `Anyone`.318 ///319 /// # Permissions320 ///321 /// * Collection Owner.322 ///323 /// # Arguments324 ///325 /// * collection_id.326 ///327 /// * mode: [AccessMode]328 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]329 #[transactional]330 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult331 {332 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);333334 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;335 target_collection.check_is_owner(&sender)?;336337 target_collection.access = mode;338 target_collection.save()339 }340341 /// Allows Anyone to create tokens if:342 /// * Allow List is enabled, and343 /// * Address is added to allow list, and344 /// * This method was called with True parameter345 ///346 /// # Permissions347 /// * Collection Owner348 ///349 /// # Arguments350 ///351 /// * collection_id.352 ///353 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.354 #[weight = <SelfWeightOf<T>>::set_mint_permission()]355 #[transactional]356 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult357 {358 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);359360 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;361 target_collection.check_is_owner(&sender)?;362363 target_collection.mint_mode = mint_permission;364 target_collection.save()365 }366367 /// Change the owner of the collection.368 ///369 /// # Permissions370 ///371 /// * Collection Owner.372 ///373 /// # Arguments374 ///375 /// * collection_id.376 ///377 /// * new_owner.378 #[weight = <SelfWeightOf<T>>::change_collection_owner()]379 #[transactional]380 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {381382 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);383384 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;385 target_collection.check_is_owner(&sender)?;386387 target_collection.owner = new_owner;388 target_collection.save()389 }390391 /// Adds an admin of the Collection.392 /// 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.393 ///394 /// # Permissions395 ///396 /// * Collection Owner.397 /// * Collection Admin.398 ///399 /// # Arguments400 ///401 /// * collection_id: ID of the Collection to add admin for.402 ///403 /// * new_admin_id: Address of new admin to add.404 #[weight = <SelfWeightOf<T>>::add_collection_admin()]405 #[transactional]406 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {407 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);408 let collection = <CollectionHandle<T>>::try_get(collection_id)?;409410 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)411 }412413 /// 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.414 ///415 /// # Permissions416 ///417 /// * Collection Owner.418 /// * Collection Admin.419 ///420 /// # Arguments421 ///422 /// * collection_id: ID of the Collection to remove admin for.423 ///424 /// * account_id: Address of admin to remove.425 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]426 #[transactional]427 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {428 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);429 let collection = <CollectionHandle<T>>::try_get(collection_id)?;430431 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)432 }433434 /// # Permissions435 ///436 /// * Collection Owner437 ///438 /// # Arguments439 ///440 /// * collection_id.441 ///442 /// * new_sponsor.443 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]444 #[transactional]445 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {446 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);447448 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;449 target_collection.check_is_owner(&sender)?;450451 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);452 target_collection.save()453 }454455 /// # Permissions456 ///457 /// * Sponsor.458 ///459 /// # Arguments460 ///461 /// * collection_id.462 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]463 #[transactional]464 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {465 let sender = ensure_signed(origin)?;466467 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;468 ensure!(469 target_collection.sponsorship.pending_sponsor() == Some(&sender),470 Error::<T>::ConfirmUnsetSponsorFail471 );472473 target_collection.sponsorship = SponsorshipState::Confirmed(sender);474 target_collection.save()475 }476477 /// Switch back to pay-per-own-transaction model.478 ///479 /// # Permissions480 ///481 /// * Collection owner.482 ///483 /// # Arguments484 ///485 /// * collection_id.486 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]487 #[transactional]488 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {489 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);490491 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;492 target_collection.check_is_owner(&sender)?;493494 target_collection.sponsorship = SponsorshipState::Disabled;495 target_collection.save()496 }497498 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.499 ///500 /// # Permissions501 ///502 /// * Collection Owner.503 /// * Collection Admin.504 /// * Anyone if505 /// * Allow List is enabled, and506 /// * Address is added to allow list, and507 /// * MintPermission is enabled (see SetMintPermission method)508 ///509 /// # Arguments510 ///511 /// * collection_id: ID of the collection.512 ///513 /// * owner: Address, initial owner of the NFT.514 ///515 /// * data: Token data to store on chain.516 #[weight = <CommonWeights<T>>::create_item()]517 #[transactional]518 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {519 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);520521 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))522 }523524 /// This method creates multiple items in a collection created with CreateCollection method.525 ///526 /// # Permissions527 ///528 /// * Collection Owner.529 /// * Collection Admin.530 /// * Anyone if531 /// * Allow List is enabled, and532 /// * Address is added to allow list, and533 /// * MintPermission is enabled (see SetMintPermission method)534 ///535 /// # Arguments536 ///537 /// * collection_id: ID of the collection.538 ///539 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].540 ///541 /// * owner: Address, initial owner of the NFT.542 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]543 #[transactional]544 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {545 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);546 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);547548 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))549 }550551 // TODO! transaction weight552553 /// Set transfers_enabled value for particular collection554 ///555 /// # Permissions556 ///557 /// * Collection Owner.558 ///559 /// # Arguments560 ///561 /// * collection_id: ID of the collection.562 ///563 /// * value: New flag value.564 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]565 #[transactional]566 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {567 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);568 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;569 target_collection.check_is_owner(&sender)?;570571 // =========572573 target_collection.limits.transfers_enabled = Some(value);574 target_collection.save()575 }576577 /// Destroys a concrete instance of NFT.578 ///579 /// # Permissions580 ///581 /// * Collection Owner.582 /// * Collection Admin.583 /// * Current NFT Owner.584 ///585 /// # Arguments586 ///587 /// * collection_id: ID of the collection.588 ///589 /// * item_id: ID of NFT to burn.590 #[weight = <CommonWeights<T>>::burn_item()]591 #[transactional]592 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {593 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);594595 dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))596 }597598 /// Destroys a concrete instance of NFT on behalf of the owner599 /// See also: [`approve`]600 ///601 /// # Permissions602 ///603 /// * Collection Owner.604 /// * Collection Admin.605 /// * Current NFT Owner.606 ///607 /// # Arguments608 ///609 /// * collection_id: ID of the collection.610 ///611 /// * item_id: ID of NFT to burn.612 ///613 /// * from: owner of item614 #[weight = <CommonWeights<T>>::burn_from()]615 #[transactional]616 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {617 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);618619 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))620 }621622 /// Change ownership of the token.623 ///624 /// # Permissions625 ///626 /// * Collection Owner627 /// * Collection Admin628 /// * Current NFT owner629 ///630 /// # Arguments631 ///632 /// * recipient: Address of token recipient.633 ///634 /// * collection_id.635 ///636 /// * item_id: ID of the item637 /// * Non-Fungible Mode: Required.638 /// * Fungible Mode: Ignored.639 /// * Re-Fungible Mode: Required.640 ///641 /// * value: Amount to transfer.642 /// * Non-Fungible Mode: Ignored643 /// * Fungible Mode: Must specify transferred amount644 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)645 #[weight = <CommonWeights<T>>::transfer()]646 #[transactional]647 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {648 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);649650 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))651 }652653 /// Set, change, or remove approved address to transfer the ownership of the NFT.654 ///655 /// # Permissions656 ///657 /// * Collection Owner658 /// * Collection Admin659 /// * Current NFT owner660 ///661 /// # Arguments662 ///663 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).664 ///665 /// * collection_id.666 ///667 /// * item_id: ID of the item.668 #[weight = <CommonWeights<T>>::approve()]669 #[transactional]670 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {671 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);672673 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))674 }675676 /// 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.677 ///678 /// # Permissions679 /// * Collection Owner680 /// * Collection Admin681 /// * Current NFT owner682 /// * Address approved by current NFT owner683 ///684 /// # Arguments685 ///686 /// * from: Address that owns token.687 ///688 /// * recipient: Address of token recipient.689 ///690 /// * collection_id.691 ///692 /// * item_id: ID of the item.693 ///694 /// * value: Amount to transfer.695 #[weight = <CommonWeights<T>>::transfer_from()]696 #[transactional]697 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {698 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);699700 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))701 }702703 /// Set off-chain data schema.704 ///705 /// # Permissions706 ///707 /// * Collection Owner708 /// * Collection Admin709 ///710 /// # Arguments711 ///712 /// * collection_id.713 ///714 /// * schema: String representing the offchain data schema.715 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]716 #[transactional]717 pub fn set_variable_meta_data (718 origin,719 collection_id: CollectionId,720 item_id: TokenId,721 data: Vec<u8>722 ) -> DispatchResultWithPostInfo {723 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);724725 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))726 }727728 /// Set meta_update_permission value for particular collection729 ///730 /// # Permissions731 ///732 /// * Collection Owner.733 ///734 /// # Arguments735 ///736 /// * collection_id: ID of the collection.737 ///738 /// * value: New flag value.739 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]740 #[transactional]741 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {742 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);743 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;744745 ensure!(746 target_collection.meta_update_permission != MetaUpdatePermission::None,747 <CommonError<T>>::MetadataFlagFrozen,748 );749 target_collection.check_is_owner(&sender)?;750751 target_collection.meta_update_permission = value;752753 target_collection.save()754 }755756 /// Set schema standard757 /// ImageURL758 /// Unique759 ///760 /// # Permissions761 ///762 /// * Collection Owner763 /// * Collection Admin764 ///765 /// # Arguments766 ///767 /// * collection_id.768 ///769 /// * schema: SchemaVersion: enum770 #[weight = <SelfWeightOf<T>>::set_schema_version()]771 #[transactional]772 pub fn set_schema_version(773 origin,774 collection_id: CollectionId,775 version: SchemaVersion776 ) -> DispatchResult {777 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);778 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;779 target_collection.check_is_owner_or_admin(&sender)?;780 target_collection.schema_version = version;781 target_collection.save()782 }783784 /// Set off-chain data schema.785 ///786 /// # Permissions787 ///788 /// * Collection Owner789 /// * Collection Admin790 ///791 /// # Arguments792 ///793 /// * collection_id.794 ///795 /// * schema: String representing the offchain data schema.796 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]797 #[transactional]798 pub fn set_offchain_schema(799 origin,800 collection_id: CollectionId,801 schema: Vec<u8>802 ) -> DispatchResult {803 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);804 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;805 target_collection.check_is_owner_or_admin(&sender)?;806807 // check schema limit808 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");809810 target_collection.offchain_schema = schema;811 target_collection.save()812 }813814 /// Set const on-chain data schema.815 ///816 /// # Permissions817 ///818 /// * Collection Owner819 /// * Collection Admin820 ///821 /// # Arguments822 ///823 /// * collection_id.824 ///825 /// * schema: String representing the const on-chain data schema.826 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]827 #[transactional]828 pub fn set_const_on_chain_schema (829 origin,830 collection_id: CollectionId,831 schema: Vec<u8>832 ) -> DispatchResult {833 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);834 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;835 target_collection.check_is_owner_or_admin(&sender)?;836837 // check schema limit838 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");839840 target_collection.const_on_chain_schema = schema;841 target_collection.save()842 }843844 /// Set variable on-chain data schema.845 ///846 /// # Permissions847 ///848 /// * Collection Owner849 /// * Collection Admin850 ///851 /// # Arguments852 ///853 /// * collection_id.854 ///855 /// * schema: String representing the variable on-chain data schema.856 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]857 #[transactional]858 pub fn set_variable_on_chain_schema (859 origin,860 collection_id: CollectionId,861 schema: Vec<u8>862 ) -> DispatchResult {863 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);864 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;865 target_collection.check_is_owner_or_admin(&sender)?;866867 // check schema limit868 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");869870 target_collection.variable_on_chain_schema = schema;871 target_collection.save()872 }873874 #[weight = <SelfWeightOf<T>>::set_collection_limits()]875 #[transactional]876 pub fn set_collection_limits(877 origin,878 collection_id: CollectionId,879 new_limit: CollectionLimits,880 ) -> DispatchResult {881 let mut new_limit = new_limit;882 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);883 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;884 target_collection.check_is_owner(&sender)?;885 let old_limit = &target_collection.limits;886887 macro_rules! limit_default {888 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{889 $(890 if let Some($new) = $new.$field {891 let $old = $old.$field($($arg)?);892 let _ = $new;893 let _ = $old;894 $check895 } else {896 $new.$field = $old.$field897 }898 )*899 }};900 }901902 limit_default!(old_limit, new_limit,903 account_token_ownership_limit => ensure!(904 new_limit <= MAX_TOKEN_OWNERSHIP,905 <Error<T>>::CollectionLimitBoundsExceeded,906 ),907 sponsor_transfer_timeout(match target_collection.mode {908 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,909 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,910 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,911 }) => ensure!(912 new_limit <= MAX_SPONSOR_TIMEOUT,913 <Error<T>>::CollectionLimitBoundsExceeded,914 ),915 sponsored_data_size => ensure!(916 new_limit <= CUSTOM_DATA_LIMIT,917 <Error<T>>::CollectionLimitBoundsExceeded,918 ),919 token_limit => ensure!(920 old_limit >= new_limit && new_limit > 0,921 <CommonError<T>>::CollectionTokenLimitExceeded922 ),923 owner_can_transfer => ensure!(924 old_limit || !new_limit,925 <Error<T>>::OwnerPermissionsCantBeReverted,926 ),927 owner_can_destroy => ensure!(928 old_limit || !new_limit,929 <Error<T>>::OwnerPermissionsCantBeReverted,930 ),931 sponsored_data_rate_limit => {},932 transfers_enabled => {},933 );934935 target_collection.limits = new_limit;936937 target_collection.save()938 }939 }940}941942// TODO: limit returned entries?943impl<T: Config> Pallet<T> {944 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {945 <IsAdmin<T>>::iter_prefix((collection,))946 .map(|(a, _)| a)947 .collect()948 }949 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {950 <Allowlist<T>>::iter_prefix((collection,))951 .map(|(a, _)| a)952 .collect()953 }954}1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,24 IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32 pallet_prelude::DispatchResultWithPostInfo,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use nft_data_structs::{38 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,39 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,40 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,41 NFT_SPONSOR_TRANSFER_TIMEOUT, AccessMode, Collection, CreateItemData, CollectionLimits,42 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,43};44use pallet_common::{45 account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,46 CommonWeightInfo,47};48use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};49use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};50use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};5152#[cfg(test)]53mod mock;5455#[cfg(test)]56mod tests;5758mod eth;59mod sponsorship;60pub use sponsorship::NftSponsorshipHandler;61pub use eth::sponsoring::NftEthSponsorshipHandler;6263pub use eth::NftErcSupport;6465pub mod common;66use common::CommonWeights;67pub mod dispatch;68use dispatch::dispatch_call;6970#[cfg(feature = "runtime-benchmarks")]71mod benchmarking;72pub mod weights;73use weights::WeightInfo;7475decl_error! {76 /// Error for non-fungible-token module.77 pub enum Error for Module<T: Config> {78 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.79 CollectionDecimalPointLimitExceeded,80 /// This address is not set as sponsor, use setCollectionSponsor first.81 ConfirmUnsetSponsorFail,82 /// Length of items properties must be greater than 0.83 EmptyArgument,84 /// Collection limit bounds per collection exceeded85 CollectionLimitBoundsExceeded,86 /// Tried to enable permissions which are only permitted to be disabled87 OwnerPermissionsCantBeReverted,88 }89}90pub trait Config:91 system::Config92 + pallet_evm_coder_substrate::Config93 + pallet_common::Config94 + pallet_nonfungible::Config95 + pallet_refungible::Config96 + pallet_fungible::Config97 + Sized98 + TypeInfo99{100 /// Weight information for extrinsics in this pallet.101 type WeightInfo: WeightInfo;102}103104type SelfWeightOf<T> = <T as Config>::WeightInfo;105106// # Used definitions107//108// ## User control levels109//110// chain-controlled - key is uncontrolled by user111// i.e autoincrementing index112// can use non-cryptographic hash113// real - key is controlled by user114// but it is hard to generate enough colliding values, i.e owner of signed txs115// can use non-cryptographic hash116// controlled - key is completly controlled by users117// i.e maps with mutable keys118// should use cryptographic hash119//120// ## User control level downgrade reasons121//122// ?1 - chain-controlled -> controlled123// collections/tokens can be destroyed, resulting in massive holes124// ?2 - chain-controlled -> controlled125// same as ?1, but can be only added, resulting in easier exploitation126// ?3 - real -> controlled127// no confirmation required, so addresses can be easily generated128decl_storage! {129 trait Store for Module<T: Config> as Nft {130131 //#region Private members132 /// Used for migrations133 ChainVersion: u64;134 //#endregion135136 //#region Tokens transfer rate limit baskets137 /// (Collection id (controlled?2), who created (real))138 /// TODO: Off chain worker should remove from this map when collection gets removed139 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;140 /// Collection id (controlled?2), token id (controlled?2)141 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;142 /// Collection id (controlled?2), owning user (real)143 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;144 /// Collection id (controlled?2), token id (controlled?2)145 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;146 //#endregion147148 /// Variable metadata sponsoring149 /// Collection id (controlled?2), token id (controlled?2)150 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;151 }152}153154decl_module! {155 pub struct Module<T: Config> for enum Call156 where157 origin: T::Origin158 {159 type Error = Error<T>;160161 fn on_initialize(_now: T::BlockNumber) -> Weight {162 0163 }164165 /// 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 and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.166 ///167 /// # Permissions168 ///169 /// * Anyone.170 ///171 /// # Arguments172 ///173 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.174 ///175 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.176 ///177 /// * token_prefix: UTF-8 string with token prefix.178 ///179 /// * mode: [CollectionMode] collection type and type dependent data.180 // returns collection ID181 #[weight = <SelfWeightOf<T>>::create_collection()]182 #[transactional]183 pub fn create_collection(origin,184 collection_name: Vec<u16>,185 collection_description: Vec<u16>,186 token_prefix: Vec<u8>,187 mode: CollectionMode) -> DispatchResult {188189 // Anyone can create a collection190 let who = ensure_signed(origin)?;191192 // Create new collection193 let new_collection = Collection {194 owner: who.clone(),195 name: collection_name,196 mode: mode.clone(),197 mint_mode: false,198 access: AccessMode::Normal,199 description: collection_description,200 token_prefix,201 offchain_schema: Vec::new(),202 schema_version: SchemaVersion::ImageURL,203 sponsorship: SponsorshipState::Disabled,204 variable_on_chain_schema: Vec::new(),205 const_on_chain_schema: Vec::new(),206 limits: Default::default(),207 meta_update_permission: Default::default(),208 };209210 let _id = match mode {211 CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},212 CollectionMode::Fungible(decimal_points) => {213 // check params214 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);215 <PalletFungible<T>>::init_collection(new_collection)?216 }217 CollectionMode::ReFungible => {218 <PalletRefungible<T>>::init_collection(new_collection)?219 }220 };221222 Ok(())223 }224225 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.226 ///227 /// # Permissions228 ///229 /// * Collection Owner.230 ///231 /// # Arguments232 ///233 /// * collection_id: collection to destroy.234 #[weight = <SelfWeightOf<T>>::destroy_collection()]235 #[transactional]236 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {237 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);238239 let collection = <CollectionHandle<T>>::try_get(collection_id)?;240 collection.check_is_owner(&sender)?;241242 // =========243244 match collection.mode {245 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,246 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,247 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,248 }249250 <NftTransferBasket<T>>::remove_prefix(collection_id, None);251 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);252 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);253254 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);255256 Ok(())257 }258259 /// Add an address to allow list.260 ///261 /// # Permissions262 ///263 /// * Collection Owner264 /// * Collection Admin265 ///266 /// # Arguments267 ///268 /// * collection_id.269 ///270 /// * address.271 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]272 #[transactional]273 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{274275 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);276 let collection = <CollectionHandle<T>>::try_get(collection_id)?;277278 <PalletCommon<T>>::toggle_allowlist(279 &collection,280 &sender,281 &address,282 true,283 )?;284285 Ok(())286 }287288 /// Remove an address from allow list.289 ///290 /// # Permissions291 ///292 /// * Collection Owner293 /// * Collection Admin294 ///295 /// # Arguments296 ///297 /// * collection_id.298 ///299 /// * address.300 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]301 #[transactional]302 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{303304 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);305 let collection = <CollectionHandle<T>>::try_get(collection_id)?;306307 <PalletCommon<T>>::toggle_allowlist(308 &collection,309 &sender,310 &address,311 false,312 )?;313314 Ok(())315 }316317 /// Toggle between normal and allow list access for the methods with access for `Anyone`.318 ///319 /// # Permissions320 ///321 /// * Collection Owner.322 ///323 /// # Arguments324 ///325 /// * collection_id.326 ///327 /// * mode: [AccessMode]328 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]329 #[transactional]330 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult331 {332 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);333334 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;335 target_collection.check_is_owner(&sender)?;336337 target_collection.access = mode;338 target_collection.save()339 }340341 /// Allows Anyone to create tokens if:342 /// * Allow List is enabled, and343 /// * Address is added to allow list, and344 /// * This method was called with True parameter345 ///346 /// # Permissions347 /// * Collection Owner348 ///349 /// # Arguments350 ///351 /// * collection_id.352 ///353 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.354 #[weight = <SelfWeightOf<T>>::set_mint_permission()]355 #[transactional]356 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult357 {358 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);359360 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;361 target_collection.check_is_owner(&sender)?;362363 target_collection.mint_mode = mint_permission;364 target_collection.save()365 }366367 /// Change the owner of the collection.368 ///369 /// # Permissions370 ///371 /// * Collection Owner.372 ///373 /// # Arguments374 ///375 /// * collection_id.376 ///377 /// * new_owner.378 #[weight = <SelfWeightOf<T>>::change_collection_owner()]379 #[transactional]380 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {381382 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);383384 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;385 target_collection.check_is_owner(&sender)?;386387 target_collection.owner = new_owner;388 target_collection.save()389 }390391 /// Adds an admin of the Collection.392 /// 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.393 ///394 /// # Permissions395 ///396 /// * Collection Owner.397 /// * Collection Admin.398 ///399 /// # Arguments400 ///401 /// * collection_id: ID of the Collection to add admin for.402 ///403 /// * new_admin_id: Address of new admin to add.404 #[weight = <SelfWeightOf<T>>::add_collection_admin()]405 #[transactional]406 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {407 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);408 let collection = <CollectionHandle<T>>::try_get(collection_id)?;409410 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)411 }412413 /// 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.414 ///415 /// # Permissions416 ///417 /// * Collection Owner.418 /// * Collection Admin.419 ///420 /// # Arguments421 ///422 /// * collection_id: ID of the Collection to remove admin for.423 ///424 /// * account_id: Address of admin to remove.425 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]426 #[transactional]427 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {428 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);429 let collection = <CollectionHandle<T>>::try_get(collection_id)?;430431 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)432 }433434 /// # Permissions435 ///436 /// * Collection Owner437 ///438 /// # Arguments439 ///440 /// * collection_id.441 ///442 /// * new_sponsor.443 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]444 #[transactional]445 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {446 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);447448 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;449 target_collection.check_is_owner(&sender)?;450451 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);452 target_collection.save()453 }454455 /// # Permissions456 ///457 /// * Sponsor.458 ///459 /// # Arguments460 ///461 /// * collection_id.462 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]463 #[transactional]464 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {465 let sender = ensure_signed(origin)?;466467 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;468 ensure!(469 target_collection.sponsorship.pending_sponsor() == Some(&sender),470 Error::<T>::ConfirmUnsetSponsorFail471 );472473 target_collection.sponsorship = SponsorshipState::Confirmed(sender);474 target_collection.save()475 }476477 /// Switch back to pay-per-own-transaction model.478 ///479 /// # Permissions480 ///481 /// * Collection owner.482 ///483 /// # Arguments484 ///485 /// * collection_id.486 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]487 #[transactional]488 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {489 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);490491 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;492 target_collection.check_is_owner(&sender)?;493494 target_collection.sponsorship = SponsorshipState::Disabled;495 target_collection.save()496 }497498 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.499 ///500 /// # Permissions501 ///502 /// * Collection Owner.503 /// * Collection Admin.504 /// * Anyone if505 /// * Allow List is enabled, and506 /// * Address is added to allow list, and507 /// * MintPermission is enabled (see SetMintPermission method)508 ///509 /// # Arguments510 ///511 /// * collection_id: ID of the collection.512 ///513 /// * owner: Address, initial owner of the NFT.514 ///515 /// * data: Token data to store on chain.516 #[weight = <CommonWeights<T>>::create_item()]517 #[transactional]518 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {519 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);520521 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))522 }523524 /// This method creates multiple items in a collection created with CreateCollection method.525 ///526 /// # Permissions527 ///528 /// * Collection Owner.529 /// * Collection Admin.530 /// * Anyone if531 /// * Allow List is enabled, and532 /// * Address is added to allow list, and533 /// * MintPermission is enabled (see SetMintPermission method)534 ///535 /// # Arguments536 ///537 /// * collection_id: ID of the collection.538 ///539 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].540 ///541 /// * owner: Address, initial owner of the NFT.542 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]543 #[transactional]544 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {545 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);546 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);547548 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))549 }550551 // TODO! transaction weight552553 /// Set transfers_enabled value for particular collection554 ///555 /// # Permissions556 ///557 /// * Collection Owner.558 ///559 /// # Arguments560 ///561 /// * collection_id: ID of the collection.562 ///563 /// * value: New flag value.564 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]565 #[transactional]566 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {567 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);568 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;569 target_collection.check_is_owner(&sender)?;570571 // =========572573 target_collection.limits.transfers_enabled = Some(value);574 target_collection.save()575 }576577 /// Destroys a concrete instance of NFT.578 ///579 /// # Permissions580 ///581 /// * Collection Owner.582 /// * Collection Admin.583 /// * Current NFT Owner.584 ///585 /// # Arguments586 ///587 /// * collection_id: ID of the collection.588 ///589 /// * item_id: ID of NFT to burn.590 #[weight = <CommonWeights<T>>::burn_item()]591 #[transactional]592 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {593 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);594595 dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))596 }597598 /// Destroys a concrete instance of NFT on behalf of the owner599 /// See also: [`approve`]600 ///601 /// # Permissions602 ///603 /// * Collection Owner.604 /// * Collection Admin.605 /// * Current NFT Owner.606 ///607 /// # Arguments608 ///609 /// * collection_id: ID of the collection.610 ///611 /// * item_id: ID of NFT to burn.612 ///613 /// * from: owner of item614 #[weight = <CommonWeights<T>>::burn_from()]615 #[transactional]616 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {617 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);618619 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))620 }621622 /// Change ownership of the token.623 ///624 /// # Permissions625 ///626 /// * Collection Owner627 /// * Collection Admin628 /// * Current NFT owner629 ///630 /// # Arguments631 ///632 /// * recipient: Address of token recipient.633 ///634 /// * collection_id.635 ///636 /// * item_id: ID of the item637 /// * Non-Fungible Mode: Required.638 /// * Fungible Mode: Ignored.639 /// * Re-Fungible Mode: Required.640 ///641 /// * value: Amount to transfer.642 /// * Non-Fungible Mode: Ignored643 /// * Fungible Mode: Must specify transferred amount644 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)645 #[weight = <CommonWeights<T>>::transfer()]646 #[transactional]647 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {648 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);649650 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))651 }652653 /// Set, change, or remove approved address to transfer the ownership of the NFT.654 ///655 /// # Permissions656 ///657 /// * Collection Owner658 /// * Collection Admin659 /// * Current NFT owner660 ///661 /// # Arguments662 ///663 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).664 ///665 /// * collection_id.666 ///667 /// * item_id: ID of the item.668 #[weight = <CommonWeights<T>>::approve()]669 #[transactional]670 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {671 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);672673 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))674 }675676 /// 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.677 ///678 /// # Permissions679 /// * Collection Owner680 /// * Collection Admin681 /// * Current NFT owner682 /// * Address approved by current NFT owner683 ///684 /// # Arguments685 ///686 /// * from: Address that owns token.687 ///688 /// * recipient: Address of token recipient.689 ///690 /// * collection_id.691 ///692 /// * item_id: ID of the item.693 ///694 /// * value: Amount to transfer.695 #[weight = <CommonWeights<T>>::transfer_from()]696 #[transactional]697 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {698 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);699700 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))701 }702703 /// Set off-chain data schema.704 ///705 /// # Permissions706 ///707 /// * Collection Owner708 /// * Collection Admin709 ///710 /// # Arguments711 ///712 /// * collection_id.713 ///714 /// * schema: String representing the offchain data schema.715 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]716 #[transactional]717 pub fn set_variable_meta_data (718 origin,719 collection_id: CollectionId,720 item_id: TokenId,721 data: Vec<u8>722 ) -> DispatchResultWithPostInfo {723 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);724725 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))726 }727728 /// Set meta_update_permission value for particular collection729 ///730 /// # Permissions731 ///732 /// * Collection Owner.733 ///734 /// # Arguments735 ///736 /// * collection_id: ID of the collection.737 ///738 /// * value: New flag value.739 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]740 #[transactional]741 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {742 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);743 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;744745 ensure!(746 target_collection.meta_update_permission != MetaUpdatePermission::None,747 <CommonError<T>>::MetadataFlagFrozen,748 );749 target_collection.check_is_owner(&sender)?;750751 target_collection.meta_update_permission = value;752753 target_collection.save()754 }755756 /// Set schema standard757 /// ImageURL758 /// Unique759 ///760 /// # Permissions761 ///762 /// * Collection Owner763 /// * Collection Admin764 ///765 /// # Arguments766 ///767 /// * collection_id.768 ///769 /// * schema: SchemaVersion: enum770 #[weight = <SelfWeightOf<T>>::set_schema_version()]771 #[transactional]772 pub fn set_schema_version(773 origin,774 collection_id: CollectionId,775 version: SchemaVersion776 ) -> DispatchResult {777 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);778 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;779 target_collection.check_is_owner_or_admin(&sender)?;780 target_collection.schema_version = version;781 target_collection.save()782 }783784 /// Set off-chain data schema.785 ///786 /// # Permissions787 ///788 /// * Collection Owner789 /// * Collection Admin790 ///791 /// # Arguments792 ///793 /// * collection_id.794 ///795 /// * schema: String representing the offchain data schema.796 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]797 #[transactional]798 pub fn set_offchain_schema(799 origin,800 collection_id: CollectionId,801 schema: Vec<u8>802 ) -> DispatchResult {803 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);804 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;805 target_collection.check_is_owner_or_admin(&sender)?;806807 // check schema limit808 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");809810 target_collection.offchain_schema = schema;811 target_collection.save()812 }813814 /// Set const on-chain data schema.815 ///816 /// # Permissions817 ///818 /// * Collection Owner819 /// * Collection Admin820 ///821 /// # Arguments822 ///823 /// * collection_id.824 ///825 /// * schema: String representing the const on-chain data schema.826 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]827 #[transactional]828 pub fn set_const_on_chain_schema (829 origin,830 collection_id: CollectionId,831 schema: Vec<u8>832 ) -> DispatchResult {833 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);834 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;835 target_collection.check_is_owner_or_admin(&sender)?;836837 // check schema limit838 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");839840 target_collection.const_on_chain_schema = schema;841 target_collection.save()842 }843844 /// Set variable on-chain data schema.845 ///846 /// # Permissions847 ///848 /// * Collection Owner849 /// * Collection Admin850 ///851 /// # Arguments852 ///853 /// * collection_id.854 ///855 /// * schema: String representing the variable on-chain data schema.856 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]857 #[transactional]858 pub fn set_variable_on_chain_schema (859 origin,860 collection_id: CollectionId,861 schema: Vec<u8>862 ) -> DispatchResult {863 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);864 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;865 target_collection.check_is_owner_or_admin(&sender)?;866867 // check schema limit868 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");869870 target_collection.variable_on_chain_schema = schema;871 target_collection.save()872 }873874 #[weight = <SelfWeightOf<T>>::set_collection_limits()]875 #[transactional]876 pub fn set_collection_limits(877 origin,878 collection_id: CollectionId,879 new_limit: CollectionLimits,880 ) -> DispatchResult {881 let mut new_limit = new_limit;882 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);883 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;884 target_collection.check_is_owner(&sender)?;885 let old_limit = &target_collection.limits;886887 macro_rules! limit_default {888 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{889 $(890 if let Some($new) = $new.$field {891 let $old = $old.$field($($arg)?);892 let _ = $new;893 let _ = $old;894 $check895 } else {896 $new.$field = $old.$field897 }898 )*899 }};900 }901902 limit_default!(old_limit, new_limit,903 account_token_ownership_limit => ensure!(904 new_limit <= MAX_TOKEN_OWNERSHIP,905 <Error<T>>::CollectionLimitBoundsExceeded,906 ),907 sponsor_transfer_timeout(match target_collection.mode {908 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,909 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,910 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,911 }) => ensure!(912 new_limit <= MAX_SPONSOR_TIMEOUT,913 <Error<T>>::CollectionLimitBoundsExceeded,914 ),915 sponsored_data_size => ensure!(916 new_limit <= CUSTOM_DATA_LIMIT,917 <Error<T>>::CollectionLimitBoundsExceeded,918 ),919 token_limit => ensure!(920 old_limit >= new_limit && new_limit > 0,921 <CommonError<T>>::CollectionTokenLimitExceeded922 ),923 owner_can_transfer => ensure!(924 old_limit || !new_limit,925 <Error<T>>::OwnerPermissionsCantBeReverted,926 ),927 owner_can_destroy => ensure!(928 old_limit || !new_limit,929 <Error<T>>::OwnerPermissionsCantBeReverted,930 ),931 sponsored_data_rate_limit => {},932 transfers_enabled => {},933 );934935 target_collection.limits = new_limit;936937 target_collection.save()938 }939 }940}pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -133,8 +133,8 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {
- PalletCommon::init_collection(data)
+ pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(data)
}
pub fn destroy_collection(
collection: NonfungibleHandle<T>,
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -156,8 +156,8 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {
- PalletCommon::init_collection(data)
+ pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(data)
}
pub fn destroy_collection(
collection: RefungibleHandle<T>,
primitives/nft/src/lib.rsdiffbeforeafterboth--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -207,8 +207,8 @@
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
-pub struct Collection<T: frame_system::Config> {
- pub owner: T::AccountId,
+pub struct Collection<AccountId> {
+ pub owner: AccountId,
pub mode: CollectionMode,
pub access: AccessMode,
pub name: Vec<u16>, // 64 include null escape char
@@ -217,7 +217,7 @@
pub mint_mode: bool,
pub offchain_schema: Vec<u8>,
pub schema_version: SchemaVersion,
- pub sponsorship: SponsorshipState<T::AccountId>,
+ pub sponsorship: SponsorshipState<AccountId>,
pub limits: CollectionLimits, // Collection private restrictions
pub variable_on_chain_schema: Vec<u8>, //
pub const_on_chain_schema: Vec<u8>, //
@@ -413,3 +413,11 @@
CreateItemData::Fungible(item)
}
}
+
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
+pub struct CollectionStats {
+ pub created: u32,
+ pub destroyed: u32,
+ pub alive: u32,
+}
primitives/rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -1,6 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use nft_data_structs::{CollectionId, TokenId};
+use nft_data_structs::{CollectionId, TokenId, Collection, CollectionStats};
use sp_std::vec::Vec;
use sp_core::H160;
use codec::Decode;
@@ -32,6 +32,9 @@
fn adminlist(collection: CollectionId) -> Vec<CrossAccountId>;
fn allowlist(collection: CollectionId) -> Vec<CrossAccountId>;
+ fn allowed(collection: CollectionId, user: CrossAccountId) -> bool;
fn last_token_id(collection: CollectionId) -> TokenId;
+ fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>>;
+ fn collection_stats() -> CollectionStats;
}
}
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -1040,14 +1040,23 @@
.or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))
}
fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {
- <pallet_nft::Pallet<Runtime>>::adminlist(collection)
+ <pallet_common::Pallet<Runtime>>::adminlist(collection)
}
fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {
- <pallet_nft::Pallet<Runtime>>::allowlist(collection)
+ <pallet_common::Pallet<Runtime>>::allowlist(collection)
+ }
+ fn allowed(collection: CollectionId, user: CrossAccountId) -> bool {
+ <pallet_common::Pallet<Runtime>>::allowed(collection, user)
}
fn last_token_id(collection: CollectionId) -> TokenId {
dispatch_nft_runtime!(collection.last_token_id())
}
+ fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>> {
+ <pallet_common::CollectionById<Runtime>>::get(collection)
+ }
+ fn collection_stats() -> CollectionStats {
+ <pallet_common::Pallet<Runtime>>::collection_stats()
+ }
}
impl sp_api::Core<Block> for Runtime {
tests/src/addCollectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/addCollectionAdmin.test.ts
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -8,7 +8,7 @@
import chaiAsPromised from 'chai-as-promised';
import privateKey from './substrate/privateKey';
import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
-import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId} from './util/helpers';
+import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId, queryCollectionExpectSuccess} from './util/helpers';
chai.use(chaiAsPromised);
const expect = chai.expect;
@@ -20,7 +20,7 @@
const alice = privateKey('//Alice');
const bob = privateKey('//Bob');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.equal(alice.address);
const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
@@ -38,7 +38,7 @@
const bob = privateKey('//Bob');
const charlie = privateKey('//CHARLIE');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.equal(alice.address);
const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
tests/src/addToAllowList.test.tsdiffbeforeafterboth--- a/tests/src/addToAllowList.test.ts
+++ b/tests/src/addToAllowList.test.ts
@@ -18,6 +18,7 @@
normalizeAccountId,
addCollectionAdminExpectSuccess,
addToAllowListExpectFail,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -55,7 +56,7 @@
it('Allow list an address in the collection that does not exist', async () => {
await usingApi(async (api) => {
// tslint:disable-next-line: no-bitwise
- const collectionId = ((await api.query.common.createdCollectionCount()).toNumber()) + 1;
+ const collectionId = await getCreatedCollectionCount(api) + 1;
const bob = privateKey('//Bob');
const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(bob.address));
tests/src/approve.test.tsdiffbeforeafterboth--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -18,6 +18,7 @@
transferExpectSuccess,
addCollectionAdminExpectSuccess,
adminApproveFromExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -94,13 +95,13 @@
it('Approve for a collection that does not exist', async () => {
await usingApi(async (api: ApiPromise) => {
// nft
- const nftCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const nftCollectionCount = await getCreatedCollectionCount(api);
await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);
// fungible
- const fungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const fungibleCollectionCount = await getCreatedCollectionCount(api);
await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);
// reFungible
- const reFungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const reFungibleCollectionCount = await getCreatedCollectionCount(api);
await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);
});
});
tests/src/change-collection-owner.test.tsdiffbeforeafterboth--- a/tests/src/change-collection-owner.test.ts
+++ b/tests/src/change-collection-owner.test.ts
@@ -22,6 +22,7 @@
setMintPermissionExpectFailure,
destroyCollectionExpectFailure,
setPublicAccessModeExpectSuccess,
+ queryCollectionExpectSuccess,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -34,13 +35,13 @@
const alice = privateKey('//Alice');
const bob = privateKey('//Bob');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection =await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.deep.eq(alice.address);
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
await submitTransactionAsync(alice, changeOwnerTx);
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
});
});
@@ -53,7 +54,7 @@
const alice = privateKey('//Alice');
const bob = privateKey('//Bob');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.deep.eq(alice.address);
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
@@ -62,7 +63,7 @@
const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);
await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
});
});
@@ -74,13 +75,13 @@
const bob = privateKey('//Bob');
const charlie = privateKey('//Charlie');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.deep.eq(alice.address);
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
await submitTransactionAsync(alice, changeOwnerTx);
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
// After changing the owner of the collection, all privileged methods are available to the new owner
@@ -118,20 +119,20 @@
const bob = privateKey('//Bob');
const charlie = privateKey('//Charlie');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.deep.eq(alice.address);
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
await submitTransactionAsync(alice, changeOwnerTx);
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
const changeOwnerTx2 = api.tx.nft.changeCollectionOwner(collectionId, charlie.address);
await submitTransactionAsync(bob, changeOwnerTx2);
// ownership lost
- const collectionAfterOwnerChange2 = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange2 = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange2.owner.toString()).to.be.deep.eq(charlie.address);
});
});
@@ -147,7 +148,7 @@
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);
// Verifying that nothing bad happened (network is live, new collections can be created, etc.)
@@ -166,7 +167,7 @@
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);
// Verifying that nothing bad happened (network is live, new collections can be created, etc.)
@@ -195,7 +196,7 @@
const bob = privateKey('//Bob');
const charlie = privateKey('//Charlie');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.deep.eq(alice.address);
const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
@@ -204,7 +205,7 @@
const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);
await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;
- const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Alice');
tests/src/confirmSponsorship.test.tsdiffbeforeafterboth--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -20,6 +20,7 @@
addToAllowListExpectSuccess,
normalizeAccountId,
addCollectionAdminExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
import {Keyring} from '@polkadot/api';
import {IKeyringPair} from '@polkadot/types/types';
@@ -335,7 +336,7 @@
// Find the collection that never existed
let collectionId = 0;
await usingApi(async (api) => {
- collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ collectionId = await getCreatedCollectionCount(api) + 1;
});
await confirmSponsorshipExpectFailure(collectionId, '//Bob');
tests/src/contracts.test.tsdiffbeforeafterboth--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.test.ts
@@ -228,7 +228,7 @@
const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);
await submitTransactionAsync(alice, changeAdminTx);
- expect(await isAllowlisted(collectionId, bob.address)).to.be.false;
+ expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false;
{
const transferTx = contract.tx.toggleAllowList(value, gasLimit, collectionId, bob.address, true);
@@ -236,7 +236,7 @@
const result = getGenericResult(events);
expect(result.success).to.be.true;
- expect(await isAllowlisted(collectionId, bob.address)).to.be.true;
+ expect(await isAllowlisted(api, collectionId, bob.address)).to.be.true;
}
{
const transferTx = contract.tx.toggleAllowList(value, gasLimit, collectionId, bob.address, false);
@@ -244,7 +244,7 @@
const result = getGenericResult(events);
expect(result.success).to.be.true;
- expect(await isAllowlisted(collectionId, bob.address)).to.be.false;
+ expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false;
}
});
});
tests/src/createMultipleItems.test.tsdiffbeforeafterboth--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -20,6 +20,7 @@
getLastTokenId,
getVariableMetadata,
getConstMetadata,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -273,7 +274,7 @@
it('Create token in not existing collection', async () => {
await usingApi(async (api: ApiPromise) => {
- const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ const collectionId = await getCreatedCollectionCount(api) + 1;
const createMultipleItemsTx = api.tx.nft
.createMultipleItems(collectionId, normalizeAccountId(alice.address), ['NFT', 'NFT', 'NFT']);
await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected;
tests/src/destroyCollection.test.tsdiffbeforeafterboth--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -13,6 +13,7 @@
destroyCollectionExpectFailure,
setCollectionLimitsExpectSuccess,
addCollectionAdminExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -46,7 +47,7 @@
it('(!negative test!) Destroy a collection that never existed', async () => {
await usingApi(async (api) => {
// Find the collection that never existed
- const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ const collectionId = await getCreatedCollectionCount(api) + 1;
await destroyCollectionExpectFailure(collectionId);
});
});
tests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -1,7 +1,7 @@
// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
/* eslint-disable */
-import type { NftDataStructsCollectionId, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr } from './nft';
+import type { NftDataStructsCollection, NftDataStructsCollectionId, NftDataStructsCollectionStats, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr } from './nft';
import type { Bytes, HashMap, Json, Metadata, Null, Option, StorageKey, Text, U256, U64, Vec, bool, u128, u32, u64 } from '@polkadot/types';
import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';
import type { EpochAuthorship } from '@polkadot/types/interfaces/babe';
@@ -373,6 +373,10 @@
**/
allowance: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, sender: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: NftDataStructsTokenId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
/**
+ * Check if user is allowed to use collection
+ **/
+ allowed: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, account: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
+ /**
* Get allowlist
**/
allowlist: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletCommonAccountBasicCrossAccountIdRepr>>>;
@@ -381,6 +385,14 @@
**/
balance: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, account: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: NftDataStructsTokenId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
/**
+ * Get collection by specified id
+ **/
+ collectionById: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<NftDataStructsCollection>>>;
+ /**
+ * Get collection stats
+ **/
+ collectionStats: AugmentedRpc<(at?: Hash | string | Uint8Array) => Observable<NftDataStructsCollectionStats>>;
+ /**
* Get tokens contained in collection
**/
collectionTokens: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<NftDataStructsTokenId>>>;
tests/src/interfaces/augment-types.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -2,7 +2,7 @@
/* eslint-disable */
import type { EthereumBlock, EthereumLog, EthereumReceipt, EthereumTransactionLegacyTransaction, EvmCoreErrorExitReason, FpRpcTransactionStatus } from './ethereum';
-import type { NftDataStructsAccessMode, NftDataStructsCollection, NftDataStructsCollectionId, NftDataStructsCollectionLimits, NftDataStructsCollectionMode, NftDataStructsCreateItemData, NftDataStructsMetaUpdatePermission, NftDataStructsSchemaVersion, NftDataStructsSponsorshipState, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2 } from './nft';
+import type { NftDataStructsAccessMode, NftDataStructsCollection, NftDataStructsCollectionId, NftDataStructsCollectionLimits, NftDataStructsCollectionMode, NftDataStructsCollectionStats, NftDataStructsCreateItemData, NftDataStructsMetaUpdatePermission, NftDataStructsSchemaVersion, NftDataStructsSponsorshipState, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2 } from './nft';
import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundStatus, CumulusPalletXcmpQueueOutboundStatus, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1PersistedValidationData } from './polkadot';
import type { BitVec, Bool, Bytes, Data, I128, I16, I256, I32, I64, I8, Json, Null, Raw, StorageKey, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types';
import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
@@ -627,6 +627,7 @@
NftDataStructsCollectionId: NftDataStructsCollectionId;
NftDataStructsCollectionLimits: NftDataStructsCollectionLimits;
NftDataStructsCollectionMode: NftDataStructsCollectionMode;
+ NftDataStructsCollectionStats: NftDataStructsCollectionStats;
NftDataStructsCreateItemData: NftDataStructsCreateItemData;
NftDataStructsMetaUpdatePermission: NftDataStructsMetaUpdatePermission;
NftDataStructsSchemaVersion: NftDataStructsSchemaVersion;
tests/src/interfaces/nft/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/nft/definitions.ts
+++ b/tests/src/interfaces/nft/definitions.ts
@@ -40,6 +40,9 @@
constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),
variableMetadata: fun('Get token variable metadata', [collectionParam, tokenParam], 'Vec<u8>'),
tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),
+ collectionById: fun('Get collection by specified id', [collectionParam], 'Option<NftDataStructsCollection>'),
+ collectionStats: fun('Get collection stats', [], 'NftDataStructsCollectionStats'),
+ allowed: fun('Check if user is allowed to use collection', [collectionParam, crossAccountParam()], 'bool'),
},
types: {
PalletCommonAccountBasicCrossAccountIdRepr: {
@@ -64,6 +67,11 @@
constOnChainSchema: 'Vec<u8>',
metaUpdatePermission: 'NftDataStructsMetaUpdatePermission',
},
+ NftDataStructsCollectionStats: {
+ created: 'u32',
+ destroyed: 'u32',
+ alive: 'u32',
+ },
NftDataStructsCollectionId: 'u32',
NftDataStructsTokenId: 'u32',
PalletNonfungibleItemData: mkDummy('NftItemData'),
tests/src/interfaces/nft/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/nft/types.ts
+++ b/tests/src/interfaces/nft/types.ts
@@ -48,6 +48,13 @@
readonly dummyCollectionMode: u32;
}
+/** @name NftDataStructsCollectionStats */
+export interface NftDataStructsCollectionStats extends Struct {
+ readonly created: u32;
+ readonly destroyed: u32;
+ readonly alive: u32;
+}
+
/** @name NftDataStructsCreateItemData */
export interface NftDataStructsCreateItemData extends Struct {
readonly dummyCreateItemData: u32;
tests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/removeCollectionAdmin.test.ts
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -8,7 +8,7 @@
import chaiAsPromised from 'chai-as-promised';
import privateKey from './substrate/privateKey';
import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
-import {createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId} from './util/helpers';
+import {createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId, queryCollectionExpectSuccess} from './util/helpers';
chai.use(chaiAsPromised);
const expect = chai.expect;
@@ -19,7 +19,7 @@
const collectionId = await createCollectionExpectSuccess();
const alice = privateKey('//Alice');
const bob = privateKey('//Bob');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.deep.eq(alice.address);
// first - add collection admin Bob
const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
@@ -43,7 +43,7 @@
const alice = privateKey('//Alice');
const bob = privateKey('//Bob');
const charlie = privateKey('//Charlie');
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
// first - add collection admin Bob
const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
tests/src/removeCollectionSponsor.test.tsdiffbeforeafterboth--- a/tests/src/removeCollectionSponsor.test.ts
+++ b/tests/src/removeCollectionSponsor.test.ts
@@ -18,6 +18,7 @@
removeCollectionSponsorExpectFailure,
normalizeAccountId,
addCollectionAdminExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
import {Keyring} from '@polkadot/api';
import {IKeyringPair} from '@polkadot/types/types';
@@ -98,7 +99,7 @@
// Find the collection that never existed
let collectionId = 0;
await usingApi(async (api) => {
- collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ collectionId = await getCreatedCollectionCount(api) + 1;
});
await removeCollectionSponsorExpectFailure(collectionId);
tests/src/removeFromAllowList.test.tsdiffbeforeafterboth--- a/tests/src/removeFromAllowList.test.ts
+++ b/tests/src/removeFromAllowList.test.ts
@@ -37,13 +37,13 @@
});
it('ensure bob is not in allowlist after removal', async () => {
- await usingApi(async () => {
+ await usingApi(async api => {
const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
await enableAllowListExpectSuccess(alice, collectionId);
await addToAllowListExpectSuccess(alice, collectionId, bob.address);
await removeFromAllowListExpectSuccess(alice, collectionId, normalizeAccountId(bob.address));
- expect(await isAllowlisted(collectionId, bob.address)).to.be.false;
+ expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false;
});
});
@@ -104,13 +104,13 @@
});
it('ensure address is not in allowlist after removal', async () => {
- await usingApi(async () => {
+ await usingApi(async api => {
const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
await enableAllowListExpectSuccess(alice, collectionId);
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
await addToAllowListExpectSuccess(alice, collectionId, charlie.address);
await removeFromAllowListExpectSuccess(bob, collectionId, normalizeAccountId(charlie.address));
- expect(await isAllowlisted(collectionId, charlie.address)).to.be.false;
+ expect(await isAllowlisted(api, collectionId, charlie.address)).to.be.false;
});
});
tests/src/setCollectionSponsor.test.tsdiffbeforeafterboth--- a/tests/src/setCollectionSponsor.test.ts
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -11,6 +11,7 @@
destroyCollectionExpectSuccess,
setCollectionSponsorExpectFailure,
addCollectionAdminExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
import {Keyring} from '@polkadot/api';
import {IKeyringPair} from '@polkadot/types/types';
@@ -77,7 +78,7 @@
// Find the collection that never existed
let collectionId = 0;
await usingApi(async (api) => {
- collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ collectionId = await getCreatedCollectionCount(api) + 1;
});
await setCollectionSponsorExpectFailure(collectionId, bob.address);
tests/src/setConstOnChainSchema.test.tsdiffbeforeafterboth--- a/tests/src/setConstOnChainSchema.test.ts
+++ b/tests/src/setConstOnChainSchema.test.ts
@@ -12,6 +12,8 @@
createCollectionExpectSuccess,
destroyCollectionExpectSuccess,
addCollectionAdminExpectSuccess,
+ queryCollectionExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -37,7 +39,7 @@
it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess();
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
await submitTransactionAsync(alice, setShema);
@@ -47,7 +49,7 @@
it('Collection admin can set the scheme', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess();
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
@@ -60,7 +62,7 @@
const collectionId = await createCollectionExpectSuccess();
const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
await submitTransactionAsync(alice, setShema);
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.constOnChainSchema.toString()).to.be.eq(shema);
});
});
@@ -71,7 +73,7 @@
it('Set a non-existent collection', async () => {
await usingApi(async (api) => {
// tslint:disable-next-line: radix
- const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ const collectionId = await getCreatedCollectionCount(api) + 1;
const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
await expect(submitTransactionExpectFailAsync(alice, setShema)).to.be.rejected;
});
@@ -97,7 +99,7 @@
it('Execute method not on behalf of the collection owner', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess();
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
await expect(submitTransactionExpectFailAsync(bob, setShema)).to.be.rejected;
tests/src/setPublicAccessMode.test.tsdiffbeforeafterboth--- a/tests/src/setPublicAccessMode.test.ts
+++ b/tests/src/setPublicAccessMode.test.ts
@@ -19,6 +19,7 @@
enableAllowListExpectSuccess,
normalizeAccountId,
addCollectionAdminExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -60,7 +61,7 @@
it('Set a non-existent collection', async () => {
await usingApi(async (api: ApiPromise) => {
// tslint:disable-next-line: radix
- const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ const collectionId = await getCreatedCollectionCount(api) + 1;
const tx = api.tx.nft.setPublicAccessMode(collectionId, 'AllowList');
await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
});
tests/src/setVariableOnChainSchema.test.tsdiffbeforeafterboth--- a/tests/src/setVariableOnChainSchema.test.ts
+++ b/tests/src/setVariableOnChainSchema.test.ts
@@ -12,6 +12,8 @@
createCollectionExpectSuccess,
destroyCollectionExpectSuccess,
addCollectionAdminExpectSuccess,
+ queryCollectionExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -37,7 +39,7 @@
it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess();
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
await submitTransactionAsync(alice, setSchema);
@@ -49,7 +51,7 @@
const collectionId = await createCollectionExpectSuccess();
const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
await submitTransactionAsync(alice, setSchema);
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.variableOnChainSchema.toString()).to.be.eq(schema);
});
@@ -61,7 +63,7 @@
it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess();
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
@@ -75,7 +77,7 @@
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
await submitTransactionAsync(bob, setSchema);
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.variableOnChainSchema.toString()).to.be.eq(schema);
});
@@ -87,7 +89,7 @@
it('Set a non-existent collection', async () => {
await usingApi(async (api) => {
// tslint:disable-next-line: radix
- const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;
+ const collectionId = await getCreatedCollectionCount(api) + 1;
const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
await expect(submitTransactionExpectFailAsync(alice, setSchema)).to.be.rejected;
});
@@ -113,7 +115,7 @@
it('Execute method not on behalf of the collection owner', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess();
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.eq(alice.address);
const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
await expect(submitTransactionExpectFailAsync(bob, setSchema)).to.be.rejected;
tests/src/transfer.test.tsdiffbeforeafterboth--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -19,6 +19,7 @@
transferExpectFailure,
transferExpectSuccess,
addCollectionAdminExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
let alice: IKeyringPair;
@@ -132,13 +133,13 @@
it('Transfer with not existed collection_id', async () => {
await usingApi(async (api) => {
// nft
- const nftCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const nftCollectionCount = await getCreatedCollectionCount(api);
await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1);
// fungible
- const fungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const fungibleCollectionCount = await getCreatedCollectionCount(api);
await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1);
// reFungible
- const reFungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const reFungibleCollectionCount = await getCreatedCollectionCount(api);
await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1);
});
});
tests/src/transferFrom.test.tsdiffbeforeafterboth--- a/tests/src/transferFrom.test.ts
+++ b/tests/src/transferFrom.test.ts
@@ -19,6 +19,7 @@
transferFromExpectSuccess,
burnItemExpectSuccess,
setCollectionLimitsExpectSuccess,
+ getCreatedCollectionCount,
} from './util/helpers';
chai.use(chaiAsPromised);
@@ -109,18 +110,18 @@
it('transferFrom for a collection that does not exist', async () => {
await usingApi(async (api: ApiPromise) => {
// nft
- const nftCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const nftCollectionCount = await getCreatedCollectionCount(api);
await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);
await transferFromExpectFail(nftCollectionCount + 1, 1, bob, alice, charlie, 1);
// fungible
- const fungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const fungibleCollectionCount = await getCreatedCollectionCount(api);
await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);
await transferFromExpectFail(fungibleCollectionCount + 1, 0, bob, alice, charlie, 1);
// reFungible
- const reFungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();
+ const reFungibleCollectionCount = await getCreatedCollectionCount(api);
await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);
await transferFromExpectFail(reFungibleCollectionCount + 1, 1, bob, alice, charlie, 1);
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -269,7 +269,7 @@
let collectionId = 0;
await usingApi(async (api) => {
// Get number of collections before the transaction
- const collectionCountBefore = (await api.query.common.createdCollectionCount()).toNumber();
+ const collectionCountBefore = await getCreatedCollectionCount(api);
// Run the CreateCollection transaction
const alicePrivateKey = privateKey('//Alice');
@@ -288,10 +288,10 @@
const result = getCreateCollectionResult(events);
// Get number of collections after the transaction
- const collectionCountAfter = (await api.query.common.createdCollectionCount()).toNumber();
+ const collectionCountAfter = await getCreatedCollectionCount(api);
// Get the collection
- const collection = (await api.query.common.collectionById(result.collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, result.collectionId);
// What to expect
// tslint:disable-next-line:no-unused-expression
@@ -325,7 +325,7 @@
await usingApi(async (api) => {
// Get number of collections before the transaction
- const collectionCountBefore = (await api.query.common.createdCollectionCount()).toNumber();
+ const collectionCountBefore = await getCreatedCollectionCount(api);
// Run the CreateCollection transaction
const alicePrivateKey = privateKey('//Alice');
@@ -334,7 +334,7 @@
const result = getCreateCollectionResult(events);
// Get number of collections after the transaction
- const collectionCountAfter = (await api.query.common.createdCollectionCount()).toNumber();
+ const collectionCountAfter = await getCreatedCollectionCount(api);
// What to expect
// tslint:disable-next-line:no-unused-expression
@@ -364,7 +364,7 @@
}
export async function findNotExistingCollection(api: ApiPromise): Promise<number> {
- const totalNumber = (await api.query.common.createdCollectionCount()).toNumber();
+ const totalNumber = await getCreatedCollectionCount(api);
const newCollection: number = totalNumber + 1;
return newCollection;
}
@@ -398,7 +398,7 @@
expect(result).to.be.true;
// What to expect
- expect((await api.query.common.collectionById(collectionId)).isNone).to.be.true;
+ expect(await getDetailedCollectionInfo(api, collectionId)).to.be.null;
});
}
@@ -432,7 +432,7 @@
const result = getGenericResult(events);
// Get the collection
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
// What to expect
expect(result.success).to.be.true;
@@ -452,7 +452,7 @@
const result = getGenericResult(events);
// Get the collection
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
// What to expect
expect(result.success).to.be.true;
@@ -490,7 +490,7 @@
const result = getGenericResult(events);
// Get the collection
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
// What to expect
expect(result.success).to.be.true;
@@ -1057,7 +1057,7 @@
const result = getGenericResult(events);
// Get the collection
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
// What to expect
// tslint:disable-next-line:no-unused-expression
@@ -1105,7 +1105,7 @@
expect(result.success).to.be.true;
// Get the collection
- const collection = (await api.query.common.collectionById(collectionId)).unwrap();
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.mintMode.toHuman()).to.be.equal(enabled);
});
@@ -1137,15 +1137,13 @@
});
}
-export async function isAllowlisted(collectionId: number, address: string | CrossAccountId) {
- return await usingApi(async (api) => {
- return (await api.query.common.allowlist(collectionId, normalizeAccountId(address))).toJSON();
- });
+export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId) {
+ return (await api.rpc.nft.allowed(collectionId, normalizeAccountId(address))).toJSON();
}
export async function addToAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId | CrossAccountId) {
await usingApi(async (api) => {
- expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.false;
+ expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.false;
// Run the transaction
const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(address));
@@ -1153,14 +1151,14 @@
const result = getGenericResult(events);
expect(result.success).to.be.true;
- expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.true;
+ expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;
});
}
export async function addToAllowListAgainExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {
await usingApi(async (api) => {
- expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.true;
+ expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;
// Run the transaction
const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(address));
@@ -1168,7 +1166,7 @@
const result = getGenericResult(events);
expect(result.success).to.be.true;
- expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.true;
+ expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;
});
}
@@ -1214,16 +1212,16 @@
export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)
: Promise<NftDataStructsCollection | null> => {
- return (await api.query.common.collectionById(collectionId)).unwrapOr(null);
+ return (await api.rpc.nft.collectionById(collectionId)).unwrapOr(null);
};
export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {
// set global object - collectionsCount
- return (await api.query.common.createdCollectionCount()).toNumber();
+ return (await api.rpc.nft.collectionStats()).created.toNumber();
};
export async function queryCollectionExpectSuccess(api: ApiPromise, collectionId: number): Promise<NftDataStructsCollection> {
- return (await api.query.common.collectionById(collectionId)).unwrap();
+ return (await api.rpc.nft.collectionById(collectionId)).unwrap();
}
export async function waitNewBlocks(blocksCount = 1): Promise<void> {