ant_node/payment/
pricing.rs1use evmlib::common::Amount;
28
29const PRICING_DIVISOR: u128 = 6000;
31
32const DIVISOR_SQUARED: u128 = PRICING_DIVISOR * PRICING_DIVISOR;
34
35const PRICE_BASELINE_WEI: u128 = 3_906_250_000_000_000;
39
40const PRICE_COEFFICIENT_WEI: u128 = 35_156_250_000_000_000;
44
45#[must_use]
52pub fn calculate_price(close_records_stored: usize) -> Amount {
53 let n = Amount::from(close_records_stored);
54 let n_squared = n.saturating_mul(n);
55 let quadratic_wei = n_squared.saturating_mul(Amount::from(PRICE_COEFFICIENT_WEI))
56 / Amount::from(DIVISOR_SQUARED);
57 Amount::from(PRICE_BASELINE_WEI).saturating_add(quadratic_wei)
58}
59
60#[cfg(test)]
61#[allow(clippy::unwrap_used, clippy::expect_used)]
62mod tests {
63 use super::*;
64
65 const WEI_PER_TOKEN: u128 = 1_000_000_000_000_000_000;
67
68 fn expected_price(n: u64) -> Amount {
70 let n_amt = Amount::from(n);
71 let quad =
72 n_amt * n_amt * Amount::from(PRICE_COEFFICIENT_WEI) / Amount::from(DIVISOR_SQUARED);
73 Amount::from(PRICE_BASELINE_WEI) + quad
74 }
75
76 #[test]
77 fn test_zero_records_gets_baseline() {
78 let price = calculate_price(0);
80 assert_eq!(price, Amount::from(PRICE_BASELINE_WEI));
81 }
82
83 #[test]
84 fn test_baseline_is_nonzero_spam_barrier() {
85 assert!(calculate_price(0) > Amount::ZERO);
88 assert!(calculate_price(1) > calculate_price(0));
89 }
90
91 #[test]
92 fn test_one_record_above_baseline() {
93 let price = calculate_price(1);
94 assert_eq!(price, expected_price(1));
95 assert!(price > Amount::from(PRICE_BASELINE_WEI));
96 }
97
98 #[test]
99 fn test_at_divisor_is_baseline_plus_k() {
100 let price = calculate_price(6000);
103 let expected = Amount::from(PRICE_BASELINE_WEI + PRICE_COEFFICIENT_WEI);
104 assert_eq!(price, expected);
105 }
106
107 #[test]
108 fn test_double_divisor_is_baseline_plus_four_k() {
109 let price = calculate_price(12000);
111 let expected = Amount::from(PRICE_BASELINE_WEI + 4 * PRICE_COEFFICIENT_WEI);
112 assert_eq!(price, expected);
113 }
114
115 #[test]
116 fn test_triple_divisor_is_baseline_plus_nine_k() {
117 let price = calculate_price(18000);
119 let expected = Amount::from(PRICE_BASELINE_WEI + 9 * PRICE_COEFFICIENT_WEI);
120 assert_eq!(price, expected);
121 }
122
123 #[test]
124 fn test_smooth_pricing_no_staircase() {
125 let price_6k = calculate_price(6000);
127 let price_11k = calculate_price(11999);
128 assert!(
129 price_11k > price_6k,
130 "11999 records ({price_11k}) should cost more than 6000 ({price_6k})"
131 );
132 }
133
134 #[test]
135 fn test_price_increases_with_records() {
136 let price_low = calculate_price(6000);
137 let price_mid = calculate_price(12000);
138 let price_high = calculate_price(18000);
139 assert!(price_mid > price_low);
140 assert!(price_high > price_mid);
141 }
142
143 #[test]
144 fn test_price_increases_monotonically() {
145 let mut prev_price = Amount::ZERO;
146 for records in (0..60000).step_by(100) {
147 let price = calculate_price(records);
148 assert!(
149 price >= prev_price,
150 "Price at {records} records ({price}) should be >= previous ({prev_price})"
151 );
152 prev_price = price;
153 }
154 }
155
156 #[test]
157 fn test_large_value_no_overflow() {
158 let price = calculate_price(usize::MAX);
159 assert!(price > Amount::ZERO);
160 }
161
162 #[test]
163 fn test_price_deterministic() {
164 let price1 = calculate_price(12000);
165 let price2 = calculate_price(12000);
166 assert_eq!(price1, price2);
167 }
168
169 #[test]
170 fn test_quadratic_growth_excluding_baseline() {
171 let base = Amount::from(PRICE_BASELINE_WEI);
174 let quad_6k = calculate_price(6000) - base;
175 let quad_12k = calculate_price(12000) - base;
176 let quad_24k = calculate_price(24000) - base;
177 assert_eq!(quad_12k, quad_6k * Amount::from(4u64));
178 assert_eq!(quad_24k, quad_6k * Amount::from(16u64));
179 }
180
181 #[test]
182 fn test_small_record_counts_near_baseline() {
183 let price = calculate_price(100);
185 assert_eq!(price, expected_price(100));
186 assert!(price < Amount::from(WEI_PER_TOKEN)); assert!(price > Amount::from(PRICE_BASELINE_WEI)); }
189}