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] Fee Handler #161

Merged
merged 11 commits into from
Dec 22, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 0 additions & 1 deletion pallets/payment/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,5 @@ std = [
'scale-info/std',
'orml-traits/std',
'frame-benchmarking/std',
'orml-tokens/std'
]
runtime-benchmarks = ["frame-benchmarking"]
21 changes: 9 additions & 12 deletions pallets/payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ pub mod pallet {
/// Dispute resolution account
type DisputeResolver: DisputeResolver<Self::AccountId>;
/// Fee handler trait
type FeeHandler: FeeHandler<Self::AccountId, AssetIdOf<Self>, BalanceOf<Self>>;
/// Fee recipent account
#[pallet::constant]
type FeeRecipientAccount: Get<Self::AccountId>;
type FeeHandler: FeeHandler<Self::AccountId>;
/// Incentive percentage - amount witheld from sender
#[pallet::constant]
type IncentivePercentage: Get<Percent>;
Expand Down Expand Up @@ -184,12 +181,15 @@ pub mod pallet {
recipient.clone(),
|maybe_payment| -> DispatchResult {
let incentive_amount = T::IncentivePercentage::get() * amount;
let (fee_recipient, fee_percent) = T::FeeHandler::apply_fees(&from, &recipient);
let fee_amount = fee_percent * amount;
let new_payment = Some(PaymentDetail {
asset,
amount,
incentive_amount,
state: PaymentState::Created,
resolver_account: T::DisputeResolver::get_origin(),
fee_detail: (fee_recipient, fee_amount),
});
match maybe_payment {
Some(x) => {
Expand All @@ -200,8 +200,8 @@ pub mod pallet {
x.state != PaymentState::Created,
Error::<T>::PaymentAlreadyInProcess
);
// reserve the incentive amount from the payment creator
T::Asset::reserve(asset, &from, incentive_amount)?;
// reserve the incentive + fees amount from the payment creator
T::Asset::reserve(asset, &from, incentive_amount + fee_amount)?;
// transfer amount to recipient
T::Asset::transfer(asset, &from, &recipient, amount)?;
// reserved the amount in the recipient account
Expand Down Expand Up @@ -240,15 +240,12 @@ pub mod pallet {
T::Asset::unreserve(payment.asset, &from, payment.incentive_amount);
// unreserve the amount to the recipent
T::Asset::unreserve(payment.asset, &to, payment.amount);
// calculate fee charged for transaction
let fee_amount =
T::FeeHandler::apply_fees(&from, &to, payment.asset, payment.amount);
// transfer fee amount to marketplace
T::Asset::transfer(
payment.asset,
&from,
&T::FeeRecipientAccount::get(),
fee_amount,
&from, // fee is paid by payment creator
stanly-johnson marked this conversation as resolved.
Show resolved Hide resolved
&payment.fee_detail.0, // account of fee recipient
payment.fee_detail.1, // amount of fee
)?;
payment.state = PaymentState::Released;

Expand Down
10 changes: 4 additions & 6 deletions pallets/payment/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,17 @@ impl crate::types::DisputeResolver<AccountId> for MockDisputeResolver {
}

pub struct MockFeeHandler;
impl crate::types::FeeHandler<AccountId, u32, u32> for MockFeeHandler {
fn apply_fees(_from: &AccountId, to: &AccountId, _asset: u32, _amount: u32) -> u32 {
impl crate::types::FeeHandler<AccountId> for MockFeeHandler {
fn apply_fees(_from: &AccountId, to: &AccountId) -> (AccountId, Percent) {
match to {
&PAYMENT_RECIPENT_FEE_CHARGED => 1u32,
_ => 0u32,
&PAYMENT_RECIPENT_FEE_CHARGED => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(10)),
_ => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(0)),
}
}
}

parameter_types! {
pub const IncentivePercentage: Percent = Percent::from_percent(10);
pub const FeeRecipientAccount: AccountId = FEE_RECIPIENT_ACCOUNT;
}

impl payment::Config for Test {
Expand All @@ -121,7 +120,6 @@ impl payment::Config for Test {
type DisputeResolver = MockDisputeResolver;
type IncentivePercentage = IncentivePercentage;
type FeeHandler = MockFeeHandler;
type FeeRecipientAccount = FeeRecipientAccount;
}

// Build genesis storage according to the mock runtime.
Expand Down
31 changes: 20 additions & 11 deletions pallets/payment/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ fn test_create_payment_works() {
amount: 20,
incentive_amount: 2,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
stanly-johnson marked this conversation as resolved.
Show resolved Hide resolved
})
);
// the payment amount should be reserved correctly
Expand All @@ -52,7 +53,8 @@ fn test_create_payment_works() {
amount: 20,
incentive_amount: 2,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);
});
Expand All @@ -75,7 +77,8 @@ fn test_cancel_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);
// the payment amount should be reserved
Expand Down Expand Up @@ -103,7 +106,8 @@ fn test_cancel_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Cancelled,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);
// cannot call cancel again
Expand Down Expand Up @@ -131,7 +135,8 @@ fn test_release_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);
// the payment amount should be reserved
Expand All @@ -153,7 +158,8 @@ fn test_release_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Released,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);
// cannot call release again
Expand Down Expand Up @@ -218,7 +224,8 @@ fn test_set_state_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Released,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);

Expand Down Expand Up @@ -250,7 +257,8 @@ fn test_set_state_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Cancelled,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 0)
})
);
});
Expand All @@ -273,7 +281,8 @@ fn test_charging_fee_payment_works() {
amount: 40,
incentive_amount: 4,
state: PaymentState::Created,
resolver_account: RESOLVER_ACCOUNT
resolver_account: RESOLVER_ACCOUNT,
fee_detail: (FEE_RECIPIENT_ACCOUNT, 4)
})
);
// the payment amount should be reserved
Expand All @@ -283,9 +292,9 @@ fn test_charging_fee_payment_works() {
// should succeed for valid payment
assert_ok!(Payment::release(Origin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT_FEE_CHARGED));
// the payment amount should be transferred
assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 59);
assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 56);
assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 40);
assert_eq!(Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), 1);
assert_eq!(Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), 4);
assert_eq!(Tokens::total_issuance(CURRENCY_ID), 100);
});
}
9 changes: 6 additions & 3 deletions pallets/payment/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(unused_qualifications)]
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_runtime::DispatchResult;
use sp_runtime::{DispatchResult, Percent};

#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -11,6 +11,7 @@ pub struct PaymentDetail<Asset, Amount, Account> {
pub incentive_amount: Amount,
pub state: PaymentState,
pub resolver_account: Account,
pub fee_detail: (Account, Amount),
}

#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)]
Expand Down Expand Up @@ -60,6 +61,8 @@ pub trait DisputeResolver<Account> {
fn get_origin() -> Account;
}

pub trait FeeHandler<Account, Asset, Amount> {
fn apply_fees(from: &Account, to: &Account, asset: Asset, amount: Amount) -> Amount;
/// Fee Handler trait that defines how to handle marketplace fees to every payment/swap
pub trait FeeHandler<Account> {
/// Get the distribution of fees to marketplace participants
fn apply_fees(from: &Account, to: &Account) -> (Account, Percent);
}
9 changes: 9 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,14 @@ impl virto_payment::DisputeResolver<AccountId> for VirtoDisputeResolver {
}
}

pub struct VirtoFeeHandler;
impl virto_payment::FeeHandler<AccountId> for VirtoFeeHandler {
fn apply_fees(_from: &AccountId, _to: &AccountId) -> (AccountId, Percent) {
const VIRTO_MARKETPLACE_FEE_PERCENT: Percent = Percent::from_percent(0);
(Sudo::key(), VIRTO_MARKETPLACE_FEE_PERCENT)
}
}

parameter_types! {
pub const IncentivePercentage: Percent = Percent::from_percent(10);
}
Expand All @@ -611,6 +619,7 @@ impl virto_payment::Config for Runtime {
type Asset = Assets;
type DisputeResolver = VirtoDisputeResolver;
type IncentivePercentage = IncentivePercentage;
type FeeHandler = VirtoFeeHandler;
}

parameter_types! {
Expand Down