1use anchor_lang::prelude::*;
2use borsh::BorshSerialize;
3use gmsol_utils::InitSpace;
4
5use crate::{
6 events::Event,
7 states::{order::TransferOut, position::PositionState},
8};
9
10use super::{TradeData, TradeFees, TradeOutputAmounts, TradePnl, TradePrice, TradePrices};
11
12#[cfg(feature = "utils")]
13use crate::{
14 events::{TradeFlag, TradeFlagContainer},
15 states::Position,
16};
17
18#[derive(Clone, BorshSerialize)]
21pub(crate) struct TradeEventRef<'a>(&'a TradeData);
22
23impl<'a> From<&'a TradeData> for TradeEventRef<'a> {
24 fn from(value: &'a TradeData) -> Self {
25 Self(value)
26 }
27}
28
29impl anchor_lang::Discriminator for TradeEventRef<'_> {
30 const DISCRIMINATOR: [u8; 8] = TradeEvent::DISCRIMINATOR;
31}
32
33impl InitSpace for TradeEventRef<'_> {
34 const INIT_SPACE: usize = <TradeData as anchor_lang::Space>::INIT_SPACE;
36}
37
38impl Event for TradeEventRef<'_> {}
39
40static_assertions::const_assert_eq!(TradeEventRef::<'static>::INIT_SPACE, TradeEvent::INIT_SPACE);
41
42#[event]
44#[derive(Clone, InitSpace)]
45#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct TradeEvent {
48 pub flags: u8,
52 #[cfg_attr(feature = "debug", debug(skip))]
53 pub(crate) padding_0: [u8; 7],
54 pub trade_id: u64,
56 #[cfg_attr(
58 feature = "serde",
59 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
60 )]
61 pub authority: Pubkey,
62 #[cfg_attr(
64 feature = "serde",
65 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
66 )]
67 pub store: Pubkey,
68 #[cfg_attr(
70 feature = "serde",
71 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
72 )]
73 pub market_token: Pubkey,
74 #[cfg_attr(
76 feature = "serde",
77 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
78 )]
79 pub user: Pubkey,
80 #[cfg_attr(
82 feature = "serde",
83 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
84 )]
85 pub position: Pubkey,
86 #[cfg_attr(
88 feature = "serde",
89 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
90 )]
91 pub order: Pubkey,
92 #[cfg_attr(
94 feature = "serde",
95 serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
96 )]
97 pub final_output_token: Pubkey,
98 pub ts: i64,
100 pub slot: u64,
102 pub before: EventPositionState,
104 pub after: EventPositionState,
106 pub transfer_out: EventTransferOut,
108 #[cfg_attr(feature = "debug", debug(skip))]
109 pub(crate) padding_1: [u8; 8],
110 pub prices: EventTradePrices,
112 pub execution_price: u128,
114 pub price_impact_value: i128,
116 pub price_impact_diff: u128,
118 pub pnl: EventTradePnl,
120 pub fees: EventTradeFees,
122 #[cfg_attr(feature = "serde", serde(default))]
124 pub output_amounts: EventTradeOutputAmounts,
125}
126
127#[cfg(feature = "utils")]
128impl TradeEvent {
129 pub fn get_flag(&self, flag: TradeFlag) -> bool {
131 let map = TradeFlagContainer::from_value(self.flags);
132 map.get_flag(flag)
133 }
134
135 pub fn is_long(&self) -> bool {
137 self.get_flag(TradeFlag::IsLong)
138 }
139
140 pub fn is_collateral_long(&self) -> bool {
142 self.get_flag(TradeFlag::IsCollateralLong)
143 }
144
145 pub fn is_increase(&self) -> bool {
147 self.get_flag(TradeFlag::IsIncrease)
148 }
149
150 pub fn updated_at(&self) -> i64 {
152 self.after.increased_at.max(self.after.decreased_at)
153 }
154
155 pub fn delta_size_in_usd(&self) -> u128 {
157 self.after.size_in_usd.abs_diff(self.before.size_in_usd)
158 }
159
160 pub fn delta_size_in_tokens(&self) -> u128 {
162 self.after
163 .size_in_tokens
164 .abs_diff(self.before.size_in_tokens)
165 }
166
167 pub fn delta_collateral_amount(&self) -> u128 {
169 self.after
170 .collateral_amount
171 .abs_diff(self.before.collateral_amount)
172 }
173
174 pub fn delta_borrowing_factor(&self) -> u128 {
176 self.after
177 .borrowing_factor
178 .abs_diff(self.before.borrowing_factor)
179 }
180
181 pub fn delta_funding_fee_amount_per_size(&self) -> u128 {
183 self.after
184 .funding_fee_amount_per_size
185 .abs_diff(self.before.funding_fee_amount_per_size)
186 }
187
188 pub fn funding_fee(&self) -> u128 {
190 self.delta_funding_fee_amount_per_size()
191 .saturating_mul(self.before.size_in_usd)
192 }
193
194 pub fn delta_claimable_funding_amount_per_size(&self, is_long_token: bool) -> u128 {
196 if is_long_token {
197 self.after
198 .long_token_claimable_funding_amount_per_size
199 .abs_diff(self.before.long_token_claimable_funding_amount_per_size)
200 } else {
201 self.after
202 .short_token_claimable_funding_amount_per_size
203 .abs_diff(self.before.short_token_claimable_funding_amount_per_size)
204 }
205 }
206
207 pub fn to_position(&self, meta: &impl crate::states::HasMarketMeta) -> Position {
209 use crate::states::position::PositionKind;
210
211 let mut position = Position::default();
212
213 let kind = if self.is_long() {
214 PositionKind::Long
215 } else {
216 PositionKind::Short
217 };
218
219 let collateral_token = if self.is_collateral_long() {
220 meta.market_meta().long_token_mint
221 } else {
222 meta.market_meta().short_token_mint
223 };
224
225 position
226 .try_init(
227 kind,
228 0,
230 self.store,
231 &self.user,
232 &self.market_token,
233 &collateral_token,
234 )
235 .unwrap();
236 position.state = self.after.clone().into();
237 position
238 }
239}
240
241#[cfg(feature = "display")]
242impl std::fmt::Display for TradeEvent {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 use crate::utils::pubkey::optional_address;
245
246 f.debug_struct("TradeEvent")
247 .field("trade_id", &self.trade_id)
248 .field("store", &self.store.to_string())
249 .field("market_token", &self.market_token.to_string())
250 .field("user", &self.user.to_string())
251 .field("position", &self.position.to_string())
252 .field("order", &self.order.to_string())
253 .field(
254 "final_output_token",
255 &optional_address(&self.final_output_token),
256 )
257 .field("ts", &self.ts)
258 .field("slot", &self.slot)
259 .field("is_long", &self.is_long())
260 .field("is_collateral_long", &self.is_collateral_long())
261 .field("is_increase", &self.is_increase())
262 .field("delta_collateral_amount", &self.delta_collateral_amount())
263 .field("delta_size_in_usd", &self.delta_size_in_usd())
264 .field("delta_size_in_tokens", &self.delta_size_in_tokens())
265 .field("prices", &self.prices)
266 .field("execution_price", &self.execution_price)
267 .field("price_impact_value", &self.price_impact_value)
268 .field("price_impact_diff", &self.price_impact_diff)
269 .field("pnl", &self.pnl)
270 .field("fees", &self.fees)
271 .field("output_amounts", &self.output_amounts)
272 .field("transfer_out", &self.transfer_out)
273 .finish_non_exhaustive()
274 }
275}
276
277#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
279#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
280#[derive(AnchorDeserialize, AnchorSerialize, InitSpace, Clone)]
281pub struct EventPositionState {
282 pub trade_id: u64,
284 pub increased_at: i64,
286 pub updated_at_slot: u64,
288 pub decreased_at: i64,
290 pub size_in_tokens: u128,
292 pub collateral_amount: u128,
294 pub size_in_usd: u128,
296 pub borrowing_factor: u128,
298 pub funding_fee_amount_per_size: u128,
300 pub long_token_claimable_funding_amount_per_size: u128,
302 pub short_token_claimable_funding_amount_per_size: u128,
304 #[cfg_attr(feature = "debug", debug(skip))]
306 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
307 pub(crate) reserved: [u8; 128],
308}
309
310static_assertions::const_assert_eq!(EventPositionState::INIT_SPACE, PositionState::INIT_SPACE);
311
312#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
314#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
315#[derive(AnchorSerialize, AnchorDeserialize, Default, InitSpace, Clone)]
316pub struct EventTransferOut {
317 pub executed: u8,
319 #[cfg_attr(feature = "debug", debug(skip))]
320 pub(crate) padding_0: [u8; 7],
321 pub final_output_token: u64,
323 pub secondary_output_token: u64,
325 pub long_token: u64,
327 pub short_token: u64,
329 pub long_token_for_claimable_account_of_user: u64,
331 pub short_token_for_claimable_account_of_user: u64,
333 pub long_token_for_claimable_account_of_holding: u64,
335 pub short_token_for_claimable_account_of_holding: u64,
337}
338
339static_assertions::const_assert_eq!(EventTransferOut::INIT_SPACE, TransferOut::INIT_SPACE);
340
341#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
343#[cfg_attr(feature = "debug", derive(Debug))]
344#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
345pub struct EventTradePrice {
346 pub min: u128,
348 pub max: u128,
350}
351
352static_assertions::const_assert_eq!(EventTradePrice::INIT_SPACE, TradePrice::INIT_SPACE);
353
354#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
356#[cfg_attr(feature = "debug", derive(Debug))]
357#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
358pub struct EventTradePrices {
359 pub index: EventTradePrice,
361 pub long: EventTradePrice,
363 pub short: EventTradePrice,
365}
366
367static_assertions::const_assert_eq!(EventTradePrices::INIT_SPACE, TradePrices::INIT_SPACE);
368
369#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371#[cfg_attr(feature = "debug", derive(Debug))]
372#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
373pub struct EventTradePnl {
374 pub pnl: i128,
376 pub uncapped_pnl: i128,
378}
379
380static_assertions::const_assert_eq!(EventTradePnl::INIT_SPACE, TradePnl::INIT_SPACE);
381
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
384#[cfg_attr(feature = "debug", derive(Debug))]
385#[derive(AnchorSerialize, AnchorDeserialize, InitSpace, Clone)]
386pub struct EventTradeFees {
387 pub order_fee_for_receiver_amount: u128,
389 pub order_fee_for_pool_amount: u128,
391 pub liquidation_fee_amount: u128,
393 pub liquidation_fee_for_receiver_amount: u128,
395 pub total_borrowing_fee_amount: u128,
397 pub borrowing_fee_for_receiver_amount: u128,
399 pub funding_fee_amount: u128,
401 pub claimable_funding_fee_long_token_amount: u128,
403 pub claimable_funding_fee_short_token_amount: u128,
405}
406
407static_assertions::const_assert_eq!(EventTradeFees::INIT_SPACE, TradeFees::INIT_SPACE);
408
409#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
411#[cfg_attr(feature = "debug", derive(Debug))]
412#[derive(AnchorSerialize, AnchorDeserialize, Default, InitSpace, Clone)]
413pub struct EventTradeOutputAmounts {
414 pub output_amount: u128,
416 pub secondary_output_amount: u128,
418}
419
420static_assertions::const_assert_eq!(
421 EventTradeOutputAmounts::INIT_SPACE,
422 TradeOutputAmounts::INIT_SPACE
423);