use crate::anchor_traits::*;
use crate::cfg_client;
#[allow(unused_imports)]
use crate::impl_account_deserialize;
use crate::OnDemandError;
use rust_decimal::Decimal;
use sha2::{Digest, Sha256};
use solana_program::clock::Clock;
use solana_program::pubkey::Pubkey;
use std::cell::{Ref, RefMut};
use num::integer::Roots;
#[repr(C)]
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct PullFeedAccountData {
pub submissions: [OracleSubmission; 32],
pub authority: Pubkey,
pub queue: Pubkey,
pub feed_hash: [u8; 32],
pub initialized_at: i64,
pub permissions: u64,
pub max_variance: u64,
pub min_responses: u32,
padding1: [u8; 4],
pub _ebuf: [u8; 1024],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct OracleSubmission {
pub oracle: Pubkey,
pub slot: u64,
padding: [u8; 8],
pub value: i128,
}
impl OracleSubmission {
pub fn is_empty(&self) -> bool {
self.slot == 0
}
}
impl Discriminator for PullFeedAccountData {
const DISCRIMINATOR: [u8; 8] = [196, 27, 108, 196, 10, 215, 219, 40];
}
cfg_client! {
impl_account_deserialize!(PullFeedAccountData);
}
impl PullFeedAccountData {
pub fn parse<'info>(
data: Ref<'info, &mut [u8]>,
) -> Result<Ref<'info, Self>, OnDemandError> {
if data.len() < Self::discriminator().len() {
return Err(OnDemandError::InvalidDiscriminator);
}
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != Self::discriminator() {
return Err(OnDemandError::InvalidDiscriminator);
}
Ok(Ref::map(data, |data: &&mut [u8]| {
bytemuck::from_bytes(&data[8..std::mem::size_of::<Self>() + 8])
}))
}
pub fn parse_mut<'info>(
data: RefMut<'info, &mut [u8]>,
) -> Result<RefMut<'info, Self>, OnDemandError> {
if data.len() < Self::discriminator().len() {
return Err(OnDemandError::InvalidDiscriminator);
}
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != Self::discriminator() {
return Err(OnDemandError::InvalidDiscriminator);
}
Ok(RefMut::map(data, |data: &mut &mut [u8]| {
bytemuck::from_bytes_mut(&mut data[8..std::mem::size_of::<Self>() + 8])
}))
}
pub fn generate_checksum(&self, result: i128, slothash: [u8; 32]) -> [u8; 32] {
Self::generate_checksum_inner(
self.queue,
self.feed_hash,
result,
slothash,
self.max_variance,
self.min_responses,
)
}
pub fn generate_checksum_inner(
queue: Pubkey,
feed_hash: [u8; 32],
result: i128,
slothash: [u8; 32],
max_variance: u64,
min_responses: u32,
) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(queue.to_bytes());
hasher.update(feed_hash);
hasher.update(result.to_le_bytes());
hasher.update(slothash);
hasher.update(max_variance.to_le_bytes());
hasher.update(min_responses.to_le_bytes());
hasher.finalize().to_vec().try_into().unwrap()
}
pub fn generate_checksum_with_timestamp(
queue: Pubkey,
feed_hash: [u8; 32],
result: i128,
slothash: [u8; 32],
max_variance: u64,
min_responses: u32,
timestamp: u64,
) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(queue.to_bytes());
hasher.update(feed_hash);
hasher.update(result.to_le_bytes());
hasher.update(slothash);
hasher.update(max_variance.to_le_bytes());
hasher.update(min_responses.to_le_bytes());
hasher.update(timestamp.to_le_bytes());
hasher.finalize().to_vec().try_into().unwrap()
}
pub fn get_value(
&self,
clock: &Clock,
max_staleness: u64,
min_samples: u32,
only_positive: bool,
) -> Result<Decimal, OnDemandError> {
let submissions = self
.submissions
.iter()
.take_while(|s| !s.is_empty())
.filter(|s| s.slot > clock.slot - max_staleness)
.collect::<Vec<_>>();
if submissions.len() < min_samples as usize {
return Err(OnDemandError::NotEnoughSamples);
}
let median =
lower_bound_median(&mut submissions.iter().map(|s| s.value).collect::<Vec<_>>())
.ok_or(OnDemandError::NotEnoughSamples)?;
if only_positive && median <= 0 {
return Err(OnDemandError::IllegalFeedValue);
}
return Ok(Decimal::from_i128_with_scale(median, 18));
}
pub fn range(
&self,
clock: &Clock,
max_staleness: u64,
) -> Result<Decimal, OnDemandError> {
let mut submissions = self
.submissions
.iter()
.take_while(|s| !s.is_empty())
.filter(|s| s.slot > clock.slot - max_staleness)
.map(|s| s.value)
.collect::<Vec<_>>();
let median = lower_bound_median(&mut submissions)
.ok_or(OnDemandError::NotEnoughSamples)?;
let (min, max) = find_min_max(&submissions)
.ok_or(OnDemandError::NotEnoughSamples)?;
let lower_range = median - min;
let upper_range = max - median;
assert!(lower_range >= 0);
assert!(upper_range >= 0);
let range = std::cmp::max(lower_range, upper_range);
Ok(Decimal::from_i128_with_scale(range, 18))
}
pub fn std_deviation(
&self,
clock: &Clock,
max_staleness: u64,
) -> Result<Decimal, OnDemandError> {
let submissions = self
.submissions
.iter()
.take_while(|s| !s.is_empty())
.filter(|s| s.slot > clock.slot - max_staleness)
.map(|s| s.value)
.collect::<Vec<_>>();
standard_deviation(&submissions)
.ok_or(OnDemandError::NotEnoughSamples)
.map(|std_dev| Decimal::from_i128_with_scale(std_dev, 18))
}
}
fn standard_deviation(numbers: &[i128]) -> Option<i128> {
let len = numbers.len() as i128;
if len == 0 {
return None;
}
let mean = mean(numbers)?;
let variance: i128 = numbers.iter()
.map(|&value| {
let diff = value - mean;
diff * diff
})
.sum::<i128>() / len;
Some(variance.sqrt())
}
fn mean(numbers: &[i128]) -> Option<i128> {
let len = numbers.len() as i128;
if len == 0 {
return None;
}
let sum: i128 = numbers.iter().sum();
Some(sum / len)
}
pub fn lower_bound_median(numbers: &mut Vec<i128>) -> Option<i128> {
numbers.sort();
let len = numbers.len();
if len == 0 {
return None; }
Some(numbers[len / 2])
}
fn find_min_max(decimals: &[i128]) -> Option<(i128, i128)> {
let min = decimals.iter().min()?;
let max = decimals.iter().max()?;
Some((*min, *max))
}