Skip to main content

pyra_redis/
typed.rs

1//! Typed fetch helpers for shared Redis data.
2//!
3//! These methods combine [`RedisKey`] patterns with JSON deserialization
4//! to return strongly-typed Pyra account data from Redis.
5//!
6//! All account data is stored by the indexer wrapped in [`Cache<T>`],
7//! which includes the Solana slot at which the data was last updated.
8//! These helpers unwrap the `Cache` and return the inner account type.
9//! If you need the slot information, use [`RedisClient::get_json::<Cache<T>>`] directly.
10
11use std::collections::HashMap;
12
13use pyra_tokens::AssetId;
14use solana_pubkey::Pubkey;
15
16use pyra_types::{Cache, SpendLimitsOrderAccount, Vault, WithdrawOrderAccount};
17
18use crate::{RedisClient, RedisKey, RedisResult};
19
20impl RedisClient {
21    // ── Single-key fetches ────────────────────────────────────────────
22
23    /// Fetch a Pyra vault account by its PDA address.
24    pub async fn fetch_vault(&self, vault: &Pubkey) -> RedisResult<Option<Vault>> {
25        let key = RedisKey::vault(vault);
26        let cached: Option<Cache<Vault>> = self.get_json(key.as_str()).await?;
27        Ok(cached.map(|c| c.account))
28    }
29
30    /// Fetch a withdraw order account by its PDA address.
31    pub async fn fetch_withdraw_order(
32        &self,
33        order: &Pubkey,
34    ) -> RedisResult<Option<WithdrawOrderAccount>> {
35        let key = RedisKey::withdraw_order(order);
36        let cached: Option<Cache<WithdrawOrderAccount>> = self.get_json(key.as_str()).await?;
37        Ok(cached.map(|c| c.account))
38    }
39
40    /// Fetch a spend limits order account by its PDA address.
41    pub async fn fetch_spend_limits_order(
42        &self,
43        order: &Pubkey,
44    ) -> RedisResult<Option<SpendLimitsOrderAccount>> {
45        let key = RedisKey::spend_limits_order(order);
46        let cached: Option<Cache<SpendLimitsOrderAccount>> = self.get_json(key.as_str()).await?;
47        Ok(cached.map(|c| c.account))
48    }
49
50    /// Fetch the oracle price for an asset (stored as a plain `f64` by the indexer).
51    pub async fn fetch_price(&self, asset_id: AssetId) -> RedisResult<Option<f64>> {
52        let key = RedisKey::price(asset_id);
53        self.get_json(key.as_str()).await
54    }
55
56    // ── Batch fetches ─────────────────────────────────────────────────
57
58    /// Fetch prices for multiple assets in a single MGET round-trip.
59    ///
60    /// Returns only assets that have a cached price.
61    pub async fn fetch_prices(&self, asset_ids: &[AssetId]) -> RedisResult<HashMap<AssetId, f64>> {
62        if asset_ids.is_empty() {
63            return Ok(HashMap::new());
64        }
65
66        let keys: Vec<String> = asset_ids
67            .iter()
68            .map(|&id| RedisKey::price(id).to_string())
69            .collect();
70
71        let values = self.mget(&keys).await?;
72        let mut result = HashMap::with_capacity(asset_ids.len());
73
74        for (id, val) in asset_ids.iter().zip(values.into_iter()) {
75            if let Some(raw) = val {
76                if let Ok(price) = serde_json::from_str::<f64>(&raw) {
77                    result.insert(*id, price);
78                }
79            }
80        }
81
82        Ok(result)
83    }
84}