1use std::str::FromStr;
2
3use uuid::Uuid;
4
5use super::{
6 BdkStorage, FailedSendAttemptRecord, FinalizedSendIntentRecord, BDK_NAMESPACE,
7 FINALIZED_INTENT_NAMESPACE, FINALIZED_SEND_INTENT_QUOTE_ID_NAMESPACE, SEND_INTENT_NAMESPACE,
8 SEND_INTENT_QUOTE_ID_NAMESPACE,
9};
10use crate::error::Error;
11use crate::send::batch_transaction::record::{SendBatchRecord, SendBatchState};
12use crate::send::payment_intent::record::{SendIntentRecord, SendIntentState};
13
14impl BdkStorage {
15 pub async fn create_send_intent_if_absent(
19 &self,
20 intent: &SendIntentRecord,
21 ) -> Result<(), Error> {
22 let mut tx = self
23 .kv_store
24 .begin_transaction()
25 .await
26 .map_err(Error::from)?;
27
28 let active = tx
29 .kv_read(
30 BDK_NAMESPACE,
31 SEND_INTENT_QUOTE_ID_NAMESPACE,
32 &intent.quote_id,
33 )
34 .await
35 .map_err(Error::from)?;
36
37 if active.is_some() {
38 tx.rollback().await.map_err(Error::from)?;
39 return Err(Error::DuplicateQuoteId(intent.quote_id.clone()));
40 }
41
42 let finalized = tx
43 .kv_read(
44 BDK_NAMESPACE,
45 FINALIZED_SEND_INTENT_QUOTE_ID_NAMESPACE,
46 &intent.quote_id,
47 )
48 .await
49 .map_err(Error::from)?;
50
51 if finalized.is_some() {
52 tx.rollback().await.map_err(Error::from)?;
53 return Err(Error::DuplicateQuoteId(intent.quote_id.clone()));
54 }
55
56 let serialized = serde_json::to_vec(intent)?;
57 tx.kv_write(
58 BDK_NAMESPACE,
59 SEND_INTENT_NAMESPACE,
60 &intent.intent_id.to_string(),
61 &serialized,
62 )
63 .await
64 .map_err(Error::from)?;
65 tx.kv_write(
66 BDK_NAMESPACE,
67 SEND_INTENT_QUOTE_ID_NAMESPACE,
68 &intent.quote_id,
69 intent.intent_id.to_string().as_bytes(),
70 )
71 .await
72 .map_err(Error::from)?;
73 tx.commit().await.map_err(Error::from)?;
74 Ok(())
75 }
76
77 pub async fn create_or_retry_failed_send_intent(
80 &self,
81 intent: &SendIntentRecord,
82 ) -> Result<SendIntentRecord, Error> {
83 let mut tx = self
84 .kv_store
85 .begin_transaction()
86 .await
87 .map_err(Error::from)?;
88
89 let finalized = tx
90 .kv_read(
91 BDK_NAMESPACE,
92 FINALIZED_SEND_INTENT_QUOTE_ID_NAMESPACE,
93 &intent.quote_id,
94 )
95 .await
96 .map_err(Error::from)?;
97
98 if finalized.is_some() {
99 tx.rollback().await.map_err(Error::from)?;
100 return Err(Error::DuplicateQuoteId(intent.quote_id.clone()));
101 }
102
103 let active = tx
104 .kv_read(
105 BDK_NAMESPACE,
106 SEND_INTENT_QUOTE_ID_NAMESPACE,
107 &intent.quote_id,
108 )
109 .await
110 .map_err(Error::from)?;
111
112 let record = if let Some(intent_id_bytes) = active {
113 let intent_id_str = std::str::from_utf8(&intent_id_bytes)
114 .map_err(|e| Error::Wallet(format!("Invalid quote-id index entry: {}", e)))?;
115 let intent_id = Uuid::from_str(intent_id_str)
116 .map_err(|e| Error::Wallet(format!("Invalid indexed intent id: {}", e)))?;
117 let intent_bytes = tx
118 .kv_read(BDK_NAMESPACE, SEND_INTENT_NAMESPACE, &intent_id.to_string())
119 .await
120 .map_err(Error::from)?
121 .ok_or(Error::SendIntentNotFound(intent_id))?;
122 let existing: SendIntentRecord = serde_json::from_slice(&intent_bytes)?;
123
124 if !matches!(existing.state, SendIntentState::Failed { .. }) {
125 tx.rollback().await.map_err(Error::from)?;
126 return Err(Error::DuplicateQuoteId(intent.quote_id.clone()));
127 }
128
129 SendIntentRecord {
130 intent_id,
131 quote_id: intent.quote_id.clone(),
132 address: intent.address.clone(),
133 amount_sat: intent.amount_sat,
134 max_fee_amount_sat: intent.max_fee_amount_sat,
135 tier: intent.tier,
136 metadata: intent.metadata.clone(),
137 state: intent.state.clone(),
138 }
139 } else {
140 tx.kv_write(
141 BDK_NAMESPACE,
142 SEND_INTENT_QUOTE_ID_NAMESPACE,
143 &intent.quote_id,
144 intent.intent_id.to_string().as_bytes(),
145 )
146 .await
147 .map_err(Error::from)?;
148 intent.clone()
149 };
150
151 let serialized = serde_json::to_vec(&record)?;
152 tx.kv_write(
153 BDK_NAMESPACE,
154 SEND_INTENT_NAMESPACE,
155 &record.intent_id.to_string(),
156 &serialized,
157 )
158 .await
159 .map_err(Error::from)?;
160 tx.commit().await.map_err(Error::from)?;
161 Ok(record)
162 }
163
164 pub async fn get_send_intent(
166 &self,
167 intent_id: &Uuid,
168 ) -> Result<Option<SendIntentRecord>, Error> {
169 self.get_record::<SendIntentRecord>(&intent_id.to_string())
170 .await
171 }
172
173 pub async fn update_send_intent(
175 &self,
176 intent_id: &Uuid,
177 new_state: &SendIntentState,
178 ) -> Result<(), Error> {
179 let key = intent_id.to_string();
180 if self.get_send_intent(intent_id).await?.is_none() {
181 return Err(Error::SendIntentNotFound(*intent_id));
182 }
183
184 self.update_record_state::<SendIntentRecord, SendIntentState>(&key, new_state)
185 .await
186 }
187
188 pub async fn delete_send_intent(&self, intent_id: &Uuid) -> Result<(), Error> {
190 let Some(intent) = self.get_send_intent(intent_id).await? else {
191 return Ok(());
192 };
193
194 let mut tx = self
195 .kv_store
196 .begin_transaction()
197 .await
198 .map_err(Error::from)?;
199 tx.kv_remove(BDK_NAMESPACE, SEND_INTENT_NAMESPACE, &intent_id.to_string())
200 .await
201 .map_err(Error::from)?;
202 tx.kv_remove(
203 BDK_NAMESPACE,
204 SEND_INTENT_QUOTE_ID_NAMESPACE,
205 &intent.quote_id,
206 )
207 .await
208 .map_err(Error::from)?;
209 tx.commit().await.map_err(Error::from)?;
210 Ok(())
211 }
212
213 pub async fn get_all_send_intents(&self) -> Result<Vec<SendIntentRecord>, Error> {
215 self.list_records::<SendIntentRecord>().await
216 }
217
218 pub async fn get_pending_send_intents(&self) -> Result<Vec<SendIntentRecord>, Error> {
220 let all = self.get_all_send_intents().await?;
221 Ok(all
222 .into_iter()
223 .filter(|i| matches!(i.state, SendIntentState::Pending { .. }))
224 .collect())
225 }
226
227 pub async fn add_failed_send_attempt(
229 &self,
230 record: &FailedSendAttemptRecord,
231 ) -> Result<(), Error> {
232 self.put_record(record).await
233 }
234
235 pub async fn get_failed_send_attempts_by_quote_id(
237 &self,
238 quote_id: &str,
239 ) -> Result<Vec<FailedSendAttemptRecord>, Error> {
240 let all = self.list_records::<FailedSendAttemptRecord>().await?;
241 Ok(all
242 .into_iter()
243 .filter(|record| record.quote_id == quote_id)
244 .collect())
245 }
246
247 pub async fn store_send_batch(&self, batch: &SendBatchRecord) -> Result<(), Error> {
251 self.put_record(batch).await
252 }
253
254 pub async fn get_send_batch(&self, batch_id: &Uuid) -> Result<Option<SendBatchRecord>, Error> {
256 self.get_record::<SendBatchRecord>(&batch_id.to_string())
257 .await
258 }
259
260 pub async fn update_send_batch(
262 &self,
263 batch_id: &Uuid,
264 new_state: &SendBatchState,
265 ) -> Result<(), Error> {
266 let key = batch_id.to_string();
267 if self.get_send_batch(batch_id).await?.is_none() {
268 return Err(Error::SendBatchNotFound(*batch_id));
269 }
270
271 self.update_record_state::<SendBatchRecord, SendBatchState>(&key, new_state)
272 .await
273 }
274
275 pub async fn delete_send_batch(&self, batch_id: &Uuid) -> Result<(), Error> {
277 self.delete_record::<SendBatchRecord>(&batch_id.to_string())
278 .await
279 }
280
281 pub async fn get_all_send_batches(&self) -> Result<Vec<SendBatchRecord>, Error> {
283 self.list_records::<SendBatchRecord>().await
284 }
285
286 pub async fn get_finalized_intent(
290 &self,
291 intent_id: &Uuid,
292 ) -> Result<Option<FinalizedSendIntentRecord>, Error> {
293 self.get_record::<FinalizedSendIntentRecord>(&intent_id.to_string())
294 .await
295 }
296
297 pub async fn get_finalized_intent_by_quote_id(
299 &self,
300 quote_id: &str,
301 ) -> Result<Option<FinalizedSendIntentRecord>, Error> {
302 let Some(intent_id_bytes) = self
303 .kv_store
304 .kv_read(
305 BDK_NAMESPACE,
306 FINALIZED_SEND_INTENT_QUOTE_ID_NAMESPACE,
307 quote_id,
308 )
309 .await
310 .map_err(Error::from)?
311 else {
312 return Ok(None);
313 };
314
315 let intent_id_str = std::str::from_utf8(&intent_id_bytes)
316 .map_err(|e| Error::Wallet(format!("Invalid intent-id index entry: {}", e)))?;
317 let intent_id = Uuid::from_str(intent_id_str)
318 .map_err(|e| Error::Wallet(format!("Invalid indexed intent id: {}", e)))?;
319
320 self.get_record::<FinalizedSendIntentRecord>(&intent_id.to_string())
321 .await
322 }
323
324 pub async fn get_send_intent_by_quote_id(
328 &self,
329 quote_id: &str,
330 ) -> Result<Option<SendIntentRecord>, Error> {
331 let Some(intent_id_bytes) = self
332 .kv_store
333 .kv_read(BDK_NAMESPACE, SEND_INTENT_QUOTE_ID_NAMESPACE, quote_id)
334 .await
335 .map_err(Error::from)?
336 else {
337 return Ok(None);
338 };
339
340 let intent_id = std::str::from_utf8(&intent_id_bytes)
341 .map_err(|e| Error::Wallet(format!("Invalid quote-id index entry: {}", e)))?;
342 let intent_id = Uuid::from_str(intent_id)
343 .map_err(|e| Error::Wallet(format!("Invalid indexed intent id: {}", e)))?;
344
345 self.get_send_intent(&intent_id).await
346 }
347
348 pub async fn finalize_send_intent(
350 &self,
351 intent_id: &Uuid,
352 record: &FinalizedSendIntentRecord,
353 ) -> Result<(), Error> {
354 let Some(intent) = self.get_send_intent(intent_id).await? else {
355 return Err(Error::SendIntentNotFound(*intent_id));
356 };
357
358 let serialized = serde_json::to_vec(record)?;
359 let mut tx = self
360 .kv_store
361 .begin_transaction()
362 .await
363 .map_err(Error::from)?;
364 tx.kv_write(
365 BDK_NAMESPACE,
366 FINALIZED_INTENT_NAMESPACE,
367 &record.intent_id.to_string(),
368 &serialized,
369 )
370 .await
371 .map_err(Error::from)?;
372 tx.kv_write(
373 BDK_NAMESPACE,
374 FINALIZED_SEND_INTENT_QUOTE_ID_NAMESPACE,
375 &intent.quote_id,
376 record.intent_id.to_string().as_bytes(),
377 )
378 .await
379 .map_err(Error::from)?;
380 tx.kv_remove(BDK_NAMESPACE, SEND_INTENT_NAMESPACE, &intent_id.to_string())
381 .await
382 .map_err(Error::from)?;
383 tx.kv_remove(
384 BDK_NAMESPACE,
385 SEND_INTENT_QUOTE_ID_NAMESPACE,
386 &intent.quote_id,
387 )
388 .await
389 .map_err(Error::from)?;
390 tx.commit().await.map_err(Error::from)?;
391 Ok(())
392 }
393}