1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
//! Option<T> type for optional accounts.
//!
//! # Example
//! ```ignore
//! #[derive(Accounts)]
//! pub struct Example {
//! pub my_acc: Option<Account<'info, MyData>>
//! }
//! ```
use std::collections::BTreeSet;
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
use crate::{
error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas,
};
impl<'info, B, T: Accounts<'info, B>> Accounts<'info, B> for Option<T> {
fn try_accounts(
program_id: &Pubkey,
accounts: &mut &'info [AccountInfo<'info>],
ix_data: &[u8],
bumps: &mut B,
reallocs: &mut BTreeSet<Pubkey>,
) -> Result<Self> {
if accounts.is_empty() {
return if cfg!(feature = "allow-missing-optionals") {
// We don't care if accounts is empty (when this feature is active),
// so if that's the case we return None. This allows adding optional
// accounts at the end of the Accounts struct without causing a breaking
// change. This is safe and will error out if a required account is then
// added after the optional account and the accounts aren't passed in.
Ok(None)
} else {
// If the feature is inactive (it is off by default), then we error out
// like every other Account.
Err(ErrorCode::AccountNotEnoughKeys.into())
};
}
// If there are enough accounts, it will check the program_id and return
// None if it matches, popping the first account off the accounts vec.
if accounts[0].key == program_id {
*accounts = &accounts[1..];
Ok(None)
} else {
// If the program_id doesn't equal the account key, we default to
// the try_accounts implementation for the inner type and then wrap that with
// Some. This should handle all possible valid cases.
T::try_accounts(program_id, accounts, ix_data, bumps, reallocs).map(Some)
}
}
}
impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option<T> {
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
self.as_ref()
.map_or_else(Vec::new, |account| account.to_account_infos())
}
}
impl<T: ToAccountMetas> ToAccountMetas for Option<T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
self.as_ref()
.expect("Cannot run `to_account_metas` on None")
.to_account_metas(is_signer)
}
}
impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option<T> {
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
self.as_ref()
.map_or(Ok(()), |t| T::close(t, sol_destination))
}
}
impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option<T> {
fn exit(&self, program_id: &Pubkey) -> Result<()> {
self.as_ref().map_or(Ok(()), |t| t.exit(program_id))
}
}