use std::collections::HashMap;
use solana_pubkey::Pubkey;
use crate::state::{self, VaultState};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VaultReserve {
pub reserve: Pubkey,
pub lending_market: Pubkey,
}
pub struct VaultInfo {
pub address: Pubkey,
pub token_mint: Pubkey,
pub token_program: Pubkey,
reserves: Vec<VaultReserve>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VaultInfoError {
AccountData(state::AccountDataError),
MissingReserve(Pubkey),
}
impl core::fmt::Display for VaultInfoError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::AccountData(err) => write!(f, "{err}"),
Self::MissingReserve(reserve) => {
write!(f, "no ReserveInfo provided for active reserve {reserve}")
}
}
}
}
impl std::error::Error for VaultInfoError {}
impl From<state::AccountDataError> for VaultInfoError {
fn from(err: state::AccountDataError) -> Self {
Self::AccountData(err)
}
}
impl VaultInfo {
pub fn active_reserve_addresses(vault: &VaultState) -> Vec<Pubkey> {
vault
.vault_allocation_strategy
.iter()
.map(|a| a.reserve)
.filter(|r| r != &Pubkey::default())
.collect()
}
pub fn from_vault_state(
address: Pubkey,
vault: &VaultState,
reserves: &[ReserveInfo],
) -> Result<Self, VaultInfoError> {
let lending_markets: HashMap<Pubkey, Pubkey> = reserves
.iter()
.map(|r| (r.address, r.lending_market))
.collect();
let reserves = Self::active_reserve_addresses(vault)
.into_iter()
.map(|reserve| {
let lending_market = lending_markets
.get(&reserve)
.copied()
.ok_or(VaultInfoError::MissingReserve(reserve))?;
Ok(VaultReserve {
reserve,
lending_market,
})
})
.collect::<Result<Vec<_>, VaultInfoError>>()?;
Ok(Self {
address,
token_mint: vault.token_mint,
token_program: vault.token_program,
reserves,
})
}
pub fn from_account_data(
address: Pubkey,
data: &[u8],
reserves: &[ReserveInfo],
) -> Result<Self, VaultInfoError> {
let vault = state::from_account_data::<VaultState>(data)?;
Self::from_vault_state(address, vault, reserves)
}
pub fn reserves(&self) -> &[VaultReserve] {
&self.reserves
}
pub fn reserve_addresses(&self) -> impl Iterator<Item = Pubkey> + '_ {
self.reserves.iter().map(|r| r.reserve)
}
}
pub struct ReserveInfo {
pub address: Pubkey,
pub lending_market: Pubkey,
pub liquidity_supply_vault: Pubkey,
pub collateral_mint: Pubkey,
}
impl ReserveInfo {
pub fn from_reserve(address: Pubkey, reserve: &klend_interface::state::Reserve) -> Self {
Self {
address,
lending_market: reserve.lending_market,
liquidity_supply_vault: reserve.liquidity.supply_vault,
collateral_mint: reserve.collateral.mint_pubkey,
}
}
pub fn from_account_data(
address: Pubkey,
data: &[u8],
) -> Result<Self, klend_interface::state::AccountDataError> {
let reserve =
klend_interface::state::from_account_data::<klend_interface::state::Reserve>(data)?;
Ok(Self::from_reserve(address, reserve))
}
}
impl From<(Pubkey, &klend_interface::state::Reserve)> for ReserveInfo {
fn from((address, reserve): (Pubkey, &klend_interface::state::Reserve)) -> Self {
Self::from_reserve(address, reserve)
}
}
impl TryFrom<(Pubkey, &[u8])> for ReserveInfo {
type Error = klend_interface::state::AccountDataError;
fn try_from((address, data): (Pubkey, &[u8])) -> Result<Self, Self::Error> {
Self::from_account_data(address, data)
}
}