cardano_serialization_lib/
fees.rs1use super::*;
2use crate::rational::Rational;
3
4#[wasm_bindgen]
5#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
6pub struct LinearFee {
7 constant: Coin,
8 coefficient: Coin,
9}
10
11#[wasm_bindgen]
12impl LinearFee {
13 pub fn constant(&self) -> Coin {
14 self.constant
15 }
16
17 pub fn coefficient(&self) -> Coin {
18 self.coefficient
19 }
20
21 pub fn new(coefficient: &Coin, constant: &Coin) -> Self {
22 Self {
23 constant: constant.clone(),
24 coefficient: coefficient.clone(),
25 }
26 }
27}
28
29#[wasm_bindgen]
30pub fn min_fee(tx: &Transaction, linear_fee: &LinearFee) -> Result<Coin, JsError> {
31 min_fee_for_size(tx.to_bytes().len(), linear_fee)
32}
33
34pub fn min_fee_for_size(size: usize, linear_fee: &LinearFee) -> Result<Coin, JsError> {
35 BigNum::from(size)
36 .checked_mul(&linear_fee.coefficient())?
37 .checked_add(&linear_fee.constant())
38}
39
40#[wasm_bindgen]
41pub fn calculate_ex_units_ceil_cost(
42 ex_units: &ExUnits,
43 ex_unit_prices: &ExUnitPrices,
44) -> Result<Coin, JsError> {
45 let mem_price: Rational = ex_unit_prices.mem_price().into();
46 let steps_price: Rational = ex_unit_prices.step_price().into();
47 let mem_ratio = mem_price.mul_bignum(&ex_units.mem())?;
48 let steps_ratio = steps_price.mul_bignum(&ex_units.steps())?;
49 let total = mem_ratio.add(&steps_ratio);
50 total.to_bignum_ceil()
51}
52
53#[wasm_bindgen]
54pub fn min_script_fee(tx: &Transaction, ex_unit_prices: &ExUnitPrices) -> Result<Coin, JsError> {
55 if let Some(redeemers) = &tx.witness_set.redeemers {
56 let total_ex_units: ExUnits = redeemers.total_ex_units()?;
57 return calculate_ex_units_ceil_cost(&total_ex_units, ex_unit_prices);
58 }
59 Ok(Coin::zero())
60}
61
62#[wasm_bindgen]
63pub fn min_ref_script_fee(
64 total_ref_scripts_size: usize,
65 ref_script_coins_per_byte: &UnitInterval,
66) -> Result<Coin, JsError> {
67 let multiplier = Rational::new(BigInt::from(12), BigInt::from(10)); let size_increment: usize = 25_600; let ref_multiplier: Rational = ref_script_coins_per_byte.into();
70 let total_fee = tier_ref_script_fee(
71 multiplier,
72 size_increment,
73 ref_multiplier,
74 total_ref_scripts_size,
75 )?;
76
77 Ok(total_fee)
78}
79
80fn tier_ref_script_fee(
81 multiplier: Rational,
82 size_increment: usize,
83 base_fee: Rational,
84 total_size: usize,
85) -> Result<BigNum, JsError> {
86 if multiplier.is_negative_or_zero() || size_increment == 0 {
87 return Err(JsError::from_str(
88 "Size increment and multiplier must be positive",
89 ));
90 }
91
92 let full_tiers = (total_size / size_increment) as u32;
93 let partial_tier_size = total_size % size_increment;
94 let tier_price = base_fee.mul_usize(size_increment);
95
96 let mut acc = Rational::zero();
97
98 if full_tiers > 0 {
99 let progression_enumerator = Rational::one().sub(&multiplier.pow(full_tiers));
100 let progression_denominator = Rational::one().sub(&multiplier);
101 let tier_progression_sum = progression_enumerator.div_ratio(&progression_denominator);
102 acc = acc.add(&tier_price.mul_ratio(&tier_progression_sum));
103 }
104
105 if partial_tier_size > 0 {
107 let last_tier_price = base_fee.mul_ratio(&multiplier.pow(full_tiers));
108 let partial_tier_fee = last_tier_price.mul_usize(partial_tier_size);
109 acc = acc.add(&partial_tier_fee);
110 }
111
112 acc.to_bignum_floor()
113}