#[cfg(RUSTC_WITH_SPECIALIZATION)] use solana_frozen_abi::abi_example::AbiExample; use { solana_sdk::{ account::{AccountSharedData, ReadableAccount}, account_utils::StateMut, instruction::InstructionError, pubkey::Pubkey, stake::state::{Delegation, StakeState}, }, std::marker::PhantomData, thiserror::Error, }; /// An account and a stake state deserialized from the account. /// Generic type T enforces type-safety so that StakeAccount can /// only wrap a stake-state which is a Delegation; whereas StakeAccount<()> /// wraps any account with stake state. #[derive(Clone, Debug, Default)] pub struct StakeAccount { account: AccountSharedData, stake_state: StakeState, _phantom: PhantomData, } #[derive(Debug, Error)] #[allow(clippy::enum_variant_names)] pub enum Error { #[error(transparent)] InstructionError(#[from] InstructionError), #[error("Invalid delegation: {0:?}")] InvalidDelegation(Box), #[error("Invalid stake account owner: {0}")] InvalidOwner(/*owner:*/ Pubkey), } impl StakeAccount { #[inline] pub(crate) fn lamports(&self) -> u64 { self.account.lamports() } #[inline] pub(crate) fn stake_state(&self) -> &StakeState { &self.stake_state } pub(crate) fn account(&self) -> &AccountSharedData { &self.account } } impl StakeAccount { #[inline] pub(crate) fn delegation(&self) -> Delegation { // Safe to unwrap here because StakeAccount will always // only wrap a stake-state which is a delegation. self.stake_state.delegation().unwrap() } } impl TryFrom for StakeAccount<()> { type Error = Error; fn try_from(account: AccountSharedData) -> Result { if account.owner() != &solana_stake_program::id() { return Err(Error::InvalidOwner(*account.owner())); } let stake_state = account.state()?; Ok(Self { account, stake_state, _phantom: PhantomData, }) } } impl TryFrom for StakeAccount { type Error = Error; fn try_from(account: AccountSharedData) -> Result { let stake_account = StakeAccount::<()>::try_from(account)?; if stake_account.stake_state.delegation().is_none() { return Err(Error::InvalidDelegation(Box::new( stake_account.stake_state, ))); } Ok(Self { account: stake_account.account, stake_state: stake_account.stake_state, _phantom: PhantomData, }) } } impl From> for StakeAccount<()> { #[inline] fn from(stake_account: StakeAccount) -> Self { Self { account: stake_account.account, stake_state: stake_account.stake_state, _phantom: PhantomData, } } } impl From> for (AccountSharedData, StakeState) { #[inline] fn from(stake_account: StakeAccount) -> Self { (stake_account.account, stake_account.stake_state) } } impl PartialEq> for StakeAccount { fn eq(&self, other: &StakeAccount) -> bool { let StakeAccount { account, stake_state, _phantom, } = other; account == &self.account && stake_state == &self.stake_state } } #[cfg(RUSTC_WITH_SPECIALIZATION)] impl AbiExample for StakeAccount { fn example() -> Self { use solana_sdk::{ account::Account, stake::state::{Meta, Stake}, }; let stake_state = StakeState::Stake(Meta::example(), Stake::example()); let mut account = Account::example(); account.data.resize(196, 0u8); account.owner = solana_stake_program::id(); account.set_state(&stake_state).unwrap(); Self::try_from(AccountSharedData::from(account)).unwrap() } }