Skip to main content

anchor_lang/accounts/
sysvar.rs

1//! Type validating that the account is a sysvar and deserializing it
2
3use {
4    crate::{
5        error::ErrorCode,
6        solana_program::{account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey},
7        Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas,
8    },
9    solana_sysvar::{Sysvar as SolanaSysvar, SysvarSerialize as SolanaSysvarSerialize},
10    std::{
11        collections::BTreeSet,
12        fmt,
13        ops::{Deref, DerefMut},
14    },
15};
16
17/// Type validating that the account is a sysvar and deserializing it.
18///
19/// If possible, sysvars should not be used via accounts
20/// but by using the [`get`](https://docs.rs/solana-program/latest/solana_program/sysvar/trait.Sysvar.html#method.get)
21/// function on the desired sysvar. This is because using `get`
22/// does not run the risk of Anchor having a bug in its `Sysvar` type
23/// and using `get` also decreases tx size, making space for other
24/// accounts that cannot be requested via syscall.
25///
26/// # Example
27/// ```ignore
28/// // OK - via account in the account validation struct
29/// #[derive(Accounts)]
30/// pub struct Example<'info> {
31///     pub clock: Sysvar<'info, Clock>
32/// }
33/// // BETTER - via syscall in the instruction function
34/// fn better(ctx: Context<Better>) -> Result<()> {
35///     let clock = Clock::get()?;
36/// }
37/// ```
38pub struct Sysvar<'info, T: SolanaSysvar> {
39    info: &'info AccountInfo<'info>,
40    account: T,
41}
42
43impl<T: SolanaSysvarSerialize + fmt::Debug> fmt::Debug for Sysvar<'_, T> {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        f.debug_struct("Sysvar")
46            .field("info", &self.info)
47            .field("account", &self.account)
48            .finish()
49    }
50}
51
52impl<'info, T: SolanaSysvarSerialize> Sysvar<'info, T> {
53    pub fn from_account_info(acc_info: &'info AccountInfo<'info>) -> Result<Sysvar<'info, T>> {
54        match T::from_account_info(acc_info) {
55            Ok(val) => Ok(Sysvar {
56                info: acc_info,
57                account: val,
58            }),
59            Err(_) => Err(ErrorCode::AccountSysvarMismatch.into()),
60        }
61    }
62}
63
64impl<T: SolanaSysvarSerialize> Clone for Sysvar<'_, T> {
65    fn clone(&self) -> Self {
66        Self {
67            info: self.info,
68            account: T::from_account_info(self.info).unwrap(),
69        }
70    }
71}
72
73impl<'info, B, T: SolanaSysvarSerialize> Accounts<'info, B> for Sysvar<'info, T> {
74    fn try_accounts(
75        _program_id: &Pubkey,
76        accounts: &mut &'info [AccountInfo<'info>],
77        _ix_data: &[u8],
78        _bumps: &mut B,
79        _reallocs: &mut BTreeSet<Pubkey>,
80    ) -> Result<Self> {
81        if accounts.is_empty() {
82            return Err(ErrorCode::AccountNotEnoughKeys.into());
83        }
84        let account = &accounts[0];
85        *accounts = &accounts[1..];
86        Sysvar::from_account_info(account)
87    }
88}
89
90impl<T: SolanaSysvarSerialize> ToAccountMetas for Sysvar<'_, T> {
91    fn to_account_metas(&self, _is_signer: Option<bool>) -> Vec<AccountMeta> {
92        vec![AccountMeta::new_readonly(*self.info.key, false)]
93    }
94}
95
96impl<'info, T: SolanaSysvarSerialize> ToAccountInfos<'info> for Sysvar<'info, T> {
97    fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
98        vec![self.info.clone()]
99    }
100}
101
102impl<'info, T: SolanaSysvarSerialize> AsRef<AccountInfo<'info>> for Sysvar<'info, T> {
103    fn as_ref(&self) -> &AccountInfo<'info> {
104        self.info
105    }
106}
107
108impl<T: SolanaSysvarSerialize> Deref for Sysvar<'_, T> {
109    type Target = T;
110
111    fn deref(&self) -> &Self::Target {
112        &self.account
113    }
114}
115
116impl<T: SolanaSysvarSerialize> DerefMut for Sysvar<'_, T> {
117    fn deref_mut(&mut self) -> &mut Self::Target {
118        &mut self.account
119    }
120}
121
122impl<'info, T: SolanaSysvarSerialize> AccountsExit<'info> for Sysvar<'info, T> {}
123
124impl<T: SolanaSysvarSerialize> Key for Sysvar<'_, T> {
125    fn key(&self) -> Pubkey {
126        *self.info.key
127    }
128}