Created
April 12, 2025 08:29
-
-
Save mgild/38153809869777ed7e1871dc2ba1f715 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 anyhow::Error as AnyhowError; | |
use crate::{ | |
protos::{oracle_job::*, OracleJob}, | |
TaskInterface, TaskInterfaceAsync, TaskOutput, TaskResult, TaskRunnerContext, | |
}; | |
async fn get_duration_from_vault( | |
rpc_client: &RpcClient, | |
vault_address: Pubkey, | |
) -> Result<(u64, u64), AnyhowError> { | |
let account = rpc_client | |
.get_account(&vault_address) | |
.await | |
.map_err(|e| anyhow!("Exponent: Failed to get account: {}", e))?; | |
// 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(anyhow!("Exponent: AccountData is too small")); | |
} | |
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, AnyhowError> { | |
let end_price = Decimal::ONE; | |
let current_timestamp = SystemTime::now() | |
.duration_since(UNIX_EPOCH) | |
.unwrap() | |
.as_secs(); | |
if current_timestamp <= start_timestamp { | |
return Ok(start_price); | |
} | |
if current_timestamp >= maturity_timestamp { | |
return Ok(end_price); | |
} | |
let time_diff = maturity_timestamp.checked_sub(start_timestamp) | |
.context("Exponent: Maturity timestamp is less than start timestamp")?; | |
let maturity_time_diff = current_timestamp.checked_sub(start_timestamp) | |
.context("Exponent: Maturity timestamp is less than start timestamp")?; | |
let time_progress = Decimal::from(time_diff).checked_div(Decimal::from(maturity_time_diff)) | |
.context("Exponent: Division by zero in time progress calculation")?; | |
let price_diff = end_price.checked_sub(start_price) | |
.context("Exponent: MathError")?; | |
let price = start_price.checked_add( | |
price_diff.checked_mul(time_progress).context("Exponent: MathError")? | |
).context("Exponent: OverflowError")?; | |
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?; | |
let maturity_timestamp = start_timestamp.checked_add(maturity_duration) | |
.context("Exponent: MathError")?; | |
let current_price = get_current_price(start_timestamp, maturity_timestamp, *start_price)?; | |
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.clone().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