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
use crate::prelude::*;
use std::cell::Ref;

#[derive(Copy, Clone, AnchorSerialize, AnchorDeserialize)]
pub enum OracleResponseType {
    TypeSuccess,
    TypeError,
    TypeDisagreement,
    TypeNoResponse,
}

#[zero_copy(unsafe)]
#[derive(Default)]
#[repr(packed)]
pub struct OracleMetrics {
    /// Number of consecutive successful update request.
    pub consecutive_success: u64,
    /// Number of consecutive update request that resulted in an error.
    pub consecutive_error: u64,
    /// Number of consecutive update request that resulted in a disagreement with the accepted median result.
    pub consecutive_disagreement: u64,
    /// Number of consecutive update request that were posted on-chain late and not included in an accepted result.
    pub consecutive_late_response: u64,
    /// Number of consecutive update request that resulted in a failure.
    pub consecutive_failure: u64,
    /// Total number of successful update request.
    pub total_success: u128,
    /// Total number of update request that resulted in an error.
    pub total_error: u128,
    /// Total number of update request that resulted in a disagreement with the accepted median result.
    pub total_disagreement: u128,
    /// Total number of update request that were posted on-chain late and not included in an accepted result.
    pub total_late_response: u128,
}

#[account(zero_copy(unsafe))]
#[repr(packed)]
pub struct OracleAccountData {
    /// Name of the oracle to store on-chain.
    pub name: [u8; 32],
    /// Metadata of the oracle to store on-chain.
    pub metadata: [u8; 128],
    /// The account delegated as the authority for making account changes or withdrawing funds from a staking wallet.
    pub oracle_authority: Pubkey,
    /// Unix timestamp when the oracle last heartbeated
    pub last_heartbeat: i64,
    /// Flag dictating if an oracle is active and has heartbeated before the queue's oracle timeout parameter.
    pub num_in_use: u32,
    // Must be unique per oracle account and authority should be a pda
    /// Stake account and reward/slashing wallet.
    pub token_account: Pubkey,
    /// Public key of the oracle queue who has granted it permission to use its resources.
    pub queue_pubkey: Pubkey,
    /// Oracle track record.
    pub metrics: OracleMetrics,
    /// The PDA bump to derive the pubkey.
    pub bump: u8,
    /// Reserved for future info.
    pub _ebuf: [u8; 255],
}

impl OracleAccountData {
    pub fn size() -> usize {
        8 + std::mem::size_of::<OracleAccountData>()
    }

    /// Returns the deserialized Switchboard Oracle account
    ///
    /// # Arguments
    ///
    /// * `account_info` - A Solana AccountInfo referencing an existing Switchboard Oracle
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use switchboard_solana::OracleAccountData;
    ///
    /// let oracle = OracleAccountData::new(oracle_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() < OracleAccountData::discriminator().len() {
            return Err(ErrorCode::AccountDiscriminatorNotFound.into());
        }

        let mut disc_bytes = [0u8; 8];
        disc_bytes.copy_from_slice(&data[..8]);
        if disc_bytes != OracleAccountData::discriminator() {
            return Err(ErrorCode::AccountDiscriminatorMismatch.into());
        }

        Ok(Ref::map(data, |data| {
            bytemuck::from_bytes(&data[8..std::mem::size_of::<OracleAccountData>() + 8])
        }))
    }

    /// Returns the deserialized Switchboard Oracle account
    ///
    /// # Arguments
    ///
    /// * `data` - A Solana AccountInfo's data buffer
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use switchboard_solana::OracleAccountData;
    ///
    /// let oracle = OracleAccountData::new(oracle_account_info.try_borrow_data()?)?;
    /// ```
    pub fn new_from_bytes(data: &[u8]) -> anchor_lang::Result<&OracleAccountData> {
        if data.len() < OracleAccountData::discriminator().len() {
            return Err(ErrorCode::AccountDiscriminatorNotFound.into());
        }

        let mut disc_bytes = [0u8; 8];
        disc_bytes.copy_from_slice(&data[..8]);
        if disc_bytes != OracleAccountData::discriminator() {
            return Err(ErrorCode::AccountDiscriminatorMismatch.into());
        }

        Ok(bytemuck::from_bytes(
            &data[8..std::mem::size_of::<OracleAccountData>() + 8],
        ))
    }
}