1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
use crate::cfg_client;
use crate::prelude::*;
use bytemuck::try_cast_slice_mut;
use std::cell::Ref;
#[account(zero_copy(unsafe))]
#[repr(packed)]
pub struct OracleQueueAccountData {
/// Name of the queue to store on-chain.
pub name: [u8; 32],
/// Metadata of the queue to store on-chain.
pub metadata: [u8; 64],
/// The account delegated as the authority for making account changes or assigning permissions targeted at the queue.
pub authority: Pubkey,
/// Interval when stale oracles will be removed if they fail to heartbeat.
pub oracle_timeout: u32,
/// Rewards to provide oracles and round openers on this queue.
pub reward: u64,
/// The minimum amount of stake oracles must present to remain on the queue.
pub min_stake: u64,
/// Whether slashing is enabled on this queue.
pub slashing_enabled: bool,
/// The tolerated variance amount oracle results can have from the accepted round result before being slashed.
/// slashBound = varianceToleranceMultiplier * stdDeviation Default: 2
pub variance_tolerance_multiplier: SwitchboardDecimal,
/// Number of update rounds new feeds are on probation for.
/// If a feed returns 429s within probation period, auto disable permissions.
pub feed_probation_period: u32,
//
/// Current index of the oracle rotation.
pub curr_idx: u32,
/// Current number of oracles on a queue.
pub size: u32,
/// Garbage collection index.
pub gc_idx: u32,
/// Consecutive failure limit for a feed before feed permission is revoked.
pub consecutive_feed_failure_limit: u64,
/// Consecutive failure limit for an oracle before oracle permission is revoked.
pub consecutive_oracle_failure_limit: u64,
/// Enabling this setting means data feeds do not need explicit permission to join the queue and request new values from its oracles.
pub unpermissioned_feeds_enabled: bool,
/// Enabling this setting means VRF accounts do not need explicit permission to join the queue and request new values from its oracles.
pub unpermissioned_vrf_enabled: bool,
/// TODO: Revenue percentage rewarded to job curators overall.
pub curator_reward_cut: SwitchboardDecimal,
/// Prevent new leases from being funded n this queue.
/// Useful to turn down a queue for migrations, since authority is always immutable.
pub lock_lease_funding: bool,
/// Token mint used for the oracle queue rewards and slashing.
pub mint: Pubkey,
/// Whether oracles are permitted to fulfill buffer relayer update request.
pub enable_buffer_relayers: bool,
/// Reserved for future info.
pub _ebuf: [u8; 968],
/// Maximum number of oracles a queue can support.
pub max_size: u32,
/// The public key of the OracleQueueBuffer account holding a collection of Oracle pubkeys that haver successfully heartbeated before the queues `oracleTimeout`.
pub data_buffer: Pubkey,
}
impl Default for OracleQueueAccountData {
fn default() -> Self {
unsafe { std::mem::zeroed() }
}
}
impl OracleQueueAccountData {
pub fn size() -> usize {
std::mem::size_of::<OracleQueueAccountData>() + 8
}
pub fn convert_buffer(buf: &mut [u8]) -> &mut [Pubkey] {
try_cast_slice_mut(&mut buf[8..]).unwrap()
}
pub fn len(&self) -> u32 {
self.size
}
pub fn is_empty(&self) -> bool {
self.size == 0
}
pub fn get_mint(&self) -> Pubkey {
if self.mint == Pubkey::default() {
return anchor_spl::token::spl_token::ID;
}
self.mint
}
pub fn max_round_rewards(&self, batch_size: u32) -> u64 {
self.reward
.checked_mul(batch_size.checked_add(1).unwrap().into())
.unwrap()
}
/// Returns the deserialized Switchboard OracleQueue account
///
/// # Arguments
///
/// * `account_info` - A Solana AccountInfo referencing an existing Switchboard OracleQueue
///
/// # Examples
///
/// ```ignore
/// use switchboard_solana::OracleQueueAccountData;
///
/// let oracle_queue = OracleQueueAccountData::new(queue_account_info)?;
/// ```
pub fn new<'info>(
account_info: &'info AccountInfo<'info>,
) -> anchor_lang::Result<Ref<'info, Self>> {
let data = account_info.try_borrow_data()?;
if data.len() < OracleQueueAccountData::discriminator().len() {
return Err(ErrorCode::AccountDiscriminatorNotFound.into());
}
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != OracleQueueAccountData::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(Ref::map(data, |data| {
bytemuck::from_bytes(&data[8..std::mem::size_of::<OracleQueueAccountData>() + 8])
}))
}
/// Returns the deserialized Switchboard OracleQueue account
///
/// # Arguments
///
/// * `data` - A Solana AccountInfo's data buffer
///
/// # Examples
///
/// ```ignore
/// use switchboard_solana::OracleQueueAccountData;
///
/// let oracle_queue = OracleQueueAccountData::new(oracle_account_info.try_borrow_data()?)?;
/// ```
pub fn new_from_bytes(data: &[u8]) -> anchor_lang::Result<&OracleQueueAccountData> {
if data.len() < OracleQueueAccountData::discriminator().len() {
return Err(ErrorCode::AccountDiscriminatorNotFound.into());
}
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != OracleQueueAccountData::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(bytemuck::from_bytes(
&data[8..std::mem::size_of::<OracleQueueAccountData>() + 8],
))
}
cfg_client! {
pub fn fetch(
client: &solana_client::rpc_client::RpcClient,
pubkey: Pubkey,
) -> std::result::Result<Self, switchboard_common::SbError> {
crate::client::fetch_zerocopy_account(client, pubkey)
}
pub async fn fetch_async(
client: &solana_client::nonblocking::rpc_client::RpcClient,
pubkey: Pubkey,
) -> std::result::Result<Self, switchboard_common::SbError> {
crate::client::fetch_zerocopy_account_async(client, pubkey).await
}
pub fn fetch_sync<T: solana_sdk::client::SyncClient>(
client: &T,
pubkey: Pubkey,
) -> std::result::Result<Self, switchboard_common::SbError> {
crate::client::fetch_zerocopy_account_sync(client, pubkey)
}
pub async fn fetch_buffer(&self,
client: &solana_client::nonblocking::rpc_client::RpcClient,
) -> Result<Vec<Pubkey>, SwitchboardError> {
let buffer = client.get_account_data(&self.data_buffer).await
.map_err(|_| SwitchboardError::NetworkError)?;
let buffer: &[Pubkey] = bytemuck::try_cast_slice(&buffer.get(8..).unwrap_or(&[]))
.map_err(|_| SwitchboardError::AccountDeserializationError)?;
Ok(buffer[..self.size as usize].to_vec())
}
pub async fn fetch_garbage_collection_key(&self,
client: &solana_client::nonblocking::rpc_client::RpcClient,
) -> Result<Option<Pubkey>, SwitchboardError> {
let buf = self.fetch_buffer(client).await?;
let gc_oracle = *buf.get(self.gc_idx as usize).unwrap_or(&Pubkey::default());
if gc_oracle != Pubkey::default() {
Ok(Some(gc_oracle))
} else {
Ok(None)
}
}
}
}