use std::mem::size_of;
use fix::prelude::*;
use super::sol_price::LstSolPrice;
use crate::error::CoreError;
const TOTAL_LAMPORTS_OFFSET: usize = 258;
const POOL_TOKEN_SUPPLY_OFFSET: usize = TOTAL_LAMPORTS_OFFSET + U64_SIZE;
const LAST_UPDATE_EPOCH_OFFSET: usize = POOL_TOKEN_SUPPLY_OFFSET + U64_SIZE;
const U64_SIZE: usize = size_of::<u64>();
#[derive(Debug, Clone, Copy)]
pub struct SplStakePool {
pub total_lamports: UFix64<N9>,
pub pool_token_supply: UFix64<N9>,
pub last_update_epoch: u64,
}
impl SplStakePool {
pub fn from_bytes(data: &[u8]) -> Result<SplStakePool, CoreError> {
let total_lamports = data
.get(TOTAL_LAMPORTS_OFFSET..TOTAL_LAMPORTS_OFFSET + U64_SIZE)
.and_then(|b| b.try_into().ok())
.map(u64::from_le_bytes)
.map(UFix64::new)
.ok_or(CoreError::StakePoolAccountData)?;
let pool_token_supply = data
.get(POOL_TOKEN_SUPPLY_OFFSET..POOL_TOKEN_SUPPLY_OFFSET + U64_SIZE)
.and_then(|b| b.try_into().ok())
.map(u64::from_le_bytes)
.map(UFix64::new)
.ok_or(CoreError::StakePoolAccountData)?;
let last_update_epoch = data
.get(LAST_UPDATE_EPOCH_OFFSET..LAST_UPDATE_EPOCH_OFFSET + U64_SIZE)
.and_then(|b| b.try_into().ok())
.map(u64::from_le_bytes)
.ok_or(CoreError::StakePoolAccountData)?;
Ok(SplStakePool {
total_lamports,
pool_token_supply,
last_update_epoch,
})
}
pub fn true_price(&self) -> Result<LstSolPrice, CoreError> {
let price = SplStakePool::true_price_inner(
self.total_lamports,
self.pool_token_supply,
)
.ok_or(CoreError::StakePoolDivByZero)?;
Ok(LstSolPrice::new(price.into(), self.last_update_epoch))
}
fn true_price_inner(
total_lamports: UFix64<N9>,
pool_token_supply: UFix64<N9>,
) -> Option<UFix64<N9>> {
(pool_token_supply != UFix64::zero())
.then_some(total_lamports)
.and_then(|tl| tl.mul_div_floor(UFix64::one(), pool_token_supply))
}
}
#[cfg(test)]
mod tests {
use fix::prelude::*;
use crate::error::CoreError;
use crate::lst::stake_pool::SplStakePool;
#[test]
fn true_price_zero_supply_returns_div_by_zero() {
let pool = SplStakePool {
total_lamports: UFix64::<N9>::one(),
pool_token_supply: UFix64::<N9>::zero(),
last_update_epoch: 0,
};
assert_eq!(pool.true_price().err(), Some(CoreError::StakePoolDivByZero),);
}
}