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.rsdiffbeforeafterboth--- a/pallets/unique/src/tests.rs
+++ /dev/null
@@ -1,2912 +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/>.
-
-// Tests to be written here
-use super::*;
-use crate::mock::*;
-use crate::{AccessMode, CollectionMode};
-use up_data_structs::{
- COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
- CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
- TokenId, MAX_TOKEN_OWNERSHIP,
-};
-use frame_support::{assert_noop, assert_ok, assert_err};
-use sp_std::convert::TryInto;
-use pallet_balances;
-
-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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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 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 = TemplateModule::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!(
- TemplateModule::create_collection_ex(origin1, data),
- Error::<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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);
-// }
-// assert_eq!(TemplateModule::balance_count(1, 1), 3000);
-// assert_eq!(TemplateModule::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!(TemplateModule::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!(TemplateModule::transfer(
- origin2.clone(),
- account(3),
- CollectionId(1),
- TokenId(0),
- 3
- ));
-
- // split item and new owner has account scenario
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(
- TemplateModule::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!(TemplateModule::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!(
- TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::set_mint_permission(
- origin1.clone(),
- CollectionId(1),
- true
- ));
- assert_ok!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- CollectionId(1),
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(2)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(3)
- ));
-
- // Account 1 approves account 2 for NFT 1
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::set_mint_permission(
- origin1.clone(),
- CollectionId(1),
- true
- ));
- assert_ok!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- CollectionId(1),
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(2)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(3)
- ));
-
- // Account 1 approves account 2 for 1023 pieces of RFT 1
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::set_mint_permission(
- origin1.clone(),
- CollectionId(1),
- true
- ));
- assert_ok!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- CollectionId(1),
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(2)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(3)
- ));
-
- // do approve
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(
- TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::burn_item(
- origin1.clone(),
- collection_id,
- TokenId(1),
- 1
- ));
-
- // burn item again
- assert_noop!(
- TemplateModule::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!(TemplateModule::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!(TemplateModule::burn_item(
- origin1.clone(),
- CollectionId(1),
- TokenId(0),
- 5
- ));
- assert_noop!(
- TemplateModule::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!(TemplateModule::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!(
- TemplateModule::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!(TemplateModule::set_mint_permission(
- origin1.clone(),
- collection_id,
- true
- ));
- assert_ok!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
-
- assert_ok!(TemplateModule::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!(TemplateModule::burn_item(
- origin1.clone(),
- collection_id,
- TokenId(1),
- 1023
- ));
- assert_noop!(
- TemplateModule::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!(TemplateModule::add_collection_admin(
- origin1.clone(),
- collection1_id,
- account(2)
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::add_collection_admin(
- origin1.clone(),
- collection1_id,
- account(2)
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::set_mint_permission(
- origin1.clone(),
- CollectionId(1),
- true
- ));
- assert_ok!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- CollectionId(1),
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- CollectionId(1),
- account(2)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1,
- CollectionId(1),
- account(3)
- ));
-
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::add_collection_admin(
- origin1,
- collection_id,
- account(2)
- ));
- assert_ok!(TemplateModule::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!(
- TemplateModule::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!(
- TemplateModule::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!(TemplateModule::destroy_collection(
- origin1.clone(),
- collection_id
- ));
- assert_noop!(
- TemplateModule::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!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::add_collection_admin(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- // Owner adds address 3 to allow list
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1,
- collection_id,
- account(3)
- ));
-
- // Admin removes address 3 from allow list
- assert_ok!(TemplateModule::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!(TemplateModule::add_to_allow_list(
- origin1,
- collection_id,
- account(2)
- ));
- assert_noop!(
- TemplateModule::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!(
- TemplateModule::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!(TemplateModule::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!(TemplateModule::destroy_collection(origin1, collection_id));
-
- // Attempt to remove account 2 from collection allow-list => error
- assert_noop!(
- TemplateModule::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!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
- assert_ok!(TemplateModule::remove_from_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
- assert_eq!(
- <pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
- false
- );
- assert_ok!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- // do approve
- assert_ok!(TemplateModule::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!(TemplateModule::remove_from_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- // do approve
- assert_ok!(TemplateModule::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!(TemplateModule::remove_from_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
-
- // do approve
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- assert_ok!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(1)
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account(2)
- ));
-
- // Sself-approve account 1 for NFT 1
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1.clone(),
- collection_id,
- false
- ));
-
- assert_ok!(TemplateModule::add_collection_admin(
- origin1,
- collection_id,
- account(2)
- ));
-
- assert_ok!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1.clone(),
- collection_id,
- false
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1,
- collection_id,
- account(2)
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1,
- collection_id,
- false
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1.clone(),
- collection_id,
- true
- ));
-
- assert_ok!(TemplateModule::add_collection_admin(
- origin1,
- collection_id,
- account(2)
- ));
-
- assert_ok!(TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1,
- collection_id,
- true
- ));
-
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1.clone(),
- collection_id,
- true
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1,
- collection_id,
- account(2)
- ));
-
- assert_ok!(TemplateModule::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!(
- TemplateModule::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!(
- TemplateModule::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!(TemplateModule::add_collection_admin(
- origin1.clone(),
- collection_id,
- account(2)
- ));
- assert_ok!(TemplateModule::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!(TemplateModule::add_collection_admin(
- origin1.clone(),
- collection_id,
- account((2 + i).into())
- ));
- }
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_const_on_chain_schema(
- origin1,
- collection_id,
- b"test const on chain schema".to_vec().try_into().unwrap()
- ));
-
- assert_eq!(
- <pallet_common::CollectionById<Test>>::get(collection_id)
- .unwrap()
- .const_on_chain_schema,
- b"test const on chain schema".to_vec()
- );
- assert_eq!(
- <pallet_common::CollectionById<Test>>::get(collection_id)
- .unwrap()
- .variable_on_chain_schema,
- 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!(TemplateModule::set_variable_on_chain_schema(
- origin1,
- collection_id,
- b"test variable on chain schema"
- .to_vec()
- .try_into()
- .unwrap()
- ));
-
- assert_eq!(
- <pallet_common::CollectionById<Test>>::get(collection_id)
- .unwrap()
- .const_on_chain_schema,
- b"".to_vec()
- );
- assert_eq!(
- <pallet_common::CollectionById<Test>>::get(collection_id)
- .unwrap()
- .variable_on_chain_schema,
- 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!(TemplateModule::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!(TemplateModule::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!(
- TemplateModule::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!(TemplateModule::set_meta_update_permission_flag(
- origin1.clone(),
- collection_id,
- MetaUpdatePermission::ItemOwner,
- ));
-
- let variable_data = b"ten chars.".to_vec();
- assert_ok!(TemplateModule::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!(TemplateModule::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!(TemplateModule::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!(TemplateModule::set_mint_permission(
- origin2.clone(),
- collection_id,
- true
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin2.clone(),
- collection_id,
- account(1)
- ));
-
- assert_ok!(TemplateModule::add_collection_admin(
- origin2.clone(),
- collection_id,
- account(1)
- ));
-
- let data = default_nft_data();
- create_test_item(collection_id, &data.into());
-
- assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::Admin,
- ));
-
- let variable_data = b"test.".to_vec();
- assert_ok!(TemplateModule::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!(TemplateModule::set_mint_permission(
- origin2.clone(),
- collection_id,
- true
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin2.clone(),
- collection_id,
- account(1)
- ));
-
- let data = default_nft_data();
- create_test_item(collection_id, &data.into());
-
- assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::Admin,
- ));
-
- let variable_data = b"test.".to_vec();
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::None,
- ));
- assert_noop!(
- TemplateModule::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!(TemplateModule::set_meta_update_permission_flag(
- origin1.clone(),
- collection_id,
- MetaUpdatePermission::None,
- ));
-
- let variable_data = b"test.".to_vec();
- assert_noop!(
- TemplateModule::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!(TemplateModule::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!(
- TemplateModule::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!(TemplateModule::set_collection_sponsor(
- origin1.clone(),
- collection_id,
- user1
- ));
- assert_ok!(TemplateModule::confirm_sponsorship(
- origin1.clone(),
- collection_id
- ));
-
- // Expect error while have no permissions
- assert!(TemplateModule::create_item(
- origin2.clone(),
- collection_id,
- account2.clone(),
- default_nft_data().into()
- )
- .is_err());
-
- assert_ok!(TemplateModule::set_public_access_mode(
- origin1.clone(),
- collection_id,
- AccessMode::AllowList
- ));
- assert_ok!(TemplateModule::add_to_allow_list(
- origin1.clone(),
- collection_id,
- account2.clone()
- ));
- assert_ok!(TemplateModule::set_mint_permission(
- origin1.clone(),
- collection_id,
- true
- ));
-
- assert_ok!(TemplateModule::create_item(
- origin2,
- collection_id,
- account2,
- default_nft_data().into()
- ));
- });
-}
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.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//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.1819#![cfg_attr(not(feature = "std"), no_std)]20// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.21#![recursion_limit = "1024"]22#![allow(clippy::from_over_into, clippy::identity_op)]23#![allow(clippy::fn_to_numeric_cast_with_truncation)]24// Make the WASM binary available.25#[cfg(feature = "std")]26include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));2728use sp_api::impl_runtime_apis;29use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};30use sp_runtime::DispatchError;31// #[cfg(any(feature = "std", test))]32// pub use sp_runtime::BuildStorage;3334use sp_runtime::{35 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,36 traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, AccountIdConversion, Zero},37 transaction_validity::{TransactionSource, TransactionValidity},38 ApplyExtrinsicResult, RuntimeAppPublic,39};4041use sp_std::prelude::*;4243#[cfg(feature = "std")]44use sp_version::NativeVersion;45use sp_version::RuntimeVersion;46pub use pallet_transaction_payment::{47 Multiplier, TargetedFeeAdjustment, FeeDetails, RuntimeDispatchInfo,48};49// A few exports that help ease life for downstream crates.50pub use pallet_balances::Call as BalancesCall;51pub use pallet_evm::{52 EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _,53};54pub use frame_support::{55 construct_runtime, match_types,56 dispatch::DispatchResult,57 PalletId, parameter_types, StorageValue, ConsensusEngineId,58 traits::{59 tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,60 Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,61 OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,62 },63 weights::{64 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},65 DispatchClass, DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,66 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,67 },68};69use up_data_structs::mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping};70use up_data_structs::{CollectionId, TokenId, CollectionStats, CollectionLimits, RpcCollection};71// use pallet_contracts::weights::WeightInfo;72// #[cfg(any(feature = "std", test))]73use frame_system::{74 self as frame_system, EnsureRoot, EnsureSigned,75 limits::{BlockWeights, BlockLength},76};77use sp_arithmetic::{78 traits::{BaseArithmetic, Unsigned},79};80use smallvec::smallvec;81use codec::{Encode, Decode};82use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping};83use fp_rpc::TransactionStatus;84use sp_runtime::{85 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},86 transaction_validity::TransactionValidityError,87 SaturatedConversion,88};8990// pub use pallet_timestamp::Call as TimestampCall;91pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;9293// Polkadot imports94use pallet_xcm::XcmPassthrough;95use polkadot_parachain::primitives::Sibling;96use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};97use xcm_builder::{98 AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,99 EnsureXcmOrigin, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser,100 RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,101 SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,102 ParentIsPreset,103};104use xcm_executor::{Config, XcmExecutor, Assets};105use sp_std::{marker::PhantomData};106107use xcm::latest::{108 // Xcm,109 AssetId::{Concrete},110 Fungibility::Fungible as XcmFungible,111 MultiAsset,112 Error as XcmError,113};114use xcm_executor::traits::{MatchesFungible, WeightTrader};115//use xcm_executor::traits::MatchesFungible;116use sp_runtime::traits::CheckedConversion;117118use unique_runtime_common::{119 impl_common_runtime_apis,120 types::*,121 constants::*,122 dispatch::{CollectionDispatchT, CollectionDispatch},123};124125pub const RUNTIME_NAME: &str = "opal";126pub const TOKEN_SYMBOL: &str = "OPL";127128type CrossAccountId = pallet_evm::account::BasicCrossAccountId<Runtime>;129130impl RuntimeInstance for Runtime {131 type CrossAccountId = self::CrossAccountId;132 type TransactionConverter = self::TransactionConverter;133134 fn get_transaction_converter() -> TransactionConverter {135 TransactionConverter136 }137}138139/// The type for looking up accounts. We don't expect more than 4 billion of them, but you140/// never know...141pub type AccountIndex = u32;142143/// Balance of an account.144pub type Balance = u128;145146/// Index of a transaction in the chain.147pub type Index = u32;148149/// A hash of some data used by the chain.150pub type Hash = sp_core::H256;151152/// Digest item type.153pub type DigestItem = generic::DigestItem;154155/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know156/// the specifics of the runtime. They can then be made to be agnostic over specific formats157/// of data like extrinsics, allowing for them to continue syncing the network through upgrades158/// to even the core data structures.159pub mod opaque {160 use sp_std::prelude::*;161 use sp_runtime::impl_opaque_keys;162 use super::Aura;163164 pub use unique_runtime_common::types::*;165166 impl_opaque_keys! {167 pub struct SessionKeys {168 pub aura: Aura,169 }170 }171}172173/// This runtime version.174pub const VERSION: RuntimeVersion = RuntimeVersion {175 spec_name: create_runtime_str!(RUNTIME_NAME),176 impl_name: create_runtime_str!(RUNTIME_NAME),177 authoring_version: 1,178 spec_version: 920000,179 impl_version: 0,180 apis: RUNTIME_API_VERSIONS,181 transaction_version: 1,182 state_version: 0,183};184185#[derive(codec::Encode, codec::Decode)]186pub enum XCMPMessage<XAccountId, XBalance> {187 /// Transfer tokens to the given account from the Parachain account.188 TransferToken(XAccountId, XBalance),189}190191/// The version information used to identify this runtime when compiled natively.192#[cfg(feature = "std")]193pub fn native_version() -> NativeVersion {194 NativeVersion {195 runtime_version: VERSION,196 can_author_with: Default::default(),197 }198}199200type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;201202pub struct DealWithFees;203impl OnUnbalanced<NegativeImbalance> for DealWithFees {204 fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance>) {205 if let Some(fees) = fees_then_tips.next() {206 // for fees, 100% to treasury207 let mut split = fees.ration(100, 0);208 if let Some(tips) = fees_then_tips.next() {209 // for tips, if any, 100% to treasury210 tips.ration_merge_into(100, 0, &mut split);211 }212 Treasury::on_unbalanced(split.0);213 // Author::on_unbalanced(split.1);214 }215 }216}217218parameter_types! {219 pub const BlockHashCount: BlockNumber = 2400;220 pub RuntimeBlockLength: BlockLength =221 BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);222 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);223 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;224 pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()225 .base_block(BlockExecutionWeight::get())226 .for_class(DispatchClass::all(), |weights| {227 weights.base_extrinsic = ExtrinsicBaseWeight::get();228 })229 .for_class(DispatchClass::Normal, |weights| {230 weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);231 })232 .for_class(DispatchClass::Operational, |weights| {233 weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);234 // Operational transactions have some extra reserved space, so that they235 // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.236 weights.reserved = Some(237 MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT238 );239 })240 .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)241 .build_or_panic();242 pub const Version: RuntimeVersion = VERSION;243 pub const SS58Prefix: u8 = 42;244}245246parameter_types! {247 pub const ChainId: u64 = 8882;248}249250pub struct FixedFee;251impl FeeCalculator for FixedFee {252 fn min_gas_price() -> U256 {253 MIN_GAS_PRICE.into()254 }255}256257// Assuming slowest ethereum opcode is SSTORE, with gas price of 20000 as our worst case258// (contract, which only writes a lot of data),259// approximating on top of our real store write weight260parameter_types! {261 pub const WritesPerSecond: u64 = WEIGHT_PER_SECOND / <Runtime as frame_system::Config>::DbWeight::get().write;262 pub const GasPerSecond: u64 = WritesPerSecond::get() * 20000;263 pub const WeightPerGas: u64 = WEIGHT_PER_SECOND / GasPerSecond::get();264}265266/// Limiting EVM execution to 50% of block for substrate users and management tasks267/// EVM transaction consumes more weight than substrate's, so we can't rely on them being268/// scheduled fairly269const EVM_DISPATCH_RATIO: Perbill = Perbill::from_percent(50);270parameter_types! {271 pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * EVM_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT / WeightPerGas::get());272}273274pub enum FixedGasWeightMapping {}275impl GasWeightMapping for FixedGasWeightMapping {276 fn gas_to_weight(gas: u64) -> Weight {277 gas.saturating_mul(WeightPerGas::get())278 }279 fn weight_to_gas(weight: Weight) -> u64 {280 weight / WeightPerGas::get()281 }282}283284impl pallet_evm::account::Config for Runtime {285 type CrossAccountId = pallet_evm::account::BasicCrossAccountId<Self>;286 type EvmAddressMapping = pallet_evm::HashedAddressMapping<Self::Hashing>;287 type EvmBackwardsAddressMapping = fp_evm_mapping::MapBackwardsAddressTruncated;288}289290impl pallet_evm::Config for Runtime {291 type BlockGasLimit = BlockGasLimit;292 type FeeCalculator = FixedFee;293 type GasWeightMapping = FixedGasWeightMapping;294 type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;295 type CallOrigin = EnsureAddressTruncated;296 type WithdrawOrigin = EnsureAddressTruncated;297 type AddressMapping = HashedAddressMapping<Self::Hashing>;298 type PrecompilesType = ();299 type PrecompilesValue = ();300 type Currency = Balances;301 type Event = Event;302 type OnMethodCall = (303 pallet_evm_migration::OnMethodCall<Self>,304 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,305 CollectionDispatchT<Self>,306 );307 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;308 type ChainId = ChainId;309 type Runner = pallet_evm::runner::stack::Runner<Self>;310 type OnChargeTransaction = pallet_evm::EVMCurrencyAdapter<Balances, DealWithFees>;311 type TransactionValidityHack = pallet_evm_transaction_payment::TransactionValidityHack<Self>;312 type FindAuthor = EthereumFindAuthor<Aura>;313}314315impl pallet_evm_migration::Config for Runtime {316 type WeightInfo = pallet_evm_migration::weights::SubstrateWeight<Self>;317}318319pub struct EthereumFindAuthor<F>(core::marker::PhantomData<F>);320impl<F: FindAuthor<u32>> FindAuthor<H160> for EthereumFindAuthor<F> {321 fn find_author<'a, I>(digests: I) -> Option<H160>322 where323 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,324 {325 if let Some(author_index) = F::find_author(digests) {326 let authority_id = Aura::authorities()[author_index as usize].clone();327 return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24]));328 }329 None330 }331}332333impl pallet_ethereum::Config for Runtime {334 type Event = Event;335 type StateRoot = pallet_ethereum::IntermediateStateRoot<Self>;336}337338impl pallet_randomness_collective_flip::Config for Runtime {}339340impl frame_system::Config for Runtime {341 /// The data to be stored in an account.342 type AccountData = pallet_balances::AccountData<Balance>;343 /// The identifier used to distinguish between accounts.344 type AccountId = AccountId;345 /// The basic call filter to use in dispatchable.346 type BaseCallFilter = Everything;347 /// Maximum number of block number to block hash mappings to keep (oldest pruned first).348 type BlockHashCount = BlockHashCount;349 /// The maximum length of a block (in bytes).350 type BlockLength = RuntimeBlockLength;351 /// The index type for blocks.352 type BlockNumber = BlockNumber;353 /// The weight of the overhead invoked on the block import process, independent of the extrinsics included in that block.354 type BlockWeights = RuntimeBlockWeights;355 /// The aggregated dispatch type that is available for extrinsics.356 type Call = Call;357 /// The weight of database operations that the runtime can invoke.358 type DbWeight = RocksDbWeight;359 /// The ubiquitous event type.360 type Event = Event;361 /// The type for hashing blocks and tries.362 type Hash = Hash;363 /// The hashing algorithm used.364 type Hashing = BlakeTwo256;365 /// The header type.366 type Header = generic::Header<BlockNumber, BlakeTwo256>;367 /// The index type for storing how many extrinsics an account has signed.368 type Index = Index;369 /// The lookup mechanism to get account ID from whatever is passed in dispatchers.370 type Lookup = AccountIdLookup<AccountId, ()>;371 /// What to do if an account is fully reaped from the system.372 type OnKilledAccount = ();373 /// What to do if a new account is created.374 type OnNewAccount = ();375 type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;376 /// The ubiquitous origin type.377 type Origin = Origin;378 /// This type is being generated by `construct_runtime!`.379 type PalletInfo = PalletInfo;380 /// This is used as an identifier of the chain. 42 is the generic substrate prefix.381 type SS58Prefix = SS58Prefix;382 /// Weight information for the extrinsics of this pallet.383 type SystemWeightInfo = frame_system::weights::SubstrateWeight<Self>;384 /// Version of the runtime.385 type Version = Version;386 type MaxConsumers = ConstU32<16>;387}388389parameter_types! {390 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;391}392393impl pallet_timestamp::Config for Runtime {394 /// A timestamp: milliseconds since the unix epoch.395 type Moment = u64;396 type OnTimestampSet = ();397 type MinimumPeriod = MinimumPeriod;398 type WeightInfo = ();399}400401parameter_types! {402 // pub const ExistentialDeposit: u128 = 500;403 pub const ExistentialDeposit: u128 = 0;404 pub const MaxLocks: u32 = 50;405}406407impl pallet_balances::Config for Runtime {408 type MaxLocks = MaxLocks;409 type MaxReserves = ();410 type ReserveIdentifier = [u8; 8];411 /// The type for recording an account's balance.412 type Balance = Balance;413 /// The ubiquitous event type.414 type Event = Event;415 type DustRemoval = Treasury;416 type ExistentialDeposit = ExistentialDeposit;417 type AccountStore = System;418 type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;419}420421pub const fn deposit(items: u32, bytes: u32) -> Balance {422 items as Balance * 15 * CENTIUNIQUE + (bytes as Balance) * 6 * CENTIUNIQUE423}424425/*426parameter_types! {427 pub TombstoneDeposit: Balance = deposit(428 1,429 sp_std::mem::size_of::<pallet_contracts::Pallet<Runtime>> as u32,430 );431 pub DepositPerContract: Balance = TombstoneDeposit::get();432 pub const DepositPerStorageByte: Balance = deposit(0, 1);433 pub const DepositPerStorageItem: Balance = deposit(1, 0);434 pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);435 pub const SurchargeReward: Balance = 150 * MILLIUNIQUE;436 pub const SignedClaimHandicap: u32 = 2;437 pub const MaxDepth: u32 = 32;438 pub const MaxValueSize: u32 = 16 * 1024;439 pub const MaxCodeSize: u32 = 1024 * 1024 * 25; // 25 Mb440 // The lazy deletion runs inside on_initialize.441 pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *442 RuntimeBlockWeights::get().max_block;443 // The weight needed for decoding the queue should be less or equal than a fifth444 // of the overall weight dedicated to the lazy deletion.445 pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (446 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -447 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)448 )) / 5) as u32;449 pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();450}451452impl pallet_contracts::Config for Runtime {453 type Time = Timestamp;454 type Randomness = RandomnessCollectiveFlip;455 type Currency = Balances;456 type Event = Event;457 type RentPayment = ();458 type SignedClaimHandicap = SignedClaimHandicap;459 type TombstoneDeposit = TombstoneDeposit;460 type DepositPerContract = DepositPerContract;461 type DepositPerStorageByte = DepositPerStorageByte;462 type DepositPerStorageItem = DepositPerStorageItem;463 type RentFraction = RentFraction;464 type SurchargeReward = SurchargeReward;465 type WeightPrice = pallet_transaction_payment::Pallet<Self>;466 type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;467 type ChainExtension = NFTExtension;468 type DeletionQueueDepth = DeletionQueueDepth;469 type DeletionWeightLimit = DeletionWeightLimit;470 type Schedule = Schedule;471 type CallStack = [pallet_contracts::Frame<Self>; 31];472}473*/474475parameter_types! {476 /// This value increases the priority of `Operational` transactions by adding477 /// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.478 pub const OperationalFeeMultiplier: u8 = 5;479}480481/// Linear implementor of `WeightToFeePolynomial`482pub struct LinearFee<T>(sp_std::marker::PhantomData<T>);483484impl<T> WeightToFeePolynomial for LinearFee<T>485where486 T: BaseArithmetic + From<u32> + Copy + Unsigned,487{488 type Balance = T;489490 fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {491 smallvec!(WeightToFeeCoefficient {492 // Targeting 0.1 Unique per NFT transfer493 coeff_integer: WEIGHT_TO_FEE_COEFF.into(),494 coeff_frac: Perbill::zero(),495 negative: false,496 degree: 1,497 })498 }499}500501impl pallet_transaction_payment::Config for Runtime {502 type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, DealWithFees>;503 type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;504 type OperationalFeeMultiplier = OperationalFeeMultiplier;505 type WeightToFee = LinearFee<Balance>;506 type FeeMultiplierUpdate = ();507}508509parameter_types! {510 pub const ProposalBond: Permill = Permill::from_percent(5);511 pub const ProposalBondMinimum: Balance = 1 * UNIQUE;512 pub const ProposalBondMaximum: Balance = 1000 * UNIQUE;513 pub const SpendPeriod: BlockNumber = 5 * MINUTES;514 pub const Burn: Permill = Permill::from_percent(0);515 pub const TipCountdown: BlockNumber = 1 * DAYS;516 pub const TipFindersFee: Percent = Percent::from_percent(20);517 pub const TipReportDepositBase: Balance = 1 * UNIQUE;518 pub const DataDepositPerByte: Balance = 1 * CENTIUNIQUE;519 pub const BountyDepositBase: Balance = 1 * UNIQUE;520 pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;521 pub const TreasuryModuleId: PalletId = PalletId(*b"py/trsry");522 pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;523 pub const MaximumReasonLength: u32 = 16384;524 pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);525 pub const BountyValueMinimum: Balance = 5 * UNIQUE;526 pub const MaxApprovals: u32 = 100;527}528529impl pallet_treasury::Config for Runtime {530 type PalletId = TreasuryModuleId;531 type Currency = Balances;532 type ApproveOrigin = EnsureRoot<AccountId>;533 type RejectOrigin = EnsureRoot<AccountId>;534 type Event = Event;535 type OnSlash = ();536 type ProposalBond = ProposalBond;537 type ProposalBondMinimum = ProposalBondMinimum;538 type ProposalBondMaximum = ProposalBondMaximum;539 type SpendPeriod = SpendPeriod;540 type Burn = Burn;541 type BurnDestination = ();542 type SpendFunds = ();543 type WeightInfo = pallet_treasury::weights::SubstrateWeight<Self>;544 type MaxApprovals = MaxApprovals;545}546547impl pallet_sudo::Config for Runtime {548 type Event = Event;549 type Call = Call;550}551552pub struct RelayChainBlockNumberProvider<T>(sp_std::marker::PhantomData<T>);553554impl<T: cumulus_pallet_parachain_system::Config> BlockNumberProvider555 for RelayChainBlockNumberProvider<T>556{557 type BlockNumber = BlockNumber;558559 fn current_block_number() -> Self::BlockNumber {560 cumulus_pallet_parachain_system::Pallet::<T>::validation_data()561 .map(|d| d.relay_parent_number)562 .unwrap_or_default()563 }564}565566parameter_types! {567 pub const MinVestedTransfer: Balance = 10 * UNIQUE;568 pub const MaxVestingSchedules: u32 = 28;569}570571impl orml_vesting::Config for Runtime {572 type Event = Event;573 type Currency = pallet_balances::Pallet<Runtime>;574 type MinVestedTransfer = MinVestedTransfer;575 type VestedTransferOrigin = EnsureSigned<AccountId>;576 type WeightInfo = ();577 type MaxVestingSchedules = MaxVestingSchedules;578 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;579}580581parameter_types! {582 pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;583 pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;584}585586impl cumulus_pallet_parachain_system::Config for Runtime {587 type Event = Event;588 type SelfParaId = parachain_info::Pallet<Self>;589 type OnSystemEvent = ();590 // type DownwardMessageHandlers = cumulus_primitives_utility::UnqueuedDmpAsParent<591 // MaxDownwardMessageWeight,592 // XcmExecutor<XcmConfig>,593 // Call,594 // >;595 type OutboundXcmpMessageSource = XcmpQueue;596 type DmpMessageHandler = DmpQueue;597 type ReservedDmpWeight = ReservedDmpWeight;598 type ReservedXcmpWeight = ReservedXcmpWeight;599 type XcmpMessageHandler = XcmpQueue;600}601602impl parachain_info::Config for Runtime {}603604impl cumulus_pallet_aura_ext::Config for Runtime {}605606parameter_types! {607 pub const RelayLocation: MultiLocation = MultiLocation::parent();608 pub const RelayNetwork: NetworkId = NetworkId::Polkadot;609 pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();610 pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();611}612613/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used614/// when determining ownership of accounts for asset transacting and when attempting to use XCM615/// `Transact` in order to determine the dispatch Origin.616pub type LocationToAccountId = (617 // The parent (Relay-chain) origin converts to the default `AccountId`.618 ParentIsPreset<AccountId>,619 // Sibling parachain origins convert to AccountId via the `ParaId::into`.620 SiblingParachainConvertsVia<Sibling, AccountId>,621 // Straight up local `AccountId32` origins just alias directly to `AccountId`.622 AccountId32Aliases<RelayNetwork, AccountId>,623);624625pub struct OnlySelfCurrency;626impl<B: TryFrom<u128>> MatchesFungible<B> for OnlySelfCurrency {627 fn matches_fungible(a: &MultiAsset) -> Option<B> {628 match (&a.id, &a.fun) {629 (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount),630 _ => None,631 }632 }633}634635/// Means for transacting assets on this chain.636pub type LocalAssetTransactor = CurrencyAdapter<637 // Use this currency:638 Balances,639 // Use this currency when it is a fungible asset matching the given location or name:640 OnlySelfCurrency,641 // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:642 LocationToAccountId,643 // Our chain's account ID type (we can't get away without mentioning it explicitly):644 AccountId,645 // We don't track any teleports.646 (),647>;648649/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,650/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can651/// biases the kind of local `Origin` it will become.652pub type XcmOriginToTransactDispatchOrigin = (653 // Sovereign account converter; this attempts to derive an `AccountId` from the origin location654 // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for655 // foreign chains who want to have a local sovereign account on this chain which they control.656 SovereignSignedViaLocation<LocationToAccountId, Origin>,657 // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when658 // recognised.659 RelayChainAsNative<RelayOrigin, Origin>,660 // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when661 // recognised.662 SiblingParachainAsNative<cumulus_pallet_xcm::Origin, Origin>,663 // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a664 // transaction from the Root origin.665 ParentAsSuperuser<Origin>,666 // Native signed account converter; this just converts an `AccountId32` origin into a normal667 // `Origin::Signed` origin of the same 32-byte value.668 SignedAccountId32AsNative<RelayNetwork, Origin>,669 // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.670 XcmPassthrough<Origin>,671);672673parameter_types! {674 // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.675 pub UnitWeightCost: Weight = 1_000_000;676 // 1200 UNIQUEs buy 1 second of weight.677 pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), 1_200 * UNIQUE);678 pub const MaxInstructions: u32 = 100;679 pub const MaxAuthorities: u32 = 100_000;680}681682match_types! {683 pub type ParentOrParentsUnitPlurality: impl Contains<MultiLocation> = {684 MultiLocation { parents: 1, interior: Here } |685 MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) }686 };687}688689pub type Barrier = (690 TakeWeightCredit,691 AllowTopLevelPaidExecutionFrom<Everything>,692 AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>,693 // ^^^ Parent & its unit plurality gets free execution694);695696pub struct UsingOnlySelfCurrencyComponents<697 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,698 AssetId: Get<MultiLocation>,699 AccountId,700 Currency: CurrencyT<AccountId>,701 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,702>(703 Weight,704 Currency::Balance,705 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,706);707impl<708 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,709 AssetId: Get<MultiLocation>,710 AccountId,711 Currency: CurrencyT<AccountId>,712 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,713 > WeightTrader714 for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>715{716 fn new() -> Self {717 Self(0, Zero::zero(), PhantomData)718 }719720 fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {721 let amount = WeightToFee::calc(&weight);722 let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;723724 // location to this parachain through relay chain725 let option1: xcm::v1::AssetId = Concrete(MultiLocation {726 parents: 1,727 interior: X1(Parachain(ParachainInfo::parachain_id().into())),728 });729 // direct location730 let option2: xcm::v1::AssetId = Concrete(MultiLocation {731 parents: 0,732 interior: Here,733 });734735 let required = if payment.fungible.contains_key(&option1) {736 (option1, u128_amount).into()737 } else if payment.fungible.contains_key(&option2) {738 (option2, u128_amount).into()739 } else {740 (Concrete(MultiLocation::default()), u128_amount).into()741 };742743 let unused = payment744 .checked_sub(required)745 .map_err(|_| XcmError::TooExpensive)?;746 self.0 = self.0.saturating_add(weight);747 self.1 = self.1.saturating_add(amount);748 Ok(unused)749 }750751 fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {752 let weight = weight.min(self.0);753 let amount = WeightToFee::calc(&weight);754 self.0 -= weight;755 self.1 = self.1.saturating_sub(amount);756 let amount: u128 = amount.saturated_into();757 if amount > 0 {758 Some((AssetId::get(), amount).into())759 } else {760 None761 }762 }763}764impl<765 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,766 AssetId: Get<MultiLocation>,767 AccountId,768 Currency: CurrencyT<AccountId>,769 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,770 > Drop771 for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>772{773 fn drop(&mut self) {774 OnUnbalanced::on_unbalanced(Currency::issue(self.1));775 }776}777778pub struct XcmConfig;779impl Config for XcmConfig {780 type Call = Call;781 type XcmSender = XcmRouter;782 // How to withdraw and deposit an asset.783 type AssetTransactor = LocalAssetTransactor;784 type OriginConverter = XcmOriginToTransactDispatchOrigin;785 type IsReserve = NativeAsset;786 type IsTeleporter = (); // Teleportation is disabled787 type LocationInverter = LocationInverter<Ancestry>;788 type Barrier = Barrier;789 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;790 type Trader = UsingOnlySelfCurrencyComponents<791 IdentityFee<Balance>,792 RelayLocation,793 AccountId,794 Balances,795 (),796 >;797 type ResponseHandler = (); // Don't handle responses for now.798 type SubscriptionService = PolkadotXcm;799800 type AssetTrap = PolkadotXcm;801 type AssetClaims = PolkadotXcm;802}803804// parameter_types! {805// pub const MaxDownwardMessageWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10;806// }807808/// No local origins on this chain are allowed to dispatch XCM sends/executions.809pub type LocalOriginToLocation = (SignedToAccountId32<Origin, AccountId, RelayNetwork>,);810811/// The means for routing XCM messages which are not for local execution into the right message812/// queues.813pub type XcmRouter = (814 // Two routers - use UMP to communicate with the relay chain:815 cumulus_primitives_utility::ParentAsUmp<ParachainSystem, ()>,816 // ..and XCMP to communicate with the sibling chains.817 XcmpQueue,818);819820impl pallet_evm_coder_substrate::Config for Runtime {821 type EthereumTransactionSender = pallet_ethereum::Pallet<Self>;822 type GasWeightMapping = FixedGasWeightMapping;823}824825impl pallet_xcm::Config for Runtime {826 type Event = Event;827 type SendXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;828 type XcmRouter = XcmRouter;829 type ExecuteXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;830 type XcmExecuteFilter = Everything;831 type XcmExecutor = XcmExecutor<XcmConfig>;832 type XcmTeleportFilter = Everything;833 type XcmReserveTransferFilter = Everything;834 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;835 type LocationInverter = LocationInverter<Ancestry>;836 type Origin = Origin;837 type Call = Call;838 const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;839 type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;840}841842impl cumulus_pallet_xcm::Config for Runtime {843 type Event = Event;844 type XcmExecutor = XcmExecutor<XcmConfig>;845}846847impl cumulus_pallet_xcmp_queue::Config for Runtime {848 type WeightInfo = ();849 type Event = Event;850 type XcmExecutor = XcmExecutor<XcmConfig>;851 type ChannelInfo = ParachainSystem;852 type VersionWrapper = ();853 type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;854 type ControllerOrigin = EnsureRoot<AccountId>;855 type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;856}857858impl cumulus_pallet_dmp_queue::Config for Runtime {859 type Event = Event;860 type XcmExecutor = XcmExecutor<XcmConfig>;861 type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;862}863864impl pallet_aura::Config for Runtime {865 type AuthorityId = AuraId;866 type DisabledValidators = ();867 type MaxAuthorities = MaxAuthorities;868}869870parameter_types! {871 pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account();872 pub const CollectionCreationPrice: Balance = 2 * UNIQUE;873}874875impl pallet_common::Config for Runtime {876 type Event = Event;877 type Currency = Balances;878 type CollectionCreationPrice = CollectionCreationPrice;879 type TreasuryAccountId = TreasuryAccountId;880 type CollectionDispatch = CollectionDispatchT<Self>;881882 type EvmTokenAddressMapping = EvmTokenAddressMapping;883 type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;884}885886impl pallet_structure::Config for Runtime {887 type Event = Event;888 type Call = Call;889 type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;890}891892impl pallet_fungible::Config for Runtime {893 type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;894}895impl pallet_refungible::Config for Runtime {896 type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;897}898impl pallet_nonfungible::Config for Runtime {899 type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;900}901902impl pallet_unique::Config for Runtime {903 type Event = Event;904 type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;905}906907parameter_types! {908 pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied909}910911/// Used for the pallet inflation912impl pallet_inflation::Config for Runtime {913 type Currency = Balances;914 type TreasuryAccountId = TreasuryAccountId;915 type InflationBlockInterval = InflationBlockInterval;916 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;917}918919// parameter_types! {920// pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *921// RuntimeBlockWeights::get().max_block;922// pub const MaxScheduledPerBlock: u32 = 50;923// }924925type EvmSponsorshipHandler = (926 pallet_unique::UniqueEthSponsorshipHandler<Runtime>,927 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,928);929type SponsorshipHandler = (930 pallet_unique::UniqueSponsorshipHandler<Runtime>,931 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,932 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,933);934935// impl pallet_unq_scheduler::Config for Runtime {936// type Event = Event;937// type Origin = Origin;938// type PalletsOrigin = OriginCaller;939// type Call = Call;940// type MaximumWeight = MaximumSchedulerWeight;941// type ScheduleOrigin = EnsureSigned<AccountId>;942// type MaxScheduledPerBlock = MaxScheduledPerBlock;943// type SponsorshipHandler = SponsorshipHandler;944// type WeightInfo = ();945// }946947impl pallet_evm_transaction_payment::Config for Runtime {948 type EvmSponsorshipHandler = EvmSponsorshipHandler;949 type Currency = Balances;950}951952impl pallet_charge_transaction::Config for Runtime {953 type SponsorshipHandler = SponsorshipHandler;954}955956// impl pallet_contract_helpers::Config for Runtime {957// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;958// }959960parameter_types! {961 // 0x842899ECF380553E8a4de75bF534cdf6fBF64049962 pub const HelpersContractAddress: H160 = H160([963 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49,964 ]);965}966967impl pallet_evm_contract_helpers::Config for Runtime {968 type ContractAddress = HelpersContractAddress;969 type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;970}971972construct_runtime!(973 pub enum Runtime where974 Block = Block,975 NodeBlock = opaque::Block,976 UncheckedExtrinsic = UncheckedExtrinsic977 {978 ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Config, Storage, Inherent, Event<T>, ValidateUnsigned} = 20,979 ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21,980981 Aura: pallet_aura::{Pallet, Config<T>} = 22,982 AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 23,983984 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 30,985 RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage} = 31,986 Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 32,987 TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 33,988 Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,989 Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,990 System: frame_system::{Pallet, Call, Storage, Config, Event<T>} = 36,991 Vesting: orml_vesting::{Pallet, Storage, Call, Event<T>, Config<T>} = 37,992 // Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,993 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,994995 // XCM helpers.996 XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event<T>} = 50,997 PolkadotXcm: pallet_xcm::{Pallet, Call, Event<T>, Origin} = 51,998 CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event<T>, Origin} = 52,999 DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event<T>} = 53,10001001 // Unique Pallets1002 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,1003 Unique: pallet_unique::{Pallet, Call, Storage, Event<T>} = 61,1004 // Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,1005 // free = 631006 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,1007 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,1008 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,1009 Fungible: pallet_fungible::{Pallet, Storage} = 67,1010 Refungible: pallet_refungible::{Pallet, Storage} = 68,1011 Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69,1012 Structure: pallet_structure::{Pallet, Call, Storage, Event<T>} = 70,10131014 // Frontier1015 EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,1016 Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101,10171018 EvmCoderSubstrate: pallet_evm_coder_substrate::{Pallet, Storage} = 150,1019 EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151,1020 EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152,1021 EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153,1022 }1023);10241025pub struct TransactionConverter;10261027impl fp_rpc::ConvertTransaction<UncheckedExtrinsic> for TransactionConverter {1028 fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic {1029 UncheckedExtrinsic::new_unsigned(1030 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),1031 )1032 }1033}10341035impl fp_rpc::ConvertTransaction<opaque::UncheckedExtrinsic> for TransactionConverter {1036 fn convert_transaction(1037 &self,1038 transaction: pallet_ethereum::Transaction,1039 ) -> opaque::UncheckedExtrinsic {1040 let extrinsic = UncheckedExtrinsic::new_unsigned(1041 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),1042 );1043 let encoded = extrinsic.encode();1044 opaque::UncheckedExtrinsic::decode(&mut &encoded[..])1045 .expect("Encoded extrinsic is always valid")1046 }1047}10481049/// The address format for describing accounts.1050pub type Address = sp_runtime::MultiAddress<AccountId, ()>;1051/// Block header type as expected by this runtime.1052pub type Header = generic::Header<BlockNumber, BlakeTwo256>;1053/// Block type as expected by this runtime.1054pub type Block = generic::Block<Header, UncheckedExtrinsic>;1055/// A Block signed with a Justification1056pub type SignedBlock = generic::SignedBlock<Block>;1057/// BlockId type as expected by this runtime.1058pub type BlockId = generic::BlockId<Block>;1059/// The SignedExtension to the basic transaction logic.1060pub type SignedExtra = (1061 frame_system::CheckSpecVersion<Runtime>,1062 // system::CheckTxVersion<Runtime>,1063 frame_system::CheckGenesis<Runtime>,1064 frame_system::CheckEra<Runtime>,1065 frame_system::CheckNonce<Runtime>,1066 frame_system::CheckWeight<Runtime>,1067 pallet_charge_transaction::ChargeTransactionPayment<Runtime>,1068 //pallet_contract_helpers::ContractHelpersExtension<Runtime>,1069);1070/// Unchecked extrinsic type as expected by this runtime.1071pub type UncheckedExtrinsic =1072 fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;1073/// Extrinsic type that has already been checked.1074pub type CheckedExtrinsic = fp_self_contained::CheckedExtrinsic<AccountId, Call, SignedExtra, H160>;1075/// Executive: handles dispatch to the various modules.1076pub type Executive = frame_executive::Executive<1077 Runtime,1078 Block,1079 frame_system::ChainContext<Runtime>,1080 Runtime,1081 AllPalletsReversedWithSystemFirst,1082>;10831084impl_opaque_keys! {1085 pub struct SessionKeys {1086 pub aura: Aura,1087 }1088}10891090impl fp_self_contained::SelfContainedCall for Call {1091 type SignedInfo = H160;10921093 fn is_self_contained(&self) -> bool {1094 match self {1095 Call::Ethereum(call) => call.is_self_contained(),1096 _ => false,1097 }1098 }10991100 fn check_self_contained(&self) -> Option<Result<Self::SignedInfo, TransactionValidityError>> {1101 match self {1102 Call::Ethereum(call) => call.check_self_contained(),1103 _ => None,1104 }1105 }11061107 fn validate_self_contained(&self, info: &Self::SignedInfo) -> Option<TransactionValidity> {1108 match self {1109 Call::Ethereum(call) => call.validate_self_contained(info),1110 _ => None,1111 }1112 }11131114 fn pre_dispatch_self_contained(1115 &self,1116 info: &Self::SignedInfo,1117 ) -> Option<Result<(), TransactionValidityError>> {1118 match self {1119 Call::Ethereum(call) => call.pre_dispatch_self_contained(info),1120 _ => None,1121 }1122 }11231124 fn apply_self_contained(1125 self,1126 info: Self::SignedInfo,1127 ) -> Option<sp_runtime::DispatchResultWithInfo<PostDispatchInfoOf<Self>>> {1128 match self {1129 call @ Call::Ethereum(pallet_ethereum::Call::transact { .. }) => Some(call.dispatch(1130 Origin::from(pallet_ethereum::RawOrigin::EthereumTransaction(info)),1131 )),1132 _ => None,1133 }1134 }1135}11361137macro_rules! dispatch_unique_runtime {1138 ($collection:ident.$method:ident($($name:ident),*)) => {{1139 let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);1140 let dispatch = collection.as_dyn();11411142 Ok(dispatch.$method($($name),*))1143 }};1144}11451146impl_common_runtime_apis!();11471148struct CheckInherents;11491150impl cumulus_pallet_parachain_system::CheckInherents<Block> for CheckInherents {1151 fn check_inherents(1152 block: &Block,1153 relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof,1154 ) -> sp_inherents::CheckInherentsResult {1155 let relay_chain_slot = relay_state_proof1156 .read_slot()1157 .expect("Could not read the relay chain slot from the proof");11581159 let inherent_data =1160 cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration(1161 relay_chain_slot,1162 sp_std::time::Duration::from_secs(6),1163 )1164 .create_inherent_data()1165 .expect("Could not create the timestamp inherent data");11661167 inherent_data.check_extrinsics(block)1168 }1169}11701171cumulus_pallet_parachain_system::register_validate_block!(1172 Runtime = Runtime,1173 BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,1174 CheckInherents = CheckInherents,1175);1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.1819#![cfg_attr(not(feature = "std"), no_std)]20// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.21#![recursion_limit = "1024"]22#![allow(clippy::from_over_into, clippy::identity_op)]23#![allow(clippy::fn_to_numeric_cast_with_truncation)]24// Make the WASM binary available.25#[cfg(feature = "std")]26include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));2728use sp_api::impl_runtime_apis;29use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};30use sp_runtime::DispatchError;31// #[cfg(any(feature = "std", test))]32// pub use sp_runtime::BuildStorage;3334use sp_runtime::{35 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,36 traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, AccountIdConversion, Zero},37 transaction_validity::{TransactionSource, TransactionValidity},38 ApplyExtrinsicResult, RuntimeAppPublic,39};4041use sp_std::prelude::*;4243#[cfg(feature = "std")]44use sp_version::NativeVersion;45use sp_version::RuntimeVersion;46pub use pallet_transaction_payment::{47 Multiplier, TargetedFeeAdjustment, FeeDetails, RuntimeDispatchInfo,48};49// A few exports that help ease life for downstream crates.50pub use pallet_balances::Call as BalancesCall;51pub use pallet_evm::{52 EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _,53};54pub use frame_support::{55 construct_runtime, match_types,56 dispatch::DispatchResult,57 PalletId, parameter_types, StorageValue, ConsensusEngineId,58 traits::{59 tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,60 Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,61 OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,62 },63 weights::{64 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},65 DispatchClass, DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,66 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,67 },68};69use up_data_structs::mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping};70use up_data_structs::{CollectionId, TokenId, CollectionStats, CollectionLimits, RpcCollection};71// use pallet_contracts::weights::WeightInfo;72// #[cfg(any(feature = "std", test))]73use frame_system::{74 self as frame_system, EnsureRoot, EnsureSigned,75 limits::{BlockWeights, BlockLength},76};77use sp_arithmetic::{78 traits::{BaseArithmetic, Unsigned},79};80use smallvec::smallvec;81use codec::{Encode, Decode};82use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping};83use fp_rpc::TransactionStatus;84use sp_runtime::{85 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},86 transaction_validity::TransactionValidityError,87 SaturatedConversion,88};8990// pub use pallet_timestamp::Call as TimestampCall;91pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;9293// Polkadot imports94use pallet_xcm::XcmPassthrough;95use polkadot_parachain::primitives::Sibling;96use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};97use xcm_builder::{98 AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,99 EnsureXcmOrigin, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser,100 RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,101 SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,102 ParentIsPreset,103};104use xcm_executor::{Config, XcmExecutor, Assets};105use sp_std::{marker::PhantomData};106107use xcm::latest::{108 // Xcm,109 AssetId::{Concrete},110 Fungibility::Fungible as XcmFungible,111 MultiAsset,112 Error as XcmError,113};114use xcm_executor::traits::{MatchesFungible, WeightTrader};115//use xcm_executor::traits::MatchesFungible;116use sp_runtime::traits::CheckedConversion;117118use unique_runtime_common::{119 impl_common_runtime_apis,120 types::*,121 constants::*,122 dispatch::{CollectionDispatchT, CollectionDispatch},123 sponsoring::UniqueSponsorshipHandler,124 eth_sponsoring::UniqueEthSponsorshipHandler,125 weights::CommonWeights,126};127128pub const RUNTIME_NAME: &str = "opal";129pub const TOKEN_SYMBOL: &str = "OPL";130131type CrossAccountId = pallet_evm::account::BasicCrossAccountId<Runtime>;132133impl RuntimeInstance for Runtime {134 type CrossAccountId = self::CrossAccountId;135 type TransactionConverter = self::TransactionConverter;136137 fn get_transaction_converter() -> TransactionConverter {138 TransactionConverter139 }140}141142/// The type for looking up accounts. We don't expect more than 4 billion of them, but you143/// never know...144pub type AccountIndex = u32;145146/// Balance of an account.147pub type Balance = u128;148149/// Index of a transaction in the chain.150pub type Index = u32;151152/// A hash of some data used by the chain.153pub type Hash = sp_core::H256;154155/// Digest item type.156pub type DigestItem = generic::DigestItem;157158/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know159/// the specifics of the runtime. They can then be made to be agnostic over specific formats160/// of data like extrinsics, allowing for them to continue syncing the network through upgrades161/// to even the core data structures.162pub mod opaque {163 use sp_std::prelude::*;164 use sp_runtime::impl_opaque_keys;165 use super::Aura;166167 pub use unique_runtime_common::types::*;168169 impl_opaque_keys! {170 pub struct SessionKeys {171 pub aura: Aura,172 }173 }174}175176/// This runtime version.177pub const VERSION: RuntimeVersion = RuntimeVersion {178 spec_name: create_runtime_str!(RUNTIME_NAME),179 impl_name: create_runtime_str!(RUNTIME_NAME),180 authoring_version: 1,181 spec_version: 920000,182 impl_version: 0,183 apis: RUNTIME_API_VERSIONS,184 transaction_version: 1,185 state_version: 0,186};187188#[derive(codec::Encode, codec::Decode)]189pub enum XCMPMessage<XAccountId, XBalance> {190 /// Transfer tokens to the given account from the Parachain account.191 TransferToken(XAccountId, XBalance),192}193194/// The version information used to identify this runtime when compiled natively.195#[cfg(feature = "std")]196pub fn native_version() -> NativeVersion {197 NativeVersion {198 runtime_version: VERSION,199 can_author_with: Default::default(),200 }201}202203type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;204205pub struct DealWithFees;206impl OnUnbalanced<NegativeImbalance> for DealWithFees {207 fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance>) {208 if let Some(fees) = fees_then_tips.next() {209 // for fees, 100% to treasury210 let mut split = fees.ration(100, 0);211 if let Some(tips) = fees_then_tips.next() {212 // for tips, if any, 100% to treasury213 tips.ration_merge_into(100, 0, &mut split);214 }215 Treasury::on_unbalanced(split.0);216 // Author::on_unbalanced(split.1);217 }218 }219}220221parameter_types! {222 pub const BlockHashCount: BlockNumber = 2400;223 pub RuntimeBlockLength: BlockLength =224 BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);225 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);226 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;227 pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()228 .base_block(BlockExecutionWeight::get())229 .for_class(DispatchClass::all(), |weights| {230 weights.base_extrinsic = ExtrinsicBaseWeight::get();231 })232 .for_class(DispatchClass::Normal, |weights| {233 weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);234 })235 .for_class(DispatchClass::Operational, |weights| {236 weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);237 // Operational transactions have some extra reserved space, so that they238 // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.239 weights.reserved = Some(240 MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT241 );242 })243 .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)244 .build_or_panic();245 pub const Version: RuntimeVersion = VERSION;246 pub const SS58Prefix: u8 = 42;247}248249parameter_types! {250 pub const ChainId: u64 = 8882;251}252253pub struct FixedFee;254impl FeeCalculator for FixedFee {255 fn min_gas_price() -> U256 {256 MIN_GAS_PRICE.into()257 }258}259260// Assuming slowest ethereum opcode is SSTORE, with gas price of 20000 as our worst case261// (contract, which only writes a lot of data),262// approximating on top of our real store write weight263parameter_types! {264 pub const WritesPerSecond: u64 = WEIGHT_PER_SECOND / <Runtime as frame_system::Config>::DbWeight::get().write;265 pub const GasPerSecond: u64 = WritesPerSecond::get() * 20000;266 pub const WeightPerGas: u64 = WEIGHT_PER_SECOND / GasPerSecond::get();267}268269/// Limiting EVM execution to 50% of block for substrate users and management tasks270/// EVM transaction consumes more weight than substrate's, so we can't rely on them being271/// scheduled fairly272const EVM_DISPATCH_RATIO: Perbill = Perbill::from_percent(50);273parameter_types! {274 pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * EVM_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT / WeightPerGas::get());275}276277pub enum FixedGasWeightMapping {}278impl GasWeightMapping for FixedGasWeightMapping {279 fn gas_to_weight(gas: u64) -> Weight {280 gas.saturating_mul(WeightPerGas::get())281 }282 fn weight_to_gas(weight: Weight) -> u64 {283 weight / WeightPerGas::get()284 }285}286287impl pallet_evm::account::Config for Runtime {288 type CrossAccountId = pallet_evm::account::BasicCrossAccountId<Self>;289 type EvmAddressMapping = pallet_evm::HashedAddressMapping<Self::Hashing>;290 type EvmBackwardsAddressMapping = fp_evm_mapping::MapBackwardsAddressTruncated;291}292293impl pallet_evm::Config for Runtime {294 type BlockGasLimit = BlockGasLimit;295 type FeeCalculator = FixedFee;296 type GasWeightMapping = FixedGasWeightMapping;297 type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;298 type CallOrigin = EnsureAddressTruncated;299 type WithdrawOrigin = EnsureAddressTruncated;300 type AddressMapping = HashedAddressMapping<Self::Hashing>;301 type PrecompilesType = ();302 type PrecompilesValue = ();303 type Currency = Balances;304 type Event = Event;305 type OnMethodCall = (306 pallet_evm_migration::OnMethodCall<Self>,307 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,308 CollectionDispatchT<Self>,309 );310 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;311 type ChainId = ChainId;312 type Runner = pallet_evm::runner::stack::Runner<Self>;313 type OnChargeTransaction = pallet_evm::EVMCurrencyAdapter<Balances, DealWithFees>;314 type TransactionValidityHack = pallet_evm_transaction_payment::TransactionValidityHack<Self>;315 type FindAuthor = EthereumFindAuthor<Aura>;316}317318impl pallet_evm_migration::Config for Runtime {319 type WeightInfo = pallet_evm_migration::weights::SubstrateWeight<Self>;320}321322pub struct EthereumFindAuthor<F>(core::marker::PhantomData<F>);323impl<F: FindAuthor<u32>> FindAuthor<H160> for EthereumFindAuthor<F> {324 fn find_author<'a, I>(digests: I) -> Option<H160>325 where326 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,327 {328 if let Some(author_index) = F::find_author(digests) {329 let authority_id = Aura::authorities()[author_index as usize].clone();330 return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24]));331 }332 None333 }334}335336impl pallet_ethereum::Config for Runtime {337 type Event = Event;338 type StateRoot = pallet_ethereum::IntermediateStateRoot<Self>;339}340341impl pallet_randomness_collective_flip::Config for Runtime {}342343impl frame_system::Config for Runtime {344 /// The data to be stored in an account.345 type AccountData = pallet_balances::AccountData<Balance>;346 /// The identifier used to distinguish between accounts.347 type AccountId = AccountId;348 /// The basic call filter to use in dispatchable.349 type BaseCallFilter = Everything;350 /// Maximum number of block number to block hash mappings to keep (oldest pruned first).351 type BlockHashCount = BlockHashCount;352 /// The maximum length of a block (in bytes).353 type BlockLength = RuntimeBlockLength;354 /// The index type for blocks.355 type BlockNumber = BlockNumber;356 /// The weight of the overhead invoked on the block import process, independent of the extrinsics included in that block.357 type BlockWeights = RuntimeBlockWeights;358 /// The aggregated dispatch type that is available for extrinsics.359 type Call = Call;360 /// The weight of database operations that the runtime can invoke.361 type DbWeight = RocksDbWeight;362 /// The ubiquitous event type.363 type Event = Event;364 /// The type for hashing blocks and tries.365 type Hash = Hash;366 /// The hashing algorithm used.367 type Hashing = BlakeTwo256;368 /// The header type.369 type Header = generic::Header<BlockNumber, BlakeTwo256>;370 /// The index type for storing how many extrinsics an account has signed.371 type Index = Index;372 /// The lookup mechanism to get account ID from whatever is passed in dispatchers.373 type Lookup = AccountIdLookup<AccountId, ()>;374 /// What to do if an account is fully reaped from the system.375 type OnKilledAccount = ();376 /// What to do if a new account is created.377 type OnNewAccount = ();378 type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;379 /// The ubiquitous origin type.380 type Origin = Origin;381 /// This type is being generated by `construct_runtime!`.382 type PalletInfo = PalletInfo;383 /// This is used as an identifier of the chain. 42 is the generic substrate prefix.384 type SS58Prefix = SS58Prefix;385 /// Weight information for the extrinsics of this pallet.386 type SystemWeightInfo = frame_system::weights::SubstrateWeight<Self>;387 /// Version of the runtime.388 type Version = Version;389 type MaxConsumers = ConstU32<16>;390}391392parameter_types! {393 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;394}395396impl pallet_timestamp::Config for Runtime {397 /// A timestamp: milliseconds since the unix epoch.398 type Moment = u64;399 type OnTimestampSet = ();400 type MinimumPeriod = MinimumPeriod;401 type WeightInfo = ();402}403404parameter_types! {405 // pub const ExistentialDeposit: u128 = 500;406 pub const ExistentialDeposit: u128 = 0;407 pub const MaxLocks: u32 = 50;408}409410impl pallet_balances::Config for Runtime {411 type MaxLocks = MaxLocks;412 type MaxReserves = ();413 type ReserveIdentifier = [u8; 8];414 /// The type for recording an account's balance.415 type Balance = Balance;416 /// The ubiquitous event type.417 type Event = Event;418 type DustRemoval = Treasury;419 type ExistentialDeposit = ExistentialDeposit;420 type AccountStore = System;421 type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;422}423424pub const fn deposit(items: u32, bytes: u32) -> Balance {425 items as Balance * 15 * CENTIUNIQUE + (bytes as Balance) * 6 * CENTIUNIQUE426}427428/*429parameter_types! {430 pub TombstoneDeposit: Balance = deposit(431 1,432 sp_std::mem::size_of::<pallet_contracts::Pallet<Runtime>> as u32,433 );434 pub DepositPerContract: Balance = TombstoneDeposit::get();435 pub const DepositPerStorageByte: Balance = deposit(0, 1);436 pub const DepositPerStorageItem: Balance = deposit(1, 0);437 pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);438 pub const SurchargeReward: Balance = 150 * MILLIUNIQUE;439 pub const SignedClaimHandicap: u32 = 2;440 pub const MaxDepth: u32 = 32;441 pub const MaxValueSize: u32 = 16 * 1024;442 pub const MaxCodeSize: u32 = 1024 * 1024 * 25; // 25 Mb443 // The lazy deletion runs inside on_initialize.444 pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *445 RuntimeBlockWeights::get().max_block;446 // The weight needed for decoding the queue should be less or equal than a fifth447 // of the overall weight dedicated to the lazy deletion.448 pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (449 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -450 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)451 )) / 5) as u32;452 pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();453}454455impl pallet_contracts::Config for Runtime {456 type Time = Timestamp;457 type Randomness = RandomnessCollectiveFlip;458 type Currency = Balances;459 type Event = Event;460 type RentPayment = ();461 type SignedClaimHandicap = SignedClaimHandicap;462 type TombstoneDeposit = TombstoneDeposit;463 type DepositPerContract = DepositPerContract;464 type DepositPerStorageByte = DepositPerStorageByte;465 type DepositPerStorageItem = DepositPerStorageItem;466 type RentFraction = RentFraction;467 type SurchargeReward = SurchargeReward;468 type WeightPrice = pallet_transaction_payment::Pallet<Self>;469 type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;470 type ChainExtension = NFTExtension;471 type DeletionQueueDepth = DeletionQueueDepth;472 type DeletionWeightLimit = DeletionWeightLimit;473 type Schedule = Schedule;474 type CallStack = [pallet_contracts::Frame<Self>; 31];475}476*/477478parameter_types! {479 /// This value increases the priority of `Operational` transactions by adding480 /// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.481 pub const OperationalFeeMultiplier: u8 = 5;482}483484/// Linear implementor of `WeightToFeePolynomial`485pub struct LinearFee<T>(sp_std::marker::PhantomData<T>);486487impl<T> WeightToFeePolynomial for LinearFee<T>488where489 T: BaseArithmetic + From<u32> + Copy + Unsigned,490{491 type Balance = T;492493 fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {494 smallvec!(WeightToFeeCoefficient {495 // Targeting 0.1 Unique per NFT transfer496 coeff_integer: WEIGHT_TO_FEE_COEFF.into(),497 coeff_frac: Perbill::zero(),498 negative: false,499 degree: 1,500 })501 }502}503504impl pallet_transaction_payment::Config for Runtime {505 type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, DealWithFees>;506 type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;507 type OperationalFeeMultiplier = OperationalFeeMultiplier;508 type WeightToFee = LinearFee<Balance>;509 type FeeMultiplierUpdate = ();510}511512parameter_types! {513 pub const ProposalBond: Permill = Permill::from_percent(5);514 pub const ProposalBondMinimum: Balance = 1 * UNIQUE;515 pub const ProposalBondMaximum: Balance = 1000 * UNIQUE;516 pub const SpendPeriod: BlockNumber = 5 * MINUTES;517 pub const Burn: Permill = Permill::from_percent(0);518 pub const TipCountdown: BlockNumber = 1 * DAYS;519 pub const TipFindersFee: Percent = Percent::from_percent(20);520 pub const TipReportDepositBase: Balance = 1 * UNIQUE;521 pub const DataDepositPerByte: Balance = 1 * CENTIUNIQUE;522 pub const BountyDepositBase: Balance = 1 * UNIQUE;523 pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;524 pub const TreasuryModuleId: PalletId = PalletId(*b"py/trsry");525 pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;526 pub const MaximumReasonLength: u32 = 16384;527 pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);528 pub const BountyValueMinimum: Balance = 5 * UNIQUE;529 pub const MaxApprovals: u32 = 100;530}531532impl pallet_treasury::Config for Runtime {533 type PalletId = TreasuryModuleId;534 type Currency = Balances;535 type ApproveOrigin = EnsureRoot<AccountId>;536 type RejectOrigin = EnsureRoot<AccountId>;537 type Event = Event;538 type OnSlash = ();539 type ProposalBond = ProposalBond;540 type ProposalBondMinimum = ProposalBondMinimum;541 type ProposalBondMaximum = ProposalBondMaximum;542 type SpendPeriod = SpendPeriod;543 type Burn = Burn;544 type BurnDestination = ();545 type SpendFunds = ();546 type WeightInfo = pallet_treasury::weights::SubstrateWeight<Self>;547 type MaxApprovals = MaxApprovals;548}549550impl pallet_sudo::Config for Runtime {551 type Event = Event;552 type Call = Call;553}554555pub struct RelayChainBlockNumberProvider<T>(sp_std::marker::PhantomData<T>);556557impl<T: cumulus_pallet_parachain_system::Config> BlockNumberProvider558 for RelayChainBlockNumberProvider<T>559{560 type BlockNumber = BlockNumber;561562 fn current_block_number() -> Self::BlockNumber {563 cumulus_pallet_parachain_system::Pallet::<T>::validation_data()564 .map(|d| d.relay_parent_number)565 .unwrap_or_default()566 }567}568569parameter_types! {570 pub const MinVestedTransfer: Balance = 10 * UNIQUE;571 pub const MaxVestingSchedules: u32 = 28;572}573574impl orml_vesting::Config for Runtime {575 type Event = Event;576 type Currency = pallet_balances::Pallet<Runtime>;577 type MinVestedTransfer = MinVestedTransfer;578 type VestedTransferOrigin = EnsureSigned<AccountId>;579 type WeightInfo = ();580 type MaxVestingSchedules = MaxVestingSchedules;581 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;582}583584parameter_types! {585 pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;586 pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;587}588589impl cumulus_pallet_parachain_system::Config for Runtime {590 type Event = Event;591 type SelfParaId = parachain_info::Pallet<Self>;592 type OnSystemEvent = ();593 // type DownwardMessageHandlers = cumulus_primitives_utility::UnqueuedDmpAsParent<594 // MaxDownwardMessageWeight,595 // XcmExecutor<XcmConfig>,596 // Call,597 // >;598 type OutboundXcmpMessageSource = XcmpQueue;599 type DmpMessageHandler = DmpQueue;600 type ReservedDmpWeight = ReservedDmpWeight;601 type ReservedXcmpWeight = ReservedXcmpWeight;602 type XcmpMessageHandler = XcmpQueue;603}604605impl parachain_info::Config for Runtime {}606607impl cumulus_pallet_aura_ext::Config for Runtime {}608609parameter_types! {610 pub const RelayLocation: MultiLocation = MultiLocation::parent();611 pub const RelayNetwork: NetworkId = NetworkId::Polkadot;612 pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();613 pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();614}615616/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used617/// when determining ownership of accounts for asset transacting and when attempting to use XCM618/// `Transact` in order to determine the dispatch Origin.619pub type LocationToAccountId = (620 // The parent (Relay-chain) origin converts to the default `AccountId`.621 ParentIsPreset<AccountId>,622 // Sibling parachain origins convert to AccountId via the `ParaId::into`.623 SiblingParachainConvertsVia<Sibling, AccountId>,624 // Straight up local `AccountId32` origins just alias directly to `AccountId`.625 AccountId32Aliases<RelayNetwork, AccountId>,626);627628pub struct OnlySelfCurrency;629impl<B: TryFrom<u128>> MatchesFungible<B> for OnlySelfCurrency {630 fn matches_fungible(a: &MultiAsset) -> Option<B> {631 match (&a.id, &a.fun) {632 (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount),633 _ => None,634 }635 }636}637638/// Means for transacting assets on this chain.639pub type LocalAssetTransactor = CurrencyAdapter<640 // Use this currency:641 Balances,642 // Use this currency when it is a fungible asset matching the given location or name:643 OnlySelfCurrency,644 // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:645 LocationToAccountId,646 // Our chain's account ID type (we can't get away without mentioning it explicitly):647 AccountId,648 // We don't track any teleports.649 (),650>;651652/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,653/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can654/// biases the kind of local `Origin` it will become.655pub type XcmOriginToTransactDispatchOrigin = (656 // Sovereign account converter; this attempts to derive an `AccountId` from the origin location657 // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for658 // foreign chains who want to have a local sovereign account on this chain which they control.659 SovereignSignedViaLocation<LocationToAccountId, Origin>,660 // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when661 // recognised.662 RelayChainAsNative<RelayOrigin, Origin>,663 // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when664 // recognised.665 SiblingParachainAsNative<cumulus_pallet_xcm::Origin, Origin>,666 // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a667 // transaction from the Root origin.668 ParentAsSuperuser<Origin>,669 // Native signed account converter; this just converts an `AccountId32` origin into a normal670 // `Origin::Signed` origin of the same 32-byte value.671 SignedAccountId32AsNative<RelayNetwork, Origin>,672 // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.673 XcmPassthrough<Origin>,674);675676parameter_types! {677 // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.678 pub UnitWeightCost: Weight = 1_000_000;679 // 1200 UNIQUEs buy 1 second of weight.680 pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), 1_200 * UNIQUE);681 pub const MaxInstructions: u32 = 100;682 pub const MaxAuthorities: u32 = 100_000;683}684685match_types! {686 pub type ParentOrParentsUnitPlurality: impl Contains<MultiLocation> = {687 MultiLocation { parents: 1, interior: Here } |688 MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) }689 };690}691692pub type Barrier = (693 TakeWeightCredit,694 AllowTopLevelPaidExecutionFrom<Everything>,695 AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>,696 // ^^^ Parent & its unit plurality gets free execution697);698699pub struct UsingOnlySelfCurrencyComponents<700 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,701 AssetId: Get<MultiLocation>,702 AccountId,703 Currency: CurrencyT<AccountId>,704 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,705>(706 Weight,707 Currency::Balance,708 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,709);710impl<711 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,712 AssetId: Get<MultiLocation>,713 AccountId,714 Currency: CurrencyT<AccountId>,715 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,716 > WeightTrader717 for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>718{719 fn new() -> Self {720 Self(0, Zero::zero(), PhantomData)721 }722723 fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {724 let amount = WeightToFee::calc(&weight);725 let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;726727 // location to this parachain through relay chain728 let option1: xcm::v1::AssetId = Concrete(MultiLocation {729 parents: 1,730 interior: X1(Parachain(ParachainInfo::parachain_id().into())),731 });732 // direct location733 let option2: xcm::v1::AssetId = Concrete(MultiLocation {734 parents: 0,735 interior: Here,736 });737738 let required = if payment.fungible.contains_key(&option1) {739 (option1, u128_amount).into()740 } else if payment.fungible.contains_key(&option2) {741 (option2, u128_amount).into()742 } else {743 (Concrete(MultiLocation::default()), u128_amount).into()744 };745746 let unused = payment747 .checked_sub(required)748 .map_err(|_| XcmError::TooExpensive)?;749 self.0 = self.0.saturating_add(weight);750 self.1 = self.1.saturating_add(amount);751 Ok(unused)752 }753754 fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {755 let weight = weight.min(self.0);756 let amount = WeightToFee::calc(&weight);757 self.0 -= weight;758 self.1 = self.1.saturating_sub(amount);759 let amount: u128 = amount.saturated_into();760 if amount > 0 {761 Some((AssetId::get(), amount).into())762 } else {763 None764 }765 }766}767impl<768 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,769 AssetId: Get<MultiLocation>,770 AccountId,771 Currency: CurrencyT<AccountId>,772 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,773 > Drop774 for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>775{776 fn drop(&mut self) {777 OnUnbalanced::on_unbalanced(Currency::issue(self.1));778 }779}780781pub struct XcmConfig;782impl Config for XcmConfig {783 type Call = Call;784 type XcmSender = XcmRouter;785 // How to withdraw and deposit an asset.786 type AssetTransactor = LocalAssetTransactor;787 type OriginConverter = XcmOriginToTransactDispatchOrigin;788 type IsReserve = NativeAsset;789 type IsTeleporter = (); // Teleportation is disabled790 type LocationInverter = LocationInverter<Ancestry>;791 type Barrier = Barrier;792 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;793 type Trader = UsingOnlySelfCurrencyComponents<794 IdentityFee<Balance>,795 RelayLocation,796 AccountId,797 Balances,798 (),799 >;800 type ResponseHandler = (); // Don't handle responses for now.801 type SubscriptionService = PolkadotXcm;802803 type AssetTrap = PolkadotXcm;804 type AssetClaims = PolkadotXcm;805}806807// parameter_types! {808// pub const MaxDownwardMessageWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10;809// }810811/// No local origins on this chain are allowed to dispatch XCM sends/executions.812pub type LocalOriginToLocation = (SignedToAccountId32<Origin, AccountId, RelayNetwork>,);813814/// The means for routing XCM messages which are not for local execution into the right message815/// queues.816pub type XcmRouter = (817 // Two routers - use UMP to communicate with the relay chain:818 cumulus_primitives_utility::ParentAsUmp<ParachainSystem, ()>,819 // ..and XCMP to communicate with the sibling chains.820 XcmpQueue,821);822823impl pallet_evm_coder_substrate::Config for Runtime {824 type EthereumTransactionSender = pallet_ethereum::Pallet<Self>;825 type GasWeightMapping = FixedGasWeightMapping;826}827828impl pallet_xcm::Config for Runtime {829 type Event = Event;830 type SendXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;831 type XcmRouter = XcmRouter;832 type ExecuteXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;833 type XcmExecuteFilter = Everything;834 type XcmExecutor = XcmExecutor<XcmConfig>;835 type XcmTeleportFilter = Everything;836 type XcmReserveTransferFilter = Everything;837 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;838 type LocationInverter = LocationInverter<Ancestry>;839 type Origin = Origin;840 type Call = Call;841 const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;842 type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;843}844845impl cumulus_pallet_xcm::Config for Runtime {846 type Event = Event;847 type XcmExecutor = XcmExecutor<XcmConfig>;848}849850impl cumulus_pallet_xcmp_queue::Config for Runtime {851 type WeightInfo = ();852 type Event = Event;853 type XcmExecutor = XcmExecutor<XcmConfig>;854 type ChannelInfo = ParachainSystem;855 type VersionWrapper = ();856 type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;857 type ControllerOrigin = EnsureRoot<AccountId>;858 type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;859}860861impl cumulus_pallet_dmp_queue::Config for Runtime {862 type Event = Event;863 type XcmExecutor = XcmExecutor<XcmConfig>;864 type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;865}866867impl pallet_aura::Config for Runtime {868 type AuthorityId = AuraId;869 type DisabledValidators = ();870 type MaxAuthorities = MaxAuthorities;871}872873parameter_types! {874 pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account();875 pub const CollectionCreationPrice: Balance = 2 * UNIQUE;876}877878impl pallet_common::Config for Runtime {879 type Event = Event;880 type Currency = Balances;881 type CollectionCreationPrice = CollectionCreationPrice;882 type TreasuryAccountId = TreasuryAccountId;883 type CollectionDispatch = CollectionDispatchT<Self>;884885 type EvmTokenAddressMapping = EvmTokenAddressMapping;886 type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;887}888889impl pallet_structure::Config for Runtime {890 type Event = Event;891 type Call = Call;892 type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;893}894895impl pallet_fungible::Config for Runtime {896 type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;897}898impl pallet_refungible::Config for Runtime {899 type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;900}901impl pallet_nonfungible::Config for Runtime {902 type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;903}904905impl pallet_unique::Config for Runtime {906 type Event = Event;907 type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;908 type CommonWeightInfo = CommonWeights<Self>;909}910911parameter_types! {912 pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied913}914915/// Used for the pallet inflation916impl pallet_inflation::Config for Runtime {917 type Currency = Balances;918 type TreasuryAccountId = TreasuryAccountId;919 type InflationBlockInterval = InflationBlockInterval;920 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;921}922923// parameter_types! {924// pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *925// RuntimeBlockWeights::get().max_block;926// pub const MaxScheduledPerBlock: u32 = 50;927// }928929type EvmSponsorshipHandler = (930 UniqueEthSponsorshipHandler<Runtime>,931 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,932);933type SponsorshipHandler = (934 UniqueSponsorshipHandler<Runtime>,935 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,936 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,937);938939// impl pallet_unq_scheduler::Config for Runtime {940// type Event = Event;941// type Origin = Origin;942// type PalletsOrigin = OriginCaller;943// type Call = Call;944// type MaximumWeight = MaximumSchedulerWeight;945// type ScheduleOrigin = EnsureSigned<AccountId>;946// type MaxScheduledPerBlock = MaxScheduledPerBlock;947// type SponsorshipHandler = SponsorshipHandler;948// type WeightInfo = ();949// }950951impl pallet_evm_transaction_payment::Config for Runtime {952 type EvmSponsorshipHandler = EvmSponsorshipHandler;953 type Currency = Balances;954}955956impl pallet_charge_transaction::Config for Runtime {957 type SponsorshipHandler = SponsorshipHandler;958}959960// impl pallet_contract_helpers::Config for Runtime {961// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;962// }963964parameter_types! {965 // 0x842899ECF380553E8a4de75bF534cdf6fBF64049966 pub const HelpersContractAddress: H160 = H160([967 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49,968 ]);969}970971impl pallet_evm_contract_helpers::Config for Runtime {972 type ContractAddress = HelpersContractAddress;973 type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;974}975976construct_runtime!(977 pub enum Runtime where978 Block = Block,979 NodeBlock = opaque::Block,980 UncheckedExtrinsic = UncheckedExtrinsic981 {982 ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Config, Storage, Inherent, Event<T>, ValidateUnsigned} = 20,983 ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21,984985 Aura: pallet_aura::{Pallet, Config<T>} = 22,986 AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 23,987988 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 30,989 RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage} = 31,990 Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 32,991 TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 33,992 Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,993 Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,994 System: frame_system::{Pallet, Call, Storage, Config, Event<T>} = 36,995 Vesting: orml_vesting::{Pallet, Storage, Call, Event<T>, Config<T>} = 37,996 // Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,997 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,998999 // XCM helpers.1000 XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event<T>} = 50,1001 PolkadotXcm: pallet_xcm::{Pallet, Call, Event<T>, Origin} = 51,1002 CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event<T>, Origin} = 52,1003 DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event<T>} = 53,10041005 // Unique Pallets1006 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,1007 Unique: pallet_unique::{Pallet, Call, Storage, Event<T>} = 61,1008 // Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,1009 // free = 631010 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,1011 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,1012 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,1013 Fungible: pallet_fungible::{Pallet, Storage} = 67,1014 Refungible: pallet_refungible::{Pallet, Storage} = 68,1015 Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69,1016 Structure: pallet_structure::{Pallet, Call, Storage, Event<T>} = 70,10171018 // Frontier1019 EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,1020 Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101,10211022 EvmCoderSubstrate: pallet_evm_coder_substrate::{Pallet, Storage} = 150,1023 EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151,1024 EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152,1025 EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153,1026 }1027);10281029pub struct TransactionConverter;10301031impl fp_rpc::ConvertTransaction<UncheckedExtrinsic> for TransactionConverter {1032 fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic {1033 UncheckedExtrinsic::new_unsigned(1034 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),1035 )1036 }1037}10381039impl fp_rpc::ConvertTransaction<opaque::UncheckedExtrinsic> for TransactionConverter {1040 fn convert_transaction(1041 &self,1042 transaction: pallet_ethereum::Transaction,1043 ) -> opaque::UncheckedExtrinsic {1044 let extrinsic = UncheckedExtrinsic::new_unsigned(1045 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),1046 );1047 let encoded = extrinsic.encode();1048 opaque::UncheckedExtrinsic::decode(&mut &encoded[..])1049 .expect("Encoded extrinsic is always valid")1050 }1051}10521053/// The address format for describing accounts.1054pub type Address = sp_runtime::MultiAddress<AccountId, ()>;1055/// Block header type as expected by this runtime.1056pub type Header = generic::Header<BlockNumber, BlakeTwo256>;1057/// Block type as expected by this runtime.1058pub type Block = generic::Block<Header, UncheckedExtrinsic>;1059/// A Block signed with a Justification1060pub type SignedBlock = generic::SignedBlock<Block>;1061/// BlockId type as expected by this runtime.1062pub type BlockId = generic::BlockId<Block>;1063/// The SignedExtension to the basic transaction logic.1064pub type SignedExtra = (1065 frame_system::CheckSpecVersion<Runtime>,1066 // system::CheckTxVersion<Runtime>,1067 frame_system::CheckGenesis<Runtime>,1068 frame_system::CheckEra<Runtime>,1069 frame_system::CheckNonce<Runtime>,1070 frame_system::CheckWeight<Runtime>,1071 pallet_charge_transaction::ChargeTransactionPayment<Runtime>,1072 //pallet_contract_helpers::ContractHelpersExtension<Runtime>,1073);1074/// Unchecked extrinsic type as expected by this runtime.1075pub type UncheckedExtrinsic =1076 fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;1077/// Extrinsic type that has already been checked.1078pub type CheckedExtrinsic = fp_self_contained::CheckedExtrinsic<AccountId, Call, SignedExtra, H160>;1079/// Executive: handles dispatch to the various modules.1080pub type Executive = frame_executive::Executive<1081 Runtime,1082 Block,1083 frame_system::ChainContext<Runtime>,1084 Runtime,1085 AllPalletsReversedWithSystemFirst,1086>;10871088impl_opaque_keys! {1089 pub struct SessionKeys {1090 pub aura: Aura,1091 }1092}10931094impl fp_self_contained::SelfContainedCall for Call {1095 type SignedInfo = H160;10961097 fn is_self_contained(&self) -> bool {1098 match self {1099 Call::Ethereum(call) => call.is_self_contained(),1100 _ => false,1101 }1102 }11031104 fn check_self_contained(&self) -> Option<Result<Self::SignedInfo, TransactionValidityError>> {1105 match self {1106 Call::Ethereum(call) => call.check_self_contained(),1107 _ => None,1108 }1109 }11101111 fn validate_self_contained(&self, info: &Self::SignedInfo) -> Option<TransactionValidity> {1112 match self {1113 Call::Ethereum(call) => call.validate_self_contained(info),1114 _ => None,1115 }1116 }11171118 fn pre_dispatch_self_contained(1119 &self,1120 info: &Self::SignedInfo,1121 ) -> Option<Result<(), TransactionValidityError>> {1122 match self {1123 Call::Ethereum(call) => call.pre_dispatch_self_contained(info),1124 _ => None,1125 }1126 }11271128 fn apply_self_contained(1129 self,1130 info: Self::SignedInfo,1131 ) -> Option<sp_runtime::DispatchResultWithInfo<PostDispatchInfoOf<Self>>> {1132 match self {1133 call @ Call::Ethereum(pallet_ethereum::Call::transact { .. }) => Some(call.dispatch(1134 Origin::from(pallet_ethereum::RawOrigin::EthereumTransaction(info)),1135 )),1136 _ => None,1137 }1138 }1139}11401141macro_rules! dispatch_unique_runtime {1142 ($collection:ident.$method:ident($($name:ident),*)) => {{1143 let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);1144 let dispatch = collection.as_dyn();11451146 Ok(dispatch.$method($($name),*))1147 }};1148}11491150impl_common_runtime_apis!();11511152struct CheckInherents;11531154impl cumulus_pallet_parachain_system::CheckInherents<Block> for CheckInherents {1155 fn check_inherents(1156 block: &Block,1157 relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof,1158 ) -> sp_inherents::CheckInherentsResult {1159 let relay_chain_slot = relay_state_proof1160 .read_slot()1161 .expect("Could not read the relay chain slot from the proof");11621163 let inherent_data =1164 cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration(1165 relay_chain_slot,1166 sp_std::time::Duration::from_secs(6),1167 )1168 .create_inherent_data()1169 .expect("Could not create the timestamp inherent data");11701171 inherent_data.check_extrinsics(block)1172 }1173}11741175cumulus_pallet_parachain_system::register_validate_block!(1176 Runtime = Runtime,1177 BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,1178 CheckInherents = CheckInherents,1179);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()
+ ));
+ });
+}