Skip to content

Commit

Permalink
+Add pyth::PriceUpdateV2 to prevent importing pyth and having conflic…
Browse files Browse the repository at this point in the history
…t with solana version

+ add OraclePrice
  • Loading branch information
Adrena-Corto committed Oct 28, 2024
1 parent 342598c commit 3926e86
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 1 deletion.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"bytemuck",
"pyth",
"sablier",
"Zeroable"
]
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ bytemuck = "1.19.0"
anyhow = "1.0.91"
solana-program = "~2.0.10"
num-traits = "0.2.19"
log = "0.4.22"
log = "0.4.22"
borsh = "1.5.1"
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ pub use {

pub mod liquidation_price;
pub mod math;
pub mod oracle_price;
pub mod pda;
pub mod pyth;
pub mod types;

declare_id!("13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwet");
Expand Down
80 changes: 80 additions & 0 deletions src/oracle_price.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use {
crate::{math, pyth::PriceUpdateV2, Cortex},
anyhow::{anyhow, Result},
};

// pub const ORACLE_EXPONENT_SCALE: i32 = -9;
pub const ORACLE_PRICE_SCALE: u128 = 1_000_000_000;
pub const ORACLE_MAX_PRICE: u64 = (1 << 28) - 1;

// In BPS
pub const MAX_PRICE_ERROR: u16 = 300;

#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
pub struct OraclePrice {
pub price: u64,
pub exponent: i32,
pub confidence: u64,
}

impl OraclePrice {
pub fn new(price: u64, exponent: i32, conf: u64) -> Self {
Self {
price,
exponent,
confidence: conf,
}
}

pub fn low(&self) -> Self {
Self {
price: self.price - self.confidence,
exponent: self.exponent,
confidence: 0,
}
}

pub fn high(&self) -> Self {
Self {
price: self.price + self.confidence,
exponent: self.exponent,
confidence: 0,
}
}

pub fn new_from_pyth_price_update_v2(price_update_v2: &PriceUpdateV2) -> Result<Self> {
let pyth_price = price_update_v2.price_message;

// Check for maximum confidence
{
let confidence_bps: u64 = math::checked_as_u64(math::checked_ceil_div::<u128>(
pyth_price.conf as u128 * Cortex::BPS_POWER,
pyth_price.price as u128,
)?)?;

if pyth_price.price <= 0 || confidence_bps > MAX_PRICE_ERROR as u64 {
return Err(anyhow!("Pyth price is out of bounds"));
}
}

OraclePrice {
// price is i64 and > 0 per check above
price: pyth_price.price as u64,
exponent: pyth_price.exponent,
confidence: pyth_price.conf,
}
.scale_to_exponent(-(Cortex::PRICE_DECIMALS as i32))
}

pub fn scale_to_exponent(&self, target_exponent: i32) -> Result<OraclePrice> {
if target_exponent == self.exponent {
return Ok(*self);
}

Ok(OraclePrice {
price: math::scale_to_exponent(self.price, self.exponent, target_exponent)?,
exponent: target_exponent,
confidence: self.confidence,
})
}
}
57 changes: 57 additions & 0 deletions src/pyth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use {
anchor_lang::prelude::Pubkey,
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
};

#[derive(BorshSerialize, BorshDeserialize, BorshSchema, Copy, Clone, PartialEq, Debug)]
pub enum VerificationLevel {
Partial { num_signatures: u8 },
Full,
}

/// Id of a feed producing the message. One feed produces one or more messages.
pub type FeedId = [u8; 32];

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
pub struct PriceFeedMessage {
pub feed_id: FeedId,
pub price: i64,
pub conf: u64,
pub exponent: i32,
/// The timestamp of this price update in seconds
pub publish_time: i64,
/// The timestamp of the previous price update. This field is intended to allow users to
/// identify the single unique price update for any moment in time:
/// for any time t, the unique update is the one such that prev_publish_time < t <= publish_time.
///
/// Note that there may not be such an update while we are migrating to the new message-sending logic,
/// as some price updates on pythnet may not be sent to other chains (because the message-sending
/// logic may not have triggered). We can solve this problem by making the message-sending mandatory
/// (which we can do once publishers have migrated over).
///
/// Additionally, this field may be equal to publish_time if the message is sent on a slot where
/// where the aggregation was unsuccesful. This problem will go away once all publishers have
/// migrated over to a recent version of pyth-agent.
pub prev_publish_time: i64,
pub ema_price: i64,
pub ema_conf: u64,
}

/// A price update account. This account is used by the Pyth Receiver program to store a verified price update from a Pyth price feed.
/// It contains:
/// - `write_authority`: The write authority for this account. This authority can close this account to reclaim rent or update the account to contain a different price update.
/// - `verification_level`: The [`VerificationLevel`] of this price update. This represents how many Wormhole guardian signatures have been verified for this price update.
/// - `price_message`: The actual price update.
/// - `posted_slot`: The slot at which this price update was posted.
#[derive(BorshSchema, BorshSerialize, BorshDeserialize)]
pub struct PriceUpdateV2 {
pub write_authority: Pubkey,
pub verification_level: VerificationLevel,
pub price_message: PriceFeedMessage,
pub posted_slot: u64,
}

impl PriceUpdateV2 {
pub const LEN: usize = 8 + 32 + 2 + 32 + 8 + 8 + 4 + 8 + 8 + 8 + 8 + 8;
}

0 comments on commit 3926e86

Please sign in to comment.