Skip to content

Commit fae5612

Browse files
committed
feat: Added new endpoint GET /historical_fee?date=YYYY-MM-DDTHH:MM:SS
Signed-off-by: kevkevinpal <oapallikunnel@gmail.com>
1 parent 048b90a commit fae5612

3 files changed

Lines changed: 113 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2025 Block, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package xyz.block.augurref.api
18+
19+
import io.ktor.http.ContentType
20+
import io.ktor.http.HttpStatusCode
21+
import io.ktor.server.application.call
22+
import io.ktor.server.response.respond
23+
import io.ktor.server.response.respondText
24+
import io.ktor.server.routing.Route
25+
import io.ktor.server.routing.get
26+
import org.slf4j.LoggerFactory
27+
import xyz.block.augurref.service.MempoolCollector
28+
29+
/**
30+
* Configure historical fee estimate endpoint
31+
*/
32+
fun Route.configureHistoricalFeesEndpoint(mempoolCollector: MempoolCollector) {
33+
val logger = LoggerFactory.getLogger("xyz.block.augurref.api.HistoricalFeeEstimateEndpoint")
34+
35+
get("/historical_fee") {
36+
logger.info("Received request for historical fee estimates")
37+
38+
// Extract date param from query parameters
39+
val dateParam = call.request.queryParameters["date"]
40+
41+
if (dateParam == null) {
42+
call.respondText(
43+
"Date parameter is required",
44+
status = HttpStatusCode.BadRequest,
45+
contentType = ContentType.Text.Plain,
46+
)
47+
return@get
48+
}
49+
val date = dateParam.toLongOrNull()
50+
51+
// Fetch historical fee estimate based on date
52+
val feeEstimate = if (date != null) {
53+
logger.info("Fetching historical fee estimate for date: $date")
54+
mempoolCollector.getFeeEstimateForDate(date)
55+
} else {
56+
logger.warn("date is null")
57+
call.respondText(
58+
"Failed to parse date, please input a unixtimestamp",
59+
status = HttpStatusCode.InternalServerError,
60+
contentType = ContentType.Text.Plain,
61+
)
62+
return@get
63+
}
64+
65+
if (feeEstimate == null) {
66+
logger.warn("No historical fee estimates available for $date")
67+
call.respondText(
68+
"No historical fee estimates available for $date",
69+
status = HttpStatusCode.ServiceUnavailable,
70+
contentType = ContentType.Text.Plain,
71+
)
72+
} else {
73+
logger.info("Transforming historical fee estimates for response")
74+
val response = transformFeeEstimate(feeEstimate)
75+
logger.debug("Returning historical fee estimates with ${response.estimates.size} targets")
76+
call.respond(response)
77+
}
78+
}
79+
}

app/src/main/kotlin/xyz/block/augurref/server/HttpServer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
2626
import io.ktor.server.routing.routing
2727
import org.slf4j.LoggerFactory
2828
import xyz.block.augurref.api.configureFeesEndpoint
29+
import xyz.block.augurref.api.configureHistoricalFeesEndpoint
2930
import xyz.block.augurref.config.ServerConfig
3031
import xyz.block.augurref.service.MempoolCollector
3132

@@ -58,6 +59,7 @@ class HttpServer(
5859
// Configure routes
5960
routing {
6061
configureFeesEndpoint(mempoolCollector)
62+
configureHistoricalFeesEndpoint(mempoolCollector)
6163
}
6264
}.start(wait = false)
6365

app/src/main/kotlin/xyz/block/augurref/service/MempoolCollector.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import xyz.block.augurref.bitcoin.BitcoinRpcClient
2424
import xyz.block.augurref.persistence.MempoolPersistence
2525
import java.time.Instant
2626
import java.time.LocalDateTime
27+
import java.time.ZoneId
2728
import java.util.concurrent.atomic.AtomicReference
2829
import kotlin.concurrent.fixedRateTimer
2930

@@ -71,6 +72,37 @@ class MempoolCollector(
7172
return latestFeeEstimate.get()
7273
}
7374

75+
/**
76+
* Get the fee estimate for specific date
77+
*/
78+
fun getFeeEstimateForDate(unixTimestamp: Long): FeeEstimate? {
79+
val dateTime = Instant
80+
.ofEpochSecond(unixTimestamp)
81+
.atZone(ZoneId.of("UTC"))
82+
.withZoneSameInstant(ZoneId.systemDefault())
83+
.toLocalDateTime()
84+
// Fetch the last day's snapshots
85+
logger.debug("Fetching snapshots from the last day")
86+
val lastDaySnapshots = persistence.getSnapshots(
87+
dateTime.minusDays(1),
88+
dateTime,
89+
)
90+
logger.debug("Retrieved ${lastDaySnapshots.size} snapshots from the last day")
91+
92+
if (lastDaySnapshots.isNotEmpty()) {
93+
// Calculate fee estimate for x blocks
94+
logger.debug("Calculating fee estimates")
95+
val newFeeEstimate = feeEstimator.calculateEstimates(lastDaySnapshots)
96+
return newFeeEstimate
97+
} else {
98+
logger.warn("No snapshots available for fee estimation")
99+
}
100+
return FeeEstimate(
101+
estimates = emptyMap(),
102+
timestamp = dateTime.atZone(ZoneId.of("UTC")).toInstant(),
103+
)
104+
}
105+
74106
/**
75107
* Get the latest fee estimate for block target
76108
*/

0 commit comments

Comments
 (0)