use cfg_traits::{investments::ForeignInvestment, swaps::SwapInfo, StatusNotificationHook};
use cfg_types::investments::CollectedAmount;
use frame_support::pallet_prelude::*;
use sp_std::marker::PhantomData;
use crate::{
entities::{InvestmentInfo, RedemptionInfo},
pallet::{Config, Error, ForeignInvestmentInfo, ForeignRedemptionInfo, Pallet},
pool_currency_of,
swaps::fulfilled_order,
Action,
};
impl<T: Config> ForeignInvestment<T::AccountId> for Pallet<T> {
type Amount = T::ForeignBalance;
type CurrencyId = T::CurrencyId;
type InvestmentId = T::InvestmentId;
type TrancheAmount = T::TrancheBalance;
fn increase_foreign_investment(
who: &T::AccountId,
investment_id: T::InvestmentId,
foreign_amount: T::ForeignBalance,
foreign_currency: T::CurrencyId,
) -> DispatchResult {
ForeignInvestmentInfo::<T>::mutate_exists(who, investment_id, |entry| {
let info = entry.get_or_insert(InvestmentInfo::new(foreign_currency));
info.ensure_same_foreign(foreign_currency)?;
info.ensure_no_pending_cancel(investment_id)?;
let (increased, pending) = info.increase(who, investment_id, foreign_amount)?;
info.post_increase_swap(who, investment_id, increased, increased.into(), pending)?;
remove_entry(info.is_completed(who, investment_id)?, entry)
})
}
fn cancel_foreign_investment(
who: &T::AccountId,
investment_id: T::InvestmentId,
foreign_currency: T::CurrencyId,
) -> DispatchResult {
ForeignInvestmentInfo::<T>::mutate_exists(who, investment_id, |entry| {
let info = entry.as_mut().ok_or(Error::<T>::InfoNotFound)?;
info.ensure_same_foreign(foreign_currency)?;
info.ensure_no_pending_cancel(investment_id)?;
let (cancelled, pending) = info.cancel(who, investment_id)?;
info.post_cancel_swap(who, investment_id, cancelled, pending)?;
remove_entry(info.is_completed(who, investment_id)?, entry)
})
}
fn increase_foreign_redemption(
who: &T::AccountId,
investment_id: T::InvestmentId,
tranche_tokens_amount: T::TrancheBalance,
payout_foreign_currency: T::CurrencyId,
) -> DispatchResult {
ForeignRedemptionInfo::<T>::mutate_exists(who, investment_id, |entry| -> DispatchResult {
let info = entry.get_or_insert(RedemptionInfo::new(payout_foreign_currency));
info.ensure_same_foreign(payout_foreign_currency)?;
info.increase_redemption(who, investment_id, tranche_tokens_amount)?;
remove_entry(info.is_completed(who, investment_id)?, entry)
})
}
fn cancel_foreign_redemption(
who: &T::AccountId,
investment_id: T::InvestmentId,
payout_foreign_currency: T::CurrencyId,
) -> Result<T::TrancheBalance, DispatchError> {
ForeignRedemptionInfo::<T>::mutate_exists(who, investment_id, |entry| {
let info = entry.as_mut().ok_or(Error::<T>::InfoNotFound)?;
info.ensure_same_foreign(payout_foreign_currency)?;
let cancelled = info.cancel_redeemption(who, investment_id)?;
remove_entry(info.is_completed(who, investment_id)?, entry)?;
Ok(cancelled)
})
}
}
impl<T: Config> StatusNotificationHook for Pallet<T> {
type Error = DispatchError;
type Id = T::OrderId;
type Status = SwapInfo<T::SwapBalance, T::SwapBalance, T::CurrencyId, T::SwapRatio>;
fn notify_status_change(order_id: T::OrderId, swap_info: Self::Status) -> DispatchResult {
let (who, (investment_id, action)) = match fulfilled_order::<T>(&order_id, &swap_info) {
Some(location) => location,
None => return Ok(()), };
let pool_currency = pool_currency_of::<T>(investment_id)?;
let swapped_amount_in = swap_info.swapped_in;
let swapped_amount_out = swap_info.swapped_out;
let pending_amount = swap_info.remaining.amount_out;
match action {
Action::Investment => {
ForeignInvestmentInfo::<T>::mutate_exists(&who, investment_id, |entry| {
let info = entry.as_mut().ok_or(Error::<T>::InfoNotFound)?;
if pool_currency == swap_info.remaining.currency_in {
info.post_increase_swap(
&who,
investment_id,
swapped_amount_in.into(),
swapped_amount_out.into(),
pending_amount.into(),
)
} else {
info.post_cancel_swap(
&who,
investment_id,
swapped_amount_in.into(),
pending_amount.into(),
)?;
remove_entry(info.is_completed(&who, investment_id)?, entry)
}
})
}
Action::Redemption => {
ForeignRedemptionInfo::<T>::mutate_exists(&who, investment_id, |entry| {
let info = entry.as_mut().ok_or(Error::<T>::InfoNotFound)?;
info.post_swap(
&who,
investment_id,
swapped_amount_in.into(),
pending_amount.into(),
)?;
remove_entry(info.is_completed(&who, investment_id)?, entry)
})
}
}
}
}
pub struct CollectedInvestmentHook<T>(PhantomData<T>);
impl<T: Config> StatusNotificationHook for CollectedInvestmentHook<T> {
type Error = DispatchError;
type Id = (T::AccountId, T::InvestmentId);
type Status = CollectedAmount<T::TrancheBalance, T::PoolBalance>;
fn notify_status_change(
(who, investment_id): (T::AccountId, T::InvestmentId),
collected: CollectedAmount<T::TrancheBalance, T::PoolBalance>,
) -> DispatchResult {
ForeignInvestmentInfo::<T>::mutate_exists(&who, investment_id, |entry| {
if let Some(info) = entry.as_mut() {
info.ensure_no_pending_cancel(investment_id)?;
info.post_collect(&who, investment_id, collected)?;
remove_entry(info.is_completed(&who, investment_id)?, entry)?;
}
Ok(())
})
}
}
pub struct CollectedRedemptionHook<T>(PhantomData<T>);
impl<T: Config> StatusNotificationHook for CollectedRedemptionHook<T> {
type Error = DispatchError;
type Id = (T::AccountId, T::InvestmentId);
type Status = CollectedAmount<T::PoolBalance, T::TrancheBalance>;
fn notify_status_change(
(who, investment_id): (T::AccountId, T::InvestmentId),
collected: CollectedAmount<T::PoolBalance, T::TrancheBalance>,
) -> DispatchResult {
ForeignRedemptionInfo::<T>::mutate_exists(&who, investment_id, |entry| {
if let Some(info) = entry.as_mut() {
let (amount, pending) =
info.post_collect_and_swap(&who, investment_id, collected)?;
info.post_swap(&who, investment_id, amount, pending)?;
remove_entry(info.is_completed(&who, investment_id)?, entry)?;
}
Ok(())
})
}
}
fn remove_entry<Entry>(condition: bool, entry: &mut Option<Entry>) -> DispatchResult {
if condition {
*entry = None;
}
Ok(())
}