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}