darkpool_client/
economics.rs1use ethers::types::U256;
4use tracing::warn;
5
6#[derive(Debug, Clone)]
7pub struct PriceData {
8 pub eth_usd: U256,
10 pub asset_usd: U256,
12 pub gas_price: U256,
13}
14
15#[derive(Debug, Clone)]
16pub struct FeeEstimate {
17 pub fee_amount: U256,
18 pub gas_limit: U256,
19 pub gas_price: U256,
20 pub premium_bps: u64,
21}
22
23#[derive(Debug, Clone)]
24pub struct FeeConfig {
25 pub premium_bps: u64,
27 pub default_gas_limit: U256,
28}
29
30impl Default for FeeConfig {
31 fn default() -> Self {
32 Self {
33 premium_bps: 1200,
34 default_gas_limit: U256::from(300_000),
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
40pub struct FeeManager {
41 pub config: FeeConfig,
42}
43
44impl FeeManager {
45 #[must_use]
46 pub fn new(config: FeeConfig) -> Self {
47 Self { config }
48 }
49
50 #[must_use]
52 pub fn calculate_fee(&self, gas_limit: U256, prices: &PriceData) -> FeeEstimate {
53 let gas_cost_wei = gas_limit * prices.gas_price;
54 let premium_multiplier = U256::from(10_000 + self.config.premium_bps);
55 let fee_with_premium = (gas_cost_wei * premium_multiplier) / U256::from(10_000);
56
57 let fee_in_asset = if prices.asset_usd > U256::zero() {
58 (fee_with_premium * prices.eth_usd) / prices.asset_usd
59 } else {
60 warn!(
61 "Asset price is zero in calculate_fee. Returning max fee to prevent \
62 incorrect fee calculation. Check price oracle."
63 );
64 U256::MAX
65 };
66
67 FeeEstimate {
68 fee_amount: fee_in_asset,
69 gas_limit,
70 gas_price: prices.gas_price,
71 premium_bps: self.config.premium_bps,
72 }
73 }
74
75 #[must_use]
77 pub fn calculate_fee_with_decimals(
78 &self,
79 gas_limit: U256,
80 prices: &PriceData,
81 asset_decimals: u8,
82 ) -> FeeEstimate {
83 let mut estimate = self.calculate_fee(gas_limit, prices);
84 if asset_decimals < 18 {
85 let decimal_diff = 18 - asset_decimals;
86 let divisor = U256::from(10u64).pow(U256::from(decimal_diff));
87 estimate.fee_amount /= divisor;
88 } else if asset_decimals > 18 {
89 let decimal_diff = asset_decimals - 18;
90 let multiplier = U256::from(10u64).pow(U256::from(decimal_diff));
91 estimate.fee_amount *= multiplier;
92 }
93
94 estimate
95 }
96}
97
98impl Default for FeeManager {
99 fn default() -> Self {
100 Self::new(FeeConfig::default())
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_fee_manager_calculate() {
110 let manager = FeeManager::new(FeeConfig {
111 premium_bps: 1200,
112 default_gas_limit: U256::from(300_000),
113 });
114
115 let prices = PriceData {
116 eth_usd: U256::from(3000_00000000u64),
117 asset_usd: U256::from(1_00000000u64),
118 gas_price: U256::from(20_000_000_000u64),
119 };
120
121 let estimate = manager.calculate_fee(U256::from(100_000), &prices);
122 assert!(estimate.fee_amount > U256::zero());
123 assert_eq!(estimate.premium_bps, 1200);
124 }
125
126 #[test]
127 fn test_fee_manager_with_decimals() {
128 let manager = FeeManager::default();
129
130 let prices = PriceData {
131 eth_usd: U256::from(3000_00000000u64),
132 asset_usd: U256::from(1_00000000u64),
133 gas_price: U256::from(20_000_000_000u64),
134 };
135
136 let estimate_18 = manager.calculate_fee(U256::from(100_000), &prices);
137 let estimate_6 = manager.calculate_fee_with_decimals(U256::from(100_000), &prices, 6);
138
139 assert!(estimate_6.fee_amount < estimate_18.fee_amount);
140 }
141
142 #[test]
143 fn test_fee_estimate() {
144 let prices = PriceData {
145 eth_usd: U256::from(3000_00000000u64),
146 asset_usd: U256::from(1_00000000u64),
147 gas_price: U256::from(20_000_000_000u64),
148 };
149
150 let gas_limit = U256::from(100_000);
151 let gas_cost = gas_limit * prices.gas_price;
152 let with_premium = (gas_cost * U256::from(11200)) / U256::from(10000);
153 assert!(with_premium > gas_cost);
154 }
155}