Skip to main content

hypercall_db_diesel/
oracle.rs

1//! OracleWriter implementation for DatabaseHandler.
2//!
3//! Persists TWAP price samples and finalized settlement prices
4//! for the mark price oracle.
5
6use anyhow::{Context, Result};
7use diesel::prelude::*;
8use tracing::debug;
9
10use crate::database_handler::DatabaseHandler;
11use crate::models::{NewOraclePriceSample, NewOracleSettlementPrice, OracleSettlementPrice};
12use crate::schema::{oracle_price_samples, oracle_settlement_prices};
13use hypercall_db::types::oracle::{NewOraclePriceSampleInput, NewOracleSettlementPriceInput};
14
15impl hypercall_db::OracleWriter for DatabaseHandler {
16    fn save_oracle_price_samples_sync(&self, samples: &[NewOraclePriceSampleInput]) -> Result<()> {
17        if samples.is_empty() {
18            return Ok(());
19        }
20
21        let mut conn = self.pool().get().context("Failed to get DB connection")?;
22
23        let new_samples: Vec<NewOraclePriceSample> = samples
24            .iter()
25            .map(|s| NewOraclePriceSample {
26                symbol: s.symbol.clone(),
27                expiry_timestamp: s.expiry_timestamp,
28                sample_timestamp_ms: s.sample_timestamp_ms,
29                price: s.price,
30                source: s.source.clone(),
31            })
32            .collect();
33
34        diesel::insert_into(oracle_price_samples::table)
35            .values(&new_samples)
36            .on_conflict((
37                oracle_price_samples::symbol,
38                oracle_price_samples::expiry_timestamp,
39                oracle_price_samples::sample_timestamp_ms,
40                oracle_price_samples::source,
41            ))
42            .do_nothing()
43            .execute(&mut conn)?;
44
45        debug!("Persisted {} oracle price samples", samples.len(),);
46
47        Ok(())
48    }
49
50    fn save_oracle_settlement_price_sync(
51        &self,
52        settlement: &NewOracleSettlementPriceInput,
53    ) -> Result<()> {
54        let mut conn = self.pool().get().context("Failed to get DB connection")?;
55
56        let new_settlement = NewOracleSettlementPrice {
57            symbol: settlement.symbol.clone(),
58            expiry_timestamp: settlement.expiry_timestamp,
59            settlement_price: settlement.settlement_price,
60            sample_count: settlement.sample_count,
61            window_start: settlement.window_start,
62            window_end: settlement.window_end,
63            algorithm: settlement.algorithm.clone(),
64        };
65
66        diesel::insert_into(oracle_settlement_prices::table)
67            .values(&new_settlement)
68            .on_conflict((
69                oracle_settlement_prices::symbol,
70                oracle_settlement_prices::expiry_timestamp,
71            ))
72            .do_nothing()
73            .execute(&mut conn)?;
74
75        Ok(())
76    }
77
78    fn get_oracle_settlement_price_sync(
79        &self,
80        symbol: &str,
81        expiry_timestamp: i64,
82    ) -> Result<Option<f64>> {
83        let mut conn = self.pool().get().context("Failed to get DB connection")?;
84
85        let result: Option<OracleSettlementPrice> = oracle_settlement_prices::table
86            .filter(oracle_settlement_prices::symbol.eq(symbol))
87            .filter(oracle_settlement_prices::expiry_timestamp.eq(expiry_timestamp))
88            .first(&mut conn)
89            .optional()?;
90
91        Ok(result.map(|r| r.settlement_price))
92    }
93}