use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use gmsol_model::PoolKind;
#[zero_copy]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PoolStorage {
pub(super) rev: u64,
padding: [u8; 8],
pool: Pool,
}
impl PoolStorage {
pub(crate) fn set_is_pure(&mut self, is_pure: bool) {
self.pool.set_is_pure(is_pure);
}
pub fn pool(&self) -> &Pool {
&self.pool
}
pub(super) fn pool_mut(&mut self) -> &mut Pool {
&mut self.pool
}
}
#[zero_copy]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(BorshSerialize, BorshDeserialize, InitSpace)]
pub struct Pool {
is_pure: u8,
#[cfg_attr(feature = "serde", serde(skip))]
#[cfg_attr(feature = "debug", debug(skip))]
padding: [u8; 15],
pub(super) long_token_amount: u128,
pub(super) short_token_amount: u128,
}
const PURE_VALUE: u8 = 1;
impl Pool {
fn set_is_pure(&mut self, is_pure: bool) {
self.is_pure = if is_pure { PURE_VALUE } else { 0 };
}
fn is_pure(&self) -> bool {
!matches!(self.is_pure, 0)
}
}
impl gmsol_model::Balance for Pool {
type Num = u128;
type Signed = i128;
fn long_amount(&self) -> gmsol_model::Result<Self::Num> {
if self.is_pure() {
debug_assert_eq!(
self.short_token_amount, 0,
"short token amount must be zero"
);
Ok(self.long_token_amount.div_ceil(2))
} else {
Ok(self.long_token_amount)
}
}
fn short_amount(&self) -> gmsol_model::Result<Self::Num> {
if self.is_pure() {
debug_assert_eq!(
self.short_token_amount, 0,
"short token amount must be zero"
);
Ok(self.long_token_amount / 2)
} else {
Ok(self.short_token_amount)
}
}
}
impl gmsol_model::Pool for Pool {
fn apply_delta_to_long_amount(&mut self, delta: &Self::Signed) -> gmsol_model::Result<()> {
self.long_token_amount = self.long_token_amount.checked_add_signed(*delta).ok_or(
gmsol_model::Error::Computation("apply delta to long amount"),
)?;
Ok(())
}
fn apply_delta_to_short_amount(&mut self, delta: &Self::Signed) -> gmsol_model::Result<()> {
let amount = if self.is_pure() {
&mut self.long_token_amount
} else {
&mut self.short_token_amount
};
*amount = amount
.checked_add_signed(*delta)
.ok_or(gmsol_model::Error::Computation(
"apply delta to short amount",
))?;
Ok(())
}
fn checked_apply_delta(
&self,
delta: gmsol_model::Delta<&Self::Signed>,
) -> gmsol_model::Result<Self> {
let mut ans = *self;
if let Some(amount) = delta.long() {
ans.apply_delta_to_long_amount(amount)?;
}
if let Some(amount) = delta.short() {
ans.apply_delta_to_short_amount(amount)?;
}
Ok(ans)
}
}
#[zero_copy]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pools {
primary: PoolStorage,
swap_impact: PoolStorage,
claimable_fee: PoolStorage,
open_interest_for_long: PoolStorage,
open_interest_for_short: PoolStorage,
open_interest_in_tokens_for_long: PoolStorage,
open_interest_in_tokens_for_short: PoolStorage,
position_impact: PoolStorage,
borrowing_factor: PoolStorage,
funding_amount_per_size_for_long: PoolStorage,
funding_amount_per_size_for_short: PoolStorage,
claimable_funding_amount_per_size_for_long: PoolStorage,
claimable_funding_amount_per_size_for_short: PoolStorage,
collateral_sum_for_long: PoolStorage,
collateral_sum_for_short: PoolStorage,
total_borrowing: PoolStorage,
#[cfg_attr(feature = "debug", debug(skip))]
reserved: [PoolStorage; 16],
}
impl Pools {
pub(super) fn init(&mut self, is_pure: bool) {
self.primary.set_is_pure(is_pure);
self.swap_impact.set_is_pure(is_pure);
self.claimable_fee.set_is_pure(is_pure);
self.open_interest_for_long.set_is_pure(is_pure);
self.open_interest_for_short.set_is_pure(is_pure);
self.open_interest_in_tokens_for_long.set_is_pure(is_pure);
self.open_interest_in_tokens_for_short.set_is_pure(is_pure);
self.position_impact.set_is_pure(false);
self.borrowing_factor.set_is_pure(false);
self.funding_amount_per_size_for_long.set_is_pure(is_pure);
self.funding_amount_per_size_for_short.set_is_pure(is_pure);
self.claimable_funding_amount_per_size_for_long
.set_is_pure(is_pure);
self.claimable_funding_amount_per_size_for_short
.set_is_pure(is_pure);
self.collateral_sum_for_long.set_is_pure(is_pure);
self.collateral_sum_for_short.set_is_pure(is_pure);
self.total_borrowing.set_is_pure(false);
}
pub(super) fn get(&self, kind: PoolKind) -> Option<&PoolStorage> {
let pool = match kind {
PoolKind::Primary => &self.primary,
PoolKind::SwapImpact => &self.swap_impact,
PoolKind::ClaimableFee => &self.claimable_fee,
PoolKind::OpenInterestForLong => &self.open_interest_for_long,
PoolKind::OpenInterestForShort => &self.open_interest_for_short,
PoolKind::OpenInterestInTokensForLong => &self.open_interest_in_tokens_for_long,
PoolKind::OpenInterestInTokensForShort => &self.open_interest_in_tokens_for_short,
PoolKind::PositionImpact => &self.position_impact,
PoolKind::BorrowingFactor => &self.borrowing_factor,
PoolKind::FundingAmountPerSizeForLong => &self.funding_amount_per_size_for_long,
PoolKind::FundingAmountPerSizeForShort => &self.funding_amount_per_size_for_short,
PoolKind::ClaimableFundingAmountPerSizeForLong => {
&self.claimable_funding_amount_per_size_for_long
}
PoolKind::ClaimableFundingAmountPerSizeForShort => {
&self.claimable_funding_amount_per_size_for_short
}
PoolKind::CollateralSumForLong => &self.collateral_sum_for_long,
PoolKind::CollateralSumForShort => &self.collateral_sum_for_short,
PoolKind::TotalBorrowing => &self.total_borrowing,
_ => return None,
};
Some(pool)
}
pub(super) fn get_mut(&mut self, kind: PoolKind) -> Option<&mut PoolStorage> {
let pool = match kind {
PoolKind::Primary => &mut self.primary,
PoolKind::SwapImpact => &mut self.swap_impact,
PoolKind::ClaimableFee => &mut self.claimable_fee,
PoolKind::OpenInterestForLong => &mut self.open_interest_for_long,
PoolKind::OpenInterestForShort => &mut self.open_interest_for_short,
PoolKind::OpenInterestInTokensForLong => &mut self.open_interest_in_tokens_for_long,
PoolKind::OpenInterestInTokensForShort => &mut self.open_interest_in_tokens_for_short,
PoolKind::PositionImpact => &mut self.position_impact,
PoolKind::BorrowingFactor => &mut self.borrowing_factor,
PoolKind::FundingAmountPerSizeForLong => &mut self.funding_amount_per_size_for_long,
PoolKind::FundingAmountPerSizeForShort => &mut self.funding_amount_per_size_for_short,
PoolKind::ClaimableFundingAmountPerSizeForLong => {
&mut self.claimable_funding_amount_per_size_for_long
}
PoolKind::ClaimableFundingAmountPerSizeForShort => {
&mut self.claimable_funding_amount_per_size_for_short
}
PoolKind::CollateralSumForLong => &mut self.collateral_sum_for_long,
PoolKind::CollateralSumForShort => &mut self.collateral_sum_for_short,
PoolKind::TotalBorrowing => &mut self.total_borrowing,
_ => return None,
};
Some(pool)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::events::EventPool;
#[test]
fn test_event_pool() {
let pool = Pool {
is_pure: PURE_VALUE,
padding: Default::default(),
long_token_amount: u128::MAX,
short_token_amount: u128::MAX,
};
let event_pool = EventPool {
is_pure: pool.is_pure,
padding: pool.padding,
long_token_amount: pool.long_token_amount,
short_token_amount: pool.short_token_amount,
};
let mut data = Vec::with_capacity(Pool::INIT_SPACE);
pool.serialize(&mut data)
.expect("failed to serialize `Pool`");
let mut event_data = Vec::with_capacity(Pool::INIT_SPACE);
event_pool
.serialize(&mut event_data)
.expect("failed to serialize `EventPool`");
assert_eq!(data, event_data);
}
}