templar_common/
fee.rs

1use near_sdk::{json_types::U64, near};
2
3use crate::{
4    asset::{AssetClass, FungibleAssetAmount},
5    number::Decimal,
6};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9#[near(serializers = [json, borsh])]
10pub enum Fee<T: AssetClass> {
11    Flat(FungibleAssetAmount<T>),
12    Proportional(Decimal),
13}
14
15impl<T: AssetClass> Fee<T> {
16    pub fn zero() -> Self {
17        Self::Flat(FungibleAssetAmount::zero())
18    }
19
20    pub fn of(&self, amount: FungibleAssetAmount<T>) -> Option<FungibleAssetAmount<T>> {
21        match self {
22            Fee::Flat(f) => Some(*f),
23            Fee::Proportional(factor) => (factor * u128::from(amount))
24                .to_u128_ceil()
25                .map(FungibleAssetAmount::new),
26        }
27    }
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31#[near(serializers = [json, borsh])]
32pub struct TimeBasedFee<T: AssetClass> {
33    pub fee: Fee<T>,
34    pub duration: U64,
35    pub behavior: TimeBasedFeeFunction,
36}
37
38impl<T: AssetClass> TimeBasedFee<T> {
39    pub fn zero() -> Self {
40        Self {
41            fee: Fee::Flat(0.into()),
42            duration: 0.into(),
43            behavior: TimeBasedFeeFunction::Fixed,
44        }
45    }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
49#[near(serializers = [json, borsh])]
50pub enum TimeBasedFeeFunction {
51    Fixed,
52    Linear,
53}
54
55impl<T: AssetClass> TimeBasedFee<T> {
56    pub fn of(
57        &self,
58        amount: FungibleAssetAmount<T>,
59        duration: u64,
60    ) -> Option<FungibleAssetAmount<T>> {
61        let base_fee = self.fee.of(amount)?;
62
63        if self.duration.0 == 0 {
64            return Some(0.into());
65        }
66
67        match self.behavior {
68            TimeBasedFeeFunction::Fixed => {
69                if duration >= self.duration.0 {
70                    Some(0.into())
71                } else {
72                    Some(base_fee)
73                }
74            }
75            TimeBasedFeeFunction::Linear => {
76                (Decimal::from(self.duration.0.saturating_sub(duration)) * u128::from(base_fee)
77                    / Decimal::from(self.duration.0))
78                .to_u128_ceil()
79                .map(FungibleAssetAmount::new)
80            }
81        }
82    }
83}