From 4db265a88716bdca41745c671311ee858a5121f3 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 22 Sep 2022 01:40:41 -0700 Subject: [PATCH] Test refactoring, Fixed ownership storage, and documentation --- pallets/share_distributor/Cargo.toml | 1 + pallets/share_distributor/src/functions.rs | 19 +++- pallets/share_distributor/src/lib.rs | 45 +++++++- pallets/share_distributor/src/tests.rs | 120 ++++++++++++++++----- 4 files changed, 152 insertions(+), 33 deletions(-) diff --git a/pallets/share_distributor/Cargo.toml b/pallets/share_distributor/Cargo.toml index aa0e34d5..22515d23 100644 --- a/pallets/share_distributor/Cargo.toml +++ b/pallets/share_distributor/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "pallet-share_distributor" version = "4.0.0-dev" +description = "Distribute to the asset new owners/contributors, the ownership nft, and the ownership token" authors = ["Fair Squares"] homepage = "https://fair-squares.nl" edition = "2021" diff --git a/pallets/share_distributor/src/functions.rs b/pallets/share_distributor/src/functions.rs index e4a29174..88e45f5e 100644 --- a/pallets/share_distributor/src/functions.rs +++ b/pallets/share_distributor/src/functions.rs @@ -7,6 +7,8 @@ use super::*; use enum_iterator::all; impl Pallet { + +///The function below create a virtual account from the NFT collection and item id's pub fn virtual_account(collection_id: T::NftCollectionId, item_id: T::NftItemId) -> DispatchResult { //Create virtual account let text0 = format!("{:?}_{:?}_account",collection_id,item_id.clone()); @@ -33,6 +35,7 @@ pub fn virtual_account(collection_id: T::NftCollectionId, item_id: T::NftItemId) Ok(()) } +///This function executes all actions relatives to nft transfer from the seller to the virtual account pub fn nft_transaction(collection_id: T::NftCollectionId, item_id: T::NftItemId,virtual_id:T::AccountId) -> DispatchResult { //Get collection @@ -52,10 +55,11 @@ Ok(()) } +///Collect contributors to the bid, and their shares pub fn owner_and_shares(collection_id: T::NftCollectionId, item_id: T::NftItemId) -> Vec<(T::AccountId, Percent)>{ //Get owners and their reserved contribution to the bid - let infos = HousingFund::Reservations::::get((collection_id,item_id)).unwrap(); + let infos = HousingFund::Reservations::::get((collection_id.clone(),item_id.clone())).unwrap(); let vec0 = infos.contributions; let price = infos.amount; let mut vec = Vec::new() ; @@ -65,22 +69,30 @@ pub fn owner_and_shares(collection_id: T::NftCollectionId, item_id: T::NftItemId let share = Percent::from_rational(contribution,price0); debug_assert!(!share.is_zero()); vec.push((i.0.clone(),share)); + //Update Virtual_account storage + Virtual::::mutate(collection_id.clone(),item_id.clone(),|val|{ + let mut val0 = val.clone().unwrap(); + val0.owners.push(i.0.clone()); + *val = Some(val0); + + }); } vec } - +///Create 100 Ownership tokens owned by a virtual account pub fn create_tokens(origin: OriginFor,collection_id: T::NftCollectionId, item_id: T::NftItemId,account: T::AccountId) -> DispatchResult{ //Get token class Id: + let token_id = Virtual::::get(collection_id,item_id).unwrap().token_id; let to = T::Lookup::unlookup(account.clone()); TokenId::::mutate(|val|{ let val0 = val.clone(); *val = val0+1; }); - let token_id = Virtual::::get(collection_id,item_id).unwrap().token_id; + //Create token class let res = Assets::Pallet::::force_create(origin.clone(),token_id.clone().into(),to.clone(),false,One::one()); @@ -97,6 +109,7 @@ pub fn create_tokens(origin: OriginFor,collection_id: T::NftCollectionId, ite } +///Distribute the ownership tokens to the group of new owners pub fn distribute_tokens(account:T::AccountId,collection_id: T::NftCollectionId, item_id: T::NftItemId) -> DispatchResult{ let shares = Self::owner_and_shares(collection_id.clone(),item_id.clone()); let token_id = Virtual::::get(collection_id,item_id).unwrap().token_id; diff --git a/pallets/share_distributor/src/lib.rs b/pallets/share_distributor/src/lib.rs index 6e81492b..875e6741 100644 --- a/pallets/share_distributor/src/lib.rs +++ b/pallets/share_distributor/src/lib.rs @@ -1,5 +1,28 @@ -#![cfg_attr(not(feature = "std"), no_std)] +//! # Share_Distributor Pallet +//! +//! The Share_Distributor Pallet is called by the Bidding Pallet +//! after a Finalised bid was identified on-chain. +//! It will distribute to the asset new owners, the ownership nft, and the ownership token +//! connected to the asset at the center of the transaction. +//! +//! ## Overview +//! +//! The On boarding Pallet fulfill the following tasks: +//! - Create a virtual account which will hold the nft +//! - Connect the Virtual account to the new owners/contributors +//! through the use of a storage/struct +//! - Execute the Nft transaction between Seller and Virtual account +//! - Mint 100 Ownership Tokens, which represent the asset share of +//! each owner +//! - Distribute the ownership tokens to the new owners. +//! +//! Dispatchable Functions +//! +//! * `create_virtual` - Will sequencially execute each of the steps +//! described in the Overview. + +#![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; pub use pallet_assets as Assets; @@ -88,6 +111,11 @@ pub mod pallet { nft_transfer_to: T::AccountId, nft_transfer_from: T::AccountId, when: BlockNumberOf + }, + OwnershipTokensDistributed{ + from: T::AccountId, + to: Vec, + token_id:::AssetId, } } @@ -126,7 +154,7 @@ pub mod pallet { Self::distribute_tokens(account.clone(),collection_id.clone(),item_id.clone()).ok(); - // Emit an event. + // Emit some events. let created = >::block_number(); Self::deposit_event(Event::VirtualCreated{ account: account.clone(), @@ -135,13 +163,20 @@ pub mod pallet { when: created.clone(), }); - //Emit another event Self::deposit_event(Event::NftTransactionExecuted{ - nft_transfer_to: account, - nft_transfer_from: seller, + nft_transfer_to: account.clone(), + nft_transfer_from: seller.clone(), when: created }); + let new_owners = Self::virtual_acc(collection_id.clone(),item_id.clone()).unwrap().owners; + let token_id = Self::virtual_acc(collection_id.clone(),item_id.clone()).unwrap().token_id; + Self::deposit_event(Event::OwnershipTokensDistributed{ + from: account.clone(), + to: new_owners, + token_id: token_id, + }); + Ok(()) } diff --git a/pallets/share_distributor/src/tests.rs b/pallets/share_distributor/src/tests.rs index 281df444..ceddaa2b 100644 --- a/pallets/share_distributor/src/tests.rs +++ b/pallets/share_distributor/src/tests.rs @@ -3,6 +3,8 @@ pub use crate::mock::*; pub use frame_support::{assert_noop, assert_ok}; use frame_system::pallet_prelude::OriginFor; +pub type Bvec = BoundedVec::StringLimit>; + pub fn prep_roles() { RoleModule::set_role(Origin::signed(CHARLIE).clone(), CHARLIE, Acc::SERVICER).ok(); RoleModule::account_approval(Origin::signed(ALICE), CHARLIE).ok(); @@ -19,21 +21,11 @@ pub fn prep_roles() { RoleModule::account_approval(Origin::signed(ALICE), ACCOUNT_WITH_NO_BALANCE0).ok(); } -#[test] -fn virtual0(){ - ExtBuilder::default().build().execute_with(|| { - let metadata0: BoundedVec::StringLimit> = - b"metadata0".to_vec().try_into().unwrap(); - let metadata1: BoundedVec::StringLimit> = - b"metadata1".to_vec().try_into().unwrap(); - let metadata2: BoundedVec::StringLimit> = - b"metadata2".to_vec().try_into().unwrap(); +pub fn prep_test(price1:u64,price2:u64, metadata0:Bvec,metadata1:Bvec,metadata2:Bvec){ + prep_roles(); - //put some funds in FairSquare SlashFees account - let fees_account = Onboarding::Pallet::::account_id(); - ::Currency::make_free_balance_be(&fees_account,150_000u32.into()); - + //Dave and EVE contribute to the fund assert_ok!(HousingFund::Pallet::::contribute_to_fund(Origin::signed(DAVE),50_000)); assert_ok!(HousingFund::Pallet::::contribute_to_fund(Origin::signed(EVE),50_000)); @@ -51,17 +43,40 @@ fn virtual0(){ metadata0 )); // Bob creates a proposal without submiting for review - let price = 40_000; + assert_ok!(OnboardingModule::create_and_submit_proposal( Origin::signed(BOB), NftColl::OFFICESTEST, - Some(price.clone()), + Some(price1.clone()), metadata1, false )); - + assert_ok!(OnboardingModule::create_and_submit_proposal( + Origin::signed(BOB), + NftColl::APPARTMENTSTEST, + Some(price2.clone()), + metadata2, + false + )); +} + + +#[test] +fn share_distributor0(){ + ExtBuilder::default().build().execute_with(|| { + + let metadata0 = b"metadata0".to_vec().try_into().unwrap(); + let metadata1 = b"metadata1".to_vec().try_into().unwrap(); + let metadata2 = b"metadata2".to_vec().try_into().unwrap(); + //put some funds in FairSquare SlashFees account + let fees_account = Onboarding::Pallet::::account_id(); + ::Currency::make_free_balance_be(&fees_account,150_000u32.into()); + + let price1 = 40_000; + let price2 = 30_000; + prep_test(price1.clone(),price2.clone(),metadata0,metadata1,metadata2); let coll_id0 = NftColl::OFFICESTEST.value(); let item_id0 = pallet_nft::ItemsCount::::get()[coll_id0 as usize] - 1; let origin: OriginFor = frame_system::RawOrigin::Root.into(); @@ -89,7 +104,7 @@ fn virtual0(){ let fund_op = HousingFund::FundOperation{ nft_collection_id: coll_id0.clone(), nft_item_id: item_id0.clone(), - amount: price.clone(), + amount: price1.clone(), block_number:1, contributions:vec![(EVE,25_000),(DAVE,15_000)], }; @@ -100,6 +115,7 @@ fn virtual0(){ //Create token assert_ok!(ShareDistributor::create_tokens(origin.clone(),coll_id0.clone(),item_id0.clone(),new_owner0.clone())); assert_eq!(1,ShareDistributor::token_id()); + assert_eq!(0,ShareDistributor::virtual_acc(coll_id0.clone(),item_id0.clone()).unwrap().token_id); assert_eq!(100,Assets::Pallet::::total_supply(id.clone())); //Check that new_owner0 is in possession of 100 tokens @@ -114,15 +130,8 @@ fn virtual0(){ println!("Tokens own by DAVE:{:?}\nTokens own by Eve:{:?}",balance0,balance1); // Bob creates a second proposal without submiting for review - let price = 30_000; - assert_ok!(OnboardingModule::create_and_submit_proposal( - Origin::signed(BOB), - NftColl::APPARTMENTSTEST, - Some(price.clone()), - metadata2, - false - )); + let coll_id1 = NftColl::APPARTMENTSTEST.value(); @@ -160,4 +169,65 @@ fn virtual0(){ }); +} + +#[test] +fn share_distributor1(){ + ExtBuilder::default().build().execute_with(|| { + let metadata0 = b"metadata0".to_vec().try_into().unwrap(); + let metadata1 = b"metadata1".to_vec().try_into().unwrap(); + let metadata2 = b"metadata2".to_vec().try_into().unwrap(); + //put some funds in FairSquare SlashFees account + let fees_account = Onboarding::Pallet::::account_id(); + ::Currency::make_free_balance_be(&fees_account,150_000u32.into()); + + let price1 = 40_000; + let price2 = 30_000; + prep_test(price1.clone(),price2.clone(),metadata0,metadata1,metadata2); + let coll_id0 = NftColl::OFFICESTEST.value(); + let item_id0 = pallet_nft::ItemsCount::::get()[coll_id0 as usize] - 1; + let origin: OriginFor = frame_system::RawOrigin::Root.into(); + let origin2 = Origin::signed(BOB); + + //Create a FundOperation struct for this asset + let fund_op = HousingFund::FundOperation{ + nft_collection_id: coll_id0.clone(), + nft_item_id: item_id0.clone(), + amount: price1.clone(), + block_number:1, + contributions:vec![(EVE,25_000),(DAVE,15_000)], + }; + + //Add new owners and asset to housing fund + HousingFund::Reservations::::insert((coll_id0.clone(),item_id0.clone()),fund_op); + + //Change first asset status to FINALISED + Onboarding::Pallet::::change_status(origin2.clone(),NftColl::OFFICESTEST,item_id0.clone(),Onboarding::AssetStatus::FINALISED).ok(); + + //Store initial owner + let old_owner0 = pallet_nft::Pallet::::owner(coll_id0.clone(),item_id0.clone()).unwrap(); + + assert_ok!(ShareDistributor::create_virtual(origin.clone(),coll_id0.clone(),item_id0.clone())); + let when = >::block_number(); + let new_owner0 = ShareDistributor::virtual_acc(coll_id0.clone(),item_id0.clone()).unwrap().virtual_account; + let owners = ShareDistributor::virtual_acc(coll_id0.clone(),item_id0.clone()).unwrap().owners; + assert_eq!(owners.len()>1,true); + expect_events(vec![ + crate::Event::VirtualCreated { + account: new_owner0.clone(), + collection: coll_id0.clone(), + item: item_id0.clone(), + when: when, + } + .into(), + crate::Event::NftTransactionExecuted { + nft_transfer_to: new_owner0.clone(), + nft_transfer_from: old_owner0.clone(), + when: when, + } + .into() + ]); + + + }) } \ No newline at end of file