1use nostr_sdk::{PublicKey, Timestamp};
2use serde::{Deserialize, Serialize};
3#[cfg(feature = "sqlx")]
4use sqlx::FromRow;
5#[cfg(feature = "sqlx")]
6use sqlx_crud::SqlxCrud;
7use std::{fmt::Display, str::FromStr};
8use uuid::Uuid;
9use wasm_bindgen::prelude::*;
10
11use crate::error::{CantDoReason, ServiceError};
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
112#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
114#[derive(Debug, Default, Deserialize, Serialize, Clone)]
115pub struct Order {
116 pub id: Uuid,
117 pub kind: String,
118 pub event_id: String,
119 pub hash: Option<String>,
120 pub preimage: Option<String>,
121 pub creator_pubkey: String,
122 pub cancel_initiator_pubkey: Option<String>,
123 pub buyer_pubkey: Option<String>,
124 pub master_buyer_pubkey: Option<String>,
125 pub seller_pubkey: Option<String>,
126 pub master_seller_pubkey: Option<String>,
127 pub status: String,
128 pub price_from_api: bool,
129 pub premium: i64,
130 pub payment_method: String,
131 pub amount: i64,
132 pub min_amount: Option<i64>,
133 pub max_amount: Option<i64>,
134 pub buyer_dispute: bool,
135 pub seller_dispute: bool,
136 pub buyer_cooperativecancel: bool,
137 pub seller_cooperativecancel: bool,
138 pub fee: i64,
139 pub routing_fee: i64,
140 pub fiat_code: String,
141 pub fiat_amount: i64,
142 pub buyer_invoice: Option<String>,
143 pub range_parent_id: Option<Uuid>,
144 pub invoice_held_at: i64,
145 pub taken_at: i64,
146 pub created_at: i64,
147 pub buyer_sent_rate: bool,
148 pub seller_sent_rate: bool,
149 pub failed_payment: bool,
150 pub payment_attempts: i64,
151 pub expires_at: i64,
152 pub trade_index_seller: Option<i64>,
153 pub trade_index_buyer: Option<i64>,
154 pub next_trade_pubkey: Option<String>,
155 pub next_trade_index: Option<i64>,
156}
157
158impl From<SmallOrder> for Order {
159 fn from(small_order: SmallOrder) -> Self {
160 Self {
161 id: Uuid::new_v4(),
162 kind: small_order
164 .kind
165 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
166 status: small_order
167 .status
168 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
169 amount: small_order.amount,
170 fiat_code: small_order.fiat_code,
171 min_amount: small_order.min_amount,
172 max_amount: small_order.max_amount,
173 fiat_amount: small_order.fiat_amount,
174 payment_method: small_order.payment_method,
175 premium: small_order.premium,
176 event_id: String::new(),
177 creator_pubkey: String::new(),
178 price_from_api: false,
179 fee: 0,
180 routing_fee: 0,
181 invoice_held_at: 0,
182 taken_at: 0,
183 created_at: small_order.created_at.unwrap_or(0),
184 expires_at: small_order.expires_at.unwrap_or(0),
185 payment_attempts: 0,
186 ..Default::default()
187 }
188 }
189}
190
191impl Order {
192 pub fn as_new_order(&self) -> SmallOrder {
194 SmallOrder::new(
195 Some(self.id),
196 Some(Kind::from_str(&self.kind).unwrap()),
197 Some(Status::from_str(&self.status).unwrap()),
198 self.amount,
199 self.fiat_code.clone(),
200 self.min_amount,
201 self.max_amount,
202 self.fiat_amount,
203 self.payment_method.clone(),
204 self.premium,
205 None,
206 None,
207 self.buyer_invoice.clone(),
208 Some(self.created_at),
209 Some(self.expires_at),
210 None,
211 None,
212 )
213 }
214
215 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
217 if let Ok(kind) = Kind::from_str(&self.kind) {
218 Ok(kind)
219 } else {
220 Err(ServiceError::InvalidOrderKind)
221 }
222 }
223
224 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
226 if let Ok(status) = Status::from_str(&self.status) {
227 Ok(status)
228 } else {
229 Err(ServiceError::InvalidOrderStatus)
230 }
231 }
232
233 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
235 match Status::from_str(&self.status) {
236 Ok(s) => match s == status {
237 true => Ok(()),
238 false => Err(CantDoReason::InvalidOrderStatus),
239 },
240 Err(_) => Err(CantDoReason::InvalidOrderStatus),
241 }
242 }
243
244 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
246 if self.kind != Kind::Buy.to_string() {
247 return Err(CantDoReason::InvalidOrderKind);
248 }
249 Ok(())
250 }
251 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
253 if self.kind != Kind::Sell.to_string() {
254 return Err(CantDoReason::InvalidOrderKind);
255 }
256 Ok(())
257 }
258
259 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
261 let sender = sender.to_string();
262 if self.creator_pubkey != sender {
263 return Err(CantDoReason::InvalidPubkey);
264 }
265 Ok(())
266 }
267
268 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
270 let sender = sender.to_string();
271 if self.creator_pubkey == sender {
272 return Err(CantDoReason::InvalidPubkey);
273 }
274 Ok(())
275 }
276
277 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
279 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
280 Ok(pk) => Ok(pk),
281 Err(_) => Err(ServiceError::InvalidPubkey),
282 }
283 }
284
285 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
287 if let Some(pk) = self.buyer_pubkey.as_ref() {
288 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
289 } else {
290 Err(ServiceError::InvalidPubkey)
291 }
292 }
293 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
295 if let Some(pk) = self.seller_pubkey.as_ref() {
296 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
297 } else {
298 Err(ServiceError::InvalidPubkey)
299 }
300 }
301 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
303 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
304 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
305 } else {
306 Err(ServiceError::InvalidPubkey)
307 }
308 }
309 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
311 if let Some(pk) = self.master_seller_pubkey.as_ref() {
312 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
313 } else {
314 Err(ServiceError::InvalidPubkey)
315 }
316 }
317
318 pub fn is_range_order(&self) -> bool {
320 self.min_amount.is_some() && self.max_amount.is_some()
321 }
322
323 pub fn count_failed_payment(&mut self, retries_number: i64) {
324 if !self.failed_payment {
325 self.failed_payment = true;
326 self.payment_attempts = 0;
327 } else if self.payment_attempts < retries_number {
328 self.payment_attempts += 1;
329 }
330 }
331
332 pub fn has_no_amount(&self) -> bool {
334 self.amount == 0
335 }
336
337 pub fn set_timestamp_now(&mut self) {
339 self.taken_at = Timestamp::now().as_u64() as i64
340 }
341
342 pub fn is_full_privacy_order(&self) -> (bool, bool) {
344 let (mut full_privacy_buyer, mut full_privacy_seller) = (false, false);
345
346 if self.buyer_pubkey.is_some()
348 && self.master_buyer_pubkey.is_some()
349 && self.master_buyer_pubkey == self.buyer_pubkey
350 {
351 full_privacy_buyer = true;
352 }
353
354 if self.seller_pubkey.is_some()
355 && self.master_seller_pubkey.is_some()
356 && self.master_seller_pubkey == self.seller_pubkey
357 {
358 full_privacy_seller = true;
359 }
360
361 (full_privacy_buyer, full_privacy_seller)
362 }
363 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
368 let is_seller_dispute = !is_buyer_dispute;
370
371 let mut update_seller_dispute = false;
373 let mut update_buyer_dispute = false;
374
375 if is_seller_dispute && !self.seller_dispute {
376 update_seller_dispute = true;
377 self.seller_dispute = update_seller_dispute;
378 } else if is_buyer_dispute && !self.buyer_dispute {
379 update_buyer_dispute = true;
380 self.buyer_dispute = update_buyer_dispute;
381 };
382 self.status = Status::Dispute.to_string();
384
385 if !update_buyer_dispute && !update_seller_dispute {
388 return Err(CantDoReason::DisputeCreationError);
389 }
390
391 Ok(())
392 }
393}
394
395#[derive(Debug, Default, Deserialize, Serialize, Clone)]
397pub struct SmallOrder {
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub id: Option<Uuid>,
400 pub kind: Option<Kind>,
401 pub status: Option<Status>,
402 pub amount: i64,
403 pub fiat_code: String,
404 pub min_amount: Option<i64>,
405 pub max_amount: Option<i64>,
406 pub fiat_amount: i64,
407 pub payment_method: String,
408 pub premium: i64,
409 #[serde(skip_serializing_if = "Option::is_none")]
410 pub buyer_trade_pubkey: Option<String>,
411 #[serde(skip_serializing_if = "Option::is_none")]
412 pub seller_trade_pubkey: Option<String>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub buyer_invoice: Option<String>,
415 pub created_at: Option<i64>,
416 pub expires_at: Option<i64>,
417 pub buyer_token: Option<u16>,
418 pub seller_token: Option<u16>,
419}
420
421#[allow(dead_code)]
422impl SmallOrder {
423 #[allow(clippy::too_many_arguments)]
424 pub fn new(
425 id: Option<Uuid>,
426 kind: Option<Kind>,
427 status: Option<Status>,
428 amount: i64,
429 fiat_code: String,
430 min_amount: Option<i64>,
431 max_amount: Option<i64>,
432 fiat_amount: i64,
433 payment_method: String,
434 premium: i64,
435 buyer_trade_pubkey: Option<String>,
436 seller_trade_pubkey: Option<String>,
437 buyer_invoice: Option<String>,
438 created_at: Option<i64>,
439 expires_at: Option<i64>,
440 buyer_token: Option<u16>,
441 seller_token: Option<u16>,
442 ) -> Self {
443 Self {
444 id,
445 kind,
446 status,
447 amount,
448 fiat_code,
449 min_amount,
450 max_amount,
451 fiat_amount,
452 payment_method,
453 premium,
454 buyer_trade_pubkey,
455 seller_trade_pubkey,
456 buyer_invoice,
457 created_at,
458 expires_at,
459 buyer_token,
460 seller_token,
461 }
462 }
463 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
465 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
466 }
467
468 pub fn as_json(&self) -> Result<String, ServiceError> {
470 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
471 }
472
473 pub fn sats_amount(&self) -> String {
475 if self.amount == 0 {
476 "Market price".to_string()
477 } else {
478 self.amount.to_string()
479 }
480 }
481 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
483 let premium = (self.premium != 0).then_some(self.premium);
484 let sats_amount = (self.amount != 0).then_some(self.amount);
485
486 if premium.is_some() && sats_amount.is_some() {
487 return Err(CantDoReason::InvalidParameters);
488 }
489 Ok(())
490 }
491 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
493 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
495 if min < 0 || max < 0 {
496 return Err(CantDoReason::InvalidAmount);
497 }
498 if min >= max {
499 return Err(CantDoReason::InvalidAmount);
500 }
501 if self.amount != 0 {
502 return Err(CantDoReason::InvalidAmount);
503 }
504 amounts.clear();
505 amounts.push(min);
506 amounts.push(max);
507 }
508 Ok(())
509 }
510
511 pub fn fiat_amount(&self) -> String {
513 if self.max_amount.is_some() {
514 format!("{}-{}", self.min_amount.unwrap(), self.max_amount.unwrap())
515 } else {
516 self.fiat_amount.to_string()
517 }
518 }
519}
520
521impl From<Order> for SmallOrder {
522 fn from(order: Order) -> Self {
523 let id = Some(order.id);
524 let kind = Kind::from_str(&order.kind).unwrap();
525 let status = Status::from_str(&order.status).unwrap();
526 let amount = order.amount;
527 let fiat_code = order.fiat_code.clone();
528 let min_amount = order.min_amount;
529 let max_amount = order.max_amount;
530 let fiat_amount = order.fiat_amount;
531 let payment_method = order.payment_method.clone();
532 let premium = order.premium;
533 let buyer_trade_pubkey = order.buyer_pubkey.clone();
534 let seller_trade_pubkey = order.seller_pubkey.clone();
535 let buyer_invoice = order.buyer_invoice.clone();
536
537 Self {
538 id,
539 kind: Some(kind),
540 status: Some(status),
541 amount,
542 fiat_code,
543 min_amount,
544 max_amount,
545 fiat_amount,
546 payment_method,
547 premium,
548 buyer_trade_pubkey,
549 seller_trade_pubkey,
550 buyer_invoice,
551 created_at: None,
552 expires_at: None,
553 buyer_token: None,
554 seller_token: None,
555 }
556 }
557}