templar_common/oracle/
price_transformer.rs1use near_sdk::{
2 json_types::{Base64VecU8, U64},
3 near, AccountId, Gas,
4};
5
6use crate::number::Decimal;
7
8use super::pyth::{self, PriceIdentifier};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11#[near(serializers = [json, borsh])]
12pub enum Action {
13 NormalizeNativeLstPrice { decimals: u32 },
14}
15
16impl Action {
17 pub fn apply(&self, mut price: pyth::Price, input: Decimal) -> Option<pyth::Price> {
18 match self {
19 Self::NormalizeNativeLstPrice { decimals } => {
20 let scale_factor = input / 10u128.pow(*decimals);
21
22 let price_is_negative = if price.price.0.is_negative() { -1 } else { 1 };
23 let abs_price_u128 = i128::from(price.price.0).unsigned_abs();
24 price.price.0 = price_is_negative
25 * i64::try_from((abs_price_u128 * scale_factor).to_u128_floor()?).ok()?;
26 price.conf.0 = u64::try_from((price.conf.0 * scale_factor).to_u128_ceil()?).ok()?;
27 Some(price)
28 }
29 }
30 }
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34#[near(serializers = [json, borsh])]
35pub struct Call {
36 pub account_id: AccountId,
37 pub method_name: String,
38 pub args: Base64VecU8,
39 pub gas: U64,
40}
41
42impl Call {
43 #[cfg(all(not(target_arch = "wasm32"), feature = "rpc"))]
44 #[allow(clippy::unwrap_used)]
45 pub fn new(
46 account_id: &near_sdk::AccountIdRef,
47 method_name: impl Into<String>,
48 args: impl near_sdk::serde::Serialize,
49 gas: Gas,
50 ) -> Self {
51 Self {
52 account_id: account_id.into(),
53 method_name: method_name.into(),
54 args: near_sdk::serde_json::to_vec(&args).unwrap().into(),
55 gas: gas.as_gas().into(),
56 }
57 }
58
59 #[cfg(all(not(target_arch = "wasm32"), feature = "rpc"))]
60 pub fn new_simple(account_id: &near_sdk::AccountIdRef, method_name: impl Into<String>) -> Self {
61 Self::new(
62 account_id,
63 method_name,
64 near_sdk::serde_json::Value::Null,
65 Gas::from_tgas(3),
66 )
67 }
68
69 pub fn promise(&self) -> near_sdk::Promise {
70 near_sdk::Promise::new(self.account_id.clone()).function_call(
71 self.method_name.clone(),
72 self.args.0.clone(),
73 near_sdk::NearToken::from_near(0),
74 Gas::from_gas(self.gas.0),
75 )
76 }
77
78 #[cfg(all(not(target_arch = "wasm32"), feature = "rpc"))]
79 pub fn rpc_call(&self) -> near_primitives::views::QueryRequest {
80 near_primitives::views::QueryRequest::CallFunction {
81 account_id: self.account_id.clone(),
82 method_name: self.method_name.clone(),
83 args: self.args.0.clone().into(),
84 }
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq)]
89#[near(serializers = [json, borsh])]
90pub struct PriceTransformer {
91 pub price_id: PriceIdentifier,
92 pub call: Call,
93 pub action: Action,
94}
95
96impl PriceTransformer {
97 pub fn lst(price_id: PriceIdentifier, decimals: u32, call: Call) -> Self {
98 Self {
99 price_id,
100 call,
101 action: Action::NormalizeNativeLstPrice { decimals },
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::dec;
109
110 use super::*;
111
112 #[test]
113 fn price_transformation() {
114 let transformation = Action::NormalizeNativeLstPrice { decimals: 24 };
115 let price_before = pyth::Price {
116 price: 1234.into(),
117 conf: 4.into(),
118 expo: 5,
119 publish_time: 0.into(),
120 };
121
122 let price_after = transformation
123 .apply(price_before, dec!("1.2").mul_pow10(24).unwrap())
124 .unwrap();
125
126 assert_eq!(
127 price_after,
128 pyth::Price {
129 price: 1480.into(),
130 conf: 5.into(),
131 expo: 5,
132 publish_time: 0.into(),
133 },
134 );
135 }
136}