Created
November 13, 2025 16:18
-
-
Save mgild/f15b2f0390a3e66e81d06e2ecfc0f1cd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::time::{SystemTime, UNIX_EPOCH}; | |
| use anyhow::{anyhow, Context}; | |
| use num_traits::FromPrimitive; | |
| use rust_decimal::Decimal; | |
| use solana_client::nonblocking::rpc_client::RpcClient; | |
| use solana_sdk::pubkey::Pubkey; | |
| use crate::{ | |
| oracle_job::ExponentPtLinearPricingTask, protos::OracleJob, TaskInterface, TaskInterfaceAsync, | |
| TaskOutput, TaskResult, TaskRunnerContext, | |
| }; | |
| async fn get_duration_from_vault( | |
| rpc_client: &RpcClient, | |
| vault_address: Pubkey, | |
| ) -> Result<(u64, u64), String> { | |
| let account = rpc_client | |
| .get_account(&vault_address) | |
| .await | |
| .map_err(|e| e.to_string())?; | |
| // Vault account offsets (8-byte discriminator + field offsets) | |
| let start_ts_offset = 264; | |
| let duration_offset = start_ts_offset + 4; | |
| if account.data.len() < duration_offset + 4 { | |
| return Err("Account data too short".to_string()); | |
| } | |
| let start_ts_bytes = &account.data[start_ts_offset..start_ts_offset + 4]; | |
| let duration_bytes = &account.data[duration_offset..duration_offset + 4]; | |
| let start_ts = u32::from_le_bytes(start_ts_bytes.try_into().unwrap()); | |
| let duration = u32::from_le_bytes(duration_bytes.try_into().unwrap()); | |
| Ok((start_ts as u64, duration as u64)) | |
| } | |
| fn get_current_price( | |
| start_timestamp: u64, | |
| maturity_timestamp: u64, | |
| start_price: Decimal, | |
| ) -> Result<Decimal, String> { | |
| let end_price = Decimal::ONE; | |
| let current_timestamp = SystemTime::now() | |
| .duration_since(UNIX_EPOCH) | |
| .map_err(|e| e.to_string())? | |
| .as_secs(); | |
| if current_timestamp <= start_timestamp { | |
| return Ok(start_price); | |
| } | |
| if current_timestamp >= maturity_timestamp { | |
| return Ok(end_price); | |
| } | |
| let time_progress = Decimal::from(current_timestamp - start_timestamp) | |
| / Decimal::from(maturity_timestamp - start_timestamp); | |
| let price_diff = end_price - start_price; | |
| let price = start_price + (price_diff * time_progress); | |
| Ok(price) | |
| } | |
| async fn exponent_pt_linear_price_task( | |
| client: &RpcClient, | |
| vault_address: &Pubkey, | |
| start_price: &Decimal, | |
| ) -> TaskResult { | |
| let (start_timestamp, maturity_duration) = get_duration_from_vault(client, *vault_address) | |
| .await | |
| .map_err(|e| anyhow!("Exponent: Failed to get duration from vault: {}", e))?; | |
| let maturity_timestamp = start_timestamp | |
| .checked_add(maturity_duration) | |
| .context("Exponent: MathError")?; | |
| let current_price = get_current_price(start_timestamp, maturity_timestamp, *start_price) | |
| .map_err(|e| anyhow!("Exponent: Failed to get current price: {}", e))?; | |
| Ok(TaskOutput::Num(current_price)) | |
| } | |
| impl TaskInterface for ExponentPtLinearPricingTask { | |
| fn children(&self) -> Vec<OracleJob> { | |
| Vec::new() | |
| } | |
| fn uses_input(&self) -> bool { | |
| false | |
| } | |
| } | |
| #[async_trait::async_trait] | |
| impl TaskInterfaceAsync for ExponentPtLinearPricingTask { | |
| async fn execute<'a>(&'a self, ctx: &'a mut TaskRunnerContext) -> TaskResult { | |
| let client = ctx.mainnet_rpc(); | |
| let vault = self.vault.clone().unwrap_or_default(); | |
| let vault_address: Pubkey = vault | |
| .parse() | |
| .context("ExponentTask: Failed to parse vault address")?; | |
| let start_price = self.start_price.unwrap_or_default(); | |
| let start_price = | |
| Decimal::from_f64(start_price).context("ExponentTask: Failed to parse start price")?; | |
| exponent_pt_linear_price_task(&client, &vault_address, &start_price).await | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use switchboard_utils::FromStr; | |
| use super::*; | |
| #[tokio::test] | |
| async fn exponent_pt_task_test_1() { | |
| let rpc_client = RpcClient::new("https://api.mainnet-beta.solana.com".to_string()); | |
| let start_price = Decimal::from_str("0.8").unwrap(); | |
| let vault = Pubkey::from_str("9YbaicMsXrtupkpD72pdWBfU6R7EJfSByw75sEpDM1uH").unwrap(); | |
| let out = exponent_pt_linear_price_task(&rpc_client, &vault, &start_price) | |
| .await | |
| .unwrap(); | |
| println!("Exponent PT Linear Price: {:?}", out); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment