Skip to main content

anchor_lang/accounts/
account.rs

1//! Account container that checks ownership on deserialization.
2
3use {
4    crate::{
5        bpf_writer::BpfWriter,
6        error::{Error, ErrorCode},
7        solana_program::{
8            account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey, system_program,
9        },
10        AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner,
11        Result, ToAccountInfos, ToAccountMetas,
12    },
13    std::{
14        collections::BTreeSet,
15        fmt,
16        ops::{Deref, DerefMut},
17    },
18};
19
20/// Wrapper around [`AccountInfo`] that verifies program ownership
21/// and deserializes underlying data into a Rust type.
22///
23/// # Table of Contents
24/// - [Basic Functionality](#basic-functionality)
25/// - [Using Account with non-anchor types](#using-account-with-non-anchor-types)
26/// - [Out of the box wrapper types](#out-of-the-box-wrapper-types)
27///
28/// # Basic Functionality
29///
30/// Account checks that `Account.info.owner == T::owner()`.
31/// This means that the data type that Accounts wraps around (`=T`) needs to
32/// implement the [Owner trait](crate::Owner).
33/// The `#[account]` attribute implements the Owner trait for
34/// a struct using the `crate::ID` declared by [`declare_id`](crate::declare_id)
35/// in the same program. It follows that Account can also be used
36/// with a `T` that comes from a different program.
37///
38/// Checks:
39///
40/// - `Account.info.owner == T::owner()`
41/// - `!(Account.info.owner == SystemProgram && Account.info.lamports() == 0)`
42///
43/// # Example
44/// ```ignore
45/// use anchor_lang::prelude::*;
46/// use other_program::Auth;
47///
48/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
49///
50/// #[program]
51/// mod hello_anchor {
52///     use super::*;
53///     pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
54///         if (*ctx.accounts.auth_account).authorized {
55///             (*ctx.accounts.my_account).data = data;
56///         }
57///         Ok(())
58///     }
59/// }
60///
61/// #[account]
62/// #[derive(Default)]
63/// pub struct MyData {
64///     pub data: u64
65/// }
66///
67/// #[derive(Accounts)]
68/// pub struct SetData<'info> {
69///     #[account(mut)]
70///     pub my_account: Account<'info, MyData> // checks that my_account.info.owner == Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
71///     pub auth_account: Account<'info, Auth> // checks that auth_account.info.owner == FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9
72/// }
73///
74/// // In a different program
75///
76/// ...
77/// declare_id!("FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9");
78/// #[account]
79/// #[derive(Default)]
80/// pub struct Auth {
81///     pub authorized: bool
82/// }
83/// ...
84/// ```
85///
86/// # Using Account with non-anchor programs
87///
88/// Account can also be used with non-anchor programs. The data types from
89/// those programs are not annotated with `#[account]` so you have to
90/// - create a wrapper type around the structs you want to wrap with Account
91/// - implement the functions required by Account yourself
92///
93/// instead of using `#[account]`. You only have to implement a fraction of the
94/// functions `#[account]` generates. See the example below for the code you have
95/// to write.
96///
97/// The mint wrapper type that Anchor provides out of the box for the token program ([source](https://github.com/solana-foundation/anchor/blob/master/spl/src/token.rs))
98/// ```ignore
99/// #[derive(Clone)]
100/// pub struct Mint(spl_token::state::Mint);
101///
102/// // This is necessary so we can use "anchor_spl::token::Mint::LEN"
103/// // because rust does not resolve "anchor_spl::token::Mint::LEN" to
104/// // "spl_token::state::Mint::LEN" automatically
105/// impl Mint {
106///     pub const LEN: usize = spl_token::state::Mint::LEN;
107/// }
108///
109/// // You don't have to implement the "try_deserialize" function
110/// // from this trait. It delegates to
111/// // "try_deserialize_unchecked" by default which is what we want here
112/// // because non-anchor accounts don't have a discriminator to check
113/// impl anchor_lang::AccountDeserialize for Mint {
114///     fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
115///         spl_token::state::Mint::unpack(buf).map(Mint)
116///     }
117/// }
118/// // AccountSerialize defaults to a no-op which is what we want here
119/// // because it's a foreign program, so our program does not
120/// // have permission to write to the foreign program's accounts anyway
121/// impl anchor_lang::AccountSerialize for Mint {}
122///
123/// impl anchor_lang::Owner for Mint {
124///     fn owner() -> Pubkey {
125///         // pub use spl_token::ID is used at the top of the file
126///         ID
127///     }
128/// }
129///
130/// // Implement the "std::ops::Deref" trait for better user experience
131/// impl Deref for Mint {
132///     type Target = spl_token::state::Mint;
133///
134///     fn deref(&self) -> &Self::Target {
135///         &self.0
136///     }
137/// }
138/// ```
139///
140/// ## Out of the box wrapper types
141///
142/// ### Accessing BPFUpgradeableLoader Data
143///
144/// Anchor provides wrapper types to access data stored in programs owned by the BPFUpgradeableLoader
145/// such as the upgrade authority. If you're interested in the data of a program account, you can use
146/// ```ignore
147/// Account<'info, BpfUpgradeableLoaderState>
148/// ```
149/// and then match on its contents inside your instruction function.
150///
151/// Alternatively, you can use
152/// ```ignore
153/// Account<'info, ProgramData>
154/// ```
155/// to let anchor do the matching for you and return the ProgramData variant of BpfUpgradeableLoaderState.
156///
157/// # Example
158/// ```ignore
159/// use anchor_lang::prelude::*;
160/// use crate::program::MyProgram;
161///
162/// declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
163///
164/// #[program]
165/// pub mod my_program {
166///     use super::*;
167///
168///     pub fn set_initial_admin(
169///         ctx: Context<SetInitialAdmin>,
170///         admin_key: Pubkey
171///     ) -> Result<()> {
172///         ctx.accounts.admin_settings.admin_key = admin_key;
173///         Ok(())
174///     }
175///
176///     pub fn set_admin(...){...}
177///
178///     pub fn set_settings(...){...}
179/// }
180///
181/// #[account]
182/// #[derive(Default, Debug)]
183/// pub struct AdminSettings {
184///     admin_key: Pubkey
185/// }
186///
187/// #[derive(Accounts)]
188/// pub struct SetInitialAdmin<'info> {
189///     #[account(init, payer = authority, seeds = [b"admin"], bump)]
190///     pub admin_settings: Account<'info, AdminSettings>,
191///     #[account(mut)]
192///     pub authority: Signer<'info>,
193///     #[account(constraint = program.programdata_address()? == Some(program_data.key()))]
194///     pub program: Program<'info, MyProgram>,
195///     #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
196///     pub program_data: Account<'info, ProgramData>,
197///     pub system_program: Program<'info, System>,
198/// }
199/// ```
200///
201/// This example solves a problem you may face if your program has admin settings: How do you set the
202/// admin key for restricted functionality after deployment? Setting the admin key itself should
203/// be a restricted action but how do you restrict it without having set an admin key?
204/// You're stuck in a loop.
205/// One solution is to use the upgrade authority of the program as the initial
206/// (or permanent) admin key.
207///
208/// ### SPL Types
209///
210/// Anchor provides wrapper types to access accounts owned by the token program. Use
211/// ```ignore
212/// use anchor_spl::token::TokenAccount;
213///
214/// #[derive(Accounts)]
215/// pub struct Example {
216///     pub my_acc: Account<'info, TokenAccount>
217/// }
218/// ```
219/// to access token accounts and
220/// ```ignore
221/// use anchor_spl::token::Mint;
222///
223/// #[derive(Accounts)]
224/// pub struct Example {
225///     pub my_acc: Account<'info, Mint>
226/// }
227/// ```
228/// to access mint accounts.
229#[derive(Clone)]
230pub struct Account<'info, T: AccountSerialize + AccountDeserialize + Clone> {
231    account: T,
232    info: &'info AccountInfo<'info>,
233}
234
235impl<T: AccountSerialize + AccountDeserialize + Clone + fmt::Debug> fmt::Debug for Account<'_, T> {
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        self.fmt_with_name("Account", f)
238    }
239}
240
241impl<T: AccountSerialize + AccountDeserialize + Clone + fmt::Debug> Account<'_, T> {
242    pub(crate) fn fmt_with_name(&self, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        f.debug_struct(name)
244            .field("account", &self.account)
245            .field("info", &self.info)
246            .finish()
247    }
248}
249
250impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Account<'a, T> {
251    pub(crate) fn new(info: &'a AccountInfo<'a>, account: T) -> Account<'a, T> {
252        Self { info, account }
253    }
254
255    pub(crate) fn exit_with_expected_owner(
256        &self,
257        expected_owner: &Pubkey,
258        program_id: &Pubkey,
259    ) -> Result<()> {
260        // Only persist if the owner is the current program and the account is not closed.
261        if expected_owner == program_id && !crate::common::is_closed(self.info) {
262            let mut data = self.info.try_borrow_mut_data()?;
263            let dst: &mut [u8] = &mut data;
264            let mut writer = BpfWriter::new(dst);
265            self.account.try_serialize(&mut writer)?;
266        }
267        Ok(())
268    }
269
270    pub fn into_inner(self) -> T {
271        self.account
272    }
273
274    /// Sets the inner account.
275    ///
276    /// Instead of this:
277    /// ```ignore
278    /// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
279    ///     (*ctx.accounts.user_to_create).name = new_user.name;
280    ///     (*ctx.accounts.user_to_create).age = new_user.age;
281    ///     (*ctx.accounts.user_to_create).address = new_user.address;
282    /// }
283    /// ```
284    /// You can do this:
285    /// ```ignore
286    /// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
287    ///     ctx.accounts.user_to_create.set_inner(new_user);
288    /// }
289    /// ```
290    pub fn set_inner(&mut self, inner: T) {
291        self.account = inner;
292    }
293}
294
295impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T> {
296    /// Reloads the account from storage. This is useful, for example, when
297    /// observing side effects after CPI.
298    ///
299    /// This method also re-validates that the program owner has not
300    /// changed since the initial validation
301    pub fn reload(&mut self) -> Result<()> {
302        if self.info.owner != &T::owner() {
303            return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
304                .with_pubkeys((*self.info.owner, T::owner())));
305        }
306
307        let mut data: &[u8] = &self.info.try_borrow_data()?;
308        self.account = T::try_deserialize(&mut data)?;
309        Ok(())
310    }
311
312    /// Deserializes the given `info` into a `Account`.
313    #[inline(never)]
314    pub fn try_from(info: &'a AccountInfo<'a>) -> Result<Account<'a, T>> {
315        if info.owner == &system_program::ID && info.lamports() == 0 {
316            return Err(ErrorCode::AccountNotInitialized.into());
317        }
318        if info.owner != &T::owner() {
319            return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
320                .with_pubkeys((*info.owner, T::owner())));
321        }
322        let mut data: &[u8] = &info.try_borrow_data()?;
323        Ok(Account::new(info, T::try_deserialize(&mut data)?))
324    }
325
326    /// Deserializes the given `info` into a `Account` without checking
327    /// the account discriminator. Be careful when using this and avoid it if
328    /// possible.
329    #[inline(never)]
330    pub fn try_from_unchecked(info: &'a AccountInfo<'a>) -> Result<Account<'a, T>> {
331        if info.owner == &system_program::ID && info.lamports() == 0 {
332            return Err(ErrorCode::AccountNotInitialized.into());
333        }
334        if info.owner != &T::owner() {
335            return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
336                .with_pubkeys((*info.owner, T::owner())));
337        }
338        let mut data: &[u8] = &info.try_borrow_data()?;
339        Ok(Account::new(info, T::try_deserialize_unchecked(&mut data)?))
340    }
341}
342
343impl<'info, B, T: AccountSerialize + AccountDeserialize + Owner + Clone> Accounts<'info, B>
344    for Account<'info, T>
345where
346    T: AccountSerialize + AccountDeserialize + Owner + Clone,
347{
348    #[inline(never)]
349    fn try_accounts(
350        _program_id: &Pubkey,
351        accounts: &mut &'info [AccountInfo<'info>],
352        _ix_data: &[u8],
353        _bumps: &mut B,
354        _reallocs: &mut BTreeSet<Pubkey>,
355    ) -> Result<Self> {
356        if accounts.is_empty() {
357            return Err(ErrorCode::AccountNotEnoughKeys.into());
358        }
359        let account = &accounts[0];
360        *accounts = &accounts[1..];
361        Account::try_from(account)
362    }
363}
364
365impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsExit<'info>
366    for Account<'info, T>
367{
368    fn exit(&self, program_id: &Pubkey) -> Result<()> {
369        self.exit_with_expected_owner(&T::owner(), program_id)
370    }
371}
372
373impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsClose<'info>
374    for Account<'info, T>
375{
376    fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
377        crate::common::close(self.as_ref(), sol_destination.as_ref())
378    }
379}
380
381impl<T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas for Account<'_, T> {
382    fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
383        let is_signer = is_signer.unwrap_or(self.info.is_signer);
384        let meta = match self.info.is_writable {
385            false => AccountMeta::new_readonly(*self.info.key, is_signer),
386            true => AccountMeta::new(*self.info.key, is_signer),
387        };
388        vec![meta]
389    }
390}
391
392impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info>
393    for Account<'info, T>
394{
395    fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
396        vec![self.info.clone()]
397    }
398}
399
400impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef<AccountInfo<'info>>
401    for Account<'info, T>
402{
403    fn as_ref(&self) -> &AccountInfo<'info> {
404        self.info
405    }
406}
407
408impl<T: AccountSerialize + AccountDeserialize + Clone> AsRef<T> for Account<'_, T> {
409    fn as_ref(&self) -> &T {
410        &self.account
411    }
412}
413
414impl<T: AccountSerialize + AccountDeserialize + Clone> Deref for Account<'_, T> {
415    type Target = T;
416
417    fn deref(&self) -> &Self::Target {
418        &(self).account
419    }
420}
421
422impl<T: AccountSerialize + AccountDeserialize + Clone> DerefMut for Account<'_, T> {
423    fn deref_mut(&mut self) -> &mut Self::Target {
424        #[cfg(feature = "anchor-debug")]
425        if !self.info.is_writable {
426            crate::solana_program::msg!("The given Account is not mutable");
427            panic!();
428        }
429        &mut self.account
430    }
431}
432
433impl<T: AccountSerialize + AccountDeserialize + Clone> Key for Account<'_, T> {
434    fn key(&self) -> Pubkey {
435        *self.info.key
436    }
437}