penumbra_sdk_dex/lp/
action.rs1use serde::{Deserialize, Serialize};
2
3use penumbra_sdk_asset::{balance, Balance, Value};
4use penumbra_sdk_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
5use penumbra_sdk_txhash::{EffectHash, EffectingData};
6
7use super::{position, position::Position, LpNft};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(try_from = "pb::PositionOpen", into = "pb::PositionOpen")]
15pub struct PositionOpen {
16 pub position: Position,
21 pub encrypted_metadata: Option<Vec<u8>>,
26}
27
28impl EffectingData for PositionOpen {
29 fn effect_hash(&self) -> EffectHash {
30 EffectHash::from_proto_effecting_data(&self.to_proto())
33 }
34}
35
36impl PositionOpen {
37 pub fn balance(&self) -> Balance {
39 let opened_position_nft = Value {
40 amount: 1u64.into(),
41 asset_id: LpNft::new(self.position.id(), position::State::Opened).asset_id(),
42 };
43
44 let reserves = self.position.reserves.balance(&self.position.phi.pair);
45
46 Balance::from(opened_position_nft) - reserves
48 }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(try_from = "pb::PositionClose", into = "pb::PositionClose")]
62pub struct PositionClose {
63 pub position_id: position::Id,
64}
65
66impl EffectingData for PositionClose {
67 fn effect_hash(&self) -> EffectHash {
68 EffectHash::from_proto_effecting_data(&self.to_proto())
69 }
70}
71
72impl PositionClose {
73 pub fn balance(&self) -> Balance {
75 let opened_position_nft = Value {
76 amount: 1u64.into(),
77 asset_id: LpNft::new(self.position_id, position::State::Opened).asset_id(),
78 };
79
80 let closed_position_nft = Value {
81 amount: 1u64.into(),
82 asset_id: LpNft::new(self.position_id, position::State::Closed).asset_id(),
83 };
84
85 Balance::from(closed_position_nft) - opened_position_nft
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
96#[serde(try_from = "pb::PositionWithdraw", into = "pb::PositionWithdraw")]
97pub struct PositionWithdraw {
98 pub position_id: position::Id,
99 pub reserves_commitment: balance::Commitment,
103 pub sequence: u64,
105}
106
107impl EffectingData for PositionWithdraw {
108 fn effect_hash(&self) -> EffectHash {
109 EffectHash::from_proto_effecting_data(&self.to_proto())
110 }
111}
112
113impl DomainType for PositionOpen {
114 type Proto = pb::PositionOpen;
115}
116
117impl From<PositionOpen> for pb::PositionOpen {
118 fn from(value: PositionOpen) -> Self {
119 Self {
120 position: Some(value.position.into()),
121 encrypted_metadata: value.encrypted_metadata.unwrap_or_default(),
122 }
123 }
124}
125
126impl TryFrom<pb::PositionOpen> for PositionOpen {
127 type Error = anyhow::Error;
128
129 fn try_from(value: pb::PositionOpen) -> Result<Self, Self::Error> {
130 if !value.encrypted_metadata.is_empty() && value.encrypted_metadata.len() != 50 {
132 anyhow::bail!("encrypted_metadata must be exactly 50 bytes if present");
133 }
134
135 Ok(Self {
136 position: value
137 .position
138 .ok_or_else(|| anyhow::anyhow!("missing position"))?
139 .try_into()?,
140 encrypted_metadata: if value.encrypted_metadata.is_empty() {
141 None
142 } else {
143 Some(value.encrypted_metadata)
144 },
145 })
146 }
147}
148
149impl DomainType for PositionClose {
150 type Proto = pb::PositionClose;
151}
152
153impl From<PositionClose> for pb::PositionClose {
154 fn from(value: PositionClose) -> Self {
155 Self {
156 position_id: Some(value.position_id.into()),
157 }
158 }
159}
160
161impl TryFrom<pb::PositionClose> for PositionClose {
162 type Error = anyhow::Error;
163
164 fn try_from(value: pb::PositionClose) -> Result<Self, Self::Error> {
165 Ok(Self {
166 position_id: value
167 .position_id
168 .ok_or_else(|| anyhow::anyhow!("missing position_id"))?
169 .try_into()?,
170 })
171 }
172}
173
174impl DomainType for PositionWithdraw {
175 type Proto = pb::PositionWithdraw;
176}
177
178impl From<PositionWithdraw> for pb::PositionWithdraw {
179 fn from(value: PositionWithdraw) -> Self {
180 Self {
181 position_id: Some(value.position_id.into()),
182 reserves_commitment: Some(value.reserves_commitment.into()),
183 sequence: value.sequence,
184 }
185 }
186}
187
188impl TryFrom<pb::PositionWithdraw> for PositionWithdraw {
189 type Error = anyhow::Error;
190
191 fn try_from(value: pb::PositionWithdraw) -> Result<Self, Self::Error> {
192 Ok(Self {
193 position_id: value
194 .position_id
195 .ok_or_else(|| anyhow::anyhow!("missing position_id"))?
196 .try_into()?,
197 reserves_commitment: value
198 .reserves_commitment
199 .ok_or_else(|| anyhow::anyhow!("missing balance_commitment"))?
200 .try_into()?,
201 sequence: value.sequence,
202 })
203 }
204}