difftreelog
Merge pull request #277 from UniqueNetwork/feature/create-collection-ex
in: master
Add createCollectionEx call
18 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -14,7 +14,10 @@
use up_data_structs::{
COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo,
- TokenId, Weight, WithdrawReasons, CollectionStats, CustomDataLimit,
+ TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,
+ NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,
+ CustomDataLimit, CreateCollectionData, SponsorshipState,
};
pub use pallet::*;
use sp_core::H160;
@@ -285,6 +288,10 @@
TokenVariableDataLimitExceeded,
/// Exceeded max admin count
CollectionAdminCountExceeded,
+ /// Collection limit bounds per collection exceeded
+ CollectionLimitBoundsExceeded,
+ /// Tried to enable permissions which are only permitted to be disabled
+ OwnerPermissionsCantBeReverted,
/// Collection settings not allowing items transferring
TransferNotAllowed,
@@ -395,7 +402,10 @@
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
{
ensure!(
data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,
@@ -418,6 +428,29 @@
// =========
+ let collection = Collection {
+ owner: owner.clone(),
+ name: data.name,
+ mode: data.mode.clone(),
+ mint_mode: false,
+ access: data.access.unwrap_or_default(),
+ description: data.description,
+ token_prefix: data.token_prefix,
+ offchain_schema: data.offchain_schema,
+ schema_version: data.schema_version.unwrap_or_default(),
+ sponsorship: data
+ .pending_sponsor
+ .map(SponsorshipState::Unconfirmed)
+ .unwrap_or_default(),
+ variable_on_chain_schema: data.variable_on_chain_schema,
+ const_on_chain_schema: data.const_on_chain_schema,
+ limits: data
+ .limits
+ .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))
+ .unwrap_or_else(|| Ok(CollectionLimits::default()))?,
+ meta_update_permission: data.meta_update_permission.unwrap_or_default(),
+ };
+
// Take a (non-refundable) deposit of collection creation
{
let mut imbalance =
@@ -429,7 +462,7 @@
),
);
<T as Config>::Currency::settle(
- &data.owner,
+ &owner,
imbalance,
WithdrawReasons::TRANSFER,
ExistenceRequirement::KeepAlive,
@@ -438,12 +471,8 @@
}
<CreatedCollectionCount<T>>::put(created_count);
- <Pallet<T>>::deposit_event(Event::CollectionCreated(
- id,
- data.mode.id(),
- data.owner.clone(),
- ));
- <CollectionById<T>>::insert(id, data);
+ <Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));
+ <CollectionById<T>>::insert(id, collection);
Ok(id)
}
@@ -527,6 +556,61 @@
Ok(())
}
+
+ pub fn clamp_limits(
+ mode: CollectionMode,
+ old_limit: &CollectionLimits,
+ mut new_limit: CollectionLimits,
+ ) -> Result<CollectionLimits, DispatchError> {
+ macro_rules! limit_default {
+ ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
+ $(
+ if let Some($new) = $new.$field {
+ let $old = $old.$field($($arg)?);
+ let _ = $new;
+ let _ = $old;
+ $check
+ } else {
+ $new.$field = $old.$field
+ }
+ )*
+ }};
+ }
+
+ limit_default!(old_limit, new_limit,
+ account_token_ownership_limit => ensure!(
+ new_limit <= MAX_TOKEN_OWNERSHIP,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ sponsor_transfer_timeout(match mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ }) => ensure!(
+ new_limit <= MAX_SPONSOR_TIMEOUT,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ sponsored_data_size => ensure!(
+ new_limit <= CUSTOM_DATA_LIMIT,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ token_limit => ensure!(
+ old_limit >= new_limit && new_limit > 0,
+ <Error<T>>::CollectionTokenLimitExceeded
+ ),
+ owner_can_transfer => ensure!(
+ old_limit || !new_limit,
+ <Error<T>>::OwnerPermissionsCantBeReverted,
+ ),
+ owner_can_destroy => ensure!(
+ old_limit || !new_limit,
+ <Error<T>>::OwnerPermissionsCantBeReverted,
+ ),
+ sponsored_data_rate_limit => {},
+ transfers_enabled => {},
+ );
+ Ok(new_limit)
+ }
}
#[macro_export]
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -2,7 +2,7 @@
use core::ops::Deref;
use frame_support::{ensure};
-use up_data_structs::{AccessMode, Collection, CollectionId, TokenId};
+use up_data_structs::{AccessMode, Collection, CollectionId, TokenId, CreateCollectionData};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
};
@@ -100,8 +100,11 @@
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: FungibleHandle<T>,
pallets/nonfungible/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23use erc::ERC721Events;4use frame_support::{BoundedVec, ensure};5use up_data_structs::{AccessMode, Collection, CollectionId, CustomDataLimit, TokenId};6use pallet_common::{7 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,8};9use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};10use sp_core::H160;11use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};12use sp_std::{vec::Vec, vec};13use core::ops::Deref;14use sp_std::collections::btree_map::BTreeMap;15use codec::{Encode, Decode, MaxEncodedLen};16use scale_info::TypeInfo;1718pub use pallet::*;19#[cfg(feature = "runtime-benchmarks")]20pub mod benchmarking;21pub mod common;22pub mod erc;23pub mod weights;2425pub struct CreateItemData<T: Config> {26 pub const_data: BoundedVec<u8, CustomDataLimit>,27 pub variable_data: BoundedVec<u8, CustomDataLimit>,28 pub owner: T::CrossAccountId,29}30pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;3132#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]33pub struct ItemData<CrossAccountId> {34 pub const_data: BoundedVec<u8, CustomDataLimit>,35 pub variable_data: BoundedVec<u8, CustomDataLimit>,36 pub owner: CrossAccountId,37}3839#[frame_support::pallet]40pub mod pallet {41 use super::*;42 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};43 use up_data_structs::{CollectionId, TokenId};44 use super::weights::WeightInfo;4546 #[pallet::error]47 pub enum Error<T> {48 /// Not Nonfungible item data used to mint in Nonfungible collection.49 NotNonfungibleDataUsedToMintFungibleCollectionToken,50 /// Used amount > 1 with NFT51 NonfungibleItemsHaveNoAmount,52 }5354 #[pallet::config]55 pub trait Config: frame_system::Config + pallet_common::Config {56 type WeightInfo: WeightInfo;57 }5859 #[pallet::pallet]60 #[pallet::generate_store(pub(super) trait Store)]61 pub struct Pallet<T>(_);6263 #[pallet::storage]64 pub type TokensMinted<T: Config> =65 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;66 #[pallet::storage]67 pub type TokensBurnt<T: Config> =68 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;6970 #[pallet::storage]71 pub type TokenData<T: Config> = StorageNMap<72 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),73 Value = ItemData<T::CrossAccountId>,74 QueryKind = OptionQuery,75 >;7677 /// Used to enumerate tokens owned by account78 #[pallet::storage]79 pub type Owned<T: Config> = StorageNMap<80 Key = (81 Key<Twox64Concat, CollectionId>,82 Key<Blake2_128Concat, T::CrossAccountId>,83 Key<Twox64Concat, TokenId>,84 ),85 Value = bool,86 QueryKind = ValueQuery,87 >;8889 #[pallet::storage]90 pub type AccountBalance<T: Config> = StorageNMap<91 Key = (92 Key<Twox64Concat, CollectionId>,93 Key<Blake2_128Concat, T::CrossAccountId>,94 ),95 Value = u32,96 QueryKind = ValueQuery,97 >;9899 #[pallet::storage]100 pub type Allowance<T: Config> = StorageNMap<101 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),102 Value = T::CrossAccountId,103 QueryKind = OptionQuery,104 >;105}106107pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);108impl<T: Config> NonfungibleHandle<T> {109 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {110 Self(inner)111 }112 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {113 self.0114 }115}116impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {117 fn recorder(&self) -> &SubstrateRecorder<T> {118 self.0.recorder()119 }120 fn into_recorder(self) -> SubstrateRecorder<T> {121 self.0.into_recorder()122 }123}124impl<T: Config> Deref for NonfungibleHandle<T> {125 type Target = pallet_common::CollectionHandle<T>;126127 fn deref(&self) -> &Self::Target {128 &self.0129 }130}131132impl<T: Config> Pallet<T> {133 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {134 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)135 }136 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {137 <TokenData<T>>::contains_key((collection.id, token))138 }139}140141// unchecked calls skips any permission checks142impl<T: Config> Pallet<T> {143 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {144 <PalletCommon<T>>::init_collection(data)145 }146 pub fn destroy_collection(147 collection: NonfungibleHandle<T>,148 sender: &T::CrossAccountId,149 ) -> DispatchResult {150 let id = collection.id;151152 // =========153154 PalletCommon::destroy_collection(collection.0, sender)?;155156 <TokenData<T>>::remove_prefix((id,), None);157 <Owned<T>>::remove_prefix((id,), None);158 <TokensMinted<T>>::remove(id);159 <TokensBurnt<T>>::remove(id);160 <Allowance<T>>::remove_prefix((id,), None);161 <AccountBalance<T>>::remove_prefix((id,), None);162 Ok(())163 }164165 pub fn burn(166 collection: &NonfungibleHandle<T>,167 sender: &T::CrossAccountId,168 token: TokenId,169 ) -> DispatchResult {170 let token_data =171 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;172 ensure!(173 &token_data.owner == sender174 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),175 <CommonError<T>>::NoPermission176 );177178 if collection.access == AccessMode::AllowList {179 collection.check_allowlist(sender)?;180 }181182 let burnt = <TokensBurnt<T>>::get(collection.id)183 .checked_add(1)184 .ok_or(ArithmeticError::Overflow)?;185186 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))187 .checked_sub(1)188 .ok_or(ArithmeticError::Overflow)?;189190 if balance == 0 {191 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));192 } else {193 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);194 }195 // =========196197 <Owned<T>>::remove((collection.id, &token_data.owner, token));198 <TokensBurnt<T>>::insert(collection.id, burnt);199 <TokenData<T>>::remove((collection.id, token));200 let old_spender = <Allowance<T>>::take((collection.id, token));201202 if let Some(old_spender) = old_spender {203 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(204 collection.id,205 token,206 sender.clone(),207 old_spender,208 0,209 ));210 }211212 collection.log_mirrored(ERC721Events::Transfer {213 from: *token_data.owner.as_eth(),214 to: H160::default(),215 token_id: token.into(),216 });217 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(218 collection.id,219 token,220 token_data.owner,221 1,222 ));223 Ok(())224 }225226 pub fn transfer(227 collection: &NonfungibleHandle<T>,228 from: &T::CrossAccountId,229 to: &T::CrossAccountId,230 token: TokenId,231 ) -> DispatchResult {232 ensure!(233 collection.limits.transfers_enabled(),234 <CommonError<T>>::TransferNotAllowed235 );236237 let token_data =238 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;239 ensure!(240 &token_data.owner == from241 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),242 <CommonError<T>>::NoPermission243 );244245 if collection.access == AccessMode::AllowList {246 collection.check_allowlist(from)?;247 collection.check_allowlist(to)?;248 }249 <PalletCommon<T>>::ensure_correct_receiver(to)?;250251 let balance_from = <AccountBalance<T>>::get((collection.id, from))252 .checked_sub(1)253 .ok_or(<CommonError<T>>::TokenValueTooLow)?;254 let balance_to = if from != to {255 let balance_to = <AccountBalance<T>>::get((collection.id, to))256 .checked_add(1)257 .ok_or(ArithmeticError::Overflow)?;258259 ensure!(260 balance_to < collection.limits.account_token_ownership_limit(),261 <CommonError<T>>::AccountTokenLimitExceeded,262 );263264 Some(balance_to)265 } else {266 None267 };268269 // =========270271 <TokenData<T>>::insert(272 (collection.id, token),273 ItemData {274 owner: to.clone(),275 ..token_data276 },277 );278279 if let Some(balance_to) = balance_to {280 // from != to281 if balance_from == 0 {282 <AccountBalance<T>>::remove((collection.id, from));283 } else {284 <AccountBalance<T>>::insert((collection.id, from), balance_from);285 }286 <AccountBalance<T>>::insert((collection.id, to), balance_to);287 <Owned<T>>::remove((collection.id, from, token));288 <Owned<T>>::insert((collection.id, to, token), true);289 }290 Self::set_allowance_unchecked(collection, from, token, None, true);291292 collection.log_mirrored(ERC721Events::Transfer {293 from: *from.as_eth(),294 to: *to.as_eth(),295 token_id: token.into(),296 });297 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(298 collection.id,299 token,300 from.clone(),301 to.clone(),302 1,303 ));304 Ok(())305 }306307 pub fn create_multiple_items(308 collection: &NonfungibleHandle<T>,309 sender: &T::CrossAccountId,310 data: Vec<CreateItemData<T>>,311 ) -> DispatchResult {312 if !collection.is_owner_or_admin(sender) {313 ensure!(314 collection.mint_mode,315 <CommonError<T>>::PublicMintingNotAllowed316 );317 collection.check_allowlist(sender)?;318319 for item in data.iter() {320 collection.check_allowlist(&item.owner)?;321 }322 }323324 for data in data.iter() {325 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;326 }327328 let first_token = <TokensMinted<T>>::get(collection.id);329 let tokens_minted = first_token330 .checked_add(data.len() as u32)331 .ok_or(ArithmeticError::Overflow)?;332 ensure!(333 tokens_minted <= collection.limits.token_limit(),334 <CommonError<T>>::CollectionTokenLimitExceeded335 );336337 let mut balances = BTreeMap::new();338 for data in &data {339 let balance = balances340 .entry(&data.owner)341 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));342 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;343344 ensure!(345 *balance <= collection.limits.account_token_ownership_limit(),346 <CommonError<T>>::AccountTokenLimitExceeded,347 );348 }349350 // =========351352 <TokensMinted<T>>::insert(collection.id, tokens_minted);353 for (account, balance) in balances {354 <AccountBalance<T>>::insert((collection.id, account), balance);355 }356 for (i, data) in data.into_iter().enumerate() {357 let token = first_token + i as u32 + 1;358359 <TokenData<T>>::insert(360 (collection.id, token),361 ItemData {362 const_data: data.const_data,363 variable_data: data.variable_data,364 owner: data.owner.clone(),365 },366 );367 <Owned<T>>::insert((collection.id, &data.owner, token), true);368369 collection.log_mirrored(ERC721Events::Transfer {370 from: H160::default(),371 to: *data.owner.as_eth(),372 token_id: token.into(),373 });374 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(375 collection.id,376 TokenId(token),377 data.owner.clone(),378 1,379 ));380 }381 Ok(())382 }383384 pub fn set_allowance_unchecked(385 collection: &NonfungibleHandle<T>,386 sender: &T::CrossAccountId,387 token: TokenId,388 spender: Option<&T::CrossAccountId>,389 assume_implicit_eth: bool,390 ) {391 if let Some(spender) = spender {392 let old_spender = <Allowance<T>>::get((collection.id, token));393 <Allowance<T>>::insert((collection.id, token), spender);394 // In ERC721 there is only one possible approved user of token, so we set395 // approved user to spender396 collection.log_mirrored(ERC721Events::Approval {397 owner: *sender.as_eth(),398 approved: *spender.as_eth(),399 token_id: token.into(),400 });401 // In Unique chain, any token can have any amount of approved users, so we need to402 // set allowance of old owner to 0, and allowance of new owner to 1403 if old_spender.as_ref() != Some(spender) {404 if let Some(old_owner) = old_spender {405 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(406 collection.id,407 token,408 sender.clone(),409 old_owner,410 0,411 ));412 }413 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(414 collection.id,415 token,416 sender.clone(),417 spender.clone(),418 1,419 ));420 }421 } else {422 let old_spender = <Allowance<T>>::take((collection.id, token));423 if !assume_implicit_eth {424 // In ERC721 there is only one possible approved user of token, so we set425 // approved user to zero address426 collection.log_mirrored(ERC721Events::Approval {427 owner: *sender.as_eth(),428 approved: H160::default(),429 token_id: token.into(),430 });431 }432 // In Unique chain, any token can have any amount of approved users, so we need to433 // set allowance of old owner to 0434 if let Some(old_spender) = old_spender {435 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(436 collection.id,437 token,438 sender.clone(),439 old_spender,440 0,441 ));442 }443 }444 }445446 pub fn set_allowance(447 collection: &NonfungibleHandle<T>,448 sender: &T::CrossAccountId,449 token: TokenId,450 spender: Option<&T::CrossAccountId>,451 ) -> DispatchResult {452 if collection.access == AccessMode::AllowList {453 collection.check_allowlist(sender)?;454 if let Some(spender) = spender {455 collection.check_allowlist(spender)?;456 }457 }458459 if let Some(spender) = spender {460 <PalletCommon<T>>::ensure_correct_receiver(spender)?;461 }462 let token_data =463 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;464 if &token_data.owner != sender {465 ensure!(466 collection.ignores_owned_amount(sender),467 <CommonError<T>>::CantApproveMoreThanOwned468 );469 }470471 // =========472473 Self::set_allowance_unchecked(collection, sender, token, spender, false);474 Ok(())475 }476477 pub fn transfer_from(478 collection: &NonfungibleHandle<T>,479 spender: &T::CrossAccountId,480 from: &T::CrossAccountId,481 to: &T::CrossAccountId,482 token: TokenId,483 ) -> DispatchResult {484 if spender.conv_eq(from) {485 return Self::transfer(collection, from, to, token);486 }487 if collection.access == AccessMode::AllowList {488 // `from`, `to` checked in [`transfer`]489 collection.check_allowlist(spender)?;490 }491492 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {493 ensure!(494 collection.ignores_allowance(spender),495 <CommonError<T>>::TokenValueNotEnough496 );497 }498499 // =========500501 Self::transfer(collection, from, to, token)?;502 // Allowance is reset in [`transfer`]503 Ok(())504 }505506 pub fn burn_from(507 collection: &NonfungibleHandle<T>,508 spender: &T::CrossAccountId,509 from: &T::CrossAccountId,510 token: TokenId,511 ) -> DispatchResult {512 if spender.conv_eq(from) {513 return Self::burn(collection, from, token);514 }515 if collection.access == AccessMode::AllowList {516 // `from` checked in [`burn`]517 collection.check_allowlist(spender)?;518 }519520 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {521 ensure!(522 collection.ignores_allowance(spender),523 <CommonError<T>>::TokenValueNotEnough524 );525 }526527 // =========528529 Self::burn(collection, from, token)530 }531532 pub fn set_variable_metadata(533 collection: &NonfungibleHandle<T>,534 sender: &T::CrossAccountId,535 token: TokenId,536 data: BoundedVec<u8, CustomDataLimit>,537 ) -> DispatchResult {538 let token_data =539 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;540 collection.check_can_update_meta(sender, &token_data.owner)?;541542 // =========543544 <TokenData<T>>::insert(545 (collection.id, token),546 ItemData {547 variable_data: data,548 ..token_data549 },550 );551 Ok(())552 }553554 /// Delegated to `create_multiple_items`555 pub fn create_item(556 collection: &NonfungibleHandle<T>,557 sender: &T::CrossAccountId,558 data: CreateItemData<T>,559 ) -> DispatchResult {560 Self::create_multiple_items(collection, sender, vec![data])561 }562}1#![cfg_attr(not(feature = "std"), no_std)]23use erc::ERC721Events;4use frame_support::{BoundedVec, ensure};5use up_data_structs::{6 AccessMode, Collection, CollectionId, CustomDataLimit, TokenId, CreateCollectionData,7};8use pallet_common::{9 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,10};11use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};12use sp_core::H160;13use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};14use sp_std::{vec::Vec, vec};15use core::ops::Deref;16use sp_std::collections::btree_map::BTreeMap;17use codec::{Encode, Decode, MaxEncodedLen};18use scale_info::TypeInfo;1920pub use pallet::*;21#[cfg(feature = "runtime-benchmarks")]22pub mod benchmarking;23pub mod common;24pub mod erc;25pub mod weights;2627pub struct CreateItemData<T: Config> {28 pub const_data: BoundedVec<u8, CustomDataLimit>,29 pub variable_data: BoundedVec<u8, CustomDataLimit>,30 pub owner: T::CrossAccountId,31}32pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;3334#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]35pub struct ItemData<CrossAccountId> {36 pub const_data: BoundedVec<u8, CustomDataLimit>,37 pub variable_data: BoundedVec<u8, CustomDataLimit>,38 pub owner: CrossAccountId,39}4041#[frame_support::pallet]42pub mod pallet {43 use super::*;44 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};45 use up_data_structs::{CollectionId, TokenId};46 use super::weights::WeightInfo;4748 #[pallet::error]49 pub enum Error<T> {50 /// Not Nonfungible item data used to mint in Nonfungible collection.51 NotNonfungibleDataUsedToMintFungibleCollectionToken,52 /// Used amount > 1 with NFT53 NonfungibleItemsHaveNoAmount,54 }5556 #[pallet::config]57 pub trait Config: frame_system::Config + pallet_common::Config {58 type WeightInfo: WeightInfo;59 }6061 #[pallet::pallet]62 #[pallet::generate_store(pub(super) trait Store)]63 pub struct Pallet<T>(_);6465 #[pallet::storage]66 pub type TokensMinted<T: Config> =67 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;68 #[pallet::storage]69 pub type TokensBurnt<T: Config> =70 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;7172 #[pallet::storage]73 pub type TokenData<T: Config> = StorageNMap<74 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),75 Value = ItemData<T::CrossAccountId>,76 QueryKind = OptionQuery,77 >;7879 /// Used to enumerate tokens owned by account80 #[pallet::storage]81 pub type Owned<T: Config> = StorageNMap<82 Key = (83 Key<Twox64Concat, CollectionId>,84 Key<Blake2_128Concat, T::CrossAccountId>,85 Key<Twox64Concat, TokenId>,86 ),87 Value = bool,88 QueryKind = ValueQuery,89 >;9091 #[pallet::storage]92 pub type AccountBalance<T: Config> = StorageNMap<93 Key = (94 Key<Twox64Concat, CollectionId>,95 Key<Blake2_128Concat, T::CrossAccountId>,96 ),97 Value = u32,98 QueryKind = ValueQuery,99 >;100101 #[pallet::storage]102 pub type Allowance<T: Config> = StorageNMap<103 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),104 Value = T::CrossAccountId,105 QueryKind = OptionQuery,106 >;107}108109pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);110impl<T: Config> NonfungibleHandle<T> {111 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {112 Self(inner)113 }114 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {115 self.0116 }117}118impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {119 fn recorder(&self) -> &SubstrateRecorder<T> {120 self.0.recorder()121 }122 fn into_recorder(self) -> SubstrateRecorder<T> {123 self.0.into_recorder()124 }125}126impl<T: Config> Deref for NonfungibleHandle<T> {127 type Target = pallet_common::CollectionHandle<T>;128129 fn deref(&self) -> &Self::Target {130 &self.0131 }132}133134impl<T: Config> Pallet<T> {135 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {136 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)137 }138 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {139 <TokenData<T>>::contains_key((collection.id, token))140 }141}142143// unchecked calls skips any permission checks144impl<T: Config> Pallet<T> {145 pub fn init_collection(146 owner: T::AccountId,147 data: CreateCollectionData<T::AccountId>,148 ) -> Result<CollectionId, DispatchError> {149 <PalletCommon<T>>::init_collection(owner, data)150 }151 pub fn destroy_collection(152 collection: NonfungibleHandle<T>,153 sender: &T::CrossAccountId,154 ) -> DispatchResult {155 let id = collection.id;156157 // =========158159 PalletCommon::destroy_collection(collection.0, sender)?;160161 <TokenData<T>>::remove_prefix((id,), None);162 <Owned<T>>::remove_prefix((id,), None);163 <TokensMinted<T>>::remove(id);164 <TokensBurnt<T>>::remove(id);165 <Allowance<T>>::remove_prefix((id,), None);166 <AccountBalance<T>>::remove_prefix((id,), None);167 Ok(())168 }169170 pub fn burn(171 collection: &NonfungibleHandle<T>,172 sender: &T::CrossAccountId,173 token: TokenId,174 ) -> DispatchResult {175 let token_data =176 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;177 ensure!(178 &token_data.owner == sender179 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),180 <CommonError<T>>::NoPermission181 );182183 if collection.access == AccessMode::AllowList {184 collection.check_allowlist(sender)?;185 }186187 let burnt = <TokensBurnt<T>>::get(collection.id)188 .checked_add(1)189 .ok_or(ArithmeticError::Overflow)?;190191 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))192 .checked_sub(1)193 .ok_or(ArithmeticError::Overflow)?;194195 if balance == 0 {196 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));197 } else {198 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);199 }200 // =========201202 <Owned<T>>::remove((collection.id, &token_data.owner, token));203 <TokensBurnt<T>>::insert(collection.id, burnt);204 <TokenData<T>>::remove((collection.id, token));205 let old_spender = <Allowance<T>>::take((collection.id, token));206207 if let Some(old_spender) = old_spender {208 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(209 collection.id,210 token,211 sender.clone(),212 old_spender,213 0,214 ));215 }216217 collection.log_mirrored(ERC721Events::Transfer {218 from: *token_data.owner.as_eth(),219 to: H160::default(),220 token_id: token.into(),221 });222 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(223 collection.id,224 token,225 token_data.owner,226 1,227 ));228 Ok(())229 }230231 pub fn transfer(232 collection: &NonfungibleHandle<T>,233 from: &T::CrossAccountId,234 to: &T::CrossAccountId,235 token: TokenId,236 ) -> DispatchResult {237 ensure!(238 collection.limits.transfers_enabled(),239 <CommonError<T>>::TransferNotAllowed240 );241242 let token_data =243 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;244 ensure!(245 &token_data.owner == from246 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),247 <CommonError<T>>::NoPermission248 );249250 if collection.access == AccessMode::AllowList {251 collection.check_allowlist(from)?;252 collection.check_allowlist(to)?;253 }254 <PalletCommon<T>>::ensure_correct_receiver(to)?;255256 let balance_from = <AccountBalance<T>>::get((collection.id, from))257 .checked_sub(1)258 .ok_or(<CommonError<T>>::TokenValueTooLow)?;259 let balance_to = if from != to {260 let balance_to = <AccountBalance<T>>::get((collection.id, to))261 .checked_add(1)262 .ok_or(ArithmeticError::Overflow)?;263264 ensure!(265 balance_to < collection.limits.account_token_ownership_limit(),266 <CommonError<T>>::AccountTokenLimitExceeded,267 );268269 Some(balance_to)270 } else {271 None272 };273274 // =========275276 <TokenData<T>>::insert(277 (collection.id, token),278 ItemData {279 owner: to.clone(),280 ..token_data281 },282 );283284 if let Some(balance_to) = balance_to {285 // from != to286 if balance_from == 0 {287 <AccountBalance<T>>::remove((collection.id, from));288 } else {289 <AccountBalance<T>>::insert((collection.id, from), balance_from);290 }291 <AccountBalance<T>>::insert((collection.id, to), balance_to);292 <Owned<T>>::remove((collection.id, from, token));293 <Owned<T>>::insert((collection.id, to, token), true);294 }295 Self::set_allowance_unchecked(collection, from, token, None, true);296297 collection.log_mirrored(ERC721Events::Transfer {298 from: *from.as_eth(),299 to: *to.as_eth(),300 token_id: token.into(),301 });302 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(303 collection.id,304 token,305 from.clone(),306 to.clone(),307 1,308 ));309 Ok(())310 }311312 pub fn create_multiple_items(313 collection: &NonfungibleHandle<T>,314 sender: &T::CrossAccountId,315 data: Vec<CreateItemData<T>>,316 ) -> DispatchResult {317 if !collection.is_owner_or_admin(sender) {318 ensure!(319 collection.mint_mode,320 <CommonError<T>>::PublicMintingNotAllowed321 );322 collection.check_allowlist(sender)?;323324 for item in data.iter() {325 collection.check_allowlist(&item.owner)?;326 }327 }328329 for data in data.iter() {330 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;331 }332333 let first_token = <TokensMinted<T>>::get(collection.id);334 let tokens_minted = first_token335 .checked_add(data.len() as u32)336 .ok_or(ArithmeticError::Overflow)?;337 ensure!(338 tokens_minted <= collection.limits.token_limit(),339 <CommonError<T>>::CollectionTokenLimitExceeded340 );341342 let mut balances = BTreeMap::new();343 for data in &data {344 let balance = balances345 .entry(&data.owner)346 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));347 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;348349 ensure!(350 *balance <= collection.limits.account_token_ownership_limit(),351 <CommonError<T>>::AccountTokenLimitExceeded,352 );353 }354355 // =========356357 <TokensMinted<T>>::insert(collection.id, tokens_minted);358 for (account, balance) in balances {359 <AccountBalance<T>>::insert((collection.id, account), balance);360 }361 for (i, data) in data.into_iter().enumerate() {362 let token = first_token + i as u32 + 1;363364 <TokenData<T>>::insert(365 (collection.id, token),366 ItemData {367 const_data: data.const_data,368 variable_data: data.variable_data,369 owner: data.owner.clone(),370 },371 );372 <Owned<T>>::insert((collection.id, &data.owner, token), true);373374 collection.log_mirrored(ERC721Events::Transfer {375 from: H160::default(),376 to: *data.owner.as_eth(),377 token_id: token.into(),378 });379 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(380 collection.id,381 TokenId(token),382 data.owner.clone(),383 1,384 ));385 }386 Ok(())387 }388389 pub fn set_allowance_unchecked(390 collection: &NonfungibleHandle<T>,391 sender: &T::CrossAccountId,392 token: TokenId,393 spender: Option<&T::CrossAccountId>,394 assume_implicit_eth: bool,395 ) {396 if let Some(spender) = spender {397 let old_spender = <Allowance<T>>::get((collection.id, token));398 <Allowance<T>>::insert((collection.id, token), spender);399 // In ERC721 there is only one possible approved user of token, so we set400 // approved user to spender401 collection.log_mirrored(ERC721Events::Approval {402 owner: *sender.as_eth(),403 approved: *spender.as_eth(),404 token_id: token.into(),405 });406 // In Unique chain, any token can have any amount of approved users, so we need to407 // set allowance of old owner to 0, and allowance of new owner to 1408 if old_spender.as_ref() != Some(spender) {409 if let Some(old_owner) = old_spender {410 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(411 collection.id,412 token,413 sender.clone(),414 old_owner,415 0,416 ));417 }418 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(419 collection.id,420 token,421 sender.clone(),422 spender.clone(),423 1,424 ));425 }426 } else {427 let old_spender = <Allowance<T>>::take((collection.id, token));428 if !assume_implicit_eth {429 // In ERC721 there is only one possible approved user of token, so we set430 // approved user to zero address431 collection.log_mirrored(ERC721Events::Approval {432 owner: *sender.as_eth(),433 approved: H160::default(),434 token_id: token.into(),435 });436 }437 // In Unique chain, any token can have any amount of approved users, so we need to438 // set allowance of old owner to 0439 if let Some(old_spender) = old_spender {440 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(441 collection.id,442 token,443 sender.clone(),444 old_spender,445 0,446 ));447 }448 }449 }450451 pub fn set_allowance(452 collection: &NonfungibleHandle<T>,453 sender: &T::CrossAccountId,454 token: TokenId,455 spender: Option<&T::CrossAccountId>,456 ) -> DispatchResult {457 if collection.access == AccessMode::AllowList {458 collection.check_allowlist(sender)?;459 if let Some(spender) = spender {460 collection.check_allowlist(spender)?;461 }462 }463464 if let Some(spender) = spender {465 <PalletCommon<T>>::ensure_correct_receiver(spender)?;466 }467 let token_data =468 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;469 if &token_data.owner != sender {470 ensure!(471 collection.ignores_owned_amount(sender),472 <CommonError<T>>::CantApproveMoreThanOwned473 );474 }475476 // =========477478 Self::set_allowance_unchecked(collection, sender, token, spender, false);479 Ok(())480 }481482 pub fn transfer_from(483 collection: &NonfungibleHandle<T>,484 spender: &T::CrossAccountId,485 from: &T::CrossAccountId,486 to: &T::CrossAccountId,487 token: TokenId,488 ) -> DispatchResult {489 if spender.conv_eq(from) {490 return Self::transfer(collection, from, to, token);491 }492 if collection.access == AccessMode::AllowList {493 // `from`, `to` checked in [`transfer`]494 collection.check_allowlist(spender)?;495 }496497 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {498 ensure!(499 collection.ignores_allowance(spender),500 <CommonError<T>>::TokenValueNotEnough501 );502 }503504 // =========505506 Self::transfer(collection, from, to, token)?;507 // Allowance is reset in [`transfer`]508 Ok(())509 }510511 pub fn burn_from(512 collection: &NonfungibleHandle<T>,513 spender: &T::CrossAccountId,514 from: &T::CrossAccountId,515 token: TokenId,516 ) -> DispatchResult {517 if spender.conv_eq(from) {518 return Self::burn(collection, from, token);519 }520 if collection.access == AccessMode::AllowList {521 // `from` checked in [`burn`]522 collection.check_allowlist(spender)?;523 }524525 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {526 ensure!(527 collection.ignores_allowance(spender),528 <CommonError<T>>::TokenValueNotEnough529 );530 }531532 // =========533534 Self::burn(collection, from, token)535 }536537 pub fn set_variable_metadata(538 collection: &NonfungibleHandle<T>,539 sender: &T::CrossAccountId,540 token: TokenId,541 data: BoundedVec<u8, CustomDataLimit>,542 ) -> DispatchResult {543 let token_data =544 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;545 collection.check_can_update_meta(sender, &token_data.owner)?;546547 // =========548549 <TokenData<T>>::insert(550 (collection.id, token),551 ItemData {552 variable_data: data,553 ..token_data554 },555 );556 Ok(())557 }558559 /// Delegated to `create_multiple_items`560 pub fn create_item(561 collection: &NonfungibleHandle<T>,562 sender: &T::CrossAccountId,563 data: CreateItemData<T>,564 ) -> DispatchResult {565 Self::create_multiple_items(collection, sender, vec![data])566 }567}pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -3,6 +3,7 @@
use frame_support::{ensure, BoundedVec};
use up_data_structs::{
AccessMode, Collection, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
+ CreateCollectionData,
};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
@@ -155,8 +156,11 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: RefungibleHandle<T>,
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -36,13 +36,11 @@
use frame_system::{self as system, ensure_signed};
use sp_runtime::{sp_std::prelude::Vec};
use up_data_structs::{
- MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,
- VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- NFT_SPONSOR_TRANSFER_TIMEOUT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
+ MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,
+ OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
MAX_TOKEN_PREFIX_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,
CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
- CustomDataLimit,
+ CreateCollectionData, CustomDataLimit,
};
use pallet_common::{
account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
@@ -84,10 +82,6 @@
ConfirmUnsetSponsorFail,
/// Length of items properties must be greater than 0.
EmptyArgument,
- /// Collection limit bounds per collection exceeded
- CollectionLimitBoundsExceeded,
- /// Tried to enable permissions which are only permitted to be disabled
- OwnerPermissionsCantBeReverted,
}
}
@@ -321,42 +315,39 @@
// returns collection ID
#[weight = <SelfWeightOf<T>>::create_collection()]
#[transactional]
+ #[deprecated]
pub fn create_collection(origin,
collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
- mode: CollectionMode) -> DispatchResult {
-
- // Anyone can create a collection
- let who = ensure_signed(origin)?;
-
- // Create new collection
- let new_collection = Collection {
- owner: who,
+ mode: CollectionMode) -> DispatchResult {
+ let data: CreateCollectionData<T::AccountId> = CreateCollectionData {
name: collection_name,
- mode: mode.clone(),
- mint_mode: false,
- access: AccessMode::Normal,
description: collection_description,
token_prefix,
- offchain_schema: BoundedVec::default(),
- schema_version: SchemaVersion::ImageURL,
- sponsorship: SponsorshipState::Disabled,
- variable_on_chain_schema: BoundedVec::default(),
- const_on_chain_schema: BoundedVec::default(),
- limits: Default::default(),
- meta_update_permission: Default::default(),
+ mode,
+ ..Default::default()
};
+ Self::create_collection_ex(origin, data)
+ }
+
+ /// This method creates a collection
+ ///
+ /// Prefer it to deprecated [`created_collection`] method
+ #[weight = <SelfWeightOf<T>>::create_collection()]
+ #[transactional]
+ pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
+ let owner = ensure_signed(origin)?;
- let _id = match mode {
- CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},
+ let _id = match data.mode {
+ CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},
CollectionMode::Fungible(decimal_points) => {
// check params
ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);
- <PalletFungible<T>>::init_collection(new_collection)?
+ <PalletFungible<T>>::init_collection(owner, data)?
}
CollectionMode::ReFungible => {
- <PalletRefungible<T>>::init_collection(new_collection)?
+ <PalletRefungible<T>>::init_collection(owner, data)?
}
};
@@ -1093,61 +1084,12 @@
collection_id: CollectionId,
new_limit: CollectionLimits,
) -> DispatchResult {
- let mut new_limit = new_limit;
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
target_collection.check_is_owner(&sender)?;
let old_limit = &target_collection.limits;
- macro_rules! limit_default {
- ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
- $(
- if let Some($new) = $new.$field {
- let $old = $old.$field($($arg)?);
- let _ = $new;
- let _ = $old;
- $check
- } else {
- $new.$field = $old.$field
- }
- )*
- }};
- }
-
- limit_default!(old_limit, new_limit,
- account_token_ownership_limit => ensure!(
- new_limit <= MAX_TOKEN_OWNERSHIP,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- sponsor_transfer_timeout(match target_collection.mode {
- CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- }) => ensure!(
- new_limit <= MAX_SPONSOR_TIMEOUT,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- sponsored_data_size => ensure!(
- new_limit <= CUSTOM_DATA_LIMIT,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- token_limit => ensure!(
- old_limit >= new_limit && new_limit > 0,
- <CommonError<T>>::CollectionTokenLimitExceeded
- ),
- owner_can_transfer => ensure!(
- old_limit || !new_limit,
- <Error<T>>::OwnerPermissionsCantBeReverted,
- ),
- owner_can_destroy => ensure!(
- old_limit || !new_limit,
- <Error<T>>::OwnerPermissionsCantBeReverted,
- ),
- sponsored_data_rate_limit => {},
- transfers_enabled => {},
- );
-
- target_collection.limits = new_limit;
+ target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(
collection_id
pallets/unique/src/tests.rsdiffbeforeafterboth--- a/pallets/unique/src/tests.rs
+++ b/pallets/unique/src/tests.rs
@@ -5,7 +5,7 @@
use up_data_structs::{
COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
- TokenId,
+ TokenId, MAX_TOKEN_OWNERSHIP,
};
use frame_support::{assert_noop, assert_ok};
use sp_std::convert::TryInto;
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -263,6 +263,31 @@
pub meta_update_permission: MetaUpdatePermission,
}
+#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Debug, Derivative, MaxEncodedLen)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
+#[derivative(Default(bound = ""))]
+pub struct CreateCollectionData<AccountId> {
+ #[derivative(Default(value = "CollectionMode::NFT"))]
+ pub mode: CollectionMode,
+ pub access: Option<AccessMode>,
+ #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
+ #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
+ #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
+ #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,
+ pub schema_version: Option<SchemaVersion>,
+ pub pending_sponsor: Option<AccountId>,
+ pub limits: Option<CollectionLimits>,
+ #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,
+ #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,
+ pub meta_update_permission: Option<MetaUpdatePermission>,
+}
+
#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct NftItemType<AccountId> {
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -68,9 +68,10 @@
"testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
"testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
"testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",
+ "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
"polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --input src/interfaces/ --package .",
- "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint ws://localhost:9944 --output src/interfaces/ --package .",
- "polkadot-types": "yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
+ "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",
+ "polkadot-types": "yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
},
"author": "",
"license": "SEE LICENSE IN ../LICENSE",
tests/src/check-event/createCollectionEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/createCollectionEvent.test.ts
+++ b/tests/src/check-event/createCollectionEvent.test.ts
@@ -27,7 +27,7 @@
});
it('Check event from createCollection(): ', async () => {
await usingApi(async (api: ApiPromise) => {
- const tx = api.tx.unique.createCollection([0x31], [0x32], '0x33', 'NFT');
+ const tx = api.tx.unique.createCollectionEx({name: [0x31], description: [0x32], tokenPrefix: '0x33', mode: 'NFT'});
const events = await submitTransactionAsync(alice, tx);
const msg = JSON.stringify(uniqueEventMessage(events));
expect(msg).to.be.contain(checkSection);
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -3,12 +3,11 @@
// file 'LICENSE', which is part of this source code package.
//
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import {createCollectionExpectFailure, createCollectionExpectSuccess} from './util/helpers';
+import {expect} from 'chai';
+import privateKey from './substrate/privateKey';
+import usingApi, {executeTransaction, submitTransactionAsync} from './substrate/substrate-api';
+import {createCollectionExpectFailure, createCollectionExpectSuccess, getCreateCollectionResult, getDetailedCollectionInfo} from './util/helpers';
-chai.use(chaiAsPromised);
-
describe('integration test: ext. createCollection():', () => {
it('Create new NFT collection', async () => {
await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
@@ -28,6 +27,45 @@
it('Create new ReFungible collection', async () => {
await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
});
+ it('Create new collection with extra fields', async () => {
+ await usingApi(async api => {
+ const alice = privateKey('//Alice');
+ const bob = privateKey('//Bob');
+ const tx = api.tx.unique.createCollectionEx({
+ mode: {Fungible: 8},
+ access: 'AllowList',
+ name: [1],
+ description: [2],
+ tokenPrefix: '0x000000',
+ offchainSchema: '0x111111',
+ schemaVersion: 'Unique',
+ pendingSponsor: bob.address,
+ limits: {
+ accountTokenOwnershipLimit: 3,
+ },
+ variableOnChainSchema: '0x222222',
+ constOnChainSchema: '0x333333',
+ metaUpdatePermission: 'Admin',
+ });
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getCreateCollectionResult(events);
+
+ const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;
+ expect(collection.owner.toString()).to.equal(alice.address);
+ expect(collection.mode.asFungible.toNumber()).to.equal(8);
+ expect(collection.access.isAllowList).to.be.true;
+ expect(collection.name.map(v => v.toNumber())).to.deep.equal([1]);
+ expect(collection.description.map(v => v.toNumber())).to.deep.equal([2]);
+ expect(collection.tokenPrefix.toString()).to.equal('0x000000');
+ expect(collection.offchainSchema.toString()).to.equal('0x111111');
+ expect(collection.schemaVersion.isUnique).to.be.true;
+ expect(collection.sponsorship.asUnconfirmed.toString()).to.equal(bob.address);
+ expect(collection.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.equal(3);
+ expect(collection.variableOnChainSchema.toString()).to.equal('0x222222');
+ expect(collection.constOnChainSchema.toString()).to.equal('0x333333');
+ expect(collection.metaUpdatePermission.isAdmin).to.be.true;
+ });
+ });
});
describe('(!negative test!) integration test: ext. createCollection():', () => {
@@ -40,4 +78,11 @@
it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});
});
+ it('fails when bad limits are set', async () => {
+ await usingApi(async api => {
+ const alice = privateKey('//Alice');
+ const tx = api.tx.unique.createCollectionEx({mode: 'NFT', limits: {tokenLimit: 0}});
+ await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/^common.CollectionTokenLimitExceeded$/);
+ });
+ });
});
tests/src/interfaces/.gitignorediffbeforeafterboth--- /dev/null
+++ b/tests/src/interfaces/.gitignore
@@ -0,0 +1 @@
+metadata.json
tests/src/interfaces/augment-api-errors.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -69,6 +69,10 @@
**/
CollectionDescriptionLimitExceeded: AugmentedError<ApiType>;
/**
+ * Collection limit bounds per collection exceeded
+ **/
+ CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
+ /**
* Collection name can not be longer than 63 char.
**/
CollectionNameLimitExceeded: AugmentedError<ApiType>;
@@ -97,6 +101,10 @@
**/
NoPermission: AugmentedError<ApiType>;
/**
+ * Tried to enable permissions which are only permitted to be disabled
+ **/
+ OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
+ /**
* Collection is not in mint mode.
**/
PublicMintingNotAllowed: AugmentedError<ApiType>;
@@ -437,10 +445,6 @@
**/
CollectionDecimalPointLimitExceeded: AugmentedError<ApiType>;
/**
- * Collection limit bounds per collection exceeded
- **/
- CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
- /**
* This address is not set as sponsor, use setCollectionSponsor first.
**/
ConfirmUnsetSponsorFail: AugmentedError<ApiType>;
@@ -448,10 +452,6 @@
* Length of items properties must be greater than 0.
**/
EmptyArgument: AugmentedError<ApiType>;
- /**
- * Tried to enable permissions which are only permitted to be disabled
- **/
- OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
/**
* Generic error
**/
tests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -2,7 +2,7 @@
/* eslint-disable */
import type { CumulusPrimitivesParachainInherentParachainInherentData } from './polkadot';
-import type { PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion } from './unique';
+import type { PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion } from './unique';
import type { ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types';
import type { Bytes, Compact, Option, U256, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types';
import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
@@ -671,6 +671,12 @@
**/
createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
/**
+ * This method creates a collection
+ *
+ * Prefer it to deprecated [`created_collection`] method
+ **/
+ createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; offchainSchema?: any; schemaVersion?: any; pendingSponsor?: any; limits?: any; variableOnChainSchema?: any; constOnChainSchema?: any; metaUpdatePermission?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [UpDataStructsCreateCollectionData]>;
+ /**
* This method creates a concrete instance of NFT Collection created with CreateCollection method.
*
* # Permissions
tests/src/interfaces/augment-types.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -3,7 +3,7 @@
import type { EthereumBlock, EthereumLog, EthereumReceipt, EthereumTransactionLegacyTransaction, EvmCoreErrorExitReason, FpRpcTransactionStatus } from './ethereum';
import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundStatus, CumulusPalletXcmpQueueOutboundStatus, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1PersistedValidationData } from './polkadot';
-import type { PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionId, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsorshipState, UpDataStructsTokenId } from './unique';
+import type { PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionId, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsorshipState, UpDataStructsTokenId } from './unique';
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';
import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations';
@@ -1021,6 +1021,7 @@
UpDataStructsCollectionLimits: UpDataStructsCollectionLimits;
UpDataStructsCollectionMode: UpDataStructsCollectionMode;
UpDataStructsCollectionStats: UpDataStructsCollectionStats;
+ UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData;
UpDataStructsCreateItemData: UpDataStructsCreateItemData;
UpDataStructsMetaUpdatePermission: UpDataStructsMetaUpdatePermission;
UpDataStructsSchemaVersion: UpDataStructsSchemaVersion;
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -67,6 +67,20 @@
constOnChainSchema: 'Vec<u8>',
metaUpdatePermission: 'UpDataStructsMetaUpdatePermission',
},
+ UpDataStructsCreateCollectionData: {
+ mode: 'UpDataStructsCollectionMode',
+ access: 'Option<UpDataStructsAccessMode>',
+ name: 'Vec<u16>',
+ description: 'Vec<u16>',
+ tokenPrefix: 'Vec<u8>',
+ offchainSchema: 'Vec<u8>',
+ schemaVersion: 'Option<UpDataStructsSchemaVersion>',
+ pendingSponsor: 'Option<AccountId>',
+ limits: 'Option<UpDataStructsCollectionLimits>',
+ variableOnChainSchema: 'Vec<u8>',
+ constOnChainSchema: 'Vec<u8>',
+ metaUpdatePermission: 'Option<UpDataStructsMetaUpdatePermission>',
+ },
UpDataStructsCollectionStats: {
created: 'u32',
destroyed: 'u32',
@@ -76,7 +90,13 @@
UpDataStructsTokenId: 'u32',
PalletNonfungibleItemData: mkDummy('NftItemData'),
PalletRefungibleItemData: mkDummy('RftItemData'),
- UpDataStructsCollectionMode: mkDummy('CollectionMode'),
+ UpDataStructsCollectionMode: {
+ _enum: {
+ NFT: null,
+ Fungible: 'u32',
+ ReFungible: null,
+ },
+ },
UpDataStructsCreateItemData: mkDummy('CreateItemData'),
UpDataStructsCollectionLimits: {
accountTokenOwnershipLimit: 'Option<u32>',
@@ -101,7 +121,9 @@
UpDataStructsAccessMode: {
_enum: ['Normal', 'AllowList'],
},
- UpDataStructsSchemaVersion: mkDummy('SchemaVersion'),
+ UpDataStructsSchemaVersion: {
+ _enum: ['ImageURL', 'Unique'],
+ },
PalletUnqSchedulerScheduledV2: mkDummy('ScheduledV2'),
PalletUnqSchedulerCallSpec: mkDummy('CallSpec'),
tests/src/interfaces/unique/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/types.ts
+++ b/tests/src/interfaces/unique/types.ts
@@ -77,8 +77,11 @@
}
/** @name UpDataStructsCollectionMode */
-export interface UpDataStructsCollectionMode extends Struct {
- readonly dummyCollectionMode: u32;
+export interface UpDataStructsCollectionMode extends Enum {
+ readonly isNft: boolean;
+ readonly isFungible: boolean;
+ readonly asFungible: u32;
+ readonly isReFungible: boolean;
}
/** @name UpDataStructsCollectionStats */
@@ -88,6 +91,22 @@
readonly alive: u32;
}
+/** @name UpDataStructsCreateCollectionData */
+export interface UpDataStructsCreateCollectionData extends Struct {
+ readonly mode: UpDataStructsCollectionMode;
+ readonly access: Option<UpDataStructsAccessMode>;
+ readonly name: Vec<u16>;
+ readonly description: Vec<u16>;
+ readonly tokenPrefix: Bytes;
+ readonly offchainSchema: Bytes;
+ readonly schemaVersion: Option<UpDataStructsSchemaVersion>;
+ readonly pendingSponsor: Option<AccountId>;
+ readonly limits: Option<UpDataStructsCollectionLimits>;
+ readonly variableOnChainSchema: Bytes;
+ readonly constOnChainSchema: Bytes;
+ readonly metaUpdatePermission: Option<UpDataStructsMetaUpdatePermission>;
+}
+
/** @name UpDataStructsCreateItemData */
export interface UpDataStructsCreateItemData extends Struct {
readonly dummyCreateItemData: u32;
@@ -101,8 +120,9 @@
}
/** @name UpDataStructsSchemaVersion */
-export interface UpDataStructsSchemaVersion extends Struct {
- readonly dummySchemaVersion: u32;
+export interface UpDataStructsSchemaVersion extends Enum {
+ readonly isImageUrl: boolean;
+ readonly isUnique: boolean;
}
/** @name UpDataStructsSponsorshipState */
tests/src/substrate/substrate-api.tsdiffbeforeafterboth--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -95,6 +95,32 @@
return TransactionStatus.Fail;
}
+export function executeTransaction(api: ApiPromise, sender: IKeyringPair, transaction: SubmittableExtrinsic<'promise'>): Promise<EventRecord[]> {
+ return new Promise(async (res, rej) => {
+ try {
+ await transaction.signAndSend(sender, ({events, status}) => {
+ if (!status.isInBlock && !status.isFinalized) return;
+ for (const {event} of events) {
+ if (api.events.system.ExtrinsicSuccess.is(event)) {
+ res(events);
+ } else if (api.events.system.ExtrinsicFailed.is(event)) {
+ const {data: [error]} = event;
+ if (error.isModule) {
+ const decoded = api.registry.findMetaError(error.asModule);
+ const {method, section} = decoded;
+ rej(new Error(`${section}.${method}`));
+ } else {
+ rej(new Error(error.toString()));
+ }
+ }
+ }
+ });
+ } catch (e) {
+ rej(e);
+ }
+ });
+}
+
export function
submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
/* eslint no-async-promise-executor: "off" */
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -49,7 +49,7 @@
};
} else if ('Substrate' in input) {
return input;
- }else if ('substrate' in input) {
+ } else if ('substrate' in input) {
return {
Substrate: (input as any).substrate,
};
@@ -116,15 +116,15 @@
export interface IChainLimits {
collectionNumbersLimit: number;
- accountTokenOwnershipLimit: number;
- collectionsAdminsLimit: number;
- customDataLimit: number;
- nftSponsorTransferTimeout: number;
- fungibleSponsorTransferTimeout: number;
- refungibleSponsorTransferTimeout: number;
- offchainSchemaLimit: number;
- variableOnChainSchemaLimit: number;
- constOnChainSchemaLimit: number;
+ accountTokenOwnershipLimit: number;
+ collectionsAdminsLimit: number;
+ customDataLimit: number;
+ nftSponsorTransferTimeout: number;
+ fungibleSponsorTransferTimeout: number;
+ refungibleSponsorTransferTimeout: number;
+ offchainSchemaLimit: number;
+ variableOnChainSchemaLimit: number;
+ constOnChainSchemaLimit: number;
}
export interface IReFungibleTokenDataType {
@@ -283,7 +283,7 @@
modeprm = {refungible: null};
}
- const tx = api.tx.unique.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm as any);
+ const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});
const events = await submitTransactionAsync(alicePrivateKey, tx);
const result = getCreateCollectionResult(events);
@@ -329,7 +329,7 @@
// Run the CreateCollection transaction
const alicePrivateKey = privateKey('//Alice');
- const tx = api.tx.unique.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm as any);
+ const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});
const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
const result = getCreateCollectionResult(events);
@@ -557,7 +557,7 @@
await usingApi(async (api) => {
- const tx = api.tx.unique.setTransfersEnabledFlag (collectionId, enabled);
+ const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);
const events = await submitTransactionAsync(sender, tx);
const result = getGenericResult(events);
@@ -569,7 +569,7 @@
await usingApi(async (api) => {
- const tx = api.tx.unique.setTransfersEnabledFlag (collectionId, enabled);
+ const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);
const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
const result = getGenericResult(events);
@@ -811,8 +811,7 @@
}
export async function
-getFreeBalance(account: IKeyringPair) : Promise<bigint>
-{
+getFreeBalance(account: IKeyringPair): Promise<bigint> {
let balance = 0n;
await usingApi(async (api) => {
balance = BigInt((await api.query.system.account(account.address)).data.free.toString());