use std::fmt::Debug;
use light_account::Pack;
use light_token::instruction::derive_token_ata;
use solana_pubkey::Pubkey;
use super::{AccountInterface, TokenAccountInterface};
use crate::indexer::CompressedAccount;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AccountToFetch {
Pda { address: Pubkey, program_id: Pubkey },
Token { address: Pubkey },
Ata { wallet_owner: Pubkey, mint: Pubkey },
Mint { address: Pubkey },
}
impl AccountToFetch {
pub fn pda(address: Pubkey, program_id: Pubkey) -> Self {
Self::Pda {
address,
program_id,
}
}
pub fn token(address: Pubkey) -> Self {
Self::Token { address }
}
pub fn ata(wallet_owner: Pubkey, mint: Pubkey) -> Self {
Self::Ata { wallet_owner, mint }
}
pub fn mint(address: Pubkey) -> Self {
Self::Mint { address }
}
#[must_use]
pub fn pubkey(&self) -> Pubkey {
match self {
Self::Pda { address, .. } => *address,
Self::Token { address } => *address,
Self::Ata { wallet_owner, mint } => derive_token_ata(wallet_owner, mint),
Self::Mint { address } => *address,
}
}
}
#[derive(Clone, Debug)]
pub struct PdaSpec<V> {
pub interface: AccountInterface,
pub variant: V,
pub program_id: Pubkey,
}
impl<V> PdaSpec<V> {
#[must_use]
pub fn new(interface: AccountInterface, variant: V, program_id: Pubkey) -> Self {
Self {
interface,
variant,
program_id,
}
}
#[inline]
#[must_use]
pub fn address(&self) -> Pubkey {
self.interface.key
}
#[inline]
#[must_use]
pub fn program_id(&self) -> Pubkey {
self.program_id
}
#[inline]
#[must_use]
pub fn is_cold(&self) -> bool {
self.interface.is_cold()
}
#[inline]
#[must_use]
pub fn is_hot(&self) -> bool {
self.interface.is_hot()
}
#[must_use]
pub fn compressed(&self) -> Option<&CompressedAccount> {
self.interface.cold.as_ref()
}
#[must_use]
pub fn hash(&self) -> Option<[u8; 32]> {
self.interface.hash()
}
#[inline]
#[must_use]
pub fn data(&self) -> &[u8] {
self.interface.data()
}
}
#[derive(Clone, Debug)]
pub enum AccountSpec<V> {
Pda(PdaSpec<V>),
Ata(Box<TokenAccountInterface>),
Mint(AccountInterface),
}
impl<V> AccountSpec<V> {
#[inline]
#[must_use]
pub fn is_cold(&self) -> bool {
match self {
Self::Pda(s) => s.is_cold(),
Self::Ata(s) => s.is_cold(),
Self::Mint(s) => s.is_cold(),
}
}
#[inline]
#[must_use]
pub fn is_hot(&self) -> bool {
!self.is_cold()
}
#[must_use]
pub fn pubkey(&self) -> Pubkey {
match self {
Self::Pda(s) => s.address(),
Self::Ata(s) => s.key,
Self::Mint(s) => s.key,
}
}
}
impl<V> From<PdaSpec<V>> for AccountSpec<V> {
fn from(spec: PdaSpec<V>) -> Self {
Self::Pda(spec)
}
}
impl From<TokenAccountInterface> for AccountSpec<()> {
fn from(interface: TokenAccountInterface) -> Self {
Self::Ata(Box::new(interface))
}
}
impl From<AccountInterface> for AccountSpec<()> {
fn from(interface: AccountInterface) -> Self {
Self::Mint(interface)
}
}
#[inline]
#[must_use]
pub fn any_cold<V>(specs: &[AccountSpec<V>]) -> bool {
specs.iter().any(|s| s.is_cold())
}
#[inline]
#[must_use]
pub fn all_hot<V>(specs: &[AccountSpec<V>]) -> bool {
specs.iter().all(|s| s.is_hot())
}
pub trait LightProgramInterface: Sized {
type Variant: Pack<solana_instruction::AccountMeta> + Clone + Debug;
type Instruction;
type Error: std::error::Error;
#[must_use]
fn program_id(&self) -> Pubkey;
fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result<Self, Self::Error>;
#[must_use]
fn get_accounts_to_update(&self, ix: &Self::Instruction) -> Vec<AccountToFetch>;
fn update(&mut self, accounts: &[AccountInterface]) -> Result<(), Self::Error>;
#[must_use]
fn get_all_specs(&self) -> Vec<AccountSpec<Self::Variant>>;
#[must_use]
fn get_specs_for_instruction(&self, ix: &Self::Instruction) -> Vec<AccountSpec<Self::Variant>>;
#[must_use]
fn get_cold_specs(&self) -> Vec<AccountSpec<Self::Variant>> {
self.get_all_specs()
.into_iter()
.filter(|s| s.is_cold())
.collect()
}
#[must_use]
fn get_cold_specs_for_instruction(
&self,
ix: &Self::Instruction,
) -> Vec<AccountSpec<Self::Variant>> {
self.get_specs_for_instruction(ix)
.into_iter()
.filter(|s| s.is_cold())
.collect()
}
#[must_use]
fn needs_loading(&self, ix: &Self::Instruction) -> bool {
any_cold(&self.get_specs_for_instruction(ix))
}
}
#[inline]
#[must_use]
pub fn discriminator(data: &[u8]) -> Option<[u8; 8]> {
data.get(..8).and_then(|s| s.try_into().ok())
}
#[inline]
#[must_use]
pub fn matches_discriminator(data: &[u8], disc: &[u8; 8]) -> bool {
discriminator(data) == Some(*disc)
}