use gmsol_programs::gmsol_store::{accounts::Position, types::PositionState};
use gmsol_utils::{market::MarketMeta, token_config::TokenMapAccess};
use crate::utils::{market::MarketDecimals, Amount, Value};
use super::StringPubkey;
#[derive(Debug, Clone)]
#[cfg_attr(serde, derive(serde::Serialize, serde::Deserialize))]
pub struct SerdePosition {
pub store: StringPubkey,
pub is_long: bool,
pub owner: StringPubkey,
pub market_token: StringPubkey,
pub collateral_token: StringPubkey,
pub is_collateral_long_token: bool,
pub state: SerdePositionState,
}
impl SerdePosition {
pub fn from_position(
position: &Position,
meta: &MarketMeta,
token_map: &impl TokenMapAccess,
) -> crate::Result<Self> {
let is_collateral_long_token = meta
.to_token_side(&position.collateral_token)
.map_err(crate::Error::custom)?;
let decimals = MarketDecimals::new(meta, token_map)?;
Ok(Self {
store: position.store.into(),
is_long: position.try_is_long()?,
owner: position.owner.into(),
market_token: position.market_token.into(),
collateral_token: position.collateral_token.into(),
is_collateral_long_token,
state: SerdePositionState::from_state(
&position.state,
decimals,
is_collateral_long_token,
)?,
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(serde, derive(serde::Serialize, serde::Deserialize))]
pub struct SerdePositionState {
pub trade_id: u64,
pub increased_at: i64,
pub updated_at_slot: u64,
pub decreased_at: i64,
pub size_in_tokens: Amount,
pub collateral_amount: Amount,
pub size_in_usd: Value,
pub borrowing_factor: Value,
pub funding_fee_amount_per_size: Amount,
pub long_token_claimable_funding_amount_per_size: Amount,
pub short_token_claimable_funding_amount_per_size: Amount,
}
impl SerdePositionState {
pub fn from_state(
state: &PositionState,
decimals: MarketDecimals,
is_collateral_long_token: bool,
) -> crate::Result<Self> {
let collateral_token_decimals = if is_collateral_long_token {
decimals.long_token_decimals
} else {
decimals.short_token_decimals
};
Ok(Self {
trade_id: state.trade_id,
increased_at: state.increased_at,
updated_at_slot: state.updated_at_slot,
decreased_at: state.decreased_at,
size_in_tokens: Amount::from_u128(state.size_in_tokens, decimals.index_token_decimals)?,
collateral_amount: Amount::from_u128(
state.collateral_amount,
collateral_token_decimals,
)?,
size_in_usd: Value::from_u128(state.size_in_usd),
borrowing_factor: Value::from_u128(state.borrowing_factor),
funding_fee_amount_per_size: unpack_funding_amount_per_size(
state.funding_fee_amount_per_size,
collateral_token_decimals,
)?,
long_token_claimable_funding_amount_per_size: unpack_funding_amount_per_size(
state.long_token_claimable_funding_amount_per_size,
decimals.long_token_decimals,
)?,
short_token_claimable_funding_amount_per_size: unpack_funding_amount_per_size(
state.short_token_claimable_funding_amount_per_size,
decimals.short_token_decimals,
)?,
})
}
}
fn unpack_funding_amount_per_size(factor: u128, token_decimals: u8) -> crate::Result<Amount> {
use gmsol_programs::constants::FUNDING_AMOUNT_PER_SIZE_ADJUSTMENT;
use rust_decimal::{prelude::FromPrimitive, Decimal};
let adjustment = Decimal::from_i128(FUNDING_AMOUNT_PER_SIZE_ADJUSTMENT as i128).unwrap();
let mut amount = Amount::from_u128(factor, token_decimals)?;
amount.0 /= adjustment;
Ok(amount)
}