Skip to main content

anchor_lang/accounts/
interface_account.rs

1//! Account container that checks ownership on deserialization.
2
3use {
4    crate::{
5        accounts::account::Account,
6        error::ErrorCode,
7        solana_program::{
8            account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey, system_program,
9        },
10        AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, CheckOwner,
11        Key, Owners, Result, ToAccountInfos, ToAccountMetas,
12    },
13    std::{
14        collections::BTreeSet,
15        fmt,
16        ops::{Deref, DerefMut},
17    },
18};
19
20/// Wrapper around [`AccountInfo`]
21/// that verifies program ownership and deserializes underlying data into a Rust type.
22///
23/// # Table of Contents
24/// - [Basic Functionality](#basic-functionality)
25/// - [Using InterfaceAccount with non-anchor types](#using-interface-account-with-non-anchor-types)
26/// - [Out of the box wrapper types](#out-of-the-box-wrapper-types)
27///
28/// # Basic Functionality
29///
30/// InterfaceAccount checks that `T::owners().contains(Account.info.owner)`.
31/// This means that the data type that Accounts wraps around (`=T`) needs to
32/// implement the [Owners trait](crate::Owners).
33/// The `#[account]` attribute implements the Owners trait for
34/// a struct using multiple `crate::ID`s declared by [`declareId`](crate::declare_id)
35/// in the same program. It follows that InterfaceAccount can also be used
36/// with a `T` that comes from a different program.
37///
38/// Checks:
39///
40/// - `T::owners().contains(InterfaceAccount.info.owner)`
41/// - `!(InterfaceAccount.info.owner == SystemProgram && InterfaceAccount.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: InterfaceAccount<'info, MyData> // checks that my_account.info.owner == Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
71///     pub auth_account: InterfaceAccount<'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 InterfaceAccount with non-anchor programs
87///
88/// InterfaceAccount 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 InterfaceAccount
91/// - implement the functions required by InterfaceAccount 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/// ### SPL Types
143///
144/// Anchor provides wrapper types to access accounts owned by the token programs. Use
145/// ```ignore
146/// use anchor_spl::token_interface::TokenAccount;
147///
148/// #[derive(Accounts)]
149/// pub struct Example {
150///     pub my_acc: InterfaceAccount<'info, TokenAccount>
151/// }
152/// ```
153/// to access token accounts and
154/// ```ignore
155/// use anchor_spl::token_interface::Mint;
156///
157/// #[derive(Accounts)]
158/// pub struct Example {
159///     pub my_acc: InterfaceAccount<'info, Mint>
160/// }
161/// ```
162/// to access mint accounts.
163#[derive(Clone)]
164pub struct InterfaceAccount<'info, T: AccountSerialize + AccountDeserialize + Clone> {
165    account: Account<'info, T>,
166    // The owner here is used to make sure that changes aren't incorrectly propagated
167    // to an account with a modified owner
168    owner: Pubkey,
169}
170
171impl<T: AccountSerialize + AccountDeserialize + Clone + fmt::Debug> fmt::Debug
172    for InterfaceAccount<'_, T>
173{
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        self.account.fmt_with_name("InterfaceAccount", f)
176    }
177}
178
179impl<'a, T: AccountSerialize + AccountDeserialize + Clone> InterfaceAccount<'a, T> {
180    fn new(info: &'a AccountInfo<'a>, account: T) -> Self {
181        let owner = *info.owner;
182        Self {
183            account: Account::new(info, account),
184            owner,
185        }
186    }
187
188    pub fn into_inner(self) -> T {
189        self.account.into_inner()
190    }
191
192    /// Sets the inner account.
193    ///
194    /// Instead of this:
195    /// ```ignore
196    /// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
197    ///     (*ctx.accounts.user_to_create).name = new_user.name;
198    ///     (*ctx.accounts.user_to_create).age = new_user.age;
199    ///     (*ctx.accounts.user_to_create).address = new_user.address;
200    /// }
201    /// ```
202    /// You can do this:
203    /// ```ignore
204    /// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
205    ///     ctx.accounts.user_to_create.set_inner(new_user);
206    /// }
207    /// ```
208    pub fn set_inner(&mut self, inner: T) {
209        self.account.set_inner(inner);
210    }
211}
212
213impl<'a, T: AccountSerialize + AccountDeserialize + CheckOwner + Clone> InterfaceAccount<'a, T> {
214    /// Deserializes the given `info` into an `InterfaceAccount`.
215    #[inline(never)]
216    pub fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
217        if info.owner == &system_program::ID && info.lamports() == 0 {
218            return Err(ErrorCode::AccountNotInitialized.into());
219        }
220        T::check_owner(info.owner)?;
221        let mut data: &[u8] = &info.try_borrow_data()?;
222        Ok(Self::new(info, T::try_deserialize(&mut data)?))
223    }
224
225    /// Deserializes the given `info` into an `InterfaceAccount` without checking the account
226    /// discriminator. Be careful when using this and avoid it if possible.
227    #[inline(never)]
228    pub fn try_from_unchecked(info: &'a AccountInfo<'a>) -> Result<Self> {
229        if info.owner == &system_program::ID && info.lamports() == 0 {
230            return Err(ErrorCode::AccountNotInitialized.into());
231        }
232        T::check_owner(info.owner)?;
233        let mut data: &[u8] = &info.try_borrow_data()?;
234        Ok(Self::new(info, T::try_deserialize_unchecked(&mut data)?))
235    }
236
237    /// Reloads the account from storage. This is useful, for example, when observing side effects
238    /// after CPI.
239    ///
240    /// This method also validates that the account is owned by one of the expected programs.
241    pub fn reload(&mut self) -> Result<()> {
242        let info: &AccountInfo = self.account.as_ref();
243        T::check_owner(info.owner)?;
244
245        // Re-deserialize fresh data into the inner account.
246        self.account.set_inner({
247            let mut data: &[u8] = &info.try_borrow_data()?;
248            T::try_deserialize(&mut data)?
249        });
250        Ok(())
251    }
252}
253
254impl<'info, B, T: AccountSerialize + AccountDeserialize + CheckOwner + Clone> Accounts<'info, B>
255    for InterfaceAccount<'info, T>
256{
257    #[inline(never)]
258    fn try_accounts(
259        _program_id: &Pubkey,
260        accounts: &mut &'info [AccountInfo<'info>],
261        _ix_data: &[u8],
262        _bumps: &mut B,
263        _reallocs: &mut BTreeSet<Pubkey>,
264    ) -> Result<Self> {
265        if accounts.is_empty() {
266            return Err(ErrorCode::AccountNotEnoughKeys.into());
267        }
268        let account = &accounts[0];
269        *accounts = &accounts[1..];
270        Self::try_from(account)
271    }
272}
273
274impl<'info, T: AccountSerialize + AccountDeserialize + Owners + Clone> AccountsExit<'info>
275    for InterfaceAccount<'info, T>
276{
277    fn exit(&self, program_id: &Pubkey) -> Result<()> {
278        self.account
279            .exit_with_expected_owner(&self.owner, program_id)
280    }
281}
282
283impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsClose<'info>
284    for InterfaceAccount<'info, T>
285{
286    fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
287        self.account.close(sol_destination)
288    }
289}
290
291impl<T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas for InterfaceAccount<'_, T> {
292    fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
293        self.account.to_account_metas(is_signer)
294    }
295}
296
297impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info>
298    for InterfaceAccount<'info, T>
299{
300    fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
301        self.account.to_account_infos()
302    }
303}
304
305impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef<AccountInfo<'info>>
306    for InterfaceAccount<'info, T>
307{
308    fn as_ref(&self) -> &AccountInfo<'info> {
309        self.account.as_ref()
310    }
311}
312
313impl<T: AccountSerialize + AccountDeserialize + Clone> AsRef<T> for InterfaceAccount<'_, T> {
314    fn as_ref(&self) -> &T {
315        self.account.as_ref()
316    }
317}
318
319impl<T: AccountSerialize + AccountDeserialize + Clone> Deref for InterfaceAccount<'_, T> {
320    type Target = T;
321
322    fn deref(&self) -> &Self::Target {
323        self.account.deref()
324    }
325}
326
327impl<T: AccountSerialize + AccountDeserialize + Clone> DerefMut for InterfaceAccount<'_, T> {
328    fn deref_mut(&mut self) -> &mut Self::Target {
329        self.account.deref_mut()
330    }
331}
332
333impl<T: AccountSerialize + AccountDeserialize + Clone> Key for InterfaceAccount<'_, T> {
334    fn key(&self) -> Pubkey {
335        self.account.key()
336    }
337}