levana_perpswap_cosmos/contracts/market/
history.rs

1//! History data types
2use crate::prelude::*;
3
4/// History events
5pub mod events {
6    use super::*;
7    use crate::constants::event_key;
8    use crate::contracts::market::entry::{
9        LpAction, LpActionKind, PositionAction, PositionActionKind,
10    };
11    use crate::contracts::market::position::PositionId;
12
13    /// Trade volume increased by the given amount
14    pub struct TradeVolumeEvent {
15        /// Additional trade volume
16        pub volume_usd: Usd,
17    }
18
19    impl From<TradeVolumeEvent> for Event {
20        fn from(src: TradeVolumeEvent) -> Self {
21            Event::new("history-trade-volume")
22                .add_attribute("volume-usd", src.volume_usd.to_string())
23        }
24    }
25    impl TryFrom<Event> for TradeVolumeEvent {
26        type Error = anyhow::Error;
27
28        fn try_from(evt: Event) -> anyhow::Result<Self> {
29            Ok(TradeVolumeEvent {
30                volume_usd: evt.decimal_attr("volume-usd")?,
31            })
32        }
33    }
34
35    /// Realized PnL
36    pub struct PnlEvent {
37        /// In collateral
38        pub pnl: Signed<Collateral>,
39        /// In USD
40        pub pnl_usd: Signed<Usd>,
41    }
42
43    impl From<PnlEvent> for Event {
44        fn from(src: PnlEvent) -> Self {
45            Event::new("history-pnl")
46                .add_attribute("pnl", src.pnl.to_string())
47                .add_attribute("pnl-usd", src.pnl_usd.to_string())
48        }
49    }
50    impl TryFrom<Event> for PnlEvent {
51        type Error = anyhow::Error;
52
53        fn try_from(evt: Event) -> anyhow::Result<Self> {
54            Ok(PnlEvent {
55                pnl: evt.number_attr("pnl")?,
56                pnl_usd: evt.number_attr("pnl-usd")?,
57            })
58        }
59    }
60
61    /// Action taken on a position
62    pub struct PositionActionEvent {
63        /// Which position
64        pub pos_id: PositionId,
65        /// Action
66        pub action: PositionAction,
67    }
68
69    impl From<PositionActionEvent> for Event {
70        fn from(src: PositionActionEvent) -> Self {
71            let evt = Event::new("history-position-action")
72                .add_attribute(event_key::POS_ID, src.pos_id.to_string())
73                .add_attribute(
74                    event_key::POSITION_ACTION_KIND,
75                    match src.action.kind {
76                        PositionActionKind::Open => "open",
77                        PositionActionKind::Update => "update",
78                        PositionActionKind::Close => "close",
79                        PositionActionKind::Transfer => "transfer",
80                    },
81                )
82                .add_attribute(
83                    event_key::POSITION_ACTION_TIMESTAMP,
84                    src.action.timestamp.to_string(),
85                )
86                .add_attribute(
87                    event_key::POSITION_ACTION_COLLATERAL,
88                    src.action.collateral.to_string(),
89                )
90                .add_attribute(
91                    event_key::POSITION_ACTION_TRANSFER,
92                    src.action.transfer_collateral.to_string(),
93                );
94
95            let evt = match src.action.price_timestamp {
96                Some(price_timestamp) => evt.add_attribute(
97                    event_key::POSITION_ACTION_PRICE_TIMESTAMP,
98                    price_timestamp.to_string(),
99                ),
100                None => evt,
101            };
102
103            let evt = match src.action.leverage {
104                Some(leverage) => {
105                    evt.add_attribute(event_key::POSITION_ACTION_LEVERAGE, leverage.to_string())
106                }
107                None => evt,
108            };
109            let evt = match src.action.max_gains {
110                Some(max_gains) => {
111                    evt.add_attribute(event_key::POSITION_ACTION_MAX_GAINS, max_gains.to_string())
112                }
113                None => evt,
114            };
115
116            let evt = match src.action.trade_fee {
117                None => evt,
118                Some(trade_fee) => {
119                    evt.add_attribute(event_key::POSITION_ACTION_TRADE_FEE, trade_fee.to_string())
120                }
121            };
122
123            let evt = match src.action.delta_neutrality_fee {
124                None => evt,
125                Some(delta_neutrality_fee) => evt.add_attribute(
126                    event_key::POSITION_ACTION_DELTA_NEUTRALITY_FEE,
127                    delta_neutrality_fee.to_string(),
128                ),
129            };
130
131            let evt = match src.action.old_owner {
132                None => evt,
133                Some(old_owner) => evt.add_attribute(
134                    event_key::POSITION_ACTION_OLD_OWNER,
135                    old_owner.into_string(),
136                ),
137            };
138
139            let evt = match src.action.take_profit_trader {
140                None => evt,
141                Some(take_profit_trader) => evt.add_attribute(
142                    event_key::TAKE_PROFIT_OVERRIDE,
143                    take_profit_trader.to_string(),
144                ),
145            };
146
147            let evt = match src.action.stop_loss_override {
148                None => evt,
149                Some(stop_loss_override) => evt.add_attribute(
150                    event_key::STOP_LOSS_OVERRIDE,
151                    stop_loss_override.to_string(),
152                ),
153            };
154
155            match src.action.new_owner {
156                None => evt,
157                Some(new_owner) => evt.add_attribute(
158                    event_key::POSITION_ACTION_NEW_OWNER,
159                    new_owner.into_string(),
160                ),
161            }
162        }
163    }
164
165    impl TryFrom<Event> for PositionActionEvent {
166        type Error = anyhow::Error;
167
168        fn try_from(evt: Event) -> anyhow::Result<Self> {
169            let pos_id = PositionId::new(evt.u64_attr(event_key::POS_ID)?);
170            Ok(PositionActionEvent {
171                pos_id,
172                action: PositionAction {
173                    id: Some(pos_id),
174                    kind: evt.map_attr_result(event_key::POSITION_ACTION_KIND, |s| match s {
175                        "open" => Ok(PositionActionKind::Open),
176                        "update" => Ok(PositionActionKind::Update),
177                        "close" => Ok(PositionActionKind::Close),
178                        "transfer" => Ok(PositionActionKind::Transfer),
179                        _ => Err(PerpError::unimplemented().into()),
180                    })?,
181                    timestamp: evt.timestamp_attr(event_key::POSITION_ACTION_TIMESTAMP)?,
182                    price_timestamp: evt
183                        .try_timestamp_attr(event_key::POSITION_ACTION_PRICE_TIMESTAMP)?,
184                    collateral: evt.decimal_attr(event_key::POSITION_ACTION_COLLATERAL)?,
185                    transfer_collateral: evt.signed_attr(event_key::POSITION_ACTION_TRANSFER)?,
186                    leverage: evt.try_leverage_to_base_attr(event_key::POSITION_ACTION_LEVERAGE)?,
187                    max_gains: evt
188                        .try_map_attr(event_key::POSITION_ACTION_MAX_GAINS, |value| {
189                            MaxGainsInQuote::try_from(value)
190                        })
191                        .transpose()?,
192                    trade_fee: evt.try_decimal_attr(event_key::POSITION_ACTION_TRADE_FEE)?,
193                    delta_neutrality_fee: evt
194                        .try_number_attr(event_key::POSITION_ACTION_DELTA_NEUTRALITY_FEE)?,
195                    old_owner: evt.try_unchecked_addr_attr(event_key::POSITION_ACTION_OLD_OWNER)?,
196                    new_owner: evt.try_unchecked_addr_attr(event_key::POSITION_ACTION_NEW_OWNER)?,
197                    take_profit_trader: evt
198                        .try_map_attr(event_key::TAKE_PROFIT_OVERRIDE, |s| {
199                            TakeProfitTrader::try_from(s)
200                        })
201                        .transpose()?,
202                    stop_loss_override: evt
203                        .try_price_base_in_quote(event_key::STOP_LOSS_OVERRIDE)?,
204                },
205            })
206        }
207    }
208
209    /// Event when a new action is added to the liquidity provider history.
210    pub struct LpActionEvent {
211        /// Liquidity provider
212        pub addr: Addr,
213        /// Action that occurred
214        pub action: LpAction,
215        /// Identifier for the action
216        pub action_id: u64,
217    }
218
219    impl From<LpActionEvent> for Event {
220        fn from(src: LpActionEvent) -> Self {
221            let event = Event::new("history-lp-action")
222                .add_attribute(event_key::LP_ACTION_ADDRESS, src.addr.to_string())
223                .add_attribute(event_key::LP_ACTION_ID, src.action_id.to_string())
224                .add_attribute(
225                    event_key::LP_ACTION_KIND,
226                    match src.action.kind {
227                        LpActionKind::DepositLp => "deposit-lp",
228                        LpActionKind::DepositXlp => "deposit-xlp",
229                        LpActionKind::ReinvestYieldLp => "reinvest-yield-lp",
230                        LpActionKind::ReinvestYieldXlp => "reinvest-yield-xlp",
231                        LpActionKind::UnstakeXlp => "unstake-xlp",
232                        LpActionKind::Withdraw => "withdraw",
233                        LpActionKind::ClaimYield => "claim-yield",
234                        LpActionKind::CollectLp => "collect-lp",
235                    },
236                )
237                .add_attribute(
238                    event_key::LP_ACTION_TIMESTAMP,
239                    src.action.timestamp.to_string(),
240                )
241                .add_attribute(
242                    event_key::LP_ACTION_COLLATERAL,
243                    src.action.collateral.to_string(),
244                )
245                .add_attribute(
246                    event_key::LP_ACTION_COLLATERAL_USD,
247                    src.action.collateral_usd.to_string(),
248                );
249            match src.action.tokens {
250                Some(tokens) => {
251                    event.add_attribute(event_key::LP_ACTION_TOKENS, tokens.to_string())
252                }
253                None => event,
254            }
255        }
256    }
257
258    impl TryFrom<Event> for LpActionEvent {
259        type Error = anyhow::Error;
260
261        fn try_from(evt: Event) -> anyhow::Result<Self> {
262            Ok(LpActionEvent {
263                addr: evt.unchecked_addr_attr(event_key::LP_ACTION_ADDRESS)?,
264                action_id: evt.u64_attr(event_key::LP_ACTION_ID)?,
265                action: LpAction {
266                    kind: evt.map_attr_result(event_key::LP_ACTION_KIND, |s| match s {
267                        "deposit-lp" => Ok(LpActionKind::DepositLp),
268                        "deposit-xlp" => Ok(LpActionKind::DepositXlp),
269                        "reinvest-yield-lp" => Ok(LpActionKind::ReinvestYieldLp),
270                        "reinvest-yield-xlp" => Ok(LpActionKind::ReinvestYieldXlp),
271                        "unstake-xlp" => Ok(LpActionKind::UnstakeXlp),
272                        "withdraw" => Ok(LpActionKind::Withdraw),
273                        "claim-yield" => Ok(LpActionKind::ClaimYield),
274                        "collect-lp" => Ok(LpActionKind::CollectLp),
275                        _ => Err(PerpError::unimplemented().into()),
276                    })?,
277                    timestamp: evt.timestamp_attr(event_key::LP_ACTION_TIMESTAMP)?,
278                    tokens: evt.try_decimal_attr(event_key::LP_ACTION_TOKENS)?,
279                    collateral: evt.decimal_attr(event_key::LP_ACTION_COLLATERAL)?,
280                    collateral_usd: evt.decimal_attr(event_key::LP_ACTION_COLLATERAL_USD)?,
281                },
282            })
283        }
284    }
285
286    /// Liquidity deposited into the pool
287    pub struct LpDepositEvent {
288        /// Deposited amount in collateral
289        pub deposit: Collateral,
290        /// Current value of deposit in USD
291        pub deposit_usd: Usd,
292    }
293
294    impl From<LpDepositEvent> for Event {
295        fn from(src: LpDepositEvent) -> Self {
296            Event::new("history-lp-deposit")
297                .add_attribute("deposit", src.deposit.to_string())
298                .add_attribute("deposit-usd", src.deposit_usd.to_string())
299        }
300    }
301    impl TryFrom<Event> for LpDepositEvent {
302        type Error = anyhow::Error;
303
304        fn try_from(evt: Event) -> anyhow::Result<Self> {
305            Ok(LpDepositEvent {
306                deposit: evt.decimal_attr("deposit")?,
307                deposit_usd: evt.decimal_attr("deposit-usd")?,
308            })
309        }
310    }
311
312    /// LP yield was claimed
313    pub struct LpYieldEvent {
314        /// Liquidity provider
315        pub addr: Addr,
316        /// Yield amount in collateral
317        pub r#yield: Collateral,
318        /// Yield value in USD at current rate
319        pub yield_usd: Usd,
320    }
321
322    impl From<LpYieldEvent> for Event {
323        fn from(
324            LpYieldEvent {
325                addr,
326                r#yield,
327                yield_usd,
328            }: LpYieldEvent,
329        ) -> Self {
330            Event::new("history-lp-yield")
331                .add_attribute("addr", addr.to_string())
332                .add_attribute("yield", r#yield.to_string())
333                .add_attribute("yield-usd", yield_usd.to_string())
334        }
335    }
336    impl TryFrom<Event> for LpYieldEvent {
337        type Error = anyhow::Error;
338
339        fn try_from(evt: Event) -> anyhow::Result<Self> {
340            Ok(LpYieldEvent {
341                addr: evt.unchecked_addr_attr("addr")?,
342                r#yield: evt.decimal_attr("yield")?,
343                yield_usd: evt.decimal_attr("yield-usd")?,
344            })
345        }
346    }
347}