penumbra_sdk_dex/lp/
plan.rs1use ark_ff::Zero;
2use decaf377::Fr;
3use penumbra_sdk_asset::{balance, Balance, Value};
4use penumbra_sdk_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 lp::{position, LpNft, Reserves},
9 TradingPair,
10};
11
12use super::action::PositionWithdraw;
13
14#[derive(Clone, Debug, Deserialize, Serialize)]
16#[serde(
17 try_from = "pb::PositionWithdrawPlan",
18 into = "pb::PositionWithdrawPlan"
19)]
20pub struct PositionWithdrawPlan {
21 pub reserves: Reserves,
22 pub position_id: position::Id,
23 pub pair: TradingPair,
24 pub sequence: u64,
25 pub rewards: Vec<Value>,
26}
27
28impl PositionWithdrawPlan {
29 pub fn position_withdraw(&self) -> PositionWithdraw {
31 PositionWithdraw {
32 position_id: self.position_id,
33 reserves_commitment: self.reserves_commitment(),
34 sequence: self.sequence,
35 }
36 }
37
38 pub fn reserves_commitment(&self) -> balance::Commitment {
39 let mut reserves_balance = self.reserves.balance(&self.pair);
40 for reward in &self.rewards {
41 reserves_balance += *reward;
42 }
43 reserves_balance.commit(Fr::zero())
44 }
45
46 pub fn balance(&self) -> Balance {
47 let mut balance = self.reserves.balance(&self.pair);
51
52 balance -= if self.sequence == 0 {
55 Value {
56 amount: 1u64.into(),
57 asset_id: LpNft::new(self.position_id, position::State::Closed).asset_id(),
58 }
59 } else {
60 Value {
61 amount: 1u64.into(),
62 asset_id: LpNft::new(
63 self.position_id,
64 position::State::Withdrawn {
65 sequence: self.sequence - 1,
66 },
67 )
68 .asset_id(),
69 }
70 };
71 balance += Value {
72 amount: 1u64.into(),
73 asset_id: LpNft::new(
74 self.position_id,
75 position::State::Withdrawn {
76 sequence: self.sequence,
77 },
78 )
79 .asset_id(),
80 };
81
82 balance
83 }
84}
85
86impl DomainType for PositionWithdrawPlan {
87 type Proto = pb::PositionWithdrawPlan;
88}
89
90impl From<PositionWithdrawPlan> for pb::PositionWithdrawPlan {
91 fn from(msg: PositionWithdrawPlan) -> Self {
92 Self {
93 reserves: Some(msg.reserves.into()),
94 position_id: Some(msg.position_id.into()),
95 pair: Some(msg.pair.into()),
96 sequence: msg.sequence,
97 rewards: msg.rewards.into_iter().map(Into::into).collect(),
98 }
99 }
100}
101
102impl TryFrom<pb::PositionWithdrawPlan> for PositionWithdrawPlan {
103 type Error = anyhow::Error;
104 fn try_from(msg: pb::PositionWithdrawPlan) -> Result<Self, Self::Error> {
105 Ok(Self {
106 reserves: msg
107 .reserves
108 .ok_or_else(|| anyhow::anyhow!("missing reserves"))?
109 .try_into()?,
110 position_id: msg
111 .position_id
112 .ok_or_else(|| anyhow::anyhow!("missing position_id"))?
113 .try_into()?,
114 pair: msg
115 .pair
116 .ok_or_else(|| anyhow::anyhow!("missing pair"))?
117 .try_into()?,
118 sequence: msg.sequence,
119 rewards: msg
120 .rewards
121 .into_iter()
122 .map(TryInto::try_into)
123 .collect::<Result<_, _>>()?,
124 })
125 }
126}