switchboard_solana/oracle_program/accounts/oracle.rs
1use crate::prelude::*;
2use std::cell::Ref;
3
4#[derive(Copy, Clone, AnchorSerialize, AnchorDeserialize)]
5pub enum OracleResponseType {
6 TypeSuccess,
7 TypeError,
8 TypeDisagreement,
9 TypeNoResponse,
10}
11
12#[zero_copy(unsafe)]
13#[derive(Default)]
14#[repr(packed)]
15pub struct OracleMetrics {
16 /// Number of consecutive successful update request.
17 pub consecutive_success: u64,
18 /// Number of consecutive update request that resulted in an error.
19 pub consecutive_error: u64,
20 /// Number of consecutive update request that resulted in a disagreement with the accepted median result.
21 pub consecutive_disagreement: u64,
22 /// Number of consecutive update request that were posted on-chain late and not included in an accepted result.
23 pub consecutive_late_response: u64,
24 /// Number of consecutive update request that resulted in a failure.
25 pub consecutive_failure: u64,
26 /// Total number of successful update request.
27 pub total_success: u128,
28 /// Total number of update request that resulted in an error.
29 pub total_error: u128,
30 /// Total number of update request that resulted in a disagreement with the accepted median result.
31 pub total_disagreement: u128,
32 /// Total number of update request that were posted on-chain late and not included in an accepted result.
33 pub total_late_response: u128,
34}
35
36#[account(zero_copy(unsafe))]
37#[repr(packed)]
38pub struct OracleAccountData {
39 /// Name of the oracle to store on-chain.
40 pub name: [u8; 32],
41 /// Metadata of the oracle to store on-chain.
42 pub metadata: [u8; 128],
43 /// The account delegated as the authority for making account changes or withdrawing funds from a staking wallet.
44 pub oracle_authority: Pubkey,
45 /// Unix timestamp when the oracle last heartbeated
46 pub last_heartbeat: i64,
47 /// Flag dictating if an oracle is active and has heartbeated before the queue's oracle timeout parameter.
48 pub num_in_use: u32,
49 // Must be unique per oracle account and authority should be a pda
50 /// Stake account and reward/slashing wallet.
51 pub token_account: Pubkey,
52 /// Public key of the oracle queue who has granted it permission to use its resources.
53 pub queue_pubkey: Pubkey,
54 /// Oracle track record.
55 pub metrics: OracleMetrics,
56 /// The PDA bump to derive the pubkey.
57 pub bump: u8,
58 /// Reserved for future info.
59 pub _ebuf: [u8; 255],
60}
61
62impl OracleAccountData {
63 pub fn size() -> usize {
64 8 + std::mem::size_of::<OracleAccountData>()
65 }
66
67 /// Returns the deserialized Switchboard Oracle account
68 ///
69 /// # Arguments
70 ///
71 /// * `account_info` - A Solana AccountInfo referencing an existing Switchboard Oracle
72 ///
73 /// # Examples
74 ///
75 /// ```ignore
76 /// use switchboard_solana::OracleAccountData;
77 ///
78 /// let oracle = OracleAccountData::new(oracle_account_info)?;
79 /// ```
80 pub fn new<'info>(
81 account_info: &'info AccountInfo<'info>,
82 ) -> anchor_lang::Result<Ref<'info, Self>> {
83 let data = account_info.try_borrow_data()?;
84 if data.len() < OracleAccountData::discriminator().len() {
85 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
86 }
87
88 let mut disc_bytes = [0u8; 8];
89 disc_bytes.copy_from_slice(&data[..8]);
90 if disc_bytes != OracleAccountData::discriminator() {
91 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
92 }
93
94 Ok(Ref::map(data, |data| {
95 bytemuck::from_bytes(&data[8..std::mem::size_of::<OracleAccountData>() + 8])
96 }))
97 }
98
99 /// Returns the deserialized Switchboard Oracle account
100 ///
101 /// # Arguments
102 ///
103 /// * `data` - A Solana AccountInfo's data buffer
104 ///
105 /// # Examples
106 ///
107 /// ```ignore
108 /// use switchboard_solana::OracleAccountData;
109 ///
110 /// let oracle = OracleAccountData::new(oracle_account_info.try_borrow_data()?)?;
111 /// ```
112 pub fn new_from_bytes(data: &[u8]) -> anchor_lang::Result<&OracleAccountData> {
113 if data.len() < OracleAccountData::discriminator().len() {
114 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
115 }
116
117 let mut disc_bytes = [0u8; 8];
118 disc_bytes.copy_from_slice(&data[..8]);
119 if disc_bytes != OracleAccountData::discriminator() {
120 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
121 }
122
123 Ok(bytemuck::from_bytes(
124 &data[8..std::mem::size_of::<OracleAccountData>() + 8],
125 ))
126 }
127}