gmsol_store/events/trade/
event.rs

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/// This is a cheaper variant of [`TradeEvent`], sharing the same format
19/// for serialization.
20#[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    // The borsh init space of `TradeData` is used here.
35    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/// Trade event.
43#[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    /// Trade flag.
49    // Note: The concrete type can be replaced with the type alias `TradeFlag`.
50    // However, this will cause the IDL build to fail in `anchor v0.30.1`.
51    pub flags: u8,
52    #[cfg_attr(feature = "debug", debug(skip))]
53    pub(crate) padding_0: [u8; 7],
54    /// Trade id.
55    pub trade_id: u64,
56    /// Authority.
57    #[cfg_attr(
58        feature = "serde",
59        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
60    )]
61    pub authority: Pubkey,
62    /// Store address.
63    #[cfg_attr(
64        feature = "serde",
65        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
66    )]
67    pub store: Pubkey,
68    /// Market token.
69    #[cfg_attr(
70        feature = "serde",
71        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
72    )]
73    pub market_token: Pubkey,
74    /// User.
75    #[cfg_attr(
76        feature = "serde",
77        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
78    )]
79    pub user: Pubkey,
80    /// Position address.
81    #[cfg_attr(
82        feature = "serde",
83        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
84    )]
85    pub position: Pubkey,
86    /// Order address.
87    #[cfg_attr(
88        feature = "serde",
89        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
90    )]
91    pub order: Pubkey,
92    /// Final output token.
93    #[cfg_attr(
94        feature = "serde",
95        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
96    )]
97    pub final_output_token: Pubkey,
98    /// Trade ts.
99    pub ts: i64,
100    /// Trade slot.
101    pub slot: u64,
102    /// Before state.
103    pub before: EventPositionState,
104    /// After state.
105    pub after: EventPositionState,
106    /// Transfer out.
107    pub transfer_out: EventTransferOut,
108    #[cfg_attr(feature = "debug", debug(skip))]
109    pub(crate) padding_1: [u8; 8],
110    /// Prices.
111    pub prices: EventTradePrices,
112    /// Execution price.
113    pub execution_price: u128,
114    /// Price impact value.
115    pub price_impact_value: i128,
116    /// Price impact diff.
117    pub price_impact_diff: u128,
118    /// Processed pnl.
119    pub pnl: EventTradePnl,
120    /// Fees.
121    pub fees: EventTradeFees,
122    /// Output amounts.
123    #[cfg_attr(feature = "serde", serde(default))]
124    pub output_amounts: EventTradeOutputAmounts,
125}
126
127#[cfg(feature = "utils")]
128impl TradeEvent {
129    /// Get trade data flag.
130    pub fn get_flag(&self, flag: TradeFlag) -> bool {
131        let map = TradeFlagContainer::from_value(self.flags);
132        map.get_flag(flag)
133    }
134
135    /// Return whether the position side is long.
136    pub fn is_long(&self) -> bool {
137        self.get_flag(TradeFlag::IsLong)
138    }
139
140    /// Return whether the collateral side is long.
141    pub fn is_collateral_long(&self) -> bool {
142        self.get_flag(TradeFlag::IsCollateralLong)
143    }
144
145    /// Return whether the trade is caused by an increase order.
146    pub fn is_increase(&self) -> bool {
147        self.get_flag(TradeFlag::IsIncrease)
148    }
149
150    /// Updated at.
151    pub fn updated_at(&self) -> i64 {
152        self.after.increased_at.max(self.after.decreased_at)
153    }
154
155    /// Delta size in usd.
156    pub fn delta_size_in_usd(&self) -> u128 {
157        self.after.size_in_usd.abs_diff(self.before.size_in_usd)
158    }
159
160    /// Delta size in tokens.
161    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    /// Delta collateral amount.
168    pub fn delta_collateral_amount(&self) -> u128 {
169        self.after
170            .collateral_amount
171            .abs_diff(self.before.collateral_amount)
172    }
173
174    /// Delta borrowing factor.
175    pub fn delta_borrowing_factor(&self) -> u128 {
176        self.after
177            .borrowing_factor
178            .abs_diff(self.before.borrowing_factor)
179    }
180
181    /// Delta funding fee amount per size.
182    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    /// Funding fee amount.
189    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    /// Delta claimable amount per size.
195    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    /// Create position from this event.
208    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                // Note: there's no need to provide a correct bump here for now.
229                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/// Position State.
278#[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    /// Trade id.
283    pub trade_id: u64,
284    /// The time that the position last increased at.
285    pub increased_at: i64,
286    /// Updated at slot.
287    pub updated_at_slot: u64,
288    /// The time that the position last decreased at.
289    pub decreased_at: i64,
290    /// Size in tokens.
291    pub size_in_tokens: u128,
292    /// Collateral amount.
293    pub collateral_amount: u128,
294    /// Size in usd.
295    pub size_in_usd: u128,
296    /// Borrowing factor.
297    pub borrowing_factor: u128,
298    /// Funding fee amount per size.
299    pub funding_fee_amount_per_size: u128,
300    /// Long token claimable funding amount per size.
301    pub long_token_claimable_funding_amount_per_size: u128,
302    /// Short token claimable funding amount per size.
303    pub short_token_claimable_funding_amount_per_size: u128,
304    /// Reserved.
305    #[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/// Transfer Out.
313#[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    /// Executed.
318    pub executed: u8,
319    #[cfg_attr(feature = "debug", debug(skip))]
320    pub(crate) padding_0: [u8; 7],
321    /// Final output token.
322    pub final_output_token: u64,
323    /// Secondary output token.
324    pub secondary_output_token: u64,
325    /// Long token.
326    pub long_token: u64,
327    /// Short token.
328    pub short_token: u64,
329    /// Long token amount for claimable account of user.
330    pub long_token_for_claimable_account_of_user: u64,
331    /// Short token amount for cliamable account of user.
332    pub short_token_for_claimable_account_of_user: u64,
333    /// Long token amount for claimable account of holding.
334    pub long_token_for_claimable_account_of_holding: u64,
335    /// Short token amount for claimable account of holding.
336    pub short_token_for_claimable_account_of_holding: u64,
337}
338
339static_assertions::const_assert_eq!(EventTransferOut::INIT_SPACE, TransferOut::INIT_SPACE);
340
341/// Price.
342#[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    /// Min price.
347    pub min: u128,
348    /// Max price.
349    pub max: u128,
350}
351
352static_assertions::const_assert_eq!(EventTradePrice::INIT_SPACE, TradePrice::INIT_SPACE);
353
354/// Trade Prices.
355#[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    /// Index token price.
360    pub index: EventTradePrice,
361    /// Long token price.
362    pub long: EventTradePrice,
363    /// Short token price.
364    pub short: EventTradePrice,
365}
366
367static_assertions::const_assert_eq!(EventTradePrices::INIT_SPACE, TradePrices::INIT_SPACE);
368
369/// Trade PnL.
370#[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    /// Final PnL value.
375    pub pnl: i128,
376    /// Uncapped PnL value.
377    pub uncapped_pnl: i128,
378}
379
380static_assertions::const_assert_eq!(EventTradePnl::INIT_SPACE, TradePnl::INIT_SPACE);
381
382/// Trade Fees.
383#[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    /// Order fee for receiver amount.
388    pub order_fee_for_receiver_amount: u128,
389    /// Order fee for pool amount.
390    pub order_fee_for_pool_amount: u128,
391    /// Total liquidation fee amount.
392    pub liquidation_fee_amount: u128,
393    /// Liquidation fee for pool amount.
394    pub liquidation_fee_for_receiver_amount: u128,
395    /// Total borrowing fee amount.
396    pub total_borrowing_fee_amount: u128,
397    /// Borrowing fee for receiver amount.
398    pub borrowing_fee_for_receiver_amount: u128,
399    /// Funding fee amount.
400    pub funding_fee_amount: u128,
401    /// Claimable funding fee long token amount.
402    pub claimable_funding_fee_long_token_amount: u128,
403    /// Claimable funding fee short token amount.
404    pub claimable_funding_fee_short_token_amount: u128,
405}
406
407static_assertions::const_assert_eq!(EventTradeFees::INIT_SPACE, TradeFees::INIT_SPACE);
408
409/// Output amounts.
410#[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    /// Output amount.
415    pub output_amount: u128,
416    /// Secondary output amount.
417    pub secondary_output_amount: u128,
418}
419
420static_assertions::const_assert_eq!(
421    EventTradeOutputAmounts::INIT_SPACE,
422    TradeOutputAmounts::INIT_SPACE
423);