#![deprecated = "This crate has been deprecated. Please use pyth-sdk-solana instead."]
pub use self::price_conf::PriceConf;
pub use self::error::PythError;
mod entrypoint;
mod error;
mod price_conf;
pub mod processor;
pub mod instruction;
use std::mem::size_of;
use borsh::{BorshSerialize, BorshDeserialize};
use bytemuck::{
cast_slice, from_bytes, try_cast_slice,
Pod, PodCastError, Zeroable,
};
#[cfg(target_arch = "bpf")]
use solana_program::{clock::Clock, sysvar::Sysvar};
solana_program::declare_id!("PythC11111111111111111111111111111111111111");
pub const MAGIC : u32 = 0xa1b2c3d4;
pub const VERSION_2 : u32 = 2;
pub const VERSION : u32 = VERSION_2;
pub const MAP_TABLE_SIZE : usize = 640;
pub const PROD_ACCT_SIZE : usize = 512;
pub const PROD_HDR_SIZE : usize = 48;
pub const PROD_ATTR_SIZE : usize = PROD_ACCT_SIZE - PROD_HDR_SIZE;
pub const MAX_SLOT_DIFFERENCE : u64 = 25;
#[derive(Copy, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub enum AccountType
{
Unknown,
Mapping,
Product,
Price
}
impl Default for AccountType {
fn default() -> Self {
AccountType::Unknown
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub enum PriceStatus
{
Unknown,
Trading,
Halted,
Auction
}
impl Default for PriceStatus {
fn default() -> Self {
PriceStatus::Unknown
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub enum CorpAction
{
NoCorpAct
}
impl Default for CorpAction {
fn default() -> Self {
CorpAction::NoCorpAct
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub enum PriceType
{
Unknown,
Price
}
impl Default for PriceType {
fn default() -> Self {
PriceType::Unknown
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub struct AccKey
{
pub val: [u8;32]
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Mapping
{
pub magic : u32,
pub ver : u32,
pub atype : u32,
pub size : u32,
pub num : u32,
pub unused : u32,
pub next : AccKey,
pub products : [AccKey;MAP_TABLE_SIZE]
}
#[cfg(target_endian = "little")]
unsafe impl Zeroable for Mapping {}
#[cfg(target_endian = "little")]
unsafe impl Pod for Mapping {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Product
{
pub magic : u32,
pub ver : u32,
pub atype : u32,
pub size : u32,
pub px_acc : AccKey,
pub attr : [u8;PROD_ATTR_SIZE]
}
impl Product {
pub fn iter(&self) -> AttributeIter {
AttributeIter { attrs: &self.attr }
}
}
#[cfg(target_endian = "little")]
unsafe impl Zeroable for Product {}
#[cfg(target_endian = "little")]
unsafe impl Pod for Product {}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub struct PriceInfo
{
pub price : i64,
pub conf : u64,
pub status : PriceStatus,
pub corp_act : CorpAction,
pub pub_slot : u64
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub struct PriceComp
{
pub publisher : AccKey,
pub agg : PriceInfo,
pub latest : PriceInfo
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub struct Ema
{
pub val : i64,
pub numer : i64,
pub denom : i64
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[repr(C)]
pub struct Price
{
pub magic : u32,
pub ver : u32,
pub atype : u32,
pub size : u32,
pub ptype : PriceType,
pub expo : i32,
pub num : u32,
pub num_qt : u32,
pub last_slot : u64,
pub valid_slot : u64,
pub ema_price : Ema,
pub ema_confidence : Ema,
pub drv1 : i64,
pub drv2 : i64,
pub prod : AccKey,
pub next : AccKey,
pub prev_slot : u64,
pub prev_price : i64,
pub prev_conf : u64,
pub drv3 : i64,
pub agg : PriceInfo,
pub comp : [PriceComp;32]
}
#[cfg(target_endian = "little")]
unsafe impl Zeroable for Price {}
#[cfg(target_endian = "little")]
unsafe impl Pod for Price {}
impl Price {
pub fn get_current_price_status(&self) -> PriceStatus {
#[cfg(target_arch = "bpf")]
if matches!(self.agg.status, PriceStatus::Trading) &&
Clock::get().unwrap().slot - self.agg.pub_slot > MAX_SLOT_DIFFERENCE {
return PriceStatus::Unknown;
}
self.agg.status
}
pub fn get_current_price(&self) -> Option<PriceConf> {
if !matches!(self.get_current_price_status(), PriceStatus::Trading) {
None
} else {
Some(PriceConf {
price: self.agg.price,
conf: self.agg.conf,
expo: self.expo
})
}
}
pub fn get_ema_price(&self) -> Option<PriceConf> {
Some(PriceConf { price: self.ema_price.val, conf: self.ema_confidence.val as u64, expo: self.expo })
}
pub fn get_price_in_quote(&self, quote: &Price, result_expo: i32) -> Option<PriceConf> {
return match (self.get_current_price(), quote.get_current_price()) {
(Some(base_price_conf), Some(quote_price_conf)) =>
base_price_conf.div("e_price_conf)?.scale_to_exponent(result_expo),
(_, _) => None,
}
}
pub fn price_basket(amounts: &[(Price, i64, i32)], result_expo: i32) -> Option<PriceConf> {
assert!(amounts.len() > 0);
let mut res = PriceConf { price: 0, conf: 0, expo: result_expo };
for i in 0..amounts.len() {
res = res.add(
&amounts[i].0.get_current_price()?.cmul(amounts[i].1, amounts[i].2)?.scale_to_exponent(result_expo)?
)?
}
Some(res)
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct AccKeyU64
{
pub val: [u64;4]
}
#[cfg(target_endian = "little")]
unsafe impl Zeroable for AccKeyU64 {}
#[cfg(target_endian = "little")]
unsafe impl Pod for AccKeyU64 {}
impl AccKey
{
pub fn is_valid( &self ) -> bool {
match load::<AccKeyU64>( &self.val ) {
Ok(k8) => k8.val[0]!=0 || k8.val[1]!=0 || k8.val[2]!=0 || k8.val[3]!=0,
Err(_) => false,
}
}
}
fn load<T: Pod>(data: &[u8]) -> Result<&T, PodCastError> {
let size = size_of::<T>();
if data.len() >= size {
Ok(from_bytes(cast_slice::<u8, u8>(try_cast_slice(
&data[0..size],
)?)))
} else {
Err(PodCastError::SizeMismatch)
}
}
pub fn load_mapping(data: &[u8]) -> Result<&Mapping, PythError> {
let pyth_mapping = load::<Mapping>(&data).map_err(|_| PythError::InvalidAccountData)?;
if pyth_mapping.magic != MAGIC {
return Err(PythError::InvalidAccountData);
}
if pyth_mapping.ver != VERSION_2 {
return Err(PythError::BadVersionNumber);
}
if pyth_mapping.atype != AccountType::Mapping as u32 {
return Err(PythError::WrongAccountType);
}
return Ok(pyth_mapping);
}
pub fn load_product(data: &[u8]) -> Result<&Product, PythError> {
let pyth_product = load::<Product>(&data).map_err(|_| PythError::InvalidAccountData)?;
if pyth_product.magic != MAGIC {
return Err(PythError::InvalidAccountData);
}
if pyth_product.ver != VERSION_2 {
return Err(PythError::BadVersionNumber);
}
if pyth_product.atype != AccountType::Product as u32 {
return Err(PythError::WrongAccountType);
}
return Ok(pyth_product);
}
pub fn load_price(data: &[u8]) -> Result<&Price, PythError> {
let pyth_price = load::<Price>(&data).map_err(|_| PythError::InvalidAccountData)?;
if pyth_price.magic != MAGIC {
return Err(PythError::InvalidAccountData);
}
if pyth_price.ver != VERSION_2 {
return Err(PythError::BadVersionNumber);
}
if pyth_price.atype != AccountType::Price as u32 {
return Err(PythError::WrongAccountType);
}
return Ok(pyth_price);
}
pub struct AttributeIter<'a> {
attrs: &'a [u8],
}
impl<'a> Iterator for AttributeIter<'a> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<Self::Item> {
if self.attrs.is_empty() {
return None;
}
let (key, data) = get_attr_str(self.attrs);
let (val, data) = get_attr_str(data);
self.attrs = data;
return Some((key, val));
}
}
fn get_attr_str(buf: &[u8]) -> (&str, &[u8]) {
if buf.is_empty() {
return ("", &[]);
}
let len = buf[0] as usize;
let str = std::str::from_utf8(&buf[1..len + 1]).expect("attr should be ascii or utf-8");
let remaining_buf = &buf[len + 1..];
(str, remaining_buf)
}