#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod types;
pub mod weights;
pub use weights::WeightInfo;
#[frame_support::pallet]
pub mod pallet {
#[cfg(feature = "runtime-benchmarks")]
use cfg_traits::benchmarking::PoolFeesBenchmarkHelper;
use cfg_traits::{
changes::ChangeGuard,
fee::{FeeAmountProration, PoolFeeBucket, PoolFeesInspect, PoolFeesMutate},
EpochTransitionHook, PoolInspect, PoolNAV, PoolReserve, PreConditions, Seconds, TimeAsSecs,
};
use cfg_types::{
pools::{
PayableFeeAmount, PoolFee, PoolFeeAmount, PoolFeeAmounts, PoolFeeEditor, PoolFeeInfo,
PoolFeeType, PoolFeesList, PoolFeesOfBucket,
},
portfolio,
portfolio::{InitialPortfolioValuation, PortfolioValuationUpdateType},
};
use frame_support::{
pallet_prelude::*,
traits::{
fungibles::{Inspect, Mutate},
tokens,
tokens::Preservation,
},
PalletId,
};
use frame_system::pallet_prelude::*;
use parity_scale_codec::HasCompact;
#[cfg(feature = "runtime-benchmarks")]
use sp_arithmetic::fixed_point::FixedPointNumber;
use sp_arithmetic::{
traits::{EnsureAdd, EnsureAddAssign, EnsureSub, EnsureSubAssign, One, Saturating, Zero},
ArithmeticError, FixedPointOperand,
};
use sp_runtime::{traits::AccountIdConversion, SaturatedConversion};
use sp_std::vec::Vec;
use strum::IntoEnumIterator;
use super::*;
use crate::types::Change;
pub type PoolFeeInfoOf<T> = PoolFeeInfo<
<T as frame_system::Config>::AccountId,
<T as Config>::Balance,
<T as Config>::Rate,
>;
pub type PoolFeeOf<T> = PoolFee<
<T as frame_system::Config>::AccountId,
<T as Config>::FeeId,
PoolFeeAmounts<<T as Config>::Balance, <T as Config>::Rate>,
>;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type FeeId: Parameter
+ Member
+ Default
+ TypeInfo
+ MaxEncodedLen
+ Copy
+ EnsureAdd
+ One
+ Ord;
type Balance: tokens::Balance + FixedPointOperand + From<Seconds>;
type CurrencyId: Parameter + Member + Copy + TypeInfo + MaxEncodedLen;
type PoolId: Member + Parameter + Default + Copy + HasCompact + MaxEncodedLen;
type Rate: Parameter
+ Member
+ cfg_types::fixed_point::FixedPointNumberExtension
+ TypeInfo
+ MaxEncodedLen;
type Tokens: Mutate<Self::AccountId>
+ Inspect<Self::AccountId, AssetId = Self::CurrencyId, Balance = Self::Balance>;
type RuntimeChange: From<Change<Self>> + TryInto<Change<Self>>;
type ChangeGuard: ChangeGuard<
PoolId = Self::PoolId,
ChangeId = Self::Hash,
Change = Self::RuntimeChange,
>;
type PoolReserve: PoolReserve<
Self::AccountId,
Self::CurrencyId,
Balance = Self::Balance,
PoolId = Self::PoolId,
>;
type IsPoolAdmin: PreConditions<(Self::AccountId, Self::PoolId), Result = bool>;
type MaxPoolFeesPerBucket: Get<u32>;
type MaxFeesPerPool: Get<u32>;
#[pallet::constant]
type PalletId: Get<PalletId>;
type Time: TimeAsSecs;
type WeightInfo: WeightInfo;
}
#[pallet::storage]
pub type FeeIds<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::PoolId,
Blake2_128Concat,
PoolFeeBucket,
BoundedVec<T::FeeId, T::MaxPoolFeesPerBucket>,
ValueQuery,
>;
#[pallet::storage]
pub type LastFeeId<T: Config> = StorageValue<_, T::FeeId, ValueQuery>;
#[pallet::storage]
pub type FeeIdsToPoolBucket<T: Config> =
StorageMap<_, Blake2_128Concat, T::FeeId, (T::PoolId, PoolFeeBucket), OptionQuery>;
#[pallet::storage]
pub type ActiveFees<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::PoolId,
Blake2_128Concat,
PoolFeeBucket,
BoundedVec<PoolFeeOf<T>, T::MaxPoolFeesPerBucket>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn portfolio_valuation)]
pub(crate) type PortfolioValuation<T: Config> = StorageMap<
_,
Blake2_128Concat,
T::PoolId,
portfolio::PortfolioValuation<T::Balance, T::FeeId, T::MaxFeesPerPool>,
ValueQuery,
InitialPortfolioValuation<T::Time>,
>;
#[pallet::storage]
pub(crate) type AssetsUnderManagement<T: Config> =
StorageMap<_, Blake2_128Concat, T::PoolId, T::Balance, ValueQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Proposed {
pool_id: T::PoolId,
fee_id: T::FeeId,
bucket: PoolFeeBucket,
fee: PoolFeeInfoOf<T>,
},
Added {
pool_id: T::PoolId,
bucket: PoolFeeBucket,
fee_id: T::FeeId,
fee: PoolFeeInfoOf<T>,
},
Removed {
pool_id: T::PoolId,
bucket: PoolFeeBucket,
fee_id: T::FeeId,
},
Charged {
pool_id: T::PoolId,
fee_id: T::FeeId,
amount: T::Balance,
pending: T::Balance,
},
Uncharged {
pool_id: T::PoolId,
fee_id: T::FeeId,
amount: T::Balance,
pending: T::Balance,
},
Paid {
pool_id: T::PoolId,
fee_id: T::FeeId,
amount: T::Balance,
destination: T::AccountId,
},
Accrued {
pool_id: T::PoolId,
fee_id: T::FeeId,
pending: T::Balance,
disbursement: T::Balance,
},
PortfolioValuationUpdated {
pool_id: T::PoolId,
valuation: T::Balance,
update_type: PortfolioValuationUpdateType,
},
}
#[pallet::error]
pub enum Error<T> {
FeeIdAlreadyExists,
FeeNotFound,
PoolNotFound,
NotPoolAdmin,
MaxPoolFeesPerBucket,
ChangeIdNotPoolFees,
UnauthorizedCharge,
UnauthorizedEdit,
CannotBeCharged,
NothingCharged,
NothingUncharged,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::propose_new_fee())]
pub fn propose_new_fee(
origin: OriginFor<T>,
pool_id: T::PoolId,
bucket: PoolFeeBucket,
fee: PoolFeeInfoOf<T>,
) -> DispatchResult {
let who = ensure_signed_or_root(origin)?;
ensure!(
T::PoolReserve::pool_exists(pool_id),
Error::<T>::PoolNotFound
);
if let Some(signer) = who {
ensure!(
T::IsPoolAdmin::check((signer, pool_id)),
Error::<T>::NotPoolAdmin
);
}
let fee_id = Self::generate_fee_id()?;
T::ChangeGuard::note(
pool_id,
Change::AppendFee(fee_id, bucket, fee.clone()).into(),
)?;
Self::deposit_event(Event::<T>::Proposed {
pool_id,
fee_id,
bucket,
fee,
});
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::apply_new_fee(T::MaxPoolFeesPerBucket::get()))]
pub fn apply_new_fee(
origin: OriginFor<T>,
pool_id: T::PoolId,
change_id: T::Hash,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
ensure!(
T::PoolReserve::pool_exists(pool_id),
Error::<T>::PoolNotFound
);
let (fee_id, bucket, fee) = Self::get_released_change(pool_id, change_id)
.map(|Change::AppendFee(id, bucket, fee)| (id, bucket, fee))?;
let fee_count = Self::add_fee_with_id(pool_id, fee_id, bucket, fee)?;
Ok(Some(T::WeightInfo::apply_new_fee(fee_count)).into())
}
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::remove_fee(T::MaxPoolFeesPerBucket::get()))]
pub fn remove_fee(origin: OriginFor<T>, fee_id: T::FeeId) -> DispatchResult {
let who = ensure_signed_or_root(origin)?;
let fee = Self::get_active_fee(fee_id)?;
ensure!(
match (fee.editor, who) {
(PoolFeeEditor::Account(editor), Some(signer)) => editor == signer,
(PoolFeeEditor::Root, None) => true,
_ => false,
},
Error::<T>::UnauthorizedEdit
);
Self::do_remove_fee(fee_id)?;
Ok(())
}
#[pallet::call_index(3)]
#[pallet::weight(T::WeightInfo::charge_fee(T::MaxPoolFeesPerBucket::get()))]
pub fn charge_fee(
origin: OriginFor<T>,
fee_id: T::FeeId,
amount: T::Balance,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(!amount.is_zero(), Error::<T>::NothingCharged);
let (pool_id, pending, fee_count) = Self::mutate_active_fee(fee_id, |fee| {
ensure!(
fee.destination == who,
DispatchError::from(Error::<T>::UnauthorizedCharge)
);
match fee.amounts.fee_type {
PoolFeeType::ChargedUpTo { .. } => {
fee.amounts.pending.ensure_add_assign(amount)?;
Ok(fee.amounts.pending)
}
_ => Err(DispatchError::from(Error::<T>::CannotBeCharged)),
}
})?;
Self::deposit_event(Event::<T>::Charged {
pool_id,
fee_id,
amount,
pending,
});
Ok(Some(T::WeightInfo::charge_fee(fee_count)).into())
}
#[pallet::call_index(4)]
#[pallet::weight(T::WeightInfo::uncharge_fee(T::MaxPoolFeesPerBucket::get()))]
pub fn uncharge_fee(
origin: OriginFor<T>,
fee_id: T::FeeId,
amount: T::Balance,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(!amount.is_zero(), Error::<T>::NothingUncharged);
let (pool_id, pending, fee_count) = Self::mutate_active_fee(fee_id, |fee| {
ensure!(
fee.destination == who,
DispatchError::from(Error::<T>::UnauthorizedCharge)
);
match fee.amounts.fee_type {
PoolFeeType::ChargedUpTo { .. } => {
fee.amounts.pending.ensure_sub_assign(amount)?;
Ok(fee.amounts.pending)
}
_ => Err(DispatchError::from(Error::<T>::CannotBeCharged)),
}
})?;
Self::deposit_event(Event::<T>::Uncharged {
pool_id,
fee_id,
amount,
pending,
});
Ok(Some(T::WeightInfo::uncharge_fee(fee_count)).into())
}
#[pallet::call_index(5)]
#[pallet::weight(T::WeightInfo::update_portfolio_valuation(T::MaxPoolFeesPerBucket::get()))]
pub fn update_portfolio_valuation(
origin: OriginFor<T>,
pool_id: T::PoolId,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
ensure!(
T::PoolReserve::pool_exists(pool_id),
Error::<T>::PoolNotFound
);
let (_, count) =
Self::update_portfolio_valuation_for_pool(pool_id, &mut T::Balance::zero())?;
Ok(Some(T::WeightInfo::update_portfolio_valuation(count)).into())
}
}
impl<T: Config> Pallet<T> {
pub fn account_id() -> T::AccountId {
T::PalletId::get().into_account_truncating()
}
pub fn get_active_fee(fee_id: T::FeeId) -> Result<PoolFeeOf<T>, DispatchError> {
Ok(FeeIdsToPoolBucket::<T>::get(fee_id)
.and_then(|(pool_id, bucket)| {
ActiveFees::<T>::get(pool_id, bucket)
.into_iter()
.find(|fee| fee.id == fee_id)
})
.ok_or(Error::<T>::FeeNotFound)?)
}
fn mutate_active_fee(
fee_id: T::FeeId,
mut f: impl FnMut(&mut PoolFeeOf<T>) -> Result<T::Balance, DispatchError>,
) -> Result<(T::PoolId, T::Balance, u32), DispatchError> {
let (pool_id, bucket) =
FeeIdsToPoolBucket::<T>::get(fee_id).ok_or(Error::<T>::FeeNotFound)?;
ActiveFees::<T>::mutate(pool_id, bucket, |fees| {
let pos = fees
.iter()
.position(|fee| fee.id == fee_id)
.ok_or(Error::<T>::FeeNotFound)?;
if let Some(fee) = fees.get_mut(pos) {
Ok((pool_id, f(fee)?, fees.len().saturated_into()))
} else {
Ok((pool_id, T::Balance::zero(), fees.len().saturated_into()))
}
})
}
pub(crate) fn generate_fee_id() -> Result<T::FeeId, ArithmeticError> {
LastFeeId::<T>::try_mutate(|last_fee_id| {
last_fee_id.ensure_add_assign(One::one())?;
Ok(*last_fee_id)
})
}
fn get_released_change(
pool_id: T::PoolId,
change_id: T::Hash,
) -> Result<Change<T>, DispatchError> {
T::ChangeGuard::released(pool_id, change_id)?
.try_into()
.map_err(|_| Error::<T>::ChangeIdNotPoolFees.into())
}
pub(crate) fn pay_active_fees(
pool_id: T::PoolId,
bucket: PoolFeeBucket,
) -> Result<(), DispatchError> {
let pool_currency =
T::PoolReserve::currency_for(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ActiveFees::<T>::mutate(pool_id, bucket, |fees| {
for fee in fees.iter_mut() {
if !fee.amounts.disbursement.is_zero() {
T::Tokens::transfer(
pool_currency,
&T::PalletId::get().into_account_truncating(),
&fee.destination,
fee.amounts.disbursement,
Preservation::Expendable,
)?;
Self::deposit_event(Event::<T>::Paid {
pool_id,
fee_id: fee.id,
amount: fee.amounts.disbursement,
destination: fee.destination.clone(),
});
}
fee.amounts.disbursement = T::Balance::zero();
}
Ok(())
})
}
pub(crate) fn update_active_fees(
pool_id: T::PoolId,
bucket: PoolFeeBucket,
reserve: &mut T::Balance,
assets_under_management: T::Balance,
epoch_duration: Seconds,
) -> Result<T::Balance, DispatchError> {
ActiveFees::<T>::mutate(pool_id, bucket, |fees| {
for fee in fees.iter_mut() {
let limit = fee.amounts.limit();
let epoch_amount = <PoolFeeAmount<
<T as Config>::Balance,
<T as Config>::Rate,
> as FeeAmountProration<T::Balance, T::Rate, Seconds>>::saturated_prorated_amount(
limit,
assets_under_management,
epoch_duration,
);
let fee_amount = match fee.amounts.payable {
PayableFeeAmount::UpTo(payable) => {
let payable_amount = payable.ensure_add(epoch_amount)?;
fee.amounts.payable = PayableFeeAmount::UpTo(payable_amount);
Ok(fee.amounts.pending.min(payable_amount))
}
PayableFeeAmount::AllPending => {
fee.amounts.pending.ensure_add_assign(epoch_amount)?;
Ok(fee.amounts.pending)
}
}
.map_err(|e: DispatchError| e)?;
let disbursement = fee_amount.min(*reserve);
reserve.ensure_sub_assign(disbursement)?;
fee.amounts.pending.ensure_sub_assign(disbursement)?;
fee.amounts.disbursement.ensure_add_assign(disbursement)?;
if let PayableFeeAmount::UpTo(payable) = fee.amounts.payable {
fee.amounts.payable =
PayableFeeAmount::UpTo(payable.ensure_sub(disbursement)?)
};
if let PoolFeeType::Fixed { .. } = fee.amounts.fee_type {
Self::deposit_event(Event::<T>::Accrued {
pool_id,
fee_id: fee.id,
pending: fee.amounts.pending,
disbursement: fee.amounts.disbursement,
});
}
}
Ok::<(), DispatchError>(())
})?;
Ok(*reserve)
}
fn do_remove_fee(fee_id: T::FeeId) -> Result<(), DispatchError> {
FeeIdsToPoolBucket::<T>::mutate_exists(fee_id, |maybe_key| {
maybe_key
.as_ref()
.map(|(pool_id, bucket)| {
ActiveFees::<T>::mutate(pool_id, bucket, |fees| {
let pos = fees
.iter()
.position(|fee| fee.id == fee_id)
.ok_or(Error::<T>::FeeNotFound)?;
fees.remove(pos);
Ok::<(), DispatchError>(())
})?;
FeeIds::<T>::mutate(pool_id, bucket, |fee_ids| {
let pos = fee_ids
.iter()
.position(|id| id == &fee_id)
.ok_or(Error::<T>::FeeNotFound)?;
fee_ids.remove(pos);
Ok::<(T::PoolId, PoolFeeBucket), DispatchError>((*pool_id, *bucket))
})
})
.transpose()?
.map(|(pool_id, bucket)| {
Self::deposit_event(Event::<T>::Removed {
pool_id,
bucket,
fee_id,
});
Ok::<(), DispatchError>(())
});
*maybe_key = None;
Ok::<(), DispatchError>(())
})?;
Ok(())
}
pub fn update_portfolio_valuation_for_pool(
pool_id: T::PoolId,
reserve: &mut T::Balance,
) -> Result<(T::Balance, u32), DispatchError> {
let fee_nav = PortfolioValuation::<T>::get(pool_id);
let aum = AssetsUnderManagement::<T>::get(pool_id);
let time_diff = T::Time::now().saturating_sub(fee_nav.last_updated());
for bucket in PoolFeeBucket::iter() {
Self::update_active_fees(pool_id, bucket, reserve, aum, time_diff)?;
}
let values = PoolFeeBucket::iter()
.flat_map(|bucket| {
let fees = ActiveFees::<T>::get(pool_id, bucket);
fees.into_iter().map(|fee| (fee.id, fee.amounts.pending))
})
.collect::<Vec<_>>();
let portfolio =
portfolio::PortfolioValuation::from_values(T::Time::now(), values.clone())?;
let valuation = portfolio.value();
PortfolioValuation::<T>::insert(pool_id, portfolio);
Self::deposit_event(Event::<T>::PortfolioValuationUpdated {
pool_id,
valuation,
update_type: PortfolioValuationUpdateType::Exact,
});
Ok((valuation, values.len().saturated_into()))
}
fn add_fee_with_id(
pool_id: T::PoolId,
fee_id: T::FeeId,
bucket: PoolFeeBucket,
fee: PoolFeeInfoOf<T>,
) -> Result<u32, DispatchError> {
ensure!(
!FeeIdsToPoolBucket::<T>::contains_key(fee_id),
Error::<T>::FeeIdAlreadyExists
);
FeeIdsToPoolBucket::<T>::insert(fee_id, (pool_id, bucket));
FeeIds::<T>::mutate(pool_id, bucket, |list| list.try_push(fee_id))
.map_err(|_| Error::<T>::MaxPoolFeesPerBucket)?;
let fee_count = ActiveFees::<T>::mutate(pool_id, bucket, |list| {
list.try_push(PoolFeeOf::<T>::from_info(fee.clone(), fee_id))
.map_err(|_| Error::<T>::MaxPoolFeesPerBucket)?;
Ok::<usize, Error<T>>(list.len())
})?;
Self::deposit_event(Event::<T>::Added {
pool_id,
bucket,
fee,
fee_id,
});
Ok(fee_count.saturated_into())
}
pub fn get_pool_fees(
pool_id: T::PoolId,
) -> PoolFeesList<T::FeeId, T::AccountId, T::Balance, T::Rate> {
PoolFeeBucket::iter()
.map(|bucket| PoolFeesOfBucket {
bucket,
fees: ActiveFees::<T>::get(pool_id, bucket).into_inner(),
})
.collect()
}
}
impl<T: Config> PoolFeesMutate for Pallet<T> {
type FeeInfo = PoolFeeInfoOf<T>;
type PoolId = T::PoolId;
fn add_fee(
pool_id: Self::PoolId,
bucket: PoolFeeBucket,
fee: Self::FeeInfo,
) -> Result<(), DispatchError> {
let fee_id = Self::generate_fee_id()?;
Self::add_fee_with_id(pool_id, fee_id, bucket, fee).map(|_| ())
}
}
impl<T: Config> PoolFeesInspect for Pallet<T> {
type PoolId = T::PoolId;
fn get_max_fee_count() -> u32 {
T::MaxFeesPerPool::get()
}
fn get_max_fees_per_bucket() -> u32 {
T::MaxPoolFeesPerBucket::get()
}
fn get_pool_fee_bucket_count(pool: Self::PoolId, bucket: PoolFeeBucket) -> u32 {
ActiveFees::<T>::get(pool, bucket).len().saturated_into()
}
fn get_pool_fee_count(pool: Self::PoolId) -> u32 {
PoolFeeBucket::iter().fold(0u32, |count, bucket| {
count.saturating_add(Self::get_pool_fee_bucket_count(pool, bucket))
})
}
}
impl<T: Config> EpochTransitionHook for Pallet<T> {
type Balance = T::Balance;
type Error = DispatchError;
type PoolId = T::PoolId;
type Time = Seconds;
fn on_closing_mutate_reserve(
pool_id: Self::PoolId,
assets_under_management: Self::Balance,
reserve: &mut Self::Balance,
) -> Result<(), Self::Error> {
let res_pre_fees = *reserve;
Self::update_portfolio_valuation_for_pool(pool_id, reserve)?;
AssetsUnderManagement::<T>::insert(pool_id, assets_under_management);
let total_fee_amount = res_pre_fees.saturating_sub(*reserve);
if !total_fee_amount.is_zero() {
let pool_currency =
T::PoolReserve::currency_for(pool_id).ok_or(Error::<T>::PoolNotFound)?;
let pool_account = T::PoolReserve::account_for(pool_id);
T::Tokens::transfer(
pool_currency,
&pool_account,
&T::PalletId::get().into_account_truncating(),
total_fee_amount,
Preservation::Expendable,
)?;
}
Ok(())
}
fn on_execution_pre_fulfillments(pool_id: Self::PoolId) -> Result<(), Self::Error> {
Self::pay_active_fees(pool_id, PoolFeeBucket::Top)?;
Ok(())
}
}
impl<T: Config> PoolNAV<T::PoolId, T::Balance> for Pallet<T> {
type ClassId = ();
type RuntimeOrigin = T::RuntimeOrigin;
fn nav(pool_id: T::PoolId) -> Option<(T::Balance, Seconds)> {
let portfolio = PortfolioValuation::<T>::get(pool_id);
Some((portfolio.value(), portfolio.last_updated()))
}
fn update_nav(pool_id: T::PoolId) -> Result<T::Balance, DispatchError> {
Ok(Self::update_portfolio_valuation_for_pool(pool_id, &mut T::Balance::zero())?.0)
}
fn initialise(_: OriginFor<T>, _: T::PoolId, _: Self::ClassId) -> DispatchResult {
Ok(())
}
}
#[cfg(feature = "runtime-benchmarks")]
impl<T: Config> PoolFeesBenchmarkHelper for Pallet<T> {
type PoolFeeInfo = PoolFeeInfoOf<T>;
type PoolId = T::PoolId;
fn get_pool_fee_infos(n: u32) -> Vec<Self::PoolFeeInfo> {
(0..n).map(|_| Self::get_default_fixed_fee_info()).collect()
}
fn add_pool_fees(pool_id: Self::PoolId, bucket: PoolFeeBucket, n: u32) {
let fee_infos = Self::get_pool_fee_infos(n);
for fee_info in fee_infos {
frame_support::assert_ok!(Self::add_fee(pool_id, bucket, fee_info));
}
}
fn get_default_fixed_fee_info() -> Self::PoolFeeInfo {
let destination = frame_benchmarking::account::<T::AccountId>(
"fee destination",
benchmarking::ACCOUNT_INDEX,
benchmarking::ACCOUNT_SEED,
);
let editor = frame_benchmarking::account::<T::AccountId>(
"fee editor",
benchmarking::ACCOUNT_INDEX + 1,
benchmarking::ACCOUNT_SEED + 1,
);
PoolFeeInfoOf::<T> {
destination,
editor: PoolFeeEditor::Account(editor),
fee_type: PoolFeeType::<T::Balance, T::Rate>::Fixed {
limit: PoolFeeAmount::<T::Balance, T::Rate>::ShareOfPortfolioValuation(
T::Rate::saturating_from_rational(1, 100),
),
},
}
}
fn get_default_charged_fee_info() -> Self::PoolFeeInfo {
let destination = frame_benchmarking::account::<T::AccountId>(
"fee destination",
benchmarking::ACCOUNT_INDEX,
benchmarking::ACCOUNT_SEED,
);
let editor = frame_benchmarking::account::<T::AccountId>(
"fee editor",
benchmarking::ACCOUNT_INDEX + 1,
benchmarking::ACCOUNT_SEED + 1,
);
PoolFeeInfoOf::<T> {
destination,
editor: PoolFeeEditor::Account(editor),
fee_type: PoolFeeType::<T::Balance, T::Rate>::ChargedUpTo {
limit: PoolFeeAmount::<T::Balance, T::Rate>::AmountPerSecond(1000u64.into()),
},
}
}
}
}