1use candid::Nat;
3use ethers_core::types::U256;
4use evm_rpc_canister_types::{
5 BlockTag, EvmRpcCanister, FeeHistory, FeeHistoryArgs, FeeHistoryResult, MultiFeeHistoryResult,
6 RpcServices,
7};
8use serde_bytes::ByteBuf;
9use std::ops::Add;
10
11use crate::conversions::nat_to_u256;
12
13const MIN_SUGGEST_MAX_PRIORITY_FEE_PER_GAS: u32 = 1_500_000_000;
15
16pub async fn fee_history(
30 block_count: Nat,
31 newest_block: BlockTag,
32 reward_percentiles: Option<Vec<u8>>,
33 rpc_services: RpcServices,
34 evm_rpc: EvmRpcCanister,
35) -> FeeHistory {
36 let fee_history_args: FeeHistoryArgs = FeeHistoryArgs {
37 blockCount: block_count,
38 newestBlock: newest_block,
39 rewardPercentiles: reward_percentiles.map(ByteBuf::from),
40 };
41
42 let cycles = 10_000_000_000;
43
44 match evm_rpc
45 .eth_fee_history(rpc_services, None, fee_history_args, cycles)
46 .await
47 {
48 Ok((res,)) => match res {
49 MultiFeeHistoryResult::Consistent(fee_history) => match fee_history {
50 FeeHistoryResult::Ok(fee_history) => fee_history,
51 FeeHistoryResult::Err(e) => {
52 ic_cdk::trap(format!("Error: {:?}", e).as_str());
53 }
54 },
55 MultiFeeHistoryResult::Inconsistent(_) => {
56 ic_cdk::trap("Fee history is inconsistent");
57 }
58 },
59 Err(e) => ic_cdk::trap(format!("Error: {:?}", e).as_str()),
60 }
61}
62
63pub struct FeeEstimates {
65 pub max_fee_per_gas: U256,
66 pub max_priority_fee_per_gas: U256,
67}
68
69fn median_index(length: usize) -> usize {
79 if length == 0 {
80 panic!("Cannot find a median index for an array of length zero.");
81 }
82 (length - 1) / 2
83}
84
85pub async fn estimate_transaction_fees(
93 block_count: u8,
94 rpc_services: RpcServices,
95 evm_rpc: EvmRpcCanister,
96) -> FeeEstimates {
97 let fee_history = fee_history(
104 Nat::from(block_count),
105 BlockTag::Latest,
106 Some(vec![95]),
107 rpc_services,
108 evm_rpc,
109 )
110 .await;
111
112 let median_index = median_index(block_count.into());
113
114 let base_fee_per_gas = fee_history.baseFeePerGas.last().unwrap().clone();
116
117 let mut percentile_95: Vec<Nat> = fee_history
119 .reward
120 .into_iter()
121 .flat_map(|x| x.into_iter())
122 .collect();
123 percentile_95.sort_unstable();
125 let median_reward = percentile_95
128 .get(median_index)
129 .unwrap_or(&Nat::from(0_u8))
130 .clone();
131
132 let max_priority_fee_per_gas = median_reward
133 .clone()
134 .add(base_fee_per_gas)
135 .max(Nat::from(MIN_SUGGEST_MAX_PRIORITY_FEE_PER_GAS));
136
137 FeeEstimates {
138 max_fee_per_gas: nat_to_u256(&max_priority_fee_per_gas),
139 max_priority_fee_per_gas: nat_to_u256(&median_reward),
140 }
141}