From 089f1beb359fe376aa53691a8a575e98bbdf9f37 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 10:43:02 +1300 Subject: [PATCH 001/155] Add nft utils to add more nft support functions --- pallets/nft/src/utils.rs | 52 +++++++++++++++++++++++++++++++++ primitives/metaverse/src/lib.rs | 1 - 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 pallets/nft/src/utils.rs diff --git a/pallets/nft/src/utils.rs b/pallets/nft/src/utils.rs new file mode 100644 index 000000000..0702a96d1 --- /dev/null +++ b/pallets/nft/src/utils.rs @@ -0,0 +1,52 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support::pallet_prelude::*; +use sp_runtime::traits::Verify; + +use crate::*; + +impl Pallet { + /// Validates the signature of the given data with the provided signer's account ID. + /// + /// # Errors + /// + /// This function returns a [`WrongSignature`](crate::Error::WrongSignature) error if the + /// signature is invalid or the verification process fails. + pub fn validate_signature( + data: &Vec, + signature: &T::OffchainSignature, + signer: &T::AccountId, + ) -> DispatchResult { + if signature.verify(&**data, &signer) { + return Ok(()); + } + + // NOTE: for security reasons modern UIs implicitly wrap the data requested to sign into + // , that's why we support both wrapped and raw versions. + let prefix = b""; + let suffix = b""; + let mut wrapped: Vec = Vec::with_capacity(data.len() + prefix.len() + suffix.len()); + wrapped.extend(prefix); + wrapped.extend(data); + wrapped.extend(suffix); + + ensure!(signature.verify(&*wrapped, &signer), Error::::WrongSignature); + + Ok(()) + } +} diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index fe456f3ad..c149368ba 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -32,7 +32,6 @@ use sp_runtime::{FixedU128, OpaqueExtrinsic as UncheckedExtrinsic}; use sp_std::collections::btree_map::BTreeMap; use sp_std::prelude::*; use sp_std::vec::Vec; - use xcm::v3::MultiLocation; pub mod continuum; From 0f3464d1b1b287a20d058ced5e5766ad032aa48d Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 10:43:21 +1300 Subject: [PATCH 002/155] Add utils mod into nft pallet --- pallets/nft/src/lib.rs | 14 +++++++++++++- pallets/nft/src/migration.rs | 1 - 2 files changed, 13 insertions(+), 2 deletions(-) delete mode 100644 pallets/nft/src/migration.rs diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index b01dbae01..eb7c873f7 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -62,6 +62,7 @@ pub mod benchmarking; mod mock; #[cfg(test)] mod tests; +mod utils; pub mod weights; @@ -75,8 +76,9 @@ pub enum StorageVersion { #[frame_support::pallet] pub mod pallet { + use frame_system::offchain::Signer; use orml_traits::{MultiCurrency, MultiCurrencyExtended}; - use sp_runtime::traits::CheckedSub; + use sp_runtime::traits::{CheckedSub, IdentifyAccount, Verify}; use sp_runtime::ArithmeticError; use primitive_traits::{CollectionType, NftAssetData, NftGroupCollectionData, NftMetadata, TokenType}; @@ -135,6 +137,16 @@ pub mod pallet { /// The fee will be unreserved after the storage is freed. #[pallet::constant] type StorageDepositFee: Get>; + + /// Off-Chain signature type. + /// + /// Can verify whether an `Self::OffchainPublic` created a signature. + type OffchainSignature: Verify + Parameter; + + /// Off-Chain public key. + /// + /// Must identify as an on-chain `Self::AccountId`. + type OffchainPublic: IdentifyAccount; } pub type ClassIdOf = ::ClassId; diff --git a/pallets/nft/src/migration.rs b/pallets/nft/src/migration.rs deleted file mode 100644 index 8b1378917..000000000 --- a/pallets/nft/src/migration.rs +++ /dev/null @@ -1 +0,0 @@ - From b1b5fa1a61b8771b24d5550c0d503873c8bc70e6 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 10:50:10 +1300 Subject: [PATCH 003/155] Implement type alias for pre-signed minting config --- pallets/nft/src/lib.rs | 11 ++++++++++- pallets/nft/src/utils.rs | 4 +--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index eb7c873f7..4ce1fa606 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -153,6 +153,14 @@ pub mod pallet { pub type TokenIdOf = ::TokenId; pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + /// A type alias for the pre-signed minting configuration for a specified collection. + pub(super) type PreSignedMintOf = PreSignedMint< + ::CollectionId, + ::ItemId, + ::AccountId, + BlockNumberFor, + BalanceOf, + >; #[pallet::storage] #[pallet::getter(fn get_group_collection)] @@ -395,8 +403,9 @@ pub mod pallet { /// Invalid stackable NFT amount InvalidStackableNftAmount, /// Invalid current total issuance - /// Invalid current total issuance InvalidCurrentTotalIssuance, + /// Wrong signature + WrongSignature, } #[pallet::call] diff --git a/pallets/nft/src/utils.rs b/pallets/nft/src/utils.rs index 0702a96d1..68c01e8bd 100644 --- a/pallets/nft/src/utils.rs +++ b/pallets/nft/src/utils.rs @@ -36,8 +36,6 @@ impl Pallet { return Ok(()); } - // NOTE: for security reasons modern UIs implicitly wrap the data requested to sign into - // , that's why we support both wrapped and raw versions. let prefix = b""; let suffix = b""; let mut wrapped: Vec = Vec::with_capacity(data.len() + prefix.len() + suffix.len()); @@ -45,7 +43,7 @@ impl Pallet { wrapped.extend(data); wrapped.extend(suffix); - ensure!(signature.verify(&*wrapped, &signer), Error::::WrongSignature); + ensure!(signature.verify(&*wrapped, &signer), Error::::WrongSignature); Ok(()) } From 6dff3e5209c3f26a0c6e053e0634b52fc2d75991 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 10:54:53 +1300 Subject: [PATCH 004/155] Add presigned mint struct that allow owner to specify detail of nft attribute --- pallets/nft/src/lib.rs | 12 +++++++----- traits/core-primitives/src/lib.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index 4ce1fa606..0c6391110 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -52,7 +52,7 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use auction_manager::{Auction, CheckAuctionItemHandler}; pub use pallet::*; pub use primitive_traits::{Attributes, NFTTrait, NftClassData, NftGroupCollectionData, NftMetadata, TokenType}; -use primitive_traits::{CollectionType, NftAssetData, NftAssetDataV1, NftClassDataV1}; +use primitive_traits::{CollectionType, NftAssetData, NftAssetDataV1, NftClassDataV1, PreSignedMint}; use primitives::{AssetId, BlockNumber, ClassId, GroupCollectionId, Hash, ItemId, TokenId}; pub use weights::WeightInfo; @@ -81,7 +81,9 @@ pub mod pallet { use sp_runtime::traits::{CheckedSub, IdentifyAccount, Verify}; use sp_runtime::ArithmeticError; - use primitive_traits::{CollectionType, NftAssetData, NftGroupCollectionData, NftMetadata, TokenType}; + use primitive_traits::{ + CollectionType, NftAssetData, NftGroupCollectionData, NftMetadata, PreSignedMint, TokenType, + }; use primitives::FungibleTokenId; use super::*; @@ -154,9 +156,9 @@ pub mod pallet { pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// A type alias for the pre-signed minting configuration for a specified collection. - pub(super) type PreSignedMintOf = PreSignedMint< - ::CollectionId, - ::ItemId, + pub(super) type PreSignedMintOf = PreSignedMint< + ClassIdOf, + TokenIdOf, ::AccountId, BlockNumberFor, BalanceOf, diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 97c02e08c..0c449faea 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -162,6 +162,22 @@ pub struct MetaverseFund { pub currency_id: FungibleTokenId, } +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct PreSignedMint { + /// A collection of the item to be minted. + pub(super) collection: ClassId, + /// Item's ID. + pub(super) item: TokenId, + /// Additional item's key-value attributes. + pub(super) attributes: Vec<(Vec, Vec)>, + /// Additional item's metadata. + pub(super) metadata: Vec, + /// Restrict the claim to a particular account. + pub(super) only_account: Option, + /// An optional price the claimer would need to pay for the mint. + pub(super) mint_price: Option, +} + pub trait MetaverseTrait { /// Create metaverse fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId; From 16f88008682cca11a10c78328ad5647825dbe786 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 11:45:59 +1300 Subject: [PATCH 005/155] Implement presigned nft minting that allow offchain minting --- pallets/nft/src/lib.rs | 111 +++++++++++++++++++++++++++++- traits/core-primitives/src/lib.rs | 12 ++-- 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index 0c6391110..7456ced1a 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -157,10 +157,9 @@ pub mod pallet { <::Currency as Currency<::AccountId>>::Balance; /// A type alias for the pre-signed minting configuration for a specified collection. pub(super) type PreSignedMintOf = PreSignedMint< - ClassIdOf, - TokenIdOf, + ::ClassId, + ::TokenId, ::AccountId, - BlockNumberFor, BalanceOf, >; @@ -970,6 +969,28 @@ pub mod pallet { Ok(()) }) } + + /// Mint an item by providing the pre-signed approval. + /// + /// Origin must be Signed. + /// + /// - `mint_data`: The pre-signed approval that consists of the information about the item, + /// its metadata, attributes, who can mint it (`None` for anyone) and until what block + /// number. + /// - `signature`: The signature of the `data` object. + /// - `signer`: The `data` object's signer. Should be an Issuer of the collection. + #[pallet::call_index(37)] + #[pallet::weight(T::WeightInfo::mint_pre_signed(mint_data.attributes.len() as u32))] + pub fn mint_pre_signed( + origin: OriginFor, + mint_data: Box>, + signature: T::OffchainSignature, + signer: T::AccountId, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + Self::validate_signature(&Encode::encode(&mint_data), &signature, &signer)?; + Self::do_mint_pre_signed(origin, *mint_data, signer) + } } #[pallet::hooks] @@ -1146,6 +1167,90 @@ impl Pallet { Ok((new_asset_ids, last_token_id)) } + // Mint with pre-signed approval from collection owner + pub(crate) fn do_mint_pre_signed( + mint_to: T::AccountId, + mint_data: PreSignedMintOf, + signer: T::AccountId, + ) -> DispatchResult { + let PreSignedMint { + class_id, + token_id, + attributes, + metadata, + only_account, + mint_price, + } = mint_data; + + // Make sure collection is not locked + ensure!(!Self::is_collection_locked(&class_id), Error::::CollectionIsLocked); + + // Check metadata length + ensure!( + attributes.len() <= T::MaxMetadata::get() as usize, + Error::::ExceedMaximumMetadataLength + ); + + // If specific account recipient specified, this will make sure requirement pass + if let Some(account) = only_account { + ensure!(account == mint_to, Error::::NoPermission); + } + + // Get class info of the collection + let class_info = NftModule::::classes(class_id).ok_or(Error::::ClassIdNotFound)?; + + // Ensure signer is owner of collection + ensure!(&signer == class_info.owner, Error::::NoPermission); + + // If minting price is specified, this will transfer token to collection owner. + if let Some(price) = mint_price { + ::Currency::transfer( + &mint_to, + &class_info.owner, + price, + ExistenceRequirement::KeepAlive, + )?; + } + + // Update class total issuance + Self::update_class_total_issuance(&sender, &class_id, One::one())?; + + let class_fund: T::AccountId = T::Treasury::get().into_account_truncating(); + let deposit = T::AssetMintingFee::get().saturating_mul(Into::>::into(One::one())); + ::Currency::transfer(&mint_to, &class_fund, deposit, ExistenceRequirement::KeepAlive)?; + + let new_nft_data = NftAssetData { + deposit, + attributes: attributes, + is_locked: false, + }; + + // Mint specific token id + NftModule::::mint_with_token_id(&sender, class_id, token_id, metadata.clone(), new_nft_data.clone())?; + + // Emit New Nft minted event + Self::deposit_event(Event::::NewNftMinted( + (class_id, token_id), + (class_id, token_id), + mint_to.clone(), + class_id, + One::one(), + token_id, + )); + + Ok(()) + } + + /// A helper method to construct metadata. + /// + /// # Errors + /// + /// This function returns an [`IncorrectMetadata`](crate::Error::IncorrectMetadata) dispatch + /// error if the provided metadata is too long. + pub fn construct_metadata(metadata: Vec) -> Result, DispatchError> { + Ok(BoundedVec::try_from(metadata).map_err(|_| Error::::ExceedMaximumMetadataLength)?) + } + /// Internal NFT class creation fn do_create_class( sender: &T::AccountId, diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 0c449faea..92ae0967e 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -163,15 +163,15 @@ pub struct MetaverseFund { } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct PreSignedMint { +pub struct PreSignedMint { /// A collection of the item to be minted. - pub(super) collection: ClassId, - /// Item's ID. - pub(super) item: TokenId, + pub(super) class_id: ClassId, + /// TokenId. + pub(super) token_id: TokenId, /// Additional item's key-value attributes. - pub(super) attributes: Vec<(Vec, Vec)>, + pub(super) attributes: Attributes, /// Additional item's metadata. - pub(super) metadata: Vec, + pub(super) metadata: NftMetadata, /// Restrict the claim to a particular account. pub(super) only_account: Option, /// An optional price the claimer would need to pay for the mint. From 422858cc5e69af0be51c44a4f4a2c017adb30fbf Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 13:59:55 +1300 Subject: [PATCH 006/155] Make tokenid in signature optional allows more flexible option --- pallets/nft/src/lib.rs | 30 +++++++++++++++++++++--------- traits/core-primitives/src/lib.rs | 12 ++++++------ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index 7456ced1a..9c582166b 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -979,8 +979,7 @@ pub mod pallet { /// number. /// - `signature`: The signature of the `data` object. /// - `signer`: The `data` object's signer. Should be an Issuer of the collection. - #[pallet::call_index(37)] - #[pallet::weight(T::WeightInfo::mint_pre_signed(mint_data.attributes.len() as u32))] + #[pallet::weight(T::WeightInfo::force_update_total_issuance())] pub fn mint_pre_signed( origin: OriginFor, mint_data: Box>, @@ -1200,7 +1199,7 @@ impl Pallet { let class_info = NftModule::::classes(class_id).ok_or(Error::::ClassIdNotFound)?; // Ensure signer is owner of collection - ensure!(&signer == class_info.owner, Error::::NoPermission); + ensure!(signer == class_info.owner, Error::::NoPermission); // If minting price is specified, this will transfer token to collection owner. if let Some(price) = mint_price { @@ -1213,10 +1212,10 @@ impl Pallet { } // Update class total issuance - Self::update_class_total_issuance(&sender, &class_id, One::one())?; + Self::update_class_total_issuance(&signer, &class_id, One::one())?; let class_fund: T::AccountId = T::Treasury::get().into_account_truncating(); - let deposit = T::AssetMintingFee::get().saturating_mul(Into::>::into(One::one())); + let deposit = T::AssetMintingFee::get().saturating_mul(Into::>::into(1 as u32)); ::Currency::transfer(&mint_to, &class_fund, deposit, ExistenceRequirement::KeepAlive)?; let new_nft_data = NftAssetData { @@ -1225,17 +1224,30 @@ impl Pallet { is_locked: false, }; + let mut new_token_id: TokenIdOf = Default::default(); + // Mint specific token id - NftModule::::mint_with_token_id(&sender, class_id, token_id, metadata.clone(), new_nft_data.clone())?; + if let Some(provided_token_id) = token_id { + NftModule::::mint_with_token_id( + &mint_to, + class_id, + provided_token_id, + metadata.clone(), + new_nft_data.clone(), + )?; + new_token_id = provided_token_id + } else { + new_token_id = NftModule::::mint(&mint_to, class_id, metadata.clone(), new_nft_data.clone())?; + } // Emit New Nft minted event Self::deposit_event(Event::::NewNftMinted( - (class_id, token_id), - (class_id, token_id), + (class_id, new_token_id), + (class_id, new_token_id), mint_to.clone(), class_id, One::one(), - token_id, + new_token_id, )); Ok(()) diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 92ae0967e..4ea550bab 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -165,17 +165,17 @@ pub struct MetaverseFund { #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct PreSignedMint { /// A collection of the item to be minted. - pub(super) class_id: ClassId, + pub class_id: ClassId, /// TokenId. - pub(super) token_id: TokenId, + pub token_id: Option, /// Additional item's key-value attributes. - pub(super) attributes: Attributes, + pub attributes: Attributes, /// Additional item's metadata. - pub(super) metadata: NftMetadata, + pub metadata: NftMetadata, /// Restrict the claim to a particular account. - pub(super) only_account: Option, + pub only_account: Option, /// An optional price the claimer would need to pay for the mint. - pub(super) mint_price: Option, + pub mint_price: Option, } pub trait MetaverseTrait { From 1557f6edc9dede91398a2abd5b46104732f32774 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 14:11:04 +1300 Subject: [PATCH 007/155] Implement offchain signature into nft pallet --- runtime/continuum/src/lib.rs | 2 ++ runtime/metaverse/src/lib.rs | 8 +++++--- runtime/pioneer/src/lib.rs | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/continuum/src/lib.rs b/runtime/continuum/src/lib.rs index 395a5d14c..9ad479369 100644 --- a/runtime/continuum/src/lib.rs +++ b/runtime/continuum/src/lib.rs @@ -1452,6 +1452,8 @@ impl nft::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = ::Signer; } parameter_types! { diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index 7da3a3aa7..66fda6b2c 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -60,6 +60,8 @@ use pallet_evm::{ }; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; +//use pallet_evm::{EnsureAddressTruncated, HashedAddressMapping}; +use polkadot_primitives::v2::MAX_POV_SIZE; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -92,13 +94,11 @@ use currencies::BasicCurrencyAdapter; pub use estate::{MintingRateInfo, Range as MintingRange}; use evm_mapping::EvmAddressMapping; use metaverse_runtime_common::{precompiles::MetaverseNetworkPrecompiles, CurrencyHooks}; -use primitives::{Amount, Balance, BlockNumber, ClassId, FungibleTokenId, Moment, NftId, RoundIndex, TokenId}; -//use pallet_evm::{EnsureAddressTruncated, HashedAddressMapping}; -use polkadot_primitives::v2::MAX_POV_SIZE; use primitives::evm::{ CurrencyIdType, Erc20Mapping, EvmAddress, H160_POSITION_CURRENCY_ID_TYPE, H160_POSITION_FUNGIBLE_TOKEN, H160_POSITION_MINING_RESOURCE, H160_POSITION_TOKEN, H160_POSITION_TOKEN_NFT, H160_POSITION_TOKEN_NFT_CLASS_ID_END, }; +use primitives::{Amount, Balance, BlockNumber, ClassId, FungibleTokenId, Moment, NftId, RoundIndex, TokenId}; // primitives imports use crate::opaque::SessionKeys; @@ -588,6 +588,8 @@ impl nft::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = ::Signer; } parameter_types! { diff --git a/runtime/pioneer/src/lib.rs b/runtime/pioneer/src/lib.rs index ec66d3f43..2e50e8558 100644 --- a/runtime/pioneer/src/lib.rs +++ b/runtime/pioneer/src/lib.rs @@ -1452,6 +1452,8 @@ impl nft::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = ::Signer; } parameter_types! { From 7ae6183ee36fd1bbb5cde5729f97b62acfa61a94 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 15:59:09 +1300 Subject: [PATCH 008/155] Add nft presigned method benchmarking --- pallets/nft/src/benchmarking.rs | 51 ++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/pallets/nft/src/benchmarking.rs b/pallets/nft/src/benchmarking.rs index 5f0659965..17350517a 100644 --- a/pallets/nft/src/benchmarking.rs +++ b/pallets/nft/src/benchmarking.rs @@ -19,23 +19,30 @@ //! Benchmarks for the nft module. #![cfg(feature = "runtime-benchmarks")] -use crate::Call; -#[allow(unused)] -use crate::Pallet as NftModule; -pub use crate::*; + use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_support::traits::Get; use frame_system::RawOrigin; use orml_traits::BasicCurrencyExtended; -use primitive_traits::CollectionType; -use primitives::{AssetId, Balance, ClassId}; //use core_primitives::NFTTrait; use scale_info::Type; -use sp_runtime::traits::{AccountIdConversion, StaticLookup, UniqueSaturatedInto}; +use sp_io::crypto::{sr25519_generate, sr25519_sign}; use sp_runtime::Perbill; +use sp_runtime::{ + traits::{AccountIdConversion, ConvertInto, IdentifyAccount, StaticLookup, UniqueSaturatedInto}, + AccountId32, MultiSignature, MultiSigner, +}; use sp_std::prelude::*; use sp_std::vec; +use primitive_traits::CollectionType; +use primitives::{AssetId, Balance, ClassId}; + +use crate::Call; +#[allow(unused)] +use crate::Pallet as NftModule; +pub use crate::*; + pub struct Pallet(crate::Pallet); const SEED: u32 = 0; @@ -64,6 +71,11 @@ fn test_attributes(x: u8) -> Attributes { } benchmarks! { + where_clause { + where + T::OffchainSignature: From, + T::AccountId: From, + } // create NFT group create_group{ @@ -158,6 +170,31 @@ benchmarks! { crate::Pallet::::create_group(RawOrigin::Root.into(), vec![1], vec![1]); crate::Pallet::::create_class(RawOrigin::Signed(caller.clone()).into(), vec![1], test_attributes(1), 0u32.into(), TokenType::Transferable, CollectionType::Collectable, Perbill::from_percent(0u32), None); }: _(RawOrigin::Root, 0u32.into(), 0u32.into(), 1u32.into()) + mint_pre_signed{ + let caller_public = sr25519_generate(0.into(), None); + let caller = MultiSigner::Sr25519(caller_public).into_account().into(); + let initial_balance = dollar(1000); + + ::Currency::make_free_balance_be(&caller, initial_balance.unique_saturated_into()); + + crate::Pallet::::create_group(RawOrigin::Root.into(), vec![1], vec![1]); + crate::Pallet::::create_class(RawOrigin::Signed(caller.clone()).into(), vec![1], test_attributes(1), 0u32.into(), TokenType::Transferable, CollectionType::Collectable, Perbill::from_percent(0u32),None); + + let mint_data = PreSignedMint { + class_id: 0u32.into(), + token_id: None, + attributes: test_attributes(1), + metadata: vec![1], + only_account: None, + mint_price: None, + }; + + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &caller_public, &message).unwrap()); + + let target = funded_account::("target", 1); + + }: _(RawOrigin::Signed(target), Box::new(mint_data), signature.into(), caller) } impl_benchmark_test_suite!(Pallet, crate::benchmarking::tests::new_test_ext(), crate::mock::Test); From a62b40f57aaa882378ebf76f3219c45197bab713 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 18:30:02 +1300 Subject: [PATCH 009/155] Update weight for mint_pre_signed --- pallets/nft/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index 9c582166b..a2709e35e 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -979,7 +979,7 @@ pub mod pallet { /// number. /// - `signature`: The signature of the `data` object. /// - `signer`: The `data` object's signer. Should be an Issuer of the collection. - #[pallet::weight(T::WeightInfo::force_update_total_issuance())] + #[pallet::weight(T::WeightInfo::mint_pre_signed())] pub fn mint_pre_signed( origin: OriginFor, mint_data: Box>, From 5a5e60cfd3afffc196f3ec7ff43af90d59e09bdd Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 18:30:20 +1300 Subject: [PATCH 010/155] Add module weight into different runtimes --- runtime/continuum/src/weights/module_nft.rs | 5 +++++ runtime/metaverse/src/weights/module_nft.rs | 5 +++++ runtime/pioneer/src/weights/module_nft.rs | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/runtime/continuum/src/weights/module_nft.rs b/runtime/continuum/src/weights/module_nft.rs index 86909c591..1bd3bbfb2 100644 --- a/runtime/continuum/src/weights/module_nft.rs +++ b/runtime/continuum/src/weights/module_nft.rs @@ -66,4 +66,9 @@ impl nft::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn mint_pre_signed() -> Weight { + Weight::from_parts(91_000_000, 19036) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } } diff --git a/runtime/metaverse/src/weights/module_nft.rs b/runtime/metaverse/src/weights/module_nft.rs index 86909c591..1bd3bbfb2 100644 --- a/runtime/metaverse/src/weights/module_nft.rs +++ b/runtime/metaverse/src/weights/module_nft.rs @@ -66,4 +66,9 @@ impl nft::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn mint_pre_signed() -> Weight { + Weight::from_parts(91_000_000, 19036) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } } diff --git a/runtime/pioneer/src/weights/module_nft.rs b/runtime/pioneer/src/weights/module_nft.rs index 86909c591..1bd3bbfb2 100644 --- a/runtime/pioneer/src/weights/module_nft.rs +++ b/runtime/pioneer/src/weights/module_nft.rs @@ -66,4 +66,9 @@ impl nft::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn mint_pre_signed() -> Weight { + Weight::from_parts(91_000_000, 19036) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } } From 85edf7905113f4767a601932c8f4f38c5c70cbda Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 18:30:40 +1300 Subject: [PATCH 011/155] Generated weights by benchmarking --- pallets/nft/src/weights.rs | 133 ++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 53 deletions(-) diff --git a/pallets/nft/src/weights.rs b/pallets/nft/src/weights.rs index 1111bfbde..f3f838a01 100644 --- a/pallets/nft/src/weights.rs +++ b/pallets/nft/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for nft //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-16, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-26, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -27,6 +27,7 @@ // pallet // --execution=wasm // --wasm-execution=compiled +// --template=./template/weight-template.hbs // --pallet // nft // --extrinsic @@ -35,37 +36,36 @@ // 20 // --repeat // 10 -// --template=./template/weight-template.hbs // --output -// ./pallets/nft/src/weights.rs +// pallets/nft/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use frame_support::{traits::Get, weights::{constants::RocksDbWeight, Weight}}; use sp_std::marker::PhantomData; /// Weight functions needed for nft. -pub trait WeightInfo { fn create_group() -> Weight; fn create_class() -> Weight; fn mint() -> Weight; fn mint_stackable_nft() -> Weight; fn transfer() -> Weight; fn transfer_stackable_nft() -> Weight; fn transfer_batch() -> Weight; fn sign_asset() -> Weight; fn set_hard_limit() -> Weight; fn withdraw_funds_from_class_fund() -> Weight; fn force_update_total_issuance() -> Weight;} +pub trait WeightInfo { fn create_group() -> Weight; fn create_class() -> Weight; fn mint() -> Weight; fn mint_stackable_nft() -> Weight; fn transfer() -> Weight; fn transfer_stackable_nft() -> Weight; fn transfer_batch() -> Weight; fn sign_asset() -> Weight; fn set_hard_limit() -> Weight; fn withdraw_funds_from_class_fund() -> Weight; fn force_update_total_issuance() -> Weight; fn mint_pre_signed() -> Weight;} /// Weights for nft using the for collator node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Nft NextGroupCollectionId (r:1 w:1) - // Proof Skipped: Nft NextGroupCollectionId (max_values: Some(1), max_size: None, mode: Measured) - // Storage: Nft AllNftGroupCollection (r:1 w:1) - // Proof Skipped: Nft AllNftGroupCollection (max_values: Some(1), max_size: None, mode: Measured) - // Storage: Nft GroupCollections (r:0 w:1) - // Proof Skipped: Nft GroupCollections (max_values: None, max_size: None, mode: Measured) - fn create_group() -> Weight { - // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `1317` - // Minimum execution time: 11_475 nanoseconds. - Weight::from_parts(12_117_000, 1317) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) - } +// Proof Skipped: Nft NextGroupCollectionId (max_values: Some(1), max_size: None, mode: Measured) +// Storage: Nft AllNftGroupCollection (r:1 w:1) +// Proof Skipped: Nft AllNftGroupCollection (max_values: Some(1), max_size: None, mode: Measured) +// Storage: Nft GroupCollections (r:0 w:1) +// Proof Skipped: Nft GroupCollections (max_values: None, max_size: None, mode: Measured) +fn create_group() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1317` + // Minimum execution time: 15_000 nanoseconds. + Weight::from_parts(16_000_000, 1317) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(3)) +} // Storage: OrmlNFT NextClassId (r:1 w:1) // Proof Skipped: OrmlNFT NextClassId (max_values: Some(1), max_size: None, mode: Measured) // Storage: Nft GroupCollections (r:1 w:0) @@ -80,8 +80,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `461` // Estimated: `7417` - // Minimum execution time: 35_571 nanoseconds. - Weight::from_parts(37_465_000, 7417) + // Minimum execution time: 43_000 nanoseconds. + Weight::from_parts(45_000_000, 7417) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -101,8 +101,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `784` // Estimated: `23976` - // Minimum execution time: 63_219 nanoseconds. - Weight::from_parts(69_706_000, 23976) + // Minimum execution time: 65_000 nanoseconds. + Weight::from_parts(66_000_000, 23976) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(10)) } @@ -124,8 +124,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `550` // Estimated: `15931` - // Minimum execution time: 46_695 nanoseconds. - Weight::from_parts(111_946_000, 15931) + // Minimum execution time: 48_000 nanoseconds. + Weight::from_parts(49_000_000, 15931) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(8)) } @@ -145,8 +145,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `802` // Estimated: `17187` - // Minimum execution time: 31_317 nanoseconds. - Weight::from_parts(33_226_000, 17187) + // Minimum execution time: 36_000 nanoseconds. + Weight::from_parts(37_000_000, 17187) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -160,8 +160,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `630` // Estimated: `11790` - // Minimum execution time: 27_633 nanoseconds. - Weight::from_parts(28_946_000, 11790) + // Minimum execution time: 33_000 nanoseconds. + Weight::from_parts(33_000_000, 11790) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -181,8 +181,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `980` // Estimated: `25680` - // Minimum execution time: 54_804 nanoseconds. - Weight::from_parts(63_183_000, 25680) + // Minimum execution time: 63_000 nanoseconds. + Weight::from_parts(64_000_000, 25680) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) } @@ -196,8 +196,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `1002` // Estimated: `14763` - // Minimum execution time: 79_688 nanoseconds. - Weight::from_parts(115_994_000, 14763) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_parts(58_000_000, 14763) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -207,8 +207,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `282` // Estimated: `2757` - // Minimum execution time: 12_053 nanoseconds. - Weight::from_parts(23_425_000, 2757) + // Minimum execution time: 15_000 nanoseconds. + Weight::from_parts(16_000_000, 2757) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -220,8 +220,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `587` // Estimated: `8268` - // Minimum execution time: 56_702 nanoseconds. - Weight::from_parts(61_491_000, 8268) + // Minimum execution time: 31_000 nanoseconds. + Weight::from_parts(31_000_000, 8268) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -231,67 +231,94 @@ impl WeightInfo for SubstrateWeight { // Storage: Nf // Proof Size summary in bytes: // Measured: `282` // Estimated: `2757` - // Minimum execution time: 10_855 nanoseconds. - Weight::from_parts(27_207_000, 2757) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_parts(14_000_000, 2757) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:1) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn mint_pre_signed() -> Weight { + // Proof Size summary in bytes: + // Measured: `786` + // Estimated: `19036` + // Minimum execution time: 87_000 nanoseconds. + Weight::from_parts(91_000_000, 19036) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } } + // For backwards compatibility and tests impl WeightInfo for () { fn create_group() -> Weight { - Weight::from_parts(12_117_000, 1317) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(3)) - } + Weight::from_parts(16_000_000, 1317) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(3)) +} fn create_class() -> Weight { - Weight::from_parts(37_465_000, 7417) + Weight::from_parts(45_000_000, 7417) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(4)) } fn mint() -> Weight { - Weight::from_parts(69_706_000, 23976) + Weight::from_parts(66_000_000, 23976) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().writes(10)) } fn mint_stackable_nft() -> Weight { - Weight::from_parts(111_946_000, 15931) + Weight::from_parts(49_000_000, 15931) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(8)) } fn transfer() -> Weight { - Weight::from_parts(33_226_000, 17187) + Weight::from_parts(37_000_000, 17187) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(3)) } fn transfer_stackable_nft() -> Weight { - Weight::from_parts(28_946_000, 11790) + Weight::from_parts(33_000_000, 11790) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(2)) } fn transfer_batch() -> Weight { - Weight::from_parts(63_183_000, 25680) + Weight::from_parts(64_000_000, 25680) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().writes(6)) } fn sign_asset() -> Weight { - Weight::from_parts(115_994_000, 14763) + Weight::from_parts(58_000_000, 14763) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(4)) } fn set_hard_limit() -> Weight { - Weight::from_parts(23_425_000, 2757) + Weight::from_parts(16_000_000, 2757) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } fn withdraw_funds_from_class_fund() -> Weight { - Weight::from_parts(61_491_000, 8268) + Weight::from_parts(31_000_000, 8268) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } fn force_update_total_issuance() -> Weight { - Weight::from_parts(27_207_000, 2757) + Weight::from_parts(14_000_000, 2757) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } + fn mint_pre_signed() -> Weight { + Weight::from_parts(91_000_000, 19036) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(6)) + } } From aef976cbbe67bc68c638fa41f224bd32558f3b07 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 26 Oct 2023 18:32:14 +1300 Subject: [PATCH 012/155] Add generated weights by benchmarking --- pallets/nft/src/weights.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nft/src/weights.rs b/pallets/nft/src/weights.rs index f3f838a01..3ea116349 100644 --- a/pallets/nft/src/weights.rs +++ b/pallets/nft/src/weights.rs @@ -37,7 +37,7 @@ // --repeat // 10 // --output -// pallets/nft/weights.rs +// pallets/nft/src/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] From e37335985634c5b935bf6431ae221b57227203d5 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 27 Oct 2023 14:44:29 +1300 Subject: [PATCH 013/155] Update mock and unit tests of nft with accountid32 --- pallets/nft/src/tests.rs | 245 +++++++++++++++++++++++-------------- pallets/nft/src/weights.rs | 2 +- runtime/common/src/mock.rs | 9 +- 3 files changed, 162 insertions(+), 94 deletions(-) diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index d91e41ad5..8c8a20382 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -10,6 +10,12 @@ use primitives::{Balance, FungibleTokenId}; #[cfg(test)] use super::*; +type AccountIdOf = ::AccountId; + +fn account(id: u8) -> AccountIdOf { + [id; 32].into() +} + fn free_bit_balance(who: &AccountId) -> Balance { ::MultiCurrency::free_balance(mining_resource_id(), &who) } @@ -131,7 +137,7 @@ fn create_group_should_work() { #[test] fn create_group_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_noop!(Nft::create_group(origin, vec![1], vec![1]), BadOrigin); }); @@ -140,7 +146,7 @@ fn create_group_should_fail() { #[test] fn create_class_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -170,21 +176,21 @@ fn create_class_should_work() { } ); - let event = mock::RuntimeEvent::Nft(crate::Event::NewNftClassCreated(ALICE, CLASS_ID)); + let event = mock::RuntimeEvent::Nft(crate::Event::NewNftClassCreated(account(1), CLASS_ID)); assert_eq!(last_event(), event); assert_eq!( free_native_balance(class_id_account()), class_deposit + ::StorageDepositFee::get() ); - assert_eq!(Balances::free_balance(ALICE), 99997); + assert_eq!(Balances::free_balance(account(1)), 99997); }); } #[test] fn create_class_with_royalty_fee_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -214,82 +220,87 @@ fn create_class_with_royalty_fee_should_work() { } ); - let event = mock::RuntimeEvent::Nft(crate::Event::NewNftClassCreated(ALICE, CLASS_ID)); + let event = mock::RuntimeEvent::Nft(crate::Event::NewNftClassCreated(account(1), CLASS_ID)); assert_eq!(last_event(), event); assert_eq!( free_native_balance(class_id_account()), class_deposit + ::StorageDepositFee::get() ); - assert_eq!(Balances::free_balance(ALICE), 99997); + assert_eq!(Balances::free_balance(account(1)), 99997); }); } #[test] fn mint_asset_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::enable_promotion(RuntimeOrigin::root(), true)); init_test_nft(origin.clone()); assert_eq!(free_native_balance(class_id_account()), 4); - assert_eq!(OrmlNft::tokens_by_owner((ALICE, 0, 0)), ()); + assert_eq!(OrmlNft::tokens_by_owner((account(1), 0, 0)), ()); - let event = mock::RuntimeEvent::Nft(crate::Event::NewNftMinted((0, 0), (0, 0), ALICE, CLASS_ID, 1, 0)); + let event = mock::RuntimeEvent::Nft(crate::Event::NewNftMinted((0, 0), (0, 0), account(1), CLASS_ID, 1, 0)); assert_eq!(last_event(), event); // mint two assets assert_ok!(Nft::mint(origin.clone(), CLASS_ID, vec![1], test_attributes(1), 2)); // bit balance should be 0 (minted 2 NFT) - assert_eq!(free_bit_balance(&ALICE), 0); + assert_eq!(free_bit_balance(&account(1)), 0); - assert_eq!(OrmlNft::tokens_by_owner((ALICE, 0, 0)), ()); - assert_eq!(OrmlNft::tokens_by_owner((ALICE, 0, 1)), ()); - assert_eq!(OrmlNft::tokens_by_owner((ALICE, 0, 2)), ()); + assert_eq!(OrmlNft::tokens_by_owner((account(1), 0, 0)), ()); + assert_eq!(OrmlNft::tokens_by_owner((account(1), 0, 1)), ()); + assert_eq!(OrmlNft::tokens_by_owner((account(1), 0, 2)), ()); }) } #[test] fn mint_asset_with_promotion_enabled_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::enable_promotion(RuntimeOrigin::root(), true)); init_test_nft(origin.clone()); // bit balance should be 0 (minted 1 NFT) - assert_eq!(free_bit_balance(&ALICE), 0); + assert_eq!(free_bit_balance(&account(1)), 0); }) } #[test] fn mint_asset_with_promotion_disabled_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::enable_promotion(RuntimeOrigin::root(), false)); init_test_nft(origin.clone()); // bit balance should be 1 (minted 1 NFT) - assert_eq!(free_bit_balance(&ALICE), 0); + assert_eq!(free_bit_balance(&account(1)), 0); }) } #[test] fn mint_stackable_asset_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::enable_promotion(RuntimeOrigin::root(), true)); init_test_stackable_nft(origin.clone()); assert_eq!(free_native_balance(class_id_account()), 4); - assert_eq!(OrmlNft::tokens_by_owner((ALICE, 0, 0)), ()); + assert_eq!(OrmlNft::tokens_by_owner((account(1), 0, 0)), ()); assert_eq!( - OrmlNft::get_stackable_collections_balances((0, 0, ALICE)), + OrmlNft::get_stackable_collections_balances((0, 0, account(1))), 100u32.into() ); - let event = mock::RuntimeEvent::Nft(crate::Event::NewStackableNftMinted(ALICE, CLASS_ID, 0, 100u32.into())); + let event = mock::RuntimeEvent::Nft(crate::Event::NewStackableNftMinted( + account(1), + CLASS_ID, + 0, + 100u32.into(), + )); assert_eq!(last_event(), event); }) } @@ -297,8 +308,8 @@ fn mint_stackable_asset_should_work() { #[test] fn mint_asset_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); - let invalid_owner = RuntimeOrigin::signed(BOB); + let origin = RuntimeOrigin::signed(account(1)); + let invalid_owner = RuntimeOrigin::signed(account(2)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -328,7 +339,7 @@ fn mint_asset_should_fail() { #[test] fn mint_exceed_max_minting_limit_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -355,8 +366,8 @@ fn mint_exceed_max_minting_limit_should_fail() { #[test] fn mint_stackable_asset_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); - let invalid_owner = RuntimeOrigin::signed(BOB); + let origin = RuntimeOrigin::signed(account(1)); + let invalid_owner = RuntimeOrigin::signed(account(2)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -392,7 +403,7 @@ fn mint_stackable_asset_should_fail() { #[test] fn mint_exceed_max_batch_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1])); assert_ok!(Nft::create_class( origin.clone(), @@ -414,10 +425,10 @@ fn mint_exceed_max_batch_should_fail() { #[test] fn transfer_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_nft(origin.clone()); - assert_ok!(Nft::transfer(origin, BOB, (0, 0))); - let event = mock::RuntimeEvent::Nft(crate::Event::TransferedNft(ALICE, BOB, 0, (0, 0))); + assert_ok!(Nft::transfer(origin, account(2), (0, 0))); + let event = mock::RuntimeEvent::Nft(crate::Event::TransferedNft(account(1), account(2), 0, (0, 0))); assert_eq!(last_event(), event); }) } @@ -425,18 +436,29 @@ fn transfer_should_work() { #[test] fn transfer_stackable_nft_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_stackable_nft(origin.clone()); assert_eq!( - OrmlNft::get_stackable_collections_balances((0, 0, ALICE)), + OrmlNft::get_stackable_collections_balances((0, 0, account(1))), 100u32.into() ); - assert_ok!(Nft::transfer_stackable_nft(origin, BOB, (0, 0), 50u32.into())); - assert_eq!(OrmlNft::get_stackable_collections_balances((0, 0, BOB)), 50u32.into()); - assert_eq!(OrmlNft::get_stackable_collections_balances((0, 0, ALICE)), 50u32.into()); + assert_ok!(Nft::transfer_stackable_nft(origin, account(2), (0, 0), 50u32.into())); + assert_eq!( + OrmlNft::get_stackable_collections_balances((0, 0, account(2))), + 50u32.into() + ); + assert_eq!( + OrmlNft::get_stackable_collections_balances((0, 0, account(1))), + 50u32.into() + ); - let event = mock::RuntimeEvent::Nft(crate::Event::TransferedStackableNft(ALICE, BOB, (0, 0), 50u32.into())); + let event = mock::RuntimeEvent::Nft(crate::Event::TransferedStackableNft( + account(1), + account(2), + (0, 0), + 50u32.into(), + )); assert_eq!(last_event(), event); }) } @@ -444,48 +466,48 @@ fn transfer_stackable_nft_should_work() { #[test] fn transfer_stackable_nft_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); - let failing_origin = RuntimeOrigin::signed(BOB); + let origin = RuntimeOrigin::signed(account(1)); + let failing_origin = RuntimeOrigin::signed(account(2)); init_test_stackable_nft(origin.clone()); init_test_nft(origin.clone()); assert_noop!( - Nft::transfer_stackable_nft(origin.clone(), BOB, (0, 1), 0u32.into()), + Nft::transfer_stackable_nft(origin.clone(), account(2), (0, 1), 0u32.into()), Error::::InvalidStackableNftTransfer ); assert_noop!( - Nft::transfer_stackable_nft(origin.clone(), BOB, (0, 1), 10u32.into()), + Nft::transfer_stackable_nft(origin.clone(), account(2), (0, 1), 10u32.into()), Error::::InvalidStackableNftTransfer ); assert_noop!( - Nft::transfer_stackable_nft(origin.clone(), BOB, (0, 0), 101u32.into()), + Nft::transfer_stackable_nft(origin.clone(), account(2), (0, 0), 101u32.into()), Error::::InvalidStackableNftTransfer ); - ReservedStackableNftBalance::::insert(ALICE, (0, 0), 70); + ReservedStackableNftBalance::::insert(account(1), (0, 0), 70); assert_noop!( - Nft::transfer_stackable_nft(origin.clone(), BOB, (0, 0), 71u128), + Nft::transfer_stackable_nft(origin.clone(), account(2), (0, 0), 71u128), Error::::InvalidStackableNftTransfer ); assert_noop!( - Nft::transfer_stackable_nft(failing_origin, ALICE, (0, 0), 10u32.into()), + Nft::transfer_stackable_nft(failing_origin, account(1), (0, 0), 10u32.into()), Error::::InvalidStackableNftTransfer ); - ReservedStackableNftBalance::::insert(ALICE, (0, 0), 0); + ReservedStackableNftBalance::::insert(account(1), (0, 0), 0); - assert_ok!(Nft::transfer_stackable_nft(origin, BOB, (0, 0), 71u32.into())); + assert_ok!(Nft::transfer_stackable_nft(origin, account(2), (0, 0), 71u32.into())); }) } #[test] fn burn_nft_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_nft(origin.clone()); assert_ok!(Nft::mint(origin.clone(), CLASS_ID, vec![1], test_attributes(1), 1)); assert_ok!(Nft::burn(origin, (0, 1))); @@ -497,13 +519,18 @@ fn burn_nft_should_work() { #[test] fn burn_nft_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_stackable_nft(origin.clone()); assert_noop!(Nft::burn(origin.clone(), (0, 0)), Error::::InvalidAssetType); - assert_ok!(Nft::transfer_stackable_nft(origin.clone(), BOB, (0, 0), 100u32.into())); + assert_ok!(Nft::transfer_stackable_nft( + origin.clone(), + account(2), + (0, 0), + 100u32.into() + )); assert_noop!(Nft::burn(origin.clone(), (0, 0)), Error::::InvalidAssetType); }) @@ -512,7 +539,7 @@ fn burn_nft_should_fail() { #[test] fn transfer_batch_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_nft(origin.clone()); assert_ok!(Nft::create_class( origin.clone(), @@ -525,8 +552,11 @@ fn transfer_batch_should_work() { None )); assert_ok!(Nft::mint(origin.clone(), 1, vec![1], test_attributes(1), 4)); - assert_ok!(Nft::transfer_batch(origin, vec![(BOB, (1, 0)), (BOB, (1, 1))])); - let event = mock::RuntimeEvent::Nft(crate::Event::TransferedNft(ALICE, BOB, 1, (1, 1))); + assert_ok!(Nft::transfer_batch( + origin, + vec![(account(2), (1, 0)), (account(2), (1, 1))] + )); + let event = mock::RuntimeEvent::Nft(crate::Event::TransferedNft(account(1), account(2), 1, (1, 1))); assert_eq!(last_event(), event); }) } @@ -534,7 +564,7 @@ fn transfer_batch_should_work() { #[test] fn transfer_batch_exceed_length_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_nft(origin.clone()); assert_ok!(Nft::create_class( origin.clone(), @@ -548,7 +578,15 @@ fn transfer_batch_exceed_length_should_fail() { )); assert_ok!(Nft::mint(origin.clone(), 1, vec![1], test_attributes(1), 4)); assert_noop!( - Nft::transfer_batch(origin, vec![(BOB, (0, 0)), (BOB, (0, 1)), (BOB, (0, 2)), (BOB, (0, 3))]), + Nft::transfer_batch( + origin, + vec![ + (account(2), (0, 0)), + (account(2), (0, 1)), + (account(2), (0, 2)), + (account(2), (0, 3)) + ] + ), Error::::ExceedMaximumBatchTransfer ); }) @@ -557,7 +595,7 @@ fn transfer_batch_exceed_length_should_fail() { #[test] fn transfer_batch_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_stackable_nft(origin.clone()); init_test_nft(origin.clone()); assert_ok!(Nft::create_class( @@ -572,11 +610,11 @@ fn transfer_batch_should_fail() { )); assert_ok!(Nft::mint(origin.clone(), 1, vec![1], test_attributes(1), 1)); assert_noop!( - Nft::transfer_batch(origin.clone(), vec![(BOB, (0, 0)), (BOB, (0, 1))]), + Nft::transfer_batch(origin.clone(), vec![(account(2), (0, 0)), (account(2), (0, 1))]), Error::::InvalidAssetType ); assert_noop!( - Nft::transfer_batch(origin.clone(), vec![(BOB, (0, 3)), (BOB, (0, 6))]), + Nft::transfer_batch(origin.clone(), vec![(account(2), (0, 3)), (account(2), (0, 6))]), Error::::AssetInfoNotFound ); }) @@ -596,13 +634,19 @@ fn do_create_group_collection_should_work() { #[test] fn do_transfer_should_fail() { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); ExtBuilder::default().build().execute_with(|| { - assert_noop!(Nft::do_transfer(ALICE, BOB, (0, 0)), Error::::ClassIdNotFound); + assert_noop!( + Nft::do_transfer(account(1), account(2), (0, 0)), + Error::::ClassIdNotFound + ); init_test_nft(origin.clone()); - assert_noop!(Nft::do_transfer(BOB, ALICE, (0, 0)), Error::::NoPermission); + assert_noop!( + Nft::do_transfer(account(2), account(1), (0, 0)), + Error::::NoPermission + ); assert_ok!(Nft::create_class( origin.clone(), @@ -617,46 +661,63 @@ fn do_transfer_should_fail() { assert_ok!(Nft::mint(origin.clone(), 1, vec![1], test_attributes(1), 1)); assert_noop!( - Nft::do_transfer(ALICE, BOB, (0, 1)), + Nft::do_transfer(account(1), account(2), (0, 1)), Error::::AssetInfoNotFound ); init_test_stackable_nft(origin.clone()); - assert_noop!(Nft::do_transfer(ALICE, BOB, (0, 1)), Error::::InvalidAssetType); + assert_noop!( + Nft::do_transfer(account(1), account(2), (0, 1)), + Error::::InvalidAssetType + ); - assert_ok!(Nft::transfer_stackable_nft(origin.clone(), BOB, (0, 1), 100u32.into())); + assert_ok!(Nft::transfer_stackable_nft( + origin.clone(), + account(2), + (0, 1), + 100u32.into() + )); - assert_noop!(Nft::do_transfer(ALICE, BOB, (0, 1)), Error::::InvalidAssetType); + assert_noop!( + Nft::do_transfer(account(1), account(2), (0, 1)), + Error::::InvalidAssetType + ); }) } #[test] fn do_transfer_should_fail_if_bound_to_address() { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); ExtBuilder::default().build().execute_with(|| { - assert_noop!(Nft::do_transfer(ALICE, BOB, (0, 0)), Error::::ClassIdNotFound); + assert_noop!( + Nft::do_transfer(account(1), account(2), (0, 0)), + Error::::ClassIdNotFound + ); init_bound_to_address_nft(origin.clone()); // Owner allowed to transfer - assert_ok!(Nft::transfer(origin.clone(), BOB, (0, 0))); + assert_ok!(Nft::transfer(origin.clone(), account(2), (0, 0))); - let event = mock::RuntimeEvent::Nft(crate::Event::TransferedNft(ALICE, BOB, 0, (0, 0))); + let event = mock::RuntimeEvent::Nft(crate::Event::TransferedNft(account(1), account(2), 0, (0, 0))); assert_eq!(last_event(), event); - // Reject ownership if BOB tries to transfer - assert_noop!(Nft::do_transfer(BOB, ALICE, (0, 0)), Error::::NonTransferable); + // Reject ownership if account(2) tries to transfer + assert_noop!( + Nft::do_transfer(account(2), account(1), (0, 0)), + Error::::NonTransferable + ); }) } #[test] fn do_check_nft_ownership_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_nft(origin.clone()); - assert_ok!(Nft::check_nft_ownership(&ALICE, &(CLASS_ID, TOKEN_ID)), true); - assert_ok!(Nft::check_nft_ownership(&BOB, &(CLASS_ID, TOKEN_ID)), false); + assert_ok!(Nft::check_nft_ownership(&account(1), &(CLASS_ID, TOKEN_ID)), true); + assert_ok!(Nft::check_nft_ownership(&account(2), &(CLASS_ID, TOKEN_ID)), false); }) } @@ -664,7 +725,7 @@ fn do_check_nft_ownership_should_work() { fn do_check_nft_ownership_should_fail() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Nft::check_nft_ownership(&ALICE, &(CLASS_ID, TOKEN_ID)), + Nft::check_nft_ownership(&account(1), &(CLASS_ID, TOKEN_ID)), Error::::AssetInfoNotFound ); }) @@ -673,7 +734,7 @@ fn do_check_nft_ownership_should_fail() { #[test] fn do_withdraw_funds_from_class_fund_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); assert_noop!( Nft::withdraw_funds_from_class_fund(origin.clone(), NON_EXISTING_CLASS_ID), Error::::ClassIdNotFound @@ -689,7 +750,7 @@ fn do_withdraw_funds_from_class_fund_should_fail() { Perbill::from_percent(0u32), None )); - let non_class_owner_origin = RuntimeOrigin::signed(BOB); + let non_class_owner_origin = RuntimeOrigin::signed(account(2)); assert_noop!( Nft::withdraw_funds_from_class_fund(non_class_owner_origin, CLASS_ID), Error::::NoPermission @@ -700,14 +761,18 @@ fn do_withdraw_funds_from_class_fund_should_fail() { #[test] fn do_withdraw_funds_from_class_fund_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); init_test_nft(origin.clone()); let class_fund: AccountId = ::PalletId::get().into_sub_account_truncating(CLASS_ID); - assert_ok!(::Currency::transfer(origin.clone(), class_fund, 100)); - assert_eq!(free_native_balance(ALICE), 99896); - assert_eq!(free_native_balance(class_fund), 100); + assert_ok!(::Currency::transfer( + origin.clone(), + class_fund.clone(), + 100 + )); + assert_eq!(free_native_balance(account(1)), 99896); + assert_eq!(free_native_balance(class_fund.clone()), 100); assert_ok!(Nft::withdraw_funds_from_class_fund(origin.clone(), CLASS_ID)); - assert_eq!(free_native_balance(ALICE), 99995); + assert_eq!(free_native_balance(account(1)), 99995); assert_eq!(free_native_balance(class_fund), 1); }) } @@ -715,8 +780,8 @@ fn do_withdraw_funds_from_class_fund_should_work() { #[test] fn setting_hard_limit_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); - let failing_origin = RuntimeOrigin::signed(BOB); + let origin = RuntimeOrigin::signed(account(1)); + let failing_origin = RuntimeOrigin::signed(account(2)); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -761,7 +826,7 @@ fn setting_hard_limit_should_fail() { #[test] fn setting_hard_limit_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); let class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -796,7 +861,7 @@ fn setting_hard_limit_should_work() { #[test] fn force_updating_total_issuance_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); let class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -827,7 +892,7 @@ fn force_updating_total_issuance_should_work() { #[test] fn force_updating_total_issuance_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); let class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -855,7 +920,7 @@ fn force_updating_total_issuance_should_fail() { #[test] fn force_updating_new_royal_fee_should_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(ALICE); + let origin = RuntimeOrigin::signed(account(1)); let class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -889,7 +954,7 @@ fn force_updating_new_royal_fee_should_work() { #[test] fn force_updating_new_royal_fee_should_fail() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(BOB); + let origin = RuntimeOrigin::signed(account(2)); let class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( @@ -904,7 +969,7 @@ fn force_updating_new_royal_fee_should_fail() { )); // Non-root signer is not allowed assert_noop!( - Nft::force_update_royalty_fee(RuntimeOrigin::signed(ALICE), CLASS_ID, Perbill::from_percent(0u32)), + Nft::force_update_royalty_fee(RuntimeOrigin::signed(account(1)), CLASS_ID, Perbill::from_percent(0u32)), BadOrigin ); diff --git a/pallets/nft/src/weights.rs b/pallets/nft/src/weights.rs index 3ea116349..568a8dfe1 100644 --- a/pallets/nft/src/weights.rs +++ b/pallets/nft/src/weights.rs @@ -128,7 +128,7 @@ fn create_group() -> Weight { Weight::from_parts(49_000_000, 15931) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(8)) - } + } // Storage: Auction ItemsInAuction (r:1 w:0) // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) // Storage: Nft LockedCollection (r:1 w:0) diff --git a/runtime/common/src/mock.rs b/runtime/common/src/mock.rs index 7d99dbf43..2fefd33be 100644 --- a/runtime/common/src/mock.rs +++ b/runtime/common/src/mock.rs @@ -16,14 +16,13 @@ use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, HashedAddressMapping, Pr use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_core::{Blake2Hasher, Decode, Encode, Hasher, MaxEncodedLen, H160, H256, U256}; -use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, ConstU32, IdentityLookup}; -use sp_runtime::{AccountId32, DispatchError, Perbill}; +use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, ConstU32, IdentityLookup, Verify}; +use sp_runtime::{AccountId32, DispatchError, MultiSignature, Perbill}; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{NftAssetData, NftClassData}; use evm_mapping::AddressMapping as AddressMappingEvm; use evm_mapping::EvmAddressMapping; - use precompile_utils::precompile_set::*; use precompile_utils::EvmResult; use primitives::evm::{ @@ -45,6 +44,8 @@ pub type Balance = u128; pub type BlockNumber = u32; pub type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; pub type Block = frame_system::mocking::MockBlock; +type Signature = MultiSignature; +type AccountPublic = ::Signer; pub const COLLECTION_ID: u64 = 0; pub const CLASS_ID: ClassId = 0u32; @@ -439,6 +440,8 @@ impl nft_pallet::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; } // Configure a mock runtime to test the pallet. From 6945ff499d04fb039c527880242e506aaf932075 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 27 Oct 2023 14:44:48 +1300 Subject: [PATCH 014/155] Implement new config into mock pallets --- pallets/auction/src/mock.rs | 10 +++++++--- pallets/economy/src/mock.rs | 7 ++++++- pallets/nft/src/mock.rs | 25 ++++++++++++++----------- pallets/reward/src/mock.rs | 9 +++++++-- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/pallets/auction/src/mock.rs b/pallets/auction/src/mock.rs index e91e72bac..618a5d9c1 100644 --- a/pallets/auction/src/mock.rs +++ b/pallets/auction/src/mock.rs @@ -5,8 +5,8 @@ use frame_support::{construct_runtime, pallet_prelude::Hooks, parameter_types, P use frame_system::EnsureRoot; use orml_traits::parameter_type_with_key; use sp_core::H256; -use sp_runtime::traits::AccountIdConversion; -use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; +use sp_runtime::traits::{AccountIdConversion, IdentifyAccount, Verify}; +use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbill}; use auction_manager::{CheckAuctionItemHandler, ListingLevel}; use core_primitives::{MetaverseInfo, MetaverseMetadata, MetaverseTrait, NftAssetData, NftClassData}; @@ -23,10 +23,12 @@ parameter_types! { pub const BlockHashCount: u32 = 256; } -pub type AccountId = u128; +pub type AccountId = ::AccountId; pub type Balance = u128; pub type BlockNumber = u64; pub type MetaverseId = u64; +type Signature = MultiSignature; +type AccountPublic = ::Signer; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; @@ -384,6 +386,8 @@ impl pallet_nft::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; } parameter_types! { diff --git a/pallets/economy/src/mock.rs b/pallets/economy/src/mock.rs index f6da3a3ca..0afbb0679 100644 --- a/pallets/economy/src/mock.rs +++ b/pallets/economy/src/mock.rs @@ -6,7 +6,8 @@ use frame_system::EnsureSignedBy; use orml_traits::currency::MutationHooks; use orml_traits::parameter_type_with_key; use sp_core::H256; -use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; +use sp_runtime::traits::Verify; +use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbill}; use auction_manager::*; use core_primitives::NftAssetData; @@ -21,6 +22,8 @@ use super::*; pub type AccountId = u128; pub type Balance = u128; pub type BlockNumber = u64; +type Signature = MultiSignature; +type AccountPublic = ::Signer; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; @@ -376,6 +379,8 @@ impl pallet_nft::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; } parameter_types! { diff --git a/pallets/nft/src/mock.rs b/pallets/nft/src/mock.rs index d98b5bb3f..171fe8337 100644 --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -5,10 +5,11 @@ use frame_support::traits::{EqualPrivilegeOnly, Nothing}; use frame_support::{construct_runtime, parameter_types}; use frame_system::EnsureRoot; use orml_traits::parameter_type_with_key; +use sp_core::crypto::AccountId32; use sp_core::H256; use sp_runtime::testing::Header; -use sp_runtime::traits::IdentityLookup; -use sp_runtime::Perbill; +use sp_runtime::traits::{IdentifyAccount, IdentityLookup, Verify}; +use sp_runtime::{MultiSignature, Perbill}; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, ListingLevel}; pub use primitive_traits::{CollectionType, NftAssetData, NftClassData}; @@ -22,12 +23,12 @@ parameter_types! { pub const BlockHashCount: u32 = 256; } -pub type AccountId = u128; +pub type AccountId = ::AccountId; pub type Balance = u128; pub type BlockNumber = u64; +pub type Signature = MultiSignature; +pub type AccountPublic = ::Signer; -pub const ALICE: AccountId = 1; -pub const BOB: AccountId = 2; pub const CLASS_ID: ::ClassId = 0; pub const CLASS_ID_1: ::ClassId = 1; pub const NON_EXISTING_CLASS_ID: ::ClassId = 1000; @@ -94,7 +95,7 @@ pub struct MockAuctionManager; impl Auction for MockAuctionManager { type Balance = Balance; - fn auction_info(_id: u64) -> Option> { + fn auction_info(_id: u64) -> Option> { None } @@ -102,7 +103,7 @@ impl Auction for MockAuctionManager { None } - fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { + fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { Ok(()) } @@ -111,7 +112,7 @@ impl Auction for MockAuctionManager { } fn new_auction( - _recipient: u128, + _recipient: AccountId32, _initial_amount: Self::Balance, _start: u64, _end: Option, @@ -123,7 +124,7 @@ impl Auction for MockAuctionManager { _auction_type: AuctionType, _item_id: ItemId, _end: Option, - _recipient: u128, + _recipient: AccountId32, _initial_amount: Self::Balance, _start: u64, _listing_level: ListingLevel, @@ -161,7 +162,7 @@ impl Auction for MockAuctionManager { fn collect_royalty_fee( _high_bid_price: &Self::Balance, - _high_bidder: &u128, + _high_bidder: &AccountId32, _asset_id: &(u32, u64), _social_currency_id: FungibleTokenId, ) -> DispatchResult { @@ -252,6 +253,8 @@ impl Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; } parameter_types! { @@ -303,7 +306,7 @@ impl ExtBuilder { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, 100000), (BOB, 1000)], + balances: vec![([1; 32].into(), 100000), ([2; 32].into(), 1000)], } .assimilate_storage(&mut t) .unwrap(); diff --git a/pallets/reward/src/mock.rs b/pallets/reward/src/mock.rs index ac67ad3c5..57bfb81d2 100644 --- a/pallets/reward/src/mock.rs +++ b/pallets/reward/src/mock.rs @@ -5,7 +5,8 @@ use frame_support::{construct_runtime, ord_parameter_types, parameter_types, Pal use frame_system::EnsureSignedBy; use orml_traits::parameter_type_with_key; use sp_core::H256; -use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; +use sp_runtime::traits::{IdentifyAccount, Verify}; +use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbill}; use auction_manager::*; use core_primitives::NftAssetData; @@ -17,10 +18,12 @@ use crate as reward; use super::*; -pub type AccountId = u128; +pub type AccountId = ::AccountId; pub type Balance = u128; pub type BlockNumber = u64; pub type Hash = H256; +type Signature = MultiSignature; +type AccountPublic = ::Signer; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; @@ -262,6 +265,8 @@ impl pallet_nft::Config for Runtime { type AssetMintingFee = AssetMintingFee; type ClassMintingFee = ClassMintingFee; type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; } parameter_types! { From c3bef34c4a93e0f1ad11350e4b9925b6f27a0319 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 27 Oct 2023 14:45:15 +1300 Subject: [PATCH 015/155] Change u128 of account into AccountId32 --- pallets/economy/src/tests.rs | 172 ++++++++++++++++++++++------------- 1 file changed, 108 insertions(+), 64 deletions(-) diff --git a/pallets/economy/src/tests.rs b/pallets/economy/src/tests.rs index a67f3e08b..957d005d9 100644 --- a/pallets/economy/src/tests.rs +++ b/pallets/economy/src/tests.rs @@ -30,6 +30,12 @@ use primitives::GroupCollectionId; use super::*; +type AccountIdOf = ::AccountId; + +fn account(id: u8) -> AccountIdOf { + [id; 32].into() +} + fn init_test_nft(owner: RuntimeOrigin, collection_id: GroupCollectionId, class_id: ClassId) { //Create group collection before class assert_ok!(NFTModule::create_group(RuntimeOrigin::root(), vec![1], vec![1])); @@ -66,7 +72,7 @@ fn get_mining_currency() -> FungibleTokenId { fn set_bit_power_exchange_rate_should_fail_bad_origin() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::set_bit_power_exchange_rate(RuntimeOrigin::signed(BOB), EXCHANGE_RATE), + EconomyModule::set_bit_power_exchange_rate(RuntimeOrigin::signed(account(2)), EXCHANGE_RATE), BadOrigin ); }); @@ -88,7 +94,7 @@ fn set_bit_power_exchange_rate_should_work() { fn stake_should_fail_insufficient_balance() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_EXCESS_BALANCE, None), + EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_EXCESS_BALANCE, None), Error::::InsufficientBalanceForStaking ); }); @@ -98,10 +104,10 @@ fn stake_should_fail_insufficient_balance() { fn stake_should_fail_exit_queue_scheduled() { ExtBuilder::default().build().execute_with(|| { // Add account entry to ExitQueue - ExitQueue::::insert(ALICE, CURRENT_ROUND, STAKE_BALANCE); + ExitQueue::::insert(account(1), CURRENT_ROUND, STAKE_BALANCE); assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BELOW_MINIMUM_BALANCE, None), + EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_BELOW_MINIMUM_BALANCE, None), Error::::ExitQueueAlreadyScheduled ); }); @@ -111,7 +117,7 @@ fn stake_should_fail_exit_queue_scheduled() { fn stake_should_fail_below_minimum() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BELOW_MINIMUM_BALANCE, None), + EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_BELOW_MINIMUM_BALANCE, None), Error::::StakeBelowMinimum ); }); @@ -121,7 +127,7 @@ fn stake_should_fail_below_minimum() { fn stake_should_fail_for_non_existing_estate() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, Some(8u32.into())), + EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(8u32.into())), Error::::StakeEstateDoesNotExist ); }); @@ -131,7 +137,11 @@ fn stake_should_fail_for_non_existing_estate() { fn stake_should_fail_for_estate_not_owned_by_staker() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, Some(EXISTING_ESTATE_ID)), + EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + Some(EXISTING_ESTATE_ID) + ), Error::::StakerNotEstateOwner ); }); @@ -140,14 +150,14 @@ fn stake_should_fail_for_estate_not_owned_by_staker() { fn stake_should_fail_for_estate_owned_by_staker_but_having_previously_staked_bond() { ExtBuilder::default().build().execute_with(|| { let prepopulated_bond = Bond { - staker: BOB, + staker: account(2), amount: STAKE_BALANCE, }; EstateStakingInfo::::insert(&OWNED_ESTATE_ID, prepopulated_bond); assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, Some(OWNED_ESTATE_ID)), + EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID)), Error::::PreviousOwnerStillStakesAtEstate ); }); @@ -156,16 +166,20 @@ fn stake_should_fail_for_estate_owned_by_staker_but_having_previously_staked_bon #[test] fn stake_should_work() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); assert_eq!( last_event(), - RuntimeEvent::Economy(crate::Event::SelfStakedToEconomy101(ALICE, STAKE_BALANCE)) + RuntimeEvent::Economy(crate::Event::SelfStakedToEconomy101(account(1), STAKE_BALANCE)) ); - assert_eq!(Balances::reserved_balance(ALICE), STAKE_BALANCE); + assert_eq!(Balances::reserved_balance(account(1)), STAKE_BALANCE); - assert_eq!(EconomyModule::get_staking_info(ALICE), STAKE_BALANCE); + assert_eq!(EconomyModule::get_staking_info(account(1)), STAKE_BALANCE); assert_eq!(EconomyModule::total_stake(), STAKE_BALANCE); }); @@ -175,7 +189,7 @@ fn stake_should_work() { fn stake_should_work_for_estate() { ExtBuilder::default().build().execute_with(|| { assert_ok!(EconomyModule::stake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID) )); @@ -183,16 +197,16 @@ fn stake_should_work_for_estate() { assert_eq!( last_event(), RuntimeEvent::Economy(crate::Event::EstateStakedToEconomy101( - ALICE, + account(1), OWNED_ESTATE_ID, STAKE_BALANCE )) ); - assert_eq!(Balances::reserved_balance(ALICE), STAKE_BALANCE); + assert_eq!(Balances::reserved_balance(account(1)), STAKE_BALANCE); assert_eq!( EconomyModule::get_estate_staking_info(OWNED_ESTATE_ID).unwrap().staker, - ALICE + account(1) ); assert_eq!( EconomyModule::get_estate_staking_info(OWNED_ESTATE_ID).unwrap().amount, @@ -206,15 +220,19 @@ fn stake_should_work_for_estate() { #[test] fn stake_should_work_with_more_operations() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), 100, None)); + assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(account(1)), 100, None)); let total_staked_balance = STAKE_BALANCE + 100u128; - assert_eq!(Balances::reserved_balance(ALICE), total_staked_balance); + assert_eq!(Balances::reserved_balance(account(1)), total_staked_balance); - assert_eq!(EconomyModule::get_staking_info(ALICE), total_staked_balance); + assert_eq!(EconomyModule::get_staking_info(account(1)), total_staked_balance); assert_eq!(EconomyModule::total_stake(), total_staked_balance); }); @@ -224,7 +242,7 @@ fn stake_should_work_with_more_operations() { fn unstake_should_fail_exceeds_staked_amount() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::unstake(RuntimeOrigin::signed(ALICE), UNSTAKE_AMOUNT, None), + EconomyModule::unstake(RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, None), Error::::UnstakeAmountExceedStakedAmount ); }); @@ -233,10 +251,14 @@ fn unstake_should_fail_exceeds_staked_amount() { #[test] fn unstake_should_fail_unstake_zero() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); assert_noop!( - EconomyModule::unstake(RuntimeOrigin::signed(ALICE), 0u128, None), + EconomyModule::unstake(RuntimeOrigin::signed(account(1)), 0u128, None), Error::::UnstakeAmountIsZero ); }); @@ -246,7 +268,7 @@ fn unstake_should_fail_unstake_zero() { fn unstake_should_fail_for_non_existing_estate() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - EconomyModule::unstake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, Some(8u32.into())), + EconomyModule::unstake(RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(8u32.into())), Error::::StakeEstateDoesNotExist ); }); @@ -256,14 +278,14 @@ fn unstake_should_fail_for_non_existing_estate() { fn unstake_should_fail_for_estate_the_account_has_not_staked_in() { ExtBuilder::default().build().execute_with(|| { let prepopulated_bond = Bond { - staker: BOB, + staker: account(2), amount: STAKE_BALANCE, }; EstateStakingInfo::::insert(&OWNED_ESTATE_ID, prepopulated_bond); assert_noop!( - EconomyModule::unstake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, Some(OWNED_ESTATE_ID)), + EconomyModule::unstake(RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID)), Error::::NoFundsStakedAtEstate ); }); @@ -272,26 +294,33 @@ fn unstake_should_fail_for_estate_the_account_has_not_staked_in() { #[test] fn unstake_should_work() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); assert_ok!(EconomyModule::unstake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, None )); assert_eq!( last_event(), - RuntimeEvent::Economy(crate::Event::SelfStakingRemovedFromEconomy101(ALICE, UNSTAKE_AMOUNT)) + RuntimeEvent::Economy(crate::Event::SelfStakingRemovedFromEconomy101( + account(1), + UNSTAKE_AMOUNT + )) ); let total_staked_balance = STAKE_BALANCE - UNSTAKE_AMOUNT; - assert_eq!(EconomyModule::get_staking_info(ALICE), total_staked_balance); + assert_eq!(EconomyModule::get_staking_info(account(1)), total_staked_balance); assert_eq!(EconomyModule::total_stake(), total_staked_balance); let next_round: RoundIndex = CURRENT_ROUND.saturating_add(1); assert_eq!( - EconomyModule::staking_exit_queue(ALICE, next_round), + EconomyModule::staking_exit_queue(account(1), next_round), Some(UNSTAKE_AMOUNT) ); }); @@ -301,18 +330,18 @@ fn unstake_should_work() { fn unstake_should_work_for_estate() { ExtBuilder::default().build().execute_with(|| { assert_ok!(EconomyModule::stake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID) )); assert_noop!( - EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, Some(OWNED_ESTATE_ID)), + EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID)), Error::::StakeAmountExceedMaximumAmount ); assert_ok!(EconomyModule::unstake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, Some(OWNED_ESTATE_ID) )); @@ -320,7 +349,7 @@ fn unstake_should_work_for_estate() { assert_eq!( last_event(), RuntimeEvent::Economy(crate::Event::EstateStakingRemovedFromEconomy101( - ALICE, + account(1), OWNED_ESTATE_ID, UNSTAKE_AMOUNT )) @@ -329,7 +358,7 @@ fn unstake_should_work_for_estate() { let total_staked_balance = STAKE_BALANCE - UNSTAKE_AMOUNT; assert_eq!( EconomyModule::get_estate_staking_info(OWNED_ESTATE_ID).unwrap().staker, - ALICE + account(1) ); assert_eq!( EconomyModule::get_estate_staking_info(OWNED_ESTATE_ID).unwrap().amount, @@ -339,7 +368,7 @@ fn unstake_should_work_for_estate() { let next_round: RoundIndex = CURRENT_ROUND.saturating_add(1); assert_eq!( - EconomyModule::estate_staking_exit_queue((ALICE, next_round, OWNED_ESTATE_ID)), + EconomyModule::estate_staking_exit_queue((account(1), next_round, OWNED_ESTATE_ID)), Some(UNSTAKE_AMOUNT) ); }); @@ -348,56 +377,67 @@ fn unstake_should_work_for_estate() { #[test] fn withdraw_unstake_should_work() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); assert_ok!(EconomyModule::unstake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, None )); assert_eq!( last_event(), - RuntimeEvent::Economy(crate::Event::SelfStakingRemovedFromEconomy101(ALICE, UNSTAKE_AMOUNT)) + RuntimeEvent::Economy(crate::Event::SelfStakingRemovedFromEconomy101( + account(1), + UNSTAKE_AMOUNT + )) ); let total_staked_balance = STAKE_BALANCE - UNSTAKE_AMOUNT; - assert_eq!(EconomyModule::get_staking_info(ALICE), total_staked_balance); + assert_eq!(EconomyModule::get_staking_info(account(1)), total_staked_balance); assert_eq!(EconomyModule::total_stake(), total_staked_balance); let next_round: RoundIndex = CURRENT_ROUND.saturating_add(1); assert_eq!( - EconomyModule::staking_exit_queue(ALICE, next_round), + EconomyModule::staking_exit_queue(account(1), next_round), Some(UNSTAKE_AMOUNT) ); // Default round length is 20 blocks so moving 25 blocks will move to the next round run_to_block(25); assert_ok!(EconomyModule::withdraw_unreserved( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), next_round )); - // ALICE balance free_balance was 9000 and added 9010 after withdraw unreserved - assert_eq!(Balances::free_balance(ALICE), FREE_BALANCE); + // account(1) balance free_balance was 9000 and added 9010 after withdraw unreserved + assert_eq!(Balances::free_balance(account(1)), FREE_BALANCE); }); } #[test] fn unstake_should_work_with_single_round() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); assert_ok!(EconomyModule::unstake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, None )); - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(BOB), 200, None)); + assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(account(2)), 200, None)); let alice_staked_balance = STAKE_BALANCE - UNSTAKE_AMOUNT; - assert_eq!(EconomyModule::get_staking_info(ALICE), alice_staked_balance); + assert_eq!(EconomyModule::get_staking_info(account(1)), alice_staked_balance); let total_staked_balance = alice_staked_balance + 200; assert_eq!(EconomyModule::total_stake(), total_staked_balance); @@ -407,25 +447,29 @@ fn unstake_should_work_with_single_round() { #[test] fn unstake_should_fail_with_existing_queue() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, None)); + assert_ok!(EconomyModule::stake( + RuntimeOrigin::signed(account(1)), + STAKE_BALANCE, + None + )); assert_ok!(EconomyModule::unstake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, None )); - assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(BOB), 200, None)); + assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(account(2)), 200, None)); let alice_staked_balance = STAKE_BALANCE - UNSTAKE_AMOUNT; - assert_eq!(EconomyModule::get_staking_info(ALICE), alice_staked_balance); + assert_eq!(EconomyModule::get_staking_info(account(1)), alice_staked_balance); let total_staked_balance = alice_staked_balance + 200; assert_eq!(EconomyModule::total_stake(), total_staked_balance); assert_noop!( - EconomyModule::unstake(RuntimeOrigin::signed(ALICE), UNSTAKE_AMOUNT, None), + EconomyModule::unstake(RuntimeOrigin::signed(account(1)), UNSTAKE_AMOUNT, None), Error::::ExitQueueAlreadyScheduled ); }); @@ -434,10 +478,10 @@ fn unstake_should_fail_with_existing_queue() { #[test] fn unstake_new_estate_owner_should_fail_if_estate_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - //assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(ALICE), STAKE_BALANCE, + //assert_ok!(EconomyModule::stake(RuntimeOrigin::signed(account(1)), STAKE_BALANCE, // Some(OWNED_ESTATE_ID))); assert_noop!( - EconomyModule::unstake_new_estate_owner(RuntimeOrigin::signed(ALICE), 1000u64), + EconomyModule::unstake_new_estate_owner(RuntimeOrigin::signed(account(1)), 1000u64), Error::::StakeEstateDoesNotExist ); }); @@ -447,12 +491,12 @@ fn unstake_new_estate_owner_should_fail_if_estate_does_not_exist() { fn unstake_new_estate_owner_should_fail_if_not_estate_owner() { ExtBuilder::default().build().execute_with(|| { assert_ok!(EconomyModule::stake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID) )); assert_noop!( - EconomyModule::unstake_new_estate_owner(RuntimeOrigin::signed(BOB), OWNED_ESTATE_ID), + EconomyModule::unstake_new_estate_owner(RuntimeOrigin::signed(account(2)), OWNED_ESTATE_ID), Error::::StakerNotEstateOwner ); }); @@ -462,12 +506,12 @@ fn unstake_new_estate_owner_should_fail_if_not_estate_owner() { fn unstake_new_estate_owner_should_fail_if_no_previous_owner_has_staked_balance_left() { ExtBuilder::default().build().execute_with(|| { assert_ok!(EconomyModule::stake( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), STAKE_BALANCE, Some(OWNED_ESTATE_ID) )); assert_noop!( - EconomyModule::unstake_new_estate_owner(RuntimeOrigin::signed(ALICE), OWNED_ESTATE_ID), + EconomyModule::unstake_new_estate_owner(RuntimeOrigin::signed(account(1)), OWNED_ESTATE_ID), Error::::StakerNotPreviousOwner ); }); @@ -477,19 +521,19 @@ fn unstake_new_estate_owner_should_fail_if_no_previous_owner_has_staked_balance_ fn unstake_new_estate_owner_should_work() { ExtBuilder::default().build().execute_with(|| { let prepopulated_bond = Bond { - staker: BOB, + staker: account(2), amount: STAKE_BALANCE, }; EstateStakingInfo::::insert(&OWNED_ESTATE_ID, prepopulated_bond); assert_ok!(EconomyModule::unstake_new_estate_owner( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(account(1)), OWNED_ESTATE_ID )); assert_eq!( last_event(), RuntimeEvent::Economy(crate::Event::EstateStakingRemovedFromEconomy101( - ALICE, + account(1), OWNED_ESTATE_ID, STAKE_BALANCE )) From 39478c27d589a119f99e924f05c4953dca466bd0 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 27 Oct 2023 15:32:37 +1300 Subject: [PATCH 016/155] Update auction mock and unit test AccountId type --- pallets/auction/src/mock.rs | 52 +++++++++++++++++------------------- pallets/auction/src/tests.rs | 16 +++++++---- pallets/nft/src/tests.rs | 1 - 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/pallets/auction/src/mock.rs b/pallets/auction/src/mock.rs index 618a5d9c1..be22e2920 100644 --- a/pallets/auction/src/mock.rs +++ b/pallets/auction/src/mock.rs @@ -4,6 +4,7 @@ use frame_support::traits::{EqualPrivilegeOnly, Nothing}; use frame_support::{construct_runtime, pallet_prelude::Hooks, parameter_types, PalletId}; use frame_system::EnsureRoot; use orml_traits::parameter_type_with_key; +use sp_core::crypto::AccountId32; use sp_core::H256; use sp_runtime::traits::{AccountIdConversion, IdentifyAccount, Verify}; use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbill}; @@ -30,9 +31,6 @@ pub type MetaverseId = u64; type Signature = MultiSignature; type AccountPublic = ::Signer; -pub const ALICE: AccountId = 1; -pub const BOB: AccountId = 2; -pub const NO_METAVERSE_OWNER: AccountId = 3; pub const CLASS_ID: u32 = 0; pub const COLLECTION_ID: u64 = 0; pub const ALICE_METAVERSE_ID: MetaverseId = 1; @@ -45,8 +43,6 @@ pub const LAND_UNIT_EXIST: (i32, i32) = (0, 0); pub const LAND_UNIT_EXIST_1: (i32, i32) = (1, 1); pub const LAND_UNIT_NOT_EXIST: (i32, i32) = (99, 99); -pub const GENERAL_METAVERSE_FUND: AccountId = 102; - pub const UNDEPLOYED_LAND_BLOCK_ID_EXIST: UndeployedLandBlockId = 4; pub const UNDEPLOYED_LAND_BLOCK_ID_NOT_EXIST: UndeployedLandBlockId = 5; @@ -95,7 +91,7 @@ impl pallet_balances::Config for Runtime { pub struct Continuumm; -impl MapTrait for Continuumm { +impl MapTrait for Continuumm { fn transfer_spot( _spot_id: MapSpotId, _from: AccountId, @@ -107,7 +103,7 @@ impl MapTrait for Continuumm { pub struct EstateHandler; -impl Estate for EstateHandler { +impl Estate for EstateHandler { fn transfer_estate(_estate_id: EstateId, _from: &AccountId, _to: &AccountId) -> Result { Ok(1) } @@ -185,8 +181,8 @@ impl AuctionHandler for Handler { new_bid: (AccountId, Balance), _last_bid: Option<(AccountId, Balance)>, ) -> OnNewBidResult { - // Test with Alice bid - if new_bid.0 == ALICE || new_bid.0 == BOB { + // Test with [1,32].into() bid + if new_bid.0 == [1; 32].into() || new_bid.0 == [2; 32].into() { OnNewBidResult { accept_bid: true, auction_end_change: Change::NoChange, @@ -250,14 +246,16 @@ impl MetaverseTrait for MetaverseInfoSource { } fn check_ownership(who: &AccountId, metaverse_id: &MetaverseId) -> bool { - match *who { - ALICE => *metaverse_id == ALICE_METAVERSE_ID, - BOB => *metaverse_id == BOB_METAVERSE_ID, - _ => false, + if who == &AccountId32::new([1; 32]) { + *metaverse_id == ALICE_METAVERSE_ID + } else if who == &AccountId32::new([2; 32]) { + *metaverse_id == BOB_METAVERSE_ID + } else { + false } } - fn get_metaverse(_metaverse_id: u64) -> Option> { + fn get_metaverse(_metaverse_id: u64) -> Option> { None } @@ -282,11 +280,11 @@ impl MetaverseTrait for MetaverseInfoSource { } fn get_metaverse_treasury(metaverse_id: MetaverseId) -> AccountId { - GENERAL_METAVERSE_FUND + [102; 32].into() } fn get_network_treasury() -> AccountId { - GENERAL_METAVERSE_FUND + [102; 32].into() } fn check_if_metaverse_estate( @@ -304,7 +302,7 @@ impl MetaverseTrait for MetaverseInfoSource { } fn is_metaverse_owner(who: &AccountId) -> bool { - who != &NO_METAVERSE_OWNER + who != &[3; 32].into() } } @@ -446,15 +444,15 @@ impl ExtBuilder { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, 100000), (BOB, 500), (NO_METAVERSE_OWNER, 500)], + balances: vec![([1; 32].into(), 100000), ([2; 32].into(), 500), ([3; 32].into(), 500)], } .assimilate_storage(&mut t) .unwrap(); orml_tokens::GenesisConfig:: { balances: vec![ - (ALICE, FungibleTokenId::MiningResource(0), 10000), - (BOB, FungibleTokenId::MiningResource(0), 5000), + ([1; 32].into(), FungibleTokenId::MiningResource(0), 10000), + ([2; 32].into(), FungibleTokenId::MiningResource(0), 5000), ], } .assimilate_storage(&mut t) @@ -488,7 +486,7 @@ pub struct MockAuctionManager; impl Auction for MockAuctionManager { type Balance = Balance; - fn auction_info(_id: u64) -> Option> { + fn auction_info(_id: u64) -> Option> { None } @@ -496,7 +494,7 @@ impl Auction for MockAuctionManager { None } - fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { + fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { Ok(()) } @@ -505,7 +503,7 @@ impl Auction for MockAuctionManager { } fn new_auction( - _recipient: u128, + _recipient: AccountId, _initial_amount: Self::Balance, _start: u64, _end: Option, @@ -517,7 +515,7 @@ impl Auction for MockAuctionManager { _auction_type: AuctionType, _item_id: ItemId, _end: Option, - _recipient: u128, + _recipient: AccountId, _initial_amount: Self::Balance, _start: u64, _listing_level: ListingLevel, @@ -548,8 +546,8 @@ impl Auction for MockAuctionManager { fn local_auction_bid_handler( _now: u64, _id: u64, - _new_bid: (u128, Self::Balance), - _last_bid: Option<(u128, Self::Balance)>, + _new_bid: (AccountId, Self::Balance), + _last_bid: Option<(AccountId, Self::Balance)>, _social_currency_id: FungibleTokenId, ) -> DispatchResult { Ok(()) @@ -557,7 +555,7 @@ impl Auction for MockAuctionManager { fn collect_royalty_fee( _high_bid_price: &Self::Balance, - _high_bidder: &u128, + _high_bidder: &AccountId, _asset_id: &(u32, u64), _social_currency_id: FungibleTokenId, ) -> DispatchResult { diff --git a/pallets/auction/src/tests.rs b/pallets/auction/src/tests.rs index ab4a5b657..d40984cdd 100644 --- a/pallets/auction/src/tests.rs +++ b/pallets/auction/src/tests.rs @@ -1,6 +1,7 @@ #![cfg(test)] use frame_support::{assert_noop, assert_ok}; +use sp_core::crypto::AccountId32; use sp_std::collections::btree_map::BTreeMap; use auction_manager::ListingLevel; @@ -11,6 +12,11 @@ use primitives::{ClassId, FungibleTokenId}; use super::*; +pub const ALICE: AccountId = AccountId32::new([1; 32]); +pub const BOB: AccountId = AccountId32::new([2; 32]); +pub const NO_METAVERSE_OWNER: AccountId = AccountId32::new([3; 32]); +pub const GENERAL_METAVERSE_FUND: AccountId = AccountId32::new([102; 32]); + fn init_test_nft(owner: RuntimeOrigin) { //Create group collection before class assert_ok!(NFTModule::::create_group( @@ -785,7 +791,7 @@ fn bid_works() { assert_eq!( AuctionModule::auctions(0), Some(AuctionInfo { - bid: Some((1, 200)), + bid: Some((ALICE, 200)), start: 1, end: Some(101), }) @@ -825,7 +831,7 @@ fn bid_anti_snipe_duration_works() { assert_eq!( AuctionModule::auctions(0), Some(AuctionInfo { - bid: Some((1, 200)), + bid: Some((ALICE, 200)), start: 1, end: Some(106), }) @@ -848,7 +854,7 @@ fn bid_anti_snipe_duration_works() { // Verify if auction finalized with new end time. assert_eq!( last_event(), - RuntimeEvent::AuctionModule(crate::Event::AuctionFinalized(0, 1, 201)) + RuntimeEvent::AuctionModule(crate::Event::AuctionFinalized(0, ALICE, 201)) ); }); } @@ -1046,7 +1052,7 @@ fn asset_transfers_after_auction() { // Verify asset transfers to alice after end of auction assert_eq!( last_event(), - RuntimeEvent::AuctionModule(crate::Event::AuctionFinalized(0, 1, 200)) + RuntimeEvent::AuctionModule(crate::Event::AuctionFinalized(0, ALICE, 200)) ); // Verify transfer of fund (minus gas) @@ -1100,7 +1106,7 @@ fn asset_transfers_after_multicurrency_auction() { // Verify asset transfers to alice after end of auction assert_eq!( last_event(), - RuntimeEvent::AuctionModule(crate::Event::AuctionFinalized(0, 1, 200)) + RuntimeEvent::AuctionModule(crate::Event::AuctionFinalized(0, ALICE, 200)) ); // Verify transfer of fund diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index 8c8a20382..792df6861 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -11,7 +11,6 @@ use primitives::{Balance, FungibleTokenId}; use super::*; type AccountIdOf = ::AccountId; - fn account(id: u8) -> AccountIdOf { [id; 32].into() } From 8a99919003743bf8fd4124547ac740516400f0fc Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 27 Oct 2023 15:48:32 +1300 Subject: [PATCH 017/155] Update reward mock and unit test AccountId type --- pallets/reward/src/mock.rs | 25 +++++++++++++------------ pallets/reward/src/tests.rs | 34 ++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/pallets/reward/src/mock.rs b/pallets/reward/src/mock.rs index 57bfb81d2..6c045b21b 100644 --- a/pallets/reward/src/mock.rs +++ b/pallets/reward/src/mock.rs @@ -4,6 +4,7 @@ use frame_support::traits::Nothing; use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; use frame_system::EnsureSignedBy; use orml_traits::parameter_type_with_key; +use sp_core::crypto::AccountId32; use sp_core::H256; use sp_runtime::traits::{IdentifyAccount, Verify}; use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbill}; @@ -25,11 +26,11 @@ pub type Hash = H256; type Signature = MultiSignature; type AccountPublic = ::Signer; -pub const ALICE: AccountId = 1; -pub const BOB: AccountId = 2; -pub const CHARLIE: AccountId = 3; -pub const DONNA: AccountId = 4; -pub const EVA: AccountId = 5; +pub const ALICE: AccountId = AccountId32::new([1; 32]); +pub const BOB: AccountId = AccountId32::new([2; 32]); +pub const CHARLIE: AccountId = AccountId32::new([3; 32]); +pub const DONNA: AccountId = AccountId32::new([4; 32]); +pub const EVA: AccountId = AccountId32::new([5; 32]); pub const COLLECTION_ID: u64 = 0; pub const CLASS_ID: u32 = 0; @@ -165,7 +166,7 @@ pub struct MockAuctionManager; impl Auction for MockAuctionManager { type Balance = Balance; - fn auction_info(_id: u64) -> Option> { + fn auction_info(_id: u64) -> Option> { None } @@ -173,7 +174,7 @@ impl Auction for MockAuctionManager { None } - fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { + fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { Ok(()) } @@ -182,7 +183,7 @@ impl Auction for MockAuctionManager { } fn new_auction( - _recipient: u128, + _recipient: AccountId, _initial_amount: Self::Balance, _start: u64, _end: Option, @@ -194,7 +195,7 @@ impl Auction for MockAuctionManager { _auction_type: AuctionType, _item_id: ItemId, _end: Option, - _recipient: u128, + _recipient: AccountId, _initial_amount: Self::Balance, _start: u64, _listing_level: ListingLevel, @@ -217,8 +218,8 @@ impl Auction for MockAuctionManager { fn local_auction_bid_handler( _now: u64, _id: u64, - _new_bid: (u128, Self::Balance), - _last_bid: Option<(u128, Self::Balance)>, + _new_bid: (AccountId, Self::Balance), + _last_bid: Option<(AccountId, Self::Balance)>, _social_currency_id: FungibleTokenId, ) -> DispatchResult { Ok(()) @@ -226,7 +227,7 @@ impl Auction for MockAuctionManager { fn collect_royalty_fee( _high_bid_price: &Self::Balance, - _high_bidder: &u128, + _high_bidder: &AccountId, _asset_id: &(u32, u64), _social_currency_id: FungibleTokenId, ) -> DispatchResult { diff --git a/pallets/reward/src/tests.rs b/pallets/reward/src/tests.rs index 323fb03eb..793a39b07 100644 --- a/pallets/reward/src/tests.rs +++ b/pallets/reward/src/tests.rs @@ -18,15 +18,16 @@ #![cfg(test)] use frame_support::{assert_err, assert_noop, assert_ok, sp_runtime::runtime_logger}; +use hex_literal::hex; use sp_std::collections::btree_map::BTreeMap; use sp_std::default::Default; -use super::*; use core_primitives::Attributes; -use hex_literal::hex; use mock::{Balance, RuntimeEvent, *}; use primitives::{CampaignInfo, FungibleTokenId, Hash}; +use super::*; + fn init_test_nft(owner: RuntimeOrigin) { //Create group collection before class assert_ok!(NFTModule::create_group(RuntimeOrigin::root(), vec![1], vec![1])); @@ -572,7 +573,11 @@ fn set_reward_fails() { ); assert_noop!( - Reward::set_reward(RuntimeOrigin::signed(ALICE), 0, vec![(BOB, 3), (100, 3), (102, 3)]), + Reward::set_reward( + RuntimeOrigin::signed(ALICE), + 0, + vec![(BOB, 3), ([100; 32].into(), 3), ([102; 32].into(), 3)] + ), Error::::RewardsListSizeAboveMaximum ); @@ -584,7 +589,7 @@ fn set_reward_fails() { ); assert_noop!( - Reward::set_reward(RuntimeOrigin::signed(3), 0, vec![(BOB, 5)]), + Reward::set_reward(RuntimeOrigin::signed(CHARLIE), 0, vec![(BOB, 5)]), Error::::InvalidSetRewardOrigin ); @@ -674,7 +679,7 @@ fn set_reward_root_fails() { ); assert_noop!( - Reward::set_reward_root(RuntimeOrigin::signed(3), 0, 5, test_hash(2u64)), + Reward::set_reward_root(RuntimeOrigin::signed(CHARLIE), 0, 5, test_hash(2u64)), Error::::InvalidSetRewardOrigin ); @@ -722,12 +727,17 @@ fn set_nft_reward_fails() { )); assert_noop!( - Reward::set_nft_reward(RuntimeOrigin::signed(3), 0, vec![(BOB, 1)], 1), + Reward::set_nft_reward(RuntimeOrigin::signed(CHARLIE), 0, vec![(BOB, 1)], 1), Error::::InvalidSetRewardOrigin ); assert_noop!( - Reward::set_nft_reward(RuntimeOrigin::signed(ALICE), 0, vec![(BOB, 1), (102, 1), (100, 1)], 3), + Reward::set_nft_reward( + RuntimeOrigin::signed(ALICE), + 0, + vec![(BOB, 1), (CHARLIE, 1), (DONNA, 1)], + 3 + ), Error::::RewardsListSizeAboveMaximum ); @@ -761,12 +771,12 @@ fn set_nft_reward_fails() { assert_ok!(Reward::set_nft_reward( RuntimeOrigin::signed(ALICE), 0, - vec![(106, 2)], + vec![([106; 32].into(), 2)], 2 )); assert_noop!( - Reward::set_nft_reward(RuntimeOrigin::signed(ALICE), 0, vec![(100, 1)], 1), + Reward::set_nft_reward(RuntimeOrigin::signed(ALICE), 0, vec![(CHARLIE, 1)], 1), Error::::RewardExceedCap ); @@ -837,7 +847,7 @@ fn set_nft_reward_root_fails() { )); assert_noop!( - Reward::set_nft_reward_root(RuntimeOrigin::signed(3), 1, test_hash(2u64)), + Reward::set_nft_reward_root(RuntimeOrigin::signed(CHARLIE), 1, test_hash(2u64)), Error::::InvalidSetRewardOrigin ); @@ -1742,7 +1752,7 @@ fn close_campaign_using_merkle_root_works() { RuntimeOrigin::signed(ALICE), 0, 4, - test_claim_hash(3, 3) + test_claim_hash(CHARLIE, 3) )); run_to_block(11); @@ -1875,7 +1885,7 @@ fn close_campaign_using_merkle_root_fails() { RuntimeOrigin::signed(ALICE), 0, 4, - test_claim_hash(3, 3) + test_claim_hash(CHARLIE, 3) )); run_to_block(17); From a2eecb31312436568fdb3052c77372566729716b Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 27 Oct 2023 17:15:07 +1300 Subject: [PATCH 018/155] Fix all unit tests on new AccountId type --- pallets/economy/src/mock.rs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/pallets/economy/src/mock.rs b/pallets/economy/src/mock.rs index 0afbb0679..caccaa029 100644 --- a/pallets/economy/src/mock.rs +++ b/pallets/economy/src/mock.rs @@ -5,8 +5,9 @@ use frame_support::{construct_runtime, ord_parameter_types, parameter_types, Pal use frame_system::EnsureSignedBy; use orml_traits::currency::MutationHooks; use orml_traits::parameter_type_with_key; +use sp_core::crypto::AccountId32; use sp_core::H256; -use sp_runtime::traits::Verify; +use sp_runtime::traits::{IdentifyAccount, Verify}; use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbill}; use auction_manager::*; @@ -19,15 +20,15 @@ use crate as economy; use super::*; -pub type AccountId = u128; +pub type AccountId = ::AccountId; pub type Balance = u128; pub type BlockNumber = u64; type Signature = MultiSignature; type AccountPublic = ::Signer; -pub const ALICE: AccountId = 1; -pub const BOB: AccountId = 2; -pub const FREEDY: AccountId = 3; +pub const ALICE: AccountId = AccountId32::new([1; 32]); +pub const BOB: AccountId = AccountId32::new([2; 32]); +pub const FREEDY: AccountId = AccountId32::new([3; 32]); pub const DISTRIBUTOR_COLLECTION_ID: u64 = 0; pub const DISTRIBUTOR_CLASS_ID: ClassId = 0; @@ -124,15 +125,15 @@ impl pallet_balances::Config for Runtime { pub struct EstateHandler; -impl Estate for EstateHandler { - fn transfer_estate(estate_id: EstateId, _from: &u128, _to: &u128) -> Result { +impl Estate for EstateHandler { + fn transfer_estate(estate_id: EstateId, _from: &AccountId, _to: &AccountId) -> Result { Ok(estate_id) } fn transfer_landunit( coordinate: (i32, i32), - _from: &u128, - _to: &(u128, primitives::MetaverseId), + _from: &AccountId, + _to: &(AccountId, primitives::MetaverseId), ) -> Result<(i32, i32), DispatchError> { Ok(coordinate) } @@ -216,8 +217,8 @@ impl pallet_mining::Config for Runtime { } ord_parameter_types! { - pub const One: AccountId = 1; - pub const Two: AccountId = 2; + pub const One: AccountId = AccountId32::new([1;32]); + pub const Two: AccountId = AccountId32::new([2;32]); pub const PowerAmountPerBlock: u32 = 10; } impl Config for Runtime { @@ -279,7 +280,7 @@ pub struct MockAuctionManager; impl Auction for MockAuctionManager { type Balance = Balance; - fn auction_info(_id: u64) -> Option> { + fn auction_info(_id: u64) -> Option> { None } @@ -287,7 +288,7 @@ impl Auction for MockAuctionManager { None } - fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { + fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { Ok(()) } @@ -296,7 +297,7 @@ impl Auction for MockAuctionManager { } fn new_auction( - _recipient: u128, + _recipient: AccountId, _initial_amount: Self::Balance, _start: u64, _end: Option, @@ -308,7 +309,7 @@ impl Auction for MockAuctionManager { _auction_type: AuctionType, _item_id: ItemId, _end: Option, - _recipient: u128, + _recipient: AccountId, _initial_amount: Self::Balance, _start: u64, _listing_level: ListingLevel, @@ -331,8 +332,8 @@ impl Auction for MockAuctionManager { fn local_auction_bid_handler( _now: u64, _id: u64, - _new_bid: (u128, Self::Balance), - _last_bid: Option<(u128, Self::Balance)>, + _new_bid: (AccountId, Self::Balance), + _last_bid: Option<(AccountId, Self::Balance)>, _social_currency_id: FungibleTokenId, ) -> DispatchResult { Ok(()) @@ -340,7 +341,7 @@ impl Auction for MockAuctionManager { fn collect_royalty_fee( _high_bid_price: &Self::Balance, - _high_bidder: &u128, + _high_bidder: &AccountId, _asset_id: &(u32, u64), _social_currency_id: FungibleTokenId, ) -> DispatchResult { From 95a124c0decdf1f25168c9c58fba050b3ef5c080 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 28 Oct 2023 11:32:40 +1300 Subject: [PATCH 019/155] Add signature verification test to ensure message encode and verify correctly --- pallets/nft/src/tests.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index 792df6861..3622cc66f 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -1,7 +1,9 @@ use frame_support::{assert_noop, assert_ok}; use orml_nft::Pallet as NftModule; use orml_traits::MultiCurrency; -use sp_runtime::traits::BadOrigin; +use sp_core::Pair; +use sp_runtime::traits::{BadOrigin, IdentifyAccount}; +use sp_runtime::{MultiSignature, MultiSigner}; use sp_std::default::Default; use mock::*; @@ -979,3 +981,31 @@ fn force_updating_new_royal_fee_should_fail() { ); }) } + +#[test] +fn validate_signature() { + ExtBuilder::default().build().execute_with(|| { + let alice_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let alice_signer = MultiSigner::Sr25519(alice_pair.public()); + let alice = alice_signer.clone().into_account(); + let mint_data: PreSignedMint = PreSignedMint { + class_id: 0, + attributes: test_attributes(1), + metadata: vec![], + only_account: None, + mint_price: None, + token_id: None, + }; + let encoded_data = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(alice_pair.sign(&encoded_data)); + assert_ok!(Nft::validate_signature(&encoded_data, &signature, &alice)); + + let mut wrapped_data: Vec = Vec::new(); + wrapped_data.extend(b""); + wrapped_data.extend(&encoded_data); + wrapped_data.extend(b""); + + let signature = MultiSignature::Sr25519(alice_pair.sign(&wrapped_data)); + assert_ok!(Nft::validate_signature(&encoded_data, &signature, &alice)); + }) +} From 0ead854e27d06feb92aa894b313b1a11ac9d684a Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 28 Oct 2023 12:06:05 +1300 Subject: [PATCH 020/155] Increase initial balance of account 0 --- pallets/nft/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nft/src/mock.rs b/pallets/nft/src/mock.rs index 171fe8337..6fc8ecaf0 100644 --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -306,7 +306,7 @@ impl ExtBuilder { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![([1; 32].into(), 100000), ([2; 32].into(), 1000)], + balances: vec![([0; 32].into(), 100), ([1; 32].into(), 100000), ([2; 32].into(), 1000)], } .assimilate_storage(&mut t) .unwrap(); From da767188335a98fe3ce5a7cd3f21158cfd5bd8bd Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 28 Oct 2023 12:06:27 +1300 Subject: [PATCH 021/155] Presign mint test to ensure the logic works --- pallets/nft/src/tests.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index 3622cc66f..dace8c95f 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -1009,3 +1009,39 @@ fn validate_signature() { assert_ok!(Nft::validate_signature(&encoded_data, &signature, &alice)); }) } + +#[test] +fn pre_signed_mints_should_work() { + ExtBuilder::default().build().execute_with(|| { + let alice = account(1); + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data: PreSignedMint = PreSignedMint { + class_id: 0, + attributes: test_attributes(1), + metadata: vec![], + only_account: None, + mint_price: None, + token_id: None, + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(alice.clone()), + user_1.clone(), + 100 + )); + + init_test_nft(RuntimeOrigin::signed(user_1.clone())); + + // User id already signed message so Alice should able to mint nft from pre-signed message + assert_ok!(Nft::mint_pre_signed( + RuntimeOrigin::signed(alice.clone()), + Box::new(mint_data.clone()), + signature.clone(), + user_1.clone(), + )); + assert_eq!(OrmlNft::tokens_by_owner((alice, 0, 0)), ()); + }) +} From 437b56e196b64b6b48b00594699b1fbba2452c20 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 28 Oct 2023 22:26:25 +1300 Subject: [PATCH 022/155] Make sure can't mint with wrong signature --- pallets/nft/src/tests.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index dace8c95f..61f7be413 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -1043,5 +1043,26 @@ fn pre_signed_mints_should_work() { user_1.clone(), )); assert_eq!(OrmlNft::tokens_by_owner((alice, 0, 0)), ()); + + // validate the `only_account` field + let only_account_mint_data: PreSignedMint = PreSignedMint { + class_id: 0, + attributes: test_attributes(1), + metadata: vec![], + only_account: Some(account(2)), + mint_price: None, + token_id: None, + }; + + // can't mint with the wrong signature + assert_noop!( + Nft::mint_pre_signed( + RuntimeOrigin::signed(account(3).clone()), + Box::new(only_account_mint_data.clone()), + signature.clone(), + user_1.clone(), + ), + Error::::WrongSignature + ); }) } From c7f00bede20577582887fa6673f39e2764883315 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 28 Oct 2023 22:35:27 +1300 Subject: [PATCH 023/155] Presign mint should only work if only account specified --- pallets/nft/src/tests.rs | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index 61f7be413..27feaf6af 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -1043,9 +1043,17 @@ fn pre_signed_mints_should_work() { user_1.clone(), )); assert_eq!(OrmlNft::tokens_by_owner((alice, 0, 0)), ()); + }) +} - // validate the `only_account` field - let only_account_mint_data: PreSignedMint = PreSignedMint { +#[test] +fn pre_signed_mint_should_work_with_only_account() { + ExtBuilder::default().build().execute_with(|| { + let alice = account(1); + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data: PreSignedMint = PreSignedMint { class_id: 0, attributes: test_attributes(1), metadata: vec![], @@ -1053,16 +1061,32 @@ fn pre_signed_mints_should_work() { mint_price: None, token_id: None, }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(alice.clone()), + user_1.clone(), + 100 + )); + + init_test_nft(RuntimeOrigin::signed(user_1.clone())); - // can't mint with the wrong signature assert_noop!( Nft::mint_pre_signed( - RuntimeOrigin::signed(account(3).clone()), - Box::new(only_account_mint_data.clone()), + RuntimeOrigin::signed(alice.clone()), + Box::new(mint_data.clone()), signature.clone(), user_1.clone(), ), - Error::::WrongSignature + Error::::NoPermission ); + + assert_ok!(Nft::mint_pre_signed( + RuntimeOrigin::signed(account(2)), + Box::new(mint_data.clone()), + signature.clone(), + user_1.clone(), + )); + assert_eq!(OrmlNft::tokens_by_owner((account(2), 0, 0)), ()); }) } From 2fa8e65a08561504c0112f5f93bae5d8c10720a4 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 28 Oct 2023 22:44:20 +1300 Subject: [PATCH 024/155] Implement mint fee collection deduct correctly from protocol and collection owner --- pallets/nft/src/tests.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index 27feaf6af..a54463fbe 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -1090,3 +1090,43 @@ fn pre_signed_mint_should_work_with_only_account() { assert_eq!(OrmlNft::tokens_by_owner((account(2), 0, 0)), ()); }) } + +#[test] +fn pre_signed_mint_should_collect_fee_with_mint_price() { + ExtBuilder::default().build().execute_with(|| { + let alice = account(1); + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data: PreSignedMint = PreSignedMint { + class_id: 0, + attributes: test_attributes(1), + metadata: vec![], + only_account: None, + mint_price: Some(50), + token_id: None, + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(alice.clone()), + user_1.clone(), + 100 + )); + assert_eq!(Balances::free_balance(user_1.clone()), 100); + assert_eq!(Balances::free_balance(alice.clone()), 99900); + + init_test_nft(RuntimeOrigin::signed(user_1.clone())); + assert_eq!(Balances::free_balance(user_1.clone()), 96); // Deduct fee + + assert_ok!(Nft::mint_pre_signed( + RuntimeOrigin::signed(alice.clone()), + Box::new(mint_data.clone()), + signature.clone(), + user_1.clone(), + )); + assert_eq!(Balances::free_balance(user_1.clone()), 146); // Get 50 mint fees from NFT + assert_eq!(Balances::free_balance(alice.clone()), 99849); // Pay 1 mint fee for protocol and 50 mint price = 99900 - 51 + assert_eq!(OrmlNft::tokens_by_owner((account(2), 0, 0)), ()); + }) +} From 175bab1c18dc42ffd9bed60b0ae92a5cc8d5f551 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 29 Oct 2023 11:23:50 +1300 Subject: [PATCH 025/155] Fix js generated leaf test issue --- pallets/reward/src/tests.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pallets/reward/src/tests.rs b/pallets/reward/src/tests.rs index 793a39b07..ea8d3466d 100644 --- a/pallets/reward/src/tests.rs +++ b/pallets/reward/src/tests.rs @@ -110,6 +110,13 @@ fn test_claim_hash(who: AccountId, balance: Balance) -> Hash { keccak_256(&keccak_256(&leaf)).into() } +fn test_claim_hash_account_u128(who: u128, balance: Balance) -> Hash { + let mut leaf: Vec = who.encode(); + leaf.extend(balance.encode()); + + keccak_256(&keccak_256(&leaf)).into() +} + fn test_claim_nft_hash(who: AccountId, token: (ClassId, TokenId)) -> Hash { let mut leaf: Vec = who.encode(); leaf.extend(token.0.encode()); @@ -2247,7 +2254,10 @@ fn js_encoded_data_matches_blockchain_encoded_data() { assert_eq!( leaf, - vec![2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + vec![ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ); // SCALE encoding for [2u128, 10u128] }); } @@ -2258,22 +2268,22 @@ fn js_generated_leafs_match_blockchain_generated_leafs() { let bob_hash = Hash::from_slice(&hex!( "64b39d59f54b02b6d862584c58735a0d3ff7c8d1ee46250809f4c244ca13d5ca" )); - assert_eq!(test_claim_hash(BOB, 10), bob_hash); + assert_eq!(test_claim_hash_account_u128(2, 10), bob_hash); let charlie_hash = Hash::from_slice(&hex!( "d90b5864238131f03c065e80a5e0c04aadb2493984702ef3bb279dcd3cb8ac7d" )); - assert_eq!(test_claim_hash(CHARLIE, 25), charlie_hash); + assert_eq!(test_claim_hash_account_u128(3, 25), charlie_hash); let donna_hash = Hash::from_slice(&hex!( "77ead2ce9a216ed6ac05f5d8a2c7d12373428794b33d56f65163073769976208" )); - assert_eq!(test_claim_hash(DONNA, 50), donna_hash); + assert_eq!(test_claim_hash_account_u128(4, 50), donna_hash); let eva_hash = Hash::from_slice(&hex!( "7ecf6a4f9809680533d36217de280ae07964f4c65595308405e2c860bc52d4bf" )); - assert_eq!(test_claim_hash(EVA, 75), eva_hash); + assert_eq!(test_claim_hash_account_u128(5, 75), eva_hash); }); } From 4c921a6f02c7ee68887396adc3cce66781d6c1c0 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 29 Oct 2023 11:52:45 +1300 Subject: [PATCH 026/155] Create land minting infrastructure --- pallets/land-minting/src/lib.rs | 2327 +++++++++++++++++++++ pallets/land-minting/src/mock.rs | 637 ++++++ pallets/land-minting/src/rate.rs | 167 ++ pallets/land-minting/src/tests.rs | 2908 +++++++++++++++++++++++++++ pallets/land-minting/src/weights.rs | 699 +++++++ pallets/reward/src/tests.rs | 2 +- 6 files changed, 6739 insertions(+), 1 deletion(-) create mode 100644 pallets/land-minting/src/lib.rs create mode 100644 pallets/land-minting/src/mock.rs create mode 100644 pallets/land-minting/src/rate.rs create mode 100644 pallets/land-minting/src/tests.rs create mode 100644 pallets/land-minting/src/weights.rs diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs new file mode 100644 index 000000000..3c124e779 --- /dev/null +++ b/pallets/land-minting/src/lib.rs @@ -0,0 +1,2327 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::pallet_prelude::*; +use frame_support::{ + dispatch::DispatchResult, + ensure, log, + traits::{Currency, ExistenceRequirement, Get}, + transactional, PalletId, +}; +use frame_system::pallet_prelude::*; +use frame_system::{ensure_root, ensure_signed}; +use scale_info::TypeInfo; + +use sp_runtime::{ + traits::{AccountIdConversion, Convert, One, Saturating, Zero}, + ArithmeticError, DispatchError, Perbill, SaturatedConversion, +}; +use sp_std::vec::Vec; + +use auction_manager::{Auction, CheckAuctionItemHandler}; +use core_primitives::*; +pub use pallet::*; +use primitives::estate::EstateInfo; +use primitives::{ + estate::{Estate, LandUnitStatus, LeaseContract, OwnerId}, + Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, TokenId, UndeployedLandBlock, + UndeployedLandBlockId, UndeployedLandBlockType, +}; +pub use rate::{MintingRateInfo, Range}; +pub use weights::WeightInfo; + +//#[cfg(feature = "runtime-benchmarks")] +//pub mod benchmarking; + +#[cfg(test)] +mod mock; +mod rate; + +#[cfg(test)] +mod tests; + +pub mod weights; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; + use sp_runtime::traits::{CheckedAdd, CheckedSub, Zero}; + + use primitives::estate::EstateInfo; + use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; + use primitives::{Balance, RoundIndex, UndeployedLandBlockId}; + + use crate::rate::{round_issuance_range, MintingRateInfo}; + + use super::*; + + #[pallet::pallet] + #[pallet::generate_store(trait Store)] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Land treasury source + #[pallet::constant] + type LandTreasury: Get; + + /// Source of metaverse info + type MetaverseInfoSource: MetaverseTrait; + + /// Currency type + type Currency: Currency + ReservableCurrency; + + /// Minimum land price + type MinimumLandPrice: Get>; + + /// Council origin which allows to update max bound + type CouncilOrigin: EnsureOrigin; + + /// Auction handler + type AuctionHandler: Auction + CheckAuctionItemHandler>; + + /// Minimum number of blocks per round + #[pallet::constant] + type MinBlocksPerRound: Get; + + /// Weight implementation for estate extrinsics + type WeightInfo: WeightInfo; + + /// Minimum staking balance + #[pallet::constant] + type MinimumStake: Get>; + + /// Delay of staking reward payment (in number of rounds) + #[pallet::constant] + type RewardPaymentDelay: Get; + + /// NFT trait required for land and estate tokenization + type NFTTokenizationSource: NFTTrait, ClassId = ClassId, TokenId = TokenId>; + + /// Default max bound for each metaverse mapping system, this could change through proposal + type DefaultMaxBound: Get<(i32, i32)>; + + /// Network fee charged when deploying a land block or creating an estate + #[pallet::constant] + type NetworkFee: Get>; + + /// Maximum lease offers for an estate + #[pallet::constant] + type MaxOffersPerEstate: Get; + + /// Minimum lease price per block + #[pallet::constant] + type MinLeasePricePerBlock: Get>; + + /// Maximum lease period duration (in number of blocks) + #[pallet::constant] + type MaxLeasePeriod: Get; + + /// The period for each lease offer will be available for acceptance (in number of blocks) + #[pallet::constant] + type LeaseOfferExpiryPeriod: Get; + + /// Storage deposit free charged when saving data into the blockchain. + /// The fee will be unreserved after the storage is freed. + #[pallet::constant] + type StorageDepositFee: Get>; + + /// Allows converting block numbers into balance + type BlockNumberToBalance: Convert>; + } + + pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + + #[pallet::storage] + #[pallet::getter(fn all_land_units_count)] + /// Track the total number of land units + pub(super) type AllLandUnitsCount = StorageValue<_, u64, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn all_undeployed_land_unit)] + /// Track the total of undeployed land units + pub(super) type TotalUndeployedLandUnit = StorageValue<_, u64, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn get_land_units)] + /// Index land owners by metaverse ID and coordinate + pub type LandUnits = StorageDoubleMap< + _, + Twox64Concat, + MetaverseId, + Twox64Concat, + (i32, i32), + OwnerId, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn next_estate_id)] + /// Track the next estate ID + pub type NextEstateId = StorageValue<_, EstateId, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn all_estates_count)] + /// Track the total of estates + pub(super) type AllEstatesCount = StorageValue<_, u64, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn get_estates)] + /// Store estate information + pub(super) type Estates = StorageMap<_, Twox64Concat, EstateId, EstateInfo, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn get_estate_owner)] + /// Track estate owners + pub type EstateOwner = + StorageMap<_, Twox64Concat, EstateId, OwnerId, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn next_undeployed_land_block_id)] + /// Track the next undeployed land ID + pub(super) type NextUndeployedLandBlockId = StorageValue<_, UndeployedLandBlockId, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn get_undeployed_land_block)] + /// Store undeployed land blocks + pub(super) type UndeployedLandBlocks = + StorageMap<_, Blake2_128Concat, UndeployedLandBlockId, UndeployedLandBlock, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn get_undeployed_land_block_owner)] + /// Index undeployed land blocks by account ID + pub type UndeployedLandBlocksOwner = + StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, UndeployedLandBlockId, (), OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn round)] + /// Current round index and next round scheduled transition + pub type Round = StorageValue<_, RoundInfo, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn minting_rate_config)] + /// Minting rate configuration + pub type MintingRateConfig = StorageValue<_, MintingRateInfo, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn leases)] + /// Current active estate leases + pub type EstateLeases = + StorageMap<_, Twox64Concat, EstateId, LeaseContract, T::BlockNumber>, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn leasors)] + /// Current estate leasors + pub type EstateLeasors = + StorageDoubleMap<_, Blake2_128Concat, T::AccountId, Twox64Concat, EstateId, (), OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn lease_offers)] + /// Current estate lease offers + pub type EstateLeaseOffers = StorageDoubleMap< + _, + Twox64Concat, + EstateId, + Blake2_128Concat, + T::AccountId, + LeaseContract, T::BlockNumber>, + OptionQuery, + >; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub minting_rate_config: MintingRateInfo, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + minting_rate_config: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + >::put(self.minting_rate_config.clone()); + + // Start Round 1 at Block 0 + let round: RoundInfo = RoundInfo::new(1u32, 0u32.into(), T::MinBlocksPerRound::get()); + + let round_issuance_per_round = round_issuance_range::(self.minting_rate_config.clone()); + + >::put(round); + >::deposit_event(Event::NewRound( + T::BlockNumber::zero(), + 1u32, + round_issuance_per_round.max, + )); + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub (crate) fn deposit_event)] + pub enum Event { + /// New lands are minted [Beneficial Account Id, Metaverse Id, Coordinates] + NewLandsMinted(T::AccountId, MetaverseId, Vec<(i32, i32)>), + /// Land unit is transferred [Metaverse Id, Coordinates, From Account Id, To Account Id] + TransferredLandUnit(MetaverseId, (i32, i32), T::AccountId, T::AccountId), + /// Estate unit is transferred [Estate Id, From Account Id, To Account Id] + TransferredEstate(EstateId, T::AccountId, T::AccountId), + /// New land is minted [Beneficial Account Id, Metaverse Id, Coordinates] + NewLandUnitMinted(OwnerId, MetaverseId, (i32, i32)), + /// New estate is minted [Estate Id, OwnerId, Metaverse Id, Coordinates] + NewEstateMinted( + EstateId, + OwnerId, + MetaverseId, + Vec<(i32, i32)>, + ), + /// Max bound is set for a metaverse [Metaverse Id, Min and Max Coordinate] + MaxBoundSet(MetaverseId, (i32, i32)), + /// Land block is deployed [From Account Id, Metaverse Id, Undeployed Land Block Id, + /// Coordinates] + LandBlockDeployed(T::AccountId, MetaverseId, UndeployedLandBlockId, Vec<(i32, i32)>), + /// Undeployed land block is issued [Beneficial Account Id, Undeployed Land + /// Block Id] + UndeployedLandBlockIssued(T::AccountId, UndeployedLandBlockId), + /// Undeployed land block is transferred [From Account Id, To Account Id, Undeployed + /// Land Block Id] + UndeployedLandBlockTransferred(T::AccountId, T::AccountId, UndeployedLandBlockId), + /// Undeployed land block is approved [Owner Account Id, Approved Account Id, Undeployed + /// Land Block Id] + UndeployedLandBlockApproved(T::AccountId, T::AccountId, UndeployedLandBlockId), + /// Estate is destroyed [Estate Id, Owner Id] + EstateDestroyed(EstateId, OwnerId), + /// Estate is updated [Estate Id, Owner Id, Coordinates] + EstateUpdated(EstateId, OwnerId, Vec<(i32, i32)>), + /// Land unit is added to an estate [Estate Id, Owner Id, Coordinates] + LandUnitAdded(EstateId, OwnerId, Vec<(i32, i32)>), + /// Land unit is removed from an estate [Estate Id, Owner Id, Coordinates] + LandUnitsRemoved(EstateId, OwnerId, Vec<(i32, i32)>), + /// Undeployed land block is unapproved [Undeployed Land Block Id] + UndeployedLandBlockUnapproved(UndeployedLandBlockId), + /// Undeployed land block is freezed [Undeployed Land Block Id] + UndeployedLandBlockFreezed(UndeployedLandBlockId), + /// Undeployed land block is unfreezed [Undeployed Land Block Id] + UndeployedLandBlockUnfreezed(UndeployedLandBlockId), + /// Undeployed land block is burnt [Undeployed Land Block Id] + UndeployedLandBlockBurnt(UndeployedLandBlockId), + /// Estate lease offer is created [AccountId, Estate Id, Total rent] + EstateLeaseOfferCreated(T::AccountId, EstateId, BalanceOf), + /// Estate lease offer is accepted [Estate Id, Leasor account Id, Lease End Block] + EstateLeaseOfferAccepted(EstateId, T::AccountId, T::BlockNumber), + /// Estate lease offer is removed [AccountId, Estate Id] + EstateLeaseOfferRemoved(T::AccountId, EstateId), + /// Estate lease contract ended [Estate Id] + EstateLeaseContractEnded(EstateId), + /// Estate lease contract was cancelled [Estate Id] + EstateLeaseContractCancelled(EstateId), + /// Estate rent collected [EstateId, Balance] + EstateRentCollected(EstateId, BalanceOf), + /// New staking round started [Starting Block, Round, Total Land Unit] + NewRound(T::BlockNumber, RoundIndex, u64), + } + + #[pallet::error] + pub enum Error { + /// No permission + NoPermission, + /// No available estate ID + NoAvailableEstateId, + /// Insufficient fund + InsufficientFund, + /// Estate ID already exist + EstateIdAlreadyExist, + /// Land unit is not available + LandUnitIsNotAvailable, + /// Land unit is out of bound + LandUnitIsOutOfBound, + /// Undeployed land block is not found + UndeployedLandBlockNotFound, + /// Undeployed land block is not transferable + UndeployedLandBlockIsNotTransferable, + /// Undeployed land block does not hae enough land units + UndeployedLandBlockDoesNotHaveEnoughLandUnits, + /// Number of land block credit and land unit does not match + UndeployedLandBlockUnitAndInputDoesNotMatch, + /// Account is not the owner of a given undeployed land block + UndeployedLandBlockNotOwned, + /// Already own the undeployed land block + AlreadyOwnTheUndeployedLandBlock, + /// Undeployed land block is freezed + UndeployedLandBlockFreezed, + /// Undeployed land block is already freezed + UndeployedLandBlockAlreadyFreezed, + /// Undeployed land block is not frozen + UndeployedLandBlockNotFrozen, + /// Already owning the estate + AlreadyOwnTheEstate, + /// Already owning the land unit + AlreadyOwnTheLandUnit, + /// Estate is not in auction + EstateNotInAuction, + /// Land unit is not in auction + LandUnitNotInAuction, + /// Estate is already in auction + EstateAlreadyInAuction, + /// Land unit is already in auction + LandUnitAlreadyInAuction, + /// Undeployed land block is already in auction + UndeployedLandBlockAlreadyInAuction, + /// Estate is does not exist + EstateDoesNotExist, + /// Land unit does not exist + LandUnitDoesNotExist, + /// Only frozen undeployed land block can be destroyed + OnlyFrozenUndeployedLandBlockCanBeDestroyed, + /// Below minimum staking amount + BelowMinimumStake, + /// Value overflow + Overflow, + /// Estate stake is already left + EstateStakeAlreadyLeft, + /// Account has not staked anything + AccountHasNoStake, + /// Invalid owner value + InvalidOwnerValue, + /// Coordinate for estate is not valid + CoordinatesForEstateIsNotValid, + /// Insufficient balance for deploying land blocks or creating estates + InsufficientBalanceForDeployingLandOrCreatingEstate, + // Land Unit already formed in Estate + LandUnitAlreadyInEstate, + /// Estate is already leased + EstateIsAlreadyLeased, + /// Estate lease offer limit is reached + EstateLeaseOffersQueueLimitIsReached, + /// Lease offer price per block is below the minimum + LeaseOfferPriceBelowMinimum, + /// Lease offer does not exist + LeaseOfferDoesNotExist, + /// Lease offer already exists + LeaseOfferAlreadyExists, + /// Lease offer is not expired + LeaseOfferIsNotExpired, + /// Lease does not exist + LeaseDoesNotExist, + /// Lease is not expired + LeaseIsNotExpired, + /// Lease is expired + LeaseIsExpired, + /// Lease duration beyond max duration + LeaseOfferDurationAboveMaximum, + /// No unclaimed rent balance + NoUnclaimedRentLeft, + } + + // TO DO: Implement offchain removal of expired lease offers + //#[pallet::hooks] + //impl Hooks> for Pallet { + // fn offchain_worker(block_number: T::BlockNumber) { + // } + //} + + #[pallet::call] + impl Pallet { + /// Minting of a land unit, only used by council to manually mint single land for + /// beneficiary + /// + /// The dispatch origin for this call must be _Root_. + /// - `beneficiary`: the account which will be the owner of the land unit + /// - `metaverse_id`: the metaverse id that the land united will be minted on + /// - `coordinate`: coordinate of the land unit + /// + /// Emits `NewLandsMinted` if successful. + #[pallet::weight(T::WeightInfo::mint_land())] + pub fn mint_land( + origin: OriginFor, + beneficiary: T::AccountId, + metaverse_id: MetaverseId, + coordinate: (i32, i32), + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + // Mint land unit + let owner = Self::mint_land_unit( + metaverse_id, + OwnerId::Account(beneficiary.clone()), + beneficiary, + coordinate, + LandUnitStatus::NonExisting, + )?; + + // Update total land count + Self::set_total_land_unit(One::one(), false)?; + + Self::deposit_event(Event::::NewLandUnitMinted(owner, metaverse_id, coordinate)); + + Ok(().into()) + } + + /// Minting of a land units, only used by council to manually mint number of lands for + /// beneficiary + /// + /// The dispatch origin for this call must be _Root_. + /// - `beneficiary`: the account which will be the owner of the land units + /// - `metaverse_id`: the metaverse id that the land units will be minted on + /// - `coordinates`: list of land units coordinates + /// + /// Emits `NewLandsMinted` if successful. + #[pallet::weight(T::WeightInfo::mint_lands())] + pub fn mint_lands( + origin: OriginFor, + beneficiary: T::AccountId, + metaverse_id: MetaverseId, + coordinates: Vec<(i32, i32)>, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + // Mint land units + for coordinate in coordinates.clone() { + Self::mint_land_unit( + metaverse_id, + OwnerId::Account(beneficiary.clone()), + beneficiary.clone(), + coordinate, + LandUnitStatus::NonExisting, + )?; + } + + // Update total land count + Self::set_total_land_unit(coordinates.len() as u64, false)?; + + Self::deposit_event(Event::::NewLandsMinted( + beneficiary.clone(), + metaverse_id.clone(), + coordinates.clone(), + )); + + Ok(().into()) + } + + /// Mint new estate with no existing land units, only used for council to manually mint + /// estate for beneficiary + /// + /// The dispatch origin for this call must be _Root_. + /// - `beneficiary`: the account which will be the owner of the land units + /// - `metaverse_id`: the metaverse id that the land units will be minted on + /// - `coordinates`: list of land units coordinates + /// + /// Emits `NewEstateMinted` if successful. + #[pallet::weight(T::WeightInfo::mint_estate())] + pub fn mint_estate( + origin: OriginFor, + beneficiary: T::AccountId, + metaverse_id: MetaverseId, + coordinates: Vec<(i32, i32)>, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + // Generate new estate id + let new_estate_id = Self::get_new_estate_id()?; + + // Generate sub account from estate + let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(new_estate_id); + + // Estate as owner + let token_properties = Self::get_estate_token_properties(metaverse_id, new_estate_id); + let class_id = T::MetaverseInfoSource::get_metaverse_estate_class(metaverse_id)?; + let token_id: TokenId = + T::NFTTokenizationSource::mint_token(&beneficiary, class_id, token_properties.0, token_properties.1)?; + let token_owner = OwnerId::Token(class_id, token_id); + + // Mint land units + for coordinate in coordinates.clone() { + Self::mint_land_unit( + metaverse_id, + token_owner.clone(), + estate_account_id.clone(), + coordinate, + LandUnitStatus::NonExistingWithEstate, + )?; + } + // Update total land count + Self::set_total_land_unit(coordinates.len() as u64, false)?; + + // Update estate information + Self::update_estate_information(new_estate_id, metaverse_id, token_owner, coordinates)?; + Ok(().into()) + } + + /// Transferring a land unit if it is not already in auction + /// + /// The dispatch origin for this call must be _Signed_. + /// Only the owner of a land can make this call. + /// - `to`: the account which will be the owner of the land units + /// - `metaverse_id`: the metaverse id of the land unit + /// - `coordinate`: the coordinate of the land unit + /// + /// Emits `TransferredLandUnit` if successful. + #[pallet::weight(T::WeightInfo::transfer_land())] + pub fn transfer_land( + origin: OriginFor, + to: T::AccountId, + metaverse_id: MetaverseId, + coordinate: (i32, i32), + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + Self::do_transfer_landunit(coordinate, &who, &to, metaverse_id)?; + Ok(().into()) + } + + /// Create new estate from existing land units + /// + /// The dispatch origin for this call must be _Signed_. + /// - `metaverse_id`: the metaverse id that the land units will be minted on + /// - `coordinates`: list of land units coordinates + /// + /// Emits `NewEstateMinted` if successful. + #[pallet::weight(T::WeightInfo::create_estate().saturating_mul(coordinates.len() as u64))] + #[transactional] + pub fn create_estate( + origin: OriginFor, + metaverse_id: MetaverseId, + coordinates: Vec<(i32, i32)>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + ensure!( + Self::verify_land_unit_for_estate(coordinates.clone()), + Error::::CoordinatesForEstateIsNotValid + ); + // Collect network fee + Self::collect_network_fee(&who)?; + // Generate new estate id + let new_estate_id = Self::get_new_estate_id()?; + // Generate sub account from estate + let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(new_estate_id); + + let token_properties = Self::get_estate_token_properties(metaverse_id, new_estate_id); + let class_id = T::MetaverseInfoSource::get_metaverse_estate_class(metaverse_id)?; + let token_id: TokenId = + T::NFTTokenizationSource::mint_token(&who, class_id, token_properties.0, token_properties.1)?; + let beneficiary = OwnerId::Token(class_id, token_id); + + let storage_fee: BalanceOf = + Perbill::from_percent(100u32.saturating_mul(coordinates.len() as u32)) * T::StorageDepositFee::get(); + T::Currency::transfer( + &who, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + + // Mint land units + for coordinate in coordinates.clone() { + Self::mint_land_unit( + metaverse_id, + beneficiary.clone(), + estate_account_id.clone(), + coordinate, + LandUnitStatus::Existing(who.clone()), + )?; + } + + // Update estate information + Self::update_estate_information(new_estate_id, metaverse_id, beneficiary, coordinates.clone())?; + + Ok(().into()) + } + + /// Transfer estate ownership if it is not in auction. + /// + /// The dispatch origin for this call must be _Signed_. + /// Only the owner of an estate can make this call. + /// - `to`: the account which will be the owner of the estate + /// - `estate_id`: the estate ID of the the estate that will be transferred + /// + /// Emits `TransferredEstate` if successful. + #[pallet::weight(T::WeightInfo::transfer_estate())] + #[transactional] + pub fn transfer_estate( + origin: OriginFor, + to: T::AccountId, + estate_id: EstateId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + Self::do_transfer_estate(estate_id, &who, &to)?; + Ok(().into()) + } + + /// Deploy raw land block to metaverse and turn raw land block to land unit with given + /// coordinates + /// + /// The dispatch origin for this call must be _Signed_. + /// Only the undeployed land block owner can make this call. + /// - `undeployed_land_block_id`: the undeployed land block ID + /// - `metaverse_id`: the metaverse ID that the land block will be deployed on + /// - `land_block_coordinates`: the coordinates of the land block + /// - `coordinates`: list of land units coordinates + /// + /// Emits `LandBlockDeployed` if successful. + #[pallet::weight(T::WeightInfo::deploy_land_block() * coordinates.len() as u64)] + #[transactional] + pub fn deploy_land_block( + origin: OriginFor, + undeployed_land_block_id: UndeployedLandBlockId, + metaverse_id: MetaverseId, + land_block_coordinate: (i32, i32), + coordinates: Vec<(i32, i32)>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + ensure!( + T::MetaverseInfoSource::check_ownership(&who, &metaverse_id), + Error::::NoPermission + ); + + // Ensure the max bound is set for the metaverse + let max_bound = T::DefaultMaxBound::get(); + + // Check whether the coordinate is within the bound + ensure!( + (land_block_coordinate.0 >= max_bound.0 && max_bound.1 >= land_block_coordinate.0) + && (land_block_coordinate.1 >= max_bound.0 && max_bound.1 >= land_block_coordinate.1), + Error::::LandUnitIsOutOfBound + ); + + ensure!( + Self::verify_land_unit_in_bound(&land_block_coordinate, &coordinates), + Error::::LandUnitIsOutOfBound + ); + + let undeployed_land_block_record = UndeployedLandBlocks::::get(undeployed_land_block_id) + .ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + undeployed_land_block_record.owner == who.clone(), + Error::::NoPermission + ); + + ensure!( + undeployed_land_block_record.is_locked == false, + Error::::UndeployedLandBlockFreezed + ); + + let land_units_to_mint = coordinates.len() as u32; + + // Ensure undeployed land block only deployed once + ensure!( + undeployed_land_block_record.number_land_units == land_units_to_mint, + Error::::UndeployedLandBlockUnitAndInputDoesNotMatch + ); + + // Collect network fee + Self::collect_network_fee(&who)?; + + // Mint land units + for coordinate in coordinates.clone() { + Self::mint_land_unit( + metaverse_id, + OwnerId::Account(who.clone()), + who.clone(), + coordinate, + LandUnitStatus::NonExisting, + )?; + } + + // Update total land count + Self::set_total_land_unit(coordinates.len() as u64, false)?; + + // Burn undeployed land block + Self::do_burn_undeployed_land_block(undeployed_land_block_id)?; + + Self::deposit_event(Event::::LandBlockDeployed( + who.clone(), + metaverse_id, + undeployed_land_block_id, + coordinates, + )); + + Ok(().into()) + } + + /// Issues new undeployed land block(s) + /// + /// The dispatch origin for this call must be _Root_. + /// - `beneficiary`: the account which will be the owner of the undeployed land block(s) + /// - `number_of_land_block`: the number of undeployed land block(s) that will be created + /// - `number_land_units_per_land_block`: the number of land units in each undeployed land + /// block + /// - `land_block_coordinates`: the coordinates of the undeployed land block + /// + /// Emits `UndeployedLandBlockIssued` if successful. + #[pallet::weight(T::WeightInfo::issue_undeployed_land_blocks())] + #[transactional] + pub fn issue_undeployed_land_blocks( + who: OriginFor, + beneficiary: T::AccountId, + number_of_land_block: u32, + number_land_units_per_land_block: u32, + undeployed_land_block_type: UndeployedLandBlockType, + ) -> DispatchResultWithPostInfo { + ensure_root(who)?; + + Self::do_issue_undeployed_land_blocks( + &beneficiary, + number_of_land_block, + number_land_units_per_land_block, + undeployed_land_block_type, + )?; + + Ok(().into()) + } + + /// Freezes undeployed land block which is not already frozen + /// + /// The dispatch origin for this call must be _Root_. + /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be freezed + /// + /// Emits `UndeployedLandBlockFreezed` if successful. + #[pallet::weight(T::WeightInfo::freeze_undeployed_land_blocks())] + #[transactional] + pub fn freeze_undeployed_land_blocks( + origin: OriginFor, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + Self::do_freeze_undeployed_land_block(undeployed_land_block_id)?; + + Ok(().into()) + } + + /// Unfreezes undeployed land block which is frozen. + /// + /// The dispatch origin for this call must be _Root_. + /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be unfreezed + /// + /// Emits `UndeployedLandBlockUnfreezed` if successful. + #[pallet::weight(T::WeightInfo::unfreeze_undeployed_land_blocks())] + #[transactional] + pub fn unfreeze_undeployed_land_blocks( + origin: OriginFor, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + UndeployedLandBlocks::::try_mutate_exists( + &undeployed_land_block_id, + |undeployed_land_block| -> DispatchResultWithPostInfo { + let undeployed_land_block_record = undeployed_land_block + .as_mut() + .ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock( + undeployed_land_block_id + )), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + ensure!( + undeployed_land_block_record.is_locked == true, + Error::::UndeployedLandBlockNotFrozen + ); + + undeployed_land_block_record.is_locked = false; + + Self::deposit_event(Event::::UndeployedLandBlockUnfreezed(undeployed_land_block_id)); + + Ok(().into()) + }, + ) + } + + /// Transfer undeployed land block owner if it is not in auction. + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the undeployed land block owner can make this call. + /// - `to`: the account that will receive the undeployed land block + /// - `undeployed_land_block_id`: the ID of the land block that will be transferred + /// + /// Emits `UndeployedLandBlockTransferred` if successful. + #[pallet::weight(T::WeightInfo::transfer_undeployed_land_blocks())] + #[transactional] + pub fn transfer_undeployed_land_blocks( + origin: OriginFor, + to: T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + Self::do_transfer_undeployed_land_block(&who, &to, undeployed_land_block_id)?; + + Ok(().into()) + } + + /// Burn raw land block that will reduce total supply + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the undeployed land block owner can make this call. + /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be burned + /// + /// Emits `UndeployedLandBlockBurnt` if successful. + #[pallet::weight(T::WeightInfo::burn_undeployed_land_blocks())] + #[transactional] + pub fn burn_undeployed_land_blocks( + origin: OriginFor, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + Self::do_burn_undeployed_land_block(undeployed_land_block_id)?; + + Ok(().into()) + } + + /// Approve existing undeployed land block which is not frozen. + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the undeployed land block owner can make this call. + /// - `to`: the account for which the undeployed land block will be approved + /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be burned + /// + /// Emits `UndeployedLandBlockApproved` if successful + #[pallet::weight(T::WeightInfo::approve_undeployed_land_blocks())] + #[transactional] + pub fn approve_undeployed_land_blocks( + origin: OriginFor, + to: T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + UndeployedLandBlocks::::try_mutate_exists( + &undeployed_land_block_id, + |undeployed_land_block| -> DispatchResultWithPostInfo { + let mut undeployed_land_block_record = undeployed_land_block + .as_mut() + .ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + undeployed_land_block_record.owner == who.clone(), + Error::::NoPermission + ); + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock( + undeployed_land_block_id + )), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + ensure!( + undeployed_land_block_record.is_locked == false, + Error::::UndeployedLandBlockAlreadyFreezed + ); + + undeployed_land_block_record.approved = Some(to.clone()); + + Self::deposit_event(Event::::UndeployedLandBlockApproved( + who.clone(), + to.clone(), + undeployed_land_block_id.clone(), + )); + + Ok(().into()) + }, + ) + } + + /// Unapprove existing undeployed land block which is not frozen. + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the undeployed land block owner can make this call. + /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be + /// unapproved + /// + /// Emits `UndeployedLandBlockUnapproved` if successful + #[pallet::weight(T::WeightInfo::unapprove_undeployed_land_blocks())] + #[transactional] + pub fn unapprove_undeployed_land_blocks( + origin: OriginFor, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + UndeployedLandBlocks::::try_mutate_exists( + &undeployed_land_block_id, + |undeployed_land_block| -> DispatchResultWithPostInfo { + let mut undeployed_land_block_record = undeployed_land_block + .as_mut() + .ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + undeployed_land_block_record.owner == who.clone(), + Error::::NoPermission + ); + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock( + undeployed_land_block_id + )), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + ensure!( + undeployed_land_block_record.is_locked == false, + Error::::UndeployedLandBlockAlreadyFreezed + ); + + undeployed_land_block_record.approved = None; + + Self::deposit_event(Event::::UndeployedLandBlockUnapproved( + undeployed_land_block_id.clone(), + )); + + Ok(().into()) + }, + ) + } + + /// Dissolve estate to land units if it is not in auction. + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the estate owner can make this call. + /// - `estate_id`: the ID of the estate that will be dissolved + /// + /// Emits `EstateDestroyed` if successful + #[pallet::weight(T::WeightInfo::dissolve_estate())] + #[transactional] + pub fn dissolve_estate(origin: OriginFor, estate_id: EstateId) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; + match estate_owner_value { + OwnerId::Token(c, t) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(c, t)), + Error::::EstateAlreadyInAuction + ); + //ensure there is record of the estate owner with estate id and account id + ensure!( + Self::check_if_land_or_estate_owner(&who, &estate_owner_value), + Error::::NoPermission + ); + let estate_info = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; + EstateOwner::::try_mutate_exists(&estate_id, |estate_owner| { + // Reset estate ownership + match estate_owner_value { + OwnerId::Token(class_id, token_id) => { + T::NFTTokenizationSource::burn_nft(&who, &(class_id, token_id)); + *estate_owner = None; + } + OwnerId::Account(ref a) => { + *estate_owner = None; + } + } + + // Remove estate + Estates::::remove(&estate_id); + + // Update total estates + let total_estates_count = Self::all_estates_count(); + let new_total_estates_count = total_estates_count + .checked_sub(One::one()) + .ok_or("Overflow adding new count to total estates")?; + AllEstatesCount::::put(new_total_estates_count); + + // Mint new land tokens to replace the lands in the dissolved estate + let estate_account_id: T::AccountId = + T::LandTreasury::get().into_sub_account_truncating(estate_id); + let storage_fee: BalanceOf = + Perbill::from_percent(100u32.saturating_mul(estate_info.land_units.len() as u32)) + * T::StorageDepositFee::get(); + T::Currency::transfer( + &who, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + for land_unit in estate_info.land_units { + // Transfer land unit from treasury to estate owner + Self::mint_land_unit( + estate_info.metaverse_id, + estate_owner_value.clone(), + who.clone(), + land_unit, + LandUnitStatus::RemovedFromEstate, + )?; + } + + Self::deposit_event(Event::::EstateDestroyed( + estate_id.clone(), + estate_owner_value.clone(), + )); + + Ok(().into()) + }) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Add more land units to existing estate that is not in auction + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the estate owner can make this call. + /// They must also own the land units. + /// - `estate_id`: the ID of the estate that the land units will be added to + /// - `land_units`: list of land unit coordinates that will be added to estate + /// + /// Emits `LandUnitAdded` if successful + #[pallet::weight(T::WeightInfo::add_land_unit_to_estate())] + #[transactional] + pub fn add_land_unit_to_estate( + origin: OriginFor, + estate_id: EstateId, + land_units: Vec<(i32, i32)>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; + match estate_owner_value { + OwnerId::Token(c, t) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(c, t)), + Error::::EstateAlreadyInAuction + ); + ensure!( + Self::check_if_land_or_estate_owner(&who, &estate_owner_value), + Error::::NoPermission + ); + + let estate_info: EstateInfo = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; + let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(estate_id); + + let storage_fee: BalanceOf = + Perbill::from_percent(100u32.saturating_mul(land_units.len() as u32)) + * T::StorageDepositFee::get(); + T::Currency::transfer( + &who, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + + // Check land unit ownership + for land_unit in land_units.clone() { + let metaverse_land_unit = Self::get_land_units(estate_info.metaverse_id, land_unit) + .ok_or(Error::::UndeployedLandBlockNotFound)?; + ensure!( + Self::check_if_land_or_estate_owner(&who, &metaverse_land_unit,), + Error::::LandUnitDoesNotExist + ); + + // Mint land unit + Self::mint_land_unit( + estate_info.metaverse_id, + estate_owner_value.clone(), + estate_account_id.clone(), + land_unit, + LandUnitStatus::Existing(who.clone()), + )?; + } + + // Mutate estates + Estates::::try_mutate_exists(&estate_id, |maybe_estate_info| { + // Append new coordinates to estate + let mut_estate_info = maybe_estate_info.as_mut().ok_or(Error::::EstateDoesNotExist)?; + mut_estate_info.land_units.append(&mut land_units.clone()); + + Self::deposit_event(Event::::LandUnitAdded( + estate_id.clone(), + estate_owner_value.clone(), + land_units.clone(), + )); + + Ok(().into()) + }) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Remove land units from existing estate if it is not in auction. + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the estate owner can make this call. + /// - `estate_id`: the ID of the estate that the land units will be removed from + /// - `land_units`: list of land unit coordinates that will be added to estate + /// + /// Emits `LandUnitsRemoved` if successful + #[pallet::weight(T::WeightInfo::remove_land_unit_from_estate())] + #[transactional] + pub fn remove_land_unit_from_estate( + origin: OriginFor, + estate_id: EstateId, + land_units: Vec<(i32, i32)>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; + match estate_owner_value { + OwnerId::Token(c, t) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(c, t)), + Error::::EstateAlreadyInAuction + ); + ensure!( + Self::check_if_land_or_estate_owner(&who, &estate_owner_value), + Error::::NoPermission + ); + let estate_info: EstateInfo = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; + let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(estate_id); + + // Mutate estates + Estates::::try_mutate_exists(&estate_id, |maybe_estate_info| { + let mut mut_estate_info = maybe_estate_info.as_mut().ok_or(Error::::EstateDoesNotExist)?; + + let storage_fee: BalanceOf = + Perbill::from_percent(100u32.saturating_mul(land_units.len() as u32)) + * T::StorageDepositFee::get(); + T::Currency::transfer( + &who, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + // Mutate land unit ownership + for land_unit in land_units.clone() { + // Transfer land unit from treasury to estate owner + Self::mint_land_unit( + estate_info.metaverse_id, + estate_owner_value.clone(), + who.clone(), + land_unit, + LandUnitStatus::RemovedFromEstate, + )?; + // Remove coordinates from estate + let index = mut_estate_info + .land_units + .iter() + .position(|x| *x == land_unit) + .ok_or(Error::::LandUnitIsNotAvailable)?; + mut_estate_info.land_units.remove(index); + } + + Self::deposit_event(Event::::LandUnitsRemoved( + estate_id.clone(), + estate_owner_value.clone(), + land_units.clone(), + )); + + Ok(().into()) + }) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Create a lease offer for estate that is not leased + /// + /// The dispatch origin for this call must be _Singed_. + /// Only origin that is not the estate owner can make this call. + /// - `estate_id`: the ID of the estate that will be leased + /// - `price_per_block`: lease price per block + /// - `duration`: lease duration (in number of blocks) + /// + /// Emits `EstateLeaseOfferCreated` if successful + #[pallet::weight(T::WeightInfo::create_lease_offer())] + #[transactional] + pub fn create_lease_offer( + origin: OriginFor, + estate_id: EstateId, + price_per_block: BalanceOf, + duration: u32, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; + ensure!( + !Self::check_if_land_or_estate_owner(&who, &estate_owner_value), + Error::::NoPermission + ); + ensure!( + !EstateLeaseOffers::::contains_key(estate_id, who.clone()), + Error::::LeaseOfferAlreadyExists + ); + ensure!( + !EstateLeases::::contains_key(estate_id), + Error::::EstateIsAlreadyLeased + ); + ensure!( + price_per_block >= T::MinLeasePricePerBlock::get(), + Error::::LeaseOfferPriceBelowMinimum + ); + ensure!( + duration <= T::MaxLeasePeriod::get(), + Error::::LeaseOfferDurationAboveMaximum + ); + + match estate_owner_value { + OwnerId::Token(class_id, token_id) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(class_id, token_id)), + Error::::EstateAlreadyInAuction + ); + ensure!( + EstateLeaseOffers::::iter_key_prefix(estate_id).count() as u32 + <= T::MaxOffersPerEstate::get(), + Error::::EstateLeaseOffersQueueLimitIsReached + ); + + let current_block_number = >::block_number(); + let end_block = current_block_number + T::LeaseOfferExpiryPeriod::get().into(); + let unclaimed_rent: BalanceOf = price_per_block * duration.into(); + + let lease_offer = LeaseContract { + price_per_block, + duration, + end_block, + start_block: end_block + 1u32.into(), + unclaimed_rent, + }; + + EstateLeaseOffers::::insert(estate_id, who.clone(), lease_offer); + T::Currency::reserve(&who, unclaimed_rent); + + Self::deposit_event(Event::::EstateLeaseOfferCreated(who, estate_id, unclaimed_rent)); + + Ok(().into()) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Accept lease offer for estate that is not leased + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the estate owner can make this call. + /// - `estate_id`: the ID of the estate that will be leased + /// - `recipient`: the account that made the lease offer + /// + /// Emits `EstateLeaseOfferAccepted` if successful + #[pallet::weight(T::WeightInfo::accept_lease_offer())] + #[transactional] + pub fn accept_lease_offer( + origin: OriginFor, + estate_id: EstateId, + recipient: T::AccountId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!( + !EstateLeases::::contains_key(estate_id), + Error::::EstateIsAlreadyLeased + ); + + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; + match estate_owner_value { + OwnerId::Token(class_id, token_id) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(class_id, token_id)), + Error::::EstateAlreadyInAuction + ); + ensure!( + Self::check_if_land_or_estate_owner(&who, &estate_owner_value), + Error::::NoPermission + ); + + let mut lease = + Self::lease_offers(estate_id, recipient.clone()).ok_or(Error::::LeaseOfferDoesNotExist)?; + + lease.start_block = >::block_number(); + lease.end_block = lease.start_block + lease.duration.into(); + // 200% storage fee since there are 2 storage inserts + let storage_fee: BalanceOf = Perbill::from_percent(200) * T::StorageDepositFee::get(); + T::Currency::transfer( + &who, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + + EstateLeaseOffers::::remove_prefix(estate_id, None); + EstateLeases::::insert(estate_id, lease.clone()); + EstateLeasors::::insert(recipient.clone(), estate_id, ()); + T::NFTTokenizationSource::set_lock_nft((class_id, token_id), true)?; + + Self::deposit_event(Event::::EstateLeaseOfferAccepted( + estate_id, + recipient.clone(), + lease.end_block, + )); + + Ok(().into()) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Cancels existing lease + /// + /// The dispatch origin for this call must be _Root_. + /// - `estate_id`: the ID of the estate that will be leased + /// - `leasor`: the account that is leasing the estate + /// + /// Emits `EstateLeaseContractCancelled` if successful + #[pallet::weight(T::WeightInfo::cancel_lease())] + #[transactional] + pub fn cancel_lease( + origin: OriginFor, + estate_owner: T::AccountId, + estate_id: EstateId, + leasor: T::AccountId, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let lease = Self::leases(estate_id).ok_or(Error::::LeaseDoesNotExist)?; + ensure!( + EstateLeasors::::contains_key(leasor.clone(), estate_id), + Error::::LeaseDoesNotExist + ); + ensure!( + lease.end_block > >::block_number(), + Error::::LeaseIsExpired + ); + + let total_rent: BalanceOf = lease.price_per_block * lease.duration.into(); + let rent_period = >::block_number() - lease.start_block; + let rent_claim_amount = lease.price_per_block * T::BlockNumberToBalance::convert(rent_period) + + lease.unclaimed_rent + - total_rent; + + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; + match estate_owner_value { + OwnerId::Token(class_id, token_id) => { + //let estate_owner = T::NFTTokenizationSource::get_asset_owner(&(class_id, token_id))?; + ensure!( + Self::check_if_land_or_estate_owner(&estate_owner, &estate_owner_value), + Error::::NoPermission + ); + T::Currency::unreserve(&leasor, lease.unclaimed_rent.into()); + ::Currency::transfer( + &leasor, + &estate_owner, + rent_claim_amount, + ExistenceRequirement::KeepAlive, + )?; + + EstateLeasors::::remove(leasor.clone(), estate_id); + EstateLeases::::remove(estate_id); + T::NFTTokenizationSource::set_lock_nft((class_id, token_id), false)?; + + Self::deposit_event(Event::::EstateLeaseContractCancelled(estate_id)); + Ok(().into()) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Removes expired lease + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the estate owner can make this call. + /// - `estate_id`: the ID of the estate that will be leased + /// - `leasor`: the account that is leasing the estate + /// + /// Emits `EstateLeaseContractEnded` if successful + #[pallet::weight(T::WeightInfo::remove_expired_lease())] + #[transactional] + pub fn remove_expired_lease( + origin: OriginFor, + estate_id: EstateId, + leasor: T::AccountId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let lease = Self::leases(estate_id).ok_or(Error::::LeaseDoesNotExist)?; + ensure!( + EstateLeasors::::contains_key(leasor.clone(), estate_id), + Error::::LeaseDoesNotExist + ); + ensure!( + lease.end_block <= >::block_number(), + Error::::LeaseIsNotExpired + ); + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; + match estate_owner_value { + OwnerId::Token(class_id, token_id) => { + ensure!( + Self::check_if_land_or_estate_owner(&who, &estate_owner_value), + Error::::NoPermission + ); + + T::Currency::unreserve(&leasor, lease.unclaimed_rent.into()); + ::Currency::transfer( + &leasor, + &who, + lease.unclaimed_rent.into(), + ExistenceRequirement::KeepAlive, + )?; + + EstateLeasors::::remove(leasor, estate_id); + EstateLeases::::remove(estate_id); + T::NFTTokenizationSource::set_lock_nft((class_id, token_id), false)?; + + Self::deposit_event(Event::::EstateLeaseContractEnded(estate_id)); + Ok(().into()) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + + /// Removes lease offer + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the account made the lease offer can make this call. + /// - `estate_id`: the ID of the estate that will be leased + /// + /// Emits `EstateLeaseOfferRemoved` if successful + #[pallet::weight(T::WeightInfo::remove_lease_offer())] + #[transactional] + pub fn remove_lease_offer(origin: OriginFor, estate_id: EstateId) -> DispatchResultWithPostInfo { + let leasor = ensure_signed(origin)?; + let lease_offer = + Self::lease_offers(estate_id, leasor.clone()).ok_or(Error::::LeaseOfferDoesNotExist)?; + EstateLeaseOffers::::remove(estate_id, leasor.clone()); + T::Currency::unreserve(&leasor, lease_offer.unclaimed_rent.into()); + Self::deposit_event(Event::::EstateLeaseOfferRemoved(leasor, estate_id)); + Ok(().into()) + } + + /// Collect rent for a leased estate + /// + /// The dispatch origin for this call must be _Singed_. + /// Only the estate owner can make this call. + /// - `estate_id`: the ID of the estate that will be leased + /// + /// Emits `EstateRentCollected` if successful + #[pallet::weight(T::WeightInfo::collect_rent())] + #[transactional] + pub fn collect_rent( + origin: OriginFor, + estate_id: EstateId, + leasor: T::AccountId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!( + Self::check_estate_ownership(who.clone(), estate_id)?, + Error::::NoPermission + ); + ensure!( + EstateLeasors::::contains_key(leasor.clone(), estate_id), + Error::::LeaseDoesNotExist + ); + let current_block = >::block_number(); + EstateLeases::::try_mutate_exists(&estate_id, |estate_lease_value| { + let mut lease = estate_lease_value.as_mut().ok_or(Error::::LeaseDoesNotExist)?; + + ensure!(lease.end_block > current_block, Error::::LeaseIsExpired); + + let total_rent: BalanceOf = lease.price_per_block * lease.duration.into(); + let rent_period = >::block_number() - lease.start_block; + let rent_claim_amount = lease.price_per_block * T::BlockNumberToBalance::convert(rent_period) + + lease.unclaimed_rent + - total_rent; + + ::Currency::unreserve(&leasor, rent_claim_amount); + ::Currency::transfer( + &leasor, + &who, + rent_claim_amount.into(), + ExistenceRequirement::KeepAlive, + )?; + + lease.unclaimed_rent -= rent_claim_amount; + + Self::deposit_event(Event::::EstateRentCollected(estate_id, rent_claim_amount.into())); + Ok(().into()) + }) + } + } +} + +impl Pallet { + /// Internal getter for new estate ID + fn get_new_estate_id() -> Result { + let estate_id = NextEstateId::::try_mutate(|id| -> Result { + let current_id = *id; + *id = id.checked_add(One::one()).ok_or(Error::::NoAvailableEstateId)?; + Ok(current_id) + })?; + Ok(estate_id) + } + + /// Internal minting of land unit + fn mint_land_unit( + metaverse_id: MetaverseId, + token_owner: OwnerId, + to: T::AccountId, + coordinate: (i32, i32), + land_unit_status: LandUnitStatus, + ) -> Result, DispatchError> { + let mut owner = token_owner.clone(); + + match land_unit_status { + // Use case - create new estate. + LandUnitStatus::Existing(a) => { + ensure!( + LandUnits::::contains_key(metaverse_id, coordinate), + Error::::LandUnitIsNotAvailable + ); + + let existing_owner_value = Self::get_land_units(metaverse_id, coordinate); + match existing_owner_value { + Some(owner_value) => match owner_value { + OwnerId::Token(class_id, token_id) => { + // Implement check if user own nft + ensure!( + T::NFTTokenizationSource::check_ownership(&a, &(class_id, token_id))?, + Error::::NoPermission + ); + + if let OwnerId::Token(owner_class_id, owner_token_id) = token_owner { + ensure!(owner_class_id != class_id, Error::::LandUnitAlreadyInEstate) + } + + // Ensure not locked + T::NFTTokenizationSource::set_lock_nft((class_id, token_id), false)?; + T::NFTTokenizationSource::burn_nft(&a, &(class_id, token_id)); + + LandUnits::::insert(metaverse_id, coordinate, token_owner.clone()); + } + _ => (), + }, + /* It doesn't make sense to mint existing land unit when ownership doesn't exists */ + _ => (), + } + } + LandUnitStatus::NonExisting => { + ensure!( + !LandUnits::::contains_key(metaverse_id, coordinate), + Error::::LandUnitIsNotAvailable + ); + + let token_properties = Self::get_land_token_properties(metaverse_id, coordinate); + let class_id = T::MetaverseInfoSource::get_metaverse_land_class(metaverse_id)?; + let token_id = + T::NFTTokenizationSource::mint_token(&to, class_id, token_properties.0, token_properties.1)?; + owner = OwnerId::Token(class_id, token_id); + LandUnits::::insert(metaverse_id, coordinate, OwnerId::Token(class_id, token_id.clone())); + } + LandUnitStatus::NonExistingWithEstate => { + ensure!( + !LandUnits::::contains_key(metaverse_id, coordinate), + Error::::LandUnitIsNotAvailable + ); + + owner = token_owner.clone(); + + LandUnits::::insert(metaverse_id, coordinate, token_owner.clone()); + } + LandUnitStatus::RemovedFromEstate => { + ensure!( + LandUnits::::contains_key(metaverse_id, coordinate), + Error::::LandUnitIsNotAvailable + ); + + let token_properties = Self::get_land_token_properties(metaverse_id, coordinate); + let class_id = T::MetaverseInfoSource::get_metaverse_land_class(metaverse_id)?; + let token_id = + T::NFTTokenizationSource::mint_token(&to, class_id, token_properties.0, token_properties.1)?; + owner = OwnerId::Token(class_id, token_id); + LandUnits::::remove(metaverse_id, coordinate); + LandUnits::::insert(metaverse_id, coordinate, OwnerId::Token(class_id, token_id)); + } + } + Ok(owner) + } + + /// Internal updating information about an estate + fn update_estate_information( + new_estate_id: EstateId, + metaverse_id: MetaverseId, + estate_owner: OwnerId, + coordinates: Vec<(i32, i32)>, + ) -> DispatchResult { + // Update total estates + let total_estates_count = Self::all_estates_count(); + let new_total_estates_count = total_estates_count + .checked_add(One::one()) + .ok_or("Overflow adding new count to total estates")?; + AllEstatesCount::::put(new_total_estates_count); + + // Update estates + let estate_info = EstateInfo { + metaverse_id, + land_units: coordinates.clone(), + }; + + Estates::::insert(new_estate_id, estate_info); + EstateOwner::::insert(new_estate_id, estate_owner.clone()); + + Self::deposit_event(Event::::NewEstateMinted( + new_estate_id.clone(), + estate_owner, + metaverse_id, + coordinates.clone(), + )); + + Ok(()) + } + + /// Internal getter of new undeployed land block ID + fn get_new_undeployed_land_block_id() -> Result { + let undeployed_land_block_id = + NextUndeployedLandBlockId::::try_mutate(|id| -> Result { + let current_id = *id; + *id = id.checked_add(One::one()).ok_or(Error::::NoAvailableEstateId)?; + Ok(current_id) + })?; + Ok(undeployed_land_block_id) + } + + /// Internal transfer of undeployed land block + fn do_transfer_undeployed_land_block( + who: &T::AccountId, + to: &T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + // 200% storage fee since there are 2 storage inserts + let storage_fee: BalanceOf = Perbill::from_percent(200) * T::StorageDepositFee::get(); + T::Currency::transfer( + &who, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + + UndeployedLandBlocks::::try_mutate( + &undeployed_land_block_id, + |undeployed_land_block| -> Result { + let undeployed_land_block_record = undeployed_land_block + .as_mut() + .ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + undeployed_land_block_record.owner == who.clone(), + Error::::NoPermission + ); + + ensure!( + undeployed_land_block_record.is_locked == false, + Error::::UndeployedLandBlockAlreadyFreezed + ); + + ensure!( + undeployed_land_block_record.undeployed_land_block_type == UndeployedLandBlockType::Transferable, + Error::::UndeployedLandBlockIsNotTransferable + ); + + undeployed_land_block_record.owner = to.clone(); + + UndeployedLandBlocksOwner::::remove(who.clone(), &undeployed_land_block_id); + UndeployedLandBlocksOwner::::insert(to.clone(), &undeployed_land_block_id, ()); + + Self::deposit_event(Event::::UndeployedLandBlockTransferred( + who.clone(), + to.clone(), + undeployed_land_block_id.clone(), + )); + + Ok(undeployed_land_block_id) + }, + ) + } + + /// Internal burn of undeployed land block + fn do_burn_undeployed_land_block( + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + let undeployed_land_block_info = + UndeployedLandBlocks::::get(undeployed_land_block_id).ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + ensure!( + !undeployed_land_block_info.is_locked, + Error::::OnlyFrozenUndeployedLandBlockCanBeDestroyed + ); + + Self::set_total_undeployed_land_unit(undeployed_land_block_info.number_land_units as u64, true)?; + UndeployedLandBlocksOwner::::remove(undeployed_land_block_info.owner, &undeployed_land_block_id); + UndeployedLandBlocks::::remove(&undeployed_land_block_id); + + Self::deposit_event(Event::::UndeployedLandBlockBurnt(undeployed_land_block_id.clone())); + + Ok(undeployed_land_block_id) + } + + /// Internal freeze of undeployed land block + fn do_freeze_undeployed_land_block( + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + UndeployedLandBlocks::::try_mutate_exists( + &undeployed_land_block_id, + |undeployed_land_block| -> Result { + let undeployed_land_block_record = undeployed_land_block + .as_mut() + .ok_or(Error::::UndeployedLandBlockNotFound)?; + + ensure!( + undeployed_land_block_record.is_locked == false, + Error::::UndeployedLandBlockAlreadyFreezed + ); + + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), + Error::::UndeployedLandBlockAlreadyInAuction + ); + + undeployed_land_block_record.is_locked = true; + + Self::deposit_event(Event::::UndeployedLandBlockFreezed(undeployed_land_block_id)); + + Ok(undeployed_land_block_id) + }, + ) + } + + /// Internal issue of undeployed land block + fn do_issue_undeployed_land_blocks( + beneficiary: &T::AccountId, + number_of_land_block: u32, + number_land_units_per_land_block: u32, + undeployed_land_block_type: UndeployedLandBlockType, + ) -> Result, DispatchError> { + let mut undeployed_land_block_ids: Vec = Vec::new(); + + // 2 inserts per land blocks + let storage_fee: BalanceOf = + Perbill::from_percent(number_of_land_block.saturating_mul(2).saturating_mul(100)) + * T::StorageDepositFee::get(); + + T::Currency::transfer( + beneficiary, + &T::MetaverseInfoSource::get_network_treasury(), + storage_fee.saturated_into(), + ExistenceRequirement::KeepAlive, + )?; + + for _ in 0..number_of_land_block { + let new_undeployed_land_block_id = Self::get_new_undeployed_land_block_id()?; + + let undeployed_land_block = UndeployedLandBlock { + id: new_undeployed_land_block_id, + number_land_units: number_land_units_per_land_block, + undeployed_land_block_type, + approved: None, + is_locked: false, + owner: beneficiary.clone(), + }; + + UndeployedLandBlocks::::insert(new_undeployed_land_block_id, undeployed_land_block); + UndeployedLandBlocksOwner::::insert(beneficiary.clone(), new_undeployed_land_block_id, ()); + + // Update total undeployed land count + Self::set_total_undeployed_land_unit(number_land_units_per_land_block as u64, false)?; + + Self::deposit_event(Event::::UndeployedLandBlockIssued( + beneficiary.clone(), + new_undeployed_land_block_id.clone(), + )); + + undeployed_land_block_ids.push(new_undeployed_land_block_id); + } + + Ok(undeployed_land_block_ids) + } + + /// Internal transfer of estate + fn do_transfer_estate( + estate_id: EstateId, + from: &T::AccountId, + to: &T::AccountId, + ) -> Result { + EstateOwner::::try_mutate_exists(&estate_id, |estate_owner| -> Result { + //ensure there is record of the estate owner with estate id and account id + ensure!(from != to, Error::::AlreadyOwnTheEstate); + let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; + let estate_info = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; + ensure!( + !EstateLeases::::contains_key(estate_id), + Error::::EstateIsAlreadyLeased + ); + + match estate_owner_value { + OwnerId::Token(class_id, token_id) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(class_id, token_id)), + Error::::EstateAlreadyInAuction + ); + ensure!( + Self::check_if_land_or_estate_owner(from, &estate_owner_value), + Error::::NoPermission + ); + T::NFTTokenizationSource::transfer_nft(from, to, &(class_id, token_id)); + + Self::deposit_event(Event::::TransferredEstate( + estate_id.clone(), + from.clone(), + to.clone(), + )); + + Ok(estate_id) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + }) + } + + /// Internal transfer of land unit + fn do_transfer_landunit( + coordinate: (i32, i32), + from: &T::AccountId, + to: &T::AccountId, + metaverse_id: MetaverseId, + ) -> Result<(i32, i32), DispatchError> { + LandUnits::::try_mutate_exists( + &metaverse_id, + &coordinate, + |land_unit_owner| -> Result<(i32, i32), DispatchError> { + // ensure there is record of the land unit with bit country id and coordinate + ensure!(land_unit_owner.is_some(), Error::::NoPermission); + ensure!(from != to, Error::::AlreadyOwnTheLandUnit); + match land_unit_owner { + Some(owner) => { + ensure!( + Self::check_if_land_or_estate_owner(from, owner), + Error::::NoPermission + ); + match owner { + OwnerId::Token(class_id, token_id) => { + ensure!( + !T::AuctionHandler::check_item_in_auction(ItemId::NFT(*class_id, *token_id)), + Error::::LandUnitAlreadyInAuction + ); + + T::NFTTokenizationSource::transfer_nft(from, to, &(*class_id, *token_id)); + // Update + Self::deposit_event(Event::::TransferredLandUnit( + metaverse_id.clone(), + coordinate.clone(), + from.clone(), + to.clone(), + )); + + Ok(coordinate) + } + _ => Err(Error::::InvalidOwnerValue.into()), + } + } + None => Err(DispatchError::Other("No Permissson")), + } + }, + ) + } + + /// Internal setting of total undeployed land units + fn set_total_undeployed_land_unit(total: u64, deduct: bool) -> Result<(), DispatchError> { + let total_undeployed_land_units = Self::all_undeployed_land_unit(); + + if deduct { + let new_total_undeployed_land_unit_count = total_undeployed_land_units + .checked_sub(total) + .ok_or("Overflow deducting new count to total undeployed lands")?; + TotalUndeployedLandUnit::::put(new_total_undeployed_land_unit_count); + } else { + let new_total_undeployed_land_unit_count = total_undeployed_land_units + .checked_add(total) + .ok_or("Overflow adding new count to total undeployed lands")?; + TotalUndeployedLandUnit::::put(new_total_undeployed_land_unit_count); + } + + Ok(()) + } + + /// Internal setting of total land units + fn set_total_land_unit(total: u64, deduct: bool) -> Result<(), DispatchError> { + let total_land_units_count = Self::all_land_units_count(); + + if deduct { + let new_total_land_units_count = total_land_units_count + .checked_sub(total) + .ok_or("Overflow deducting new count to total lands")?; + AllLandUnitsCount::::put(new_total_land_units_count); + } else { + let new_total_land_units_count = total_land_units_count + .checked_add(total) + .ok_or("Overflow adding new count to total lands")?; + AllLandUnitsCount::::put(new_total_land_units_count); + } + Ok(()) + } + + /// Internal getter of land token properties + fn get_land_token_properties(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> (NftMetadata, Attributes) { + let mut land_coordinate_attribute = Vec::::new(); + land_coordinate_attribute.append(&mut coordinate.0.to_be_bytes().to_vec()); + land_coordinate_attribute.append(&mut coordinate.1.to_be_bytes().to_vec()); + + let mut nft_metadata: NftMetadata = NftMetadata::new(); + nft_metadata.append(&mut land_coordinate_attribute.clone()); + + let mut nft_attributes: Attributes = Attributes::new(); + nft_attributes.insert("MetaverseId:".as_bytes().to_vec(), metaverse_id.to_be_bytes().to_vec()); + nft_attributes.insert("Coordinate:".as_bytes().to_vec(), land_coordinate_attribute); + + return (nft_metadata, nft_attributes); + } + + /// Internal getter of estate token properties + fn get_estate_token_properties(metaverse_id: MetaverseId, estate_id: EstateId) -> (NftMetadata, Attributes) { + let mut nft_metadata: NftMetadata = NftMetadata::new(); + nft_metadata.append(&mut metaverse_id.to_be_bytes().to_vec()); + nft_metadata.append(&mut estate_id.to_be_bytes().to_vec()); + + let mut nft_attributes: Attributes = Attributes::new(); + nft_attributes.insert("MetaverseId:".as_bytes().to_vec(), metaverse_id.to_be_bytes().to_vec()); + nft_attributes.insert("Estate Id:".as_bytes().to_vec(), estate_id.to_be_bytes().to_vec()); + + return (nft_metadata, nft_attributes); + } + + fn check_if_land_or_estate_owner(who: &T::AccountId, owner_id: &OwnerId) -> bool { + match owner_id { + OwnerId::Token(class_id, token_id) => { + return T::NFTTokenizationSource::check_ownership(who, &(*class_id, *token_id)).unwrap_or(false); + } + _ => return false, + } + } + + fn verify_land_unit_for_estate(land_units: Vec<(i32, i32)>) -> bool { + if land_units.len() == 1 { + return false; + } + + let mut vec_axis = land_units.iter().map(|lu| lu.0).collect::>(); + let mut vec_yaxis = land_units.iter().map(|lu| lu.1).collect::>(); + + // Sort by ascending and dedup + vec_axis.sort(); + vec_axis.dedup(); + vec_yaxis.sort(); + vec_yaxis.dedup(); + + let mut is_axis_valid = true; + let mut is_yaxis_valid = true; + + // Ensure axis is next to each other + for (i, axis) in vec_axis.iter().enumerate() { + if axis != &vec_axis[i] { + let valid = axis.saturating_sub(vec_axis[i + 1]); + if valid != 1 { + is_axis_valid = false; + break; + } + } + } + + // Ensure yaxis is next to each other + for (i, yaxis) in vec_yaxis.iter().enumerate() { + if yaxis != &vec_yaxis[i] { + let valid = yaxis.saturating_sub(vec_yaxis[i + 1]); + if valid != 1 { + is_yaxis_valid = false; + break; + } + } + } + + is_axis_valid && is_yaxis_valid + } + + fn verify_land_unit_in_bound(block_coordinate: &(i32, i32), land_unit_coordinates: &Vec<(i32, i32)>) -> bool { + let mut vec_axis = land_unit_coordinates.iter().map(|lu| lu.0).collect::>(); + let mut vec_yaxis = land_unit_coordinates.iter().map(|lu| lu.1).collect::>(); + + let max_axis = vec_axis.iter().max().unwrap_or(&i32::MAX); + let max_yaxis = vec_yaxis.iter().max().unwrap_or(&i32::MAX); + let min_axis = vec_axis.iter().min().unwrap_or(&i32::MIN); + let min_yaxis = vec_yaxis.iter().min().unwrap_or(&i32::MIN); + + let top_left_axis = block_coordinate + .0 + .saturating_mul(100i32) + .saturating_sub(50i32) + .saturating_div(10i32) + .saturating_add(1i32); + let top_right_axis = block_coordinate + .0 + .saturating_mul(100i32) + .saturating_add(50i32) + .saturating_div(10i32); + let top_left_yaxis = block_coordinate + .1 + .saturating_mul(100i32) + .saturating_add(50i32) + .saturating_div(10i32); + let top_right_yaxis = block_coordinate + .1 + .saturating_mul(100i32) + .saturating_sub(50i32) + .saturating_div(10i32) + .saturating_add(1i32); + + top_left_axis <= *min_axis + && top_right_axis >= *max_axis + && top_left_yaxis >= *max_yaxis + && top_right_yaxis <= *min_yaxis + } + + /// Remove all land unit and estate + pub fn remove_all_estate_storage() -> Weight { + log::info!("Start removing all land unit and estates"); + LandUnits::::remove_all(None); + Estates::::remove_all(None); + EstateOwner::::remove_all(None); + NextEstateId::::put(1); + AllLandUnitsCount::::put(0); + AllEstatesCount::::put(0); + Weight::from_ref_time(0) + } + + fn collect_network_fee( + recipient: &T::AccountId, + // social_currency_id: FungibleTokenId, + ) -> DispatchResult { + let network_fund = T::MetaverseInfoSource::get_network_treasury(); + //if social_currency_id == FungibleTokenId::NativeToken(0) { + ::Currency::transfer( + &recipient, + &network_fund, + T::NetworkFee::get(), + ExistenceRequirement::KeepAlive, + )?; + // } else { + // T::FungibleTokenCurrency::transfer( + // social_currency_id.clone(), + // &recipient, + // &network_fund, + // T::NetworkFee::get(), + //)?; + //} + Ok(()) + } +} + +impl MetaverseLandTrait for Pallet { + fn get_user_land_units(who: &T::AccountId, metaverse_id: &MetaverseId) -> Vec<(i32, i32)> { + // Check land units owner. + let mut total_land_units: Vec<(i32, i32)> = Vec::default(); + + let land_in_metaverse = LandUnits::::iter_prefix(metaverse_id) + .filter(|(_, owner)| Self::check_if_land_or_estate_owner(who, owner)) + .collect::>(); + + for land_unit in land_in_metaverse { + let land = land_unit.0; + total_land_units.push(land); + } + + total_land_units + } + + fn is_user_own_metaverse_land(who: &T::AccountId, metaverse_id: &MetaverseId) -> bool { + Self::get_user_land_units(&who, metaverse_id).len() > 0 + } + + fn check_landunit(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> Result { + Ok(LandUnits::::contains_key(metaverse_id, coordinate)) + } +} + +impl UndeployedLandBlocksTrait for Pallet { + fn issue_undeployed_land_blocks( + beneficiary: &T::AccountId, + number_of_land_block: u32, + number_land_units_per_land_block: u32, + undeployed_land_block_type: UndeployedLandBlockType, + ) -> Result, DispatchError> { + let new_undeployed_land_block_id = Self::do_issue_undeployed_land_blocks( + &beneficiary, + number_of_land_block, + number_land_units_per_land_block, + undeployed_land_block_type, + )?; + + Ok(new_undeployed_land_block_id) + } + + fn transfer_undeployed_land_block( + who: &T::AccountId, + to: &T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + Self::do_transfer_undeployed_land_block(who, to, undeployed_land_block_id)?; + + Ok(undeployed_land_block_id) + } + + fn burn_undeployed_land_block( + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + let undeployed_land_block_id = Self::do_burn_undeployed_land_block(undeployed_land_block_id)?; + + Ok(undeployed_land_block_id) + } + + fn freeze_undeployed_land_block( + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + let undeployed_land_block_id = Self::do_freeze_undeployed_land_block(undeployed_land_block_id)?; + + Ok(undeployed_land_block_id) + } + + fn check_undeployed_land_block( + owner: &T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + let undeployed_land_block = + Self::get_undeployed_land_block(undeployed_land_block_id).ok_or(Error::::UndeployedLandBlockNotFound)?; + + if undeployed_land_block.is_locked + || undeployed_land_block.undeployed_land_block_type == UndeployedLandBlockType::BoundToAddress + || undeployed_land_block.owner != *owner + { + return Ok(false); + } + return Ok(true); + } +} + +impl Estate for Pallet { + fn transfer_estate(estate_id: EstateId, from: &T::AccountId, to: &T::AccountId) -> Result { + let estate_id = Self::do_transfer_estate(estate_id, from, to)?; + Ok(estate_id) + } + + fn transfer_landunit( + coordinate: (i32, i32), + from: &T::AccountId, + to: &(T::AccountId, MetaverseId), + ) -> Result<(i32, i32), DispatchError> { + let coordinate = Self::do_transfer_landunit(coordinate, from, &(to).0, to.1)?; + Ok(coordinate) + } + + fn transfer_undeployed_land_block( + who: &T::AccountId, + to: &T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + let undeployed_land_block_id = Self::do_transfer_undeployed_land_block(who, to, undeployed_land_block_id)?; + + Ok(undeployed_land_block_id) + } + + fn check_estate(estate_id: EstateId) -> Result { + Ok(Estates::::contains_key(estate_id)) + } + + fn check_landunit(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> Result { + Ok(LandUnits::::contains_key(metaverse_id, coordinate)) + } + + fn check_undeployed_land_block( + owner: &T::AccountId, + undeployed_land_block_id: UndeployedLandBlockId, + ) -> Result { + let undeployed_land_block = + Self::get_undeployed_land_block(undeployed_land_block_id).ok_or(Error::::UndeployedLandBlockNotFound)?; + + if undeployed_land_block.is_locked + || undeployed_land_block.undeployed_land_block_type == UndeployedLandBlockType::BoundToAddress + || undeployed_land_block.owner != *owner + { + return Ok(false); + } + return Ok(true); + } + + fn get_total_land_units(estate_id: Option) -> u64 { + match estate_id { + Some(id) => { + if let Some(estate_info) = Estates::::get(id) { + estate_info.land_units.len() as u64 + } else { + 0 + } + } + None => AllLandUnitsCount::::get(), + } + } + + fn get_total_undeploy_land_units() -> u64 { + TotalUndeployedLandUnit::::get() + } + + fn check_estate_ownership(owner: T::AccountId, estate_id: EstateId) -> Result { + let owner_value = Self::get_estate_owner(estate_id); + match owner_value { + Some(token_value) => match token_value { + OwnerId::Token(c, t) => T::NFTTokenizationSource::check_ownership(&owner, &(c, t)), + OwnerId::Account(_) => Err(Error::::InvalidOwnerValue.into()), + }, + None => Ok(false), + } + } + + fn is_estate_leasor(leasor: T::AccountId, estate_id: EstateId) -> Result { + Ok(EstateLeasors::::contains_key(leasor, estate_id)) + } + + fn is_estate_leased(estate_id: EstateId) -> Result { + Ok(EstateLeases::::contains_key(estate_id)) + } +} diff --git a/pallets/land-minting/src/mock.rs b/pallets/land-minting/src/mock.rs new file mode 100644 index 000000000..3856a489b --- /dev/null +++ b/pallets/land-minting/src/mock.rs @@ -0,0 +1,637 @@ +#![cfg(test)] + +use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; +use frame_system::EnsureSignedBy; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{ConvertInto, IdentityLookup}, + DispatchError, Perbill, +}; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::default::Default; +use sp_std::vec::Vec; + +use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; +use core_primitives::{CollectionType, NftClassData, TokenType}; +use primitives::{ + AssetId, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId, LAND_CLASS_ID, +}; + +use crate as estate; + +use super::*; + +pub type AccountId = u128; +pub type Balance = u128; +pub type MetaverseId = u64; +pub type BlockNumber = u64; +pub type EstateId = u64; + +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 5; +pub const CHARLIE: AccountId = 6; +pub const DOM: AccountId = 7; +pub const BENEFICIARY_ID: AccountId = 99; +pub const AUCTION_BENEFICIARY_ID: AccountId = 100; +pub const CLASS_FUND_ID: AccountId = 123; +pub const METAVERSE_ID: MetaverseId = 0; +pub const DOLLARS: Balance = 1_000_000_000_000_000_000; +pub const ALICE_METAVERSE_ID: MetaverseId = 1; +pub const BOB_METAVERSE_ID: MetaverseId = 2; +pub const MAX_BOUND: (i32, i32) = (-100, 100); +pub const LANDBLOCK_COORDINATE: (i32, i32) = (0, 0); +pub const COORDINATE_IN_1: (i32, i32) = (-4, 4); +pub const COORDINATE_IN_2: (i32, i32) = (-4, 5); +pub const COORDINATE_IN_3: (i32, i32) = (-4, 6); +pub const COORDINATE_IN_4: (i32, i32) = (-4, 8); +pub const COORDINATE_OUT: (i32, i32) = (0, 101); +pub const COORDINATE_IN_AUCTION: (i32, i32) = (-4, 7); +pub const ESTATE_IN_AUCTION: EstateId = 3; +pub const UNDEPLOYED_LAND_BLOCK_IN_AUCTION: UndeployedLandBlockId = 1; + +pub const BOND_AMOUNT_1: Balance = 1000; +pub const BOND_AMOUNT_2: Balance = 2000; +pub const BOND_AMOUNT_BELOW_MINIMUM: Balance = 100; +pub const BOND_LESS_AMOUNT_1: Balance = 100; + +pub const ESTATE_ID: EstateId = 0; + +pub const ASSET_ID_1: TokenId = 101; +pub const ASSET_ID_2: TokenId = 100; +pub const ASSET_CLASS_ID: ClassId = 5; +pub const ASSET_TOKEN_ID: TokenId = 6; +pub const ASSET_COLLECTION_ID: GroupCollectionId = 7; +pub const METAVERSE_LAND_CLASS: ClassId = 15; +pub const METAVERSE_LAND_IN_AUCTION_TOKEN: TokenId = 4; +pub const METAVERSE_ESTATE_CLASS: ClassId = 16; +pub const METAVERSE_ESTATE_IN_AUCTION_TOKEN: TokenId = 3; + +pub const OWNER_ACCOUNT_ID: OwnerId = OwnerId::Account(BENEFICIARY_ID); +pub const OWNER_ID_ALICE: OwnerId = OwnerId::Account(ALICE); +pub const OWNER_LAND_ASSET_ID: OwnerId = OwnerId::Token(METAVERSE_LAND_CLASS, ASSET_ID_1); +pub const OWNER_ESTATE_ASSET_ID: OwnerId = + OwnerId::Token(METAVERSE_ESTATE_CLASS, ASSET_ID_2); + +pub const GENERAL_METAVERSE_FUND: AccountId = 102; + +ord_parameter_types! { + pub const One: AccountId = ALICE; +} + +// Configure a mock runtime to test the pallet. + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); +} + +// pub type AdaptedBasicCurrency = +// currencies::BasicCurrencyAdapter; + +parameter_types! { + pub const GetNativeCurrencyId: FungibleTokenId = FungibleTokenId::NativeToken(0); + pub const MiningCurrencyId: FungibleTokenId = FungibleTokenId::MiningResource(0); + pub const LandTreasuryPalletId: PalletId = PalletId(*b"bit/land"); + pub const MinimumLandPrice: Balance = 10 * DOLLARS; +} + +pub struct MetaverseInfoSource {} + +impl MetaverseTrait for MetaverseInfoSource { + fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId { + 1u64 + } + + fn check_ownership(who: &AccountId, metaverse_id: &MetaverseId) -> bool { + match *who { + ALICE => *metaverse_id == ALICE_METAVERSE_ID, + BOB => *metaverse_id == BOB_METAVERSE_ID, + _ => false, + } + } + + fn get_metaverse(_metaverse_id: u64) -> Option> { + None + } + + fn get_metaverse_token(_metaverse_id: u64) -> Option { + None + } + + fn update_metaverse_token(_metaverse_id: u64, _currency_id: FungibleTokenId) -> Result<(), DispatchError> { + Ok(()) + } + + fn get_metaverse_land_class(metaverse_id: MetaverseId) -> Result { + Ok(METAVERSE_LAND_CLASS) + } + + fn get_metaverse_estate_class(metaverse_id: MetaverseId) -> Result { + Ok(METAVERSE_ESTATE_CLASS) + } + + fn get_metaverse_marketplace_listing_fee(metaverse_id: MetaverseId) -> Result { + Ok(Perbill::from_percent(1u32)) + } + + fn get_metaverse_treasury(metaverse_id: MetaverseId) -> AccountId { + GENERAL_METAVERSE_FUND + } + + fn get_network_treasury() -> AccountId { + GENERAL_METAVERSE_FUND + } + + fn check_if_metaverse_estate( + metaverse_id: primitives::MetaverseId, + class_id: &ClassId, + ) -> Result { + if class_id == &METAVERSE_LAND_CLASS || class_id == &METAVERSE_ESTATE_CLASS { + return Ok(true); + } + return Ok(false); + } + + fn check_if_metaverse_has_any_land(_metaverse_id: primitives::MetaverseId) -> Result { + Ok(true) + } + + fn is_metaverse_owner(who: &AccountId) -> bool { + true + } +} + +pub struct MockAuctionManager; + +impl Auction for MockAuctionManager { + type Balance = Balance; + + fn auction_info(_id: u64) -> Option> { + None + } + + fn auction_item(id: AuctionId) -> Option> { + None + } + + fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { + Ok(()) + } + + fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + Ok(()) + } + + fn new_auction( + _recipient: u128, + _initial_amount: Self::Balance, + _start: u64, + _end: Option, + ) -> Result { + Ok(1) + } + + fn create_auction( + _auction_type: AuctionType, + _item_id: ItemId, + _end: Option, + _recipient: u128, + _initial_amount: Self::Balance, + _start: u64, + _listing_level: ListingLevel, + _listing_fee: Perbill, + _currency_id: FungibleTokenId, + ) -> Result { + Ok(1) + } + + fn remove_auction(_id: u64, _item_id: ItemId) {} + + fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + Ok(()) + } + + fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + Ok(()) + } + + fn local_auction_bid_handler( + _now: u64, + _id: u64, + _new_bid: (u128, Self::Balance), + _last_bid: Option<(u128, Self::Balance)>, + _social_currency_id: FungibleTokenId, + ) -> DispatchResult { + Ok(()) + } + + fn collect_royalty_fee( + _high_bid_price: &Self::Balance, + _high_bidder: &u128, + _asset_id: &(u32, u64), + _social_currency_id: FungibleTokenId, + ) -> DispatchResult { + Ok(()) + } +} + +impl CheckAuctionItemHandler for MockAuctionManager { + fn check_item_in_auction(item_id: ItemId) -> bool { + match item_id { + ItemId::NFT(METAVERSE_LAND_CLASS, METAVERSE_LAND_IN_AUCTION_TOKEN) => { + return true; + } + ItemId::NFT(METAVERSE_ESTATE_CLASS, METAVERSE_ESTATE_IN_AUCTION_TOKEN) => { + return true; + } + ItemId::UndeployedLandBlock(UNDEPLOYED_LAND_BLOCK_IN_AUCTION) => { + return true; + } + _ => { + return false; + } + } + } +} + +fn test_attributes(x: u8) -> Attributes { + let mut attr: Attributes = BTreeMap::new(); + attr.insert(vec![x, x + 5], vec![x, x + 10]); + attr +} + +pub struct MockNFTHandler; + +impl NFTTrait for MockNFTHandler { + type TokenId = TokenId; + type ClassId = ClassId; + + fn check_ownership(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Result { + let nft_value = *asset_id; + if (*who == ALICE && (nft_value.1 == 1 || nft_value.1 == 3)) + || (*who == BOB && (nft_value.1 == 2 || nft_value.1 == 4)) + || (*who == BENEFICIARY_ID && (nft_value.1 == 100 || nft_value.1 == 101)) + | (*who == AUCTION_BENEFICIARY_ID + && (nft_value.1 == METAVERSE_ESTATE_IN_AUCTION_TOKEN + || nft_value.1 == METAVERSE_LAND_IN_AUCTION_TOKEN)) + { + return Ok(true); + } + Ok(false) + } + + fn check_collection_and_class( + collection_id: GroupCollectionId, + class_id: Self::ClassId, + ) -> Result { + if class_id == ASSET_CLASS_ID && collection_id == ASSET_COLLECTION_ID { + return Ok(true); + } + Ok(false) + } + fn get_nft_group_collection(nft_collection: &Self::ClassId) -> Result { + Ok(ASSET_COLLECTION_ID) + } + + fn is_stackable(asset_id: (Self::ClassId, Self::TokenId)) -> Result { + Ok(false) + } + + fn create_token_class( + sender: &AccountId, + metadata: NftMetadata, + attributes: Attributes, + collection_id: GroupCollectionId, + token_type: TokenType, + collection_type: CollectionType, + royalty_fee: Perbill, + mint_limit: Option, + ) -> Result { + match *sender { + ALICE => { + if collection_id == 0 { + Ok(0) + } else if collection_id == 1 { + Ok(1) + } else { + Ok(2) + } + } + BOB => Ok(3), + BENEFICIARY_ID => Ok(ASSET_CLASS_ID), + _ => Ok(100), + } + } + + fn mint_token( + sender: &AccountId, + class_id: ClassId, + metadata: NftMetadata, + attributes: Attributes, + ) -> Result { + match *sender { + ALICE => Ok(1), + BOB => Ok(2), + BENEFICIARY_ID => { + if class_id == METAVERSE_LAND_CLASS { + return Ok(ASSET_ID_1); + } else if class_id == METAVERSE_ESTATE_CLASS { + return Ok(ASSET_ID_2); + } else { + return Ok(200); + } + } + AUCTION_BENEFICIARY_ID => { + if class_id == METAVERSE_LAND_CLASS { + return Ok(METAVERSE_LAND_IN_AUCTION_TOKEN); + } else if class_id == METAVERSE_ESTATE_CLASS { + return Ok(METAVERSE_ESTATE_IN_AUCTION_TOKEN); + } else { + return Ok(201); + } + } + _ => { + if class_id == 0 { + return Ok(1000); + } else { + return Ok(1001); + } + } + } + } + + fn transfer_nft(from: &AccountId, to: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + Ok(()) + } + + fn check_item_on_listing(class_id: Self::ClassId, token_id: Self::TokenId) -> Result { + Ok(true) + } + + fn burn_nft(account: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + Ok(()) + } + fn is_transferable(nft: &(Self::ClassId, Self::TokenId)) -> Result { + Ok(true) + } + + fn get_class_fund(class_id: &Self::ClassId) -> AccountId { + CLASS_FUND_ID + } + + fn get_nft_detail(asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { + let new_data = NftClassData { + deposit: 0, + attributes: test_attributes(1), + token_type: TokenType::Transferable, + collection_type: CollectionType::Collectable, + is_locked: false, + royalty_fee: Perbill::from_percent(0u32), + mint_limit: None, + total_minted_tokens: 0u32, + }; + Ok(new_data) + } + + fn set_lock_collection(class_id: Self::ClassId, is_locked: bool) -> sp_runtime::DispatchResult { + Ok(()) + } + + fn set_lock_nft(token_id: (Self::ClassId, Self::TokenId), is_locked: bool) -> sp_runtime::DispatchResult { + Ok(()) + } + + fn get_nft_class_detail(_class_id: Self::ClassId) -> Result, DispatchError> { + let new_data = NftClassData { + deposit: 0, + attributes: test_attributes(1), + token_type: TokenType::Transferable, + collection_type: CollectionType::Collectable, + is_locked: false, + royalty_fee: Perbill::from_percent(0u32), + mint_limit: None, + total_minted_tokens: 0u32, + }; + Ok(new_data) + } + + fn get_total_issuance(class_id: Self::ClassId) -> Result { + Ok(10u64) + } + + fn get_asset_owner(asset_id: &(Self::ClassId, Self::TokenId)) -> Result { + Ok(ALICE) + } + + fn mint_token_with_id( + sender: &AccountId, + class_id: Self::ClassId, + token_id: Self::TokenId, + metadata: core_primitives::NftMetadata, + attributes: core_primitives::Attributes, + ) -> Result { + match *sender { + ALICE => Ok(1), + BOB => Ok(2), + BENEFICIARY_ID => { + if class_id == METAVERSE_LAND_CLASS { + return Ok(ASSET_ID_1); + } else if class_id == METAVERSE_ESTATE_CLASS { + return Ok(ASSET_ID_2); + } else { + return Ok(200); + } + } + AUCTION_BENEFICIARY_ID => { + if class_id == METAVERSE_LAND_CLASS { + return Ok(METAVERSE_LAND_IN_AUCTION_TOKEN); + } else if class_id == METAVERSE_ESTATE_CLASS { + return Ok(METAVERSE_ESTATE_IN_AUCTION_TOKEN); + } else { + return Ok(201); + } + } + _ => { + if class_id == 0 { + return Ok(1000); + } else { + return Ok(1001); + } + } + } + } + + fn get_free_stackable_nft_balance(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { + 1000 + } + + fn reserve_stackable_nft_balance( + who: &AccountId, + asset_id: &(Self::ClassId, Self::TokenId), + amount: Balance, + ) -> DispatchResult { + Ok(()) + } + + fn unreserve_stackable_nft_balance( + who: &AccountId, + asset_id: &(Self::ClassId, Self::TokenId), + amount: Balance, + ) -> sp_runtime::DispatchResult { + Ok(()) + } + + fn transfer_stackable_nft( + sender: &AccountId, + to: &AccountId, + nft: &(Self::ClassId, Self::TokenId), + amount: Balance, + ) -> sp_runtime::DispatchResult { + Ok(()) + } +} + +parameter_types! { + pub const MinBlocksPerRound: u32 = 10; + pub const MinimumStake: Balance = 200; + /// Reward payments are delayed by 2 hours (2 * 300 * block_time) + pub const RewardPaymentDelay: u32 = 2; + pub const DefaultMaxBound: (i32,i32) = MAX_BOUND; + pub const NetworkFee: Balance = 1; // Network fee + pub const MaxOffersPerEstate: u32 = 2; + pub const MinLeasePricePerBlock: Balance = 1u128; + pub const MaxLeasePeriod: u32 = 9; + pub const LeaseOfferExpiryPeriod: u32 = 6; + pub StorageDepositFee: Balance = 1; +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type LandTreasury = LandTreasuryPalletId; + type MetaverseInfoSource = MetaverseInfoSource; + type Currency = Balances; + type MinimumLandPrice = MinimumLandPrice; + type CouncilOrigin = EnsureSignedBy; + type AuctionHandler = MockAuctionManager; + type MinBlocksPerRound = MinBlocksPerRound; + type WeightInfo = (); + type MinimumStake = MinimumStake; + type RewardPaymentDelay = RewardPaymentDelay; + type NFTTokenizationSource = MockNFTHandler; + type DefaultMaxBound = DefaultMaxBound; + type NetworkFee = NetworkFee; + type MaxOffersPerEstate = MaxOffersPerEstate; + type MinLeasePricePerBlock = MinLeasePricePerBlock; + type MaxLeasePeriod = MaxLeasePeriod; + type LeaseOfferExpiryPeriod = LeaseOfferExpiryPeriod; + type BlockNumberToBalance = ConvertInto; + type StorageDepositFee = StorageDepositFee; +} + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Estate: estate:: {Pallet, Call, Storage, Event}, + } +); + +pub type EstateModule = Pallet; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +pub struct ExtBuilder; + +impl Default for ExtBuilder { + fn default() -> Self { + ExtBuilder + } +} + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, 100000), (BOB, 100000), (BENEFICIARY_ID, 1000000)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +pub fn last_event() -> RuntimeEvent { + frame_system::Pallet::::events() + .pop() + .expect("Event expected") + .event +} + +fn next_block() { + EstateModule::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + EstateModule::on_initialize(System::block_number()); +} + +pub fn run_to_block(n: u64) { + while System::block_number() < n { + next_block(); + } +} diff --git a/pallets/land-minting/src/rate.rs b/pallets/land-minting/src/rate.rs new file mode 100644 index 000000000..747a72029 --- /dev/null +++ b/pallets/land-minting/src/rate.rs @@ -0,0 +1,167 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_runtime::{Perbill, RuntimeDebug}; + +use crate::{AllLandUnitsCount, TotalUndeployedLandUnit}; +// Helper methods to compute the issuance rate for undeployed land. +use crate::pallet::{Config, Pallet}; + +const SECONDS_PER_YEAR: u32 = 31557600; +const SECONDS_PER_BLOCK: u32 = 12; +const BLOCKS_PER_YEAR: u32 = SECONDS_PER_YEAR / SECONDS_PER_BLOCK; + +fn rounds_per_year() -> u32 { + let blocks_per_round = >::round().length; + BLOCKS_PER_YEAR / blocks_per_round +} + +fn get_annual_max_issuance(max_supply: u64, annual_percentage: u64) -> u64 { + let total_land_unit_circulating = >::get(); + let total_undeployed_land_unit_circulating = >::get(); + let circulating = total_land_unit_circulating.saturating_add(total_undeployed_land_unit_circulating); + max_supply.saturating_sub(circulating).saturating_mul(annual_percentage) +} + +/// Compute round issuance range from round inflation range and current total issuance +pub fn round_issuance_range(config: MintingRateInfo) -> Range { + // Get total round per year + let total_round_per_year = rounds_per_year::(); + + // Get total land unit circulating + let total_land_unit_circulating = >::get(); + + // Get total undeployed land unit circulating + let total_undeployed_land_unit_circulating = >::get(); + + // Total circulating + let circulating = total_land_unit_circulating.saturating_add(total_undeployed_land_unit_circulating); + + // Total annual minting percent + let annual_percentage = Perbill::from_percent(config.annual as u32).deconstruct(); + + // Round percentage minting rate + let round_percentage = annual_percentage.checked_div(total_round_per_year).unwrap(); + + // Convert to percentage + let round_percentage_per_bill = Perbill::from_parts(round_percentage); + + // Return range - could implement more cases in the future. + Range { + min: round_percentage_per_bill * circulating, + ideal: round_percentage_per_bill * circulating, + max: round_percentage_per_bill * circulating, + } +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Default, RuntimeDebug, TypeInfo)] +pub struct Range { + pub min: T, + pub ideal: T, + pub max: T, +} + +impl Range { + pub fn is_valid(&self) -> bool { + self.max >= self.ideal && self.ideal >= self.min + } +} + +impl From for Range { + fn from(other: T) -> Range { + Range { + min: other, + ideal: other, + max: other, + } + } +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Eq, PartialEq, Clone, Encode, Decode, Default, RuntimeDebug, TypeInfo)] +pub struct MintingRateInfo { + /// Number of metaverse expectations + pub expect: Range, + /// Annual minting range + pub annual: u64, + /// Max total supply + pub max: u64, +} + +impl MintingRateInfo { + pub fn new(annual: u64, expect: Range, max: u64) -> MintingRateInfo { + MintingRateInfo { expect, annual, max } + } + + /// Set minting rate expectations + pub fn set_expectations(&mut self, expect: Range) { + self.expect = expect; + } + + /// Set minting rate expectations + pub fn set_max(&mut self, max: u64) { + self.max = max; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Compute round issuance range from round inflation range and current total issuance + pub fn mock_round_issuance_per_year( + config: MintingRateInfo, + circulation: u64, + total_round_per_year: u32, + ) -> Range { + let annual_percentage = Perbill::from_percent(config.annual as u32).deconstruct(); + let round_percentage = annual_percentage.checked_div(total_round_per_year).unwrap(); + + let round_percentage_per_bill = Perbill::from_parts(round_percentage); + + Range { + min: round_percentage_per_bill * circulation, + ideal: round_percentage_per_bill * circulation, + max: round_percentage_per_bill * circulation, + } + } + + #[test] + fn simple_round_issuance() { + // 5% minting rate for 100_000 land unit = 100 minted over the year + // let's assume there are 10 periods in a year + // => mint 100 over 10 periods => 10 minted per period + + let mock_config: MintingRateInfo = MintingRateInfo { + expect: Default::default(), + annual: 5, + max: 100_000, + }; + + let round_issuance = mock_round_issuance_per_year(mock_config, 2_000, 10); + + // make sure 10 land unit deploy per period + assert_eq!(round_issuance.min, 10); + assert_eq!(round_issuance.ideal, 10); + assert_eq!(round_issuance.max, 10); + } +} diff --git a/pallets/land-minting/src/tests.rs b/pallets/land-minting/src/tests.rs new file mode 100644 index 000000000..d597baa73 --- /dev/null +++ b/pallets/land-minting/src/tests.rs @@ -0,0 +1,2908 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(test)] + +use frame_support::{assert_err, assert_noop, assert_ok}; +use sp_runtime::traits::BadOrigin; + +use mock::{RuntimeEvent, *}; + +use super::*; + +fn estate_sub_account(estate_id: mock::EstateId) -> AccountId { + ::LandTreasury::get().into_sub_account_truncating(estate_id) +} + +#[test] +fn mint_land_should_reject_non_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::mint_land( + RuntimeOrigin::signed(ALICE), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + ), + BadOrigin + ); + }); +} + +#[test] +fn mint_land_should_work_with_one_coordinate() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( + OWNER_LAND_ASSET_ID, + METAVERSE_ID, + COORDINATE_IN_1, + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 1); + }); +} + +#[test] +fn mint_land_token_should_work_have_correct_owner() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), None); + + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( + OWNER_LAND_ASSET_ID, + METAVERSE_ID, + COORDINATE_IN_1, + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 1); + + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), + Some(OWNER_LAND_ASSET_ID) + ); + }); +} + +#[test] +fn mint_land_should_reject_with_duplicate_coordinates() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( + OWNER_LAND_ASSET_ID, + METAVERSE_ID, + COORDINATE_IN_1, + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 1); + assert_noop!( + EstateModule::mint_land(RuntimeOrigin::root(), BENEFICIARY_ID, METAVERSE_ID, COORDINATE_IN_1), + Error::::LandUnitIsNotAvailable + ); + }); +} + +#[test] +fn mint_lands_should_reject_with_duplicate_coordinates() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_lands( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandsMinted( + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2], + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 2); + assert_noop!( + EstateModule::mint_lands( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + ), + Error::::LandUnitIsNotAvailable + ); + }); +} + +#[test] +fn mint_land_should_work_with_different_coordinate() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( + OWNER_LAND_ASSET_ID, + METAVERSE_ID, + COORDINATE_IN_1, + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 1); + + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_2 + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( + OWNER_LAND_ASSET_ID, + METAVERSE_ID, + COORDINATE_IN_2, + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 2); + }); +} + +#[test] +fn mint_lands_should_reject_non_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::mint_lands( + RuntimeOrigin::signed(ALICE), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + ), + BadOrigin + ); + }); +} + +#[test] +fn mint_lands_should_work_with_one_coordinate() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_lands( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 1 + ); + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandsMinted( + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1], + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 1); + }); +} + +#[test] +fn mint_lands_should_work_with_more_than_one_coordinate() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_lands( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::NewLandsMinted( + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2], + )) + ); + + assert_eq!(EstateModule::all_land_units_count(), 2); + }); +} + +#[test] +fn transfer_land_token_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), + Some(OWNER_LAND_ASSET_ID) + ); + + assert_ok!(EstateModule::transfer_land( + RuntimeOrigin::signed(BENEFICIARY_ID), + ALICE, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), + Some(OWNER_LAND_ASSET_ID) + ); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::TransferredLandUnit( + METAVERSE_ID, + COORDINATE_IN_1, + BENEFICIARY_ID, + ALICE, + )) + ); + }); +} + +#[test] +fn transfer_land_should_reject_no_permission() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), + Some(OWNER_LAND_ASSET_ID) + ); + + assert_noop!( + EstateModule::transfer_land(RuntimeOrigin::signed(BOB), ALICE, METAVERSE_ID, COORDINATE_IN_1), + Error::::NoPermission + ); + }); +} + +#[test] +fn transfer_land_should_do_fail_for_same_account() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), + Some(OWNER_LAND_ASSET_ID) + ); + + assert_noop!( + EstateModule::transfer_land( + RuntimeOrigin::signed(BENEFICIARY_ID), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + ), + Error::::AlreadyOwnTheLandUnit + ); + + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), + Some(OWNER_LAND_ASSET_ID) + ); + }); +} + +#[test] +fn transfer_land_should_do_fail_for_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + AUCTION_BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_AUCTION + )); + assert_eq!( + EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_AUCTION), + Some(OwnerId::Token(METAVERSE_LAND_CLASS, METAVERSE_LAND_IN_AUCTION_TOKEN)) + ); + + assert_noop!( + EstateModule::transfer_land( + RuntimeOrigin::signed(AUCTION_BENEFICIARY_ID), + BOB, + METAVERSE_ID, + COORDINATE_IN_AUCTION + ), + Error::::LandUnitAlreadyInAuction + ); + }); +} + +#[test] +fn mint_estate_should_reject_non_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::mint_estate( + RuntimeOrigin::signed(ALICE), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + ), + BadOrigin + ); + }); +} + +#[test] +fn mint_estate_should_fail_for_minted_land() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_err!( + EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + ), + Error::::LandUnitIsNotAvailable + ); + }); +} + +#[test] +fn dissolve_estate_should_work() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + + // Destroy estate + assert_ok!(EstateModule::dissolve_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + estate_id, + )); + + assert_eq!(EstateModule::all_estates_count(), 0); + assert_eq!(EstateModule::get_estates(estate_id), None); + assert_eq!(EstateModule::get_estate_owner(estate_id), None); + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + }); +} + +#[test] +fn dissolve_estate_should_reject_non_owner() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_err!( + EstateModule::dissolve_estate(RuntimeOrigin::signed(ALICE), 0), + Error::::NoPermission + ); + }); +} + +#[test] +fn add_land_unit_to_estate_should_reject_non_owner() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_2] + )); + + assert_err!( + EstateModule::add_land_unit_to_estate(RuntimeOrigin::signed(ALICE), 0, vec![COORDINATE_IN_2]), + Error::::NoPermission + ); + }); +} + +#[test] +fn add_land_unit_to_estate_should_work() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1] + }) + ); + + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 1 + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_eq!(EstateModule::all_land_units_count(), 1); + + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_2 + )); + // Update estate + assert_ok!(EstateModule::add_land_unit_to_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + estate_id, + vec![COORDINATE_IN_2] + )); + + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + assert_eq!(EstateModule::all_land_units_count(), 2); + }); +} + +#[test] +fn remove_land_unit_from_estate_should_reject_non_owner() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_err!( + EstateModule::remove_land_unit_from_estate(RuntimeOrigin::signed(ALICE), 0, vec![COORDINATE_IN_2]), + Error::::NoPermission + ); + }); +} + +#[test] +fn remove_land_unit_from_estate_should_work() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + assert_eq!(EstateModule::all_land_units_count(), 2); + + // Update estate + assert_ok!(EstateModule::remove_land_unit_from_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + estate_id, + vec![COORDINATE_IN_2] + )); + + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1] + }) + ); + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + assert_eq!(EstateModule::all_land_units_count(), 2); + }); +} + +#[test] +fn mint_estate_and_land_should_return_correct_total_land_unit() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + (-6, 6) + )); + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 3 + ); + }); +} + +#[test] +fn mint_estate_should_return_none_for_non_exist_estate() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + let estate_id_non_exists: u64 = 999; + assert_eq!(EstateModule::get_estates(estate_id_non_exists), None); + assert_eq!(EstateModule::get_estate_owner(estate_id_non_exists), None); + }); +} + +#[test] +fn transfer_estate_token_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_ok!(EstateModule::transfer_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + ALICE, + estate_id + )); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::TransferredEstate(estate_id, BENEFICIARY_ID, ALICE)) + ); + }); +} + +#[test] +fn transfer_estate_should_reject_no_permission() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_noop!( + EstateModule::transfer_estate(RuntimeOrigin::signed(BOB), ALICE, estate_id), + Error::::NoPermission + ); + }); +} + +#[test] +fn transfer_estate_should_reject_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_2] + )); + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_3] + )); + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + AUCTION_BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_AUCTION] + )); + assert_noop!( + EstateModule::transfer_estate(RuntimeOrigin::signed(AUCTION_BENEFICIARY_ID), ALICE, ESTATE_IN_AUCTION), + Error::::EstateAlreadyInAuction + ); + }); +} + +#[test] +fn transfer_estate_should_fail_with_same_account() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + assert_noop!( + EstateModule::transfer_estate(RuntimeOrigin::signed(BENEFICIARY_ID), BENEFICIARY_ID, estate_id), + Error::::AlreadyOwnTheEstate + ); + + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + }); +} + +#[test] +fn create_estate_token_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_lands( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + assert_eq!(Balances::free_balance(BENEFICIARY_ID), 999998); + }); +} + +#[test] +fn create_estate_token_after_minting_account_and_token_based_lands_should_give_correct_total_user_land_units() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_1 + )); + + assert_ok!(EstateModule::mint_land( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + COORDINATE_IN_2 + )); + + assert_ok!(EstateModule::create_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + assert_eq!( + EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), + 2 + ); + assert_eq!(EstateModule::all_land_units_count(), 2); + assert_eq!(Balances::free_balance(BENEFICIARY_ID), 999998); + }); +} + +#[test] +fn create_estate_should_return_none_for_non_exist_estate() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_lands( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_estate( + RuntimeOrigin::signed(BENEFICIARY_ID), + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + assert_eq!(Balances::free_balance(BENEFICIARY_ID), 999998); + + let estate_id: u64 = 0; + assert_eq!(EstateModule::all_estates_count(), 1); + assert_eq!(EstateModule::next_estate_id(), 1); + assert_eq!( + EstateModule::get_estates(estate_id), + Some(EstateInfo { + metaverse_id: METAVERSE_ID, + land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] + }) + ); + assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); + + let estate_id_non_exists: u64 = 999; + assert_eq!(EstateModule::get_estates(estate_id_non_exists), None); + assert_eq!(EstateModule::get_estate_owner(estate_id_non_exists), None); + }); +} + +#[test] +fn issue_land_block_should_fail_if_not_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::signed(ALICE), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + ), + BadOrigin + ); + }); +} + +#[test] +fn issue_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(BOB, 0)) + ); + + assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 20); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + }); +} + +#[test] +fn issue_two_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(BOB, 0)) + ); + + assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); + + let first_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match first_issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 20); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + ALICE, + 1, + 30, + UndeployedLandBlockType::Transferable + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(ALICE, 1)) + ); + + assert_eq!(EstateModule::get_undeployed_land_block_owner(ALICE, 1), Some(())); + + let second_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match second_issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, ALICE); + assert_eq!(a.number_land_units, 30); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + }); +} + +#[test] +fn freeze_undeployed_land_block_should_fail_if_not_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), + BadOrigin + ); + }); +} + +#[test] +fn freeze_undeployed_land_block_should_fail_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), + Error::::UndeployedLandBlockNotFound + ); + }); +} + +#[test] +fn freeze_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(UNDEPLOYED_LAND_BLOCK_IN_AUCTION); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + assert_noop!( + EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), UNDEPLOYED_LAND_BLOCK_IN_AUCTION), + Error::::UndeployedLandBlockAlreadyInAuction + ); + }); +} + +#[test] +fn freeze_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 20); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_ok!(EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockFreezed(0)) + ); + + assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); + + let frozen_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match frozen_undeployed_land_block { + Some(a) => { + assert_eq!(a.is_locked, true); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + }); +} + +#[test] +fn freeze_undeployed_land_block_should_fail_already_freezed() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + assert_ok!(EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockFreezed(0)) + ); + + assert_noop!( + EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), + Error::::UndeployedLandBlockAlreadyFreezed + ); + }); +} + +#[test] +fn unfreeze_undeployed_land_block_should_fail_if_not_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), + BadOrigin + ); + }); +} + +#[test] +fn unfreeze_undeployed_land_block_should_fail_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), + Error::::UndeployedLandBlockNotFound + ); + }); +} + +#[test] +fn unfreeze_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_noop!( + EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), UNDEPLOYED_LAND_BLOCK_IN_AUCTION), + Error::::UndeployedLandBlockAlreadyInAuction + ); + }); +} + +#[test] +fn unfreeze_undeployed_land_block_should_fail_not_frozen() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + assert_noop!( + EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), + Error::::UndeployedLandBlockNotFrozen + ); + }); +} + +#[test] +fn unfreeze_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + assert_ok!(EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); + + let freezed_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match freezed_undeployed_land_block { + Some(a) => { + assert_eq!(a.is_locked, true); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_ok!(EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockUnfreezed(0)) + ); + + let unfreezed_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match unfreezed_undeployed_land_block { + Some(a) => { + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + }); +} + +#[test] +fn transfer_undeployed_land_block_should_fail_if_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, 0), + Error::::UndeployedLandBlockNotFound + ); + }); +} + +#[test] +fn transfer_undeployed_land_block_should_fail_if_not_owner() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_noop!( + EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, undeployed_land_block_id), + Error::::NoPermission + ); + }); +} + +#[test] +fn transfer_undeployed_land_block_should_fail_if_freezed() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_ok!(EstateModule::freeze_undeployed_land_blocks( + RuntimeOrigin::root(), + undeployed_land_block_id + )); + + assert_noop!( + EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(BOB), ALICE, undeployed_land_block_id), + Error::::UndeployedLandBlockAlreadyFreezed + ); + }); +} + +#[test] +fn transfer_undeployed_land_block_should_fail_if_not_transferable() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_noop!( + EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(BOB), ALICE, undeployed_land_block_id), + Error::::UndeployedLandBlockIsNotTransferable + ); + }); +} + +#[test] +fn transfer_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_noop!( + EstateModule::transfer_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + ALICE, + UNDEPLOYED_LAND_BLOCK_IN_AUCTION + ), + Error::::UndeployedLandBlockAlreadyInAuction + ); + }); +} + +#[test] +fn transfer_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::Transferable + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + Some(()) + ); + + assert_ok!(EstateModule::transfer_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + ALICE, + undeployed_land_block_id + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockTransferred( + BOB, + ALICE, + undeployed_land_block_id, + )) + ); + + let transferred_issued_undeployed_land_block = + EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match transferred_issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, ALICE); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + None + ); + assert_eq!( + EstateModule::get_undeployed_land_block_owner(ALICE, undeployed_land_block_id), + Some(()) + ); + }); +} + +#[test] +fn deploy_undeployed_land_block_should_fail_if_not_found() { + ExtBuilder::default().build().execute_with(|| { + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_noop!( + EstateModule::deploy_land_block( + RuntimeOrigin::signed(ALICE), + undeployed_land_block_id, + ALICE_METAVERSE_ID, + LANDBLOCK_COORDINATE, + vec![COORDINATE_IN_1] + ), + Error::::UndeployedLandBlockNotFound + ); + assert_eq!(Balances::free_balance(BOB), 100000); + }); +} + +#[test] +fn deploy_undeployed_land_block_should_fail_if_not_owner() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_noop!( + EstateModule::deploy_land_block( + RuntimeOrigin::signed(ALICE), + undeployed_land_block_id, + METAVERSE_ID, + LANDBLOCK_COORDINATE, + vec![COORDINATE_IN_1] + ), + Error::::NoPermission + ); + assert_eq!(Balances::free_balance(ALICE), 100000); + }); +} + +#[test] +fn deploy_undeployed_land_block_should_fail_if_freezed() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_ok!(EstateModule::freeze_undeployed_land_blocks( + RuntimeOrigin::root(), + undeployed_land_block_id + )); + + assert_noop!( + EstateModule::deploy_land_block( + RuntimeOrigin::signed(BOB), + undeployed_land_block_id, + BOB_METAVERSE_ID, + LANDBLOCK_COORDINATE, + vec![COORDINATE_IN_1] + ), + Error::::UndeployedLandBlockFreezed + ); + assert_eq!(Balances::free_balance(BOB), 99999); + }); +} + +#[test] +fn deploy_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_noop!( + EstateModule::deploy_land_block( + RuntimeOrigin::signed(BOB), + UNDEPLOYED_LAND_BLOCK_IN_AUCTION, + METAVERSE_ID, + LANDBLOCK_COORDINATE, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + ), + Error::::UndeployedLandBlockAlreadyInAuction + ); + assert_eq!(Balances::free_balance(BOB), 99998); + }); +} + +#[test] +fn deploy_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 2, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + let undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match undeployed_land_block { + Some(a) => { + assert_eq!(a.number_land_units, 2); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_ok!(EstateModule::deploy_land_block( + RuntimeOrigin::signed(BOB), + undeployed_land_block_id, + BOB_METAVERSE_ID, + LANDBLOCK_COORDINATE, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::LandBlockDeployed( + BOB, + BOB_METAVERSE_ID, + undeployed_land_block_id, + vec![COORDINATE_IN_1, COORDINATE_IN_2], + )) + ); + + let updated_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + + assert_eq!(updated_undeployed_land_block, None); + + assert_eq!(EstateModule::all_land_units_count(), 2); + assert_eq!(Balances::free_balance(BOB), 99998); + }); +} + +#[test] +fn approve_undeployed_land_block_should_fail_if_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::approve_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, 0), + Error::::UndeployedLandBlockNotFound + ); + }); +} + +#[test] +fn approve_undeployed_land_block_should_fail_if_not_owner() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_noop!( + EstateModule::approve_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, undeployed_land_block_id), + Error::::NoPermission + ); + }); +} + +#[test] +fn approve_undeployed_land_block_should_fail_if_freezed() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_ok!(EstateModule::freeze_undeployed_land_blocks( + RuntimeOrigin::root(), + undeployed_land_block_id + )); + + assert_noop!( + EstateModule::approve_undeployed_land_blocks(RuntimeOrigin::signed(BOB), ALICE, undeployed_land_block_id), + Error::::UndeployedLandBlockAlreadyFreezed + ); + }); +} + +#[test] +fn approve_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_noop!( + EstateModule::approve_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + ALICE, + UNDEPLOYED_LAND_BLOCK_IN_AUCTION + ), + Error::::UndeployedLandBlockAlreadyInAuction + ); + }); +} + +#[test] +fn approve_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::Transferable + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + assert_eq!(a.approved, None); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + Some(()) + ); + + assert_ok!(EstateModule::approve_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + ALICE, + undeployed_land_block_id + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockApproved( + BOB, + ALICE, + undeployed_land_block_id, + )) + ); + + let transferred_issued_undeployed_land_block = + EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match transferred_issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + assert_eq!(a.approved, Some(ALICE)); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + Some(()) + ); + }); +} + +#[test] +fn unapprove_undeployed_land_block_should_fail_if_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::unapprove_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), + Error::::UndeployedLandBlockNotFound + ); + }); +} + +#[test] +fn unapprove_undeployed_land_block_should_fail_if_not_owner() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_noop!( + EstateModule::unapprove_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), undeployed_land_block_id), + Error::::NoPermission + ); + }); +} + +#[test] +fn unapprove_undeployed_land_block_should_fail_if_freezed() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + assert_ok!(EstateModule::freeze_undeployed_land_blocks( + RuntimeOrigin::root(), + undeployed_land_block_id + )); + + assert_noop!( + EstateModule::unapprove_undeployed_land_blocks(RuntimeOrigin::signed(BOB), undeployed_land_block_id), + Error::::UndeployedLandBlockAlreadyFreezed + ); + }); +} + +#[test] +fn unapprove_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_noop!( + EstateModule::unapprove_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + UNDEPLOYED_LAND_BLOCK_IN_AUCTION + ), + Error::::UndeployedLandBlockAlreadyInAuction + ); + }); +} + +#[test] +fn unapprove_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::Transferable + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + assert_eq!(a.approved, None); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + Some(()) + ); + assert_ok!(EstateModule::approve_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + ALICE, + undeployed_land_block_id + )); + + let approved_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match approved_issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + assert_eq!(a.approved, Some(ALICE)); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_ok!(EstateModule::unapprove_undeployed_land_blocks( + RuntimeOrigin::signed(BOB), + undeployed_land_block_id + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockUnapproved(undeployed_land_block_id)) + ); + + let unapproved_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match unapproved_issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + assert_eq!(a.approved, None); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + }); +} + +#[test] +fn burn_undeployed_land_block_should_fail_if_not_root() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::burn_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), + BadOrigin + ); + }); +} + +#[test] +fn burn_undeployed_land_block_should_fail_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EstateModule::burn_undeployed_land_blocks(RuntimeOrigin::root(), 0), + Error::::UndeployedLandBlockNotFound + ); + }); +} + +#[test] +fn burn_undeployed_land_block_should_fail_if_already_in_auction() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 1, + UndeployedLandBlockType::Transferable, + )); + + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 21, + UndeployedLandBlockType::Transferable, + )); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 21); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_noop!( + EstateModule::burn_undeployed_land_blocks(RuntimeOrigin::root(), UNDEPLOYED_LAND_BLOCK_IN_AUCTION), + Error::::UndeployedLandBlockAlreadyInAuction + ); + }); +} + +#[test] +fn burn_undeployed_land_block_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 20, + UndeployedLandBlockType::BoundToAddress + )); + + let undeployed_land_block_id: UndeployedLandBlockId = 0; + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); + match issued_undeployed_land_block { + Some(a) => { + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 20); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + Some(()) + ); + + assert_ok!(EstateModule::burn_undeployed_land_blocks( + RuntimeOrigin::root(), + undeployed_land_block_id + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockBurnt(undeployed_land_block_id)) + ); + + assert_eq!( + EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), + None + ); + + assert_eq!(EstateModule::get_undeployed_land_block(undeployed_land_block_id), None) + }); +} + +#[test] +fn ensure_land_unit_within_land_block_bound_should_work() { + // let coordinates: Vec<(i32, i32)> = vec![(-4, 0), (-3, 0), (-3, 0), (0, 5)]; + // assert_eq!(EstateModule::verify_land_unit_in_bound(&(0, 0), &coordinates), true); + + let second_coordinates: Vec<(i32, i32)> = vec![(-204, 25), (-203, 24), (-195, 20), (-197, 16)]; + assert_eq!( + EstateModule::verify_land_unit_in_bound(&(-20, 2), &second_coordinates), + true + ); + + let third_coordinates: Vec<(i32, i32)> = vec![(-64, 5), (-64, 4), (-64, 4), (-55, -4)]; + assert_eq!( + EstateModule::verify_land_unit_in_bound(&(-6, 0), &third_coordinates), + true + ); + + // Combined in and out bound should fail + let fourth_coordinates: Vec<(i32, i32)> = vec![(-5, 3), (-4, 6), (-5, 4)]; + assert_eq!( + EstateModule::verify_land_unit_in_bound(&(0, 0), &fourth_coordinates), + false + ); +} + +#[test] +fn ensure_land_unit_out_of_land_block_bound_should_fail() { + let coordinates: Vec<(i32, i32)> = vec![(-51, 0), (-48, 0), (-47, 0), (0, 51)]; + assert_eq!(EstateModule::verify_land_unit_in_bound(&(0, 0), &coordinates), false); + + let second_coordinates: Vec<(i32, i32)> = vec![(-250, 2), (-248, 2), (-150, 2), (-151, 6)]; + assert_eq!( + EstateModule::verify_land_unit_in_bound(&(-200, 2), &second_coordinates), + false + ); +} + +#[test] +fn issue_land_block_and_create_estate_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::issue_undeployed_land_blocks( + RuntimeOrigin::root(), + BOB, + 1, + 2, + UndeployedLandBlockType::BoundToAddress + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(BOB, 0)) + ); + + assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); + + let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); + match issued_undeployed_land_block { + Some(a) => { + // Verify details of UndeployedLandBlock + assert_eq!(a.owner, BOB); + assert_eq!(a.number_land_units, 2); + assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); + assert_eq!(a.is_locked, false); + } + _ => { + // Should fail test + assert_eq!(0, 1); + } + } + + // Bob can deploy raw land block to his metaverse + assert_ok!(EstateModule::deploy_land_block( + RuntimeOrigin::signed(BOB), + 0, + BOB_METAVERSE_ID, + LANDBLOCK_COORDINATE, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + assert_eq!(Balances::free_balance(BOB), 99998); + + assert_eq!( + EstateModule::get_land_units(BOB_METAVERSE_ID, COORDINATE_IN_1), + Some(OwnerId::Token(METAVERSE_LAND_CLASS, 2)) + ); + + assert_eq!( + EstateModule::get_land_units(BOB_METAVERSE_ID, COORDINATE_IN_2), + Some(OwnerId::Token(METAVERSE_LAND_CLASS, 2)) + ); + }); +} + +#[test] +fn create_estate_lease_offer_should_fail() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 1u64, 10u128, 8u32), + Error::::EstateDoesNotExist + ); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, 10u128, 8u32), + Error::::NoPermission + ); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, 0u128, 8u32), + Error::::LeaseOfferPriceBelowMinimum + ); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, 2u128, 1000u32), + Error::::LeaseOfferDurationAboveMaximum + ); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, 2u128, 7u32), + Error::::LeaseOfferAlreadyExists + ); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(CHARLIE), 0u64, 12u128, 8u32), + Error::::EstateIsAlreadyLeased + ); + + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + AUCTION_BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_2] + )); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(BOB), 1u64, 100u128, 8u32), + Error::::EstateAlreadyInAuction + ); + + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_3] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(BOB), + 2u64, + 12u128, + 8u32 + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 2u64, + 13u128, + 8u32 + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(CHARLIE), + 2u64, + 14u128, + 8u32 + )); + + assert_noop!( + EstateModule::create_lease_offer(RuntimeOrigin::signed(DOM), 2u64, 15u128, 8u32), + Error::::EstateLeaseOffersQueueLimitIsReached + ); + }); +} + +#[test] +fn create_estate_lease_offer_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseOfferCreated(ALICE, 0, 80)) + ); + + let lease_contract = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 7, + start_block: 8, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); + + assert_eq!(Balances::free_balance(ALICE), 99920); + }); +} + +#[test] +fn accept_estate_lease_offer_should_fail() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(BOB), + 0u64, + 10u128, + 8u32 + )); + + assert_noop!( + EstateModule::accept_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, BOB), + Error::::NoPermission + ); + //TO DO: Offer cannot be accepted after asset is listed on auction + + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(BOB), + 1u64, + 10u128, + 8u32 + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 1u64, + 10u128, + 8u32 + )); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 1u64, + ALICE + )); + + assert_noop!( + EstateModule::accept_lease_offer(RuntimeOrigin::signed(BENEFICIARY_ID), 1u64, BOB), + Error::::EstateIsAlreadyLeased + ); + + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_3] + )); + + assert_noop!( + EstateModule::accept_lease_offer(RuntimeOrigin::signed(BENEFICIARY_ID), 2u64, BOB), + Error::::LeaseOfferDoesNotExist + ); + }); +} + +#[test] +fn accept_estate_lease_offer_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + let lease_contract = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 7, + start_block: 8, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) + ); + + let lease = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 9, + start_block: 1, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::leases(0u64), Some(lease)); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); + + assert_eq!(Balances::free_balance(ALICE), 99920); + }); +} + +#[test] +fn cancel_lease_should_fail() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_noop!( + EstateModule::cancel_lease(RuntimeOrigin::signed(BOB), BENEFICIARY_ID, 0u64, ALICE), + BadOrigin + ); + + assert_noop!( + EstateModule::cancel_lease(RuntimeOrigin::root(), BENEFICIARY_ID, 1u64, ALICE), + Error::::LeaseDoesNotExist + ); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_noop!( + EstateModule::cancel_lease(RuntimeOrigin::root(), BENEFICIARY_ID, 0u64, BOB), + Error::::LeaseDoesNotExist + ); + assert_noop!( + EstateModule::cancel_lease(RuntimeOrigin::root(), BOB, 0u64, ALICE), + Error::::NoPermission + ); + + run_to_block(22); + + assert_noop!( + EstateModule::cancel_lease(RuntimeOrigin::root(), BENEFICIARY_ID, 0u64, ALICE), + Error::::LeaseIsExpired + ); + }); +} + +#[test] +fn cancel_lease_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + let lease_contract = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 7, + start_block: 8, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) + ); + + let lease = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 9, + start_block: 1, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::leases(0u64), Some(lease)); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + run_to_block(5); + + assert_ok!(EstateModule::cancel_lease( + RuntimeOrigin::root(), + BENEFICIARY_ID, + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseContractCancelled(0)) + ); + + assert_eq!(EstateModule::leases(0u64), None); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), None); + + assert_eq!(Balances::free_balance(ALICE), 99960); + assert_eq!(Balances::free_balance(BENEFICIARY_ID), 1000039); + }); +} + +#[test] +fn remove_expired_lease_should_fail() { + ExtBuilder::default().build().execute_with(|| { + // Mint estate + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_noop!( + EstateModule::remove_expired_lease(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), + Error::::LeaseDoesNotExist + ); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + run_to_block(3); + + assert_noop!( + EstateModule::remove_expired_lease(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), + Error::::LeaseIsNotExpired + ); + + run_to_block(22); + + assert_noop!( + EstateModule::remove_expired_lease(RuntimeOrigin::signed(BOB), 0u64, ALICE), + Error::::NoPermission + ); + }); +} + +#[test] +fn remove_expired_lease_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + let lease_contract = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 7, + start_block: 8, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) + ); + + let lease = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 9, + start_block: 1, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::leases(0u64), Some(lease)); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + run_to_block(10); + + assert_ok!(EstateModule::remove_expired_lease( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseContractEnded(0u64)) + ); + + assert_eq!(EstateModule::leases(0u64), None); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), None); + + assert_eq!(Balances::free_balance(ALICE), 99920); + assert_eq!(Balances::free_balance(BENEFICIARY_ID), 1000079); + }); +} + +#[test] +fn remove_lease_offer_should_fail() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1] + )); + + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_noop!( + EstateModule::remove_lease_offer(RuntimeOrigin::signed(BOB), 0u64), + Error::::LeaseOfferDoesNotExist + ); + + assert_noop!( + EstateModule::remove_lease_offer(RuntimeOrigin::signed(ALICE), 1u64), + Error::::LeaseOfferDoesNotExist + ); + }); +} + +#[test] +fn remove_lease_offer_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + let lease_contract = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 7, + start_block: 8, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); + + assert_ok!(EstateModule::remove_lease_offer(RuntimeOrigin::signed(ALICE), 0u64)); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseOfferRemoved(ALICE, 0u64)) + ); + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); + assert_eq!(Balances::free_balance(ALICE), 100000); + }); +} + +#[test] +fn collect_rent_should_fail() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_noop!( + EstateModule::collect_rent(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), + Error::::LeaseDoesNotExist + ); + + assert_noop!( + EstateModule::collect_rent(RuntimeOrigin::signed(ALICE), 0u64, BENEFICIARY_ID), + Error::::NoPermission + ); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_noop!( + EstateModule::collect_rent(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, BOB), + Error::::LeaseDoesNotExist + ); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + run_to_block(22); + + assert_noop!( + EstateModule::collect_rent(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), + Error::::LeaseIsExpired + ); + }); +} + +#[test] +fn collect_rent_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EstateModule::mint_estate( + RuntimeOrigin::root(), + BENEFICIARY_ID, + METAVERSE_ID, + vec![COORDINATE_IN_1, COORDINATE_IN_2] + )); + + assert_ok!(EstateModule::create_lease_offer( + RuntimeOrigin::signed(ALICE), + 0u64, + 10u128, + 8u32 + )); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + let lease_contract = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 7, + start_block: 8, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); + + assert_ok!(EstateModule::accept_lease_offer( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) + ); + + let mut lease = LeaseContract { + price_per_block: 10u128, + duration: 8u32, + end_block: 9, + start_block: 1, + unclaimed_rent: 80u128, + }; + + assert_eq!(EstateModule::leases(0u64), Some(lease.clone())); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); + + assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); + + assert_eq!(Balances::free_balance(ALICE), 99920); + + run_to_block(4); + + assert_ok!(EstateModule::collect_rent( + RuntimeOrigin::signed(BENEFICIARY_ID), + 0u64, + ALICE + )); + + assert_eq!( + last_event(), + RuntimeEvent::Estate(crate::Event::EstateRentCollected(0, 30)) + ); + + lease.unclaimed_rent = 50u128; + + assert_eq!(EstateModule::leases(0u64), Some(lease)); + + assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); + + assert_eq!(Balances::free_balance(ALICE), 99920); + assert_eq!(Balances::free_balance(BENEFICIARY_ID), 1000029); + }); +} diff --git a/pallets/land-minting/src/weights.rs b/pallets/land-minting/src/weights.rs new file mode 100644 index 000000000..f848ec15c --- /dev/null +++ b/pallets/land-minting/src/weights.rs @@ -0,0 +1,699 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for estate +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-18, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/metaverse-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// estate +// --extrinsic +// * +// --steps +// 20 +// --repeat +// 10 +// --template=./template/weight-template.hbs +// --output +// ./pallets/estate/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for estate. +pub trait WeightInfo { fn mint_land() -> Weight; fn mint_lands() -> Weight; fn transfer_land() -> Weight; fn mint_estate() -> Weight; fn dissolve_estate() -> Weight; fn add_land_unit_to_estate() -> Weight; fn remove_land_unit_from_estate() -> Weight; fn create_estate() -> Weight; fn transfer_estate() -> Weight; fn issue_undeployed_land_blocks() -> Weight; fn freeze_undeployed_land_blocks() -> Weight; fn unfreeze_undeployed_land_blocks() -> Weight; fn approve_undeployed_land_blocks() -> Weight; fn unapprove_undeployed_land_blocks() -> Weight; fn transfer_undeployed_land_blocks() -> Weight; fn deploy_land_block() -> Weight; fn burn_undeployed_land_blocks() -> Weight; fn create_lease_offer() -> Weight; fn accept_lease_offer() -> Weight; fn cancel_lease() -> Weight; fn remove_expired_lease() -> Weight; fn remove_lease_offer() -> Weight; fn collect_rent() -> Weight; fn on_initialize() -> Weight;} + +/// Weights for estate using the for collator node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: Estate AllLandUnitsCount (r:1 w:1) + // Proof Skipped: Estate AllLandUnitsCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:1) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn mint_land() -> Weight { + // Proof Size summary in bytes: + // Measured: `2339` + // Estimated: `36660` + // Minimum execution time: 56_882 nanoseconds. + Weight::from_parts(59_273_000, 36660) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(7)) + } + // Storage: Estate LandUnits (r:2 w:2) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:2 w:2) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: Estate AllLandUnitsCount (r:1 w:1) + // Proof Skipped: Estate AllLandUnitsCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:2) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn mint_lands() -> Weight { + // Proof Size summary in bytes: + // Measured: `2339` + // Estimated: `41610` + // Minimum execution time: 82_026 nanoseconds. + Weight::from_parts(83_541_000, 41610) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(10)) + } + // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT StackableCollection (r:1 w:0) + // Proof Skipped: OrmlNFT StackableCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:0) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:2) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn transfer_land() -> Weight { + // Proof Size summary in bytes: + // Measured: `1915` + // Estimated: `28255` + // Minimum execution time: 46_193 nanoseconds. + Weight::from_parts(47_423_000, 28255) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: Estate NextEstateId (r:1 w:1) + // Proof Skipped: Estate NextEstateId (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Estate AllLandUnitsCount (r:1 w:1) + // Proof Skipped: Estate AllLandUnitsCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate AllEstatesCount (r:1 w:1) + // Proof Skipped: Estate AllEstatesCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate EstateOwner (r:0 w:1) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Estate Estates (r:0 w:1) + // Proof Skipped: Estate Estates (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:1) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn mint_estate() -> Weight { + // Proof Size summary in bytes: + // Measured: `2356` + // Estimated: `47210` + // Minimum execution time: 62_931 nanoseconds. + Weight::from_parts(64_479_000, 47210) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(11)) + } + // Storage: Estate EstateOwner (r:1 w:1) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:2 w:2) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Estate Estates (r:1 w:1) + // Proof Skipped: Estate Estates (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:2 w:2) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: Estate AllEstatesCount (r:1 w:1) + // Proof Skipped: Estate AllEstatesCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:2) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn dissolve_estate() -> Weight { + // Proof Size summary in bytes: + // Measured: `3118` + // Estimated: `67224` + // Minimum execution time: 99_063 nanoseconds. + Weight::from_parts(110_008_000, 67224) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(13)) + } + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:2 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Estate Estates (r:1 w:1) + // Proof Skipped: Estate Estates (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:1) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn add_land_unit_to_estate() -> Weight { + // Proof Size summary in bytes: + // Measured: `2593` + // Estimated: `38079` + // Minimum execution time: 69_608 nanoseconds. + Weight::from_parts(71_706_000, 38079) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(6)) + } + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:2 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Estate Estates (r:1 w:1) + // Proof Skipped: Estate Estates (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:1) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn remove_land_unit_from_estate() -> Weight { + // Proof Size summary in bytes: + // Measured: `3027` + // Estimated: `60226` + // Minimum execution time: 86_578 nanoseconds. + Weight::from_parts(90_257_000, 60226) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().writes(8)) + } + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate NextEstateId (r:1 w:1) + // Proof Skipped: Estate NextEstateId (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:3 w:3) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:2 w:2) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: Estate LandUnits (r:2 w:2) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Estate AllEstatesCount (r:1 w:1) + // Proof Skipped: Estate AllEstatesCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate EstateOwner (r:0 w:1) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Estate Estates (r:0 w:1) + // Proof Skipped: Estate Estates (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:3) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn create_estate() -> Weight { + // Proof Size summary in bytes: + // Measured: `3192` + // Estimated: `66058` + // Minimum execution time: 121_302 nanoseconds. + Weight::from_parts(131_741_000, 66058) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(17)) + } + // Storage: Estate EstateOwner (r:1 w:1) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Estate Estates (r:1 w:0) + // Proof Skipped: Estate Estates (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeases (r:1 w:0) + // Proof Skipped: Estate EstateLeases (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT StackableCollection (r:1 w:0) + // Proof Skipped: OrmlNFT StackableCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:0) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:2) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn transfer_estate() -> Weight { + // Proof Size summary in bytes: + // Measured: `2025` + // Estimated: `38025` + // Minimum execution time: 52_643 nanoseconds. + Weight::from_parts(54_808_000, 38025) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate NextUndeployedLandBlockId (r:1 w:1) + // Proof Skipped: Estate NextUndeployedLandBlockId (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate TotalUndeployedLandUnit (r:1 w:1) + // Proof Skipped: Estate TotalUndeployedLandUnit (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate UndeployedLandBlocks (r:0 w:20) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Estate UndeployedLandBlocksOwner (r:0 w:20) + // Proof Skipped: Estate UndeployedLandBlocksOwner (max_values: None, max_size: None, mode: Measured) + fn issue_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `1558` + // Estimated: `9825` + // Minimum execution time: 159_581 nanoseconds. + Weight::from_parts(162_875_000, 9825) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(43)) + } + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + fn freeze_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `1442` + // Estimated: `7834` + // Minimum execution time: 21_762 nanoseconds. + Weight::from_parts(22_833_000, 7834) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + fn unfreeze_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `1442` + // Estimated: `7834` + // Minimum execution time: 20_225 nanoseconds. + Weight::from_parts(21_423_000, 7834) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + fn approve_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `1442` + // Estimated: `7834` + // Minimum execution time: 20_246 nanoseconds. + Weight::from_parts(21_819_000, 7834) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + fn unapprove_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `1475` + // Estimated: `7900` + // Minimum execution time: 20_346 nanoseconds. + Weight::from_parts(21_237_000, 7900) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Estate UndeployedLandBlocksOwner (r:0 w:2) + // Proof Skipped: Estate UndeployedLandBlocksOwner (max_values: None, max_size: None, mode: Measured) + fn transfer_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `2040` + // Estimated: `13673` + // Minimum execution time: 38_679 nanoseconds. + Weight::from_parts(41_173_000, 13673) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: Metaverse MetaverseOwner (r:1 w:0) + // Proof Skipped: Metaverse MetaverseOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate LandUnits (r:1 w:1) + // Proof Skipped: Estate LandUnits (max_values: None, max_size: None, mode: Measured) + // Storage: Metaverse Metaverses (r:1 w:0) + // Proof Skipped: Metaverse Metaverses (max_values: None, max_size: None, mode: Measured) + // Storage: Nft LockedCollection (r:1 w:0) + // Proof Skipped: Nft LockedCollection (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT NextTokenId (r:1 w:1) + // Proof Skipped: OrmlNFT NextTokenId (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Classes (r:1 w:1) + // Proof Skipped: OrmlNFT Classes (max_values: None, max_size: None, mode: Measured) + // Storage: Estate AllLandUnitsCount (r:1 w:1) + // Proof Skipped: Estate AllLandUnitsCount (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate TotalUndeployedLandUnit (r:1 w:1) + // Proof Skipped: Estate TotalUndeployedLandUnit (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate UndeployedLandBlocksOwner (r:0 w:1) + // Proof Skipped: Estate UndeployedLandBlocksOwner (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT TokensByOwner (r:0 w:1) + // Proof Skipped: OrmlNFT TokensByOwner (max_values: None, max_size: None, mode: Measured) + fn deploy_land_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `2802` + // Estimated: `64897` + // Minimum execution time: 91_163 nanoseconds. + Weight::from_parts(100_659_000, 64897) + .saturating_add(T::DbWeight::get().reads(13)) + .saturating_add(T::DbWeight::get().writes(11)) + } + // Storage: Estate UndeployedLandBlocks (r:1 w:1) + // Proof Skipped: Estate UndeployedLandBlocks (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: Estate TotalUndeployedLandUnit (r:1 w:1) + // Proof Skipped: Estate TotalUndeployedLandUnit (max_values: Some(1), max_size: None, mode: Measured) + // Storage: Estate UndeployedLandBlocksOwner (r:0 w:1) + // Proof Skipped: Estate UndeployedLandBlocksOwner (max_values: None, max_size: None, mode: Measured) + fn burn_undeployed_land_blocks() -> Weight { + // Proof Size summary in bytes: + // Measured: `1304` + // Estimated: `10661` + // Minimum execution time: 23_162 nanoseconds. + Weight::from_parts(32_196_000, 10661) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:0) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeaseOffers (r:2 w:1) + // Proof Skipped: Estate EstateLeaseOffers (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeases (r:1 w:0) + // Proof Skipped: Estate EstateLeases (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn create_lease_offer() -> Weight { + // Proof Size summary in bytes: + // Measured: `1986` + // Estimated: `27383` + // Minimum execution time: 94_497 nanoseconds. + Weight::from_parts(98_902_000, 27383) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Estate EstateLeases (r:1 w:1) + // Proof Skipped: Estate EstateLeases (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: Auction ItemsInAuction (r:1 w:0) + // Proof Skipped: Auction ItemsInAuction (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeaseOffers (r:1 w:1) + // Proof Skipped: Estate EstateLeaseOffers (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Estate EstateLeasors (r:0 w:1) + // Proof Skipped: Estate EstateLeasors (max_values: None, max_size: None, mode: Measured) + fn accept_lease_offer() -> Weight { + // Proof Size summary in bytes: + // Measured: `2311` + // Estimated: `28844` + // Minimum execution time: 53_956 nanoseconds. + Weight::from_parts(57_019_000, 28844) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: Estate EstateLeases (r:1 w:1) + // Proof Skipped: Estate EstateLeases (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeasors (r:1 w:1) + // Proof Skipped: Estate EstateLeasors (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn cancel_lease() -> Weight { + // Proof Size summary in bytes: + // Measured: `4017` + // Estimated: `28571` + // Minimum execution time: 55_907 nanoseconds. + Weight::from_parts(57_720_000, 28571) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: Estate EstateLeases (r:1 w:1) + // Proof Skipped: Estate EstateLeases (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeasors (r:1 w:1) + // Proof Skipped: Estate EstateLeasors (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:1) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn remove_expired_lease() -> Weight { + // Proof Size summary in bytes: + // Measured: `4017` + // Estimated: `28571` + // Minimum execution time: 56_789 nanoseconds. + Weight::from_parts(57_681_000, 28571) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: Estate EstateLeaseOffers (r:1 w:1) + // Proof Skipped: Estate EstateLeaseOffers (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn remove_lease_offer() -> Weight { + // Proof Size summary in bytes: + // Measured: `3250` + // Estimated: `8328` + // Minimum execution time: 33_999 nanoseconds. + Weight::from_parts(41_923_000, 8328) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Estate EstateOwner (r:1 w:0) + // Proof Skipped: Estate EstateOwner (max_values: None, max_size: None, mode: Measured) + // Storage: OrmlNFT Tokens (r:1 w:0) + // Proof Skipped: OrmlNFT Tokens (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeasors (r:1 w:0) + // Proof Skipped: Estate EstateLeasors (max_values: None, max_size: None, mode: Measured) + // Storage: Estate EstateLeases (r:1 w:1) + // Proof Skipped: Estate EstateLeases (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:1 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn collect_rent() -> Weight { + // Proof Size summary in bytes: + // Measured: `4017` + // Estimated: `28571` + // Minimum execution time: 51_821 nanoseconds. + Weight::from_parts(53_171_000, 28571) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + fn on_initialize() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 176 nanoseconds. + Weight::from_parts(191_000, 0) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { fn mint_land() -> Weight { + Weight::from_parts(59_273_000, 36660) + .saturating_add(RocksDbWeight::get().reads(8)) + .saturating_add(RocksDbWeight::get().writes(7)) + } + fn mint_lands() -> Weight { + Weight::from_parts(83_541_000, 41610) + .saturating_add(RocksDbWeight::get().reads(10)) + .saturating_add(RocksDbWeight::get().writes(10)) + } + fn transfer_land() -> Weight { + Weight::from_parts(47_423_000, 28255) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(4)) + } + fn mint_estate() -> Weight { + Weight::from_parts(64_479_000, 47210) + .saturating_add(RocksDbWeight::get().reads(10)) + .saturating_add(RocksDbWeight::get().writes(11)) + } + fn dissolve_estate() -> Weight { + Weight::from_parts(110_008_000, 67224) + .saturating_add(RocksDbWeight::get().reads(14)) + .saturating_add(RocksDbWeight::get().writes(13)) + } + fn add_land_unit_to_estate() -> Weight { + Weight::from_parts(71_706_000, 38079) + .saturating_add(RocksDbWeight::get().reads(8)) + .saturating_add(RocksDbWeight::get().writes(6)) + } + fn remove_land_unit_from_estate() -> Weight { + Weight::from_parts(90_257_000, 60226) + .saturating_add(RocksDbWeight::get().reads(12)) + .saturating_add(RocksDbWeight::get().writes(8)) + } + fn create_estate() -> Weight { + Weight::from_parts(131_741_000, 66058) + .saturating_add(RocksDbWeight::get().reads(14)) + .saturating_add(RocksDbWeight::get().writes(17)) + } + fn transfer_estate() -> Weight { + Weight::from_parts(54_808_000, 38025) + .saturating_add(RocksDbWeight::get().reads(8)) + .saturating_add(RocksDbWeight::get().writes(4)) + } + fn issue_undeployed_land_blocks() -> Weight { + Weight::from_parts(162_875_000, 9825) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(43)) + } + fn freeze_undeployed_land_blocks() -> Weight { + Weight::from_parts(22_833_000, 7834) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + fn unfreeze_undeployed_land_blocks() -> Weight { + Weight::from_parts(21_423_000, 7834) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + fn approve_undeployed_land_blocks() -> Weight { + Weight::from_parts(21_819_000, 7834) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + fn unapprove_undeployed_land_blocks() -> Weight { + Weight::from_parts(21_237_000, 7900) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + fn transfer_undeployed_land_blocks() -> Weight { + Weight::from_parts(41_173_000, 13673) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(4)) + } + fn deploy_land_block() -> Weight { + Weight::from_parts(100_659_000, 64897) + .saturating_add(RocksDbWeight::get().reads(13)) + .saturating_add(RocksDbWeight::get().writes(11)) + } + fn burn_undeployed_land_blocks() -> Weight { + Weight::from_parts(32_196_000, 10661) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) + } + fn create_lease_offer() -> Weight { + Weight::from_parts(98_902_000, 27383) + .saturating_add(RocksDbWeight::get().reads(7)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + fn accept_lease_offer() -> Weight { + Weight::from_parts(57_019_000, 28844) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + fn cancel_lease() -> Weight { + Weight::from_parts(57_720_000, 28571) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(4)) + } + fn remove_expired_lease() -> Weight { + Weight::from_parts(57_681_000, 28571) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(4)) + } + fn remove_lease_offer() -> Weight { + Weight::from_parts(41_923_000, 8328) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + fn collect_rent() -> Weight { + Weight::from_parts(53_171_000, 28571) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + fn on_initialize() -> Weight { + Weight::from_parts(191_000, 0) + } +} diff --git a/pallets/reward/src/tests.rs b/pallets/reward/src/tests.rs index ea8d3466d..96cd76536 100644 --- a/pallets/reward/src/tests.rs +++ b/pallets/reward/src/tests.rs @@ -2288,7 +2288,7 @@ fn js_generated_leafs_match_blockchain_generated_leafs() { } #[test] -fn merkle_proof_based_cmapaing_works_with_js_generated_root() { +fn merkle_proof_based_campaign_works_with_js_generated_root() { ExtBuilder::default().build().execute_with(|| { let campaign_id = 0; assert_ok!(Reward::add_set_reward_origin(RuntimeOrigin::signed(ALICE), ALICE)); From 540d309c81454a213b1bc247e80cbe768c518da1 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 29 Oct 2023 17:25:22 +1300 Subject: [PATCH 027/155] Create the first basic implementation of the land minting logic --- Cargo.lock | 24 + pallets/land-minting/Cargo.toml | 66 + pallets/land-minting/src/lib.rs | 2148 +------------------------------ 3 files changed, 101 insertions(+), 2137 deletions(-) create mode 100644 pallets/land-minting/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 23b66e533..f3fe62500 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7583,6 +7583,30 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-land-minting" +version = "2.0.0-rc6" +dependencies = [ + "auction-manager", + "bit-country-primitives", + "core-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "orml-tokens", + "orml-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "substrate-fixed", +] + [[package]] name = "pallet-membership" version = "4.0.0-dev" diff --git a/pallets/land-minting/Cargo.toml b/pallets/land-minting/Cargo.toml new file mode 100644 index 000000000..1b68911fd --- /dev/null +++ b/pallets/land-minting/Cargo.toml @@ -0,0 +1,66 @@ +[package] +authors = ['Metaverse Network '] +description = 'Metaverse Network pallet for land minting logic.' +edition = '2021' +homepage = 'https://metaverse.network' +license = 'Unlicense' +name = 'pallet-land-minting' +repository = 'https://github.com/bit-country' +version = '2.0.0-rc6' + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +serde = { workspace = true, optional = true } +scale-info = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-version = { workspace = true } +sp-std = { workspace = true } +sp-io = { workspace = true } +substrate-fixed = { workspace = true } +pallet-balances = { workspace = true, optional = true } +orml-traits = { workspace = true } +orml-tokens = { workspace = true } + +# local packages +core-primitives = { path = "../../traits/core-primitives", default-features = false } +primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } + + +[dependencies.auction-manager] +default-features = false +package = 'auction-manager' +path = '../../traits/auction-manager' +version = '2.0.0-rc6' + + + +[features] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +default = ['std'] +std = [ + 'codec/std', + "serde", + 'scale-info/std', + 'frame-support/std', + 'frame-system/std', + 'sp-runtime/std', + 'sp-core/std', + 'core-primitives/std', + 'primitives/std', + 'sp-io/std', + 'pallet-balances/std', + 'auction-manager/std', + # 'pallet-nft/std', + 'frame-benchmarking/std' +] \ No newline at end of file diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 3c124e779..ecc87bf3c 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -27,7 +27,6 @@ use frame_support::{ use frame_system::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed}; use scale_info::TypeInfo; - use sp_runtime::{ traits::{AccountIdConversion, Convert, One, Saturating, Zero}, ArithmeticError, DispatchError, Perbill, SaturatedConversion, @@ -61,11 +60,13 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; + use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_runtime::traits::{CheckedAdd, CheckedSub, Zero}; + use sp_runtime::Permill; use primitives::estate::EstateInfo; use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; - use primitives::{Balance, RoundIndex, UndeployedLandBlockId}; + use primitives::{Balance, CurrencyId, RoundIndex, UndeployedLandBlockId}; use crate::rate::{round_issuance_range, MintingRateInfo}; @@ -90,19 +91,8 @@ pub mod pallet { /// Currency type type Currency: Currency + ReservableCurrency; - - /// Minimum land price - type MinimumLandPrice: Get>; - - /// Council origin which allows to update max bound - type CouncilOrigin: EnsureOrigin; - - /// Auction handler - type AuctionHandler: Auction + CheckAuctionItemHandler>; - - /// Minimum number of blocks per round - #[pallet::constant] - type MinBlocksPerRound: Get; + /// Multi currencies type that handles different currency type in auction + type MultiCurrency: MultiReservableCurrency; /// Weight implementation for estate extrinsics type WeightInfo: WeightInfo; @@ -125,22 +115,6 @@ pub mod pallet { #[pallet::constant] type NetworkFee: Get>; - /// Maximum lease offers for an estate - #[pallet::constant] - type MaxOffersPerEstate: Get; - - /// Minimum lease price per block - #[pallet::constant] - type MinLeasePricePerBlock: Get>; - - /// Maximum lease period duration (in number of blocks) - #[pallet::constant] - type MaxLeasePeriod: Get; - - /// The period for each lease offer will be available for acceptance (in number of blocks) - #[pallet::constant] - type LeaseOfferExpiryPeriod: Get; - /// Storage deposit free charged when saving data into the blockchain. /// The fee will be unreserved after the storage is freed. #[pallet::constant] @@ -151,102 +125,16 @@ pub mod pallet { } pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + pub type CurrencyIdOf = + <::MultiCurrency as MultiCurrency<::AccountId>>::CurrencyId; #[pallet::storage] - #[pallet::getter(fn all_land_units_count)] - /// Track the total number of land units - pub(super) type AllLandUnitsCount = StorageValue<_, u64, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn all_undeployed_land_unit)] - /// Track the total of undeployed land units - pub(super) type TotalUndeployedLandUnit = StorageValue<_, u64, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn get_land_units)] - /// Index land owners by metaverse ID and coordinate - pub type LandUnits = StorageDoubleMap< - _, - Twox64Concat, - MetaverseId, - Twox64Concat, - (i32, i32), - OwnerId, - OptionQuery, - >; - - #[pallet::storage] - #[pallet::getter(fn next_estate_id)] - /// Track the next estate ID - pub type NextEstateId = StorageValue<_, EstateId, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn all_estates_count)] - /// Track the total of estates - pub(super) type AllEstatesCount = StorageValue<_, u64, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn get_estates)] - /// Store estate information - pub(super) type Estates = StorageMap<_, Twox64Concat, EstateId, EstateInfo, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn get_estate_owner)] - /// Track estate owners - pub type EstateOwner = - StorageMap<_, Twox64Concat, EstateId, OwnerId, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn next_undeployed_land_block_id)] - /// Track the next undeployed land ID - pub(super) type NextUndeployedLandBlockId = StorageValue<_, UndeployedLandBlockId, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn get_undeployed_land_block)] - /// Store undeployed land blocks - pub(super) type UndeployedLandBlocks = - StorageMap<_, Blake2_128Concat, UndeployedLandBlockId, UndeployedLandBlock, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn get_undeployed_land_block_owner)] - /// Index undeployed land blocks by account ID - pub type UndeployedLandBlocksOwner = - StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, UndeployedLandBlockId, (), OptionQuery>; + #[pallet::getter(fn fees)] + pub type Fees = StorageValue<_, (Permill, Permill), ValueQuery>; #[pallet::storage] - #[pallet::getter(fn round)] - /// Current round index and next round scheduled transition - pub type Round = StorageValue<_, RoundInfo, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn minting_rate_config)] - /// Minting rate configuration - pub type MintingRateConfig = StorageValue<_, MintingRateInfo, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn leases)] - /// Current active estate leases - pub type EstateLeases = - StorageMap<_, Twox64Concat, EstateId, LeaseContract, T::BlockNumber>, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn leasors)] - /// Current estate leasors - pub type EstateLeasors = - StorageDoubleMap<_, Blake2_128Concat, T::AccountId, Twox64Concat, EstateId, (), OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn lease_offers)] - /// Current estate lease offers - pub type EstateLeaseOffers = StorageDoubleMap< - _, - Twox64Concat, - EstateId, - Blake2_128Concat, - T::AccountId, - LeaseContract, T::BlockNumber>, - OptionQuery, - >; + #[pallet::getter(fn token_pool)] + pub type TokenPool = StorageMap<_, Twox64Concat, (ClassId, CurrencyIdOf), BalanceOf, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -262,85 +150,9 @@ pub mod pallet { } } - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - >::put(self.minting_rate_config.clone()); - - // Start Round 1 at Block 0 - let round: RoundInfo = RoundInfo::new(1u32, 0u32.into(), T::MinBlocksPerRound::get()); - - let round_issuance_per_round = round_issuance_range::(self.minting_rate_config.clone()); - - >::put(round); - >::deposit_event(Event::NewRound( - T::BlockNumber::zero(), - 1u32, - round_issuance_per_round.max, - )); - } - } - #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { - /// New lands are minted [Beneficial Account Id, Metaverse Id, Coordinates] - NewLandsMinted(T::AccountId, MetaverseId, Vec<(i32, i32)>), - /// Land unit is transferred [Metaverse Id, Coordinates, From Account Id, To Account Id] - TransferredLandUnit(MetaverseId, (i32, i32), T::AccountId, T::AccountId), - /// Estate unit is transferred [Estate Id, From Account Id, To Account Id] - TransferredEstate(EstateId, T::AccountId, T::AccountId), - /// New land is minted [Beneficial Account Id, Metaverse Id, Coordinates] - NewLandUnitMinted(OwnerId, MetaverseId, (i32, i32)), - /// New estate is minted [Estate Id, OwnerId, Metaverse Id, Coordinates] - NewEstateMinted( - EstateId, - OwnerId, - MetaverseId, - Vec<(i32, i32)>, - ), - /// Max bound is set for a metaverse [Metaverse Id, Min and Max Coordinate] - MaxBoundSet(MetaverseId, (i32, i32)), - /// Land block is deployed [From Account Id, Metaverse Id, Undeployed Land Block Id, - /// Coordinates] - LandBlockDeployed(T::AccountId, MetaverseId, UndeployedLandBlockId, Vec<(i32, i32)>), - /// Undeployed land block is issued [Beneficial Account Id, Undeployed Land - /// Block Id] - UndeployedLandBlockIssued(T::AccountId, UndeployedLandBlockId), - /// Undeployed land block is transferred [From Account Id, To Account Id, Undeployed - /// Land Block Id] - UndeployedLandBlockTransferred(T::AccountId, T::AccountId, UndeployedLandBlockId), - /// Undeployed land block is approved [Owner Account Id, Approved Account Id, Undeployed - /// Land Block Id] - UndeployedLandBlockApproved(T::AccountId, T::AccountId, UndeployedLandBlockId), - /// Estate is destroyed [Estate Id, Owner Id] - EstateDestroyed(EstateId, OwnerId), - /// Estate is updated [Estate Id, Owner Id, Coordinates] - EstateUpdated(EstateId, OwnerId, Vec<(i32, i32)>), - /// Land unit is added to an estate [Estate Id, Owner Id, Coordinates] - LandUnitAdded(EstateId, OwnerId, Vec<(i32, i32)>), - /// Land unit is removed from an estate [Estate Id, Owner Id, Coordinates] - LandUnitsRemoved(EstateId, OwnerId, Vec<(i32, i32)>), - /// Undeployed land block is unapproved [Undeployed Land Block Id] - UndeployedLandBlockUnapproved(UndeployedLandBlockId), - /// Undeployed land block is freezed [Undeployed Land Block Id] - UndeployedLandBlockFreezed(UndeployedLandBlockId), - /// Undeployed land block is unfreezed [Undeployed Land Block Id] - UndeployedLandBlockUnfreezed(UndeployedLandBlockId), - /// Undeployed land block is burnt [Undeployed Land Block Id] - UndeployedLandBlockBurnt(UndeployedLandBlockId), - /// Estate lease offer is created [AccountId, Estate Id, Total rent] - EstateLeaseOfferCreated(T::AccountId, EstateId, BalanceOf), - /// Estate lease offer is accepted [Estate Id, Leasor account Id, Lease End Block] - EstateLeaseOfferAccepted(EstateId, T::AccountId, T::BlockNumber), - /// Estate lease offer is removed [AccountId, Estate Id] - EstateLeaseOfferRemoved(T::AccountId, EstateId), - /// Estate lease contract ended [Estate Id] - EstateLeaseContractEnded(EstateId), - /// Estate lease contract was cancelled [Estate Id] - EstateLeaseContractCancelled(EstateId), - /// Estate rent collected [EstateId, Balance] - EstateRentCollected(EstateId, BalanceOf), /// New staking round started [Starting Block, Round, Total Land Unit] NewRound(T::BlockNumber, RoundIndex, u64), } @@ -349,101 +161,8 @@ pub mod pallet { pub enum Error { /// No permission NoPermission, - /// No available estate ID - NoAvailableEstateId, - /// Insufficient fund - InsufficientFund, - /// Estate ID already exist - EstateIdAlreadyExist, - /// Land unit is not available - LandUnitIsNotAvailable, - /// Land unit is out of bound - LandUnitIsOutOfBound, - /// Undeployed land block is not found - UndeployedLandBlockNotFound, - /// Undeployed land block is not transferable - UndeployedLandBlockIsNotTransferable, - /// Undeployed land block does not hae enough land units - UndeployedLandBlockDoesNotHaveEnoughLandUnits, - /// Number of land block credit and land unit does not match - UndeployedLandBlockUnitAndInputDoesNotMatch, - /// Account is not the owner of a given undeployed land block - UndeployedLandBlockNotOwned, - /// Already own the undeployed land block - AlreadyOwnTheUndeployedLandBlock, - /// Undeployed land block is freezed - UndeployedLandBlockFreezed, - /// Undeployed land block is already freezed - UndeployedLandBlockAlreadyFreezed, - /// Undeployed land block is not frozen - UndeployedLandBlockNotFrozen, - /// Already owning the estate - AlreadyOwnTheEstate, - /// Already owning the land unit - AlreadyOwnTheLandUnit, - /// Estate is not in auction - EstateNotInAuction, - /// Land unit is not in auction - LandUnitNotInAuction, - /// Estate is already in auction - EstateAlreadyInAuction, - /// Land unit is already in auction - LandUnitAlreadyInAuction, - /// Undeployed land block is already in auction - UndeployedLandBlockAlreadyInAuction, - /// Estate is does not exist - EstateDoesNotExist, - /// Land unit does not exist - LandUnitDoesNotExist, - /// Only frozen undeployed land block can be destroyed - OnlyFrozenUndeployedLandBlockCanBeDestroyed, - /// Below minimum staking amount - BelowMinimumStake, - /// Value overflow - Overflow, - /// Estate stake is already left - EstateStakeAlreadyLeft, - /// Account has not staked anything - AccountHasNoStake, - /// Invalid owner value - InvalidOwnerValue, - /// Coordinate for estate is not valid - CoordinatesForEstateIsNotValid, - /// Insufficient balance for deploying land blocks or creating estates - InsufficientBalanceForDeployingLandOrCreatingEstate, - // Land Unit already formed in Estate - LandUnitAlreadyInEstate, - /// Estate is already leased - EstateIsAlreadyLeased, - /// Estate lease offer limit is reached - EstateLeaseOffersQueueLimitIsReached, - /// Lease offer price per block is below the minimum - LeaseOfferPriceBelowMinimum, - /// Lease offer does not exist - LeaseOfferDoesNotExist, - /// Lease offer already exists - LeaseOfferAlreadyExists, - /// Lease offer is not expired - LeaseOfferIsNotExpired, - /// Lease does not exist - LeaseDoesNotExist, - /// Lease is not expired - LeaseIsNotExpired, - /// Lease is expired - LeaseIsExpired, - /// Lease duration beyond max duration - LeaseOfferDurationAboveMaximum, - /// No unclaimed rent balance - NoUnclaimedRentLeft, } - // TO DO: Implement offchain removal of expired lease offers - //#[pallet::hooks] - //impl Hooks> for Pallet { - // fn offchain_worker(block_number: T::BlockNumber) { - // } - //} - #[pallet::call] impl Pallet { /// Minting of a land unit, only used by council to manually mint single land for @@ -464,1115 +183,8 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_root(origin)?; - // Mint land unit - let owner = Self::mint_land_unit( - metaverse_id, - OwnerId::Account(beneficiary.clone()), - beneficiary, - coordinate, - LandUnitStatus::NonExisting, - )?; - - // Update total land count - Self::set_total_land_unit(One::one(), false)?; - - Self::deposit_event(Event::::NewLandUnitMinted(owner, metaverse_id, coordinate)); - - Ok(().into()) - } - - /// Minting of a land units, only used by council to manually mint number of lands for - /// beneficiary - /// - /// The dispatch origin for this call must be _Root_. - /// - `beneficiary`: the account which will be the owner of the land units - /// - `metaverse_id`: the metaverse id that the land units will be minted on - /// - `coordinates`: list of land units coordinates - /// - /// Emits `NewLandsMinted` if successful. - #[pallet::weight(T::WeightInfo::mint_lands())] - pub fn mint_lands( - origin: OriginFor, - beneficiary: T::AccountId, - metaverse_id: MetaverseId, - coordinates: Vec<(i32, i32)>, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - // Mint land units - for coordinate in coordinates.clone() { - Self::mint_land_unit( - metaverse_id, - OwnerId::Account(beneficiary.clone()), - beneficiary.clone(), - coordinate, - LandUnitStatus::NonExisting, - )?; - } - - // Update total land count - Self::set_total_land_unit(coordinates.len() as u64, false)?; - - Self::deposit_event(Event::::NewLandsMinted( - beneficiary.clone(), - metaverse_id.clone(), - coordinates.clone(), - )); - - Ok(().into()) - } - - /// Mint new estate with no existing land units, only used for council to manually mint - /// estate for beneficiary - /// - /// The dispatch origin for this call must be _Root_. - /// - `beneficiary`: the account which will be the owner of the land units - /// - `metaverse_id`: the metaverse id that the land units will be minted on - /// - `coordinates`: list of land units coordinates - /// - /// Emits `NewEstateMinted` if successful. - #[pallet::weight(T::WeightInfo::mint_estate())] - pub fn mint_estate( - origin: OriginFor, - beneficiary: T::AccountId, - metaverse_id: MetaverseId, - coordinates: Vec<(i32, i32)>, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - // Generate new estate id - let new_estate_id = Self::get_new_estate_id()?; - - // Generate sub account from estate - let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(new_estate_id); - - // Estate as owner - let token_properties = Self::get_estate_token_properties(metaverse_id, new_estate_id); - let class_id = T::MetaverseInfoSource::get_metaverse_estate_class(metaverse_id)?; - let token_id: TokenId = - T::NFTTokenizationSource::mint_token(&beneficiary, class_id, token_properties.0, token_properties.1)?; - let token_owner = OwnerId::Token(class_id, token_id); - - // Mint land units - for coordinate in coordinates.clone() { - Self::mint_land_unit( - metaverse_id, - token_owner.clone(), - estate_account_id.clone(), - coordinate, - LandUnitStatus::NonExistingWithEstate, - )?; - } - // Update total land count - Self::set_total_land_unit(coordinates.len() as u64, false)?; - - // Update estate information - Self::update_estate_information(new_estate_id, metaverse_id, token_owner, coordinates)?; - Ok(().into()) - } - - /// Transferring a land unit if it is not already in auction - /// - /// The dispatch origin for this call must be _Signed_. - /// Only the owner of a land can make this call. - /// - `to`: the account which will be the owner of the land units - /// - `metaverse_id`: the metaverse id of the land unit - /// - `coordinate`: the coordinate of the land unit - /// - /// Emits `TransferredLandUnit` if successful. - #[pallet::weight(T::WeightInfo::transfer_land())] - pub fn transfer_land( - origin: OriginFor, - to: T::AccountId, - metaverse_id: MetaverseId, - coordinate: (i32, i32), - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - Self::do_transfer_landunit(coordinate, &who, &to, metaverse_id)?; - Ok(().into()) - } - - /// Create new estate from existing land units - /// - /// The dispatch origin for this call must be _Signed_. - /// - `metaverse_id`: the metaverse id that the land units will be minted on - /// - `coordinates`: list of land units coordinates - /// - /// Emits `NewEstateMinted` if successful. - #[pallet::weight(T::WeightInfo::create_estate().saturating_mul(coordinates.len() as u64))] - #[transactional] - pub fn create_estate( - origin: OriginFor, - metaverse_id: MetaverseId, - coordinates: Vec<(i32, i32)>, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - ensure!( - Self::verify_land_unit_for_estate(coordinates.clone()), - Error::::CoordinatesForEstateIsNotValid - ); - // Collect network fee - Self::collect_network_fee(&who)?; - // Generate new estate id - let new_estate_id = Self::get_new_estate_id()?; - // Generate sub account from estate - let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(new_estate_id); - - let token_properties = Self::get_estate_token_properties(metaverse_id, new_estate_id); - let class_id = T::MetaverseInfoSource::get_metaverse_estate_class(metaverse_id)?; - let token_id: TokenId = - T::NFTTokenizationSource::mint_token(&who, class_id, token_properties.0, token_properties.1)?; - let beneficiary = OwnerId::Token(class_id, token_id); - - let storage_fee: BalanceOf = - Perbill::from_percent(100u32.saturating_mul(coordinates.len() as u32)) * T::StorageDepositFee::get(); - T::Currency::transfer( - &who, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - - // Mint land units - for coordinate in coordinates.clone() { - Self::mint_land_unit( - metaverse_id, - beneficiary.clone(), - estate_account_id.clone(), - coordinate, - LandUnitStatus::Existing(who.clone()), - )?; - } - - // Update estate information - Self::update_estate_information(new_estate_id, metaverse_id, beneficiary, coordinates.clone())?; - - Ok(().into()) - } - - /// Transfer estate ownership if it is not in auction. - /// - /// The dispatch origin for this call must be _Signed_. - /// Only the owner of an estate can make this call. - /// - `to`: the account which will be the owner of the estate - /// - `estate_id`: the estate ID of the the estate that will be transferred - /// - /// Emits `TransferredEstate` if successful. - #[pallet::weight(T::WeightInfo::transfer_estate())] - #[transactional] - pub fn transfer_estate( - origin: OriginFor, - to: T::AccountId, - estate_id: EstateId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - Self::do_transfer_estate(estate_id, &who, &to)?; - Ok(().into()) - } - - /// Deploy raw land block to metaverse and turn raw land block to land unit with given - /// coordinates - /// - /// The dispatch origin for this call must be _Signed_. - /// Only the undeployed land block owner can make this call. - /// - `undeployed_land_block_id`: the undeployed land block ID - /// - `metaverse_id`: the metaverse ID that the land block will be deployed on - /// - `land_block_coordinates`: the coordinates of the land block - /// - `coordinates`: list of land units coordinates - /// - /// Emits `LandBlockDeployed` if successful. - #[pallet::weight(T::WeightInfo::deploy_land_block() * coordinates.len() as u64)] - #[transactional] - pub fn deploy_land_block( - origin: OriginFor, - undeployed_land_block_id: UndeployedLandBlockId, - metaverse_id: MetaverseId, - land_block_coordinate: (i32, i32), - coordinates: Vec<(i32, i32)>, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - ensure!( - T::MetaverseInfoSource::check_ownership(&who, &metaverse_id), - Error::::NoPermission - ); - - // Ensure the max bound is set for the metaverse - let max_bound = T::DefaultMaxBound::get(); - - // Check whether the coordinate is within the bound - ensure!( - (land_block_coordinate.0 >= max_bound.0 && max_bound.1 >= land_block_coordinate.0) - && (land_block_coordinate.1 >= max_bound.0 && max_bound.1 >= land_block_coordinate.1), - Error::::LandUnitIsOutOfBound - ); - - ensure!( - Self::verify_land_unit_in_bound(&land_block_coordinate, &coordinates), - Error::::LandUnitIsOutOfBound - ); - - let undeployed_land_block_record = UndeployedLandBlocks::::get(undeployed_land_block_id) - .ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - undeployed_land_block_record.owner == who.clone(), - Error::::NoPermission - ); - - ensure!( - undeployed_land_block_record.is_locked == false, - Error::::UndeployedLandBlockFreezed - ); - - let land_units_to_mint = coordinates.len() as u32; - - // Ensure undeployed land block only deployed once - ensure!( - undeployed_land_block_record.number_land_units == land_units_to_mint, - Error::::UndeployedLandBlockUnitAndInputDoesNotMatch - ); - - // Collect network fee - Self::collect_network_fee(&who)?; - - // Mint land units - for coordinate in coordinates.clone() { - Self::mint_land_unit( - metaverse_id, - OwnerId::Account(who.clone()), - who.clone(), - coordinate, - LandUnitStatus::NonExisting, - )?; - } - - // Update total land count - Self::set_total_land_unit(coordinates.len() as u64, false)?; - - // Burn undeployed land block - Self::do_burn_undeployed_land_block(undeployed_land_block_id)?; - - Self::deposit_event(Event::::LandBlockDeployed( - who.clone(), - metaverse_id, - undeployed_land_block_id, - coordinates, - )); - - Ok(().into()) - } - - /// Issues new undeployed land block(s) - /// - /// The dispatch origin for this call must be _Root_. - /// - `beneficiary`: the account which will be the owner of the undeployed land block(s) - /// - `number_of_land_block`: the number of undeployed land block(s) that will be created - /// - `number_land_units_per_land_block`: the number of land units in each undeployed land - /// block - /// - `land_block_coordinates`: the coordinates of the undeployed land block - /// - /// Emits `UndeployedLandBlockIssued` if successful. - #[pallet::weight(T::WeightInfo::issue_undeployed_land_blocks())] - #[transactional] - pub fn issue_undeployed_land_blocks( - who: OriginFor, - beneficiary: T::AccountId, - number_of_land_block: u32, - number_land_units_per_land_block: u32, - undeployed_land_block_type: UndeployedLandBlockType, - ) -> DispatchResultWithPostInfo { - ensure_root(who)?; - - Self::do_issue_undeployed_land_blocks( - &beneficiary, - number_of_land_block, - number_land_units_per_land_block, - undeployed_land_block_type, - )?; - - Ok(().into()) - } - - /// Freezes undeployed land block which is not already frozen - /// - /// The dispatch origin for this call must be _Root_. - /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be freezed - /// - /// Emits `UndeployedLandBlockFreezed` if successful. - #[pallet::weight(T::WeightInfo::freeze_undeployed_land_blocks())] - #[transactional] - pub fn freeze_undeployed_land_blocks( - origin: OriginFor, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - Self::do_freeze_undeployed_land_block(undeployed_land_block_id)?; - - Ok(().into()) - } - - /// Unfreezes undeployed land block which is frozen. - /// - /// The dispatch origin for this call must be _Root_. - /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be unfreezed - /// - /// Emits `UndeployedLandBlockUnfreezed` if successful. - #[pallet::weight(T::WeightInfo::unfreeze_undeployed_land_blocks())] - #[transactional] - pub fn unfreeze_undeployed_land_blocks( - origin: OriginFor, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - UndeployedLandBlocks::::try_mutate_exists( - &undeployed_land_block_id, - |undeployed_land_block| -> DispatchResultWithPostInfo { - let undeployed_land_block_record = undeployed_land_block - .as_mut() - .ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock( - undeployed_land_block_id - )), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - ensure!( - undeployed_land_block_record.is_locked == true, - Error::::UndeployedLandBlockNotFrozen - ); - - undeployed_land_block_record.is_locked = false; - - Self::deposit_event(Event::::UndeployedLandBlockUnfreezed(undeployed_land_block_id)); - - Ok(().into()) - }, - ) - } - - /// Transfer undeployed land block owner if it is not in auction. - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the undeployed land block owner can make this call. - /// - `to`: the account that will receive the undeployed land block - /// - `undeployed_land_block_id`: the ID of the land block that will be transferred - /// - /// Emits `UndeployedLandBlockTransferred` if successful. - #[pallet::weight(T::WeightInfo::transfer_undeployed_land_blocks())] - #[transactional] - pub fn transfer_undeployed_land_blocks( - origin: OriginFor, - to: T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - Self::do_transfer_undeployed_land_block(&who, &to, undeployed_land_block_id)?; - - Ok(().into()) - } - - /// Burn raw land block that will reduce total supply - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the undeployed land block owner can make this call. - /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be burned - /// - /// Emits `UndeployedLandBlockBurnt` if successful. - #[pallet::weight(T::WeightInfo::burn_undeployed_land_blocks())] - #[transactional] - pub fn burn_undeployed_land_blocks( - origin: OriginFor, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - Self::do_burn_undeployed_land_block(undeployed_land_block_id)?; - - Ok(().into()) - } - - /// Approve existing undeployed land block which is not frozen. - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the undeployed land block owner can make this call. - /// - `to`: the account for which the undeployed land block will be approved - /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be burned - /// - /// Emits `UndeployedLandBlockApproved` if successful - #[pallet::weight(T::WeightInfo::approve_undeployed_land_blocks())] - #[transactional] - pub fn approve_undeployed_land_blocks( - origin: OriginFor, - to: T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - UndeployedLandBlocks::::try_mutate_exists( - &undeployed_land_block_id, - |undeployed_land_block| -> DispatchResultWithPostInfo { - let mut undeployed_land_block_record = undeployed_land_block - .as_mut() - .ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - undeployed_land_block_record.owner == who.clone(), - Error::::NoPermission - ); - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock( - undeployed_land_block_id - )), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - ensure!( - undeployed_land_block_record.is_locked == false, - Error::::UndeployedLandBlockAlreadyFreezed - ); - - undeployed_land_block_record.approved = Some(to.clone()); - - Self::deposit_event(Event::::UndeployedLandBlockApproved( - who.clone(), - to.clone(), - undeployed_land_block_id.clone(), - )); - - Ok(().into()) - }, - ) - } - - /// Unapprove existing undeployed land block which is not frozen. - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the undeployed land block owner can make this call. - /// - `undeployed_land_block_id`: the ID of the undeployed land block that will be - /// unapproved - /// - /// Emits `UndeployedLandBlockUnapproved` if successful - #[pallet::weight(T::WeightInfo::unapprove_undeployed_land_blocks())] - #[transactional] - pub fn unapprove_undeployed_land_blocks( - origin: OriginFor, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - UndeployedLandBlocks::::try_mutate_exists( - &undeployed_land_block_id, - |undeployed_land_block| -> DispatchResultWithPostInfo { - let mut undeployed_land_block_record = undeployed_land_block - .as_mut() - .ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - undeployed_land_block_record.owner == who.clone(), - Error::::NoPermission - ); - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock( - undeployed_land_block_id - )), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - ensure!( - undeployed_land_block_record.is_locked == false, - Error::::UndeployedLandBlockAlreadyFreezed - ); - - undeployed_land_block_record.approved = None; - - Self::deposit_event(Event::::UndeployedLandBlockUnapproved( - undeployed_land_block_id.clone(), - )); - - Ok(().into()) - }, - ) - } - - /// Dissolve estate to land units if it is not in auction. - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the estate owner can make this call. - /// - `estate_id`: the ID of the estate that will be dissolved - /// - /// Emits `EstateDestroyed` if successful - #[pallet::weight(T::WeightInfo::dissolve_estate())] - #[transactional] - pub fn dissolve_estate(origin: OriginFor, estate_id: EstateId) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; - match estate_owner_value { - OwnerId::Token(c, t) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(c, t)), - Error::::EstateAlreadyInAuction - ); - //ensure there is record of the estate owner with estate id and account id - ensure!( - Self::check_if_land_or_estate_owner(&who, &estate_owner_value), - Error::::NoPermission - ); - let estate_info = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; - EstateOwner::::try_mutate_exists(&estate_id, |estate_owner| { - // Reset estate ownership - match estate_owner_value { - OwnerId::Token(class_id, token_id) => { - T::NFTTokenizationSource::burn_nft(&who, &(class_id, token_id)); - *estate_owner = None; - } - OwnerId::Account(ref a) => { - *estate_owner = None; - } - } - - // Remove estate - Estates::::remove(&estate_id); - - // Update total estates - let total_estates_count = Self::all_estates_count(); - let new_total_estates_count = total_estates_count - .checked_sub(One::one()) - .ok_or("Overflow adding new count to total estates")?; - AllEstatesCount::::put(new_total_estates_count); - - // Mint new land tokens to replace the lands in the dissolved estate - let estate_account_id: T::AccountId = - T::LandTreasury::get().into_sub_account_truncating(estate_id); - let storage_fee: BalanceOf = - Perbill::from_percent(100u32.saturating_mul(estate_info.land_units.len() as u32)) - * T::StorageDepositFee::get(); - T::Currency::transfer( - &who, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - for land_unit in estate_info.land_units { - // Transfer land unit from treasury to estate owner - Self::mint_land_unit( - estate_info.metaverse_id, - estate_owner_value.clone(), - who.clone(), - land_unit, - LandUnitStatus::RemovedFromEstate, - )?; - } - - Self::deposit_event(Event::::EstateDestroyed( - estate_id.clone(), - estate_owner_value.clone(), - )); - - Ok(().into()) - }) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Add more land units to existing estate that is not in auction - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the estate owner can make this call. - /// They must also own the land units. - /// - `estate_id`: the ID of the estate that the land units will be added to - /// - `land_units`: list of land unit coordinates that will be added to estate - /// - /// Emits `LandUnitAdded` if successful - #[pallet::weight(T::WeightInfo::add_land_unit_to_estate())] - #[transactional] - pub fn add_land_unit_to_estate( - origin: OriginFor, - estate_id: EstateId, - land_units: Vec<(i32, i32)>, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; - match estate_owner_value { - OwnerId::Token(c, t) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(c, t)), - Error::::EstateAlreadyInAuction - ); - ensure!( - Self::check_if_land_or_estate_owner(&who, &estate_owner_value), - Error::::NoPermission - ); - - let estate_info: EstateInfo = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; - let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(estate_id); - - let storage_fee: BalanceOf = - Perbill::from_percent(100u32.saturating_mul(land_units.len() as u32)) - * T::StorageDepositFee::get(); - T::Currency::transfer( - &who, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - - // Check land unit ownership - for land_unit in land_units.clone() { - let metaverse_land_unit = Self::get_land_units(estate_info.metaverse_id, land_unit) - .ok_or(Error::::UndeployedLandBlockNotFound)?; - ensure!( - Self::check_if_land_or_estate_owner(&who, &metaverse_land_unit,), - Error::::LandUnitDoesNotExist - ); - - // Mint land unit - Self::mint_land_unit( - estate_info.metaverse_id, - estate_owner_value.clone(), - estate_account_id.clone(), - land_unit, - LandUnitStatus::Existing(who.clone()), - )?; - } - - // Mutate estates - Estates::::try_mutate_exists(&estate_id, |maybe_estate_info| { - // Append new coordinates to estate - let mut_estate_info = maybe_estate_info.as_mut().ok_or(Error::::EstateDoesNotExist)?; - mut_estate_info.land_units.append(&mut land_units.clone()); - - Self::deposit_event(Event::::LandUnitAdded( - estate_id.clone(), - estate_owner_value.clone(), - land_units.clone(), - )); - - Ok(().into()) - }) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Remove land units from existing estate if it is not in auction. - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the estate owner can make this call. - /// - `estate_id`: the ID of the estate that the land units will be removed from - /// - `land_units`: list of land unit coordinates that will be added to estate - /// - /// Emits `LandUnitsRemoved` if successful - #[pallet::weight(T::WeightInfo::remove_land_unit_from_estate())] - #[transactional] - pub fn remove_land_unit_from_estate( - origin: OriginFor, - estate_id: EstateId, - land_units: Vec<(i32, i32)>, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; - match estate_owner_value { - OwnerId::Token(c, t) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(c, t)), - Error::::EstateAlreadyInAuction - ); - ensure!( - Self::check_if_land_or_estate_owner(&who, &estate_owner_value), - Error::::NoPermission - ); - let estate_info: EstateInfo = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; - let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(estate_id); - - // Mutate estates - Estates::::try_mutate_exists(&estate_id, |maybe_estate_info| { - let mut mut_estate_info = maybe_estate_info.as_mut().ok_or(Error::::EstateDoesNotExist)?; - - let storage_fee: BalanceOf = - Perbill::from_percent(100u32.saturating_mul(land_units.len() as u32)) - * T::StorageDepositFee::get(); - T::Currency::transfer( - &who, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - // Mutate land unit ownership - for land_unit in land_units.clone() { - // Transfer land unit from treasury to estate owner - Self::mint_land_unit( - estate_info.metaverse_id, - estate_owner_value.clone(), - who.clone(), - land_unit, - LandUnitStatus::RemovedFromEstate, - )?; - // Remove coordinates from estate - let index = mut_estate_info - .land_units - .iter() - .position(|x| *x == land_unit) - .ok_or(Error::::LandUnitIsNotAvailable)?; - mut_estate_info.land_units.remove(index); - } - - Self::deposit_event(Event::::LandUnitsRemoved( - estate_id.clone(), - estate_owner_value.clone(), - land_units.clone(), - )); - - Ok(().into()) - }) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Create a lease offer for estate that is not leased - /// - /// The dispatch origin for this call must be _Singed_. - /// Only origin that is not the estate owner can make this call. - /// - `estate_id`: the ID of the estate that will be leased - /// - `price_per_block`: lease price per block - /// - `duration`: lease duration (in number of blocks) - /// - /// Emits `EstateLeaseOfferCreated` if successful - #[pallet::weight(T::WeightInfo::create_lease_offer())] - #[transactional] - pub fn create_lease_offer( - origin: OriginFor, - estate_id: EstateId, - price_per_block: BalanceOf, - duration: u32, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; - ensure!( - !Self::check_if_land_or_estate_owner(&who, &estate_owner_value), - Error::::NoPermission - ); - ensure!( - !EstateLeaseOffers::::contains_key(estate_id, who.clone()), - Error::::LeaseOfferAlreadyExists - ); - ensure!( - !EstateLeases::::contains_key(estate_id), - Error::::EstateIsAlreadyLeased - ); - ensure!( - price_per_block >= T::MinLeasePricePerBlock::get(), - Error::::LeaseOfferPriceBelowMinimum - ); - ensure!( - duration <= T::MaxLeasePeriod::get(), - Error::::LeaseOfferDurationAboveMaximum - ); - - match estate_owner_value { - OwnerId::Token(class_id, token_id) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(class_id, token_id)), - Error::::EstateAlreadyInAuction - ); - ensure!( - EstateLeaseOffers::::iter_key_prefix(estate_id).count() as u32 - <= T::MaxOffersPerEstate::get(), - Error::::EstateLeaseOffersQueueLimitIsReached - ); - - let current_block_number = >::block_number(); - let end_block = current_block_number + T::LeaseOfferExpiryPeriod::get().into(); - let unclaimed_rent: BalanceOf = price_per_block * duration.into(); - - let lease_offer = LeaseContract { - price_per_block, - duration, - end_block, - start_block: end_block + 1u32.into(), - unclaimed_rent, - }; - - EstateLeaseOffers::::insert(estate_id, who.clone(), lease_offer); - T::Currency::reserve(&who, unclaimed_rent); - - Self::deposit_event(Event::::EstateLeaseOfferCreated(who, estate_id, unclaimed_rent)); - - Ok(().into()) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Accept lease offer for estate that is not leased - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the estate owner can make this call. - /// - `estate_id`: the ID of the estate that will be leased - /// - `recipient`: the account that made the lease offer - /// - /// Emits `EstateLeaseOfferAccepted` if successful - #[pallet::weight(T::WeightInfo::accept_lease_offer())] - #[transactional] - pub fn accept_lease_offer( - origin: OriginFor, - estate_id: EstateId, - recipient: T::AccountId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - ensure!( - !EstateLeases::::contains_key(estate_id), - Error::::EstateIsAlreadyLeased - ); - - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; - match estate_owner_value { - OwnerId::Token(class_id, token_id) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(class_id, token_id)), - Error::::EstateAlreadyInAuction - ); - ensure!( - Self::check_if_land_or_estate_owner(&who, &estate_owner_value), - Error::::NoPermission - ); - - let mut lease = - Self::lease_offers(estate_id, recipient.clone()).ok_or(Error::::LeaseOfferDoesNotExist)?; - - lease.start_block = >::block_number(); - lease.end_block = lease.start_block + lease.duration.into(); - // 200% storage fee since there are 2 storage inserts - let storage_fee: BalanceOf = Perbill::from_percent(200) * T::StorageDepositFee::get(); - T::Currency::transfer( - &who, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - - EstateLeaseOffers::::remove_prefix(estate_id, None); - EstateLeases::::insert(estate_id, lease.clone()); - EstateLeasors::::insert(recipient.clone(), estate_id, ()); - T::NFTTokenizationSource::set_lock_nft((class_id, token_id), true)?; - - Self::deposit_event(Event::::EstateLeaseOfferAccepted( - estate_id, - recipient.clone(), - lease.end_block, - )); - - Ok(().into()) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Cancels existing lease - /// - /// The dispatch origin for this call must be _Root_. - /// - `estate_id`: the ID of the estate that will be leased - /// - `leasor`: the account that is leasing the estate - /// - /// Emits `EstateLeaseContractCancelled` if successful - #[pallet::weight(T::WeightInfo::cancel_lease())] - #[transactional] - pub fn cancel_lease( - origin: OriginFor, - estate_owner: T::AccountId, - estate_id: EstateId, - leasor: T::AccountId, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - let lease = Self::leases(estate_id).ok_or(Error::::LeaseDoesNotExist)?; - ensure!( - EstateLeasors::::contains_key(leasor.clone(), estate_id), - Error::::LeaseDoesNotExist - ); - ensure!( - lease.end_block > >::block_number(), - Error::::LeaseIsExpired - ); - - let total_rent: BalanceOf = lease.price_per_block * lease.duration.into(); - let rent_period = >::block_number() - lease.start_block; - let rent_claim_amount = lease.price_per_block * T::BlockNumberToBalance::convert(rent_period) - + lease.unclaimed_rent - - total_rent; - - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; - match estate_owner_value { - OwnerId::Token(class_id, token_id) => { - //let estate_owner = T::NFTTokenizationSource::get_asset_owner(&(class_id, token_id))?; - ensure!( - Self::check_if_land_or_estate_owner(&estate_owner, &estate_owner_value), - Error::::NoPermission - ); - T::Currency::unreserve(&leasor, lease.unclaimed_rent.into()); - ::Currency::transfer( - &leasor, - &estate_owner, - rent_claim_amount, - ExistenceRequirement::KeepAlive, - )?; - - EstateLeasors::::remove(leasor.clone(), estate_id); - EstateLeases::::remove(estate_id); - T::NFTTokenizationSource::set_lock_nft((class_id, token_id), false)?; - - Self::deposit_event(Event::::EstateLeaseContractCancelled(estate_id)); - Ok(().into()) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Removes expired lease - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the estate owner can make this call. - /// - `estate_id`: the ID of the estate that will be leased - /// - `leasor`: the account that is leasing the estate - /// - /// Emits `EstateLeaseContractEnded` if successful - #[pallet::weight(T::WeightInfo::remove_expired_lease())] - #[transactional] - pub fn remove_expired_lease( - origin: OriginFor, - estate_id: EstateId, - leasor: T::AccountId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - let lease = Self::leases(estate_id).ok_or(Error::::LeaseDoesNotExist)?; - ensure!( - EstateLeasors::::contains_key(leasor.clone(), estate_id), - Error::::LeaseDoesNotExist - ); - ensure!( - lease.end_block <= >::block_number(), - Error::::LeaseIsNotExpired - ); - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::EstateDoesNotExist)?; - match estate_owner_value { - OwnerId::Token(class_id, token_id) => { - ensure!( - Self::check_if_land_or_estate_owner(&who, &estate_owner_value), - Error::::NoPermission - ); - - T::Currency::unreserve(&leasor, lease.unclaimed_rent.into()); - ::Currency::transfer( - &leasor, - &who, - lease.unclaimed_rent.into(), - ExistenceRequirement::KeepAlive, - )?; - - EstateLeasors::::remove(leasor, estate_id); - EstateLeases::::remove(estate_id); - T::NFTTokenizationSource::set_lock_nft((class_id, token_id), false)?; - - Self::deposit_event(Event::::EstateLeaseContractEnded(estate_id)); - Ok(().into()) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - - /// Removes lease offer - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the account made the lease offer can make this call. - /// - `estate_id`: the ID of the estate that will be leased - /// - /// Emits `EstateLeaseOfferRemoved` if successful - #[pallet::weight(T::WeightInfo::remove_lease_offer())] - #[transactional] - pub fn remove_lease_offer(origin: OriginFor, estate_id: EstateId) -> DispatchResultWithPostInfo { - let leasor = ensure_signed(origin)?; - let lease_offer = - Self::lease_offers(estate_id, leasor.clone()).ok_or(Error::::LeaseOfferDoesNotExist)?; - EstateLeaseOffers::::remove(estate_id, leasor.clone()); - T::Currency::unreserve(&leasor, lease_offer.unclaimed_rent.into()); - Self::deposit_event(Event::::EstateLeaseOfferRemoved(leasor, estate_id)); Ok(().into()) } - - /// Collect rent for a leased estate - /// - /// The dispatch origin for this call must be _Singed_. - /// Only the estate owner can make this call. - /// - `estate_id`: the ID of the estate that will be leased - /// - /// Emits `EstateRentCollected` if successful - #[pallet::weight(T::WeightInfo::collect_rent())] - #[transactional] - pub fn collect_rent( - origin: OriginFor, - estate_id: EstateId, - leasor: T::AccountId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - ensure!( - Self::check_estate_ownership(who.clone(), estate_id)?, - Error::::NoPermission - ); - ensure!( - EstateLeasors::::contains_key(leasor.clone(), estate_id), - Error::::LeaseDoesNotExist - ); - let current_block = >::block_number(); - EstateLeases::::try_mutate_exists(&estate_id, |estate_lease_value| { - let mut lease = estate_lease_value.as_mut().ok_or(Error::::LeaseDoesNotExist)?; - - ensure!(lease.end_block > current_block, Error::::LeaseIsExpired); - - let total_rent: BalanceOf = lease.price_per_block * lease.duration.into(); - let rent_period = >::block_number() - lease.start_block; - let rent_claim_amount = lease.price_per_block * T::BlockNumberToBalance::convert(rent_period) - + lease.unclaimed_rent - - total_rent; - - ::Currency::unreserve(&leasor, rent_claim_amount); - ::Currency::transfer( - &leasor, - &who, - rent_claim_amount.into(), - ExistenceRequirement::KeepAlive, - )?; - - lease.unclaimed_rent -= rent_claim_amount; - - Self::deposit_event(Event::::EstateRentCollected(estate_id, rent_claim_amount.into())); - Ok(().into()) - }) - } } } @@ -1586,742 +198,4 @@ impl Pallet { })?; Ok(estate_id) } - - /// Internal minting of land unit - fn mint_land_unit( - metaverse_id: MetaverseId, - token_owner: OwnerId, - to: T::AccountId, - coordinate: (i32, i32), - land_unit_status: LandUnitStatus, - ) -> Result, DispatchError> { - let mut owner = token_owner.clone(); - - match land_unit_status { - // Use case - create new estate. - LandUnitStatus::Existing(a) => { - ensure!( - LandUnits::::contains_key(metaverse_id, coordinate), - Error::::LandUnitIsNotAvailable - ); - - let existing_owner_value = Self::get_land_units(metaverse_id, coordinate); - match existing_owner_value { - Some(owner_value) => match owner_value { - OwnerId::Token(class_id, token_id) => { - // Implement check if user own nft - ensure!( - T::NFTTokenizationSource::check_ownership(&a, &(class_id, token_id))?, - Error::::NoPermission - ); - - if let OwnerId::Token(owner_class_id, owner_token_id) = token_owner { - ensure!(owner_class_id != class_id, Error::::LandUnitAlreadyInEstate) - } - - // Ensure not locked - T::NFTTokenizationSource::set_lock_nft((class_id, token_id), false)?; - T::NFTTokenizationSource::burn_nft(&a, &(class_id, token_id)); - - LandUnits::::insert(metaverse_id, coordinate, token_owner.clone()); - } - _ => (), - }, - /* It doesn't make sense to mint existing land unit when ownership doesn't exists */ - _ => (), - } - } - LandUnitStatus::NonExisting => { - ensure!( - !LandUnits::::contains_key(metaverse_id, coordinate), - Error::::LandUnitIsNotAvailable - ); - - let token_properties = Self::get_land_token_properties(metaverse_id, coordinate); - let class_id = T::MetaverseInfoSource::get_metaverse_land_class(metaverse_id)?; - let token_id = - T::NFTTokenizationSource::mint_token(&to, class_id, token_properties.0, token_properties.1)?; - owner = OwnerId::Token(class_id, token_id); - LandUnits::::insert(metaverse_id, coordinate, OwnerId::Token(class_id, token_id.clone())); - } - LandUnitStatus::NonExistingWithEstate => { - ensure!( - !LandUnits::::contains_key(metaverse_id, coordinate), - Error::::LandUnitIsNotAvailable - ); - - owner = token_owner.clone(); - - LandUnits::::insert(metaverse_id, coordinate, token_owner.clone()); - } - LandUnitStatus::RemovedFromEstate => { - ensure!( - LandUnits::::contains_key(metaverse_id, coordinate), - Error::::LandUnitIsNotAvailable - ); - - let token_properties = Self::get_land_token_properties(metaverse_id, coordinate); - let class_id = T::MetaverseInfoSource::get_metaverse_land_class(metaverse_id)?; - let token_id = - T::NFTTokenizationSource::mint_token(&to, class_id, token_properties.0, token_properties.1)?; - owner = OwnerId::Token(class_id, token_id); - LandUnits::::remove(metaverse_id, coordinate); - LandUnits::::insert(metaverse_id, coordinate, OwnerId::Token(class_id, token_id)); - } - } - Ok(owner) - } - - /// Internal updating information about an estate - fn update_estate_information( - new_estate_id: EstateId, - metaverse_id: MetaverseId, - estate_owner: OwnerId, - coordinates: Vec<(i32, i32)>, - ) -> DispatchResult { - // Update total estates - let total_estates_count = Self::all_estates_count(); - let new_total_estates_count = total_estates_count - .checked_add(One::one()) - .ok_or("Overflow adding new count to total estates")?; - AllEstatesCount::::put(new_total_estates_count); - - // Update estates - let estate_info = EstateInfo { - metaverse_id, - land_units: coordinates.clone(), - }; - - Estates::::insert(new_estate_id, estate_info); - EstateOwner::::insert(new_estate_id, estate_owner.clone()); - - Self::deposit_event(Event::::NewEstateMinted( - new_estate_id.clone(), - estate_owner, - metaverse_id, - coordinates.clone(), - )); - - Ok(()) - } - - /// Internal getter of new undeployed land block ID - fn get_new_undeployed_land_block_id() -> Result { - let undeployed_land_block_id = - NextUndeployedLandBlockId::::try_mutate(|id| -> Result { - let current_id = *id; - *id = id.checked_add(One::one()).ok_or(Error::::NoAvailableEstateId)?; - Ok(current_id) - })?; - Ok(undeployed_land_block_id) - } - - /// Internal transfer of undeployed land block - fn do_transfer_undeployed_land_block( - who: &T::AccountId, - to: &T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - // 200% storage fee since there are 2 storage inserts - let storage_fee: BalanceOf = Perbill::from_percent(200) * T::StorageDepositFee::get(); - T::Currency::transfer( - &who, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - - UndeployedLandBlocks::::try_mutate( - &undeployed_land_block_id, - |undeployed_land_block| -> Result { - let undeployed_land_block_record = undeployed_land_block - .as_mut() - .ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - undeployed_land_block_record.owner == who.clone(), - Error::::NoPermission - ); - - ensure!( - undeployed_land_block_record.is_locked == false, - Error::::UndeployedLandBlockAlreadyFreezed - ); - - ensure!( - undeployed_land_block_record.undeployed_land_block_type == UndeployedLandBlockType::Transferable, - Error::::UndeployedLandBlockIsNotTransferable - ); - - undeployed_land_block_record.owner = to.clone(); - - UndeployedLandBlocksOwner::::remove(who.clone(), &undeployed_land_block_id); - UndeployedLandBlocksOwner::::insert(to.clone(), &undeployed_land_block_id, ()); - - Self::deposit_event(Event::::UndeployedLandBlockTransferred( - who.clone(), - to.clone(), - undeployed_land_block_id.clone(), - )); - - Ok(undeployed_land_block_id) - }, - ) - } - - /// Internal burn of undeployed land block - fn do_burn_undeployed_land_block( - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - let undeployed_land_block_info = - UndeployedLandBlocks::::get(undeployed_land_block_id).ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - ensure!( - !undeployed_land_block_info.is_locked, - Error::::OnlyFrozenUndeployedLandBlockCanBeDestroyed - ); - - Self::set_total_undeployed_land_unit(undeployed_land_block_info.number_land_units as u64, true)?; - UndeployedLandBlocksOwner::::remove(undeployed_land_block_info.owner, &undeployed_land_block_id); - UndeployedLandBlocks::::remove(&undeployed_land_block_id); - - Self::deposit_event(Event::::UndeployedLandBlockBurnt(undeployed_land_block_id.clone())); - - Ok(undeployed_land_block_id) - } - - /// Internal freeze of undeployed land block - fn do_freeze_undeployed_land_block( - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - UndeployedLandBlocks::::try_mutate_exists( - &undeployed_land_block_id, - |undeployed_land_block| -> Result { - let undeployed_land_block_record = undeployed_land_block - .as_mut() - .ok_or(Error::::UndeployedLandBlockNotFound)?; - - ensure!( - undeployed_land_block_record.is_locked == false, - Error::::UndeployedLandBlockAlreadyFreezed - ); - - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::UndeployedLandBlock(undeployed_land_block_id)), - Error::::UndeployedLandBlockAlreadyInAuction - ); - - undeployed_land_block_record.is_locked = true; - - Self::deposit_event(Event::::UndeployedLandBlockFreezed(undeployed_land_block_id)); - - Ok(undeployed_land_block_id) - }, - ) - } - - /// Internal issue of undeployed land block - fn do_issue_undeployed_land_blocks( - beneficiary: &T::AccountId, - number_of_land_block: u32, - number_land_units_per_land_block: u32, - undeployed_land_block_type: UndeployedLandBlockType, - ) -> Result, DispatchError> { - let mut undeployed_land_block_ids: Vec = Vec::new(); - - // 2 inserts per land blocks - let storage_fee: BalanceOf = - Perbill::from_percent(number_of_land_block.saturating_mul(2).saturating_mul(100)) - * T::StorageDepositFee::get(); - - T::Currency::transfer( - beneficiary, - &T::MetaverseInfoSource::get_network_treasury(), - storage_fee.saturated_into(), - ExistenceRequirement::KeepAlive, - )?; - - for _ in 0..number_of_land_block { - let new_undeployed_land_block_id = Self::get_new_undeployed_land_block_id()?; - - let undeployed_land_block = UndeployedLandBlock { - id: new_undeployed_land_block_id, - number_land_units: number_land_units_per_land_block, - undeployed_land_block_type, - approved: None, - is_locked: false, - owner: beneficiary.clone(), - }; - - UndeployedLandBlocks::::insert(new_undeployed_land_block_id, undeployed_land_block); - UndeployedLandBlocksOwner::::insert(beneficiary.clone(), new_undeployed_land_block_id, ()); - - // Update total undeployed land count - Self::set_total_undeployed_land_unit(number_land_units_per_land_block as u64, false)?; - - Self::deposit_event(Event::::UndeployedLandBlockIssued( - beneficiary.clone(), - new_undeployed_land_block_id.clone(), - )); - - undeployed_land_block_ids.push(new_undeployed_land_block_id); - } - - Ok(undeployed_land_block_ids) - } - - /// Internal transfer of estate - fn do_transfer_estate( - estate_id: EstateId, - from: &T::AccountId, - to: &T::AccountId, - ) -> Result { - EstateOwner::::try_mutate_exists(&estate_id, |estate_owner| -> Result { - //ensure there is record of the estate owner with estate id and account id - ensure!(from != to, Error::::AlreadyOwnTheEstate); - let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; - let estate_info = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; - ensure!( - !EstateLeases::::contains_key(estate_id), - Error::::EstateIsAlreadyLeased - ); - - match estate_owner_value { - OwnerId::Token(class_id, token_id) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(class_id, token_id)), - Error::::EstateAlreadyInAuction - ); - ensure!( - Self::check_if_land_or_estate_owner(from, &estate_owner_value), - Error::::NoPermission - ); - T::NFTTokenizationSource::transfer_nft(from, to, &(class_id, token_id)); - - Self::deposit_event(Event::::TransferredEstate( - estate_id.clone(), - from.clone(), - to.clone(), - )); - - Ok(estate_id) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - }) - } - - /// Internal transfer of land unit - fn do_transfer_landunit( - coordinate: (i32, i32), - from: &T::AccountId, - to: &T::AccountId, - metaverse_id: MetaverseId, - ) -> Result<(i32, i32), DispatchError> { - LandUnits::::try_mutate_exists( - &metaverse_id, - &coordinate, - |land_unit_owner| -> Result<(i32, i32), DispatchError> { - // ensure there is record of the land unit with bit country id and coordinate - ensure!(land_unit_owner.is_some(), Error::::NoPermission); - ensure!(from != to, Error::::AlreadyOwnTheLandUnit); - match land_unit_owner { - Some(owner) => { - ensure!( - Self::check_if_land_or_estate_owner(from, owner), - Error::::NoPermission - ); - match owner { - OwnerId::Token(class_id, token_id) => { - ensure!( - !T::AuctionHandler::check_item_in_auction(ItemId::NFT(*class_id, *token_id)), - Error::::LandUnitAlreadyInAuction - ); - - T::NFTTokenizationSource::transfer_nft(from, to, &(*class_id, *token_id)); - // Update - Self::deposit_event(Event::::TransferredLandUnit( - metaverse_id.clone(), - coordinate.clone(), - from.clone(), - to.clone(), - )); - - Ok(coordinate) - } - _ => Err(Error::::InvalidOwnerValue.into()), - } - } - None => Err(DispatchError::Other("No Permissson")), - } - }, - ) - } - - /// Internal setting of total undeployed land units - fn set_total_undeployed_land_unit(total: u64, deduct: bool) -> Result<(), DispatchError> { - let total_undeployed_land_units = Self::all_undeployed_land_unit(); - - if deduct { - let new_total_undeployed_land_unit_count = total_undeployed_land_units - .checked_sub(total) - .ok_or("Overflow deducting new count to total undeployed lands")?; - TotalUndeployedLandUnit::::put(new_total_undeployed_land_unit_count); - } else { - let new_total_undeployed_land_unit_count = total_undeployed_land_units - .checked_add(total) - .ok_or("Overflow adding new count to total undeployed lands")?; - TotalUndeployedLandUnit::::put(new_total_undeployed_land_unit_count); - } - - Ok(()) - } - - /// Internal setting of total land units - fn set_total_land_unit(total: u64, deduct: bool) -> Result<(), DispatchError> { - let total_land_units_count = Self::all_land_units_count(); - - if deduct { - let new_total_land_units_count = total_land_units_count - .checked_sub(total) - .ok_or("Overflow deducting new count to total lands")?; - AllLandUnitsCount::::put(new_total_land_units_count); - } else { - let new_total_land_units_count = total_land_units_count - .checked_add(total) - .ok_or("Overflow adding new count to total lands")?; - AllLandUnitsCount::::put(new_total_land_units_count); - } - Ok(()) - } - - /// Internal getter of land token properties - fn get_land_token_properties(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> (NftMetadata, Attributes) { - let mut land_coordinate_attribute = Vec::::new(); - land_coordinate_attribute.append(&mut coordinate.0.to_be_bytes().to_vec()); - land_coordinate_attribute.append(&mut coordinate.1.to_be_bytes().to_vec()); - - let mut nft_metadata: NftMetadata = NftMetadata::new(); - nft_metadata.append(&mut land_coordinate_attribute.clone()); - - let mut nft_attributes: Attributes = Attributes::new(); - nft_attributes.insert("MetaverseId:".as_bytes().to_vec(), metaverse_id.to_be_bytes().to_vec()); - nft_attributes.insert("Coordinate:".as_bytes().to_vec(), land_coordinate_attribute); - - return (nft_metadata, nft_attributes); - } - - /// Internal getter of estate token properties - fn get_estate_token_properties(metaverse_id: MetaverseId, estate_id: EstateId) -> (NftMetadata, Attributes) { - let mut nft_metadata: NftMetadata = NftMetadata::new(); - nft_metadata.append(&mut metaverse_id.to_be_bytes().to_vec()); - nft_metadata.append(&mut estate_id.to_be_bytes().to_vec()); - - let mut nft_attributes: Attributes = Attributes::new(); - nft_attributes.insert("MetaverseId:".as_bytes().to_vec(), metaverse_id.to_be_bytes().to_vec()); - nft_attributes.insert("Estate Id:".as_bytes().to_vec(), estate_id.to_be_bytes().to_vec()); - - return (nft_metadata, nft_attributes); - } - - fn check_if_land_or_estate_owner(who: &T::AccountId, owner_id: &OwnerId) -> bool { - match owner_id { - OwnerId::Token(class_id, token_id) => { - return T::NFTTokenizationSource::check_ownership(who, &(*class_id, *token_id)).unwrap_or(false); - } - _ => return false, - } - } - - fn verify_land_unit_for_estate(land_units: Vec<(i32, i32)>) -> bool { - if land_units.len() == 1 { - return false; - } - - let mut vec_axis = land_units.iter().map(|lu| lu.0).collect::>(); - let mut vec_yaxis = land_units.iter().map(|lu| lu.1).collect::>(); - - // Sort by ascending and dedup - vec_axis.sort(); - vec_axis.dedup(); - vec_yaxis.sort(); - vec_yaxis.dedup(); - - let mut is_axis_valid = true; - let mut is_yaxis_valid = true; - - // Ensure axis is next to each other - for (i, axis) in vec_axis.iter().enumerate() { - if axis != &vec_axis[i] { - let valid = axis.saturating_sub(vec_axis[i + 1]); - if valid != 1 { - is_axis_valid = false; - break; - } - } - } - - // Ensure yaxis is next to each other - for (i, yaxis) in vec_yaxis.iter().enumerate() { - if yaxis != &vec_yaxis[i] { - let valid = yaxis.saturating_sub(vec_yaxis[i + 1]); - if valid != 1 { - is_yaxis_valid = false; - break; - } - } - } - - is_axis_valid && is_yaxis_valid - } - - fn verify_land_unit_in_bound(block_coordinate: &(i32, i32), land_unit_coordinates: &Vec<(i32, i32)>) -> bool { - let mut vec_axis = land_unit_coordinates.iter().map(|lu| lu.0).collect::>(); - let mut vec_yaxis = land_unit_coordinates.iter().map(|lu| lu.1).collect::>(); - - let max_axis = vec_axis.iter().max().unwrap_or(&i32::MAX); - let max_yaxis = vec_yaxis.iter().max().unwrap_or(&i32::MAX); - let min_axis = vec_axis.iter().min().unwrap_or(&i32::MIN); - let min_yaxis = vec_yaxis.iter().min().unwrap_or(&i32::MIN); - - let top_left_axis = block_coordinate - .0 - .saturating_mul(100i32) - .saturating_sub(50i32) - .saturating_div(10i32) - .saturating_add(1i32); - let top_right_axis = block_coordinate - .0 - .saturating_mul(100i32) - .saturating_add(50i32) - .saturating_div(10i32); - let top_left_yaxis = block_coordinate - .1 - .saturating_mul(100i32) - .saturating_add(50i32) - .saturating_div(10i32); - let top_right_yaxis = block_coordinate - .1 - .saturating_mul(100i32) - .saturating_sub(50i32) - .saturating_div(10i32) - .saturating_add(1i32); - - top_left_axis <= *min_axis - && top_right_axis >= *max_axis - && top_left_yaxis >= *max_yaxis - && top_right_yaxis <= *min_yaxis - } - - /// Remove all land unit and estate - pub fn remove_all_estate_storage() -> Weight { - log::info!("Start removing all land unit and estates"); - LandUnits::::remove_all(None); - Estates::::remove_all(None); - EstateOwner::::remove_all(None); - NextEstateId::::put(1); - AllLandUnitsCount::::put(0); - AllEstatesCount::::put(0); - Weight::from_ref_time(0) - } - - fn collect_network_fee( - recipient: &T::AccountId, - // social_currency_id: FungibleTokenId, - ) -> DispatchResult { - let network_fund = T::MetaverseInfoSource::get_network_treasury(); - //if social_currency_id == FungibleTokenId::NativeToken(0) { - ::Currency::transfer( - &recipient, - &network_fund, - T::NetworkFee::get(), - ExistenceRequirement::KeepAlive, - )?; - // } else { - // T::FungibleTokenCurrency::transfer( - // social_currency_id.clone(), - // &recipient, - // &network_fund, - // T::NetworkFee::get(), - //)?; - //} - Ok(()) - } -} - -impl MetaverseLandTrait for Pallet { - fn get_user_land_units(who: &T::AccountId, metaverse_id: &MetaverseId) -> Vec<(i32, i32)> { - // Check land units owner. - let mut total_land_units: Vec<(i32, i32)> = Vec::default(); - - let land_in_metaverse = LandUnits::::iter_prefix(metaverse_id) - .filter(|(_, owner)| Self::check_if_land_or_estate_owner(who, owner)) - .collect::>(); - - for land_unit in land_in_metaverse { - let land = land_unit.0; - total_land_units.push(land); - } - - total_land_units - } - - fn is_user_own_metaverse_land(who: &T::AccountId, metaverse_id: &MetaverseId) -> bool { - Self::get_user_land_units(&who, metaverse_id).len() > 0 - } - - fn check_landunit(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> Result { - Ok(LandUnits::::contains_key(metaverse_id, coordinate)) - } -} - -impl UndeployedLandBlocksTrait for Pallet { - fn issue_undeployed_land_blocks( - beneficiary: &T::AccountId, - number_of_land_block: u32, - number_land_units_per_land_block: u32, - undeployed_land_block_type: UndeployedLandBlockType, - ) -> Result, DispatchError> { - let new_undeployed_land_block_id = Self::do_issue_undeployed_land_blocks( - &beneficiary, - number_of_land_block, - number_land_units_per_land_block, - undeployed_land_block_type, - )?; - - Ok(new_undeployed_land_block_id) - } - - fn transfer_undeployed_land_block( - who: &T::AccountId, - to: &T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - Self::do_transfer_undeployed_land_block(who, to, undeployed_land_block_id)?; - - Ok(undeployed_land_block_id) - } - - fn burn_undeployed_land_block( - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - let undeployed_land_block_id = Self::do_burn_undeployed_land_block(undeployed_land_block_id)?; - - Ok(undeployed_land_block_id) - } - - fn freeze_undeployed_land_block( - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - let undeployed_land_block_id = Self::do_freeze_undeployed_land_block(undeployed_land_block_id)?; - - Ok(undeployed_land_block_id) - } - - fn check_undeployed_land_block( - owner: &T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - let undeployed_land_block = - Self::get_undeployed_land_block(undeployed_land_block_id).ok_or(Error::::UndeployedLandBlockNotFound)?; - - if undeployed_land_block.is_locked - || undeployed_land_block.undeployed_land_block_type == UndeployedLandBlockType::BoundToAddress - || undeployed_land_block.owner != *owner - { - return Ok(false); - } - return Ok(true); - } -} - -impl Estate for Pallet { - fn transfer_estate(estate_id: EstateId, from: &T::AccountId, to: &T::AccountId) -> Result { - let estate_id = Self::do_transfer_estate(estate_id, from, to)?; - Ok(estate_id) - } - - fn transfer_landunit( - coordinate: (i32, i32), - from: &T::AccountId, - to: &(T::AccountId, MetaverseId), - ) -> Result<(i32, i32), DispatchError> { - let coordinate = Self::do_transfer_landunit(coordinate, from, &(to).0, to.1)?; - Ok(coordinate) - } - - fn transfer_undeployed_land_block( - who: &T::AccountId, - to: &T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - let undeployed_land_block_id = Self::do_transfer_undeployed_land_block(who, to, undeployed_land_block_id)?; - - Ok(undeployed_land_block_id) - } - - fn check_estate(estate_id: EstateId) -> Result { - Ok(Estates::::contains_key(estate_id)) - } - - fn check_landunit(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> Result { - Ok(LandUnits::::contains_key(metaverse_id, coordinate)) - } - - fn check_undeployed_land_block( - owner: &T::AccountId, - undeployed_land_block_id: UndeployedLandBlockId, - ) -> Result { - let undeployed_land_block = - Self::get_undeployed_land_block(undeployed_land_block_id).ok_or(Error::::UndeployedLandBlockNotFound)?; - - if undeployed_land_block.is_locked - || undeployed_land_block.undeployed_land_block_type == UndeployedLandBlockType::BoundToAddress - || undeployed_land_block.owner != *owner - { - return Ok(false); - } - return Ok(true); - } - - fn get_total_land_units(estate_id: Option) -> u64 { - match estate_id { - Some(id) => { - if let Some(estate_info) = Estates::::get(id) { - estate_info.land_units.len() as u64 - } else { - 0 - } - } - None => AllLandUnitsCount::::get(), - } - } - - fn get_total_undeploy_land_units() -> u64 { - TotalUndeployedLandUnit::::get() - } - - fn check_estate_ownership(owner: T::AccountId, estate_id: EstateId) -> Result { - let owner_value = Self::get_estate_owner(estate_id); - match owner_value { - Some(token_value) => match token_value { - OwnerId::Token(c, t) => T::NFTTokenizationSource::check_ownership(&owner, &(c, t)), - OwnerId::Account(_) => Err(Error::::InvalidOwnerValue.into()), - }, - None => Ok(false), - } - } - - fn is_estate_leasor(leasor: T::AccountId, estate_id: EstateId) -> Result { - Ok(EstateLeasors::::contains_key(leasor, estate_id)) - } - - fn is_estate_leased(estate_id: EstateId) -> Result { - Ok(EstateLeases::::contains_key(estate_id)) - } } From 3c236a610dd0d7822c89d0ba867724ba3461aec1 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 29 Oct 2023 22:04:19 +1300 Subject: [PATCH 028/155] WIP - create pool function --- Cargo.lock | 1 + pallets/asset-manager/Cargo.toml | 4 +- pallets/asset-manager/src/lib.rs | 7 ++ pallets/estate/src/rate.rs | 2 +- pallets/land-minting/src/lib.rs | 66 ++++++++++--------- .../land-minting/src/{rate.rs => utils.rs} | 13 +++- primitives/metaverse/src/lib.rs | 2 +- traits/core-primitives/src/lib.rs | 4 ++ 8 files changed, 65 insertions(+), 34 deletions(-) rename pallets/land-minting/src/{rate.rs => utils.rs} (93%) diff --git a/Cargo.lock b/Cargo.lock index f3fe62500..4c1b16e5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,6 +411,7 @@ name = "asset-manager" version = "0.1.0" dependencies = [ "bit-country-primitives", + "core-primitives", "frame-support", "frame-system", "hex", diff --git a/pallets/asset-manager/Cargo.toml b/pallets/asset-manager/Cargo.toml index ca6303094..61d84ad3b 100644 --- a/pallets/asset-manager/Cargo.toml +++ b/pallets/asset-manager/Cargo.toml @@ -12,12 +12,13 @@ version = '0.1.0' log = { workspace = true } serde = { workspace = true, optional = true } scale-info = { workspace = true } -codec = { package = "parity-scale-codec", version ="3.1.5", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } sp-runtime = { workspace = true } sp-std = { workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } +core-primitives = { path = "../../traits/core-primitives", default-features = false } xcm = { workspace = true } [dev-dependencies] @@ -42,5 +43,6 @@ std = [ "frame-support/std", "frame-system/std", "primitives/std", + "core-primitives/std", "xcm/std", ] \ No newline at end of file diff --git a/pallets/asset-manager/src/lib.rs b/pallets/asset-manager/src/lib.rs index c8c33008d..ec1f418e6 100644 --- a/pallets/asset-manager/src/lib.rs +++ b/pallets/asset-manager/src/lib.rs @@ -37,6 +37,7 @@ use sp_std::{boxed::Box, vec::Vec}; use xcm::v3::MultiLocation; use xcm::VersionedMultiLocation; +use core_primitives::CurrencyIdManagement; pub use pallet::*; use primitives::{ AssetIds, AssetMetadata, BuyWeightRate, CurrencyId, ForeignAssetIdMapping, FungibleTokenId, Ratio, TokenId, @@ -346,3 +347,9 @@ where None } } + +impl CurrencyIdManagement for ForeignAssetMapping { + fn check_token_exist(currency_id: CurrencyId) -> bool { + return true; + } +} diff --git a/pallets/estate/src/rate.rs b/pallets/estate/src/rate.rs index 747a72029..788907d6d 100644 --- a/pallets/estate/src/rate.rs +++ b/pallets/estate/src/rate.rs @@ -19,7 +19,7 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_runtime::{Perbill, RuntimeDebug}; +use sp_runtime::{Perbill, Permill, RuntimeDebug}; use crate::{AllLandUnitsCount, TotalUndeployedLandUnit}; // Helper methods to compute the issuance rate for undeployed land. diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index ecc87bf3c..15d98415f 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -42,7 +42,7 @@ use primitives::{ Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, TokenId, UndeployedLandBlock, UndeployedLandBlockId, UndeployedLandBlockType, }; -pub use rate::{MintingRateInfo, Range}; +pub use utils::{MintingRateInfo, Range}; pub use weights::WeightInfo; //#[cfg(feature = "runtime-benchmarks")] @@ -50,7 +50,7 @@ pub use weights::WeightInfo; #[cfg(test)] mod mock; -mod rate; +mod utils; #[cfg(test)] mod tests; @@ -68,7 +68,7 @@ pub mod pallet { use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; use primitives::{Balance, CurrencyId, RoundIndex, UndeployedLandBlockId}; - use crate::rate::{round_issuance_range, MintingRateInfo}; + use crate::utils::{round_issuance_range, MintingRateInfo}; use super::*; @@ -155,47 +155,53 @@ pub mod pallet { pub enum Event { /// New staking round started [Starting Block, Round, Total Land Unit] NewRound(T::BlockNumber, RoundIndex, u64), + /// New pool created + PoolCreated(T::AccountId, ClassId, u32, CurrencyIdOf), } #[pallet::error] pub enum Error { /// No permission NoPermission, + /// Currency is not supported + CurrencyIsNotSupported, } #[pallet::call] impl Pallet { - /// Minting of a land unit, only used by council to manually mint single land for - /// beneficiary - /// - /// The dispatch origin for this call must be _Root_. - /// - `beneficiary`: the account which will be the owner of the land unit - /// - `metaverse_id`: the metaverse id that the land united will be minted on - /// - `coordinate`: coordinate of the land unit - /// - /// Emits `NewLandsMinted` if successful. #[pallet::weight(T::WeightInfo::mint_land())] - pub fn mint_land( - origin: OriginFor, - beneficiary: T::AccountId, - metaverse_id: MetaverseId, - coordinate: (i32, i32), - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; + pub fn create_pool(origin: OriginFor, max_supply: u32, currency_id: CurrencyIdOf) -> DispatchResult { + // Check if user signed + // Emit events + // Check if user signed + let who = ensure_signed(origin)?; + + // Ensure currency_id supported + ensure!( + currency_id == FungibleTokenId::NativeToken(0) || currency_id == FungibleTokenId::NativeToken(1), + Error::::CurrencyIsNotSupported + ); + + // Collect pool creation fee + Self::collect_pool_creation_fee(&who)?; + + // Create new NFT collection + // This will return a unique collection ID for the new pool + let class_id = T::NFTTokenizationSource.create_collection(who.clone(), max_supply, currency_id)?; + + // Add tuple class_id, currency_id + TokenPool::::insert((class_id, currency_id), Zero::zero); + + // Emit event for pool creation + Self::deposit_event(Event::PoolCreated(who, class_id, max_supply, currency_id)); + Ok(().into()) + } + #[pallet::weight(T::WeightInfo::mint_land())] + pub fn deposit(origin: OriginFor, class_id: ClassId, amount: BalanceOf) -> DispatchResult { Ok(().into()) } } } -impl Pallet { - /// Internal getter for new estate ID - fn get_new_estate_id() -> Result { - let estate_id = NextEstateId::::try_mutate(|id| -> Result { - let current_id = *id; - *id = id.checked_add(One::one()).ok_or(Error::::NoAvailableEstateId)?; - Ok(current_id) - })?; - Ok(estate_id) - } -} +impl Pallet {} diff --git a/pallets/land-minting/src/rate.rs b/pallets/land-minting/src/utils.rs similarity index 93% rename from pallets/land-minting/src/rate.rs rename to pallets/land-minting/src/utils.rs index 747a72029..b08d85bf2 100644 --- a/pallets/land-minting/src/rate.rs +++ b/pallets/land-minting/src/utils.rs @@ -19,7 +19,7 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_runtime::{Perbill, RuntimeDebug}; +use sp_runtime::{Perbill, Permill, RuntimeDebug}; use crate::{AllLandUnitsCount, TotalUndeployedLandUnit}; // Helper methods to compute the issuance rate for undeployed land. @@ -29,6 +29,17 @@ const SECONDS_PER_YEAR: u32 = 31557600; const SECONDS_PER_BLOCK: u32 = 12; const BLOCKS_PER_YEAR: u32 = SECONDS_PER_YEAR / SECONDS_PER_BLOCK; +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Eq, PartialEq, Clone, Encode, Decode, Default, RuntimeDebug, TypeInfo)] +pub struct PoolInfo { + pub creator: AccountId, + pub commission: Permill, + /// Currency id of the pool + pub currency_id: CurrencyId, + /// Max total supply + pub max: u32, +} + fn rounds_per_year() -> u32 { let blocks_per_round = >::round().length; BLOCKS_PER_YEAR / blocks_per_round diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index c149368ba..61be8ba4e 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -192,7 +192,7 @@ impl FungibleTokenId { pub fn decimals(&self) -> u8 { match self { FungibleTokenId::NativeToken(0) => 18, // Native token - FungibleTokenId::NativeToken(1) | FungibleTokenId::NativeToken(2) | FungibleTokenId::Stable(0) => 12, // KSM KAR KUSD + FungibleTokenId::NativeToken(1) | FungibleTokenId::NativeToken(2) | FungibleTokenId::Stable(0) => 12, // KSM FungibleTokenId::MiningResource(0) => 18, _ => 18, } diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 4ea550bab..5946eb38b 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -400,3 +400,7 @@ impl MiningResourceRateInfo { self.mining_reward = mining_reward; } } + +pub trait CurrencyIdManagement { + fn check_token_exist(currency_id: CurrencyId) -> bool; +} From e3f70f0a12b9e1a0a615988e8e4396c7ef24f8e2 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 30 Oct 2023 15:59:35 +1300 Subject: [PATCH 029/155] WIP - update new pool creation concept --- pallets/land-minting/src/lib.rs | 50 ++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 15d98415f..74d2430a4 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -66,9 +66,9 @@ pub mod pallet { use primitives::estate::EstateInfo; use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; - use primitives::{Balance, CurrencyId, RoundIndex, UndeployedLandBlockId}; + use primitives::{AccountId, Balance, CurrencyId, PoolId, RoundIndex, UndeployedLandBlockId}; - use crate::utils::{round_issuance_range, MintingRateInfo}; + use crate::utils::{round_issuance_range, MintingRateInfo, PoolInfo}; use super::*; @@ -128,13 +128,17 @@ pub mod pallet { pub type CurrencyIdOf = <::MultiCurrency as MultiCurrency<::AccountId>>::CurrencyId; + #[pallet::storage] + #[pallet::getter(fn next_class_id)] + pub type NextPoolId = StorageValue<_, PoolId, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn fees)] pub type Fees = StorageValue<_, (Permill, Permill), ValueQuery>; #[pallet::storage] #[pallet::getter(fn token_pool)] - pub type TokenPool = StorageMap<_, Twox64Concat, (ClassId, CurrencyIdOf), BalanceOf, ValueQuery>; + pub type Pool = StorageMap<_, Twox64Concat, PoolId, PoolInfo, T::AccountId>, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -156,7 +160,7 @@ pub mod pallet { /// New staking round started [Starting Block, Round, Total Land Unit] NewRound(T::BlockNumber, RoundIndex, u64), /// New pool created - PoolCreated(T::AccountId, ClassId, u32, CurrencyIdOf), + PoolCreated(T::AccountId, u32, CurrencyIdOf), } #[pallet::error] @@ -165,15 +169,19 @@ pub mod pallet { NoPermission, /// Currency is not supported CurrencyIsNotSupported, + /// No available next pool id + NoAvailablePoolId, } #[pallet::call] impl Pallet { #[pallet::weight(T::WeightInfo::mint_land())] - pub fn create_pool(origin: OriginFor, max_supply: u32, currency_id: CurrencyIdOf) -> DispatchResult { - // Check if user signed - // Emit events - // Check if user signed + pub fn create_pool( + origin: OriginFor, + currency_id: CurrencyIdOf, + max_nft_reward: u32, + commission: Permill, + ) -> DispatchResult { let who = ensure_signed(origin)?; // Ensure currency_id supported @@ -182,23 +190,39 @@ pub mod pallet { Error::::CurrencyIsNotSupported ); + // TODO Check commission below threshold + // Collect pool creation fee Self::collect_pool_creation_fee(&who)?; - // Create new NFT collection - // This will return a unique collection ID for the new pool - let class_id = T::NFTTokenizationSource.create_collection(who.clone(), max_supply, currency_id)?; + // Next pool id + let next_pool_id = NextPoolId::::try_mutate(|id| -> Result { + let current_id = *id; + *id = id.checked_add(&1u32).ok_or(Error::::NoAvailablePoolId)?; + Ok(current_id) + })?; + + let new_pool = PoolInfo { + creator: who.clone(), + commission: commission, + currency_id: currency_id, + max: max_nft_reward, + }; // Add tuple class_id, currency_id - TokenPool::::insert((class_id, currency_id), Zero::zero); + Pool::::insert(next_pool_id, new_pool); // Emit event for pool creation - Self::deposit_event(Event::PoolCreated(who, class_id, max_supply, currency_id)); + Self::deposit_event(Event::PoolCreated(who, max_nft_reward, currency_id)); Ok(().into()) } #[pallet::weight(T::WeightInfo::mint_land())] pub fn deposit(origin: OriginFor, class_id: ClassId, amount: BalanceOf) -> DispatchResult { + // Ensure user is signed + // Check if pool is full from max supply + // Calculate exchange rate and take fee + // Mint new token Ok(().into()) } } From 99e3d93b0b1f0970c63d41576dc5c46b6fc95188 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 30 Oct 2023 15:59:57 +1300 Subject: [PATCH 030/155] WIP - Add PoolId primitives and max nft reward --- pallets/land-minting/src/utils.rs | 2 +- primitives/metaverse/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/land-minting/src/utils.rs b/pallets/land-minting/src/utils.rs index b08d85bf2..164224e2c 100644 --- a/pallets/land-minting/src/utils.rs +++ b/pallets/land-minting/src/utils.rs @@ -36,7 +36,7 @@ pub struct PoolInfo { pub commission: Permill, /// Currency id of the pool pub currency_id: CurrencyId, - /// Max total supply + /// Max nft rewards pub max: u32, } diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index 61be8ba4e..a867f350a 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -132,6 +132,8 @@ pub type TrieIndex = u32; pub type CampaignId = u32; /// Index used for claim rewrads for merkle root campaigns pub type ClaimId = u64; +/// Pool Id to keep track of each pool +pub type PoolId = u32; /// Land Token Class Id pub const LAND_CLASS_ID: ClassId = 15; From e4e4379f141a527e0aaef7f8b7c2a1431ced1004 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 30 Oct 2023 17:58:17 +1300 Subject: [PATCH 031/155] WIP - Implement the first version of deposit --- pallets/land-minting/src/lib.rs | 81 ++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 74d2430a4..c0753d490 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -61,6 +61,7 @@ pub mod weights; pub mod pallet { use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; + use sp_core::U256; use sp_runtime::traits::{CheckedAdd, CheckedSub, Zero}; use sp_runtime::Permill; @@ -122,6 +123,9 @@ pub mod pallet { /// Allows converting block numbers into balance type BlockNumberToBalance: Convert>; + + #[pallet::constant] + type PoolAccount: Get; } pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -136,10 +140,21 @@ pub mod pallet { #[pallet::getter(fn fees)] pub type Fees = StorageValue<_, (Permill, Permill), ValueQuery>; + /// Keep track of Pool detail #[pallet::storage] - #[pallet::getter(fn token_pool)] + #[pallet::getter(fn pool)] pub type Pool = StorageMap<_, Twox64Concat, PoolId, PoolInfo, T::AccountId>, ValueQuery>; + /// Pool ledger that keeps track of Pool id and balance + #[pallet::storage] + #[pallet::getter(fn pool_ledger)] + pub type PoolLedger = StorageMap<_, Twox64Concat, PoolId, BalanceOf, ValueQuery>; + + /// Network ledger + #[pallet::storage] + #[pallet::getter(fn network_ledger)] + pub type NetworkLedger = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub minting_rate_config: MintingRateInfo, @@ -171,6 +186,10 @@ pub mod pallet { CurrencyIsNotSupported, /// No available next pool id NoAvailablePoolId, + /// Pool doesn't exists + PoolDoesNotExist, + /// Overflow + Overflow, } #[pallet::call] @@ -198,7 +217,7 @@ pub mod pallet { // Next pool id let next_pool_id = NextPoolId::::try_mutate(|id| -> Result { let current_id = *id; - *id = id.checked_add(&1u32).ok_or(Error::::NoAvailablePoolId)?; + *id = id.checked_add(1u32).ok_or(Error::::NoAvailablePoolId)?; Ok(current_id) })?; @@ -218,11 +237,61 @@ pub mod pallet { } #[pallet::weight(T::WeightInfo::mint_land())] - pub fn deposit(origin: OriginFor, class_id: ClassId, amount: BalanceOf) -> DispatchResult { + pub fn deposit(origin: OriginFor, pool_id: PoolId, amount: BalanceOf) -> DispatchResult { + // Ensure user is signed + // Check if pool is exists + // Get pool detail and add it to pool_instance + // Get currencyId from pool detail + // Get network ledger balance from currency id + // Collect deposit fee for protocol + // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount + // total issuance)/network ledger balance Deposit vAmount to user using T::MultiCurrency::deposit + // Transfer amount to PoolAccount using T::MultiCurrency::transfer + // Emit deposit event + // Ensure user is signed - // Check if pool is full from max supply - // Calculate exchange rate and take fee - // Mint new token + let who = ensure_signed(origin)?; + // Check if pool exists + let pool_instance = Pool::::get(pool_id).ok_or(Error::::PoolDoesNotExist)?; + + // Get currencyId from pool detail + let currency_id = pool_instance.currency_id; + + // Get network ledger balance from currency id + let network_ledger_balance = Self::network_ledger(currency_id); + + // Collect deposit fee for protocol + // Assuming there's a function `collect_deposit_fee` that deducts a fee for deposits. + Self::collect_deposit_fee(&who, amount)?; + + let v_currency_id = T::CurrencyIdManagement::convert_to_vcurrency(currency_id) + .map_err(|_| Error::::NotSupportTokenType)?; + // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount + // total issuance)/network ledger balance + let v_amount_total_issuance = T::MultiCurrency::total_issuance(v_currency_id); + let v_amount = U256::from(amount.saturated_into::()) + .saturating_mul(v_amount_total_issuance.saturated_into::().into()) + .checked_div(network_ledger_balance.saturated_into::().into()) + .ok_or(Error::::CalculationOverflow)? + .as_u128() + .saturated_into(); + + // Deposit vAmount to user using T::MultiCurrency::deposit + T::MultiCurrency::deposit(currency_id, &who, vamount)?; + + // Transfer amount to PoolAccount using T::MultiCurrency::transfer + // Assuming `PoolAccount` is an associated type that represents the pool's account ID or a method to + // get it. + T::MultiCurrency::transfer( + currency_id, + &who, + &T::PoolAccount::get().into_account_truncating(), + amount, + )?; + + // Emit deposit event + Self::deposit_event(Event::Deposited(who, pool_id, amount)); + Ok(().into()) } } From 33057f3175069cf9feb8ca3afacc1bb188ca6a43 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 31 Oct 2023 22:44:39 +1300 Subject: [PATCH 032/155] WIP - Implement a test redeem --- pallets/land-minting/src/lib.rs | 75 ++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index c0753d490..be8676e56 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -155,6 +155,10 @@ pub mod pallet { #[pallet::getter(fn network_ledger)] pub type NetworkLedger = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn minimum_redeem)] + pub type MinimumRedeem = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub minting_rate_config: MintingRateInfo, @@ -190,6 +194,8 @@ pub mod pallet { PoolDoesNotExist, /// Overflow Overflow, + /// Below minimum redemption + BelowMinimumRedeem, } #[pallet::call] @@ -238,17 +244,6 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::mint_land())] pub fn deposit(origin: OriginFor, pool_id: PoolId, amount: BalanceOf) -> DispatchResult { - // Ensure user is signed - // Check if pool is exists - // Get pool detail and add it to pool_instance - // Get currencyId from pool detail - // Get network ledger balance from currency id - // Collect deposit fee for protocol - // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount - // total issuance)/network ledger balance Deposit vAmount to user using T::MultiCurrency::deposit - // Transfer amount to PoolAccount using T::MultiCurrency::transfer - // Emit deposit event - // Ensure user is signed let who = ensure_signed(origin)?; // Check if pool exists @@ -262,22 +257,22 @@ pub mod pallet { // Collect deposit fee for protocol // Assuming there's a function `collect_deposit_fee` that deducts a fee for deposits. - Self::collect_deposit_fee(&who, amount)?; + let amount_after_fee = Self::collect_deposit_fee(&who, amount)?; let v_currency_id = T::CurrencyIdManagement::convert_to_vcurrency(currency_id) - .map_err(|_| Error::::NotSupportTokenType)?; + .map_err(|_| Error::::CurrencyIsNotSupported)?; // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount // total issuance)/network ledger balance let v_amount_total_issuance = T::MultiCurrency::total_issuance(v_currency_id); - let v_amount = U256::from(amount.saturated_into::()) + let v_amount = U256::from(amount_after_fee.saturated_into::()) .saturating_mul(v_amount_total_issuance.saturated_into::().into()) .checked_div(network_ledger_balance.saturated_into::().into()) - .ok_or(Error::::CalculationOverflow)? + .ok_or(ArithmeticError::Overflow)? .as_u128() .saturated_into(); // Deposit vAmount to user using T::MultiCurrency::deposit - T::MultiCurrency::deposit(currency_id, &who, vamount)?; + T::MultiCurrency::deposit(currency_id, &who, v_amount)?; // Transfer amount to PoolAccount using T::MultiCurrency::transfer // Assuming `PoolAccount` is an associated type that represents the pool's account ID or a method to @@ -291,7 +286,55 @@ pub mod pallet { // Emit deposit event Self::deposit_event(Event::Deposited(who, pool_id, amount)); + Ok(().into()) + } + + #[pallet::weight(T::WeightInfo::mint_land())] + pub fn redeem( + origin: OriginFor, + pool_id: PoolId, + vcurrency_id: CurrencyIdOf, + vamount: BalanceOf, + ) -> DispatchResult { + // Ensure user is signed + let who = ensure_signed(origin)?; + ensure!( + vamount >= MinimumRedeem::::get(vcurrency_id), + Error::::BelowMinimumRedeem + ); + let currency_id = T::CurrencyIdManagement::convert_to_currency(vcurrency_id) + .map_err(|_| Error::::NotSupportTokenType)?; + + // Check if pool exists + let pool_instance = Pool::::get(pool_id).ok_or(Error::::PoolDoesNotExist)?; + + ensure!( + currency_id == pool_instance.currency_id, + Error::::CurrencyIsNotSupported + ); + + // Get network ledger balance from currency id + let network_ledger_balance = Self::network_ledger(currency_id); + + // Collect deposit fee for protocol + // Assuming there's a function `collect_deposit_fee` that deducts a fee for deposits. + let amount_after_fee = Self::collect_deposit_fee(&who, vamount)?; + let vamount = vamount + .checked_sub(&amount_after_fee) + .ok_or(ArithmeticError::Overflow)?; + // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount + // total issuance)/network ledger balance + let v_amount_total_issuance = T::MultiCurrency::total_issuance(vcurrency_id); + let currency_amount = U256::from(vamount.saturated_into::()) + .saturating_mul(network_ledger_balance.saturated_into::().into()) + .checked_div(v_amount_total_issuance.saturated_into::().into()) + .ok_or(Error::::CalculationOverflow)? + .as_u128() + .saturated_into(); + + // Emit deposit event + Self::deposit_event(Event::Deposited(who, pool_id, vamount)); Ok(().into()) } } From 1eda34a39510fa64f3fc5e17a1d5e65e2d9214a9 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 1 Nov 2023 15:46:39 +1300 Subject: [PATCH 033/155] WIP - Add StakingRound as time unit for redeeming staking --- primitives/metaverse/src/lib.rs | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index a867f350a..662d68839 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -462,3 +462,53 @@ pub struct CampaignInfo, } +// For multiple time calculation type +#[derive(Encode, Decode, Clone, RuntimeDebug, Eq, TypeInfo, MaxEncodedLen)] +pub enum StakingRound { + Era(#[codec(compact)] u32), + Round(#[codec(compact)] u32), + Epoch(#[codec(compact)] u32), + Hour(#[codec(compact)] u32), +} + +impl Default for TimeUnit { + fn default() -> Self { + StakingRound::Era(0u32) + } +} + +impl PartialEq for StakingRound { + fn eq(&self, other: &Self) -> bool { + match (&self, other) { + (Self::Era(a), Self::Era(b)) => a.eq(b), + (Self::Round(a), Self::Round(b)) => a.eq(b), + (Self::Epoch(a), Self::Epoch(b)) => a.eq(b), + (Self::Hour(a), Self::Hour(b)) => a.eq(b), + _ => false, + } + } +} + +impl Ord for StakingRound { + fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { + match (&self, other) { + (Self::Era(a), Self::Era(b)) => a.cmp(b), + (Self::Round(a), Self::Round(b)) => a.cmp(b), + (Self::Epoch(a), Self::Epoch(b)) => a.cmp(b), + (Self::Hour(a), Self::Hour(b)) => a.cmp(b), + _ => sp_std::cmp::Ordering::Less, + } + } +} + +impl PartialOrd for StakingRound { + fn partial_cmp(&self, other: &Self) -> Option { + match (&self, other) { + (Self::Era(a), Self::Era(b)) => Some(a.cmp(b)), + (Self::Round(a), Self::Round(b)) => Some(a.cmp(b)), + (Self::Epoch(a), Self::Kblock(b)) => Some(a.cmp(b)), + (Self::Hour(a), Self::Hour(b)) => Some(a.cmp(b)), + _ => None, + } + } +} From ab081da4cef3bdbae0f95910bf3fdd1a4901bddc Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 1 Nov 2023 15:47:00 +1300 Subject: [PATCH 034/155] WIP - Implement redeem unstaking system --- pallets/land-minting/src/lib.rs | 38 +++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index be8676e56..1fe2dc679 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -45,6 +45,7 @@ use primitives::{ pub use utils::{MintingRateInfo, Range}; pub use weights::WeightInfo; +pub type QueueId = u32; //#[cfg(feature = "runtime-benchmarks")] //pub mod benchmarking; @@ -67,7 +68,7 @@ pub mod pallet { use primitives::estate::EstateInfo; use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; - use primitives::{AccountId, Balance, CurrencyId, PoolId, RoundIndex, UndeployedLandBlockId}; + use primitives::{AccountId, Balance, CurrencyId, PoolId, RoundIndex, StakingRound, UndeployedLandBlockId}; use crate::utils::{round_issuance_range, MintingRateInfo, PoolInfo}; @@ -126,6 +127,9 @@ pub mod pallet { #[pallet::constant] type PoolAccount: Get; + + #[pallet::constant] + type MaximumQueue: Get; } pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -159,6 +163,30 @@ pub mod pallet { #[pallet::getter(fn minimum_redeem)] pub type MinimumRedeem = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn network_redeem_requests)] + pub type NetworkRedeemQueue = StorageDoubleMap< + _, + Blake2_128Concat, + StakingRound, + Blake2_128Concat, + CurrencyIdOf, + (BalanceOf, BoundedVec, CurrencyIdOf), + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn user_unlock_request)] + pub type UserUnlockRequest = StorageDoubleMap< + _, + Blake2_128Concat, + CurrencyIdOf, + Blake2_128Concat, + QueueId, + (T::AccountId, BalanceOf, StakingRound), + OptionQuery, + >; + #[pallet::genesis_config] pub struct GenesisConfig { pub minting_rate_config: MintingRateInfo, @@ -329,10 +357,16 @@ pub mod pallet { let currency_amount = U256::from(vamount.saturated_into::()) .saturating_mul(network_ledger_balance.saturated_into::().into()) .checked_div(v_amount_total_issuance.saturated_into::().into()) - .ok_or(Error::::CalculationOverflow)? + .ok_or(ArithmeticError::Overflow)? .as_u128() .saturated_into(); + // Check if there is ongoing staking round in queue + // Burn v_amount from account + // Deduct total amount from PoolLedger + // Keep track of total unlock + // + // Emit deposit event Self::deposit_event(Event::Deposited(who, pool_id, vamount)); Ok(().into()) From 38b1ed462108e81d6cee09dae6078bd680be0a12 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 2 Nov 2023 09:49:26 +1300 Subject: [PATCH 035/155] WIP - Calculate the next staking round --- pallets/land-minting/src/lib.rs | 63 ++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 1fe2dc679..022b2f4fb 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -39,8 +39,8 @@ pub use pallet::*; use primitives::estate::EstateInfo; use primitives::{ estate::{Estate, LandUnitStatus, LeaseContract, OwnerId}, - Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, TokenId, UndeployedLandBlock, - UndeployedLandBlockId, UndeployedLandBlockType, + Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, StakingRound, TokenId, + UndeployedLandBlock, UndeployedLandBlockId, UndeployedLandBlockType, }; pub use utils::{MintingRateInfo, Range}; pub use weights::WeightInfo; @@ -187,6 +187,18 @@ pub mod pallet { OptionQuery, >; + #[pallet::storage] + #[pallet::getter(fn unlock_duration)] + pub type UnlockDuration = StorageMap<_, Twox64Concat, CurrencyIdOf, StakingRound>; + + #[pallet::storage] + #[pallet::getter(fn current_staking_round)] + pub type CurrentStakingRound = StorageMap<_, Twox64Concat, CurrencyIdOf, StakingRound>; + + #[pallet::storage] + #[pallet::getter(fn queue_next_id)] + pub type QueueNextId = StorageMap<_, Twox64Concat, CurrencyIdOf, u32, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub minting_rate_config: MintingRateInfo, @@ -224,6 +236,10 @@ pub mod pallet { Overflow, /// Below minimum redemption BelowMinimumRedeem, + /// No current staking round + NoCurrentStakingRound, + /// Unexpected + Unexpected, } #[pallet::call] @@ -361,11 +377,10 @@ pub mod pallet { .as_u128() .saturated_into(); - // Check if there is ongoing staking round in queue - // Burn v_amount from account - // Deduct total amount from PoolLedger - // Keep track of total unlock - // + match CurrentStakingRound::::get(currency_id) { + Some(staking_round) => {} + None => return Err(Error::::NoCurrentStakingRound.into()), + } // Emit deposit event Self::deposit_event(Event::Deposited(who, pool_id, vamount)); @@ -374,4 +389,36 @@ pub mod pallet { } } -impl Pallet {} +impl Pallet { + #[transactional] + pub fn calculate_next_staking_round(a: StakingRound, b: StakingRound) -> Result { + let result = match a { + StakingRound::Era(era_a) => match b { + StakingRound::Era(era_b) => { + StakingRound::Era(era_a.checked_add(era_b).ok_or(ArithmeticError::Overflow)?) + } + _ => return Err(Error::::Unexpected.into()), + }, + StakingRound::Round(round_a) => match b { + StakingRound::Round(round_b) => { + StakingRound::Round(round_a.checked_add(round_b).ok_or(ArithmeticError::Overflow)?) + } + _ => return Err(Error::::Unexpected.into()), + }, + StakingRound::Epoch(epoch_a) => match b { + StakingRound::Epoch(epoch_b) => { + StakingRound::Epoch(epoch_a.checked_add(epoch_b).ok_or(ArithmeticError::Overflow)?) + } + _ => return Err(Error::::Unexpected.into()), + }, + StakingRound::Hour(hour_a) => match b { + StakingRound::Hour(hour_b) => { + StakingRound::Hour(hour_a.checked_add(hour_b).ok_or(ArithmeticError::Overflow)?) + } + _ => return Err(Error::::Unexpected.into()), + }, + }; + + Ok(result) + } +} From c373e38c314f64a04884207355da7ef0bd40aed6 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 2 Nov 2023 10:08:22 +1300 Subject: [PATCH 036/155] WIP - Update pool and network ledger --- pallets/land-minting/src/lib.rs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 022b2f4fb..1850b11cf 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -149,12 +149,12 @@ pub mod pallet { #[pallet::getter(fn pool)] pub type Pool = StorageMap<_, Twox64Concat, PoolId, PoolInfo, T::AccountId>, ValueQuery>; - /// Pool ledger that keeps track of Pool id and balance + /// Pool ledger that keeps track of Pool id and balance of base currency #[pallet::storage] #[pallet::getter(fn pool_ledger)] pub type PoolLedger = StorageMap<_, Twox64Concat, PoolId, BalanceOf, ValueQuery>; - /// Network ledger + /// Network ledger that keep track of all staking across all pools #[pallet::storage] #[pallet::getter(fn network_ledger)] pub type NetworkLedger = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; @@ -318,6 +318,16 @@ pub mod pallet { // Deposit vAmount to user using T::MultiCurrency::deposit T::MultiCurrency::deposit(currency_id, &who, v_amount)?; + // Update this specific pool ledger to keep track of pool balance + PoolLedger::::mutate(&pool_id, |pool| -> Result<(), Error> { + *pool = pool.checked_add(&amount_after_fee).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; + + NetworkLedger::::mutate(¤cy_id, |pool| -> Result<(), Error> { + *pool = pool.checked_add(&amount_after_fee).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; // Transfer amount to PoolAccount using T::MultiCurrency::transfer // Assuming `PoolAccount` is an associated type that represents the pool's account ID or a method to // get it. @@ -377,8 +387,24 @@ pub mod pallet { .as_u128() .saturated_into(); + // Check current staking era - only failed when there is no current staking era + // Staking era get checked and updated every blocks match CurrentStakingRound::::get(currency_id) { - Some(staking_round) => {} + Some(staking_round) => { + // Calculate the staking duration to be locked + let new_staking_round = Self::calculate_next_staking_round( + Self::unlock_duration(currency_id).ok_or(Error::::UnlockDurationNotFound)?, + staking_round, + )?; + // Burn currency + T::MultiCurrency::withdraw(vcurrency_id, &who, vamount)?; + + // Update pool ledger + PoolLedger::::mutate(&pool_id, |pool| -> Result<(), Error> { + *pool = pool.checked_sub(¤cy_amount).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; + } None => return Err(Error::::NoCurrentStakingRound.into()), } From 250e85d169c197603c93affce9bb490a778c3e24 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 3 Nov 2023 14:04:47 +1300 Subject: [PATCH 037/155] WIP - Implement redeem logic --- pallets/land-minting/src/lib.rs | 63 +++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 1850b11cf..c5922dc54 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -219,7 +219,11 @@ pub mod pallet { /// New staking round started [Starting Block, Round, Total Land Unit] NewRound(T::BlockNumber, RoundIndex, u64), /// New pool created - PoolCreated(T::AccountId, u32, CurrencyIdOf), + PoolCreated(T::AccountId, PoolId, CurrencyIdOf), + /// Deposited + Deposited(T::AccountId, PoolId, BalanceOf), + /// Redeemed + Redeemed(T::AccountId, PoolId, BalanceOf), } #[pallet::error] @@ -404,12 +408,67 @@ pub mod pallet { *pool = pool.checked_sub(¤cy_amount).ok_or(ArithmeticError::Overflow)?; Ok(()) })?; + + let next_queue_id = Self::queue_next_id(currency_id); + UserUnlockRequest::::insert( + ¤cy_id, + &next_queue_id, + (&who, currency_amount, &new_staking_round), + ); + + if UserUnlockRequest::::get(&who, ¤cy_id).is_some() { + UserUnlockRequest::::mutate(&who, ¤cy_id, |value| -> Result<(), Error> { + if let Some((total_locked, ledger_list)) = value { + ledger_list.try_push(next_id).map_err(|_| Error::::TooManyRedeems)?; + + *total_locked = total_locked + .checked_add(&token_amount) + .ok_or(Error::::CalculationOverflow)?; + }; + Ok(()) + })?; + } else { + let mut ledger_list_origin = BoundedVec::::default(); + ledger_list_origin + .try_push(next_queue_id) + .map_err(|_| Error::::TooManyRedeems)?; + UserUnlockRequest::::insert(&who, ¤cy_id, (currency_amount, ledger_list_origin)); + } + + if let Some((_, _, _token_id)) = NetworkRedeemQueue::::get(&new_staking_round, ¤cy_id) { + NetworkRedeemQueue::::mutate( + &new_staking_round, + ¤cy_id, + |value| -> Result<(), Error> { + if let Some((total_locked, ledger_list, _token_id)) = value { + ledger_list + .try_push(next_queue_id) + .map_err(|_| Error::::TooManyRedeems)?; + *total_locked = total_locked + .checked_add(¤cy_amount) + .ok_or(Error::::CalculationOverflow)?; + }; + Ok(()) + }, + )?; + } else { + let mut ledger_list_origin = BoundedVec::::default(); + ledger_list_origin + .try_push(next_queue_id) + .map_err(|_| Error::::TooManyRedeems)?; + + NetworkRedeemQueue::::insert( + &new_staking_round, + ¤cy_id, + (currency_amount, ledger_list_origin, currency_id), + ); + } } None => return Err(Error::::NoCurrentStakingRound.into()), } // Emit deposit event - Self::deposit_event(Event::Deposited(who, pool_id, vamount)); + Self::deposit_event(Event::Redeemed(who, pool_id, vamount)); Ok(().into()) } } From b2f5e89754bbd138a5a96d53b99b4da357141bbc Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 3 Nov 2023 14:17:23 +1300 Subject: [PATCH 038/155] WIP - Update StakingRound implementation --- primitives/metaverse/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index 662d68839..5b2e39ea3 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -471,7 +471,7 @@ pub enum StakingRound { Hour(#[codec(compact)] u32), } -impl Default for TimeUnit { +impl Default for StakingRound { fn default() -> Self { StakingRound::Era(0u32) } @@ -506,7 +506,7 @@ impl PartialOrd for StakingRound { match (&self, other) { (Self::Era(a), Self::Era(b)) => Some(a.cmp(b)), (Self::Round(a), Self::Round(b)) => Some(a.cmp(b)), - (Self::Epoch(a), Self::Kblock(b)) => Some(a.cmp(b)), + (Self::Epoch(a), Self::Epoch(b)) => Some(a.cmp(b)), (Self::Hour(a), Self::Hour(b)) => Some(a.cmp(b)), _ => None, } From af6c8ff312023739bc58b92c164b159c6715e4d2 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 3 Nov 2023 14:17:44 +1300 Subject: [PATCH 039/155] WIP - Implement Error item --- pallets/land-minting/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index c5922dc54..50f823f20 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -244,6 +244,8 @@ pub mod pallet { NoCurrentStakingRound, /// Unexpected Unexpected, + /// Too many redeems + TooManyRedeems, } #[pallet::call] @@ -419,11 +421,13 @@ pub mod pallet { if UserUnlockRequest::::get(&who, ¤cy_id).is_some() { UserUnlockRequest::::mutate(&who, ¤cy_id, |value| -> Result<(), Error> { if let Some((total_locked, ledger_list)) = value { - ledger_list.try_push(next_id).map_err(|_| Error::::TooManyRedeems)?; + ledger_list + .try_push(next_queue_id) + .map_err(|_| Error::::TooManyRedeems)?; *total_locked = total_locked - .checked_add(&token_amount) - .ok_or(Error::::CalculationOverflow)?; + .checked_add(¤cy_amount) + .ok_or(ArithmeticError::Overflow)?; }; Ok(()) })?; @@ -446,7 +450,7 @@ pub mod pallet { .map_err(|_| Error::::TooManyRedeems)?; *total_locked = total_locked .checked_add(¤cy_amount) - .ok_or(Error::::CalculationOverflow)?; + .ok_or(ArithmeticError::Overflow)?; }; Ok(()) }, From cbd68b5cd03b3c9c266a0cf0e8c457ef3cd75bd6 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 3 Nov 2023 14:18:05 +1300 Subject: [PATCH 040/155] WIP - Change correct CurrencyIdManagement data type --- pallets/asset-manager/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/asset-manager/src/lib.rs b/pallets/asset-manager/src/lib.rs index ec1f418e6..d4df52a9b 100644 --- a/pallets/asset-manager/src/lib.rs +++ b/pallets/asset-manager/src/lib.rs @@ -349,7 +349,7 @@ where } impl CurrencyIdManagement for ForeignAssetMapping { - fn check_token_exist(currency_id: CurrencyId) -> bool { + fn check_token_exist(token_id: TokenId) -> bool { return true; } } From 1df30c0608ccddb6ac61f12211b34e3627a2ab61 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 3 Nov 2023 14:32:07 +1300 Subject: [PATCH 041/155] WIP - Update failed build cargo --- pallets/land-minting/src/lib.rs | 19 +--- pallets/land-minting/src/utils.rs | 142 ------------------------------ 2 files changed, 2 insertions(+), 159 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 50f823f20..e285532b2 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -35,14 +35,12 @@ use sp_std::vec::Vec; use auction_manager::{Auction, CheckAuctionItemHandler}; use core_primitives::*; -pub use pallet::*; use primitives::estate::EstateInfo; use primitives::{ estate::{Estate, LandUnitStatus, LeaseContract, OwnerId}, Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, StakingRound, TokenId, UndeployedLandBlock, UndeployedLandBlockId, UndeployedLandBlockType, }; -pub use utils::{MintingRateInfo, Range}; pub use weights::WeightInfo; pub type QueueId = u32; @@ -58,6 +56,7 @@ mod tests; pub mod weights; +pub use pallet::*; #[frame_support::pallet] pub mod pallet { use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; @@ -70,7 +69,7 @@ pub mod pallet { use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; use primitives::{AccountId, Balance, CurrencyId, PoolId, RoundIndex, StakingRound, UndeployedLandBlockId}; - use crate::utils::{round_issuance_range, MintingRateInfo, PoolInfo}; + use crate::utils::PoolInfo; use super::*; @@ -199,20 +198,6 @@ pub mod pallet { #[pallet::getter(fn queue_next_id)] pub type QueueNextId = StorageMap<_, Twox64Concat, CurrencyIdOf, u32, ValueQuery>; - #[pallet::genesis_config] - pub struct GenesisConfig { - pub minting_rate_config: MintingRateInfo, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - minting_rate_config: Default::default(), - } - } - } - #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { diff --git a/pallets/land-minting/src/utils.rs b/pallets/land-minting/src/utils.rs index 164224e2c..3379ae201 100644 --- a/pallets/land-minting/src/utils.rs +++ b/pallets/land-minting/src/utils.rs @@ -21,14 +21,9 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{Perbill, Permill, RuntimeDebug}; -use crate::{AllLandUnitsCount, TotalUndeployedLandUnit}; // Helper methods to compute the issuance rate for undeployed land. use crate::pallet::{Config, Pallet}; -const SECONDS_PER_YEAR: u32 = 31557600; -const SECONDS_PER_BLOCK: u32 = 12; -const BLOCKS_PER_YEAR: u32 = SECONDS_PER_YEAR / SECONDS_PER_BLOCK; - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Eq, PartialEq, Clone, Encode, Decode, Default, RuntimeDebug, TypeInfo)] pub struct PoolInfo { @@ -39,140 +34,3 @@ pub struct PoolInfo { /// Max nft rewards pub max: u32, } - -fn rounds_per_year() -> u32 { - let blocks_per_round = >::round().length; - BLOCKS_PER_YEAR / blocks_per_round -} - -fn get_annual_max_issuance(max_supply: u64, annual_percentage: u64) -> u64 { - let total_land_unit_circulating = >::get(); - let total_undeployed_land_unit_circulating = >::get(); - let circulating = total_land_unit_circulating.saturating_add(total_undeployed_land_unit_circulating); - max_supply.saturating_sub(circulating).saturating_mul(annual_percentage) -} - -/// Compute round issuance range from round inflation range and current total issuance -pub fn round_issuance_range(config: MintingRateInfo) -> Range { - // Get total round per year - let total_round_per_year = rounds_per_year::(); - - // Get total land unit circulating - let total_land_unit_circulating = >::get(); - - // Get total undeployed land unit circulating - let total_undeployed_land_unit_circulating = >::get(); - - // Total circulating - let circulating = total_land_unit_circulating.saturating_add(total_undeployed_land_unit_circulating); - - // Total annual minting percent - let annual_percentage = Perbill::from_percent(config.annual as u32).deconstruct(); - - // Round percentage minting rate - let round_percentage = annual_percentage.checked_div(total_round_per_year).unwrap(); - - // Convert to percentage - let round_percentage_per_bill = Perbill::from_parts(round_percentage); - - // Return range - could implement more cases in the future. - Range { - min: round_percentage_per_bill * circulating, - ideal: round_percentage_per_bill * circulating, - max: round_percentage_per_bill * circulating, - } -} - -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Default, RuntimeDebug, TypeInfo)] -pub struct Range { - pub min: T, - pub ideal: T, - pub max: T, -} - -impl Range { - pub fn is_valid(&self) -> bool { - self.max >= self.ideal && self.ideal >= self.min - } -} - -impl From for Range { - fn from(other: T) -> Range { - Range { - min: other, - ideal: other, - max: other, - } - } -} - -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Eq, PartialEq, Clone, Encode, Decode, Default, RuntimeDebug, TypeInfo)] -pub struct MintingRateInfo { - /// Number of metaverse expectations - pub expect: Range, - /// Annual minting range - pub annual: u64, - /// Max total supply - pub max: u64, -} - -impl MintingRateInfo { - pub fn new(annual: u64, expect: Range, max: u64) -> MintingRateInfo { - MintingRateInfo { expect, annual, max } - } - - /// Set minting rate expectations - pub fn set_expectations(&mut self, expect: Range) { - self.expect = expect; - } - - /// Set minting rate expectations - pub fn set_max(&mut self, max: u64) { - self.max = max; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Compute round issuance range from round inflation range and current total issuance - pub fn mock_round_issuance_per_year( - config: MintingRateInfo, - circulation: u64, - total_round_per_year: u32, - ) -> Range { - let annual_percentage = Perbill::from_percent(config.annual as u32).deconstruct(); - let round_percentage = annual_percentage.checked_div(total_round_per_year).unwrap(); - - let round_percentage_per_bill = Perbill::from_parts(round_percentage); - - Range { - min: round_percentage_per_bill * circulation, - ideal: round_percentage_per_bill * circulation, - max: round_percentage_per_bill * circulation, - } - } - - #[test] - fn simple_round_issuance() { - // 5% minting rate for 100_000 land unit = 100 minted over the year - // let's assume there are 10 periods in a year - // => mint 100 over 10 periods => 10 minted per period - - let mock_config: MintingRateInfo = MintingRateInfo { - expect: Default::default(), - annual: 5, - max: 100_000, - }; - - let round_issuance = mock_round_issuance_per_year(mock_config, 2_000, 10); - - // make sure 10 land unit deploy per period - assert_eq!(round_issuance.min, 10); - assert_eq!(round_issuance.ideal, 10); - assert_eq!(round_issuance.max, 10); - } -} From 300ca2400af530798cec39f96d27b54a1062007b Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 7 Nov 2023 21:05:24 +1300 Subject: [PATCH 042/155] WIP - Collect fees update --- pallets/land-minting/src/lib.rs | 41 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index e285532b2..2b2cc56bd 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -26,7 +26,9 @@ use frame_support::{ }; use frame_system::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed}; +use orml_traits::MultiCurrency; use scale_info::TypeInfo; +use sp_runtime::traits::CheckedSub; use sp_runtime::{ traits::{AccountIdConversion, Convert, One, Saturating, Zero}, ArithmeticError, DispatchError, Perbill, SaturatedConversion, @@ -35,6 +37,7 @@ use sp_std::vec::Vec; use auction_manager::{Auction, CheckAuctionItemHandler}; use core_primitives::*; +pub use pallet::*; use primitives::estate::EstateInfo; use primitives::{ estate::{Estate, LandUnitStatus, LeaseContract, OwnerId}, @@ -56,7 +59,6 @@ mod tests; pub mod weights; -pub use pallet::*; #[frame_support::pallet] pub mod pallet { use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; @@ -112,7 +114,7 @@ pub mod pallet { /// Default max bound for each metaverse mapping system, this could change through proposal type DefaultMaxBound: Get<(i32, i32)>; - /// Network fee charged when deploying a land block or creating an estate + /// Network fee charged when depositing or redeeming #[pallet::constant] type NetworkFee: Get>; @@ -292,7 +294,7 @@ pub mod pallet { // Collect deposit fee for protocol // Assuming there's a function `collect_deposit_fee` that deducts a fee for deposits. - let amount_after_fee = Self::collect_deposit_fee(&who, amount)?; + let amount_after_fee = Self::collect_deposit_fee(&who, currency_id, amount)?; let v_currency_id = T::CurrencyIdManagement::convert_to_vcurrency(currency_id) .map_err(|_| Error::::CurrencyIsNotSupported)?; @@ -363,8 +365,8 @@ pub mod pallet { let network_ledger_balance = Self::network_ledger(currency_id); // Collect deposit fee for protocol - // Assuming there's a function `collect_deposit_fee` that deducts a fee for deposits. - let amount_after_fee = Self::collect_deposit_fee(&who, vamount)?; + // Assuming there's a function `collect_redeem_fee` that deducts a fee for deposits. + let amount_after_fee = Self::collect_redeem_fee(&who, vcurrency_id, vamount)?; let vamount = vamount .checked_sub(&amount_after_fee) .ok_or(ArithmeticError::Overflow)?; @@ -495,4 +497,33 @@ impl Pallet { Ok(result) } + + #[transactional] + pub fn collect_deposit_fee( + who: T::AccountId, + currency_id: BalanceOf, + amount: BalanceOf, + ) -> Result, DispatchError> { + let (deposit_rate, _redeem_rate) = Fees::::get(); + + let deposit_fee = deposit_rate * amount; + let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(ArithmeticError::Overflow)?; + T::MultiCurrency::transfer(currency_id, who, &T::NetworkFee::get(), deposit_fee)?; + + return amount_exclude_fee; + } + + #[transactional] + pub fn collect_redeem_fee( + who: T::AccountId, + currency_id: BalanceOf, + amount: BalanceOf, + ) -> Result, DispatchError> { + let (_mint_rate, redeem_rate) = Fees::::get(); + let redeem_fee = redeem_rate * amount; + let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(ArithmeticError::Overflow)?; + T::MultiCurrency::transfer(currency_id, who, &T::NetworkFee::get(), redeem_fee)?; + + return amount_exclude_fee; + } } From 6ff25635ed37abc3c42e533d8f5fefe2dbfb01b6 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 11:01:44 +1300 Subject: [PATCH 043/155] WIP - Convert native to fungible --- pallets/asset-manager/src/lib.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pallets/asset-manager/src/lib.rs b/pallets/asset-manager/src/lib.rs index d4df52a9b..475dca7bf 100644 --- a/pallets/asset-manager/src/lib.rs +++ b/pallets/asset-manager/src/lib.rs @@ -348,8 +348,19 @@ where } } -impl CurrencyIdManagement for ForeignAssetMapping { - fn check_token_exist(token_id: TokenId) -> bool { +impl CurrencyIdManagement for ForeignAssetMapping { + fn check_token_exist(token_id: FungibleTokenId) -> bool { return true; } + + fn convert_to_rcurrency(currency_id: FungibleTokenId) -> Result { + match currency_id { + FungibleTokenId::NativeToken(token_id) => Ok(FungibleTokenId::FungibleToken(token_id)), + _ => Err(()), + } + } + + fn convert_to_currency(currency_id: FungibleTokenId) -> Result { + todo!() + } } From 8500dca06c4ce4048881d0bbe17bda36178253a2 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 11:02:12 +1300 Subject: [PATCH 044/155] WIP - Update logic of redeeming queue --- pallets/land-minting/src/lib.rs | 210 ++++++++++++++++++++------------ 1 file changed, 130 insertions(+), 80 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 2b2cc56bd..f998b4319 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -95,7 +95,11 @@ pub mod pallet { /// Currency type type Currency: Currency + ReservableCurrency; /// Multi currencies type that handles different currency type in auction - type MultiCurrency: MultiReservableCurrency; + type MultiCurrency: MultiReservableCurrency< + Self::AccountId, + CurrencyId = FungibleTokenId, + Balance = BalanceOf, + >; /// Weight implementation for estate extrinsics type WeightInfo: WeightInfo; @@ -114,7 +118,7 @@ pub mod pallet { /// Default max bound for each metaverse mapping system, this could change through proposal type DefaultMaxBound: Get<(i32, i32)>; - /// Network fee charged when depositing or redeeming + /// Network fee charged on pool creation #[pallet::constant] type NetworkFee: Get>; @@ -131,11 +135,11 @@ pub mod pallet { #[pallet::constant] type MaximumQueue: Get; + + type CurrencyIdConversion: CurrencyIdManagement; } pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; - pub type CurrencyIdOf = - <::MultiCurrency as MultiCurrency<::AccountId>>::CurrencyId; #[pallet::storage] #[pallet::getter(fn next_class_id)] @@ -148,7 +152,7 @@ pub mod pallet { /// Keep track of Pool detail #[pallet::storage] #[pallet::getter(fn pool)] - pub type Pool = StorageMap<_, Twox64Concat, PoolId, PoolInfo, T::AccountId>, ValueQuery>; + pub type Pool = StorageMap<_, Twox64Concat, PoolId, PoolInfo, OptionQuery>; /// Pool ledger that keeps track of Pool id and balance of base currency #[pallet::storage] @@ -158,30 +162,45 @@ pub mod pallet { /// Network ledger that keep track of all staking across all pools #[pallet::storage] #[pallet::getter(fn network_ledger)] - pub type NetworkLedger = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + pub type NetworkLedger = StorageMap<_, Twox64Concat, FungibleTokenId, BalanceOf, ValueQuery>; #[pallet::storage] #[pallet::getter(fn minimum_redeem)] - pub type MinimumRedeem = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + pub type MinimumRedeem = StorageMap<_, Twox64Concat, FungibleTokenId, BalanceOf, ValueQuery>; + /// Keep track of each staking round, how many items in queue need to be redeem #[pallet::storage] - #[pallet::getter(fn network_redeem_requests)] - pub type NetworkRedeemQueue = StorageDoubleMap< + #[pallet::getter(fn staking_round_redeem_requests)] + pub type StakingRoundRedeemQueue = StorageDoubleMap< _, Blake2_128Concat, StakingRound, Blake2_128Concat, - CurrencyIdOf, - (BalanceOf, BoundedVec, CurrencyIdOf), + FungibleTokenId, + (BalanceOf, BoundedVec, FungibleTokenId), + OptionQuery, + >; + + /// Keep track of user ledger that how many queue items that needs to be unlocked + #[pallet::storage] + #[pallet::getter(fn user_redeem_requests)] + pub type UserCurrencyRedeemQueue = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + FungibleTokenId, + (BalanceOf, BoundedVec), OptionQuery, >; + /// Keep track of queue item as well as account that locked amount of currency can be redeemed #[pallet::storage] - #[pallet::getter(fn user_unlock_request)] - pub type UserUnlockRequest = StorageDoubleMap< + #[pallet::getter(fn currency_redeem_requests)] + pub type CurrencyRedeemQueue = StorageDoubleMap< _, Blake2_128Concat, - CurrencyIdOf, + FungibleTokenId, Blake2_128Concat, QueueId, (T::AccountId, BalanceOf, StakingRound), @@ -190,15 +209,15 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn unlock_duration)] - pub type UnlockDuration = StorageMap<_, Twox64Concat, CurrencyIdOf, StakingRound>; + pub type UnlockDuration = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound>; #[pallet::storage] #[pallet::getter(fn current_staking_round)] - pub type CurrentStakingRound = StorageMap<_, Twox64Concat, CurrencyIdOf, StakingRound>; + pub type CurrentStakingRound = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound>; #[pallet::storage] #[pallet::getter(fn queue_next_id)] - pub type QueueNextId = StorageMap<_, Twox64Concat, CurrencyIdOf, u32, ValueQuery>; + pub type QueueNextId = StorageMap<_, Twox64Concat, FungibleTokenId, u32, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] @@ -206,7 +225,7 @@ pub mod pallet { /// New staking round started [Starting Block, Round, Total Land Unit] NewRound(T::BlockNumber, RoundIndex, u64), /// New pool created - PoolCreated(T::AccountId, PoolId, CurrencyIdOf), + PoolCreated(T::AccountId, PoolId, FungibleTokenId), /// Deposited Deposited(T::AccountId, PoolId, BalanceOf), /// Redeemed @@ -233,6 +252,12 @@ pub mod pallet { Unexpected, /// Too many redeems TooManyRedeems, + /// Arthimetic Overflow + ArithmeticOverflow, + /// Token type is not supported + NotSupportTokenType, + /// Unlock duration not found + UnlockDurationNotFound, } #[pallet::call] @@ -240,7 +265,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::mint_land())] pub fn create_pool( origin: OriginFor, - currency_id: CurrencyIdOf, + currency_id: FungibleTokenId, max_nft_reward: u32, commission: Permill, ) -> DispatchResult { @@ -255,7 +280,7 @@ pub mod pallet { // TODO Check commission below threshold // Collect pool creation fee - Self::collect_pool_creation_fee(&who)?; + Self::collect_pool_creation_fee(&who, currency_id)?; // Next pool id let next_pool_id = NextPoolId::::try_mutate(|id| -> Result { @@ -266,8 +291,8 @@ pub mod pallet { let new_pool = PoolInfo { creator: who.clone(), - commission: commission, - currency_id: currency_id, + commission, + currency_id, max: max_nft_reward, }; @@ -296,29 +321,33 @@ pub mod pallet { // Assuming there's a function `collect_deposit_fee` that deducts a fee for deposits. let amount_after_fee = Self::collect_deposit_fee(&who, currency_id, amount)?; - let v_currency_id = T::CurrencyIdManagement::convert_to_vcurrency(currency_id) + let r_currency_id = T::CurrencyIdConversion::convert_to_rcurrency(currency_id) .map_err(|_| Error::::CurrencyIsNotSupported)?; - // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount + // Calculate rAmount as receipt of amount locked. The formula based on rAmount = (amount * rAmount // total issuance)/network ledger balance - let v_amount_total_issuance = T::MultiCurrency::total_issuance(v_currency_id); - let v_amount = U256::from(amount_after_fee.saturated_into::()) - .saturating_mul(v_amount_total_issuance.saturated_into::().into()) + let r_amount_total_issuance = T::MultiCurrency::total_issuance(r_currency_id); + let r_amount = U256::from(amount_after_fee.saturated_into::()) + .saturating_mul(r_amount_total_issuance.saturated_into::().into()) .checked_div(network_ledger_balance.saturated_into::().into()) - .ok_or(ArithmeticError::Overflow)? + .ok_or(Error::::ArithmeticOverflow)? .as_u128() .saturated_into(); - // Deposit vAmount to user using T::MultiCurrency::deposit - T::MultiCurrency::deposit(currency_id, &who, v_amount)?; + // Deposit rAmount to user using T::MultiCurrency::deposit + T::MultiCurrency::deposit(currency_id, &who, r_amount)?; // Update this specific pool ledger to keep track of pool balance PoolLedger::::mutate(&pool_id, |pool| -> Result<(), Error> { - *pool = pool.checked_add(&amount_after_fee).ok_or(ArithmeticError::Overflow)?; + *pool = pool + .checked_add(&amount_after_fee) + .ok_or(Error::::ArithmeticOverflow)?; Ok(()) })?; NetworkLedger::::mutate(¤cy_id, |pool| -> Result<(), Error> { - *pool = pool.checked_add(&amount_after_fee).ok_or(ArithmeticError::Overflow)?; + *pool = pool + .checked_add(&amount_after_fee) + .ok_or(Error::::ArithmeticOverflow)?; Ok(()) })?; // Transfer amount to PoolAccount using T::MultiCurrency::transfer @@ -328,7 +357,7 @@ pub mod pallet { currency_id, &who, &T::PoolAccount::get().into_account_truncating(), - amount, + amount_after_fee, )?; // Emit deposit event @@ -340,17 +369,17 @@ pub mod pallet { pub fn redeem( origin: OriginFor, pool_id: PoolId, - vcurrency_id: CurrencyIdOf, - vamount: BalanceOf, + v_currency_id: FungibleTokenId, + r_amount: BalanceOf, ) -> DispatchResult { // Ensure user is signed let who = ensure_signed(origin)?; ensure!( - vamount >= MinimumRedeem::::get(vcurrency_id), + r_amount >= MinimumRedeem::::get(v_currency_id), Error::::BelowMinimumRedeem ); - let currency_id = T::CurrencyIdManagement::convert_to_currency(vcurrency_id) + let currency_id = T::CurrencyIdConversion::convert_to_currency(v_currency_id) .map_err(|_| Error::::NotSupportTokenType)?; // Check if pool exists @@ -366,17 +395,17 @@ pub mod pallet { // Collect deposit fee for protocol // Assuming there's a function `collect_redeem_fee` that deducts a fee for deposits. - let amount_after_fee = Self::collect_redeem_fee(&who, vcurrency_id, vamount)?; - let vamount = vamount + let amount_after_fee = Self::collect_redeem_fee(&who, v_currency_id, r_amount)?; + let r_amount = r_amount .checked_sub(&amount_after_fee) - .ok_or(ArithmeticError::Overflow)?; - // Calculate vAmount as receipt of amount locked. The formula based on vAmount = (amount * vAmount + .ok_or(Error::::ArithmeticOverflow)?; + // Calculate rAmount as receipt of amount locked. The formula based on rAmount = (amount * rAmount // total issuance)/network ledger balance - let v_amount_total_issuance = T::MultiCurrency::total_issuance(vcurrency_id); - let currency_amount = U256::from(vamount.saturated_into::()) + let r_amount_total_issuance = T::MultiCurrency::total_issuance(v_currency_id); + let currency_amount = U256::from(r_amount.saturated_into::()) .saturating_mul(network_ledger_balance.saturated_into::().into()) - .checked_div(v_amount_total_issuance.saturated_into::().into()) - .ok_or(ArithmeticError::Overflow)? + .checked_div(r_amount_total_issuance.saturated_into::().into()) + .ok_or(Error::::ArithmeticOverflow)? .as_u128() .saturated_into(); @@ -390,65 +419,80 @@ pub mod pallet { staking_round, )?; // Burn currency - T::MultiCurrency::withdraw(vcurrency_id, &who, vamount)?; + T::MultiCurrency::withdraw(v_currency_id, &who, amount_after_fee)?; // Update pool ledger PoolLedger::::mutate(&pool_id, |pool| -> Result<(), Error> { - *pool = pool.checked_sub(¤cy_amount).ok_or(ArithmeticError::Overflow)?; + *pool = pool + .checked_sub(¤cy_amount) + .ok_or(Error::::ArithmeticOverflow)?; Ok(()) })?; + // Get current queue_id let next_queue_id = Self::queue_next_id(currency_id); - UserUnlockRequest::::insert( + + // Add request into network currency redeem queue + CurrencyRedeemQueue::::insert( ¤cy_id, &next_queue_id, - (&who, currency_amount, &new_staking_round), + (who, currency_amount, new_staking_round), ); - if UserUnlockRequest::::get(&who, ¤cy_id).is_some() { - UserUnlockRequest::::mutate(&who, ¤cy_id, |value| -> Result<(), Error> { - if let Some((total_locked, ledger_list)) = value { - ledger_list + // Handle ledger of user and currency - user,currency: total_amount_unlocked, vec![queue_id] + // Check if you already has any redeem requests + if UserCurrencyRedeemQueue::::get(&who, ¤cy_id).is_some() { + // Add new queue id into the list + UserCurrencyRedeemQueue::::mutate(&who, ¤cy_id, |value| -> Result<(), Error> { + // + if let Some((amount_need_unlocked, existing_queue)) = value { + existing_queue .try_push(next_queue_id) .map_err(|_| Error::::TooManyRedeems)?; - *total_locked = total_locked + *amount_need_unlocked = amount_need_unlocked .checked_add(¤cy_amount) - .ok_or(ArithmeticError::Overflow)?; + .ok_or(Error::::ArithmeticOverflow)?; }; Ok(()) })?; } else { - let mut ledger_list_origin = BoundedVec::::default(); - ledger_list_origin + let mut new_queue = BoundedVec::::default(); + new_queue .try_push(next_queue_id) .map_err(|_| Error::::TooManyRedeems)?; - UserUnlockRequest::::insert(&who, ¤cy_id, (currency_amount, ledger_list_origin)); + UserCurrencyRedeemQueue::::insert(&who, ¤cy_id, (currency_amount, new_queue)); } - if let Some((_, _, _token_id)) = NetworkRedeemQueue::::get(&new_staking_round, ¤cy_id) { - NetworkRedeemQueue::::mutate( + // Handle ledger of staking round - executed by hooks on every block - staking_round,currency: + // total_amount_unlocked, vec![queue_id], currency + + // Check if there any existing claim of the next staking round + if let Some((_, _, _token_id)) = StakingRoundRedeemQueue::::get(&new_staking_round, ¤cy_id) + { + StakingRoundRedeemQueue::::mutate( &new_staking_round, ¤cy_id, |value| -> Result<(), Error> { - if let Some((total_locked, ledger_list, _token_id)) = value { - ledger_list + // Add new queue item + if let Some((amount_need_unlocked, existing_queue, _token_id)) = value { + existing_queue .try_push(next_queue_id) .map_err(|_| Error::::TooManyRedeems)?; - *total_locked = total_locked + *amount_need_unlocked = amount_need_unlocked .checked_add(¤cy_amount) - .ok_or(ArithmeticError::Overflow)?; + .ok_or(Error::::ArithmeticOverflow)?; }; Ok(()) }, )?; } else { - let mut ledger_list_origin = BoundedVec::::default(); - ledger_list_origin + let mut new_queue = BoundedVec::::default(); + new_queue .try_push(next_queue_id) .map_err(|_| Error::::TooManyRedeems)?; - NetworkRedeemQueue::::insert( + StakingRoundRedeemQueue::::insert( &new_staking_round, ¤cy_id, (currency_amount, ledger_list_origin, currency_id), @@ -459,7 +503,7 @@ pub mod pallet { } // Emit deposit event - Self::deposit_event(Event::Redeemed(who, pool_id, vamount)); + Self::deposit_event(Event::Redeemed(who, pool_id, r_amount)); Ok(().into()) } } @@ -471,25 +515,25 @@ impl Pallet { let result = match a { StakingRound::Era(era_a) => match b { StakingRound::Era(era_b) => { - StakingRound::Era(era_a.checked_add(era_b).ok_or(ArithmeticError::Overflow)?) + StakingRound::Era(era_a.checked_add(era_b).ok_or(Error::::ArithmeticOverflow)?) } _ => return Err(Error::::Unexpected.into()), }, StakingRound::Round(round_a) => match b { StakingRound::Round(round_b) => { - StakingRound::Round(round_a.checked_add(round_b).ok_or(ArithmeticError::Overflow)?) + StakingRound::Round(round_a.checked_add(round_b).ok_or(Error::::ArithmeticOverflow)?) } _ => return Err(Error::::Unexpected.into()), }, StakingRound::Epoch(epoch_a) => match b { StakingRound::Epoch(epoch_b) => { - StakingRound::Epoch(epoch_a.checked_add(epoch_b).ok_or(ArithmeticError::Overflow)?) + StakingRound::Epoch(epoch_a.checked_add(epoch_b).ok_or(Error::::ArithmeticOverflow)?) } _ => return Err(Error::::Unexpected.into()), }, StakingRound::Hour(hour_a) => match b { StakingRound::Hour(hour_b) => { - StakingRound::Hour(hour_a.checked_add(hour_b).ok_or(ArithmeticError::Overflow)?) + StakingRound::Hour(hour_a.checked_add(hour_b).ok_or(Error::::ArithmeticOverflow)?) } _ => return Err(Error::::Unexpected.into()), }, @@ -500,30 +544,36 @@ impl Pallet { #[transactional] pub fn collect_deposit_fee( - who: T::AccountId, - currency_id: BalanceOf, + who: &T::AccountId, + currency_id: FungibleTokenId, amount: BalanceOf, ) -> Result, DispatchError> { let (deposit_rate, _redeem_rate) = Fees::::get(); let deposit_fee = deposit_rate * amount; - let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(ArithmeticError::Overflow)?; - T::MultiCurrency::transfer(currency_id, who, &T::NetworkFee::get(), deposit_fee)?; + let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(Error::::ArithmeticOverflow)?; + T::MultiCurrency::transfer(currency_id, who, &T::PoolAccount::get(), deposit_fee)?; return amount_exclude_fee; } #[transactional] pub fn collect_redeem_fee( - who: T::AccountId, - currency_id: BalanceOf, + who: &T::AccountId, + currency_id: FungibleTokenId, amount: BalanceOf, ) -> Result, DispatchError> { let (_mint_rate, redeem_rate) = Fees::::get(); let redeem_fee = redeem_rate * amount; - let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(ArithmeticError::Overflow)?; - T::MultiCurrency::transfer(currency_id, who, &T::NetworkFee::get(), redeem_fee)?; + let amount_exclude_fee = amount.checked_sub(&redeem_fee).ok_or(Error::::ArithmeticOverflow)?; + T::MultiCurrency::transfer(currency_id, who, &T::PoolAccount::get(), redeem_fee)?; return amount_exclude_fee; } + + #[transactional] + pub fn collect_pool_creation_fee(who: &T::AccountId, currency_id: FungibleTokenId) -> DispatchResult { + let pool_fee = T::NetworkFee::get(); + T::MultiCurrency::transfer(currency_id, who, &T::PoolAccount::get(), pool_fee) + } } From 6c5fb998d089dddc82042b670118d797b85b7227 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 11:02:35 +1300 Subject: [PATCH 045/155] WIP - Update pool info currency id type --- pallets/land-minting/src/utils.rs | 8 +++++--- traits/core-primitives/src/lib.rs | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pallets/land-minting/src/utils.rs b/pallets/land-minting/src/utils.rs index 3379ae201..32213d80e 100644 --- a/pallets/land-minting/src/utils.rs +++ b/pallets/land-minting/src/utils.rs @@ -21,16 +21,18 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{Perbill, Permill, RuntimeDebug}; +use primitives::FungibleTokenId; + // Helper methods to compute the issuance rate for undeployed land. use crate::pallet::{Config, Pallet}; #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Eq, PartialEq, Clone, Encode, Decode, Default, RuntimeDebug, TypeInfo)] -pub struct PoolInfo { +#[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct PoolInfo { pub creator: AccountId, pub commission: Permill, /// Currency id of the pool - pub currency_id: CurrencyId, + pub currency_id: FungibleTokenId, /// Max nft rewards pub max: u32, } diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 5946eb38b..72f15aa0d 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -401,6 +401,8 @@ impl MiningResourceRateInfo { } } -pub trait CurrencyIdManagement { - fn check_token_exist(currency_id: CurrencyId) -> bool; +pub trait CurrencyIdManagement { + fn check_token_exist(currency_id: FungibleTokenId) -> bool; + fn convert_to_rcurrency(currency_id: FungibleTokenId) -> Result; + fn convert_to_currency(currency_id: FungibleTokenId) -> Result; } From b0be447b07997e1797f1fb9b8a7e5085d5e7be50 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 11:14:40 +1300 Subject: [PATCH 046/155] WIP - Fix build errors of inserting into redeem queue --- pallets/land-minting/src/lib.rs | 37 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index f998b4319..5db8bfb35 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -436,7 +436,7 @@ pub mod pallet { CurrencyRedeemQueue::::insert( ¤cy_id, &next_queue_id, - (who, currency_amount, new_staking_round), + (&who, currency_amount, &new_staking_round), ); // Handle ledger of user and currency - user,currency: total_amount_unlocked, vec![queue_id] @@ -495,7 +495,7 @@ pub mod pallet { StakingRoundRedeemQueue::::insert( &new_staking_round, ¤cy_id, - (currency_amount, ledger_list_origin, currency_id), + (currency_amount, new_queue, currency_id), ); } } @@ -510,7 +510,6 @@ pub mod pallet { } impl Pallet { - #[transactional] pub fn calculate_next_staking_round(a: StakingRound, b: StakingRound) -> Result { let result = match a { StakingRound::Era(era_a) => match b { @@ -542,7 +541,6 @@ impl Pallet { Ok(result) } - #[transactional] pub fn collect_deposit_fee( who: &T::AccountId, currency_id: FungibleTokenId, @@ -552,12 +550,16 @@ impl Pallet { let deposit_fee = deposit_rate * amount; let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(Error::::ArithmeticOverflow)?; - T::MultiCurrency::transfer(currency_id, who, &T::PoolAccount::get(), deposit_fee)?; - - return amount_exclude_fee; + T::MultiCurrency::transfer( + currency_id, + who, + &T::PoolAccount::get().into_account_truncating(), + deposit_fee, + )?; + + return Ok(amount_exclude_fee); } - #[transactional] pub fn collect_redeem_fee( who: &T::AccountId, currency_id: FungibleTokenId, @@ -566,14 +568,23 @@ impl Pallet { let (_mint_rate, redeem_rate) = Fees::::get(); let redeem_fee = redeem_rate * amount; let amount_exclude_fee = amount.checked_sub(&redeem_fee).ok_or(Error::::ArithmeticOverflow)?; - T::MultiCurrency::transfer(currency_id, who, &T::PoolAccount::get(), redeem_fee)?; - - return amount_exclude_fee; + T::MultiCurrency::transfer( + currency_id, + who, + &T::PoolAccount::get().into_account_truncating(), + redeem_fee, + )?; + + return Ok(amount_exclude_fee); } - #[transactional] pub fn collect_pool_creation_fee(who: &T::AccountId, currency_id: FungibleTokenId) -> DispatchResult { let pool_fee = T::NetworkFee::get(); - T::MultiCurrency::transfer(currency_id, who, &T::PoolAccount::get(), pool_fee) + T::MultiCurrency::transfer( + currency_id, + who, + &T::PoolAccount::get().into_account_truncating(), + pool_fee, + ) } } From daf6ce2fe3c0e71099ae361766a098faea96abf8 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 12:22:39 +1300 Subject: [PATCH 047/155] WIP - Update queue next id after user claimed --- pallets/land-minting/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pallets/land-minting/src/lib.rs b/pallets/land-minting/src/lib.rs index 5db8bfb35..84a68be55 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/land-minting/src/lib.rs @@ -502,6 +502,11 @@ pub mod pallet { None => return Err(Error::::NoCurrentStakingRound.into()), } + QueueNextId::::mutate(¤cy_id, |queue_id| -> Result<(), Error> { + *queue_id = queue_id.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; + Ok(()) + })?; + // Emit deposit event Self::deposit_event(Event::Redeemed(who, pool_id, r_amount)); Ok(().into()) From 23d4f20ee0afff4983feaee61edf8361e211523b Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 14:44:54 +1300 Subject: [PATCH 048/155] WIP - Create spp logic --- Cargo.lock | 48 ++++++++++++------------ pallets/{land-minting => spp}/Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) rename pallets/{land-minting => spp}/Cargo.toml (98%) diff --git a/Cargo.lock b/Cargo.lock index 4c1b16e5b..0a3200878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7584,30 +7584,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-land-minting" -version = "2.0.0-rc6" -dependencies = [ - "auction-manager", - "bit-country-primitives", - "core-primitives", - "frame-benchmarking", - "frame-support", - "frame-system", - "orml-tokens", - "orml-traits", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-version", - "substrate-fixed", -] - [[package]] name = "pallet-membership" version = "4.0.0-dev" @@ -8045,6 +8021,30 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-spp" +version = "2.0.0-rc6" +dependencies = [ + "auction-manager", + "bit-country-primitives", + "core-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "orml-tokens", + "orml-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "substrate-fixed", +] + [[package]] name = "pallet-staking" version = "4.0.0-dev" diff --git a/pallets/land-minting/Cargo.toml b/pallets/spp/Cargo.toml similarity index 98% rename from pallets/land-minting/Cargo.toml rename to pallets/spp/Cargo.toml index 1b68911fd..bb0c5c93e 100644 --- a/pallets/land-minting/Cargo.toml +++ b/pallets/spp/Cargo.toml @@ -4,7 +4,7 @@ description = 'Metaverse Network pallet for land minting logic.' edition = '2021' homepage = 'https://metaverse.network' license = 'Unlicense' -name = 'pallet-land-minting' +name = 'pallet-spp' repository = 'https://github.com/bit-country' version = '2.0.0-rc6' From 106e7d12f123d7343e16b7ff890266e50720edcf Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 14:45:17 +1300 Subject: [PATCH 049/155] WIP - Update folder structure on ssp logic --- pallets/{land-minting => spp}/src/mock.rs | 0 pallets/{land-minting => spp}/src/tests.rs | 0 pallets/{land-minting => spp}/src/utils.rs | 0 pallets/{land-minting => spp}/src/weights.rs | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename pallets/{land-minting => spp}/src/mock.rs (100%) rename pallets/{land-minting => spp}/src/tests.rs (100%) rename pallets/{land-minting => spp}/src/utils.rs (100%) rename pallets/{land-minting => spp}/src/weights.rs (100%) diff --git a/pallets/land-minting/src/mock.rs b/pallets/spp/src/mock.rs similarity index 100% rename from pallets/land-minting/src/mock.rs rename to pallets/spp/src/mock.rs diff --git a/pallets/land-minting/src/tests.rs b/pallets/spp/src/tests.rs similarity index 100% rename from pallets/land-minting/src/tests.rs rename to pallets/spp/src/tests.rs diff --git a/pallets/land-minting/src/utils.rs b/pallets/spp/src/utils.rs similarity index 100% rename from pallets/land-minting/src/utils.rs rename to pallets/spp/src/utils.rs diff --git a/pallets/land-minting/src/weights.rs b/pallets/spp/src/weights.rs similarity index 100% rename from pallets/land-minting/src/weights.rs rename to pallets/spp/src/weights.rs From be61add45c7c16252c95f79d5113fe4b28d8d1a3 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 14:45:44 +1300 Subject: [PATCH 050/155] WIP - Implement hooks on initialize --- pallets/{land-minting => spp}/src/lib.rs | 292 ++++++++++++++++++++++- 1 file changed, 283 insertions(+), 9 deletions(-) rename pallets/{land-minting => spp}/src/lib.rs (68%) diff --git a/pallets/land-minting/src/lib.rs b/pallets/spp/src/lib.rs similarity index 68% rename from pallets/land-minting/src/lib.rs rename to pallets/spp/src/lib.rs index 84a68be55..842237564 100644 --- a/pallets/land-minting/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -28,7 +28,7 @@ use frame_system::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed}; use orml_traits::MultiCurrency; use scale_info::TypeInfo; -use sp_runtime::traits::CheckedSub; +use sp_runtime::traits::{CheckedAdd, CheckedSub}; use sp_runtime::{ traits::{AccountIdConversion, Convert, One, Saturating, Zero}, ArithmeticError, DispatchError, Perbill, SaturatedConversion, @@ -215,23 +215,47 @@ pub mod pallet { #[pallet::getter(fn current_staking_round)] pub type CurrentStakingRound = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound>; + #[pallet::storage] + #[pallet::getter(fn last_staking_round)] + pub type LastStakingRound = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn queue_next_id)] pub type QueueNextId = StorageMap<_, Twox64Concat, FungibleTokenId, u32, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn iteration_limit)] + pub type IterationLimit = StorageValue<_, u32, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { - /// New staking round started [Starting Block, Round, Total Land Unit] - NewRound(T::BlockNumber, RoundIndex, u64), /// New pool created - PoolCreated(T::AccountId, PoolId, FungibleTokenId), + PoolCreated { + from: T::AccountId, + pool_id: PoolId, + currency_id: FungibleTokenId, + }, /// Deposited - Deposited(T::AccountId, PoolId, BalanceOf), + Deposited { + from: T::AccountId, + pool_id: PoolId, + amount: BalanceOf, + }, /// Redeemed - Redeemed(T::AccountId, PoolId, BalanceOf), + Redeemed { + from: T::AccountId, + pool_id: PoolId, + amount: BalanceOf, + }, + /// Redeemed success + RedeemSuccess { + queue_id: QueueId, + currency_id: FungibleTokenId, + to: T::AccountId, + token_amount: BalanceOf, + }, } - #[pallet::error] pub enum Error { /// No permission @@ -258,6 +282,8 @@ pub mod pallet { NotSupportTokenType, /// Unlock duration not found UnlockDurationNotFound, + /// Staking round not found + StakingRoundNotFound, } #[pallet::call] @@ -361,7 +387,7 @@ pub mod pallet { )?; // Emit deposit event - Self::deposit_event(Event::Deposited(who, pool_id, amount)); + Self::deposit_event(Event::Deposited { who, pool_id, amount }); Ok(().into()) } @@ -508,7 +534,11 @@ pub mod pallet { })?; // Emit deposit event - Self::deposit_event(Event::Redeemed(who, pool_id, r_amount)); + Self::deposit_event(Event::Redeemed { + who, + pool_id, + amount: r_amount, + }); Ok(().into()) } } @@ -592,4 +622,248 @@ impl Pallet { pool_fee, ) } + + #[transactional] + fn on_initialize() -> DispatchResult { + for currency in CurrentStakingRound::::iter_keys() { + Self::handle_redeem_staking_round_hook(currency)?; + } + Ok(()) + } + + fn handle_redeem_staking_round_hook(currency: FungibleTokenId) -> DispatchResult { + let last_staking_round = LastStakingRound::::get(currency); + let unlock_duration = match UnlockDuration::::get(currency) { + Some(StakingRound::Era(unlock_duration_era)) => unlock_duration_era, + Some(StakingRound::Round(unlock_duration_round)) => unlock_duration_round, + Some(StakingRound::Epoch(unlock_duration_epoch)) => unlock_duration_epoch, + Some(StakingRound::Hour(unlock_duration_hour)) => unlock_duration_hour, + _ => 0, + }; + + let current_staking_round = match CurrentStakingRound::::get(currency) { + Some(StakingRound::Era(unlock_duration_era)) => unlock_duration_era, + Some(StakingRound::Round(unlock_duration_round)) => unlock_duration_round, + Some(StakingRound::Epoch(unlock_duration_epoch)) => unlock_duration_epoch, + Some(StakingRound::Hour(unlock_duration_hour)) => unlock_duration_hour, + _ => 0, + }; + + // Check current staking round queue with last staking round if there is any pending redeem requests + if let Some((_total_locked, existing_queue, currency_id)) = + StakingRoundRedeemQueue::::get(last_staking_round.clone(), currency) + { + for queue_id in existing_queue.iter().take(Self::iteration_limit() as usize) { + if let Some((account, unlock_amount, staking_round)) = + CurrencyRedeemQueue::::get(currency_id, queue_id) + { + let pool_account_balance = + T::MultiCurrency::free_balance(currency_id, &T::PoolAccount::get().into_account_truncating()); + if pool_account_balance != BalanceOf::::zero() { + Self::update_queue_request( + currency_id, + account, + queue_id, + unlock_amount, + pool_account_balance, + staking_round, + ) + .ok(); + } + } + } + } else { + LastStakingRound::::mutate(currency, |last_staking_round| -> Result<(), Error> { + match last_staking_round { + StakingRound::Era(era) => { + if current_staking_round + unlock_duration > *era { + *era = era.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; + } + Ok(()) + } + StakingRound::Round(round) => { + if current_staking_round + unlock_duration > *round { + *round = round.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; + } + Ok(()) + } + StakingRound::Epoch(epoch) => { + if current_staking_round + unlock_duration > *epoch { + *kblock = kblock.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; + } + Ok(()) + } + StakingRound::Hour(hour) => { + if current_staking_round + unlock_duration > *hour { + *hour = hour.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; + } + Ok(()) + } + _ => Ok(()), + } + })?; + }; + + Ok(()) + } + + #[transactional] + fn update_queue_request( + currency_id: FungibleTokenId, + account: T::AccountId, + queue_id: &QueueId, + mut unlock_amount: BalanceOf, + pool_account_balance: BalanceOf, + staking_round: StakingRound, + ) -> DispatchResult { + // Get minimum balance of currency + let ed = T::MultiCurrency::minimum_balance(currency_id); + let mut account_to_send = account.clone(); + + // If unlock amount less than existential deposit, to avoid error kill the process, transfer the + // unlock_amount to pool address instead + if unlock_amount < ed { + let receiver_balance = T::MultiCurrency::total_balance(currency_id, &account); + + // Check if even after receiving unlock amount, account still below ED then transfer fund to + // PoolAccount + let receiver_balance_after = receiver_balance + .checked_add(&unlock_amount) + .ok_or(ArithmeticError::Overflow)?; + if receiver_balance_after < ed { + account_to_send = T::PoolAccount::get(); + } + } + + // If pool account balance greater than unlock amount + if pool_account_balance >= unlock_amount { + // Transfer amount from PoolAccount to users + T::MultiCurrency::transfer( + currency_id, + &T::PoolAccount::get().into_account_truncating(), + &account_to_send, + unlock_amount, + )?; + + // Remove currency redeem queue + CurrencyRedeemQueue::::remove(¤cy_id, &queue_id); + + // Edit staking round redeem queue with locked amount + StakingRoundRedeemQueue::::mutate_exists( + &staking_round, + ¤cy_id, + |value| -> Result<(), Error> { + if let Some((total_locked_origin, existing_queue, _)) = value { + // If total locked == unlock_amount, then set value to zero + if total_locked_origin == &unlock_amount { + *value = None; + return Ok(()); + } + // Otherwise, deduct unlock amount + *total_locked_origin = total_locked_origin + .checked_sub(&unlock_amount) + .ok_or(Error::::ArithmeticOverflow)?; + // Only keep items that not with processed queue_id + existing_queue.retain(|x| x != queue_id); + } else { + return Err(Error::::StakingRoundRedeemNotFound); + } + Ok(()) + }, + )?; + + UserCurrencyRedeemQueue::::mutate_exists(&account, ¤cy_id, |value| -> Result<(), Error> { + if let Some((total_locked_origin, existing_queue)) = value { + if total_locked_origin == &unlock_amount { + *value = None; + return Ok(()); + } + existing_queue.retain(|x| x != queue_id); + *total_locked_origin = total_locked_origin + .checked_sub(&unlock_amount) + .ok_or(Error::::ArithmeticOverflow)?; + } else { + return Err(Error::::UserCurrencyRedeemQueueNotFound); + } + Ok(()) + })?; + } else { + // When pool account balance less than amount need to be unlocked then use pool remaining balance as + // unlock amount + unlock_amount = pool_account_balance; + T::MultiCurrency::transfer( + currency_id, + &T::PoolAccount::get().into_account_truncating(), + &account_to_send, + unlock_amount, + )?; + + CurrencyRedeemQueue::::mutate_exists(¤cy_id, &queue_id, |value| -> Result<(), Error> { + if let Some((_, total_locked_origin, _, _)) = value { + if total_locked_origin == &unlock_amount { + *value = None; + return Ok(()); + } + *total_locked_origin = total_locked_origin + .checked_sub(&unlock_amount) + .ok_or(Error::::ArithmeticOverflow)?; + } else { + return Err(Error::::CurrencyRedeemQueueNotFound); + } + Ok(()) + })?; + + StakingRoundRedeemQueue::::mutate_exists( + &staking_round, + ¤cy_id, + |value| -> Result<(), Error> { + if let Some((total_locked_origin, _existing_queue, _)) = value { + if total_locked_origin == &unlock_amount { + *value = None; + return Ok(()); + } + *total_locked_origin = total_locked_origin + .checked_sub(&unlock_amount) + .ok_or(Error::::ArithmeticOverflow)?; + } else { + return Err(Error::::StakingRoundRedeemNotFound); + } + Ok(()) + }, + )?; + + UserCurrencyRedeemQueue::::mutate_exists(&account, ¤cy_id, |value| -> Result<(), Error> { + if let Some((total_locked_origin, _existing_queue)) = value { + if total_locked_origin == &unlock_amount { + *value = None; + return Ok(()); + } + + *total_locked_origin = total_locked_origin + .checked_sub(&unlock_amount) + .ok_or(Error::::ArithmeticOverflow)?; + } else { + return Err(Error::::UserUnlockLedgerNotFound); + } + Ok(()) + })?; + } + + pool_account_balance + .checked_sub(&unlock_amount) + .ok_or(Error::::ArithmeticOverflow)?; + + PoolLedger::::mutate(¤cy_id, |pool| -> Result<(), Error> { + *pool = pool.checked_sub(&unlock_amount).ok_or(Error::::ArithmeticOverflow)?; + Ok(()) + })?; + + Self::deposit_event(Event::RedeemSuccess { + queue_id: *queue_id, + currency_id, + to: account_to_send, + token_amount: unlock_amount, + }); + Ok(()) + } } From e7a0edde83b8fefbeddbb5ae66971734f1c2dffa Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 14:59:39 +1300 Subject: [PATCH 051/155] WIP - Update that fix all build error --- pallets/spp/src/lib.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 842237564..a97ca9cff 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -284,6 +284,12 @@ pub mod pallet { UnlockDurationNotFound, /// Staking round not found StakingRoundNotFound, + /// Staking round redeem queue not found + StakingRoundRedeemNotFound, + /// User currency redeem queue not found + UserCurrencyRedeemQueueNotFound, + /// Redeem queue per currency not found + CurrencyRedeemQueueNotFound, } #[pallet::call] @@ -326,7 +332,11 @@ pub mod pallet { Pool::::insert(next_pool_id, new_pool); // Emit event for pool creation - Self::deposit_event(Event::PoolCreated(who, max_nft_reward, currency_id)); + Self::deposit_event(Event::PoolCreated { + from: who, + pool_id: next_pool_id, + currency_id, + }); Ok(().into()) } @@ -387,7 +397,11 @@ pub mod pallet { )?; // Emit deposit event - Self::deposit_event(Event::Deposited { who, pool_id, amount }); + Self::deposit_event(Event::Deposited { + from: who, + pool_id, + amount, + }); Ok(().into()) } @@ -535,7 +549,7 @@ pub mod pallet { // Emit deposit event Self::deposit_event(Event::Redeemed { - who, + from: who, pool_id, amount: r_amount, }); @@ -689,7 +703,7 @@ impl Pallet { } StakingRound::Epoch(epoch) => { if current_staking_round + unlock_duration > *epoch { - *kblock = kblock.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; + *epoch = epoch.checked_add(1).ok_or(Error::::ArithmeticOverflow)?; } Ok(()) } @@ -731,7 +745,7 @@ impl Pallet { .checked_add(&unlock_amount) .ok_or(ArithmeticError::Overflow)?; if receiver_balance_after < ed { - account_to_send = T::PoolAccount::get(); + account_to_send = T::PoolAccount::get().into_account_truncating(); } } @@ -799,7 +813,7 @@ impl Pallet { )?; CurrencyRedeemQueue::::mutate_exists(¤cy_id, &queue_id, |value| -> Result<(), Error> { - if let Some((_, total_locked_origin, _, _)) = value { + if let Some((_, total_locked_origin, _)) = value { if total_locked_origin == &unlock_amount { *value = None; return Ok(()); @@ -843,7 +857,7 @@ impl Pallet { .checked_sub(&unlock_amount) .ok_or(Error::::ArithmeticOverflow)?; } else { - return Err(Error::::UserUnlockLedgerNotFound); + return Err(Error::::UserCurrencyRedeemQueueNotFound); } Ok(()) })?; @@ -853,7 +867,7 @@ impl Pallet { .checked_sub(&unlock_amount) .ok_or(Error::::ArithmeticOverflow)?; - PoolLedger::::mutate(¤cy_id, |pool| -> Result<(), Error> { + NetworkLedger::::mutate(¤cy_id, |pool| -> Result<(), Error> { *pool = pool.checked_sub(&unlock_amount).ok_or(Error::::ArithmeticOverflow)?; Ok(()) })?; From 768fc917e5551c70fae639ff262ce746906032d6 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 15:25:22 +1300 Subject: [PATCH 052/155] WIP - Update cargo fix all suggestions --- pallets/spp/src/lib.rs | 30 +++++++++++------------------- pallets/spp/src/utils.rs | 3 +-- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index a97ca9cff..88470592a 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -20,30 +20,24 @@ use frame_support::pallet_prelude::*; use frame_support::{ dispatch::DispatchResult, - ensure, log, - traits::{Currency, ExistenceRequirement, Get}, + ensure, + traits::{Currency, Get}, transactional, PalletId, }; +use frame_system::ensure_signed; use frame_system::pallet_prelude::*; -use frame_system::{ensure_root, ensure_signed}; use orml_traits::MultiCurrency; -use scale_info::TypeInfo; + use sp_runtime::traits::{CheckedAdd, CheckedSub}; use sp_runtime::{ - traits::{AccountIdConversion, Convert, One, Saturating, Zero}, - ArithmeticError, DispatchError, Perbill, SaturatedConversion, + traits::{AccountIdConversion, Convert, Saturating, Zero}, + ArithmeticError, DispatchError, SaturatedConversion, }; -use sp_std::vec::Vec; -use auction_manager::{Auction, CheckAuctionItemHandler}; use core_primitives::*; pub use pallet::*; -use primitives::estate::EstateInfo; -use primitives::{ - estate::{Estate, LandUnitStatus, LeaseContract, OwnerId}, - Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, StakingRound, TokenId, - UndeployedLandBlock, UndeployedLandBlockId, UndeployedLandBlockType, -}; + +use primitives::{ClassId, FungibleTokenId, StakingRound, TokenId}; pub use weights::WeightInfo; pub type QueueId = u32; @@ -61,15 +55,13 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; + use frame_support::traits::{Currency, ReservableCurrency}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; - use sp_runtime::traits::{CheckedAdd, CheckedSub, Zero}; + use sp_runtime::traits::{CheckedAdd, CheckedSub}; use sp_runtime::Permill; - use primitives::estate::EstateInfo; - use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; - use primitives::{AccountId, Balance, CurrencyId, PoolId, RoundIndex, StakingRound, UndeployedLandBlockId}; + use primitives::{PoolId, StakingRound}; use crate::utils::PoolInfo; diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index 32213d80e..bec2e5c66 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -19,12 +19,11 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_runtime::{Perbill, Permill, RuntimeDebug}; +use sp_runtime::{Permill, RuntimeDebug}; use primitives::FungibleTokenId; // Helper methods to compute the issuance rate for undeployed land. -use crate::pallet::{Config, Pallet}; #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] From c1191263ce547cbafe8bad537c10d9e32dc70f13 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 15:34:19 +1300 Subject: [PATCH 053/155] WIP - Fix all warning from cargo compilation --- modules/bridge/src/lib.rs | 18 +++-- modules/bridge/src/mock.rs | 84 +++++++++++------------ modules/bridge/src/tests.rs | 11 ++- pallets/auction/src/lib.rs | 28 +++----- pallets/auction/src/mock.rs | 46 ++++++------- pallets/auction/src/tests.rs | 12 ++-- pallets/continuum/src/lib.rs | 10 +-- pallets/continuum/src/mock.rs | 22 +++--- pallets/continuum/src/tests.rs | 4 +- pallets/crowdloan/src/lib.rs | 25 ++----- pallets/crowdloan/src/mock.rs | 31 ++++----- pallets/crowdloan/src/tests.rs | 2 +- pallets/economy/src/lib.rs | 28 ++++---- pallets/economy/src/mock.rs | 30 ++++---- pallets/economy/src/tests.rs | 5 +- pallets/estate/src/lib.rs | 34 +++++----- pallets/estate/src/mock.rs | 94 +++++++++++++------------- pallets/estate/src/rate.rs | 2 +- pallets/governance/src/lib.rs | 12 ++-- pallets/governance/src/mock.rs | 84 +++++++++++------------ pallets/governance/src/tests.rs | 2 +- pallets/metaverse/src/lib.rs | 11 +-- pallets/metaverse/src/mock.rs | 69 ++++++++++--------- pallets/metaverse/src/tests.rs | 3 +- pallets/mining/src/lib.rs | 25 +++---- pallets/mining/src/mining.rs | 11 +-- pallets/mining/src/mock.rs | 40 +++++------ pallets/mining/src/tests.rs | 10 ++- pallets/nft/src/lib.rs | 18 +++-- pallets/nft/src/mock.rs | 8 +-- pallets/nft/src/tests.rs | 8 +-- pallets/reward/src/lib.rs | 53 +++++++-------- pallets/reward/src/mock.rs | 13 ++-- pallets/reward/src/tests.rs | 26 +++---- runtime/common/src/currencies.rs | 20 +++--- runtime/common/src/lib.rs | 9 +-- runtime/common/src/mock.rs | 30 ++++---- runtime/common/src/nft.rs | 12 ++-- runtime/common/src/precompiles.rs | 4 +- runtime/common/src/tests/currencies.rs | 4 +- runtime/common/src/tests/nft.rs | 13 ++-- 41 files changed, 454 insertions(+), 517 deletions(-) diff --git a/modules/bridge/src/lib.rs b/modules/bridge/src/lib.rs index e8c26764e..5390eda15 100644 --- a/modules/bridge/src/lib.rs +++ b/modules/bridge/src/lib.rs @@ -11,7 +11,7 @@ use sp_core::{H160, U256}; use sp_std::prelude::*; pub use pallet::*; -use primitives::{Balance, FungibleTokenId}; +use primitives::FungibleTokenId; pub type ResourceId = H160; pub type ChainId = u8; @@ -29,12 +29,12 @@ pub mod pallet { use frame_support::traits::{Currency, ExistenceRequirement, LockableCurrency, ReservableCurrency}; use frame_support::PalletId; use orml_traits::MultiCurrency; - use sp_arithmetic::traits::{CheckedMul, Saturating, Zero}; - use sp_runtime::traits::{AccountIdConversion, CheckedDiv}; - use sp_runtime::{ArithmeticError, ModuleError}; + use sp_arithmetic::traits::{Saturating, Zero}; + use sp_runtime::traits::AccountIdConversion; + use sp_runtime::ModuleError; use core_primitives::NFTTrait; - use primitives::evm::CurrencyIdType::FungibleToken; + use primitives::{Attributes, ClassId, NftMetadata, TokenId}; use super::*; @@ -300,7 +300,11 @@ pub mod pallet { Ok(()) } Err(err) => match err { - DispatchError::Module(ModuleError { index, error, message }) => { + DispatchError::Module(ModuleError { + index: _, + error: _, + message, + }) => { if message == Some("AssetInfoNotFound") { if let Ok(_mint_succeeded) = T::NFTHandler::mint_token_with_id(&to, class_id, token_id, metadata, Attributes::new()) @@ -335,7 +339,7 @@ pub mod pallet { let bridge_id = T::PalletId::get().into_account_truncating(); ensure!(BridgeFee::::contains_key(&chain_id), Error::::FeeOptionsMissing); - let (min_fee, fee_scale) = Self::bridge_fee(chain_id); + let (min_fee, _fee_scale) = Self::bridge_fee(chain_id); T::Currency::transfer(&source, &bridge_id, min_fee.into(), ExistenceRequirement::AllowDeath)?; diff --git a/modules/bridge/src/mock.rs b/modules/bridge/src/mock.rs index 1fbfa7bd9..32894d6bf 100644 --- a/modules/bridge/src/mock.rs +++ b/modules/bridge/src/mock.rs @@ -3,22 +3,18 @@ use std::collections::BTreeMap; use std::vec; -use frame_support::traits::{EqualPrivilegeOnly, Nothing}; +use frame_support::traits::Nothing; use frame_support::{construct_runtime, ord_parameter_types, pallet_prelude::Hooks, parameter_types, PalletId}; -use frame_system::{EnsureRoot, EnsureSignedBy}; +use frame_system::EnsureRoot; use orml_traits::parameter_type_with_key; use sp_core::H256; use sp_runtime::traits::AccountIdConversion; use sp_runtime::{testing::Header, traits::IdentityLookup, ModuleError, Perbill}; -use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; -use core_primitives::{ - Attributes, CollectionType, MetaverseInfo, MetaverseMetadata, MetaverseTrait, NFTTrait, NftAssetData, NftClassData, - NftMetadata, TokenType, -}; +use core_primitives::{Attributes, CollectionType, NFTTrait, NftClassData, NftMetadata, TokenType}; use primitives::{ - continuum::MapTrait, estate::Estate, Amount, AuctionId, ClassId, EstateId, FungibleTokenId, GroupCollectionId, - ItemId, MapSpotId, NftOffer, TokenId, UndeployedLandBlockId, + continuum::MapTrait, Amount, ClassId, EstateId, FungibleTokenId, GroupCollectionId, MapSpotId, TokenId, + UndeployedLandBlockId, }; use crate as bridge; @@ -283,7 +279,7 @@ impl NFTTrait for MockNFTHandler { { return Ok(true); } - if (nft_value.1 == 5) { + if nft_value.1 == 5 { return Err(DispatchError::Module(ModuleError { index: 5, error: [0, 0, 0, 0], @@ -293,7 +289,7 @@ impl NFTTrait for MockNFTHandler { Ok(false) } - fn is_stackable(asset_id: (Self::ClassId, Self::TokenId)) -> Result { + fn is_stackable(_asset_id: (Self::ClassId, Self::TokenId)) -> Result { Ok(false) } @@ -306,19 +302,19 @@ impl NFTTrait for MockNFTHandler { } Ok(false) } - fn get_nft_group_collection(nft_collection: &Self::ClassId) -> Result { + fn get_nft_group_collection(_nft_collection: &Self::ClassId) -> Result { Ok(ASSET_COLLECTION_ID) } fn create_token_class( sender: &AccountId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, collection_id: GroupCollectionId, - token_type: TokenType, - collection_type: CollectionType, - royalty_fee: Perbill, - mint_limit: Option, + _token_type: TokenType, + _collection_type: CollectionType, + _royalty_fee: Perbill, + _mint_limit: Option, ) -> Result { match *sender { ALICE => { @@ -339,8 +335,8 @@ impl NFTTrait for MockNFTHandler { fn mint_token( sender: &AccountId, class_id: ClassId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -364,26 +360,26 @@ impl NFTTrait for MockNFTHandler { } } - fn transfer_nft(from: &AccountId, to: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn transfer_nft(_from: &AccountId, _to: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn check_item_on_listing(class_id: Self::ClassId, token_id: Self::TokenId) -> Result { + fn check_item_on_listing(_class_id: Self::ClassId, _token_id: Self::TokenId) -> Result { Ok(true) } - fn burn_nft(account: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn burn_nft(_account: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn is_transferable(nft: &(Self::ClassId, Self::TokenId)) -> Result { + fn is_transferable(_nft: &(Self::ClassId, Self::TokenId)) -> Result { Ok(true) } - fn get_class_fund(class_id: &Self::ClassId) -> AccountId { + fn get_class_fund(_class_id: &Self::ClassId) -> AccountId { CLASS_FUND_ID } - fn get_nft_detail(asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { + fn get_nft_detail(_asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { let new_data = NftClassData { deposit: 0, attributes: test_attributes(1), @@ -397,11 +393,11 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn set_lock_collection(class_id: Self::ClassId, is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_collection(_class_id: Self::ClassId, _is_locked: bool) -> sp_runtime::DispatchResult { todo!() } - fn set_lock_nft(token_id: (Self::ClassId, Self::TokenId), is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_nft(_token_id: (Self::ClassId, Self::TokenId), _is_locked: bool) -> sp_runtime::DispatchResult { todo!() } @@ -419,20 +415,20 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn get_total_issuance(class_id: Self::ClassId) -> Result { + fn get_total_issuance(_class_id: Self::ClassId) -> Result { Ok(10u64) } - fn get_asset_owner(asset_id: &(Self::ClassId, Self::TokenId)) -> Result { + fn get_asset_owner(_asset_id: &(Self::ClassId, Self::TokenId)) -> Result { Ok(ALICE) } fn mint_token_with_id( sender: &AccountId, class_id: Self::ClassId, - token_id: Self::TokenId, - metadata: NftMetadata, - attributes: Attributes, + _token_id: Self::TokenId, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -456,31 +452,31 @@ impl NFTTrait for MockNFTHandler { } } - fn get_free_stackable_nft_balance(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { + fn get_free_stackable_nft_balance(_who: &AccountId, _asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { 1000 } fn reserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> DispatchResult { Ok(()) } fn unreserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } fn transfer_stackable_nft( - sender: &AccountId, - to: &AccountId, - nft: &(Self::ClassId, Self::TokenId), - amount: Balance, + _sender: &AccountId, + _to: &AccountId, + _nft: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } diff --git a/modules/bridge/src/tests.rs b/modules/bridge/src/tests.rs index c3e5f13a4..0308dcb72 100644 --- a/modules/bridge/src/tests.rs +++ b/modules/bridge/src/tests.rs @@ -1,16 +1,13 @@ #![cfg(test)] -use std::str::{from_utf8, FromStr}; +use std::str::FromStr; -use frame_support::{assert_noop, assert_ok}; +use frame_support::assert_ok; use sp_core::H160; -use mock::{Balances, BridgeModule, ExtBuilder, One, Runtime, RuntimeEvent, RuntimeOrigin, System, Tokens}; -use primitives::evm::{CurrencyIdType, EvmAddress, H160_POSITION_CURRENCY_ID_TYPE, H160_POSITION_TOKEN}; -use primitives::FungibleTokenId::FungibleToken; -use primitives::{TokenId, TokenSymbol}; +use mock::{Balances, BridgeModule, ExtBuilder, RuntimeOrigin}; -use crate::mock::{BridgeSovereignPalletId, ALICE, BOB}; +use crate::mock::{ALICE, BOB}; use super::*; diff --git a/pallets/auction/src/lib.rs b/pallets/auction/src/lib.rs index 8afd48829..67403e434 100644 --- a/pallets/auction/src/lib.rs +++ b/pallets/auction/src/lib.rs @@ -31,7 +31,7 @@ use frame_system::{self as system, ensure_signed}; use sp_core::sp_std::convert::TryInto; use sp_runtime::SaturatedConversion; use sp_runtime::{ - traits::{CheckedDiv, One, Saturating, Zero}, + traits::{One, Saturating, Zero}, DispatchError, DispatchResult, Perbill, }; use sp_std::vec::Vec; @@ -58,17 +58,13 @@ pub mod weights; pub struct AuctionLogicHandler; pub mod migration_v2 { - use codec::FullCodec; + use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; - use sp_runtime::{traits::AtLeast32BitUnsigned, DispatchError, RuntimeDebug}; - use sp_std::{ - cmp::{Eq, PartialEq}, - fmt::Debug, - vec::Vec, - }; + use sp_runtime::RuntimeDebug; + use sp_std::cmp::{Eq, PartialEq}; use auction_manager::{AuctionType, ListingLevel}; use primitives::{AssetId, EstateId, FungibleTokenId, MetaverseId}; @@ -104,20 +100,18 @@ pub mod migration_v2 { #[frame_support::pallet] pub mod pallet { + use frame_support::dispatch::DispatchResultWithPostInfo; use frame_support::log; use frame_support::sp_runtime::traits::CheckedSub; - use frame_support::{dispatch::DispatchResultWithPostInfo, traits::tokens::currency}; - use frame_system::ensure_root; + use frame_system::pallet_prelude::OriginFor; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_runtime::traits::CheckedAdd; use sp_runtime::ArithmeticError; - use auction_manager::{AuctionItemV1, CheckAuctionItemHandler, ListingLevel}; + use auction_manager::{CheckAuctionItemHandler, ListingLevel}; use core_primitives::{MetaverseTrait, NFTTrait}; - use primitives::{AssetId, Balance, ClassId, FungibleTokenId, MetaverseId, TokenId}; - - use crate::migration_v2::V1ItemId; + use primitives::{Balance, ClassId, FungibleTokenId, MetaverseId, TokenId}; use super::*; @@ -1191,7 +1185,7 @@ pub mod pallet { /// Internal auction bid handler fn auction_bid_handler(from: T::AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { - let mut auction_item: AuctionItem> = + let auction_item: AuctionItem> = Self::get_auction_item(id.clone()).ok_or(Error::::AuctionDoesNotExist)?; ensure!( auction_item.auction_type == AuctionType::Auction, @@ -1426,7 +1420,7 @@ pub mod pallet { ::Currency::unreserve(&auction_item.recipient, T::NetworkFeeReserve::get()); // Transfer balance from buy it now user to asset owner - let mut currency_transfer; + let currency_transfer; if auction_item.currency_id == FungibleTokenId::NativeToken(0) { currency_transfer = ::Currency::transfer( &from, @@ -1618,7 +1612,7 @@ pub mod pallet { } // Handle balance transfer - let mut currency_transfer; + let currency_transfer; if auction_item.currency_id == FungibleTokenId::NativeToken(0) { currency_transfer = ::Currency::transfer( &high_bidder, diff --git a/pallets/auction/src/mock.rs b/pallets/auction/src/mock.rs index be22e2920..0352d86ce 100644 --- a/pallets/auction/src/mock.rs +++ b/pallets/auction/src/mock.rs @@ -12,7 +12,7 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbil use auction_manager::{CheckAuctionItemHandler, ListingLevel}; use core_primitives::{MetaverseInfo, MetaverseMetadata, MetaverseTrait, NftAssetData, NftClassData}; use primitives::{ - continuum::MapTrait, estate::Estate, Amount, AuctionId, ClassId, EstateId, FungibleTokenId, MapSpotId, NftOffer, + continuum::MapTrait, estate::Estate, Amount, AuctionId, ClassId, EstateId, FungibleTokenId, MapSpotId, UndeployedLandBlockId, }; @@ -117,9 +117,9 @@ impl Estate for EstateHandler { } fn transfer_undeployed_land_block( - who: &AccountId, - to: &AccountId, - undeployed_land_block_id: UndeployedLandBlockId, + _who: &AccountId, + _to: &AccountId, + _undeployed_land_block_id: UndeployedLandBlockId, ) -> Result { Ok(2) } @@ -141,7 +141,7 @@ impl Estate for EstateHandler { } fn check_undeployed_land_block( - owner: &AccountId, + _owner: &AccountId, undeployed_land_block_id: UndeployedLandBlockId, ) -> Result { match undeployed_land_block_id { @@ -151,7 +151,7 @@ impl Estate for EstateHandler { } } - fn get_total_land_units(estate_id: Option) -> u64 { + fn get_total_land_units(_estate_id: Option) -> u64 { 100 } @@ -159,15 +159,15 @@ impl Estate for EstateHandler { 100 } - fn check_estate_ownership(owner: AccountId, estate_id: EstateId) -> Result { + fn check_estate_ownership(_owner: AccountId, _estate_id: EstateId) -> Result { Ok(false) } - fn is_estate_leasor(leasor: AccountId, estate_id: EstateId) -> Result { + fn is_estate_leasor(_leasor: AccountId, _estate_id: EstateId) -> Result { Ok(false) } - fn is_estate_leased(estate_id: EstateId) -> Result { + fn is_estate_leased(_estate_id: EstateId) -> Result { Ok(false) } } @@ -241,7 +241,7 @@ parameter_types! { pub struct MetaverseInfoSource {} impl MetaverseTrait for MetaverseInfoSource { - fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId { + fn create_metaverse(_who: &AccountId, _metadata: MetaverseMetadata) -> MetaverseId { 1u64 } @@ -267,19 +267,19 @@ impl MetaverseTrait for MetaverseInfoSource { Ok(()) } - fn get_metaverse_land_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_land_class(_metaverse_id: MetaverseId) -> Result { Ok(15u32) } - fn get_metaverse_estate_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_estate_class(_metaverse_id: MetaverseId) -> Result { Ok(16u32) } - fn get_metaverse_marketplace_listing_fee(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_marketplace_listing_fee(_metaverse_id: MetaverseId) -> Result { Ok(Perbill::from_percent(1u32)) } - fn get_metaverse_treasury(metaverse_id: MetaverseId) -> AccountId { + fn get_metaverse_treasury(_metaverse_id: MetaverseId) -> AccountId { [102; 32].into() } @@ -288,7 +288,7 @@ impl MetaverseTrait for MetaverseInfoSource { } fn check_if_metaverse_estate( - metaverse_id: primitives::MetaverseId, + _metaverse_id: primitives::MetaverseId, class_id: &ClassId, ) -> Result { if class_id == &15u32 || class_id == &16u32 { @@ -490,7 +490,7 @@ impl Auction for MockAuctionManager { None } - fn auction_item(id: AuctionId) -> Option> { + fn auction_item(_id: AuctionId) -> Option> { None } @@ -498,7 +498,7 @@ impl Auction for MockAuctionManager { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> frame_support::dispatch::DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> frame_support::dispatch::DispatchResult { Ok(()) } @@ -528,17 +528,17 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} fn auction_bid_handler( - from: AccountId, - id: AuctionId, - value: Self::Balance, + _from: AccountId, + _id: AuctionId, + _value: Self::Balance, ) -> frame_support::dispatch::DispatchResult { Ok(()) } fn buy_now_handler( - from: AccountId, - auction_id: AuctionId, - value: Self::Balance, + _from: AccountId, + _auction_id: AuctionId, + _value: Self::Balance, ) -> frame_support::dispatch::DispatchResult { Ok(()) } diff --git a/pallets/auction/src/tests.rs b/pallets/auction/src/tests.rs index d40984cdd..9a0616e6d 100644 --- a/pallets/auction/src/tests.rs +++ b/pallets/auction/src/tests.rs @@ -7,8 +7,8 @@ use sp_std::collections::btree_map::BTreeMap; use auction_manager::ListingLevel; use core_primitives::{Attributes, CollectionType, NFTTrait, TokenType}; use mock::{RuntimeEvent, *}; -use primitives::ItemId::NFT; -use primitives::{ClassId, FungibleTokenId}; + +use primitives::FungibleTokenId; use super::*; @@ -1691,7 +1691,7 @@ fn auction_bundle_should_update_new_price_according_new_bid() { assert_eq!(Balances::free_balance(ALICE), 99991); let tokens_after_bid = vec![(0, 0, 150), (0, 1, 150)]; - let item_updated_after_bid = AuctionModule::items_in_auction(ItemId::Bundle(tokens.clone())); + let _item_updated_after_bid = AuctionModule::items_in_auction(ItemId::Bundle(tokens.clone())); let auction_item = AuctionModule::get_auction_item(0).unwrap(); assert_eq!(auction_item.item_id, ItemId::Bundle(tokens_after_bid)); @@ -2112,7 +2112,7 @@ fn withdraw_offer_should_work() { fn finalize_auction_should_fail() { ExtBuilder::default().build().execute_with(|| { let owner = RuntimeOrigin::signed(BOB); - let bidder = RuntimeOrigin::signed(ALICE); + let _bidder = RuntimeOrigin::signed(ALICE); init_test_nft(owner.clone()); init_test_nft(owner.clone()); assert_ok!(AuctionModule::create_auction( @@ -2152,7 +2152,7 @@ fn finalize_auction_should_fail() { fn cancel_listing_should_work() { ExtBuilder::default().build().execute_with(|| { let owner = RuntimeOrigin::signed(BOB); - let bidder = RuntimeOrigin::signed(ALICE); + let _bidder = RuntimeOrigin::signed(ALICE); init_test_nft(owner.clone()); assert_ok!(AuctionModule::create_auction( AuctionType::Auction, @@ -2192,7 +2192,7 @@ fn cancel_listing_should_work() { fn cancel_listing_should_fail() { ExtBuilder::default().build().execute_with(|| { let owner = RuntimeOrigin::signed(BOB); - let bidder = RuntimeOrigin::signed(ALICE); + let _bidder = RuntimeOrigin::signed(ALICE); init_test_nft(owner.clone()); init_test_nft(owner.clone()); assert_ok!(AuctionModule::create_auction( diff --git a/pallets/continuum/src/lib.rs b/pallets/continuum/src/lib.rs index e73c0aca7..52649c14f 100644 --- a/pallets/continuum/src/lib.rs +++ b/pallets/continuum/src/lib.rs @@ -47,18 +47,15 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::DispatchResult, - ensure, log, + ensure, traits::ExistenceRequirement, traits::{Currency, Get, LockableCurrency, ReservableCurrency}, transactional, PalletId, }; use frame_system::{ensure_root, ensure_signed}; use scale_info::TypeInfo; -use sp_runtime::traits::CheckedAdd; -use sp_runtime::{ - traits::{AccountIdConversion, One, Zero}, - DispatchError, Perbill, RuntimeDebug, -}; + +use sp_runtime::{traits::AccountIdConversion, DispatchError, Perbill, RuntimeDebug}; use sp_std::vec; use sp_std::vec::Vec; @@ -112,7 +109,6 @@ pub struct AuctionSlot { pub mod pallet { use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*}; use frame_system::pallet_prelude::OriginFor; - use sp_arithmetic::traits::UniqueSaturatedInto; use core_primitives::TokenType; use primitives::{AuctionId, MapSpotId}; diff --git a/pallets/continuum/src/mock.rs b/pallets/continuum/src/mock.rs index a49353f1f..a57a3abf0 100644 --- a/pallets/continuum/src/mock.rs +++ b/pallets/continuum/src/mock.rs @@ -25,7 +25,7 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; use auction_manager::{Auction, AuctionInfo, AuctionItem, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{MetaverseInfo, MetaverseMetadata, MetaverseTrait}; -use primitives::FungibleTokenId::FungibleToken; + use primitives::{AuctionId, ClassId, FungibleTokenId}; use crate as continuum; @@ -147,11 +147,11 @@ impl Auction for MockAuctionManager { return None; } - fn update_auction(id: AuctionId, _info: AuctionInfo) -> DispatchResult { + fn update_auction(_id: AuctionId, _info: AuctionInfo) -> DispatchResult { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> DispatchResult { Ok(()) } @@ -180,11 +180,11 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + fn auction_bid_handler(_from: AccountId, _id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + fn buy_now_handler(_from: AccountId, _auction_id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } @@ -228,7 +228,7 @@ parameter_types! { pub struct MetaverseInfoSource {} impl MetaverseTrait for MetaverseInfoSource { - fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId { + fn create_metaverse(_who: &AccountId, _metadata: MetaverseMetadata) -> MetaverseId { 1u64 } @@ -253,15 +253,15 @@ impl MetaverseTrait for MetaverseInfoSource { Ok(()) } - fn get_metaverse_land_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_land_class(_metaverse_id: MetaverseId) -> Result { Ok(15u32) } - fn get_metaverse_estate_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_estate_class(_metaverse_id: MetaverseId) -> Result { Ok(16u32) } - fn get_metaverse_marketplace_listing_fee(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_marketplace_listing_fee(_metaverse_id: MetaverseId) -> Result { Ok(Perbill::from_percent(1u32)) } @@ -278,7 +278,7 @@ impl MetaverseTrait for MetaverseInfoSource { } fn check_if_metaverse_estate( - metaverse_id: primitives::MetaverseId, + _metaverse_id: primitives::MetaverseId, class_id: &ClassId, ) -> Result { if class_id == &15u32 || class_id == &16u32 { @@ -295,7 +295,7 @@ impl MetaverseTrait for MetaverseInfoSource { } } - fn is_metaverse_owner(who: &AccountId) -> bool { + fn is_metaverse_owner(_who: &AccountId) -> bool { true } } diff --git a/pallets/continuum/src/tests.rs b/pallets/continuum/src/tests.rs index db3b7c10c..048f61345 100644 --- a/pallets/continuum/src/tests.rs +++ b/pallets/continuum/src/tests.rs @@ -21,8 +21,8 @@ use frame_support::{assert_noop, assert_ok}; use sp_runtime::traits::BadOrigin; use core_primitives::TokenType; -use mock::BlockNumber as MBlockNumber; -use mock::{RuntimeEvent, *}; + +use mock::*; use super::*; diff --git a/pallets/crowdloan/src/lib.rs b/pallets/crowdloan/src/lib.rs index ebe710e1e..ed189b346 100644 --- a/pallets/crowdloan/src/lib.rs +++ b/pallets/crowdloan/src/lib.rs @@ -18,26 +18,17 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; -use frame_support::traits::{Currency, ExistenceRequirement, VestingSchedule}; -use frame_support::{dispatch::DispatchResult, ensure, traits::Get, PalletId}; +use frame_support::traits::{Currency, VestingSchedule}; +use frame_support::{dispatch::DispatchResult, ensure, traits::Get}; use frame_system::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed}; use pallet_vesting::{Pallet as VestingModule, VestingInfo}; -use scale_info::TypeInfo; -use sp_runtime::traits::Convert; -use sp_runtime::{ - traits::{AccountIdConversion, One, Saturating, Zero}, - DispatchError, -}; + +use sp_runtime::traits::Saturating; use sp_std::{convert::TryInto, vec::Vec}; -use auction_manager::{Auction, CheckAuctionItemHandler}; -use core_primitives::*; pub use pallet::*; -use primitives::{ - estate::Estate, Balance, EstateId, ItemId, MetaverseId, UndeployedLandBlock, UndeployedLandBlockId, - UndeployedLandBlockType, -}; + pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -53,11 +44,9 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use frame_support::traits::{Currency, ExistenceRequirement, Imbalance, ReservableCurrency, VestingSchedule}; + use frame_support::traits::{Currency, ExistenceRequirement, VestingSchedule}; use pallet_vesting::VestingInfo; - use sp_runtime::traits::{CheckedAdd, CheckedSub, Convert, ConvertInto, StaticLookup, Zero}; - - use primitives::UndeployedLandBlockId; + use sp_runtime::traits::{Convert, StaticLookup}; use super::*; diff --git a/pallets/crowdloan/src/mock.rs b/pallets/crowdloan/src/mock.rs index d13741bbb..d7366fa24 100644 --- a/pallets/crowdloan/src/mock.rs +++ b/pallets/crowdloan/src/mock.rs @@ -1,14 +1,11 @@ #![cfg(test)] use frame_support::traits::WithdrawReasons; -use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; -use frame_system::EnsureSignedBy; -use sp_core::H256; -use sp_runtime::traits::{ConvertInto, Identity}; -use sp_runtime::{testing::Header, traits::IdentityLookup, DispatchError, Perbill}; +use frame_support::{construct_runtime, ord_parameter_types, parameter_types}; -use auction_manager::{Auction, AuctionInfo, AuctionType, CheckAuctionItemHandler, ListingLevel}; -use primitives::FungibleTokenId; +use sp_core::H256; +use sp_runtime::traits::ConvertInto; +use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; use crate as crowdloan; @@ -124,29 +121,29 @@ impl VestingSchedule for VestingScheduleTrait { type Moment = (); type Currency = Balances; - fn vesting_balance(who: &AccountId) -> Option { + fn vesting_balance(_who: &AccountId) -> Option { None } fn add_vesting_schedule( - who: &AccountId, - locked: Balance, - per_block: Balance, - starting_block: Self::Moment, + _who: &AccountId, + _locked: Balance, + _per_block: Balance, + _starting_block: Self::Moment, ) -> DispatchResult { Ok(()) } fn can_add_vesting_schedule( - who: &AccountId, - locked: Balance, - per_block: Balance, - starting_block: Self::Moment, + _who: &AccountId, + _locked: Balance, + _per_block: Balance, + _starting_block: Self::Moment, ) -> DispatchResult { Ok(()) } - fn remove_vesting_schedule(who: &AccountId, schedule_index: u32) -> DispatchResult { + fn remove_vesting_schedule(_who: &AccountId, _schedule_index: u32) -> DispatchResult { Ok(()) } } diff --git a/pallets/crowdloan/src/tests.rs b/pallets/crowdloan/src/tests.rs index e64629b57..79d295b2d 100644 --- a/pallets/crowdloan/src/tests.rs +++ b/pallets/crowdloan/src/tests.rs @@ -17,7 +17,7 @@ #![cfg(test)] -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::{assert_noop, assert_ok}; use sp_runtime::traits::BadOrigin; use mock::{RuntimeEvent, *}; diff --git a/pallets/economy/src/lib.rs b/pallets/economy/src/lib.rs index be695632a..ea06898a0 100644 --- a/pallets/economy/src/lib.rs +++ b/pallets/economy/src/lib.rs @@ -17,16 +17,16 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode, HasCompact}; -use frame_support::traits::{LockIdentifier, WithdrawReasons}; +use codec::Encode; + use frame_support::{ - ensure, log, + ensure, pallet_prelude::*, - traits::{Currency, ExistenceRequirement, LockableCurrency, ReservableCurrency}, + traits::{Currency, LockableCurrency, ReservableCurrency}, transactional, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::*}; -use orml_traits::{DataFeeder, DataProvider, MultiCurrency, MultiReservableCurrency}; +use orml_traits::{DataProvider, MultiCurrency, MultiReservableCurrency}; use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, Saturating}; use sp_runtime::{ traits::{AccountIdConversion, One, Zero}, @@ -38,7 +38,7 @@ use core_primitives::NFTTrait; use core_primitives::*; pub use pallet::*; use primitives::{estate::Estate, EstateId}; -use primitives::{AssetId, Balance, ClassId, DomainId, FungibleTokenId, MetaverseId, NftId, PowerAmount, RoundIndex}; +use primitives::{Balance, ClassId, DomainId, FungibleTokenId, PowerAmount, RoundIndex}; pub use weights::WeightInfo; //#[cfg(feature = "runtime-benchmarks")] @@ -54,12 +54,12 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use orml_traits::MultiCurrencyExtended; + use sp_runtime::traits::{CheckedAdd, CheckedSub, Saturating}; use sp_runtime::ArithmeticError; - use primitives::staking::{Bond, RoundInfo}; - use primitives::{ClassId, GroupCollectionId, NftId}; + use primitives::staking::Bond; + use primitives::{ClassId, NftId}; use super::*; @@ -338,7 +338,7 @@ pub mod pallet { Error::::ExitQueueAlreadyScheduled ); - let mut staked_balance = StakingInfo::::get(&who); + let staked_balance = StakingInfo::::get(&who); let total = staked_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; ensure!(total >= T::MinimumStake::get(), Error::::StakeBelowMinimum); @@ -440,7 +440,7 @@ pub mod pallet { match estate { None => { - let mut staked_balance = StakingInfo::::get(&who); + let staked_balance = StakingInfo::::get(&who); ensure!(amount <= staked_balance, Error::::UnstakeAmountExceedStakedAmount); let remaining = staked_balance.checked_sub(&amount).ok_or(ArithmeticError::Underflow)?; @@ -672,7 +672,7 @@ pub mod pallet { match estate { None => { - let mut staked_balance = StakingInfo::::get(&who); + let staked_balance = StakingInfo::::get(&who); ensure!(amount <= staked_balance, Error::::UnstakeAmountExceedStakedAmount); let remaining = staked_balance.checked_sub(&amount).ok_or(ArithmeticError::Underflow)?; @@ -771,7 +771,7 @@ pub mod pallet { ensure!(!amount.is_zero(), Error::::UnstakeAmountIsZero); // Update staking info - let mut staked_reserved_balance = T::Currency::reserved_balance(&who); + let staked_reserved_balance = T::Currency::reserved_balance(&who); ensure!( amount <= staked_reserved_balance, Error::::UnstakeAmountExceedStakedAmount @@ -795,7 +795,7 @@ impl Pallet { pub fn convert_power_to_bit(power_amount: Balance, commission: Perbill) -> (Balance, Balance) { let rate = Self::get_bit_power_exchange_rate(); - let mut bit_required = power_amount + let bit_required = power_amount .checked_mul(rate) .ok_or(ArithmeticError::Overflow) .unwrap_or(Zero::zero()); diff --git a/pallets/economy/src/mock.rs b/pallets/economy/src/mock.rs index caccaa029..6969f2bf4 100644 --- a/pallets/economy/src/mock.rs +++ b/pallets/economy/src/mock.rs @@ -3,7 +3,7 @@ use frame_support::traits::Nothing; use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; use frame_system::EnsureSignedBy; -use orml_traits::currency::MutationHooks; + use orml_traits::parameter_type_with_key; use sp_core::crypto::AccountId32; use sp_core::H256; @@ -139,9 +139,9 @@ impl Estate for EstateHandler { } fn transfer_undeployed_land_block( - who: &AccountId, - to: &AccountId, - undeployed_land_block_id: UndeployedLandBlockId, + _who: &AccountId, + _to: &AccountId, + _undeployed_land_block_id: UndeployedLandBlockId, ) -> Result { Ok(2) } @@ -160,18 +160,18 @@ impl Estate for EstateHandler { Ok(false) } - fn check_landunit(_metaverse_id: primitives::MetaverseId, coordinate: (i32, i32)) -> Result { + fn check_landunit(_metaverse_id: primitives::MetaverseId, _coordinate: (i32, i32)) -> Result { Ok(true) } fn check_undeployed_land_block( - owner: &AccountId, - undeployed_land_block_id: UndeployedLandBlockId, + _owner: &AccountId, + _undeployed_land_block_id: UndeployedLandBlockId, ) -> Result { Ok(true) } - fn get_total_land_units(estate_id: Option) -> u64 { + fn get_total_land_units(_estate_id: Option) -> u64 { 10 } @@ -179,11 +179,11 @@ impl Estate for EstateHandler { 10 } - fn is_estate_leasor(leasor: AccountId, estate_id: EstateId) -> Result { + fn is_estate_leasor(_leasor: AccountId, _estate_id: EstateId) -> Result { Ok(false) } - fn is_estate_leased(estate_id: EstateId) -> Result { + fn is_estate_leased(_estate_id: EstateId) -> Result { Ok(false) } } @@ -191,7 +191,7 @@ impl Estate for EstateHandler { pub struct MetaverseStakingHandler; impl MetaverseStakingTrait for MetaverseStakingHandler { - fn update_staking_reward(round: RoundIndex, total_reward: u128) -> sp_runtime::DispatchResult { + fn update_staking_reward(_round: RoundIndex, _total_reward: u128) -> sp_runtime::DispatchResult { Ok(()) } } @@ -284,7 +284,7 @@ impl Auction for MockAuctionManager { None } - fn auction_item(id: AuctionId) -> Option> { + fn auction_item(_id: AuctionId) -> Option> { None } @@ -292,7 +292,7 @@ impl Auction for MockAuctionManager { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> DispatchResult { Ok(()) } @@ -321,11 +321,11 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + fn auction_bid_handler(_from: AccountId, _id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + fn buy_now_handler(_from: AccountId, _auction_id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } diff --git a/pallets/economy/src/tests.rs b/pallets/economy/src/tests.rs index 957d005d9..64e848a46 100644 --- a/pallets/economy/src/tests.rs +++ b/pallets/economy/src/tests.rs @@ -17,12 +17,11 @@ #![cfg(test)] -use frame_support::{assert_err, assert_noop, assert_ok}; -use orml_nft::Tokens; +use frame_support::{assert_noop, assert_ok}; + use sp_runtime::traits::BadOrigin; use sp_std::default::Default; -use auction_manager::ListingLevel; use core_primitives::{Attributes, CollectionType, TokenType}; use mock::{RuntimeEvent, *}; use primitives::staking::Bond; diff --git a/pallets/estate/src/lib.rs b/pallets/estate/src/lib.rs index 3c124e779..b7a31ca30 100644 --- a/pallets/estate/src/lib.rs +++ b/pallets/estate/src/lib.rs @@ -26,11 +26,10 @@ use frame_support::{ }; use frame_system::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed}; -use scale_info::TypeInfo; use sp_runtime::{ - traits::{AccountIdConversion, Convert, One, Saturating, Zero}, - ArithmeticError, DispatchError, Perbill, SaturatedConversion, + traits::{AccountIdConversion, Convert, One, Saturating}, + DispatchError, Perbill, SaturatedConversion, }; use sp_std::vec::Vec; @@ -40,7 +39,7 @@ pub use pallet::*; use primitives::estate::EstateInfo; use primitives::{ estate::{Estate, LandUnitStatus, LeaseContract, OwnerId}, - Attributes, ClassId, EstateId, FungibleTokenId, ItemId, MetaverseId, NftMetadata, TokenId, UndeployedLandBlock, + Attributes, ClassId, EstateId, ItemId, MetaverseId, NftMetadata, TokenId, UndeployedLandBlock, UndeployedLandBlockId, UndeployedLandBlockType, }; pub use rate::{MintingRateInfo, Range}; @@ -61,11 +60,11 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { use frame_support::traits::{Currency, Imbalance, ReservableCurrency}; - use sp_runtime::traits::{CheckedAdd, CheckedSub, Zero}; + use sp_runtime::traits::{CheckedSub, Zero}; use primitives::estate::EstateInfo; - use primitives::staking::{Bond, RoundInfo, StakeSnapshot}; - use primitives::{Balance, RoundIndex, UndeployedLandBlockId}; + use primitives::staking::RoundInfo; + use primitives::{RoundIndex, UndeployedLandBlockId}; use crate::rate::{round_issuance_range, MintingRateInfo}; @@ -1047,7 +1046,7 @@ pub mod pallet { T::NFTTokenizationSource::burn_nft(&who, &(class_id, token_id)); *estate_owner = None; } - OwnerId::Account(ref a) => { + OwnerId::Account(ref _a) => { *estate_owner = None; } } @@ -1063,7 +1062,7 @@ pub mod pallet { AllEstatesCount::::put(new_total_estates_count); // Mint new land tokens to replace the lands in the dissolved estate - let estate_account_id: T::AccountId = + let _estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(estate_id); let storage_fee: BalanceOf = Perbill::from_percent(100u32.saturating_mul(estate_info.land_units.len() as u32)) @@ -1207,11 +1206,12 @@ pub mod pallet { Error::::NoPermission ); let estate_info: EstateInfo = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; - let estate_account_id: T::AccountId = T::LandTreasury::get().into_sub_account_truncating(estate_id); + let _estate_account_id: T::AccountId = + T::LandTreasury::get().into_sub_account_truncating(estate_id); // Mutate estates Estates::::try_mutate_exists(&estate_id, |maybe_estate_info| { - let mut mut_estate_info = maybe_estate_info.as_mut().ok_or(Error::::EstateDoesNotExist)?; + let mut_estate_info = maybe_estate_info.as_mut().ok_or(Error::::EstateDoesNotExist)?; let storage_fee: BalanceOf = Perbill::from_percent(100u32.saturating_mul(land_units.len() as u32)) @@ -1549,7 +1549,7 @@ pub mod pallet { ); let current_block = >::block_number(); EstateLeases::::try_mutate_exists(&estate_id, |estate_lease_value| { - let mut lease = estate_lease_value.as_mut().ok_or(Error::::LeaseDoesNotExist)?; + let lease = estate_lease_value.as_mut().ok_or(Error::::LeaseDoesNotExist)?; ensure!(lease.end_block > current_block, Error::::LeaseIsExpired); @@ -1615,7 +1615,7 @@ impl Pallet { Error::::NoPermission ); - if let OwnerId::Token(owner_class_id, owner_token_id) = token_owner { + if let OwnerId::Token(owner_class_id, _owner_token_id) = token_owner { ensure!(owner_class_id != class_id, Error::::LandUnitAlreadyInEstate) } @@ -1881,11 +1881,11 @@ impl Pallet { from: &T::AccountId, to: &T::AccountId, ) -> Result { - EstateOwner::::try_mutate_exists(&estate_id, |estate_owner| -> Result { + EstateOwner::::try_mutate_exists(&estate_id, |_estate_owner| -> Result { //ensure there is record of the estate owner with estate id and account id ensure!(from != to, Error::::AlreadyOwnTheEstate); let estate_owner_value = Self::get_estate_owner(&estate_id).ok_or(Error::::NoPermission)?; - let estate_info = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; + let _estate_info = Estates::::get(estate_id).ok_or(Error::::EstateDoesNotExist)?; ensure!( !EstateLeases::::contains_key(estate_id), Error::::EstateIsAlreadyLeased @@ -2081,8 +2081,8 @@ impl Pallet { } fn verify_land_unit_in_bound(block_coordinate: &(i32, i32), land_unit_coordinates: &Vec<(i32, i32)>) -> bool { - let mut vec_axis = land_unit_coordinates.iter().map(|lu| lu.0).collect::>(); - let mut vec_yaxis = land_unit_coordinates.iter().map(|lu| lu.1).collect::>(); + let vec_axis = land_unit_coordinates.iter().map(|lu| lu.0).collect::>(); + let vec_yaxis = land_unit_coordinates.iter().map(|lu| lu.1).collect::>(); let max_axis = vec_axis.iter().max().unwrap_or(&i32::MAX); let max_yaxis = vec_yaxis.iter().max().unwrap_or(&i32::MAX); diff --git a/pallets/estate/src/mock.rs b/pallets/estate/src/mock.rs index 3856a489b..c20c8137d 100644 --- a/pallets/estate/src/mock.rs +++ b/pallets/estate/src/mock.rs @@ -14,9 +14,7 @@ use sp_std::vec::Vec; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{CollectionType, NftClassData, TokenType}; -use primitives::{ - AssetId, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId, LAND_CLASS_ID, -}; +use primitives::{Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId}; use crate as estate; @@ -144,7 +142,7 @@ parameter_types! { pub struct MetaverseInfoSource {} impl MetaverseTrait for MetaverseInfoSource { - fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId { + fn create_metaverse(_who: &AccountId, _metadata: MetaverseMetadata) -> MetaverseId { 1u64 } @@ -168,19 +166,19 @@ impl MetaverseTrait for MetaverseInfoSource { Ok(()) } - fn get_metaverse_land_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_land_class(_metaverse_id: MetaverseId) -> Result { Ok(METAVERSE_LAND_CLASS) } - fn get_metaverse_estate_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_estate_class(_metaverse_id: MetaverseId) -> Result { Ok(METAVERSE_ESTATE_CLASS) } - fn get_metaverse_marketplace_listing_fee(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_marketplace_listing_fee(_metaverse_id: MetaverseId) -> Result { Ok(Perbill::from_percent(1u32)) } - fn get_metaverse_treasury(metaverse_id: MetaverseId) -> AccountId { + fn get_metaverse_treasury(_metaverse_id: MetaverseId) -> AccountId { GENERAL_METAVERSE_FUND } @@ -189,7 +187,7 @@ impl MetaverseTrait for MetaverseInfoSource { } fn check_if_metaverse_estate( - metaverse_id: primitives::MetaverseId, + _metaverse_id: primitives::MetaverseId, class_id: &ClassId, ) -> Result { if class_id == &METAVERSE_LAND_CLASS || class_id == &METAVERSE_ESTATE_CLASS { @@ -202,7 +200,7 @@ impl MetaverseTrait for MetaverseInfoSource { Ok(true) } - fn is_metaverse_owner(who: &AccountId) -> bool { + fn is_metaverse_owner(_who: &AccountId) -> bool { true } } @@ -216,7 +214,7 @@ impl Auction for MockAuctionManager { None } - fn auction_item(id: AuctionId) -> Option> { + fn auction_item(_id: AuctionId) -> Option> { None } @@ -224,7 +222,7 @@ impl Auction for MockAuctionManager { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> DispatchResult { Ok(()) } @@ -253,11 +251,11 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + fn auction_bid_handler(_from: AccountId, _id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + fn buy_now_handler(_from: AccountId, _auction_id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } @@ -335,23 +333,23 @@ impl NFTTrait for MockNFTHandler { } Ok(false) } - fn get_nft_group_collection(nft_collection: &Self::ClassId) -> Result { + fn get_nft_group_collection(_nft_collection: &Self::ClassId) -> Result { Ok(ASSET_COLLECTION_ID) } - fn is_stackable(asset_id: (Self::ClassId, Self::TokenId)) -> Result { + fn is_stackable(_asset_id: (Self::ClassId, Self::TokenId)) -> Result { Ok(false) } fn create_token_class( sender: &AccountId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, collection_id: GroupCollectionId, - token_type: TokenType, - collection_type: CollectionType, - royalty_fee: Perbill, - mint_limit: Option, + _token_type: TokenType, + _collection_type: CollectionType, + _royalty_fee: Perbill, + _mint_limit: Option, ) -> Result { match *sender { ALICE => { @@ -372,8 +370,8 @@ impl NFTTrait for MockNFTHandler { fn mint_token( sender: &AccountId, class_id: ClassId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -406,26 +404,26 @@ impl NFTTrait for MockNFTHandler { } } - fn transfer_nft(from: &AccountId, to: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn transfer_nft(_from: &AccountId, _to: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn check_item_on_listing(class_id: Self::ClassId, token_id: Self::TokenId) -> Result { + fn check_item_on_listing(_class_id: Self::ClassId, _token_id: Self::TokenId) -> Result { Ok(true) } - fn burn_nft(account: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn burn_nft(_account: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn is_transferable(nft: &(Self::ClassId, Self::TokenId)) -> Result { + fn is_transferable(_nft: &(Self::ClassId, Self::TokenId)) -> Result { Ok(true) } - fn get_class_fund(class_id: &Self::ClassId) -> AccountId { + fn get_class_fund(_class_id: &Self::ClassId) -> AccountId { CLASS_FUND_ID } - fn get_nft_detail(asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { + fn get_nft_detail(_asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { let new_data = NftClassData { deposit: 0, attributes: test_attributes(1), @@ -439,11 +437,11 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn set_lock_collection(class_id: Self::ClassId, is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_collection(_class_id: Self::ClassId, _is_locked: bool) -> sp_runtime::DispatchResult { Ok(()) } - fn set_lock_nft(token_id: (Self::ClassId, Self::TokenId), is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_nft(_token_id: (Self::ClassId, Self::TokenId), _is_locked: bool) -> sp_runtime::DispatchResult { Ok(()) } @@ -461,20 +459,20 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn get_total_issuance(class_id: Self::ClassId) -> Result { + fn get_total_issuance(_class_id: Self::ClassId) -> Result { Ok(10u64) } - fn get_asset_owner(asset_id: &(Self::ClassId, Self::TokenId)) -> Result { + fn get_asset_owner(_asset_id: &(Self::ClassId, Self::TokenId)) -> Result { Ok(ALICE) } fn mint_token_with_id( sender: &AccountId, class_id: Self::ClassId, - token_id: Self::TokenId, - metadata: core_primitives::NftMetadata, - attributes: core_primitives::Attributes, + _token_id: Self::TokenId, + _metadata: core_primitives::NftMetadata, + _attributes: core_primitives::Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -507,31 +505,31 @@ impl NFTTrait for MockNFTHandler { } } - fn get_free_stackable_nft_balance(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { + fn get_free_stackable_nft_balance(_who: &AccountId, _asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { 1000 } fn reserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> DispatchResult { Ok(()) } fn unreserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } fn transfer_stackable_nft( - sender: &AccountId, - to: &AccountId, - nft: &(Self::ClassId, Self::TokenId), - amount: Balance, + _sender: &AccountId, + _to: &AccountId, + _nft: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } diff --git a/pallets/estate/src/rate.rs b/pallets/estate/src/rate.rs index 788907d6d..747a72029 100644 --- a/pallets/estate/src/rate.rs +++ b/pallets/estate/src/rate.rs @@ -19,7 +19,7 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_runtime::{Perbill, Permill, RuntimeDebug}; +use sp_runtime::{Perbill, RuntimeDebug}; use crate::{AllLandUnitsCount, TotalUndeployedLandUnit}; // Helper methods to compute the issuance rate for undeployed land. diff --git a/pallets/governance/src/lib.rs b/pallets/governance/src/lib.rs index 0f748ebf7..a1dfa13bd 100644 --- a/pallets/governance/src/lib.rs +++ b/pallets/governance/src/lib.rs @@ -459,7 +459,7 @@ pub mod pallet { let mut metaverse_has_referendum_running: bool = false; for (_, referendum_info) in ReferendumInfoOf::::iter_prefix(metaverse_id) { match referendum_info { - ReferendumInfo::Ongoing(status) => { + ReferendumInfo::Ongoing(_status) => { metaverse_has_referendum_running = true; break; } @@ -514,7 +514,7 @@ pub mod pallet { metaverse_id: MetaverseId, ) -> DispatchResultWithPostInfo { ensure_root(origin)?; - let proposal_info = Self::proposals(metaverse_id, proposal).ok_or(Error::::ProposalDoesNotExist)?; + let _proposal_info = Self::proposals(metaverse_id, proposal).ok_or(Error::::ProposalDoesNotExist)?; if let Some((depositors, deposit)) = >::take(proposal) { >::remove(metaverse_id, proposal); Self::update_proposals_per_metaverse_number(metaverse_id, false); // slash depositors @@ -670,7 +670,7 @@ pub mod pallet { ReferendumInfoOf::::insert(&metaverse, &referendum, ReferendumInfo::Ongoing(status)); Self::deposit_event(Event::VoteRemoved(from, referendum)); } - Some(ReferendumInfo::Finished { end, passed, title }) => { + Some(ReferendumInfo::Finished { end, passed, title: _ }) => { let prior = &mut voting_record.prior; if let Some((lock_periods, balance)) = vote.locked_if(passed) { let mut lock_value: T::BlockNumber = @@ -893,7 +893,7 @@ impl Pallet { } /// Table the waiting public proposal with the highest backing for a vote. - fn launch_public(now: T::BlockNumber, metaverse_id: MetaverseId) -> DispatchResult { + fn launch_public(_now: T::BlockNumber, metaverse_id: MetaverseId) -> DispatchResult { let launch_block = Self::get_proposal_launch_block(metaverse_id)?; if let Some((_, proposal)) = Proposals::::iter_prefix(metaverse_id).enumerate().max_by_key( // defensive only: All current public proposals have an amount locked @@ -1059,7 +1059,7 @@ impl Pallet { } else { let preimage = >::take(&metaverse_id, &referendum_status.proposal_hash); if let Some(PreimageStatus::Available { - data, + data: _, provider, deposit, .. @@ -1075,7 +1075,7 @@ impl Pallet { /// Internal enacting of successfully passed proposal fn do_enact_proposal( - proposal_id: ProposalId, + _proposal_id: ProposalId, metaverse_id: MetaverseId, referendum_id: ReferendumId, proposal_hash: T::Hash, diff --git a/pallets/governance/src/mock.rs b/pallets/governance/src/mock.rs index 85d0294b0..6e9f212f8 100644 --- a/pallets/governance/src/mock.rs +++ b/pallets/governance/src/mock.rs @@ -136,7 +136,7 @@ impl pallet_scheduler::Config for Runtime { pub struct MetaverseInfo {} impl MetaverseTrait for MetaverseInfo { - fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId { + fn create_metaverse(_who: &AccountId, _metadata: MetaverseMetadata) -> MetaverseId { 1u64 } @@ -160,19 +160,19 @@ impl MetaverseTrait for MetaverseInfo { Ok(()) } - fn get_metaverse_land_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_land_class(_metaverse_id: MetaverseId) -> Result { Ok(15u32) } - fn get_metaverse_estate_class(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_estate_class(_metaverse_id: MetaverseId) -> Result { Ok(16u32) } - fn get_metaverse_marketplace_listing_fee(metaverse_id: MetaverseId) -> Result { + fn get_metaverse_marketplace_listing_fee(_metaverse_id: MetaverseId) -> Result { Ok(Perbill::from_percent(1u32)) } - fn get_metaverse_treasury(metaverse_id: MetaverseId) -> AccountId { + fn get_metaverse_treasury(_metaverse_id: MetaverseId) -> AccountId { GENERAL_METAVERSE_FUND } @@ -181,7 +181,7 @@ impl MetaverseTrait for MetaverseInfo { } fn check_if_metaverse_estate( - metaverse_id: primitives::MetaverseId, + _metaverse_id: primitives::MetaverseId, class_id: &ClassId, ) -> Result { if class_id == &15u32 || class_id == &16u32 { @@ -194,7 +194,7 @@ impl MetaverseTrait for MetaverseInfo { Ok(true) } - fn is_metaverse_owner(who: &AccountId) -> bool { + fn is_metaverse_owner(_who: &AccountId) -> bool { true } } @@ -213,7 +213,7 @@ impl MetaverseLandTrait for MetaverseLandInfo { _ => false, } } - fn check_landunit(metaverse_id: MetaverseId, coordinate: (i32, i32)) -> Result { + fn check_landunit(_metaverse_id: MetaverseId, _coordinate: (i32, i32)) -> Result { Ok(false) } } @@ -258,7 +258,7 @@ impl NFTTrait for MockNFTHandler { Ok(false) } - fn is_stackable(asset_id: (Self::ClassId, Self::TokenId)) -> Result { + fn is_stackable(_asset_id: (Self::ClassId, Self::TokenId)) -> Result { Ok(false) } @@ -271,19 +271,19 @@ impl NFTTrait for MockNFTHandler { } Ok(false) } - fn get_nft_group_collection(nft_collection: &Self::ClassId) -> Result { + fn get_nft_group_collection(_nft_collection: &Self::ClassId) -> Result { Ok(ASSET_COLLECTION_ID) } fn create_token_class( sender: &AccountId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, collection_id: GroupCollectionId, - token_type: TokenType, - collection_type: CollectionType, - royalty_fee: Perbill, - mint_limit: Option, + _token_type: TokenType, + _collection_type: CollectionType, + _royalty_fee: Perbill, + _mint_limit: Option, ) -> Result { match *sender { ALICE => { @@ -304,8 +304,8 @@ impl NFTTrait for MockNFTHandler { fn mint_token( sender: &AccountId, class_id: ClassId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -329,26 +329,26 @@ impl NFTTrait for MockNFTHandler { } } - fn transfer_nft(from: &AccountId, to: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn transfer_nft(_from: &AccountId, _to: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn check_item_on_listing(class_id: Self::ClassId, token_id: Self::TokenId) -> Result { + fn check_item_on_listing(_class_id: Self::ClassId, _token_id: Self::TokenId) -> Result { Ok(true) } - fn burn_nft(account: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn burn_nft(_account: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn is_transferable(nft: &(Self::ClassId, Self::TokenId)) -> Result { + fn is_transferable(_nft: &(Self::ClassId, Self::TokenId)) -> Result { Ok(true) } - fn get_class_fund(class_id: &Self::ClassId) -> AccountId { + fn get_class_fund(_class_id: &Self::ClassId) -> AccountId { CLASS_FUND_ID } - fn get_nft_detail(asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { + fn get_nft_detail(_asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { let new_data = NftClassData { deposit: 0, attributes: test_attributes(1), @@ -362,11 +362,11 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn set_lock_collection(class_id: Self::ClassId, is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_collection(_class_id: Self::ClassId, _is_locked: bool) -> sp_runtime::DispatchResult { todo!() } - fn set_lock_nft(token_id: (Self::ClassId, Self::TokenId), is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_nft(_token_id: (Self::ClassId, Self::TokenId), _is_locked: bool) -> sp_runtime::DispatchResult { todo!() } @@ -384,20 +384,20 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn get_total_issuance(class_id: Self::ClassId) -> Result { + fn get_total_issuance(_class_id: Self::ClassId) -> Result { Ok(10u64) } - fn get_asset_owner(asset_id: &(Self::ClassId, Self::TokenId)) -> Result { + fn get_asset_owner(_asset_id: &(Self::ClassId, Self::TokenId)) -> Result { Ok(ALICE) } fn mint_token_with_id( sender: &AccountId, class_id: Self::ClassId, - token_id: Self::TokenId, - metadata: NftMetadata, - attributes: Attributes, + _token_id: Self::TokenId, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -420,31 +420,31 @@ impl NFTTrait for MockNFTHandler { } } } - fn get_free_stackable_nft_balance(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { + fn get_free_stackable_nft_balance(_who: &AccountId, _asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { 1000 } fn reserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> DispatchResult { Ok(()) } fn unreserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } fn transfer_stackable_nft( - sender: &AccountId, - to: &AccountId, - nft: &(Self::ClassId, Self::TokenId), - amount: Balance, + _sender: &AccountId, + _to: &AccountId, + _nft: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } diff --git a/pallets/governance/src/tests.rs b/pallets/governance/src/tests.rs index 6433a6905..886638493 100644 --- a/pallets/governance/src/tests.rs +++ b/pallets/governance/src/tests.rs @@ -382,7 +382,7 @@ fn vote_work() { #[test] fn vote_when_not_country_member_does_not_work() { ExtBuilder::default().build().execute_with(|| { - let origin = RuntimeOrigin::signed(BOB); + let _origin = RuntimeOrigin::signed(BOB); let hash = set_freeze_metaverse_proposal_hash(1); add_freeze_metaverse_preimage_alice(hash); assert_ok!(GovernanceModule::propose( diff --git a/pallets/metaverse/src/lib.rs b/pallets/metaverse/src/lib.rs index 68bca1bce..a0621c741 100644 --- a/pallets/metaverse/src/lib.rs +++ b/pallets/metaverse/src/lib.rs @@ -80,7 +80,7 @@ pub struct MetaverseStakingPoints { #[frame_support::pallet] pub mod pallet { use orml_traits::MultiCurrencyExtended; - use sp_runtime::traits::{CheckedAdd, Saturating}; + use sp_runtime::ArithmeticError; use primitives::staking::RoundInfo; @@ -545,7 +545,7 @@ impl Pallet { } /// Minting of a land class for the metaverse - fn mint_metaverse_land_class(sender: &T::AccountId, metaverse_id: MetaverseId) -> Result { + fn mint_metaverse_land_class(_sender: &T::AccountId, metaverse_id: MetaverseId) -> Result { // Pre-mint class for lands let mut land_class_attributes = Attributes::new(); land_class_attributes.insert("MetaverseId:".as_bytes().to_vec(), "MetaverseId:".as_bytes().to_vec()); @@ -565,7 +565,10 @@ impl Pallet { } /// Minting of an estate class for the metaverse - fn mint_metaverse_estate_class(sender: &T::AccountId, metaverse_id: MetaverseId) -> Result { + fn mint_metaverse_estate_class( + _sender: &T::AccountId, + metaverse_id: MetaverseId, + ) -> Result { // Pre-mint class for estates let mut estate_class_attributes = Attributes::new(); estate_class_attributes.insert("MetaverseId:".as_bytes().to_vec(), metaverse_id.to_be_bytes().to_vec()); @@ -611,7 +614,7 @@ impl Pallet { let default_land_class_id = TryInto::::try_into(0u32).unwrap_or_default(); let default_estate_class_id = TryInto::::try_into(1u32).unwrap_or_default(); - Metaverses::::translate(|k, metaverse_info_v1: MetaverseInfoV1| { + Metaverses::::translate(|_k, metaverse_info_v1: MetaverseInfoV1| { upgraded_metaverse_items += 1; let v2: MetaverseInfo = MetaverseInfo { diff --git a/pallets/metaverse/src/mock.rs b/pallets/metaverse/src/mock.rs index 5b12d9bf5..a238459de 100644 --- a/pallets/metaverse/src/mock.rs +++ b/pallets/metaverse/src/mock.rs @@ -7,7 +7,6 @@ use orml_traits::parameter_type_with_key; use sp_core::H256; use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; -use primitives::staking::RoundInfo; use primitives::{Amount, ClassId, GroupCollectionId, TokenId}; use crate as metaverse; @@ -128,22 +127,22 @@ impl NFTTrait for MockNFTHandler { } Ok(false) } - fn get_nft_group_collection(nft_collection: &Self::ClassId) -> Result { + fn get_nft_group_collection(_nft_collection: &Self::ClassId) -> Result { Ok(ASSET_COLLECTION_ID) } - fn is_stackable(asset_id: (Self::ClassId, Self::TokenId)) -> Result { + fn is_stackable(_asset_id: (Self::ClassId, Self::TokenId)) -> Result { Ok(false) } fn create_token_class( sender: &AccountId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, collection_id: GroupCollectionId, - token_type: TokenType, - collection_type: CollectionType, - royalty_fee: Perbill, - mint_limit: Option, + _token_type: TokenType, + _collection_type: CollectionType, + _royalty_fee: Perbill, + _mint_limit: Option, ) -> Result { match *sender { ALICE => Ok(100), @@ -164,8 +163,8 @@ impl NFTTrait for MockNFTHandler { fn mint_token( sender: &AccountId, class_id: ClassId, - metadata: NftMetadata, - attributes: Attributes, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -189,26 +188,26 @@ impl NFTTrait for MockNFTHandler { } } - fn transfer_nft(from: &AccountId, to: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn transfer_nft(_from: &AccountId, _to: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn check_item_on_listing(class_id: Self::ClassId, token_id: Self::TokenId) -> Result { + fn check_item_on_listing(_class_id: Self::ClassId, _token_id: Self::TokenId) -> Result { Ok(true) } - fn burn_nft(account: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn burn_nft(_account: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { Ok(()) } - fn is_transferable(nft: &(Self::ClassId, Self::TokenId)) -> Result { + fn is_transferable(_nft: &(Self::ClassId, Self::TokenId)) -> Result { Ok(true) } - fn get_class_fund(class_id: &Self::ClassId) -> AccountId { + fn get_class_fund(_class_id: &Self::ClassId) -> AccountId { CLASS_FUND_ID } - fn get_nft_detail(asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { + fn get_nft_detail(_asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { let new_data = NftClassData { deposit: 0, attributes: test_attributes(1), @@ -222,11 +221,11 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn set_lock_collection(class_id: Self::ClassId, is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_collection(_class_id: Self::ClassId, _is_locked: bool) -> sp_runtime::DispatchResult { todo!() } - fn set_lock_nft(token_id: (Self::ClassId, Self::TokenId), is_locked: bool) -> sp_runtime::DispatchResult { + fn set_lock_nft(_token_id: (Self::ClassId, Self::TokenId), _is_locked: bool) -> sp_runtime::DispatchResult { todo!() } @@ -244,20 +243,20 @@ impl NFTTrait for MockNFTHandler { Ok(new_data) } - fn get_total_issuance(class_id: Self::ClassId) -> Result { + fn get_total_issuance(_class_id: Self::ClassId) -> Result { Ok(10u64) } - fn get_asset_owner(asset_id: &(Self::ClassId, Self::TokenId)) -> Result { + fn get_asset_owner(_asset_id: &(Self::ClassId, Self::TokenId)) -> Result { Ok(ALICE) } fn mint_token_with_id( sender: &AccountId, class_id: Self::ClassId, - token_id: Self::TokenId, - metadata: NftMetadata, - attributes: Attributes, + _token_id: Self::TokenId, + _metadata: NftMetadata, + _attributes: Attributes, ) -> Result { match *sender { ALICE => Ok(1), @@ -281,31 +280,31 @@ impl NFTTrait for MockNFTHandler { } } - fn get_free_stackable_nft_balance(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { + fn get_free_stackable_nft_balance(_who: &AccountId, _asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { 1000 } fn reserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> DispatchResult { Ok(()) } fn unreserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, + _who: &AccountId, + _asset_id: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } fn transfer_stackable_nft( - sender: &AccountId, - to: &AccountId, - nft: &(Self::ClassId, Self::TokenId), - amount: Balance, + _sender: &AccountId, + _to: &AccountId, + _nft: &(Self::ClassId, Self::TokenId), + _amount: Balance, ) -> sp_runtime::DispatchResult { Ok(()) } diff --git a/pallets/metaverse/src/tests.rs b/pallets/metaverse/src/tests.rs index 39ba4822c..f75e1203d 100644 --- a/pallets/metaverse/src/tests.rs +++ b/pallets/metaverse/src/tests.rs @@ -17,12 +17,11 @@ #![cfg(test)] -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::{assert_noop, assert_ok}; use sp_runtime::traits::BadOrigin; use sp_runtime::Perbill; use mock::{RuntimeEvent, *}; -use primitives::staking::RoundInfo; #[cfg(test)] use super::*; diff --git a/pallets/mining/src/lib.rs b/pallets/mining/src/lib.rs index 1d1b34f55..bdb6ce7f4 100644 --- a/pallets/mining/src/lib.rs +++ b/pallets/mining/src/lib.rs @@ -19,34 +19,28 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; -use frame_support::traits::{Currency, Get, WithdrawReasons}; +use frame_support::traits::{Currency, Get}; use frame_support::PalletId; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo}, ensure, pallet_prelude::*, traits::ExistenceRequirement, - transactional, Parameter, }; use frame_system::pallet_prelude::*; use frame_system::{self as system, ensure_signed}; -use orml_traits::{ - arithmetic::{Signed, SimpleArithmetic}, - BalanceStatus, BasicCurrency, BasicCurrencyExtended, BasicLockableCurrency, BasicReservableCurrency, - LockIdentifier, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency, MultiReservableCurrency, -}; +use orml_traits::{BasicCurrency, LockIdentifier, MultiCurrency, MultiCurrencyExtended}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AccountIdConversion, AtLeast32Bit, One, StaticLookup, Zero}, - DispatchError, Perbill, + traits::{AccountIdConversion, Zero}, + DispatchError, }; use sp_std::vec::Vec; -use auction_manager::SwapManager; use core_primitives::*; pub use pallet::*; use primitives::staking::RoundInfo; -use primitives::{Balance, CurrencyId, FungibleTokenId, MetaverseId}; +use primitives::{Balance, FungibleTokenId}; pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -80,16 +74,15 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use frame_support::sp_runtime::traits::Saturating; - use frame_support::sp_runtime::{FixedPointNumber, SaturatedConversion}; - use frame_support::traits::OnUnbalanced; - use pallet_balances::NegativeImbalance; + + use frame_support::sp_runtime::SaturatedConversion; + use sp_runtime::Perbill; use sp_std::convert::TryInto; use primitives::estate::Estate; use primitives::staking::{MetaverseStakingTrait, RoundInfo}; - use primitives::{FungibleTokenId, RoundIndex, TokenId, VestingSchedule}; + use primitives::{FungibleTokenId, RoundIndex, VestingSchedule}; use crate::mining::round_issuance_range; diff --git a/pallets/mining/src/mining.rs b/pallets/mining/src/mining.rs index 750e698bf..a35a4a0ff 100644 --- a/pallets/mining/src/mining.rs +++ b/pallets/mining/src/mining.rs @@ -15,17 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use codec::{Decode, Encode}; -use orml_traits::arithmetic::{CheckedDiv, CheckedMul}; use orml_traits::MultiCurrency; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_runtime::traits::{Saturating, Zero}; -use sp_runtime::{ArithmeticError, Perbill, RuntimeDebug}; + +use sp_runtime::Perbill; use core_primitives::{MiningRange, MiningResourceRateInfo}; -use primitives::{estate::Estate, Balance, FungibleTokenId}; +use primitives::{Balance, FungibleTokenId}; // Helper methods to compute the issuance rate for undeployed land. use crate::pallet::{Config, Pallet}; diff --git a/pallets/mining/src/mock.rs b/pallets/mining/src/mock.rs index cac0f11ba..007245e98 100644 --- a/pallets/mining/src/mock.rs +++ b/pallets/mining/src/mock.rs @@ -1,8 +1,8 @@ -use frame_support::pallet_prelude::{GenesisBuild, Hooks, MaybeSerializeDeserialize}; -use frame_support::sp_runtime::traits::AtLeast32Bit; +use frame_support::pallet_prelude::GenesisBuild; + use frame_support::traits::Nothing; -use frame_support::{construct_runtime, ord_parameter_types, parameter_types, traits::EnsureOrigin, weights::Weight}; -use frame_system::{EnsureRoot, EnsureSignedBy}; +use frame_support::{construct_runtime, ord_parameter_types, parameter_types}; +use frame_system::EnsureSignedBy; use orml_traits::parameter_type_with_key; use sp_core::H256; use sp_runtime::{ @@ -13,11 +13,11 @@ use sp_runtime::{ use primitives::estate::Estate; use primitives::staking::MetaverseStakingTrait; -use primitives::FungibleTokenId::FungibleToken; + use primitives::{Amount, CurrencyId, EstateId, FungibleTokenId, RoundIndex, UndeployedLandBlockId}; use crate as mining; -use crate::{Config, Module}; +use crate::Config; use super::*; @@ -137,22 +137,22 @@ parameter_types! { pub struct EstateHandler; impl Estate for EstateHandler { - fn transfer_estate(estate_id: EstateId, from: &u128, to: &u128) -> Result { + fn transfer_estate(estate_id: EstateId, _from: &u128, _to: &u128) -> Result { Ok(estate_id) } fn transfer_landunit( coordinate: (i32, i32), - from: &u128, - to: &(u128, primitives::MetaverseId), + _from: &u128, + _to: &(u128, primitives::MetaverseId), ) -> Result<(i32, i32), DispatchError> { Ok(coordinate) } fn transfer_undeployed_land_block( - who: &AccountId, - to: &AccountId, - undeployed_land_block_id: UndeployedLandBlockId, + _who: &AccountId, + _to: &AccountId, + _undeployed_land_block_id: UndeployedLandBlockId, ) -> Result { Ok(2) } @@ -161,18 +161,18 @@ impl Estate for EstateHandler { Ok(true) } - fn check_landunit(_metaverse_id: primitives::MetaverseId, coordinate: (i32, i32)) -> Result { + fn check_landunit(_metaverse_id: primitives::MetaverseId, _coordinate: (i32, i32)) -> Result { Ok(true) } fn check_undeployed_land_block( - owner: &AccountId, - undeployed_land_block_id: UndeployedLandBlockId, + _owner: &AccountId, + _undeployed_land_block_id: UndeployedLandBlockId, ) -> Result { Ok(true) } - fn get_total_land_units(estate_id: Option) -> u64 { + fn get_total_land_units(_estate_id: Option) -> u64 { 10 } @@ -180,15 +180,15 @@ impl Estate for EstateHandler { 10 } - fn check_estate_ownership(owner: AccountId, estate_id: EstateId) -> Result { + fn check_estate_ownership(_owner: AccountId, _estate_id: EstateId) -> Result { Ok(false) } - fn is_estate_leasor(leasor: AccountId, estate_id: EstateId) -> Result { + fn is_estate_leasor(_leasor: AccountId, _estate_id: EstateId) -> Result { Ok(false) } - fn is_estate_leased(estate_id: EstateId) -> Result { + fn is_estate_leased(_estate_id: EstateId) -> Result { Ok(false) } } @@ -196,7 +196,7 @@ impl Estate for EstateHandler { pub struct MetaverseStakingHandler; impl MetaverseStakingTrait for MetaverseStakingHandler { - fn update_staking_reward(round: RoundIndex, total_reward: u128) -> sp_runtime::DispatchResult { + fn update_staking_reward(_round: RoundIndex, _total_reward: u128) -> sp_runtime::DispatchResult { Ok(()) } } diff --git a/pallets/mining/src/tests.rs b/pallets/mining/src/tests.rs index 52586b8ef..3b6198186 100644 --- a/pallets/mining/src/tests.rs +++ b/pallets/mining/src/tests.rs @@ -1,10 +1,8 @@ use frame_support::{assert_noop, assert_ok}; -use sp_core::blake2_256; -use sp_runtime::traits::BadOrigin; -use sp_runtime::{AccountId32, Perbill}; -use sp_std::vec::Vec; -use mock::{RuntimeEvent, *}; +use sp_runtime::Perbill; + +use mock::*; use primitives::Balance; // Unit testing for metaverse currency, metaverse treasury @@ -55,7 +53,7 @@ fn burn_mining_resource_should_work() { assert_eq!(get_mining_balance(), 1000); - let event = mock::RuntimeEvent::MiningModule(crate::Event::MiningResourceMintedTo(BOB, 1000)); + let _event = mock::RuntimeEvent::MiningModule(crate::Event::MiningResourceMintedTo(BOB, 1000)); assert_ok!(MiningModule::burn(origin, BOB, 300)); assert_eq!(get_mining_balance(), 700); diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index a2709e35e..d4333af59 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -26,8 +26,6 @@ #![allow(clippy::upper_case_acronyms)] #![cfg_attr(not(feature = "std"), no_std)] -use core::result; - use codec::Encode; use frame_support::traits::Len; use frame_support::{ @@ -39,7 +37,7 @@ use frame_support::{ }; use frame_system::pallet_prelude::*; use orml_nft::{ClassInfo, ClassInfoOf, Classes, Pallet as NftModule, TokenInfo, TokenInfoOf, TokenMetadataOf, Tokens}; -use scale_info::TypeInfo; + use sp_runtime::traits::Saturating; use sp_runtime::Perbill; use sp_runtime::{ @@ -52,8 +50,8 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use auction_manager::{Auction, CheckAuctionItemHandler}; pub use pallet::*; pub use primitive_traits::{Attributes, NFTTrait, NftClassData, NftGroupCollectionData, NftMetadata, TokenType}; -use primitive_traits::{CollectionType, NftAssetData, NftAssetDataV1, NftClassDataV1, PreSignedMint}; -use primitives::{AssetId, BlockNumber, ClassId, GroupCollectionId, Hash, ItemId, TokenId}; +use primitive_traits::{CollectionType, NftAssetData, NftClassDataV1, PreSignedMint}; +use primitives::{AssetId, ClassId, GroupCollectionId, ItemId, TokenId}; pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -76,7 +74,7 @@ pub enum StorageVersion { #[frame_support::pallet] pub mod pallet { - use frame_system::offchain::Signer; + use orml_traits::{MultiCurrency, MultiCurrencyExtended}; use sp_runtime::traits::{CheckedSub, IdentifyAccount, Verify}; use sp_runtime::ArithmeticError; @@ -474,7 +472,7 @@ pub mod pallet { mint_limit: Option, ) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; - let class_id = Self::do_create_class( + let _class_id = Self::do_create_class( &sender, metadata, attributes, @@ -556,7 +554,7 @@ pub mod pallet { let result = NftModule::::mint_stackable_nft(&sender, class_id, metadata, new_stackable_nft_data, amount); match result { - Ok((token_id, balance)) => { + Ok((token_id, _balance)) => { Self::deposit_event(Event::::NewStackableNftMinted(sender, class_id, token_id, amount)); Ok(().into()) } @@ -1370,8 +1368,8 @@ impl Pallet { log::info!("Start upgrading nft class data v2"); log::info!("Start upgrading nft token data v2"); let mut num_nft_classes = 0; - let mut num_nft_tokens = 0; - let mut asset_by_owner_updates = 0; + let _num_nft_tokens = 0; + let _asset_by_owner_updates = 0; Classes::::translate( |k, diff --git a/pallets/nft/src/mock.rs b/pallets/nft/src/mock.rs index 6fc8ecaf0..ab7304ee9 100644 --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -99,7 +99,7 @@ impl Auction for MockAuctionManager { None } - fn auction_item(id: AuctionId) -> Option> { + fn auction_item(_id: AuctionId) -> Option> { None } @@ -107,7 +107,7 @@ impl Auction for MockAuctionManager { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> DispatchResult { Ok(()) } @@ -136,11 +136,11 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + fn auction_bid_handler(_from: AccountId, _id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + fn buy_now_handler(_from: AccountId, _auction_id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } diff --git a/pallets/nft/src/tests.rs b/pallets/nft/src/tests.rs index a54463fbe..d2cd87bda 100644 --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -863,7 +863,7 @@ fn setting_hard_limit_should_work() { fn force_updating_total_issuance_should_work() { ExtBuilder::default().build().execute_with(|| { let origin = RuntimeOrigin::signed(account(1)); - let class_deposit = ::ClassMintingFee::get(); + let _class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -894,7 +894,7 @@ fn force_updating_total_issuance_should_work() { fn force_updating_total_issuance_should_fail() { ExtBuilder::default().build().execute_with(|| { let origin = RuntimeOrigin::signed(account(1)); - let class_deposit = ::ClassMintingFee::get(); + let _class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -922,7 +922,7 @@ fn force_updating_total_issuance_should_fail() { fn force_updating_new_royal_fee_should_work() { ExtBuilder::default().build().execute_with(|| { let origin = RuntimeOrigin::signed(account(1)); - let class_deposit = ::ClassMintingFee::get(); + let _class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), @@ -956,7 +956,7 @@ fn force_updating_new_royal_fee_should_work() { fn force_updating_new_royal_fee_should_fail() { ExtBuilder::default().build().execute_with(|| { let origin = RuntimeOrigin::signed(account(2)); - let class_deposit = ::ClassMintingFee::get(); + let _class_deposit = ::ClassMintingFee::get(); assert_ok!(Nft::create_group(RuntimeOrigin::root(), vec![1], vec![1],)); assert_ok!(Nft::create_class( origin.clone(), diff --git a/pallets/reward/src/lib.rs b/pallets/reward/src/lib.rs index 28681399c..ac4a96a4c 100644 --- a/pallets/reward/src/lib.rs +++ b/pallets/reward/src/lib.rs @@ -17,9 +17,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode, HasCompact}; +use codec::Encode; use frame_support::storage::{child, ChildTriePrefixIterator}; -use frame_support::traits::{LockIdentifier, WithdrawReasons}; + use frame_support::{ ensure, log, pallet_prelude::*, @@ -27,23 +27,21 @@ use frame_support::{ transactional, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::*}; -use orml_traits::{DataFeeder, DataProvider, MultiCurrency, MultiReservableCurrency}; +use orml_traits::{DataProvider, MultiCurrency, MultiReservableCurrency}; use sp_core::Encode as SPEncode; use sp_io::hashing::keccak_256; -use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, Hash as Hasher, Saturating}; +use sp_runtime::traits::{Hash as Hasher, Saturating}; use sp_runtime::{ - traits::{AccountIdConversion, One, Zero}, - ArithmeticError, DispatchError, Perbill, SaturatedConversion, + traits::{AccountIdConversion, Zero}, + DispatchError, Perbill, SaturatedConversion, }; -use sp_std::{collections::btree_map::BTreeMap, prelude::*, vec::Vec}; +use sp_std::{prelude::*, vec::Vec}; use core_primitives::NFTTrait; use core_primitives::*; pub use pallet::*; -use primitives::{ - estate::Estate, CampaignId, CampaignInfo, CampaignInfoV1, CampaignInfoV2, EstateId, Hash, RewardType, TrieIndex, -}; -use primitives::{Balance, ClassId, FungibleTokenId, NftId}; +use primitives::{Balance, ClassId, FungibleTokenId}; +use primitives::{CampaignId, CampaignInfo, CampaignInfoV2, Hash, RewardType, TrieIndex}; pub use weights::WeightInfo; //#[cfg(feature = "runtime-benchmarks")] @@ -59,13 +57,12 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use frame_support::traits::tokens::currency; + use frame_support::traits::ExistenceRequirement::AllowDeath; - use orml_traits::{rewards, MultiCurrencyExtended}; - use sp_runtime::traits::{CheckedAdd, CheckedSub, Saturating}; + + use sp_runtime::traits::{CheckedAdd, Saturating}; use sp_runtime::ArithmeticError; - use primitives::staking::RoundInfo; use primitives::{CampaignId, CampaignInfo, ClassId, NftId}; use super::*; @@ -379,7 +376,7 @@ pub mod pallet { /// - `properties`: information relevant for the campaign. /// /// Emits `NewRewardCampaignCreated` if successful. - #[pallet::weight(T::WeightInfo::create_campaign().saturating_mul((1u64.saturating_add(reward.len() as u64))))] + #[pallet::weight(T::WeightInfo::create_campaign().saturating_mul(1u64.saturating_add(reward.len() as u64)))] #[transactional] pub fn create_nft_campaign( origin: OriginFor, @@ -518,7 +515,7 @@ pub mod pallet { /// - `leaf_nodes`: list of the merkle tree nodes required for merkle-proof calculation. /// /// Emits `RewardClaimed` if successful. - #[pallet::weight(T::WeightInfo::claim_reward_root().saturating_mul((1u64.saturating_add(leaf_nodes.len() as u64))))] + #[pallet::weight(T::WeightInfo::claim_reward_root().saturating_mul(1u64.saturating_add(leaf_nodes.len() as u64)))] #[transactional] pub fn claim_reward_root( origin: OriginFor, @@ -582,7 +579,7 @@ pub mod pallet { /// - `amount`: the amount of NFTs that the account is going to claim /// /// Emits `RewardClaimed` if successful. - #[pallet::weight(T::WeightInfo::claim_nft_reward().saturating_mul((1u64.saturating_add(*amount))))] + #[pallet::weight(T::WeightInfo::claim_nft_reward().saturating_mul(1u64.saturating_add(*amount)))] #[transactional] pub fn claim_nft_reward(origin: OriginFor, id: CampaignId, amount: u64) -> DispatchResult { let who = ensure_signed(origin)?; @@ -641,7 +638,7 @@ pub mod pallet { /// - `leaf_nodes`: list of the merkle tree nodes required for merkle-proof calculation. /// /// Emits `RewardClaimed` if successful. - #[pallet::weight(T::WeightInfo::claim_nft_reward_root().saturating_mul((1u64.saturating_add(reward_tokens.len() as u64))))] + #[pallet::weight(T::WeightInfo::claim_nft_reward_root().saturating_mul(1u64.saturating_add(reward_tokens.len() as u64)))] #[transactional] pub fn claim_nft_reward_root( origin: OriginFor, @@ -855,7 +852,7 @@ pub mod pallet { let mut new_cap = cap.clone(); let mut rewards_list: Vec<(T::AccountId, Vec<(ClassId, NftId)>)> = Vec::new(); let mut tokens: Vec<(ClassId, TokenId)> = Vec::new(); - let mut total_amount_left: u64 = total_nfts_amount; + let total_amount_left: u64 = total_nfts_amount; for (to, amount) in rewards { let (t, _) = Self::reward_get_nft(campaign.trie_index, &to); ensure!(t.is_empty(), Error::::AccountAlreadyRewarded); @@ -866,7 +863,7 @@ pub mod pallet { ); total_amount_left.saturating_sub(amount); - for l in 0..amount { + for _l in 0..amount { let token = new_cap.pop().ok_or(Error::::RewardExceedCap)?; tokens.push(token); } @@ -945,13 +942,13 @@ pub mod pallet { /// - `merkle_roots_quanity`: the amount of merkle roots that were used for setting rewards. /// /// Emits `RewardCampaignClosed` and/or `RewardCampaignRootClosed` if successful. - #[pallet::weight(T::WeightInfo::close_campaign().saturating_mul((1u64.saturating_add(*merkle_roots_quantity))))] + #[pallet::weight(T::WeightInfo::close_campaign().saturating_mul(1u64.saturating_add(*merkle_roots_quantity)))] #[transactional] pub fn close_campaign(origin: OriginFor, id: CampaignId, merkle_roots_quantity: u64) -> DispatchResult { let who = ensure_signed(origin)?; let now = frame_system::Pallet::::block_number(); - let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; + let campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; ensure!(who == campaign.creator, Error::::NotCampaignCreator); @@ -1012,7 +1009,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let now = frame_system::Pallet::::block_number(); - let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; + let campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; ensure!(who == campaign.creator, Error::::NotCampaignCreator); @@ -1069,7 +1066,7 @@ pub mod pallet { T::AdminOrigin::ensure_origin(origin)?; let now = frame_system::Pallet::::block_number(); - let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; + let campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; ensure!(campaign.end > now, Error::::CampaignEnded); @@ -1100,7 +1097,7 @@ pub mod pallet { T::AdminOrigin::ensure_origin(origin)?; let now = frame_system::Pallet::::block_number(); - let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; + let campaign = Self::campaigns(id).ok_or(Error::::CampaignIsNotFound)?; ensure!(campaign.end > now, Error::::CampaignEnded); @@ -1179,7 +1176,7 @@ pub mod pallet { impl Hooks for Pallet { /// Hook that is called every time a new block is finalized. fn on_finalize(block_number: T::BlockNumber) { - for (id, info) in Campaigns::::iter() + for (id, _info) in Campaigns::::iter() .filter(|(_, campaign_info)| campaign_info.end == block_number) .collect::>() { @@ -1394,7 +1391,7 @@ impl Pallet { let mut upgraded_campaign_items = 0; Campaigns::::translate( - |k, campaign_info_v2: CampaignInfoV2, T::BlockNumber>| { + |_k, campaign_info_v2: CampaignInfoV2, T::BlockNumber>| { upgraded_campaign_items += 1; let v3_reward = RewardType::FungibleTokens(FungibleTokenId::NativeToken(0), campaign_info_v2.reward); diff --git a/pallets/reward/src/mock.rs b/pallets/reward/src/mock.rs index 6c045b21b..2fa488767 100644 --- a/pallets/reward/src/mock.rs +++ b/pallets/reward/src/mock.rs @@ -11,9 +11,8 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, MultiSignature, Perbil use auction_manager::*; use core_primitives::NftAssetData; -use primitives::estate::Estate; -use primitives::staking::MetaverseStakingTrait; -use primitives::{Amount, AuctionId, EstateId, FungibleTokenId, ItemId, UndeployedLandBlockId}; + +use primitives::{Amount, AuctionId, FungibleTokenId, ItemId}; use crate as reward; @@ -170,7 +169,7 @@ impl Auction for MockAuctionManager { None } - fn auction_item(id: AuctionId) -> Option> { + fn auction_item(_id: AuctionId) -> Option> { None } @@ -178,7 +177,7 @@ impl Auction for MockAuctionManager { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> DispatchResult { Ok(()) } @@ -207,11 +206,11 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + fn auction_bid_handler(_from: AccountId, _id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + fn buy_now_handler(_from: AccountId, _auction_id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } diff --git a/pallets/reward/src/tests.rs b/pallets/reward/src/tests.rs index 96cd76536..7cf44dbcb 100644 --- a/pallets/reward/src/tests.rs +++ b/pallets/reward/src/tests.rs @@ -17,13 +17,13 @@ #![cfg(test)] -use frame_support::{assert_err, assert_noop, assert_ok, sp_runtime::runtime_logger}; +use frame_support::{assert_noop, assert_ok}; use hex_literal::hex; use sp_std::collections::btree_map::BTreeMap; use sp_std::default::Default; use core_primitives::Attributes; -use mock::{Balance, RuntimeEvent, *}; +use mock::{Balance, *}; use primitives::{CampaignInfo, FungibleTokenId, Hash}; use super::*; @@ -235,7 +235,7 @@ fn create_multicurrency_campaign_works() { #[test] fn create_nft_campaign_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); assert_noop!( @@ -270,7 +270,7 @@ fn create_nft_campaign_fails() { #[test] fn create_campaign_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_noop!( Reward::create_campaign( @@ -718,7 +718,7 @@ fn set_reward_root_fails() { #[test] fn set_nft_reward_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::add_set_reward_origin(RuntimeOrigin::signed(ALICE), ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); @@ -814,7 +814,7 @@ fn set_nft_reward_fails() { #[test] fn set_nft_reward_root_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::add_set_reward_origin(RuntimeOrigin::signed(ALICE), ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); @@ -1397,7 +1397,7 @@ fn claim_reward_root_fails() { #[test] fn claim_nft_reward_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::add_set_reward_origin(RuntimeOrigin::signed(ALICE), ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); @@ -1480,7 +1480,7 @@ fn claim_nft_reward_fails() { #[test] fn claim_nft_reward_root_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::add_set_reward_origin(RuntimeOrigin::signed(ALICE), ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); @@ -1816,7 +1816,7 @@ fn close_multicurrency_campaign_works() { #[test] fn close_campaign_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::create_campaign( RuntimeOrigin::signed(ALICE), BOB, @@ -1869,7 +1869,7 @@ fn close_campaign_fails() { #[test] fn close_campaign_using_merkle_root_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::add_set_reward_origin(RuntimeOrigin::signed(ALICE), ALICE)); assert_ok!(Reward::create_campaign( RuntimeOrigin::signed(ALICE), @@ -1951,7 +1951,7 @@ fn close_campaign_using_merkle_root_fails() { #[test] fn close_nft_campaign_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); @@ -2108,7 +2108,7 @@ fn cancel_multicurrency_campaign_works() { #[test] fn cancel_nft_campaign_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); init_test_nft(RuntimeOrigin::signed(ALICE)); @@ -2159,7 +2159,7 @@ fn cancel_nft_campaign_fails() { #[test] fn cancel_campaign_fails() { ExtBuilder::default().build().execute_with(|| { - let campaign_id = 0; + let _campaign_id = 0; assert_ok!(Reward::create_campaign( RuntimeOrigin::signed(ALICE), BOB, diff --git a/runtime/common/src/currencies.rs b/runtime/common/src/currencies.rs index 540aae788..3a9fc1e6e 100644 --- a/runtime/common/src/currencies.rs +++ b/runtime/common/src/currencies.rs @@ -1,24 +1,23 @@ use frame_support::pallet_prelude::Get; use frame_support::traits::{Currency, OriginTrait}; use orml_traits::{BasicCurrency, MultiCurrency as MultiCurrencyTrait}; -use pallet_evm::Context; + use pallet_evm::{ - AddressMapping, ExitRevert, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, - PrecompileResult, PrecompileSet, + ExitRevert, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use sp_core::{H160, U256}; -use sp_runtime::traits::{AccountIdConversion, Dispatchable, Zero}; +use sp_runtime::traits::Dispatchable; use sp_std::{marker::PhantomData, prelude::*}; use evm_mapping::AddressMapping as EvmMapping; -use evm_mapping::EvmAddressMapping; + use precompile_utils::data::{Address, EvmData, EvmDataWriter}; use precompile_utils::handle::PrecompileHandleExt; use precompile_utils::modifier::FunctionModifier; use precompile_utils::prelude::RuntimeHelper; use precompile_utils::{succeed, EvmResult}; use primitives::evm::{Erc20Mapping, Output}; -use primitives::{evm, AssetIds, AssetMetadata, Balance, FungibleTokenId}; +use primitives::{AssetIds, Balance, FungibleTokenId}; #[precompile_utils_macro::generate_function_selector] #[derive(Debug, PartialEq)] @@ -114,7 +113,7 @@ where })) } - fn is_precompile(&self, address: H160) -> bool { + fn is_precompile(&self, _address: H160) -> bool { todo!() } } @@ -143,7 +142,7 @@ where if let Some(currency_id) = Runtime::decode_evm_address(address) { log::debug!(target: "evm", "multicurrency: currency id: {:?}", currency_id); - let result = { + let _result = { let selector = match handle.read_selector() { Ok(selector) => selector, Err(e) => return Err(e), @@ -193,7 +192,10 @@ where >, BalanceOf: TryFrom + Into + EvmData, { - fn not_supported(currency_id: FungibleTokenId, handle: &mut impl PrecompileHandle) -> EvmResult { + fn not_supported( + _currency_id: FungibleTokenId, + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { Err(PrecompileFailure::Error { exit_status: pallet_evm::ExitError::Other("not supported".into()), }) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 7dd0aa42d..56fc2368e 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -16,22 +16,17 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] -use codec::Encode; -use cumulus_pallet_parachain_system::CheckAssociatedRelayNumber; use frame_support::{ traits::Get, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, }; use orml_traits::currency::MutationHooks; -use polkadot_parachain::primitives::RelayChainBlockNumber; + use sp_runtime::{FixedPointNumber, FixedU128}; use sp_std::{marker::PhantomData, prelude::*}; use xcm::latest::prelude::*; use xcm_builder::TakeRevenue; -use xcm_executor::{ - traits::{DropAssets, WeightTrader}, - Assets, -}; +use xcm_executor::{traits::WeightTrader, Assets}; use primitives::BuyWeightRate; diff --git a/runtime/common/src/mock.rs b/runtime/common/src/mock.rs index 2fefd33be..dbe1ecb1c 100644 --- a/runtime/common/src/mock.rs +++ b/runtime/common/src/mock.rs @@ -1,22 +1,19 @@ -use std::ptr::hash; - use frame_support::{ construct_runtime, dispatch::DispatchResult, parameter_types, - traits::{AsEnsureOriginWithArg, Everything, Nothing}, + traits::{Everything, Nothing}, weights::Weight, PalletId, }; -use frame_system::{EnsureNever, EnsureRoot}; -use hex_literal::hex; +use frame_system::EnsureRoot; + use orml_traits::parameter_type_with_key; -use pallet_evm::{AddressMapping, PrecompileHandle, PrecompileOutput}; use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, HashedAddressMapping, Precompile, PrecompileSet}; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_core::{Blake2Hasher, Decode, Encode, Hasher, MaxEncodedLen, H160, H256, U256}; -use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, ConstU32, IdentityLookup, Verify}; +use pallet_evm::{PrecompileHandle, PrecompileOutput}; + +use sp_core::{MaxEncodedLen, H160, H256, U256}; +use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, Verify}; use sp_runtime::{AccountId32, DispatchError, MultiSignature, Perbill}; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; @@ -29,11 +26,10 @@ use primitives::evm::{ CurrencyIdType, Erc20Mapping, EvmAddress, H160_POSITION_CURRENCY_ID_TYPE, H160_POSITION_TOKEN, H160_POSITION_TOKEN_NFT, H160_POSITION_TOKEN_NFT_CLASS_ID_END, }; -use primitives::{Amount, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, ItemId, TokenId}; +use primitives::{Amount, AuctionId, ClassId, FungibleTokenId, ItemId, TokenId}; use crate::currencies::MultiCurrencyPrecompile; use crate::nft::NftPrecompile; -use crate::precompiles::MetaverseNetworkPrecompiles; use super::*; @@ -142,7 +138,7 @@ where } } - fn is_precompile(&self, address: H160) -> bool { + fn is_precompile(&self, _address: H160) -> bool { true } } @@ -311,7 +307,7 @@ impl Auction for MockAuctionManager { None } - fn auction_item(id: AuctionId) -> Option> { + fn auction_item(_id: AuctionId) -> Option> { None } @@ -319,7 +315,7 @@ impl Auction for MockAuctionManager { Ok(()) } - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> DispatchResult { Ok(()) } @@ -348,11 +344,11 @@ impl Auction for MockAuctionManager { fn remove_auction(_id: u64, _item_id: ItemId) {} - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { + fn auction_bid_handler(_from: AccountId, _id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { + fn buy_now_handler(_from: AccountId, _auction_id: AuctionId, _value: Self::Balance) -> DispatchResult { Ok(()) } diff --git a/runtime/common/src/nft.rs b/runtime/common/src/nft.rs index e10cebea6..e03599195 100644 --- a/runtime/common/src/nft.rs +++ b/runtime/common/src/nft.rs @@ -1,27 +1,25 @@ use core_primitives::{Attributes, CollectionType, NftMetadata, TokenType}; use evm_mapping::AddressMapping as EvmMapping; -use evm_mapping::EvmAddressMapping; + use frame_support::pallet_prelude::Get; use frame_support::traits::{Currency, OriginTrait}; use frame_system::RawOrigin; use orml_traits::{BasicCurrency, MultiCurrency}; use pallet_evm::{ - AddressMapping, ExitRevert, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, - PrecompileResult, PrecompileSet, + ExitRevert, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use sp_core::{H160, U256}; use sp_runtime::traits::{AccountIdConversion, Dispatchable}; use sp_runtime::Perbill; use sp_std::{marker::PhantomData, prelude::*}; -use codec::{DecodeAll, Encode}; use precompile_utils::data::{Address, EvmData, EvmDataWriter}; use precompile_utils::handle::PrecompileHandleExt; use precompile_utils::modifier::FunctionModifier; use precompile_utils::prelude::RuntimeHelper; use precompile_utils::{succeed, EvmResult}; use primitives::evm::{Erc20Mapping, Output}; -use primitives::{evm, Balance, ClassId, GroupCollectionId, TokenId}; +use primitives::{ClassId, GroupCollectionId, TokenId}; #[precompile_utils_macro::generate_function_selector] #[derive(Debug, PartialEq)] @@ -119,7 +117,7 @@ where Some(result) } - fn is_precompile(&self, address: H160) -> bool { + fn is_precompile(&self, _address: H160) -> bool { todo!() } } @@ -449,7 +447,7 @@ where let class_id = input.read::>()?.into(); // Build call info - let owner: H160 = input.read::
()?.into(); + let _owner: H160 = input.read::
()?.into(); let who = ::AddressMapping::get_account_id(&handle.context().caller); log::debug!(target: "evm", "withdraw funds from class {:?} fund", class_id); diff --git a/runtime/common/src/precompiles.rs b/runtime/common/src/precompiles.rs index 6774a1d83..6f1ce2d1e 100644 --- a/runtime/common/src/precompiles.rs +++ b/runtime/common/src/precompiles.rs @@ -1,4 +1,4 @@ -use pallet_evm::{ExitRevert, Precompile, PrecompileFailure, PrecompileHandle, PrecompileResult, PrecompileSet}; +use pallet_evm::{Precompile, PrecompileHandle, PrecompileResult, PrecompileSet}; use pallet_evm_precompile_blake2::Blake2F; use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use pallet_evm_precompile_dispatch::Dispatch; @@ -78,7 +78,7 @@ where } } - fn is_precompile(&self, address: H160) -> bool { + fn is_precompile(&self, _address: H160) -> bool { // sp_std::vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 1027, 400] // .into_iter() // .map(hash) diff --git a/runtime/common/src/tests/currencies.rs b/runtime/common/src/tests/currencies.rs index 8ab5cf926..612cede31 100644 --- a/runtime/common/src/tests/currencies.rs +++ b/runtime/common/src/tests/currencies.rs @@ -1,5 +1,3 @@ -use asset_manager::BalanceOf; -use frame_support::assert_noop; use hex_literal::hex; use sp_core::{H160, U256}; use sp_runtime::traits::Zero; @@ -242,7 +240,7 @@ fn balance_of_native_currencies_works() { .with_balances(vec![(alice_account_id(), 100000)]) .build() .execute_with(|| { - let mut evm_writer = EvmDataWriter::new_with_selector(Action::BalanceOf); + let _evm_writer = EvmDataWriter::new_with_selector(Action::BalanceOf); EvmMapping::claim_default_account(RuntimeOrigin::signed(alice_account_id())); precompiles() .prepare_test( diff --git a/runtime/common/src/tests/nft.rs b/runtime/common/src/tests/nft.rs index e17f1cc46..06701033c 100644 --- a/runtime/common/src/tests/nft.rs +++ b/runtime/common/src/tests/nft.rs @@ -1,23 +1,20 @@ -use frame_support::assert_noop; -use hex_literal::hex; -use sp_core::{ByteArray, H160, U256}; -use sp_runtime::traits::{AccountIdConversion, Zero}; +use sp_core::{ByteArray, U256}; +use sp_runtime::traits::AccountIdConversion; use sp_runtime::Perbill; use sp_std::collections::btree_map::BTreeMap; use precompile_utils::data::{Address, Bytes, EvmDataWriter}; use precompile_utils::testing::*; -use primitives::evm::Output; + use primitives::FungibleTokenId; use crate::mock::*; use crate::nft::Action; -use evm_mapping::AddressMapping as AddressMappingEvm; + use orml_nft::Pallet as NftModule; use orml_traits::BasicCurrency; -use pallet_evm::AddressMapping; -use core_primitives::{Attributes, CollectionType, NftAssetData, NftClassData, NftMetadata, TokenType}; +use core_primitives::{Attributes, CollectionType, NftMetadata, TokenType}; fn precompiles() -> Precompiles { PrecompilesValue::get() From a3b586b92ffa7b7d4c920102da900a3742bcdeee Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 15:34:49 +1300 Subject: [PATCH 054/155] WIP - Implement convert reciept token to native token --- pallets/asset-manager/src/lib.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pallets/asset-manager/src/lib.rs b/pallets/asset-manager/src/lib.rs index 475dca7bf..e1c1739ed 100644 --- a/pallets/asset-manager/src/lib.rs +++ b/pallets/asset-manager/src/lib.rs @@ -23,7 +23,6 @@ #![allow(clippy::unused_unit)] use frame_support::{ - assert_ok, dispatch::DispatchResult, ensure, pallet_prelude::*, @@ -31,17 +30,14 @@ use frame_support::{ transactional, }; use frame_system::pallet_prelude::*; -use scale_info::prelude::format; use sp_runtime::{traits::One, ArithmeticError, FixedPointNumber, FixedU128}; -use sp_std::{boxed::Box, vec::Vec}; +use sp_std::boxed::Box; use xcm::v3::MultiLocation; use xcm::VersionedMultiLocation; use core_primitives::CurrencyIdManagement; pub use pallet::*; -use primitives::{ - AssetIds, AssetMetadata, BuyWeightRate, CurrencyId, ForeignAssetIdMapping, FungibleTokenId, Ratio, TokenId, -}; +use primitives::{AssetIds, AssetMetadata, BuyWeightRate, ForeignAssetIdMapping, FungibleTokenId, Ratio, TokenId}; mod mock; mod tests; @@ -349,7 +345,7 @@ where } impl CurrencyIdManagement for ForeignAssetMapping { - fn check_token_exist(token_id: FungibleTokenId) -> bool { + fn check_token_exist(_token_id: FungibleTokenId) -> bool { return true; } @@ -360,7 +356,10 @@ impl CurrencyIdManagement for ForeignAssetMapping { } } - fn convert_to_currency(currency_id: FungibleTokenId) -> Result { - todo!() + fn convert_to_currency(_currency_id: FungibleTokenId) -> Result { + match currency_id { + FungibleTokenId::FungibleToken(token_id) => Ok(FungibleTokenId::NativeToken(token_id)), + _ => Err(()), + } } } From 189fb2ce03583624f43d7c57195e3c05fe26ae5a Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 15:56:30 +1300 Subject: [PATCH 055/155] WIP - Fix build issue on wrong currency_id --- pallets/asset-manager/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/asset-manager/src/lib.rs b/pallets/asset-manager/src/lib.rs index e1c1739ed..7a793cb42 100644 --- a/pallets/asset-manager/src/lib.rs +++ b/pallets/asset-manager/src/lib.rs @@ -356,7 +356,7 @@ impl CurrencyIdManagement for ForeignAssetMapping { } } - fn convert_to_currency(_currency_id: FungibleTokenId) -> Result { + fn convert_to_currency(currency_id: FungibleTokenId) -> Result { match currency_id { FungibleTokenId::FungibleToken(token_id) => Ok(FungibleTokenId::NativeToken(token_id)), _ => Err(()), From d693ce30cdc3741ed18ef8494fda852a5dcd84d7 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 9 Nov 2023 17:51:24 +1300 Subject: [PATCH 056/155] WIP - Fix build issue from suggestions --- node/src/chain_spec/metaverse.rs | 10 +++------- node/src/cli.rs | 1 - node/src/command.rs | 11 +++++------ node/src/rpc/rpc_metaverse.rs | 12 ++++++------ node/src/service/metaverse.rs | 2 +- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/node/src/chain_spec/metaverse.rs b/node/src/chain_spec/metaverse.rs index 64d0d0010..1c88f618a 100644 --- a/node/src/chain_spec/metaverse.rs +++ b/node/src/chain_spec/metaverse.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use std::str::FromStr; use hex_literal::hex; @@ -8,16 +7,13 @@ use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::crypto::UncheckedInto; use sp_core::{sr25519, Pair, Public, H160, U256}; use sp_finality_grandpa::AuthorityId as GrandpaId; -use sp_runtime::{ - traits::{IdentifyAccount, Verify}, - Perbill, -}; +use sp_runtime::traits::{IdentifyAccount, Verify}; use metaverse_runtime::{ constants::currency::*, opaque::SessionKeys, wasm_binary_unwrap, AccountId, AuraConfig, BalancesConfig, BaseFeeConfig, CollatorSelectionConfig, DemocracyConfig, EVMConfig, EstateConfig, EthereumConfig, GenesisAccount, - GenesisConfig, GrandpaConfig, MintingRange, MintingRateInfo, OracleMembershipConfig, SessionConfig, Signature, - SudoConfig, SystemConfig, + GenesisConfig, GrandpaConfig, MintingRateInfo, OracleMembershipConfig, SessionConfig, Signature, SudoConfig, + SystemConfig, }; use primitives::Balance; diff --git a/node/src/cli.rs b/node/src/cli.rs index 53d21dab7..10454a8e5 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -5,7 +5,6 @@ use cumulus_client_cli; use url::Url; use crate::chain_spec; -use crate::chain_spec::Extensions; fn validate_relay_chain_url(arg: &str) -> Result<(), String> { let url = Url::parse(arg).map_err(|e| e.to_string())?; diff --git a/node/src/command.rs b/node/src/command.rs index 41a6fc8a6..6c77ee62c 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -15,21 +15,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{io::Write, net::SocketAddr, sync::Arc}; +use std::{io::Write, net::SocketAddr}; use codec::Encode; use cumulus_client_cli::generate_genesis_block; -use cumulus_primitives_core::ParaId; + use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::info; use sc_cli::{ - ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, Result, Role, + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli, }; use sc_service::config::{BasePath, PrometheusConfig}; use sc_service::PartialComponents; use sp_core::hexdisplay::HexDisplay; -use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; +use sp_runtime::traits::Block as BlockT; #[cfg(feature = "with-continuum-runtime")] use continuum_runtime::RuntimeApi; @@ -41,12 +41,11 @@ use pioneer_runtime::RuntimeApi; use crate::service::{continuum_partial, ContinuumParachainRuntimeExecutor}; #[cfg(feature = "with-pioneer-runtime")] use crate::service::{pioneer_partial, ParachainRuntimeExecutor}; -use crate::service::{CONTINUUM_RUNTIME_NOT_AVAILABLE, METAVERSE_RUNTIME_NOT_AVAILABLE, PIONEER_RUNTIME_NOT_AVAILABLE}; +use crate::service::{CONTINUUM_RUNTIME_NOT_AVAILABLE, PIONEER_RUNTIME_NOT_AVAILABLE}; use crate::{ chain_spec, cli::{Cli, RelayChainCli, Subcommand}, service, - service::ExecutorDispatch, }; fn load_spec(id: &str) -> std::result::Result, String> { diff --git a/node/src/rpc/rpc_metaverse.rs b/node/src/rpc/rpc_metaverse.rs index f46faed58..b4113a02e 100644 --- a/node/src/rpc/rpc_metaverse.rs +++ b/node/src/rpc/rpc_metaverse.rs @@ -6,12 +6,12 @@ use fc_rpc::{ EthBlockDataCacheTask, OverrideHandle, RuntimeApiStorageOverride, SchemaV1Override, SchemaV2Override, SchemaV3Override, StorageOverride, }; -use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; +use fc_rpc_core::types::{FeeHistoryCache, FilterPool}; use fp_storage::EthereumStorageSchema; -use jsonrpc_pubsub::manager::SubscriptionManager; + use jsonrpsee::RpcModule; use pallet_transaction_payment_rpc; -use sc_cli::SubstrateCli; + // Substrate use sc_client_api::{ backend::{AuxStore, Backend, StateBackend, StorageProvider}, @@ -23,7 +23,7 @@ use sc_transaction_pool::{ChainApi, Pool}; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; -use sp_blockchain::{Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_runtime::traits::BlakeTwo256; use substrate_frame_rpc_system::{System, SystemApiServer}; @@ -138,8 +138,8 @@ where A: ChainApi + 'static, { use fc_rpc::{ - Eth, EthApiServer, EthDevSigner, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, - NetApiServer, Web3, Web3ApiServer, + Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net, NetApiServer, Web3, + Web3ApiServer, }; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use substrate_frame_rpc_system::SystemApiServer; diff --git a/node/src/service/metaverse.rs b/node/src/service/metaverse.rs index 2dacd366b..1c4c478d1 100644 --- a/node/src/service/metaverse.rs +++ b/node/src/service/metaverse.rs @@ -14,7 +14,7 @@ use fc_consensus::FrontierBlockImport; use fc_rpc::EthTask; use fc_rpc_core::types::{FeeHistoryCache, FilterPool}; use futures::StreamExt; -use sc_client_api::{BlockBackend, BlockchainEvents, ExecutorProvider}; +use sc_client_api::{BlockBackend, BlockchainEvents}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_executor::NativeElseWasmExecutor; use sc_finality_grandpa::SharedVoterState; From 15f53037f7d77442cac91804a0ab69cc25f62031 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 13 Nov 2023 16:00:31 +1300 Subject: [PATCH 057/155] WIP - Unit test - setting up mock testing --- Cargo.lock | 1 + pallets/spp/Cargo.toml | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a3200878..f6a4f98e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8028,6 +8028,7 @@ dependencies = [ "auction-manager", "bit-country-primitives", "core-primitives", + "currencies", "frame-benchmarking", "frame-support", "frame-system", diff --git a/pallets/spp/Cargo.toml b/pallets/spp/Cargo.toml index bb0c5c93e..e6009caf7 100644 --- a/pallets/spp/Cargo.toml +++ b/pallets/spp/Cargo.toml @@ -31,7 +31,7 @@ orml-tokens = { workspace = true } # local packages core-primitives = { path = "../../traits/core-primitives", default-features = false } primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } - +currencies = { package = "currencies", path = "../currencies", default-features = false } [dependencies.auction-manager] default-features = false @@ -61,6 +61,7 @@ std = [ 'sp-io/std', 'pallet-balances/std', 'auction-manager/std', - # 'pallet-nft/std', - 'frame-benchmarking/std' + 'frame-benchmarking/std', + 'orml-tokens/std', + "currencies/std" ] \ No newline at end of file From 656926b8dd3a144f74964f8ef78eef19f1e513c0 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 13 Nov 2023 16:00:54 +1300 Subject: [PATCH 058/155] WIP - Unit test - implement all mock pallet to start writing unit test --- pallets/nft/src/lib.rs | 2 - pallets/spp/src/lib.rs | 13 -- pallets/spp/src/mock.rs | 454 ++++------------------------------------ 3 files changed, 45 insertions(+), 424 deletions(-) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index d4333af59..b45d145e2 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -37,7 +37,6 @@ use frame_support::{ }; use frame_system::pallet_prelude::*; use orml_nft::{ClassInfo, ClassInfoOf, Classes, Pallet as NftModule, TokenInfo, TokenInfoOf, TokenMetadataOf, Tokens}; - use sp_runtime::traits::Saturating; use sp_runtime::Perbill; use sp_runtime::{ @@ -74,7 +73,6 @@ pub enum StorageVersion { #[frame_support::pallet] pub mod pallet { - use orml_traits::{MultiCurrency, MultiCurrencyExtended}; use sp_runtime::traits::{CheckedSub, IdentifyAccount, Verify}; use sp_runtime::ArithmeticError; diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 88470592a..323cc4216 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -27,7 +27,6 @@ use frame_support::{ use frame_system::ensure_signed; use frame_system::pallet_prelude::*; use orml_traits::MultiCurrency; - use sp_runtime::traits::{CheckedAdd, CheckedSub}; use sp_runtime::{ traits::{AccountIdConversion, Convert, Saturating, Zero}, @@ -36,7 +35,6 @@ use sp_runtime::{ use core_primitives::*; pub use pallet::*; - use primitives::{ClassId, FungibleTokenId, StakingRound, TokenId}; pub use weights::WeightInfo; @@ -76,14 +74,6 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// Land treasury source - #[pallet::constant] - type LandTreasury: Get; - - /// Source of metaverse info - type MetaverseInfoSource: MetaverseTrait; - /// Currency type type Currency: Currency + ReservableCurrency; /// Multi currencies type that handles different currency type in auction @@ -104,9 +94,6 @@ pub mod pallet { #[pallet::constant] type RewardPaymentDelay: Get; - /// NFT trait required for land and estate tokenization - type NFTTokenizationSource: NFTTrait, ClassId = ClassId, TokenId = TokenId>; - /// Default max bound for each metaverse mapping system, this could change through proposal type DefaultMaxBound: Get<(i32, i32)>; diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 3856a489b..04adc6cce 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -1,7 +1,9 @@ #![cfg(test)] +use frame_support::traits::Nothing; use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; use frame_system::EnsureSignedBy; +use orml_traits::parameter_type_with_key; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -15,10 +17,11 @@ use sp_std::vec::Vec; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{CollectionType, NftClassData, TokenType}; use primitives::{ - AssetId, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId, LAND_CLASS_ID, + Amount, AssetId, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId, + LAND_CLASS_ID, }; -use crate as estate; +use crate as spp; use super::*; @@ -48,7 +51,6 @@ pub const COORDINATE_IN_4: (i32, i32) = (-4, 8); pub const COORDINATE_OUT: (i32, i32) = (0, 101); pub const COORDINATE_IN_AUCTION: (i32, i32) = (-4, 7); pub const ESTATE_IN_AUCTION: EstateId = 3; -pub const UNDEPLOYED_LAND_BLOCK_IN_AUCTION: UndeployedLandBlockId = 1; pub const BOND_AMOUNT_1: Balance = 1000; pub const BOND_AMOUNT_2: Balance = 2000; @@ -67,12 +69,6 @@ pub const METAVERSE_LAND_IN_AUCTION_TOKEN: TokenId = 4; pub const METAVERSE_ESTATE_CLASS: ClassId = 16; pub const METAVERSE_ESTATE_IN_AUCTION_TOKEN: TokenId = 3; -pub const OWNER_ACCOUNT_ID: OwnerId = OwnerId::Account(BENEFICIARY_ID); -pub const OWNER_ID_ALICE: OwnerId = OwnerId::Account(ALICE); -pub const OWNER_LAND_ASSET_ID: OwnerId = OwnerId::Token(METAVERSE_LAND_CLASS, ASSET_ID_1); -pub const OWNER_ESTATE_ASSET_ID: OwnerId = - OwnerId::Token(METAVERSE_ESTATE_CLASS, ASSET_ID_2); - pub const GENERAL_METAVERSE_FUND: AccountId = 102; ord_parameter_types! { @@ -136,405 +132,50 @@ impl pallet_balances::Config for Runtime { parameter_types! { pub const GetNativeCurrencyId: FungibleTokenId = FungibleTokenId::NativeToken(0); - pub const MiningCurrencyId: FungibleTokenId = FungibleTokenId::MiningResource(0); + pub const LandTreasuryPalletId: PalletId = PalletId(*b"bit/land"); pub const LandTreasuryPalletId: PalletId = PalletId(*b"bit/land"); pub const MinimumLandPrice: Balance = 10 * DOLLARS; } -pub struct MetaverseInfoSource {} - -impl MetaverseTrait for MetaverseInfoSource { - fn create_metaverse(who: &AccountId, metadata: MetaverseMetadata) -> MetaverseId { - 1u64 - } - - fn check_ownership(who: &AccountId, metaverse_id: &MetaverseId) -> bool { - match *who { - ALICE => *metaverse_id == ALICE_METAVERSE_ID, - BOB => *metaverse_id == BOB_METAVERSE_ID, - _ => false, - } - } - - fn get_metaverse(_metaverse_id: u64) -> Option> { - None - } - - fn get_metaverse_token(_metaverse_id: u64) -> Option { - None - } - - fn update_metaverse_token(_metaverse_id: u64, _currency_id: FungibleTokenId) -> Result<(), DispatchError> { - Ok(()) - } - - fn get_metaverse_land_class(metaverse_id: MetaverseId) -> Result { - Ok(METAVERSE_LAND_CLASS) - } - - fn get_metaverse_estate_class(metaverse_id: MetaverseId) -> Result { - Ok(METAVERSE_ESTATE_CLASS) - } - - fn get_metaverse_marketplace_listing_fee(metaverse_id: MetaverseId) -> Result { - Ok(Perbill::from_percent(1u32)) - } - - fn get_metaverse_treasury(metaverse_id: MetaverseId) -> AccountId { - GENERAL_METAVERSE_FUND - } - - fn get_network_treasury() -> AccountId { - GENERAL_METAVERSE_FUND - } - - fn check_if_metaverse_estate( - metaverse_id: primitives::MetaverseId, - class_id: &ClassId, - ) -> Result { - if class_id == &METAVERSE_LAND_CLASS || class_id == &METAVERSE_ESTATE_CLASS { - return Ok(true); - } - return Ok(false); - } - - fn check_if_metaverse_has_any_land(_metaverse_id: primitives::MetaverseId) -> Result { - Ok(true) - } - - fn is_metaverse_owner(who: &AccountId) -> bool { - true - } -} - -pub struct MockAuctionManager; - -impl Auction for MockAuctionManager { - type Balance = Balance; - - fn auction_info(_id: u64) -> Option> { - None - } - - fn auction_item(id: AuctionId) -> Option> { - None - } - - fn update_auction(_id: u64, _info: AuctionInfo) -> DispatchResult { - Ok(()) - } - - fn update_auction_item(id: AuctionId, item_id: ItemId) -> DispatchResult { - Ok(()) - } - - fn new_auction( - _recipient: u128, - _initial_amount: Self::Balance, - _start: u64, - _end: Option, - ) -> Result { - Ok(1) - } - - fn create_auction( - _auction_type: AuctionType, - _item_id: ItemId, - _end: Option, - _recipient: u128, - _initial_amount: Self::Balance, - _start: u64, - _listing_level: ListingLevel, - _listing_fee: Perbill, - _currency_id: FungibleTokenId, - ) -> Result { - Ok(1) - } - - fn remove_auction(_id: u64, _item_id: ItemId) {} - - fn auction_bid_handler(from: AccountId, id: AuctionId, value: Self::Balance) -> DispatchResult { - Ok(()) - } - - fn buy_now_handler(from: AccountId, auction_id: AuctionId, value: Self::Balance) -> DispatchResult { - Ok(()) - } - - fn local_auction_bid_handler( - _now: u64, - _id: u64, - _new_bid: (u128, Self::Balance), - _last_bid: Option<(u128, Self::Balance)>, - _social_currency_id: FungibleTokenId, - ) -> DispatchResult { - Ok(()) - } - - fn collect_royalty_fee( - _high_bid_price: &Self::Balance, - _high_bidder: &u128, - _asset_id: &(u32, u64), - _social_currency_id: FungibleTokenId, - ) -> DispatchResult { - Ok(()) - } -} - -impl CheckAuctionItemHandler for MockAuctionManager { - fn check_item_in_auction(item_id: ItemId) -> bool { - match item_id { - ItemId::NFT(METAVERSE_LAND_CLASS, METAVERSE_LAND_IN_AUCTION_TOKEN) => { - return true; - } - ItemId::NFT(METAVERSE_ESTATE_CLASS, METAVERSE_ESTATE_IN_AUCTION_TOKEN) => { - return true; - } - ItemId::UndeployedLandBlock(UNDEPLOYED_LAND_BLOCK_IN_AUCTION) => { - return true; - } - _ => { - return false; - } - } - } -} - fn test_attributes(x: u8) -> Attributes { let mut attr: Attributes = BTreeMap::new(); attr.insert(vec![x, x + 5], vec![x, x + 10]); attr } -pub struct MockNFTHandler; - -impl NFTTrait for MockNFTHandler { - type TokenId = TokenId; - type ClassId = ClassId; - - fn check_ownership(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Result { - let nft_value = *asset_id; - if (*who == ALICE && (nft_value.1 == 1 || nft_value.1 == 3)) - || (*who == BOB && (nft_value.1 == 2 || nft_value.1 == 4)) - || (*who == BENEFICIARY_ID && (nft_value.1 == 100 || nft_value.1 == 101)) - | (*who == AUCTION_BENEFICIARY_ID - && (nft_value.1 == METAVERSE_ESTATE_IN_AUCTION_TOKEN - || nft_value.1 == METAVERSE_LAND_IN_AUCTION_TOKEN)) - { - return Ok(true); - } - Ok(false) - } - - fn check_collection_and_class( - collection_id: GroupCollectionId, - class_id: Self::ClassId, - ) -> Result { - if class_id == ASSET_CLASS_ID && collection_id == ASSET_COLLECTION_ID { - return Ok(true); - } - Ok(false) - } - fn get_nft_group_collection(nft_collection: &Self::ClassId) -> Result { - Ok(ASSET_COLLECTION_ID) - } - - fn is_stackable(asset_id: (Self::ClassId, Self::TokenId)) -> Result { - Ok(false) - } - - fn create_token_class( - sender: &AccountId, - metadata: NftMetadata, - attributes: Attributes, - collection_id: GroupCollectionId, - token_type: TokenType, - collection_type: CollectionType, - royalty_fee: Perbill, - mint_limit: Option, - ) -> Result { - match *sender { - ALICE => { - if collection_id == 0 { - Ok(0) - } else if collection_id == 1 { - Ok(1) - } else { - Ok(2) - } - } - BOB => Ok(3), - BENEFICIARY_ID => Ok(ASSET_CLASS_ID), - _ => Ok(100), - } - } - - fn mint_token( - sender: &AccountId, - class_id: ClassId, - metadata: NftMetadata, - attributes: Attributes, - ) -> Result { - match *sender { - ALICE => Ok(1), - BOB => Ok(2), - BENEFICIARY_ID => { - if class_id == METAVERSE_LAND_CLASS { - return Ok(ASSET_ID_1); - } else if class_id == METAVERSE_ESTATE_CLASS { - return Ok(ASSET_ID_2); - } else { - return Ok(200); - } - } - AUCTION_BENEFICIARY_ID => { - if class_id == METAVERSE_LAND_CLASS { - return Ok(METAVERSE_LAND_IN_AUCTION_TOKEN); - } else if class_id == METAVERSE_ESTATE_CLASS { - return Ok(METAVERSE_ESTATE_IN_AUCTION_TOKEN); - } else { - return Ok(201); - } - } - _ => { - if class_id == 0 { - return Ok(1000); - } else { - return Ok(1001); - } - } - } - } - - fn transfer_nft(from: &AccountId, to: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { - Ok(()) - } - - fn check_item_on_listing(class_id: Self::ClassId, token_id: Self::TokenId) -> Result { - Ok(true) - } - - fn burn_nft(account: &AccountId, nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { - Ok(()) - } - fn is_transferable(nft: &(Self::ClassId, Self::TokenId)) -> Result { - Ok(true) - } - - fn get_class_fund(class_id: &Self::ClassId) -> AccountId { - CLASS_FUND_ID - } - - fn get_nft_detail(asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { - let new_data = NftClassData { - deposit: 0, - attributes: test_attributes(1), - token_type: TokenType::Transferable, - collection_type: CollectionType::Collectable, - is_locked: false, - royalty_fee: Perbill::from_percent(0u32), - mint_limit: None, - total_minted_tokens: 0u32, - }; - Ok(new_data) - } - - fn set_lock_collection(class_id: Self::ClassId, is_locked: bool) -> sp_runtime::DispatchResult { - Ok(()) - } - - fn set_lock_nft(token_id: (Self::ClassId, Self::TokenId), is_locked: bool) -> sp_runtime::DispatchResult { - Ok(()) - } - - fn get_nft_class_detail(_class_id: Self::ClassId) -> Result, DispatchError> { - let new_data = NftClassData { - deposit: 0, - attributes: test_attributes(1), - token_type: TokenType::Transferable, - collection_type: CollectionType::Collectable, - is_locked: false, - royalty_fee: Perbill::from_percent(0u32), - mint_limit: None, - total_minted_tokens: 0u32, - }; - Ok(new_data) - } - - fn get_total_issuance(class_id: Self::ClassId) -> Result { - Ok(10u64) - } - - fn get_asset_owner(asset_id: &(Self::ClassId, Self::TokenId)) -> Result { - Ok(ALICE) - } - - fn mint_token_with_id( - sender: &AccountId, - class_id: Self::ClassId, - token_id: Self::TokenId, - metadata: core_primitives::NftMetadata, - attributes: core_primitives::Attributes, - ) -> Result { - match *sender { - ALICE => Ok(1), - BOB => Ok(2), - BENEFICIARY_ID => { - if class_id == METAVERSE_LAND_CLASS { - return Ok(ASSET_ID_1); - } else if class_id == METAVERSE_ESTATE_CLASS { - return Ok(ASSET_ID_2); - } else { - return Ok(200); - } - } - AUCTION_BENEFICIARY_ID => { - if class_id == METAVERSE_LAND_CLASS { - return Ok(METAVERSE_LAND_IN_AUCTION_TOKEN); - } else if class_id == METAVERSE_ESTATE_CLASS { - return Ok(METAVERSE_ESTATE_IN_AUCTION_TOKEN); - } else { - return Ok(201); - } - } - _ => { - if class_id == 0 { - return Ok(1000); - } else { - return Ok(1001); - } - } - } - } +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: FungibleTokenId| -> Balance { + Default::default() + }; +} - fn get_free_stackable_nft_balance(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { - 1000 - } +impl orml_tokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = FungibleTokenId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = (); + type MaxLocks = (); + type ReserveIdentifier = [u8; 8]; + type MaxReserves = (); + type DustRemovalWhitelist = Nothing; +} - fn reserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, - ) -> DispatchResult { - Ok(()) - } +pub type AdaptedBasicCurrency = currencies::BasicCurrencyAdapter; - fn unreserve_stackable_nft_balance( - who: &AccountId, - asset_id: &(Self::ClassId, Self::TokenId), - amount: Balance, - ) -> sp_runtime::DispatchResult { - Ok(()) - } +parameter_types! { + pub const NativeCurrencyId: FungibleTokenId = FungibleTokenId::NativeToken(0); + pub const MiningCurrencyId: FungibleTokenId = FungibleTokenId::MiningResource(0); +} - fn transfer_stackable_nft( - sender: &AccountId, - to: &AccountId, - nft: &(Self::ClassId, Self::TokenId), - amount: Balance, - ) -> sp_runtime::DispatchResult { - Ok(()) - } +impl currencies::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MultiSocialCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type GetNativeCurrencyId = NativeCurrencyId; + type WeightInfo = (); } parameter_types! { @@ -553,25 +194,18 @@ parameter_types! { impl Config for Runtime { type RuntimeEvent = RuntimeEvent; - type LandTreasury = LandTreasuryPalletId; - type MetaverseInfoSource = MetaverseInfoSource; type Currency = Balances; - type MinimumLandPrice = MinimumLandPrice; - type CouncilOrigin = EnsureSignedBy; - type AuctionHandler = MockAuctionManager; - type MinBlocksPerRound = MinBlocksPerRound; type WeightInfo = (); type MinimumStake = MinimumStake; type RewardPaymentDelay = RewardPaymentDelay; - type NFTTokenizationSource = MockNFTHandler; type DefaultMaxBound = DefaultMaxBound; type NetworkFee = NetworkFee; - type MaxOffersPerEstate = MaxOffersPerEstate; - type MinLeasePricePerBlock = MinLeasePricePerBlock; - type MaxLeasePeriod = MaxLeasePeriod; - type LeaseOfferExpiryPeriod = LeaseOfferExpiryPeriod; type BlockNumberToBalance = ConvertInto; type StorageDepositFee = StorageDepositFee; + type MultiCurrency = Currencies; + type PoolAccount = (); + type MaximumQueue = (); + type CurrencyIdConversion = (); } construct_runtime!( @@ -582,11 +216,13 @@ construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Estate: estate:: {Pallet, Call, Storage, Event}, + Currencies: currencies::{ Pallet, Storage, Call, Event}, + Tokens: orml_tokens::{Pallet, Call, Storage, Config, Event}, + Spp: spp:: {Pallet, Call, Storage, Event}, } ); -pub type EstateModule = Pallet; +pub type SppModule = Pallet; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -625,9 +261,9 @@ pub fn last_event() -> RuntimeEvent { } fn next_block() { - EstateModule::on_finalize(System::block_number()); + SppModule::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); - EstateModule::on_initialize(System::block_number()); + SppModule::on_initialize(System::block_number()).unwrap(); } pub fn run_to_block(n: u64) { From 1f80c1dc15da5dde6147900b5b125f0e42cc0b78 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 13 Nov 2023 16:01:23 +1300 Subject: [PATCH 059/155] WIP - Unit test - create the first example test to ensure the compilation success --- pallets/spp/src/tests.rs | 2882 +------------------------------------- 1 file changed, 2 insertions(+), 2880 deletions(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index d597baa73..95eef7654 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -24,2885 +24,7 @@ use mock::{RuntimeEvent, *}; use super::*; -fn estate_sub_account(estate_id: mock::EstateId) -> AccountId { - ::LandTreasury::get().into_sub_account_truncating(estate_id) -} - -#[test] -fn mint_land_should_reject_non_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::mint_land( - RuntimeOrigin::signed(ALICE), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - ), - BadOrigin - ); - }); -} - -#[test] -fn mint_land_should_work_with_one_coordinate() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( - OWNER_LAND_ASSET_ID, - METAVERSE_ID, - COORDINATE_IN_1, - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 1); - }); -} - -#[test] -fn mint_land_token_should_work_have_correct_owner() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), None); - - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( - OWNER_LAND_ASSET_ID, - METAVERSE_ID, - COORDINATE_IN_1, - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 1); - - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), - Some(OWNER_LAND_ASSET_ID) - ); - }); -} - -#[test] -fn mint_land_should_reject_with_duplicate_coordinates() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( - OWNER_LAND_ASSET_ID, - METAVERSE_ID, - COORDINATE_IN_1, - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 1); - assert_noop!( - EstateModule::mint_land(RuntimeOrigin::root(), BENEFICIARY_ID, METAVERSE_ID, COORDINATE_IN_1), - Error::::LandUnitIsNotAvailable - ); - }); -} - -#[test] -fn mint_lands_should_reject_with_duplicate_coordinates() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_lands( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandsMinted( - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2], - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 2); - assert_noop!( - EstateModule::mint_lands( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - ), - Error::::LandUnitIsNotAvailable - ); - }); -} - -#[test] -fn mint_land_should_work_with_different_coordinate() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( - OWNER_LAND_ASSET_ID, - METAVERSE_ID, - COORDINATE_IN_1, - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 1); - - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_2 - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandUnitMinted( - OWNER_LAND_ASSET_ID, - METAVERSE_ID, - COORDINATE_IN_2, - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 2); - }); -} - -#[test] -fn mint_lands_should_reject_non_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::mint_lands( - RuntimeOrigin::signed(ALICE), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - ), - BadOrigin - ); - }); -} - -#[test] -fn mint_lands_should_work_with_one_coordinate() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_lands( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 1 - ); - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandsMinted( - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1], - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 1); - }); -} - -#[test] -fn mint_lands_should_work_with_more_than_one_coordinate() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_lands( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::NewLandsMinted( - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2], - )) - ); - - assert_eq!(EstateModule::all_land_units_count(), 2); - }); -} - -#[test] -fn transfer_land_token_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), - Some(OWNER_LAND_ASSET_ID) - ); - - assert_ok!(EstateModule::transfer_land( - RuntimeOrigin::signed(BENEFICIARY_ID), - ALICE, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), - Some(OWNER_LAND_ASSET_ID) - ); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::TransferredLandUnit( - METAVERSE_ID, - COORDINATE_IN_1, - BENEFICIARY_ID, - ALICE, - )) - ); - }); -} - -#[test] -fn transfer_land_should_reject_no_permission() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), - Some(OWNER_LAND_ASSET_ID) - ); - - assert_noop!( - EstateModule::transfer_land(RuntimeOrigin::signed(BOB), ALICE, METAVERSE_ID, COORDINATE_IN_1), - Error::::NoPermission - ); - }); -} - -#[test] -fn transfer_land_should_do_fail_for_same_account() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), - Some(OWNER_LAND_ASSET_ID) - ); - - assert_noop!( - EstateModule::transfer_land( - RuntimeOrigin::signed(BENEFICIARY_ID), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - ), - Error::::AlreadyOwnTheLandUnit - ); - - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_1), - Some(OWNER_LAND_ASSET_ID) - ); - }); -} - -#[test] -fn transfer_land_should_do_fail_for_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - AUCTION_BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_AUCTION - )); - assert_eq!( - EstateModule::get_land_units(METAVERSE_ID, COORDINATE_IN_AUCTION), - Some(OwnerId::Token(METAVERSE_LAND_CLASS, METAVERSE_LAND_IN_AUCTION_TOKEN)) - ); - - assert_noop!( - EstateModule::transfer_land( - RuntimeOrigin::signed(AUCTION_BENEFICIARY_ID), - BOB, - METAVERSE_ID, - COORDINATE_IN_AUCTION - ), - Error::::LandUnitAlreadyInAuction - ); - }); -} - -#[test] -fn mint_estate_should_reject_non_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::mint_estate( - RuntimeOrigin::signed(ALICE), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - ), - BadOrigin - ); - }); -} - -#[test] -fn mint_estate_should_fail_for_minted_land() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_err!( - EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - ), - Error::::LandUnitIsNotAvailable - ); - }); -} - -#[test] -fn dissolve_estate_should_work() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - - // Destroy estate - assert_ok!(EstateModule::dissolve_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - estate_id, - )); - - assert_eq!(EstateModule::all_estates_count(), 0); - assert_eq!(EstateModule::get_estates(estate_id), None); - assert_eq!(EstateModule::get_estate_owner(estate_id), None); - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - }); -} - -#[test] -fn dissolve_estate_should_reject_non_owner() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_err!( - EstateModule::dissolve_estate(RuntimeOrigin::signed(ALICE), 0), - Error::::NoPermission - ); - }); -} - -#[test] -fn add_land_unit_to_estate_should_reject_non_owner() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_2] - )); - - assert_err!( - EstateModule::add_land_unit_to_estate(RuntimeOrigin::signed(ALICE), 0, vec![COORDINATE_IN_2]), - Error::::NoPermission - ); - }); -} - -#[test] -fn add_land_unit_to_estate_should_work() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1] - }) - ); - - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 1 - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_eq!(EstateModule::all_land_units_count(), 1); - - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_2 - )); - // Update estate - assert_ok!(EstateModule::add_land_unit_to_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - estate_id, - vec![COORDINATE_IN_2] - )); - - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - assert_eq!(EstateModule::all_land_units_count(), 2); - }); -} - -#[test] -fn remove_land_unit_from_estate_should_reject_non_owner() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_err!( - EstateModule::remove_land_unit_from_estate(RuntimeOrigin::signed(ALICE), 0, vec![COORDINATE_IN_2]), - Error::::NoPermission - ); - }); -} - -#[test] -fn remove_land_unit_from_estate_should_work() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - assert_eq!(EstateModule::all_land_units_count(), 2); - - // Update estate - assert_ok!(EstateModule::remove_land_unit_from_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - estate_id, - vec![COORDINATE_IN_2] - )); - - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1] - }) - ); - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - assert_eq!(EstateModule::all_land_units_count(), 2); - }); -} - -#[test] -fn mint_estate_and_land_should_return_correct_total_land_unit() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - (-6, 6) - )); - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 3 - ); - }); -} - -#[test] -fn mint_estate_should_return_none_for_non_exist_estate() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - let estate_id_non_exists: u64 = 999; - assert_eq!(EstateModule::get_estates(estate_id_non_exists), None); - assert_eq!(EstateModule::get_estate_owner(estate_id_non_exists), None); - }); -} - -#[test] -fn transfer_estate_token_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_ok!(EstateModule::transfer_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - ALICE, - estate_id - )); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::TransferredEstate(estate_id, BENEFICIARY_ID, ALICE)) - ); - }); -} - -#[test] -fn transfer_estate_should_reject_no_permission() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_noop!( - EstateModule::transfer_estate(RuntimeOrigin::signed(BOB), ALICE, estate_id), - Error::::NoPermission - ); - }); -} - -#[test] -fn transfer_estate_should_reject_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_2] - )); - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_3] - )); - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - AUCTION_BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_AUCTION] - )); - assert_noop!( - EstateModule::transfer_estate(RuntimeOrigin::signed(AUCTION_BENEFICIARY_ID), ALICE, ESTATE_IN_AUCTION), - Error::::EstateAlreadyInAuction - ); - }); -} - -#[test] -fn transfer_estate_should_fail_with_same_account() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - assert_noop!( - EstateModule::transfer_estate(RuntimeOrigin::signed(BENEFICIARY_ID), BENEFICIARY_ID, estate_id), - Error::::AlreadyOwnTheEstate - ); - - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - }); -} - -#[test] -fn create_estate_token_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_lands( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - assert_eq!(Balances::free_balance(BENEFICIARY_ID), 999998); - }); -} - -#[test] -fn create_estate_token_after_minting_account_and_token_based_lands_should_give_correct_total_user_land_units() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_1 - )); - - assert_ok!(EstateModule::mint_land( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - COORDINATE_IN_2 - )); - - assert_ok!(EstateModule::create_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - assert_eq!( - EstateModule::get_user_land_units(&BENEFICIARY_ID, &METAVERSE_ID).len(), - 2 - ); - assert_eq!(EstateModule::all_land_units_count(), 2); - assert_eq!(Balances::free_balance(BENEFICIARY_ID), 999998); - }); -} - -#[test] -fn create_estate_should_return_none_for_non_exist_estate() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_lands( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_estate( - RuntimeOrigin::signed(BENEFICIARY_ID), - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - assert_eq!(Balances::free_balance(BENEFICIARY_ID), 999998); - - let estate_id: u64 = 0; - assert_eq!(EstateModule::all_estates_count(), 1); - assert_eq!(EstateModule::next_estate_id(), 1); - assert_eq!( - EstateModule::get_estates(estate_id), - Some(EstateInfo { - metaverse_id: METAVERSE_ID, - land_units: vec![COORDINATE_IN_1, COORDINATE_IN_2] - }) - ); - assert_eq!(EstateModule::get_estate_owner(estate_id), Some(OWNER_ESTATE_ASSET_ID)); - - let estate_id_non_exists: u64 = 999; - assert_eq!(EstateModule::get_estates(estate_id_non_exists), None); - assert_eq!(EstateModule::get_estate_owner(estate_id_non_exists), None); - }); -} - -#[test] -fn issue_land_block_should_fail_if_not_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::signed(ALICE), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - ), - BadOrigin - ); - }); -} - -#[test] -fn issue_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(BOB, 0)) - ); - - assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 20); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - }); -} - -#[test] -fn issue_two_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(BOB, 0)) - ); - - assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); - - let first_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match first_issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 20); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - ALICE, - 1, - 30, - UndeployedLandBlockType::Transferable - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(ALICE, 1)) - ); - - assert_eq!(EstateModule::get_undeployed_land_block_owner(ALICE, 1), Some(())); - - let second_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match second_issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, ALICE); - assert_eq!(a.number_land_units, 30); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - }); -} - -#[test] -fn freeze_undeployed_land_block_should_fail_if_not_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), - BadOrigin - ); - }); -} - -#[test] -fn freeze_undeployed_land_block_should_fail_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), - Error::::UndeployedLandBlockNotFound - ); - }); -} - -#[test] -fn freeze_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(UNDEPLOYED_LAND_BLOCK_IN_AUCTION); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - assert_noop!( - EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), UNDEPLOYED_LAND_BLOCK_IN_AUCTION), - Error::::UndeployedLandBlockAlreadyInAuction - ); - }); -} - -#[test] -fn freeze_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 20); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_ok!(EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockFreezed(0)) - ); - - assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); - - let frozen_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match frozen_undeployed_land_block { - Some(a) => { - assert_eq!(a.is_locked, true); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - }); -} - -#[test] -fn freeze_undeployed_land_block_should_fail_already_freezed() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - assert_ok!(EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockFreezed(0)) - ); - - assert_noop!( - EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), - Error::::UndeployedLandBlockAlreadyFreezed - ); - }); -} - -#[test] -fn unfreeze_undeployed_land_block_should_fail_if_not_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), - BadOrigin - ); - }); -} - -#[test] -fn unfreeze_undeployed_land_block_should_fail_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), - Error::::UndeployedLandBlockNotFound - ); - }); -} - -#[test] -fn unfreeze_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_noop!( - EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), UNDEPLOYED_LAND_BLOCK_IN_AUCTION), - Error::::UndeployedLandBlockAlreadyInAuction - ); - }); -} - -#[test] -fn unfreeze_undeployed_land_block_should_fail_not_frozen() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - assert_noop!( - EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), 0), - Error::::UndeployedLandBlockNotFrozen - ); - }); -} - -#[test] -fn unfreeze_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - assert_ok!(EstateModule::freeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); - - let freezed_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match freezed_undeployed_land_block { - Some(a) => { - assert_eq!(a.is_locked, true); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_ok!(EstateModule::unfreeze_undeployed_land_blocks(RuntimeOrigin::root(), 0)); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockUnfreezed(0)) - ); - - let unfreezed_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match unfreezed_undeployed_land_block { - Some(a) => { - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - }); -} - -#[test] -fn transfer_undeployed_land_block_should_fail_if_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, 0), - Error::::UndeployedLandBlockNotFound - ); - }); -} - -#[test] -fn transfer_undeployed_land_block_should_fail_if_not_owner() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_noop!( - EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, undeployed_land_block_id), - Error::::NoPermission - ); - }); -} - -#[test] -fn transfer_undeployed_land_block_should_fail_if_freezed() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_ok!(EstateModule::freeze_undeployed_land_blocks( - RuntimeOrigin::root(), - undeployed_land_block_id - )); - - assert_noop!( - EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(BOB), ALICE, undeployed_land_block_id), - Error::::UndeployedLandBlockAlreadyFreezed - ); - }); -} - -#[test] -fn transfer_undeployed_land_block_should_fail_if_not_transferable() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_noop!( - EstateModule::transfer_undeployed_land_blocks(RuntimeOrigin::signed(BOB), ALICE, undeployed_land_block_id), - Error::::UndeployedLandBlockIsNotTransferable - ); - }); -} - -#[test] -fn transfer_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_noop!( - EstateModule::transfer_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - ALICE, - UNDEPLOYED_LAND_BLOCK_IN_AUCTION - ), - Error::::UndeployedLandBlockAlreadyInAuction - ); - }); -} - -#[test] -fn transfer_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::Transferable - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - Some(()) - ); - - assert_ok!(EstateModule::transfer_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - ALICE, - undeployed_land_block_id - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockTransferred( - BOB, - ALICE, - undeployed_land_block_id, - )) - ); - - let transferred_issued_undeployed_land_block = - EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match transferred_issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, ALICE); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - None - ); - assert_eq!( - EstateModule::get_undeployed_land_block_owner(ALICE, undeployed_land_block_id), - Some(()) - ); - }); -} - -#[test] -fn deploy_undeployed_land_block_should_fail_if_not_found() { - ExtBuilder::default().build().execute_with(|| { - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_noop!( - EstateModule::deploy_land_block( - RuntimeOrigin::signed(ALICE), - undeployed_land_block_id, - ALICE_METAVERSE_ID, - LANDBLOCK_COORDINATE, - vec![COORDINATE_IN_1] - ), - Error::::UndeployedLandBlockNotFound - ); - assert_eq!(Balances::free_balance(BOB), 100000); - }); -} - -#[test] -fn deploy_undeployed_land_block_should_fail_if_not_owner() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_noop!( - EstateModule::deploy_land_block( - RuntimeOrigin::signed(ALICE), - undeployed_land_block_id, - METAVERSE_ID, - LANDBLOCK_COORDINATE, - vec![COORDINATE_IN_1] - ), - Error::::NoPermission - ); - assert_eq!(Balances::free_balance(ALICE), 100000); - }); -} - -#[test] -fn deploy_undeployed_land_block_should_fail_if_freezed() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_ok!(EstateModule::freeze_undeployed_land_blocks( - RuntimeOrigin::root(), - undeployed_land_block_id - )); - - assert_noop!( - EstateModule::deploy_land_block( - RuntimeOrigin::signed(BOB), - undeployed_land_block_id, - BOB_METAVERSE_ID, - LANDBLOCK_COORDINATE, - vec![COORDINATE_IN_1] - ), - Error::::UndeployedLandBlockFreezed - ); - assert_eq!(Balances::free_balance(BOB), 99999); - }); -} - -#[test] -fn deploy_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_noop!( - EstateModule::deploy_land_block( - RuntimeOrigin::signed(BOB), - UNDEPLOYED_LAND_BLOCK_IN_AUCTION, - METAVERSE_ID, - LANDBLOCK_COORDINATE, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - ), - Error::::UndeployedLandBlockAlreadyInAuction - ); - assert_eq!(Balances::free_balance(BOB), 99998); - }); -} - -#[test] -fn deploy_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 2, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - let undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match undeployed_land_block { - Some(a) => { - assert_eq!(a.number_land_units, 2); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_ok!(EstateModule::deploy_land_block( - RuntimeOrigin::signed(BOB), - undeployed_land_block_id, - BOB_METAVERSE_ID, - LANDBLOCK_COORDINATE, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::LandBlockDeployed( - BOB, - BOB_METAVERSE_ID, - undeployed_land_block_id, - vec![COORDINATE_IN_1, COORDINATE_IN_2], - )) - ); - - let updated_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - - assert_eq!(updated_undeployed_land_block, None); - - assert_eq!(EstateModule::all_land_units_count(), 2); - assert_eq!(Balances::free_balance(BOB), 99998); - }); -} - -#[test] -fn approve_undeployed_land_block_should_fail_if_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::approve_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, 0), - Error::::UndeployedLandBlockNotFound - ); - }); -} - #[test] -fn approve_undeployed_land_block_should_fail_if_not_owner() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_noop!( - EstateModule::approve_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), BOB, undeployed_land_block_id), - Error::::NoPermission - ); - }); -} - -#[test] -fn approve_undeployed_land_block_should_fail_if_freezed() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_ok!(EstateModule::freeze_undeployed_land_blocks( - RuntimeOrigin::root(), - undeployed_land_block_id - )); - - assert_noop!( - EstateModule::approve_undeployed_land_blocks(RuntimeOrigin::signed(BOB), ALICE, undeployed_land_block_id), - Error::::UndeployedLandBlockAlreadyFreezed - ); - }); -} - -#[test] -fn approve_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_noop!( - EstateModule::approve_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - ALICE, - UNDEPLOYED_LAND_BLOCK_IN_AUCTION - ), - Error::::UndeployedLandBlockAlreadyInAuction - ); - }); -} - -#[test] -fn approve_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::Transferable - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - assert_eq!(a.approved, None); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - Some(()) - ); - - assert_ok!(EstateModule::approve_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - ALICE, - undeployed_land_block_id - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockApproved( - BOB, - ALICE, - undeployed_land_block_id, - )) - ); - - let transferred_issued_undeployed_land_block = - EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match transferred_issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - assert_eq!(a.approved, Some(ALICE)); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - Some(()) - ); - }); -} - -#[test] -fn unapprove_undeployed_land_block_should_fail_if_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::unapprove_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), - Error::::UndeployedLandBlockNotFound - ); - }); -} - -#[test] -fn unapprove_undeployed_land_block_should_fail_if_not_owner() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_noop!( - EstateModule::unapprove_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), undeployed_land_block_id), - Error::::NoPermission - ); - }); -} - -#[test] -fn unapprove_undeployed_land_block_should_fail_if_freezed() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - assert_ok!(EstateModule::freeze_undeployed_land_blocks( - RuntimeOrigin::root(), - undeployed_land_block_id - )); - - assert_noop!( - EstateModule::unapprove_undeployed_land_blocks(RuntimeOrigin::signed(BOB), undeployed_land_block_id), - Error::::UndeployedLandBlockAlreadyFreezed - ); - }); -} - -#[test] -fn unapprove_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_noop!( - EstateModule::unapprove_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - UNDEPLOYED_LAND_BLOCK_IN_AUCTION - ), - Error::::UndeployedLandBlockAlreadyInAuction - ); - }); -} - -#[test] -fn unapprove_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::Transferable - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - assert_eq!(a.approved, None); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - Some(()) - ); - assert_ok!(EstateModule::approve_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - ALICE, - undeployed_land_block_id - )); - - let approved_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match approved_issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - assert_eq!(a.approved, Some(ALICE)); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_ok!(EstateModule::unapprove_undeployed_land_blocks( - RuntimeOrigin::signed(BOB), - undeployed_land_block_id - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockUnapproved(undeployed_land_block_id)) - ); - - let unapproved_issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match unapproved_issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - assert_eq!(a.approved, None); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - }); -} - -#[test] -fn burn_undeployed_land_block_should_fail_if_not_root() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::burn_undeployed_land_blocks(RuntimeOrigin::signed(ALICE), 0), - BadOrigin - ); - }); -} - -#[test] -fn burn_undeployed_land_block_should_fail_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - EstateModule::burn_undeployed_land_blocks(RuntimeOrigin::root(), 0), - Error::::UndeployedLandBlockNotFound - ); - }); -} - -#[test] -fn burn_undeployed_land_block_should_fail_if_already_in_auction() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 1, - UndeployedLandBlockType::Transferable, - )); - - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 21, - UndeployedLandBlockType::Transferable, - )); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(1); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 21); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::Transferable); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_noop!( - EstateModule::burn_undeployed_land_blocks(RuntimeOrigin::root(), UNDEPLOYED_LAND_BLOCK_IN_AUCTION), - Error::::UndeployedLandBlockAlreadyInAuction - ); - }); -} - -#[test] -fn burn_undeployed_land_block_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 20, - UndeployedLandBlockType::BoundToAddress - )); - - let undeployed_land_block_id: UndeployedLandBlockId = 0; - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(undeployed_land_block_id); - match issued_undeployed_land_block { - Some(a) => { - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 20); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - Some(()) - ); - - assert_ok!(EstateModule::burn_undeployed_land_blocks( - RuntimeOrigin::root(), - undeployed_land_block_id - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockBurnt(undeployed_land_block_id)) - ); - - assert_eq!( - EstateModule::get_undeployed_land_block_owner(BOB, undeployed_land_block_id), - None - ); - - assert_eq!(EstateModule::get_undeployed_land_block(undeployed_land_block_id), None) - }); -} - -#[test] -fn ensure_land_unit_within_land_block_bound_should_work() { - // let coordinates: Vec<(i32, i32)> = vec![(-4, 0), (-3, 0), (-3, 0), (0, 5)]; - // assert_eq!(EstateModule::verify_land_unit_in_bound(&(0, 0), &coordinates), true); - - let second_coordinates: Vec<(i32, i32)> = vec![(-204, 25), (-203, 24), (-195, 20), (-197, 16)]; - assert_eq!( - EstateModule::verify_land_unit_in_bound(&(-20, 2), &second_coordinates), - true - ); - - let third_coordinates: Vec<(i32, i32)> = vec![(-64, 5), (-64, 4), (-64, 4), (-55, -4)]; - assert_eq!( - EstateModule::verify_land_unit_in_bound(&(-6, 0), &third_coordinates), - true - ); - - // Combined in and out bound should fail - let fourth_coordinates: Vec<(i32, i32)> = vec![(-5, 3), (-4, 6), (-5, 4)]; - assert_eq!( - EstateModule::verify_land_unit_in_bound(&(0, 0), &fourth_coordinates), - false - ); -} - -#[test] -fn ensure_land_unit_out_of_land_block_bound_should_fail() { - let coordinates: Vec<(i32, i32)> = vec![(-51, 0), (-48, 0), (-47, 0), (0, 51)]; - assert_eq!(EstateModule::verify_land_unit_in_bound(&(0, 0), &coordinates), false); - - let second_coordinates: Vec<(i32, i32)> = vec![(-250, 2), (-248, 2), (-150, 2), (-151, 6)]; - assert_eq!( - EstateModule::verify_land_unit_in_bound(&(-200, 2), &second_coordinates), - false - ); -} - -#[test] -fn issue_land_block_and_create_estate_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::issue_undeployed_land_blocks( - RuntimeOrigin::root(), - BOB, - 1, - 2, - UndeployedLandBlockType::BoundToAddress - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::UndeployedLandBlockIssued(BOB, 0)) - ); - - assert_eq!(EstateModule::get_undeployed_land_block_owner(BOB, 0), Some(())); - - let issued_undeployed_land_block = EstateModule::get_undeployed_land_block(0); - match issued_undeployed_land_block { - Some(a) => { - // Verify details of UndeployedLandBlock - assert_eq!(a.owner, BOB); - assert_eq!(a.number_land_units, 2); - assert_eq!(a.undeployed_land_block_type, UndeployedLandBlockType::BoundToAddress); - assert_eq!(a.is_locked, false); - } - _ => { - // Should fail test - assert_eq!(0, 1); - } - } - - // Bob can deploy raw land block to his metaverse - assert_ok!(EstateModule::deploy_land_block( - RuntimeOrigin::signed(BOB), - 0, - BOB_METAVERSE_ID, - LANDBLOCK_COORDINATE, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - assert_eq!(Balances::free_balance(BOB), 99998); - - assert_eq!( - EstateModule::get_land_units(BOB_METAVERSE_ID, COORDINATE_IN_1), - Some(OwnerId::Token(METAVERSE_LAND_CLASS, 2)) - ); - - assert_eq!( - EstateModule::get_land_units(BOB_METAVERSE_ID, COORDINATE_IN_2), - Some(OwnerId::Token(METAVERSE_LAND_CLASS, 2)) - ); - }); -} - -#[test] -fn create_estate_lease_offer_should_fail() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 1u64, 10u128, 8u32), - Error::::EstateDoesNotExist - ); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, 10u128, 8u32), - Error::::NoPermission - ); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, 0u128, 8u32), - Error::::LeaseOfferPriceBelowMinimum - ); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, 2u128, 1000u32), - Error::::LeaseOfferDurationAboveMaximum - ); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, 2u128, 7u32), - Error::::LeaseOfferAlreadyExists - ); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(CHARLIE), 0u64, 12u128, 8u32), - Error::::EstateIsAlreadyLeased - ); - - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - AUCTION_BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_2] - )); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(BOB), 1u64, 100u128, 8u32), - Error::::EstateAlreadyInAuction - ); - - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_3] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(BOB), - 2u64, - 12u128, - 8u32 - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 2u64, - 13u128, - 8u32 - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(CHARLIE), - 2u64, - 14u128, - 8u32 - )); - - assert_noop!( - EstateModule::create_lease_offer(RuntimeOrigin::signed(DOM), 2u64, 15u128, 8u32), - Error::::EstateLeaseOffersQueueLimitIsReached - ); - }); -} - -#[test] -fn create_estate_lease_offer_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseOfferCreated(ALICE, 0, 80)) - ); - - let lease_contract = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 7, - start_block: 8, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); - - assert_eq!(Balances::free_balance(ALICE), 99920); - }); -} - -#[test] -fn accept_estate_lease_offer_should_fail() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(BOB), - 0u64, - 10u128, - 8u32 - )); - - assert_noop!( - EstateModule::accept_lease_offer(RuntimeOrigin::signed(ALICE), 0u64, BOB), - Error::::NoPermission - ); - //TO DO: Offer cannot be accepted after asset is listed on auction - - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(BOB), - 1u64, - 10u128, - 8u32 - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 1u64, - 10u128, - 8u32 - )); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 1u64, - ALICE - )); - - assert_noop!( - EstateModule::accept_lease_offer(RuntimeOrigin::signed(BENEFICIARY_ID), 1u64, BOB), - Error::::EstateIsAlreadyLeased - ); - - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_3] - )); - - assert_noop!( - EstateModule::accept_lease_offer(RuntimeOrigin::signed(BENEFICIARY_ID), 2u64, BOB), - Error::::LeaseOfferDoesNotExist - ); - }); -} - -#[test] -fn accept_estate_lease_offer_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - let lease_contract = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 7, - start_block: 8, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) - ); - - let lease = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 9, - start_block: 1, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::leases(0u64), Some(lease)); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); - - assert_eq!(Balances::free_balance(ALICE), 99920); - }); -} - -#[test] -fn cancel_lease_should_fail() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_noop!( - EstateModule::cancel_lease(RuntimeOrigin::signed(BOB), BENEFICIARY_ID, 0u64, ALICE), - BadOrigin - ); - - assert_noop!( - EstateModule::cancel_lease(RuntimeOrigin::root(), BENEFICIARY_ID, 1u64, ALICE), - Error::::LeaseDoesNotExist - ); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_noop!( - EstateModule::cancel_lease(RuntimeOrigin::root(), BENEFICIARY_ID, 0u64, BOB), - Error::::LeaseDoesNotExist - ); - assert_noop!( - EstateModule::cancel_lease(RuntimeOrigin::root(), BOB, 0u64, ALICE), - Error::::NoPermission - ); - - run_to_block(22); - - assert_noop!( - EstateModule::cancel_lease(RuntimeOrigin::root(), BENEFICIARY_ID, 0u64, ALICE), - Error::::LeaseIsExpired - ); - }); -} - -#[test] -fn cancel_lease_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - let lease_contract = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 7, - start_block: 8, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) - ); - - let lease = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 9, - start_block: 1, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::leases(0u64), Some(lease)); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - run_to_block(5); - - assert_ok!(EstateModule::cancel_lease( - RuntimeOrigin::root(), - BENEFICIARY_ID, - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseContractCancelled(0)) - ); - - assert_eq!(EstateModule::leases(0u64), None); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), None); - - assert_eq!(Balances::free_balance(ALICE), 99960); - assert_eq!(Balances::free_balance(BENEFICIARY_ID), 1000039); - }); -} - -#[test] -fn remove_expired_lease_should_fail() { - ExtBuilder::default().build().execute_with(|| { - // Mint estate - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_noop!( - EstateModule::remove_expired_lease(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), - Error::::LeaseDoesNotExist - ); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - run_to_block(3); - - assert_noop!( - EstateModule::remove_expired_lease(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), - Error::::LeaseIsNotExpired - ); - - run_to_block(22); - - assert_noop!( - EstateModule::remove_expired_lease(RuntimeOrigin::signed(BOB), 0u64, ALICE), - Error::::NoPermission - ); - }); -} - -#[test] -fn remove_expired_lease_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - let lease_contract = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 7, - start_block: 8, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) - ); - - let lease = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 9, - start_block: 1, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::leases(0u64), Some(lease)); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - run_to_block(10); - - assert_ok!(EstateModule::remove_expired_lease( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseContractEnded(0u64)) - ); - - assert_eq!(EstateModule::leases(0u64), None); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), None); - - assert_eq!(Balances::free_balance(ALICE), 99920); - assert_eq!(Balances::free_balance(BENEFICIARY_ID), 1000079); - }); -} - -#[test] -fn remove_lease_offer_should_fail() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1] - )); - - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_noop!( - EstateModule::remove_lease_offer(RuntimeOrigin::signed(BOB), 0u64), - Error::::LeaseOfferDoesNotExist - ); - - assert_noop!( - EstateModule::remove_lease_offer(RuntimeOrigin::signed(ALICE), 1u64), - Error::::LeaseOfferDoesNotExist - ); - }); -} - -#[test] -fn remove_lease_offer_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - let lease_contract = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 7, - start_block: 8, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); - - assert_ok!(EstateModule::remove_lease_offer(RuntimeOrigin::signed(ALICE), 0u64)); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseOfferRemoved(ALICE, 0u64)) - ); - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); - assert_eq!(Balances::free_balance(ALICE), 100000); - }); -} - -#[test] -fn collect_rent_should_fail() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_noop!( - EstateModule::collect_rent(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), - Error::::LeaseDoesNotExist - ); - - assert_noop!( - EstateModule::collect_rent(RuntimeOrigin::signed(ALICE), 0u64, BENEFICIARY_ID), - Error::::NoPermission - ); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_noop!( - EstateModule::collect_rent(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, BOB), - Error::::LeaseDoesNotExist - ); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - run_to_block(22); - - assert_noop!( - EstateModule::collect_rent(RuntimeOrigin::signed(BENEFICIARY_ID), 0u64, ALICE), - Error::::LeaseIsExpired - ); - }); -} - -#[test] -fn collect_rent_should_work() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(EstateModule::mint_estate( - RuntimeOrigin::root(), - BENEFICIARY_ID, - METAVERSE_ID, - vec![COORDINATE_IN_1, COORDINATE_IN_2] - )); - - assert_ok!(EstateModule::create_lease_offer( - RuntimeOrigin::signed(ALICE), - 0u64, - 10u128, - 8u32 - )); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - let lease_contract = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 7, - start_block: 8, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), Some(lease_contract)); - - assert_ok!(EstateModule::accept_lease_offer( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateLeaseOfferAccepted(0, ALICE, 9)) - ); - - let mut lease = LeaseContract { - price_per_block: 10u128, - duration: 8u32, - end_block: 9, - start_block: 1, - unclaimed_rent: 80u128, - }; - - assert_eq!(EstateModule::leases(0u64), Some(lease.clone())); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); - - assert_eq!(EstateModule::lease_offers(0u64, ALICE), None); - - assert_eq!(Balances::free_balance(ALICE), 99920); - - run_to_block(4); - - assert_ok!(EstateModule::collect_rent( - RuntimeOrigin::signed(BENEFICIARY_ID), - 0u64, - ALICE - )); - - assert_eq!( - last_event(), - RuntimeEvent::Estate(crate::Event::EstateRentCollected(0, 30)) - ); - - lease.unclaimed_rent = 50u128; - - assert_eq!(EstateModule::leases(0u64), Some(lease)); - - assert_eq!(EstateModule::leasors(ALICE, 0u64), Some(())); - - assert_eq!(Balances::free_balance(ALICE), 99920); - assert_eq!(Balances::free_balance(BENEFICIARY_ID), 1000029); - }); +fn test_one() { + ExtBuilder::default().build().execute_with(|| assert_eq!(1, 1)); } From 15486b934e0fee712e047d1fb4416e217360a857 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 13 Nov 2023 17:33:24 +1300 Subject: [PATCH 060/155] WIP - Unit test - complete the first test run successful --- Cargo.lock | 1 + pallets/spp/Cargo.toml | 4 +++- pallets/spp/src/mock.rs | 18 +++++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6a4f98e6..39faaaf1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8025,6 +8025,7 @@ dependencies = [ name = "pallet-spp" version = "2.0.0-rc6" dependencies = [ + "asset-manager", "auction-manager", "bit-country-primitives", "core-primitives", diff --git a/pallets/spp/Cargo.toml b/pallets/spp/Cargo.toml index e6009caf7..917fae3f0 100644 --- a/pallets/spp/Cargo.toml +++ b/pallets/spp/Cargo.toml @@ -32,6 +32,7 @@ orml-tokens = { workspace = true } core-primitives = { path = "../../traits/core-primitives", default-features = false } primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } currencies = { package = "currencies", path = "../currencies", default-features = false } +asset-manager = { package = "asset-manager", path = "../asset-manager", default-features = false } [dependencies.auction-manager] default-features = false @@ -63,5 +64,6 @@ std = [ 'auction-manager/std', 'frame-benchmarking/std', 'orml-tokens/std', - "currencies/std" + "currencies/std", + "asset-manager/std" ] \ No newline at end of file diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 04adc6cce..608a255f5 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -14,6 +14,7 @@ use sp_std::collections::btree_map::BTreeMap; use sp_std::default::Default; use sp_std::vec::Vec; +use asset_manager::ForeignAssetMapping; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{CollectionType, NftClassData, TokenType}; use primitives::{ @@ -133,7 +134,7 @@ impl pallet_balances::Config for Runtime { parameter_types! { pub const GetNativeCurrencyId: FungibleTokenId = FungibleTokenId::NativeToken(0); pub const LandTreasuryPalletId: PalletId = PalletId(*b"bit/land"); - pub const LandTreasuryPalletId: PalletId = PalletId(*b"bit/land"); + pub const PoolAccountPalletId: PalletId = PalletId(*b"bit/pool"); pub const MinimumLandPrice: Balance = 10 * DOLLARS; } @@ -178,6 +179,12 @@ impl currencies::Config for Runtime { type WeightInfo = (); } +impl asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RegisterOrigin = EnsureSignedBy; +} + parameter_types! { pub const MinBlocksPerRound: u32 = 10; pub const MinimumStake: Balance = 200; @@ -190,6 +197,7 @@ parameter_types! { pub const MaxLeasePeriod: u32 = 9; pub const LeaseOfferExpiryPeriod: u32 = 6; pub StorageDepositFee: Balance = 1; + pub const MaximumQueue: u32 = 50; } impl Config for Runtime { @@ -203,9 +211,9 @@ impl Config for Runtime { type BlockNumberToBalance = ConvertInto; type StorageDepositFee = StorageDepositFee; type MultiCurrency = Currencies; - type PoolAccount = (); - type MaximumQueue = (); - type CurrencyIdConversion = (); + type PoolAccount = PoolAccountPalletId; + type MaximumQueue = MaximumQueue; + type CurrencyIdConversion = ForeignAssetMapping; } construct_runtime!( @@ -216,6 +224,7 @@ construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + AssetManager: asset_manager::{ Pallet, Storage, Call, Event}, Currencies: currencies::{ Pallet, Storage, Call, Event}, Tokens: orml_tokens::{Pallet, Call, Storage, Config, Event}, Spp: spp:: {Pallet, Call, Storage, Event}, @@ -263,7 +272,6 @@ pub fn last_event() -> RuntimeEvent { fn next_block() { SppModule::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); - SppModule::on_initialize(System::block_number()).unwrap(); } pub fn run_to_block(n: u64) { From 04deac1c3234487039600f1174d7af32d85719c2 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 10:45:15 +1300 Subject: [PATCH 061/155] WIP - Unit test - add ksm/rksm set up for alice and bob --- pallets/spp/src/mock.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 608a255f5..5754e0c85 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -236,7 +236,9 @@ pub type SppModule = Pallet; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; -pub struct ExtBuilder; +pub struct ExtBuilder { + endowed_accounts: Vec<(AccountId, FungibleTokenId, Balance)>, +} impl Default for ExtBuilder { fn default() -> Self { @@ -260,6 +262,18 @@ impl ExtBuilder { ext.execute_with(|| System::set_block_number(1)); ext } + + pub fn balances(mut self, endowed_accounts: Vec<(AccountId, FungibleTokenId, Balance)>) -> Self { + self.endowed_accounts = endowed_accounts; + self + } + + pub fn ksm_setup_for_alice_and_bob(self) -> Self { + self.balances(vec![ + (ALICE, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM + (BOB, FungibleTokenId::FungibleToken(1), 1000), //rKSM + ]) + } } pub fn last_event() -> RuntimeEvent { From e53dfbc4d90b89f31312b343b53db8707ff6052a Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 10:46:26 +1300 Subject: [PATCH 062/155] WIP - Unit test - add missing default endowed account --- pallets/spp/src/mock.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 5754e0c85..7a53632e8 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -242,7 +242,9 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { - ExtBuilder + Self { + endowed_accounts: vec![], + } } } From 2ce040e99fee5b66579ded97f4eabb9a9312c00e Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 11:02:28 +1300 Subject: [PATCH 063/155] WIP - Unit test - create ksm pool works --- pallets/spp/src/mock.rs | 8 +++++++- pallets/spp/src/tests.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 7a53632e8..d6dcfc4a0 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -255,7 +255,13 @@ impl ExtBuilder { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, 100000), (BOB, 100000), (BENEFICIARY_ID, 1000000)], + balances: vec![(ALICE, 1000000000), (BOB, 100000), (BENEFICIARY_ID, 1000000)], + } + .assimilate_storage(&mut t) + .unwrap(); + + orml_tokens::GenesisConfig:: { + balances: self.endowed_accounts.into_iter().collect::>(), } .assimilate_storage(&mut t) .unwrap(); diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 95eef7654..8ceff4d59 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -19,12 +19,42 @@ use frame_support::{assert_err, assert_noop, assert_ok}; use sp_runtime::traits::BadOrigin; +use sp_runtime::{Perbill, Permill}; use mock::{RuntimeEvent, *}; +use crate::utils::PoolInfo; + use super::*; #[test] fn test_one() { ExtBuilder::default().build().execute_with(|| assert_eq!(1, 1)); } + +#[test] +fn create_ksm_pool_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Permill::from_percent(5) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 1); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Permill::from_percent(5), + currency_id: FungibleTokenId::NativeToken(1), + max: 50 + } + ) + }); +} From d1d01b05a47ee9de0e27a07098c63850ad38264e Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 11:28:29 +1300 Subject: [PATCH 064/155] WIP - Implement logic of first deposit and second deposit scenario --- pallets/spp/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 323cc4216..3ffdcbda2 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -341,12 +341,16 @@ pub mod pallet { // Calculate rAmount as receipt of amount locked. The formula based on rAmount = (amount * rAmount // total issuance)/network ledger balance let r_amount_total_issuance = T::MultiCurrency::total_issuance(r_currency_id); - let r_amount = U256::from(amount_after_fee.saturated_into::()) - .saturating_mul(r_amount_total_issuance.saturated_into::().into()) - .checked_div(network_ledger_balance.saturated_into::().into()) - .ok_or(Error::::ArithmeticOverflow)? - .as_u128() - .saturated_into(); + let mut r_amount = amount_after_fee; + // This will apply for subsequence deposits - the first deposit r_amount = amount_after_fee + if network_ledger_balance != BalanceOf::::zero() { + r_amount = U256::from(amount_after_fee.saturated_into::()) + .saturating_mul(r_amount_total_issuance.saturated_into::().into()) + .checked_div(network_ledger_balance.saturated_into::().into()) + .ok_or(Error::::ArithmeticOverflow)? + .as_u128() + .saturated_into(); + } // Deposit rAmount to user using T::MultiCurrency::deposit T::MultiCurrency::deposit(currency_id, &who, r_amount)?; From bb9f9a17be0bdf88e704d2428cbf4128246986bf Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 11:46:14 +1300 Subject: [PATCH 065/155] WIP - Update correct reciept currency id --- pallets/spp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 3ffdcbda2..4069227d2 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -353,7 +353,7 @@ pub mod pallet { } // Deposit rAmount to user using T::MultiCurrency::deposit - T::MultiCurrency::deposit(currency_id, &who, r_amount)?; + T::MultiCurrency::deposit(r_currency_id, &who, r_amount)?; // Update this specific pool ledger to keep track of pool balance PoolLedger::::mutate(&pool_id, |pool| -> Result<(), Error> { From adc62fab9530f5f0b3bec3f38fb3acd60af12c48 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 11:46:28 +1300 Subject: [PATCH 066/155] WIP - Unit test - Deposit works --- pallets/spp/src/mock.rs | 2 +- pallets/spp/src/tests.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index d6dcfc4a0..ca2bd5f1f 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -279,7 +279,7 @@ impl ExtBuilder { pub fn ksm_setup_for_alice_and_bob(self) -> Self { self.balances(vec![ (ALICE, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM - (BOB, FungibleTokenId::FungibleToken(1), 1000), //rKSM + (BOB, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM//rKSM ]) } } diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 8ceff4d59..4df4d4236 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -58,3 +58,35 @@ fn create_ksm_pool_works() { ) }); } + +#[test] +fn deposit_ksm_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Permill::from_percent(5) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 1); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Permill::from_percent(5), + currency_id: FungibleTokenId::NativeToken(1), + max: 50 + } + ); + + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + let r_ksm_amount = Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free; + // This is true because fee hasn't been set up. + assert_eq!(r_ksm_amount, 10000) + }); +} From 6b442d01b58cdd13808e406840ffae12a33690d1 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 12:43:30 +1300 Subject: [PATCH 067/155] WIP - Unit test - All deposit works --- pallets/spp/src/mock.rs | 2 +- pallets/spp/src/tests.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index ca2bd5f1f..b90291f6b 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -279,7 +279,7 @@ impl ExtBuilder { pub fn ksm_setup_for_alice_and_bob(self) -> Self { self.balances(vec![ (ALICE, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM - (BOB, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM//rKSM + (BOB, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM ]) } } diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 4df4d4236..bc60ae553 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -85,8 +85,17 @@ fn deposit_ksm_works() { ); assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); - let r_ksm_amount = Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free; // This is true because fee hasn't been set up. - assert_eq!(r_ksm_amount, 10000) + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + + assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); + + // Deposit another 10000 KSM + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); + + assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); }); } From 137ce7b51e7b7d7229c48a7655856229d54ad7cc Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 14:07:34 +1300 Subject: [PATCH 068/155] WIP - Unit test - Redeem rksm works. --- pallets/spp/src/tests.rs | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index bc60ae553..29d37709a 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -99,3 +99,69 @@ fn deposit_ksm_works() { assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); }); } + +#[test] +fn redeem_rksm_request_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Permill::from_percent(5) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 1); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Permill::from_percent(5), + currency_id: FungibleTokenId::NativeToken(1), + max: 50 + } + ); + + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + // This is true because fee hasn't been set up. + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + + assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); + + // Deposit another 10000 KSM + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); + + assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); + + assert_noop!( + SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(1), 10000), + Error::::PoolDoesNotExist + ); + + assert_noop!( + SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(0), 10000), + Error::::CurrencyIsNotSupported + ); + + assert_noop!( + SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(1), 10000), + Error::::NoCurrentStakingRound + ); + + UnlockDuration::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); + // Bump current staking round to 1 + CurrentStakingRound::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); + assert_ok!(SppModule::redeem( + RuntimeOrigin::signed(BOB), + 0, + FungibleTokenId::FungibleToken(1), + 10000 + )); + }); +} From 593a12e15dafdf9cc5da2faf056f9555f6c8da85 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 18:44:08 +1300 Subject: [PATCH 069/155] WIP - Unit test - ledger reduce after user redeem --- pallets/spp/src/tests.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 29d37709a..c3b246520 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -163,5 +163,8 @@ fn redeem_rksm_request_works() { FungibleTokenId::FungibleToken(1), 10000 )); + + // After Bob redeems, pool ledger 0 should have only 10000 + assert_eq!(PoolLedger::::get(0), 10000); }); } From 10b905f37c244f256f6523da3fc864ee49474be8 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 18:44:18 +1300 Subject: [PATCH 070/155] WIP - Update correct redeem logic --- pallets/spp/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 4069227d2..e66f96763 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -417,11 +417,8 @@ pub mod pallet { let network_ledger_balance = Self::network_ledger(currency_id); // Collect deposit fee for protocol - // Assuming there's a function `collect_redeem_fee` that deducts a fee for deposits. let amount_after_fee = Self::collect_redeem_fee(&who, v_currency_id, r_amount)?; - let r_amount = r_amount - .checked_sub(&amount_after_fee) - .ok_or(Error::::ArithmeticOverflow)?; + let r_amount = amount_after_fee; // Calculate rAmount as receipt of amount locked. The formula based on rAmount = (amount * rAmount // total issuance)/network ledger balance let r_amount_total_issuance = T::MultiCurrency::total_issuance(v_currency_id); From 8e7fc4eab87656ee94f3d7f4682f7daaf52d45d8 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 19:14:39 +1300 Subject: [PATCH 071/155] WIP - Unit test - redeem queue system works --- pallets/spp/src/tests.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index c3b246520..97b0692bb 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -166,5 +166,18 @@ fn redeem_rksm_request_works() { // After Bob redeems, pool ledger 0 should have only 10000 assert_eq!(PoolLedger::::get(0), 10000); + + // Verify if redeem queue has requests + + let queue_id = QueueNextId::::get(FungibleTokenId::NativeToken(1)); + assert_eq!(queue_id, 1); + let mut queue_items = BoundedVec::default(); + assert_ok!(queue_items.try_push(0)); + let user_redeem_queue = UserCurrencyRedeemQueue::::get(BOB, FungibleTokenId::NativeToken(1)); + let currency_redeem_queue = CurrencyRedeemQueue::::get(FungibleTokenId::NativeToken(1), queue_id); + let staking_round_redeem_queue = + StakingRoundRedeemQueue::::get(StakingRound::Era(1), FungibleTokenId::NativeToken(1)); + assert_eq!(user_redeem_queue, Some(10000, queue_items)); + assert_eq!(currency_redeem_queue, Some(BOB, 10000, StakingRound::Era(1))); }); } From a4923d8345e4a4ca4d7141fd187152ca8843b6fa Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 21:31:24 +1300 Subject: [PATCH 072/155] WIP - Unit test - redeem queues added as expected --- pallets/spp/src/tests.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 97b0692bb..9cb1b9e86 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -174,10 +174,17 @@ fn redeem_rksm_request_works() { let mut queue_items = BoundedVec::default(); assert_ok!(queue_items.try_push(0)); let user_redeem_queue = UserCurrencyRedeemQueue::::get(BOB, FungibleTokenId::NativeToken(1)); - let currency_redeem_queue = CurrencyRedeemQueue::::get(FungibleTokenId::NativeToken(1), queue_id); + let currency_redeem_queue = CurrencyRedeemQueue::::get(FungibleTokenId::NativeToken(1), 0); let staking_round_redeem_queue = - StakingRoundRedeemQueue::::get(StakingRound::Era(1), FungibleTokenId::NativeToken(1)); - assert_eq!(user_redeem_queue, Some(10000, queue_items)); - assert_eq!(currency_redeem_queue, Some(BOB, 10000, StakingRound::Era(1))); + StakingRoundRedeemQueue::::get(StakingRound::Era(2), FungibleTokenId::NativeToken(1)); + // Verify if user redeem queue has total unlocked and queue items + assert_eq!(user_redeem_queue, Some((10000, queue_items.clone()))); + // If user redeem of Era 1, fund will be released at Era 2 + assert_eq!(currency_redeem_queue, Some((BOB, 10000, StakingRound::Era(2)))); + // Redeem added into staking round redeem queue for Era 2 + assert_eq!( + staking_round_redeem_queue, + Some((10000, queue_items.clone(), FungibleTokenId::NativeToken(1))) + ); }); } From d93a13ca4cf7a5c68f2eedfbccb4e0d491759309 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 23:00:09 +1300 Subject: [PATCH 073/155] WIP - Add relaychain block number provider trait to update relaychain block every hook --- pallets/spp/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index e66f96763..51ba6b407 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -56,7 +56,7 @@ pub mod pallet { use frame_support::traits::{Currency, ReservableCurrency}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; - use sp_runtime::traits::{CheckedAdd, CheckedSub}; + use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedSub}; use sp_runtime::Permill; use primitives::{PoolId, StakingRound}; @@ -109,6 +109,9 @@ pub mod pallet { /// Allows converting block numbers into balance type BlockNumberToBalance: Convert>; + /// Block number provider for the relaychain. + type RelayChainBlockNumber: BlockNumberProvider>; + #[pallet::constant] type PoolAccount: Get; @@ -198,6 +201,16 @@ pub mod pallet { #[pallet::getter(fn last_staking_round)] pub type LastStakingRound = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound, ValueQuery>; + /// The relaychain block number of last staking round + #[pallet::storage] + #[pallet::getter(fn last_staking_round_updated_block)] + pub type LastEraUpdatedBlock = StorageValue<_, BlockNumberFor, ValueQuery>; + + /// The internal of relaychain block number between era. + #[pallet::storage] + #[pallet::getter(fn update_staking_round_frequency)] + pub type UpdateEraFrequency = StorageValue<_, BlockNumberFor, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn queue_next_id)] pub type QueueNextId = StorageMap<_, Twox64Concat, FungibleTokenId, u32, ValueQuery>; From 4b51aa37442f704b8f171f534292ae6af63bbc1a Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 23:23:20 +1300 Subject: [PATCH 074/155] WIP - Implement relaychain hooks to update era index and handle redeem requests --- pallets/spp/src/lib.rs | 68 ++++++++++++++++++++++++++------- primitives/metaverse/src/lib.rs | 2 + 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 51ba6b407..758609711 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -27,7 +27,7 @@ use frame_support::{ use frame_system::ensure_signed; use frame_system::pallet_prelude::*; use orml_traits::MultiCurrency; -use sp_runtime::traits::{CheckedAdd, CheckedSub}; +use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedSub}; use sp_runtime::{ traits::{AccountIdConversion, Convert, Saturating, Zero}, ArithmeticError, DispatchError, SaturatedConversion, @@ -35,7 +35,7 @@ use sp_runtime::{ use core_primitives::*; pub use pallet::*; -use primitives::{ClassId, FungibleTokenId, StakingRound, TokenId}; +use primitives::{ClassId, EraIndex, EraIndex, FungibleTokenId, StakingRound, TokenId}; pub use weights::WeightInfo; pub type QueueId = u32; @@ -197,18 +197,25 @@ pub mod pallet { #[pallet::getter(fn current_staking_round)] pub type CurrentStakingRound = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound>; + /// The current era of relaychain + /// + /// RelayChainCurrentEra : EraIndex + #[pallet::storage] + #[pallet::getter(fn relay_chain_current_era)] + pub type RelayChainCurrentEra = StorageValue<_, EraIndex, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn last_staking_round)] pub type LastStakingRound = StorageMap<_, Twox64Concat, FungibleTokenId, StakingRound, ValueQuery>; /// The relaychain block number of last staking round #[pallet::storage] - #[pallet::getter(fn last_staking_round_updated_block)] + #[pallet::getter(fn last_era_updated_block)] pub type LastEraUpdatedBlock = StorageValue<_, BlockNumberFor, ValueQuery>; /// The internal of relaychain block number between era. #[pallet::storage] - #[pallet::getter(fn update_staking_round_frequency)] + #[pallet::getter(fn update_era_frequency)] pub type UpdateEraFrequency = StorageValue<_, BlockNumberFor, ValueQuery>; #[pallet::storage] @@ -248,6 +255,7 @@ pub mod pallet { token_amount: BalanceOf, }, } + #[pallet::error] pub enum Error { /// No permission @@ -284,6 +292,19 @@ pub mod pallet { CurrencyRedeemQueueNotFound, } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + let era_number = Self::get_era_index(T::RelayChainBlockNumber::current_block_number()); + if !era_number.is_zero() { + let _ = Self::update_current_era(era_number); + Self::handle_redeem_requests().map_err(|err| {}).ok(); + } + + T::WeightInfo::on_initialize() + } + } + #[pallet::call] impl Pallet { #[pallet::weight(T::WeightInfo::mint_land())] @@ -630,15 +651,7 @@ impl Pallet { ) } - #[transactional] - fn on_initialize() -> DispatchResult { - for currency in CurrentStakingRound::::iter_keys() { - Self::handle_redeem_staking_round_hook(currency)?; - } - Ok(()) - } - - fn handle_redeem_staking_round_hook(currency: FungibleTokenId) -> DispatchResult { + fn handle_update_staking_round(currency: FungibleTokenId) -> DispatchResult { let last_staking_round = LastStakingRound::::get(currency); let unlock_duration = match UnlockDuration::::get(currency) { Some(StakingRound::Era(unlock_duration_era)) => unlock_duration_era, @@ -873,4 +886,33 @@ impl Pallet { }); Ok(()) } + + pub fn get_era_index(relaychain_block_number: BlockNumberFor) -> EraIndex { + relaychain_block_number + .checked_sub(&Self::last_era_updated_block()) + .and_then(|n| n.checked_div(&Self::update_era_frequency())) + .and_then(|n| TryInto::::try_into(n).ok()) + .unwrap_or_else(Zero::zero) + } + + #[transactional] + fn handle_redeem_requests() -> DispatchResult { + for currency in CurrentStakingRound::::iter_keys() { + Self::handle_update_staking_round(currency)?; + } + Ok(()) + } + + pub fn update_current_era(era_index: EraIndex) -> DispatchResult { + let previous_era = Self::relay_chain_current_era(); + let new_era = previous_era.saturating_add(era_index); + + RelayChainCurrentEra::::put(new_era); + LastEraUpdatedBlock::::put(T::RelayChainBlockNumber::current_block_number()); + Self::deposit_event(Event::::CurrentEraUpdated { new_era_index: new_era }); + + Self::handle_redeem_requests()?; + + Ok(()) + } } diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index 5b2e39ea3..49fe3ce07 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -104,6 +104,8 @@ pub type ReferendumId = u64; pub type LandId = u64; /// EstateId pub type EstateId = u64; +/// Number of era on relaychain +pub type EraIndex = u32; /// Social Token Id type pub type TokenId = u64; /// Undeployed LandBlock Id type From 5a59cc238a17355b85544ba4823ebc338bea1294 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 Nov 2023 23:35:27 +1300 Subject: [PATCH 075/155] WIP - Fix build issue --- pallets/spp/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 758609711..43acad2bd 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -35,7 +35,7 @@ use sp_runtime::{ use core_primitives::*; pub use pallet::*; -use primitives::{ClassId, EraIndex, EraIndex, FungibleTokenId, StakingRound, TokenId}; +use primitives::{ClassId, EraIndex, FungibleTokenId, StakingRound, TokenId}; pub use weights::WeightInfo; pub type QueueId = u32; @@ -254,6 +254,8 @@ pub mod pallet { to: T::AccountId, token_amount: BalanceOf, }, + /// Current era updated + CurrentEraUpdated { new_era_index: EraIndex }, } #[pallet::error] From a5b961371d4bea2b580cbf34b5e7b5428dec40b8 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 15 Nov 2023 10:23:46 +1300 Subject: [PATCH 076/155] WIP - Add era config set up function allow council set up the first era config --- pallets/spp/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++++ pallets/spp/src/tests.rs | 3 +++ 2 files changed, 43 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 43acad2bd..d22c369cf 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -119,6 +119,9 @@ pub mod pallet { type MaximumQueue: Get; type CurrencyIdConversion: CurrencyIdManagement; + + /// Origin represented Governance + type GovernanceOrigin: EnsureOrigin<::RuntimeOrigin>; } pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -256,6 +259,10 @@ pub mod pallet { }, /// Current era updated CurrentEraUpdated { new_era_index: EraIndex }, + /// Last era updated + LastEraUpdated { last_era_block: BlockNumberFor }, + /// Update era frequency + UpdateEraFrequency { frequency: BlockNumberFor }, } #[pallet::error] @@ -292,6 +299,8 @@ pub mod pallet { UserCurrencyRedeemQueueNotFound, /// Redeem queue per currency not found CurrencyRedeemQueueNotFound, + /// The last era updated block is invalid + InvalidLastEraUpdatedBlock, } #[pallet::hooks] @@ -571,6 +580,37 @@ pub mod pallet { }); Ok(().into()) } + + #[pallet::weight(< T as Config >::WeightInfo::mint_land())] + pub fn update_era_config( + origin: OriginFor, + last_era_updated_block: Option>, + frequency: Option>, + ) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; + + if let Some(change) = frequency { + UpdateEraFrequency::::put(change); + Self::deposit_event(Event::::UpdateEraFrequency { frequency: change }); + } + + if let Some(change) = last_era_updated_block { + let update_era_frequency = UpdateEraFrequency::::get(); + let current_relay_chain_block = T::RelayChainBlockNumber::current_block_number(); + if !update_era_frequency.is_zero() { + ensure!( + change > current_relay_chain_block.saturating_sub(update_era_frequency) + && change <= current_relay_chain_block, + Error::::InvalidLastEraUpdatedBlock + ); + + LastEraUpdatedBlock::::put(change); + Self::deposit_event(Event::::LastEraUpdated { last_era_block: change }); + } + } + + Ok(()) + } } } diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 9cb1b9e86..1610e0b6a 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -188,3 +188,6 @@ fn redeem_rksm_request_works() { ); }); } + +#[test] +fn current_era_update_works() {} From 38761a7f1824e16b64181dab0580c1b4e902abf9 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 15 Nov 2023 10:50:16 +1300 Subject: [PATCH 077/155] WIP - Fix add missing library into command node setup. --- node/src/command.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/src/command.rs b/node/src/command.rs index 6c77ee62c..c2be223b2 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -19,7 +19,7 @@ use std::{io::Write, net::SocketAddr}; use codec::Encode; use cumulus_client_cli::generate_genesis_block; - +use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::info; use sc_cli::{ @@ -29,6 +29,7 @@ use sc_cli::{ use sc_service::config::{BasePath, PrometheusConfig}; use sc_service::PartialComponents; use sp_core::hexdisplay::HexDisplay; +use sp_runtime::traits::AccountIdConversion; use sp_runtime::traits::Block as BlockT; #[cfg(feature = "with-continuum-runtime")] From 41c466f7f1cfce0c6eee0cb0321606fb4258c4f4 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 15 Nov 2023 10:50:48 +1300 Subject: [PATCH 078/155] WIP - Implement mock relaychain block number --- pallets/spp/src/lib.rs | 2 ++ pallets/spp/src/mock.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index d22c369cf..a28d007b2 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -581,6 +581,8 @@ pub mod pallet { Ok(().into()) } + /// This function only for governance origin to execute when starting the protocol or + /// changes of era duration. #[pallet::weight(< T as Config >::WeightInfo::mint_land())] pub fn update_era_config( origin: OriginFor, diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index b90291f6b..c980d0bf4 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -74,6 +74,7 @@ pub const GENERAL_METAVERSE_FUND: AccountId = 102; ord_parameter_types! { pub const One: AccountId = ALICE; + pub const Admin: AccountId = ALICE; } // Configure a mock runtime to test the pallet. @@ -185,6 +186,14 @@ impl asset_manager::Config for Runtime { type RegisterOrigin = EnsureSignedBy; } +impl BlockNumberProvider for MockRelayBlockNumberProvider { + type BlockNumber = BlockNumber; + + fn current_block_number() -> Self::BlockNumber { + Self::get() + } +} + parameter_types! { pub const MinBlocksPerRound: u32 = 10; pub const MinimumStake: Balance = 200; @@ -198,6 +207,7 @@ parameter_types! { pub const LeaseOfferExpiryPeriod: u32 = 6; pub StorageDepositFee: Balance = 1; pub const MaximumQueue: u32 = 50; + pub static MockRelayBlockNumberProvider: BlockNumber = 0; } impl Config for Runtime { @@ -214,6 +224,8 @@ impl Config for Runtime { type PoolAccount = PoolAccountPalletId; type MaximumQueue = MaximumQueue; type CurrencyIdConversion = ForeignAssetMapping; + type RelayChainBlockNumber = MockRelayBlockNumberProvider; + type GovernanceOrigin = EnsureSignedBy; } construct_runtime!( From 70dd1625b083cff29a9e6659fc9f22c1659c6bc1 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 15 Nov 2023 21:32:52 +1300 Subject: [PATCH 079/155] WIP - Implement redeem request distribute funds correctly every era --- pallets/spp/src/lib.rs | 24 ++++----- pallets/spp/src/tests.rs | 114 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 15 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index a28d007b2..f146430df 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -301,6 +301,8 @@ pub mod pallet { CurrencyRedeemQueueNotFound, /// The last era updated block is invalid InvalidLastEraUpdatedBlock, + /// Fail to process redeem requests + FailedToProcessRedemption, } #[pallet::hooks] @@ -309,7 +311,7 @@ pub mod pallet { let era_number = Self::get_era_index(T::RelayChainBlockNumber::current_block_number()); if !era_number.is_zero() { let _ = Self::update_current_era(era_number); - Self::handle_redeem_requests().map_err(|err| {}).ok(); + Self::handle_redeem_requests(era_number).map_err(|err| {}).ok(); } T::WeightInfo::on_initialize() @@ -588,6 +590,7 @@ pub mod pallet { origin: OriginFor, last_era_updated_block: Option>, frequency: Option>, + last_staking_round: StakingRound, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -607,6 +610,7 @@ pub mod pallet { ); LastEraUpdatedBlock::::put(change); + LastStakingRound::::insert(FungibleTokenId::NativeToken(1), last_staking_round); Self::deposit_event(Event::::LastEraUpdated { last_era_block: change }); } } @@ -695,7 +699,7 @@ impl Pallet { ) } - fn handle_update_staking_round(currency: FungibleTokenId) -> DispatchResult { + fn handle_update_staking_round(era_index: EraIndex, currency: FungibleTokenId) -> DispatchResult { let last_staking_round = LastStakingRound::::get(currency); let unlock_duration = match UnlockDuration::::get(currency) { Some(StakingRound::Era(unlock_duration_era)) => unlock_duration_era, @@ -705,13 +709,7 @@ impl Pallet { _ => 0, }; - let current_staking_round = match CurrentStakingRound::::get(currency) { - Some(StakingRound::Era(unlock_duration_era)) => unlock_duration_era, - Some(StakingRound::Round(unlock_duration_round)) => unlock_duration_round, - Some(StakingRound::Epoch(unlock_duration_epoch)) => unlock_duration_epoch, - Some(StakingRound::Hour(unlock_duration_hour)) => unlock_duration_hour, - _ => 0, - }; + let current_staking_round = era_index; // Check current staking round queue with last staking round if there is any pending redeem requests if let Some((_total_locked, existing_queue, currency_id)) = @@ -940,9 +938,9 @@ impl Pallet { } #[transactional] - fn handle_redeem_requests() -> DispatchResult { + fn handle_redeem_requests(era_index: EraIndex) -> DispatchResult { for currency in CurrentStakingRound::::iter_keys() { - Self::handle_update_staking_round(currency)?; + Self::handle_update_staking_round(era_index, currency)?; } Ok(()) } @@ -953,10 +951,8 @@ impl Pallet { RelayChainCurrentEra::::put(new_era); LastEraUpdatedBlock::::put(T::RelayChainBlockNumber::current_block_number()); + Self::handle_redeem_requests(new_era)?; Self::deposit_event(Event::::CurrentEraUpdated { new_era_index: new_era }); - - Self::handle_redeem_requests()?; - Ok(()) } } diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 1610e0b6a..026a3e318 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -190,4 +190,116 @@ fn redeem_rksm_request_works() { } #[test] -fn current_era_update_works() {} +fn current_era_update_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + assert_eq!(SppModule::last_era_updated_block(), 0); + assert_eq!(SppModule::update_era_frequency(), 0); + assert_eq!(MockRelayBlockNumberProvider::current_block_number(), 0); + // Current relaychain block is 102. + MockRelayBlockNumberProvider::set(102); + RelayChainCurrentEra::::put(1); + IterationLimit::::put(50); + // The correct set up era config is the last era block records is 101 with duration is 100 blocks + assert_ok!(SppModule::update_era_config( + RuntimeOrigin::signed(Admin::get()), + Some(101), + Some(100), + StakingRound::Era(1), + )); + + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Permill::from_percent(5) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 1); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Permill::from_percent(5), + currency_id: FungibleTokenId::NativeToken(1), + max: 50, + } + ); + + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + // This is true because fee hasn't been set up. + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + + assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); + + // Deposit another 10000 KSM + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); + + assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); + + // Pool summary + // Pool Total deposited: 20000 + // Network deposited: 20000, NativeToken(1) + + // Bob summary + // Holding: 20000 FungibleToken(1) reciept token of NativeToken(1) + + assert_noop!( + SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(1), 10000), + Error::::PoolDoesNotExist + ); + + assert_noop!( + SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(0), 10000), + Error::::CurrencyIsNotSupported + ); + + assert_noop!( + SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(1), 10000), + Error::::NoCurrentStakingRound + ); + + UnlockDuration::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); // Bump current staking round to 1 + CurrentStakingRound::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); + + // Bob successfully redeemed + assert_ok!(SppModule::redeem( + RuntimeOrigin::signed(BOB), + 0, + FungibleTokenId::FungibleToken(1), + 10000 + )); + + // After Bob redeems, pool ledger 0 should have only 10000 + assert_eq!(PoolLedger::::get(0), 10000); + + // Verify if redeem queue has requests + let queue_id = QueueNextId::::get(FungibleTokenId::NativeToken(1)); + assert_eq!(queue_id, 1); + let mut queue_items = BoundedVec::default(); + assert_ok!(queue_items.try_push(0)); + let user_redeem_queue = UserCurrencyRedeemQueue::::get(BOB, FungibleTokenId::NativeToken(1)); + let currency_redeem_queue = CurrencyRedeemQueue::::get(FungibleTokenId::NativeToken(1), 0); + let staking_round_redeem_queue = + StakingRoundRedeemQueue::::get(StakingRound::Era(2), FungibleTokenId::NativeToken(1)); + // Verify if user redeem queue has total unlocked and queue items + assert_eq!(user_redeem_queue, Some((10000, queue_items.clone()))); + // If user redeem of Era 1, fund will be released at Era 2 + assert_eq!(currency_redeem_queue, Some((BOB, 10000, StakingRound::Era(2)))); + // Redeem added into staking round redeem queue for Era 2 + assert_eq!( + staking_round_redeem_queue, + Some((10000, queue_items.clone(), FungibleTokenId::NativeToken(1))) + ); + + // Move to era 2 to allow user redeem token successfully + MockRelayBlockNumberProvider::set(202); + SppModule::on_initialize(100); + }); +} From 2a761558f01952dc926256ebbc7076747220a810 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 16 Nov 2023 17:38:20 +1300 Subject: [PATCH 080/155] WIP - Fix duplicate queue processing requests --- pallets/spp/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index f146430df..01a786b79 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -310,8 +310,7 @@ pub mod pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { let era_number = Self::get_era_index(T::RelayChainBlockNumber::current_block_number()); if !era_number.is_zero() { - let _ = Self::update_current_era(era_number); - Self::handle_redeem_requests(era_number).map_err(|err| {}).ok(); + let _ = Self::update_current_era(era_number).map_err(|err| err).ok(); } T::WeightInfo::on_initialize() @@ -700,7 +699,7 @@ impl Pallet { } fn handle_update_staking_round(era_index: EraIndex, currency: FungibleTokenId) -> DispatchResult { - let last_staking_round = LastStakingRound::::get(currency); + let last_staking_round = StakingRound::Era(era_index as u32); let unlock_duration = match UnlockDuration::::get(currency) { Some(StakingRound::Era(unlock_duration_era)) => unlock_duration_era, Some(StakingRound::Round(unlock_duration_round)) => unlock_duration_round, @@ -937,7 +936,6 @@ impl Pallet { .unwrap_or_else(Zero::zero) } - #[transactional] fn handle_redeem_requests(era_index: EraIndex) -> DispatchResult { for currency in CurrentStakingRound::::iter_keys() { Self::handle_update_staking_round(era_index, currency)?; @@ -945,6 +943,7 @@ impl Pallet { Ok(()) } + #[transactional] pub fn update_current_era(era_index: EraIndex) -> DispatchResult { let previous_era = Self::relay_chain_current_era(); let new_era = previous_era.saturating_add(era_index); @@ -955,4 +954,8 @@ impl Pallet { Self::deposit_event(Event::::CurrentEraUpdated { new_era_index: new_era }); Ok(()) } + + pub fn get_pool_account() -> T::AccountId { + (T::PoolAccount::get().into_account_truncating(),) + } } From a756d00df1a4a46ea324855db53990aee9020805 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 16 Nov 2023 18:09:26 +1300 Subject: [PATCH 081/155] WIP - Add function get pool account --- pallets/spp/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 01a786b79..bb8909ae4 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -955,7 +955,7 @@ impl Pallet { Ok(()) } - pub fn get_pool_account() -> T::AccountId { - (T::PoolAccount::get().into_account_truncating(),) + pub fn get_pool_account() -> T::AccountId { + T::PoolAccount::get().into_account_truncating() } } From be05516d4dd69fa15e41315691bd2eb73fbc8a0c Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 16 Nov 2023 18:09:53 +1300 Subject: [PATCH 082/155] Complete unit test on successfully redeem native token after era changes --- pallets/spp/src/mock.rs | 4 ++-- pallets/spp/src/tests.rs | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index c980d0bf4..d2ed479bf 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -290,8 +290,8 @@ impl ExtBuilder { pub fn ksm_setup_for_alice_and_bob(self) -> Self { self.balances(vec![ - (ALICE, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM - (BOB, FungibleTokenId::NativeToken(1), 1000000000000000000000), //KSM + (ALICE, FungibleTokenId::NativeToken(1), 20000), //KSM + (BOB, FungibleTokenId::NativeToken(1), 20000), //KSM ]) } } diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 026a3e318..2001395a1 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -228,10 +228,13 @@ fn current_era_update_works() { max: 50, } ); - + // Verify BOB account with 20000 KSM + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 20000); assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); // This is true because fee hasn't been set up. assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + // Bob KSM balance become 10000 + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 10000); assert_eq!(PoolLedger::::get(0), 10000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); @@ -239,6 +242,8 @@ fn current_era_update_works() { // Deposit another 10000 KSM assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); + // Bob KSM now is 0 + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 0); assert_eq!(PoolLedger::::get(0), 20000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); @@ -279,6 +284,9 @@ fn current_era_update_works() { // After Bob redeems, pool ledger 0 should have only 10000 assert_eq!(PoolLedger::::get(0), 10000); + // After Bob redeem, make sure BOB KSM balance remains the same as it will only released next era + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 0); + // Verify if redeem queue has requests let queue_id = QueueNextId::::get(FungibleTokenId::NativeToken(1)); assert_eq!(queue_id, 1); @@ -299,7 +307,29 @@ fn current_era_update_works() { ); // Move to era 2 to allow user redeem token successfully + LastStakingRound::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(0)); MockRelayBlockNumberProvider::set(202); - SppModule::on_initialize(100); + SppModule::on_initialize(200); + + let pool_account = SppModule::get_pool_account(); + assert_eq!( + Tokens::accounts(pool_account, FungibleTokenId::NativeToken(1)).free, + 10001 + ); + + // After KSM released, BOB balance now is + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 10000); + assert_eq!( + CurrencyRedeemQueue::::get(FungibleTokenId::NativeToken(1), 0), + None + ); + assert_eq!( + UserCurrencyRedeemQueue::::get(BOB, FungibleTokenId::NativeToken(1)), + None + ); + assert_eq!( + StakingRoundRedeemQueue::::get(StakingRound::Era(2), FungibleTokenId::NativeToken(1)), + None + ); }); } From 7b9f67a211cea96f0f48934656eb52b87dfa4ac2 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 16 Nov 2023 18:19:54 +1300 Subject: [PATCH 083/155] More test cases to avoid double distribution --- pallets/spp/src/tests.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 2001395a1..d5c383807 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -307,7 +307,6 @@ fn current_era_update_works() { ); // Move to era 2 to allow user redeem token successfully - LastStakingRound::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(0)); MockRelayBlockNumberProvider::set(202); SppModule::on_initialize(200); @@ -331,5 +330,34 @@ fn current_era_update_works() { StakingRoundRedeemQueue::::get(StakingRound::Era(2), FungibleTokenId::NativeToken(1)), None ); + + // Move to era 3, make sure no double redeem process + MockRelayBlockNumberProvider::set(302); + SppModule::on_initialize(300); + + // Pool account remain the same + assert_eq!( + Tokens::accounts(pool_account, FungibleTokenId::NativeToken(1)).free, + 10001 + ); + + // BOB balance remain the same + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 10000); + assert_eq!( + CurrencyRedeemQueue::::get(FungibleTokenId::NativeToken(1), 0), + None + ); + assert_eq!( + UserCurrencyRedeemQueue::::get(BOB, FungibleTokenId::NativeToken(1)), + None + ); + assert_eq!( + StakingRoundRedeemQueue::::get(StakingRound::Era(2), FungibleTokenId::NativeToken(1)), + None + ); + assert_eq!( + StakingRoundRedeemQueue::::get(StakingRound::Era(3), FungibleTokenId::NativeToken(1)), + None + ); }); } From baf901acc97e53c465b976bd069618d13ac1aecf Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 17 Nov 2023 12:54:12 +1300 Subject: [PATCH 084/155] WIP - add boosting conviction and boost info data type --- pallets/spp/src/utils.rs | 120 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index bec2e5c66..e8fb8fc59 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -19,7 +19,10 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_runtime::{Permill, RuntimeDebug}; +use sp_runtime::{ + traits::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, IntegerSquareRoot, Saturating, Zero}, + Permill, RuntimeDebug, +}; use primitives::FungibleTokenId; @@ -35,3 +38,118 @@ pub struct PoolInfo { /// Max nft rewards pub max: u32, } + +/// Amount of votes and capital placed in delegation for an account. +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BoostingDelegations { + /// The number of votes (this is post-conviction). + pub votes: Balance, + /// The amount of raw capital, used for the turnout. + pub capital: Balance, +} + +/// A value denoting the strength of conviction of a vote. +#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo)] +pub enum BoostingConviction { + /// 0.1x votes, unlocked. + None, + /// 1x votes, locked for an enactment period following a successful vote. + Locked1x, + /// 2x votes, locked for 2x enactment periods following a successful vote. + Locked2x, + /// 3x votes, locked for 4x... + Locked3x, + /// 4x votes, locked for 8x... + Locked4x, + /// 5x votes, locked for 16x... + Locked5x, + /// 6x votes, locked for 32x... + Locked6x, +} + +impl Default for BoostingConviction { + fn default() -> Self { + BoostingConviction::None + } +} + +impl From for u8 { + fn from(c: BoostingConviction) -> u8 { + match c { + BoostingConviction::None => 0, + BoostingConviction::Locked1x => 1, + BoostingConviction::Locked2x => 2, + BoostingConviction::Locked3x => 3, + BoostingConviction::Locked4x => 4, + BoostingConviction::Locked5x => 5, + BoostingConviction::Locked6x => 6, + } + } +} + +impl TryFrom for BoostingConviction { + type Error = (); + fn try_from(i: u8) -> Result { + Ok(match i { + 0 => BoostingConviction::None, + 1 => BoostingConviction::Locked1x, + 2 => BoostingConviction::Locked2x, + 3 => BoostingConviction::Locked3x, + 4 => BoostingConviction::Locked4x, + 5 => BoostingConviction::Locked5x, + 6 => BoostingConviction::Locked6x, + _ => return Err(()), + }) + } +} + +impl BoostingConviction { + /// The amount of time (in number of periods) that our conviction implies a successful voter's + /// balance should be locked for. + pub fn lock_periods(self) -> u32 { + match self { + BoostingConviction::None => 0, + BoostingConviction::Locked1x => 1, + BoostingConviction::Locked2x => 2, + BoostingConviction::Locked3x => 4, + BoostingConviction::Locked4x => 8, + BoostingConviction::Locked5x => 16, + BoostingConviction::Locked6x => 32, + } + } + + /// The votes of a voter of the given `balance` with our conviction. + pub fn votes + Zero + Copy + CheckedMul + CheckedDiv + Bounded>( + self, + capital: B, + ) -> BoostingDelegations { + let votes = match self { + BoostingConviction::None => capital.checked_div(&10u8.into()).unwrap_or_else(Zero::zero), + x => capital.checked_mul(&u8::from(x).into()).unwrap_or_else(B::max_value), + }; + BoostingDelegations { votes, capital } + } +} + +impl Bounded for BoostingConviction { + fn min_value() -> Self { + BoostingConviction::None + } + fn max_value() -> Self { + BoostingConviction::Locked6x + } +} + +#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BoostInfo { + pub(crate) balance: Balance, + pub(crate) conviction: BoostingConviction, +} + +impl BoostInfo { + /// Returns `Some` of the lock periods that the account is locked for, assuming that the + /// referendum passed if `approved` is `true`. + pub fn get_locked_period(self) -> (u32, Balance) { + return (self.conviction.lock_periods(), self.balance); + } +} From 72612ee06b4e790c05b49b31d6eea47c3016476e Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 17 Nov 2023 12:56:32 +1300 Subject: [PATCH 085/155] WIP - integrate boosting mechanism --- Cargo.lock | 18 +++++++++ Cargo.toml | 89 +++++++++++++++++++++--------------------- pallets/spp/Cargo.toml | 4 +- pallets/spp/src/lib.rs | 8 +++- 4 files changed, 73 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39faaaf1c..fc2087ec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6637,6 +6637,23 @@ dependencies = [ "sp-std", ] +[[package]] +name = "orml-rewards" +version = "0.4.1-dev" +source = "git+https://github.com/bit-country/open-runtime-module-library?branch=v-9.38#5861b6707c69031e8d70515b0aea6d222f5a5053" +dependencies = [ + "frame-support", + "frame-system", + "orml-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "orml-tokens" version = "0.4.1-dev" @@ -8033,6 +8050,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "orml-rewards", "orml-tokens", "orml-traits", "pallet-balances", diff --git a/Cargo.toml b/Cargo.toml index 41220719b..3f959a986 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ url = "2.3.1" tracing-core = "0.1.28" impl-trait-for-tuples = "0.2.2" num_enum = { version = "0.5.3", default-features = false } -getrandom = { version = "0.2.7", features = ["js"]} +getrandom = { version = "0.2.7", features = ["js"] } blake2-rfc = { version = "0.2.18", default-features = false } hex = { version = "0.4", default-features = false } funty = { version = "=1.1.0", default-features = false } @@ -38,13 +38,13 @@ similar-asserts = { version = "1.1.0" } # General (ethereum) ethabi = { version = "18.0.0", default-features = false } -evm = { git = "https://github.com/rust-blockchain/evm", rev = "51b8c2ce3104265e1fd5bb0fe5cdfd2e0938239c", default-features = false, features = [ "with-codec" ] } +evm = { git = "https://github.com/rust-blockchain/evm", rev = "51b8c2ce3104265e1fd5bb0fe5cdfd2e0938239c", default-features = false, features = ["with-codec"] } # General (precompile macro) proc-macro2 = "1.0" quote = "1.0" sha3 = "0.8" -syn = { version = "1.0", features = [ "extra-traits", "fold", "full", "visit" ] } +syn = { version = "1.0", features = ["extra-traits", "fold", "full", "visit"] } # General (node only) parking_lot = "0.12.1" @@ -60,7 +60,7 @@ frame-executive = { git = 'https://github.com/paritytech/substrate', branch = 'p frame-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } frame-support = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } frame-system = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -frame-system-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false} +frame-system-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } frame-system-rpc-runtime-api = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } frame-try-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38", default-features = false } @@ -85,7 +85,7 @@ sc-consensus-epochs = { git = "https://github.com/paritytech/substrate", branch sc-consensus-slots = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } sc-consensus-manual-seal = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } -sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38"} +sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } sc-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } sc-finality-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } @@ -101,22 +101,22 @@ sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkad # Substrate Primitive Dependencies (general) sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-block-builder = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } -sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-api = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } sp-core = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-consensus-aura = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-inherents = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-io = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-version = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } -sp-application-crypto = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -sp-arithmetic = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-consensus-aura = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-inherents = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-io = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-version = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } +sp-application-crypto = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } +sp-arithmetic = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } # Substrate Utilities -node-primitives = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38"} +node-primitives = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } try-runtime-cli = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38" } substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } @@ -128,13 +128,13 @@ sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "polkad sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } -pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38"} +pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } # Substrate Pallets pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } pallet-balances = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } -pallet-collective = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false} +pallet-collective = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } pallet-democracy = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } pallet-randomness-collective-flip = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } pallet-sudo = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.38', default-features = false } @@ -186,38 +186,39 @@ pallet-collator-selection = { git = 'https://github.com/paritytech/cumulus', bra # Polkadot polkadot-cli = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" } polkadot-service = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" } -polkadot-parachain = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } -polkadot-runtime-parachains = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } -polkadot-primitives = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } -xcm = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } -xcm-builder = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } -xcm-executor = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } -pallet-xcm = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38" , default-features = false } +polkadot-parachain = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } +polkadot-runtime-parachains = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } +polkadot-primitives = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } +xcm = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } +xcm-builder = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } +xcm-executor = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } +pallet-xcm = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } polkadot-runtime-common = { git = 'https://github.com/paritytech/polkadot', branch = "release-v0.9.38", default-features = false } kusama-runtime = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.38", default-features = false } # ORML -orml-currencies = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-tokens = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-traits = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-nft = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-unknown-tokens = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-xtokens = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-xcm = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-xcm-support = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } -orml-oracle = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-currencies = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-tokens = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-traits = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-nft = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-unknown-tokens = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-xtokens = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-xcm = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-xcm-support = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-oracle = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } orml-benchmarking = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } +orml-rewards = { git = "https://github.com/bit-country/open-runtime-module-library", branch = "v-9.38", default-features = false } # EVM pallet-dynamic-fee = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } -fp-consensus = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fp-storage = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fc-cli = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fc-rpc = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fc-rpc-core = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fc-consensus = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fc-mapping-sync = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} -fc-db = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38"} +fp-consensus = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fp-storage = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fc-cli = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fc-rpc = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fc-rpc-core = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fc-consensus = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fc-mapping-sync = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } +fc-db = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38" } fp-self-contained = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38", default-features = false } fp-rpc = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38", default-features = false } fp-evm = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38", default-features = false } @@ -231,7 +232,7 @@ pallet-evm-precompile-ed25519 = { git = "https://github.com/justinphamnz/frontie pallet-evm-precompile-modexp = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38", default-features = false } pallet-evm-precompile-sha3fips = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38", default-features = false } pallet-evm-precompile-simple = { git = "https://github.com/justinphamnz/frontier", branch = "polkadot-v0.9.38", default-features = false } -libsecp256k1 = { version = "0.6", default-features = false, features = ["hmac", "static-context"]} +libsecp256k1 = { version = "0.6", default-features = false, features = ["hmac", "static-context"] } # ink! pallet-contracts = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } diff --git a/pallets/spp/Cargo.toml b/pallets/spp/Cargo.toml index 917fae3f0..30082b8de 100644 --- a/pallets/spp/Cargo.toml +++ b/pallets/spp/Cargo.toml @@ -27,6 +27,7 @@ substrate-fixed = { workspace = true } pallet-balances = { workspace = true, optional = true } orml-traits = { workspace = true } orml-tokens = { workspace = true } +orml-rewards = { workspace = true } # local packages core-primitives = { path = "../../traits/core-primitives", default-features = false } @@ -65,5 +66,6 @@ std = [ 'frame-benchmarking/std', 'orml-tokens/std', "currencies/std", - "asset-manager/std" + "asset-manager/std", + "orml-rewards/std" ] \ No newline at end of file diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index bb8909ae4..2211da5eb 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -61,7 +61,7 @@ pub mod pallet { use primitives::{PoolId, StakingRound}; - use crate::utils::PoolInfo; + use crate::utils::{BoostInfo, PoolInfo}; use super::*; @@ -616,6 +616,12 @@ pub mod pallet { Ok(()) } + + /// This function allow reward voting for the pool + #[pallet::weight(< T as Config >::WeightInfo::mint_land())] + pub fn boost(origin: OriginFor, pool_id: PoolId, vote: BoostInfo>) -> DispatchResult { + Ok(()) + } } } From 776281fc4d8a200c28591c26b67e1c30ed208a5e Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 17 Nov 2023 21:19:41 +1300 Subject: [PATCH 086/155] WIP - Add boost mechanism that calculates boosting weights --- pallets/economy/src/lib.rs | 2 -- pallets/spp/src/lib.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/pallets/economy/src/lib.rs b/pallets/economy/src/lib.rs index ea06898a0..124e7997a 100644 --- a/pallets/economy/src/lib.rs +++ b/pallets/economy/src/lib.rs @@ -18,7 +18,6 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Encode; - use frame_support::{ ensure, pallet_prelude::*, @@ -54,7 +53,6 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use sp_runtime::traits::{CheckedAdd, CheckedSub, Saturating}; use sp_runtime::ArithmeticError; diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 2211da5eb..353e95eba 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -53,10 +53,10 @@ pub mod weights; #[frame_support::pallet] pub mod pallet { - use frame_support::traits::{Currency, ReservableCurrency}; + use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; - use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedSub}; + use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub}; use sp_runtime::Permill; use primitives::{PoolId, StakingRound}; @@ -75,7 +75,9 @@ pub mod pallet { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Currency type - type Currency: Currency + ReservableCurrency; + type Currency: Currency + + ReservableCurrency + + LockableCurrency; /// Multi currencies type that handles different currency type in auction type MultiCurrency: MultiReservableCurrency< Self::AccountId, @@ -303,6 +305,8 @@ pub mod pallet { InvalidLastEraUpdatedBlock, /// Fail to process redeem requests FailedToProcessRedemption, + /// Insufficient Fund + InsufficientFund, } #[pallet::hooks] @@ -620,6 +624,28 @@ pub mod pallet { /// This function allow reward voting for the pool #[pallet::weight(< T as Config >::WeightInfo::mint_land())] pub fn boost(origin: OriginFor, pool_id: PoolId, vote: BoostInfo>) -> DispatchResult { + // Ensure user is signed + let who = ensure_signed(origin)?; + + // Ensure user has balance to vote + ensure!( + vote.balance <= T::Currency::free_balance(&who), + Error::::InsufficientFund + ); + + // Check if pool exists + let pool_instance = Pool::::get(pool_id).ok_or(Error::::PoolDoesNotExist)?; + // Convert boost conviction into shares + let vote_conviction = vote.conviction.lock_periods(); + // Calculate lock period from UnlockDuration block number x conviction + let current_block: T::BlockNumber = >::block_number(); + + let mut unlock_at = current_block.saturating_add(UpdateEraFrequency::::get()); + if !vote_conviction.is_zero() { + unlock_at.saturating_mul(vote_conviction.into()); + } + // Locked token + // Add shares into the rewards pool Ok(()) } } From 9c00cd0986494650c427517e038a5596520aef2e Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 17 Nov 2023 21:55:09 +1300 Subject: [PATCH 087/155] WIP - Add locking type that allow accumulating balance and locks --- pallets/spp/src/utils.rs | 45 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index e8fb8fc59..45edf1b95 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -24,7 +24,7 @@ use sp_runtime::{ Permill, RuntimeDebug, }; -use primitives::FungibleTokenId; +use primitives::{FungibleTokenId, PoolId}; // Helper methods to compute the issuance rate for undeployed land. @@ -153,3 +153,46 @@ impl BoostInfo { return (self.conviction.lock_periods(), self.balance); } } + +/// A "prior" lock, i.e. a lock for some now-forgotten reason. +#[derive(Encode, Decode, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo)] +pub struct PriorLock(BlockNumber, Balance); + +impl PriorLock { + /// Accumulates an additional lock. + pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) { + self.0 = self.0.max(until); + self.1 = self.1.max(amount); + } + + pub fn locked(&self) -> Balance { + self.1 + } + + pub fn update(&mut self, now: BlockNumber) { + if now >= self.0 { + self.0 = Zero::zero(); + self.1 = Zero::zero(); + } + } +} + +#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BoostingRecord { + pub(crate) votes: Vec<(PoolId, Vote)>, + pub(crate) prior: PriorLock, +} + +impl BoostingRecord { + pub fn update(&mut self, now: BlockNumber) { + self.prior.update(now); + } + + /// The amount of this account's balance that much currently be locked due to voting. + pub fn locked_balance(&self) -> Balance { + self.votes + .iter() + .map(|i| i.1.balance) + .fold(self.prior.locked(), |a, i| a.max(i)) + } +} From 3d0d5cb951b7cb99c4546a414c7655f599f9aea1 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 17 Nov 2023 21:55:52 +1300 Subject: [PATCH 088/155] WIP - Implement boosting info per account storage --- pallets/spp/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 353e95eba..cadd3b278 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -61,7 +61,7 @@ pub mod pallet { use primitives::{PoolId, StakingRound}; - use crate::utils::{BoostInfo, PoolInfo}; + use crate::utils::{BoostInfo, BoostingRecord, PoolInfo}; use super::*; @@ -231,6 +231,12 @@ pub mod pallet { #[pallet::getter(fn iteration_limit)] pub type IterationLimit = StorageValue<_, u32, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn boosting_record)] + /// Store boosting records for each account + pub type BoostingOf = + StorageMap<_, Twox64Concat, T::AccountId, BoostingRecord, T::BlockNumber>, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { From 0441ea8b47e588597ea388be5929421011fce0bd Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 18 Nov 2023 22:04:43 +1300 Subject: [PATCH 089/155] WIP - Add boost calculation and locking logic for existing boost and new boost --- pallets/spp/src/lib.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index cadd3b278..4c8eef690 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -313,6 +313,8 @@ pub mod pallet { FailedToProcessRedemption, /// Insufficient Fund InsufficientFund, + /// Error while adding new boost + MaxVotesReached, } #[pallet::hooks] @@ -647,10 +649,34 @@ pub mod pallet { let current_block: T::BlockNumber = >::block_number(); let mut unlock_at = current_block.saturating_add(UpdateEraFrequency::::get()); + let mut total_balance = vote.balance; if !vote_conviction.is_zero() { unlock_at.saturating_mul(vote_conviction.into()); + total_balance.saturating_mul(vote_conviction.into()); } // Locked token + + BoostingOf::::try_mutate(who, |voting| -> DispatchResult { + let votes = &mut voting.votes; + let prior_lock = &mut voting.prior; + match votes.binary_search_by_key(&pool_id, |i| i.0) { + Ok(i) => { + // User already boosted, this is adding up their boosting weight + votes[i] + .1 + .add(total_balance.clone()) + .ok_or(Error::::ArithmeticOverflow)?; + voting + .prior + .accumulate(unlock_at, votes[i].1.balance.saturating_add(total_balance)) + } + Err(i) => { + votes.insert(i, (pool_id, vote)); + voting.prior.accumulate(unlock_at, total_balance); + } + } + Ok(()) + })?; // Add shares into the rewards pool Ok(()) } From 6b737b4e2c88b2298ef94290d4ff21fe189f2533 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 18 Nov 2023 22:05:11 +1300 Subject: [PATCH 090/155] WIP - Implement Copy trait and add logic to calculate boost info --- pallets/spp/src/utils.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index 45edf1b95..83e1ec37f 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -146,12 +146,17 @@ pub struct BoostInfo { pub(crate) conviction: BoostingConviction, } -impl BoostInfo { +impl BoostInfo { /// Returns `Some` of the lock periods that the account is locked for, assuming that the /// referendum passed if `approved` is `true`. pub fn get_locked_period(self) -> (u32, Balance) { return (self.conviction.lock_periods(), self.balance); } + + pub fn add(&mut self, balance: Balance) -> Option<()> { + self.balance.saturating_add(balance.into()); + Some(()) + } } /// A "prior" lock, i.e. a lock for some now-forgotten reason. @@ -179,7 +184,7 @@ impl PriorLock { - pub(crate) votes: Vec<(PoolId, Vote)>, + pub(crate) votes: Vec<(PoolId, BoostInfo)>, pub(crate) prior: PriorLock, } From 9679d14e6d59aaf7a6d8d16aa5bbe1b36867d27a Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 19 Nov 2023 10:57:17 +1300 Subject: [PATCH 091/155] WIP - Implement reward share calculation when user boosted --- pallets/spp/src/lib.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 4c8eef690..2c6fd1a5f 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -56,7 +56,7 @@ pub mod pallet { use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; - use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub}; + use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub, UniqueSaturatedInto}; use sp_runtime::Permill; use primitives::{PoolId, StakingRound}; @@ -71,7 +71,15 @@ pub mod pallet { pub struct Pallet(PhantomData); #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: + frame_system::Config + + orml_rewards::Config< + Share = BalanceOf, + Balance = BalanceOf, + PoolId = PoolId, + CurrencyId = FungibleTokenId, + > + { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Currency type @@ -271,6 +279,12 @@ pub mod pallet { LastEraUpdated { last_era_block: BlockNumberFor }, /// Update era frequency UpdateEraFrequency { frequency: BlockNumberFor }, + /// Boosted successful + Boosted { + booster: T::AccountId, + pool_id: PoolId, + boost_info: BoostInfo>, + }, } #[pallet::error] @@ -656,9 +670,8 @@ pub mod pallet { } // Locked token - BoostingOf::::try_mutate(who, |voting| -> DispatchResult { + BoostingOf::::try_mutate(who.clone(), |voting| -> DispatchResult { let votes = &mut voting.votes; - let prior_lock = &mut voting.prior; match votes.binary_search_by_key(&pool_id, |i| i.0) { Ok(i) => { // User already boosted, this is adding up their boosting weight @@ -671,13 +684,21 @@ pub mod pallet { .accumulate(unlock_at, votes[i].1.balance.saturating_add(total_balance)) } Err(i) => { - votes.insert(i, (pool_id, vote)); + votes.insert(i, (pool_id, vote.clone())); voting.prior.accumulate(unlock_at, total_balance); } } Ok(()) })?; // Add shares into the rewards pool + >::add_share(&who, &pool_id, total_balance.unique_saturated_into()); + // Emit Boosted event + Self::deposit_event(Event::::Boosted { + booster: who.clone(), + pool_id, + boost_info: vote.clone(), + }); + Ok(()) } } From b90650c2b39538698e323ac0a47466d9d4827cb7 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 19 Nov 2023 12:17:11 +1300 Subject: [PATCH 092/155] WIP - Apply extend lock to currency with amount of boosted --- pallets/spp/src/lib.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 2c6fd1a5f..a69ef0ac2 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; +use frame_support::traits::LockIdentifier; use frame_support::{ dispatch::DispatchResult, ensure, @@ -26,7 +27,7 @@ use frame_support::{ }; use frame_system::ensure_signed; use frame_system::pallet_prelude::*; -use orml_traits::MultiCurrency; +use orml_traits::{MultiCurrency, RewardHandler}; use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedSub}; use sp_runtime::{ traits::{AccountIdConversion, Convert, Saturating, Zero}, @@ -35,7 +36,7 @@ use sp_runtime::{ use core_primitives::*; pub use pallet::*; -use primitives::{ClassId, EraIndex, FungibleTokenId, StakingRound, TokenId}; +use primitives::{ClassId, EraIndex, FungibleTokenId, PoolId, StakingRound, TokenId}; pub use weights::WeightInfo; pub type QueueId = u32; @@ -51,9 +52,11 @@ mod tests; pub mod weights; +const BOOSTING_ID: LockIdentifier = *b"bc/boost"; + #[frame_support::pallet] pub mod pallet { - use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency}; + use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency, WithdrawReasons}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub, UniqueSaturatedInto}; @@ -656,7 +659,8 @@ pub mod pallet { ); // Check if pool exists - let pool_instance = Pool::::get(pool_id).ok_or(Error::::PoolDoesNotExist)?; + ensure!(Pool::::get(pool_id).is_some(), Error::::PoolDoesNotExist); + // Still need to work out some // Convert boost conviction into shares let vote_conviction = vote.conviction.lock_periods(); // Calculate lock period from UnlockDuration block number x conviction @@ -690,6 +694,8 @@ pub mod pallet { } Ok(()) })?; + T::Currency::extend_lock(BOOSTING_ID, &who, vote.balance, WithdrawReasons::TRANSFER); + // Add shares into the rewards pool >::add_share(&who, &pool_id, total_balance.unique_saturated_into()); // Emit Boosted event @@ -1044,3 +1050,15 @@ impl Pallet { T::PoolAccount::get().into_account_truncating() } } + +impl RewardHandler for Pallet { + type Balance = BalanceOf; + type PoolId = PoolId; + + fn payout(who: &T::AccountId, pool_id: &Self::PoolId, currency_id: FungibleTokenId, payout_amount: Self::Balance) { + if payout_amount.is_zero() { + return; + } + // TODO implement payout logic + } +} From 6889610e3e9086780b00a3ad0275a5aa59e0a54f Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 19 Nov 2023 12:17:40 +1300 Subject: [PATCH 093/155] WIP - exposed block number and balance of prior lock for test --- pallets/spp/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index 83e1ec37f..367303352 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -161,7 +161,7 @@ impl BoostInfo { /// A "prior" lock, i.e. a lock for some now-forgotten reason. #[derive(Encode, Decode, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo)] -pub struct PriorLock(BlockNumber, Balance); +pub struct PriorLock(pub(crate) BlockNumber, pub(crate) Balance); impl PriorLock { /// Accumulates an additional lock. From d4df72a48da44a3daa77b6227034ce63ee1dfd34 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 19 Nov 2023 12:17:59 +1300 Subject: [PATCH 094/155] WIP - Unit test - boosting works --- pallets/spp/src/mock.rs | 11 ++++++- pallets/spp/src/tests.rs | 65 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index d2ed479bf..77283e38f 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -18,7 +18,7 @@ use asset_manager::ForeignAssetMapping; use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{CollectionType, NftClassData, TokenType}; use primitives::{ - Amount, AssetId, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId, + Amount, AssetId, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, PoolId, TokenId, LAND_CLASS_ID, }; @@ -194,6 +194,14 @@ impl BlockNumberProvider for MockRelayBlockNumberProvider { } } +impl orml_rewards::Config for Runtime { + type Share = Balance; + type Balance = Balance; + type PoolId = PoolId; + type CurrencyId = FungibleTokenId; + type Handler = SppModule; +} + parameter_types! { pub const MinBlocksPerRound: u32 = 10; pub const MinimumStake: Balance = 200; @@ -240,6 +248,7 @@ construct_runtime!( Currencies: currencies::{ Pallet, Storage, Call, Event}, Tokens: orml_tokens::{Pallet, Call, Storage, Config, Event}, Spp: spp:: {Pallet, Call, Storage, Event}, + RewardsModule: orml_rewards } ); diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index d5c383807..15442cd22 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -23,7 +23,7 @@ use sp_runtime::{Perbill, Permill}; use mock::{RuntimeEvent, *}; -use crate::utils::PoolInfo; +use crate::utils::{BoostInfo, BoostingConviction, BoostingRecord, PoolInfo, PriorLock}; use super::*; @@ -361,3 +361,66 @@ fn current_era_update_works() { ); }); } + +#[test] +fn boosting_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Permill::from_percent(5) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 1); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Permill::from_percent(5), + currency_id: FungibleTokenId::NativeToken(1), + max: 50 + } + ); + + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + // This is true because fee hasn't been set up. + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + + assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); + + // Deposit another 10000 KSM + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); + + assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); + + // Boosting works + assert_ok!(SppModule::boost( + RuntimeOrigin::signed(BOB), + 0, + BoostInfo { + balance: 1000, + conviction: BoostingConviction::None + } + )); + let boosting_of = BoostingOf::::get(BOB); + let some_record = BoostingRecord { + votes: vec![( + 0, + BoostInfo { + balance: 1000, + conviction: BoostingConviction::None, + }, + )], + prior: PriorLock(1, 1000), + }; + assert_eq!(boosting_of, some_record) + }); +} From 21a345a177a2bd00e78a37c0c3251b29010946ef Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 19 Nov 2023 21:08:06 +1300 Subject: [PATCH 095/155] WIP - Unit test - complete boosting test that ensure balance is not usable after boosted. --- pallets/spp/src/lib.rs | 7 ++++++- pallets/spp/src/tests.rs | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index a69ef0ac2..3f2b24471 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -694,7 +694,12 @@ pub mod pallet { } Ok(()) })?; - T::Currency::extend_lock(BOOSTING_ID, &who, vote.balance, WithdrawReasons::TRANSFER); + T::Currency::extend_lock( + BOOSTING_ID, + &who, + vote.balance, + frame_support::traits::WithdrawReasons::TRANSFER, + ); // Add shares into the rewards pool >::add_share(&who, &pool_id, total_balance.unique_saturated_into()); diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 15442cd22..4f63e1363 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -18,6 +18,7 @@ #![cfg(test)] use frame_support::{assert_err, assert_noop, assert_ok}; +use pallet_balances::BalanceLock; use sp_runtime::traits::BadOrigin; use sp_runtime::{Perbill, Permill}; @@ -32,6 +33,14 @@ fn test_one() { ExtBuilder::default().build().execute_with(|| assert_eq!(1, 1)); } +fn the_lock(amount: Balance) -> BalanceLock { + BalanceLock { + id: BOOSTING_ID, + amount, + reasons: pallet_balances::Reasons::Misc, + } +} + #[test] fn create_ksm_pool_works() { ExtBuilder::default() @@ -402,11 +411,12 @@ fn boosting_works() { assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); // Boosting works + let bob_free_balance = Balances::free_balance(BOB); assert_ok!(SppModule::boost( RuntimeOrigin::signed(BOB), 0, BoostInfo { - balance: 1000, + balance: bob_free_balance, conviction: BoostingConviction::None } )); @@ -415,12 +425,13 @@ fn boosting_works() { votes: vec![( 0, BoostInfo { - balance: 1000, + balance: bob_free_balance, conviction: BoostingConviction::None, }, )], - prior: PriorLock(1, 1000), + prior: PriorLock(1, bob_free_balance), }; - assert_eq!(boosting_of, some_record) + assert_eq!(boosting_of, some_record); + assert_eq!(Balances::usable_balance(&BOB), 0); }); } From ff743c3de448dd6a207303910b00c705f04ba2c2 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 20 Nov 2023 15:21:39 +1300 Subject: [PATCH 096/155] WIP - Reserve pool id 0 as network pool --- pallets/spp/src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 3f2b24471..ba35a81e4 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -59,7 +59,7 @@ pub mod pallet { use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency, WithdrawReasons}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; - use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub, UniqueSaturatedInto}; + use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub, One, UniqueSaturatedInto}; use sp_runtime::Permill; use primitives::{PoolId, StakingRound}; @@ -248,6 +248,11 @@ pub mod pallet { pub type BoostingOf = StorageMap<_, Twox64Concat, T::AccountId, BoostingRecord, T::BlockNumber>, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn network_boost_info)] + /// Store boosting records for each account + pub type NetworkBoostingInfo = StorageValue<_, BoostingRecord, T::BlockNumber>, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { @@ -363,11 +368,15 @@ pub mod pallet { Error::::CurrencyIsNotSupported ); - // TODO Check commission below threshold - // Collect pool creation fee Self::collect_pool_creation_fee(&who, currency_id)?; + // Ensure no pool id is zero + let current_pool_id = NextPoolId::::get(); + if current_pool_id.is_zero() { + NextPoolId::::put(1u32); + } + // Next pool id let next_pool_id = NextPoolId::::try_mutate(|id| -> Result { let current_id = *id; From e253806e92b72c89b23a93fdae8a86540687e9b2 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 20 Nov 2023 15:21:56 +1300 Subject: [PATCH 097/155] WIP - Unit test - verify all pool id 0 logic works --- pallets/spp/src/tests.rs | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 4f63e1363..b7e50ecf1 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -55,7 +55,7 @@ fn create_ksm_pool_works() { )); let next_pool_id = NextPoolId::::get(); - assert_eq!(next_pool_id, 1); + assert_eq!(next_pool_id, 2); assert_eq!( Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { @@ -82,7 +82,7 @@ fn deposit_ksm_works() { )); let next_pool_id = NextPoolId::::get(); - assert_eq!(next_pool_id, 1); + assert_eq!(next_pool_id, 2); assert_eq!( Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { @@ -93,18 +93,18 @@ fn deposit_ksm_works() { } ); - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); // This is true because fee hasn't been set up. assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); - assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(PoolLedger::::get(1), 10000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); // Deposit another 10000 KSM - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); - assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(PoolLedger::::get(1), 20000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); }); } @@ -123,7 +123,7 @@ fn redeem_rksm_request_works() { )); let next_pool_id = NextPoolId::::get(); - assert_eq!(next_pool_id, 1); + assert_eq!(next_pool_id, 2); assert_eq!( Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { @@ -134,32 +134,32 @@ fn redeem_rksm_request_works() { } ); - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); // This is true because fee hasn't been set up. assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); - assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(PoolLedger::::get(1), 10000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); // Deposit another 10000 KSM - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); - assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(PoolLedger::::get(1), 20000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); assert_noop!( - SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(1), 10000), + SppModule::redeem(RuntimeOrigin::signed(BOB), 2, FungibleTokenId::FungibleToken(1), 10000), Error::::PoolDoesNotExist ); assert_noop!( - SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(0), 10000), + SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(0), 10000), Error::::CurrencyIsNotSupported ); assert_noop!( - SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(1), 10000), + SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(1), 10000), Error::::NoCurrentStakingRound ); @@ -168,13 +168,13 @@ fn redeem_rksm_request_works() { CurrentStakingRound::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); assert_ok!(SppModule::redeem( RuntimeOrigin::signed(BOB), - 0, + 1, FungibleTokenId::FungibleToken(1), 10000 )); // After Bob redeems, pool ledger 0 should have only 10000 - assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(PoolLedger::::get(1), 10000); // Verify if redeem queue has requests @@ -227,7 +227,7 @@ fn current_era_update_works() { )); let next_pool_id = NextPoolId::::get(); - assert_eq!(next_pool_id, 1); + assert_eq!(next_pool_id, 2); assert_eq!( Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { @@ -239,22 +239,22 @@ fn current_era_update_works() { ); // Verify BOB account with 20000 KSM assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 20000); - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); // This is true because fee hasn't been set up. assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); // Bob KSM balance become 10000 assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 10000); - assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(PoolLedger::::get(1), 10000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); // Deposit another 10000 KSM - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); // Bob KSM now is 0 assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 0); - assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(PoolLedger::::get(1), 20000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); // Pool summary @@ -265,17 +265,17 @@ fn current_era_update_works() { // Holding: 20000 FungibleToken(1) reciept token of NativeToken(1) assert_noop!( - SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(1), 10000), + SppModule::redeem(RuntimeOrigin::signed(BOB), 2, FungibleTokenId::FungibleToken(1), 10000), Error::::PoolDoesNotExist ); assert_noop!( - SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(0), 10000), + SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(0), 10000), Error::::CurrencyIsNotSupported ); assert_noop!( - SppModule::redeem(RuntimeOrigin::signed(BOB), 0, FungibleTokenId::FungibleToken(1), 10000), + SppModule::redeem(RuntimeOrigin::signed(BOB), 1, FungibleTokenId::FungibleToken(1), 10000), Error::::NoCurrentStakingRound ); @@ -285,13 +285,13 @@ fn current_era_update_works() { // Bob successfully redeemed assert_ok!(SppModule::redeem( RuntimeOrigin::signed(BOB), - 0, + 1, FungibleTokenId::FungibleToken(1), 10000 )); // After Bob redeems, pool ledger 0 should have only 10000 - assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(PoolLedger::::get(1), 10000); // After Bob redeem, make sure BOB KSM balance remains the same as it will only released next era assert_eq!(Tokens::accounts(BOB, FungibleTokenId::NativeToken(1)).free, 0); @@ -385,7 +385,7 @@ fn boosting_works() { )); let next_pool_id = NextPoolId::::get(); - assert_eq!(next_pool_id, 1); + assert_eq!(next_pool_id, 2); assert_eq!( Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { @@ -396,25 +396,25 @@ fn boosting_works() { } ); - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); // This is true because fee hasn't been set up. assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); - assert_eq!(PoolLedger::::get(0), 10000); + assert_eq!(PoolLedger::::get(1), 10000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); // Deposit another 10000 KSM - assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 0, 10000)); + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); - assert_eq!(PoolLedger::::get(0), 20000); + assert_eq!(PoolLedger::::get(1), 20000); assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); // Boosting works let bob_free_balance = Balances::free_balance(BOB); assert_ok!(SppModule::boost( RuntimeOrigin::signed(BOB), - 0, + 1, BoostInfo { balance: bob_free_balance, conviction: BoostingConviction::None @@ -423,7 +423,7 @@ fn boosting_works() { let boosting_of = BoostingOf::::get(BOB); let some_record = BoostingRecord { votes: vec![( - 0, + 1, BoostInfo { balance: bob_free_balance, conviction: BoostingConviction::None, From 47afe4a2a72f970bb56e5107746cfbb642b79b0a Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 20 Nov 2023 16:01:16 +1300 Subject: [PATCH 098/155] WIP - Add network reward ledger when user boosted --- pallets/spp/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index ba35a81e4..dbec98d8b 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -712,6 +712,9 @@ pub mod pallet { // Add shares into the rewards pool >::add_share(&who, &pool_id, total_balance.unique_saturated_into()); + // Add shares into the network pool + >::add_share(&who, &Zero::zero(), total_balance.unique_saturated_into()); + // Emit Boosted event Self::deposit_event(Event::::Boosted { booster: who.clone(), From 56245b39042d6a52e0dcb467b1b9daa2716aa99e Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 20 Nov 2023 16:01:37 +1300 Subject: [PATCH 099/155] WIP - Unit test - verify if all share pools work --- pallets/spp/src/tests.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index b7e50ecf1..a9b9d913d 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -433,5 +433,9 @@ fn boosting_works() { }; assert_eq!(boosting_of, some_record); assert_eq!(Balances::usable_balance(&BOB), 0); + let pool_1_shared_rewards = RewardsModule::shares_and_withdrawn_rewards(1, BOB); + let network_shared_rewards = RewardsModule::shares_and_withdrawn_rewards(0, BOB); + assert_eq!(pool_1_shared_rewards, (bob_free_balance, Default::default())); + assert_eq!(network_shared_rewards, (bob_free_balance, Default::default())); }); } From 1de4a4a07a7b3d56cb8b64816583469d4cf5bd3e Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 20 Nov 2023 22:55:57 +1300 Subject: [PATCH 100/155] WIP - Implement claim reward and reward ledger storage --- pallets/spp/src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index dbec98d8b..7ede5d5cf 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -56,6 +56,8 @@ const BOOSTING_ID: LockIdentifier = *b"bc/boost"; #[frame_support::pallet] pub mod pallet { + use std::collections::BTreeMap; + use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency, WithdrawReasons}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; @@ -250,8 +252,14 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn network_boost_info)] - /// Store boosting records for each account - pub type NetworkBoostingInfo = StorageValue<_, BoostingRecord, T::BlockNumber>, ValueQuery>; + /// Store boosting records for each pool + pub type NetworkBoostingInfo = StorageMap<_, Twox64Concat, PoolId, BalanceOf, ValueQuery>; + + /// PoolRewardAmountPerEra: double_map Pool, FungibleTokenId => RewardAmountPerEra + #[pallet::storage] + #[pallet::getter(fn incentive_reward_amounts)] + pub type PoolRewardAmountPerEra = + StorageDoubleMap<_, Twox64Concat, PoolId, Twox64Concat, FungibleTokenId, BalanceOf, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] @@ -724,6 +732,73 @@ pub mod pallet { Ok(()) } + + /// This function allow reward voting for the pool + #[pallet::weight(< T as Config >::WeightInfo::mint_land())] + pub fn claim_reward(origin: OriginFor, pool_id: PoolId) -> DispatchResult { + // Ensure user is signed + let who = ensure_signed(origin)?; + + // orml_rewards will claim rewards for all currencies rewards + >::claim_rewards(&who, &pool_id); + + // Check if pool exists + ensure!(Pool::::get(pool_id).is_some(), Error::::PoolDoesNotExist); + // Still need to work out some + // Convert boost conviction into shares + let vote_conviction = vote.conviction.lock_periods(); + // Calculate lock period from UnlockDuration block number x conviction + let current_block: T::BlockNumber = >::block_number(); + + let mut unlock_at = current_block.saturating_add(UpdateEraFrequency::::get()); + let mut total_balance = vote.balance; + if !vote_conviction.is_zero() { + unlock_at.saturating_mul(vote_conviction.into()); + total_balance.saturating_mul(vote_conviction.into()); + } + // Locked token + + BoostingOf::::try_mutate(who.clone(), |voting| -> DispatchResult { + let votes = &mut voting.votes; + match votes.binary_search_by_key(&pool_id, |i| i.0) { + Ok(i) => { + // User already boosted, this is adding up their boosting weight + votes[i] + .1 + .add(total_balance.clone()) + .ok_or(Error::::ArithmeticOverflow)?; + voting + .prior + .accumulate(unlock_at, votes[i].1.balance.saturating_add(total_balance)) + } + Err(i) => { + votes.insert(i, (pool_id, vote.clone())); + voting.prior.accumulate(unlock_at, total_balance); + } + } + Ok(()) + })?; + T::Currency::extend_lock( + BOOSTING_ID, + &who, + vote.balance, + frame_support::traits::WithdrawReasons::TRANSFER, + ); + + // Add shares into the rewards pool + >::add_share(&who, &pool_id, total_balance.unique_saturated_into()); + // Add shares into the network pool + >::add_share(&who, &Zero::zero(), total_balance.unique_saturated_into()); + + // Emit Boosted event + Self::deposit_event(Event::::Boosted { + booster: who.clone(), + pool_id, + boost_info: vote.clone(), + }); + + Ok(()) + } } } From 6cf87ed5da77e8e10a6df221cf30174bcef0008b Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 10:35:06 +1300 Subject: [PATCH 101/155] WIP - Add function to handle pool reward distribution per era --- pallets/spp/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 7ede5d5cf..a37a1e5e7 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -951,6 +951,8 @@ impl Pallet { Ok(()) } + fn handle_reward_distribution_to_each_pool(pool_id: PoolId) -> DispatchResult {} + #[transactional] fn update_queue_request( currency_id: FungibleTokenId, @@ -1126,6 +1128,13 @@ impl Pallet { Ok(()) } + fn handle_boosting_reward_per_era(era_index: EraIndex) -> DispatchResult { + for boosted_pool_keys in NetworkBoostingInfo::::iter_keys() { + Self::handle_reward_distribution_to_each_pool(boosted_pool_keys)?; + } + Ok(()) + } + #[transactional] pub fn update_current_era(era_index: EraIndex) -> DispatchResult { let previous_era = Self::relay_chain_current_era(); From 6483bf357e43e301981529af55ebd92e47445fdb Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 13:23:48 +1300 Subject: [PATCH 102/155] WIP - Set up pool treasury account --- pallets/spp/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index a37a1e5e7..365b3b685 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -261,6 +261,10 @@ pub mod pallet { pub type PoolRewardAmountPerEra = StorageDoubleMap<_, Twox64Concat, PoolId, Twox64Concat, FungibleTokenId, BalanceOf, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn reward_frequency_per_era)] + pub type RewardEraFrequency = StorageValue<_, (BlockNumberFor, BalanceOf), ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { @@ -1150,6 +1154,10 @@ impl Pallet { pub fn get_pool_account() -> T::AccountId { T::PoolAccount::get().into_account_truncating() } + + pub fn get_pool_treasury(pool_id: PoolId) -> T::AccountId { + return T::PoolAccount::get().into_sub_account_truncating(pool_id); + } } impl RewardHandler for Pallet { From b7e098807d305bdf1e4c68c8534d7171244c10ce Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 14:13:32 +1300 Subject: [PATCH 103/155] WIP - Accumulating reward of network pool for all boosters --- pallets/spp/src/lib.rs | 81 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 365b3b685..3fa13da53 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -130,6 +130,9 @@ pub mod pallet { #[pallet::constant] type PoolAccount: Get; + #[pallet::constant] + type RewardPayoutAccount: Get; + #[pallet::constant] type MaximumQueue: Get; @@ -263,7 +266,11 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn reward_frequency_per_era)] - pub type RewardEraFrequency = StorageValue<_, (BlockNumberFor, BalanceOf), ValueQuery>; + pub type RewardEraFrequency = StorageValue<_, BalanceOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn is_reward_distribution_origin)] + pub type RewardDistributionOrigin = StorageValue<_, T::AccountId, OptionQuery>; #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] @@ -305,6 +312,10 @@ pub mod pallet { pool_id: PoolId, boost_info: BoostInfo>, }, + /// Reward distribution added + RewardDistributionAdded { who: T::AccountId }, + /// Reward distribution removed + RewardDistributionRemoved { who: T::AccountId }, } #[pallet::error] @@ -349,6 +360,10 @@ pub mod pallet { InsufficientFund, /// Error while adding new boost MaxVotesReached, + /// Reward distribution origin already exists + OriginsAlreadyExist, + /// Origin doesn't exists + OriginDoesNotExists, } #[pallet::hooks] @@ -804,6 +819,31 @@ pub mod pallet { Ok(()) } } + + #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] + pub fn add_reward_distribution_origin(origin: OriginFor, who: T::AccountId) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; + + ensure!( + Self::is_reward_distribution_origin() != who, + Error::::OriginsAlreadyExist + ); + + RewardDistributionOrigin::::put(who.clone()); + Self::deposit_event(Event::RewardDistributionAdded { who }); + Ok(()) + } + + #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] + pub fn remove_reward_distribution_origin(origin: OriginFor, who: T::AccountId) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; + + ensure!(is_reward_distribution_origin == who, Error::::OriginDoesNotExists); + + RewardDistributionOrigin::::remove(who.clone()); + Self::deposit_event(Event::RewardDistributionRemoved { who }); + Ok(()) + } } impl Pallet { @@ -955,7 +995,32 @@ impl Pallet { Ok(()) } - fn handle_reward_distribution_to_each_pool(pool_id: PoolId) -> DispatchResult {} + fn handle_reward_distribution_to_network_pool() -> DispatchResult { + // Get reward per era + // Accumulate reward to pool_id + let reward_per_era = RewardEraFrequency::::get(); + let reward_distribution_origin = RewardDistributionOrigin::::get(); + let reward_distribution_balance = T::Currency::free_balance(&RewardDistributionOrigin::::get()); + + if reward_distribution_balance.is_zero() || !reward_distribution_origin.is_some() { + // Ignore if reward distributor balance is zero + Ok(()) + } + + let mut amount_to_send = reward_per_era.clone(); + // Make user distributor account has enough balance + if amount_to_send > reward_distribution_balance { + amount_to_send = reward_distribution_balance + } + + T::Currency::transfer( + reward_distribution_origin, + Self::get_reward_payout_account_id(), + amount_to_send, + )?; + >::accumulate_reward(&Zero::zero(), FungibleTokenId::NativeToken(0), amount_to_send)?; + Ok(()) + } #[transactional] fn update_queue_request( @@ -1132,13 +1197,6 @@ impl Pallet { Ok(()) } - fn handle_boosting_reward_per_era(era_index: EraIndex) -> DispatchResult { - for boosted_pool_keys in NetworkBoostingInfo::::iter_keys() { - Self::handle_reward_distribution_to_each_pool(boosted_pool_keys)?; - } - Ok(()) - } - #[transactional] pub fn update_current_era(era_index: EraIndex) -> DispatchResult { let previous_era = Self::relay_chain_current_era(); @@ -1147,6 +1205,7 @@ impl Pallet { RelayChainCurrentEra::::put(new_era); LastEraUpdatedBlock::::put(T::RelayChainBlockNumber::current_block_number()); Self::handle_redeem_requests(new_era)?; + Self::handle_reward_distribution_to_network_pool()?; Self::deposit_event(Event::::CurrentEraUpdated { new_era_index: new_era }); Ok(()) } @@ -1158,6 +1217,10 @@ impl Pallet { pub fn get_pool_treasury(pool_id: PoolId) -> T::AccountId { return T::PoolAccount::get().into_sub_account_truncating(pool_id); } + + pub fn get_reward_payout_account_id() -> T::AccountId { + T::RewardPayoutAccount::get().into_account_truncating() + } } impl RewardHandler for Pallet { From 9d86b1fdee0f226a3bd59ae12a06fe0cc193fd42 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 15:29:02 +1300 Subject: [PATCH 104/155] WIP - Claim reward logic that allow rewards distributed can be claimed by booster --- pallets/spp/src/lib.rs | 152 +++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 67 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 3fa13da53..ea47d2dda 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -21,7 +21,7 @@ use frame_support::pallet_prelude::*; use frame_support::traits::LockIdentifier; use frame_support::{ dispatch::DispatchResult, - ensure, + ensure, log, traits::{Currency, Get}, transactional, PalletId, }; @@ -272,6 +272,21 @@ pub mod pallet { #[pallet::getter(fn is_reward_distribution_origin)] pub type RewardDistributionOrigin = StorageValue<_, T::AccountId, OptionQuery>; + /// The pending rewards amount, actual available rewards amount may be deducted + /// + /// PendingRewards: double_map PoolId, AccountId => BTreeMap + #[pallet::storage] + #[pallet::getter(fn pending_multi_rewards)] + pub type PendingRewards = StorageDoubleMap< + _, + Twox64Concat, + PoolId, + Twox64Concat, + T::AccountId, + BTreeMap>, + ValueQuery, + >; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { @@ -751,73 +766,13 @@ pub mod pallet { Ok(()) } + } - /// This function allow reward voting for the pool - #[pallet::weight(< T as Config >::WeightInfo::mint_land())] - pub fn claim_reward(origin: OriginFor, pool_id: PoolId) -> DispatchResult { - // Ensure user is signed - let who = ensure_signed(origin)?; - - // orml_rewards will claim rewards for all currencies rewards - >::claim_rewards(&who, &pool_id); - - // Check if pool exists - ensure!(Pool::::get(pool_id).is_some(), Error::::PoolDoesNotExist); - // Still need to work out some - // Convert boost conviction into shares - let vote_conviction = vote.conviction.lock_periods(); - // Calculate lock period from UnlockDuration block number x conviction - let current_block: T::BlockNumber = >::block_number(); - - let mut unlock_at = current_block.saturating_add(UpdateEraFrequency::::get()); - let mut total_balance = vote.balance; - if !vote_conviction.is_zero() { - unlock_at.saturating_mul(vote_conviction.into()); - total_balance.saturating_mul(vote_conviction.into()); - } - // Locked token - - BoostingOf::::try_mutate(who.clone(), |voting| -> DispatchResult { - let votes = &mut voting.votes; - match votes.binary_search_by_key(&pool_id, |i| i.0) { - Ok(i) => { - // User already boosted, this is adding up their boosting weight - votes[i] - .1 - .add(total_balance.clone()) - .ok_or(Error::::ArithmeticOverflow)?; - voting - .prior - .accumulate(unlock_at, votes[i].1.balance.saturating_add(total_balance)) - } - Err(i) => { - votes.insert(i, (pool_id, vote.clone())); - voting.prior.accumulate(unlock_at, total_balance); - } - } - Ok(()) - })?; - T::Currency::extend_lock( - BOOSTING_ID, - &who, - vote.balance, - frame_support::traits::WithdrawReasons::TRANSFER, - ); - - // Add shares into the rewards pool - >::add_share(&who, &pool_id, total_balance.unique_saturated_into()); - // Add shares into the network pool - >::add_share(&who, &Zero::zero(), total_balance.unique_saturated_into()); - - // Emit Boosted event - Self::deposit_event(Event::::Boosted { - booster: who.clone(), - pool_id, - boost_info: vote.clone(), - }); + #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] + pub fn claim_rewards(origin: OriginFor, pool_id: PoolId) -> DispatchResult { + let who = ensure_signed(origin)?; - Ok(()) - } + Self::do_claim_rewards(who, pool_id) } #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] @@ -1221,16 +1176,79 @@ impl Pallet { pub fn get_reward_payout_account_id() -> T::AccountId { T::RewardPayoutAccount::get().into_account_truncating() } + + fn do_claim_rewards(who: T::AccountId, pool_id: PoolId) -> DispatchResult { + if pool_id == Zero::zero() { + >::claim_rewards(&who, &pool_id); + + PendingRewards::::mutate_exists(pool_id, &who, |maybe_pending_multi_rewards| { + if let Some(pending_multi_rewards) = maybe_pending_multi_rewards { + for (currency_id, pending_reward) in pending_multi_rewards.iter_mut() { + if pending_reward.is_zero() { + continue; + } + + match Self::payout_reward(pool_id, &who, *currency_id, payout_amount) { + Ok(_) => { + // update state + *pending_reward = Zero::zero(); + + Self::deposit_event(Event::ClaimRewards { + who: who.clone(), + pool: pool_id, + reward_currency_id: FungibleTokenId::NativeToken(0), + actual_amount: payout_amount, + }); + } + Err(e) => { + log::error!( + target: "spp", + "payout_reward: failed to payout {:?} to {:?} to pool {:?}: {:?}", + payout_amount, who, pool_id, e + ); + } + } + } + } + }) + } + + Ok(()) + } + + /// Ensure atomic + #[transactional] + fn payout_reward( + pool_id: PoolId, + who: &T::AccountId, + reward_currency_id: FungibleTokenId, + payout_amount: BalanceOf, + ) -> DispatchResult { + T::MultiCurrency::transfer( + reward_currency_id, + &Self::get_reward_payout_account_id(), + who, + payout_amount, + )?; + Ok(()) + } } impl RewardHandler for Pallet { type Balance = BalanceOf; type PoolId = PoolId; + /// This function trigger by orml_reward claim_rewards, it will modify and add pending reward + /// into PendingRewards for users to claim fn payout(who: &T::AccountId, pool_id: &Self::PoolId, currency_id: FungibleTokenId, payout_amount: Self::Balance) { if payout_amount.is_zero() { return; } - // TODO implement payout logic + PendingRewards::::mutate(pool_id, who, |rewards| { + rewards + .entry(currency_id) + .and_modify(|current| *current = current.saturating_add(payout_amount)) + .or_insert(payout_amount); + }); } } From 1494a41a60cb4385c1bd222b7f77309041b251c1 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 18:07:25 +1300 Subject: [PATCH 105/155] WIP - Fix build compilation errors of handle reward distribution logic --- pallets/spp/src/lib.rs | 76 +++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index ea47d2dda..5cb1ef496 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; -use frame_support::traits::LockIdentifier; +use frame_support::traits::{ExistenceRequirement, LockIdentifier}; use frame_support::{ dispatch::DispatchResult, ensure, log, @@ -133,6 +133,9 @@ pub mod pallet { #[pallet::constant] type RewardPayoutAccount: Get; + #[pallet::constant] + type RewardHoldingAccount: Get; + #[pallet::constant] type MaximumQueue: Get; @@ -268,10 +271,6 @@ pub mod pallet { #[pallet::getter(fn reward_frequency_per_era)] pub type RewardEraFrequency = StorageValue<_, BalanceOf, ValueQuery>; - #[pallet::storage] - #[pallet::getter(fn is_reward_distribution_origin)] - pub type RewardDistributionOrigin = StorageValue<_, T::AccountId, OptionQuery>; - /// The pending rewards amount, actual available rewards amount may be deducted /// /// PendingRewards: double_map PoolId, AccountId => BTreeMap @@ -327,10 +326,13 @@ pub mod pallet { pool_id: PoolId, boost_info: BoostInfo>, }, - /// Reward distribution added - RewardDistributionAdded { who: T::AccountId }, - /// Reward distribution removed - RewardDistributionRemoved { who: T::AccountId }, + /// Claim rewards. + ClaimRewards { + who: T::AccountId, + pool: PoolId, + reward_currency_id: FungibleTokenId, + claimed_amount: BalanceOf, + }, } #[pallet::error] @@ -766,38 +768,13 @@ pub mod pallet { Ok(()) } - } - - #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] - pub fn claim_rewards(origin: OriginFor, pool_id: PoolId) -> DispatchResult { - let who = ensure_signed(origin)?; - - Self::do_claim_rewards(who, pool_id) - } - - #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] - pub fn add_reward_distribution_origin(origin: OriginFor, who: T::AccountId) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; - ensure!( - Self::is_reward_distribution_origin() != who, - Error::::OriginsAlreadyExist - ); - - RewardDistributionOrigin::::put(who.clone()); - Self::deposit_event(Event::RewardDistributionAdded { who }); - Ok(()) - } - - #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] - pub fn remove_reward_distribution_origin(origin: OriginFor, who: T::AccountId) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; - - ensure!(is_reward_distribution_origin == who, Error::::OriginDoesNotExists); + #[pallet::weight(< T as pallet::Config >::WeightInfo::mint_land())] + pub fn claim_rewards(origin: OriginFor, pool_id: PoolId) -> DispatchResult { + let who = ensure_signed(origin)?; - RewardDistributionOrigin::::remove(who.clone()); - Self::deposit_event(Event::RewardDistributionRemoved { who }); - Ok(()) + Self::do_claim_rewards(who, pool_id) + } } } @@ -954,12 +931,12 @@ impl Pallet { // Get reward per era // Accumulate reward to pool_id let reward_per_era = RewardEraFrequency::::get(); - let reward_distribution_origin = RewardDistributionOrigin::::get(); - let reward_distribution_balance = T::Currency::free_balance(&RewardDistributionOrigin::::get()); + let reward_distribution_origin = T::RewardHoldingAccount::get().into_account_truncating(); + let reward_distribution_balance = T::Currency::free_balance(&reward_distribution_origin); - if reward_distribution_balance.is_zero() || !reward_distribution_origin.is_some() { + if reward_distribution_balance.is_zero() { // Ignore if reward distributor balance is zero - Ok(()) + return Ok(()); } let mut amount_to_send = reward_per_era.clone(); @@ -969,9 +946,10 @@ impl Pallet { } T::Currency::transfer( - reward_distribution_origin, - Self::get_reward_payout_account_id(), + &reward_distribution_origin, + &Self::get_reward_payout_account_id(), amount_to_send, + ExistenceRequirement::KeepAlive, )?; >::accumulate_reward(&Zero::zero(), FungibleTokenId::NativeToken(0), amount_to_send)?; Ok(()) @@ -1178,7 +1156,7 @@ impl Pallet { } fn do_claim_rewards(who: T::AccountId, pool_id: PoolId) -> DispatchResult { - if pool_id == Zero::zero() { + if pool_id.is_zero() { >::claim_rewards(&who, &pool_id); PendingRewards::::mutate_exists(pool_id, &who, |maybe_pending_multi_rewards| { @@ -1188,7 +1166,7 @@ impl Pallet { continue; } - match Self::payout_reward(pool_id, &who, *currency_id, payout_amount) { + match Self::payout_reward(pool_id, &who, *currency_id, *pending_reward) { Ok(_) => { // update state *pending_reward = Zero::zero(); @@ -1197,14 +1175,14 @@ impl Pallet { who: who.clone(), pool: pool_id, reward_currency_id: FungibleTokenId::NativeToken(0), - actual_amount: payout_amount, + claimed_amount: *pending_reward, }); } Err(e) => { log::error!( target: "spp", "payout_reward: failed to payout {:?} to {:?} to pool {:?}: {:?}", - payout_amount, who, pool_id, e + pending_reward, who, pool_id, e ); } } From 519f92edb9108033e6b90ed62c27c93f62ebd762 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 18:08:46 +1300 Subject: [PATCH 106/155] WIP - Update reward distribution logic comment --- pallets/spp/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 5cb1ef496..a15cbcde8 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -928,25 +928,25 @@ impl Pallet { } fn handle_reward_distribution_to_network_pool() -> DispatchResult { - // Get reward per era - // Accumulate reward to pool_id + // Get reward per era that set up Governance let reward_per_era = RewardEraFrequency::::get(); - let reward_distribution_origin = T::RewardHoldingAccount::get().into_account_truncating(); - let reward_distribution_balance = T::Currency::free_balance(&reward_distribution_origin); + // Get reward holding account + let reward_holding_origin = T::RewardHoldingAccount::get().into_account_truncating(); + let reward_holding_balance = T::Currency::free_balance(&reward_distribution_origin); - if reward_distribution_balance.is_zero() { + if reward_holding_balance.is_zero() { // Ignore if reward distributor balance is zero return Ok(()); } let mut amount_to_send = reward_per_era.clone(); - // Make user distributor account has enough balance - if amount_to_send > reward_distribution_balance { - amount_to_send = reward_distribution_balance + // Make sure user distributor account has enough balance + if amount_to_send > reward_holding_balance { + amount_to_send = reward_holding_balance } T::Currency::transfer( - &reward_distribution_origin, + &reward_holding_origin, &Self::get_reward_payout_account_id(), amount_to_send, ExistenceRequirement::KeepAlive, From 1d52db7886576c2e35664208653e88c7358c5d66 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 19:08:16 +1300 Subject: [PATCH 107/155] WIP - Remove max bound --- pallets/spp/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index a15cbcde8..41c74c37f 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -109,9 +109,6 @@ pub mod pallet { #[pallet::constant] type RewardPaymentDelay: Get; - /// Default max bound for each metaverse mapping system, this could change through proposal - type DefaultMaxBound: Get<(i32, i32)>; - /// Network fee charged on pool creation #[pallet::constant] type NetworkFee: Get>; @@ -932,7 +929,7 @@ impl Pallet { let reward_per_era = RewardEraFrequency::::get(); // Get reward holding account let reward_holding_origin = T::RewardHoldingAccount::get().into_account_truncating(); - let reward_holding_balance = T::Currency::free_balance(&reward_distribution_origin); + let reward_holding_balance = T::Currency::free_balance(&reward_holding_origin); if reward_holding_balance.is_zero() { // Ignore if reward distributor balance is zero From 964e7290f8c8d6083e0a65b94a9a3976fb35441a Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 19:08:36 +1300 Subject: [PATCH 108/155] WIP - Implement extra config for mock runtime --- pallets/spp/src/mock.rs | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 77283e38f..1dbbd347f 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -41,37 +41,12 @@ pub const AUCTION_BENEFICIARY_ID: AccountId = 100; pub const CLASS_FUND_ID: AccountId = 123; pub const METAVERSE_ID: MetaverseId = 0; pub const DOLLARS: Balance = 1_000_000_000_000_000_000; -pub const ALICE_METAVERSE_ID: MetaverseId = 1; -pub const BOB_METAVERSE_ID: MetaverseId = 2; -pub const MAX_BOUND: (i32, i32) = (-100, 100); -pub const LANDBLOCK_COORDINATE: (i32, i32) = (0, 0); -pub const COORDINATE_IN_1: (i32, i32) = (-4, 4); -pub const COORDINATE_IN_2: (i32, i32) = (-4, 5); -pub const COORDINATE_IN_3: (i32, i32) = (-4, 6); -pub const COORDINATE_IN_4: (i32, i32) = (-4, 8); -pub const COORDINATE_OUT: (i32, i32) = (0, 101); -pub const COORDINATE_IN_AUCTION: (i32, i32) = (-4, 7); -pub const ESTATE_IN_AUCTION: EstateId = 3; pub const BOND_AMOUNT_1: Balance = 1000; pub const BOND_AMOUNT_2: Balance = 2000; pub const BOND_AMOUNT_BELOW_MINIMUM: Balance = 100; pub const BOND_LESS_AMOUNT_1: Balance = 100; -pub const ESTATE_ID: EstateId = 0; - -pub const ASSET_ID_1: TokenId = 101; -pub const ASSET_ID_2: TokenId = 100; -pub const ASSET_CLASS_ID: ClassId = 5; -pub const ASSET_TOKEN_ID: TokenId = 6; -pub const ASSET_COLLECTION_ID: GroupCollectionId = 7; -pub const METAVERSE_LAND_CLASS: ClassId = 15; -pub const METAVERSE_LAND_IN_AUCTION_TOKEN: TokenId = 4; -pub const METAVERSE_ESTATE_CLASS: ClassId = 16; -pub const METAVERSE_ESTATE_IN_AUCTION_TOKEN: TokenId = 3; - -pub const GENERAL_METAVERSE_FUND: AccountId = 102; - ord_parameter_types! { pub const One: AccountId = ALICE; pub const Admin: AccountId = ALICE; @@ -134,9 +109,9 @@ impl pallet_balances::Config for Runtime { parameter_types! { pub const GetNativeCurrencyId: FungibleTokenId = FungibleTokenId::NativeToken(0); - pub const LandTreasuryPalletId: PalletId = PalletId(*b"bit/land"); pub const PoolAccountPalletId: PalletId = PalletId(*b"bit/pool"); - pub const MinimumLandPrice: Balance = 10 * DOLLARS; + pub const RewardPayoutAccount: PalletId = PalletId(*b"bit/payo"); + pub const RewardHoldingAccount: PalletId = PalletId(*b"bit/hold"); } fn test_attributes(x: u8) -> Attributes { @@ -207,7 +182,6 @@ parameter_types! { pub const MinimumStake: Balance = 200; /// Reward payments are delayed by 2 hours (2 * 300 * block_time) pub const RewardPaymentDelay: u32 = 2; - pub const DefaultMaxBound: (i32,i32) = MAX_BOUND; pub const NetworkFee: Balance = 1; // Network fee pub const MaxOffersPerEstate: u32 = 2; pub const MinLeasePricePerBlock: Balance = 1u128; @@ -224,7 +198,6 @@ impl Config for Runtime { type WeightInfo = (); type MinimumStake = MinimumStake; type RewardPaymentDelay = RewardPaymentDelay; - type DefaultMaxBound = DefaultMaxBound; type NetworkFee = NetworkFee; type BlockNumberToBalance = ConvertInto; type StorageDepositFee = StorageDepositFee; @@ -234,6 +207,8 @@ impl Config for Runtime { type CurrencyIdConversion = ForeignAssetMapping; type RelayChainBlockNumber = MockRelayBlockNumberProvider; type GovernanceOrigin = EnsureSignedBy; + type RewardPayoutAccount = RewardPayoutAccount; + type RewardHoldingAccount = RewardHoldingAccount; } construct_runtime!( From 37a0d4eaceb634b008120452adad47409c5916c3 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 23:20:04 +1300 Subject: [PATCH 109/155] WIP - Implement extra reward holding account id --- pallets/spp/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 41c74c37f..f4cac11fa 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -1152,6 +1152,10 @@ impl Pallet { T::RewardPayoutAccount::get().into_account_truncating() } + pub fn get_reward_holding_account_id() -> T::AccountId { + T::RewardHoldingAccount::get().into_account_truncating() + } + fn do_claim_rewards(who: T::AccountId, pool_id: PoolId) -> DispatchResult { if pool_id.is_zero() { >::claim_rewards(&who, &pool_id); From b0c1982756bdd05e26f7a51b53406f77aa6f5ea9 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Nov 2023 23:20:29 +1300 Subject: [PATCH 110/155] WIP - Unit test - boosting and reward distribution works unit tests --- pallets/spp/src/tests.rs | 115 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index a9b9d913d..e39f948bf 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -173,7 +173,7 @@ fn redeem_rksm_request_works() { 10000 )); - // After Bob redeems, pool ledger 0 should have only 10000 + // After Bob redeems, pool ledger 1 should have only 10000 assert_eq!(PoolLedger::::get(1), 10000); // Verify if redeem queue has requests @@ -439,3 +439,116 @@ fn boosting_works() { assert_eq!(network_shared_rewards, (bob_free_balance, Default::default())); }); } + +#[test] +fn boosting_and_claim_reward_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + // Era config set up + // Current relaychain block is 102. + MockRelayBlockNumberProvider::set(102); + RelayChainCurrentEra::::put(1); + IterationLimit::::put(50); + // The correct set up era config is the last era block records is 101 with duration is 100 blocks + assert_ok!(SppModule::update_era_config( + RuntimeOrigin::signed(Admin::get()), + Some(101), + Some(100), + StakingRound::Era(1), + )); + + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Permill::from_percent(5) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 2); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Permill::from_percent(5), + currency_id: FungibleTokenId::NativeToken(1), + max: 50 + } + ); + + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); + // This is true because fee hasn't been set up. + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + + assert_eq!(PoolLedger::::get(1), 10000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); + + // Deposit another 10000 KSM + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 20000); + + assert_eq!(PoolLedger::::get(1), 20000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 20000); + + // Boosting works + let bob_free_balance = Balances::free_balance(BOB); + assert_ok!(SppModule::boost( + RuntimeOrigin::signed(BOB), + 1, + BoostInfo { + balance: 15000, + conviction: BoostingConviction::None + } + )); + let boosting_of = BoostingOf::::get(BOB); + let some_record = BoostingRecord { + votes: vec![( + 1, + BoostInfo { + balance: 15000, + conviction: BoostingConviction::None, + }, + )], + prior: PriorLock(101, 15000), + }; + assert_eq!(boosting_of, some_record); + assert_eq!(Balances::usable_balance(&BOB), bob_free_balance - 15000); + let pool_1_shared_rewards = RewardsModule::shares_and_withdrawn_rewards(1, BOB); + let network_shared_rewards = RewardsModule::shares_and_withdrawn_rewards(0, BOB); + assert_eq!(pool_1_shared_rewards, (15000, Default::default())); + assert_eq!(network_shared_rewards, (15000, Default::default())); + + // Set reward per era. - 1000 NativeToken(0) per 100 blocks + RewardEraFrequency::::put(1000); + // Simulate Council transfer 10000 NativeToken to reward_payout_account so that account has + // sufficient balance for reward distribution + let reward_holding_account = SppModule::get_reward_holding_account_id(); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(ALICE), + reward_holding_account.clone(), + 10000 + )); + + // Move to era 2, now protocol distribute 1000 NEER to incentivise boosters + MockRelayBlockNumberProvider::set(202); + SppModule::on_initialize(200); + + let network_reward_pool = RewardsModule::pool_infos(0u32); + let reward_accumulated = RewardsModule::shares_and_withdrawn_rewards(0, BOB); + + // Verify after 1 era, total rewards should have 1000 NEER and 0 claimed + assert_eq!( + network_reward_pool, + orml_rewards::PoolInfo { + total_shares: 15000, + rewards: vec![(FungibleTokenId::NativeToken(0), (1000, 0))].into_iter().collect() + } + ); + + // Reward records of BOB holding 15000 shares and 0 claimed + assert_eq!(reward_accumulated, (15000, Default::default())); + // Reward distribution works, now let's do claim rewards + }); +} From 42d0fb11a426d68271a7404e40fb796372c6d920 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 22 Nov 2023 10:46:09 +1300 Subject: [PATCH 111/155] WIP - Copy pending reward into payout to avoid variable changes --- pallets/spp/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index f4cac11fa..0303c34ea 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -1167,7 +1167,9 @@ impl Pallet { continue; } - match Self::payout_reward(pool_id, &who, *currency_id, *pending_reward) { + let payout_amount = pending_reward.clone(); + + match Self::payout_reward(pool_id, &who, *currency_id, payout_amount) { Ok(_) => { // update state *pending_reward = Zero::zero(); @@ -1176,7 +1178,7 @@ impl Pallet { who: who.clone(), pool: pool_id, reward_currency_id: FungibleTokenId::NativeToken(0), - claimed_amount: *pending_reward, + claimed_amount: payout_amount, }); } Err(e) => { From 8b548c0a2a5be48566e33d5fcdabc6e0f5668fca Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 22 Nov 2023 10:46:28 +1300 Subject: [PATCH 112/155] WIP - Unit test - ensure claim reward successful --- pallets/spp/src/tests.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index e39f948bf..858ad3da6 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -549,6 +549,44 @@ fn boosting_and_claim_reward_works() { // Reward records of BOB holding 15000 shares and 0 claimed assert_eq!(reward_accumulated, (15000, Default::default())); - // Reward distribution works, now let's do claim rewards + // Reward distribution works, now claim rewards + let bob_balance_before_claiming_boosting_reward = Balances::free_balance(BOB); + // Bob claim rewards + assert_ok!(SppModule::claim_rewards(RuntimeOrigin::signed(BOB), 0)); + assert_eq!( + last_event(), + mock::RuntimeEvent::Spp(crate::Event::ClaimRewards { + who: BOB, + pool: 0, + reward_currency_id: FungibleTokenId::NativeToken(0), + claimed_amount: 1000, + }) + ); + + // Bob free balance now will be bob_balance_before_claiming_boosting_reward + 1000 as claimed reward + assert_eq!( + Balances::free_balance(BOB), + bob_balance_before_claiming_boosting_reward + 1000 + ); + + // Bob try to claim again but getting no reward + assert_ok!(SppModule::claim_rewards(RuntimeOrigin::signed(BOB), 0)); + // Bob balance doesn't increase + assert_eq!( + Balances::free_balance(BOB), + bob_balance_before_claiming_boosting_reward + 1000 + ); + + // Move to era 3, now protocol distribute another 1000 NEER to incentivise boosters + MockRelayBlockNumberProvider::set(302); + SppModule::on_initialize(300); + + // Bob try to claim reward for new era + assert_ok!(SppModule::claim_rewards(RuntimeOrigin::signed(BOB), 0)); + // Bob balance should increase 2000 + assert_eq!( + Balances::free_balance(BOB), + bob_balance_before_claiming_boosting_reward + 2000 + ); }); } From cddb58eddf6eb3d4c66b2dcf9e9c884c74b2e64a Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 22 Nov 2023 11:17:58 +1300 Subject: [PATCH 113/155] WIP - Unit test - add more cases of another booster --- pallets/spp/src/mock.rs | 2 +- pallets/spp/src/tests.rs | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index 1dbbd347f..ae1304727 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -251,7 +251,7 @@ impl ExtBuilder { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, 1000000000), (BOB, 100000), (BENEFICIARY_ID, 1000000)], + balances: vec![(ALICE, 1000000000), (BOB, 100000), (CHARLIE, 100000), (DOM, 100000)], } .assimilate_storage(&mut t) .unwrap(); diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 858ad3da6..480b20b4b 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -588,5 +588,55 @@ fn boosting_and_claim_reward_works() { Balances::free_balance(BOB), bob_balance_before_claiming_boosting_reward + 2000 ); + + // Charlie now boost pool 1 with 15000 (share 50% of reward with Bob) + assert_ok!(SppModule::boost( + RuntimeOrigin::signed(CHARLIE), + 1, + BoostInfo { + balance: 15000, + conviction: BoostingConviction::None + } + )); + // Charlie now should have 15000 shares in the pool + assert_eq!( + RewardsModule::shares_and_withdrawn_rewards(1, CHARLIE), + (15000, Default::default()) + ); + + // Network pool ledger should have total shares of 30,000 , 2000 total reward and claimed 2000 by + // Bob. However, as Charlie boosted, network pool inflate 15,000 shares, added 50% reward and 50% + // claimed reward to avoid dilution. + assert_eq!( + RewardsModule::pool_infos(0u32), + orml_rewards::PoolInfo { + total_shares: 30000, + rewards: vec![(FungibleTokenId::NativeToken(0), (4000, 4000))] + .into_iter() + .collect() + } + ); + + let charlie_balance_before_claiming_boosting_reward = Balances::free_balance(CHARLIE); + + // Move to era 4, now protocol distribute another 1000 NEER to incentivise boosters + MockRelayBlockNumberProvider::set(402); + SppModule::on_initialize(400); + + // Bob try to claim reward for new era + assert_ok!(SppModule::claim_rewards(RuntimeOrigin::signed(BOB), 0)); + // Bob balance should increase 500 as Charlie shares 50% rewards + assert_eq!( + Balances::free_balance(BOB), + bob_balance_before_claiming_boosting_reward + 2500 + ); + + // Charlie try to claim reward for new era + assert_ok!(SppModule::claim_rewards(RuntimeOrigin::signed(CHARLIE), 0)); + // Charlie balance should increase 500 + assert_eq!( + Balances::free_balance(CHARLIE), + charlie_balance_before_claiming_boosting_reward + 500 + ); }); } From 68a504aae32eadea03d24d4975fd10046815308a Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 22 Nov 2023 20:32:36 +1300 Subject: [PATCH 114/155] WIP - Handle reward distribution to all pool treasury --- pallets/spp/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 0303c34ea..e9d39b222 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -28,10 +28,10 @@ use frame_support::{ use frame_system::ensure_signed; use frame_system::pallet_prelude::*; use orml_traits::{MultiCurrency, RewardHandler}; -use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedSub}; +use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedSub, UniqueSaturatedInto}; use sp_runtime::{ traits::{AccountIdConversion, Convert, Saturating, Zero}, - ArithmeticError, DispatchError, SaturatedConversion, + ArithmeticError, DispatchError, Perbill, Permill, SaturatedConversion, }; use core_primitives::*; @@ -270,7 +270,7 @@ pub mod pallet { /// The pending rewards amount, actual available rewards amount may be deducted /// - /// PendingRewards: double_map PoolId, AccountId => BTreeMap + /// PendingRewards: double_map PoolId, AccountId => BTreeMap #[pallet::storage] #[pallet::getter(fn pending_multi_rewards)] pub type PendingRewards = StorageDoubleMap< @@ -283,6 +283,12 @@ pub mod pallet { ValueQuery, >; + /// The estimated staking reward rate per era on relaychain. + /// + /// EstimatedRewardRatePerEra: value: Rate + #[pallet::storage] + pub type EstimatedRewardRatePerEra = StorageValue<_, Permill, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { @@ -952,6 +958,31 @@ impl Pallet { Ok(()) } + fn handle_reward_distribution_to_pool_treasury(previous_era: EraIndex, new_era: EraIndex) -> DispatchResult { + // Get reward per era for pool treasury + let reward_rate_per_era = EstimatedRewardRatePerEra::::get(); + // Get total compound reward rate based on number of era. + let reward_rate = reward_rate_per_era + .saturating_add(Permill::one()) + .saturating_pow(new_era.saturating_sub(previous_era).unique_saturated_into()) + .saturating_sub(Permill::one()); + + if !reward_rate.is_zero() { + let mut total_reward_staking: BalanceOf = Zero::zero(); + + // iterate all pool ledgers + for (pool_id, pool_ledgers) in PoolLedger::::iter() { + let reward_staking = reward_rate.saturating_mul_int(pool_ledgers); + + if !reward_staking.is_zero() { + total_reward_staking = total_reward_staking.saturating_add(reward_staking); + } + } + } + + Ok(()) + } + #[transactional] fn update_queue_request( currency_id: FungibleTokenId, From 73c096a13729e15496842c563f7d925b3c4aed13 Mon Sep 17 00:00:00 2001 From: Ray Lu Date: Wed, 22 Nov 2023 21:17:39 +1300 Subject: [PATCH 115/155] Update tests.rs Added second pool in the pool test. --- pallets/spp/src/tests.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 480b20b4b..188d08d73 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -47,6 +47,8 @@ fn create_ksm_pool_works() { .ksm_setup_for_alice_and_bob() .build() .execute_with(|| { + + // Create the first pool assert_ok!(SppModule::create_pool( RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), @@ -54,8 +56,11 @@ fn create_ksm_pool_works() { Permill::from_percent(5) )); - let next_pool_id = NextPoolId::::get(); + // Check the next pool id will increment + let next_pool_id: u32 = NextPoolId::::get(); assert_eq!(next_pool_id, 2); + + // Check if the pool details as expected. assert_eq!( Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { @@ -64,7 +69,29 @@ fn create_ksm_pool_works() { currency_id: FungibleTokenId::NativeToken(1), max: 50 } - ) + ); + + // Create a second pool + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(BOB), + FungibleTokenId::NativeToken(1), + 10, + Permill::from_percent(1) + )); + + // Check Id will increment + let next_pool_id: u32 = NextPoolId::::get(); + assert_eq!(next_pool_id, 3); + // Check the second pool has the right information as expected + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: BOB, + commission: Permill::from_percent(1), + currency_id: FungibleTokenId::NativeToken(1), + max: 10 + } + ); }); } From 29e2a9314ea53e1ef3acf4b2c0e47ff8b207b1a9 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 22 Nov 2023 23:31:05 +1300 Subject: [PATCH 116/155] WIP - Inflate more share of commission reward into pool treasury --- pallets/spp/src/lib.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index e9d39b222..7a8df6afd 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -28,7 +28,9 @@ use frame_support::{ use frame_system::ensure_signed; use frame_system::pallet_prelude::*; use orml_traits::{MultiCurrency, RewardHandler}; -use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedSub, UniqueSaturatedInto}; +use sp_runtime::traits::{ + BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, UniqueSaturatedInto, +}; use sp_runtime::{ traits::{AccountIdConversion, Convert, Saturating, Zero}, ArithmeticError, DispatchError, Perbill, Permill, SaturatedConversion, @@ -968,14 +970,22 @@ impl Pallet { .saturating_sub(Permill::one()); if !reward_rate.is_zero() { - let mut total_reward_staking: BalanceOf = Zero::zero(); - // iterate all pool ledgers - for (pool_id, pool_ledgers) in PoolLedger::::iter() { - let reward_staking = reward_rate.saturating_mul_int(pool_ledgers); + for (pool_id, pool_amount) in PoolLedger::::iter() { + let reward_staking = reward_rate * Permill::from_percent(1u32) * pool_amount; if !reward_staking.is_zero() { - total_reward_staking = total_reward_staking.saturating_add(reward_staking); + let pool_treasury_account = Self::get_pool_treasury(pool_id); + T::MultiCurrency::deposit( + FungibleTokenId::FungibleToken(1), + &pool_treasury_account, + reward_staking, + ); + >::accumulate_reward( + &pool_id, + FungibleTokenId::FungibleToken(1), + reward_staking, + )?; } } } From 1c72db4e83d24f5ac40193073a91485e87e9594d Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 10:23:41 +1300 Subject: [PATCH 117/155] WIP - Add safe bounded check for fractional avoid precision loss. --- primitives/metaverse/src/bounded.rs | 240 ++++++++++++++++++++++++++++ primitives/metaverse/src/lib.rs | 1 + 2 files changed, 241 insertions(+) create mode 100644 primitives/metaverse/src/bounded.rs diff --git a/primitives/metaverse/src/bounded.rs b/primitives/metaverse/src/bounded.rs new file mode 100644 index 000000000..0e900f738 --- /dev/null +++ b/primitives/metaverse/src/bounded.rs @@ -0,0 +1,240 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::Get; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize}; +use sp_runtime::{ + traits::{CheckedSub, One, Zero}, + FixedPointNumber, FixedU128, RuntimeDebug, +}; +use sp_std::{marker::PhantomData, prelude::*, result::Result}; + +use primitives::{Balance, BlockNumber}; + +pub type Rate = FixedU128; + +/// The bounded type errors. +#[derive(RuntimeDebug, PartialEq, Eq)] +pub enum Error { + /// The value is out of bound. + OutOfBounds, + /// The change diff exceeds the max absolute value. + ExceedMaxChangeAbs, +} +/// An abstract definition of bounded type. The type is within the range of `Range` +/// and while update the inner value, the max absolute value of the diff is `MaxChangeAbs`. +/// The `Default` value is minimum value of the range. +#[cfg_attr(feature = "std", derive(Serialize), serde(transparent))] +#[derive(Encode, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)] +#[scale_info(skip_type_params(Range, MaxChangeAbs))] +pub struct BoundedType( + T, + #[cfg_attr(feature = "std", serde(skip_serializing))] PhantomData<(Range, MaxChangeAbs)>, +); + +impl, MaxChangeAbs: Get> Decode + for BoundedType +{ + fn decode(input: &mut I) -> Result { + let inner = T::decode(input)?; + Self::try_from(inner).map_err(|_| "BoundedType: value out of bounds".into()) + } +} + +#[cfg(feature = "std")] +impl<'de, T, Range, MaxChangeAbs> Deserialize<'de> for BoundedType +where + T: Encode + Decode + CheckedSub + PartialOrd + Deserialize<'de>, + Range: Get<(T, T)>, + MaxChangeAbs: Get, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value: T = T::deserialize(deserializer)?; + Self::try_from(value).map_err(|_| SerdeError::custom("out of bounds")) + } +} + +impl, MaxChangeAbs: Get> Default + for BoundedType +{ + fn default() -> Self { + let (min, _) = Range::get(); + Self(min, PhantomData) + } +} + +impl BoundedType +where + T: Encode + Decode + CheckedSub + PartialOrd, + Range: Get<(T, T)>, + MaxChangeAbs: Get, +{ + /// Try to create a new instance of `BoundedType`. Returns `Err` if out of bound. + pub fn try_from(value: T) -> Result { + let (min, max) = Range::get(); + if value < min || value > max { + return Err(Error::OutOfBounds); + } + Ok(Self(value, PhantomData)) + } + + /// Set the inner value. Returns `Err` if out of bound or the diff with current value exceeds + /// the max absolute value. + pub fn try_set(&mut self, value: T) -> Result<(), Error> { + let (min, max) = Range::get(); + let max_change_abs = MaxChangeAbs::get(); + let old_value = &self.0; + if value < min || value > max { + return Err(Error::OutOfBounds); + } + + let abs = if value > *old_value { + value + .checked_sub(old_value) + .expect("greater number subtracting smaller one can't underflow; qed") + } else { + old_value + .checked_sub(&value) + .expect("greater number subtracting smaller one can't underflow; qed") + }; + if abs > max_change_abs { + return Err(Error::ExceedMaxChangeAbs); + } + + self.0 = value; + Ok(()) + } + + pub fn into_inner(self) -> T { + self.0 + } + + pub fn inner(&self) -> &T { + &self.0 + } +} + +/// Fractional range between `Rate::zero()` and `Rate::one()`. +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] +pub struct Fractional; +impl Get<(Rate, Rate)> for Fractional { + fn get() -> (Rate, Rate) { + (Rate::zero(), Rate::one()) + } +} + +/// Maximum absolute change is 1/5. +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] +pub struct OneFifth; +impl Get for OneFifth { + fn get() -> Rate { + Rate::saturating_from_rational(1, 5) + } +} + +pub type BoundedRate = BoundedType; + +/// Fractional rate. +/// +/// The range is between 0 to 1, and max absolute value of change diff is 1/5. +pub type FractionalRate = BoundedRate; + +pub type BoundedBalance = BoundedType; + +pub type BoundedBlockNumber = BoundedType; + +#[cfg(test)] +mod tests { + use frame_support::{assert_err, assert_ok}; + + use super::*; + + #[test] + fn fractional_rate_works() { + assert_err!( + FractionalRate::try_from(Rate::from_rational(11, 10)), + Error::OutOfBounds + ); + + let mut rate = FractionalRate::try_from(Rate::from_rational(8, 10)).unwrap(); + assert_ok!(rate.try_set(Rate::from_rational(10, 10))); + assert_err!(rate.try_set(Rate::from_rational(11, 10)), Error::OutOfBounds); + assert_err!(rate.try_set(Rate::from_rational(79, 100)), Error::ExceedMaxChangeAbs); + + assert_eq!(FractionalRate::default().into_inner(), Rate::zero()); + } + + #[test] + fn encode_decode_works() { + let rate = FractionalRate::try_from(Rate::from_rational(8, 10)).unwrap(); + let encoded = rate.encode(); + assert_eq!(FractionalRate::decode(&mut &encoded[..]).unwrap(), rate); + + assert_eq!(encoded, Rate::from_rational(8, 10).encode()); + } + + #[test] + fn decode_fails_if_out_of_bounds() { + let bad_rate = BoundedType::(Rate::from_rational(11, 10), PhantomData); + let bad_rate_encoded = bad_rate.encode(); + assert_err!( + FractionalRate::decode(&mut &bad_rate_encoded[..]), + "BoundedType: value out of bounds" + ); + } + + #[test] + fn ser_de_works() { + let rate = FractionalRate::try_from(Rate::from_rational(8, 10)).unwrap(); + assert_eq!(serde_json::json!(&rate).to_string(), r#""800000000000000000""#); + + let deserialized: FractionalRate = serde_json::from_str(r#""800000000000000000""#).unwrap(); + assert_eq!(deserialized, rate); + } + + #[test] + fn deserialize_fails_if_out_of_bounds() { + let failed: Result = serde_json::from_str(r#""1100000000000000000""#); + match failed { + Err(msg) => assert_eq!(msg.to_string(), "out of bounds"), + _ => panic!("should fail"), + } + } + + #[test] + fn bounded_type_default_is_range_min() { + #[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] + pub struct OneToTwo; + impl Get<(Rate, Rate)> for OneToTwo { + fn get() -> (Rate, Rate) { + (Rate::one(), Rate::from_rational(2, 1)) + } + } + + type BoundedRateOneToTwo = BoundedRate; + + assert_eq!(BoundedRateOneToTwo::default().into_inner(), Rate::one()); + } +} diff --git a/primitives/metaverse/src/lib.rs b/primitives/metaverse/src/lib.rs index 49fe3ce07..ad1544bc9 100644 --- a/primitives/metaverse/src/lib.rs +++ b/primitives/metaverse/src/lib.rs @@ -34,6 +34,7 @@ use sp_std::prelude::*; use sp_std::vec::Vec; use xcm::v3::MultiLocation; +pub mod bounded; pub mod continuum; pub mod estate; pub mod evm; From ca55483d72ee30987f236135d99e0e9fb448a3c1 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 10:24:30 +1300 Subject: [PATCH 118/155] WIP - Update type path. --- pallets/spp/src/lib.rs | 2 +- primitives/metaverse/src/bounded.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 7a8df6afd..678f1cebd 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -980,7 +980,7 @@ impl Pallet { FungibleTokenId::FungibleToken(1), &pool_treasury_account, reward_staking, - ); + )?; >::accumulate_reward( &pool_id, FungibleTokenId::FungibleToken(1), diff --git a/primitives/metaverse/src/bounded.rs b/primitives/metaverse/src/bounded.rs index 0e900f738..991640223 100644 --- a/primitives/metaverse/src/bounded.rs +++ b/primitives/metaverse/src/bounded.rs @@ -28,7 +28,7 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, prelude::*, result::Result}; -use primitives::{Balance, BlockNumber}; +use crate::{Balance, BlockNumber}; pub type Rate = FixedU128; From 76fcbd136392e9d5e64c2a79e5978a214b8740e4 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 12:18:56 +1300 Subject: [PATCH 119/155] WIP - Update reward rate into fixedu128 to avoid precision loss --- pallets/spp/src/lib.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 678f1cebd..58f137b6a 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -29,16 +29,17 @@ use frame_system::ensure_signed; use frame_system::pallet_prelude::*; use orml_traits::{MultiCurrency, RewardHandler}; use sp_runtime::traits::{ - BlockNumberProvider, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, UniqueSaturatedInto, + BlockNumberProvider, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, UniqueSaturatedInto, }; use sp_runtime::{ traits::{AccountIdConversion, Convert, Saturating, Zero}, - ArithmeticError, DispatchError, Perbill, Permill, SaturatedConversion, + ArithmeticError, DispatchError, FixedPointNumber, Perbill, Permill, SaturatedConversion, }; use core_primitives::*; pub use pallet::*; -use primitives::{ClassId, EraIndex, FungibleTokenId, PoolId, StakingRound, TokenId}; +use primitives::bounded::Rate; +use primitives::{ClassId, EraIndex, FungibleTokenId, PoolId, Ratio, StakingRound, TokenId}; pub use weights::WeightInfo; pub type QueueId = u32; @@ -66,6 +67,7 @@ pub mod pallet { use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub, One, UniqueSaturatedInto}; use sp_runtime::Permill; + use primitives::bounded::FractionalRate; use primitives::{PoolId, StakingRound}; use crate::utils::{BoostInfo, BoostingRecord, PoolInfo}; @@ -289,7 +291,7 @@ pub mod pallet { /// /// EstimatedRewardRatePerEra: value: Rate #[pallet::storage] - pub type EstimatedRewardRatePerEra = StorageValue<_, Permill, ValueQuery>; + pub type EstimatedRewardRatePerEra = StorageValue<_, FractionalRate, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] @@ -960,31 +962,44 @@ impl Pallet { Ok(()) } + pub(crate) fn estimated_reward_rate_per_era() -> Rate { + EstimatedRewardRatePerEra::::get().into_inner() + } + fn handle_reward_distribution_to_pool_treasury(previous_era: EraIndex, new_era: EraIndex) -> DispatchResult { + let era_changes = new_era.saturating_sub(previous_era); + ensure!(!era_changes.is_zero(), Error::::Unexpected); // Get reward per era for pool treasury - let reward_rate_per_era = EstimatedRewardRatePerEra::::get(); + let reward_rate_per_era = Self::estimated_reward_rate_per_era(); // Get total compound reward rate based on number of era. let reward_rate = reward_rate_per_era - .saturating_add(Permill::one()) - .saturating_pow(new_era.saturating_sub(previous_era).unique_saturated_into()) - .saturating_sub(Permill::one()); + .saturating_add(Rate::one()) + .saturating_pow(era_changes.unique_saturated_into()) + .saturating_sub(Rate::one()); if !reward_rate.is_zero() { // iterate all pool ledgers for (pool_id, pool_amount) in PoolLedger::::iter() { - let reward_staking = reward_rate * Permill::from_percent(1u32) * pool_amount; + let mut total_reward_staking: BalanceOf = Zero::zero(); + let mut reward_staking = reward_rate.saturating_mul_int(pool_amount); if !reward_staking.is_zero() { let pool_treasury_account = Self::get_pool_treasury(pool_id); + total_reward_staking = total_reward_staking.saturating_add(reward_staking); + + let reward_commission_amount = Ratio::checked_from_rational(1, 100) + .unwrap_or_default() + .saturating_mul_int(total_reward_staking); + T::MultiCurrency::deposit( FungibleTokenId::FungibleToken(1), &pool_treasury_account, - reward_staking, + reward_commission_amount, )?; >::accumulate_reward( &pool_id, FungibleTokenId::FungibleToken(1), - reward_staking, + reward_commission_amount, )?; } } From c161cefaac6411a597c186fcaaf803ba2c9d2d37 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 12:52:46 +1300 Subject: [PATCH 120/155] WIP - Update fees from permill to rate --- pallets/spp/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 58f137b6a..b3c6e6200 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -154,7 +154,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn fees)] - pub type Fees = StorageValue<_, (Permill, Permill), ValueQuery>; + pub type Fees = StorageValue<_, (FractionalRate, FractionalRate), ValueQuery>; /// Keep track of Pool detail #[pallet::storage] @@ -409,7 +409,7 @@ pub mod pallet { origin: OriginFor, currency_id: FungibleTokenId, max_nft_reward: u32, - commission: Permill, + commission: Rate, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -824,7 +824,7 @@ impl Pallet { ) -> Result, DispatchError> { let (deposit_rate, _redeem_rate) = Fees::::get(); - let deposit_fee = deposit_rate * amount; + let deposit_fee = deposit_rate.into_inner().saturating_mul_int(amount); let amount_exclude_fee = amount.checked_sub(&deposit_fee).ok_or(Error::::ArithmeticOverflow)?; T::MultiCurrency::transfer( currency_id, @@ -842,7 +842,7 @@ impl Pallet { amount: BalanceOf, ) -> Result, DispatchError> { let (_mint_rate, redeem_rate) = Fees::::get(); - let redeem_fee = redeem_rate * amount; + let redeem_fee = redeem_rate.into_inner().saturating_mul_int(amount); let amount_exclude_fee = amount.checked_sub(&redeem_fee).ok_or(Error::::ArithmeticOverflow)?; T::MultiCurrency::transfer( currency_id, @@ -987,7 +987,7 @@ impl Pallet { let pool_treasury_account = Self::get_pool_treasury(pool_id); total_reward_staking = total_reward_staking.saturating_add(reward_staking); - let reward_commission_amount = Ratio::checked_from_rational(1, 100) + let reward_commission_amount = Rate::checked_from_rational(1, 100) .unwrap_or_default() .saturating_mul_int(total_reward_staking); From 655f8d12a291452d13a8c809f54b2692a4b6bc07 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 12:52:59 +1300 Subject: [PATCH 121/155] WIP - Update pool info struct property --- pallets/spp/src/utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index 367303352..d9f893956 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -24,6 +24,7 @@ use sp_runtime::{ Permill, RuntimeDebug, }; +use primitives::bounded::{FractionalRate, Rate}; use primitives::{FungibleTokenId, PoolId}; // Helper methods to compute the issuance rate for undeployed land. @@ -32,7 +33,7 @@ use primitives::{FungibleTokenId, PoolId}; #[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct PoolInfo { pub creator: AccountId, - pub commission: Permill, + pub commission: Rate, /// Currency id of the pool pub currency_id: FungibleTokenId, /// Max nft rewards From 467b0cdf171307245deb0f9bce001b060925f71e Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 12:53:22 +1300 Subject: [PATCH 122/155] WIP - Fix unit tests to ensure all changes applied correctly --- pallets/auction/src/lib.rs | 2 -- pallets/spp/src/tests.rs | 25 +++++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pallets/auction/src/lib.rs b/pallets/auction/src/lib.rs index 67403e434..e338a1cf7 100644 --- a/pallets/auction/src/lib.rs +++ b/pallets/auction/src/lib.rs @@ -58,7 +58,6 @@ pub mod weights; pub struct AuctionLogicHandler; pub mod migration_v2 { - use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] @@ -103,7 +102,6 @@ pub mod pallet { use frame_support::dispatch::DispatchResultWithPostInfo; use frame_support::log; use frame_support::sp_runtime::traits::CheckedSub; - use frame_system::pallet_prelude::OriginFor; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_runtime::traits::CheckedAdd; diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 480b20b4b..2ef804e66 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -23,6 +23,7 @@ use sp_runtime::traits::BadOrigin; use sp_runtime::{Perbill, Permill}; use mock::{RuntimeEvent, *}; +use primitives::bounded::FractionalRate; use crate::utils::{BoostInfo, BoostingConviction, BoostingRecord, PoolInfo, PriorLock}; @@ -51,7 +52,7 @@ fn create_ksm_pool_works() { RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), 50, - Permill::from_percent(5) + Rate::saturating_from_rational(5, 100) )); let next_pool_id = NextPoolId::::get(); @@ -60,7 +61,7 @@ fn create_ksm_pool_works() { Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { creator: ALICE, - commission: Permill::from_percent(5), + commission: Rate::saturating_from_rational(5, 100), currency_id: FungibleTokenId::NativeToken(1), max: 50 } @@ -78,7 +79,7 @@ fn deposit_ksm_works() { RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), 50, - Permill::from_percent(5) + Rate::saturating_from_rational(5, 100) )); let next_pool_id = NextPoolId::::get(); @@ -87,7 +88,7 @@ fn deposit_ksm_works() { Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { creator: ALICE, - commission: Permill::from_percent(5), + commission: Rate::saturating_from_rational(5, 100), currency_id: FungibleTokenId::NativeToken(1), max: 50 } @@ -119,7 +120,7 @@ fn redeem_rksm_request_works() { RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), 50, - Permill::from_percent(5) + Rate::saturating_from_rational(5, 100) )); let next_pool_id = NextPoolId::::get(); @@ -128,7 +129,7 @@ fn redeem_rksm_request_works() { Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { creator: ALICE, - commission: Permill::from_percent(5), + commission: Rate::saturating_from_rational(5, 100), currency_id: FungibleTokenId::NativeToken(1), max: 50 } @@ -223,7 +224,7 @@ fn current_era_update_works() { RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), 50, - Permill::from_percent(5) + Rate::saturating_from_rational(5, 100) )); let next_pool_id = NextPoolId::::get(); @@ -232,7 +233,7 @@ fn current_era_update_works() { Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { creator: ALICE, - commission: Permill::from_percent(5), + commission: Rate::saturating_from_rational(5, 100), currency_id: FungibleTokenId::NativeToken(1), max: 50, } @@ -381,7 +382,7 @@ fn boosting_works() { RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), 50, - Permill::from_percent(5) + Rate::saturating_from_rational(5, 100) )); let next_pool_id = NextPoolId::::get(); @@ -390,7 +391,7 @@ fn boosting_works() { Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { creator: ALICE, - commission: Permill::from_percent(5), + commission: Rate::saturating_from_rational(5, 100), currency_id: FungibleTokenId::NativeToken(1), max: 50 } @@ -463,7 +464,7 @@ fn boosting_and_claim_reward_works() { RuntimeOrigin::signed(ALICE), FungibleTokenId::NativeToken(1), 50, - Permill::from_percent(5) + Rate::saturating_from_rational(5, 100) )); let next_pool_id = NextPoolId::::get(); @@ -472,7 +473,7 @@ fn boosting_and_claim_reward_works() { Pool::::get(next_pool_id - 1).unwrap(), PoolInfo:: { creator: ALICE, - commission: Permill::from_percent(5), + commission: Rate::saturating_from_rational(5, 100), currency_id: FungibleTokenId::NativeToken(1), max: 50 } From cceec466c9d4aeba8e5c79036571a85451067160 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 13:26:10 +1300 Subject: [PATCH 123/155] WIP - Include estimated rate per era config update --- pallets/spp/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index b3c6e6200..6da76747a 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -340,6 +340,8 @@ pub mod pallet { reward_currency_id: FungibleTokenId, claimed_amount: BalanceOf, }, + /// Reward rate per era updated. + EstimatedRewardRatePerEraUpdated { reward_rate_per_era: Rate }, } #[pallet::error] @@ -388,6 +390,8 @@ pub mod pallet { OriginsAlreadyExist, /// Origin doesn't exists OriginDoesNotExists, + /// Invalid rate input + InvalidRate, } #[pallet::hooks] @@ -679,6 +683,7 @@ pub mod pallet { last_era_updated_block: Option>, frequency: Option>, last_staking_round: StakingRound, + estimated_reward_rate_per_era: Option, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -703,6 +708,13 @@ pub mod pallet { } } + if let Some(reward_rate_per_era) = estimated_reward_rate_per_era { + EstimatedRewardRatePerEra::::mutate(|rate| -> DispatchResult { + rate.try_set(reward_rate_per_era) + .map_err(|_| Error::::InvalidRate.into()) + })?; + Self::deposit_event(Event::::EstimatedRewardRatePerEraUpdated { reward_rate_per_era }); + } Ok(()) } From 5f46fd6de7ca4f5db0d15d0249b903b8b28ffa73 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 13:28:09 +1300 Subject: [PATCH 124/155] WIP - Unit test - update unit test of updating era config --- pallets/spp/src/tests.rs | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 2ef804e66..43d08b6f2 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -218,6 +218,7 @@ fn current_era_update_works() { Some(101), Some(100), StakingRound::Era(1), + Some(Rate::saturating_from_rational(35, 100000)) )); assert_ok!(SppModule::create_pool( @@ -458,6 +459,7 @@ fn boosting_and_claim_reward_works() { Some(101), Some(100), StakingRound::Era(1), + Some(Rate::saturating_from_rational(35, 100000)) )); assert_ok!(SppModule::create_pool( @@ -641,3 +643,98 @@ fn boosting_and_claim_reward_works() { ); }); } + +#[test] +fn pool_treasury_distribution_works() { + ExtBuilder::default() + .ksm_setup_for_alice_and_bob() + .build() + .execute_with(|| { + // Era config set up + // Current relaychain block is 102. + MockRelayBlockNumberProvider::set(102); + RelayChainCurrentEra::::put(1); + IterationLimit::::put(50); + // The correct set up era config is the last era block records is 101 with duration is 100 blocks + assert_ok!(SppModule::update_era_config( + RuntimeOrigin::signed(Admin::get()), + Some(101), + Some(100), + StakingRound::Era(1), + Some(Rate::saturating_from_rational(35, 100000)) + )); + + assert_ok!(SppModule::create_pool( + RuntimeOrigin::signed(ALICE), + FungibleTokenId::NativeToken(1), + 50, + Rate::saturating_from_rational(5, 100) + )); + + let next_pool_id = NextPoolId::::get(); + assert_eq!(next_pool_id, 2); + assert_eq!( + Pool::::get(next_pool_id - 1).unwrap(), + PoolInfo:: { + creator: ALICE, + commission: Rate::saturating_from_rational(5, 100), + currency_id: FungibleTokenId::NativeToken(1), + max: 50 + } + ); + + assert_ok!(SppModule::deposit(RuntimeOrigin::signed(BOB), 1, 10000)); + // This is true because fee hasn't been set up. + assert_eq!(Tokens::accounts(BOB, FungibleTokenId::FungibleToken(1)).free, 10000); + + assert_eq!(PoolLedger::::get(1), 10000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 10000); + + // Boosting works + let bob_free_balance = Balances::free_balance(BOB); + assert_ok!(SppModule::boost( + RuntimeOrigin::signed(BOB), + 1, + BoostInfo { + balance: 15000, + conviction: BoostingConviction::None + } + )); + let boosting_of = BoostingOf::::get(BOB); + let some_record = BoostingRecord { + votes: vec![( + 1, + BoostInfo { + balance: 15000, + conviction: BoostingConviction::None, + }, + )], + prior: PriorLock(101, 15000), + }; + assert_eq!(boosting_of, some_record); + assert_eq!(Balances::usable_balance(&BOB), bob_free_balance - 15000); + let pool_1_shared_rewards = RewardsModule::shares_and_withdrawn_rewards(1, BOB); + let network_shared_rewards = RewardsModule::shares_and_withdrawn_rewards(0, BOB); + assert_eq!(pool_1_shared_rewards, (15000, Default::default())); + assert_eq!(network_shared_rewards, (15000, Default::default())); + + // Charlie boosted with 15000 Native token + assert_ok!(SppModule::boost( + RuntimeOrigin::signed(CHARLIE), + 1, + BoostInfo { + balance: 15000, + conviction: BoostingConviction::None + } + )); + // Charlie now should have 15000 shares in the pool + assert_eq!( + RewardsModule::shares_and_withdrawn_rewards(1, CHARLIE), + (15000, Default::default()) + ); + + // Move to era 2 + MockRelayBlockNumberProvider::set(202); + SppModule::on_initialize(200); + }); +} From 359665ef9ebb119a2e4260d8a4f7c4228b47c71c Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 14:45:04 +1300 Subject: [PATCH 125/155] WIP - Increase network ledger with amount of staked rewards --- pallets/spp/src/lib.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 6da76747a..1784ccfdc 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -988,33 +988,54 @@ impl Pallet { .saturating_add(Rate::one()) .saturating_pow(era_changes.unique_saturated_into()) .saturating_sub(Rate::one()); + let mut total_reward_staking: BalanceOf = Zero::zero(); if !reward_rate.is_zero() { // iterate all pool ledgers for (pool_id, pool_amount) in PoolLedger::::iter() { - let mut total_reward_staking: BalanceOf = Zero::zero(); let mut reward_staking = reward_rate.saturating_mul_int(pool_amount); if !reward_staking.is_zero() { let pool_treasury_account = Self::get_pool_treasury(pool_id); total_reward_staking = total_reward_staking.saturating_add(reward_staking); - let reward_commission_amount = Rate::checked_from_rational(1, 100) - .unwrap_or_default() - .saturating_mul_int(total_reward_staking); + let pool_treasury_commission = Rate::checked_from_rational(1, 100).unwrap_or_default(); + let pool_treasury_reward_commission_amount = + pool_treasury_commission.saturating_mul_int(reward_staking); + + // Increase reward staking of pool ledger + PoolLedger::::mutate(pool_id, |total_staked| -> Result<(), Error> { + *total_staked = total_staked + .checked_add(&reward_staking) + .ok_or(Error::::ArithmeticOverflow)?; + + Ok(()) + })?; T::MultiCurrency::deposit( FungibleTokenId::FungibleToken(1), &pool_treasury_account, - reward_commission_amount, + pool_treasury_reward_commission_amount, )?; >::accumulate_reward( &pool_id, FungibleTokenId::FungibleToken(1), - reward_commission_amount, + pool_treasury_reward_commission_amount, )?; } } + + if !total_reward_staking.is_zero() { + NetworkLedger::::mutate( + &FungibleTokenId::NativeToken(1), + |total_staked| -> Result<(), Error> { + *total_staked = total_staked + .checked_add(&total_reward_staking) + .ok_or(Error::::ArithmeticOverflow)?; + Ok(()) + }, + )?; + } } Ok(()) From 1d0ef8a451719b9d0b991731e4d10f3c8433d1c5 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 24 Nov 2023 15:53:30 +1300 Subject: [PATCH 126/155] WIP - Unit test Reward distribution works --- pallets/spp/src/tests.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/tests.rs b/pallets/spp/src/tests.rs index 43d08b6f2..450c28b33 100644 --- a/pallets/spp/src/tests.rs +++ b/pallets/spp/src/tests.rs @@ -645,7 +645,7 @@ fn boosting_and_claim_reward_works() { } #[test] -fn pool_treasury_distribution_works() { +fn reward_distribution_works() { ExtBuilder::default() .ksm_setup_for_alice_and_bob() .build() @@ -655,13 +655,15 @@ fn pool_treasury_distribution_works() { MockRelayBlockNumberProvider::set(102); RelayChainCurrentEra::::put(1); IterationLimit::::put(50); + UnlockDuration::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); // Bump current staking round to 1 + CurrentStakingRound::::insert(FungibleTokenId::NativeToken(1), StakingRound::Era(1)); // The correct set up era config is the last era block records is 101 with duration is 100 blocks assert_ok!(SppModule::update_era_config( RuntimeOrigin::signed(Admin::get()), Some(101), Some(100), StakingRound::Era(1), - Some(Rate::saturating_from_rational(35, 100000)) + Some(Rate::saturating_from_rational(20, 100)) // Set reward rate per era is 20%. )); assert_ok!(SppModule::create_pool( @@ -736,5 +738,17 @@ fn pool_treasury_distribution_works() { // Move to era 2 MockRelayBlockNumberProvider::set(202); SppModule::on_initialize(200); + + assert_ok!(SppModule::handle_reward_distribution_to_pool_treasury(1, 2)); + let pool_treasury = SppModule::get_pool_treasury(1); + + assert_eq!( + Currencies::free_balance(FungibleTokenId::FungibleToken(1), &pool_treasury), + 20 + ); + + assert_eq!(Currencies::total_issuance(FungibleTokenId::FungibleToken(1)), 10020); + assert_eq!(PoolLedger::::get(1), 12000); + assert_eq!(NetworkLedger::::get(FungibleTokenId::NativeToken(1)), 12000); }); } From 3a79bdeb96fd6939dd7ab3708a5c55374d97bf2c Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 12:14:13 +1300 Subject: [PATCH 127/155] WIP - Add spp into runtime --- Cargo.lock | 1 + runtime/metaverse/Cargo.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fc2087ec6..a3bb61f9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5823,6 +5823,7 @@ dependencies = [ "pallet-reward", "pallet-scheduler", "pallet-session", + "pallet-spp", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", diff --git a/runtime/metaverse/Cargo.toml b/runtime/metaverse/Cargo.toml index 03fe82227..85e6ed61a 100644 --- a/runtime/metaverse/Cargo.toml +++ b/runtime/metaverse/Cargo.toml @@ -104,6 +104,7 @@ core-primitives = { path = "../../traits/core-primitives", default-features = fa metaverse-runtime-common = { path = "../common", default-features = false } asset-manager = { path = "../../pallets/asset-manager", default-features = false } evm-mapping = { package = "pallet-evm-mapping", path = "../../pallets/evm-mapping", default-features = false } +spp = { package = "pallet-spp", path = "../../pallets/spp", default-features = false } modules-bridge = { path = "../../modules/bridge", default-features = false } @@ -176,6 +177,7 @@ std = [ "economy/std", "emergency/std", "evm-mapping/std", + "spp/std", "pallet-utility/std", "pallet-collator-selection/std", "orml-benchmarking/std", From 603295bc1dbe9f7b5cfe38e72f6091f70484269f Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 12:14:43 +1300 Subject: [PATCH 128/155] WIP - Remove unused config type for spp pallet --- pallets/spp/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 1784ccfdc..2431047d6 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -109,10 +109,6 @@ pub mod pallet { #[pallet::constant] type MinimumStake: Get>; - /// Delay of staking reward payment (in number of rounds) - #[pallet::constant] - type RewardPaymentDelay: Get; - /// Network fee charged on pool creation #[pallet::constant] type NetworkFee: Get>; @@ -122,9 +118,6 @@ pub mod pallet { #[pallet::constant] type StorageDepositFee: Get>; - /// Allows converting block numbers into balance - type BlockNumberToBalance: Convert>; - /// Block number provider for the relaychain. type RelayChainBlockNumber: BlockNumberProvider>; From 8fb4966433f2e0b1913e1c16476b4591b8dfe62f Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 12:15:40 +1300 Subject: [PATCH 129/155] WIP - Include spp into metaverse runtime --- runtime/metaverse/src/lib.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index 66fda6b2c..a522b48f9 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -60,7 +60,6 @@ use pallet_evm::{ }; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; -//use pallet_evm::{EnsureAddressTruncated, HashedAddressMapping}; use polkadot_primitives::v2::MAX_POV_SIZE; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; @@ -87,6 +86,8 @@ use sp_std::prelude::*; use sp_version::NativeVersion; use sp_version::RuntimeVersion; +//use pallet_evm::{EnsureAddressTruncated, HashedAddressMapping}; +use asset_manager::ForeignAssetMapping; pub use constants::{currency::*, time::*}; use core_primitives::{NftAssetData, NftClassData}; // External imports @@ -364,6 +365,10 @@ parameter_types! { pub const EconomyTreasury: PalletId = PalletId(*b"bit/econ"); pub const LocalMetaverseFundPalletId: PalletId = PalletId(*b"bit/meta"); pub const BridgeSovereignPalletId: PalletId = PalletId(*b"bit/brgd"); + pub const PoolAccountPalletId: PalletId = PalletId(*b"bit/pool"); + pub const RewardPayoutAccountPalletId: PalletId = PalletId(*b"bit/pout"); + pub const RewardHoldingAccountPalletId: PalletId = PalletId(*b"bit/hold"); + pub const MaxAuthorities: u32 = 50; pub const MaxSetIdSessionEntries: u64 = u64::MAX; } @@ -1437,6 +1442,27 @@ impl modules_bridge::Config for Runtime { type PalletId = BridgeSovereignPalletId; } +parameter_types! { + pub const MaximumQueue: u32 = 50; +} + +impl spp::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MultiCurrency = Currencies; + type WeightInfo = weights::module_estate::WeightInfo; + type MinimumStake = MinimumStake; + type NetworkFee = NetworkFee; + type StorageDepositFee = StorageDepositFee; + type RelayChainBlockNumber = (); + type PoolAccount = PoolAccountPalletId; + type RewardPayoutAccount = RewardPayoutAccountPalletId; + type RewardHoldingAccount = RewardHoldingAccountPalletId; + type MaximumQueue = MaximumQueue; + type CurrencyIdConversion = ForeignAssetMapping; + type GovernanceOrigin = EnsureRootOrTwoThirdsCouncilCollective; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -1510,6 +1536,9 @@ construct_runtime!( // Bridge BridgeSupport: modules_bridge::{Pallet, Call, Storage, Event}, + + // Spp + Spp: spp::{Pallet, Call, Storage, Event} } ); From 3f186a9b525bb0eef822c504c1f8430fa7d85a94 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 12:25:21 +1300 Subject: [PATCH 130/155] WIP - Add mock module_spp weight --- runtime/metaverse/src/weights/mod.rs | 1 + runtime/metaverse/src/weights/module_spp.rs | 168 ++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 runtime/metaverse/src/weights/module_spp.rs diff --git a/runtime/metaverse/src/weights/mod.rs b/runtime/metaverse/src/weights/mod.rs index 8d70a266d..f934833ce 100644 --- a/runtime/metaverse/src/weights/mod.rs +++ b/runtime/metaverse/src/weights/mod.rs @@ -9,3 +9,4 @@ pub mod module_metaverse; pub mod module_mining; pub mod module_nft; pub mod module_reward; +pub mod module_spp; diff --git a/runtime/metaverse/src/weights/module_spp.rs b/runtime/metaverse/src/weights/module_spp.rs new file mode 100644 index 000000000..8e4985b6e --- /dev/null +++ b/runtime/metaverse/src/weights/module_spp.rs @@ -0,0 +1,168 @@ +// This file is part of Metaverse.Network & Bit.Country. + +// Copyright (C) 2020-2022 Metaverse.Network & Bit.Country . +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for estate +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-11-03, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// ./target/release/metaverse-node +// benchmark +// --chain=dev +// --pallet=estate +// --extrinsic=* +// --steps=20 +// --repeat=10 +// --execution=wasm +// --wasm-execution=compiled +// --template=./template/runtime-weight-template.hbs +// --output +// ./pallets/estate/src/weights.rs + + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for estate. +pub struct WeightInfo(PhantomData); + +impl spp::WeightInfo for WeightInfo { + fn mint_land() -> Weight { + Weight::from_parts(59_273_000, 36660) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(7)) + } + fn mint_lands() -> Weight { + Weight::from_parts(83_541_000, 41610) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(10)) + } + fn transfer_land() -> Weight { + Weight::from_parts(47_423_000, 28255) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + fn mint_estate() -> Weight { + Weight::from_parts(64_479_000, 47210) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(11)) + } + fn dissolve_estate() -> Weight { + Weight::from_parts(110_008_000, 67224) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(13)) + } + fn add_land_unit_to_estate() -> Weight { + Weight::from_parts(71_706_000, 38079) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(6)) + } + fn remove_land_unit_from_estate() -> Weight { + Weight::from_parts(90_257_000, 60226) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().writes(8)) + } + fn create_estate() -> Weight { + Weight::from_parts(131_741_000, 66058) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(17)) + } + fn transfer_estate() -> Weight { + Weight::from_parts(54_808_000, 38025) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) + } + fn issue_undeployed_land_blocks() -> Weight { + Weight::from_parts(162_875_000, 9825) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(43)) + } + fn freeze_undeployed_land_blocks() -> Weight { + Weight::from_parts(22_833_000, 7834) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn unfreeze_undeployed_land_blocks() -> Weight { + Weight::from_parts(21_423_000, 7834) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn approve_undeployed_land_blocks() -> Weight { + Weight::from_parts(21_819_000, 7834) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn unapprove_undeployed_land_blocks() -> Weight { + Weight::from_parts(21_237_000, 7900) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn transfer_undeployed_land_blocks() -> Weight { + Weight::from_parts(41_173_000, 13673) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(4)) + } + fn deploy_land_block() -> Weight { + Weight::from_parts(100_659_000, 64897) + .saturating_add(T::DbWeight::get().reads(13)) + .saturating_add(T::DbWeight::get().writes(11)) + } + fn burn_undeployed_land_blocks() -> Weight { + Weight::from_parts(32_196_000, 10661) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn create_lease_offer() -> Weight { + Weight::from_parts(98_902_000, 27383) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(2)) + } + fn accept_lease_offer() -> Weight { + Weight::from_parts(57_019_000, 28844) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) + } + fn cancel_lease() -> Weight { + Weight::from_parts(57_720_000, 28571) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + fn remove_expired_lease() -> Weight { + Weight::from_parts(57_681_000, 28571) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + fn remove_lease_offer() -> Weight { + Weight::from_parts(41_923_000, 8328) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + fn collect_rent() -> Weight { + Weight::from_parts(53_171_000, 28571) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + fn on_initialize() -> Weight { + Weight::from_parts(191_000, 0) + } +} \ No newline at end of file From 1fd74b84818845162ac048d68ad4e0cce80db5ff Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 12:25:38 +1300 Subject: [PATCH 131/155] WIP - Implement cumulus parachain info into metaverse runtime --- Cargo.lock | 11 +++++++++++ runtime/metaverse/Cargo.toml | 23 ++++++++++++++++++++++- runtime/metaverse/src/lib.rs | 18 ++++++++++++++---- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3bb61f9b..030695fb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5772,6 +5772,15 @@ dependencies = [ "auction-manager", "bit-country-primitives", "core-primitives", + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", "currencies", "fp-evm", "fp-rpc", @@ -5791,6 +5800,7 @@ dependencies = [ "orml-currencies", "orml-nft", "orml-oracle", + "orml-rewards", "orml-tokens", "orml-traits", "pallet-auction", @@ -5831,6 +5841,7 @@ dependencies = [ "pallet-treasury", "pallet-utility", "pallet-vesting", + "parachain-info", "parity-scale-codec", "polkadot-primitives", "scale-info", diff --git a/runtime/metaverse/Cargo.toml b/runtime/metaverse/Cargo.toml index 85e6ed61a..216dca109 100644 --- a/runtime/metaverse/Cargo.toml +++ b/runtime/metaverse/Cargo.toml @@ -84,6 +84,7 @@ orml-tokens = { workspace = true } orml-traits = { workspace = true } orml-nft = { workspace = true } orml-oracle = { workspace = true } +orml-rewards = { workspace = true } orml-benchmarking = { workspace = true, optional = true } # metaverse dependencies primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } @@ -115,6 +116,18 @@ pallet-contracts-primitives = { workspace = true } # XCM builder ( need to be used to build the runtime benchmarking correctly) xcm-builder = { workspace = true } +# Cumulus Dependencies +cumulus-pallet-aura-ext = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-dmp-queue = { workspace = true } +cumulus-pallet-xcm = { workspace = true } +cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-timestamp = { workspace = true } +cumulus-primitives-utility = { workspace = true } +cumulus-pallet-session-benchmarking = { workspace = true } +parachain-info = { workspace = true } + [build-dependencies] substrate-wasm-builder = { workspace = true } @@ -163,6 +176,7 @@ std = [ "orml-tokens/std", "orml-nft/std", "orml-oracle/std", + "orml-rewards/std", "primitives/std", "metaverse/std", "auction/std", @@ -191,7 +205,14 @@ std = [ "pallet-contracts-primitives/std", "frame-try-runtime/std", "metaverse-runtime-common/std", - "modules-bridge/std" + "modules-bridge/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-pallet-xcm/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", ] runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index a522b48f9..0103d1725 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -24,6 +24,7 @@ extern crate orml_benchmarking; use codec::{Decode, Encode, MaxEncodedLen}; +use cumulus_pallet_parachain_system::RelaychainDataProvider; // pub use this so we can import it in the chain spec. #[cfg(feature = "std")] pub use fp_evm::GenesisAccount; @@ -99,7 +100,7 @@ use primitives::evm::{ CurrencyIdType, Erc20Mapping, EvmAddress, H160_POSITION_CURRENCY_ID_TYPE, H160_POSITION_FUNGIBLE_TOKEN, H160_POSITION_MINING_RESOURCE, H160_POSITION_TOKEN, H160_POSITION_TOKEN_NFT, H160_POSITION_TOKEN_NFT_CLASS_ID_END, }; -use primitives::{Amount, Balance, BlockNumber, ClassId, FungibleTokenId, Moment, NftId, RoundIndex, TokenId}; +use primitives::{Amount, Balance, BlockNumber, ClassId, FungibleTokenId, Moment, NftId, PoolId, RoundIndex, TokenId}; // primitives imports use crate::opaque::SessionKeys; @@ -1442,6 +1443,14 @@ impl modules_bridge::Config for Runtime { type PalletId = BridgeSovereignPalletId; } +impl orml_rewards::Config for Runtime { + type Share = Balance; + type Balance = Balance; + type PoolId = PoolId; + type CurrencyId = FungibleTokenId; + type Handler = Spp; +} + parameter_types! { pub const MaximumQueue: u32 = 50; } @@ -1450,11 +1459,11 @@ impl spp::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type MultiCurrency = Currencies; - type WeightInfo = weights::module_estate::WeightInfo; + type WeightInfo = weights::module_spp::WeightInfo; type MinimumStake = MinimumStake; type NetworkFee = NetworkFee; type StorageDepositFee = StorageDepositFee; - type RelayChainBlockNumber = (); + type RelayChainBlockNumber = RelaychainDataProvider; type PoolAccount = PoolAccountPalletId; type RewardPayoutAccount = RewardPayoutAccountPalletId; type RewardHoldingAccount = RewardHoldingAccountPalletId; @@ -1538,7 +1547,8 @@ construct_runtime!( BridgeSupport: modules_bridge::{Pallet, Call, Storage, Event}, // Spp - Spp: spp::{Pallet, Call, Storage, Event} + Spp: spp::{Pallet, Call, Storage, Event}, + Rewards: orml_rewards::{Pallet, Storage} } ); From 141df64f29c24f5f8979e6227317585d1d291379 Mon Sep 17 00:00:00 2001 From: codemeit Date: Sat, 25 Nov 2023 16:14:13 +1300 Subject: [PATCH 132/155] Update README.md --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 36c712e74..6ef92c0f2 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

[Bit.Country] Metaverse.Network

-## Start your own metaverse. An Ethereum-compatible Network for Metaverses & Games +## An Ethereum-compatible Blockchain Network Built using Substrate [![Substrate version](https://img.shields.io/badge/Substrate-3.0.0-brightgreen?logo=Parity%20Substrate)](https://substrate.dev/) [![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fbitdotcountry)](https://twitter.com/bitdotcountry) @@ -30,10 +30,7 @@ Development Note: It is still a WIP. # 1. Introduction -Metaverse Network is an EVM-enabled blockchain network for user-created metaverses and games. - -Everyone can start their own metaverse for their people with the 3D world, NFTs, play-to-earn & build communities to -earn, and takes community engagement to a new dimension on web3.0. +Metaverse.Network, featuring a seamlessly integrated enriched social layer, stands as an interesting blockchain ecosystem that extends support for both EVM (Ethereum Virtual Machine) and WASM (WebAssembly) smart contracts. This framework empowers developers to harness the network's capabilities in crafting decentralized applications (dApps) with unparalleled versatility and functionality. # 2. Build From d6eda759e4ac2a948fda16f983fd8e4e5920b8c8 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 18:35:36 +1300 Subject: [PATCH 133/155] WIP - Update correct relaychain block number provider --- runtime/metaverse/src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index 0103d1725..115ca8177 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -24,7 +24,7 @@ extern crate orml_benchmarking; use codec::{Decode, Encode, MaxEncodedLen}; -use cumulus_pallet_parachain_system::RelaychainDataProvider; +use cumulus_pallet_parachain_system::RelaychainBlockNumberProvider; // pub use this so we can import it in the chain spec. #[cfg(feature = "std")] pub use fp_evm::GenesisAccount; @@ -70,7 +70,7 @@ use sp_core::{ sp_std::marker::PhantomData, ConstBool, OpaqueMetadata, H160, H256, U256, }; -use sp_runtime::traits::DispatchInfoOf; +use sp_runtime::traits::{BlockNumberProvider, DispatchInfoOf}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ @@ -1454,6 +1454,15 @@ impl orml_rewards::Config for Runtime { parameter_types! { pub const MaximumQueue: u32 = 50; } +pub static MockRelayBlockNumberProvider: BlockNumber = 0; + +impl BlockNumberProvider for MockRelayBlockNumberProvider { + type BlockNumber = BlockNumber; + + fn current_block_number() -> Self::BlockNumber { + Self::get() + } +} impl spp::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -1463,7 +1472,7 @@ impl spp::Config for Runtime { type MinimumStake = MinimumStake; type NetworkFee = NetworkFee; type StorageDepositFee = StorageDepositFee; - type RelayChainBlockNumber = RelaychainDataProvider; + type RelayChainBlockNumber = RelaychainBlockNumberProvider; type PoolAccount = PoolAccountPalletId; type RewardPayoutAccount = RewardPayoutAccountPalletId; type RewardHoldingAccount = RewardHoldingAccountPalletId; From 53373ca7ea9f32a7a78d5d289ed7e64ce814d049 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 18:37:14 +1300 Subject: [PATCH 134/155] WIP - Temp use current chain block number as index --- pallets/spp/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 2431047d6..3d603cfda 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -390,7 +390,9 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { - let era_number = Self::get_era_index(T::RelayChainBlockNumber::current_block_number()); + // let era_number = Self::get_era_index(T::RelayChainBlockNumber::current_block_number()); + let era_number = Self::get_era_index(>::block_number()); + if !era_number.is_zero() { let _ = Self::update_current_era(era_number).map_err(|err| err).ok(); } From 5d6730d14fff415a37a460f5306b7279bb3f519b Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 21:10:27 +1300 Subject: [PATCH 135/155] WIP - Add missing BTreemap and Vec --- pallets/spp/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 3d603cfda..5672ab9de 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -59,13 +59,12 @@ const BOOSTING_ID: LockIdentifier = *b"bc/boost"; #[frame_support::pallet] pub mod pallet { - use std::collections::BTreeMap; - use frame_support::traits::{Currency, LockableCurrency, ReservableCurrency, WithdrawReasons}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_core::U256; use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedMul, CheckedSub, One, UniqueSaturatedInto}; use sp_runtime::Permill; + use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use primitives::bounded::FractionalRate; use primitives::{PoolId, StakingRound}; From 59ab0caab4b33bf0cd08dcf264d4b997b630c50a Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 25 Nov 2023 21:10:50 +1300 Subject: [PATCH 136/155] WIP - Complete adding metaverse runtime for testing --- pallets/spp/src/utils.rs | 1 + runtime/metaverse/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/spp/src/utils.rs b/pallets/spp/src/utils.rs index d9f893956..f5c70a688 100644 --- a/pallets/spp/src/utils.rs +++ b/pallets/spp/src/utils.rs @@ -23,6 +23,7 @@ use sp_runtime::{ traits::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, IntegerSquareRoot, Saturating, Zero}, Permill, RuntimeDebug, }; +use sp_std::vec::Vec; use primitives::bounded::{FractionalRate, Rate}; use primitives::{FungibleTokenId, PoolId}; diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index 115ca8177..e5a6f0c73 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -1453,8 +1453,8 @@ impl orml_rewards::Config for Runtime { parameter_types! { pub const MaximumQueue: u32 = 50; + pub const MockRelayBlockNumberProvider: BlockNumber = 0; } -pub static MockRelayBlockNumberProvider: BlockNumber = 0; impl BlockNumberProvider for MockRelayBlockNumberProvider { type BlockNumber = BlockNumber; @@ -1472,7 +1472,7 @@ impl spp::Config for Runtime { type MinimumStake = MinimumStake; type NetworkFee = NetworkFee; type StorageDepositFee = StorageDepositFee; - type RelayChainBlockNumber = RelaychainBlockNumberProvider; + type RelayChainBlockNumber = MockRelayBlockNumberProvider; type PoolAccount = PoolAccountPalletId; type RewardPayoutAccount = RewardPayoutAccountPalletId; type RewardHoldingAccount = RewardHoldingAccountPalletId; From 4754983e25e8ec7daee55686456fcc532ddd0e00 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 28 Nov 2023 11:34:46 +1300 Subject: [PATCH 137/155] Add signature expired block for presigned mint for safer revoke permission --- pallets/nft/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index b45d145e2..fc1b14d09 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -156,6 +156,7 @@ pub mod pallet { ::ClassId, ::TokenId, ::AccountId, + BlockNumberFor, BalanceOf, >; @@ -403,6 +404,8 @@ pub mod pallet { InvalidCurrentTotalIssuance, /// Wrong signature WrongSignature, + /// Signature expired + SignatureExpired, } #[pallet::call] @@ -1174,6 +1177,7 @@ impl Pallet { attributes, metadata, only_account, + expired, mint_price, } = mint_data; @@ -1191,6 +1195,9 @@ impl Pallet { ensure!(account == mint_to, Error::::NoPermission); } + let now = frame_system::Pallet::::block_number(); + ensure!(expired >= now, Error::::SignatureExpired); + // Get class info of the collection let class_info = NftModule::::classes(class_id).ok_or(Error::::ClassIdNotFound)?; From 6a1de47bc223874b52c2f3561b2633b0bcb2ac80 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 28 Nov 2023 11:35:00 +1300 Subject: [PATCH 138/155] Implement expired into presigned mint struct --- traits/core-primitives/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 72f15aa0d..5aa8b55df 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -163,7 +163,7 @@ pub struct MetaverseFund { } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct PreSignedMint { +pub struct PreSignedMint { /// A collection of the item to be minted. pub class_id: ClassId, /// TokenId. @@ -174,6 +174,8 @@ pub struct PreSignedMint { pub metadata: NftMetadata, /// Restrict the claim to a particular account. pub only_account: Option, + /// A deadline for the signature. + pub expired: BlockNumber, /// An optional price the claimer would need to pay for the mint. pub mint_price: Option, } From 0cc75f7b3f0446ac4c346ddec69722a6566e5d8f Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 28 Nov 2023 11:35:10 +1300 Subject: [PATCH 139/155] Bump runtime --- runtime/metaverse/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index e5a6f0c73..f90678a72 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -182,7 +182,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 92, + spec_version: 93, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e035dda21f9cf00be31bd5ad855110dc29dac5a9 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 28 Nov 2023 11:48:42 +1300 Subject: [PATCH 140/155] Update era config to include more initial era setup --- pallets/spp/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 5672ab9de..460888b2d 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -334,6 +334,13 @@ pub mod pallet { }, /// Reward rate per era updated. EstimatedRewardRatePerEraUpdated { reward_rate_per_era: Rate }, + /// Unlock duration updated. + UnlockDurationUpdated { + currency_id: FungibleTokenId, + unlock_duration: StakingRound, + }, + /// Iterator limit updated. + IterationLimitUpdated { new_limit: u32 }, } #[pallet::error] @@ -678,6 +685,8 @@ pub mod pallet { frequency: Option>, last_staking_round: StakingRound, estimated_reward_rate_per_era: Option, + unlock_duration: Option<(FungibleTokenId, StakingRound)>, + iteration_limit: Option, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -709,6 +718,20 @@ pub mod pallet { })?; Self::deposit_event(Event::::EstimatedRewardRatePerEraUpdated { reward_rate_per_era }); } + + if let Some((currency_id, staking_round)) = unlock_duration { + UnlockDuration::::insert(currency_id, staking_round); + Self::deposit_event(Event::::UnlockDurationUpdated { + currency_id, + unlock_duration: staking_round, + }) + } + + if let Some(new_limit) = iteration_limit { + IterationLimit::::put(new_limit); + Self::deposit_event(Event::::IterationLimitUpdated { new_limit }) + } + Ok(()) } From 6ae20143e6dfcc72c7bb44111b96507e130b1a4e Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 3 Dec 2023 16:51:15 +1300 Subject: [PATCH 141/155] Update temp internal block number for era update --- pallets/spp/src/lib.rs | 10 ++++++---- runtime/metaverse/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 460888b2d..9a2c69c82 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -697,7 +697,8 @@ pub mod pallet { if let Some(change) = last_era_updated_block { let update_era_frequency = UpdateEraFrequency::::get(); - let current_relay_chain_block = T::RelayChainBlockNumber::current_block_number(); + let current_relay_chain_block = >::block_number(); + // let current_relay_chain_block = T::RelayChainBlockNumber::current_block_number(); if !update_era_frequency.is_zero() { ensure!( change > current_relay_chain_block.saturating_sub(update_era_frequency) @@ -720,7 +721,7 @@ pub mod pallet { } if let Some((currency_id, staking_round)) = unlock_duration { - UnlockDuration::::insert(currency_id, staking_round); + UnlockDuration::::insert(currency_id, staking_round.clone()); Self::deposit_event(Event::::UnlockDurationUpdated { currency_id, unlock_duration: staking_round, @@ -728,7 +729,7 @@ pub mod pallet { } if let Some(new_limit) = iteration_limit { - IterationLimit::::put(new_limit); + IterationLimit::::put(new_limit.clone()); Self::deposit_event(Event::::IterationLimitUpdated { new_limit }) } @@ -1239,7 +1240,8 @@ impl Pallet { let new_era = previous_era.saturating_add(era_index); RelayChainCurrentEra::::put(new_era); - LastEraUpdatedBlock::::put(T::RelayChainBlockNumber::current_block_number()); + // LastEraUpdatedBlock::::put(T::RelayChainBlockNumber::current_block_number()); + LastEraUpdatedBlock::::put(>::block_number()); Self::handle_redeem_requests(new_era)?; Self::handle_reward_distribution_to_network_pool()?; Self::deposit_event(Event::::CurrentEraUpdated { new_era_index: new_era }); diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index f90678a72..99bd3a31f 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -182,7 +182,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 93, + spec_version: 95, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From d872cfd20ea8cce6687c037b82391d5e741e5699 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 3 Dec 2023 16:51:28 +1300 Subject: [PATCH 142/155] Add bob-dev into makefile --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 60b69f451..fe5d92fd9 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,11 @@ build-docker-pioneer: .PHONY: run-dev run-dev: ./target/release/metaverse-node purge-chain --dev - ./target/release/metaverse-node --dev --tmp --alice -lruntime=debug + ./target/release/metaverse-node --dev --tmp --alice --node-key 0000000000000000000000000000000000000000000000000000000000000001 -lruntime=debug + +.PHONY: run-bob-dev +run-bob-dev: + ./target/release/metaverse-node --dev --tmp --bob --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp -lruntime=debug .PHONY: run-chopsticks-pioneer run-chopsticks-pioneer: From 3bdda50558d6f90f157914c32fee8e8d0b3ed77c Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 4 Dec 2023 15:34:52 +1300 Subject: [PATCH 143/155] Create xcm-interface crate --- Cargo.lock | 51 +++++++++++++++++++++++ pallets/xcm-interface/Cargo.toml | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 pallets/xcm-interface/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 030695fb3..a63fd7ef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4347,6 +4347,19 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "insta" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", + "yaml-rust", +] + [[package]] name = "instant" version = "0.1.12" @@ -8334,6 +8347,35 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "pallet-xcm-interface" +version = "2.0.0-rc6" +dependencies = [ + "asset-manager", + "bit-country-primitives", + "core-primitives", + "cumulus-primitives-core", + "currencies", + "frame-benchmarking", + "frame-support", + "frame-system", + "insta", + "log", + "orml-tokens", + "orml-traits", + "pallet-balances", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "parachain-info" version = "0.1.0" @@ -15632,6 +15674,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yamux" version = "0.10.2" diff --git a/pallets/xcm-interface/Cargo.toml b/pallets/xcm-interface/Cargo.toml new file mode 100644 index 000000000..1a446831b --- /dev/null +++ b/pallets/xcm-interface/Cargo.toml @@ -0,0 +1,70 @@ +[package] +authors = ['Metaverse Network '] +description = 'Metaverse Network pallet for xcm interface logic.' +edition = '2021' +homepage = 'https://metaverse.network' +license = 'Unlicense' +name = 'pallet-xcm-interface' +repository = 'https://github.com/bit-country' +version = '2.0.0-rc6' + +[dependencies] +log = { version = "0.4.17", default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-runtime = { workspace = true } +sp-core = { workspace = true } +sp-std = { workspace = true } +pallet-xcm = { workspace = true } +xcm = { package = "staging-xcm", workspace = true } +# local packages +core-primitives = { path = "../../traits/core-primitives", default-features = false } +primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } +currencies = { package = "currencies", path = "../currencies", default-features = false } +asset-manager = { package = "asset-manager", path = "../asset-manager", default-features = false } + +#orml +orml-tokens = { workspace = true } +orml-traits = { workspace = true } + +[dev-dependencies] +insta = { version = "1.31.0" } + +cumulus-primitives-core = { workspace = true } +pallet-balances = { workspace = true } +sp-io = { workspace = true } +xcm-builder = { package = "staging-xcm-builder", workspace = true } +xcm-executor = { package = "staging-xcm-executor", workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "orml-traits/std", + "pallet-xcm/std", + "primitives/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-xcm/try-runtime", +] From 88a40a87a22653192d3bd6618e970ea036210d48 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 4 Dec 2023 15:35:31 +1300 Subject: [PATCH 144/155] Initiate the first lib xcm --- pallets/xcm-interface/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pallets/xcm-interface/src/lib.rs diff --git a/pallets/xcm-interface/src/lib.rs b/pallets/xcm-interface/src/lib.rs new file mode 100644 index 000000000..a30eb952c --- /dev/null +++ b/pallets/xcm-interface/src/lib.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 457beb931e8b02dabd964a45bf0166d34a7c2c6c Mon Sep 17 00:00:00 2001 From: codemeit Date: Tue, 5 Dec 2023 13:58:13 +1300 Subject: [PATCH 145/155] Update README.md --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6ef92c0f2..0d36afa76 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,5 @@ -

- -

- -

- -

-
-

[Bit.Country] Metaverse.Network

+

Metaverse.Network by BitCountry team

## An Ethereum-compatible Blockchain Network Built using Substrate @@ -102,3 +94,8 @@ cargo run -- \ --telemetry-url 'wss://telemetry.polkadot.io/submit/ 0' \ --validator ``` + +

+ +

+ From 11c69d5222d4d79ab837e4f468fc6b54293a2346 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Dec 2023 15:30:04 +1300 Subject: [PATCH 146/155] Update merge conflict on readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c50e11168..0d36afa76 100644 --- a/README.md +++ b/README.md @@ -94,3 +94,8 @@ cargo run -- \ --telemetry-url 'wss://telemetry.polkadot.io/submit/ 0' \ --validator ``` + +

+ +

+ From 725e52d911d2cad9807cee9138df83f49f95033d Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Dec 2023 15:30:19 +1300 Subject: [PATCH 147/155] Include currency network fee --- pallets/spp/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index 9a2c69c82..ca9b74533 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -285,6 +285,11 @@ pub mod pallet { #[pallet::storage] pub type EstimatedRewardRatePerEra = StorageValue<_, FractionalRate, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn network_fee)] + /// Store network fee by currency id + pub type CurrencyNetworkFee = StorageMap<_, Twox64Concat, FungibleTokenId, BalanceOf, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { @@ -341,6 +346,11 @@ pub mod pallet { }, /// Iterator limit updated. IterationLimitUpdated { new_limit: u32 }, + /// Network fee updated. + NetworkFeeUpdated { + currency_id: FungibleTokenId, + new_fee: BalanceOf, + }, } #[pallet::error] @@ -687,6 +697,7 @@ pub mod pallet { estimated_reward_rate_per_era: Option, unlock_duration: Option<(FungibleTokenId, StakingRound)>, iteration_limit: Option, + network_fee: Option<(FungibleTokenId, BalanceOf)>, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -733,6 +744,11 @@ pub mod pallet { Self::deposit_event(Event::::IterationLimitUpdated { new_limit }) } + if let Some((currency_id, new_fee)) = network_fee { + CurrencyNetworkFee::::insert(currency_id, new_fee); + Self::deposit_event(Event::::NetworkFeeUpdated { currency_id, new_fee }); + } + Ok(()) } From b19da3fb44bd04bf5878128d5cb65c59b38e38c6 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Dec 2023 15:34:35 +1300 Subject: [PATCH 148/155] Update pool creation fee collection logic --- pallets/spp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index ca9b74533..b9c32b704 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -901,7 +901,7 @@ impl Pallet { } pub fn collect_pool_creation_fee(who: &T::AccountId, currency_id: FungibleTokenId) -> DispatchResult { - let pool_fee = T::NetworkFee::get(); + let pool_fee = CurrencyNetworkFee::::get(currency_id); T::MultiCurrency::transfer( currency_id, who, From 7ec695faaa79a79705d8f90663028c3c994b6631 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Dec 2023 15:38:31 +1300 Subject: [PATCH 149/155] Add fractional rate limit to 1/2 --- primitives/metaverse/src/bounded.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/primitives/metaverse/src/bounded.rs b/primitives/metaverse/src/bounded.rs index 991640223..5b13feee1 100644 --- a/primitives/metaverse/src/bounded.rs +++ b/primitives/metaverse/src/bounded.rs @@ -154,12 +154,21 @@ impl Get for OneFifth { } } +/// Maximum absolute change is 1/2. +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] +pub struct OneHalf; +impl Get for OneHalf { + fn get() -> Rate { + Rate::saturating_from_rational(1, 2) + } +} + pub type BoundedRate = BoundedType; /// Fractional rate. /// -/// The range is between 0 to 1, and max absolute value of change diff is 1/5. -pub type FractionalRate = BoundedRate; +/// The range is between 0 to 1, and max absolute value of change diff is 1/2. +pub type FractionalRate = BoundedRate; pub type BoundedBalance = BoundedType; From 4f886a0e6f2ee77dfaad1a167ded9a3e21088b96 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Dec 2023 15:39:04 +1300 Subject: [PATCH 150/155] Bump runtime version --- runtime/metaverse/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index 99bd3a31f..e21e6ee0f 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -182,7 +182,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 95, + spec_version: 96, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 2320a8caaafb15e080cd85ec028a879d1c5a00d9 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Dec 2023 10:35:31 +1300 Subject: [PATCH 151/155] Add logging into the reward distribution logic --- pallets/spp/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index b9c32b704..cede613ea 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -1023,7 +1023,11 @@ impl Pallet { .saturating_pow(era_changes.unique_saturated_into()) .saturating_sub(Rate::one()); let mut total_reward_staking: BalanceOf = Zero::zero(); - + log::info!( + target: "pallet-spp", + "reward distribution to pool treasury era: {:?} reward rate {:?}", + era_changes, reward_rate + ); if !reward_rate.is_zero() { // iterate all pool ledgers for (pool_id, pool_amount) in PoolLedger::::iter() { From 6e4955bc5538183a22e65d1fff36840dc5d4c451 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Dec 2023 11:53:04 +1300 Subject: [PATCH 152/155] Include reward distribution to pool treasury --- pallets/spp/src/lib.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/pallets/spp/src/lib.rs b/pallets/spp/src/lib.rs index cede613ea..bedd0b5fe 100644 --- a/pallets/spp/src/lib.rs +++ b/pallets/spp/src/lib.rs @@ -711,11 +711,11 @@ pub mod pallet { let current_relay_chain_block = >::block_number(); // let current_relay_chain_block = T::RelayChainBlockNumber::current_block_number(); if !update_era_frequency.is_zero() { - ensure!( - change > current_relay_chain_block.saturating_sub(update_era_frequency) - && change <= current_relay_chain_block, - Error::::InvalidLastEraUpdatedBlock - ); + // ensure!( + // change > current_relay_chain_block.saturating_sub(update_era_frequency) + // && change <= current_relay_chain_block, + // Error::::InvalidLastEraUpdatedBlock + // ); LastEraUpdatedBlock::::put(change); LastStakingRound::::insert(FungibleTokenId::NativeToken(1), last_staking_round); @@ -1022,11 +1022,12 @@ impl Pallet { .saturating_add(Rate::one()) .saturating_pow(era_changes.unique_saturated_into()) .saturating_sub(Rate::one()); + let mut total_reward_staking: BalanceOf = Zero::zero(); log::info!( target: "pallet-spp", - "reward distribution to pool treasury era: {:?} reward rate {:?}", - era_changes, reward_rate + "reward distribution to pool treasury era: {:?} reward rate per era {:?} with reward_rate {:?}", + era_changes, reward_rate_per_era, reward_rate ); if !reward_rate.is_zero() { // iterate all pool ledgers @@ -1055,11 +1056,6 @@ impl Pallet { &pool_treasury_account, pool_treasury_reward_commission_amount, )?; - >::accumulate_reward( - &pool_id, - FungibleTokenId::FungibleToken(1), - pool_treasury_reward_commission_amount, - )?; } } @@ -1264,6 +1260,7 @@ impl Pallet { LastEraUpdatedBlock::::put(>::block_number()); Self::handle_redeem_requests(new_era)?; Self::handle_reward_distribution_to_network_pool()?; + Self::handle_reward_distribution_to_pool_treasury(previous_era, new_era)?; Self::deposit_event(Event::::CurrentEraUpdated { new_era_index: new_era }); Ok(()) } From 5ecb3ac9aa965519f7d7bcb8d891cada265b70e9 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Dec 2023 11:53:23 +1300 Subject: [PATCH 153/155] Update latest mock file --- pallets/spp/src/mock.rs | 6 ++---- runtime/metaverse/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pallets/spp/src/mock.rs b/pallets/spp/src/mock.rs index ae1304727..d5a05cb21 100644 --- a/pallets/spp/src/mock.rs +++ b/pallets/spp/src/mock.rs @@ -197,9 +197,7 @@ impl Config for Runtime { type Currency = Balances; type WeightInfo = (); type MinimumStake = MinimumStake; - type RewardPaymentDelay = RewardPaymentDelay; type NetworkFee = NetworkFee; - type BlockNumberToBalance = ConvertInto; type StorageDepositFee = StorageDepositFee; type MultiCurrency = Currencies; type PoolAccount = PoolAccountPalletId; @@ -274,8 +272,8 @@ impl ExtBuilder { pub fn ksm_setup_for_alice_and_bob(self) -> Self { self.balances(vec![ - (ALICE, FungibleTokenId::NativeToken(1), 20000), //KSM - (BOB, FungibleTokenId::NativeToken(1), 20000), //KSM + (ALICE, FungibleTokenId::NativeToken(1), 200000000000), //KSM + (BOB, FungibleTokenId::NativeToken(1), 200000000000), //KSM ]) } } diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index e21e6ee0f..a0b61b2fa 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -182,7 +182,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 96, + spec_version: 98, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e6c35f55402653be247b4ad8d7537738526f8d21 Mon Sep 17 00:00:00 2001 From: codemeit Date: Wed, 13 Dec 2023 08:23:32 +0800 Subject: [PATCH 154/155] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d36afa76..682015ffc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-

Metaverse.Network by BitCountry team

+

MNET (Metaverse.Network) by BitCountry team

## An Ethereum-compatible Blockchain Network Built using Substrate From 8a3324e1cd5e2ef37b44b3cb371bf28a54459670 Mon Sep 17 00:00:00 2001 From: codemeit Date: Wed, 13 Dec 2023 08:25:23 +0800 Subject: [PATCH 155/155] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 682015ffc..0b3612156 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Development Note: It is still a WIP. # 1. Introduction -Metaverse.Network, featuring a seamlessly integrated enriched social layer, stands as an interesting blockchain ecosystem that extends support for both EVM (Ethereum Virtual Machine) and WASM (WebAssembly) smart contracts. This framework empowers developers to harness the network's capabilities in crafting decentralized applications (dApps) with unparalleled versatility and functionality. +MNET (Metaverse.Network), featuring a seamlessly integrated enriched social layer, stands as an interesting blockchain ecosystem that extends support for both EVM (Ethereum Virtual Machine) and WASM (WebAssembly) smart contracts. This framework empowers developers to harness the network's capabilities in crafting decentralized applications (dApps) with unparalleled versatility and functionality. # 2. Build