From d46b012a5641edca355566b53d3b2ae729bced69 Mon Sep 17 00:00:00 2001 From: abdulhakim2902 Date: Fri, 7 Oct 2022 16:57:18 +0700 Subject: [PATCH 1/2] fix: implement payment with appchain token --- .../benchmarking/src/lib.rs | 7 +- .../genetic-analysis-orders/src/functions.rs | 258 ++++++ .../src/impl_genetic_analysis_orders.rs | 347 +++++++ .../genetic-analysis-orders/src/interface.rs | 3 +- pallets/genetic-analysis-orders/src/lib.rs | 858 +----------------- .../genetic-analysis-orders/src/migrations.rs | 122 ++- pallets/genetic-analysis-orders/src/tests.rs | 34 + pallets/genetic-analysis-orders/src/types.rs | 93 ++ .../genetic-analysis/benchmarking/src/lib.rs | 3 + pallets/genetic-analysis/tests/src/lib.rs | 9 + pallets/genetic-analysts/src/lib.rs | 9 +- pallets/genetic-analysts/tests/src/lib.rs | 8 + pallets/genetic-analysts/traits/src/lib.rs | 2 +- pallets/orders/src/functions.rs | 15 +- pallets/service-request/src/migrations.rs | 12 +- 15 files changed, 915 insertions(+), 865 deletions(-) create mode 100644 pallets/genetic-analysis-orders/src/functions.rs create mode 100644 pallets/genetic-analysis-orders/src/impl_genetic_analysis_orders.rs create mode 100644 pallets/genetic-analysis-orders/src/types.rs diff --git a/pallets/genetic-analysis-orders/benchmarking/src/lib.rs b/pallets/genetic-analysis-orders/benchmarking/src/lib.rs index 5c2b2e3a..1c0b40ec 100644 --- a/pallets/genetic-analysis-orders/benchmarking/src/lib.rs +++ b/pallets/genetic-analysis-orders/benchmarking/src/lib.rs @@ -103,7 +103,8 @@ benchmarks! { _genetic_analyst.services[0], 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), - "DeBio Genetic Genetic Link".as_bytes().to_vec() + "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None ) cancel_genetic_analysis_order { @@ -164,6 +165,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) @@ -230,6 +232,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) @@ -296,6 +299,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) @@ -380,6 +384,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) diff --git a/pallets/genetic-analysis-orders/src/functions.rs b/pallets/genetic-analysis-orders/src/functions.rs new file mode 100644 index 00000000..59de12b6 --- /dev/null +++ b/pallets/genetic-analysis-orders/src/functions.rs @@ -0,0 +1,258 @@ +use crate::*; + +use frame_support::{ + dispatch::DispatchError, + sp_runtime::traits::SaturatedConversion, + traits::{fungibles, ExistenceRequirement}, +}; +use primitives_price_and_currency::CurrencyType; +use scale_info::prelude::string::String; + +impl Pallet { + pub fn generate_genetic_analysis_order_id( + customer_id: &T::AccountId, + service_id: &T::Hash, + ) -> T::Hash { + let mut customer_id_bytes = customer_id.encode(); + let mut service_id_bytes = service_id.encode(); + let account_info = frame_system::Pallet::::account(customer_id); + let mut nonce_bytes = account_info.nonce.encode(); + + customer_id_bytes.append(&mut service_id_bytes); + customer_id_bytes.append(&mut nonce_bytes); + + let seed = &customer_id_bytes; + T::Hashing::hash(seed) + } + + pub fn update_genetic_analysis_order_status( + genetic_analysis_order_id: &T::Hash, + status: GeneticAnalysisOrderStatus, + ) -> Result, Error> { + let genetic_analysis_order = GeneticAnalysisOrders::::mutate( + genetic_analysis_order_id, + |genetic_analysis_order| match genetic_analysis_order { + None => None, + Some(genetic_analysis_order) => { + genetic_analysis_order.status = status; + genetic_analysis_order.updated_at = pallet_timestamp::Pallet::::get(); + Some(genetic_analysis_order.clone()) + }, + }, + ) + .ok_or(Error::::GeneticAnalysisOrderNotFound)?; + + Ok(genetic_analysis_order) + } + + pub fn insert_genetic_analysis_order_to_storage( + genetic_analysis_order: &GeneticAnalysisOrderOf, + ) { + GeneticAnalysisOrders::::insert(genetic_analysis_order.id, genetic_analysis_order); + LastGeneticAnalysisOrderByCustomer::::insert( + &genetic_analysis_order.customer_id, + genetic_analysis_order.id, + ); + Self::insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_seller( + genetic_analysis_order, + ); + Self::insert_genetic_analysis_order_id_into_pending_genetic_analysis_orders_by_seller( + genetic_analysis_order, + ); + Self::insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_customer( + genetic_analysis_order, + ); + } + + pub fn insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_seller( + genetic_analysis_order: &GeneticAnalysisOrderOf, + ) { + match GeneticAnalysisOrdersBySeller::::get(&genetic_analysis_order.seller_id) { + None => { + GeneticAnalysisOrdersBySeller::::insert( + &genetic_analysis_order.seller_id, + vec![genetic_analysis_order.id], + ); + }, + Some(mut genetic_analysis_orders) => { + genetic_analysis_orders.push(genetic_analysis_order.id); + GeneticAnalysisOrdersBySeller::::insert( + &genetic_analysis_order.seller_id, + genetic_analysis_orders, + ); + }, + } + } + + pub fn insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_customer( + genetic_analysis_order: &GeneticAnalysisOrderOf, + ) { + match GeneticAnalysisOrdersByCustomer::::get(&genetic_analysis_order.customer_id) { + None => { + GeneticAnalysisOrdersByCustomer::::insert( + &genetic_analysis_order.customer_id, + vec![genetic_analysis_order.id], + ); + }, + Some(mut genetic_analysis_orders) => { + genetic_analysis_orders.push(genetic_analysis_order.id); + GeneticAnalysisOrdersByCustomer::::insert( + &genetic_analysis_order.customer_id, + genetic_analysis_orders, + ); + }, + } + } + + pub fn insert_genetic_analysis_order_id_into_pending_genetic_analysis_orders_by_seller( + genetic_analysis_order: &GeneticAnalysisOrderOf, + ) { + match PendingGeneticAnalysisOrdersBySeller::::get(&genetic_analysis_order.seller_id) { + None => { + PendingGeneticAnalysisOrdersBySeller::::insert( + &genetic_analysis_order.seller_id, + vec![genetic_analysis_order.id], + ); + }, + Some(mut genetic_analysis_orders) => { + genetic_analysis_orders.push(genetic_analysis_order.id); + PendingGeneticAnalysisOrdersBySeller::::insert( + &genetic_analysis_order.seller_id, + genetic_analysis_orders, + ); + }, + } + } + + pub fn remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( + seller_id: &T::AccountId, + genetic_analysis_order_id: &T::Hash, + ) { + let mut genetic_analysis_orders = + PendingGeneticAnalysisOrdersBySeller::::get(seller_id).unwrap_or_default(); + genetic_analysis_orders.retain(|o_id| o_id != genetic_analysis_order_id); + PendingGeneticAnalysisOrdersBySeller::::insert(seller_id, genetic_analysis_orders); + } + + pub fn genetic_analysis_order_can_be_refunded(tracking_id: &TrackingId) -> bool { + match T::GeneticAnalysis::genetic_analysis_by_genetic_analysis_tracking_id(tracking_id) { + Some(genetic_analysis) => genetic_analysis.is_rejected(), + None => false, + } + } + + pub fn do_transfer( + currency: &CurrencyType, + sender: &T::AccountId, + receiver: &T::AccountId, + amount: BalanceOf, + keep_alive: bool, + asset_id: Option, + ) -> Result<(), Error> { + if currency == &CurrencyType::DBIO { + let existence = if keep_alive { + ExistenceRequirement::KeepAlive + } else { + ExistenceRequirement::AllowDeath + }; + + let result = CurrencyOf::::transfer(sender, receiver, amount, existence); + + if let Err(dispatch) = result { + return match dispatch { + DispatchError::Other(_) => Err(Error::::Other), + DispatchError::CannotLookup => Err(Error::::CannotLookup), + DispatchError::BadOrigin => Err(Error::::BadOrigin), + DispatchError::TooManyConsumers => Err(Error::::TooManyConsumers), + DispatchError::ConsumerRemaining => Err(Error::::ConsumerRemaining), + DispatchError::NoProviders => Err(Error::::NoProviders), + DispatchError::Token(_) => Err(Error::::Token), + DispatchError::Arithmetic(_) => Err(Error::::Arithmetic), + DispatchError::Module(_) => Err(Error::::Other), + } + } + } else { + let asset_id = asset_id.ok_or(Error::::AssetIdNotFound)?; + let result = >::transfer( + asset_id, + sender, + receiver, + amount.saturated_into(), + keep_alive, + ); + + if let Err(dispatch) = result { + return match dispatch { + DispatchError::Other(_) => Err(Error::::Other), + DispatchError::CannotLookup => Err(Error::::CannotLookup), + DispatchError::BadOrigin => Err(Error::::BadOrigin), + DispatchError::TooManyConsumers => Err(Error::::TooManyConsumers), + DispatchError::ConsumerRemaining => Err(Error::::ConsumerRemaining), + DispatchError::NoProviders => Err(Error::::NoProviders), + DispatchError::Token(_) => Err(Error::::Token), + DispatchError::Arithmetic(_) => Err(Error::::Arithmetic), + DispatchError::Module(_) => Err(Error::::Module), + } + } + } + + Ok(()) + } + + pub fn do_validate_asset_id( + currency: &CurrencyType, + asset_id: Option, + ) -> Result, Error> { + if currency == &CurrencyType::DBIO { + return Ok(None) + } + + let asset_id = asset_id.ok_or(Error::::AssetIdNotFound)?; + let symbol = >::symbol(&asset_id); + let str_symbol = String::from_utf8(symbol).map_err(|_| Error::::AssetIdNotFound)?; + + if currency.as_string().to_lowercase() != str_symbol.to_lowercase() { + return Err(Error::::AssetIdNotFound) + } + + Ok(Some(asset_id)) + } + + /// The injected pallet ID + pub fn get_pallet_id() -> AccountIdOf { + T::PalletId::get().into_account() + } + + /// The account ID that holds the funds + pub fn account_id() -> AccountIdOf { + >::get().unwrap() + } + + /// Is the balance sufficient for payment + pub fn is_balance_sufficient_for_payment( + account_id: &AccountIdOf, + price: BalanceOf, + ) -> bool { + let balance = T::Currency::free_balance(account_id); + balance >= price + } + + /// Is the pallet balance sufficient for transfer + pub fn is_pallet_balance_sufficient_for_transfer(price: BalanceOf) -> bool { + let balance = T::Currency::free_balance(&Self::account_id()); + balance >= price + } + + /// Set current escrow amount + pub fn set_escrow_amount() { + TotalEscrowAmount::::put(T::Currency::free_balance(&Self::account_id())); + } + + // Get token identifier + pub fn asset_id(currency_type: &CurrencyType) -> Result> { + currency_type + .to_asset_id() + .parse::() + .map_err(|_| Error::::WrongAssetIdFormat) + } +} diff --git a/pallets/genetic-analysis-orders/src/impl_genetic_analysis_orders.rs b/pallets/genetic-analysis-orders/src/impl_genetic_analysis_orders.rs new file mode 100644 index 00000000..3bf03ab7 --- /dev/null +++ b/pallets/genetic-analysis-orders/src/impl_genetic_analysis_orders.rs @@ -0,0 +1,347 @@ +use crate::*; +use traits_genetic_analysis_orders::{ + GeneticAnalysisOrderEventEmitter, GeneticAnalysisOrderStatusUpdater, +}; + +impl GeneticAnalysisOrderInterface for Pallet { + type GeneticAnalysisOrder = GeneticAnalysisOrderOf; + type Error = Error; + + fn create_genetic_analysis_order( + customer_id: &T::AccountId, + genetic_data_id: &T::Hash, + genetic_analyst_service_id: &T::Hash, + price_index: u32, + customer_box_public_key: &T::Hash, + genetic_link: &[u8], + asset_id: Option, + ) -> Result { + let genetic_analyst_service = + T::GeneticAnalystServices::genetic_analyst_service_by_id(genetic_analyst_service_id) + .ok_or(Error::::GeneticAnalystServiceDoesNotExist)?; + + let seller_id = genetic_analyst_service.get_owner_id(); + if !T::GeneticAnalysts::is_genetic_analyst_available(seller_id) { + // If _bool is false, then genetic analyst is unavailable + return Err(Error::::GeneticAnalystUnavailable) + } + + let genetic_data = T::GeneticData::genetic_data_by_id(genetic_data_id) + .ok_or(Error::::GeneticDataDoesNotExist)?; + + if customer_id != genetic_data.get_owner_id() { + return Err(Error::::NotOwnerOfGeneticData) + } + + let prices_by_currency = genetic_analyst_service.get_prices_by_currency(); + if prices_by_currency.is_empty() || + prices_by_currency.len() - 1 < price_index.try_into().unwrap() + { + return Err(Error::::PriceIndexNotFound) + } + + let price_by_currency = &prices_by_currency[price_index as usize]; + + let total_price = &price_by_currency.total_price; + let currency = &price_by_currency.currency; + let asset_id = Self::do_validate_asset_id(currency, asset_id)?; + let prices = &price_by_currency.price_components; + let additional_prices = &price_by_currency.additional_prices; + + let now = pallet_timestamp::Pallet::::get(); + + // Initialize GeneticAnalysis + let genetic_analysis_order_id = + Self::generate_genetic_analysis_order_id(customer_id, genetic_analyst_service_id); + + let genetic_analysis = T::GeneticAnalysis::register_genetic_analysis( + seller_id, + customer_id, + &genetic_analysis_order_id, + ) + .map_err(|_| Error::::GeneticAnalysisInitalizationError)?; + + let genetic_analysis_order = GeneticAnalysisOrder::new( + genetic_analysis_order_id, + *genetic_analyst_service_id, + customer_id.clone(), + *customer_box_public_key, + seller_id.clone(), + *genetic_data_id, + genetic_analysis.get_genetic_analysis_tracking_id().clone(), + genetic_link.to_vec(), + asset_id, + currency.clone(), + prices.clone(), + additional_prices.clone(), + *total_price, + now, + ); + Self::insert_genetic_analysis_order_to_storage(&genetic_analysis_order); + + Ok(genetic_analysis_order) + } + + fn cancel_genetic_analysis_order( + customer_id: &T::AccountId, + genetic_analysis_order_id: &T::Hash, + ) -> Result { + let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id) + .ok_or(Error::::GeneticAnalysisOrderNotFound)?; + + if &genetic_analysis_order.customer_id != customer_id { + return Err(Error::::UnauthorizedGeneticAnalysisOrderCancellation) + } + + let tracking_id = &genetic_analysis_order.genetic_analysis_tracking_id; + let _ = T::GeneticAnalysis::genetic_analysis_by_genetic_analysis_tracking_id(tracking_id) + .filter(|genetic_analysis| genetic_analysis.is_registered()) + .ok_or(Error::::OngoingGeneticAnalysisOrderCannotBeCancelled)?; + + // Default status would be cancelled + let mut genetic_analysis_order_status = GeneticAnalysisOrderStatus::Cancelled; + + if genetic_analysis_order.status == GeneticAnalysisOrderStatus::Paid { + let total_price = genetic_analysis_order.total_price; + + if !Self::is_pallet_balance_sufficient_for_transfer(total_price) { + return Err(Error::::InsufficientPalletFunds) + } + + Self::do_transfer( + &genetic_analysis_order.currency, + &Self::account_id(), + customer_id, + total_price, + false, + genetic_analysis_order.asset_id, + )?; + + // If code reaches here change status to Refunded + genetic_analysis_order_status = GeneticAnalysisOrderStatus::Refunded; + } + + Self::remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( + &genetic_analysis_order.seller_id, + genetic_analysis_order_id, + ); + + // Delete dna sample associated with the genetic_analysis_order + let _ = T::GeneticAnalysis::delete_genetic_analysis(tracking_id); + let genetic_analysis_order = Self::update_genetic_analysis_order_status( + genetic_analysis_order_id, + genetic_analysis_order_status, + )?; + + Ok(genetic_analysis_order) + } + + fn set_genetic_analysis_order_paid( + account_id: &T::AccountId, + genetic_analysis_order_id: &T::Hash, + ) -> Result { + let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id) + .ok_or(Error::::GeneticAnalysisOrderNotFound)?; + + if account_id != &genetic_analysis_order.customer_id { + let _ = EscrowKey::::get() + .filter(|admin| admin == account_id) + .ok_or(Error::::Unauthorized)?; + } + + let customer_id = &genetic_analysis_order.customer_id; + let total_price = genetic_analysis_order.total_price; + + if !Self::is_balance_sufficient_for_payment(customer_id, total_price) { + return Err(Error::::InsufficientFunds) + } + + Self::do_transfer( + &genetic_analysis_order.currency, + customer_id, + &Self::account_id(), + total_price, + true, + genetic_analysis_order.asset_id, // Set AssetId + )?; + + let genetic_analysis_order = Self::update_genetic_analysis_order_status( + genetic_analysis_order_id, + GeneticAnalysisOrderStatus::Paid, + )?; + + Ok(genetic_analysis_order) + } + + fn fulfill_genetic_analysis_order( + escrow_account_id: &T::AccountId, + genetic_analysis_order_id: &T::Hash, + ) -> Result { + // Only the admin can fulfill the genetic_analysis_order + let _ = EscrowKey::::get() + .filter(|admin| admin == escrow_account_id) + .ok_or(Error::::Unauthorized)?; + + let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id) + .ok_or(Error::::GeneticAnalysisOrderNotFound)?; + + let tracking_id = &genetic_analysis_order.genetic_analysis_tracking_id; + let _ = T::GeneticAnalysis::genetic_analysis_by_genetic_analysis_tracking_id(tracking_id) + .filter(|genetic_analysis| genetic_analysis.process_success()) + .ok_or(Error::::GeneticAnalysisNotSuccessfullyProcessed)?; + + if !Self::is_pallet_balance_sufficient_for_transfer(genetic_analysis_order.total_price) { + return Err(Error::::InsufficientPalletFunds) + } + + // Calculate 5% of the price_component_value + let mut price_component_substracted_value: BalanceOf = 0u128.saturated_into(); + for analysis_order in genetic_analysis_order.prices.iter() { + price_component_substracted_value += analysis_order.value / 20u128.saturated_into(); + } + + // 5% of the price_component_value is substracted + let total_price_paid = + genetic_analysis_order.total_price - price_component_substracted_value; + + // Withhold 5% for DBIO + Self::do_transfer( + &genetic_analysis_order.currency, + &Self::account_id(), + &genetic_analysis_order.seller_id, + total_price_paid, + true, + genetic_analysis_order.asset_id, // Set AssetId + )?; + + // Transfer 5% to DBIO Treasury + Self::do_transfer( + &genetic_analysis_order.currency, + &Self::account_id(), + &TreasuryKey::::get().unwrap(), + price_component_substracted_value, + false, + genetic_analysis_order.asset_id, // Set AssetId + )?; + + let genetic_analysis_order = Self::update_genetic_analysis_order_status( + genetic_analysis_order_id, + GeneticAnalysisOrderStatus::Fulfilled, + )?; + + Ok(genetic_analysis_order) + } + + fn set_genetic_analysis_order_refunded( + escrow_account_id: &T::AccountId, + genetic_analysis_order_id: &T::Hash, + ) -> Result { + if escrow_account_id.clone() != EscrowKey::::get().unwrap() { + return Err(Error::::Unauthorized) + } + + let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id) + .ok_or(Error::::GeneticAnalysisOrderNotFound)?; + + let tracking_id = &genetic_analysis_order.genetic_analysis_tracking_id; + if !Self::genetic_analysis_order_can_be_refunded(tracking_id) { + return Err(Error::::GeneticAnalysisOrderNotYetExpired) + } + + if !Self::is_pallet_balance_sufficient_for_transfer(genetic_analysis_order.total_price) { + return Err(Error::::InsufficientPalletFunds) + } + + // Transfer 5% to DBIO Treasury + Self::do_transfer( + &genetic_analysis_order.currency, + &Self::account_id(), + &genetic_analysis_order.customer_id, + genetic_analysis_order.total_price, + false, + genetic_analysis_order.asset_id, // Set AssetId + )?; + + let genetic_analysis_order = Self::update_genetic_analysis_order_status( + genetic_analysis_order_id, + GeneticAnalysisOrderStatus::Refunded, + )?; + + Ok(genetic_analysis_order) + } + + fn update_escrow_key( + account_id: &T::AccountId, + escrow_key: &T::AccountId, + ) -> Result<(), Self::Error> { + if account_id.clone() != EscrowKey::::get().unwrap() { + return Err(Error::::Unauthorized) + } + + EscrowKey::::put(escrow_key); + + Ok(()) + } + + fn update_treasury_key( + account_id: &T::AccountId, + treasury_key: &T::AccountId, + ) -> Result<(), Self::Error> { + if account_id.clone() != TreasuryKey::::get().unwrap() { + return Err(Error::::Unauthorized) + } + + TreasuryKey::::put(treasury_key); + + Ok(()) + } + + fn is_pending_genetic_analysis_order_ids_by_seller_exist(account_id: &T::AccountId) -> bool { + match PendingGeneticAnalysisOrdersBySeller::::get(account_id) { + Some(_arr) => !_arr.is_empty(), + None => false, + } + } +} + +impl GeneticAnalysisOrderEventEmitter for Pallet { + fn emit_event_genetic_analysis_order_failed(genetic_analysis_order_id: &HashOf) { + match Self::genetic_analysis_order_by_id(genetic_analysis_order_id) { + None => Self::deposit_event(Event::GeneticAnalysisOrderNotFound), + Some(genetic_analysis_order) => + Self::deposit_event(Event::GeneticAnalysisOrderFailed(genetic_analysis_order)), + } + } +} + +impl GeneticAnalysisOrderStatusUpdater for Pallet { + fn update_status_failed(genetic_analysis_order_id: &HashOf) { + match Self::genetic_analysis_order_by_id(genetic_analysis_order_id) { + None => Self::deposit_event(Event::GeneticAnalysisOrderNotFound), + Some(genetic_analysis_order) => { + let result = Self::update_genetic_analysis_order_status( + &genetic_analysis_order.id, + GeneticAnalysisOrderStatus::Failed, + ); + + if result.is_err() { + Self::deposit_event(Event::GeneticAnalysisOrderNotFound) + } + }, + } + } + + fn remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( + seller_id: &AccountIdOf, + genetic_analysis_order_id: &HashOf, + ) { + Self::remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( + seller_id, + genetic_analysis_order_id, + ); + } + + fn is_pending_genetic_analysis_order_by_seller_exist(seller_id: &AccountIdOf) -> bool { + Self::is_pending_genetic_analysis_order_ids_by_seller_exist(seller_id) + } +} diff --git a/pallets/genetic-analysis-orders/src/interface.rs b/pallets/genetic-analysis-orders/src/interface.rs index bb061e07..62d6c4b6 100644 --- a/pallets/genetic-analysis-orders/src/interface.rs +++ b/pallets/genetic-analysis-orders/src/interface.rs @@ -9,13 +9,14 @@ pub trait GeneticAnalysisOrderInterface { price_index: u32, customer_box_public_key: &T::Hash, genetic_link: &[u8], + asset_id: Option, ) -> Result; fn cancel_genetic_analysis_order( customer_id: &T::AccountId, genetic_analysis_order_id: &T::Hash, ) -> Result; fn set_genetic_analysis_order_paid( - customer_id: &T::AccountId, + account_id: &T::AccountId, genetic_analysis_order_id: &T::Hash, ) -> Result; fn fulfill_genetic_analysis_order( diff --git a/pallets/genetic-analysis-orders/src/lib.rs b/pallets/genetic-analysis-orders/src/lib.rs index 782d2d09..3c57017b 100644 --- a/pallets/genetic-analysis-orders/src/lib.rs +++ b/pallets/genetic-analysis-orders/src/lib.rs @@ -1,9 +1,13 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub use pallet::*; + +pub mod functions; +pub mod impl_genetic_analysis_orders; pub mod interface; pub mod migrations; +pub mod types; pub mod weights; -use interface::GeneticAnalysisOrderInterface; use frame_support::{ codec::{Decode, Encode}, @@ -13,22 +17,17 @@ use frame_support::{ RuntimeDebug, SaturatedConversion, }, sp_std::convert::TryInto, - traits::{fungibles, Currency, ExistenceRequirement, StorageVersion}, + traits::{Currency, StorageVersion}, PalletId, }; -pub use pallet::*; use primitives_price_and_currency::{CurrencyType, Price}; use primitives_tracking_id::TrackingId; -pub use scale_info::TypeInfo; use sp_std::{prelude::*, vec}; use traits_genetic_analysis::{GeneticAnalysisProvider, GeneticAnalysisTracking}; -use traits_genetic_analysis_orders::{ - GeneticAnalysisOrderEventEmitter, GeneticAnalysisOrderStatusUpdater, -}; + use traits_genetic_analyst_services::{GeneticAnalystServiceInfo, GeneticAnalystServicesProvider}; use traits_genetic_analysts::GeneticAnalystsProvider; use traits_genetic_data::{GeneticData, GeneticDataProvider}; -pub use weights::WeightInfo; #[cfg(test)] mod mock; @@ -36,111 +35,28 @@ mod mock; #[cfg(test)] mod tests; -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub enum GeneticAnalysisOrderStatus { - Unpaid, - Paid, - Fulfilled, - Refunded, - Cancelled, - Failed, -} -impl Default for GeneticAnalysisOrderStatus { - fn default() -> Self { - GeneticAnalysisOrderStatus::Unpaid - } -} - -#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct GeneticAnalysisOrder { - pub id: Hash, - pub service_id: Hash, - pub customer_id: AccountId, - pub customer_box_public_key: Hash, - pub seller_id: AccountId, - pub genetic_data_id: Hash, - pub genetic_analysis_tracking_id: TrackingId, - pub currency: CurrencyType, - pub prices: Vec>, - pub additional_prices: Vec>, - pub total_price: Balance, - pub status: GeneticAnalysisOrderStatus, - pub created_at: Moment, - pub updated_at: Moment, - pub genetic_link: Vec, -} -#[allow(clippy::too_many_arguments)] -impl - GeneticAnalysisOrder -{ - pub fn new( - id: Hash, - service_id: Hash, - customer_id: AccountId, - customer_box_public_key: Hash, - seller_id: AccountId, - genetic_data_id: Hash, - genetic_analysis_tracking_id: TrackingId, - genetic_link: Vec, - currency: CurrencyType, - prices: Vec>, - additional_prices: Vec>, - total_price: Balance, - created_at: Moment, - ) -> Self { - Self { - id, - service_id, - customer_id, - customer_box_public_key, - seller_id, - genetic_data_id, - genetic_analysis_tracking_id, - genetic_link, - currency, - prices, - additional_prices, - status: GeneticAnalysisOrderStatus::default(), - total_price, - created_at, - updated_at: Moment::default(), - } - } - - pub fn get_id(&self) -> &Hash { - &self.id - } - - pub fn get_created_at(&self) -> &Moment { - &self.created_at - } - - pub fn get_service_id(&self) -> &Hash { - &self.service_id - } -} +pub use interface::GeneticAnalysisOrderInterface; +pub use types::*; +pub use weights::WeightInfo; /// The current storage version. -const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); - -// Asset ID and Balance types -pub type AssetId = u32; -pub type AssetBalance = u128; +const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); #[frame_support::pallet] pub mod pallet { - use crate::*; + use super::*; + use frame_support::{dispatch::DispatchResultWithPostInfo, traits::tokens::fungibles}; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config + pallet_timestamp::Config { type Event: From> + IsType<::Event>; - type Assets: fungibles::Mutate< - ::AccountId, - AssetId = AssetId, - Balance = AssetBalance, - >; + type Assets: fungibles::Transfer< + ::AccountId, + AssetId = AssetId, + Balance = AssetBalance, + > + fungibles::InspectMetadata<::AccountId>; type GeneticAnalysts: GeneticAnalystsProvider; type GeneticAnalystServices: GeneticAnalystServicesProvider>; type GeneticData: GeneticDataProvider; @@ -327,6 +243,7 @@ pub mod pallet { Token, Arithmetic, WrongAssetIdFormat, + AssetIdNotFound, } #[pallet::call] @@ -339,6 +256,7 @@ pub mod pallet { price_index: u32, customer_box_public_key: T::Hash, genetic_link: Vec, + asset_id: Option, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; @@ -349,6 +267,7 @@ pub mod pallet { price_index, &customer_box_public_key, &genetic_link, + asset_id, ) { Ok(genetic_analysis_order) => { Self::deposit_event(Event::::GeneticAnalysisOrderCreated( @@ -510,738 +429,3 @@ pub mod pallet { } } } - -impl GeneticAnalysisOrderInterface for Pallet { - type GeneticAnalysisOrder = GeneticAnalysisOrderOf; - type Error = Error; - - fn create_genetic_analysis_order( - customer_id: &T::AccountId, - genetic_data_id: &T::Hash, - genetic_analyst_service_id: &T::Hash, - price_index: u32, - customer_box_public_key: &T::Hash, - genetic_link: &[u8], - ) -> Result { - let genetic_analyst_service = match T::GeneticAnalystServices::genetic_analyst_service_by_id( - genetic_analyst_service_id, - ) { - Some(_genetic_analyst_service) => _genetic_analyst_service, - None => return Err(Error::::GeneticAnalystServiceDoesNotExist), - }; - - let seller_id = genetic_analyst_service.get_owner_id(); - if !T::GeneticAnalysts::is_genetic_analyst_available(seller_id).unwrap() { - // If _bool is false, then genetic analyst is unavailable - return Err(Error::::GeneticAnalystUnavailable) - } - - let genetic_data = T::GeneticData::genetic_data_by_id(genetic_data_id); - if genetic_data.is_none() { - return Err(Error::::GeneticDataDoesNotExist) - } - - let genetic_data = genetic_data.unwrap(); - if customer_id.clone() != genetic_data.get_owner_id().clone() { - return Err(Error::::NotOwnerOfGeneticData) - } - - let prices_by_currency = genetic_analyst_service.get_prices_by_currency(); - if prices_by_currency.is_empty() || - prices_by_currency.len() - 1 < price_index.try_into().unwrap() - { - return Err(Error::::PriceIndexNotFound) - } - - let price_by_currency = &prices_by_currency[price_index as usize]; - - let total_price = &price_by_currency.total_price; - let currency = &price_by_currency.currency; - let prices = &price_by_currency.price_components; - let additional_prices = &price_by_currency.additional_prices; - - let now = pallet_timestamp::Pallet::::get(); - - // Initialize GeneticAnalysis - let genetic_analysis_order_id = - Self::generate_genetic_analysis_order_id(customer_id, genetic_analyst_service_id); - - let genetic_analysis = T::GeneticAnalysis::register_genetic_analysis( - seller_id, - customer_id, - &genetic_analysis_order_id, - ); - - if genetic_analysis.is_err() { - return Err(Error::::GeneticAnalysisInitalizationError) - } - let genetic_analysis = genetic_analysis.ok().unwrap(); - - let genetic_analysis_order = GeneticAnalysisOrder::new( - genetic_analysis_order_id, - *genetic_analyst_service_id, - customer_id.clone(), - *customer_box_public_key, - seller_id.clone(), - *genetic_data_id, - genetic_analysis.get_genetic_analysis_tracking_id().clone(), - genetic_link.to_vec(), - currency.clone(), - prices.clone(), - additional_prices.clone(), - *total_price, - now, - ); - Self::insert_genetic_analysis_order_to_storage(&genetic_analysis_order); - - Ok(genetic_analysis_order) - } - - fn cancel_genetic_analysis_order( - customer_id: &T::AccountId, - genetic_analysis_order_id: &T::Hash, - ) -> Result { - let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id); - if genetic_analysis_order.is_none() { - return Err(Error::::GeneticAnalysisOrderNotFound) - } - let genetic_analysis_order = genetic_analysis_order.unwrap(); - - if genetic_analysis_order.customer_id != customer_id.clone() { - return Err(Error::::UnauthorizedGeneticAnalysisOrderCancellation) - } - - let genetic_analysis = - T::GeneticAnalysis::genetic_analysis_by_genetic_analysis_tracking_id( - &genetic_analysis_order.genetic_analysis_tracking_id, - ) - .unwrap(); - if !genetic_analysis.is_registered() { - return Err(Error::::OngoingGeneticAnalysisOrderCannotBeCancelled) - } - - // Default status would be cancelled - let mut genetic_analysis_order_status = GeneticAnalysisOrderStatus::Cancelled; - - if genetic_analysis_order.status == GeneticAnalysisOrderStatus::Paid { - if !Self::is_pallet_balance_sufficient_for_transfer(genetic_analysis_order.total_price) - { - return Err(Error::::InsufficientPalletFunds) - } - - if genetic_analysis_order.currency == CurrencyType::DBIO { - match CurrencyOf::::transfer( - &Self::account_id(), - &genetic_analysis_order.customer_id, - genetic_analysis_order.total_price, - ExistenceRequirement::AllowDeath, - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => - return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => - return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => - return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } else { - let asset_id = Self::asset_id(&genetic_analysis_order.currency)?; - match >::teleport( - asset_id, - &Self::account_id(), - &genetic_analysis_order.customer_id, - genetic_analysis_order.total_price.saturated_into(), - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => - return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => - return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => - return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } - - // If code reaches here change status to Refunded - genetic_analysis_order_status = GeneticAnalysisOrderStatus::Refunded; - } - - Self::remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( - &genetic_analysis_order.seller_id, - genetic_analysis_order_id, - ); - - // Delete dna sample associated with the genetic_analysis_order - let _genetic_analysis = T::GeneticAnalysis::delete_genetic_analysis( - &genetic_analysis_order.genetic_analysis_tracking_id, - ); - - let genetic_analysis_order = Self::update_genetic_analysis_order_status( - genetic_analysis_order_id, - genetic_analysis_order_status, - ) - .unwrap(); - - Ok(genetic_analysis_order) - } - - fn set_genetic_analysis_order_paid( - customer_id: &T::AccountId, - genetic_analysis_order_id: &T::Hash, - ) -> Result { - let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id) - .ok_or(Error::::GeneticAnalysisOrderNotFound)?; - - if customer_id != &genetic_analysis_order.customer_id { - let _ = EscrowKey::::get() - .filter(|account_id| account_id == customer_id) - .ok_or(Error::::Unauthorized)?; - } - - if !Self::is_balance_sufficient_for_payment( - &genetic_analysis_order.customer_id, - genetic_analysis_order.total_price, - ) { - return Err(Error::::InsufficientFunds) - } - - if genetic_analysis_order.currency == CurrencyType::DBIO { - match CurrencyOf::::transfer( - &genetic_analysis_order.customer_id, - &Self::account_id(), - genetic_analysis_order.total_price, - ExistenceRequirement::AllowDeath, - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } else { - let asset_id = Self::asset_id(&genetic_analysis_order.currency)?; - match >::teleport( - asset_id, - &genetic_analysis_order.customer_id, - &Self::account_id(), - genetic_analysis_order.total_price.saturated_into(), - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } - - let genetic_analysis_order = Self::update_genetic_analysis_order_status( - genetic_analysis_order_id, - GeneticAnalysisOrderStatus::Paid, - ); - - Ok(genetic_analysis_order.unwrap()) - } - - fn fulfill_genetic_analysis_order( - escrow_account_id: &T::AccountId, - genetic_analysis_order_id: &T::Hash, - ) -> Result { - // Only the admin can fulfill the genetic_analysis_order - if escrow_account_id.clone() != EscrowKey::::get().unwrap() { - return Err(Error::::Unauthorized) - } - - let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id); - if genetic_analysis_order.is_none() { - return Err(Error::::GeneticAnalysisOrderNotFound) - } - let genetic_analysis_order = genetic_analysis_order.unwrap(); - - let genetic_analysis = T::GeneticAnalysis::genetic_analysis_by_genetic_analysis_tracking_id( - &genetic_analysis_order.genetic_analysis_tracking_id, - ); - if !genetic_analysis.unwrap().process_success() { - return Err(Error::::GeneticAnalysisNotSuccessfullyProcessed) - } - - if !Self::is_pallet_balance_sufficient_for_transfer(genetic_analysis_order.total_price) { - return Err(Error::::InsufficientPalletFunds) - } - - // Calculate 5% of the price_component_value - let mut price_component_substracted_value: BalanceOf = 0u128.saturated_into(); - for analysis_order in genetic_analysis_order.prices.iter() { - price_component_substracted_value += analysis_order.value / 20u128.saturated_into(); - } - - // 5% of the price_component_value is substracted - let total_price_paid = - genetic_analysis_order.total_price - price_component_substracted_value; - - // Withhold 5% for DBIO - if genetic_analysis_order.currency == CurrencyType::DBIO { - match CurrencyOf::::transfer( - &Self::account_id(), - &genetic_analysis_order.seller_id, - total_price_paid, - ExistenceRequirement::KeepAlive, - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } else { - let asset_id = Self::asset_id(&genetic_analysis_order.currency)?; - match >::teleport( - asset_id, - &Self::account_id(), - &genetic_analysis_order.seller_id, - total_price_paid.saturated_into(), - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } - - // Transfer 5% to DBIO Treasury - if genetic_analysis_order.currency == CurrencyType::DBIO { - match CurrencyOf::::transfer( - &Self::account_id(), - &TreasuryKey::::get().unwrap(), - price_component_substracted_value, - ExistenceRequirement::AllowDeath, - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } else { - let asset_id = Self::asset_id(&genetic_analysis_order.currency)?; - match >::teleport( - asset_id, - &Self::account_id(), - &TreasuryKey::::get().unwrap(), - price_component_substracted_value.saturated_into(), - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } - - let genetic_analysis_order = Self::update_genetic_analysis_order_status( - genetic_analysis_order_id, - GeneticAnalysisOrderStatus::Fulfilled, - ); - - Ok(genetic_analysis_order.unwrap()) - } - - fn set_genetic_analysis_order_refunded( - escrow_account_id: &T::AccountId, - genetic_analysis_order_id: &T::Hash, - ) -> Result { - if escrow_account_id.clone() != EscrowKey::::get().unwrap() { - return Err(Error::::Unauthorized) - } - - let genetic_analysis_order = GeneticAnalysisOrders::::get(genetic_analysis_order_id); - if genetic_analysis_order.is_none() { - return Err(Error::::GeneticAnalysisOrderNotFound) - } - - let genetic_analysis_order_can_be_refunded = - Self::genetic_analysis_order_can_be_refunded(genetic_analysis_order.clone().unwrap()); - if !genetic_analysis_order_can_be_refunded { - return Err(Error::::GeneticAnalysisOrderNotYetExpired) - } - - let genetic_analysis_order = genetic_analysis_order.unwrap(); - if !Self::is_pallet_balance_sufficient_for_transfer(genetic_analysis_order.total_price) { - return Err(Error::::InsufficientPalletFunds) - } - - // Transfer 5% to DBIO Treasury - if genetic_analysis_order.currency == CurrencyType::DBIO { - match CurrencyOf::::transfer( - &Self::account_id(), - &genetic_analysis_order.customer_id, - genetic_analysis_order.total_price, - ExistenceRequirement::AllowDeath, - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } else { - let asset_id = Self::asset_id(&genetic_analysis_order.currency)?; - match >::teleport( - asset_id, - &Self::account_id(), - &genetic_analysis_order.customer_id, - genetic_analysis_order.total_price.saturated_into(), - ) { - Ok(_) => (), - Err(dispatch) => match dispatch { - sp_runtime::DispatchError::Other(_) => return Err(Error::::Other), - sp_runtime::DispatchError::CannotLookup => return Err(Error::::CannotLookup), - sp_runtime::DispatchError::BadOrigin => return Err(Error::::BadOrigin), - sp_runtime::DispatchError::TooManyConsumers => - return Err(Error::::TooManyConsumers), - sp_runtime::DispatchError::ConsumerRemaining => - return Err(Error::::ConsumerRemaining), - sp_runtime::DispatchError::NoProviders => return Err(Error::::NoProviders), - sp_runtime::DispatchError::Token(_) => return Err(Error::::Token), - sp_runtime::DispatchError::Arithmetic(_) => return Err(Error::::Arithmetic), - sp_runtime::DispatchError::Module(_) => return Err(Error::::Arithmetic), - }, - } - } - - let genetic_analysis_order = Self::update_genetic_analysis_order_status( - genetic_analysis_order_id, - GeneticAnalysisOrderStatus::Refunded, - ); - Ok(genetic_analysis_order.unwrap()) - } - - fn update_escrow_key( - account_id: &T::AccountId, - escrow_key: &T::AccountId, - ) -> Result<(), Self::Error> { - if account_id.clone() != EscrowKey::::get().unwrap() { - return Err(Error::::Unauthorized) - } - - EscrowKey::::put(escrow_key); - - Ok(()) - } - - fn update_treasury_key( - account_id: &T::AccountId, - treasury_key: &T::AccountId, - ) -> Result<(), Self::Error> { - if account_id.clone() != TreasuryKey::::get().unwrap() { - return Err(Error::::Unauthorized) - } - - TreasuryKey::::put(treasury_key); - - Ok(()) - } - - fn is_pending_genetic_analysis_order_ids_by_seller_exist(account_id: &T::AccountId) -> bool { - match PendingGeneticAnalysisOrdersBySeller::::get(account_id) { - Some(_arr) => !_arr.is_empty(), - None => false, - } - } -} - -impl Pallet { - pub fn generate_genetic_analysis_order_id( - customer_id: &T::AccountId, - service_id: &T::Hash, - ) -> T::Hash { - let mut customer_id_bytes = customer_id.encode(); - let mut service_id_bytes = service_id.encode(); - let account_info = frame_system::Pallet::::account(customer_id); - let mut nonce_bytes = account_info.nonce.encode(); - - customer_id_bytes.append(&mut service_id_bytes); - customer_id_bytes.append(&mut nonce_bytes); - - let seed = &customer_id_bytes; - T::Hashing::hash(seed) - } - - pub fn update_genetic_analysis_order_status( - genetic_analysis_order_id: &T::Hash, - status: GeneticAnalysisOrderStatus, - ) -> Option> { - GeneticAnalysisOrders::::mutate(genetic_analysis_order_id, |genetic_analysis_order| { - match genetic_analysis_order { - None => None, - Some(genetic_analysis_order) => { - genetic_analysis_order.status = status; - genetic_analysis_order.updated_at = pallet_timestamp::Pallet::::get(); - Some(genetic_analysis_order.clone()) - }, - } - }) - } - - pub fn insert_genetic_analysis_order_to_storage( - genetic_analysis_order: &GeneticAnalysisOrderOf, - ) { - GeneticAnalysisOrders::::insert(genetic_analysis_order.id, genetic_analysis_order); - LastGeneticAnalysisOrderByCustomer::::insert( - &genetic_analysis_order.customer_id, - genetic_analysis_order.id, - ); - Self::insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_seller( - genetic_analysis_order, - ); - Self::insert_genetic_analysis_order_id_into_pending_genetic_analysis_orders_by_seller( - genetic_analysis_order, - ); - Self::insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_customer( - genetic_analysis_order, - ); - } - - pub fn insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_seller( - genetic_analysis_order: &GeneticAnalysisOrderOf, - ) { - match GeneticAnalysisOrdersBySeller::::get(&genetic_analysis_order.seller_id) { - None => { - GeneticAnalysisOrdersBySeller::::insert( - &genetic_analysis_order.seller_id, - vec![genetic_analysis_order.id], - ); - }, - Some(mut genetic_analysis_orders) => { - genetic_analysis_orders.push(genetic_analysis_order.id); - GeneticAnalysisOrdersBySeller::::insert( - &genetic_analysis_order.seller_id, - genetic_analysis_orders, - ); - }, - } - } - - pub fn insert_genetic_analysis_order_id_into_genetic_analysis_orders_by_customer( - genetic_analysis_order: &GeneticAnalysisOrderOf, - ) { - match GeneticAnalysisOrdersByCustomer::::get(&genetic_analysis_order.customer_id) { - None => { - GeneticAnalysisOrdersByCustomer::::insert( - &genetic_analysis_order.customer_id, - vec![genetic_analysis_order.id], - ); - }, - Some(mut genetic_analysis_orders) => { - genetic_analysis_orders.push(genetic_analysis_order.id); - GeneticAnalysisOrdersByCustomer::::insert( - &genetic_analysis_order.customer_id, - genetic_analysis_orders, - ); - }, - } - } - - pub fn insert_genetic_analysis_order_id_into_pending_genetic_analysis_orders_by_seller( - genetic_analysis_order: &GeneticAnalysisOrderOf, - ) { - match PendingGeneticAnalysisOrdersBySeller::::get(&genetic_analysis_order.seller_id) { - None => { - PendingGeneticAnalysisOrdersBySeller::::insert( - &genetic_analysis_order.seller_id, - vec![genetic_analysis_order.id], - ); - }, - Some(mut genetic_analysis_orders) => { - genetic_analysis_orders.push(genetic_analysis_order.id); - PendingGeneticAnalysisOrdersBySeller::::insert( - &genetic_analysis_order.seller_id, - genetic_analysis_orders, - ); - }, - } - } - - pub fn remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( - seller_id: &T::AccountId, - genetic_analysis_order_id: &T::Hash, - ) { - let mut genetic_analysis_orders = - PendingGeneticAnalysisOrdersBySeller::::get(seller_id).unwrap_or_default(); - genetic_analysis_orders.retain(|o_id| o_id != genetic_analysis_order_id); - PendingGeneticAnalysisOrdersBySeller::::insert(seller_id, genetic_analysis_orders); - } - - pub fn genetic_analysis_order_can_be_refunded( - genetic_analysis_order: GeneticAnalysisOrderOf, - ) -> bool { - let genetic_analysis = - T::GeneticAnalysis::genetic_analysis_by_genetic_analysis_tracking_id( - &genetic_analysis_order.genetic_analysis_tracking_id, - ) - .unwrap(); - if !genetic_analysis.is_rejected() { - return false - } - true - } - - /// The injected pallet ID - pub fn get_pallet_id() -> AccountIdOf { - T::PalletId::get().into_account() - } - - /// The account ID that holds the funds - pub fn account_id() -> AccountIdOf { - >::get().unwrap() - } - - /// Is the balance sufficient for payment - pub fn is_balance_sufficient_for_payment( - account_id: &AccountIdOf, - price: BalanceOf, - ) -> bool { - let balance = T::Currency::free_balance(account_id); - balance >= price - } - - /// Is the pallet balance sufficient for transfer - pub fn is_pallet_balance_sufficient_for_transfer(price: BalanceOf) -> bool { - let balance = T::Currency::free_balance(&Self::account_id()); - balance >= price - } - - /// Set current escrow amount - pub fn set_escrow_amount() { - TotalEscrowAmount::::put(T::Currency::free_balance(&Self::account_id())); - } - - // Get token identifier - pub fn asset_id(currency_type: &CurrencyType) -> Result> { - currency_type - .to_asset_id() - .parse::() - .map_err(|_| Error::::WrongAssetIdFormat) - } -} - -impl GeneticAnalysisOrderEventEmitter for Pallet { - fn emit_event_genetic_analysis_order_failed(genetic_analysis_order_id: &HashOf) { - match Self::genetic_analysis_order_by_id(genetic_analysis_order_id) { - None => Self::deposit_event(Event::GeneticAnalysisOrderNotFound), - Some(genetic_analysis_order) => - Self::deposit_event(Event::GeneticAnalysisOrderFailed(genetic_analysis_order)), - } - } -} - -impl GeneticAnalysisOrderStatusUpdater for Pallet { - fn update_status_failed(genetic_analysis_order_id: &HashOf) { - match Self::genetic_analysis_order_by_id(genetic_analysis_order_id) { - None => Self::deposit_event(Event::GeneticAnalysisOrderNotFound), - Some(genetic_analysis_order) => { - Self::update_genetic_analysis_order_status( - &genetic_analysis_order.id, - GeneticAnalysisOrderStatus::Failed, - ); - }, - } - } - - fn remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( - seller_id: &AccountIdOf, - genetic_analysis_order_id: &HashOf, - ) { - Self::remove_genetic_analysis_order_id_from_pending_genetic_analysis_orders_by_seller( - seller_id, - genetic_analysis_order_id, - ); - } - - fn is_pending_genetic_analysis_order_by_seller_exist(seller_id: &AccountIdOf) -> bool { - Self::is_pending_genetic_analysis_order_ids_by_seller_exist(seller_id) - } -} diff --git a/pallets/genetic-analysis-orders/src/migrations.rs b/pallets/genetic-analysis-orders/src/migrations.rs index eacf1a7f..32ff2e57 100644 --- a/pallets/genetic-analysis-orders/src/migrations.rs +++ b/pallets/genetic-analysis-orders/src/migrations.rs @@ -1,26 +1,128 @@ -use crate::{Config, Pallet, PalletAccount, Weight}; -use frame_support::traits::Get; +use crate::{ + AccountIdOf, BalanceOf, Config, GeneticAnalysisOrder, GeneticAnalysisOrderStatus, + GeneticAnalysisOrders, HashOf, MomentOf, Pallet, PalletAccount, Weight, +}; +use frame_support::{ + pallet_prelude::*, + traits::{fungibles, Get}, +}; +use primitives_price_and_currency::{CurrencyType, Price}; +use primitives_tracking_id::TrackingId; +use scale_info::prelude::string::String; +use sp_std::vec::Vec; pub fn migrate() -> Weight { use frame_support::traits::StorageVersion; - let version = StorageVersion::get::>(); let mut weight: Weight = 0; + let mut version = StorageVersion::get::>(); - if version < 2 { - weight = weight.saturating_add(v2::migrate::()); - StorageVersion::new(2).put::>(); + if version == 1 { + weight = weight.saturating_add(version::v2::migrate::()); + version = StorageVersion::new(2); } + if version == 2 { + weight = weight.saturating_add(version::v3::migrate::()); + version = StorageVersion::new(3); + } + + version.put::>(); weight } -mod v2 { +mod version { use super::*; - pub fn migrate() -> Weight { - PalletAccount::::put(>::get_pallet_id()); + pub mod v2 { + use super::*; + + pub fn migrate() -> Weight { + PalletAccount::::put(>::get_pallet_id()); + + T::DbWeight::get().writes(1) + } + } + + pub mod v3 { + use super::*; + + pub fn migrate() -> Weight { + let mut weight = T::DbWeight::get().writes(1); + + #[derive(Encode, Decode, Clone)] + pub struct OldGeneticAnalysisOrder { + pub id: Hash, + pub service_id: Hash, + pub customer_id: AccountId, + pub customer_box_public_key: Hash, + pub seller_id: AccountId, + pub genetic_data_id: Hash, + pub genetic_analysis_tracking_id: TrackingId, + pub currency: CurrencyType, + pub prices: Vec>, + pub additional_prices: Vec>, + pub total_price: Balance, + pub status: GeneticAnalysisOrderStatus, + pub created_at: Moment, + pub updated_at: Moment, + pub genetic_link: Vec, + } + + pub type OldGeneticAnalysisOrderOf = + OldGeneticAnalysisOrder, AccountIdOf, BalanceOf, MomentOf>; + + GeneticAnalysisOrders::::translate( + |_key, old_order: OldGeneticAnalysisOrderOf| { + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + let asset_id = if old_order.currency == CurrencyType::DBIO { + None + } else { + let mut i = 0_u32; + let mut asset_id: Option = None; + + while i <= 10 { + let currency = old_order.currency.clone(); + let symbol = + >::symbol(&i); + + if let Ok(str_symbol) = String::from_utf8(symbol) { + if currency.as_string().to_lowercase() == str_symbol.to_lowercase() + { + asset_id = Some(i); + break + } + } + + i += 1; + } + + asset_id + }; + + Some(GeneticAnalysisOrder { + id: old_order.id, + service_id: old_order.service_id, + customer_id: old_order.customer_id, + customer_box_public_key: old_order.customer_box_public_key, + seller_id: old_order.seller_id, + genetic_data_id: old_order.genetic_data_id, + genetic_analysis_tracking_id: old_order.genetic_analysis_tracking_id, + asset_id, + currency: old_order.currency, + prices: old_order.prices, + additional_prices: old_order.additional_prices, + total_price: old_order.total_price, + status: old_order.status, + created_at: old_order.created_at, + updated_at: old_order.updated_at, + genetic_link: old_order.genetic_link, + }) + }, + ); - T::DbWeight::get().writes(1) + weight + } } } diff --git a/pallets/genetic-analysis-orders/src/tests.rs b/pallets/genetic-analysis-orders/src/tests.rs index 8a1f6264..0d8dc2d6 100644 --- a/pallets/genetic-analysis-orders/src/tests.rs +++ b/pallets/genetic-analysis-orders/src/tests.rs @@ -76,6 +76,7 @@ fn create_genetic_analysis_order() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -95,6 +96,7 @@ fn create_genetic_analysis_order() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -165,6 +167,7 @@ fn cancel_genetic_analysis_order_works() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -189,6 +192,7 @@ fn cancel_genetic_analysis_order_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -279,6 +283,7 @@ fn cancel_genetic_analysis_order_with_refund_works() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -317,6 +322,7 @@ fn cancel_genetic_analysis_order_with_refund_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: _price_by_currency.currency, prices: _price_by_currency.price_components, additional_prices: _price_by_currency.additional_prices, @@ -357,6 +363,7 @@ fn cancel_genetic_analysis_order_with_refund_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: _price_by_currency.currency, prices: _price_by_currency.price_components, additional_prices: _price_by_currency.additional_prices, @@ -452,6 +459,7 @@ fn set_genetic_analysis_order_paid_works() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -490,6 +498,7 @@ fn set_genetic_analysis_order_paid_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: _price_by_currency.currency, prices: _price_by_currency.price_components, additional_prices: _price_by_currency.additional_prices, @@ -577,6 +586,7 @@ fn fulfill_genetic_analysis_order_works() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -625,6 +635,7 @@ fn fulfill_genetic_analysis_order_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: vec![_price.clone()], additional_prices: vec![_price.clone()], @@ -648,6 +659,7 @@ fn fulfill_genetic_analysis_order_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: vec![_price.clone()], additional_prices: vec![_price], @@ -722,6 +734,7 @@ fn set_genetic_analysis_order_refunded_works() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -759,6 +772,7 @@ fn set_genetic_analysis_order_refunded_works() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -793,6 +807,7 @@ fn cant_create_genetic_analysis_order_when_genetic_analyst_service_not_exists() 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ), Error::::GeneticAnalystServiceDoesNotExist ); @@ -858,6 +873,7 @@ fn cant_create_genetic_analysis_order_when_price_index_not_found() { 10, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ), Error::::PriceIndexNotFound ); @@ -923,6 +939,7 @@ fn cant_create_genetic_analysis_order_when_genetic_analyst_unavailable() { 10, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ), Error::::GeneticAnalystUnavailable ); @@ -988,6 +1005,7 @@ fn cant_create_genetic_analysis_order_when_genetic_data_does_not_exist() { 10, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ), Error::::GeneticDataDoesNotExist ); @@ -1053,6 +1071,7 @@ fn cant_create_genetic_analysis_order_when_not_owner_of_genetic_data() { 10, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ), Error::::NotOwnerOfGeneticData ); @@ -1117,6 +1136,7 @@ fn cant_cancel_genetic_analysis_order_when_order_ongoing() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1210,6 +1230,7 @@ fn cant_cancel_genetic_analysis_order_when_unathorized_user() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1302,6 +1323,7 @@ fn cant_set_genetic_analysis_order_paid_when_unauthorized() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1410,6 +1432,7 @@ fn cant_set_genetic_analysis_order_paid_when_insufficient_funds() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1516,6 +1539,7 @@ fn cant_fulfill_genetic_analysis_order_when_genetic_analysis_not_process() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1605,6 +1629,7 @@ fn cant_fulfill_genetic_analysis_order_when_insufficient_pallet_funds() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1730,6 +1755,7 @@ fn cant_set_genetic_analysis_order_refunded_when_not_expired() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1826,6 +1852,7 @@ fn cant_set_genetic_analysis_order_refunded_when_insufficient_funds() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1923,6 +1950,7 @@ fn call_event_should_work() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1941,6 +1969,7 @@ fn call_event_should_work() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -1968,6 +1997,7 @@ fn call_event_should_work() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -1994,6 +2024,7 @@ fn call_event_should_work() { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -2019,6 +2050,7 @@ fn call_event_should_work() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -2059,6 +2091,7 @@ fn call_event_should_work() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -2092,6 +2125,7 @@ fn call_event_should_work() { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, diff --git a/pallets/genetic-analysis-orders/src/types.rs b/pallets/genetic-analysis-orders/src/types.rs new file mode 100644 index 00000000..956ab56d --- /dev/null +++ b/pallets/genetic-analysis-orders/src/types.rs @@ -0,0 +1,93 @@ +use crate::*; +use scale_info::TypeInfo; + +// Asset ID and Balance types +pub type AssetId = u32; +pub type AssetBalance = u128; + +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub enum GeneticAnalysisOrderStatus { + Unpaid, + Paid, + Fulfilled, + Refunded, + Cancelled, + Failed, +} +impl Default for GeneticAnalysisOrderStatus { + fn default() -> Self { + GeneticAnalysisOrderStatus::Unpaid + } +} + +#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct GeneticAnalysisOrder { + pub id: Hash, + pub service_id: Hash, + pub customer_id: AccountId, + pub customer_box_public_key: Hash, + pub seller_id: AccountId, + pub genetic_data_id: Hash, + pub genetic_analysis_tracking_id: TrackingId, + pub asset_id: Option, + pub currency: CurrencyType, + pub prices: Vec>, + pub additional_prices: Vec>, + pub total_price: Balance, + pub status: GeneticAnalysisOrderStatus, + pub created_at: Moment, + pub updated_at: Moment, + pub genetic_link: Vec, +} +#[allow(clippy::too_many_arguments)] +impl + GeneticAnalysisOrder +{ + pub fn new( + id: Hash, + service_id: Hash, + customer_id: AccountId, + customer_box_public_key: Hash, + seller_id: AccountId, + genetic_data_id: Hash, + genetic_analysis_tracking_id: TrackingId, + genetic_link: Vec, + asset_id: Option, + currency: CurrencyType, + prices: Vec>, + additional_prices: Vec>, + total_price: Balance, + created_at: Moment, + ) -> Self { + Self { + id, + service_id, + customer_id, + customer_box_public_key, + seller_id, + genetic_data_id, + genetic_analysis_tracking_id, + genetic_link, + asset_id, + currency, + prices, + additional_prices, + status: GeneticAnalysisOrderStatus::default(), + total_price, + created_at, + updated_at: Moment::default(), + } + } + + pub fn get_id(&self) -> &Hash { + &self.id + } + + pub fn get_created_at(&self) -> &Moment { + &self.created_at + } + + pub fn get_service_id(&self) -> &Hash { + &self.service_id + } +} diff --git a/pallets/genetic-analysis/benchmarking/src/lib.rs b/pallets/genetic-analysis/benchmarking/src/lib.rs index ea0b66f3..ebab503a 100644 --- a/pallets/genetic-analysis/benchmarking/src/lib.rs +++ b/pallets/genetic-analysis/benchmarking/src/lib.rs @@ -103,6 +103,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) @@ -171,6 +172,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) @@ -238,6 +240,7 @@ benchmarks! { 0, T::Hashing::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, ); let _genetic_analysis_order_id_list = GeneticAnalysisOrders::::genetic_analysis_orders_by_genetic_analyst_id(caller.clone()) diff --git a/pallets/genetic-analysis/tests/src/lib.rs b/pallets/genetic-analysis/tests/src/lib.rs index 1106fff3..6584b851 100644 --- a/pallets/genetic-analysis/tests/src/lib.rs +++ b/pallets/genetic-analysis/tests/src/lib.rs @@ -82,6 +82,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -185,6 +186,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -264,6 +266,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -379,6 +382,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -480,6 +484,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -586,6 +591,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -664,6 +670,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = @@ -761,6 +768,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); assert_noop!( @@ -837,6 +845,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = diff --git a/pallets/genetic-analysts/src/lib.rs b/pallets/genetic-analysts/src/lib.rs index 4c68ffea..3a86d491 100644 --- a/pallets/genetic-analysts/src/lib.rs +++ b/pallets/genetic-analysts/src/lib.rs @@ -1067,9 +1067,10 @@ impl GeneticAnalystQualificationOwner for Pallet { /// GeneticAnalystsProvider Trait Implementation impl GeneticAnalystsProvider for Pallet { - fn is_genetic_analyst_available(id: &T::AccountId) -> Option { - let genetic_analyst = - >::genetic_analyst_by_account_id(id); - genetic_analyst.map(|genetic_analyst| genetic_analyst.is_available()) + fn is_genetic_analyst_available(id: &T::AccountId) -> bool { + match >::genetic_analyst_by_account_id(id) { + Some(genetic_analyst) => genetic_analyst.is_available(), + None => false, + } } } diff --git a/pallets/genetic-analysts/tests/src/lib.rs b/pallets/genetic-analysts/tests/src/lib.rs index b1147a63..3494b895 100644 --- a/pallets/genetic-analysts/tests/src/lib.rs +++ b/pallets/genetic-analysts/tests/src/lib.rs @@ -1509,6 +1509,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1529,6 +1530,7 @@ mod tests { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -1619,6 +1621,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1639,6 +1642,7 @@ mod tests { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -1732,6 +1736,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1752,6 +1757,7 @@ mod tests { seller_id: 1, genetic_analysis_tracking_id: _genetic_analysis[0].clone(), genetic_link: "DeBio Genetic Genetic Link".as_bytes().to_vec(), + asset_id: None, currency: CurrencyType::default(), prices: PriceByCurrency::default().price_components, additional_prices: PriceByCurrency::default().additional_prices, @@ -1847,6 +1853,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis_order_id = @@ -1957,6 +1964,7 @@ mod tests { 0, Keccak256::hash("0xhJ7TRe456FADD2726A132ABJK5RCc9E6fC5869F4".as_bytes()), "DeBio Genetic Genetic Link".as_bytes().to_vec(), + None, )); let _genetic_analysis = diff --git a/pallets/genetic-analysts/traits/src/lib.rs b/pallets/genetic-analysts/traits/src/lib.rs index 077343d5..20213017 100644 --- a/pallets/genetic-analysts/traits/src/lib.rs +++ b/pallets/genetic-analysts/traits/src/lib.rs @@ -3,5 +3,5 @@ use frame_system::Config; pub trait GeneticAnalystsProvider { - fn is_genetic_analyst_available(owner_id: &T::AccountId) -> Option; + fn is_genetic_analyst_available(owner_id: &T::AccountId) -> bool; } diff --git a/pallets/orders/src/functions.rs b/pallets/orders/src/functions.rs index fd7eb9ca..b0f8098b 100644 --- a/pallets/orders/src/functions.rs +++ b/pallets/orders/src/functions.rs @@ -134,18 +134,15 @@ impl Pallet { return Ok(None) } - if let Some(asset_id) = asset_id { - let symbol = >::symbol(&asset_id); - let str_symbol = String::from_utf8(symbol).map_err(|_| Error::::AssetIdNotFound)?; + let asset_id = asset_id.ok_or(Error::::AssetIdNotFound)?; + let symbol = >::symbol(&asset_id); + let str_symbol = String::from_utf8(symbol).map_err(|_| Error::::AssetIdNotFound)?; - if currency.as_string().to_lowercase() != str_symbol.to_lowercase() { - return Err(Error::::AssetIdNotFound) - } - - return Ok(Some(asset_id)) + if currency.as_string().to_lowercase() != str_symbol.to_lowercase() { + return Err(Error::::AssetIdNotFound) } - Err(Error::::AssetIdNotFound) + Ok(Some(asset_id)) } pub fn do_transfer( diff --git a/pallets/service-request/src/migrations.rs b/pallets/service-request/src/migrations.rs index 41d4fc8b..8d698724 100644 --- a/pallets/service-request/src/migrations.rs +++ b/pallets/service-request/src/migrations.rs @@ -245,9 +245,11 @@ mod version { RequestById::::translate(|request_id: HashOf, request: OldRequestOf| { weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - let service_invoice_opt = ServiceInvoiceById::::take(request_id); + let service_invoice_opt = ServiceInvoiceById::::take(&request_id); + let service_offer_opt = ServiceOfferById::::take(&request_id); - let (service_id, order_id) = if let Some(service_invoice) = service_invoice_opt { + let (mut service_id, order_id) = if let Some(service_invoice) = service_invoice_opt + { (Some(service_invoice.service_id), Some(service_invoice.order_id)) } else { (None, None) @@ -257,6 +259,12 @@ mod version { RequestByOrderId::::insert(&order_id, &request_id); } + if service_id.is_none() { + if let Some(service_offer) = service_offer_opt { + service_id = Some(service_offer.service_id); + } + } + let new_request = NewRequest { hash: request.hash, requester_address: request.requester_address, From 57e1e66f209b23d7cfe9051a1b602eaae04e0c78 Mon Sep 17 00:00:00 2001 From: abdulhakim2902 Date: Fri, 7 Oct 2022 16:57:36 +0700 Subject: [PATCH 2/2] chore: bump version to 2.2.2 --- node/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Cargo.toml b/node/Cargo.toml index 2f4396eb..88fdfeec 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'debio' -version = '2.2.0' +version = '2.2.2' edition = '2021' license = 'AGPL-3.0' authors = ['DeBio Dev Team ']