penumbra_sdk_dex/swap_claim/
action.rs1use anyhow::Context;
2use penumbra_sdk_asset::Balance;
3use penumbra_sdk_fee::Fee;
4use penumbra_sdk_proof_params::GROTH16_PROOF_LENGTH_BYTES;
5use penumbra_sdk_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
6use penumbra_sdk_sct::Nullifier;
7use penumbra_sdk_tct as tct;
8use penumbra_sdk_txhash::{EffectHash, EffectingData};
9use serde::{Deserialize, Serialize};
10
11use crate::BatchSwapOutputData;
12
13use super::proof::SwapClaimProof;
14
15#[derive(Debug, Clone)]
16pub struct SwapClaim {
17 pub proof: SwapClaimProof,
18 pub body: Body,
19 pub epoch_duration: u64,
20}
21
22impl SwapClaim {
23 pub fn balance(&self) -> Balance {
26 self.body.fee.value().into()
27 }
28}
29
30impl EffectingData for SwapClaim {
31 fn effect_hash(&self) -> EffectHash {
32 self.body.effect_hash()
35 }
36}
37
38impl DomainType for SwapClaim {
39 type Proto = pb::SwapClaim;
40}
41
42impl From<SwapClaim> for pb::SwapClaim {
43 fn from(sc: SwapClaim) -> Self {
44 pb::SwapClaim {
45 proof: Some(sc.proof.into()),
46 body: Some(sc.body.into()),
47 epoch_duration: sc.epoch_duration,
48 }
49 }
50}
51
52impl TryFrom<pb::SwapClaim> for SwapClaim {
53 type Error = anyhow::Error;
54 fn try_from(sc: pb::SwapClaim) -> Result<Self, Self::Error> {
55 let proof_bytes: [u8; GROTH16_PROOF_LENGTH_BYTES] = sc
56 .proof
57 .ok_or_else(|| anyhow::anyhow!("missing swap claim proof"))?
58 .inner
59 .as_slice()
60 .try_into()
61 .context("swap claim proof malformed")?;
62 Ok(Self {
63 body: sc
64 .body
65 .ok_or_else(|| anyhow::anyhow!("missing swap claim body"))?
66 .try_into()
67 .context("swap claim body malformed")?,
68 epoch_duration: sc.epoch_duration,
69 proof: SwapClaimProof(proof_bytes),
70 })
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(try_from = "pb::SwapClaimBody", into = "pb::SwapClaimBody")]
76pub struct Body {
77 pub nullifier: Nullifier,
78 pub fee: Fee,
79 pub output_1_commitment: tct::StateCommitment,
80 pub output_2_commitment: tct::StateCommitment,
81 pub output_data: BatchSwapOutputData,
82}
83
84impl EffectingData for Body {
85 fn effect_hash(&self) -> EffectHash {
86 EffectHash::from_proto_effecting_data(&self.to_proto())
87 }
88}
89
90impl DomainType for Body {
91 type Proto = pb::SwapClaimBody;
92}
93
94impl From<Body> for pb::SwapClaimBody {
95 fn from(s: Body) -> Self {
96 pb::SwapClaimBody {
97 nullifier: Some(s.nullifier.into()),
98 fee: Some(s.fee.into()),
99 output_1_commitment: Some(s.output_1_commitment.into()),
100 output_2_commitment: Some(s.output_2_commitment.into()),
101 output_data: Some(s.output_data.into()),
102 }
103 }
104}
105
106impl TryFrom<pb::SwapClaimBody> for Body {
107 type Error = anyhow::Error;
108 fn try_from(sc: pb::SwapClaimBody) -> Result<Self, Self::Error> {
109 Ok(Self {
110 nullifier: sc
111 .nullifier
112 .ok_or_else(|| anyhow::anyhow!("missing nullifier"))?
113 .try_into()?,
114 fee: sc
115 .fee
116 .ok_or_else(|| anyhow::anyhow!("missing fee"))?
117 .try_into()?,
118 output_1_commitment: sc
119 .output_1_commitment
120 .ok_or_else(|| anyhow::anyhow!("missing output_1"))?
121 .try_into()?,
122 output_2_commitment: sc
123 .output_2_commitment
124 .ok_or_else(|| anyhow::anyhow!("missing output_2"))?
125 .try_into()?,
126 output_data: sc
127 .output_data
128 .ok_or_else(|| anyhow::anyhow!("missing anchor"))?
129 .try_into()?,
130 })
131 }
132}