@@ -7,6 +7,9 @@ use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
77
88use crate :: fee_market:: {
99 calculate_next_base_gas_price,
10+ compute_fee_actual,
11+ compute_fee_proposal,
12+ compute_fee_target,
1013 get_min_gas_price_for_height,
1114 MIN_GAS_PRICE_INCREASE_DENOMINATOR ,
1215} ;
@@ -214,3 +217,144 @@ fn test_calculate_with_price_close_to_minimum() {
214217 // When price is close to minimum, should cap at min_gas_price to avoid overshooting
215218 assert_eq ! ( result, min_gas_price) ;
216219}
220+
221+ #[ test]
222+ fn test_compute_fee_actual_with_10_identical_values ( ) {
223+ let proposals: Vec < GasPrice > = vec ! [ GasPrice ( 1000 ) ; 10 ] ;
224+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , Some ( GasPrice ( 1000 ) ) ) ;
225+ }
226+
227+ #[ test]
228+ fn test_compute_fee_actual_with_ascending_values ( ) {
229+ let proposals: Vec < GasPrice > = ( 1 ..=10 ) . map ( |i| GasPrice ( i * 100 ) ) . collect ( ) ;
230+ // Sorted: 100,200,300,400,500,600,700,800,900,1000. Median = (500+600)/2 = 550.
231+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , Some ( GasPrice ( 550 ) ) ) ;
232+ }
233+
234+ #[ test]
235+ fn test_compute_fee_actual_with_descending_values ( ) {
236+ let proposals: Vec < GasPrice > = ( 1 ..=10 ) . rev ( ) . map ( |i| GasPrice ( i * 100 ) ) . collect ( ) ;
237+ // Same sorted order, same median.
238+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , Some ( GasPrice ( 550 ) ) ) ;
239+ }
240+
241+ #[ test]
242+ fn test_compute_fee_actual_with_outliers ( ) {
243+ let mut proposals: Vec < GasPrice > = vec ! [ GasPrice ( 100 ) ; 8 ] ;
244+ proposals. push ( GasPrice ( 1 ) ) ; // Low outlier
245+ proposals. push ( GasPrice ( 1_000_000 ) ) ; // High outlier
246+ // Sorted: 1,100,100,100,100,100,100,100,100,1000000. Median = (100+100)/2 = 100.
247+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , Some ( GasPrice ( 100 ) ) ) ;
248+ }
249+
250+ #[ test]
251+ fn test_compute_fee_actual_fewer_than_window_returns_none ( ) {
252+ let proposals: Vec < GasPrice > = vec ! [ GasPrice ( 100 ) ; 9 ] ;
253+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , None ) ;
254+ }
255+
256+ #[ test]
257+ fn test_compute_fee_actual_empty_returns_none ( ) {
258+ assert_eq ! ( compute_fee_actual( & [ ] , 10 ) , None ) ;
259+ }
260+
261+ #[ test]
262+ fn test_compute_fee_actual_custom_window_size ( ) {
263+ let proposals: Vec < GasPrice > = vec ! [ GasPrice ( 200 ) ; 4 ] ;
264+ assert_eq ! ( compute_fee_actual( & proposals, 4 ) , Some ( GasPrice ( 200 ) ) ) ;
265+ assert_eq ! ( compute_fee_actual( & proposals, 5 ) , None ) ;
266+ }
267+
268+ #[ test]
269+ fn test_compute_fee_actual_zero_median_returns_none ( ) {
270+ // All zeros → median is 0 → returns None (triggers l2_gas_price fallback).
271+ let proposals: Vec < GasPrice > = vec ! [ GasPrice ( 0 ) ; 10 ] ;
272+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , None ) ;
273+ }
274+
275+ #[ test]
276+ fn test_compute_fee_actual_uses_only_last_window_entries ( ) {
277+ // 12 entries: first 2 are outliers, last 10 are 500.
278+ let mut proposals: Vec < GasPrice > = vec ! [ GasPrice ( 999_999 ) ; 2 ] ;
279+ proposals. extend ( vec ! [ GasPrice ( 500 ) ; 10 ] ) ;
280+ // With window_size=10, the outliers should be ignored (only last 10 used).
281+ assert_eq ! ( compute_fee_actual( & proposals, 10 ) , Some ( GasPrice ( 500 ) ) ) ;
282+ }
283+
284+ #[ test]
285+ fn test_compute_fee_target_normal ( ) {
286+ // Target: $3e-9/gas = 3_000_000_000 atto-USD. STRK at $0.50 = 500_000_000_000_000_000.
287+ // floor = 3_000_000_000 * 10^18 / 500_000_000_000_000_000 = 6_000_000_000.
288+ let target = compute_fee_target ( 3_000_000_000 , 500_000_000_000_000_000 , 0 , u128:: MAX ) ;
289+ assert_eq ! ( target, GasPrice ( 6_000_000_000 ) ) ;
290+ }
291+
292+ #[ test]
293+ fn test_compute_fee_target_clamp_min ( ) {
294+ let target = compute_fee_target ( 1 , 10u128 . pow ( 18 ) , 100 , u128:: MAX ) ;
295+ // floor = 1 * 10^18 / 10^18 = 1, but clamped to min 100.
296+ assert_eq ! ( target, GasPrice ( 100 ) ) ;
297+ }
298+
299+ #[ test]
300+ fn test_compute_fee_target_clamp_max ( ) {
301+ // Very low STRK price → very high floor, clamped to max.
302+ let target = compute_fee_target ( 10u128 . pow ( 18 ) , 1 , 0 , 1000 ) ;
303+ assert_eq ! ( target, GasPrice ( 1000 ) ) ;
304+ }
305+
306+ #[ test]
307+ fn test_compute_fee_target_zero_rate ( ) {
308+ let target = compute_fee_target ( 100 , 0 , 0 , 999 ) ;
309+ assert_eq ! ( target, GasPrice ( 999 ) ) ;
310+ }
311+
312+ #[ test]
313+ fn test_compute_fee_proposal_oracle_failure_freezes ( ) {
314+ let proposal = compute_fee_proposal ( None , GasPrice ( 1000 ) , 2 ) ;
315+ assert_eq ! ( proposal, GasPrice ( 1000 ) ) ;
316+ }
317+
318+ #[ test]
319+ fn test_compute_fee_proposal_target_above_actual ( ) {
320+ // fee_target=2000, fee_actual=1000, margin=2ppt. Upper bound = 1000*1002/1000 = 1002.
321+ let proposal = compute_fee_proposal ( Some ( GasPrice ( 2000 ) ) , GasPrice ( 1000 ) , 2 ) ;
322+ assert_eq ! ( proposal, GasPrice ( 1002 ) ) ;
323+ }
324+
325+ #[ test]
326+ fn test_compute_fee_proposal_target_below_actual ( ) {
327+ // fee_target=500, fee_actual=1000, margin=2ppt. Lower bound = 1000*1000/1002 = 998.
328+ let proposal = compute_fee_proposal ( Some ( GasPrice ( 500 ) ) , GasPrice ( 1000 ) , 2 ) ;
329+ assert_eq ! ( proposal, GasPrice ( 998 ) ) ;
330+ }
331+
332+ #[ test]
333+ fn test_compute_fee_proposal_target_within_bounds ( ) {
334+ // fee_target=1001, fee_actual=1000, margin=2ppt. 1001 is within [998, 1002].
335+ let proposal = compute_fee_proposal ( Some ( GasPrice ( 1001 ) ) , GasPrice ( 1000 ) , 2 ) ;
336+ assert_eq ! ( proposal, GasPrice ( 1001 ) ) ;
337+ }
338+
339+ #[ test]
340+ fn test_compute_fee_proposal_fee_actual_zero_clamps_to_zero ( ) {
341+ // When fee_actual=0, both bounds are 0, so fee_proposal is always 0.
342+ let proposal = compute_fee_proposal ( Some ( GasPrice ( 1000 ) ) , GasPrice ( 0 ) , 2 ) ;
343+ assert_eq ! ( proposal, GasPrice ( 0 ) ) ;
344+ }
345+
346+ #[ test]
347+ fn test_compute_fee_actual_window_size_below_minimum_returns_none ( ) {
348+ let proposals: Vec < GasPrice > = vec ! [ GasPrice ( 100 ) ; 10 ] ;
349+ assert_eq ! ( compute_fee_actual( & proposals, 0 ) , None ) ;
350+ assert_eq ! ( compute_fee_actual( & proposals, 1 ) , None ) ;
351+ }
352+
353+ #[ test]
354+ fn test_compute_fee_proposal_custom_margin ( ) {
355+ // margin=10ppt (1%). fee_actual=10000. Upper=10000*1010/1000=10100. Lower=10000*1000/1010=9900.
356+ let proposal_up = compute_fee_proposal ( Some ( GasPrice ( 99999 ) ) , GasPrice ( 10000 ) , 10 ) ;
357+ assert_eq ! ( proposal_up, GasPrice ( 10100 ) ) ;
358+ let proposal_down = compute_fee_proposal ( Some ( GasPrice ( 1 ) ) , GasPrice ( 10000 ) , 10 ) ;
359+ assert_eq ! ( proposal_down, GasPrice ( 9900 ) ) ;
360+ }
0 commit comments