penumbra_sdk_dex/
event.rs

1use crate::{
2    lp::position::{self, Position},
3    swap::Swap,
4    swap_claim::SwapClaim,
5    BatchSwapOutputData, CandlestickData, DirectedTradingPair, SwapExecution, TradingPair,
6};
7use anyhow::{anyhow, Context};
8use penumbra_sdk_asset::asset;
9use penumbra_sdk_num::Amount;
10use penumbra_sdk_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
11use penumbra_sdk_sct::Nullifier;
12use penumbra_sdk_tct::StateCommitment;
13use prost::Name as _;
14
15#[derive(Clone, Debug)]
16pub struct EventSwap {
17    pub trading_pair: TradingPair,
18    pub delta_1_i: Amount,
19    pub delta_2_i: Amount,
20    pub swap_commitment: StateCommitment,
21}
22
23impl From<Swap> for EventSwap {
24    fn from(value: Swap) -> Self {
25        Self::from(&value)
26    }
27}
28
29impl From<&Swap> for EventSwap {
30    fn from(value: &Swap) -> Self {
31        Self {
32            trading_pair: value.body.trading_pair,
33            delta_1_i: value.body.delta_1_i,
34            delta_2_i: value.body.delta_2_i,
35            swap_commitment: value.body.payload.commitment,
36        }
37    }
38}
39
40impl TryFrom<pb::EventSwap> for EventSwap {
41    type Error = anyhow::Error;
42
43    fn try_from(value: pb::EventSwap) -> Result<Self, Self::Error> {
44        fn inner(value: pb::EventSwap) -> anyhow::Result<EventSwap> {
45            Ok(EventSwap {
46                trading_pair: value
47                    .trading_pair
48                    .ok_or(anyhow!("missing `trading_pair`"))?
49                    .try_into()?,
50                delta_1_i: value
51                    .delta_1_i
52                    .ok_or(anyhow!("missing `delta_1_i`"))?
53                    .try_into()?,
54                delta_2_i: value
55                    .delta_2_i
56                    .ok_or(anyhow!("missing `delta_2_i`"))?
57                    .try_into()?,
58                swap_commitment: value
59                    .swap_commitment
60                    .ok_or(anyhow!("missing `swap_commitment`"))?
61                    .try_into()?,
62            })
63        }
64        inner(value).context(format!("parsing {}", pb::EventSwap::NAME))
65    }
66}
67
68impl From<EventSwap> for pb::EventSwap {
69    fn from(value: EventSwap) -> Self {
70        Self {
71            trading_pair: Some(value.trading_pair.into()),
72            delta_1_i: Some(value.delta_1_i.into()),
73            delta_2_i: Some(value.delta_2_i.into()),
74            swap_commitment: Some(value.swap_commitment.into()),
75        }
76    }
77}
78
79impl DomainType for EventSwap {
80    type Proto = pb::EventSwap;
81}
82
83#[derive(Clone, Debug)]
84pub struct EventSwapClaim {
85    pub trading_pair: TradingPair,
86    pub output_1_commitment: StateCommitment,
87    pub output_2_commitment: StateCommitment,
88    pub nullifier: Nullifier,
89}
90
91impl From<SwapClaim> for EventSwapClaim {
92    fn from(value: SwapClaim) -> Self {
93        Self::from(&value)
94    }
95}
96
97impl From<&SwapClaim> for EventSwapClaim {
98    fn from(value: &SwapClaim) -> Self {
99        Self {
100            trading_pair: value.body.output_data.trading_pair,
101            output_1_commitment: value.body.output_1_commitment,
102            output_2_commitment: value.body.output_2_commitment,
103            nullifier: value.body.nullifier,
104        }
105    }
106}
107
108impl TryFrom<pb::EventSwapClaim> for EventSwapClaim {
109    type Error = anyhow::Error;
110
111    fn try_from(value: pb::EventSwapClaim) -> Result<Self, Self::Error> {
112        fn inner(value: pb::EventSwapClaim) -> anyhow::Result<EventSwapClaim> {
113            Ok(EventSwapClaim {
114                trading_pair: value
115                    .trading_pair
116                    .ok_or(anyhow!("missing `trading_pair`"))?
117                    .try_into()?,
118                output_1_commitment: value
119                    .output_1_commitment
120                    .ok_or(anyhow!("missing `output_1_commitment`"))?
121                    .try_into()?,
122                output_2_commitment: value
123                    .output_2_commitment
124                    .ok_or(anyhow!("missing `output_2_commitment`"))?
125                    .try_into()?,
126                nullifier: value
127                    .nullifier
128                    .ok_or(anyhow!("missing `nullifier`"))?
129                    .try_into()?,
130            })
131        }
132        inner(value).context(format!("parsing {}", pb::EventSwapClaim::NAME))
133    }
134}
135
136impl From<EventSwapClaim> for pb::EventSwapClaim {
137    fn from(value: EventSwapClaim) -> Self {
138        Self {
139            trading_pair: Some(value.trading_pair.into()),
140            output_1_commitment: Some(value.output_1_commitment.into()),
141            output_2_commitment: Some(value.output_2_commitment.into()),
142            nullifier: Some(value.nullifier.into()),
143        }
144    }
145}
146
147impl DomainType for EventSwapClaim {
148    type Proto = pb::EventSwapClaim;
149}
150
151#[derive(Clone, Debug)]
152pub struct EventPositionOpen {
153    pub position_id: position::Id,
154    pub trading_pair: TradingPair,
155    pub reserves_1: Amount,
156    pub reserves_2: Amount,
157    pub trading_fee: u32,
158    pub position: Position,
159}
160
161impl From<Position> for EventPositionOpen {
162    fn from(value: Position) -> Self {
163        Self {
164            position_id: value.id(),
165            trading_pair: value.phi.pair,
166            reserves_1: value.reserves_1().amount,
167            reserves_2: value.reserves_2().amount,
168            trading_fee: value.phi.component.fee,
169            position: value,
170        }
171    }
172}
173
174impl TryFrom<pb::EventPositionOpen> for EventPositionOpen {
175    type Error = anyhow::Error;
176
177    fn try_from(value: pb::EventPositionOpen) -> Result<Self, Self::Error> {
178        fn inner(value: pb::EventPositionOpen) -> anyhow::Result<EventPositionOpen> {
179            Ok(EventPositionOpen {
180                position_id: value
181                    .position_id
182                    .ok_or(anyhow!("missing `position_id`"))?
183                    .try_into()?,
184                trading_pair: value
185                    .trading_pair
186                    .ok_or(anyhow!("missing `trading_pair`"))?
187                    .try_into()?,
188                reserves_1: value
189                    .reserves_1
190                    .ok_or(anyhow!("missing `reserves_1`"))?
191                    .try_into()?,
192                reserves_2: value
193                    .reserves_2
194                    .ok_or(anyhow!("missing `reserves_2`"))?
195                    .try_into()?,
196                trading_fee: value.trading_fee,
197                position: value
198                    .position
199                    .ok_or(anyhow!("missing `position`"))?
200                    .try_into()?,
201            })
202        }
203        inner(value).context(format!("parsing {}", pb::EventPositionOpen::NAME))
204    }
205}
206
207impl From<EventPositionOpen> for pb::EventPositionOpen {
208    fn from(value: EventPositionOpen) -> Self {
209        Self {
210            position_id: Some(value.position_id.into()),
211            trading_pair: Some(value.trading_pair.into()),
212            reserves_1: Some(value.reserves_1.into()),
213            reserves_2: Some(value.reserves_2.into()),
214            trading_fee: value.trading_fee,
215            position: Some(value.position.into()),
216        }
217    }
218}
219
220impl DomainType for EventPositionOpen {
221    type Proto = pb::EventPositionOpen;
222}
223
224#[derive(Clone, Debug)]
225pub struct EventPositionClose {
226    pub position_id: position::Id,
227}
228
229impl TryFrom<pb::EventPositionClose> for EventPositionClose {
230    type Error = anyhow::Error;
231
232    fn try_from(value: pb::EventPositionClose) -> Result<Self, Self::Error> {
233        fn inner(value: pb::EventPositionClose) -> anyhow::Result<EventPositionClose> {
234            Ok(EventPositionClose {
235                position_id: value
236                    .position_id
237                    .ok_or(anyhow!("missing `position_id`"))?
238                    .try_into()?,
239            })
240        }
241        inner(value).context(format!("parsing {}", pb::EventPositionClose::NAME))
242    }
243}
244
245impl From<EventPositionClose> for pb::EventPositionClose {
246    fn from(value: EventPositionClose) -> Self {
247        Self {
248            position_id: Some(value.position_id.into()),
249        }
250    }
251}
252
253impl DomainType for EventPositionClose {
254    type Proto = pb::EventPositionClose;
255}
256
257#[derive(Clone, Debug)]
258pub struct EventQueuePositionClose {
259    pub position_id: position::Id,
260}
261
262impl TryFrom<pb::EventQueuePositionClose> for EventQueuePositionClose {
263    type Error = anyhow::Error;
264
265    fn try_from(value: pb::EventQueuePositionClose) -> Result<Self, Self::Error> {
266        fn inner(value: pb::EventQueuePositionClose) -> anyhow::Result<EventQueuePositionClose> {
267            Ok(EventQueuePositionClose {
268                position_id: value
269                    .position_id
270                    .ok_or(anyhow!("missing `position_id`"))?
271                    .try_into()?,
272            })
273        }
274        inner(value).context(format!("parsing {}", pb::EventQueuePositionClose::NAME))
275    }
276}
277
278impl From<EventQueuePositionClose> for pb::EventQueuePositionClose {
279    fn from(value: EventQueuePositionClose) -> Self {
280        Self {
281            position_id: Some(value.position_id.into()),
282        }
283    }
284}
285
286impl DomainType for EventQueuePositionClose {
287    type Proto = pb::EventQueuePositionClose;
288}
289
290#[derive(Clone, Debug)]
291pub struct EventPositionWithdraw {
292    pub position_id: position::Id,
293    pub trading_pair: TradingPair,
294    pub reserves_1: Amount,
295    pub reserves_2: Amount,
296    pub sequence: u64,
297}
298
299impl EventPositionWithdraw {
300    /// Create this event using the usual context available to us.
301    pub fn in_context(position_id: position::Id, final_position_state: &Position) -> Self {
302        let sequence =
303            if let position::State::Withdrawn { sequence, .. } = final_position_state.state {
304                sequence + 1
305            } else {
306                0
307            };
308        Self {
309            position_id,
310            trading_pair: final_position_state.phi.pair,
311            reserves_1: final_position_state.reserves.r1,
312            reserves_2: final_position_state.reserves.r2,
313            sequence,
314        }
315    }
316}
317
318impl TryFrom<pb::EventPositionWithdraw> for EventPositionWithdraw {
319    type Error = anyhow::Error;
320
321    fn try_from(value: pb::EventPositionWithdraw) -> Result<Self, Self::Error> {
322        fn inner(value: pb::EventPositionWithdraw) -> anyhow::Result<EventPositionWithdraw> {
323            Ok(EventPositionWithdraw {
324                position_id: value
325                    .position_id
326                    .ok_or(anyhow!("missing `position_id`"))?
327                    .try_into()?,
328                trading_pair: value
329                    .trading_pair
330                    .ok_or(anyhow!("missing `trading_pair`"))?
331                    .try_into()?,
332                reserves_1: value
333                    .reserves_1
334                    .ok_or(anyhow!("missing `reserves_1`"))?
335                    .try_into()?,
336                reserves_2: value
337                    .reserves_2
338                    .ok_or(anyhow!("missing `reserves_2`"))?
339                    .try_into()?,
340                sequence: value.sequence,
341            })
342        }
343        inner(value).context(format!("parsing {}", pb::EventPositionWithdraw::NAME))
344    }
345}
346
347impl From<EventPositionWithdraw> for pb::EventPositionWithdraw {
348    fn from(value: EventPositionWithdraw) -> Self {
349        Self {
350            position_id: Some(value.position_id.into()),
351            trading_pair: Some(value.trading_pair.into()),
352            reserves_1: Some(value.reserves_1.into()),
353            reserves_2: Some(value.reserves_2.into()),
354            sequence: value.sequence,
355        }
356    }
357}
358
359impl DomainType for EventPositionWithdraw {
360    type Proto = pb::EventPositionWithdraw;
361}
362
363#[derive(Clone, Debug)]
364pub struct EventPositionExecution {
365    pub position_id: position::Id,
366    pub trading_pair: TradingPair,
367    pub reserves_1: Amount,
368    pub reserves_2: Amount,
369    pub prev_reserves_1: Amount,
370    pub prev_reserves_2: Amount,
371    pub context: DirectedTradingPair,
372}
373
374impl EventPositionExecution {
375    /// Create this event using the usual context available to us.
376    pub fn in_context(
377        prev_state: &Position,
378        new_state: &Position,
379        context: DirectedTradingPair,
380    ) -> Self {
381        Self {
382            position_id: new_state.id(),
383            trading_pair: new_state.phi.pair,
384            reserves_1: new_state.reserves_1().amount,
385            reserves_2: new_state.reserves_2().amount,
386            prev_reserves_1: prev_state.reserves_1().amount,
387            prev_reserves_2: prev_state.reserves_2().amount,
388            context,
389        }
390    }
391}
392
393impl TryFrom<pb::EventPositionExecution> for EventPositionExecution {
394    type Error = anyhow::Error;
395
396    fn try_from(value: pb::EventPositionExecution) -> Result<Self, Self::Error> {
397        fn inner(value: pb::EventPositionExecution) -> anyhow::Result<EventPositionExecution> {
398            Ok(EventPositionExecution {
399                position_id: value
400                    .position_id
401                    .ok_or(anyhow!("missing `position_id`"))?
402                    .try_into()?,
403                trading_pair: value
404                    .trading_pair
405                    .ok_or(anyhow!("missing `trading_pair`"))?
406                    .try_into()?,
407                reserves_1: value
408                    .reserves_1
409                    .ok_or(anyhow!("missing `reserves_1`"))?
410                    .try_into()?,
411                reserves_2: value
412                    .reserves_2
413                    .ok_or(anyhow!("missing `reserves_2`"))?
414                    .try_into()?,
415                prev_reserves_1: value
416                    .prev_reserves_1
417                    .ok_or(anyhow!("missing `prev_reserves_1`"))?
418                    .try_into()?,
419                prev_reserves_2: value
420                    .prev_reserves_2
421                    .ok_or(anyhow!("missing `prev_reserves_2`"))?
422                    .try_into()?,
423                context: value
424                    .context
425                    .ok_or(anyhow!("missing `context`"))?
426                    .try_into()?,
427            })
428        }
429        inner(value).context(format!("parsing {}", pb::EventPositionExecution::NAME))
430    }
431}
432
433impl From<EventPositionExecution> for pb::EventPositionExecution {
434    fn from(value: EventPositionExecution) -> Self {
435        Self {
436            position_id: Some(value.position_id.into()),
437            trading_pair: Some(value.trading_pair.into()),
438            reserves_1: Some(value.reserves_1.into()),
439            reserves_2: Some(value.reserves_2.into()),
440            prev_reserves_1: Some(value.prev_reserves_1.into()),
441            prev_reserves_2: Some(value.prev_reserves_2.into()),
442            context: Some(value.context.into()),
443        }
444    }
445}
446
447impl DomainType for EventPositionExecution {
448    type Proto = pb::EventPositionExecution;
449}
450
451#[derive(Clone, Debug)]
452pub struct EventBatchSwap {
453    pub batch_swap_output_data: BatchSwapOutputData,
454    pub swap_execution_1_for_2: Option<SwapExecution>,
455    pub swap_execution_2_for_1: Option<SwapExecution>,
456}
457
458impl TryFrom<pb::EventBatchSwap> for EventBatchSwap {
459    type Error = anyhow::Error;
460
461    fn try_from(value: pb::EventBatchSwap) -> Result<Self, Self::Error> {
462        fn inner(value: pb::EventBatchSwap) -> anyhow::Result<EventBatchSwap> {
463            Ok(EventBatchSwap {
464                batch_swap_output_data: value
465                    .batch_swap_output_data
466                    .ok_or(anyhow!("missing `batch_swap_output_data`"))?
467                    .try_into()?,
468                swap_execution_1_for_2: value
469                    .swap_execution_1_for_2
470                    .map(|x| x.try_into())
471                    .transpose()?,
472                swap_execution_2_for_1: value
473                    .swap_execution_2_for_1
474                    .map(|x| x.try_into())
475                    .transpose()?,
476            })
477        }
478        inner(value).context(format!("parsing {}", pb::EventBatchSwap::NAME))
479    }
480}
481
482impl From<EventBatchSwap> for pb::EventBatchSwap {
483    fn from(value: EventBatchSwap) -> Self {
484        Self {
485            batch_swap_output_data: Some(value.batch_swap_output_data.into()),
486            swap_execution_1_for_2: value.swap_execution_1_for_2.map(|x| x.into()),
487            swap_execution_2_for_1: value.swap_execution_2_for_1.map(|x| x.into()),
488        }
489    }
490}
491
492impl DomainType for EventBatchSwap {
493    type Proto = pb::EventBatchSwap;
494}
495
496#[derive(Clone, Debug)]
497pub struct EventArbExecution {
498    pub height: u64,
499    pub swap_execution: SwapExecution,
500}
501
502impl TryFrom<pb::EventArbExecution> for EventArbExecution {
503    type Error = anyhow::Error;
504
505    fn try_from(value: pb::EventArbExecution) -> Result<Self, Self::Error> {
506        fn inner(value: pb::EventArbExecution) -> anyhow::Result<EventArbExecution> {
507            Ok(EventArbExecution {
508                height: value.height,
509                swap_execution: value
510                    .swap_execution
511                    .ok_or(anyhow!("missing `swap_execution`"))?
512                    .try_into()?,
513            })
514        }
515        inner(value).context(format!("parsing {}", pb::EventArbExecution::NAME))
516    }
517}
518
519impl From<EventArbExecution> for pb::EventArbExecution {
520    fn from(value: EventArbExecution) -> Self {
521        Self {
522            height: value.height,
523            swap_execution: Some(value.swap_execution.into()),
524        }
525    }
526}
527
528impl DomainType for EventArbExecution {
529    type Proto = pb::EventArbExecution;
530}
531
532#[derive(Clone, Debug)]
533pub struct EventValueCircuitBreakerCredit {
534    pub asset_id: asset::Id,
535    pub previous_balance: Amount,
536    pub new_balance: Amount,
537}
538
539impl TryFrom<pb::EventValueCircuitBreakerCredit> for EventValueCircuitBreakerCredit {
540    type Error = anyhow::Error;
541
542    fn try_from(value: pb::EventValueCircuitBreakerCredit) -> Result<Self, Self::Error> {
543        fn inner(
544            value: pb::EventValueCircuitBreakerCredit,
545        ) -> anyhow::Result<EventValueCircuitBreakerCredit> {
546            Ok(EventValueCircuitBreakerCredit {
547                asset_id: value
548                    .asset_id
549                    .ok_or(anyhow!("missing `asset_id`"))?
550                    .try_into()?,
551                previous_balance: value
552                    .previous_balance
553                    .ok_or(anyhow!("missing `previous_balance`"))?
554                    .try_into()?,
555                new_balance: value
556                    .new_balance
557                    .ok_or(anyhow!("missing `new_balance`"))?
558                    .try_into()?,
559            })
560        }
561        inner(value).context(format!(
562            "parsing {}",
563            pb::EventValueCircuitBreakerCredit::NAME
564        ))
565    }
566}
567
568impl From<EventValueCircuitBreakerCredit> for pb::EventValueCircuitBreakerCredit {
569    fn from(value: EventValueCircuitBreakerCredit) -> Self {
570        Self {
571            asset_id: Some(value.asset_id.into()),
572            previous_balance: Some(value.previous_balance.into()),
573            new_balance: Some(value.new_balance.into()),
574        }
575    }
576}
577
578impl DomainType for EventValueCircuitBreakerCredit {
579    type Proto = pb::EventValueCircuitBreakerCredit;
580}
581
582#[derive(Clone, Debug)]
583pub struct EventValueCircuitBreakerDebit {
584    pub asset_id: asset::Id,
585    pub previous_balance: Amount,
586    pub new_balance: Amount,
587}
588
589impl TryFrom<pb::EventValueCircuitBreakerDebit> for EventValueCircuitBreakerDebit {
590    type Error = anyhow::Error;
591
592    fn try_from(value: pb::EventValueCircuitBreakerDebit) -> Result<Self, Self::Error> {
593        fn inner(
594            value: pb::EventValueCircuitBreakerDebit,
595        ) -> anyhow::Result<EventValueCircuitBreakerDebit> {
596            Ok(EventValueCircuitBreakerDebit {
597                asset_id: value
598                    .asset_id
599                    .ok_or(anyhow!("missing `asset_id`"))?
600                    .try_into()?,
601                previous_balance: value
602                    .previous_balance
603                    .ok_or(anyhow!("missing `previous_balance`"))?
604                    .try_into()?,
605                new_balance: value
606                    .new_balance
607                    .ok_or(anyhow!("missing `new_balance`"))?
608                    .try_into()?,
609            })
610        }
611        inner(value).context(format!(
612            "parsing {}",
613            pb::EventValueCircuitBreakerDebit::NAME
614        ))
615    }
616}
617
618impl From<EventValueCircuitBreakerDebit> for pb::EventValueCircuitBreakerDebit {
619    fn from(value: EventValueCircuitBreakerDebit) -> Self {
620        Self {
621            asset_id: Some(value.asset_id.into()),
622            previous_balance: Some(value.previous_balance.into()),
623            new_balance: Some(value.new_balance.into()),
624        }
625    }
626}
627
628impl DomainType for EventValueCircuitBreakerDebit {
629    type Proto = pb::EventValueCircuitBreakerDebit;
630}
631
632#[derive(Clone, Debug)]
633pub struct EventCandlestickData {
634    pub pair: DirectedTradingPair,
635    pub stick: CandlestickData,
636}
637
638impl TryFrom<pb::EventCandlestickData> for EventCandlestickData {
639    type Error = anyhow::Error;
640
641    fn try_from(value: pb::EventCandlestickData) -> Result<Self, Self::Error> {
642        fn inner(value: pb::EventCandlestickData) -> anyhow::Result<EventCandlestickData> {
643            Ok(EventCandlestickData {
644                pair: value.pair.ok_or(anyhow!("missing `pair`"))?.try_into()?,
645                stick: value.stick.ok_or(anyhow!("missing `stick`"))?.try_into()?,
646            })
647        }
648        inner(value).context(format!("parsing {}", pb::EventCandlestickData::NAME))
649    }
650}
651
652impl From<EventCandlestickData> for pb::EventCandlestickData {
653    fn from(value: EventCandlestickData) -> Self {
654        Self {
655            pair: Some(value.pair.into()),
656            stick: Some(value.stick.into()),
657        }
658    }
659}
660
661impl DomainType for EventCandlestickData {
662    type Proto = pb::EventCandlestickData;
663}