penumbra_sdk_dex/lp/
reserves.rs1use penumbra_sdk_asset::{Balance, Value};
2use penumbra_sdk_num::Amount;
3use penumbra_sdk_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
4
5use crate::TradingPair;
6
7use super::position::MAX_RESERVE_AMOUNT;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Reserves {
17 pub r1: Amount,
18 pub r2: Amount,
19}
20
21impl Reserves {
22 pub fn check_bounds(&self) -> anyhow::Result<()> {
23 if self.r1.value() > MAX_RESERVE_AMOUNT || self.r2.value() > MAX_RESERVE_AMOUNT {
24 anyhow::bail!(format!(
25 "Reserve amounts are out-of-bounds (limit: {MAX_RESERVE_AMOUNT})"
26 ))
27 } else {
28 if self.r1.value() == 0 && self.r2.value() == 0 {
30 anyhow::bail!("initial reserves must provision some amount of either asset",);
31 }
32
33 Ok(())
34 }
35 }
36
37 pub fn balance(&self, pair: &TradingPair) -> Balance {
39 let r1 = Value {
40 amount: self.r1,
41 asset_id: pair.asset_1(),
42 };
43
44 let r2 = Value {
45 amount: self.r2,
46 asset_id: pair.asset_2(),
47 };
48
49 Balance::from(r1) + r2
50 }
51
52 pub fn flip(&self) -> Reserves {
54 Self {
55 r1: self.r2,
56 r2: self.r1,
57 }
58 }
59
60 pub fn zero() -> Self {
62 Self {
63 r1: Amount::zero(),
64 r2: Amount::zero(),
65 }
66 }
67}
68
69impl Default for Reserves {
70 fn default() -> Self {
71 Self::zero()
72 }
73}
74
75impl DomainType for Reserves {
76 type Proto = pb::Reserves;
77}
78
79impl TryFrom<pb::Reserves> for Reserves {
80 type Error = anyhow::Error;
81
82 fn try_from(value: pb::Reserves) -> Result<Self, Self::Error> {
83 Ok(Self {
84 r1: value
85 .r1
86 .ok_or_else(|| anyhow::anyhow!("missing r1"))?
87 .try_into()?,
88 r2: value
89 .r2
90 .ok_or_else(|| anyhow::anyhow!("missing r2"))?
91 .try_into()?,
92 })
93 }
94}
95
96impl From<Reserves> for pb::Reserves {
97 fn from(value: Reserves) -> Self {
98 Self {
99 r1: Some(value.r1.into()),
100 r2: Some(value.r2.into()),
101 }
102 }
103}