use aide::axum::{ApiRouter, routing::get_with}; use axum::{ extract::{Path, State}, http::HeaderMap, response::Redirect, routing::get, }; use brk_types::{ BlockCountParam, BlockFeesEntry, BlockRewardsEntry, BlockSizesWeights, DifficultyAdjustment, DifficultyAdjustmentEntry, HashrateSummary, PoolDetail, PoolInfo, PoolSlugParam, PoolsSummary, RewardStats, TimePeriodParam, }; use crate::{CacheStrategy, extended::TransformResponseExtended}; use super::AppState; pub trait MiningRoutes { fn add_mining_routes(self) -> Self; } impl MiningRoutes for ApiRouter { fn add_mining_routes(self) -> Self { self.route( "/api/v1/mining", get(Redirect::temporary("/api#tag/mining")), ) .api_route( "/api/v1/difficulty-adjustment", get_with( async |headers: HeaderMap, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, |q| q.difficulty_adjustment()).await }, |op| { op.id("get_difficulty_adjustment") .mining_tag() .summary("Difficulty adjustment") .description("Get current difficulty adjustment information including progress through the current epoch, estimated retarget date, and difficulty change prediction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*") .ok_response::() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/pools", get_with( async |headers: HeaderMap, State(state): State| { // Pool list is static, only changes on code update state.cached_json(&headers, CacheStrategy::Static, |q| Ok(q.all_pools())).await }, |op| { op.id("get_pools") .mining_tag() .summary("List all mining pools") .description("Get list of all known mining pools with their identifiers.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pools)*") .ok_response::>() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/pools/{time_period}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.mining_pools(path.time_period)).await }, |op| { op.id("get_pool_stats") .mining_tag() .summary("Mining pool statistics") .description("Get mining pool statistics for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pools)*") .ok_response::() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/pool/{slug}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.pool_detail(path.slug)).await }, |op| { op.id("get_pool") .mining_tag() .summary("Mining pool details") .description("Get detailed information about a specific mining pool including block counts and shares for different time periods.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool)*") .ok_response::() .not_modified() .not_found() .server_error() }, ), ) .api_route( "/api/v1/mining/hashrate", get_with( async |headers: HeaderMap, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, |q| q.hashrate(None)).await }, |op| { op.id("get_hashrate") .mining_tag() .summary("Network hashrate (all time)") .description("Get network hashrate and difficulty data for all time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-hashrate)*") .ok_response::() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/hashrate/{time_period}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.hashrate(Some(path.time_period))).await }, |op| { op.id("get_hashrate_by_period") .mining_tag() .summary("Network hashrate") .description("Get network hashrate and difficulty data for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-hashrate)*") .ok_response::() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/difficulty-adjustments", get_with( async |headers: HeaderMap, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, |q| q.difficulty_adjustments(None)).await }, |op| { op.id("get_difficulty_adjustments") .mining_tag() .summary("Difficulty adjustments (all time)") .description("Get historical difficulty adjustments including timestamp, block height, difficulty value, and percentage change.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustments)*") .ok_response::>() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/difficulty-adjustments/{time_period}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.difficulty_adjustments(Some(path.time_period))).await }, |op| { op.id("get_difficulty_adjustments_by_period") .mining_tag() .summary("Difficulty adjustments") .description("Get historical difficulty adjustments for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustments)*") .ok_response::>() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/blocks/fees/{time_period}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.block_fees(path.time_period)).await }, |op| { op.id("get_block_fees") .mining_tag() .summary("Block fees") .description("Get average block fees for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-fees)*") .ok_response::>() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/blocks/rewards/{time_period}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.block_rewards(path.time_period)).await }, |op| { op.id("get_block_rewards") .mining_tag() .summary("Block rewards") .description("Get average block rewards (coinbase = subsidy + fees) for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-rewards)*") .ok_response::>() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/blocks/fee-rates/{time_period}", get_with( async |Path(_path): Path| { axum::Json(serde_json::json!({ "status": "wip", "message": "This endpoint is work in progress. Percentile fields are not yet available." })) }, |op| { op.id("get_block_fee_rates") .mining_tag() .summary("Block fee rates (WIP)") .description("**Work in progress.** Get block fee rate percentiles (min, 10th, 25th, median, 75th, 90th, max) for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-feerates)*") .ok_response::() }, ), ) .api_route( "/api/v1/mining/blocks/sizes-weights/{time_period}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.block_sizes_weights(path.time_period)).await }, |op| { op.id("get_block_sizes_weights") .mining_tag() .summary("Block sizes and weights") .description("Get average block sizes and weights for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-sizes-weights)*") .ok_response::() .not_modified() .server_error() }, ), ) .api_route( "/api/v1/mining/reward-stats/{block_count}", get_with( async |headers: HeaderMap, Path(path): Path, State(state): State| { state.cached_json(&headers, CacheStrategy::Height, move |q| q.reward_stats(path.block_count)).await }, |op| { op.id("get_reward_stats") .mining_tag() .summary("Mining reward statistics") .description("Get mining reward statistics for the last N blocks including total rewards, fees, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-reward-stats)*") .ok_response::() .not_modified() .server_error() }, ), ) } }