difftreelog
test fix unit
in: master
19 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6829,37 +6829,18 @@
name = "pallet-unique"
version = "0.1.0"
dependencies = [
- "ethereum",
- "evm-coder",
- "fp-evm",
- "fp-evm-mapping",
"frame-benchmarking",
"frame-support",
"frame-system",
- "hex-literal",
- "pallet-balances",
"pallet-common",
- "pallet-ethereum",
"pallet-evm",
- "pallet-evm-coder-substrate",
- "pallet-fungible",
- "pallet-nonfungible",
- "pallet-randomness-collective-flip",
- "pallet-refungible",
- "pallet-timestamp",
- "pallet-transaction-payment",
"parity-scale-codec",
- "primitive-types",
- "rlp",
"scale-info",
- "serde",
- "sp-api",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
"up-data-structs",
- "up-sponsorship",
]
[[package]]
@@ -11918,6 +11899,35 @@
]
[[package]]
+name = "tests"
+version = "0.1.0"
+dependencies = [
+ "fp-evm-mapping",
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-common",
+ "pallet-ethereum",
+ "pallet-evm",
+ "pallet-evm-coder-substrate",
+ "pallet-fungible",
+ "pallet-nonfungible",
+ "pallet-refungible",
+ "pallet-structure",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-unique",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "unique-runtime-common",
+ "up-data-structs",
+]
+
+[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -12677,6 +12687,7 @@
name = "unique-runtime-common"
version = "0.9.18"
dependencies = [
+ "evm-coder",
"fp-rpc",
"frame-support",
"frame-system",
@@ -12693,6 +12704,7 @@
"sp-runtime",
"sp-std",
"up-data-structs",
+ "up-sponsorship",
]
[[package]]
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,16 @@
[workspace]
resolver = "2"
-members = ['node/*', 'pallets/*', 'client/*', 'primitives/*', 'crates/*']
-exclude = ["runtime/unique", "runtime/quartz"]
+members = [
+ 'node/*',
+ 'pallets/*',
+ 'client/*',
+ 'primitives/*',
+ 'crates/*',
+ 'runtime/tests',
+]
+exclude = [
+ "runtime/unique",
+ "runtime/quartz"
+]
[profile.release]
panic = 'unwind'
pallets/unique/Cargo.tomldiffbeforeafterboth--- a/pallets/unique/Cargo.toml
+++ b/pallets/unique/Cargo.toml
@@ -19,32 +19,14 @@
runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
std = [
'codec/std',
- 'serde/std',
'frame-support/std',
'frame-system/std',
- 'pallet-balances/std',
'pallet-evm/std',
- 'pallet-timestamp/std',
- 'pallet-randomness-collective-flip/std',
- 'pallet-transaction-payment/std',
'pallet-common/std',
- 'pallet-fungible/std',
- 'pallet-nonfungible/std',
- 'pallet-refungible/std',
- 'fp-evm/std',
'up-data-structs/std',
- 'up-sponsorship/std',
- 'fp-evm-mapping/std',
'sp-std/std',
- 'sp-api/std',
'sp-runtime/std',
'frame-benchmarking/std',
- 'ethereum/std',
- 'rlp/std',
-
- 'primitive-types/std',
- 'evm-coder/std',
- 'pallet-evm-coder-substrate/std',
]
limit-testing = ["up-data-structs/limit-testing"]
@@ -69,21 +51,6 @@
branch = "polkadot-v0.9.20"
[dependencies.frame-system]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.pallet-balances]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.pallet-timestamp]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.pallet-randomness-collective-flip]
default-features = false
git = "https://github.com/paritytech/substrate"
branch = "polkadot-v0.9.20"
@@ -93,16 +60,6 @@
git = "https://github.com/paritytech/substrate"
branch = "polkadot-v0.9.20"
-[dependencies.pallet-transaction-payment]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.serde]
-default-features = false
-features = ['derive']
-version = '1.0.130'
-
[dependencies.sp-runtime]
default-features = false
git = "https://github.com/paritytech/substrate"
@@ -123,28 +80,8 @@
# Local Dependencies
[dependencies]
up-data-structs = { default-features = false, path = "../../primitives/data-structs" }
-
scale-info = { version = "2.0.1", default-features = false, features = [
"derive",
] }
-ethereum = { version = "0.12.0", default-features = false }
-rlp = { default-features = false, version = "0.5.0" }
-sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20" }
-
-up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.20" }
-fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-evm-coder = { default-features = false, path = "../../crates/evm-coder" }
-pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
-primitive-types = { version = "0.11.1", default-features = false, features = [
- "serde_no_std",
-] }
-
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-hex-literal = "0.3.3"
-
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.18-2" }
pallet-common = { default-features = false, path = "../common" }
-pallet-fungible = { default-features = false, path = "../fungible" }
-pallet-nonfungible = { default-features = false, path = "../nonfungible" }
-pallet-refungible = { default-features = false, path = "../refungible" }
pallets/unique/src/common.rsdiffbeforeafterboth--- a/pallets/unique/src/common.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use core::marker::PhantomData;
-use frame_support::{weights::Weight};
-use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight};
-
-use pallet_fungible::{common::CommonWeights as FungibleWeights};
-use pallet_nonfungible::{common::CommonWeights as NonfungibleWeights};
-use pallet_refungible::{common::CommonWeights as RefungibleWeights};
-use up_data_structs::CreateItemExData;
-
-use crate::Config;
-
-macro_rules! max_weight_of {
- ($method:ident ( $($args:tt)* )) => {
- <FungibleWeights<T>>::$method($($args)*)
- .max(<NonfungibleWeights<T>>::$method($($args)*))
- .max(<RefungibleWeights<T>>::$method($($args)*))
- };
-}
-
-pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
- fn create_item() -> Weight {
- dispatch_weight::<T>() + max_weight_of!(create_item())
- }
-
- fn create_multiple_items(amount: u32) -> Weight {
- dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
- }
-
- fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
- dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))
- }
-
- fn burn_item() -> Weight {
- dispatch_weight::<T>() + max_weight_of!(burn_item())
- }
-
- fn transfer() -> Weight {
- dispatch_weight::<T>() + max_weight_of!(transfer())
- }
-
- fn approve() -> Weight {
- dispatch_weight::<T>() + max_weight_of!(approve())
- }
-
- fn transfer_from() -> Weight {
- dispatch_weight::<T>() + max_weight_of!(transfer_from())
- }
-
- fn set_variable_metadata(bytes: u32) -> Weight {
- dispatch_weight::<T>() + max_weight_of!(set_variable_metadata(bytes))
- }
-
- fn burn_from() -> Weight {
- dispatch_weight::<T>() + max_weight_of!(burn_from())
- }
-}
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-pub mod sponsoring;
pallets/unique/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/sponsoring.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-//! Implements EVM sponsoring logic via TransactionValidityHack
-
-use crate::{Config, sponsorship::*};
-use evm_coder::{Call, abi::AbiReader};
-use pallet_common::{CollectionHandle, eth::map_eth_to_id};
-use sp_core::H160;
-use sp_std::prelude::*;
-use up_sponsorship::SponsorshipHandler;
-use core::marker::PhantomData;
-use core::convert::TryInto;
-use pallet_evm::account::CrossAccountId;
-use up_data_structs::{TokenId, CreateItemData, CreateNftData};
-
-use pallet_nonfungible::erc::{
- UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,
-};
-use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
-
-pub struct UniqueEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
- for UniqueEthSponsorshipHandler<T>
-{
- fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
- let collection_id = map_eth_to_id(&call.0)?;
- let collection = <CollectionHandle<T>>::new(collection_id)?;
- let sponsor = collection.sponsorship.sponsor()?.clone();
- let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
- Some(T::CrossAccountId::from_sub(match &collection.mode {
- crate::CollectionMode::NFT => {
- let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
- match call {
- UniqueNFTCall::ERC721UniqueExtensions(
- ERC721UniqueExtensionsCall::Transfer { token_id, .. },
- ) => {
- let token_id: TokenId = token_id.try_into().ok()?;
- withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
- }
- UniqueNFTCall::ERC721Mintable(
- ERC721MintableCall::Mint { token_id, .. }
- | ERC721MintableCall::MintWithTokenUri { token_id, .. },
- ) => {
- let _token_id: TokenId = token_id.try_into().ok()?;
- withdraw_create_item::<T>(
- &collection,
- &who,
- &CreateItemData::NFT(CreateNftData::default()),
- )
- .map(|()| sponsor)
- }
- UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {
- let token_id: TokenId = token_id.try_into().ok()?;
- let from = T::CrossAccountId::from_eth(from);
- withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)
- }
- UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
- let token_id: TokenId = token_id.try_into().ok()?;
- withdraw_approve::<T>(&collection, who.as_sub(), &token_id)
- .map(|()| sponsor)
- }
- _ => None,
- }
- }
- crate::CollectionMode::Fungible(_) => {
- let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;
- #[allow(clippy::single_match)]
- match call {
- UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
- withdraw_transfer::<T>(&collection, who, &TokenId::default())
- .map(|()| sponsor)
- }
- UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {
- let from = T::CrossAccountId::from_eth(from);
- withdraw_transfer::<T>(&collection, &from, &TokenId::default())
- .map(|()| sponsor)
- }
- UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
- withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())
- .map(|()| sponsor)
- }
- _ => None,
- }
- }
- _ => None,
- }?))
- }
-}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -22,10 +22,6 @@
clippy::unused_unit
)]
-extern crate alloc;
-
-pub use serde::{Serialize, Deserialize};
-
use frame_support::{
decl_module, decl_storage, decl_error, decl_event,
dispatch::DispatchResult,
@@ -50,20 +46,6 @@
CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo,
dispatch::dispatch_call, dispatch::CollectionDispatch,
};
-
-#[cfg(test)]
-mod mock;
-
-#[cfg(test)]
-mod tests;
-
-mod eth;
-mod sponsorship;
-pub use sponsorship::{UniqueSponsorshipHandler, UniqueSponsorshipPredict};
-pub use eth::sponsoring::UniqueEthSponsorshipHandler;
-
-pub mod common;
-use common::CommonWeights;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
@@ -88,20 +70,12 @@
}
}
-pub trait Config:
- system::Config
- + pallet_evm_coder_substrate::Config
- + pallet_common::Config
- + pallet_nonfungible::Config
- + pallet_refungible::Config
- + pallet_fungible::Config
- + Sized
- + TypeInfo
-{
+pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
+ type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;
}
decl_event! {
@@ -689,7 +663,7 @@
/// * owner: Address, initial owner of the NFT.
///
/// * data: Token data to store on chain.
- #[weight = <CommonWeights<T>>::create_item()]
+ #[weight = T::CommonWeightInfo::create_item()]
#[transactional]
pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -716,7 +690,7 @@
/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].
///
/// * owner: Address, initial owner of the NFT.
- #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]
+ #[weight = T::CommonWeightInfo::create_multiple_items(items_data.len() as u32)]
#[transactional]
pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {
ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);
@@ -726,7 +700,7 @@
dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))
}
- #[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]
+ #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]
#[transactional]
pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -774,7 +748,7 @@
/// * collection_id: ID of the collection.
///
/// * item_id: ID of NFT to burn.
- #[weight = <CommonWeights<T>>::burn_item()]
+ #[weight = T::CommonWeightInfo::burn_item()]
#[transactional]
pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -806,7 +780,7 @@
/// * item_id: ID of NFT to burn.
///
/// * from: owner of item
- #[weight = <CommonWeights<T>>::burn_from()]
+ #[weight = T::CommonWeightInfo::burn_from()]
#[transactional]
pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -838,7 +812,7 @@
/// * Non-Fungible Mode: Ignored
/// * Fungible Mode: Must specify transferred amount
/// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)
- #[weight = <CommonWeights<T>>::transfer()]
+ #[weight = T::CommonWeightInfo::transfer()]
#[transactional]
pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -862,7 +836,7 @@
/// * collection_id.
///
/// * item_id: ID of the item.
- #[weight = <CommonWeights<T>>::approve()]
+ #[weight = T::CommonWeightInfo::approve()]
#[transactional]
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -889,7 +863,7 @@
/// * item_id: ID of the item.
///
/// * value: Amount to transfer.
- #[weight = <CommonWeights<T>>::transfer_from()]
+ #[weight = T::CommonWeightInfo::transfer_from()]
#[transactional]
pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -910,7 +884,7 @@
/// * collection_id.
///
/// * schema: String representing the offchain data schema.
- #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]
+ #[weight = T::CommonWeightInfo::set_variable_metadata(data.len() as u32)]
#[transactional]
pub fn set_variable_meta_data (
origin,
pallets/unique/src/mock.rsdiffbeforeafterboth--- a/pallets/unique/src/mock.rs
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-#![allow(clippy::from_over_into)]
-
-use crate as pallet_template;
-use sp_core::{H160, H256};
-use frame_support::{parameter_types, traits::Everything, weights::IdentityFee};
-use sp_runtime::{
- traits::{BlakeTwo256, IdentityLookup},
- testing::Header,
-};
-use pallet_transaction_payment::{CurrencyAdapter};
-use frame_system as system;
-use pallet_evm::{AddressMapping, runner::stack::MaybeMirroredLog, account::CrossAccountId};
-use fp_evm_mapping::EvmBackwardsAddressMapping;
-use codec::{Encode, Decode, MaxEncodedLen};
-use scale_info::TypeInfo;
-use up_data_structs::ConstU32;
-
-type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
-type Block = frame_system::mocking::MockBlock<Test>;
-
-// Configure a mock runtime to test the pallet.
-frame_support::construct_runtime!(
- pub enum Test where
- Block = Block,
- NodeBlock = Block,
- UncheckedExtrinsic = UncheckedExtrinsic,
- {
- System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
- TemplateModule: pallet_template::{Pallet, Call, Storage},
- Balances: pallet_balances::{Pallet, Call, Storage},
- Common: pallet_common::{Pallet, Storage, Event<T>},
- Fungible: pallet_fungible::{Pallet, Storage},
- Refungible: pallet_refungible::{Pallet, Storage},
- Nonfungible: pallet_nonfungible::{Pallet, Storage},
- }
-);
-
-parameter_types! {
- pub const BlockHashCount: u64 = 250;
- pub const SS58Prefix: u8 = 42;
-}
-
-impl system::Config for Test {
- type BaseCallFilter = Everything;
- type BlockWeights = ();
- type BlockLength = ();
- type DbWeight = ();
- type Origin = Origin;
- type Call = Call;
- type Index = u64;
- type BlockNumber = u64;
- type Hash = H256;
- type Hashing = BlakeTwo256;
- type AccountId = u64;
- type Lookup = IdentityLookup<Self::AccountId>;
- type Header = Header;
- type Event = ();
- type BlockHashCount = BlockHashCount;
- type Version = ();
- type PalletInfo = PalletInfo;
- type AccountData = pallet_balances::AccountData<u64>;
- type OnNewAccount = ();
- type OnKilledAccount = ();
- type SystemWeightInfo = ();
- type SS58Prefix = SS58Prefix;
- type OnSetCode = ();
- type MaxConsumers = ConstU32<16>;
-}
-
-parameter_types! {
- pub const ExistentialDeposit: u64 = 1;
- pub const MaxLocks: u32 = 50;
-}
-//frame_system::Module<Test>;
-impl pallet_balances::Config for Test {
- type AccountStore = System;
- type Balance = u64;
- type DustRemoval = ();
- type Event = ();
- type ExistentialDeposit = ExistentialDeposit;
- type WeightInfo = ();
- type MaxLocks = MaxLocks;
- type MaxReserves = ();
- type ReserveIdentifier = [u8; 8];
-}
-
-parameter_types! {
- pub const OperationalFeeMultiplier: u8 = 5;
-}
-
-impl pallet_transaction_payment::Config for Test {
- type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;
- type LengthToFee = IdentityFee<u64>;
- type WeightToFee = IdentityFee<u64>;
- type FeeMultiplierUpdate = ();
- type OperationalFeeMultiplier = OperationalFeeMultiplier;
-}
-
-parameter_types! {
- pub const MinimumPeriod: u64 = 1;
-}
-impl pallet_timestamp::Config for Test {
- type Moment = u64;
- type OnTimestampSet = ();
- type MinimumPeriod = MinimumPeriod;
- type WeightInfo = ();
-}
-
-parameter_types! {
- pub const CollectionCreationPrice: u32 = 100;
- pub TreasuryAccountId: u64 = 1234;
- pub EthereumChainId: u32 = 1111;
-}
-
-pub struct TestEvmAddressMapping;
-impl AddressMapping<u64> for TestEvmAddressMapping {
- fn into_account_id(_addr: sp_core::H160) -> u64 {
- unimplemented!()
- }
-}
-
-pub struct TestEvmBackwardsAddressMapping;
-impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {
- fn from_account_id(_account_id: u64) -> sp_core::H160 {
- unimplemented!()
- }
-}
-
-#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, TypeInfo, MaxEncodedLen)]
-pub struct TestCrossAccountId(u64, sp_core::H160);
-impl CrossAccountId<u64> for TestCrossAccountId {
- fn as_sub(&self) -> &u64 {
- &self.0
- }
- fn as_eth(&self) -> &sp_core::H160 {
- &self.1
- }
- fn from_sub(sub: u64) -> Self {
- let mut eth = [0; 20];
- eth[12..20].copy_from_slice(&sub.to_be_bytes());
- Self(sub, sp_core::H160(eth))
- }
- fn from_eth(eth: sp_core::H160) -> Self {
- let mut sub_raw = [0; 8];
- sub_raw.copy_from_slice(ð.0[0..8]);
- let sub = u64::from_be_bytes(sub_raw);
- Self(sub, eth)
- }
- fn conv_eq(&self, other: &Self) -> bool {
- self.as_sub() == other.as_sub()
- }
-}
-
-impl Default for TestCrossAccountId {
- fn default() -> Self {
- Self::from_sub(0)
- }
-}
-
-pub struct TestEtheremTransactionSender;
-impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {
- fn submit_logs_transaction(_source: H160, _logs: Vec<MaybeMirroredLog>) {}
-}
-
-impl pallet_evm_coder_substrate::Config for Test {
- type EthereumTransactionSender = TestEtheremTransactionSender;
- type GasWeightMapping = ();
-}
-
-impl pallet_common::Config for Test {
- type Event = ();
- type Currency = Balances;
- type CollectionCreationPrice = CollectionCreationPrice;
- type TreasuryAccountId = TreasuryAccountId;
-}
-
-impl pallet_evm::account::Config for Test {
- type CrossAccountId = TestCrossAccountId;
- type EvmAddressMapping = TestEvmAddressMapping;
- type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;
-}
-
-impl pallet_fungible::Config for Test {
- type WeightInfo = ();
-}
-impl pallet_refungible::Config for Test {
- type WeightInfo = ();
-}
-impl pallet_nonfungible::Config for Test {
- type WeightInfo = ();
-}
-
-impl pallet_template::Config for Test {
- type Event = ();
- type WeightInfo = ();
-}
-
-// Build genesis storage according to the mock runtime.
-pub fn new_test_ext() -> sp_io::TestExternalities {
- system::GenesisConfig::default()
- .build_storage::<Test>()
- .unwrap()
- .into()
-}
pallets/unique/src/sponsorship.rsdiffbeforeafterboth--- a/pallets/unique/src/sponsorship.rs
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use crate::{
- Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,
- FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,
- FungibleApproveBasket, RefungibleApproveBasket,
-};
-use core::marker::PhantomData;
-use up_sponsorship::SponsorshipHandler;
-use frame_support::{
- traits::{IsSubType},
- storage::{StorageMap, StorageDoubleMap, StorageNMap},
-};
-use up_data_structs::{
- CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MetaUpdatePermission,
- NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,
-};
-use sp_runtime::traits::Saturating;
-use pallet_common::{CollectionHandle};
-use pallet_evm::account::CrossAccountId;
-
-pub fn withdraw_transfer<T: Config>(
- collection: &CollectionHandle<T>,
- who: &T::CrossAccountId,
- item_id: &TokenId,
-) -> Option<()> {
- // preliminary sponsoring correctness check
- match collection.mode {
- CollectionMode::NFT => {
- let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
- if !owner.conv_eq(who) {
- return None;
- }
- }
- CollectionMode::Fungible(_) => {
- if item_id != &TokenId::default() {
- return None;
- }
- if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
- return None;
- }
- }
- CollectionMode::ReFungible => {
- if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
- return None;
- }
- }
- }
-
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let limit = collection
- .limits
- .sponsor_transfer_timeout(match collection.mode {
- CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- });
-
- let last_tx_block = match collection.mode {
- CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
- CollectionMode::Fungible(_) => {
- <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
- }
- CollectionMode::ReFungible => {
- <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))
- }
- };
-
- if let Some(last_tx_block) = last_tx_block {
- let timeout = last_tx_block + limit.into();
- if block_number < timeout {
- return None;
- }
- }
-
- match collection.mode {
- CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
- CollectionMode::Fungible(_) => {
- <FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)
- }
- CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(
- (collection.id, item_id, who.as_sub()),
- block_number,
- ),
- };
-
- Some(())
-}
-
-pub fn withdraw_create_item<T: Config>(
- collection: &CollectionHandle<T>,
- who: &T::CrossAccountId,
- _properties: &CreateItemData,
-) -> Option<()> {
- if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
- return None;
- }
-
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let limit = collection
- .limits
- .sponsor_transfer_timeout(match _properties {
- CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- });
-
- if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {
- let timeout = last_tx_block + limit.into();
- if block_number < timeout {
- return None;
- }
- }
-
- CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);
-
- Some(())
-}
-
-pub fn withdraw_set_variable_meta_data<T: Config>(
- who: &T::CrossAccountId,
- collection: &CollectionHandle<T>,
- item_id: &TokenId,
- data: &[u8],
-) -> Option<()> {
- // TODO: make it work for admins
- if collection.meta_update_permission != MetaUpdatePermission::ItemOwner {
- return None;
- }
- // preliminary sponsoring correctness check
- match collection.mode {
- CollectionMode::NFT => {
- let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
- if !owner.conv_eq(who) {
- return None;
- }
- }
- CollectionMode::Fungible(_) => {
- if item_id != &TokenId::default() {
- return None;
- }
- if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
- return None;
- }
- }
- CollectionMode::ReFungible => {
- if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
- return None;
- }
- }
- }
-
- // Can't sponsor fungible collection, this tx will be rejected
- // as invalid
- if matches!(collection.mode, CollectionMode::Fungible(_)) {
- return None;
- }
- if data.len() > collection.limits.sponsored_data_size() as usize {
- return None;
- }
-
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let limit = collection.limits.sponsored_data_rate_limit()?;
-
- if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
- let timeout = last_tx_block + limit.into();
- if block_number < timeout {
- return None;
- }
- }
-
- <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
-
- Some(())
-}
-
-pub fn withdraw_approve<T: Config>(
- collection: &CollectionHandle<T>,
- who: &T::AccountId,
- item_id: &TokenId,
-) -> Option<()> {
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let limit = collection.limits.sponsor_approve_timeout();
-
- let last_tx_block = match collection.mode {
- CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
- CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
- CollectionMode::ReFungible => {
- <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
- }
- };
-
- if let Some(last_tx_block) = last_tx_block {
- let timeout = last_tx_block + limit.into();
- if block_number < timeout {
- return None;
- }
- }
-
- match collection.mode {
- CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
- CollectionMode::Fungible(_) => {
- <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
- }
- CollectionMode::ReFungible => {
- <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
- }
- };
-
- Some(())
-}
-
-fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
- let collection = CollectionHandle::new(id)?;
- let sponsor = collection.sponsorship.sponsor().cloned()?;
- Some((sponsor, collection))
-}
-
-pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);
-impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>
-where
- T: Config,
- C: IsSubType<Call<T>>,
-{
- fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
- match IsSubType::<Call<T>>::is_sub_type(call)? {
- Call::create_item {
- collection_id,
- data,
- ..
- } => {
- let (sponsor, collection) = load(*collection_id)?;
- withdraw_create_item::<T>(
- &collection,
- &T::CrossAccountId::from_sub(who.clone()),
- data,
- )
- .map(|()| sponsor)
- }
- Call::transfer {
- collection_id,
- item_id,
- ..
- } => {
- let (sponsor, collection) = load(*collection_id)?;
- withdraw_transfer::<T>(
- &collection,
- &T::CrossAccountId::from_sub(who.clone()),
- item_id,
- )
- .map(|()| sponsor)
- }
- Call::transfer_from {
- collection_id,
- item_id,
- from,
- ..
- } => {
- let (sponsor, collection) = load(*collection_id)?;
- withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)
- }
- Call::approve {
- collection_id,
- item_id,
- ..
- } => {
- let (sponsor, collection) = load(*collection_id)?;
- withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
- }
- Call::set_variable_meta_data {
- collection_id,
- item_id,
- data,
- } => {
- let (sponsor, collection) = load(*collection_id)?;
- withdraw_set_variable_meta_data::<T>(
- &T::CrossAccountId::from_sub(who.clone()),
- &collection,
- item_id,
- data,
- )
- .map(|()| sponsor)
- }
- _ => None,
- }
- }
-}
-
-use crate::SponsorshipPredict;
-pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);
-
-impl<T> SponsorshipPredict<T> for UniqueSponsorshipPredict<T>
-where
- T: Config,
-{
- fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>
- where
- u64: From<<T as frame_system::Config>::BlockNumber>,
- {
- let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;
- let _ = collection.sponsorship.sponsor()?;
-
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let limit = collection
- .limits
- .sponsor_transfer_timeout(match collection.mode {
- CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- });
-
- let last_tx_block = match collection.mode {
- CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),
- CollectionMode::Fungible(_) => {
- <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
- }
- CollectionMode::ReFungible => {
- <ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))
- }
- };
-
- if let Some(last_tx_block) = last_tx_block {
- return Some(
- last_tx_block
- .saturating_add(limit.into())
- .saturating_sub(block_number)
- .into(),
- );
- }
-
- let token_exists = match collection.mode {
- CollectionMode::NFT => {
- <pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))
- }
- CollectionMode::Fungible(_) => true,
- CollectionMode::ReFungible => {
- <pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))
- }
- };
-
- if token_exists {
- Some(0)
- } else {
- None
- }
- }
-}
pallets/unique/src/tests.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Tests to be written here18use super::*;19use crate::mock::*;20use crate::{AccessMode, CollectionMode};21use up_data_structs::{22 COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,23 CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,24 TokenId, MAX_TOKEN_OWNERSHIP,25};26use frame_support::{assert_noop, assert_ok, assert_err};27use sp_std::convert::TryInto;28use pallet_balances;2930fn add_balance(user: u64, value: u64) {31 const DONOR_USER: u64 = 999;32 assert_ok!(<pallet_balances::Pallet<Test>>::set_balance(33 Origin::root(),34 DONOR_USER,35 value,36 037 ));38 assert_ok!(<pallet_balances::Pallet<Test>>::force_transfer(39 Origin::root(),40 DONOR_USER,41 user,42 value43 ));44}4546fn default_nft_data() -> CreateNftData {47 CreateNftData {48 const_data: vec![1, 2, 3].try_into().unwrap(),49 variable_data: vec![3, 2, 1].try_into().unwrap(),50 }51}5253fn default_fungible_data() -> CreateFungibleData {54 CreateFungibleData { value: 5 }55}5657fn default_re_fungible_data() -> CreateReFungibleData {58 CreateReFungibleData {59 const_data: vec![1, 2, 3].try_into().unwrap(),60 variable_data: vec![3, 2, 1].try_into().unwrap(),61 pieces: 1023,62 }63}6465fn create_test_collection_for_owner(66 mode: &CollectionMode,67 owner: u64,68 id: CollectionId,69) -> CollectionId {70 add_balance(owner, CollectionCreationPrice::get() as u64 + 1);7172 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();73 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();74 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();7576 let data: CreateCollectionData<u64> = CreateCollectionData {77 name: col_name1.try_into().unwrap(),78 description: col_desc1.try_into().unwrap(),79 token_prefix: token_prefix1.try_into().unwrap(),80 mode: mode.clone(),81 ..Default::default()82 };8384 let origin1 = Origin::signed(owner);85 assert_ok!(TemplateModule::create_collection_ex(origin1, data));8687 let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();88 let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();89 let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();90 assert_eq!(91 <pallet_common::CollectionById<Test>>::get(id)92 .unwrap()93 .owner,94 owner95 );96 assert_eq!(97 <pallet_common::CollectionById<Test>>::get(id).unwrap().name,98 saved_col_name99 );100 assert_eq!(101 <pallet_common::CollectionById<Test>>::get(id).unwrap().mode,102 *mode103 );104 assert_eq!(105 <pallet_common::CollectionById<Test>>::get(id)106 .unwrap()107 .description,108 saved_description109 );110 assert_eq!(111 <pallet_common::CollectionById<Test>>::get(id)112 .unwrap()113 .token_prefix,114 saved_prefix115 );116 id117}118119fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {120 create_test_collection_for_owner(&mode, 1, id)121}122123fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {124 let origin1 = Origin::signed(1);125 assert_ok!(TemplateModule::create_item(126 origin1,127 collection_id,128 account(1),129 data.clone()130 ));131}132133fn account(sub: u64) -> TestCrossAccountId {134 TestCrossAccountId::from_sub(sub)135}136137// Use cases tests region138// #region139140#[test]141fn set_version_schema() {142 new_test_ext().execute_with(|| {143 let origin1 = Origin::signed(1);144 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));145146 assert_ok!(TemplateModule::set_schema_version(147 origin1,148 collection_id,149 SchemaVersion::Unique150 ));151 assert_eq!(152 <pallet_common::CollectionById<Test>>::get(collection_id)153 .unwrap()154 .schema_version,155 SchemaVersion::Unique156 );157 });158}159160#[test]161fn check_not_sufficient_founds() {162 new_test_ext().execute_with(|| {163 let acc: u64 = 1;164 <pallet_balances::Pallet<Test>>::set_balance(Origin::root(), acc, 0, 0).unwrap();165166 let name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();167 let description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();168 let token_prefix: Vec<u8> = b"token_prefix1\0".to_vec();169170 let data: CreateCollectionData<<Test as system::Config>::AccountId> =171 CreateCollectionData {172 name: name.try_into().unwrap(),173 description: description.try_into().unwrap(),174 token_prefix: token_prefix.try_into().unwrap(),175 mode: CollectionMode::NFT,176 ..Default::default()177 };178179 let result = TemplateModule::create_collection_ex(Origin::signed(acc), data);180 assert_err!(result, <CommonError<Test>>::NotSufficientFounds);181 });182}183184#[test]185fn create_fungible_collection_fails_with_large_decimal_numbers() {186 new_test_ext().execute_with(|| {187 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();188 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();189 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();190191 let data: CreateCollectionData<u64> = CreateCollectionData {192 name: col_name1.try_into().unwrap(),193 description: col_desc1.try_into().unwrap(),194 token_prefix: token_prefix1.try_into().unwrap(),195 mode: CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1),196 ..Default::default()197 };198199 let origin1 = Origin::signed(1);200 assert_noop!(201 TemplateModule::create_collection_ex(origin1, data),202 Error::<Test>::CollectionDecimalPointLimitExceeded203 );204 });205}206207#[test]208fn create_nft_item() {209 new_test_ext().execute_with(|| {210 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));211212 let data = default_nft_data();213 create_test_item(collection_id, &data.clone().into());214215 let item = <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1)).unwrap();216 assert_eq!(item.const_data, data.const_data.into_inner());217 assert_eq!(item.variable_data, data.variable_data.into_inner());218 });219}220221// Use cases tests region222// #region223#[test]224fn create_nft_multiple_items() {225 new_test_ext().execute_with(|| {226 create_test_collection(&CollectionMode::NFT, CollectionId(1));227228 let origin1 = Origin::signed(1);229230 let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];231232 assert_ok!(TemplateModule::create_multiple_items(233 origin1,234 CollectionId(1),235 account(1),236 items_data237 .clone()238 .into_iter()239 .map(|d| { d.into() })240 .collect()241 ));242 for (index, data) in items_data.into_iter().enumerate() {243 let item = <pallet_nonfungible::TokenData<Test>>::get((244 CollectionId(1),245 TokenId((index + 1) as u32),246 ))247 .unwrap();248 assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());249 assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());250 }251 });252}253254#[test]255fn create_refungible_item() {256 new_test_ext().execute_with(|| {257 let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));258259 let data = default_re_fungible_data();260 create_test_item(collection_id, &data.clone().into());261 let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));262 let balance =263 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1)));264 assert_eq!(item.const_data, data.const_data.into_inner());265 assert_eq!(item.variable_data, data.variable_data.into_inner());266 assert_eq!(balance, 1023);267 });268}269270#[test]271fn create_multiple_refungible_items() {272 new_test_ext().execute_with(|| {273 create_test_collection(&CollectionMode::ReFungible, CollectionId(1));274275 let origin1 = Origin::signed(1);276277 let items_data = vec![278 default_re_fungible_data(),279 default_re_fungible_data(),280 default_re_fungible_data(),281 ];282283 assert_ok!(TemplateModule::create_multiple_items(284 origin1,285 CollectionId(1),286 account(1),287 items_data288 .clone()289 .into_iter()290 .map(|d| { d.into() })291 .collect()292 ));293 for (index, data) in items_data.into_iter().enumerate() {294 let item = <pallet_refungible::TokenData<Test>>::get((295 CollectionId(1),296 TokenId((index + 1) as u32),297 ));298 let balance =299 <pallet_refungible::Balance<Test>>::get((CollectionId(1), TokenId(1), account(1)));300 assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());301 assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());302 assert_eq!(balance, 1023);303 }304 });305}306307#[test]308fn create_fungible_item() {309 new_test_ext().execute_with(|| {310 let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));311312 let data = default_fungible_data();313 create_test_item(collection_id, &data.into());314315 assert_eq!(316 <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),317 5318 );319 });320}321322//#[test]323// fn create_multiple_fungible_items() {324// new_test_ext().execute_with(|| {325// default_limits();326327// create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));328329// let origin1 = Origin::signed(1);330331// let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];332333// assert_ok!(TemplateModule::create_multiple_items(334// origin1.clone(),335// 1,336// 1,337// items_data.clone().into_iter().map(|d| { d.into() }).collect()338// ));339340// for (index, _) in items_data.iter().enumerate() {341// assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);342// }343// assert_eq!(TemplateModule::balance_count(1, 1), 3000);344// assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);345// });346// }347348#[test]349fn transfer_fungible_item() {350 new_test_ext().execute_with(|| {351 let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));352353 let origin1 = Origin::signed(1);354 let origin2 = Origin::signed(2);355356 let data = default_fungible_data();357 create_test_item(collection_id, &data.into());358359 assert_eq!(360 <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),361 5362 );363364 // change owner scenario365 assert_ok!(TemplateModule::transfer(366 origin1,367 account(2),368 CollectionId(1),369 TokenId(0),370 5371 ));372 assert_eq!(373 <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),374 0375 );376377 // split item scenario378 assert_ok!(TemplateModule::transfer(379 origin2.clone(),380 account(3),381 CollectionId(1),382 TokenId(0),383 3384 ));385386 // split item and new owner has account scenario387 assert_ok!(TemplateModule::transfer(388 origin2,389 account(3),390 CollectionId(1),391 TokenId(0),392 1393 ));394 assert_eq!(395 <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(2))),396 1397 );398 assert_eq!(399 <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(3))),400 4401 );402 });403}404405#[test]406fn transfer_refungible_item() {407 new_test_ext().execute_with(|| {408 let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));409410 // Create RFT 1 in 1023 pieces for account 1411 let data = default_re_fungible_data();412 create_test_item(collection_id, &data.clone().into());413 let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));414 assert_eq!(item.const_data, data.const_data.into_inner());415 assert_eq!(item.variable_data, data.variable_data.into_inner());416 assert_eq!(417 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),418 1419 );420 assert_eq!(421 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),422 1023423 );424 assert_eq!(425 <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),426 true427 );428429 // Account 1 transfers all 1023 pieces of RFT 1 to account 2430 let origin1 = Origin::signed(1);431 let origin2 = Origin::signed(2);432 assert_ok!(TemplateModule::transfer(433 origin1,434 account(2),435 CollectionId(1),436 TokenId(1),437 1023438 ));439 assert_eq!(440 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),441 1023442 );443 assert_eq!(444 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),445 0446 );447 assert_eq!(448 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),449 1450 );451 assert_eq!(452 <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),453 false454 );455 assert_eq!(456 <pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),457 true458 );459460 // Account 2 transfers 500 pieces of RFT 1 to account 3461 assert_ok!(TemplateModule::transfer(462 origin2.clone(),463 account(3),464 CollectionId(1),465 TokenId(1),466 500467 ));468 assert_eq!(469 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),470 523471 );472 assert_eq!(473 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),474 500475 );476 assert_eq!(477 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),478 1479 );480 assert_eq!(481 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),482 1483 );484 assert_eq!(485 <pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),486 true487 );488 assert_eq!(489 <pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),490 true491 );492493 // Account 2 transfers 200 more pieces of RFT 1 to account 3 with pre-existing balance494 assert_ok!(TemplateModule::transfer(495 origin2,496 account(3),497 CollectionId(1),498 TokenId(1),499 200500 ));501 assert_eq!(502 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),503 323504 );505 assert_eq!(506 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),507 700508 );509 assert_eq!(510 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),511 1512 );513 assert_eq!(514 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),515 1516 );517 assert_eq!(518 <pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),519 true520 );521 assert_eq!(522 <pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),523 true524 );525 });526}527528#[test]529fn transfer_nft_item() {530 new_test_ext().execute_with(|| {531 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));532533 let data = default_nft_data();534 create_test_item(collection_id, &data.into());535 assert_eq!(536 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),537 1538 );539 assert_eq!(540 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),541 true542 );543544 let origin1 = Origin::signed(1);545 // default scenario546 assert_ok!(TemplateModule::transfer(547 origin1,548 account(2),549 CollectionId(1),550 TokenId(1),551 1552 ));553 assert_eq!(554 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),555 0556 );557 assert_eq!(558 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),559 1560 );561 assert_eq!(562 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),563 false564 );565 assert_eq!(566 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),567 true568 );569 });570}571572#[test]573fn transfer_nft_item_wrong_value() {574 new_test_ext().execute_with(|| {575 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));576577 let data = default_nft_data();578 create_test_item(collection_id, &data.into());579 assert_eq!(580 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),581 1582 );583 assert_eq!(584 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),585 true586 );587588 let origin1 = Origin::signed(1);589590 assert_noop!(591 TemplateModule::transfer(origin1, account(2), CollectionId(1), TokenId(1), 2)592 .map_err(|e| e.error),593 <pallet_nonfungible::Error::<Test>>::NonfungibleItemsHaveNoAmount594 );595 });596}597598#[test]599fn transfer_nft_item_zero_value() {600 new_test_ext().execute_with(|| {601 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));602603 let data = default_nft_data();604 create_test_item(collection_id, &data.into());605 assert_eq!(606 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),607 1608 );609 assert_eq!(610 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),611 true612 );613614 let origin1 = Origin::signed(1);615616 // Transferring 0 amount works on NFT...617 assert_ok!(TemplateModule::transfer(618 origin1,619 account(2),620 CollectionId(1),621 TokenId(1),622 0623 ));624 // ... and results in no transfer625 assert_eq!(626 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),627 1628 );629 assert_eq!(630 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),631 true632 );633 });634}635636#[test]637fn nft_approve_and_transfer_from() {638 new_test_ext().execute_with(|| {639 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));640641 let data = default_nft_data();642 create_test_item(collection_id, &data.into());643644 let origin1 = Origin::signed(1);645 let origin2 = Origin::signed(2);646647 assert_eq!(648 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),649 1650 );651 assert_eq!(652 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),653 true654 );655656 // neg transfer_from657 assert_noop!(658 TemplateModule::transfer_from(659 origin2.clone(),660 account(1),661 account(2),662 CollectionId(1),663 TokenId(1),664 1665 )666 .map_err(|e| e.error),667 CommonError::<Test>::ApprovedValueTooLow668 );669670 // do approve671 assert_ok!(TemplateModule::approve(672 origin1,673 account(2),674 CollectionId(1),675 TokenId(1),676 1677 ));678 assert_eq!(679 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),680 account(2)681 );682683 assert_ok!(TemplateModule::transfer_from(684 origin2,685 account(1),686 account(3),687 CollectionId(1),688 TokenId(1),689 1690 ));691 assert!(692 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()693 );694 });695}696697#[test]698fn nft_approve_and_transfer_from_allow_list() {699 new_test_ext().execute_with(|| {700 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));701702 let origin1 = Origin::signed(1);703 let origin2 = Origin::signed(2);704705 // Create NFT 1 for account 1706 let data = default_nft_data();707 create_test_item(collection_id, &data.clone().into());708 assert_eq!(709 &<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))710 .unwrap()711 .const_data,712 &data.const_data.into_inner()713 );714 assert_eq!(715 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),716 1717 );718 assert_eq!(719 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),720 true721 );722723 // Allow allow-list users to mint and add accounts 1, 2, and 3 to allow-list724 assert_ok!(TemplateModule::set_mint_permission(725 origin1.clone(),726 CollectionId(1),727 true728 ));729 assert_ok!(TemplateModule::set_public_access_mode(730 origin1.clone(),731 CollectionId(1),732 AccessMode::AllowList733 ));734 assert_ok!(TemplateModule::add_to_allow_list(735 origin1.clone(),736 CollectionId(1),737 account(1)738 ));739 assert_ok!(TemplateModule::add_to_allow_list(740 origin1.clone(),741 CollectionId(1),742 account(2)743 ));744 assert_ok!(TemplateModule::add_to_allow_list(745 origin1.clone(),746 CollectionId(1),747 account(3)748 ));749750 // Account 1 approves account 2 for NFT 1751 assert_ok!(TemplateModule::approve(752 origin1.clone(),753 account(2),754 CollectionId(1),755 TokenId(1),756 1757 ));758 assert_eq!(759 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),760 account(2)761 );762763 // Account 2 transfers NFT 1 from account 1 to account 3764 assert_ok!(TemplateModule::transfer_from(765 origin2,766 account(1),767 account(3),768 CollectionId(1),769 TokenId(1),770 1771 ));772 assert!(773 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()774 );775 });776}777778#[test]779fn refungible_approve_and_transfer_from() {780 new_test_ext().execute_with(|| {781 let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));782783 let origin1 = Origin::signed(1);784 let origin2 = Origin::signed(2);785786 // Create RFT 1 in 1023 pieces for account 1787 let data = default_re_fungible_data();788 create_test_item(collection_id, &data.into());789790 assert_eq!(791 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),792 1793 );794 assert_eq!(795 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),796 1023797 );798 assert_eq!(799 <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),800 true801 );802803 // Allow public minting, enable allow-list and add accounts 1, 2, 3 to allow-list804 assert_ok!(TemplateModule::set_mint_permission(805 origin1.clone(),806 CollectionId(1),807 true808 ));809 assert_ok!(TemplateModule::set_public_access_mode(810 origin1.clone(),811 CollectionId(1),812 AccessMode::AllowList813 ));814 assert_ok!(TemplateModule::add_to_allow_list(815 origin1.clone(),816 CollectionId(1),817 account(1)818 ));819 assert_ok!(TemplateModule::add_to_allow_list(820 origin1.clone(),821 CollectionId(1),822 account(2)823 ));824 assert_ok!(TemplateModule::add_to_allow_list(825 origin1.clone(),826 CollectionId(1),827 account(3)828 ));829830 // Account 1 approves account 2 for 1023 pieces of RFT 1831 assert_ok!(TemplateModule::approve(832 origin1,833 account(2),834 CollectionId(1),835 TokenId(1),836 1023837 ));838 assert_eq!(839 <pallet_refungible::Allowance<Test>>::get((840 CollectionId(1),841 TokenId(1),842 account(1),843 account(2)844 )),845 1023846 );847848 // Account 2 transfers 100 pieces of RFT 1 from account 1 to account 3849 assert_ok!(TemplateModule::transfer_from(850 origin2,851 account(1),852 account(3),853 CollectionId(1),854 TokenId(1),855 100856 ));857 assert_eq!(858 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),859 1860 );861 assert_eq!(862 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),863 1864 );865 assert_eq!(866 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),867 923868 );869 assert_eq!(870 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),871 100872 );873 assert_eq!(874 <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),875 true876 );877 assert_eq!(878 <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),879 true880 );881 assert_eq!(882 <pallet_refungible::Allowance<Test>>::get((883 CollectionId(1),884 TokenId(1),885 account(1),886 account(2)887 )),888 923889 );890 });891}892893#[test]894fn fungible_approve_and_transfer_from() {895 new_test_ext().execute_with(|| {896 let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));897898 let data = default_fungible_data();899 create_test_item(collection_id, &data.into());900901 let origin1 = Origin::signed(1);902 let origin2 = Origin::signed(2);903904 assert_ok!(TemplateModule::set_mint_permission(905 origin1.clone(),906 CollectionId(1),907 true908 ));909 assert_ok!(TemplateModule::set_public_access_mode(910 origin1.clone(),911 CollectionId(1),912 AccessMode::AllowList913 ));914 assert_ok!(TemplateModule::add_to_allow_list(915 origin1.clone(),916 CollectionId(1),917 account(1)918 ));919 assert_ok!(TemplateModule::add_to_allow_list(920 origin1.clone(),921 CollectionId(1),922 account(2)923 ));924 assert_ok!(TemplateModule::add_to_allow_list(925 origin1.clone(),926 CollectionId(1),927 account(3)928 ));929930 // do approve931 assert_ok!(TemplateModule::approve(932 origin1.clone(),933 account(2),934 CollectionId(1),935 TokenId(0),936 5937 ));938 assert_eq!(939 <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),940 5941 );942 assert_ok!(TemplateModule::approve(943 origin1,944 account(3),945 CollectionId(1),946 TokenId(0),947 5948 ));949 assert_eq!(950 <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),951 5952 );953 assert_eq!(954 <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(3))),955 5956 );957958 assert_ok!(TemplateModule::transfer_from(959 origin2.clone(),960 account(1),961 account(3),962 CollectionId(1),963 TokenId(0),964 4965 ));966967 assert_eq!(968 <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),969 1970 );971972 assert_noop!(973 TemplateModule::transfer_from(974 origin2,975 account(1),976 account(3),977 CollectionId(1),978 TokenId(0),979 4980 )981 .map_err(|e| e.error),982 CommonError::<Test>::ApprovedValueTooLow983 );984 });985}986987#[test]988fn change_collection_owner() {989 new_test_ext().execute_with(|| {990 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));991992 let origin1 = Origin::signed(1);993 assert_ok!(TemplateModule::change_collection_owner(994 origin1,995 collection_id,996 2997 ));998 assert_eq!(999 <pallet_common::CollectionById<Test>>::get(collection_id)1000 .unwrap()1001 .owner,1002 21003 );1004 });1005}10061007#[test]1008fn destroy_collection() {1009 new_test_ext().execute_with(|| {1010 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10111012 let origin1 = Origin::signed(1);1013 assert_ok!(TemplateModule::destroy_collection(origin1, collection_id));1014 });1015}10161017#[test]1018fn burn_nft_item() {1019 new_test_ext().execute_with(|| {1020 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10211022 let origin1 = Origin::signed(1);10231024 let data = default_nft_data();1025 create_test_item(collection_id, &data.into());10261027 // check balance (collection with id = 1, user id = 1)1028 assert_eq!(1029 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1030 11031 );10321033 // burn item1034 assert_ok!(TemplateModule::burn_item(1035 origin1.clone(),1036 collection_id,1037 TokenId(1),1038 11039 ));1040 assert_eq!(1041 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1042 01043 );1044 });1045}10461047#[test]1048fn burn_same_nft_item_twice() {1049 new_test_ext().execute_with(|| {1050 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10511052 let origin1 = Origin::signed(1);10531054 let data = default_nft_data();1055 create_test_item(collection_id, &data.into());10561057 // check balance (collection with id = 1, user id = 1)1058 assert_eq!(1059 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1060 11061 );10621063 // burn item1064 assert_ok!(TemplateModule::burn_item(1065 origin1.clone(),1066 collection_id,1067 TokenId(1),1068 11069 ));10701071 // burn item again1072 assert_noop!(1073 TemplateModule::burn_item(origin1, collection_id, TokenId(1), 1).map_err(|e| e.error),1074 CommonError::<Test>::TokenNotFound1075 );10761077 assert_eq!(1078 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1079 01080 );1081 });1082}10831084#[test]1085fn burn_fungible_item() {1086 new_test_ext().execute_with(|| {1087 let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));10881089 let origin1 = Origin::signed(1);1090 assert_ok!(TemplateModule::add_collection_admin(1091 origin1.clone(),1092 collection_id,1093 account(2)1094 ));10951096 let data = default_fungible_data();1097 create_test_item(collection_id, &data.into());10981099 // check balance (collection with id = 1, user id = 1)1100 assert_eq!(1101 <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1102 51103 );11041105 // burn item1106 assert_ok!(TemplateModule::burn_item(1107 origin1.clone(),1108 CollectionId(1),1109 TokenId(0),1110 51111 ));1112 assert_noop!(1113 TemplateModule::burn_item(origin1, CollectionId(1), TokenId(0), 5).map_err(|e| e.error),1114 CommonError::<Test>::TokenValueTooLow1115 );11161117 assert_eq!(1118 <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1119 01120 );1121 });1122}11231124#[test]1125fn burn_fungible_item_with_token_id() {1126 new_test_ext().execute_with(|| {1127 let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));11281129 let origin1 = Origin::signed(1);1130 assert_ok!(TemplateModule::add_collection_admin(1131 origin1.clone(),1132 collection_id,1133 account(2)1134 ));11351136 let data = default_fungible_data();1137 create_test_item(collection_id, &data.into());11381139 // check balance (collection with id = 1, user id = 1)1140 assert_eq!(1141 <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1142 51143 );11441145 // Try to burn item using Token ID1146 assert_noop!(1147 TemplateModule::burn_item(origin1, CollectionId(1), TokenId(1), 5).map_err(|e| e.error),1148 <pallet_fungible::Error::<Test>>::FungibleItemsHaveNoId1149 );1150 });1151}1152#[test]1153fn burn_refungible_item() {1154 new_test_ext().execute_with(|| {1155 let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));1156 let origin1 = Origin::signed(1);11571158 assert_ok!(TemplateModule::set_mint_permission(1159 origin1.clone(),1160 collection_id,1161 true1162 ));1163 assert_ok!(TemplateModule::set_public_access_mode(1164 origin1.clone(),1165 collection_id,1166 AccessMode::AllowList1167 ));1168 assert_ok!(TemplateModule::add_to_allow_list(1169 origin1.clone(),1170 collection_id,1171 account(1)1172 ));11731174 assert_ok!(TemplateModule::add_collection_admin(1175 origin1.clone(),1176 collection_id,1177 account(2)1178 ));11791180 let data = default_re_fungible_data();1181 create_test_item(collection_id, &data.into());11821183 // check balance (collection with id = 1, user id = 2)1184 assert_eq!(1185 <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),1186 11187 );1188 assert_eq!(1189 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),1190 10231191 );11921193 // burn item1194 assert_ok!(TemplateModule::burn_item(1195 origin1.clone(),1196 collection_id,1197 TokenId(1),1198 10231199 ));1200 assert_noop!(1201 TemplateModule::burn_item(origin1, collection_id, TokenId(1), 1023)1202 .map_err(|e| e.error),1203 CommonError::<Test>::TokenValueTooLow1204 );12051206 assert_eq!(1207 <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),1208 01209 );1210 });1211}12121213#[test]1214fn add_collection_admin() {1215 new_test_ext().execute_with(|| {1216 let collection1_id =1217 create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));1218 let origin1 = Origin::signed(1);12191220 // Add collection admins1221 assert_ok!(TemplateModule::add_collection_admin(1222 origin1.clone(),1223 collection1_id,1224 account(2)1225 ));1226 assert_ok!(TemplateModule::add_collection_admin(1227 origin1,1228 collection1_id,1229 account(3)1230 ));12311232 // Owner is not an admin by default1233 assert_eq!(1234 <pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(1))),1235 false1236 );1237 assert!(<pallet_common::IsAdmin<Test>>::get((1238 CollectionId(1),1239 account(2)1240 )));1241 assert!(<pallet_common::IsAdmin<Test>>::get((1242 CollectionId(1),1243 account(3)1244 )));1245 });1246}12471248#[test]1249fn remove_collection_admin() {1250 new_test_ext().execute_with(|| {1251 let collection1_id =1252 create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));1253 let origin1 = Origin::signed(1);1254 let origin2 = Origin::signed(2);12551256 // Add collection admins 2 and 31257 assert_ok!(TemplateModule::add_collection_admin(1258 origin1.clone(),1259 collection1_id,1260 account(2)1261 ));1262 assert_ok!(TemplateModule::add_collection_admin(1263 origin1,1264 collection1_id,1265 account(3)1266 ));12671268 assert!(<pallet_common::IsAdmin<Test>>::get((1269 CollectionId(1),1270 account(2)1271 )));1272 assert!(<pallet_common::IsAdmin<Test>>::get((1273 CollectionId(1),1274 account(3)1275 )));12761277 // remove admin 31278 assert_ok!(TemplateModule::remove_collection_admin(1279 origin2,1280 CollectionId(1),1281 account(3)1282 ));12831284 // 2 is still admin, 3 is not an admin anymore1285 assert!(<pallet_common::IsAdmin<Test>>::get((1286 CollectionId(1),1287 account(2)1288 )));1289 assert_eq!(1290 <pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(3))),1291 false1292 );1293 });1294}12951296#[test]1297fn balance_of() {1298 new_test_ext().execute_with(|| {1299 let nft_collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1300 let fungible_collection_id =1301 create_test_collection(&CollectionMode::Fungible(3), CollectionId(2));1302 let re_fungible_collection_id =1303 create_test_collection(&CollectionMode::ReFungible, CollectionId(3));13041305 // check balance before1306 assert_eq!(1307 <pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),1308 01309 );1310 assert_eq!(1311 <pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),1312 01313 );1314 assert_eq!(1315 <pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),1316 01317 );13181319 let nft_data = default_nft_data();1320 create_test_item(nft_collection_id, &nft_data.into());13211322 let fungible_data = default_fungible_data();1323 create_test_item(fungible_collection_id, &fungible_data.into());13241325 let re_fungible_data = default_re_fungible_data();1326 create_test_item(re_fungible_collection_id, &re_fungible_data.into());13271328 // check balance (collection with id = 1, user id = 1)1329 assert_eq!(1330 <pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),1331 11332 );1333 assert_eq!(1334 <pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),1335 51336 );1337 assert_eq!(1338 <pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),1339 11340 );13411342 assert_eq!(1343 <pallet_nonfungible::Owned<Test>>::get((nft_collection_id, account(1), TokenId(1))),1344 true1345 );1346 assert_eq!(1347 <pallet_refungible::Owned<Test>>::get((1348 re_fungible_collection_id,1349 account(1),1350 TokenId(1)1351 )),1352 true1353 );1354 });1355}13561357#[test]1358fn approve() {1359 new_test_ext().execute_with(|| {1360 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));13611362 let data = default_nft_data();1363 create_test_item(collection_id, &data.into());13641365 let origin1 = Origin::signed(1);13661367 // approve1368 assert_ok!(TemplateModule::approve(1369 origin1,1370 account(2),1371 CollectionId(1),1372 TokenId(1),1373 11374 ));1375 assert_eq!(1376 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1377 account(2)1378 );1379 });1380}13811382#[test]1383fn transfer_from() {1384 new_test_ext().execute_with(|| {1385 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1386 let origin1 = Origin::signed(1);1387 let origin2 = Origin::signed(2);13881389 let data = default_nft_data();1390 create_test_item(collection_id, &data.into());13911392 // approve1393 assert_ok!(TemplateModule::approve(1394 origin1.clone(),1395 account(2),1396 CollectionId(1),1397 TokenId(1),1398 11399 ));1400 assert_eq!(1401 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1402 account(2)1403 );14041405 assert_ok!(TemplateModule::set_mint_permission(1406 origin1.clone(),1407 CollectionId(1),1408 true1409 ));1410 assert_ok!(TemplateModule::set_public_access_mode(1411 origin1.clone(),1412 CollectionId(1),1413 AccessMode::AllowList1414 ));1415 assert_ok!(TemplateModule::add_to_allow_list(1416 origin1.clone(),1417 CollectionId(1),1418 account(1)1419 ));1420 assert_ok!(TemplateModule::add_to_allow_list(1421 origin1.clone(),1422 CollectionId(1),1423 account(2)1424 ));1425 assert_ok!(TemplateModule::add_to_allow_list(1426 origin1,1427 CollectionId(1),1428 account(3)1429 ));14301431 assert_ok!(TemplateModule::transfer_from(1432 origin2,1433 account(1),1434 account(2),1435 CollectionId(1),1436 TokenId(1),1437 11438 ));14391440 // after transfer1441 assert_eq!(1442 <pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(1))),1443 01444 );1445 assert_eq!(1446 <pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(2))),1447 11448 );1449 });1450}14511452// #endregion14531454// Coverage tests region1455// #region14561457#[test]1458fn owner_can_add_address_to_allow_list() {1459 new_test_ext().execute_with(|| {1460 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));14611462 let origin1 = Origin::signed(1);1463 assert_ok!(TemplateModule::add_to_allow_list(1464 origin1,1465 collection_id,1466 account(2)1467 ));1468 assert!(<pallet_common::Allowlist<Test>>::get((1469 collection_id,1470 account(2)1471 )));1472 });1473}14741475#[test]1476fn admin_can_add_address_to_allow_list() {1477 new_test_ext().execute_with(|| {1478 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1479 let origin1 = Origin::signed(1);1480 let origin2 = Origin::signed(2);14811482 assert_ok!(TemplateModule::add_collection_admin(1483 origin1,1484 collection_id,1485 account(2)1486 ));1487 assert_ok!(TemplateModule::add_to_allow_list(1488 origin2,1489 collection_id,1490 account(3)1491 ));1492 assert!(<pallet_common::Allowlist<Test>>::get((1493 collection_id,1494 account(3)1495 )));1496 });1497}14981499#[test]1500fn nonprivileged_user_cannot_add_address_to_allow_list() {1501 new_test_ext().execute_with(|| {1502 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15031504 let origin2 = Origin::signed(2);1505 assert_noop!(1506 TemplateModule::add_to_allow_list(origin2, collection_id, account(3)),1507 CommonError::<Test>::NoPermission1508 );1509 });1510}15111512#[test]1513fn nobody_can_add_address_to_allow_list_of_nonexisting_collection() {1514 new_test_ext().execute_with(|| {1515 let origin1 = Origin::signed(1);15161517 assert_noop!(1518 TemplateModule::add_to_allow_list(origin1, CollectionId(1), account(2)),1519 CommonError::<Test>::CollectionNotFound1520 );1521 });1522}15231524#[test]1525fn nobody_can_add_address_to_allow_list_of_deleted_collection() {1526 new_test_ext().execute_with(|| {1527 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15281529 let origin1 = Origin::signed(1);1530 assert_ok!(TemplateModule::destroy_collection(1531 origin1.clone(),1532 collection_id1533 ));1534 assert_noop!(1535 TemplateModule::add_to_allow_list(origin1, collection_id, account(2)),1536 CommonError::<Test>::CollectionNotFound1537 );1538 });1539}15401541// If address is already added to allow list, nothing happens1542#[test]1543fn address_is_already_added_to_allow_list() {1544 new_test_ext().execute_with(|| {1545 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1546 let origin1 = Origin::signed(1);15471548 assert_ok!(TemplateModule::add_to_allow_list(1549 origin1.clone(),1550 collection_id,1551 account(2)1552 ));1553 assert_ok!(TemplateModule::add_to_allow_list(1554 origin1,1555 collection_id,1556 account(2)1557 ));1558 assert!(<pallet_common::Allowlist<Test>>::get((1559 collection_id,1560 account(2)1561 )));1562 });1563}15641565#[test]1566fn owner_can_remove_address_from_allow_list() {1567 new_test_ext().execute_with(|| {1568 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15691570 let origin1 = Origin::signed(1);1571 assert_ok!(TemplateModule::add_to_allow_list(1572 origin1.clone(),1573 collection_id,1574 account(2)1575 ));1576 assert_ok!(TemplateModule::remove_from_allow_list(1577 origin1,1578 collection_id,1579 account(2)1580 ));1581 assert_eq!(1582 <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1583 false1584 );1585 });1586}15871588#[test]1589fn admin_can_remove_address_from_allow_list() {1590 new_test_ext().execute_with(|| {1591 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1592 let origin1 = Origin::signed(1);1593 let origin2 = Origin::signed(2);15941595 // Owner adds admin1596 assert_ok!(TemplateModule::add_collection_admin(1597 origin1.clone(),1598 collection_id,1599 account(2)1600 ));16011602 // Owner adds address 3 to allow list1603 assert_ok!(TemplateModule::add_to_allow_list(1604 origin1,1605 collection_id,1606 account(3)1607 ));16081609 // Admin removes address 3 from allow list1610 assert_ok!(TemplateModule::remove_from_allow_list(1611 origin2,1612 collection_id,1613 account(3)1614 ));1615 assert_eq!(1616 <pallet_common::Allowlist<Test>>::get((collection_id, account(3))),1617 false1618 );1619 });1620}16211622#[test]1623fn nonprivileged_user_cannot_remove_address_from_allow_list() {1624 new_test_ext().execute_with(|| {1625 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1626 let origin1 = Origin::signed(1);1627 let origin2 = Origin::signed(2);16281629 assert_ok!(TemplateModule::add_to_allow_list(1630 origin1,1631 collection_id,1632 account(2)1633 ));1634 assert_noop!(1635 TemplateModule::remove_from_allow_list(origin2, collection_id, account(2)),1636 CommonError::<Test>::NoPermission1637 );1638 assert!(<pallet_common::Allowlist<Test>>::get((1639 collection_id,1640 account(2)1641 )));1642 });1643}16441645#[test]1646fn nobody_can_remove_address_from_allow_list_of_nonexisting_collection() {1647 new_test_ext().execute_with(|| {1648 let origin1 = Origin::signed(1);16491650 assert_noop!(1651 TemplateModule::remove_from_allow_list(origin1, CollectionId(1), account(2)),1652 CommonError::<Test>::CollectionNotFound1653 );1654 });1655}16561657#[test]1658fn nobody_can_remove_address_from_allow_list_of_deleted_collection() {1659 new_test_ext().execute_with(|| {1660 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1661 let origin1 = Origin::signed(1);1662 let origin2 = Origin::signed(2);16631664 // Add account 2 to allow list1665 assert_ok!(TemplateModule::add_to_allow_list(1666 origin1.clone(),1667 collection_id,1668 account(2)1669 ));16701671 // Account 2 is in collection allow-list1672 assert!(<pallet_common::Allowlist<Test>>::get((1673 collection_id,1674 account(2)1675 )));16761677 // Destroy collection1678 assert_ok!(TemplateModule::destroy_collection(origin1, collection_id));16791680 // Attempt to remove account 2 from collection allow-list => error1681 assert_noop!(1682 TemplateModule::remove_from_allow_list(origin2, collection_id, account(2)),1683 CommonError::<Test>::CollectionNotFound1684 );16851686 // Account 2 is not found in collection allow-list anyway1687 assert_eq!(1688 <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1689 false1690 );1691 });1692}16931694// If address is already removed from allow list, nothing happens1695#[test]1696fn address_is_already_removed_from_allow_list() {1697 new_test_ext().execute_with(|| {1698 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1699 let origin1 = Origin::signed(1);17001701 assert_ok!(TemplateModule::add_to_allow_list(1702 origin1.clone(),1703 collection_id,1704 account(2)1705 ));1706 assert_ok!(TemplateModule::remove_from_allow_list(1707 origin1.clone(),1708 collection_id,1709 account(2)1710 ));1711 assert_eq!(1712 <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1713 false1714 );1715 assert_ok!(TemplateModule::remove_from_allow_list(1716 origin1,1717 collection_id,1718 account(2)1719 ));1720 assert_eq!(1721 <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1722 false1723 );1724 });1725}17261727// If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom (2 tests)1728#[test]1729fn allow_list_test_1() {1730 new_test_ext().execute_with(|| {1731 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));17321733 let origin1 = Origin::signed(1);17341735 let data = default_nft_data();1736 create_test_item(collection_id, &data.into());17371738 assert_ok!(TemplateModule::set_public_access_mode(1739 origin1.clone(),1740 collection_id,1741 AccessMode::AllowList1742 ));1743 assert_ok!(TemplateModule::add_to_allow_list(1744 origin1.clone(),1745 collection_id,1746 account(2)1747 ));17481749 assert_noop!(1750 TemplateModule::transfer(origin1, account(3), CollectionId(1), TokenId(1), 1)1751 .map_err(|e| e.error),1752 CommonError::<Test>::AddressNotInAllowlist1753 );1754 });1755}17561757#[test]1758fn allow_list_test_2() {1759 new_test_ext().execute_with(|| {1760 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1761 let origin1 = Origin::signed(1);17621763 let data = default_nft_data();1764 create_test_item(collection_id, &data.into());17651766 assert_ok!(TemplateModule::set_public_access_mode(1767 origin1.clone(),1768 collection_id,1769 AccessMode::AllowList1770 ));1771 assert_ok!(TemplateModule::add_to_allow_list(1772 origin1.clone(),1773 collection_id,1774 account(1)1775 ));1776 assert_ok!(TemplateModule::add_to_allow_list(1777 origin1.clone(),1778 collection_id,1779 account(2)1780 ));17811782 // do approve1783 assert_ok!(TemplateModule::approve(1784 origin1.clone(),1785 account(1),1786 collection_id,1787 TokenId(1),1788 11789 ));1790 assert_eq!(1791 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1792 account(1)1793 );17941795 assert_ok!(TemplateModule::remove_from_allow_list(1796 origin1.clone(),1797 collection_id,1798 account(1)1799 ));18001801 assert_noop!(1802 TemplateModule::transfer_from(1803 origin1,1804 account(1),1805 account(3),1806 CollectionId(1),1807 TokenId(1),1808 11809 )1810 .map_err(|e| e.error),1811 CommonError::<Test>::AddressNotInAllowlist1812 );1813 });1814}18151816// If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom (2 tests)1817#[test]1818fn allow_list_test_3() {1819 new_test_ext().execute_with(|| {1820 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18211822 let origin1 = Origin::signed(1);18231824 let data = default_nft_data();1825 create_test_item(collection_id, &data.into());18261827 assert_ok!(TemplateModule::set_public_access_mode(1828 origin1.clone(),1829 collection_id,1830 AccessMode::AllowList1831 ));1832 assert_ok!(TemplateModule::add_to_allow_list(1833 origin1.clone(),1834 collection_id,1835 account(1)1836 ));18371838 assert_noop!(1839 TemplateModule::transfer(origin1, account(3), collection_id, TokenId(1), 1)1840 .map_err(|e| e.error),1841 CommonError::<Test>::AddressNotInAllowlist1842 );1843 });1844}18451846#[test]1847fn allow_list_test_4() {1848 new_test_ext().execute_with(|| {1849 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18501851 let origin1 = Origin::signed(1);18521853 let data = default_nft_data();1854 create_test_item(collection_id, &data.into());18551856 assert_ok!(TemplateModule::set_public_access_mode(1857 origin1.clone(),1858 collection_id,1859 AccessMode::AllowList1860 ));1861 assert_ok!(TemplateModule::add_to_allow_list(1862 origin1.clone(),1863 collection_id,1864 account(1)1865 ));1866 assert_ok!(TemplateModule::add_to_allow_list(1867 origin1.clone(),1868 collection_id,1869 account(2)1870 ));18711872 // do approve1873 assert_ok!(TemplateModule::approve(1874 origin1.clone(),1875 account(1),1876 collection_id,1877 TokenId(1),1878 11879 ));1880 assert_eq!(1881 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1882 account(1)1883 );18841885 assert_ok!(TemplateModule::remove_from_allow_list(1886 origin1.clone(),1887 collection_id,1888 account(2)1889 ));18901891 assert_noop!(1892 TemplateModule::transfer_from(1893 origin1,1894 account(1),1895 account(3),1896 collection_id,1897 TokenId(1),1898 11899 )1900 .map_err(|e| e.error),1901 CommonError::<Test>::AddressNotInAllowlist1902 );1903 });1904}19051906// If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)1907#[test]1908fn allow_list_test_5() {1909 new_test_ext().execute_with(|| {1910 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19111912 let origin1 = Origin::signed(1);19131914 let data = default_nft_data();1915 create_test_item(collection_id, &data.into());19161917 assert_ok!(TemplateModule::set_public_access_mode(1918 origin1.clone(),1919 collection_id,1920 AccessMode::AllowList1921 ));1922 assert_noop!(1923 TemplateModule::burn_item(origin1.clone(), CollectionId(1), TokenId(1), 1)1924 .map_err(|e| e.error),1925 CommonError::<Test>::AddressNotInAllowlist1926 );1927 });1928}19291930// If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method).1931#[test]1932fn allow_list_test_6() {1933 new_test_ext().execute_with(|| {1934 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19351936 let origin1 = Origin::signed(1);19371938 let data = default_nft_data();1939 create_test_item(collection_id, &data.into());19401941 assert_ok!(TemplateModule::set_public_access_mode(1942 origin1.clone(),1943 collection_id,1944 AccessMode::AllowList1945 ));19461947 // do approve1948 assert_noop!(1949 TemplateModule::approve(origin1, account(1), CollectionId(1), TokenId(1), 1)1950 .map_err(|e| e.error),1951 CommonError::<Test>::AddressNotInAllowlist1952 );1953 });1954}19551956// If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests) and1957// tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests)1958#[test]1959fn allow_list_test_7() {1960 new_test_ext().execute_with(|| {1961 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19621963 let data = default_nft_data();1964 create_test_item(collection_id, &data.into());19651966 let origin1 = Origin::signed(1);19671968 assert_ok!(TemplateModule::set_public_access_mode(1969 origin1.clone(),1970 collection_id,1971 AccessMode::AllowList1972 ));1973 assert_ok!(TemplateModule::add_to_allow_list(1974 origin1.clone(),1975 collection_id,1976 account(1)1977 ));1978 assert_ok!(TemplateModule::add_to_allow_list(1979 origin1.clone(),1980 collection_id,1981 account(2)1982 ));19831984 assert_ok!(TemplateModule::transfer(1985 origin1,1986 account(2),1987 CollectionId(1),1988 TokenId(1),1989 11990 ));1991 });1992}19931994#[test]1995fn allow_list_test_8() {1996 new_test_ext().execute_with(|| {1997 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19981999 // Create NFT for account 12000 let data = default_nft_data();2001 create_test_item(collection_id, &data.into());20022003 let origin1 = Origin::signed(1);20042005 // Toggle Allow List mode and add accounts 1 and 22006 assert_ok!(TemplateModule::set_public_access_mode(2007 origin1.clone(),2008 collection_id,2009 AccessMode::AllowList2010 ));2011 assert_ok!(TemplateModule::add_to_allow_list(2012 origin1.clone(),2013 collection_id,2014 account(1)2015 ));2016 assert_ok!(TemplateModule::add_to_allow_list(2017 origin1.clone(),2018 collection_id,2019 account(2)2020 ));20212022 // Sself-approve account 1 for NFT 12023 assert_ok!(TemplateModule::approve(2024 origin1.clone(),2025 account(1),2026 CollectionId(1),2027 TokenId(1),2028 12029 ));2030 assert_eq!(2031 <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),2032 account(1)2033 );20342035 // Transfer from 1 to 22036 assert_ok!(TemplateModule::transfer_from(2037 origin1,2038 account(1),2039 account(2),2040 CollectionId(1),2041 TokenId(1),2042 12043 ));2044 });2045}20462047// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner.2048#[test]2049fn allow_list_test_9() {2050 new_test_ext().execute_with(|| {2051 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2052 let origin1 = Origin::signed(1);20532054 assert_ok!(TemplateModule::set_public_access_mode(2055 origin1.clone(),2056 collection_id,2057 AccessMode::AllowList2058 ));2059 assert_ok!(TemplateModule::set_mint_permission(2060 origin1,2061 collection_id,2062 false2063 ));20642065 let data = default_nft_data();2066 create_test_item(collection_id, &data.into());2067 });2068}20692070// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin.2071#[test]2072fn allow_list_test_10() {2073 new_test_ext().execute_with(|| {2074 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));20752076 let origin1 = Origin::signed(1);2077 let origin2 = Origin::signed(2);20782079 assert_ok!(TemplateModule::set_public_access_mode(2080 origin1.clone(),2081 collection_id,2082 AccessMode::AllowList2083 ));2084 assert_ok!(TemplateModule::set_mint_permission(2085 origin1.clone(),2086 collection_id,2087 false2088 ));20892090 assert_ok!(TemplateModule::add_collection_admin(2091 origin1,2092 collection_id,2093 account(2)2094 ));20952096 assert_ok!(TemplateModule::create_item(2097 origin2,2098 collection_id,2099 account(2),2100 default_nft_data().into()2101 ));2102 });2103}21042105// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow listed address.2106#[test]2107fn allow_list_test_11() {2108 new_test_ext().execute_with(|| {2109 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21102111 let origin1 = Origin::signed(1);2112 let origin2 = Origin::signed(2);21132114 assert_ok!(TemplateModule::set_public_access_mode(2115 origin1.clone(),2116 collection_id,2117 AccessMode::AllowList2118 ));2119 assert_ok!(TemplateModule::set_mint_permission(2120 origin1.clone(),2121 collection_id,2122 false2123 ));2124 assert_ok!(TemplateModule::add_to_allow_list(2125 origin1,2126 collection_id,2127 account(2)2128 ));21292130 assert_noop!(2131 TemplateModule::create_item(2132 origin2,2133 CollectionId(1),2134 account(2),2135 default_nft_data().into()2136 )2137 .map_err(|e| e.error),2138 CommonError::<Test>::PublicMintingNotAllowed2139 );2140 });2141}21422143// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address.2144#[test]2145fn allow_list_test_12() {2146 new_test_ext().execute_with(|| {2147 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21482149 let origin1 = Origin::signed(1);2150 let origin2 = Origin::signed(2);21512152 assert_ok!(TemplateModule::set_public_access_mode(2153 origin1.clone(),2154 collection_id,2155 AccessMode::AllowList2156 ));2157 assert_ok!(TemplateModule::set_mint_permission(2158 origin1,2159 collection_id,2160 false2161 ));21622163 assert_noop!(2164 TemplateModule::create_item(2165 origin2,2166 CollectionId(1),2167 account(2),2168 default_nft_data().into()2169 )2170 .map_err(|e| e.error),2171 CommonError::<Test>::PublicMintingNotAllowed2172 );2173 });2174}21752176// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner.2177#[test]2178fn allow_list_test_13() {2179 new_test_ext().execute_with(|| {2180 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21812182 let origin1 = Origin::signed(1);21832184 assert_ok!(TemplateModule::set_public_access_mode(2185 origin1.clone(),2186 collection_id,2187 AccessMode::AllowList2188 ));2189 assert_ok!(TemplateModule::set_mint_permission(2190 origin1,2191 collection_id,2192 true2193 ));21942195 let data = default_nft_data();2196 create_test_item(collection_id, &data.into());2197 });2198}21992200// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin.2201#[test]2202fn allow_list_test_14() {2203 new_test_ext().execute_with(|| {2204 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22052206 let origin1 = Origin::signed(1);2207 let origin2 = Origin::signed(2);22082209 assert_ok!(TemplateModule::set_public_access_mode(2210 origin1.clone(),2211 collection_id,2212 AccessMode::AllowList2213 ));2214 assert_ok!(TemplateModule::set_mint_permission(2215 origin1.clone(),2216 collection_id,2217 true2218 ));22192220 assert_ok!(TemplateModule::add_collection_admin(2221 origin1,2222 collection_id,2223 account(2)2224 ));22252226 assert_ok!(TemplateModule::create_item(2227 origin2,2228 collection_id,2229 account(2),2230 default_nft_data().into()2231 ));2232 });2233}22342235// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address.2236#[test]2237fn allow_list_test_15() {2238 new_test_ext().execute_with(|| {2239 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22402241 let origin1 = Origin::signed(1);2242 let origin2 = Origin::signed(2);22432244 assert_ok!(TemplateModule::set_public_access_mode(2245 origin1.clone(),2246 collection_id,2247 AccessMode::AllowList2248 ));2249 assert_ok!(TemplateModule::set_mint_permission(2250 origin1,2251 collection_id,2252 true2253 ));22542255 assert_noop!(2256 TemplateModule::create_item(2257 origin2,2258 collection_id,2259 account(2),2260 default_nft_data().into()2261 )2262 .map_err(|e| e.error),2263 CommonError::<Test>::AddressNotInAllowlist2264 );2265 });2266}22672268// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address.2269#[test]2270fn allow_list_test_16() {2271 new_test_ext().execute_with(|| {2272 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22732274 let origin1 = Origin::signed(1);2275 let origin2 = Origin::signed(2);22762277 assert_ok!(TemplateModule::set_public_access_mode(2278 origin1.clone(),2279 collection_id,2280 AccessMode::AllowList2281 ));2282 assert_ok!(TemplateModule::set_mint_permission(2283 origin1.clone(),2284 collection_id,2285 true2286 ));2287 assert_ok!(TemplateModule::add_to_allow_list(2288 origin1,2289 collection_id,2290 account(2)2291 ));22922293 assert_ok!(TemplateModule::create_item(2294 origin2,2295 collection_id,2296 account(2),2297 default_nft_data().into()2298 ));2299 });2300}23012302// Total number of collections. Positive test2303#[test]2304fn total_number_collections_bound() {2305 new_test_ext().execute_with(|| {2306 create_test_collection(&CollectionMode::NFT, CollectionId(1));2307 });2308}23092310#[test]2311fn create_max_collections() {2312 new_test_ext().execute_with(|| {2313 for i in 1..COLLECTION_NUMBER_LIMIT {2314 create_test_collection(&CollectionMode::NFT, CollectionId(i));2315 }2316 });2317}23182319// Total number of collections. Negative test2320#[test]2321fn total_number_collections_bound_neg() {2322 new_test_ext().execute_with(|| {2323 let origin1 = Origin::signed(1);23242325 for i in 1..=COLLECTION_NUMBER_LIMIT {2326 create_test_collection(&CollectionMode::NFT, CollectionId(i));2327 }23282329 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();2330 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();2331 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();23322333 let data: CreateCollectionData<u64> = CreateCollectionData {2334 name: col_name1.try_into().unwrap(),2335 description: col_desc1.try_into().unwrap(),2336 token_prefix: token_prefix1.try_into().unwrap(),2337 mode: CollectionMode::NFT,2338 ..Default::default()2339 };23402341 // 11-th collection in chain. Expects error2342 assert_noop!(2343 TemplateModule::create_collection_ex(origin1, data),2344 CommonError::<Test>::TotalCollectionsLimitExceeded2345 );2346 });2347}23482349// Owned tokens by a single address. Positive test2350#[test]2351fn owned_tokens_bound() {2352 new_test_ext().execute_with(|| {2353 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23542355 let data = default_nft_data();2356 create_test_item(collection_id, &data.clone().into());2357 create_test_item(collection_id, &data.into());2358 });2359}23602361// Owned tokens by a single address. Negotive test2362#[test]2363fn owned_tokens_bound_neg() {2364 new_test_ext().execute_with(|| {2365 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23662367 let origin1 = Origin::signed(1);23682369 for _ in 1..=MAX_TOKEN_OWNERSHIP {2370 let data = default_nft_data();2371 create_test_item(collection_id, &data.clone().into());2372 }23732374 let data = default_nft_data();2375 assert_noop!(2376 TemplateModule::create_item(origin1, CollectionId(1), account(1), data.into())2377 .map_err(|e| e.error),2378 CommonError::<Test>::AccountTokenLimitExceeded2379 );2380 });2381}23822383// Number of collection admins. Positive test2384#[test]2385fn collection_admins_bound() {2386 new_test_ext().execute_with(|| {2387 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23882389 let origin1 = Origin::signed(1);23902391 assert_ok!(TemplateModule::add_collection_admin(2392 origin1.clone(),2393 collection_id,2394 account(2)2395 ));2396 assert_ok!(TemplateModule::add_collection_admin(2397 origin1,2398 collection_id,2399 account(3)2400 ));2401 });2402}24032404// Number of collection admins. Negotive test2405#[test]2406fn collection_admins_bound_neg() {2407 new_test_ext().execute_with(|| {2408 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));24092410 let origin1 = Origin::signed(1);24112412 for i in 0..COLLECTION_ADMINS_LIMIT {2413 assert_ok!(TemplateModule::add_collection_admin(2414 origin1.clone(),2415 collection_id,2416 account((2 + i).into())2417 ));2418 }2419 assert_noop!(2420 TemplateModule::add_collection_admin(2421 origin1,2422 collection_id,2423 account((3 + COLLECTION_ADMINS_LIMIT).into())2424 ),2425 CommonError::<Test>::CollectionAdminCountExceeded2426 );2427 });2428}2429// #endregion24302431#[test]2432fn set_const_on_chain_schema() {2433 new_test_ext().execute_with(|| {2434 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));24352436 let origin1 = Origin::signed(1);2437 assert_ok!(TemplateModule::set_const_on_chain_schema(2438 origin1,2439 collection_id,2440 b"test const on chain schema".to_vec().try_into().unwrap()2441 ));24422443 assert_eq!(2444 <pallet_common::CollectionById<Test>>::get(collection_id)2445 .unwrap()2446 .const_on_chain_schema,2447 b"test const on chain schema".to_vec()2448 );2449 assert_eq!(2450 <pallet_common::CollectionById<Test>>::get(collection_id)2451 .unwrap()2452 .variable_on_chain_schema,2453 b"".to_vec()2454 );2455 });2456}24572458#[test]2459fn set_variable_on_chain_schema() {2460 new_test_ext().execute_with(|| {2461 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));24622463 let origin1 = Origin::signed(1);2464 assert_ok!(TemplateModule::set_variable_on_chain_schema(2465 origin1,2466 collection_id,2467 b"test variable on chain schema"2468 .to_vec()2469 .try_into()2470 .unwrap()2471 ));24722473 assert_eq!(2474 <pallet_common::CollectionById<Test>>::get(collection_id)2475 .unwrap()2476 .const_on_chain_schema,2477 b"".to_vec()2478 );2479 assert_eq!(2480 <pallet_common::CollectionById<Test>>::get(collection_id)2481 .unwrap()2482 .variable_on_chain_schema,2483 b"test variable on chain schema".to_vec()2484 );2485 });2486}24872488#[test]2489fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {2490 new_test_ext().execute_with(|| {2491 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));24922493 let origin1 = Origin::signed(1);24942495 let data = default_nft_data();2496 create_test_item(CollectionId(1), &data.into());24972498 let variable_data = b"test data".to_vec();2499 assert_ok!(TemplateModule::set_variable_meta_data(2500 origin1,2501 collection_id,2502 TokenId(1),2503 variable_data.clone().try_into().unwrap()2504 ));25052506 assert_eq!(2507 <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))2508 .unwrap()2509 .variable_data,2510 variable_data2511 );2512 });2513}25142515#[test]2516fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {2517 new_test_ext().execute_with(|| {2518 let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));25192520 let origin1 = Origin::signed(1);25212522 let data = default_re_fungible_data();2523 create_test_item(collection_id, &data.into());25242525 let variable_data = b"test data".to_vec();2526 assert_ok!(TemplateModule::set_variable_meta_data(2527 origin1,2528 collection_id,2529 TokenId(1),2530 variable_data.clone().try_into().unwrap()2531 ));25322533 assert_eq!(2534 <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1))).variable_data,2535 variable_data2536 );2537 });2538}25392540#[test]2541fn set_variable_meta_data_on_fungible_token_fails() {2542 new_test_ext().execute_with(|| {2543 let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));25442545 let origin1 = Origin::signed(1);25462547 let data = default_fungible_data();2548 create_test_item(collection_id, &data.into());25492550 let variable_data = b"test data".to_vec();2551 assert_noop!(2552 TemplateModule::set_variable_meta_data(2553 origin1,2554 collection_id,2555 TokenId(0),2556 variable_data.try_into().unwrap()2557 )2558 .map_err(|e| e.error),2559 <pallet_fungible::Error<Test>>::FungibleItemsDontHaveData2560 );2561 });2562}25632564#[test]2565fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() {2566 new_test_ext().execute_with(|| {2567 //default_limits();25682569 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));25702571 let origin1 = Origin::signed(1);25722573 let data = default_nft_data();2574 create_test_item(collection_id, &data.into());25752576 assert_ok!(TemplateModule::set_meta_update_permission_flag(2577 origin1.clone(),2578 collection_id,2579 MetaUpdatePermission::ItemOwner,2580 ));25812582 let variable_data = b"ten chars.".to_vec();2583 assert_ok!(TemplateModule::set_variable_meta_data(2584 origin1,2585 collection_id,2586 TokenId(1),2587 variable_data.clone().try_into().unwrap()2588 ));25892590 assert_eq!(2591 <pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))2592 .unwrap()2593 .variable_data,2594 variable_data2595 );2596 });2597}25982599#[test]2600fn collection_transfer_flag_works() {2601 new_test_ext().execute_with(|| {2602 let origin1 = Origin::signed(1);26032604 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2605 assert_ok!(TemplateModule::set_transfers_enabled_flag(2606 origin1,2607 collection_id,2608 true2609 ));26102611 let data = default_nft_data();2612 create_test_item(collection_id, &data.into());2613 assert_eq!(2614 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2615 12616 );2617 assert_eq!(2618 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2619 true2620 );26212622 let origin1 = Origin::signed(1);26232624 // default scenario2625 assert_ok!(TemplateModule::transfer(2626 origin1,2627 account(2),2628 collection_id,2629 TokenId(1),2630 12631 ));2632 assert_eq!(2633 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2634 false2635 );2636 assert_eq!(2637 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),2638 true2639 );2640 assert_eq!(2641 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2642 02643 );2644 assert_eq!(2645 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),2646 12647 );2648 });2649}26502651#[test]2652fn set_variable_meta_data_on_nft_with_admin_flag() {2653 new_test_ext().execute_with(|| {2654 // default_limits();26552656 let collection_id =2657 create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));26582659 let origin1 = Origin::signed(1);2660 let origin2 = Origin::signed(2);26612662 assert_ok!(TemplateModule::set_mint_permission(2663 origin2.clone(),2664 collection_id,2665 true2666 ));2667 assert_ok!(TemplateModule::add_to_allow_list(2668 origin2.clone(),2669 collection_id,2670 account(1)2671 ));26722673 assert_ok!(TemplateModule::add_collection_admin(2674 origin2.clone(),2675 collection_id,2676 account(1)2677 ));26782679 let data = default_nft_data();2680 create_test_item(collection_id, &data.into());26812682 assert_ok!(TemplateModule::set_meta_update_permission_flag(2683 origin2.clone(),2684 collection_id,2685 MetaUpdatePermission::Admin,2686 ));26872688 let variable_data = b"test.".to_vec();2689 assert_ok!(TemplateModule::set_variable_meta_data(2690 origin1,2691 collection_id,2692 TokenId(1),2693 variable_data.clone().try_into().unwrap()2694 ));26952696 assert_eq!(2697 <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))2698 .unwrap()2699 .variable_data,2700 variable_data2701 );2702 });2703}27042705#[test]2706fn set_variable_meta_data_on_nft_with_admin_flag_neg() {2707 new_test_ext().execute_with(|| {2708 // default_limits();27092710 let collection_id =2711 create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));27122713 let origin1 = Origin::signed(1);2714 let origin2 = Origin::signed(2);27152716 assert_ok!(TemplateModule::set_mint_permission(2717 origin2.clone(),2718 collection_id,2719 true2720 ));2721 assert_ok!(TemplateModule::add_to_allow_list(2722 origin2.clone(),2723 collection_id,2724 account(1)2725 ));27262727 let data = default_nft_data();2728 create_test_item(collection_id, &data.into());27292730 assert_ok!(TemplateModule::set_meta_update_permission_flag(2731 origin2.clone(),2732 collection_id,2733 MetaUpdatePermission::Admin,2734 ));27352736 let variable_data = b"test.".to_vec();2737 assert_noop!(2738 TemplateModule::set_variable_meta_data(2739 origin1,2740 collection_id,2741 TokenId(1),2742 variable_data.try_into().unwrap()2743 )2744 .map_err(|e| e.error),2745 CommonError::<Test>::NoPermission2746 );2747 });2748}27492750#[test]2751fn set_variable_meta_flag_after_freeze() {2752 new_test_ext().execute_with(|| {2753 // default_limits();27542755 let collection_id =2756 create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));27572758 let origin2 = Origin::signed(2);27592760 assert_ok!(TemplateModule::set_meta_update_permission_flag(2761 origin2.clone(),2762 collection_id,2763 MetaUpdatePermission::None,2764 ));2765 assert_noop!(2766 TemplateModule::set_meta_update_permission_flag(2767 origin2.clone(),2768 collection_id,2769 MetaUpdatePermission::Admin2770 ),2771 CommonError::<Test>::MetadataFlagFrozen2772 );2773 });2774}27752776#[test]2777fn set_variable_meta_data_on_nft_with_none_flag_neg() {2778 new_test_ext().execute_with(|| {2779 // default_limits();27802781 let collection_id =2782 create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));2783 let origin1 = Origin::signed(1);27842785 let data = default_nft_data();2786 create_test_item(collection_id, &data.into());27872788 assert_ok!(TemplateModule::set_meta_update_permission_flag(2789 origin1.clone(),2790 collection_id,2791 MetaUpdatePermission::None,2792 ));27932794 let variable_data = b"test.".to_vec();2795 assert_noop!(2796 TemplateModule::set_variable_meta_data(2797 origin1.clone(),2798 collection_id,2799 TokenId(1),2800 variable_data.try_into().unwrap()2801 )2802 .map_err(|e| e.error),2803 CommonError::<Test>::NoPermission2804 );2805 });2806}28072808#[test]2809fn collection_transfer_flag_works_neg() {2810 new_test_ext().execute_with(|| {2811 let origin1 = Origin::signed(1);28122813 let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2814 assert_ok!(TemplateModule::set_transfers_enabled_flag(2815 origin1,2816 collection_id,2817 false2818 ));28192820 let data = default_nft_data();2821 create_test_item(collection_id, &data.into());2822 assert_eq!(2823 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2824 12825 );2826 assert_eq!(2827 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2828 true2829 );28302831 let origin1 = Origin::signed(1);28322833 // default scenario2834 assert_noop!(2835 TemplateModule::transfer(origin1, account(2), CollectionId(1), TokenId(1), 1)2836 .map_err(|e| e.error),2837 CommonError::<Test>::TransferNotAllowed2838 );2839 assert_eq!(2840 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2841 12842 );2843 assert_eq!(2844 <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),2845 02846 );2847 assert_eq!(2848 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2849 true2850 );2851 assert_eq!(2852 <pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),2853 false2854 );2855 });2856}28572858#[test]2859fn collection_sponsoring() {2860 new_test_ext().execute_with(|| {2861 // default_limits();2862 let user1 = 1_u64;2863 let user2 = 777_u64;2864 let origin1 = Origin::signed(user1);2865 let origin2 = Origin::signed(user2);2866 let account2 = account(user2);28672868 let collection_id =2869 create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));2870 assert_ok!(TemplateModule::set_collection_sponsor(2871 origin1.clone(),2872 collection_id,2873 user12874 ));2875 assert_ok!(TemplateModule::confirm_sponsorship(2876 origin1.clone(),2877 collection_id2878 ));28792880 // Expect error while have no permissions2881 assert!(TemplateModule::create_item(2882 origin2.clone(),2883 collection_id,2884 account2.clone(),2885 default_nft_data().into()2886 )2887 .is_err());28882889 assert_ok!(TemplateModule::set_public_access_mode(2890 origin1.clone(),2891 collection_id,2892 AccessMode::AllowList2893 ));2894 assert_ok!(TemplateModule::add_to_allow_list(2895 origin1.clone(),2896 collection_id,2897 account2.clone()2898 ));2899 assert_ok!(TemplateModule::set_mint_permission(2900 origin1.clone(),2901 collection_id,2902 true2903 ));29042905 assert_ok!(TemplateModule::create_item(2906 origin2,2907 collection_id,2908 account2,2909 default_nft_data().into()2910 ));2911 });2912}runtime/common/Cargo.tomldiffbeforeafterboth--- a/runtime/common/Cargo.toml
+++ b/runtime/common/Cargo.toml
@@ -104,4 +104,6 @@
branch = "unique-polkadot-v0.9.20"
[dependencies]
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.18-2" }
+evm-coder = { default-features = false, path = '../../crates/evm-coder' }
+up-sponsorship = { default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring", branch = 'polkadot-v0.9.18' }
runtime/common/src/eth_sponsoring.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/common/src/eth_sponsoring.rs
@@ -0,0 +1,107 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Implements EVM sponsoring logic via TransactionValidityHack
+
+use evm_coder::{Call, abi::AbiReader};
+use pallet_common::{CollectionHandle, eth::map_eth_to_id};
+use sp_core::H160;
+use sp_std::prelude::*;
+use up_sponsorship::SponsorshipHandler;
+use core::marker::PhantomData;
+use core::convert::TryInto;
+use pallet_evm::account::CrossAccountId;
+use up_data_structs::{TokenId, CreateItemData, CreateNftData, CollectionMode};
+use pallet_unique::Config as UniqueConfig;
+
+use crate::sponsoring::*;
+
+use pallet_nonfungible::erc::{
+ UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,
+};
+use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
+use pallet_fungible::Config as FungibleConfig;
+use pallet_nonfungible::Config as NonfungibleConfig;
+use pallet_refungible::Config as RefungibleConfig;
+
+pub struct UniqueEthSponsorshipHandler<T: UniqueConfig>(PhantomData<*const T>);
+impl<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>
+ SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for UniqueEthSponsorshipHandler<T>
+{
+ fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
+ let collection_id = map_eth_to_id(&call.0)?;
+ let collection = <CollectionHandle<T>>::new(collection_id)?;
+ let sponsor = collection.sponsorship.sponsor()?.clone();
+ let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
+ Some(T::CrossAccountId::from_sub(match &collection.mode {
+ CollectionMode::NFT => {
+ let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
+ match call {
+ UniqueNFTCall::ERC721UniqueExtensions(
+ ERC721UniqueExtensionsCall::Transfer { token_id, .. },
+ ) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
+ }
+ UniqueNFTCall::ERC721Mintable(
+ ERC721MintableCall::Mint { token_id, .. }
+ | ERC721MintableCall::MintWithTokenUri { token_id, .. },
+ ) => {
+ let _token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_create_item::<T>(
+ &collection,
+ &who,
+ &CreateItemData::NFT(CreateNftData::default()),
+ )
+ .map(|()| sponsor)
+ }
+ UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ let from = T::CrossAccountId::from_eth(from);
+ withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)
+ }
+ UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_approve::<T>(&collection, who.as_sub(), &token_id)
+ .map(|()| sponsor)
+ }
+ _ => None,
+ }
+ }
+ CollectionMode::Fungible(_) => {
+ let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;
+ #[allow(clippy::single_match)]
+ match call {
+ UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
+ withdraw_transfer::<T>(&collection, who, &TokenId::default())
+ .map(|()| sponsor)
+ }
+ UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {
+ let from = T::CrossAccountId::from_eth(from);
+ withdraw_transfer::<T>(&collection, &from, &TokenId::default())
+ .map(|()| sponsor)
+ }
+ UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
+ withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())
+ .map(|()| sponsor)
+ }
+ _ => None,
+ }
+ }
+ _ => None,
+ }?))
+ }
+}
runtime/common/src/lib.rsdiffbeforeafterboth--- a/runtime/common/src/lib.rs
+++ b/runtime/common/src/lib.rs
@@ -2,5 +2,8 @@
pub mod constants;
pub mod dispatch;
+pub mod eth_sponsoring;
pub mod runtime_apis;
+pub mod sponsoring;
pub mod types;
+pub mod weights;
runtime/common/src/sponsoring.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/common/src/sponsoring.rs
@@ -0,0 +1,375 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use core::marker::PhantomData;
+use up_sponsorship::SponsorshipHandler;
+use frame_support::{
+ traits::{IsSubType},
+ storage::{StorageMap, StorageDoubleMap, StorageNMap},
+};
+use up_data_structs::{
+ CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MetaUpdatePermission,
+ NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId, CollectionMode,
+ CreateItemData,
+};
+use sp_runtime::traits::Saturating;
+use pallet_common::{CollectionHandle};
+use pallet_evm::account::CrossAccountId;
+use pallet_unique::{
+ Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,
+ NftApproveBasket, VariableMetaDataBasket, CreateItemBasket, ReFungibleTransferBasket,
+ FungibleTransferBasket, NftTransferBasket,
+};
+use pallet_fungible::Config as FungibleConfig;
+use pallet_nonfungible::Config as NonfungibleConfig;
+use pallet_refungible::Config as RefungibleConfig;
+
+pub fn withdraw_transfer<
+ T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+>(
+ collection: &CollectionHandle<T>,
+ who: &T::CrossAccountId,
+ item_id: &TokenId,
+) -> Option<()> {
+ // preliminary sponsoring correctness check
+ match collection.mode {
+ CollectionMode::NFT => {
+ let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
+ if !owner.conv_eq(who) {
+ return None;
+ }
+ }
+ CollectionMode::Fungible(_) => {
+ if item_id != &TokenId::default() {
+ return None;
+ }
+ if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
+ return None;
+ }
+ }
+ CollectionMode::ReFungible => {
+ if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
+ return None;
+ }
+ }
+ }
+
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match collection.mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
+
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
+ CollectionMode::Fungible(_) => {
+ <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
+ }
+ CollectionMode::ReFungible => {
+ <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))
+ }
+ };
+
+ if let Some(last_tx_block) = last_tx_block {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
+
+ match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
+ CollectionMode::Fungible(_) => {
+ <FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)
+ }
+ CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(
+ (collection.id, item_id, who.as_sub()),
+ block_number,
+ ),
+ };
+
+ Some(())
+}
+
+pub fn withdraw_create_item<
+ T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+>(
+ collection: &CollectionHandle<T>,
+ who: &T::CrossAccountId,
+ _properties: &CreateItemData,
+) -> Option<()> {
+ if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
+ return None;
+ }
+
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match _properties {
+ CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
+
+ if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
+
+ CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);
+
+ Some(())
+}
+
+pub fn withdraw_set_variable_meta_data<
+ T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+>(
+ who: &T::CrossAccountId,
+ collection: &CollectionHandle<T>,
+ item_id: &TokenId,
+ data: &[u8],
+) -> Option<()> {
+ // TODO: make it work for admins
+ if collection.meta_update_permission != MetaUpdatePermission::ItemOwner {
+ return None;
+ }
+ // preliminary sponsoring correctness check
+ match collection.mode {
+ CollectionMode::NFT => {
+ let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
+ if !owner.conv_eq(who) {
+ return None;
+ }
+ }
+ CollectionMode::Fungible(_) => {
+ if item_id != &TokenId::default() {
+ return None;
+ }
+ if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
+ return None;
+ }
+ }
+ CollectionMode::ReFungible => {
+ if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
+ return None;
+ }
+ }
+ }
+
+ // Can't sponsor fungible collection, this tx will be rejected
+ // as invalid
+ if matches!(collection.mode, CollectionMode::Fungible(_)) {
+ return None;
+ }
+ if data.len() > collection.limits.sponsored_data_size() as usize {
+ return None;
+ }
+
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsored_data_rate_limit()?;
+
+ if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
+
+ <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
+
+ Some(())
+}
+
+pub fn withdraw_approve<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ item_id: &TokenId,
+) -> Option<()> {
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsor_approve_timeout();
+
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
+ CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
+ CollectionMode::ReFungible => {
+ <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
+ }
+ };
+
+ if let Some(last_tx_block) = last_tx_block {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
+
+ match collection.mode {
+ CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
+ CollectionMode::Fungible(_) => {
+ <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
+ }
+ CollectionMode::ReFungible => {
+ <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
+ }
+ };
+
+ Some(())
+}
+
+fn load<T: UniqueConfig>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
+ let collection = CollectionHandle::new(id)?;
+ let sponsor = collection.sponsorship.sponsor().cloned()?;
+ Some((sponsor, collection))
+}
+
+pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);
+impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>
+where
+ T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+ C: IsSubType<UniqueCall<T>>,
+{
+ fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
+ match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {
+ UniqueCall::create_item {
+ collection_id,
+ data,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_create_item::<T>(
+ &collection,
+ &T::CrossAccountId::from_sub(who.clone()),
+ data,
+ )
+ .map(|()| sponsor)
+ }
+ UniqueCall::transfer {
+ collection_id,
+ item_id,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_transfer::<T>(
+ &collection,
+ &T::CrossAccountId::from_sub(who.clone()),
+ item_id,
+ )
+ .map(|()| sponsor)
+ }
+ UniqueCall::transfer_from {
+ collection_id,
+ item_id,
+ from,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)
+ }
+ UniqueCall::approve {
+ collection_id,
+ item_id,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
+ }
+ UniqueCall::set_variable_meta_data {
+ collection_id,
+ item_id,
+ data,
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_set_variable_meta_data::<T>(
+ &T::CrossAccountId::from_sub(who.clone()),
+ &collection,
+ item_id,
+ data,
+ )
+ .map(|()| sponsor)
+ }
+ _ => None,
+ }
+ }
+}
+
+use crate::SponsorshipPredict;
+pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);
+
+impl<T> SponsorshipPredict<T> for UniqueSponsorshipPredict<T>
+where
+ T: Config,
+{
+ fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>
+ where
+ u64: From<<T as frame_system::Config>::BlockNumber>,
+ {
+ let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;
+ let _ = collection.sponsorship.sponsor()?;
+
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match collection.mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
+
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),
+ CollectionMode::Fungible(_) => {
+ <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
+ }
+ CollectionMode::ReFungible => {
+ <ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))
+ }
+ };
+
+ if let Some(last_tx_block) = last_tx_block {
+ return Some(
+ last_tx_block
+ .saturating_add(limit.into())
+ .saturating_sub(block_number)
+ .into(),
+ );
+ }
+
+ let token_exists = match collection.mode {
+ CollectionMode::NFT => {
+ <pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))
+ }
+ CollectionMode::Fungible(_) => true,
+ CollectionMode::ReFungible => {
+ <pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))
+ }
+ };
+
+ if token_exists {
+ Some(0)
+ } else {
+ None
+ }
+ }
+}
runtime/common/src/weights.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/common/src/weights.rs
@@ -0,0 +1,76 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use core::marker::PhantomData;
+use frame_support::{weights::Weight};
+use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight};
+
+use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights};
+use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights};
+use pallet_refungible::{Config as RefungibleConfig, common::CommonWeights as RefungibleWeights};
+use up_data_structs::CreateItemExData;
+
+macro_rules! max_weight_of {
+ ($method:ident ( $($args:tt)* )) => {
+ <FungibleWeights<T>>::$method($($args)*)
+ .max(<NonfungibleWeights<T>>::$method($($args)*))
+ .max(<RefungibleWeights<T>>::$method($($args)*))
+ };
+}
+
+pub struct CommonWeights<T>(PhantomData<T>)
+where
+ T: FungibleConfig + NonfungibleConfig + RefungibleConfig;
+impl<T> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T>
+where
+ T: FungibleConfig + NonfungibleConfig + RefungibleConfig,
+{
+ fn create_item() -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(create_item())
+ }
+
+ fn create_multiple_items(amount: u32) -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
+ }
+
+ fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))
+ }
+
+ fn burn_item() -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(burn_item())
+ }
+
+ fn transfer() -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(transfer())
+ }
+
+ fn approve() -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(approve())
+ }
+
+ fn transfer_from() -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(transfer_from())
+ }
+
+ fn set_variable_metadata(bytes: u32) -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(set_variable_metadata(bytes))
+ }
+
+ fn burn_from() -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(burn_from())
+ }
+}
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -120,6 +120,9 @@
types::*,
constants::*,
dispatch::{CollectionDispatchT, CollectionDispatch},
+ sponsoring::UniqueSponsorshipHandler,
+ eth_sponsoring::UniqueEthSponsorshipHandler,
+ weights::CommonWeights,
};
pub const RUNTIME_NAME: &str = "opal";
@@ -902,6 +905,7 @@
impl pallet_unique::Config for Runtime {
type Event = Event;
type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;
+ type CommonWeightInfo = CommonWeights<Self>;
}
parameter_types! {
@@ -923,11 +927,11 @@
// }
type EvmSponsorshipHandler = (
- pallet_unique::UniqueEthSponsorshipHandler<Runtime>,
+ UniqueEthSponsorshipHandler<Runtime>,
pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
);
type SponsorshipHandler = (
- pallet_unique::UniqueSponsorshipHandler<Runtime>,
+ UniqueSponsorshipHandler<Runtime>,
//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
);
runtime/tests/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/runtime/tests/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "tests"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+unique-runtime-common = { path = '../common' }
+up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
+
+sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+sp-runtime = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+
+fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.18-2" }
+
+frame-support = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+frame-system = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+
+pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+pallet-transaction-payment = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+
+pallet-evm = { default-features = false, git = 'https://github.com/uniquenetwork/frontier.git', branch = 'unique-polkadot-v0.9.18-2' }
+pallet-ethereum = { default-features = false, git = 'https://github.com/uniquenetwork/frontier.git', branch = 'unique-polkadot-v0.9.18-2' }
+
+pallet-common = { path = '../../pallets/common' }
+pallet-structure = { path = '../../pallets/structure' }
+pallet-fungible = { path = '../../pallets/fungible' }
+pallet-nonfungible = { path = '../../pallets/nonfungible' }
+pallet-refungible = { path = '../../pallets/refungible' }
+pallet-unique = { path = '../../pallets/unique' }
+
+pallet-evm-coder-substrate = { path = '../../pallets/evm-coder-substrate' }
+
+parity-scale-codec = "*"
+scale-info = "*"
runtime/tests/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/tests/src/lib.rs
@@ -0,0 +1,238 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(clippy::from_over_into)]
+
+use sp_core::{H160, H256};
+use frame_support::{
+ parameter_types,
+ traits::{Everything, ConstU32},
+ weights::IdentityFee,
+};
+use sp_runtime::{
+ traits::{BlakeTwo256, IdentityLookup},
+ testing::Header,
+};
+use pallet_transaction_payment::{CurrencyAdapter};
+use frame_system as system;
+use pallet_evm::{AddressMapping, runner::stack::MaybeMirroredLog, account::CrossAccountId};
+use fp_evm_mapping::EvmBackwardsAddressMapping;
+use parity_scale_codec::{Encode, Decode, MaxEncodedLen};
+use scale_info::TypeInfo;
+
+use unique_runtime_common::{dispatch::CollectionDispatchT, weights::CommonWeights};
+use up_data_structs::mapping::{CrossTokenAddressMapping, EvmTokenAddressMapping};
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+type Block = frame_system::mocking::MockBlock<Test>;
+
+#[cfg(test)]
+mod tests;
+
+// Configure a mock runtime to test the pallet.
+frame_support::construct_runtime!(
+ pub enum Test where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
+ Unique: pallet_unique::{Pallet, Call, Storage},
+ Balances: pallet_balances::{Pallet, Call, Storage},
+ Common: pallet_common::{Pallet, Storage, Event<T>},
+ Fungible: pallet_fungible::{Pallet, Storage},
+ Refungible: pallet_refungible::{Pallet, Storage},
+ Nonfungible: pallet_nonfungible::{Pallet, Storage},
+ }
+);
+
+parameter_types! {
+ pub const BlockHashCount: u64 = 250;
+ pub const SS58Prefix: u8 = 42;
+}
+
+impl system::Config for Test {
+ type BaseCallFilter = Everything;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type DbWeight = ();
+ type Origin = Origin;
+ type Call = Call;
+ type Index = u64;
+ type BlockNumber = u64;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = u64;
+ type Lookup = IdentityLookup<Self::AccountId>;
+ type Header = Header;
+ type Event = ();
+ type BlockHashCount = BlockHashCount;
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData<u64>;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type SystemWeightInfo = ();
+ type SS58Prefix = SS58Prefix;
+ type OnSetCode = ();
+ type MaxConsumers = ConstU32<16>;
+}
+
+parameter_types! {
+ pub const ExistentialDeposit: u64 = 1;
+ pub const MaxLocks: u32 = 50;
+}
+//frame_system::Module<Test>;
+impl pallet_balances::Config for Test {
+ type AccountStore = System;
+ type Balance = u64;
+ type DustRemoval = ();
+ type Event = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type WeightInfo = ();
+ type MaxLocks = MaxLocks;
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 8];
+}
+
+parameter_types! {
+ pub const OperationalFeeMultiplier: u8 = 5;
+}
+
+impl pallet_transaction_payment::Config for Test {
+ type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;
+ type LengthToFee = IdentityFee<u64>;
+ type WeightToFee = IdentityFee<u64>;
+ type FeeMultiplierUpdate = ();
+ type OperationalFeeMultiplier = OperationalFeeMultiplier;
+}
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = 1;
+}
+impl pallet_timestamp::Config for Test {
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+parameter_types! {
+ pub const CollectionCreationPrice: u32 = 100;
+ pub TreasuryAccountId: u64 = 1234;
+ pub EthereumChainId: u32 = 1111;
+}
+
+pub struct TestEvmAddressMapping;
+impl AddressMapping<u64> for TestEvmAddressMapping {
+ fn into_account_id(_addr: sp_core::H160) -> u64 {
+ unimplemented!()
+ }
+}
+
+pub struct TestEvmBackwardsAddressMapping;
+impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {
+ fn from_account_id(_account_id: u64) -> sp_core::H160 {
+ unimplemented!()
+ }
+}
+
+#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, TypeInfo, MaxEncodedLen)]
+pub struct TestCrossAccountId(u64, sp_core::H160);
+impl CrossAccountId<u64> for TestCrossAccountId {
+ fn as_sub(&self) -> &u64 {
+ &self.0
+ }
+ fn as_eth(&self) -> &sp_core::H160 {
+ &self.1
+ }
+ fn from_sub(sub: u64) -> Self {
+ let mut eth = [0; 20];
+ eth[12..20].copy_from_slice(&sub.to_be_bytes());
+ Self(sub, sp_core::H160(eth))
+ }
+ fn from_eth(eth: sp_core::H160) -> Self {
+ let mut sub_raw = [0; 8];
+ sub_raw.copy_from_slice(ð.0[0..8]);
+ let sub = u64::from_be_bytes(sub_raw);
+ Self(sub, eth)
+ }
+ fn conv_eq(&self, other: &Self) -> bool {
+ self.as_sub() == other.as_sub()
+ }
+}
+
+impl Default for TestCrossAccountId {
+ fn default() -> Self {
+ Self::from_sub(0)
+ }
+}
+
+pub struct TestEtheremTransactionSender;
+impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {
+ fn submit_logs_transaction(_source: H160, _logs: Vec<MaybeMirroredLog>) {}
+}
+
+impl pallet_evm_coder_substrate::Config for Test {
+ type EthereumTransactionSender = TestEtheremTransactionSender;
+ type GasWeightMapping = ();
+}
+
+impl pallet_common::Config for Test {
+ type Event = ();
+ type Currency = Balances;
+ type CollectionCreationPrice = CollectionCreationPrice;
+ type TreasuryAccountId = TreasuryAccountId;
+
+ type CollectionDispatch = CollectionDispatchT<Self>;
+ type EvmTokenAddressMapping = EvmTokenAddressMapping;
+ type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;
+}
+
+impl pallet_evm::account::Config for Test {
+ type CrossAccountId = TestCrossAccountId;
+ type EvmAddressMapping = TestEvmAddressMapping;
+ type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;
+}
+
+impl pallet_structure::Config for Test {
+ type WeightInfo = ();
+ type Event = ();
+ type Call = Call;
+}
+impl pallet_fungible::Config for Test {
+ type WeightInfo = ();
+}
+impl pallet_refungible::Config for Test {
+ type WeightInfo = ();
+}
+impl pallet_nonfungible::Config for Test {
+ type WeightInfo = ();
+}
+
+impl pallet_unique::Config for Test {
+ type Event = ();
+ type WeightInfo = ();
+ type CommonWeightInfo = CommonWeights<Self>;
+}
+
+// Build genesis storage according to the mock runtime.
+pub fn new_test_ext() -> sp_io::TestExternalities {
+ system::GenesisConfig::default()
+ .build_storage::<Test>()
+ .unwrap()
+ .into()
+}
runtime/tests/src/tests.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/tests/src/tests.rs
@@ -0,0 +1,2889 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Tests to be written here
+use crate::{Test, TestCrossAccountId, CollectionCreationPrice, Origin, Unique, new_test_ext};
+use up_data_structs::{
+ COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
+ CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
+ TokenId, MAX_TOKEN_OWNERSHIP, CreateCollectionData, CollectionField, SchemaVersion,
+ CollectionMode, AccessMode,
+};
+use frame_support::{assert_noop, assert_ok, assert_err};
+use sp_std::convert::TryInto;
+use pallet_evm::account::CrossAccountId;
+use pallet_common::Error as CommonError;
+use pallet_unique::Error as UniqueError;
+
+fn add_balance(user: u64, value: u64) {
+ const DONOR_USER: u64 = 999;
+ assert_ok!(<pallet_balances::Pallet<Test>>::set_balance(
+ Origin::root(),
+ DONOR_USER,
+ value,
+ 0
+ ));
+ assert_ok!(<pallet_balances::Pallet<Test>>::force_transfer(
+ Origin::root(),
+ DONOR_USER,
+ user,
+ value
+ ));
+}
+
+fn default_nft_data() -> CreateNftData {
+ CreateNftData {
+ const_data: vec![1, 2, 3].try_into().unwrap(),
+ variable_data: vec![3, 2, 1].try_into().unwrap(),
+ }
+}
+
+fn default_fungible_data() -> CreateFungibleData {
+ CreateFungibleData { value: 5 }
+}
+
+fn default_re_fungible_data() -> CreateReFungibleData {
+ CreateReFungibleData {
+ const_data: vec![1, 2, 3].try_into().unwrap(),
+ variable_data: vec![3, 2, 1].try_into().unwrap(),
+ pieces: 1023,
+ }
+}
+
+fn create_test_collection_for_owner(
+ mode: &CollectionMode,
+ owner: u64,
+ id: CollectionId,
+) -> CollectionId {
+ add_balance(owner, CollectionCreationPrice::get() as u64 + 1);
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+ let data: CreateCollectionData<u64> = CreateCollectionData {
+ name: col_name1.try_into().unwrap(),
+ description: col_desc1.try_into().unwrap(),
+ token_prefix: token_prefix1.try_into().unwrap(),
+ mode: mode.clone(),
+ ..Default::default()
+ };
+
+ let origin1 = Origin::signed(owner);
+ assert_ok!(Unique::create_collection_ex(origin1, data));
+
+ let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(id)
+ .unwrap()
+ .owner,
+ owner
+ );
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(id).unwrap().name,
+ saved_col_name
+ );
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(id).unwrap().mode,
+ *mode
+ );
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(id)
+ .unwrap()
+ .description,
+ saved_description
+ );
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(id)
+ .unwrap()
+ .token_prefix,
+ saved_prefix
+ );
+ id
+}
+
+fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {
+ create_test_collection_for_owner(&mode, 1, id)
+}
+
+fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::create_item(
+ origin1,
+ collection_id,
+ account(1),
+ data.clone()
+ ));
+}
+
+fn account(sub: u64) -> TestCrossAccountId {
+ TestCrossAccountId::from_sub(sub)
+}
+
+// Use cases tests region
+// #region
+
+#[test]
+fn set_version_schema() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ assert_ok!(Unique::set_schema_version(
+ origin1,
+ collection_id,
+ SchemaVersion::Unique
+ ));
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(collection_id)
+ .unwrap()
+ .schema_version,
+ SchemaVersion::Unique
+ );
+ });
+}
+
+#[test]
+fn check_not_sufficient_founds() {
+ new_test_ext().execute_with(|| {
+ let acc: u64 = 1;
+ <pallet_balances::Pallet<Test>>::set_balance(Origin::root(), acc, 0, 0).unwrap();
+
+ let name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix: Vec<u8> = b"token_prefix1\0".to_vec();
+
+ let data: CreateCollectionData<<Test as frame_system::Config>::AccountId> =
+ CreateCollectionData {
+ name: name.try_into().unwrap(),
+ description: description.try_into().unwrap(),
+ token_prefix: token_prefix.try_into().unwrap(),
+ mode: CollectionMode::NFT,
+ ..Default::default()
+ };
+
+ let result = Unique::create_collection_ex(Origin::signed(acc), data);
+ assert_err!(result, <CommonError<Test>>::NotSufficientFounds);
+ });
+}
+
+#[test]
+fn create_fungible_collection_fails_with_large_decimal_numbers() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+ let data: CreateCollectionData<u64> = CreateCollectionData {
+ name: col_name1.try_into().unwrap(),
+ description: col_desc1.try_into().unwrap(),
+ token_prefix: token_prefix1.try_into().unwrap(),
+ mode: CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1),
+ ..Default::default()
+ };
+
+ let origin1 = Origin::signed(1);
+ assert_noop!(
+ Unique::create_collection_ex(origin1, data),
+ UniqueError::<Test>::CollectionDecimalPointLimitExceeded
+ );
+ });
+}
+
+#[test]
+fn create_nft_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+
+ let item = <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1)).unwrap();
+ assert_eq!(item.const_data, data.const_data.into_inner());
+ assert_eq!(item.variable_data, data.variable_data.into_inner());
+ });
+}
+
+// Use cases tests region
+// #region
+#[test]
+fn create_nft_multiple_items() {
+ new_test_ext().execute_with(|| {
+ create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];
+
+ assert_ok!(Unique::create_multiple_items(
+ origin1,
+ CollectionId(1),
+ account(1),
+ items_data
+ .clone()
+ .into_iter()
+ .map(|d| { d.into() })
+ .collect()
+ ));
+ for (index, data) in items_data.into_iter().enumerate() {
+ let item = <pallet_nonfungible::TokenData<Test>>::get((
+ CollectionId(1),
+ TokenId((index + 1) as u32),
+ ))
+ .unwrap();
+ assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+ assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
+ }
+ });
+}
+
+#[test]
+fn create_refungible_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.clone().into());
+ let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));
+ let balance =
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1)));
+ assert_eq!(item.const_data, data.const_data.into_inner());
+ assert_eq!(item.variable_data, data.variable_data.into_inner());
+ assert_eq!(balance, 1023);
+ });
+}
+
+#[test]
+fn create_multiple_refungible_items() {
+ new_test_ext().execute_with(|| {
+ create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let items_data = vec![
+ default_re_fungible_data(),
+ default_re_fungible_data(),
+ default_re_fungible_data(),
+ ];
+
+ assert_ok!(Unique::create_multiple_items(
+ origin1,
+ CollectionId(1),
+ account(1),
+ items_data
+ .clone()
+ .into_iter()
+ .map(|d| { d.into() })
+ .collect()
+ ));
+ for (index, data) in items_data.into_iter().enumerate() {
+ let item = <pallet_refungible::TokenData<Test>>::get((
+ CollectionId(1),
+ TokenId((index + 1) as u32),
+ ));
+ let balance =
+ <pallet_refungible::Balance<Test>>::get((CollectionId(1), TokenId(1), account(1)));
+ assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+ assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
+ assert_eq!(balance, 1023);
+ }
+ });
+}
+
+#[test]
+fn create_fungible_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+ 5
+ );
+ });
+}
+
+//#[test]
+// fn create_multiple_fungible_items() {
+// new_test_ext().execute_with(|| {
+// default_limits();
+
+// create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+// let origin1 = Origin::signed(1);
+
+// let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
+
+// assert_ok!(Unique::create_multiple_items(
+// origin1.clone(),
+// 1,
+// 1,
+// items_data.clone().into_iter().map(|d| { d.into() }).collect()
+// ));
+
+// for (index, _) in items_data.iter().enumerate() {
+// assert_eq!(Unique::fungible_item_id(1, (index + 1) as TokenId).value, 5);
+// }
+// assert_eq!(Unique::balance_count(1, 1), 3000);
+// assert_eq!(Unique::address_tokens(1, 1), [1, 2, 3]);
+// });
+// }
+
+#[test]
+fn transfer_fungible_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),
+ 5
+ );
+
+ // change owner scenario
+ assert_ok!(Unique::transfer(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(0),
+ 5
+ ));
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),
+ 0
+ );
+
+ // split item scenario
+ assert_ok!(Unique::transfer(
+ origin2.clone(),
+ account(3),
+ CollectionId(1),
+ TokenId(0),
+ 3
+ ));
+
+ // split item and new owner has account scenario
+ assert_ok!(Unique::transfer(
+ origin2,
+ account(3),
+ CollectionId(1),
+ TokenId(0),
+ 1
+ ));
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(2))),
+ 1
+ );
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((CollectionId(1), account(3))),
+ 4
+ );
+ });
+}
+
+#[test]
+fn transfer_refungible_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+ // Create RFT 1 in 1023 pieces for account 1
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.clone().into());
+ let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));
+ assert_eq!(item.const_data, data.const_data.into_inner());
+ assert_eq!(item.variable_data, data.variable_data.into_inner());
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+ 1023
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ // Account 1 transfers all 1023 pieces of RFT 1 to account 2
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(Unique::transfer(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1023
+ ));
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
+ 1023
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 0
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ false
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+ true
+ );
+
+ // Account 2 transfers 500 pieces of RFT 1 to account 3
+ assert_ok!(Unique::transfer(
+ origin2.clone(),
+ account(3),
+ CollectionId(1),
+ TokenId(1),
+ 500
+ ));
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
+ 523
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
+ 500
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),
+ true
+ );
+
+ // Account 2 transfers 200 more pieces of RFT 1 to account 3 with pre-existing balance
+ assert_ok!(Unique::transfer(
+ origin2,
+ account(3),
+ CollectionId(1),
+ TokenId(1),
+ 200
+ ));
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
+ 323
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
+ 700
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),
+ true
+ );
+ });
+}
+
+#[test]
+fn transfer_nft_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ let origin1 = Origin::signed(1);
+ // default scenario
+ assert_ok!(Unique::transfer(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 0
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ false
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+ true
+ );
+ });
+}
+
+#[test]
+fn transfer_nft_item_wrong_value() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ let origin1 = Origin::signed(1);
+
+ assert_noop!(
+ Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 2)
+ .map_err(|e| e.error),
+ <pallet_nonfungible::Error::<Test>>::NonfungibleItemsHaveNoAmount
+ );
+ });
+}
+
+#[test]
+fn transfer_nft_item_zero_value() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ let origin1 = Origin::signed(1);
+
+ // Transferring 0 amount works on NFT...
+ assert_ok!(Unique::transfer(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 0
+ ));
+ // ... and results in no transfer
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+ });
+}
+
+#[test]
+fn nft_approve_and_transfer_from() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ // neg transfer_from
+ assert_noop!(
+ Unique::transfer_from(
+ origin2.clone(),
+ account(1),
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::ApprovedValueTooLow
+ );
+
+ // do approve
+ assert_ok!(Unique::approve(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(2)
+ );
+
+ assert_ok!(Unique::transfer_from(
+ origin2,
+ account(1),
+ account(3),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()
+ );
+ });
+}
+
+#[test]
+fn nft_approve_and_transfer_from_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ // Create NFT 1 for account 1
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+ assert_eq!(
+ &<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))
+ .unwrap()
+ .const_data,
+ &data.const_data.into_inner()
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ // Allow allow-list users to mint and add accounts 1, 2, and 3 to allow-list
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ CollectionId(1),
+ true
+ ));
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ CollectionId(1),
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(2)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(3)
+ ));
+
+ // Account 1 approves account 2 for NFT 1
+ assert_ok!(Unique::approve(
+ origin1.clone(),
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(2)
+ );
+
+ // Account 2 transfers NFT 1 from account 1 to account 3
+ assert_ok!(Unique::transfer_from(
+ origin2,
+ account(1),
+ account(3),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()
+ );
+ });
+}
+
+#[test]
+fn refungible_approve_and_transfer_from() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ // Create RFT 1 in 1023 pieces for account 1
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+ 1023
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ // Allow public minting, enable allow-list and add accounts 1, 2, 3 to allow-list
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ CollectionId(1),
+ true
+ ));
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ CollectionId(1),
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(2)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(3)
+ ));
+
+ // Account 1 approves account 2 for 1023 pieces of RFT 1
+ assert_ok!(Unique::approve(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1023
+ ));
+ assert_eq!(
+ <pallet_refungible::Allowance<Test>>::get((
+ CollectionId(1),
+ TokenId(1),
+ account(1),
+ account(2)
+ )),
+ 1023
+ );
+
+ // Account 2 transfers 100 pieces of RFT 1 from account 1 to account 3
+ assert_ok!(Unique::transfer_from(
+ origin2,
+ account(1),
+ account(3),
+ CollectionId(1),
+ TokenId(1),
+ 100
+ ));
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+ 923
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
+ 100
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_refungible::Allowance<Test>>::get((
+ CollectionId(1),
+ TokenId(1),
+ account(1),
+ account(2)
+ )),
+ 923
+ );
+ });
+}
+
+#[test]
+fn fungible_approve_and_transfer_from() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ CollectionId(1),
+ true
+ ));
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ CollectionId(1),
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(2)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(3)
+ ));
+
+ // do approve
+ assert_ok!(Unique::approve(
+ origin1.clone(),
+ account(2),
+ CollectionId(1),
+ TokenId(0),
+ 5
+ ));
+ assert_eq!(
+ <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
+ 5
+ );
+ assert_ok!(Unique::approve(
+ origin1,
+ account(3),
+ CollectionId(1),
+ TokenId(0),
+ 5
+ ));
+ assert_eq!(
+ <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
+ 5
+ );
+ assert_eq!(
+ <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(3))),
+ 5
+ );
+
+ assert_ok!(Unique::transfer_from(
+ origin2.clone(),
+ account(1),
+ account(3),
+ CollectionId(1),
+ TokenId(0),
+ 4
+ ));
+
+ assert_eq!(
+ <pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
+ 1
+ );
+
+ assert_noop!(
+ Unique::transfer_from(
+ origin2,
+ account(1),
+ account(3),
+ CollectionId(1),
+ TokenId(0),
+ 4
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::ApprovedValueTooLow
+ );
+ });
+}
+
+#[test]
+fn change_collection_owner() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::change_collection_owner(origin1, collection_id, 2));
+ assert_eq!(
+ <pallet_common::CollectionById<Test>>::get(collection_id)
+ .unwrap()
+ .owner,
+ 2
+ );
+ });
+}
+
+#[test]
+fn destroy_collection() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::destroy_collection(origin1, collection_id));
+ });
+}
+
+#[test]
+fn burn_nft_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ // check balance (collection with id = 1, user id = 1)
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+
+ // burn item
+ assert_ok!(Unique::burn_item(
+ origin1.clone(),
+ collection_id,
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 0
+ );
+ });
+}
+
+#[test]
+fn burn_same_nft_item_twice() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ // check balance (collection with id = 1, user id = 1)
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+
+ // burn item
+ assert_ok!(Unique::burn_item(
+ origin1.clone(),
+ collection_id,
+ TokenId(1),
+ 1
+ ));
+
+ // burn item again
+ assert_noop!(
+ Unique::burn_item(origin1, collection_id, TokenId(1), 1).map_err(|e| e.error),
+ CommonError::<Test>::TokenNotFound
+ );
+
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 0
+ );
+ });
+}
+
+#[test]
+fn burn_fungible_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ // check balance (collection with id = 1, user id = 1)
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+ 5
+ );
+
+ // burn item
+ assert_ok!(Unique::burn_item(
+ origin1.clone(),
+ CollectionId(1),
+ TokenId(0),
+ 5
+ ));
+ assert_noop!(
+ Unique::burn_item(origin1, CollectionId(1), TokenId(0), 5).map_err(|e| e.error),
+ CommonError::<Test>::TokenValueTooLow
+ );
+
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+ 0
+ );
+ });
+}
+
+#[test]
+fn burn_fungible_item_with_token_id() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ // check balance (collection with id = 1, user id = 1)
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+ 5
+ );
+
+ // Try to burn item using Token ID
+ assert_noop!(
+ Unique::burn_item(origin1, CollectionId(1), TokenId(1), 5).map_err(|e| e.error),
+ <pallet_fungible::Error::<Test>>::FungibleItemsHaveNoId
+ );
+ });
+}
+#[test]
+fn burn_refungible_item() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ true
+ ));
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ // check balance (collection with id = 1, user id = 2)
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+ 1023
+ );
+
+ // burn item
+ assert_ok!(Unique::burn_item(
+ origin1.clone(),
+ collection_id,
+ TokenId(1),
+ 1023
+ ));
+ assert_noop!(
+ Unique::burn_item(origin1, collection_id, TokenId(1), 1023).map_err(|e| e.error),
+ CommonError::<Test>::TokenValueTooLow
+ );
+
+ assert_eq!(
+ <pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+ 0
+ );
+ });
+}
+
+#[test]
+fn add_collection_admin() {
+ new_test_ext().execute_with(|| {
+ let collection1_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ // Add collection admins
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection1_id,
+ account(2)
+ ));
+ assert_ok!(Unique::add_collection_admin(
+ origin1,
+ collection1_id,
+ account(3)
+ ));
+
+ // Owner is not an admin by default
+ assert_eq!(
+ <pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(1))),
+ false
+ );
+ assert!(<pallet_common::IsAdmin<Test>>::get((
+ CollectionId(1),
+ account(2)
+ )));
+ assert!(<pallet_common::IsAdmin<Test>>::get((
+ CollectionId(1),
+ account(3)
+ )));
+ });
+}
+
+#[test]
+fn remove_collection_admin() {
+ new_test_ext().execute_with(|| {
+ let collection1_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ // Add collection admins 2 and 3
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection1_id,
+ account(2)
+ ));
+ assert_ok!(Unique::add_collection_admin(
+ origin1,
+ collection1_id,
+ account(3)
+ ));
+
+ assert!(<pallet_common::IsAdmin<Test>>::get((
+ CollectionId(1),
+ account(2)
+ )));
+ assert!(<pallet_common::IsAdmin<Test>>::get((
+ CollectionId(1),
+ account(3)
+ )));
+
+ // remove admin 3
+ assert_ok!(Unique::remove_collection_admin(
+ origin2,
+ CollectionId(1),
+ account(3)
+ ));
+
+ // 2 is still admin, 3 is not an admin anymore
+ assert!(<pallet_common::IsAdmin<Test>>::get((
+ CollectionId(1),
+ account(2)
+ )));
+ assert_eq!(
+ <pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(3))),
+ false
+ );
+ });
+}
+
+#[test]
+fn balance_of() {
+ new_test_ext().execute_with(|| {
+ let nft_collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let fungible_collection_id =
+ create_test_collection(&CollectionMode::Fungible(3), CollectionId(2));
+ let re_fungible_collection_id =
+ create_test_collection(&CollectionMode::ReFungible, CollectionId(3));
+
+ // check balance before
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),
+ 0
+ );
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),
+ 0
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),
+ 0
+ );
+
+ let nft_data = default_nft_data();
+ create_test_item(nft_collection_id, &nft_data.into());
+
+ let fungible_data = default_fungible_data();
+ create_test_item(fungible_collection_id, &fungible_data.into());
+
+ let re_fungible_data = default_re_fungible_data();
+ create_test_item(re_fungible_collection_id, &re_fungible_data.into());
+
+ // check balance (collection with id = 1, user id = 1)
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),
+ 5
+ );
+ assert_eq!(
+ <pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),
+ 1
+ );
+
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((nft_collection_id, account(1), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_refungible::Owned<Test>>::get((
+ re_fungible_collection_id,
+ account(1),
+ TokenId(1)
+ )),
+ true
+ );
+ });
+}
+
+#[test]
+fn approve() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ let origin1 = Origin::signed(1);
+
+ // approve
+ assert_ok!(Unique::approve(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(2)
+ );
+ });
+}
+
+#[test]
+fn transfer_from() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ // approve
+ assert_ok!(Unique::approve(
+ origin1.clone(),
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(2)
+ );
+
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ CollectionId(1),
+ true
+ ));
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ CollectionId(1),
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ CollectionId(1),
+ account(2)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ CollectionId(1),
+ account(3)
+ ));
+
+ assert_ok!(Unique::transfer_from(
+ origin2,
+ account(1),
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+
+ // after transfer
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(1))),
+ 0
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(2))),
+ 1
+ );
+ });
+}
+
+// #endregion
+
+// Coverage tests region
+// #region
+
+#[test]
+fn owner_can_add_address_to_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+ assert!(<pallet_common::Allowlist<Test>>::get((
+ collection_id,
+ account(2)
+ )));
+ });
+}
+
+#[test]
+fn admin_can_add_address_to_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::add_collection_admin(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin2,
+ collection_id,
+ account(3)
+ ));
+ assert!(<pallet_common::Allowlist<Test>>::get((
+ collection_id,
+ account(3)
+ )));
+ });
+}
+
+#[test]
+fn nonprivileged_user_cannot_add_address_to_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin2 = Origin::signed(2);
+ assert_noop!(
+ Unique::add_to_allow_list(origin2, collection_id, account(3)),
+ CommonError::<Test>::NoPermission
+ );
+ });
+}
+
+#[test]
+fn nobody_can_add_address_to_allow_list_of_nonexisting_collection() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
+
+ assert_noop!(
+ Unique::add_to_allow_list(origin1, CollectionId(1), account(2)),
+ CommonError::<Test>::CollectionNotFound
+ );
+ });
+}
+
+#[test]
+fn nobody_can_add_address_to_allow_list_of_deleted_collection() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::destroy_collection(origin1.clone(), collection_id));
+ assert_noop!(
+ Unique::add_to_allow_list(origin1, collection_id, account(2)),
+ CommonError::<Test>::CollectionNotFound
+ );
+ });
+}
+
+// If address is already added to allow list, nothing happens
+#[test]
+fn address_is_already_added_to_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+ assert!(<pallet_common::Allowlist<Test>>::get((
+ collection_id,
+ account(2)
+ )));
+ });
+}
+
+#[test]
+fn owner_can_remove_address_from_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+ assert_ok!(Unique::remove_from_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+ assert_eq!(
+ <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+ false
+ );
+ });
+}
+
+#[test]
+fn admin_can_remove_address_from_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ // Owner adds admin
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ // Owner adds address 3 to allow list
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ collection_id,
+ account(3)
+ ));
+
+ // Admin removes address 3 from allow list
+ assert_ok!(Unique::remove_from_allow_list(
+ origin2,
+ collection_id,
+ account(3)
+ ));
+ assert_eq!(
+ <pallet_common::Allowlist<Test>>::get((collection_id, account(3))),
+ false
+ );
+ });
+}
+
+#[test]
+fn nonprivileged_user_cannot_remove_address_from_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+ assert_noop!(
+ Unique::remove_from_allow_list(origin2, collection_id, account(2)),
+ CommonError::<Test>::NoPermission
+ );
+ assert!(<pallet_common::Allowlist<Test>>::get((
+ collection_id,
+ account(2)
+ )));
+ });
+}
+
+#[test]
+fn nobody_can_remove_address_from_allow_list_of_nonexisting_collection() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
+
+ assert_noop!(
+ Unique::remove_from_allow_list(origin1, CollectionId(1), account(2)),
+ CommonError::<Test>::CollectionNotFound
+ );
+ });
+}
+
+#[test]
+fn nobody_can_remove_address_from_allow_list_of_deleted_collection() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ // Add account 2 to allow list
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ // Account 2 is in collection allow-list
+ assert!(<pallet_common::Allowlist<Test>>::get((
+ collection_id,
+ account(2)
+ )));
+
+ // Destroy collection
+ assert_ok!(Unique::destroy_collection(origin1, collection_id));
+
+ // Attempt to remove account 2 from collection allow-list => error
+ assert_noop!(
+ Unique::remove_from_allow_list(origin2, collection_id, account(2)),
+ CommonError::<Test>::CollectionNotFound
+ );
+
+ // Account 2 is not found in collection allow-list anyway
+ assert_eq!(
+ <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+ false
+ );
+ });
+}
+
+// If address is already removed from allow list, nothing happens
+#[test]
+fn address_is_already_removed_from_allow_list() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+ assert_ok!(Unique::remove_from_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+ assert_eq!(
+ <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+ false
+ );
+ assert_ok!(Unique::remove_from_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+ assert_eq!(
+ <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+ false
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom (2 tests)
+#[test]
+fn allow_list_test_1() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ assert_noop!(
+ Unique::transfer(origin1, account(3), CollectionId(1), TokenId(1), 1)
+ .map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+#[test]
+fn allow_list_test_2() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ // do approve
+ assert_ok!(Unique::approve(
+ origin1.clone(),
+ account(1),
+ collection_id,
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(1)
+ );
+
+ assert_ok!(Unique::remove_from_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+
+ assert_noop!(
+ Unique::transfer_from(
+ origin1,
+ account(1),
+ account(3),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom (2 tests)
+#[test]
+fn allow_list_test_3() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+
+ assert_noop!(
+ Unique::transfer(origin1, account(3), collection_id, TokenId(1), 1)
+ .map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+#[test]
+fn allow_list_test_4() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ // do approve
+ assert_ok!(Unique::approve(
+ origin1.clone(),
+ account(1),
+ collection_id,
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(1)
+ );
+
+ assert_ok!(Unique::remove_from_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ assert_noop!(
+ Unique::transfer_from(
+ origin1,
+ account(1),
+ account(3),
+ collection_id,
+ TokenId(1),
+ 1
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)
+#[test]
+fn allow_list_test_5() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_noop!(
+ Unique::burn_item(origin1.clone(), CollectionId(1), TokenId(1), 1).map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method).
+#[test]
+fn allow_list_test_6() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+
+ // do approve
+ assert_noop!(
+ Unique::approve(origin1, account(1), CollectionId(1), TokenId(1), 1)
+ .map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests) and
+// tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests)
+#[test]
+fn allow_list_test_7() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ assert_ok!(Unique::transfer(
+ origin1,
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ });
+}
+
+#[test]
+fn allow_list_test_8() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ // Create NFT for account 1
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ let origin1 = Origin::signed(1);
+
+ // Toggle Allow List mode and add accounts 1 and 2
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(1)
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+
+ // Sself-approve account 1 for NFT 1
+ assert_ok!(Unique::approve(
+ origin1.clone(),
+ account(1),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+ account(1)
+ );
+
+ // Transfer from 1 to 2
+ assert_ok!(Unique::transfer_from(
+ origin1,
+ account(1),
+ account(2),
+ CollectionId(1),
+ TokenId(1),
+ 1
+ ));
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner.
+#[test]
+fn allow_list_test_9() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin.
+#[test]
+fn allow_list_test_10() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ false
+ ));
+
+ assert_ok!(Unique::add_collection_admin(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+
+ assert_ok!(Unique::create_item(
+ origin2,
+ collection_id,
+ account(2),
+ default_nft_data().into()
+ ));
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow listed address.
+#[test]
+fn allow_list_test_11() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ false
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+
+ assert_noop!(
+ Unique::create_item(
+ origin2,
+ CollectionId(1),
+ account(2),
+ default_nft_data().into()
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::PublicMintingNotAllowed
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address.
+#[test]
+fn allow_list_test_12() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));
+
+ assert_noop!(
+ Unique::create_item(
+ origin2,
+ CollectionId(1),
+ account(2),
+ default_nft_data().into()
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::PublicMintingNotAllowed
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner.
+#[test]
+fn allow_list_test_13() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin.
+#[test]
+fn allow_list_test_14() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ true
+ ));
+
+ assert_ok!(Unique::add_collection_admin(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+
+ assert_ok!(Unique::create_item(
+ origin2,
+ collection_id,
+ account(2),
+ default_nft_data().into()
+ ));
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address.
+#[test]
+fn allow_list_test_15() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));
+
+ assert_noop!(
+ Unique::create_item(
+ origin2,
+ collection_id,
+ account(2),
+ default_nft_data().into()
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::AddressNotInAllowlist
+ );
+ });
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address.
+#[test]
+fn allow_list_test_16() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ true
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1,
+ collection_id,
+ account(2)
+ ));
+
+ assert_ok!(Unique::create_item(
+ origin2,
+ collection_id,
+ account(2),
+ default_nft_data().into()
+ ));
+ });
+}
+
+// Total number of collections. Positive test
+#[test]
+fn total_number_collections_bound() {
+ new_test_ext().execute_with(|| {
+ create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ });
+}
+
+#[test]
+fn create_max_collections() {
+ new_test_ext().execute_with(|| {
+ for i in 1..COLLECTION_NUMBER_LIMIT {
+ create_test_collection(&CollectionMode::NFT, CollectionId(i));
+ }
+ });
+}
+
+// Total number of collections. Negative test
+#[test]
+fn total_number_collections_bound_neg() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
+
+ for i in 1..=COLLECTION_NUMBER_LIMIT {
+ create_test_collection(&CollectionMode::NFT, CollectionId(i));
+ }
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+ let data: CreateCollectionData<u64> = CreateCollectionData {
+ name: col_name1.try_into().unwrap(),
+ description: col_desc1.try_into().unwrap(),
+ token_prefix: token_prefix1.try_into().unwrap(),
+ mode: CollectionMode::NFT,
+ ..Default::default()
+ };
+
+ // 11-th collection in chain. Expects error
+ assert_noop!(
+ Unique::create_collection_ex(origin1, data),
+ CommonError::<Test>::TotalCollectionsLimitExceeded
+ );
+ });
+}
+
+// Owned tokens by a single address. Positive test
+#[test]
+fn owned_tokens_bound() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+ create_test_item(collection_id, &data.into());
+ });
+}
+
+// Owned tokens by a single address. Negotive test
+#[test]
+fn owned_tokens_bound_neg() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ for _ in 1..=MAX_TOKEN_OWNERSHIP {
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+ }
+
+ let data = default_nft_data();
+ assert_noop!(
+ Unique::create_item(origin1, CollectionId(1), account(1), data.into())
+ .map_err(|e| e.error),
+ CommonError::<Test>::AccountTokenLimitExceeded
+ );
+ });
+}
+
+// Number of collection admins. Positive test
+#[test]
+fn collection_admins_bound() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account(2)
+ ));
+ assert_ok!(Unique::add_collection_admin(
+ origin1,
+ collection_id,
+ account(3)
+ ));
+ });
+}
+
+// Number of collection admins. Negotive test
+#[test]
+fn collection_admins_bound_neg() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ for i in 0..COLLECTION_ADMINS_LIMIT {
+ assert_ok!(Unique::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account((2 + i).into())
+ ));
+ }
+ assert_noop!(
+ Unique::add_collection_admin(
+ origin1,
+ collection_id,
+ account((3 + COLLECTION_ADMINS_LIMIT).into())
+ ),
+ CommonError::<Test>::CollectionAdminCountExceeded
+ );
+ });
+}
+// #endregion
+
+#[test]
+fn set_const_on_chain_schema() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::set_const_on_chain_schema(
+ origin1,
+ collection_id,
+ b"test const on chain schema".to_vec().try_into().unwrap()
+ ));
+
+ assert_eq!(
+ <pallet_common::CollectionData<Test>>::get((
+ collection_id,
+ CollectionField::ConstOnChainSchema
+ )),
+ b"test const on chain schema".to_vec()
+ );
+ assert_eq!(
+ <pallet_common::CollectionData<Test>>::get((
+ collection_id,
+ CollectionField::VariableOnChainSchema
+ )),
+ b"".to_vec()
+ );
+ });
+}
+
+#[test]
+fn set_variable_on_chain_schema() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(Unique::set_variable_on_chain_schema(
+ origin1,
+ collection_id,
+ b"test variable on chain schema"
+ .to_vec()
+ .try_into()
+ .unwrap()
+ ));
+
+ assert_eq!(
+ <pallet_common::CollectionData<Test>>::get((
+ collection_id,
+ CollectionField::ConstOnChainSchema
+ )),
+ b"".to_vec()
+ );
+ assert_eq!(
+ <pallet_common::CollectionData<Test>>::get((
+ collection_id,
+ CollectionField::VariableOnChainSchema
+ )),
+ b"test variable on chain schema".to_vec()
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(CollectionId(1), &data.into());
+
+ let variable_data = b"test data".to_vec();
+ assert_ok!(Unique::set_variable_meta_data(
+ origin1,
+ collection_id,
+ TokenId(1),
+ variable_data.clone().try_into().unwrap()
+ ));
+
+ assert_eq!(
+ <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))
+ .unwrap()
+ .variable_data,
+ variable_data
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ let variable_data = b"test data".to_vec();
+ assert_ok!(Unique::set_variable_meta_data(
+ origin1,
+ collection_id,
+ TokenId(1),
+ variable_data.clone().try_into().unwrap()
+ ));
+
+ assert_eq!(
+ <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1))).variable_data,
+ variable_data
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_fungible_token_fails() {
+ new_test_ext().execute_with(|| {
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
+ let variable_data = b"test data".to_vec();
+ assert_noop!(
+ Unique::set_variable_meta_data(
+ origin1,
+ collection_id,
+ TokenId(0),
+ variable_data.try_into().unwrap()
+ )
+ .map_err(|e| e.error),
+ <pallet_fungible::Error<Test>>::FungibleItemsDontHaveData
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() {
+ new_test_ext().execute_with(|| {
+ //default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_meta_update_permission_flag(
+ origin1.clone(),
+ collection_id,
+ MetaUpdatePermission::ItemOwner,
+ ));
+
+ let variable_data = b"ten chars.".to_vec();
+ assert_ok!(Unique::set_variable_meta_data(
+ origin1,
+ collection_id,
+ TokenId(1),
+ variable_data.clone().try_into().unwrap()
+ ));
+
+ assert_eq!(
+ <pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))
+ .unwrap()
+ .variable_data,
+ variable_data
+ );
+ });
+}
+
+#[test]
+fn collection_transfer_flag_works() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ assert_ok!(Unique::set_transfers_enabled_flag(
+ origin1,
+ collection_id,
+ true
+ ));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ let origin1 = Origin::signed(1);
+
+ // default scenario
+ assert_ok!(Unique::transfer(
+ origin1,
+ account(2),
+ collection_id,
+ TokenId(1),
+ 1
+ ));
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ false
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 0
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
+ 1
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
+
+ let collection_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_mint_permission(
+ origin2.clone(),
+ collection_id,
+ true
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin2.clone(),
+ collection_id,
+ account(1)
+ ));
+
+ assert_ok!(Unique::add_collection_admin(
+ origin2.clone(),
+ collection_id,
+ account(1)
+ ));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::Admin,
+ ));
+
+ let variable_data = b"test.".to_vec();
+ assert_ok!(Unique::set_variable_meta_data(
+ origin1,
+ collection_id,
+ TokenId(1),
+ variable_data.clone().try_into().unwrap()
+ ));
+
+ assert_eq!(
+ <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))
+ .unwrap()
+ .variable_data,
+ variable_data
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
+
+ let collection_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_mint_permission(
+ origin2.clone(),
+ collection_id,
+ true
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin2.clone(),
+ collection_id,
+ account(1)
+ ));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::Admin,
+ ));
+
+ let variable_data = b"test.".to_vec();
+ assert_noop!(
+ Unique::set_variable_meta_data(
+ origin1,
+ collection_id,
+ TokenId(1),
+ variable_data.try_into().unwrap()
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::NoPermission
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_flag_after_freeze() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
+
+ let collection_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
+
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(Unique::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::None,
+ ));
+ assert_noop!(
+ Unique::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::Admin
+ ),
+ CommonError::<Test>::MetadataFlagFrozen
+ );
+ });
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_none_flag_neg() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
+
+ let collection_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
+ let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
+ assert_ok!(Unique::set_meta_update_permission_flag(
+ origin1.clone(),
+ collection_id,
+ MetaUpdatePermission::None,
+ ));
+
+ let variable_data = b"test.".to_vec();
+ assert_noop!(
+ Unique::set_variable_meta_data(
+ origin1.clone(),
+ collection_id,
+ TokenId(1),
+ variable_data.try_into().unwrap()
+ )
+ .map_err(|e| e.error),
+ CommonError::<Test>::NoPermission
+ );
+ });
+}
+
+#[test]
+fn collection_transfer_flag_works_neg() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+ assert_ok!(Unique::set_transfers_enabled_flag(
+ origin1,
+ collection_id,
+ false
+ ));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+
+ let origin1 = Origin::signed(1);
+
+ // default scenario
+ assert_noop!(
+ Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 1)
+ .map_err(|e| e.error),
+ CommonError::<Test>::TransferNotAllowed
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+ 1
+ );
+ assert_eq!(
+ <pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
+ 0
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+ true
+ );
+ assert_eq!(
+ <pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+ false
+ );
+ });
+}
+
+#[test]
+fn collection_sponsoring() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
+ let user1 = 1_u64;
+ let user2 = 777_u64;
+ let origin1 = Origin::signed(user1);
+ let origin2 = Origin::signed(user2);
+ let account2 = account(user2);
+
+ let collection_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
+ assert_ok!(Unique::set_collection_sponsor(
+ origin1.clone(),
+ collection_id,
+ user1
+ ));
+ assert_ok!(Unique::confirm_sponsorship(origin1.clone(), collection_id));
+
+ // Expect error while have no permissions
+ assert!(Unique::create_item(
+ origin2.clone(),
+ collection_id,
+ account2.clone(),
+ default_nft_data().into()
+ )
+ .is_err());
+
+ assert_ok!(Unique::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(Unique::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account2.clone()
+ ));
+ assert_ok!(Unique::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ true
+ ));
+
+ assert_ok!(Unique::create_item(
+ origin2,
+ collection_id,
+ account2,
+ default_nft_data().into()
+ ));
+ });
+}