Skip to content

Instantly share code, notes, and snippets.

@mgild
Created April 12, 2025 08:29
Show Gist options
  • Save mgild/38153809869777ed7e1871dc2ba1f715 to your computer and use it in GitHub Desktop.
Save mgild/38153809869777ed7e1871dc2ba1f715 to your computer and use it in GitHub Desktop.
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