use std::str::FromStr;
use anchor_lang::{prelude::*, Bump};
use anchor_spl::token::Mint;
use borsh::{BorshDeserialize, BorshSerialize};
use config::MarketConfigFlag;
use gmsol_model::{price::Prices, ClockKind, PoolKind};
use gmsol_utils::market::MarketError;
use revertible::RevertibleBuffer;
use crate::{
utils::fixed_str::{bytes_to_fixed_str, fixed_str_to_bytes},
CoreError,
};
use super::{Factor, InitSpace, Oracle, Seed};
use self::{
config::{MarketConfig, MarketConfigBuffer, MarketConfigKey},
pool::{Pool, Pools},
};
pub use gmsol_utils::market::{HasMarketMeta, MarketMeta};
pub use model::AsLiquidityMarket;
pub mod utils;
pub mod clock;
pub mod config;
pub mod revertible;
pub mod pool;
pub mod status;
mod model;
pub const MAX_FLAGS: usize = 8;
const MAX_NAME_LEN: usize = 64;
#[account(zero_copy)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Market {
version: u8,
pub(crate) bump: u8,
flags: MarketFlagContainer,
padding: [u8; 13],
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
name: [u8; MAX_NAME_LEN],
pub(crate) meta: MarketMeta,
pub store: Pubkey,
config: MarketConfig,
indexer: Indexer,
state: State,
buffer: RevertibleBuffer,
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
reserved: [u8; 256],
}
#[zero_copy]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
struct State {
pools: Pools,
clocks: Clocks,
other: OtherState,
#[cfg_attr(feature = "debug", debug(skip))]
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
reserved: [u8; 1024],
}
impl Bump for Market {
fn seed(&self) -> u8 {
self.bump
}
}
impl Seed for Market {
const SEED: &'static [u8] = b"market";
}
impl InitSpace for Market {
const INIT_SPACE: usize = std::mem::size_of::<Self>();
}
impl Default for Market {
fn default() -> Self {
use bytemuck::Zeroable;
Self::zeroed()
}
}
impl AsRef<Market> for Market {
fn as_ref(&self) -> &Market {
self
}
}
impl Market {
pub fn find_market_address(
store: &Pubkey,
token: &Pubkey,
store_program_id: &Pubkey,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[Self::SEED, store.as_ref(), token.as_ref()],
store_program_id,
)
}
#[allow(clippy::too_many_arguments)]
pub fn init(
&mut self,
bump: u8,
store: Pubkey,
name: &str,
market_token_mint: Pubkey,
index_token_mint: Pubkey,
long_token_mint: Pubkey,
short_token_mint: Pubkey,
is_enabled: bool,
) -> Result<()> {
self.bump = bump;
self.store = store;
self.name = fixed_str_to_bytes(name)?;
self.set_enabled(is_enabled);
self.meta.market_token_mint = market_token_mint;
self.meta.index_token_mint = index_token_mint;
self.meta.long_token_mint = long_token_mint;
self.meta.short_token_mint = short_token_mint;
let is_pure = self.meta.long_token_mint == self.meta.short_token_mint;
self.set_flag(MarketFlag::Pure, is_pure);
self.state.pools.init(is_pure);
self.state.clocks.init_to_current()?;
self.config.init();
self.buffer.init();
Ok(())
}
pub fn meta(&self) -> &MarketMeta {
&self.meta
}
pub fn validated_meta(&self, store: &Pubkey) -> Result<&MarketMeta> {
self.validate(store)?;
Ok(self.meta())
}
pub fn name(&self) -> Result<&str> {
bytes_to_fixed_str(&self.name)
}
pub fn description(&self) -> Result<String> {
let name = self.name()?;
Ok(format!(
"Market {{ name = {name}, token = {}}}",
self.meta.market_token_mint
))
}
pub fn flag(&self, flag: MarketFlag) -> bool {
self.flags.get_flag(flag)
}
pub fn set_flag(&mut self, flag: MarketFlag, value: bool) -> bool {
self.flags.set_flag(flag, value)
}
pub fn is_pure(&self) -> bool {
self.flag(MarketFlag::Pure)
}
pub fn is_enabled(&self) -> bool {
self.flag(MarketFlag::Enabled)
}
pub fn set_enabled(&mut self, enabled: bool) -> bool {
self.set_flag(MarketFlag::Enabled, enabled)
}
pub fn is_adl_enabled(&self, is_long: bool) -> bool {
if is_long {
self.flag(MarketFlag::AutoDeleveragingEnabledForLong)
} else {
self.flag(MarketFlag::AutoDeleveragingEnabledForShort)
}
}
pub fn set_adl_enabled(&mut self, is_long: bool, enabled: bool) -> bool {
if is_long {
self.set_flag(MarketFlag::AutoDeleveragingEnabledForLong, enabled)
} else {
self.set_flag(MarketFlag::AutoDeleveragingEnabledForShort, enabled)
}
}
pub fn is_gt_minting_enabled(&self) -> bool {
self.flag(MarketFlag::GTEnabled)
}
pub fn set_is_gt_minting_enbaled(&mut self, enabled: bool) -> bool {
self.set_flag(MarketFlag::GTEnabled, enabled)
}
#[inline]
pub fn pool(&self, kind: PoolKind) -> Option<Pool> {
self.state.pools.get(kind).map(|s| s.pool()).copied()
}
pub fn try_pool(&self, kind: PoolKind) -> gmsol_model::Result<&Pool> {
Ok(self
.state
.pools
.get(kind)
.ok_or(gmsol_model::Error::MissingPoolKind(kind))?
.pool())
}
pub fn clock(&self, kind: ClockKind) -> Option<i64> {
self.state.clocks.get(kind).copied()
}
fn clocks(&self) -> &Clocks {
&self.state.clocks
}
pub fn validate(&self, store: &Pubkey) -> Result<()> {
require_keys_eq!(*store, self.store, CoreError::StoreMismatched);
require!(self.is_enabled(), CoreError::DisabledMarket);
Ok(())
}
pub fn get_config(&self, key: &str) -> Result<&Factor> {
let key = MarketConfigKey::from_str(key)
.map_err(|_| error!(CoreError::InvalidMarketConfigKey))?;
self.get_config_by_key(key)
.ok_or_else(|| error!(CoreError::Unimplemented))
}
#[inline]
pub fn get_config_by_key(&self, key: MarketConfigKey) -> Option<&Factor> {
self.config.get(key)
}
pub fn get_config_mut(&mut self, key: &str) -> Result<&mut Factor> {
let key = MarketConfigKey::from_str(key)
.map_err(|_| error!(CoreError::InvalidMarketConfigKey))?;
self.config
.get_mut(key)
.ok_or_else(|| error!(CoreError::Unimplemented))
}
pub fn get_config_flag(&self, key: &str) -> Result<bool> {
let key = MarketConfigFlag::from_str(key)
.map_err(|_| error!(CoreError::InvalidMarketConfigKey))?;
Ok(self.get_config_flag_by_key(key))
}
#[inline]
pub fn get_config_flag_by_key(&self, key: MarketConfigFlag) -> bool {
self.config.flag(key)
}
pub fn set_config_flag(&mut self, key: &str, value: bool) -> Result<bool> {
let key = MarketConfigFlag::from_str(key)
.map_err(|_| error!(CoreError::InvalidMarketConfigKey))?;
Ok(self.config.set_flag(key, value))
}
pub fn state(&self) -> &OtherState {
&self.state.other
}
pub fn indexer(&self) -> &Indexer {
&self.indexer
}
pub fn indexer_mut(&mut self) -> &mut Indexer {
&mut self.indexer
}
pub fn update_config_with_buffer(&mut self, buffer: &MarketConfigBuffer) -> Result<()> {
for entry in buffer.iter() {
let key = entry.key()?;
let current_value = self
.config
.get_mut(key)
.ok_or_else(|| error!(CoreError::Unimplemented))?;
let new_value = entry.value();
*current_value = new_value;
}
Ok(())
}
pub fn prices(&self, oracle: &Oracle) -> Result<Prices<u128>> {
oracle.market_prices(self)
}
pub fn max_pool_value_for_deposit(&self, is_long_token: bool) -> gmsol_model::Result<Factor> {
if is_long_token {
Ok(self.config.max_pool_value_for_deposit_for_long_token)
} else {
Ok(self.config.max_pool_value_for_deposit_for_short_token)
}
}
pub fn as_liquidity_market<'a>(
&'a self,
market_token: &'a Mint,
) -> AsLiquidityMarket<'a, Self> {
AsLiquidityMarket::new(self, market_token)
}
pub fn validate_shiftable(&self, target: &Self) -> Result<()> {
require_keys_eq!(
self.meta().long_token_mint,
target.meta().long_token_mint,
CoreError::TokenMintMismatched,
);
require_keys_eq!(
self.meta().short_token_mint,
target.meta().short_token_mint,
CoreError::TokenMintMismatched,
);
Ok(())
}
}
#[derive(num_enum::IntoPrimitive)]
#[repr(u8)]
pub enum MarketFlag {
Enabled,
Pure,
AutoDeleveragingEnabledForLong,
AutoDeleveragingEnabledForShort,
GTEnabled,
}
gmsol_utils::flags!(MarketFlag, MAX_FLAGS, u8);
#[zero_copy]
#[derive(BorshSerialize, BorshDeserialize, InitSpace)]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OtherState {
#[cfg_attr(feature = "debug", debug(skip))]
padding: [u8; 16],
rev: u64,
trade_count: u64,
long_token_balance: u64,
short_token_balance: u64,
funding_factor_per_second: i128,
#[cfg_attr(feature = "debug", debug(skip))]
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
reserved: [u8; 256],
}
impl OtherState {
pub fn long_token_balance_raw(&self) -> u64 {
self.long_token_balance
}
pub fn short_token_balance_raw(&self) -> u64 {
self.short_token_balance
}
pub fn funding_factor_per_second(&self) -> i128 {
self.funding_factor_per_second
}
pub fn trade_count(&self) -> u64 {
self.trade_count
}
pub fn next_trade_id(&mut self) -> Result<u64> {
let next_id = self
.trade_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.trade_count = next_id;
Ok(next_id)
}
}
impl HasMarketMeta for Market {
fn is_pure(&self) -> bool {
self.is_pure()
}
fn market_meta(&self) -> &MarketMeta {
&self.meta
}
}
#[zero_copy]
#[derive(BorshSerialize, BorshDeserialize, InitSpace)]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Clocks {
#[cfg_attr(feature = "debug", debug(skip))]
padding: [u8; 8],
rev: u64,
price_impact_distribution: i64,
borrowing: i64,
funding: i64,
adl_for_long: i64,
adl_for_short: i64,
#[cfg_attr(feature = "debug", debug(skip))]
reserved: [i64; 3],
}
impl Clocks {
fn init_to_current(&mut self) -> Result<()> {
let current = Clock::get()?.unix_timestamp;
self.price_impact_distribution = current;
self.borrowing = current;
self.funding = current;
Ok(())
}
fn get(&self, kind: ClockKind) -> Option<&i64> {
let clock = match kind {
ClockKind::PriceImpactDistribution => &self.price_impact_distribution,
ClockKind::Borrowing => &self.borrowing,
ClockKind::Funding => &self.funding,
ClockKind::AdlForLong => &self.adl_for_long,
ClockKind::AdlForShort => &self.adl_for_short,
_ => return None,
};
Some(clock)
}
fn get_mut(&mut self, kind: ClockKind) -> Option<&mut i64> {
let clock = match kind {
ClockKind::PriceImpactDistribution => &mut self.price_impact_distribution,
ClockKind::Borrowing => &mut self.borrowing,
ClockKind::Funding => &mut self.funding,
ClockKind::AdlForLong => &mut self.adl_for_long,
ClockKind::AdlForShort => &mut self.adl_for_short,
_ => return None,
};
Some(clock)
}
}
#[zero_copy]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Indexer {
trade_count: u64,
deposit_count: u64,
withdrawal_count: u64,
order_count: u64,
shift_count: u64,
glv_deposit_count: u64,
glv_withdrawal_count: u64,
#[cfg_attr(feature = "debug", debug(skip))]
padding_0: [u8; 8],
#[cfg_attr(feature = "debug", debug(skip))]
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
reserved: [u8; 128],
}
impl Indexer {
pub fn deposit_count(&self) -> u64 {
self.deposit_count
}
pub fn withdrawal_count(&self) -> u64 {
self.withdrawal_count
}
pub fn order_count(&self) -> u64 {
self.order_count
}
pub fn shift_count(&self) -> u64 {
self.shift_count
}
pub fn glv_deposit_count(&self) -> u64 {
self.glv_deposit_count
}
pub fn glv_withdrawal_count(&self) -> u64 {
self.glv_withdrawal_count
}
pub fn next_deposit_id(&mut self) -> Result<u64> {
let next_id = self
.deposit_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.deposit_count = next_id;
Ok(next_id)
}
pub fn next_withdrawal_id(&mut self) -> Result<u64> {
let next_id = self
.withdrawal_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.withdrawal_count = next_id;
Ok(next_id)
}
pub fn next_order_id(&mut self) -> Result<u64> {
let next_id = self
.order_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.order_count = next_id;
Ok(next_id)
}
pub fn next_shift_id(&mut self) -> Result<u64> {
let next_id = self
.shift_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.shift_count = next_id;
Ok(next_id)
}
pub fn next_glv_deposit_id(&mut self) -> Result<u64> {
let next_id = self
.glv_deposit_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.glv_deposit_count = next_id;
Ok(next_id)
}
pub fn next_glv_withdrawal_id(&mut self) -> Result<u64> {
let next_id = self
.glv_withdrawal_count
.checked_add(1)
.ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
self.glv_withdrawal_count = next_id;
Ok(next_id)
}
}
impl From<MarketError> for CoreError {
fn from(err: MarketError) -> Self {
msg!("Market Error: {}", err);
match err {
MarketError::NotACollateralToken => Self::InvalidArgument,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::events::{EventClocks, EventOtherState};
#[test]
fn test_event_clocks() {
let clocks = Clocks {
padding: Default::default(),
rev: u64::MAX,
price_impact_distribution: i64::MAX,
borrowing: i64::MAX,
funding: i64::MAX,
adl_for_long: i64::MAX,
adl_for_short: i64::MAX,
reserved: Default::default(),
};
let event_clocks = EventClocks {
padding: clocks.padding,
rev: clocks.rev,
price_impact_distribution: clocks.price_impact_distribution,
borrowing: clocks.borrowing,
funding: clocks.funding,
adl_for_long: clocks.adl_for_long,
adl_for_short: clocks.adl_for_short,
reserved: clocks.reserved,
};
let mut data = Vec::with_capacity(Pool::INIT_SPACE);
clocks
.serialize(&mut data)
.expect("failed to serialize `Clocks`");
let mut event_data = Vec::with_capacity(Pool::INIT_SPACE);
event_clocks
.serialize(&mut event_data)
.expect("failed to serialize `EventClocks`");
assert_eq!(data, event_data);
}
#[test]
fn test_event_other_state() {
let clocks = OtherState {
padding: Default::default(),
rev: u64::MAX,
trade_count: u64::MAX,
long_token_balance: u64::MAX,
short_token_balance: u64::MAX,
funding_factor_per_second: i128::MAX,
reserved: [0; 256],
};
let event_clocks = EventOtherState {
padding: clocks.padding,
rev: clocks.rev,
trade_count: clocks.trade_count,
long_token_balance: clocks.long_token_balance,
short_token_balance: clocks.short_token_balance,
funding_factor_per_second: clocks.funding_factor_per_second,
reserved: clocks.reserved,
};
let mut data = Vec::with_capacity(Pool::INIT_SPACE);
clocks
.serialize(&mut data)
.expect("failed to serialize `OtherState`");
let mut event_data = Vec::with_capacity(Pool::INIT_SPACE);
event_clocks
.serialize(&mut event_data)
.expect("failed to serialize `EventOtherState`");
assert_eq!(data, event_data);
}
}