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