kabu_rpc_handler/handler/
pools.rs

1use crate::dto::pagination::Pagination;
2use crate::dto::pool::{MarketStats, Pool, PoolClass, PoolDetailsResponse, PoolProtocol, PoolResponse};
3use crate::dto::quote::{Filter, QuoteRequest, QuoteResponse};
4use alloy_primitives::Address;
5use axum::extract::{Path, Query, State};
6use axum::http::StatusCode;
7use axum::Json;
8use kabu_evm_utils::error_handler::internal_error;
9use kabu_rpc_state::AppState;
10use kabu_types_entities::{PoolId, PoolWrapper};
11use revm::{DatabaseCommit, DatabaseRef};
12use std::str::FromStr;
13
14/// Get latest block
15///
16/// Get the latest block header
17#[utoipa::path(
18    get,
19    path = "/pools",
20    tag = "market",
21    tags = [],
22    params(
23        Pagination, Filter
24    ),
25    responses(
26    (status = 200, description = "All available pools", body = PoolResponse),
27    )
28)]
29pub async fn pools<DB: DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static>(
30    State(app_state): State<AppState<DB>>,
31    pagination: Query<Pagination>,
32    filter: Query<Filter>,
33) -> Result<Json<PoolResponse>, (StatusCode, String)> {
34    let pools: Vec<(Address, PoolWrapper)> = app_state
35        .bc
36        .market()
37        .read()
38        .await
39        .pools()
40        .iter()
41        .filter(|(_, pool)| match &filter.protocol {
42            None => true,
43            Some(protocol) => pool.pool.get_protocol() == protocol.into(),
44        })
45        .skip(pagination.start())
46        .take(pagination.limit)
47        .map(|(address, pool)| (address.address_or_zero(), pool.clone()))
48        .collect();
49
50    let mut ret = vec![];
51    for (pool_address, pool) in pools {
52        ret.push(Pool {
53            address: pool_address,
54            fee: pool.pool.get_fee(),
55            tokens: pool.pool.get_tokens(),
56            protocol: PoolProtocol::from(pool.pool.get_protocol()),
57            pool_class: PoolClass::from(pool.get_class()),
58        });
59    }
60    let total_pools = app_state
61        .bc
62        .market()
63        .read()
64        .await
65        .pools()
66        .iter()
67        .filter(|(_, pool)| match &filter.protocol {
68            None => true,
69            Some(protocol) => pool.pool.get_protocol() == protocol.into(),
70        })
71        .count();
72
73    Ok(Json(PoolResponse { pools: ret, total: total_pools }))
74}
75
76/// Get pool details
77///
78/// Get pool details
79#[utoipa::path(
80    get,
81    path = "/pools/{address}",
82    tag = "market",
83    tags = [],
84    params(
85        ("address" = String, Path, description = "Address of the pool"),
86    ),
87    responses(
88    (status = 200, description = "Pool detail response", body = PoolDetailsResponse),
89    )
90)]
91pub async fn pool<DB: DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static>(
92    State(app_state): State<AppState<DB>>,
93    Path(address): Path<String>,
94) -> Result<Json<PoolDetailsResponse>, (StatusCode, String)> {
95    let address = Address::from_str(&address).map_err(internal_error)?;
96
97    match app_state.bc.market().read().await.pools().get(&PoolId::Address(address)) {
98        None => Err((StatusCode::NOT_FOUND, "Pool not found".to_string())),
99        Some(pool) => Ok(Json(PoolDetailsResponse {
100            address: pool.get_address().address_or_zero(),
101            pool_class: PoolClass::from(pool.get_class()),
102            protocol: PoolProtocol::from(pool.get_protocol()),
103            fee: pool.get_fee(),
104            tokens: pool.get_tokens(),
105        })),
106    }
107}
108
109/// Market statistics
110///
111/// Get the latest market statistics
112#[utoipa::path(
113    get,
114    path = "/stats",
115    tag = "market",
116    tags = [],
117    params(
118        Pagination, Filter
119    ),
120    responses(
121        (status = 200, description = "Market stats", body = MarketStats),
122    )
123)]
124pub async fn market_stats<DB: DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static>(
125    State(app_state): State<AppState<DB>>,
126) -> Result<Json<MarketStats>, (StatusCode, String)> {
127    let total_pools = app_state.bc.market().read().await.pools().len();
128
129    Ok(Json(MarketStats { total_pools }))
130}
131
132/// Get a quote
133///
134/// Get quote for a pair of a pool
135#[utoipa::path(
136    post,
137    path = "/pools/{address}/quote",
138    tag = "market",
139    tags = [],
140    params(
141        ("address" = String, Path, description = "Address of the pool"),
142    ),
143    request_body = QuoteRequest,
144    responses(
145        (status = 200, description = "Market stats", body = QuoteResponse),
146    )
147)]
148pub async fn pool_quote<DB: DatabaseRef<Error = kabu_evm_db::KabuDBError> + DatabaseCommit + Send + Sync + Clone + 'static>(
149    State(app_state): State<AppState<DB>>,
150    Path(address): Path<String>,
151    Json(_quote_request): Json<QuoteRequest>,
152) -> Result<Json<QuoteResponse>, (StatusCode, String)> {
153    let address = Address::from_str(&address).map_err(internal_error)?;
154    match app_state.bc.market().read().await.pools().get(&PoolId::Address(address)) {
155        None => Err((StatusCode::NOT_FOUND, "Pool not found".to_string())),
156        Some(_pool) => Err((StatusCode::NOT_FOUND, "Not_implemted".to_string())),
157        //TODO : rewrite
158        /*let evm_env = Env::default();
159        let quote_result = pool.pool.calculate_out_amount(
160            &app_state.state.market_state().read().await.state_db,
161            evm_env,
162            &quote_request.token_address_from.into(),
163            &quote_request.token_address_to.into(),
164            quote_request.amount_in,
165        );
166        match quote_result {
167            Err(err) => Err((StatusCode::INTERNAL_SERVER_ERROR, err.to_string())),
168            Ok((out_amount, gas_used)) => Ok(Json(QuoteResponse { out_amount, gas_used })),
169        }*/
170    }
171}