diff --git a/Cargo.lock b/Cargo.lock index d6f149ab..4b7edf8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3338,6 +3338,7 @@ dependencies = [ "pallet-im-online", "pallet-indices", "pallet-lottery", + "pallet-mapped-assets", "pallet-membership", "pallet-multisig", "pallet-preimage", @@ -5989,6 +5990,7 @@ dependencies = [ "log", "pallet-balances", "pallet-fruniques", + "pallet-mapped-assets", "pallet-rbac", "pallet-timestamp", "pallet-uniques", 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/functions.rs b/pallets/afloat/src/functions.rs index bb51d0a9..3574f740 100644 --- a/pallets/afloat/src/functions.rs +++ b/pallets/afloat/src/functions.rs @@ -4,47 +4,45 @@ 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; +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; +use sp_runtime::traits::StaticLookup; 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(), - email: LongString::try_from(b"".to_vec()).unwrap(), - lang_key: ShortString::try_from(b"en".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()), 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"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()), 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, admin_user); + >::insert(admin.clone(), admin_user); + Self::give_role_to_user(admin, AfloatRole::Admin)?; } Ok(()) @@ -78,42 +76,30 @@ 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, - 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)?; 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, - 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, - 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 +108,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. @@ -148,52 +135,42 @@ 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 { - ensure!(>::contains_key(user_address.clone()), Error::::UserNotFound); >::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; - 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; - } + Ok(()) + })?; + + 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. /// @@ -209,19 +186,213 @@ 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 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(()) } + 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().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( + 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) + } + + + pub fn do_create_sell_order( + authority: T::AccountId, + 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); + + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + let collection_id = AfloatCollectionId::::get().unwrap(); + + let offer_id = pallet_gated_marketplace::Pallet::::do_enlist_sell_offer( + authority.clone(), + marketplace_id, + collection_id, + item_id, + price, + 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(), + expiration_date: expiration_date, + tax_credit_id: item_id, + creator_id: authority.clone(), + status: OfferStatus::default(), + offer_type: OfferType::Sell, + cancellation_date: None, + }; + + >::insert(offer_id, offer); + + + Self::deposit_event(Event::SellOrderCreated(authority)); + + Ok(()) + } + + pub fn do_create_buy_order( + authority: T::AccountId, + 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); + + let marketplace_id = AfloatMarketPlaceId::::get().unwrap(); + let collection_id = AfloatCollectionId::::get().unwrap(); + + let offer_id = pallet_gated_marketplace::Pallet::::do_enlist_buy_offer( + authority.clone(), + marketplace_id, + collection_id, + item_id, + price, + 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(), + expiration_date: expiration_date, + 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(()) + } + + pub fn do_take_sell_order( + authority: OriginFor, + order_id: [u8; 32], + ) -> 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( + 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 + { + ensure!(!Self::get_all_roles_for_user(authority.clone()).is_empty(), Error::::Unauthorized); + + 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, + { + ensure!(!Self::get_all_roles_for_user(owner.clone()).is_empty(), Error::::Unauthorized); + + 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 + admin: T::AccountId, ) -> DispatchResult where ::CollectionId: From, { @@ -259,4 +430,142 @@ 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_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( + 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(()) + } + + 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()) { + 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); + } + Ok::<(), DispatchError>(()) + })?; + Ok(()) + } + } diff --git a/pallets/afloat/src/lib.rs b/pallets/afloat/src/lib.rs index c5d1bc22..629a46b2 100644 --- a/pallets/afloat/src/lib.rs +++ b/pallets/afloat/src/lib.rs @@ -21,9 +21,7 @@ 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; + use pallet_fruniques::types::{CollectionDescription, FruniqueRole, Attributes, ParentInfo}; const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); use crate::types::*; @@ -41,6 +39,7 @@ pub mod pallet { type Rbac: RoleBasedAccessControl; type RemoveOrigin: EnsureOrigin; type Currency: Currency; + type ItemId: Parameter + Member + Default; } #[pallet::pallet] @@ -57,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. @@ -76,6 +80,14 @@ 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, + // Failed to remove afloat role + FailedToRemoveAfloatRole, } #[pallet::storage] @@ -103,10 +115,38 @@ 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::storage] + #[pallet::getter(fn afloat_offers)] + pub(super) type AfloatOffers = StorageMap< + _, + Blake2_128Concat, + StorageId, + 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 + 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))] @@ -119,46 +159,42 @@ 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()?; - + Self::create_afloat_collection(RawOrigin::Signed(creator.clone()).into(), metadata, admin.clone())?; 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"); 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, admin, marketplace)?; + pallet_gated_marketplace::Pallet::do_create_marketplace(RawOrigin::Signed(creator.clone()).into(), 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,19 +212,119 @@ 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!(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::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)?; } } Ok(()) } + + #[pallet::call_index(4)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] + pub fn create_offer( + origin: OriginFor, + args: CreateOfferArgs, + ) + -> DispatchResult + { + let who = ensure_signed(origin)?; + match args { + 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, expiration_date } => { + Self::do_create_buy_order(who, tax_credit_id, price_per_credit, tax_credit_amount, expiration_date)?; + } + } + Ok(()) + } + + #[pallet::call_index(5)] + #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().reads_writes(1,1))] + pub fn accept_offer( + origin: OriginFor, + offer_id: [u8; 32], + amount: Option, + ) + -> DispatchResult + { + ensure_signed(origin.clone())?; + 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( + 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_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/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..ce40dd7e 100644 --- a/pallets/afloat/src/tests.rs +++ b/pallets/afloat/src/tests.rs @@ -1,2 +1,512 @@ -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(), 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() { + 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); + + + 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::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + + 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); + + + 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::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + + 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); + + + 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::set_afloat_balance(RuntimeOrigin::signed(1), 4, 100000)); + + 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/pallets/afloat/src/types.rs b/pallets/afloat/src/types.rs index d0fca147..a1a0db95 100644 --- a/pallets/afloat/src/types.rs +++ b/pallets/afloat/src/types.rs @@ -1,61 +1,44 @@ 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 ShortString = BoundedVec>; pub type LongString = BoundedVec>; 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 first_name: ShortString, - pub last_name: ShortString, - pub email: LongString, - pub lang_key: ShortString, + 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, 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, + group: 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, + group, created_by, created_date, last_modified_by, last_modified_date, - phone, - credits_needed, - cpa_id, - tax_authority_id, - lock_expiration_date, } } } @@ -65,14 +48,11 @@ impl User { #[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: ShortString, + }, + AdminEdit { + cid: ShortString, + group: ShortString, }, Delete, } @@ -82,16 +62,203 @@ pub enum UpdateUserArgs { #[codec(mel_bound())] pub enum SignUpArgs { BuyerOrSeller { - first_name: ShortString, - last_name: ShortString, - email: LongString, - state: u32, + cid: ShortString, + group: ShortString, }, CPA { - first_name: ShortString, - last_name: ShortString, - email: LongString, - license_number: ShortString, - state: u32, + cid: ShortString, + group: ShortString, + }, +} + +// ! Offer structures + +#[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(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())] +pub struct Offer { + 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, + // pub fee: T::Balance, + pub tax_credit_id: ::ItemId, + pub creator_id: T::AccountId, + pub status: OfferStatus, + pub offer_type: OfferType, +} + +impl Offer { + pub fn new( + tax_credit_amount: u32, + tax_credit_amount_remaining: u32, + price_per_credit: T::Balance, + creation_date: Date, + cancellation_date: Option, + // fee: T::Balance, + tax_credit_id: ::ItemId, + creator_id: T::AccountId, + expiration_date: Date, + status: OfferStatus, + offer_type: OfferType, + ) -> Self { + Self { + tax_credit_amount, + tax_credit_amount_remaining, + price_per_credit, + creation_date, + expiration_date: expiration_date, + cancellation_date, + // fee, + 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: 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, + }, +} + +// ! Transaction structures + +#[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: StorageId, + pub seller_confirmation_date: Option, + pub buyer_confirmation_date: Option, +} + +// ! Roles structures + +#[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 + } } 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..e8eca812 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,9 +340,9 @@ impl Pallet { marketplace_id: [u8; 32], collection_id: T::CollectionId, item_id: T::ItemId, - price: BalanceOf, + 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); @@ -394,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( @@ -402,14 +415,16 @@ impl Pallet { marketplace_id: [u8; 32], collection_id: T::CollectionId, item_id: T::ItemId, - price: BalanceOf, + price: T::Balance, percentage: u32, - ) -> DispatchResult { + ) -> Result<[u8; 32], DispatchError> { //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 @@ -478,16 +497,19 @@ 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(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 +531,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 +638,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 +1193,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..dda36916 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)?; @@ -673,7 +677,9 @@ pub mod pallet { item_id, price, percentage, - ) + )?; + + Ok(()) } /// Accepts a sell order. @@ -693,9 +699,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 +750,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)?; @@ -756,7 +762,9 @@ pub mod pallet { item_id, price, percentage, - ) + )?; + + Ok(()) } /// Accepts a buy order. diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index dafdbc4f..6f9a31de 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; @@ -195,3 +212,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/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 0e8c725d..2c0203bc 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -497,73 +497,61 @@ impl RoleBasedAccessControl for Pallet { /// - `user`: The account to validate. /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope context in which the role will be validated. - fn does_user_have_any_role_in_scope( - account: T::AccountId, - pallet: IdOrVec, - scope_id: &ScopeId, - ) -> bool { - let pallet_id = pallet.to_id(); - UsersByScope::::iter_prefix((pallet_id, scope_id)) - .any(|(_, users)| users.contains(&account)) - } - /// Scope validation - /// - /// Checks if the scope exists in that pallet. - /// ### Parameters: - /// - `pallet_id`: The unique pallet identifier. - /// - `scope_id`: The scope to validate. - fn scope_exists(pallet: IdOrVec, scope_id: &ScopeId) -> DispatchResult { - ensure!(>::get(pallet.to_id()).contains(scope_id), Error::::ScopeNotFound); - Ok(()) - } - - /// Permission validation. - /// - /// Checks if the permission exists in a pallet context. - /// ### Parameters: - /// - `pallet_id`: The unique pallet identifier. - /// - `permission_id`: The permission to validate. - fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId) -> DispatchResult { - ensure!( - >::contains_key(pallet.to_id(), permission_id), - Error::::PermissionNotFound - ); - Ok(()) - } - - /// Role validation - /// - /// Checks if the role is linked to the pallet. - /// ### Parameters: - /// - `pallet_id`: The unique pallet identifier. - /// - `role_id`: The role to validate - fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId) -> DispatchResult { - // The role exists, now check if the role is assigned to that pallet - >::get(pallet.to_id()) - .iter() - .find(|pallet_role| *pallet_role == role_id) - .ok_or(Error::::RoleNotLinkedToPallet)?; - Ok(()) - } - - /// Permission linking validation - /// - /// Checks if the permission is linked to the role in the pallet context. - /// ### Parameters: - /// - `pallet_id`: The unique pallet identifier. - /// - `role_id`: The role which should have the permission. - /// - `permission_id`: The permission which the role should have. - fn is_permission_linked_to_role( - pallet: IdOrVec, - role_id: &RoleId, - permission_id: &PermissionId, - ) -> DispatchResult { - let role_permissions = >::get(pallet.to_id(), role_id); - ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole); - Ok(()) - } - - /// Get roles that have a permission + fn does_user_have_any_role_in_scope(account: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId) -> bool { + let pallet_id = pallet.to_id(); + UsersByScope::::iter_prefix((pallet_id, scope_id)).any(|(_, users)| { + users.contains(&account) + }) + } + /// Scope validation + /// + /// Checks if the scope exists in that pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope to validate. + fn scope_exists(pallet: IdOrVec, scope_id:&ScopeId) -> DispatchResult{ + ensure!(>::get(pallet.to_id()).contains(scope_id), Error::::ScopeNotFound); + Ok(()) + } + + /// Permission validation. + /// + /// Checks if the permission exists in a pallet context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `permission_id`: The permission to validate. + fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId)->DispatchResult{ + ensure!(>::contains_key(pallet.to_id(), permission_id), Error::::PermissionNotFound); + Ok(()) + } + + /// Role validation + /// + /// Checks if the role is linked to the pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role to validate + fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId)-> DispatchResult{ + // The role exists, now check if the role is assigned to that pallet + >::get(pallet.to_id()).iter().find(|pallet_role| *pallet_role==role_id ) + .ok_or(Error::::RoleNotLinkedToPallet)?; + Ok(()) + } + + /// Permission linking validation + /// + /// Checks if the permission is linked to the role in the pallet context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role which should have the permission. + /// - `permission_id`: The permission which the role should have. + fn is_permission_linked_to_role(pallet: IdOrVec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ + let role_permissions = >::get(pallet.to_id(), role_id); + ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole ); + Ok(()) + } + + /// Get roles that have a permission /// /// Returns all the roles within the pallet that have a permission /// ### Parameters: @@ -578,39 +566,45 @@ impl RoleBasedAccessControl for Pallet { .collect() } - /// Role list length - /// - /// Returns the number of user that have the specified role in a scope context. - /// ### Parameters: - /// - `pallet_id`: The unique pallet identifier. - /// - `scope_id`: The scope in which the users will be retrieved. - /// - `role_id`: The role in which the number of users will be counted. - fn get_role_users_len(pallet: IdOrVec, scope_id: &ScopeId, role_id: &RoleId) -> usize { - >::get((pallet.to_id(), scope_id, role_id)).len() - } + /// Role list length + /// + /// Returns the number of user that have the specified role in a scope context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope in which the users will be retrieved. + /// - `role_id`: The role in which the number of users will be counted. + fn get_role_users_len(pallet: IdOrVec, scope_id:&ScopeId, role_id: &RoleId) ->usize{ + >::get((pallet.to_id(), scope_id, role_id)).len() + } - fn to_id(v: Vec) -> [u8; 32] { - v.using_encoded(blake2_256) - } + fn to_id(v: Vec)->[u8;32]{ + v.using_encoded(blake2_256) + } - type MaxRolesPerPallet = T::MaxRolesPerPallet; + fn get_roles_by_user(user: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId) -> Vec { + >::get((user, pallet.to_id(), scope_id)).into() + } - type MaxPermissionsPerRole = T::MaxPermissionsPerRole; + type MaxRolesPerPallet = T::MaxRolesPerPallet; - type PermissionMaxLen = T::PermissionMaxLen; + type MaxPermissionsPerRole = T::MaxPermissionsPerRole; + + type PermissionMaxLen = T::PermissionMaxLen; + + type RoleMaxLen = T::RoleMaxLen; - type RoleMaxLen = T::RoleMaxLen; } -impl Pallet { - fn bound>(vec: Vec, err: Error) -> Result, Error> { - BoundedVec::::try_from(vec).map_err(|_| err) - } +impl Pallet{ + fn bound>(vec: Vec, err : Error )->Result, Error>{ + BoundedVec::::try_from(vec).map_err(|_| err) + } + + fn has_unique_elements(vec: Vec) -> bool{ + let mut filtered_vec = vec.clone(); + filtered_vec.sort(); + filtered_vec.dedup(); + vec.len() == filtered_vec.len() + } - fn has_unique_elements(vec: Vec) -> bool { - let mut filtered_vec = vec.clone(); - filtered_vec.sort(); - filtered_vec.dedup(); - vec.len() == filtered_vec.len() - } } diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 476e790e..18a63390 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -30,97 +30,44 @@ impl IdOrVec { } } -pub trait RoleBasedAccessControl { - type MaxRolesPerPallet: Get; - type MaxPermissionsPerRole: Get; - type RoleMaxLen: Get; - type PermissionMaxLen: Get; - // scopes - fn create_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult; - // scope removal - fn remove_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult; - // removes all from one pallet/application - fn remove_pallet_storage(pallet: IdOrVec) -> DispatchResult; - // roles creation and setting - fn create_and_set_roles( - pallet: IdOrVec, - roles: Vec>, - ) -> Result, DispatchError>; - fn create_role(role: Vec) -> Result; - fn set_role_to_pallet(pallet: IdOrVec, role_id: RoleId) -> DispatchResult; - fn set_multiple_pallet_roles(pallet: IdOrVec, roles: Vec) -> DispatchResult; - fn assign_role_to_user( - user: AccountId, - pallet: IdOrVec, - scope_id: &ScopeId, - role_id: RoleId, - ) -> DispatchResult; - // role removal - fn remove_role_from_user( - user: AccountId, - pallet: IdOrVec, - scope_id: &ScopeId, - role_id: RoleId, - ) -> DispatchResult; - // permissions - fn create_and_set_permissions( - pallet: IdOrVec, - role: RoleId, - permissions: Vec>, - ) -> Result, DispatchError>; - fn create_permission( - pallet: IdOrVec, - permissions: Vec, - ) -> Result; - fn set_permission_to_role( - pallet: IdOrVec, - role: RoleId, - permission: PermissionId, - ) -> DispatchResult; - fn set_multiple_permissions_to_role( - pallet: IdOrVec, - role: RoleId, - permission: Vec, - ) -> DispatchResult; - fn do_revoke_permission_from_role( - pallet: IdOrVec, - role: RoleId, - permission: PermissionId, - ) -> DispatchResult; - fn do_remove_permission_from_pallet( - pallet: IdOrVec, - permission: PermissionId, - ) -> DispatchResult; - // helpers - fn is_authorized( - user: AccountId, - pallet: IdOrVec, - scope_id: &ScopeId, - permission_id: &PermissionId, - ) -> DispatchResult; - fn has_role( - user: AccountId, - pallet: IdOrVec, - scope_id: &ScopeId, - role_ids: Vec, - ) -> DispatchResult; - fn scope_exists(pallet: IdOrVec, scope_id: &ScopeId) -> DispatchResult; - fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId) -> DispatchResult; - fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId) -> DispatchResult; - fn is_permission_linked_to_role( - pallet: IdOrVec, - role_id: &RoleId, - permission_id: &PermissionId, - ) -> DispatchResult; - 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_that_have_permission( - pallet: PalletId, - permission_id: &PermissionId, - ) -> Vec; +pub trait RoleBasedAccessControl{ + type MaxRolesPerPallet: Get; + type MaxPermissionsPerRole: Get; + type RoleMaxLen: Get; + type PermissionMaxLen: Get; + // scopes + fn create_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult; + // scope removal + fn remove_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult; + // removes all from one pallet/application + fn remove_pallet_storage(pallet: IdOrVec) -> DispatchResult; + // roles creation and setting + fn create_and_set_roles(pallet: IdOrVec, roles: Vec>) -> + Result, DispatchError>; + fn create_role(role: Vec)-> Result; + fn set_role_to_pallet(pallet: IdOrVec, role_id: RoleId )-> DispatchResult; + fn set_multiple_pallet_roles(pallet: IdOrVec, roles: Vec)->DispatchResult; + fn assign_role_to_user(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; + // role removal + fn remove_role_from_user(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; + // permissions + fn create_and_set_permissions(pallet: IdOrVec, role: RoleId, permissions: Vec>)-> + Result, DispatchError>; + fn create_permission(pallet: IdOrVec, permissions: Vec) -> Result; + fn set_permission_to_role( pallet: IdOrVec, role: RoleId, permission: PermissionId ) -> DispatchResult; + fn set_multiple_permissions_to_role( pallet: IdOrVec, role: RoleId, permission: Vec )-> DispatchResult; + fn do_revoke_permission_from_role(pallet: IdOrVec, role: RoleId, permission: PermissionId,) -> DispatchResult; + fn do_remove_permission_from_pallet(pallet: IdOrVec, permission: PermissionId,) -> DispatchResult; + // helpers + fn is_authorized(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; + fn has_role(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; + fn scope_exists(pallet: IdOrVec, scope_id:&ScopeId) -> DispatchResult; + fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId)->DispatchResult; + fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId)-> DispatchResult; + fn is_permission_linked_to_role(pallet: IdOrVec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; + 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; + fn get_roles_that_have_permission(pallet: PalletId, permission_id: &PermissionId) -> Vec; } diff --git a/parachain-runtime/Cargo.toml b/parachain-runtime/Cargo.toml index 424b5962..0cbe2bac 100644 --- a/parachain-runtime/Cargo.toml +++ b/parachain-runtime/Cargo.toml @@ -52,6 +52,7 @@ pallet-gated-marketplace = { default-features = false, path = "../pallets/gated- pallet-rbac = { default-features = false, path = "../pallets/rbac" } pallet-confidential-docs = { default-features = false, path = "../pallets/confidential-docs" } pallet-fund-admin = { default-features = false, path = "../pallets/fund-admin" } +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 } @@ -142,6 +143,7 @@ std = [ "pallet-rbac/std", "pallet-fund-admin/std", "pallet-template/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 f0773167..f7c6c606 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; @@ -1041,6 +1044,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; @@ -1192,6 +1218,7 @@ construct_runtime!( ConfidentialDocs: pallet_confidential_docs::{Pallet, Call, Storage, Event} = 156, FundAdmin: pallet_fund_admin::{Pallet, Call, Storage, Event} = 157, TemplateModule: pallet_template::{Pallet, Call, Storage, Event} = 158, + MappedAssets: pallet_mapped_assets::{Pallet, Call, Storage, Event} = 159, } ); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 3b0cfe66..a14fce12 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -780,6 +780,7 @@ impl pallet_afloat::Config for Runtime { type TimeProvider = Timestamp; type RemoveOrigin = EnsureRoot; type Rbac = RBAC; + type ItemId = u32; } parameter_types! {