// Copyright (C) 2024 Riften Labs AS // // This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later. // A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html use crate::db::oracle::{get_closest, get_range, get_range_with_step}; use crate::db::DB; use crate::timeutil::time_now; use bitcoin_hashes::hex::FromHex; use bitcoincash::TokenID; use rocket::http::Status; use rocket::response::status::Custom; use rocket::serde::json::Json; use rocket::{get, State}; use serde_json::{json, Value}; /// Get the closest oracle price for a given token and timestamp. /// /// Status: Stable /// /// - token_id: The 32 byte token ID /// - timestamp: Unix timestamp #[get("/delphi/closest?&")] pub fn oracle_get_closest( token_id: Option, timestamp: Option, db: &State, ) -> Result, Custom> { let current_timestamp = timestamp.unwrap_or_else(time_now); let conn = db .oracle_r .get() .map_err(|e| Custom(Status::InternalServerError, e.to_string()))?; let token_id = token_id .map(|t| { TokenID::from_hex(&t) .map_err(|e| Custom(Status::BadRequest, format!("Invalid token ID: {e}"))) }) .transpose()?; let entry = get_closest(&conn, &token_id, current_timestamp) .map_err(|e| Custom(Status::InternalServerError, e.to_string()))?; Ok(Json(entry.map_or(serde_json::Value::Null, |e| { serde_json::to_value(e).unwrap() }))) } /// Status: Deprecated #[get("/delphi/range?&&")] pub fn oracle_get_range( token_id: Option, start: Option, end: Option, db: &State, ) -> Result>, Custom> { let end_timestamp = end.unwrap_or_else(time_now); let start_timestamp = start.unwrap_or_else(|| end_timestamp - 86400); // 1 day in seconds let conn = db .oracle_r .get() .map_err(|e| Custom(Status::InternalServerError, e.to_string()))?; let token_id = token_id .map(|t| { TokenID::from_hex(&t) .map_err(|e| Custom(Status::BadRequest, format!("Invalid token ID: {e}"))) }) .transpose()?; let entries = get_range(&conn, &token_id, start_timestamp, end_timestamp) .map_err(|e| Custom(Status::InternalServerError, e.to_string()))?; Ok(Json( entries .into_iter() .map(|e| serde_json::to_value(e).unwrap()) .collect(), )) } /// Get historical oracle prices for a given token. /// /// - token: The 32 byte token ID /// - start: Unix timestamp for start of period #[get("/delphi//history?&&")] pub fn oracle_get_history( token: &str, start: Option, end: Option, stepsize: Option, db: &State, ) -> Result, Custom> { let current_timestamp = time_now(); let conn = db .oracle_r .get() .map_err(|e| Custom(Status::InternalServerError, format!("DB error: {e}")))?; let token_id = token .parse::() .map_err(|e| Custom(Status::BadRequest, format!("Invalid token ID: {e}")))?; let start_ts = start.unwrap_or(current_timestamp - 30 * 24 * 3600); // default: 30 days ago let end_ts = end.unwrap_or(current_timestamp); let entries = if let Some(step) = stepsize { get_range_with_step(&conn, &Some(token_id), start_ts, end_ts, step) .map_err(|e| Custom(Status::BadRequest, format!("Query error: {e}")))? } else { get_range(&conn, &Some(token_id), start_ts, end_ts) .map_err(|e| Custom(Status::BadRequest, format!("Query error: {e}")))? }; let json_entries: Vec = entries .into_iter() .map(|entry| { json!({ "time": entry.oracle_timestamp, "price": entry.oracle_price, "txid": entry.txid, "blockhash": entry.blockhash, "sequence": entry.oracle_sequence, }) }) .collect(); Ok(Json(serde_json::Value::Array(json_entries))) }