From d8511544d10f286db75e513971a1e04a1c9ab72a Mon Sep 17 00:00:00 2001 From: kevkevinpal Date: Mon, 11 Aug 2025 22:51:33 -0400 Subject: [PATCH] feat: added new endpoint GET /historical_fees?start_date=&end_date=&interval= Signed-off-by: kevkevinpal --- .../augurref/api/HistoricalFeeEndpoint.kt | 57 +++++++++++++++++++ .../augurref/service/MempoolCollector.kt | 36 ++++++++++++ .../augurref/api/FeeEstimateEndpointTest.kt | 1 - 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/xyz/block/augurref/api/HistoricalFeeEndpoint.kt b/app/src/main/kotlin/xyz/block/augurref/api/HistoricalFeeEndpoint.kt index c7afd2b..c986675 100644 --- a/app/src/main/kotlin/xyz/block/augurref/api/HistoricalFeeEndpoint.kt +++ b/app/src/main/kotlin/xyz/block/augurref/api/HistoricalFeeEndpoint.kt @@ -76,4 +76,61 @@ fun Route.configureHistoricalFeesEndpoint(mempoolCollector: MempoolCollector) { call.respond(response) } } + + get("/historical_fees") { + logger.info("Received request for historical fee estimates") + + // Extract params from query start_timestamp, end_timestamp, interval (seconds) + val startTimestampParam = call.request.queryParameters["start_timestamp"]?.toLongOrNull() + val endTimestampParam = call.request.queryParameters["end_timestamp"]?.toLongOrNull() + val intervalParam = call.request.queryParameters["interval"] + val interval: Int = intervalParam?.toIntOrNull() ?: 3600 + + if (startTimestampParam == null) { + call.respondText( + "start_timestamp parameter is required", + status = HttpStatusCode.BadRequest, + contentType = ContentType.Text.Plain, + ) + return@get + } + + if (endTimestampParam == null) { + call.respondText( + "end_timestamp parameter is required", + status = HttpStatusCode.BadRequest, + contentType = ContentType.Text.Plain, + ) + return@get + } + + // Fetch historical fee estimate based on date + logger.info( + "Fetching historical fee estimate for start_timestamp: $startTimestampParam to " + + "end_timestamp: $endTimestampParam for intervals: $interval seconds", + ) + val feeEstimate = mempoolCollector.getFeeEstimateForTimestampRange(startTimestampParam, endTimestampParam, interval) + + if (feeEstimate == null) { + logger.warn( + "No historical fee estimates available for start_timestamp $startTimestampParam to " + + "end_timestamp: $endTimestampParam for interval: $interval seconds", + ) + call.respondText( + "No historical fee estimates available for start_timestamp $startTimestampParam to " + + "end_timestamp: $endTimestampParam for interval: $interval seconds", + status = HttpStatusCode.ServiceUnavailable, + contentType = ContentType.Text.Plain, + ) + } else { + var mutableResponse = mutableListOf() + logger.info("Transforming historical fee estimates for response") + for (estimate in feeEstimate) { + val response = transformFeeEstimate(estimate) + mutableResponse.add(response) + } + logger.debug("Returning historical fee estimates") + call.respond(mutableResponse) + } + } } diff --git a/app/src/main/kotlin/xyz/block/augurref/service/MempoolCollector.kt b/app/src/main/kotlin/xyz/block/augurref/service/MempoolCollector.kt index 198462b..85ebea5 100644 --- a/app/src/main/kotlin/xyz/block/augurref/service/MempoolCollector.kt +++ b/app/src/main/kotlin/xyz/block/augurref/service/MempoolCollector.kt @@ -72,6 +72,42 @@ class MempoolCollector( return latestFeeEstimate.get() } + fun getFeeEstimateForTimestampRange(start_date: Long, end_date: Long, interval: Int): List? { + // Ensure start_date is not after end_date + if (start_date > end_date) { + logger.warn("Start date is after end date") + return null + } + + // Initialize result list to store fee estimates + val estimates = mutableListOf() + var currentDate = start_date + + // Iterate through the date range with the given interval + while (currentDate < end_date) { + logger.debug("Processing fee estimate for $currentDate") + val feeEstimate = getFeeEstimateForTimestamp(currentDate) + + // If fee estimate is non-null and has estimates, merge them + if (feeEstimate != null && feeEstimate.estimates.isNotEmpty()) { + estimates.add(feeEstimate) + } else { + logger.debug("No valid fee estimate for $currentDate") + } + + currentDate = currentDate + interval + } + + // Return null if no estimates were collected + if (estimates.isEmpty()) { + logger.warn("No fee estimates collected for the date range") + return null + } + + // Return a FeeEstimate with the aggregated estimates + return estimates + } + /** * Get the fee estimate for specific date */ diff --git a/app/src/test/kotlin/xyz/block/augurref/api/FeeEstimateEndpointTest.kt b/app/src/test/kotlin/xyz/block/augurref/api/FeeEstimateEndpointTest.kt index aee4d8d..a7a2454 100644 --- a/app/src/test/kotlin/xyz/block/augurref/api/FeeEstimateEndpointTest.kt +++ b/app/src/test/kotlin/xyz/block/augurref/api/FeeEstimateEndpointTest.kt @@ -205,7 +205,6 @@ class FeeEstimateEndpointTest { verify { mockMempoolCollector.getLatestFeeEstimateForBlockTarget(2.0) } } - private fun createMockFeeEstimate(timestamp: Instant): FeeEstimate { // Create mock data using the actual augur library structure val mockFeeEstimate = mockk()