1use crate::prelude::*;
2use nostr_sdk::{PublicKey, Timestamp};
3use secrecy::SecretString;
4use serde::{Deserialize, Serialize};
5#[cfg(feature = "sqlx")]
6use sqlx::FromRow;
7#[cfg(feature = "sqlx")]
8use sqlx_crud::SqlxCrud;
9use std::{fmt::Display, str::FromStr};
10use uuid::Uuid;
11use wasm_bindgen::prelude::*;
12
13#[wasm_bindgen]
15#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
16#[serde(rename_all = "kebab-case")]
17pub enum Kind {
18 Buy,
19 Sell,
20}
21
22impl FromStr for Kind {
23 type Err = ();
24
25 fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
26 match kind.to_lowercase().as_str() {
27 "buy" => std::result::Result::Ok(Self::Buy),
28 "sell" => std::result::Result::Ok(Self::Sell),
29 _ => Err(()),
30 }
31 }
32}
33
34impl Display for Kind {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 Kind::Sell => write!(f, "sell"),
38 Kind::Buy => write!(f, "buy"),
39 }
40 }
41}
42
43#[wasm_bindgen]
45#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
46#[serde(rename_all = "kebab-case")]
47pub enum Status {
48 Active,
49 Canceled,
50 CanceledByAdmin,
51 SettledByAdmin,
52 CompletedByAdmin,
53 Dispute,
54 Expired,
55 FiatSent,
56 SettledHoldInvoice,
57 Pending,
58 Success,
59 WaitingBuyerInvoice,
60 WaitingPayment,
61 CooperativelyCanceled,
62 InProgress,
63}
64
65impl Display for Status {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 Status::Active => write!(f, "active"),
69 Status::Canceled => write!(f, "canceled"),
70 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
71 Status::SettledByAdmin => write!(f, "settled-by-admin"),
72 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
73 Status::Dispute => write!(f, "dispute"),
74 Status::Expired => write!(f, "expired"),
75 Status::FiatSent => write!(f, "fiat-sent"),
76 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
77 Status::Pending => write!(f, "pending"),
78 Status::Success => write!(f, "success"),
79 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
80 Status::WaitingPayment => write!(f, "waiting-payment"),
81 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
82 Status::InProgress => write!(f, "in-progress"),
83 }
84 }
85}
86
87impl FromStr for Status {
88 type Err = ();
89 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
91 match s.to_lowercase().as_str() {
92 "active" => std::result::Result::Ok(Self::Active),
93 "canceled" => std::result::Result::Ok(Self::Canceled),
94 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
95 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
96 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
97 "dispute" => std::result::Result::Ok(Self::Dispute),
98 "expired" => std::result::Result::Ok(Self::Expired),
99 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
100 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
101 "pending" => std::result::Result::Ok(Self::Pending),
102 "success" => std::result::Result::Ok(Self::Success),
103 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
104 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
105 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
106 "in-progress" => std::result::Result::Ok(Self::InProgress),
107 _ => Err(()),
108 }
109 }
110}
111#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
113#[derive(Debug, Default, Deserialize, Serialize, Clone)]
114pub struct Order {
115 pub id: Uuid,
116 pub kind: String,
117 pub event_id: String,
118 pub hash: Option<String>,
119 pub preimage: Option<String>,
120 pub creator_pubkey: String,
121 pub cancel_initiator_pubkey: Option<String>,
122 pub buyer_pubkey: Option<String>,
123 pub master_buyer_pubkey: Option<String>,
124 pub seller_pubkey: Option<String>,
125 pub master_seller_pubkey: Option<String>,
126 pub status: String,
127 pub price_from_api: bool,
128 pub premium: i64,
129 pub payment_method: String,
130 pub amount: i64,
131 pub min_amount: Option<i64>,
132 pub max_amount: Option<i64>,
133 pub buyer_dispute: bool,
134 pub seller_dispute: bool,
135 pub buyer_cooperativecancel: bool,
136 pub seller_cooperativecancel: bool,
137 pub fee: i64,
138 pub routing_fee: i64,
139 pub fiat_code: String,
140 pub fiat_amount: i64,
141 pub buyer_invoice: Option<String>,
142 pub range_parent_id: Option<Uuid>,
143 pub invoice_held_at: i64,
144 pub taken_at: i64,
145 pub created_at: i64,
146 pub buyer_sent_rate: bool,
147 pub seller_sent_rate: bool,
148 pub failed_payment: bool,
149 pub payment_attempts: i64,
150 pub expires_at: i64,
151 pub trade_index_seller: Option<i64>,
152 pub trade_index_buyer: Option<i64>,
153 pub next_trade_pubkey: Option<String>,
154 pub next_trade_index: Option<i64>,
155}
156
157impl From<SmallOrder> for Order {
158 fn from(small_order: SmallOrder) -> Self {
159 Self {
160 id: Uuid::new_v4(),
161 kind: small_order
163 .kind
164 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
165 status: small_order
166 .status
167 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
168 amount: small_order.amount,
169 fiat_code: small_order.fiat_code,
170 min_amount: small_order.min_amount,
171 max_amount: small_order.max_amount,
172 fiat_amount: small_order.fiat_amount,
173 payment_method: small_order.payment_method,
174 premium: small_order.premium,
175 event_id: String::new(),
176 creator_pubkey: String::new(),
177 price_from_api: false,
178 fee: 0,
179 routing_fee: 0,
180 invoice_held_at: 0,
181 taken_at: 0,
182 created_at: small_order.created_at.unwrap_or(0),
183 expires_at: small_order.expires_at.unwrap_or(0),
184 payment_attempts: 0,
185 ..Default::default()
186 }
187 }
188}
189
190impl Order {
191 pub fn as_new_order(&self) -> SmallOrder {
193 SmallOrder::new(
194 Some(self.id),
195 Some(Kind::from_str(&self.kind).unwrap()),
196 Some(Status::from_str(&self.status).unwrap()),
197 self.amount,
198 self.fiat_code.clone(),
199 self.min_amount,
200 self.max_amount,
201 self.fiat_amount,
202 self.payment_method.clone(),
203 self.premium,
204 None,
205 None,
206 self.buyer_invoice.clone(),
207 Some(self.created_at),
208 Some(self.expires_at),
209 None,
210 None,
211 )
212 }
213 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
215 if let Ok(kind) = Kind::from_str(&self.kind) {
216 Ok(kind)
217 } else {
218 Err(ServiceError::InvalidOrderKind)
219 }
220 }
221
222 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
224 if let Ok(status) = Status::from_str(&self.status) {
225 Ok(status)
226 } else {
227 Err(ServiceError::InvalidOrderStatus)
228 }
229 }
230
231 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
233 match Status::from_str(&self.status) {
234 Ok(s) => match s == status {
235 true => Ok(()),
236 false => Err(CantDoReason::InvalidOrderStatus),
237 },
238 Err(_) => Err(CantDoReason::InvalidOrderStatus),
239 }
240 }
241
242 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
244 if self.kind != Kind::Buy.to_string() {
245 return Err(CantDoReason::InvalidOrderKind);
246 }
247 Ok(())
248 }
249 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
251 if self.kind != Kind::Sell.to_string() {
252 return Err(CantDoReason::InvalidOrderKind);
253 }
254 Ok(())
255 }
256
257 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
259 let sender = sender.to_string();
260 if self.creator_pubkey != sender {
261 return Err(CantDoReason::InvalidPubkey);
262 }
263 Ok(())
264 }
265
266 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
268 let sender = sender.to_string();
269 if self.creator_pubkey == sender {
270 return Err(CantDoReason::InvalidPubkey);
271 }
272 Ok(())
273 }
274
275 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
277 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
278 Ok(pk) => Ok(pk),
279 Err(_) => Err(ServiceError::InvalidPubkey),
280 }
281 }
282
283 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
285 if let Some(pk) = self.buyer_pubkey.as_ref() {
286 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
287 } else {
288 Err(ServiceError::InvalidPubkey)
289 }
290 }
291 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
293 if let Some(pk) = self.seller_pubkey.as_ref() {
294 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
295 } else {
296 Err(ServiceError::InvalidPubkey)
297 }
298 }
299 pub fn get_master_buyer_pubkey(
301 &self,
302 password: Option<&SecretString>,
303 ) -> Result<String, ServiceError> {
304 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
305 CryptoUtils::decrypt_data(pk.clone(), password).map_err(|_| ServiceError::InvalidPubkey)
306 } else {
307 Err(ServiceError::InvalidPubkey)
308 }
309 }
310 pub fn get_master_seller_pubkey(
312 &self,
313 password: Option<&SecretString>,
314 ) -> Result<String, ServiceError> {
315 if let Some(pk) = self.master_seller_pubkey.as_ref() {
316 CryptoUtils::decrypt_data(pk.clone(), password).map_err(|_| ServiceError::InvalidPubkey)
317 } else {
318 Err(ServiceError::InvalidPubkey)
319 }
320 }
321
322 pub fn is_range_order(&self) -> bool {
324 self.min_amount.is_some() && self.max_amount.is_some()
325 }
326
327 pub fn count_failed_payment(&mut self, retries_number: i64) {
328 if !self.failed_payment {
329 self.failed_payment = true;
330 self.payment_attempts = 1;
331 } else if self.payment_attempts < retries_number {
332 self.payment_attempts += 1;
333 }
334 }
335
336 pub fn has_no_amount(&self) -> bool {
338 self.amount == 0
339 }
340
341 pub fn set_timestamp_now(&mut self) {
343 self.taken_at = Timestamp::now().as_u64() as i64
344 }
345
346 pub fn is_full_privacy_order(
350 &self,
351 password: Option<&SecretString>,
352 ) -> Result<(Option<String>, Option<String>), ServiceError> {
353 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
354
355 let master_buyer_pubkey = self.get_master_buyer_pubkey(password).ok();
357 let master_seller_pubkey = self.get_master_seller_pubkey(password).ok();
358
359 if self.buyer_pubkey.as_ref() != master_buyer_pubkey.as_ref() {
361 normal_buyer_idkey = master_buyer_pubkey;
362 }
363
364 if self.seller_pubkey.as_ref() != master_seller_pubkey.as_ref() {
366 normal_seller_idkey = master_seller_pubkey;
367 }
368
369 Ok((normal_buyer_idkey, normal_seller_idkey))
370 }
371 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
376 let is_seller_dispute = !is_buyer_dispute;
378
379 let mut update_seller_dispute = false;
381 let mut update_buyer_dispute = false;
382
383 if is_seller_dispute && !self.seller_dispute {
384 update_seller_dispute = true;
385 self.seller_dispute = update_seller_dispute;
386 } else if is_buyer_dispute && !self.buyer_dispute {
387 update_buyer_dispute = true;
388 self.buyer_dispute = update_buyer_dispute;
389 };
390 self.status = Status::Dispute.to_string();
392
393 if !update_buyer_dispute && !update_seller_dispute {
396 return Err(CantDoReason::DisputeCreationError);
397 }
398
399 Ok(())
400 }
401}
402
403#[derive(Debug, Default, Deserialize, Serialize, Clone)]
405pub struct SmallOrder {
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub id: Option<Uuid>,
408 pub kind: Option<Kind>,
409 pub status: Option<Status>,
410 pub amount: i64,
411 pub fiat_code: String,
412 pub min_amount: Option<i64>,
413 pub max_amount: Option<i64>,
414 pub fiat_amount: i64,
415 pub payment_method: String,
416 pub premium: i64,
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub buyer_trade_pubkey: Option<String>,
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub seller_trade_pubkey: Option<String>,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub buyer_invoice: Option<String>,
423 pub created_at: Option<i64>,
424 pub expires_at: Option<i64>,
425 pub buyer_token: Option<u16>,
426 pub seller_token: Option<u16>,
427}
428
429#[allow(dead_code)]
430impl SmallOrder {
431 #[allow(clippy::too_many_arguments)]
432 pub fn new(
433 id: Option<Uuid>,
434 kind: Option<Kind>,
435 status: Option<Status>,
436 amount: i64,
437 fiat_code: String,
438 min_amount: Option<i64>,
439 max_amount: Option<i64>,
440 fiat_amount: i64,
441 payment_method: String,
442 premium: i64,
443 buyer_trade_pubkey: Option<String>,
444 seller_trade_pubkey: Option<String>,
445 buyer_invoice: Option<String>,
446 created_at: Option<i64>,
447 expires_at: Option<i64>,
448 buyer_token: Option<u16>,
449 seller_token: Option<u16>,
450 ) -> Self {
451 Self {
452 id,
453 kind,
454 status,
455 amount,
456 fiat_code,
457 min_amount,
458 max_amount,
459 fiat_amount,
460 payment_method,
461 premium,
462 buyer_trade_pubkey,
463 seller_trade_pubkey,
464 buyer_invoice,
465 created_at,
466 expires_at,
467 buyer_token,
468 seller_token,
469 }
470 }
471 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
473 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
474 }
475
476 pub fn as_json(&self) -> Result<String, ServiceError> {
478 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
479 }
480
481 pub fn sats_amount(&self) -> String {
483 if self.amount == 0 {
484 "Market price".to_string()
485 } else {
486 self.amount.to_string()
487 }
488 }
489 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
491 let premium = (self.premium != 0).then_some(self.premium);
492 let sats_amount = (self.amount != 0).then_some(self.amount);
493
494 if premium.is_some() && sats_amount.is_some() {
495 return Err(CantDoReason::InvalidParameters);
496 }
497 Ok(())
498 }
499 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
501 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
503 if min < 0 || max < 0 {
504 return Err(CantDoReason::InvalidAmount);
505 }
506 if min >= max {
507 return Err(CantDoReason::InvalidAmount);
508 }
509 if self.amount != 0 {
510 return Err(CantDoReason::InvalidAmount);
511 }
512 amounts.clear();
513 amounts.push(min);
514 amounts.push(max);
515 }
516 Ok(())
517 }
518}
519
520impl From<Order> for SmallOrder {
521 fn from(order: Order) -> Self {
522 let id = Some(order.id);
523 let kind = Kind::from_str(&order.kind).unwrap();
524 let status = Status::from_str(&order.status).unwrap();
525 let amount = order.amount;
526 let fiat_code = order.fiat_code.clone();
527 let min_amount = order.min_amount;
528 let max_amount = order.max_amount;
529 let fiat_amount = order.fiat_amount;
530 let payment_method = order.payment_method.clone();
531 let premium = order.premium;
532 let buyer_trade_pubkey = order.buyer_pubkey.clone();
533 let seller_trade_pubkey = order.seller_pubkey.clone();
534 let buyer_invoice = order.buyer_invoice.clone();
535
536 Self {
537 id,
538 kind: Some(kind),
539 status: Some(status),
540 amount,
541 fiat_code,
542 min_amount,
543 max_amount,
544 fiat_amount,
545 payment_method,
546 premium,
547 buyer_trade_pubkey,
548 seller_trade_pubkey,
549 buyer_invoice,
550 created_at: None,
551 expires_at: None,
552 buyer_token: None,
553 seller_token: None,
554 }
555 }
556}