penumbra_sdk_dex/swap/
action.rs

1use anyhow::Context;
2use ark_ff::Zero;
3use decaf377::Fr;
4use penumbra_sdk_asset::{balance, Balance, Value};
5use penumbra_sdk_num::Amount;
6use penumbra_sdk_proto::{
7    core::component::dex::v1 as pbc, penumbra::core::component::dex::v1 as pb, DomainType,
8};
9use penumbra_sdk_txhash::{EffectHash, EffectingData};
10use serde::{Deserialize, Serialize};
11
12use crate::TradingPair;
13
14use super::{proof::SwapProof, SwapPayload};
15
16#[derive(Clone, Debug)]
17pub struct Swap {
18    pub proof: SwapProof,
19    pub body: Body,
20}
21
22impl Swap {
23    /// Temporary method until we resolve where `IsAction::balance_commitment` should live.
24    pub fn balance_commitment_inner(&self) -> balance::Commitment {
25        let input_1 = Value {
26            amount: self.body.delta_1_i,
27            asset_id: self.body.trading_pair.asset_1(),
28        };
29        let input_1 = -Balance::from(input_1);
30        let commitment_input_1 = input_1.commit(Fr::zero());
31        let input_2 = Value {
32            amount: self.body.delta_2_i,
33            asset_id: self.body.trading_pair.asset_2(),
34        };
35        let input_2 = -Balance::from(input_2);
36        let commitment_input_2 = input_2.commit(Fr::zero());
37
38        commitment_input_1 + commitment_input_2 + self.body.fee_commitment
39    }
40}
41
42impl EffectingData for Swap {
43    fn effect_hash(&self) -> EffectHash {
44        // The effecting data is in the body of the swap, so we can
45        // just use hash the proto-encoding of the body.
46        self.body.effect_hash()
47    }
48}
49
50impl DomainType for Swap {
51    type Proto = pb::Swap;
52}
53
54impl From<Swap> for pb::Swap {
55    fn from(s: Swap) -> Self {
56        let proof: pbc::ZkSwapProof = s.proof.into();
57        pb::Swap {
58            proof: Some(proof),
59            body: Some(s.body.into()),
60        }
61    }
62}
63
64impl TryFrom<pb::Swap> for Swap {
65    type Error = anyhow::Error;
66    fn try_from(s: pb::Swap) -> Result<Self, Self::Error> {
67        Ok(Self {
68            proof: s
69                .proof
70                .ok_or_else(|| anyhow::anyhow!("missing swap proof"))?
71                .try_into()
72                .context("swap proof malformed")?,
73            body: s
74                .body
75                .ok_or_else(|| anyhow::anyhow!("missing swap body"))?
76                .try_into()
77                .context("swap body malformed")?,
78        })
79    }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(try_from = "pb::SwapBody", into = "pb::SwapBody")]
84pub struct Body {
85    pub trading_pair: TradingPair,
86    pub delta_1_i: Amount,
87    pub delta_2_i: Amount,
88    pub fee_commitment: balance::Commitment,
89    pub payload: SwapPayload,
90}
91
92impl EffectingData for Body {
93    fn effect_hash(&self) -> EffectHash {
94        EffectHash::from_proto_effecting_data(&self.to_proto())
95    }
96}
97
98impl DomainType for Body {
99    type Proto = pb::SwapBody;
100}
101
102impl From<Body> for pb::SwapBody {
103    fn from(s: Body) -> Self {
104        pb::SwapBody {
105            trading_pair: Some(s.trading_pair.into()),
106            delta_1_i: Some(s.delta_1_i.into()),
107            delta_2_i: Some(s.delta_2_i.into()),
108            fee_commitment: Some(s.fee_commitment.into()),
109            payload: Some(s.payload.into()),
110        }
111    }
112}
113
114impl TryFrom<pb::SwapBody> for Body {
115    type Error = anyhow::Error;
116    fn try_from(s: pb::SwapBody) -> Result<Self, Self::Error> {
117        Ok(Self {
118            trading_pair: s
119                .trading_pair
120                .ok_or_else(|| anyhow::anyhow!("missing trading_pair"))?
121                .try_into()?,
122
123            delta_1_i: s
124                .delta_1_i
125                .ok_or_else(|| anyhow::anyhow!("missing delta_1"))?
126                .try_into()?,
127            delta_2_i: s
128                .delta_2_i
129                .ok_or_else(|| anyhow::anyhow!("missing delta_2"))?
130                .try_into()?,
131
132            fee_commitment: s
133                .fee_commitment
134                .ok_or_else(|| anyhow::anyhow!("missing fee_commitment"))?
135                .try_into()?,
136            payload: s
137                .payload
138                .ok_or_else(|| anyhow::anyhow!("missing payload"))?
139                .try_into()?,
140        })
141    }
142}