Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] make fee handler generic #17

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 15 additions & 31 deletions payments/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub mod pallet {
storage::bounded_btree_map::BoundedBTreeMap, traits::tokens::BalanceStatus, transactional,
};
use frame_system::pallet_prelude::*;
use orml_traits::{LockIdentifier, MultiCurrency, NamedMultiReservableCurrency};
use orml_traits::{arithmetic::Zero, LockIdentifier, MultiCurrency, NamedMultiReservableCurrency};
use sp_runtime::{
traits::{CheckedAdd, Saturating},
Percent,
Expand Down Expand Up @@ -578,14 +578,12 @@ pub mod pallet {
incentive_amount,
state: payment_state,
resolver_account: T::DisputeResolver::get_resolver_account(),
fee_detail: None,
fee_amount: Zero::zero(),
};

// Calculate fee amount - this will be implemented based on the custom
// implementation of the fee provider
let (fee_recipient, fee_percent) = T::FeeHandler::apply_fees(from, recipient, &new_payment, remark);
let fee_amount = fee_percent.mul_floor(amount);
new_payment.fee_detail = Some((fee_recipient, fee_amount));
new_payment.fee_amount = T::FeeHandler::get_fee_amount(from, recipient, &new_payment, remark);

*maybe_payment = Some(new_payment.clone());

Expand All @@ -602,9 +600,7 @@ pub mod pallet {
/// the recipient but will stay in Reserve state.
#[require_transactional]
fn reserve_payment_amount(from: &T::AccountId, to: &T::AccountId, payment: PaymentDetail<T>) -> DispatchResult {
let fee_amount = payment.fee_detail.map(|(_, f)| f).unwrap_or_else(|| 0u32.into());

let total_fee_amount = payment.incentive_amount.saturating_add(fee_amount);
let total_fee_amount = payment.incentive_amount.saturating_add(payment.fee_amount);
let total_amount = total_fee_amount.saturating_add(payment.amount);

// reserve the total amount from payment creator
Expand Down Expand Up @@ -632,29 +628,17 @@ pub mod pallet {
Payment::<T>::try_mutate(from, to, |maybe_payment| -> DispatchResult {
let payment = maybe_payment.take().ok_or(Error::<T>::InvalidPayment)?;

// unreserve the incentive amount and fees from the owner account
match payment.fee_detail {
Some((fee_recipient, fee_amount)) => {
T::Asset::unreserve_named(
&PALLET_RESERVE_ID,
payment.asset,
from,
payment.incentive_amount + fee_amount,
);
// transfer fee to marketplace if operation is not cancel
if recipient_share != Percent::zero() {
T::Asset::transfer(
payment.asset,
from, // fee is paid by payment creator
&fee_recipient, // account of fee recipient
fee_amount, // amount of fee
)?;
}
}
None => {
T::Asset::unreserve_named(&PALLET_RESERVE_ID, payment.asset, from, payment.incentive_amount);
}
};
T::Asset::unreserve_named(
&PALLET_RESERVE_ID,
payment.asset,
from,
payment.incentive_amount + payment.fee_amount,
);

// transfer fee to marketplace if operation is not cancel
if recipient_share != Percent::zero() {
T::FeeHandler::apply_fees(from, to, &payment)?;
}

// Unreserve the transfer amount
T::Asset::unreserve_named(&PALLET_RESERVE_ID, payment.asset, to, payment.amount);
Expand Down
26 changes: 18 additions & 8 deletions payments/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use frame_support::{
weights::DispatchClass,
};
use frame_system as system;
use orml_traits::parameter_type_with_key;
use orml_traits::{parameter_type_with_key, MultiCurrency};
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
Percent,
traits::{BlakeTwo256, IdentityLookup, Zero},
DispatchResult, Percent,
};

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
Expand Down Expand Up @@ -115,17 +115,27 @@ impl crate::types::DisputeResolver<AccountId> for MockDisputeResolver {

pub struct MockFeeHandler;
impl crate::types::FeeHandler<Test> for MockFeeHandler {
fn apply_fees(
fn get_fee_amount(
_from: &AccountId,
to: &AccountId,
_detail: &PaymentDetail<Test>,
detail: &PaymentDetail<Test>,
_remark: Option<&[u8]>,
) -> (AccountId, Percent) {
) -> Balance {
match to {
&PAYMENT_RECIPENT_FEE_CHARGED => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(MARKETPLACE_FEE_PERCENTAGE)),
_ => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(0)),
&PAYMENT_RECIPENT_FEE_CHARGED => Percent::from_percent(MARKETPLACE_FEE_PERCENTAGE) * detail.amount,
_ => Zero::zero(),
}
}

fn apply_fees(from: &AccountId, _to: &AccountId, detail: &PaymentDetail<Test>) -> DispatchResult {
// transfer the fee amount to fee recipient account
<Tokens as MultiCurrency<AccountId>>::transfer(
detail.asset,
from, // fee is paid by payment creator
&FEE_RECIPIENT_ACCOUNT, // account of fee recipient
detail.fee_amount, // amount of fee
)
}
}

parameter_types! {
Expand Down
38 changes: 19 additions & 19 deletions payments/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use frame_support::{assert_noop, assert_ok, storage::with_transaction};
use orml_traits::{MultiCurrency, MultiReservableCurrency, NamedMultiReservableCurrency};
use sp_runtime::{Percent, TransactionOutcome};
use sp_runtime::{traits::Zero, Percent, TransactionOutcome};

type Error = crate::Error<Test>;

Expand Down Expand Up @@ -54,7 +54,7 @@ fn test_pay_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);
// the payment amount should be reserved correctly
Expand Down Expand Up @@ -102,7 +102,7 @@ fn test_pay_works() {
incentive_amount: 2,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);

Expand Down Expand Up @@ -134,7 +134,7 @@ fn test_cancel_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);
// the payment amount should be reserved
Expand Down Expand Up @@ -205,7 +205,7 @@ fn test_release_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);
// the payment amount should be reserved
Expand Down Expand Up @@ -367,7 +367,7 @@ fn test_charging_fee_payment_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount
})
);
// the payment amount should be reserved
Expand Down Expand Up @@ -426,7 +426,7 @@ fn test_charging_fee_payment_works_when_canceled() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount,
})
);
// the payment amount should be reserved
Expand Down Expand Up @@ -475,7 +475,7 @@ fn test_pay_with_remark_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);
// the payment amount should be reserved correctly
Expand Down Expand Up @@ -553,7 +553,7 @@ fn test_do_not_overwrite_logic_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::NeedsReview,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
},
);

Expand Down Expand Up @@ -613,7 +613,7 @@ fn test_request_refund() {
cancel_block: expected_cancel_block
},
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);

Expand Down Expand Up @@ -678,7 +678,7 @@ fn test_dispute_refund() {
incentive_amount: expected_incentive_amount,
state: PaymentState::NeedsReview,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);

Expand Down Expand Up @@ -723,7 +723,7 @@ fn test_request_payment() {
incentive_amount: expected_incentive_amount,
state: PaymentState::PaymentRequested,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);

Expand Down Expand Up @@ -799,7 +799,7 @@ fn test_accept_and_pay() {
incentive_amount: expected_incentive_amount,
state: PaymentState::PaymentRequested,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);

Expand Down Expand Up @@ -870,7 +870,7 @@ fn test_accept_and_pay_should_charge_fee_correctly() {
incentive_amount: expected_incentive_amount,
state: PaymentState::PaymentRequested,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount
})
);

Expand Down Expand Up @@ -947,7 +947,7 @@ fn test_create_payment_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount
})
);

Expand Down Expand Up @@ -975,7 +975,7 @@ fn test_create_payment_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount
})
);
});
Expand Down Expand Up @@ -1015,7 +1015,7 @@ fn test_reserve_payment_amount_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount
})
);

Expand Down Expand Up @@ -1065,7 +1065,7 @@ fn test_reserve_payment_amount_works() {
incentive_amount: expected_incentive_amount,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)),
fee_amount: expected_fee_amount
})
);
});
Expand Down Expand Up @@ -1302,7 +1302,7 @@ fn test_automatic_refund_works() {
cancel_block: CANCEL_BLOCK
},
resolver_account: RESOLVER_ACCOUNT,
fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)),
fee_amount: Zero::zero(),
})
);

Expand Down
13 changes: 8 additions & 5 deletions payments/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ pub struct PaymentDetail<T: pallet::Config> {
pub state: PaymentState<T>,
/// account that can settle any disputes created in the payment
pub resolver_account: T::AccountId,
/// fee charged and recipient account details
pub fee_detail: Option<(T::AccountId, BalanceOf<T>)>,
/// fee charged details
pub fee_amount: BalanceOf<T>,
}

/// The `PaymentState` enum tracks the possible states that a payment can be in.
Expand Down Expand Up @@ -95,13 +95,16 @@ pub trait DisputeResolver<Account> {
/// Fee Handler trait that defines how to handle marketplace fees to every
/// payment/swap
pub trait FeeHandler<T: pallet::Config> {
/// Get the distribution of fees to marketplace participants
fn apply_fees(
/// Get the amount of fee to charge user
fn get_fee_amount(
from: &T::AccountId,
to: &T::AccountId,
detail: &PaymentDetail<T>,
remark: Option<&[u8]>,
) -> (T::AccountId, Percent);
) -> BalanceOf<T>;

/// Deduct the fee from the user transaction
fn apply_fees(from: &T::AccountId, to: &T::AccountId, detail: &PaymentDetail<T>) -> DispatchResult;
}

/// Types of Tasks that can be scheduled in the pallet
Expand Down