1use serde::{Deserialize, Serialize};
4
5use crate::error::Error;
6use crate::mint_url::MintUrl;
7use crate::nuts::nut00::ProofsMethods;
8use crate::nuts::{
9 CurrencyUnit, MeltQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SpendingConditions,
10 State,
11};
12use crate::Amount;
13
14#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
16pub struct Melted {
17 pub state: MeltQuoteState,
19 pub preimage: Option<String>,
21 pub change: Option<Proofs>,
23 pub amount: Amount,
25 pub fee_paid: Amount,
27}
28
29impl Melted {
30 pub fn from_proofs(
32 state: MeltQuoteState,
33 preimage: Option<String>,
34 amount: Amount,
35 proofs: Proofs,
36 change_proofs: Option<Proofs>,
37 ) -> Result<Self, Error> {
38 let proofs_amount = proofs.total_amount()?;
39 let change_amount = match &change_proofs {
40 Some(change_proofs) => change_proofs.total_amount()?,
41 None => Amount::ZERO,
42 };
43
44 let fee_paid = proofs_amount
45 .checked_sub(amount + change_amount)
46 .ok_or(Error::AmountOverflow)?;
47
48 Ok(Self {
49 state,
50 preimage,
51 change: change_proofs,
52 amount,
53 fee_paid,
54 })
55 }
56
57 pub fn total_amount(&self) -> Amount {
59 self.amount + self.fee_paid
60 }
61}
62
63#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
65pub struct ProofInfo {
66 pub proof: Proof,
68 pub y: PublicKey,
70 pub mint_url: MintUrl,
72 pub state: State,
74 pub spending_condition: Option<SpendingConditions>,
76 pub unit: CurrencyUnit,
78}
79
80impl ProofInfo {
81 pub fn new(
83 proof: Proof,
84 mint_url: MintUrl,
85 state: State,
86 unit: CurrencyUnit,
87 ) -> Result<Self, Error> {
88 let y = proof.y()?;
89
90 let spending_condition: Option<SpendingConditions> = (&proof.secret).try_into().ok();
91
92 Ok(Self {
93 proof,
94 y,
95 mint_url,
96 state,
97 spending_condition,
98 unit,
99 })
100 }
101
102 pub fn matches_conditions(
104 &self,
105 mint_url: &Option<MintUrl>,
106 unit: &Option<CurrencyUnit>,
107 state: &Option<Vec<State>>,
108 spending_conditions: &Option<Vec<SpendingConditions>>,
109 ) -> bool {
110 if let Some(mint_url) = mint_url {
111 if mint_url.ne(&self.mint_url) {
112 return false;
113 }
114 }
115
116 if let Some(unit) = unit {
117 if unit.ne(&self.unit) {
118 return false;
119 }
120 }
121
122 if let Some(state) = state {
123 if !state.contains(&self.state) {
124 return false;
125 }
126 }
127
128 if let Some(spending_conditions) = spending_conditions {
129 match &self.spending_condition {
130 None => return false,
131 Some(s) => {
132 if !spending_conditions.contains(s) {
133 return false;
134 }
135 }
136 }
137 }
138
139 true
140 }
141}
142
143#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
146pub struct LnKey {
147 pub unit: CurrencyUnit,
149 pub method: PaymentMethod,
151}
152
153impl LnKey {
154 pub fn new(unit: CurrencyUnit, method: PaymentMethod) -> Self {
156 Self { unit, method }
157 }
158}
159
160#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
162pub struct QuoteTTL {
163 pub mint_ttl: u64,
165 pub melt_ttl: u64,
167}
168
169impl QuoteTTL {
170 pub fn new(mint_ttl: u64, melt_ttl: u64) -> QuoteTTL {
172 Self { mint_ttl, melt_ttl }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use std::str::FromStr;
179
180 use super::Melted;
181 use crate::nuts::{Id, Proof, PublicKey};
182 use crate::secret::Secret;
183 use crate::Amount;
184
185 #[test]
186 fn test_melted() {
187 let keyset_id = Id::from_str("00deadbeef123456").unwrap();
188 let proof = Proof::new(
189 Amount::from(64),
190 keyset_id,
191 Secret::generate(),
192 PublicKey::from_hex(
193 "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
194 )
195 .unwrap(),
196 );
197 let melted = Melted::from_proofs(
198 super::MeltQuoteState::Paid,
199 Some("preimage".to_string()),
200 Amount::from(64),
201 vec![proof.clone()],
202 None,
203 )
204 .unwrap();
205 assert_eq!(melted.amount, Amount::from(64));
206 assert_eq!(melted.fee_paid, Amount::ZERO);
207 assert_eq!(melted.total_amount(), Amount::from(64));
208 }
209
210 #[test]
211 fn test_melted_with_change() {
212 let keyset_id = Id::from_str("00deadbeef123456").unwrap();
213 let proof = Proof::new(
214 Amount::from(64),
215 keyset_id,
216 Secret::generate(),
217 PublicKey::from_hex(
218 "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
219 )
220 .unwrap(),
221 );
222 let change_proof = Proof::new(
223 Amount::from(32),
224 keyset_id,
225 Secret::generate(),
226 PublicKey::from_hex(
227 "03deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
228 )
229 .unwrap(),
230 );
231 let melted = Melted::from_proofs(
232 super::MeltQuoteState::Paid,
233 Some("preimage".to_string()),
234 Amount::from(31),
235 vec![proof.clone()],
236 Some(vec![change_proof.clone()]),
237 )
238 .unwrap();
239 assert_eq!(melted.amount, Amount::from(31));
240 assert_eq!(melted.fee_paid, Amount::from(1));
241 assert_eq!(melted.total_amount(), Amount::from(32));
242 }
243}