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