118 lines
3.8 KiB
Rust
118 lines
3.8 KiB
Rust
// Copyright (C) 2024-2026 Whiterun LLC
|
|
//
|
|
// 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::moria::{get_active_loans, get_global_history, get_loan_history, get_stats};
|
|
use crate::db::DB;
|
|
use crate::rpc::err::{bad_request, db_error, ApiErrorCode, CachedApiResult};
|
|
use crate::rpc::response::{cached_ok, CACHE_AGGREGATE};
|
|
use rocket::{get, State};
|
|
use serde_json::Value;
|
|
|
|
fn parse_nfth(hex_str: &str) -> Result<Vec<u8>, (ApiErrorCode, String)> {
|
|
let bytes = hex::decode(hex_str).map_err(|e| {
|
|
(
|
|
ApiErrorCode::InvalidParameters,
|
|
format!("Invalid nfth hex: {e}"),
|
|
)
|
|
})?;
|
|
if bytes.len() != 32 {
|
|
return Err((
|
|
ApiErrorCode::InvalidParameters,
|
|
format!("nfth must be 32 bytes (64 hex chars), got {}", bytes.len()),
|
|
));
|
|
}
|
|
Ok(bytes)
|
|
}
|
|
|
|
/// Get full loan history for a given borrower NFT hash.
|
|
///
|
|
/// - borrower_hash: 64-character hex string (32-byte P2NFTH hash identifying the loan)
|
|
///
|
|
/// Returns an array of all actions (borrow, repay, redeem, refinance, add_collateral)
|
|
/// for this loan, sorted by timestamp.
|
|
#[get("/loan/<borrower_hash>/history")]
|
|
pub async fn loan_history(borrower_hash: &str, db: &State<DB>) -> CachedApiResult<Value> {
|
|
let hash_bytes = hex::decode(borrower_hash).map_err(|e| {
|
|
bad_request(
|
|
ApiErrorCode::InvalidParameters,
|
|
&format!("Invalid borrower hash: {e}"),
|
|
)
|
|
})?;
|
|
|
|
if hash_bytes.len() != 32 {
|
|
return Err(bad_request(
|
|
ApiErrorCode::InvalidParameters,
|
|
"Borrower hash must be 32 bytes (64 hex characters)",
|
|
));
|
|
}
|
|
|
|
let entries = get_loan_history(&db.moria_r, &hash_bytes)
|
|
.await
|
|
.map_err(db_error)?;
|
|
|
|
Ok(cached_ok(
|
|
serde_json::to_value(entries).unwrap(),
|
|
CACHE_AGGREGATE,
|
|
))
|
|
}
|
|
|
|
/// Get global moria action history with pagination and optional nfth filter.
|
|
///
|
|
/// - offset: Number of entries to skip (default: 0)
|
|
/// - limit: Maximum entries to return (default: 50, max: 200)
|
|
/// - nfth: Comma-separated list of borrower NFT hashes (64-char hex each) to filter by
|
|
#[get("/history?<offset>&<limit>&<nfth>")]
|
|
pub async fn global_history(
|
|
offset: Option<i64>,
|
|
limit: Option<i64>,
|
|
nfth: Option<&str>,
|
|
db: &State<DB>,
|
|
) -> CachedApiResult<Value> {
|
|
let offset = offset.unwrap_or(0).max(0);
|
|
let limit = limit.unwrap_or(50).clamp(1, 200);
|
|
|
|
let nfth_filter: Vec<Vec<u8>> = match nfth {
|
|
Some(s) if !s.is_empty() => {
|
|
let mut filters = Vec::new();
|
|
for hash_hex in s.split(',') {
|
|
let hash_hex = hash_hex.trim();
|
|
if hash_hex.is_empty() {
|
|
continue;
|
|
}
|
|
filters.push(parse_nfth(hash_hex).map_err(|(code, msg)| bad_request(code, &msg))?);
|
|
}
|
|
filters
|
|
}
|
|
_ => Vec::new(),
|
|
};
|
|
|
|
let entries = get_global_history(&db.moria_r, &nfth_filter, offset, limit)
|
|
.await
|
|
.map_err(db_error)?;
|
|
|
|
Ok(cached_ok(
|
|
serde_json::to_value(entries).unwrap(),
|
|
CACHE_AGGREGATE,
|
|
))
|
|
}
|
|
|
|
/// List all active (not yet repaid/redeemed) loans.
|
|
#[get("/loans/active")]
|
|
pub async fn active_loans(db: &State<DB>) -> CachedApiResult<Value> {
|
|
let entries = get_active_loans(&db.moria_r).await.map_err(db_error)?;
|
|
|
|
Ok(cached_ok(
|
|
serde_json::to_value(entries).unwrap(),
|
|
CACHE_AGGREGATE,
|
|
))
|
|
}
|
|
|
|
/// Get Moria protocol statistics.
|
|
#[get("/stats")]
|
|
pub async fn moria_stats(db: &State<DB>) -> CachedApiResult<Value> {
|
|
let stats = get_stats(&db.moria_r).await.map_err(db_error)?;
|
|
|
|
Ok(cached_ok(stats, CACHE_AGGREGATE))
|
|
}
|