use near_sdk::{ json_types::{Base64VecU8, U64}, near, AccountId, Gas, NearToken, Promise, }; use crate::number::Decimal; use super::pyth::{self, PriceIdentifier}; #[derive(Clone, Debug, PartialEq, Eq)] #[near(serializers = [json, borsh])] pub enum Action { NormalizeNativeLstPrice { decimals: u32 }, } impl Action { pub fn apply(&self, mut price: pyth::Price, input: Decimal) -> Option { match self { Self::NormalizeNativeLstPrice { decimals } => { let scale_factor = input / 10u128.pow(*decimals); let price_is_negative = if price.price.0.is_negative() { -1 } else { 1 }; let abs_price_u128 = i128::from(price.price.0).unsigned_abs(); price.price.0 = price_is_negative * i64::try_from((abs_price_u128 * scale_factor).to_u128_floor()?).ok()?; price.conf.0 = u64::try_from((price.conf.0 * scale_factor).to_u128_ceil()?).ok()?; Some(price) } } } } #[derive(Clone, Debug, PartialEq, Eq)] #[near(serializers = [json, borsh])] pub struct Call { pub account_id: AccountId, pub method_name: String, pub args: Base64VecU8, pub gas: U64, } impl Call { #[cfg(not(target_arch = "wasm32"))] #[allow(clippy::unwrap_used)] pub fn new( account_id: &near_sdk::AccountIdRef, method_name: impl Into, args: impl near_sdk::serde::Serialize, gas: Gas, ) -> Self { Self { account_id: account_id.into(), method_name: method_name.into(), args: near_sdk::serde_json::to_vec(&args).unwrap().into(), gas: gas.as_gas().into(), } } #[cfg(not(target_arch = "wasm32"))] pub fn new_simple(account_id: &near_sdk::AccountIdRef, method_name: impl Into) -> Self { Self::new( account_id, method_name, near_sdk::serde_json::Value::Null, Gas::from_tgas(3), ) } pub fn promise(&self) -> Promise { Promise::new(self.account_id.clone()).function_call( self.method_name.clone(), self.args.0.clone(), NearToken::from_near(0), Gas::from_gas(self.gas.0), ) } } #[derive(Clone, Debug, PartialEq, Eq)] #[near(serializers = [json, borsh])] pub struct PriceTransformer { pub price_id: PriceIdentifier, pub call: Call, pub action: Action, } impl PriceTransformer { pub fn lst(price_id: PriceIdentifier, decimals: u32, call: Call) -> Self { Self { price_id, call, action: Action::NormalizeNativeLstPrice { decimals }, } } } #[cfg(test)] mod tests { use crate::dec; use super::*; #[test] fn price_transformation() { let transformation = Action::NormalizeNativeLstPrice { decimals: 24 }; let price_before = pyth::Price { price: 1234.into(), conf: 4.into(), expo: 5, publish_time: 0.into(), }; let price_after = transformation .apply(price_before, dec!("1.2").mul_pow10(24).unwrap()) .unwrap(); assert_eq!( price_after, pyth::Price { price: 1480.into(), conf: 5.into(), expo: 5, publish_time: 0.into(), }, ); } }