use anchor_lang::prelude::*;
use borsh::BorshSerialize;
use gmsol_utils::InitSpace;
use crate::{
events::Event,
states::{order::TransferOut, position::PositionState},
};
use super::{TradeData, TradeFees, TradeOutputAmounts, TradePnl, TradePrice, TradePrices};
#[cfg(feature = "utils")]
use crate::states::Position;
#[cfg(feature = "utils")]
use gmsol_utils::order::{TradeFlag, TradeFlagContainer};
#[derive(Clone, BorshSerialize)]
pub(crate) struct TradeEventRef<'a>(&'a TradeData);
impl<'a> From<&'a TradeData> for TradeEventRef<'a> {
fn from(value: &'a TradeData) -> Self {
Self(value)
}
}
impl anchor_lang::Discriminator for TradeEventRef<'_> {
const DISCRIMINATOR: &'static [u8] = TradeEvent::DISCRIMINATOR;
}
impl InitSpace for TradeEventRef<'_> {
const INIT_SPACE: usize = <TradeData as anchor_lang::Space>::INIT_SPACE;
}
impl Event for TradeEventRef<'_> {}
static_assertions::const_assert_eq!(TradeEventRef::<'static>::INIT_SPACE, TradeEvent::INIT_SPACE);
#[event]
#[derive(Clone, InitSpace)]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TradeEvent {
pub flags: u8,
#[cfg_attr(feature = "debug", debug(skip))]
pub(crate) padding_0: [u8; 7],
pub trade_id: u64,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub authority: Pubkey,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub store: Pubkey,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub market_token: Pubkey,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub user: Pubkey,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub position: Pubkey,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub order: Pubkey,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
)]
pub final_output_token: Pubkey,
pub ts: i64,
pub slot: u64,
pub before: EventPositionState,
pub after: EventPositionState,
pub transfer_out: EventTransferOut,
#[cfg_attr(feature = "debug", debug(skip))]
pub(crate) padding_1: [u8; 8],
pub prices: EventTradePrices,
pub execution_price: u128,
pub price_impact_value: i128,
pub price_impact_diff: u128,
pub pnl: EventTradePnl,
pub fees: EventTradeFees,
#[cfg_attr(feature = "serde", serde(default))]
pub output_amounts: EventTradeOutputAmounts,
}
#[cfg(feature = "utils")]
impl TradeEvent {
pub fn get_flag(&self, flag: TradeFlag) -> bool {
let map = TradeFlagContainer::from_value(self.flags);
map.get_flag(flag)
}
pub fn is_long(&self) -> bool {
self.get_flag(TradeFlag::IsLong)
}
pub fn is_collateral_long(&self) -> bool {
self.get_flag(TradeFlag::IsCollateralLong)
}
pub fn is_increase(&self) -> bool {
self.get_flag(TradeFlag::IsIncrease)
}
pub fn updated_at(&self) -> i64 {
self.after.increased_at.max(self.after.decreased_at)
}
pub fn delta_size_in_usd(&self) -> u128 {
self.after.size_in_usd.abs_diff(self.before.size_in_usd)
}
pub fn delta_size_in_tokens(&self) -> u128 {
self.after
.size_in_tokens
.abs_diff(self.before.size_in_tokens)
}
pub fn delta_collateral_amount(&self) -> u128 {
self.after
.collateral_amount
.abs_diff(self.before.collateral_amount)
}
pub fn delta_borrowing_factor(&self) -> u128 {
self.after
.borrowing_factor
.abs_diff(self.before.borrowing_factor)
}
pub fn delta_funding_fee_amount_per_size(&self) -> u128 {
self.after
.funding_fee_amount_per_size
.abs_diff(self.before.funding_fee_amount_per_size)
}
pub fn funding_fee(&self) -> u128 {
self.delta_funding_fee_amount_per_size()
.saturating_mul(self.before.size_in_usd)
}
pub fn delta_claimable_funding_amount_per_size(&self, is_long_token: bool) -> u128 {
if is_long_token {
self.after
.long_token_claimable_funding_amount_per_size
.abs_diff(self.before.long_token_claimable_funding_amount_per_size)
} else {
self.after
.short_token_claimable_funding_amount_per_size
.abs_diff(self.before.short_token_claimable_funding_amount_per_size)
}
}
pub fn to_position(&self, meta: &impl crate::states::HasMarketMeta) -> Position {
use crate::states::position::PositionKind;
let mut position = Position::default();
let kind = if self.is_long() {
PositionKind::Long
} else {
PositionKind::Short
};
let collateral_token = if self.is_collateral_long() {
meta.market_meta().long_token_mint
} else {
meta.market_meta().short_token_mint
};
position
.try_init(
kind,
0,
self.store,
&self.user,
&self.market_token,
&collateral_token,
)
.unwrap();
position.state = self.after.clone().into();
position
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for TradeEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::utils::pubkey::optional_address;
f.debug_struct("TradeEvent")
.field("trade_id", &self.trade_id)
.field("store", &self.store.to_string())
.field("market_token", &self.market_token.to_string())
.field("user", &self.user.to_string())
.field("position", &self.position.to_string())
.field("order", &self.order.to_string())
.field(
"final_output_token",
&optional_address(&self.final_output_token),
)
.field("ts", &self.ts)
.field("slot", &self.slot)
.field("is_long", &self.is_long())
.field("is_collateral_long", &self.is_collateral_long())
.field("is_increase", &self.is_increase())
.field("delta_collateral_amount", &self.delta_collateral_amount())
.field("delta_size_in_usd", &self.delta_size_in_usd())
.field("delta_size_in_tokens", &self.delta_size_in_tokens())
.field("prices", &self.prices)
.field("execution_price", &self.execution_price)
.field("price_impact_value", &self.price_impact_value)
.field("price_impact_diff", &self.price_impact_diff)
.field("pnl", &self.pnl)
.field("fees", &self.fees)
.field("output_amounts", &self.output_amounts)
.field("transfer_out", &self.transfer_out)
.finish_non_exhaustive()
}
}
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(AnchorDeserialize, AnchorSerialize, InitSpace, Clone)]
pub struct EventPositionState {
pub trade_id: u64,
pub increased_at: i64,
pub updated_at_slot: u64,
pub decreased_at: i64,
pub size_in_tokens: u128,
pub collateral_amount: u128,
pub size_in_usd: u128,
pub borrowing_factor: u128,
pub funding_fee_amount_per_size: u128,
pub long_token_claimable_funding_amount_per_size: u128,
pub short_token_claimable_funding_amount_per_size: u128,
#[cfg_attr(feature = "debug", debug(skip))]
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
pub(crate) reserved: [u8; 128],
}
static_assertions::const_assert_eq!(EventPositionState::INIT_SPACE, PositionState::INIT_SPACE);
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(AnchorSerialize, AnchorDeserialize, Default, InitSpace, Clone)]
pub struct EventTransferOut {
pub executed: u8,
#[cfg_attr(feature = "debug", debug(skip))]
pub(crate) padding_0: [u8; 7],
pub final_output_token: u64,
pub secondary_output_token: u64,
pub long_token: u64,
pub short_token: u64,
pub long_token_for_claimable_account_of_user: u64,
pub short_token_for_claimable_account_of_user: u64,
pub long_token_for_claimable_account_of_holding: u64,
pub short_token_for_claimable_account_of_holding: u64,
}
static_assertions::const_assert_eq!(EventTransferOut::INIT_SPACE, TransferOut::INIT_SPACE);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
pub struct EventTradePrice {
pub min: u128,
pub max: u128,
}
static_assertions::const_assert_eq!(EventTradePrice::INIT_SPACE, TradePrice::INIT_SPACE);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
pub struct EventTradePrices {
pub index: EventTradePrice,
pub long: EventTradePrice,
pub short: EventTradePrice,
}
static_assertions::const_assert_eq!(EventTradePrices::INIT_SPACE, TradePrices::INIT_SPACE);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
pub struct EventTradePnl {
pub pnl: i128,
pub uncapped_pnl: i128,
}
static_assertions::const_assert_eq!(EventTradePnl::INIT_SPACE, TradePnl::INIT_SPACE);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
pub struct EventTradeFees {
pub order_fee_for_receiver_amount: u128,
pub order_fee_for_pool_amount: u128,
pub liquidation_fee_amount: u128,
pub liquidation_fee_for_receiver_amount: u128,
pub total_borrowing_fee_amount: u128,
pub borrowing_fee_for_receiver_amount: u128,
pub funding_fee_amount: u128,
pub claimable_funding_fee_long_token_amount: u128,
pub claimable_funding_fee_short_token_amount: u128,
}
static_assertions::const_assert_eq!(EventTradeFees::INIT_SPACE, TradeFees::INIT_SPACE);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(AnchorSerialize, AnchorDeserialize, Default, InitSpace, Clone)]
pub struct EventTradeOutputAmounts {
pub output_amount: u128,
pub secondary_output_amount: u128,
}
static_assertions::const_assert_eq!(
EventTradeOutputAmounts::INIT_SPACE,
TradeOutputAmounts::INIT_SPACE
);