From 8bc120e037b8170046b7df3d2b37e7f36e7cfbf6 Mon Sep 17 00:00:00 2001 From: Dreyhh Date: Wed, 26 Apr 2023 11:36:12 -0600 Subject: [PATCH 01/22] Adds RBAC to afloat pallet --- pallets/afloat/src/functions.rs | 126 ++++++++++++++++++++++++++++++-- pallets/afloat/src/lib.rs | 25 +++++-- pallets/afloat/src/types.rs | 77 ++++++++++++++++++- 3 files changed, 216 insertions(+), 12 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index bb51d0a9..f30564ea 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -8,9 +8,15 @@ use pallet_fruniques::types::CollectionDescription; use pallet_fruniques::types::FruniqueRole; use frame_support::pallet_prelude::*; // use frame_support::traits::OriginTrait; +use pallet_rbac::types::IdOrVec; +use pallet_rbac::types::RoleBasedAccessControl; +use scale_info::prelude::vec; impl Pallet { pub fn do_initial_setup(creator: T::AccountId, admin: T::AccountId) -> DispatchResult { + + Self::initialize_rbac()?; + let creator_user: User = User { first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), last_name: ShortString::try_from(b"Creator".to_vec()).unwrap(), @@ -27,6 +33,7 @@ impl Pallet { lock_expiration_date: None, }; >::insert(creator.clone(), creator_user); + Self::give_role_to_user(creator.clone(), AfloatRole::Owner)?; if admin != creator { let admin_user: User = User { @@ -44,9 +51,10 @@ impl Pallet { tax_authority_id: 1, lock_expiration_date: None, }; - >::insert(admin, admin_user); + >::insert(admin.clone(), admin_user); + Self::give_role_to_user(admin, AfloatRole::Admin)?; } - + Ok(()) } @@ -95,6 +103,7 @@ impl Pallet { lock_expiration_date: None, }; >::insert(user_address.clone(), user); + Self::give_role_to_user(user_address.clone(), AfloatRole::BuyerOrSeller)?; Self::deposit_event(Event::NewUser(user_address.clone())); }, SignUpArgs::CPA { first_name, last_name, email, license_number, state } => { @@ -114,6 +123,7 @@ impl Pallet { lock_expiration_date: None, }; >::insert(user_address.clone(), user); + Self::give_role_to_user(user_address.clone(), AfloatRole::CPA)?; Self::deposit_event(Event::NewUser(user_address.clone())); }, } @@ -122,6 +132,7 @@ impl Pallet { Self::add_to_afloat_collection(user_address.clone(),FruniqueRole::Collaborator)?; pallet_gated_marketplace::Pallet::::self_enroll(user_address, marketplace_id)?; + Ok(()) } /// Function for editing user information. @@ -157,7 +168,6 @@ impl Pallet { cpa_id: Option, state: Option, ) -> DispatchResult { - ensure!(>::contains_key(user_address.clone()), Error::::UserNotFound); >::try_mutate::<_, _, DispatchError, _>(user_address.clone(), |user| { let user = user.as_mut().ok_or(Error::::FailedToEditUserAccount)?; @@ -209,11 +219,17 @@ impl Pallet { /// Returns Ok(()) on success. /// pub fn do_delete_user(_actor: T::AccountId, user_address: T::AccountId) -> DispatchResult { - ensure!(>::contains_key(user_address.clone()), Error::::UserNotFound); - + Self::remove_from_afloat_collection(user_address.clone(), FruniqueRole::Collaborator)?; Self::remove_from_afloat_marketplace(user_address.clone())?; + + let role = if Self::is_cpa(user_address.clone()) { + AfloatRole::CPA + } else { + AfloatRole::BuyerOrSeller + }; + Self::remove_role_from_user(user_address.clone(), role)?; >::remove(user_address.clone()); Self::deposit_event(Event::UserDeleted(user_address.clone())); Ok(()) @@ -259,4 +275,104 @@ impl Pallet { let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); pallet_gated_marketplace::Pallet::::remove_from_market_lists(invitee, MarketplaceRole::Participant, marketplace_id) } + + pub fn pallet_id() -> IdOrVec { + IdOrVec::Vec(Self::module_name().as_bytes().to_vec()) + } + + pub fn is_admin_or_owner(account: T::AccountId) -> bool { + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + ::Rbac::has_role( + account.clone(), + Self::pallet_id(), + &marketplace_id, + [AfloatRole::Admin.id(), AfloatRole::Owner.id()].to_vec(), + ) + .is_ok() + } + + pub fn is_cpa(account: T::AccountId) -> bool { + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + ::Rbac::has_role( + account.clone(), + Self::pallet_id(), + &marketplace_id, + [AfloatRole::CPA.id()].to_vec(), + ) + .is_ok() + } + + pub fn give_role_to_user( + authority: T::AccountId, + role: AfloatRole, + ) -> DispatchResult { + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + ::Rbac::assign_role_to_user( + authority, + Self::pallet_id(), + &marketplace_id, + role.id(), + )?; + + Ok(()) + } + + pub fn remove_role_from_user( + authority: T::AccountId, + role: AfloatRole, + ) -> DispatchResult { + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + ::Rbac::remove_role_from_user( + authority, + Self::pallet_id(), + &marketplace_id, + role.id(), + )?; + + Ok(()) + } + + pub fn initialize_rbac() -> DispatchResult { + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + ::Rbac::create_scope(Self::pallet_id(), marketplace_id)?; + let pallet_id = Self::pallet_id(); + let super_roles = vec![AfloatRole::Owner.to_vec(), AfloatRole::Admin.to_vec()]; + let super_role_ids = + ::Rbac::create_and_set_roles(pallet_id.clone(), super_roles)?; + let super_permissions = Permission::admin_permissions(); + for super_role in super_role_ids { + ::Rbac::create_and_set_permissions( + pallet_id.clone(), + super_role, + super_permissions.clone(), + )?; + } + let participant_roles = vec![AfloatRole::BuyerOrSeller.to_vec(), AfloatRole::CPA.to_vec()]; + ::Rbac::create_and_set_roles( + pallet_id.clone(), + participant_roles, + )?; + + Ok(()) + } + + pub fn do_delete_all_users() -> DispatchResult { + UserInfo::::iter_keys().try_for_each(|account_id| { + if !Self::is_admin_or_owner(account_id.clone()) { + Self::remove_from_afloat_collection(account_id.clone(), FruniqueRole::Collaborator)?; + Self::remove_from_afloat_marketplace(account_id.clone())?; + + let role = if Self::is_cpa(account_id.clone()) { + AfloatRole::CPA + } else { + AfloatRole::BuyerOrSeller + }; + + Self::remove_role_from_user(account_id.clone(), role)?; + UserInfo::::remove(account_id); + } + Ok::<(), DispatchError>(()) + })?; + Ok(()) + } } diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index c5d1bc22..4bb7a164 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -76,6 +76,12 @@ pub mod pallet { FailedToEditUserAccount, // Failed to create fruniques collection FailedToCreateFruniquesCollection, + // Failed to remove Fruniques role + FailedToRemoveFruniquesRole, + // User is not authorized to perform this action + Unauthorized, + // Pallet has not ben initialized yet + NotInitialized, } #[pallet::storage] @@ -135,7 +141,7 @@ pub mod pallet { pallet_gated_marketplace::Pallet::::do_initial_setup()?; - Self::do_initial_setup(creator.clone(), admin.clone())?; + let label: BoundedVec = BoundedVec::try_from(b"Afloat".to_vec()).expect("Label too long"); @@ -149,16 +155,19 @@ pub mod pallet { AfloatMarketPlaceId::::put(marketplace_id); Self::add_to_afloat_collection(admin.clone(),FruniqueRole::Admin)?; - pallet_gated_marketplace::Pallet::do_create_marketplace(creator, admin, marketplace)?; + pallet_gated_marketplace::Pallet::do_create_marketplace(creator.clone(), admin.clone(), marketplace)?; + + Self::do_initial_setup(creator, admin)?; + Ok(()) } #[pallet::call_index(1)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] pub fn kill_storage(origin: OriginFor) -> DispatchResult { - ensure_signed(origin.clone())?; - >::kill(); - let _ = >::clear(1000, None); + let who = ensure_signed(origin.clone())?; + ensure!(Self::is_admin_or_owner(who), Error::::Unauthorized); + Self::do_delete_all_users()?; Ok(()) } @@ -176,9 +185,13 @@ pub mod pallet { address: T::AccountId, args: UpdateUserArgs, ) -> DispatchResult { - // TODO: Check if the user is editing himself or is an admin + let who = ensure_signed(origin)?; + ensure!(>::contains_key(address.clone()), Error::::UserNotFound); + ensure!(!Self::is_admin_or_owner(address.clone()), Error::::Unauthorized); + ensure!(who.clone() == address || Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); + match args { UpdateUserArgs::Edit { first_name, last_name, email, lang_key, phone, credits_needed, cpa_id, state } => { Self::do_edit_user(who, address, first_name, last_name, email, lang_key, phone, credits_needed, cpa_id, state)?; diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index d0fca147..2f5e6c76 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -1,6 +1,7 @@ use super::*; use frame_support::pallet_prelude::*; - +use frame_support::sp_io::hashing::blake2_256; +use sp_runtime::sp_std::vec::Vec; pub type ShortString = BoundedVec>; pub type LongString = BoundedVec>; @@ -95,3 +96,77 @@ pub enum SignUpArgs { state: u32, }, } + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,)] +pub enum AfloatRole { + Owner, + Admin, + BuyerOrSeller, + CPA, +} + +impl Default for AfloatRole { + fn default() -> Self { + AfloatRole::BuyerOrSeller + } +} + +impl AfloatRole { + pub fn to_vec(self) -> Vec { + match self { + Self::Owner => "Owner".as_bytes().to_vec(), + Self::Admin => "Admin".as_bytes().to_vec(), + Self::BuyerOrSeller => "BuyerOrSeller".as_bytes().to_vec(), + Self::CPA => "CPA".as_bytes().to_vec(), + } + } + + pub fn id(&self) -> [u8; 32] { + self.to_vec().using_encoded(blake2_256) + } + + pub fn enum_to_vec() -> Vec> { + use crate::types::AfloatRole::*; + [ + Owner.to_vec(), + Admin.to_vec(), + BuyerOrSeller.to_vec(), + CPA.to_vec(), + ] + .to_vec() + } +} + +#[derive( + Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy, +)] +pub enum Permission { + CreateUser, + EditUser, + DeleteUser, +} + +impl Permission { + pub fn to_vec(self) -> Vec { + match self { + Self::CreateUser => "CreateUser".as_bytes().to_vec(), + Self::EditUser => "EditUser".as_bytes().to_vec(), + Self::DeleteUser => "DeleteUser".as_bytes().to_vec(), + } + } + + pub fn id(&self) -> [u8; 32] { + self.to_vec().using_encoded(blake2_256) + } + + pub fn admin_permissions() -> Vec> { + use crate::types::Permission::*; + let admin_permissions = [ + CreateUser.to_vec(), + EditUser.to_vec(), + DeleteUser.to_vec(), + ] + .to_vec(); + admin_permissions + } +} From 2c670ce572837a3c6c8346aed1f8232c7d092ef1 Mon Sep 17 00:00:00 2001 From: Dreyhh Date: Thu, 27 Apr 2023 00:27:42 -0600 Subject: [PATCH 02/22] Adds mapped-assets into gated-marketplace --- Cargo.lock | 2 + pallets/afloat/src/lib.rs | 23 +- pallets/gated-marketplace/Cargo.toml | 4 +- pallets/gated-marketplace/src/functions.rs | 101 +++-- pallets/gated-marketplace/src/lib.rs | 18 +- pallets/gated-marketplace/src/mock.rs | 68 +++- pallets/gated-marketplace/src/tests.rs | 425 ++++++++++++++++----- pallets/gated-marketplace/src/types.rs | 5 +- parachain-runtime/Cargo.toml | 2 + parachain-runtime/src/lib.rs | 27 ++ 10 files changed, 535 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4f0e0e7..222ee8f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3328,6 +3328,7 @@ dependencies = [ "pallet-im-online", "pallet-indices", "pallet-lottery", + "pallet-mapped-assets", "pallet-membership", "pallet-multisig", "pallet-preimage", @@ -5975,6 +5976,7 @@ dependencies = [ "log", "pallet-balances", "pallet-fruniques", + "pallet-mapped-assets", "pallet-rbac", "pallet-timestamp", "pallet-uniques", diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 4bb7a164..1c7ef976 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -21,7 +21,6 @@ pub mod pallet { use frame_system::RawOrigin; use pallet_gated_marketplace::types::*; use sp_runtime::Permill; - use sp_runtime::traits::StaticLookup; use pallet_fruniques::types::CollectionDescription; use pallet_fruniques::types::FruniqueRole; const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -109,6 +108,13 @@ pub mod pallet { ::CollectionId, // Afloat's frunique collection id >; + #[pallet::storage] + #[pallet::getter(fn asset_id)] + pub(super) type AfloatAssetId = StorageValue< + _, + ::AssetId, // Afloat's frunique collection id + >; + #[pallet::call] impl Pallet where @@ -125,15 +131,9 @@ pub mod pallet { { ensure_signed(origin.clone())?; let asset_id: T::AssetId = Default::default(); - let min_balance: T::Balance = T::Balance::from(1u32); - let metadata: CollectionDescription = BoundedVec::try_from(b"Afloat".to_vec()).expect("Label too long"); + AfloatAssetId::::put(asset_id.clone()); - pallet_mapped_assets::Pallet::::create( - origin.clone(), - asset_id, - T::Lookup::unlookup(creator.clone()), - min_balance, - )?; + let metadata: CollectionDescription = BoundedVec::try_from(b"Afloat".to_vec()).expect("Label too long"); pallet_fruniques::Pallet::::do_initial_setup()?; @@ -141,21 +141,20 @@ pub mod pallet { pallet_gated_marketplace::Pallet::::do_initial_setup()?; - - let label: BoundedVec = BoundedVec::try_from(b"Afloat".to_vec()).expect("Label too long"); let marketplace: Marketplace = Marketplace { label, buy_fee: Permill::from_percent(2), sell_fee: Permill::from_percent(4), + asset_id, creator: creator.clone(), }; let marketplace_id = marketplace.clone().using_encoded(blake2_256); AfloatMarketPlaceId::::put(marketplace_id); Self::add_to_afloat_collection(admin.clone(),FruniqueRole::Admin)?; - pallet_gated_marketplace::Pallet::do_create_marketplace(creator.clone(), admin.clone(), marketplace)?; + pallet_gated_marketplace::Pallet::do_create_marketplace(RawOrigin::Signed(creator.clone()).into(), admin.clone(), marketplace)?; Self::do_initial_setup(creator, admin)?; diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index f68ac2b1..b90db52b 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -30,6 +30,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "p pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } pallet-fruniques = { path = "../fruniques", default-features = false, version = "0.1.0-dev" } pallet-rbac = { path = "../rbac/", default-features = false, version = "4.0.0-dev" } +pallet-mapped-assets = { path = "../mapped-assets/", default-features = false, version = "4.0.0-dev" } [dev-dependencies] sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } @@ -49,7 +50,8 @@ std = [ "pallet-uniques/std", "pallet-fruniques/std", "pallet-timestamp/std", - "pallet-rbac/std" + "pallet-rbac/std", + "pallet-mapped-assets/std" ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 9bc0cd19..9bd51887 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -5,11 +5,11 @@ use frame_support::sp_io::hashing::blake2_256; use pallet_rbac::types::*; use scale_info::prelude::vec; // vec![] macro use sp_runtime::sp_std::vec::Vec; // vec primitive - -use frame_support::traits::Currency; -use frame_support::traits::ExistenceRequirement::KeepAlive; +use frame_system::pallet_prelude::*; use frame_support::traits::Time; use sp_runtime::Permill; +use sp_runtime::traits::StaticLookup; +use frame_system::RawOrigin; impl Pallet { pub fn do_initial_setup() -> DispatchResult { @@ -50,17 +50,30 @@ impl Pallet { } pub fn do_create_marketplace( - owner: T::AccountId, + origin: OriginFor, admin: T::AccountId, marketplace: Marketplace, ) -> DispatchResult { + let owner = ensure_signed(origin.clone())?; // Gen market id let marketplace_id = marketplace.using_encoded(blake2_256); + //Get asset id + let asset_id = marketplace.asset_id; + let min_balance: T::Balance = T::Balance::from(1u32); + + // ensure the generated id is unique ensure!( !>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists ); + // Create asset + pallet_mapped_assets::Pallet::::create( + origin, + asset_id, + T::Lookup::unlookup(owner.clone()), + min_balance, + )?; //Insert on marketplaces and marketplaces by auth ::Rbac::create_scope(Self::pallet_id(), marketplace_id)?; Self::insert_in_auth_market_lists(owner.clone(), MarketplaceRole::Owner, marketplace_id)?; @@ -327,7 +340,7 @@ impl Pallet { marketplace_id: [u8; 32], collection_id: T::CollectionId, item_id: T::ItemId, - price: BalanceOf, + price: T::Balance, percentage: u32, ) -> DispatchResult { //This function is only called by the owner of the marketplace @@ -402,7 +415,7 @@ impl Pallet { marketplace_id: [u8; 32], collection_id: T::CollectionId, item_id: T::ItemId, - price: BalanceOf, + price: T::Balance, percentage: u32, ) -> DispatchResult { @@ -410,6 +423,8 @@ impl Pallet { //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + + //ensure the collection exists //For this case user doesn't need to be the owner of the collection //but the owner of the item cannot create a buy offer for their own collection @@ -427,8 +442,12 @@ impl Pallet { &item_id )?; + //Get asset id + let asset_id = >::get(marketplace_id).ok_or(Error::::MarketplaceNotFound)?.asset_id; + //ensure user has enough balance to create the offer - let total_user_balance = T::Currency::total_balance(&authority); + let total_user_balance = pallet_mapped_assets::Pallet::::balance(asset_id,authority.clone()); + ensure!(total_user_balance >= price, Error::::NotEnoughBalance); //ensure the price is valid @@ -481,13 +500,15 @@ impl Pallet { Ok(()) } - pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8; 32]) -> DispatchResult + pub fn do_take_sell_offer(origin : OriginFor, offer_id: [u8; 32]) -> DispatchResult where ::ItemId: From, { //This extrinsic is called by the user who wants to buy the item //get offer data + let buyer = ensure_signed(origin.clone())?; let offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + let marketplace_id = offer_data.marketplace_id; Self::is_authorized(buyer.clone(), &offer_data.marketplace_id, Permission::TakeSellOffer)?; @@ -509,17 +530,33 @@ impl Pallet { //ensure the offer is open and available ensure!(offer_data.status == OfferStatus::Open, Error::::OfferIsNotAvailable); //TODO: Use free_balance instead of total_balance - //Get the buyer's balance - let total_amount_buyer = T::Currency::total_balance(&buyer); + //Get asset id + let asset_id = >::get(marketplace_id).ok_or(Error::::MarketplaceNotFound)?.asset_id; + + //ensure user has enough balance to create the offer + let total_amount_buyer = pallet_mapped_assets::Pallet::::balance(asset_id.clone(), buyer.clone()); //ensure the buyer has enough balance to buy the item ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); let marketplace = >::get(offer_data.marketplace_id).ok_or(Error::::OfferNotFound)?; - let owners_cut: BalanceOf = offer_data.price - offer_data.fee; + let owners_cut: T::Balance = offer_data.price - offer_data.fee; //Transfer the balance - T::Currency::transfer(&buyer, &owner_item, owners_cut, KeepAlive)?; - T::Currency::transfer(&buyer, &marketplace.creator, offer_data.fee, KeepAlive)?; + pallet_mapped_assets::Pallet::::transfer( + origin.clone(), + asset_id.clone().into(), + T::Lookup::unlookup(owner_item.clone()), + owners_cut, + )?; + //T::Currency::transfer(&buyer, &owner_item, owners_cut, KeepAlive)?; + + pallet_mapped_assets::Pallet::::transfer( + origin.clone(), + asset_id.clone().into(), + T::Lookup::unlookup(marketplace.creator.clone()), + offer_data.fee, + )?; + //T::Currency::transfer(&buyer, &marketplace.creator, offer_data.fee, KeepAlive)?; pallet_fruniques::Pallet::::do_thaw(&offer_data.collection_id, offer_data.item_id)?; if offer_data.percentage == Permill::from_percent(100) { @@ -600,24 +637,42 @@ impl Pallet { //ensure the offer is open and available ensure!(offer_data.status == OfferStatus::Open, Error::::OfferIsNotAvailable); - - //TODO: Use free_balance instead of total_balance - //Get the buyer's balance - let total_amount_buyer = T::Currency::total_balance(&offer_data.creator); + + let marketplace_id = offer_data.marketplace_id; + //Get asset id + let asset_id = >::get(marketplace_id).ok_or(Error::::MarketplaceNotFound)?.asset_id; + + //ensure user has enough balance to create the offer + let total_amount_buyer = pallet_mapped_assets::Pallet::::balance(asset_id.clone(), offer_data.creator.clone()); //ensure the buy_offer_creator has enough balance to buy the item ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); let marketplace = >::get(offer_data.marketplace_id).ok_or(Error::::OfferNotFound)?; - let owners_cut: BalanceOf = offer_data.price - offer_data.fee; + let owners_cut: T::Balance = offer_data.price - offer_data.fee; //Transfer the balance to the owner of the item - T::Currency::transfer(&offer_data.creator, &owner_item, owners_cut, KeepAlive)?; - T::Currency::transfer( + pallet_mapped_assets::Pallet::::transfer( + RawOrigin::Signed(offer_data.creator.clone()).into(), + asset_id.clone().into(), + T::Lookup::unlookup(owner_item.clone()), + owners_cut, + )?; + //T::Currency::transfer(&offer_data.creator, &owner_item, owners_cut, KeepAlive)?; + + pallet_mapped_assets::Pallet::::transfer( + RawOrigin::Signed(offer_data.creator.clone()).into(), + asset_id.clone().into(), + T::Lookup::unlookup(marketplace.creator.clone()), + offer_data.fee, + )?; + + /* T::Currency::transfer( &offer_data.creator, &marketplace.creator, offer_data.fee, KeepAlive, - )?; + )?; */ + pallet_fruniques::Pallet::::do_thaw(&offer_data.collection_id, offer_data.item_id)?; if offer_data.percentage == Permill::from_percent(100) { @@ -1137,8 +1192,8 @@ impl Pallet { Ok(()) } - fn is_the_offer_valid(price: BalanceOf, percentage: Permill) -> DispatchResult { - let minimun_amount: BalanceOf = 1000u32.into(); + fn is_the_offer_valid(price: T::Balance, percentage: Permill) -> DispatchResult { + let minimun_amount: T::Balance = 1000u32.into(); ensure!(price > minimun_amount, Error::::PriceMustBeGreaterThanZero); ensure!(percentage <= Permill::from_percent(99), Error::::ExceedMaxPercentage); ensure!(percentage >= Permill::from_percent(1), Error::::ExceedMinPercentage); diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 040e45be..3839683e 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -29,7 +29,7 @@ pub mod pallet { >>::Balance; #[pallet::config] - pub trait Config: frame_system::Config + pallet_fruniques::Config { + pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_mapped_assets::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type Moment: Parameter @@ -332,6 +332,8 @@ pub mod pallet { UserAlreadyBlocked, /// The owner of the NFT is not in the marketplace OwnerNotInMarketplace, + /// MappedAssetId not found + AssetNotFound, } #[pallet::call] @@ -363,15 +365,17 @@ pub mod pallet { label: BoundedVec, buy_fee: u32, sell_fee: u32, + asset_id: T::AssetId, ) -> DispatchResult { - let who = ensure_signed(origin)?; // origin will be market owner + let who = ensure_signed(origin.clone())?; // origin will be market owner let m = Marketplace { label, buy_fee: Permill::from_percent(buy_fee), sell_fee: Permill::from_percent(sell_fee), + asset_id, creator: who.clone(), }; - Self::do_create_marketplace(who, admin, m) + Self::do_create_marketplace(origin, admin, m) } /// Block or Unblock a user from apllying to a marketplace. @@ -661,7 +665,7 @@ pub mod pallet { marketplace_id: [u8; 32], collection_id: T::CollectionId, item_id: T::ItemId, - price: BalanceOf, + price: T::Balance, percentage: u32, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -693,9 +697,9 @@ pub mod pallet { #[pallet::call_index(12)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(1))] pub fn take_sell_offer(origin: OriginFor, offer_id: [u8; 32]) -> DispatchResult { - let who = ensure_signed(origin.clone())?; + ensure_signed(origin.clone())?; - Self::do_take_sell_offer(who, offer_id) + Self::do_take_sell_offer(origin, offer_id) } /// Delete an offer. @@ -744,7 +748,7 @@ pub mod pallet { marketplace_id: [u8; 32], collection_id: T::CollectionId, item_id: T::ItemId, - price: BalanceOf, + price: T::Balance, percentage: u32, ) -> DispatchResult { let who = ensure_signed(origin)?; diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 68516064..22e713a3 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -1,16 +1,30 @@ use crate as pallet_gated_marketplace; -use frame_support::{parameter_types, traits::AsEnsureOriginWithArg}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, GenesisBuild}, +}; use frame_system as system; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; - +/// Balance of an account. +pub type Balance = u128; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; use frame_system::EnsureRoot; use system::EnsureSigned; +use pallet_mapped_assets::DefaultCallback; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, Block as BlockT, IdentifyAccount, NumberFor, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, Percent, +}; +use sp_runtime::AccountId32; +type AccountId = u64; +type AssetId = u32; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub enum Test where @@ -25,9 +39,12 @@ frame_support::construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, RBAC: pallet_rbac::{Pallet, Call, Storage, Event}, + Assets: pallet_mapped_assets::{Pallet, Call, Storage, Event}, } ); + + parameter_types! { pub const BlockHashCount: u64 = 250; pub const SS58Prefix: u8 = 42; @@ -194,3 +211,50 @@ impl pallet_timestamp::Config for Test { type MinimumPeriod = (); type WeightInfo = (); } +parameter_types! { + + pub const AssetDeposit: Balance = 100; + pub const ApprovalDeposit: Balance = 1; + pub const RemoveItemsLimit: u32 = 1000; +} + +pub trait AssetsCallback { + /// Indicates that asset with `id` was successfully created by the `owner` + fn created(_id: &AssetId, _owner: &AccountId) {} + + /// Indicates that asset with `id` has just been destroyed + fn destroyed(_id: &AssetId) {} +} + +pub struct AssetsCallbackHandle; +impl pallet_mapped_assets::AssetsCallback for AssetsCallbackHandle { + fn created(_id: &AssetId, _owner: &u64) { + } + + fn destroyed(_id: &AssetId) { + } +} + + +impl pallet_mapped_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = u64; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = ConstU64<1>; + type AssetAccountDeposit = ConstU64<10>; + type MetadataDepositBase = ConstU64<1>; + type MetadataDepositPerByte = ConstU64<1>; + type ApprovalDeposit = ConstU64<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type WeightInfo = (); + type CallbackHandle = AssetsCallbackHandle; + type Extra = (); + type RemoveItemsLimit = ConstU32<5>; + type MaxReserves = MaxReserves; + type ReserveIdentifier = u32; +} \ No newline at end of file diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index 43d0988f..06cb3295 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -28,6 +28,23 @@ fn get_marketplace_id(label: &str, buy_fee: u32, sell_fee:u32, creator: u64) -> label: create_label(label), buy_fee, sell_fee, + asset_id: 1, + creator, + }; + + marketplace.using_encoded(blake2_256) + +} + +fn get_marketplace_id2(label: &str, buy_fee: u32, sell_fee:u32, creator: u64, asset: u32) -> [u8; 32] { + let buy_fee = Permill::from_percent(buy_fee); + let sell_fee = Permill::from_percent(sell_fee); + + let marketplace = Marketplace:: { + label: create_label(label), + buy_fee, + sell_fee, + asset_id: asset, creator, }; @@ -123,13 +140,15 @@ fn create_custodian_fields( #[test] fn create_marketplace_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -140,13 +159,15 @@ fn create_marketplace_works() { #[test] fn duplicate_marketplaces_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); assert_noop!( GatedMarketplace::create_marketplace( @@ -154,7 +175,8 @@ fn duplicate_marketplaces_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, ), Error::::MarketplaceAlreadyExists ); @@ -164,13 +186,15 @@ fn duplicate_marketplaces_shouldnt_work() { #[test] fn exceeding_max_roles_per_auth_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); let m_label = create_label("my marketplace"); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, m_label.clone(), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -195,13 +219,15 @@ fn exceeding_max_roles_per_auth_shouldnt_work() { #[test] fn apply_to_marketplace_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -222,13 +248,15 @@ fn apply_to_marketplace_works() { #[test] fn apply_with_custodian_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -250,13 +278,15 @@ fn apply_with_custodian_works() { #[test] fn apply_with_same_account_as_custodian_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -275,13 +305,15 @@ fn apply_with_same_account_as_custodian_shouldnt_work() { #[test] fn exceeding_max_applications_per_custodian_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -312,13 +344,15 @@ fn exceeding_max_applications_per_custodian_shouldnt_work() { #[test] fn apply_to_nonexistent_marketplace_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); // No such marletplace exists: @@ -338,13 +372,15 @@ fn apply_to_nonexistent_marketplace_shouldnt_work() { #[test] fn apply_twice_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -369,13 +405,15 @@ fn apply_twice_shouldnt_work() { #[test] fn exceeding_max_applicants_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -412,12 +450,14 @@ fn exceeding_max_applicants_shouldnt_work() { #[test] fn enroll_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -456,12 +496,14 @@ fn enroll_works() { #[test] fn enroll_rejected_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -500,12 +542,14 @@ fn enroll_rejected_works() { #[test] fn enroll_rejected_has_feedback_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -549,12 +593,14 @@ fn enroll_rejected_has_feedback_works() { #[test] fn enroll_approved_has_feedback_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -598,12 +644,14 @@ fn enroll_approved_has_feedback_works() { #[test] fn change_enroll_status_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -636,12 +684,14 @@ fn change_enroll_status_works() { #[test] fn non_authorized_user_enroll_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -669,12 +719,14 @@ fn non_authorized_user_enroll_shouldnt_work() { #[test] fn enroll_nonexistent_application_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -708,12 +760,14 @@ fn enroll_nonexistent_application_shouldnt_work() { #[test] fn add_authority_appraiser_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -733,14 +787,17 @@ fn add_authority_appraiser_works() { #[test] fn add_authority_admin_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(GatedMarketplace::add_authority( RuntimeOrigin::signed(1), @@ -756,12 +813,14 @@ fn add_authority_admin_works() { #[test] fn add_authority_redenmption_specialist_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -780,12 +839,14 @@ fn add_authority_redenmption_specialist_works() { #[test] fn add_authority_owner_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -810,12 +871,14 @@ fn add_authority_owner_shouldnt_work() { #[test] fn add_authority_cant_apply_twice_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -842,12 +905,14 @@ fn add_authority_cant_apply_twice_shouldnt_work() { #[test] fn remove_authority_appraiser_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -871,12 +936,14 @@ fn remove_authority_appraiser_works() { #[test] fn remove_authority_admin_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -900,12 +967,14 @@ fn remove_authority_admin_works() { #[test] fn remove_authority_redemption_specialist_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -929,12 +998,14 @@ fn remove_authority_redemption_specialist_work() { #[test] fn remove_authority_owner_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -953,12 +1024,14 @@ fn remove_authority_owner_shouldnt_work() { #[test] fn remove_authority_admin_by_admin_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -983,12 +1056,14 @@ fn remove_authority_admin_by_admin_shouldnt_work() { #[test] fn remove_authority_user_tries_to_remove_non_existent_role_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1013,12 +1088,14 @@ fn remove_authority_user_tries_to_remove_non_existent_role_shouldnt_work() { #[test] fn remove_authority_user_is_not_admin_or_owner_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1049,12 +1126,14 @@ fn remove_authority_user_is_not_admin_or_owner_shouldnt_work() { #[test] fn remove_authority_only_owner_can_remove_admins_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1078,12 +1157,14 @@ fn remove_authority_only_owner_can_remove_admins_works() { #[test] fn update_marketplace_marketplace_not_found_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1103,13 +1184,15 @@ fn update_marketplace_marketplace_not_found_shouldnt_work() { #[test] fn update_marketplace_user_without_permission_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); //user should be an admin or owner assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1133,12 +1216,15 @@ fn update_marketplace_user_without_permission_shouldnt_work() { #[test] fn update_label_marketplace_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1157,12 +1243,15 @@ fn update_label_marketplace_works() { #[test] fn remove_marketplace_marketplace_not_found_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1178,12 +1267,15 @@ fn remove_marketplace_marketplace_not_found_shouldnt_work() { #[test] fn remove_marketplace_user_without_permission_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1203,12 +1295,15 @@ fn remove_marketplace_user_without_permission_shouldnt_work() { #[test] fn remove_marketplace_deletes_storage_from_marketplaces_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1221,12 +1316,15 @@ fn remove_marketplace_deletes_storage_from_marketplaces_works() { #[test] fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1291,12 +1389,15 @@ fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works() { #[test] fn remove_marketplace_deletes_storage_from_authorities_by_marketplace_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1355,12 +1456,15 @@ fn remove_marketplace_deletes_storage_from_authorities_by_marketplace_works() { #[test] fn remove_marketplace_deletes_storage_from_custodians_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1384,12 +1488,15 @@ fn remove_marketplace_deletes_storage_from_custodians_works() { #[test] fn remove_marketplace_deletes_storage_from_applicants_by_marketplace_status_pending_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1434,12 +1541,14 @@ fn remove_marketplace_deletes_storage_from_applicants_by_marketplace_status_pend #[test] fn remove_marketplace_deletes_storage_from_applicants_by_marketplace_status_approved_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1491,12 +1600,14 @@ fn remove_marketplace_deletes_storage_from_applicants_by_marketplace_status_appr #[test] fn remove_marketplace_deletes_storage_from_applicants_by_marketplace_status_rejected_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1548,12 +1659,14 @@ fn remove_marketplace_deletes_storage_from_applicants_by_marketplace_status_reje #[test] fn remove_marketplace_deletes_storage_from_applicantions_by_account_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1595,12 +1708,14 @@ fn remove_marketplace_deletes_storage_from_applicantions_by_account_works() { #[test] fn remove_marketplace_deletes_storage_from_applications_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1644,12 +1759,14 @@ fn remove_marketplace_deletes_storage_from_applications_works() { #[test] fn reapply_user_has_never_applied_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1669,12 +1786,14 @@ fn reapply_user_has_never_applied_shouldnt_work() { #[test] fn reapply_with_wrong_marketplace_id_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1695,12 +1814,14 @@ fn reapply_with_wrong_marketplace_id_shouldnt_work() { #[test] fn reapply_status_application_is_still_pendding_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1733,12 +1854,14 @@ fn reapply_status_application_is_still_pendding_shouldnt_work() { #[test] fn reapply_status_application_is_already_approved_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1782,12 +1905,14 @@ fn reapply_status_application_is_already_approved_shouldnt_work() { #[test] fn reapply_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1830,13 +1955,15 @@ fn reapply_works() { fn enlist_sell_offer_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1866,13 +1993,15 @@ fn enlist_sell_offer_works() { fn enlist_sell_offer_item_does_not_exist_shouldnt_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1892,13 +2021,15 @@ fn enlist_sell_offer_item_does_not_exist_shouldnt_work() { fn enlist_sell_offer_item_already_enlisted_shouldnt_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -1927,6 +2058,7 @@ fn enlist_sell_offer_item_already_enlisted_shouldnt_work() { #[test] fn enlist_sell_offer_not_owner_tries_to_enlist_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); @@ -1935,9 +2067,12 @@ fn enlist_sell_offer_not_owner_tries_to_enlist_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -1955,15 +2090,17 @@ fn enlist_sell_offer_not_owner_tries_to_enlist_shouldnt_work() { fn enlist_sell_offer_price_must_greater_than_zero_shouldnt_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 - + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -1981,15 +2118,18 @@ fn enlist_sell_offer_price_must_greater_than_zero_shouldnt_work() { fn enlist_sell_offer_price_must_greater_than_minimun_amount_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2014,15 +2154,18 @@ fn enlist_sell_offer_price_must_greater_than_minimun_amount_works() { fn enlist_sell_offer_is_properly_stored_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2051,13 +2194,15 @@ fn enlist_sell_offer_is_properly_stored_works() { fn enlist_sell_offer_two_marketplaces() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2067,12 +2212,13 @@ fn enlist_sell_offer_two_marketplaces() { 2, create_label("my marketplace2"), 500, - 600 - + 600, + 2, )); - let m_id2 = get_marketplace_id("my marketplace2",500,600 + let m_id2 = get_marketplace_id2("my marketplace2",500,600,1,2); -, 1); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 2, 1, 10000); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); assert_ok!(Fruniques::spawn(RuntimeOrigin::signed(1), 0, dummy_description(), None, None)); @@ -2105,6 +2251,7 @@ fn enlist_sell_offer_two_marketplaces() { #[test] fn enlist_buy_offer_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1100); @@ -2113,9 +2260,14 @@ fn enlist_buy_offer_works() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2153,6 +2305,7 @@ fn enlist_buy_offer_works() { #[test] fn enlist_buy_offer_owner_cannnot_create_buy_offers_for_their_own_items_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1100); @@ -2161,7 +2314,8 @@ fn enlist_buy_offer_owner_cannnot_create_buy_offers_for_their_own_items_shouldnt 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2191,6 +2345,7 @@ fn enlist_buy_offer_owner_cannnot_create_buy_offers_for_their_own_items_shouldnt #[test] fn enlist_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); @@ -2199,7 +2354,8 @@ fn enlist_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2229,6 +2385,7 @@ fn enlist_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { #[test] fn enlist_buy_offer_price_must_greater_than_zero_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1100); @@ -2237,7 +2394,8 @@ fn enlist_buy_offer_price_must_greater_than_zero_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2267,6 +2425,7 @@ fn enlist_buy_offer_price_must_greater_than_zero_shouldnt_work() { #[test] fn enlist_buy_offer_an_item_can_receive_multiple_buy_offers() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1101); Balances::make_free_balance_be(&3, 1201); @@ -2276,9 +2435,15 @@ fn enlist_buy_offer_an_item_can_receive_multiple_buy_offers() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 3, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2339,6 +2504,7 @@ fn enlist_buy_offer_an_item_can_receive_multiple_buy_offers() { #[test] fn take_sell_offer_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2347,9 +2513,14 @@ fn take_sell_offer_works() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2376,15 +2547,17 @@ fn take_sell_offer_works() { #[test] fn take_sell_offer_owner_cannnot_be_the_buyer_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2413,6 +2586,7 @@ fn take_sell_offer_owner_cannnot_be_the_buyer_shouldnt_work() { #[test] fn take_sell_offer_id_does_not_exist_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2421,7 +2595,8 @@ fn take_sell_offer_id_does_not_exist_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2451,6 +2626,7 @@ fn take_sell_offer_id_does_not_exist_shouldnt_work() { #[test] fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1100); @@ -2459,7 +2635,8 @@ fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2488,6 +2665,7 @@ fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work() { #[test] fn take_buy_offer_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2496,9 +2674,14 @@ fn take_buy_offer_works() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2539,6 +2722,7 @@ fn take_buy_offer_works() { #[test] fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2547,9 +2731,14 @@ fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2591,6 +2780,7 @@ fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work() { #[test] fn take_buy_offer_id_does_not_exist_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2599,9 +2789,13 @@ fn take_buy_offer_id_does_not_exist_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2644,6 +2838,7 @@ fn take_buy_offer_id_does_not_exist_shouldnt_work() { #[test] fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2652,9 +2847,12 @@ fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 1200); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2686,7 +2884,7 @@ fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { OfferType::BuyOrder ); - Balances::make_free_balance_be(&2, 0); + Assets::transfer(RuntimeOrigin::signed(2), 1, 1, 1000); assert_noop!( GatedMarketplace::take_buy_offer(RuntimeOrigin::signed(1), offer_id2), Error::::NotEnoughBalance @@ -2697,6 +2895,7 @@ fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { #[test] fn remove_sell_offer_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2705,7 +2904,8 @@ fn remove_sell_offer_works() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2734,6 +2934,7 @@ fn remove_sell_offer_works() { #[test] fn remove_buy_offer_works() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2742,9 +2943,14 @@ fn remove_buy_offer_works() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2782,6 +2988,7 @@ fn remove_buy_offer_works() { #[test] fn remove_offer_id_does_not_exist_sholdnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2790,7 +2997,8 @@ fn remove_offer_id_does_not_exist_sholdnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2821,6 +3029,7 @@ fn remove_offer_id_does_not_exist_sholdnt_work() { #[test] fn remove_offer_creator_doesnt_match_sholdnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2829,7 +3038,8 @@ fn remove_offer_creator_doesnt_match_sholdnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2859,6 +3069,7 @@ fn remove_offer_creator_doesnt_match_sholdnt_work() { #[test] fn remove_offer_status_is_closed_shouldnt_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); @@ -2867,9 +3078,14 @@ fn remove_offer_status_is_closed_shouldnt_work() { 2, create_label("my marketplace"), 500, - 600 + 600, + 1, )); + + Assets::mint(RuntimeOrigin::signed(1), 1, 1, 10000); + Assets::mint(RuntimeOrigin::signed(1), 1, 2, 10000); + let m_id = get_marketplace_id("my marketplace",500,600,1); assert_ok!(Fruniques::create_collection(RuntimeOrigin::signed(1), dummy_description())); @@ -2915,13 +3131,15 @@ fn remove_offer_status_is_closed_shouldnt_work() { #[test] fn block_user_application_attempt_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2947,13 +3165,15 @@ fn block_user_application_attempt_should_fail() { #[test] fn block_user_invite_attempt_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -2981,13 +3201,15 @@ fn block_user_invite_attempt_should_fail() { #[test] fn block_user_reapply_attempt_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3021,13 +3243,15 @@ fn block_user_reapply_attempt_should_fail() { #[test] fn block_user_add_authority_attempt_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3043,13 +3267,15 @@ fn block_user_add_authority_attempt_should_fail() { #[test] fn unblock_user_should_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3079,13 +3305,15 @@ fn unblock_user_should_work() { #[test] fn block_user_while_not_admin_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3109,13 +3337,15 @@ fn block_user_while_not_admin_should_fail() { #[test] fn unblock_user_while_not_admin_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3140,6 +3370,7 @@ fn unblock_user_while_not_admin_should_fail() { #[test] fn block_user_while_marketplace_doesnt_exist_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_noop!( @@ -3152,6 +3383,7 @@ fn block_user_while_marketplace_doesnt_exist_should_fail() { #[test] fn unblock_user_while_marketplace_doesnt_exist_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_noop!( @@ -3164,13 +3396,15 @@ fn unblock_user_while_marketplace_doesnt_exist_should_fail() { #[test] fn block_user_while_user_is_a_member_of_marketplace_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3198,13 +3432,15 @@ fn block_user_while_user_is_a_member_of_marketplace_should_fail() { #[test] fn self_enroll_should_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); @@ -3220,6 +3456,7 @@ fn self_enroll_should_work() { #[test] fn self_enroll_while_marketplace_doesnt_exist_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); let m_id = get_marketplace_id("my marketplace",500,600,1); assert_noop!( @@ -3235,13 +3472,15 @@ fn self_enroll_while_marketplace_doesnt_exist_should_fail() { #[test] fn self_enroll_while_already_participant_should_fail() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); assert_ok!(GatedMarketplace::create_marketplace( RuntimeOrigin::signed(1), 1, create_label("my marketplace"), 500, - 600 + 600, + 1, )); let m_id = get_marketplace_id("my marketplace",500,600,1); diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 1b2099ba..a3efd6ca 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -25,6 +25,7 @@ pub struct Marketplace { pub label: BoundedVec, pub buy_fee: Permill, pub sell_fee: Permill, + pub asset_id: T::AssetId, pub creator: T::AccountId, } @@ -265,8 +266,8 @@ pub struct OfferData { pub item_id: T::ItemId, pub percentage: Permill, pub creator: T::AccountId, - pub price: BalanceOf, - pub fee: BalanceOf, + pub price: T::Balance, + pub fee: T::Balance, pub status: OfferStatus, pub creation_date: u64, pub offer_type: OfferType, diff --git a/parachain-runtime/Cargo.toml b/parachain-runtime/Cargo.toml index e27c8f33..49894347 100644 --- a/parachain-runtime/Cargo.toml +++ b/parachain-runtime/Cargo.toml @@ -50,6 +50,7 @@ pallet-fruniques = { path = "../pallets/fruniques", default-features = false } pallet-gated-marketplace = { path = "../pallets/gated-marketplace", default-features = false } pallet-rbac = { path = "../pallets/rbac", default-features = false } pallet-confidential-docs = { path = "../pallets/confidential-docs", default-features = false } +pallet-mapped-assets = { path = "../pallets/mapped-assets",default-features = false } # Prebuilt Pallets pallet-alliance = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } @@ -137,6 +138,7 @@ std = [ "pallet-fruniques/std", "pallet-gated-marketplace/std", "pallet-rbac/std", + "pallet-mapped-assets/std", # Prebuilt Pallets "pallet-alliance/std", "pallet-assets/std", diff --git a/parachain-runtime/src/lib.rs b/parachain-runtime/src/lib.rs index 30b23937..bd734ee6 100644 --- a/parachain-runtime/src/lib.rs +++ b/parachain-runtime/src/lib.rs @@ -60,9 +60,12 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; +use pallet_mapped_assets; + // XCM Imports use xcm::latest::prelude::BodyId; use xcm_executor::XcmExecutor; +use pallet_mapped_assets::DefaultCallback; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; @@ -1029,6 +1032,29 @@ impl pallet_gated_marketplace::Config for Runtime { type Rbac = RBAC; } +impl pallet_mapped_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = u128; + type AssetId = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type AssetAccountDeposit = ConstU128; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = StringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = u32; + type RemoveItemsLimit = RemoveItemsLimit; + type AssetIdParameter = u32; + type CallbackHandle = DefaultCallback; +} + parameter_types! { pub const MaxScopesPerPallet: u32 = 1000; pub const MaxRolesPerPallet: u32 = 20; @@ -1114,6 +1140,7 @@ construct_runtime!( GatedMarketplace: pallet_gated_marketplace::{Pallet, Call, Storage, Event} = 154, RBAC: pallet_rbac::{Pallet, Call, Storage, Event} = 155, ConfidentialDocs: pallet_confidential_docs::{Pallet, Call, Storage, Event} = 156, + MappedAssets: pallet_mapped_assets::{Pallet, Call, Storage, Event} = 157, } ); From a545014fef84b1a78ea78d51a8217da6f205dbbc Mon Sep 17 00:00:00 2001 From: Dreyhh Date: Fri, 28 Apr 2023 16:27:01 -0600 Subject: [PATCH 03/22] Creates get_roles_by_user() on RBAC pallet to improve removal of roles when deleting users on afloat pallet --- pallets/afloat/src/functions.rs | 61 +++++++++++++++++++++++++-------- pallets/afloat/src/lib.rs | 2 ++ pallets/rbac/src/functions.rs | 5 +++ pallets/rbac/src/types.rs | 1 + 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index f30564ea..bad32dd0 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -10,7 +10,11 @@ use frame_support::pallet_prelude::*; // use frame_support::traits::OriginTrait; use pallet_rbac::types::IdOrVec; use pallet_rbac::types::RoleBasedAccessControl; +use pallet_rbac::types::RoleId; use scale_info::prelude::vec; +use frame_support::sp_io::hashing::blake2_256; +use sp_runtime::sp_std::str; +use sp_runtime::sp_std::vec::Vec; impl Pallet { pub fn do_initial_setup(creator: T::AccountId, admin: T::AccountId) -> DispatchResult { @@ -223,13 +227,13 @@ impl Pallet { Self::remove_from_afloat_collection(user_address.clone(), FruniqueRole::Collaborator)?; Self::remove_from_afloat_marketplace(user_address.clone())?; - let role = if Self::is_cpa(user_address.clone()) { - AfloatRole::CPA - } else { - AfloatRole::BuyerOrSeller - }; - - Self::remove_role_from_user(user_address.clone(), role)?; + let user_roles = Self::get_all_roles_for_user(user_address.clone()); + + if !user_roles.is_empty() { + for role in user_roles { + Self::remove_role_from_user(user_address.clone(), role)?; + } + } >::remove(user_address.clone()); Self::deposit_event(Event::UserDeleted(user_address.clone())); Ok(()) @@ -356,23 +360,50 @@ impl Pallet { Ok(()) } + fn role_id_to_afloat_role(role_id: RoleId) -> Option { + AfloatRole::enum_to_vec() + .iter() + .find(|role_bytes| role_bytes.using_encoded(blake2_256) == role_id) + .map(|role_bytes| { + let role_str = str::from_utf8(role_bytes).expect("Role bytes should be valid UTF-8"); + + match role_str { + "Owner" => AfloatRole::Owner, + "Admin" => AfloatRole::Admin, + "BuyerOrSeller" => AfloatRole::BuyerOrSeller, + "CPA" => AfloatRole::CPA, + _ => panic!("Unexpected role string"), + } + }) + } + + fn get_all_roles_for_user(account_id: T::AccountId) -> Vec { + let pallet_id = Self::pallet_id(); + let scope_id = AfloatMarketPlaceId::::get().unwrap(); + + let roles_storage = ::Rbac::get_roles_by_user(account_id.clone(), pallet_id, &scope_id); + + roles_storage.into_iter().filter_map(Self::role_id_to_afloat_role).collect() + } + pub fn do_delete_all_users() -> DispatchResult { UserInfo::::iter_keys().try_for_each(|account_id| { if !Self::is_admin_or_owner(account_id.clone()) { - Self::remove_from_afloat_collection(account_id.clone(), FruniqueRole::Collaborator)?; - Self::remove_from_afloat_marketplace(account_id.clone())?; + let user_roles = Self::get_all_roles_for_user(account_id.clone()); - let role = if Self::is_cpa(account_id.clone()) { - AfloatRole::CPA - } else { - AfloatRole::BuyerOrSeller - }; + if !user_roles.is_empty() { + for role in user_roles { + Self::remove_role_from_user(account_id.clone(), role)?; + } + } - Self::remove_role_from_user(account_id.clone(), role)?; + Self::remove_from_afloat_collection(account_id.clone(), FruniqueRole::Collaborator)?; + Self::remove_from_afloat_marketplace(account_id.clone())?; UserInfo::::remove(account_id); } Ok::<(), DispatchError>(()) })?; Ok(()) } + } diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 1c7ef976..618c584f 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -81,6 +81,8 @@ pub mod pallet { Unauthorized, // Pallet has not ben initialized yet NotInitialized, + // Failed to remove afloat role + FailedToRemoveAfloatRole, } #[pallet::storage] diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 3b17dc04..f520d9b2 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -422,6 +422,10 @@ impl RoleBasedAccessControl for Pallet{ v.using_encoded(blake2_256) } + fn get_roles_by_user(user: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId) -> Vec { + >::get((user, pallet.to_id(), scope_id)).into() + } + type MaxRolesPerPallet = T::MaxRolesPerPallet; type MaxPermissionsPerRole = T::MaxPermissionsPerRole; @@ -443,4 +447,5 @@ impl Pallet{ filtered_vec.dedup(); vec.len() == filtered_vec.len() } + } \ No newline at end of file diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index e1540741..92434d8f 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -67,4 +67,5 @@ pub trait RoleBasedAccessControl{ fn get_role_users_len(pallet: IdOrVec, scope_id:&ScopeId, role_id: &RoleId) -> usize; fn to_id(v: Vec)->[u8;32]; fn does_user_have_any_role_in_scope(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId)-> bool; + fn get_roles_by_user(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId)-> Vec; } \ No newline at end of file From 16b527c19af37f42427c4aecb6a424aaa49fd487 Mon Sep 17 00:00:00 2001 From: Dreyhh Date: Wed, 3 May 2023 13:40:18 -0600 Subject: [PATCH 04/22] Adds functions to buy/sell tax credits, creates unit tests for afloat pallet --- pallets/afloat/src/functions.rs | 135 ++++++++- pallets/afloat/src/lib.rs | 93 +++++- pallets/afloat/src/mock.rs | 97 ++++++- pallets/afloat/src/tests.rs | 482 +++++++++++++++++++++++++++++++- runtime/src/lib.rs | 1 + 5 files changed, 787 insertions(+), 21 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index bad32dd0..becf87a7 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -4,8 +4,7 @@ use crate::types::*; use frame_support::traits::UnixTime; use frame_system::pallet_prelude::*; use pallet_gated_marketplace::types::MarketplaceRole; -use pallet_fruniques::types::CollectionDescription; -use pallet_fruniques::types::FruniqueRole; +use pallet_fruniques::types::{CollectionDescription, FruniqueRole, Attributes, ParentInfo}; use frame_support::pallet_prelude::*; // use frame_support::traits::OriginTrait; use pallet_rbac::types::IdOrVec; @@ -15,6 +14,7 @@ use scale_info::prelude::vec; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::str; use sp_runtime::sp_std::vec::Vec; +use sp_runtime::traits::StaticLookup; impl Pallet { pub fn do_initial_setup(creator: T::AccountId, admin: T::AccountId) -> DispatchResult { @@ -239,6 +239,137 @@ impl Pallet { Ok(()) } + pub fn do_set_afloat_balance( + origin: OriginFor, + user_address: T::AccountId, + amount: T::Balance, + ) -> DispatchResult { + + let authority = ensure_signed(origin.clone())?; + let asset_id = AfloatAssetId::::get().unwrap(); + + pallet_mapped_assets::Pallet::::mint( + origin, + asset_id.into(), + T::Lookup::unlookup(user_address.clone()), + amount, + )?; + + Self::deposit_event(Event::AfloatBalanceSet(authority, user_address, amount)); + Ok(()) + } + + pub fn do_get_afloat_balance(user_address: T::AccountId) -> T::Balance { + let asset_id = AfloatAssetId::::get().expect("AfloatAssetId should be set"); + pallet_mapped_assets::Pallet::::balance(asset_id.into(), user_address) + } + + + pub fn do_create_sell_order( + authority: T::AccountId, + item_id: ::ItemId, + price: T::Balance, + percentage: u32, + ) -> DispatchResult + { + + + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + let collection_id = AfloatCollectionId::::get().unwrap(); + + pallet_gated_marketplace::Pallet::::do_enlist_sell_offer( + authority.clone(), + marketplace_id, + collection_id, + item_id, + price, + percentage, + )?; + + Self::deposit_event(Event::SellOrderCreated(authority)); + Ok(()) + } + + pub fn do_create_buy_order( + authority: T::AccountId, + item_id: ::ItemId, + price: T::Balance, + percentage: u32, + ) -> DispatchResult + { + + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + let collection_id = AfloatCollectionId::::get().unwrap(); + + pallet_gated_marketplace::Pallet::::do_enlist_buy_offer( + authority.clone(), + marketplace_id, + collection_id, + item_id, + price, + percentage, + )?; + + Self::deposit_event(Event::BuyOrderCreated(authority)); + Ok(()) + } + + pub fn do_take_sell_order( + authority: OriginFor, + order_id: [u8; 32], + ) -> DispatchResult + where + ::ItemId: From + { + let who = ensure_signed(authority.clone())?; + + pallet_gated_marketplace::Pallet::::do_take_sell_offer( + authority.clone(), + order_id, + )?; + + Self::deposit_event(Event::SellOrderTaken(who)); + Ok(()) + } + + pub fn do_take_buy_order( + authority: T::AccountId, + order_id: [u8; 32], + ) -> DispatchResult + where + ::ItemId: From + { + + pallet_gated_marketplace::Pallet::::do_take_buy_offer( + authority.clone(), + order_id, + )?; + + Self::deposit_event(Event::BuyOrderTaken(authority)); + Ok(()) + } + + pub fn do_create_tax_credit( + owner: T::AccountId, + metadata: CollectionDescription, + attributes: Option>, + parent_info: Option>, + ) -> DispatchResult + where + ::ItemId: From, + ::CollectionId: From, + { + let collection = AfloatCollectionId::::get().unwrap(); + + pallet_fruniques::Pallet::::do_spawn( + collection, + owner, + metadata, + attributes, + parent_info, + ) + } + pub fn create_afloat_collection(origin: OriginFor, metadata: CollectionDescription, admin: T::AccountId, ) -> DispatchResult diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 618c584f..7875725d 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -21,8 +21,7 @@ pub mod pallet { use frame_system::RawOrigin; use pallet_gated_marketplace::types::*; use sp_runtime::Permill; - use pallet_fruniques::types::CollectionDescription; - use pallet_fruniques::types::FruniqueRole; + use pallet_fruniques::types::{CollectionDescription, FruniqueRole, Attributes, ParentInfo}; const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); use crate::types::*; @@ -40,6 +39,7 @@ pub mod pallet { type Rbac: RoleBasedAccessControl; type RemoveOrigin: EnsureOrigin; type Currency: Currency; + type ItemId: Parameter + Member + Default; } #[pallet::pallet] @@ -56,6 +56,11 @@ pub mod pallet { NewUser(T::AccountId), UserEdited(T::AccountId), UserDeleted(T::AccountId), + SellOrderCreated(T::AccountId), + BuyOrderCreated(T::AccountId), + SellOrderTaken(T::AccountId), + BuyOrderTaken(T::AccountId), + AfloatBalanceSet(T::AccountId, T::AccountId, T::Balance), } // Errors inform users that something went wrong. @@ -120,7 +125,8 @@ pub mod pallet { #[pallet::call] impl Pallet where - T: pallet_uniques::Config + T: pallet_uniques::Config, + ::ItemId: From { #[pallet::call_index(0)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] @@ -204,5 +210,86 @@ pub mod pallet { Ok(()) } + + #[pallet::call_index(4)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] + pub fn create_sell_order( + origin: OriginFor, + item_id: ::ItemId, + price: T::Balance, + percentage: u32, + ) + -> DispatchResult + { + let who = ensure_signed(origin)?; + Self::do_create_sell_order(who , item_id, price, percentage) + } + + #[pallet::call_index(5)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] + pub fn create_buy_order( + origin: OriginFor, + item_id: ::ItemId, + price: T::Balance, + percentage: u32, + ) + -> DispatchResult + { + let who = ensure_signed(origin)?; + Self::do_create_buy_order(who , item_id, price, percentage) + } + + #[pallet::call_index(6)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] + pub fn take_sell_order( + origin: OriginFor, + offer_id: [u8; 32], + ) + -> DispatchResult + { + ensure_signed(origin.clone())?; + Self::do_take_sell_order(origin , offer_id) + } + + #[pallet::call_index(7)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] + pub fn take_buy_order( + origin: OriginFor, + offer_id: [u8; 32], + ) + -> DispatchResult + { + let who = ensure_signed(origin)?; + Self::do_take_buy_order(who , offer_id) + } + + #[pallet::call_index(8)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(2,1))] + pub fn create_tax_credit( + origin: OriginFor, + metadata: CollectionDescription, + attributes: Option>, + parent_info: Option>, + ) + -> DispatchResult + { + let who = ensure_signed(origin)?; + Self::do_create_tax_credit(who , metadata, attributes, parent_info) + } + + #[pallet::call_index(9)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(2,1))] + pub fn set_afloat_balance( + origin: OriginFor, + beneficiary: T::AccountId, + amount: T::Balance, + ) + -> DispatchResult + { + ensure!(Self::is_admin_or_owner(ensure_signed(origin.clone())?), Error::::Unauthorized); + ensure_signed(origin.clone())?; + Self::do_set_afloat_balance(origin, beneficiary, amount) + } + } } diff --git a/pallets/afloat/src/mock.rs b/pallets/afloat/src/mock.rs index ffb891d7..92583526 100644 --- a/pallets/afloat/src/mock.rs +++ b/pallets/afloat/src/mock.rs @@ -1,17 +1,28 @@ use crate as pallet_afloat; -use frame_support::{parameter_types, traits::AsEnsureOriginWithArg}; +use frame_support::{ + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, Currency}, +}; use frame_system as system; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; use frame_system::EnsureRoot; use system::EnsureSigned; +use frame_system::RawOrigin; + +type AssetId = u32; + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub enum Test where @@ -20,21 +31,17 @@ frame_support::construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - AfloatModule: pallet_afloat::{Pallet, Call, Storage, Event}, GatedMarketplace: pallet_gated_marketplace::{Pallet, Call, Storage, Event}, Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, Fruniques: pallet_fruniques::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, RBAC: pallet_rbac::{Pallet, Call, Storage, Event}, + Assets: pallet_mapped_assets::{Pallet, Call, Storage, Event}, + Afloat: pallet_afloat::{Pallet, Call, Storage, Event}, } ); -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; -} - impl system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -53,20 +60,24 @@ impl system::Config for Test { type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); type SS58Prefix = SS58Prefix; type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = pallet_balances::AccountData; + } + impl pallet_afloat::Config for Test { type RuntimeEvent = RuntimeEvent; - type Moment = u64; - type Timestamp = Timestamp; - // type Rbac = RBAC; + type TimeProvider = pallet_timestamp::Pallet; + type RemoveOrigin = frame_system::EnsureSigned; + type Currency = pallet_balances::Pallet; + type Rbac = RBAC; + type ItemId = u32; } parameter_types! { @@ -185,7 +196,65 @@ impl pallet_rbac::Config for Test { type MaxUsersPerRole = MaxUsersPerRole; } +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = (); + type WeightInfo = (); +} + + + +pub trait AssetsCallback { + /// Indicates that asset with `id` was successfully created by the `owner` + fn created(_id: &AssetId, _owner: &AccountId) {} + + /// Indicates that asset with `id` has just been destroyed + fn destroyed(_id: &AssetId) {} +} + +pub struct AssetsCallbackHandle; +impl pallet_mapped_assets::AssetsCallback for AssetsCallbackHandle { + fn created(_id: &AssetId, _owner: &u64) { + } + + fn destroyed(_id: &AssetId) { + } +} + + +impl pallet_mapped_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = u64; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = ConstU64<1>; + type AssetAccountDeposit = ConstU64<10>; + type MetadataDepositBase = ConstU64<1>; + type MetadataDepositPerByte = ConstU64<1>; + type ApprovalDeposit = ConstU64<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type WeightInfo = (); + type CallbackHandle = AssetsCallbackHandle; + type Extra = (); + type RemoveItemsLimit = ConstU32<5>; + type MaxReserves = MaxReserves; + type ReserveIdentifier = u32; +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::default().build_storage::().unwrap().into() -} + // TODO: get initial conf? + let mut t: sp_io::TestExternalities = + frame_system::GenesisConfig::default().build_storage::().unwrap().into(); + t.execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + Afloat::initial_setup(RawOrigin::Signed(1).into(), 1, 2).expect("Error on GatedMarketplace configuring initial setup"); + }); + t +} \ No newline at end of file diff --git a/pallets/afloat/src/tests.rs b/pallets/afloat/src/tests.rs index bc3b5fc2..a3f21677 100644 --- a/pallets/afloat/src/tests.rs +++ b/pallets/afloat/src/tests.rs @@ -1,2 +1,480 @@ -use crate::{mock::*, Error}; -use frame_support::{assert_noop, assert_ok}; +use super::*; +use crate::{mock::*, types::*,Error}; +use frame_system::RawOrigin; +use frame_support::{ + assert_noop, assert_ok, + traits::Currency, + BoundedVec, +}; + +fn new_account(account_id: u64) -> ::AccountId { + account_id +} + +fn dummy_description() -> BoundedVec { + BoundedVec::::try_from(b"dummy description".to_vec()).unwrap() +} + +//owner_id = 1 +//admin_id = 2 +//buy_fee = 2% +//sell_fee = 4% + +#[test] +fn sign_up_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + Balances::make_free_balance_be(&user, 100); + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args)); + + assert!(UserInfo::::contains_key(user)); + }); +} + +#[test] +fn update_user_info_edit_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + Balances::make_free_balance_be(&user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args)); + + let update_args = UpdateUserArgs::Edit { + first_name: Some(ShortString::try_from(b"New".to_vec()).unwrap()), + last_name: Some(ShortString::try_from(b"User".to_vec()).unwrap()), + email: Some(LongString::try_from(b"Info".to_vec()).unwrap()), + lang_key: None, + phone: None, + credits_needed: None, + cpa_id: None, + state: None, + }; + + assert_ok!(Afloat::update_user_info( + RawOrigin::Signed(user.clone()).into(), + user.clone(), + update_args + )); + + let updated_user = UserInfo::::get(user).unwrap(); + assert_eq!(updated_user.first_name, ShortString::try_from(b"New".to_vec()).unwrap()); + assert_eq!(updated_user.last_name, ShortString::try_from(b"User".to_vec()).unwrap()); + assert_eq!(updated_user.email, LongString::try_from(b"Info".to_vec()).unwrap()); + + }); +} + +#[test] +fn update_other_user_info_by_not_admin_fails() { + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args)); + + let update_args = UpdateUserArgs::Edit { + first_name: Some(ShortString::try_from(b"New".to_vec()).unwrap()), + last_name: Some(ShortString::try_from(b"User".to_vec()).unwrap()), + email: Some(LongString::try_from(b"Info".to_vec()).unwrap()), + lang_key: None, + phone: None, + credits_needed: None, + cpa_id: None, + state: None, + }; + + assert_noop!( + Afloat::update_user_info( + RawOrigin::Signed(other_user.clone()).into(), + user.clone(), + update_args + ), + Error::::Unauthorized + ); + }); +} + +#[test] +fn update_other_user_info_by_admin_works() { + new_test_ext().execute_with(|| { + let owner = new_account(1); + let admin = new_account(2); + let user = new_account(3); + let other_user = new_account(4); + + Balances::make_free_balance_be(&owner, 100); + Balances::make_free_balance_be(&admin, 100); + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args)); + + let update_args = UpdateUserArgs::Edit { + first_name: Some(ShortString::try_from(b"New".to_vec()).unwrap()), + last_name: Some(ShortString::try_from(b"User".to_vec()).unwrap()), + email: Some(LongString::try_from(b"Info".to_vec()).unwrap()), + lang_key: None, + phone: None, + credits_needed: None, + cpa_id: None, + state: None, + }; + + assert_ok!(Afloat::update_user_info( + RawOrigin::Signed(admin.clone()).into(), + user.clone(), + update_args + )); + + let updated_user = UserInfo::::get(user).unwrap(); + assert_eq!(updated_user.first_name, ShortString::try_from(b"New".to_vec()).unwrap()); + assert_eq!(updated_user.last_name, ShortString::try_from(b"User".to_vec()).unwrap()); + assert_eq!(updated_user.email, LongString::try_from(b"Info".to_vec()).unwrap()); + }); +} + +#[test] +fn update_user_info_delete_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + Balances::make_free_balance_be(&user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args)); + + assert_ok!(Afloat::update_user_info( + RawOrigin::Signed(user.clone()).into(), + user.clone(), + UpdateUserArgs::Delete + )); + + assert!(!UserInfo::::contains_key(user)); + }); +} + +#[test] +fn kill_storage_works() { + new_test_ext().execute_with(|| { + let owner = new_account(1); + let admin = new_account(2); + + let user1 = new_account(3); + let user2 = new_account(4); + + Balances::make_free_balance_be(&user1, 100); + Balances::make_free_balance_be(&user2, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + + + // Add users + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user1.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user2.clone()).into(), args.clone())); + + // Ensure users exist + assert!(UserInfo::::contains_key(user1)); + assert!(UserInfo::::contains_key(user2)); + + // Kill storage with admin + assert_ok!(Afloat::kill_storage(RawOrigin::Signed(admin.clone()).into())); + + // Ensure users no longer exist + assert!(!UserInfo::::contains_key(user1)); + assert!(!UserInfo::::contains_key(user2)); + + // Ensure admin and owner still exists + assert!(UserInfo::::contains_key(admin)); + assert!(UserInfo::::contains_key(owner)); + + // Add users again + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user1.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user2.clone()).into(), args.clone())); + + }); +} + +#[test] +fn kill_storage_fails_for_non_admin() { + new_test_ext().execute_with(|| { + let user = new_account(3); + + // Attempt to kill storage with non-admin user + assert_noop!( + Afloat::kill_storage(RawOrigin::Signed(user.clone()).into()), + Error::::Unauthorized + ); + }); +} + +#[test] +fn set_afloat_balance_works(){ + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + + assert_ok!(Afloat::set_afloat_balance(RawOrigin::Signed(1).into(), other_user.clone(), 10000)); + assert_eq!(Afloat::do_get_afloat_balance(other_user.clone()), 10000); + }); + +} + + +#[test] +fn create_tax_credit_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + + + assert_ok!(Afloat::create_tax_credit( + RawOrigin::Signed(user.clone()).into(), + dummy_description(), + None, + None, + )); + + + }); +} + +#[test] +fn create_sell_order_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + let item_id = 0; + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 0, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + + assert_ok!(Afloat::create_tax_credit( + RawOrigin::Signed(3).into(), + dummy_description(), + None, + None, + )); + + assert_ok!(Afloat::create_sell_order( + RawOrigin::Signed(user.clone()).into(), + item_id, + 10000, + 10, + )); + + }); + +} + +#[test] +fn take_sell_order_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + let item_id = 0; + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 0, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + + assert_ok!(Afloat::create_tax_credit( + RawOrigin::Signed(user.clone()).into(), + dummy_description(), + None, + None, + )); + + assert_ok!(Afloat::create_sell_order( + RawOrigin::Signed(user.clone()).into(), + item_id, + 10000, + 10, + )); + + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + + assert_ok!(Afloat::take_sell_order( + RawOrigin::Signed(other_user.clone()).into(), + offer_id, + )); + + assert_eq!(Afloat::do_get_afloat_balance(user.clone()), 9600); // 10000 - 400 (sell fee) + assert_eq!(Afloat::do_get_afloat_balance(1), 400); // 400 (sell fee) + }); + +} + +#[test] +fn create_buy_order_works() { + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + let item_id = 0; + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 0, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + + assert_ok!(Afloat::create_tax_credit( + RawOrigin::Signed(user.clone()).into(), + dummy_description(), + None, + None, + )); + + assert_ok!(Afloat::create_buy_order( + RawOrigin::Signed(other_user.clone()).into(), + item_id, + 10000, + 10, + )); + + }); + +} + +#[test] +fn take_buy_order_works(){ + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + let item_id = 0; + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 0, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + + assert_ok!(Afloat::create_tax_credit( + RawOrigin::Signed(user.clone()).into(), + dummy_description(), + None, + None, + )); + + assert_ok!(Afloat::create_buy_order( + RawOrigin::Signed(other_user.clone()).into(), + item_id, + 10000, + 10, + )); + + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + + assert_ok!(Afloat::take_buy_order( + RawOrigin::Signed(user.clone()).into(), + offer_id, + )); + + assert_eq!(Afloat::do_get_afloat_balance(user.clone()), 9800); // 10000 - 200 (buy fee) + assert_eq!(Afloat::do_get_afloat_balance(1), 200); // 200 (buy fee) + + }); + +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 968fca6c..903b9829 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -776,6 +776,7 @@ impl pallet_afloat::Config for Runtime { type TimeProvider = Timestamp; type RemoveOrigin = EnsureRoot; type Rbac = RBAC; + type ItemId = u32; } parameter_types! { From e90c1f765a3e1706c1aa67ef898ca7d2f776634f Mon Sep 17 00:00:00 2001 From: Dreyhh Date: Wed, 3 May 2023 14:49:15 -0600 Subject: [PATCH 05/22] Fixes set_afoat_balance --- pallets/afloat/src/functions.rs | 53 ++++++++++++++++++++++++++------- pallets/afloat/src/lib.rs | 5 +++- pallets/afloat/src/tests.rs | 42 ++++++++++++++++++++++---- 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index becf87a7..3d19df28 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -244,21 +244,36 @@ impl Pallet { user_address: T::AccountId, amount: T::Balance, ) -> DispatchResult { - + let authority = ensure_signed(origin.clone())?; - let asset_id = AfloatAssetId::::get().unwrap(); + let asset_id = AfloatAssetId::::get().expect("AfloatAssetId should be set"); - pallet_mapped_assets::Pallet::::mint( - origin, - asset_id.into(), - T::Lookup::unlookup(user_address.clone()), - amount, - )?; + ensure!(UserInfo::::contains_key(user_address.clone()), Error::::UserNotFound); + let current_balance = Self::do_get_afloat_balance(user_address.clone()); + + if current_balance > amount { + let diff = current_balance - amount; + pallet_mapped_assets::Pallet::::burn( + origin.clone(), + asset_id.into(), + T::Lookup::unlookup(user_address.clone()), + diff, + )?; + } else if current_balance < amount { + let diff = amount - current_balance; + pallet_mapped_assets::Pallet::::mint( + origin.clone(), + asset_id.into(), + T::Lookup::unlookup(user_address.clone()), + diff, + )?; + } + Self::deposit_event(Event::AfloatBalanceSet(authority, user_address, amount)); Ok(()) } - + pub fn do_get_afloat_balance(user_address: T::AccountId) -> T::Balance { let asset_id = AfloatAssetId::::get().expect("AfloatAssetId should be set"); pallet_mapped_assets::Pallet::::balance(asset_id.into(), user_address) @@ -273,7 +288,8 @@ impl Pallet { ) -> DispatchResult { - + ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); let collection_id = AfloatCollectionId::::get().unwrap(); @@ -297,6 +313,7 @@ impl Pallet { percentage: u32, ) -> DispatchResult { + ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); let collection_id = AfloatCollectionId::::get().unwrap(); @@ -322,6 +339,8 @@ impl Pallet { ::ItemId: From { let who = ensure_signed(authority.clone())?; + + ensure!(!Self::get_all_roles_for_user(who.clone()).is_empty(), Error::::Unauthorized); pallet_gated_marketplace::Pallet::::do_take_sell_offer( authority.clone(), @@ -339,6 +358,7 @@ impl Pallet { where ::ItemId: From { + ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); pallet_gated_marketplace::Pallet::::do_take_buy_offer( authority.clone(), @@ -359,6 +379,8 @@ impl Pallet { ::ItemId: From, ::CollectionId: From, { + ensure!(!Self::get_all_roles_for_user(owner.clone()).is_empty(), Error::::Unauthorized); + let collection = AfloatCollectionId::::get().unwrap(); pallet_fruniques::Pallet::::do_spawn( @@ -426,6 +448,17 @@ impl Pallet { .is_ok() } + pub fn is_owner(account: T::AccountId) -> bool { + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + ::Rbac::has_role( + account.clone(), + Self::pallet_id(), + &marketplace_id, + [AfloatRole::Owner.id()].to_vec(), + ) + .is_ok() + } + pub fn is_cpa(account: T::AccountId) -> bool { let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); ::Rbac::has_role( diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 7875725d..52c3ebb2 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -286,8 +286,11 @@ pub mod pallet { ) -> DispatchResult { - ensure!(Self::is_admin_or_owner(ensure_signed(origin.clone())?), Error::::Unauthorized); ensure_signed(origin.clone())?; + + // Only the owner can set afloat balance + ensure!(Self::is_owner(ensure_signed(origin.clone())?), Error::::Unauthorized); + Self::do_set_afloat_balance(origin, beneficiary, amount) } diff --git a/pallets/afloat/src/tests.rs b/pallets/afloat/src/tests.rs index a3f21677..ce40dd7e 100644 --- a/pallets/afloat/src/tests.rs +++ b/pallets/afloat/src/tests.rs @@ -267,12 +267,38 @@ fn set_afloat_balance_works(){ assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); - assert_ok!(Afloat::set_afloat_balance(RawOrigin::Signed(1).into(), other_user.clone(), 10000)); - assert_eq!(Afloat::do_get_afloat_balance(other_user.clone()), 10000); + assert_ok!(Afloat::set_afloat_balance(RawOrigin::Signed(1).into(), user.clone(), 10000)); + assert_eq!(Afloat::do_get_afloat_balance(user.clone()), 10000); + assert_ok!(Afloat::set_afloat_balance(RawOrigin::Signed(1).into(), user.clone(), 1000)); + assert_eq!(Afloat::do_get_afloat_balance(user.clone()), 1000); + }); } +#[test] +fn set_balance_by_other_than_owner_fails(){ + new_test_ext().execute_with(|| { + let user = new_account(3); + let other_user = new_account(4); + + Balances::make_free_balance_be(&user, 100); + Balances::make_free_balance_be(&other_user, 100); + + let args = SignUpArgs::BuyerOrSeller { + first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + last_name: ShortString::try_from(b"User".to_vec()).unwrap(), + email: LongString::try_from(b"Afloatuser@gmail.com".to_vec()).unwrap(), + state: 1, + }; + + assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); + + assert_noop!(Afloat::set_afloat_balance(RawOrigin::Signed(3).into(), other_user.clone(), 10000), Error::::Unauthorized); + assert_noop!(Afloat::set_afloat_balance(RawOrigin::Signed(2).into(), other_user.clone(), 10000), Error::::Unauthorized); + }); + +} #[test] fn create_tax_credit_works() { @@ -352,7 +378,7 @@ fn take_sell_order_works() { Balances::make_free_balance_be(&user, 100); Balances::make_free_balance_be(&other_user, 100); - assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + let args = SignUpArgs::BuyerOrSeller { first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), @@ -364,6 +390,8 @@ fn take_sell_order_works() { assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + assert_ok!(Afloat::create_tax_credit( RawOrigin::Signed(user.clone()).into(), dummy_description(), @@ -400,7 +428,7 @@ fn create_buy_order_works() { Balances::make_free_balance_be(&user, 100); Balances::make_free_balance_be(&other_user, 100); - assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + let args = SignUpArgs::BuyerOrSeller { first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), @@ -412,6 +440,8 @@ fn create_buy_order_works() { assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + assert_ok!(Afloat::create_tax_credit( RawOrigin::Signed(user.clone()).into(), dummy_description(), @@ -439,7 +469,7 @@ fn take_buy_order_works(){ Balances::make_free_balance_be(&user, 100); Balances::make_free_balance_be(&other_user, 100); - assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + let args = SignUpArgs::BuyerOrSeller { first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), @@ -450,6 +480,8 @@ fn take_buy_order_works(){ assert_ok!(Afloat::sign_up(RawOrigin::Signed(user.clone()).into(), args.clone())); assert_ok!(Afloat::sign_up(RawOrigin::Signed(other_user.clone()).into(), args.clone())); + + assert_ok!(Afloat::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); assert_ok!(Afloat::create_tax_credit( RawOrigin::Signed(user.clone()).into(), From 47362916f54bcee44a5c02ec141eb8a7508b4dc4 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Fri, 5 May 2023 08:09:24 -0600 Subject: [PATCH 06/22] =?UTF-8?q?=E2=9C=A8=20feat(types.rs):=20add=20Offer?= =?UTF-8?q?=20and=20Transaction=20structs=20and=20OfferStatus=20enum=20The?= =?UTF-8?q?=20Offer=20and=20Transaction=20structs=20are=20added=20to=20the?= =?UTF-8?q?=20codebase=20to=20represent=20offers=20and=20transactions=20in?= =?UTF-8?q?=20the=20system.=20The=20OfferStatus=20enum=20is=20also=20added?= =?UTF-8?q?=20to=20represent=20the=20status=20of=20an=20offer.=20These=20a?= =?UTF-8?q?dditions=20will=20allow=20for=20better=20representation=20of=20?= =?UTF-8?q?the=20data=20in=20the=20system=20and=20improve=20the=20overall?= =?UTF-8?q?=20functionality=20of=20the=20codebase.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/types.rs | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 2f5e6c76..7f100b8b 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -61,6 +61,83 @@ impl User { } } +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,)] +pub enum OfferStatus { + MATCHED, + TF_FILLED, + TF_PENDING_SIGNATURE, + TF_SIGNED, + TF_AGENCY_SUBMITTED, + TF_AGENCY_APPROVED, + AFLOAT_APPROVED, +} + +impl Default for OfferStatus { + fn default() -> Self { + OfferStatus::TF_PENDING_SIGNATURE + } +} + +#[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound())] +pub struct Offer { + pub tax_credit_amount: u32, + pub tax_credit_amount_remaining: u32, + pub price_per_credit: u32, + pub creation_date: Date, + pub cancellation_date: Option, + pub fee: u32, + pub tax_credit_id: u32, + pub creator_id: T::AccountId, + pub status: OfferStatus, +} + +impl Offer { + pub fn new( + tax_credit_amount: u32, + tax_credit_amount_remaining: u32, + price_per_credit: u32, + creation_date: Date, + cancellation_date: Option, + fee: u32, + tax_credit_id: u32, + creator_id: T::AccountId, + status: OfferStatus, + ) -> Self { + Self { + tax_credit_amount, + tax_credit_amount_remaining, + price_per_credit, + creation_date, + cancellation_date, + fee, + tax_credit_id, + creator_id, + status, + } + } +} + + +#[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound())] +pub struct Transaction { + pub tax_credit_amount: u32, + pub price_per_credit: u32, + pub total_price: u32, + pub fee: u32, + pub creation_date: Date, + pub cancellation_date: Option, + pub tax_credit_id: u32, + pub seller_id: T::AccountId, + pub buyer_id: T::AccountId, + pub offer_id: u32, + pub seller_confirmation_date: Option, + pub buyer_confirmation_date: Option, +} + #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] From f12d4f332b14b25bcdc07287cc3d6b6d6b8176e3 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Fri, 5 May 2023 08:15:56 -0600 Subject: [PATCH 07/22] =?UTF-8?q?=F0=9F=94=A7=20chore(afloat):=20add=20sto?= =?UTF-8?q?rage=20for=20afloat=20offers=20and=20transactions=20?= =?UTF-8?q?=F0=9F=9A=80=20feat(afloat):=20add=20functionality=20to=20store?= =?UTF-8?q?=20offers=20and=20transactions=20This=20commit=20adds=20two=20n?= =?UTF-8?q?ew=20storage=20maps=20to=20the=20Afloat=20pallet.=20The=20`Aflo?= =?UTF-8?q?atOffers`=20map=20stores=20offers=20made=20on=20Afloat's=20frun?= =?UTF-8?q?ique=20collection,=20while=20the=20`AfloatTransactions`=20map?= =?UTF-8?q?=20stores=20transactions=20made=20on=20Afloat.=20The=20`AfloatO?= =?UTF-8?q?ffers`=20map=20is=20indexed=20by=20the=20`ItemId`=20type=20from?= =?UTF-8?q?=20the=20`pallet=5Funiques`=20pallet,=20while=20the=20`AfloatTr?= =?UTF-8?q?ansactions`=20map=20is=20indexed=20by=20a=20new=20type=20`Stora?= =?UTF-8?q?geId`,=20which=20is=20a=2032-byte=20array.=20This=20commit=20al?= =?UTF-8?q?so=20adds=20functionality=20to=20store=20offers=20and=20transac?= =?UTF-8?q?tions=20in=20the=20respective=20storage=20maps.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/lib.rs | 30 +++++++++++++++++++++++++----- pallets/afloat/src/types.rs | 2 ++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 52c3ebb2..18eba41d 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -122,8 +122,28 @@ pub mod pallet { ::AssetId, // Afloat's frunique collection id >; + #[pallet::storage] + #[pallet::getter(fn afloat_offers)] + pub(super) type AfloatOffers = StorageMap< + _, + Blake2_128Concat, + ::ItemId, + Offer, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn afloat_transactions)] + pub(super) type AfloatTransactions = StorageMap< + _, + Blake2_128Concat, + StorageId, + Transaction, + OptionQuery, + >; + #[pallet::call] - impl Pallet + impl Pallet where T: pallet_uniques::Config, ::ItemId: From @@ -144,7 +164,7 @@ pub mod pallet { let metadata: CollectionDescription = BoundedVec::try_from(b"Afloat".to_vec()).expect("Label too long"); pallet_fruniques::Pallet::::do_initial_setup()?; - + Self::create_afloat_collection(RawOrigin::Signed(creator.clone()).into(), metadata, admin.clone())?; pallet_gated_marketplace::Pallet::::do_initial_setup()?; @@ -250,7 +270,7 @@ pub mod pallet { ensure_signed(origin.clone())?; Self::do_take_sell_order(origin , offer_id) } - + #[pallet::call_index(7)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] pub fn take_buy_order( @@ -290,9 +310,9 @@ pub mod pallet { // Only the owner can set afloat balance ensure!(Self::is_owner(ensure_signed(origin.clone())?), Error::::Unauthorized); - + Self::do_set_afloat_balance(origin, beneficiary, amount) } - + } } diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 7f100b8b..c5d9e843 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -7,6 +7,8 @@ pub type ShortString = BoundedVec>; pub type LongString = BoundedVec>; pub type Date = u64; pub type CollectionId = u32; +pub type StorageId = [u8; 32]; + #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] #[scale_info(skip_type_params(T))] From eb435c93ec008b5f49202da8628e0c2063d03955 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Fri, 5 May 2023 09:13:04 -0600 Subject: [PATCH 08/22] =?UTF-8?q?=F0=9F=9A=80=20feat(types.rs):=20add=20Of?= =?UTF-8?q?ferType=20enum=20and=20offer=5Ftype=20field=20to=20Offer=20stru?= =?UTF-8?q?ct=20The=20OfferType=20enum=20is=20added=20to=20the=20types.rs?= =?UTF-8?q?=20file,=20which=20contains=20two=20variants:=20Sell=20and=20Bu?= =?UTF-8?q?y.=20The=20Offer=20struct=20is=20updated=20to=20include=20the?= =?UTF-8?q?=20offer=5Ftype=20field,=20which=20is=20of=20type=20OfferType.?= =?UTF-8?q?=20The=20CreateOfferArgs=20enum=20is=20also=20added=20to=20the?= =?UTF-8?q?=20file,=20which=20contains=20two=20variants:=20Sell=20and=20Bu?= =?UTF-8?q?y.=20These=20variants=20contain=20the=20same=20fields=20as=20th?= =?UTF-8?q?e=20Offer=20struct.=20This=20change=20allows=20for=20more=20fle?= =?UTF-8?q?xibility=20in=20creating=20offers,=20as=20it=20is=20now=20possi?= =?UTF-8?q?ble=20to=20specify=20whether=20an=20offer=20is=20a=20buy=20or?= =?UTF-8?q?=20sell=20offer.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/types.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index c5d9e843..53b03acf 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -80,6 +80,18 @@ impl Default for OfferStatus { } } +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,)] +pub enum OfferType { + Sell, + Buy +} + +impl Default for OfferType { + fn default() -> Self { + OfferType::Sell + } +} + #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] @@ -93,6 +105,7 @@ pub struct Offer { pub tax_credit_id: u32, pub creator_id: T::AccountId, pub status: OfferStatus, + pub offer_type: OfferType, } impl Offer { @@ -106,6 +119,7 @@ impl Offer { tax_credit_id: u32, creator_id: T::AccountId, status: OfferStatus, + offer_type: OfferType, ) -> Self { Self { tax_credit_amount, @@ -117,10 +131,28 @@ impl Offer { tax_credit_id, creator_id, status, + offer_type, } } } +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound())] +pub enum CreateOfferArgs { + Sell { + tax_credit_amount: u32, + price_per_credit: u32, + tax_credit_id: u32, + }, + Buy { + tax_credit_amount: u32, + price_per_credit: u32, + tax_credit_id: u32, + }, +} + + #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] #[scale_info(skip_type_params(T))] From 47ac56b2bbd79a8d9c0869411e09728bd2f71381 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Fri, 5 May 2023 09:20:34 -0600 Subject: [PATCH 09/22] =?UTF-8?q?=F0=9F=94=96=20chore(types.rs):=20change?= =?UTF-8?q?=20offer=5Fid=20type=20from=20u32=20to=20StorageId=20This=20cha?= =?UTF-8?q?nge=20improves=20the=20clarity=20of=20the=20code=20by=20using?= =?UTF-8?q?=20a=20more=20descriptive=20type=20name.=20The=20StorageId=20ty?= =?UTF-8?q?pe=20is=20a=20custom=20type=20alias=20that=20is=20defined=20els?= =?UTF-8?q?ewhere=20in=20the=20codebase.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/types.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 53b03acf..d8df22db 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -152,8 +152,6 @@ pub enum CreateOfferArgs { }, } - - #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] @@ -167,7 +165,7 @@ pub struct Transaction { pub tax_credit_id: u32, pub seller_id: T::AccountId, pub buyer_id: T::AccountId, - pub offer_id: u32, + pub offer_id: StorageId, pub seller_confirmation_date: Option, pub buyer_confirmation_date: Option, } From c61d3360c8d9c6f29fe9119b6b63d838e775571a Mon Sep 17 00:00:00 2001 From: tlacloc Date: Mon, 8 May 2023 09:46:47 -0600 Subject: [PATCH 10/22] =?UTF-8?q?=F0=9F=94=A8=20refactor(afloat):=20rename?= =?UTF-8?q?=20create=5Fsell=5Forder=20to=20create=5Foffer=20and=20create?= =?UTF-8?q?=5Fbuy=5Forder=20to=20accept=5Foffer=20The=20function=20names?= =?UTF-8?q?=20were=20changed=20to=20better=20reflect=20their=20purpose.=20?= =?UTF-8?q?The=20create=5Foffer=20function=20now=20takes=20a=20CreateOffer?= =?UTF-8?q?Args=20struct=20that=20contains=20the=20necessary=20arguments?= =?UTF-8?q?=20for=20creating=20a=20sell=20or=20buy=20order.=20The=20accept?= =?UTF-8?q?=5Foffer=20function=20now=20takes=20an=20offer=5Fid=20instead?= =?UTF-8?q?=20of=20the=20item=5Fid,=20price,=20and=20percentage=20argument?= =?UTF-8?q?s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔨 refactor(types.rs): update CreateOfferArgs to use generic types The CreateOfferArgs enum now uses generic types to allow for more flexibility in the types of the arguments. The tax_credit_id field now uses the ItemId type from the pallet_uniques module. The price_per_credit field now uses the Balance type from the Config trait. --- pallets/afloat/src/lib.rs | 29 ++++++++++++++++++----------- pallets/afloat/src/types.rs | 10 +++++----- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 18eba41d..89d74f55 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -233,32 +233,39 @@ pub mod pallet { #[pallet::call_index(4)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] - pub fn create_sell_order( + pub fn create_offer( origin: OriginFor, - item_id: ::ItemId, - price: T::Balance, - percentage: u32, + args: CreateOfferArgs, ) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_create_sell_order(who , item_id, price, percentage) + match args { + CreateOfferArgs::Sell { tax_credit_amount, tax_credit_id, price_per_credit } => { + Self::do_create_sell_order(who, tax_credit_id, price_per_credit, tax_credit_amount)?; + } + CreateOfferArgs::Buy { tax_credit_amount, tax_credit_id, price_per_credit } => { + Self::do_create_buy_order(who, tax_credit_id, price_per_credit, tax_credit_amount)?; + } + } + Ok(()) } #[pallet::call_index(5)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] - pub fn create_buy_order( + pub fn accept_offer( origin: OriginFor, - item_id: ::ItemId, - price: T::Balance, - percentage: u32, + offer_id: [u8; 32], ) -> DispatchResult { - let who = ensure_signed(origin)?; - Self::do_create_buy_order(who , item_id, price, percentage) + ensure_signed(origin.clone())?; + + // Self::do_accept_offer(origin , offer_id) + Ok(()) } + #[pallet::call_index(6)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] pub fn take_sell_order( diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index d8df22db..37287aaf 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -139,16 +139,16 @@ impl Offer { #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] -pub enum CreateOfferArgs { +pub enum CreateOfferArgs { Sell { tax_credit_amount: u32, - price_per_credit: u32, - tax_credit_id: u32, + price_per_credit: T::Balance, + tax_credit_id: ::ItemId, }, Buy { tax_credit_amount: u32, - price_per_credit: u32, - tax_credit_id: u32, + price_per_credit: T::Balance, + tax_credit_id: ::ItemId, }, } From 5684fe07771f769279002ec1c965bf6693a324eb Mon Sep 17 00:00:00 2001 From: tlacloc Date: Mon, 8 May 2023 09:49:34 -0600 Subject: [PATCH 11/22] =?UTF-8?q?=F0=9F=94=80=20chore(types.rs):=20change?= =?UTF-8?q?=20types=20of=20price=5Fper=5Fcredit,=20fee,=20and=20tax=5Fcred?= =?UTF-8?q?it=5Fid=20The=20types=20of=20price=5Fper=5Fcredit,=20fee,=20and?= =?UTF-8?q?=20tax=5Fcredit=5Fid=20have=20been=20changed=20to=20T::Balance?= =?UTF-8?q?=20and=20::ItemId=20respecti?= =?UTF-8?q?vely.=20This=20change=20was=20made=20to=20improve=20consistency?= =?UTF-8?q?=20with=20the=20types=20used=20in=20the=20rest=20of=20the=20cod?= =?UTF-8?q?ebase=20and=20to=20ensure=20that=20the=20types=20are=20compatib?= =?UTF-8?q?le=20with=20the=20configuration=20of=20the=20pallet=5Funiques?= =?UTF-8?q?=20module.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/types.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 37287aaf..46ebcd4e 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -98,11 +98,11 @@ impl Default for OfferType { pub struct Offer { pub tax_credit_amount: u32, pub tax_credit_amount_remaining: u32, - pub price_per_credit: u32, + pub price_per_credit: T::Balance, pub creation_date: Date, pub cancellation_date: Option, - pub fee: u32, - pub tax_credit_id: u32, + pub fee: T::Balance, + pub tax_credit_id: ::ItemId, pub creator_id: T::AccountId, pub status: OfferStatus, pub offer_type: OfferType, From 8613bbb507dc319d216c5778d4d7985faab83d46 Mon Sep 17 00:00:00 2001 From: Dreyhh Date: Mon, 8 May 2023 10:51:40 -0600 Subject: [PATCH 12/22] Return offer_id when creating buy/sell offers --- pallets/afloat/src/functions.rs | 6 ++++-- pallets/gated-marketplace/src/functions.rs | 9 +++++---- pallets/gated-marketplace/src/lib.rs | 8 ++++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 3d19df28..bc7dfda8 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -293,7 +293,7 @@ impl Pallet { let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); let collection_id = AfloatCollectionId::::get().unwrap(); - pallet_gated_marketplace::Pallet::::do_enlist_sell_offer( + let offer_id = pallet_gated_marketplace::Pallet::::do_enlist_sell_offer( authority.clone(), marketplace_id, collection_id, @@ -303,6 +303,7 @@ impl Pallet { )?; Self::deposit_event(Event::SellOrderCreated(authority)); + Ok(()) } @@ -318,7 +319,7 @@ impl Pallet { let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); let collection_id = AfloatCollectionId::::get().unwrap(); - pallet_gated_marketplace::Pallet::::do_enlist_buy_offer( + let offer_id = pallet_gated_marketplace::Pallet::::do_enlist_buy_offer( authority.clone(), marketplace_id, collection_id, @@ -328,6 +329,7 @@ impl Pallet { )?; Self::deposit_event(Event::BuyOrderCreated(authority)); + Ok(()) } diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 9bd51887..e8eca812 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -342,7 +342,7 @@ impl Pallet { item_id: T::ItemId, price: T::Balance, percentage: u32, - ) -> DispatchResult { + ) -> Result<[u8; 32], DispatchError> { //This function is only called by the owner of the marketplace //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -407,7 +407,7 @@ impl Pallet { pallet_fruniques::Pallet::::do_freeze(&collection_id, item_id)?; Self::deposit_event(Event::OfferStored(collection_id, item_id, offer_id)); - Ok(()) + Ok(offer_id) } pub fn do_enlist_buy_offer( @@ -417,7 +417,7 @@ impl Pallet { item_id: T::ItemId, price: T::Balance, percentage: u32, - ) -> DispatchResult { + ) -> Result<[u8; 32], DispatchError> { //ensure the marketplace exists @@ -497,7 +497,8 @@ impl Pallet { .map_err(|_| Error::::OfferStorageError)?; Self::deposit_event(Event::OfferStored(collection_id, item_id, offer_id)); - Ok(()) + + Ok(offer_id) } pub fn do_take_sell_offer(origin : OriginFor, offer_id: [u8; 32]) -> DispatchResult diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 3839683e..dda36916 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -677,7 +677,9 @@ pub mod pallet { item_id, price, percentage, - ) + )?; + + Ok(()) } /// Accepts a sell order. @@ -760,7 +762,9 @@ pub mod pallet { item_id, price, percentage, - ) + )?; + + Ok(()) } /// Accepts a buy order. From 15f3063ffa838de2089ba793eae0bd437935f4af Mon Sep 17 00:00:00 2001 From: tlacloc Date: Mon, 8 May 2023 10:58:56 -0600 Subject: [PATCH 13/22] =?UTF-8?q?=F0=9F=94=A8=20refactor(functions.rs):=20?= =?UTF-8?q?remove=20unnecessary=20whitespace=20This=20commit=20removes=20u?= =?UTF-8?q?nnecessary=20whitespace=20in=20the=20functions.rs=20file.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/functions.rs | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 3d19df28..46c70f0c 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -10,7 +10,7 @@ use frame_support::pallet_prelude::*; use pallet_rbac::types::IdOrVec; use pallet_rbac::types::RoleBasedAccessControl; use pallet_rbac::types::RoleId; -use scale_info::prelude::vec; +use scale_info::prelude::vec; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::str; use sp_runtime::sp_std::vec::Vec; @@ -58,7 +58,7 @@ impl Pallet { >::insert(admin.clone(), admin_user); Self::give_role_to_user(admin, AfloatRole::Admin)?; } - + Ok(()) } @@ -226,9 +226,9 @@ impl Pallet { Self::remove_from_afloat_collection(user_address.clone(), FruniqueRole::Collaborator)?; Self::remove_from_afloat_marketplace(user_address.clone())?; - + let user_roles = Self::get_all_roles_for_user(user_address.clone()); - + if !user_roles.is_empty() { for role in user_roles { Self::remove_role_from_user(user_address.clone(), role)?; @@ -244,14 +244,14 @@ impl Pallet { user_address: T::AccountId, amount: T::Balance, ) -> DispatchResult { - + let authority = ensure_signed(origin.clone())?; let asset_id = AfloatAssetId::::get().expect("AfloatAssetId should be set"); ensure!(UserInfo::::contains_key(user_address.clone()), Error::::UserNotFound); let current_balance = Self::do_get_afloat_balance(user_address.clone()); - + if current_balance > amount { let diff = current_balance - amount; pallet_mapped_assets::Pallet::::burn( @@ -269,7 +269,7 @@ impl Pallet { diff, )?; } - + Self::deposit_event(Event::AfloatBalanceSet(authority, user_address, amount)); Ok(()) } @@ -278,17 +278,17 @@ impl Pallet { let asset_id = AfloatAssetId::::get().expect("AfloatAssetId should be set"); pallet_mapped_assets::Pallet::::balance(asset_id.into(), user_address) } - + pub fn do_create_sell_order( authority: T::AccountId, item_id: ::ItemId, price: T::Balance, - percentage: u32, - ) -> DispatchResult + tax_credit_amount: u32, + ) -> DispatchResult { - ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); + ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); let collection_id = AfloatCollectionId::::get().unwrap(); @@ -310,8 +310,8 @@ impl Pallet { authority: T::AccountId, item_id: ::ItemId, price: T::Balance, - percentage: u32, - ) -> DispatchResult + tax_credit_amount: u32, + ) -> DispatchResult { ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); @@ -334,12 +334,12 @@ impl Pallet { pub fn do_take_sell_order( authority: OriginFor, order_id: [u8; 32], - ) -> DispatchResult + ) -> DispatchResult where ::ItemId: From { let who = ensure_signed(authority.clone())?; - + ensure!(!Self::get_all_roles_for_user(who.clone()).is_empty(), Error::::Unauthorized); pallet_gated_marketplace::Pallet::::do_take_sell_offer( @@ -354,7 +354,7 @@ impl Pallet { pub fn do_take_buy_order( authority: T::AccountId, order_id: [u8; 32], - ) -> DispatchResult + ) -> DispatchResult where ::ItemId: From { @@ -374,7 +374,7 @@ impl Pallet { metadata: CollectionDescription, attributes: Option>, parent_info: Option>, - ) -> DispatchResult + ) -> DispatchResult where ::ItemId: From, ::CollectionId: From, @@ -394,7 +394,7 @@ impl Pallet { pub fn create_afloat_collection(origin: OriginFor, metadata: CollectionDescription, - admin: T::AccountId, ) -> DispatchResult + admin: T::AccountId, ) -> DispatchResult where ::CollectionId: From, { @@ -445,7 +445,7 @@ impl Pallet { &marketplace_id, [AfloatRole::Admin.id(), AfloatRole::Owner.id()].to_vec(), ) - .is_ok() + .is_ok() } pub fn is_owner(account: T::AccountId) -> bool { @@ -456,7 +456,7 @@ impl Pallet { &marketplace_id, [AfloatRole::Owner.id()].to_vec(), ) - .is_ok() + .is_ok() } pub fn is_cpa(account: T::AccountId) -> bool { @@ -467,7 +467,7 @@ impl Pallet { &marketplace_id, [AfloatRole::CPA.id()].to_vec(), ) - .is_ok() + .is_ok() } pub fn give_role_to_user( @@ -544,9 +544,9 @@ impl Pallet { fn get_all_roles_for_user(account_id: T::AccountId) -> Vec { let pallet_id = Self::pallet_id(); let scope_id = AfloatMarketPlaceId::::get().unwrap(); - + let roles_storage = ::Rbac::get_roles_by_user(account_id.clone(), pallet_id, &scope_id); - + roles_storage.into_iter().filter_map(Self::role_id_to_afloat_role).collect() } @@ -554,13 +554,13 @@ impl Pallet { UserInfo::::iter_keys().try_for_each(|account_id| { if !Self::is_admin_or_owner(account_id.clone()) { let user_roles = Self::get_all_roles_for_user(account_id.clone()); - + if !user_roles.is_empty() { for role in user_roles { Self::remove_role_from_user(account_id.clone(), role)?; } } - + Self::remove_from_afloat_collection(account_id.clone(), FruniqueRole::Collaborator)?; Self::remove_from_afloat_marketplace(account_id.clone())?; UserInfo::::remove(account_id); @@ -569,5 +569,5 @@ impl Pallet { })?; Ok(()) } - + } From b069a3cb41105a7c6f068f26078c39d9b0240144 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Mon, 8 May 2023 11:13:10 -0600 Subject: [PATCH 14/22] =?UTF-8?q?=F0=9F=94=A5=20refactor(afloat):=20remove?= =?UTF-8?q?=20unused=20code=20The=20commented=20out=20code=20in=20the=20`P?= =?UTF-8?q?allet`=20implementation=20and=20the=20`Offer`=20struct=20are=20?= =?UTF-8?q?removed=20as=20they=20are=20not=20being=20used.=20The=20`pallet?= =?UTF-8?q?=5Funiques::Config`=20trait=20is=20used=20in=20the=20`AfloatOff?= =?UTF-8?q?ers`=20storage=20map=20instead=20of=20the=20`ItemId`=20type.=20?= =?UTF-8?q?The=20`price=5Fper=5Fcredit`=20and=20`fee`=20fields=20in=20the?= =?UTF-8?q?=20`Offer`=20struct=20are=20changed=20to=20use=20the=20`T::Bala?= =?UTF-8?q?nce`=20type=20instead=20of=20`u32`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/functions.rs | 17 +++++++++++++++-- pallets/afloat/src/lib.rs | 2 +- pallets/afloat/src/types.rs | 6 +++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index a08db8f8..513398b3 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -299,9 +299,22 @@ impl Pallet { collection_id, item_id, price, - percentage, + tax_credit_amount, )?; + // let offer: Offer = Offer { + // tax_credit_amount, + // tax_credit_amount_remaining: tax_credit_amount, + // price_per_credit: price, + // creation_date: >::block_number(), + // tax_credit_id: item_id, + // creator_id: authority, + + + // }; + // >::insert(user_address.clone(), user); + + Self::deposit_event(Event::SellOrderCreated(authority)); Ok(()) @@ -325,7 +338,7 @@ impl Pallet { collection_id, item_id, price, - percentage, + tax_credit_amount, )?; Self::deposit_event(Event::BuyOrderCreated(authority)); diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 89d74f55..9ee7174d 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -127,7 +127,7 @@ pub mod pallet { pub(super) type AfloatOffers = StorageMap< _, Blake2_128Concat, - ::ItemId, + StorageId, Offer, OptionQuery, >; diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 46ebcd4e..196c48a1 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -112,11 +112,11 @@ impl Offer { pub fn new( tax_credit_amount: u32, tax_credit_amount_remaining: u32, - price_per_credit: u32, + price_per_credit: T::Balance, creation_date: Date, cancellation_date: Option, - fee: u32, - tax_credit_id: u32, + fee: T::Balance, + tax_credit_id: ::ItemId, creator_id: T::AccountId, status: OfferStatus, offer_type: OfferType, From 316850510a4654b1a4759e6d061dc1eb02ad096d Mon Sep 17 00:00:00 2001 From: tlacloc Date: Mon, 8 May 2023 12:25:13 -0600 Subject: [PATCH 15/22] =?UTF-8?q?=F0=9F=94=A8=20refactor(afloat):=20refact?= =?UTF-8?q?or=20Offer=20struct=20and=20User=20struct=20=E2=9C=A8=20feat(af?= =?UTF-8?q?loat):=20add=20cid=20field=20to=20User=20struct=20The=20Offer?= =?UTF-8?q?=20struct=20has=20been=20refactored=20to=20remove=20the=20fee?= =?UTF-8?q?=20field,=20which=20is=20not=20currently=20being=20used.=20The?= =?UTF-8?q?=20User=20struct=20has=20been=20refactored=20to=20remove=20fiel?= =?UTF-8?q?ds=20that=20are=20not=20currently=20being=20used=20and=20to=20a?= =?UTF-8?q?dd=20a=20new=20cid=20field.=20The=20cid=20field=20is=20a=20uniq?= =?UTF-8?q?ue=20identifier=20for=20the=20user.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/functions.rs | 36 ++++++++++++++++++-------- pallets/afloat/src/lib.rs | 1 + pallets/afloat/src/types.rs | 45 +++++---------------------------- 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 513398b3..868ba2bf 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -302,17 +302,19 @@ impl Pallet { tax_credit_amount, )?; - // let offer: Offer = Offer { - // tax_credit_amount, - // tax_credit_amount_remaining: tax_credit_amount, - // price_per_credit: price, - // creation_date: >::block_number(), - // tax_credit_id: item_id, - // creator_id: authority, - + let offer: Offer = Offer { + tax_credit_amount, + tax_credit_amount_remaining: tax_credit_amount, + price_per_credit: price, + creation_date: T::TimeProvider::now().as_secs(), + tax_credit_id: item_id, + creator_id: authority.clone(), + status: OfferStatus::default(), + offer_type: OfferType::Sell, + cancellation_date: None, + }; - // }; - // >::insert(user_address.clone(), user); + >::insert(offer_id, offer); Self::deposit_event(Event::SellOrderCreated(authority)); @@ -341,6 +343,20 @@ impl Pallet { tax_credit_amount, )?; + let offer: Offer = Offer { + tax_credit_amount, + tax_credit_amount_remaining: tax_credit_amount, + price_per_credit: price, + creation_date: T::TimeProvider::now().as_secs(), + tax_credit_id: item_id, + creator_id: authority.clone(), + status: OfferStatus::default(), + offer_type: OfferType::Buy, + cancellation_date: None, + }; + + >::insert(offer_id, offer); + Self::deposit_event(Event::BuyOrderCreated(authority)); Ok(()) diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 9ee7174d..ac9f5171 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -256,6 +256,7 @@ pub mod pallet { pub fn accept_offer( origin: OriginFor, offer_id: [u8; 32], + amount: Option, ) -> DispatchResult { diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 196c48a1..01484b23 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -14,51 +14,27 @@ pub type StorageId = [u8; 32]; #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct User { - pub first_name: ShortString, - pub last_name: ShortString, - pub email: LongString, - pub lang_key: ShortString, + pub cid: ShortString, pub created_by: Option, pub created_date: Option, pub last_modified_by: Option, pub last_modified_date: Option, - pub phone: Option, - pub credits_needed: u32, - pub cpa_id: ShortString, - pub tax_authority_id: u32, - pub lock_expiration_date: Option, } impl User { pub fn new( - first_name: ShortString, - last_name: ShortString, - email: LongString, - lang_key: ShortString, + cid: ShortString, created_by: Option, created_date: Option, last_modified_by: Option, last_modified_date: Option, - phone: Option, - credits_needed: u32, - cpa_id: ShortString, - tax_authority_id: u32, - lock_expiration_date: Option, ) -> Self { Self { - first_name, - last_name, - email, - lang_key, + cid, created_by, created_date, last_modified_by, last_modified_date, - phone, - credits_needed, - cpa_id, - tax_authority_id, - lock_expiration_date, } } } @@ -101,7 +77,7 @@ pub struct Offer { pub price_per_credit: T::Balance, pub creation_date: Date, pub cancellation_date: Option, - pub fee: T::Balance, + // pub fee: T::Balance, pub tax_credit_id: ::ItemId, pub creator_id: T::AccountId, pub status: OfferStatus, @@ -115,7 +91,7 @@ impl Offer { price_per_credit: T::Balance, creation_date: Date, cancellation_date: Option, - fee: T::Balance, + // fee: T::Balance, tax_credit_id: ::ItemId, creator_id: T::AccountId, status: OfferStatus, @@ -127,7 +103,7 @@ impl Offer { price_per_credit, creation_date, cancellation_date, - fee, + // fee, tax_credit_id, creator_id, status, @@ -175,14 +151,7 @@ pub struct Transaction { #[codec(mel_bound())] pub enum UpdateUserArgs { Edit { - first_name: Option, - last_name: Option, - email: Option, - lang_key: Option, - phone: Option>, - credits_needed: Option, - cpa_id: Option, - state: Option, + cid: Option, }, Delete, } From e044b33307a9026ed847c7a1b993af91579fdc32 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 13:13:08 -0600 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=93=9D=20docs(FruniquesImplementati?= =?UTF-8?q?on):=20add=20FruniquesImplementation=20diagram=20This=20commit?= =?UTF-8?q?=20adds=20a=20new=20file,=20FruniquesImplementation,=20which=20?= =?UTF-8?q?contains=20an=20XML=20diagram=20of=20the=20FruniquesImplementat?= =?UTF-8?q?ion.=20The=20diagram=20shows=20the=20flow=20of=20events=20that?= =?UTF-8?q?=20occur=20when=20a=20user=20creates=20a=20tax=20credit=20with?= =?UTF-8?q?=20n=20editions=20and=20places=20a=20sell=20order=20for=20this?= =?UTF-8?q?=20tax=20credit.=20It=20also=20shows=20the=20flow=20of=20events?= =?UTF-8?q?=20that=20occur=20when=20a=20user=20places=20a=20buy=20offer=20?= =?UTF-8?q?for=20a=20m=20amount=20of=20tax=20credit.=20The=20diagram=20is?= =?UTF-8?q?=20intended=20to=20help=20developers=20understand=20the=20imple?= =?UTF-8?q?mentation=20of=20Fruniques.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔨 refactor(afloat): modify SignUpArgs and UpdateUserArgs structs The SignUpArgs and UpdateUserArgs structs have been modified to include only the necessary fields for the user registration and editing. The cid and group fields have been added to the User struct to improve the user's identification. --- pallets/afloat/src/FruniquesImplementation | 105 +++++++++++++++++++++ pallets/afloat/src/functions.rs | 40 +------- pallets/afloat/src/lib.rs | 8 +- pallets/afloat/src/types.rs | 68 +++++++------ 4 files changed, 150 insertions(+), 71 deletions(-) create mode 100644 pallets/afloat/src/FruniquesImplementation diff --git a/pallets/afloat/src/FruniquesImplementation b/pallets/afloat/src/FruniquesImplementation new file mode 100644 index 00000000..e7c7bce4 --- /dev/null +++ b/pallets/afloat/src/FruniquesImplementation @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 868ba2bf..f44cb876 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -90,7 +90,7 @@ impl Pallet { ) -> DispatchResult { ensure!(!>::contains_key(user_address.clone()), Error::::UserAlreadyExists); match args { - SignUpArgs::BuyerOrSeller { first_name, last_name, email, state } => { + SignUpArgs::BuyerOrSeller { cid, group } => { let user: User = User { first_name, last_name, @@ -110,7 +110,7 @@ impl Pallet { Self::give_role_to_user(user_address.clone(), AfloatRole::BuyerOrSeller)?; Self::deposit_event(Event::NewUser(user_address.clone())); }, - SignUpArgs::CPA { first_name, last_name, email, license_number, state } => { + SignUpArgs::CPA { cid, group } => { let user: User = User { first_name, last_name, @@ -124,7 +124,6 @@ impl Pallet { credits_needed: 0, cpa_id: license_number, tax_authority_id: state, - lock_expiration_date: None, }; >::insert(user_address.clone(), user); Self::give_role_to_user(user_address.clone(), AfloatRole::CPA)?; @@ -163,14 +162,7 @@ impl Pallet { pub fn do_edit_user( actor: T::AccountId, user_address: T::AccountId, - first_name: Option, - last_name: Option, - email: Option, - lang_key: Option, - phone: Option>, - credits_needed: Option, - cpa_id: Option, - state: Option, + cid: ShortString, ) -> DispatchResult { >::try_mutate::<_, _, DispatchError, _>(user_address.clone(), |user| { @@ -178,31 +170,7 @@ impl Pallet { user.last_modified_date = Some(T::TimeProvider::now().as_secs()); user.last_modified_by = Some(actor.clone()); - - if let Some(first_name) = first_name { - user.first_name = first_name; - } - if let Some(last_name) = last_name { - user.last_name = last_name; - } - if let Some(email) = email { - user.email = email; - } - if let Some(lang_key) = lang_key { - user.lang_key = lang_key; - } - if let Some(phone) = phone { - user.phone = phone; - } - if let Some(credits_needed) = credits_needed { - user.credits_needed = credits_needed; - } - if let Some(cpa_id) = cpa_id { - user.cpa_id = cpa_id; - } - if let Some(state) = state { - user.tax_authority_id = state; - } + user.cid = cid; Ok(()) })?; diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index ac9f5171..88ab11be 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -220,8 +220,8 @@ pub mod pallet { ensure!(who.clone() == address || Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); match args { - UpdateUserArgs::Edit { first_name, last_name, email, lang_key, phone, credits_needed, cpa_id, state } => { - Self::do_edit_user(who, address, first_name, last_name, email, lang_key, phone, credits_needed, cpa_id, state)?; + UpdateUserArgs::Edit { cid } => { + Self::do_edit_user(who, address, cid)?; } UpdateUserArgs::Delete => { Self::do_delete_user(who, address)?; @@ -261,12 +261,12 @@ pub mod pallet { -> DispatchResult { ensure_signed(origin.clone())?; - - // Self::do_accept_offer(origin , offer_id) Ok(()) } + + #[pallet::call_index(6)] #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] pub fn take_sell_order( diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 01484b23..6e3ae9b2 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -9,12 +9,14 @@ pub type Date = u64; pub type CollectionId = u32; pub type StorageId = [u8; 32]; +// ! User structures #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct User { pub cid: ShortString, + pub group: ShortString, // only can be modified when the user is registered (can not be modified) pub created_by: Option, pub created_date: Option, pub last_modified_by: Option, @@ -24,6 +26,7 @@ pub struct User { impl User { pub fn new( cid: ShortString, + group: ShortString, created_by: Option, created_date: Option, last_modified_by: Option, @@ -31,6 +34,7 @@ impl User { ) -> Self { Self { cid, + group, created_by, created_date, last_modified_by, @@ -39,6 +43,32 @@ impl User { } } +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound())] +pub enum UpdateUserArgs { + Edit { + cid: ShortString, + }, + Delete, +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound())] +pub enum SignUpArgs { + BuyerOrSeller { + cid: ShortString, + group: ShortString, + }, + CPA { + cid: ShortString, + group: ShortString, + }, +} + +//! Offer structures + #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,)] pub enum OfferStatus { MATCHED, @@ -72,9 +102,10 @@ impl Default for OfferType { #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct Offer { - pub tax_credit_amount: u32, - pub tax_credit_amount_remaining: u32, - pub price_per_credit: T::Balance, + pub tax_credit_amount: u32, //! + pub tax_credit_amount_remaining: u32, // != percentage, it is the amount of tax credits, + pub price_per_credit: T::Balance, // 1_000_000_000_000 + pub expiration_date: Date, pub creation_date: Date, pub cancellation_date: Option, // pub fee: T::Balance, @@ -128,6 +159,8 @@ pub enum CreateOfferArgs { }, } +// ! Transaction structures + #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEq)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] @@ -146,34 +179,7 @@ pub struct Transaction { pub buyer_confirmation_date: Option, } -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound())] -pub enum UpdateUserArgs { - Edit { - cid: Option, - }, - Delete, -} - -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound())] -pub enum SignUpArgs { - BuyerOrSeller { - first_name: ShortString, - last_name: ShortString, - email: LongString, - state: u32, - }, - CPA { - first_name: ShortString, - last_name: ShortString, - email: LongString, - license_number: ShortString, - state: u32, - }, -} +// ! Roles structures #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,)] pub enum AfloatRole { From 6eff77ea09ec388ed6ce2b947e02b84cbdbf7c3c Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 19:26:18 -0600 Subject: [PATCH 17/22] =?UTF-8?q?=F0=9F=8E=A8=20style(afloat):=20add=20fru?= =?UTF-8?q?niquesImplementation.drawio=20diagram=20This=20commit=20adds=20?= =?UTF-8?q?a=20new=20file,=20fruniquesImplementation.drawio,=20which=20con?= =?UTF-8?q?tains=20a=20diagram=20of=20the=20implementation=20of=20the=20fr?= =?UTF-8?q?uniques=20feature=20in=20the=20afloat=20pallet.=20This=20diagra?= =?UTF-8?q?m=20will=20be=20used=20to=20help=20understand=20the=20implement?= =?UTF-8?q?ation=20of=20the=20feature=20and=20to=20aid=20in=20future=20dev?= =?UTF-8?q?elopment.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔥 chore(FruniquesImplementation): remove FruniquesImplementation file The file FruniquesImplementation was removed from the repository. It contained an XML diagram that was no longer relevant to the project. 🔨 refactor(afloat): rename first_name, last_name, email, cpa_id, and tax_authority_id fields to cid, group, and license_number ✨ feat(afloat): add expiration_date field to Offer struct and CreateOfferArgs enum This commit renames several fields in the User struct to improve consistency with the naming conventions. The fields first_name, last_name, email, cpa_id, and tax_authority_id are now named cid, group, and license_number, respectively. Additionally, the Offer struct now has an expiration_date field, and the CreateOfferArgs enum has been updated to include an expiration_date field for both the Buy and Sell variants. --- pallets/afloat/fruniquesImplementation.drawio | 1 + pallets/afloat/src/FruniquesImplementation | 105 ------------------ pallets/afloat/src/functions.rs | 55 +++------ pallets/afloat/src/lib.rs | 8 +- pallets/afloat/src/types.rs | 12 +- 5 files changed, 29 insertions(+), 152 deletions(-) create mode 100644 pallets/afloat/fruniquesImplementation.drawio delete mode 100644 pallets/afloat/src/FruniquesImplementation diff --git a/pallets/afloat/fruniquesImplementation.drawio b/pallets/afloat/fruniquesImplementation.drawio new file mode 100644 index 00000000..56263978 --- /dev/null +++ b/pallets/afloat/fruniquesImplementation.drawio @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pallets/afloat/src/FruniquesImplementation b/pallets/afloat/src/FruniquesImplementation deleted file mode 100644 index e7c7bce4..00000000 --- a/pallets/afloat/src/FruniquesImplementation +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index f44cb876..7fce03c5 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -22,38 +22,24 @@ impl Pallet { Self::initialize_rbac()?; let creator_user: User = User { - first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), - last_name: ShortString::try_from(b"Creator".to_vec()).unwrap(), - email: LongString::try_from(b"".to_vec()).unwrap(), - lang_key: ShortString::try_from(b"en".to_vec()).unwrap(), - created_by: Some(creator.clone()), + cid: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + group: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + created_by: Some(admin.clone()), created_date: Some(T::TimeProvider::now().as_secs()), - last_modified_by: Some(creator.clone()), + last_modified_by: Some(admin.clone()), last_modified_date: Some(T::TimeProvider::now().as_secs()), - phone: None, - credits_needed: 0, - cpa_id: ShortString::try_from(b"0".to_vec()).unwrap(), - tax_authority_id: 1, - lock_expiration_date: None, }; >::insert(creator.clone(), creator_user); Self::give_role_to_user(creator.clone(), AfloatRole::Owner)?; if admin != creator { let admin_user: User = User { - first_name: ShortString::try_from(b"Afloat".to_vec()).unwrap(), - last_name: ShortString::try_from(b"Admin".to_vec()).unwrap(), - email: LongString::try_from(b"".to_vec()).unwrap(), - lang_key: ShortString::try_from(b"en".to_vec()).unwrap(), + cid: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + group: ShortString::try_from(b"Afloat".to_vec()).unwrap(), created_by: Some(admin.clone()), created_date: Some(T::TimeProvider::now().as_secs()), last_modified_by: Some(admin.clone()), last_modified_date: Some(T::TimeProvider::now().as_secs()), - phone: None, - credits_needed: 0, - cpa_id: ShortString::try_from(b"0".to_vec()).unwrap(), - tax_authority_id: 1, - lock_expiration_date: None, }; >::insert(admin.clone(), admin_user); Self::give_role_to_user(admin, AfloatRole::Admin)?; @@ -92,19 +78,12 @@ impl Pallet { match args { SignUpArgs::BuyerOrSeller { cid, group } => { let user: User = User { - first_name, - last_name, - email, - lang_key: ShortString::try_from(b"en".to_vec()).unwrap(), + cid: cid, + group: group, created_by: Some(actor.clone()), created_date: Some(T::TimeProvider::now().as_secs()), last_modified_by: Some(actor.clone()), last_modified_date: Some(T::TimeProvider::now().as_secs()), - phone: None, - credits_needed: 0, - cpa_id: ShortString::try_from(b"0".to_vec()).unwrap(), - tax_authority_id: state, - lock_expiration_date: None, }; >::insert(user_address.clone(), user); Self::give_role_to_user(user_address.clone(), AfloatRole::BuyerOrSeller)?; @@ -112,18 +91,12 @@ impl Pallet { }, SignUpArgs::CPA { cid, group } => { let user: User = User { - first_name, - last_name, - email, - lang_key: ShortString::try_from(b"en".to_vec()).unwrap(), - created_by: Some(user_address.clone()), + cid: cid, + group: group, + created_by: Some(actor.clone()), created_date: Some(T::TimeProvider::now().as_secs()), - last_modified_by: Some(user_address.clone()), + last_modified_by: Some(actor.clone()), last_modified_date: Some(T::TimeProvider::now().as_secs()), - phone: None, - credits_needed: 0, - cpa_id: license_number, - tax_authority_id: state, }; >::insert(user_address.clone(), user); Self::give_role_to_user(user_address.clone(), AfloatRole::CPA)?; @@ -253,6 +226,7 @@ impl Pallet { item_id: ::ItemId, price: T::Balance, tax_credit_amount: u32, + expiration_date: Date, ) -> DispatchResult { @@ -275,6 +249,7 @@ impl Pallet { tax_credit_amount_remaining: tax_credit_amount, price_per_credit: price, creation_date: T::TimeProvider::now().as_secs(), + expiration_date: expiration_date, tax_credit_id: item_id, creator_id: authority.clone(), status: OfferStatus::default(), @@ -295,6 +270,7 @@ impl Pallet { item_id: ::ItemId, price: T::Balance, tax_credit_amount: u32, + expiration_date: Date, ) -> DispatchResult { ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); @@ -316,6 +292,7 @@ impl Pallet { tax_credit_amount_remaining: tax_credit_amount, price_per_credit: price, creation_date: T::TimeProvider::now().as_secs(), + expiration_date: expiration_date, tax_credit_id: item_id, creator_id: authority.clone(), status: OfferStatus::default(), diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 88ab11be..ba887aab 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -241,11 +241,11 @@ pub mod pallet { { let who = ensure_signed(origin)?; match args { - CreateOfferArgs::Sell { tax_credit_amount, tax_credit_id, price_per_credit } => { - Self::do_create_sell_order(who, tax_credit_id, price_per_credit, tax_credit_amount)?; + CreateOfferArgs::Sell { tax_credit_amount, tax_credit_id, price_per_credit, expiration_date } => { + Self::do_create_sell_order(who, tax_credit_id, price_per_credit, tax_credit_amount, expiration_date)?; } - CreateOfferArgs::Buy { tax_credit_amount, tax_credit_id, price_per_credit } => { - Self::do_create_buy_order(who, tax_credit_id, price_per_credit, tax_credit_amount)?; + CreateOfferArgs::Buy { tax_credit_amount, tax_credit_id, price_per_credit, expiration_date } => { + Self::do_create_buy_order(who, tax_credit_id, price_per_credit, tax_credit_amount, expiration_date)?; } } Ok(()) diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 6e3ae9b2..1dc76715 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -67,7 +67,7 @@ pub enum SignUpArgs { }, } -//! Offer structures +// ! Offer structures #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,)] pub enum OfferStatus { @@ -102,9 +102,9 @@ impl Default for OfferType { #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct Offer { - pub tax_credit_amount: u32, //! - pub tax_credit_amount_remaining: u32, // != percentage, it is the amount of tax credits, - pub price_per_credit: T::Balance, // 1_000_000_000_000 + pub tax_credit_amount: u32, + pub tax_credit_amount_remaining: u32, + pub price_per_credit: T::Balance, pub expiration_date: Date, pub creation_date: Date, pub cancellation_date: Option, @@ -125,6 +125,7 @@ impl Offer { // fee: T::Balance, tax_credit_id: ::ItemId, creator_id: T::AccountId, + expiration_date: Date, status: OfferStatus, offer_type: OfferType, ) -> Self { @@ -133,6 +134,7 @@ impl Offer { tax_credit_amount_remaining, price_per_credit, creation_date, + expiration_date: expiration_date, cancellation_date, // fee, tax_credit_id, @@ -151,11 +153,13 @@ pub enum CreateOfferArgs { tax_credit_amount: u32, price_per_credit: T::Balance, tax_credit_id: ::ItemId, + expiration_date: Date, }, Buy { tax_credit_amount: u32, price_per_credit: T::Balance, tax_credit_id: ::ItemId, + expiration_date: Date, }, } From 7e10ff8843741fee7808144819cba5e0798a74eb Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 22:41:03 -0600 Subject: [PATCH 18/22] =?UTF-8?q?=F0=9F=90=9B=20fix(afloat):=20change=20ad?= =?UTF-8?q?min=20to=20creator=20in=20UserInfo=20insert=20=F0=9F=94=A7=20ch?= =?UTF-8?q?ore(afloat):=20increase=20ShortString=20size=20limit=20to=2055?= =?UTF-8?q?=20The=20UserInfo=20insert=20was=20using=20the=20wrong=20variab?= =?UTF-8?q?le,=20admin,=20instead=20of=20creator.=20This=20has=20been=20fi?= =?UTF-8?q?xed=20to=20use=20creator.=20The=20ShortString=20type=20size=20l?= =?UTF-8?q?imit=20has=20been=20increased=20to=2055=20to=20allow=20for=20lo?= =?UTF-8?q?nger=20strings=20to=20be=20stored.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/functions.rs | 4 ++-- pallets/afloat/src/types.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 7fce03c5..611ea882 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -24,9 +24,9 @@ impl Pallet { let creator_user: User = User { cid: ShortString::try_from(b"Afloat".to_vec()).unwrap(), group: ShortString::try_from(b"Afloat".to_vec()).unwrap(), - created_by: Some(admin.clone()), + created_by: Some(creator.clone()), created_date: Some(T::TimeProvider::now().as_secs()), - last_modified_by: Some(admin.clone()), + last_modified_by: Some(creator.clone()), last_modified_date: Some(T::TimeProvider::now().as_secs()), }; >::insert(creator.clone(), creator_user); diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 1dc76715..23dac506 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -3,7 +3,7 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; -pub type ShortString = BoundedVec>; +pub type ShortString = BoundedVec>; pub type LongString = BoundedVec>; pub type Date = u64; pub type CollectionId = u32; From e82e34e0641b2ea2530b77f8b3d347bbdaa5680d Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 22:42:40 -0600 Subject: [PATCH 19/22] =?UTF-8?q?=F0=9F=94=92=20chore(functions.rs):=20upd?= =?UTF-8?q?ate=20creator=20and=20admin=20user=20cids=20and=20groups=20The?= =?UTF-8?q?=20creator=20and=20admin=20user=20cids=20and=20groups=20have=20?= =?UTF-8?q?been=20updated=20to=20match=20the=20new=20values=20in=20the=20H?= =?UTF-8?q?CD=20network.=20This=20is=20a=20security=20measure=20to=20ensur?= =?UTF-8?q?e=20that=20the=20correct=20users=20are=20being=20used=20in=20th?= =?UTF-8?q?e=20pallet.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/functions.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 611ea882..9c0025b4 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -22,8 +22,8 @@ impl Pallet { Self::initialize_rbac()?; let creator_user: User = User { - cid: ShortString::try_from(b"Afloat".to_vec()).unwrap(), - group: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + cid: ShortString::try_from(b"5HeWymtD558YYKHaZvipqysBHR6PCWgvy96Hg2oah2x7CEH5".to_vec()).unwrap(), + group: ShortString::try_from(b"HCD:QmZcSrTcqBdHck73xYw2WHgEQ9tchPrwNq6hM3a3rvXAAV".to_vec()).unwrap(), created_by: Some(creator.clone()), created_date: Some(T::TimeProvider::now().as_secs()), last_modified_by: Some(creator.clone()), @@ -34,8 +34,8 @@ impl Pallet { if admin != creator { let admin_user: User = User { - cid: ShortString::try_from(b"Afloat".to_vec()).unwrap(), - group: ShortString::try_from(b"Afloat".to_vec()).unwrap(), + cid: ShortString::try_from(b"5E7RDXG1e98KFsY6qtjRsdnArSMPyRe8fYH9BWPLeLtFMA2w".to_vec()).unwrap(), + group: ShortString::try_from(b"HCD:QmbhAm22mGMVrTmAfkjUzbtZrSXVSLV28Xah5ca5NtwQ3U".to_vec()).unwrap(), created_by: Some(admin.clone()), created_date: Some(T::TimeProvider::now().as_secs()), last_modified_by: Some(admin.clone()), From bf7eae894e548992f5c0db4b3799a82c7727ad09 Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 22:48:01 -0600 Subject: [PATCH 20/22] =?UTF-8?q?=F0=9F=9A=80=20feat(functions.rs,=20lib.r?= =?UTF-8?q?s,=20types.rs):=20add=20admin=20edit=20user=20functionality=20T?= =?UTF-8?q?his=20commit=20adds=20the=20`do=5Fadmin=5Fedit=5Fuser`=20functi?= =?UTF-8?q?on=20to=20the=20`functions.rs`=20file,=20which=20allows=20an=20?= =?UTF-8?q?admin=20to=20edit=20a=20user's=20`cid`=20and=20`group`=20fields?= =?UTF-8?q?.=20The=20`AdminEdit`=20variant=20is=20added=20to=20the=20`Upda?= =?UTF-8?q?teUserArgs`=20enum=20in=20the=20`types.rs`=20file=20to=20suppor?= =?UTF-8?q?t=20this=20functionality.=20The=20`do=5Fadmin=5Fedit=5Fuser`=20?= =?UTF-8?q?function=20is=20then=20called=20in=20the=20`pallet`=20module's?= =?UTF-8?q?=20`do=5Fupdate=5Fuser`=20function=20in=20the=20`lib.rs`=20file?= =?UTF-8?q?=20when=20the=20`AdminEdit`=20variant=20is=20used.=20This=20fea?= =?UTF-8?q?ture=20allows=20admins=20to=20update=20a=20user's=20`cid`=20and?= =?UTF-8?q?=20`group`=20fields,=20which=20can=20be=20useful=20for=20managi?= =?UTF-8?q?ng=20user=20accounts.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/functions.rs | 22 ++++++++++++++++++++++ pallets/afloat/src/lib.rs | 3 +++ pallets/afloat/src/types.rs | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/pallets/afloat/src/functions.rs b/pallets/afloat/src/functions.rs index 9c0025b4..3574f740 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -150,6 +150,28 @@ impl Pallet { Ok(()) } + + pub fn do_admin_edit_user( + actor: T::AccountId, + user_address: T::AccountId, + cid: ShortString, + group: ShortString + ) -> DispatchResult { + + >::try_mutate::<_, _, DispatchError, _>(user_address.clone(), |user| { + let user = user.as_mut().ok_or(Error::::FailedToEditUserAccount)?; + + user.last_modified_date = Some(T::TimeProvider::now().as_secs()); + user.last_modified_by = Some(actor.clone()); + user.cid = cid; + user.group = group; + + Ok(()) + })?; + + Ok(()) + + } /// Function for deleting a user account. /// /// - _actor: The AccountId of the actor performing the deletion. This parameter is currently unused. diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index ba887aab..36b1f70d 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -223,6 +223,9 @@ pub mod pallet { UpdateUserArgs::Edit { cid } => { Self::do_edit_user(who, address, cid)?; } + UpdateUserArgs::AdminEdit { cid, group } => { + Self::do_admin_edit_user(who, address, cid, group)?; + } UpdateUserArgs::Delete => { Self::do_delete_user(who, address)?; } diff --git a/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index 23dac506..a1a0db95 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -50,6 +50,10 @@ pub enum UpdateUserArgs { Edit { cid: ShortString, }, + AdminEdit { + cid: ShortString, + group: ShortString, + }, Delete, } From e4e8ec015e3386806c262b24b669481dc161589b Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 22:49:23 -0600 Subject: [PATCH 21/22] =?UTF-8?q?=F0=9F=90=9B=20fix(lib.rs):=20remove=20re?= =?UTF-8?q?dundant=20authorization=20check=20The=20authorization=20check?= =?UTF-8?q?=20for=20`is=5Fadmin=5For=5Fowner`=20was=20redundant=20in=20the?= =?UTF-8?q?=20`do=5Fadmin=5Fedit=5Fuser`=20and=20`do=5Fdelete=5Fuser`=20fu?= =?UTF-8?q?nctions=20as=20it=20was=20already=20checked=20in=20the=20`ensur?= =?UTF-8?q?e!(who.clone()=20=3D=3D=20address=20||=20Self::is=5Fadmin=5For?= =?UTF-8?q?=5Fowner(who.clone()),=20Error::::Unauthorized);`=20statemen?= =?UTF-8?q?t.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index 36b1f70d..ccec4e04 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -216,7 +216,6 @@ pub mod pallet { let who = ensure_signed(origin)?; ensure!(>::contains_key(address.clone()), Error::::UserNotFound); - ensure!(!Self::is_admin_or_owner(address.clone()), Error::::Unauthorized); ensure!(who.clone() == address || Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); match args { @@ -224,9 +223,11 @@ pub mod pallet { Self::do_edit_user(who, address, cid)?; } UpdateUserArgs::AdminEdit { cid, group } => { + ensure!(!Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); Self::do_admin_edit_user(who, address, cid, group)?; } UpdateUserArgs::Delete => { + ensure!(!Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); Self::do_delete_user(who, address)?; } } From c5af71df625901fb70d4dcf54e45b3b070ed2a9c Mon Sep 17 00:00:00 2001 From: tlacloc Date: Tue, 9 May 2023 22:50:26 -0600 Subject: [PATCH 22/22] =?UTF-8?q?=F0=9F=90=9B=20fix(lib.rs):=20fix=20user?= =?UTF-8?q?=20authorization=20logic=20in=20UpdateUserArgs=20The=20user=20a?= =?UTF-8?q?uthorization=20logic=20in=20UpdateUserArgs=20was=20incorrect.?= =?UTF-8?q?=20The=20`ensure!`=20macro=20was=20checking=20for=20the=20oppos?= =?UTF-8?q?ite=20of=20what=20was=20intended,=20which=20resulted=20in=20una?= =?UTF-8?q?uthorized=20users=20being=20able=20to=20perform=20actions=20the?= =?UTF-8?q?y=20shouldn't=20be=20able=20to.=20This=20has=20been=20fixed=20b?= =?UTF-8?q?y=20changing=20the=20`ensure!`=20macro=20to=20check=20for=20the?= =?UTF-8?q?=20correct=20condition.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pallets/afloat/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index ccec4e04..629a46b2 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -223,11 +223,11 @@ pub mod pallet { Self::do_edit_user(who, address, cid)?; } UpdateUserArgs::AdminEdit { cid, group } => { - ensure!(!Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); + ensure!(Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); Self::do_admin_edit_user(who, address, cid, group)?; } UpdateUserArgs::Delete => { - ensure!(!Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); + ensure!(Self::is_admin_or_owner(who.clone()), Error::::Unauthorized); Self::do_delete_user(who, address)?; } }