hypercall_api/handlers/
gas_provider.rs1use axum::{
2 extract::{Path, State},
3 response::IntoResponse,
4};
5use serde::Serialize;
6use utoipa::ToSchema;
7
8use super::AppState;
9use crate::error::ApiError;
10use crate::gas_provider::{format_wei_as_gwei_string, GasEstimateResult, GasEstimateTier};
11use crate::sonic_json::SonicJson;
12
13#[derive(Debug, Serialize, ToSchema)]
14pub struct GasProviderFeeTierResponse {
15 #[schema(value_type = String, example = "1.2")]
16 #[serde(rename = "suggestedMaxPriorityFeePerGas")]
17 pub suggested_max_priority_fee_per_gas: String,
18 #[schema(value_type = String, example = "120")]
19 #[serde(rename = "suggestedMaxFeePerGas")]
20 pub suggested_max_fee_per_gas: String,
21}
22
23impl GasProviderFeeTierResponse {
24 fn from_tier(tier: GasEstimateTier) -> Result<Self, String> {
25 Ok(Self {
26 suggested_max_priority_fee_per_gas: format_wei_as_gwei_string(
27 tier.max_priority_fee_wei,
28 )?,
29 suggested_max_fee_per_gas: format_wei_as_gwei_string(tier.max_fee_wei)?,
30 })
31 }
32}
33
34#[derive(Debug, Serialize, ToSchema)]
35pub struct GasProviderResponse {
36 pub slow: GasProviderFeeTierResponse,
37 pub medium: GasProviderFeeTierResponse,
38 pub fast: GasProviderFeeTierResponse,
39 #[serde(rename = "superFast")]
40 pub super_fast: GasProviderFeeTierResponse,
41}
42
43impl GasProviderResponse {
44 fn from_estimates(estimates: GasEstimateResult) -> Result<Self, String> {
45 Ok(Self {
46 slow: GasProviderFeeTierResponse::from_tier(estimates.slow)?,
47 medium: GasProviderFeeTierResponse::from_tier(estimates.medium)?,
48 fast: GasProviderFeeTierResponse::from_tier(estimates.fast)?,
49 super_fast: GasProviderFeeTierResponse::from_tier(estimates.super_fast)?,
50 })
51 }
52}
53
54#[derive(Debug, Serialize, ToSchema)]
55pub struct GasProviderErrorResponse {
56 #[schema(value_type = String, example = "not_found")]
57 pub error: String,
58 #[schema(value_type = String, example = "Gas provider only supports chain_id 1")]
59 pub message: String,
60}
61
62#[utoipa::path(
64 get,
65 path = "/gas-provider/{chain_id}",
66 params(("chain_id" = u64, Path, description = "Startup-resolved chain id supported by this gas provider")),
67 responses(
68 (status = 200, description = "Gas fee estimates", body = GasProviderResponse),
69 (status = 404, description = "Unsupported chain id", body = GasProviderErrorResponse),
70 (status = 500, description = "Upstream RPC fee estimation failed", body = GasProviderErrorResponse)
71 ),
72 tag = "Markets"
73)]
74pub async fn get_gas_provider(
75 State(state): State<AppState>,
76 Path(chain_id): Path<u64>,
77) -> Result<impl IntoResponse, ApiError> {
78 let supported_chain_id = state.gas_provider_service.chain_id();
79 if chain_id != supported_chain_id {
80 return Err(ApiError::not_found(format!(
81 "Gas provider only supports chain_id {}",
82 supported_chain_id
83 )));
84 }
85
86 let estimates = state
87 .gas_provider_service
88 .estimate_fees()
89 .await
90 .map_err(|err| ApiError::internal_error(format!("Failed to estimate gas fees: {err}")))?;
91 let response = GasProviderResponse::from_estimates(estimates)
92 .map_err(|err| ApiError::internal_error(format!("Failed to serialize gas fees: {err}")))?;
93
94 Ok(SonicJson(response))
95}