Skip to main content

anchor_lang/accounts/
interface.rs

1//! Type validating that the account is one of a set of given Programs
2
3use {
4    crate::{
5        accounts::program::Program,
6        error::{Error, ErrorCode},
7        solana_program::{account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey},
8        AccountDeserialize, Accounts, AccountsExit, CheckId, Key, Result, ToAccountInfos,
9        ToAccountMetas,
10    },
11    std::{collections::BTreeSet, ops::Deref},
12};
13
14/// Type validating that the account is one of a set of given Programs
15///
16/// The `Interface` wraps over [`Program`], allowing for
17/// multiple possible program ids. Useful for any program that implements an
18/// instruction interface. For example, spl-token and spl-token-2022 both implement
19/// the spl-token interface.
20///
21/// # Table of Contents
22/// - [Basic Functionality](#basic-functionality)
23/// - [Out of the Box Types](#out-of-the-box-types)
24///
25/// # Basic Functionality
26///
27/// Checks:
28///
29/// - `expected_programs.contains(account_info.key)`
30/// - `account_info.executable == true`
31///
32/// # Example
33/// ```ignore
34/// #[program]
35/// mod my_program {
36///     fn set_admin_settings(...){...}
37/// }
38///
39/// #[account]
40/// #[derive(Default)]
41/// pub struct AdminSettings {
42///     ...
43/// }
44///
45/// #[derive(Accounts)]
46/// pub struct SetAdminSettings<'info> {
47///     #[account(mut, seeds = [b"admin"], bump)]
48///     pub admin_settings: Account<'info, AdminSettings>,
49///     #[account(constraint = program.programdata_address()? == Some(program_data.key()))]
50///     pub program: Interface<'info, MyProgram>,
51///     #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
52///     pub program_data: Account<'info, ProgramData>,
53///     pub authority: Signer<'info>,
54/// }
55/// ```
56/// The given program has a function with which the upgrade authority can set admin settings.
57///
58/// The required constraints are as follows:
59///
60/// - `program` is the account of the program itself.
61///   Its constraint checks that `program_data` is the account that contains the program's upgrade authority.
62///   Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()?`
63///   will be `None` if it's not).
64/// - `program_data`'s constraint checks that its upgrade authority is the `authority` account.
65/// - Finally, `authority` needs to sign the transaction.
66///
67/// # Out of the Box Types
68///
69/// Between the [`anchor_lang`](https://docs.rs/anchor-lang/latest/anchor_lang) and [`anchor_spl`](https://docs.rs/anchor_spl/latest/anchor_spl) crates,
70/// the following `Interface` types are provided out of the box:
71///
72/// - [`TokenInterface`](https://docs.rs/anchor-spl/latest/anchor_spl/token_interface/struct.TokenInterface.html)
73///
74#[derive(Clone)]
75pub struct Interface<'info, T>(Program<'info, T>);
76impl<'a, T> Interface<'a, T> {
77    pub(crate) fn new(info: &'a AccountInfo<'a>) -> Self {
78        Self(Program::new(info))
79    }
80    pub fn programdata_address(&self) -> Result<Option<Pubkey>> {
81        self.0.programdata_address()
82    }
83}
84impl<'a, T: CheckId> TryFrom<&'a AccountInfo<'a>> for Interface<'a, T> {
85    type Error = Error;
86    /// Deserializes the given `info` into a `Program`.
87    fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
88        T::check_id(info.key)?;
89        if !info.executable {
90            return Err(ErrorCode::InvalidProgramExecutable.into());
91        }
92        Ok(Self::new(info))
93    }
94}
95impl<'info, T> Deref for Interface<'info, T> {
96    type Target = AccountInfo<'info>;
97    fn deref(&self) -> &Self::Target {
98        &self.0
99    }
100}
101impl<'info, T> AsRef<AccountInfo<'info>> for Interface<'info, T> {
102    fn as_ref(&self) -> &AccountInfo<'info> {
103        &self.0
104    }
105}
106
107impl<'info, B, T: CheckId> Accounts<'info, B> for Interface<'info, T> {
108    #[inline(never)]
109    fn try_accounts(
110        _program_id: &Pubkey,
111        accounts: &mut &'info [AccountInfo<'info>],
112        _ix_data: &[u8],
113        _bumps: &mut B,
114        _reallocs: &mut BTreeSet<Pubkey>,
115    ) -> Result<Self> {
116        if accounts.is_empty() {
117            return Err(ErrorCode::AccountNotEnoughKeys.into());
118        }
119        let account = &accounts[0];
120        *accounts = &accounts[1..];
121        Self::try_from(account)
122    }
123}
124
125impl<T> ToAccountMetas for Interface<'_, T> {
126    fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
127        self.0.to_account_metas(is_signer)
128    }
129}
130
131impl<'info, T> ToAccountInfos<'info> for Interface<'info, T> {
132    fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
133        self.0.to_account_infos()
134    }
135}
136
137impl<'info, T: AccountDeserialize> AccountsExit<'info> for Interface<'info, T> {}
138
139impl<T: AccountDeserialize> Key for Interface<'_, T> {
140    fn key(&self) -> Pubkey {
141        self.0.key()
142    }
143}