1use crate::prelude::*;
11use nostr_sdk::{PublicKey, Timestamp};
12use serde::{Deserialize, Serialize};
13#[cfg(feature = "sqlx")]
14use sqlx::FromRow;
15use std::{fmt::Display, str::FromStr};
16use uuid::Uuid;
17use wasm_bindgen::prelude::*;
18
19#[wasm_bindgen]
21#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
22#[serde(rename_all = "kebab-case")]
23pub enum Kind {
24 Buy,
26 Sell,
28}
29
30impl FromStr for Kind {
31 type Err = ();
32
33 fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
37 match kind.to_lowercase().as_str() {
38 "buy" => std::result::Result::Ok(Self::Buy),
39 "sell" => std::result::Result::Ok(Self::Sell),
40 _ => Err(()),
41 }
42 }
43}
44
45impl Display for Kind {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Kind::Sell => write!(f, "sell"),
49 Kind::Buy => write!(f, "buy"),
50 }
51 }
52}
53
54#[wasm_bindgen]
59#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
60#[serde(rename_all = "kebab-case")]
61pub enum Status {
62 Active,
64 Canceled,
66 CanceledByAdmin,
68 SettledByAdmin,
70 CompletedByAdmin,
72 Dispute,
74 Expired,
76 FiatSent,
78 SettledHoldInvoice,
80 Pending,
82 Success,
84 WaitingBuyerInvoice,
86 WaitingPayment,
88 WaitingTakerBond,
93 CooperativelyCanceled,
95 InProgress,
97 WaitingMakerBond,
107}
108
109impl Display for Status {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 Status::Active => write!(f, "active"),
113 Status::Canceled => write!(f, "canceled"),
114 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
115 Status::SettledByAdmin => write!(f, "settled-by-admin"),
116 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
117 Status::Dispute => write!(f, "dispute"),
118 Status::Expired => write!(f, "expired"),
119 Status::FiatSent => write!(f, "fiat-sent"),
120 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
121 Status::Pending => write!(f, "pending"),
122 Status::Success => write!(f, "success"),
123 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
124 Status::WaitingPayment => write!(f, "waiting-payment"),
125 Status::WaitingTakerBond => write!(f, "waiting-taker-bond"),
126 Status::WaitingMakerBond => write!(f, "waiting-maker-bond"),
127 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
128 Status::InProgress => write!(f, "in-progress"),
129 }
130 }
131}
132
133impl FromStr for Status {
134 type Err = ();
135 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
137 match s.to_lowercase().as_str() {
138 "active" => std::result::Result::Ok(Self::Active),
139 "canceled" => std::result::Result::Ok(Self::Canceled),
140 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
141 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
142 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
143 "dispute" => std::result::Result::Ok(Self::Dispute),
144 "expired" => std::result::Result::Ok(Self::Expired),
145 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
146 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
147 "pending" => std::result::Result::Ok(Self::Pending),
148 "success" => std::result::Result::Ok(Self::Success),
149 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
150 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
151 "waiting-taker-bond" => std::result::Result::Ok(Self::WaitingTakerBond),
152 "waiting-maker-bond" => std::result::Result::Ok(Self::WaitingMakerBond),
153 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
154 "in-progress" => std::result::Result::Ok(Self::InProgress),
155 _ => Err(()),
156 }
157 }
158}
159#[cfg_attr(feature = "sqlx", derive(FromRow))]
168#[derive(Debug, Default, Deserialize, Serialize, Clone)]
169pub struct Order {
170 pub id: Uuid,
172 pub kind: String,
175 pub event_id: String,
177 pub hash: Option<String>,
179 pub preimage: Option<String>,
181 pub creator_pubkey: String,
183 pub cancel_initiator_pubkey: Option<String>,
185 pub buyer_pubkey: Option<String>,
187 pub master_buyer_pubkey: Option<String>,
190 pub seller_pubkey: Option<String>,
192 pub master_seller_pubkey: Option<String>,
195 pub status: String,
197 pub price_from_api: bool,
199 pub premium: i64,
201 pub payment_method: String,
203 pub amount: i64,
206 pub min_amount: Option<i64>,
208 pub max_amount: Option<i64>,
210 pub buyer_dispute: bool,
212 pub seller_dispute: bool,
214 pub buyer_cooperativecancel: bool,
216 pub seller_cooperativecancel: bool,
218 pub fee: i64,
220 pub routing_fee: i64,
222 pub dev_fee: i64,
224 pub dev_fee_paid: bool,
226 pub dev_fee_payment_hash: Option<String>,
228 pub fiat_code: String,
230 pub fiat_amount: i64,
232 pub buyer_invoice: Option<String>,
234 pub range_parent_id: Option<Uuid>,
236 pub invoice_held_at: i64,
238 pub taken_at: i64,
240 pub created_at: i64,
242 pub buyer_sent_rate: bool,
244 pub seller_sent_rate: bool,
246 pub failed_payment: bool,
248 pub payment_attempts: i64,
250 pub expires_at: i64,
252 pub trade_index_seller: Option<i64>,
254 pub trade_index_buyer: Option<i64>,
256 pub next_trade_pubkey: Option<String>,
259 pub next_trade_index: Option<i64>,
261 pub cashu_mint_url: Option<String>,
264 pub cashu_escrow_token: Option<String>,
267 pub cashu_escrow_locked_at: Option<i64>,
270}
271
272impl From<SmallOrder> for Order {
273 fn from(small_order: SmallOrder) -> Self {
274 Self {
275 id: Uuid::new_v4(),
276 kind: small_order
278 .kind
279 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
280 status: small_order
281 .status
282 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
283 amount: small_order.amount,
284 fiat_code: small_order.fiat_code,
285 min_amount: small_order.min_amount,
286 max_amount: small_order.max_amount,
287 fiat_amount: small_order.fiat_amount,
288 payment_method: small_order.payment_method,
289 premium: small_order.premium,
290 event_id: String::new(),
291 creator_pubkey: String::new(),
292 price_from_api: false,
293 fee: 0,
294 routing_fee: 0,
295 dev_fee: 0,
296 dev_fee_paid: false,
297 dev_fee_payment_hash: None,
298 invoice_held_at: 0,
299 taken_at: 0,
300 created_at: small_order.created_at.unwrap_or(0),
301 expires_at: small_order.expires_at.unwrap_or(0),
302 payment_attempts: 0,
303 ..Default::default()
304 }
305 }
306}
307
308impl Order {
309 pub fn as_new_order(&self) -> SmallOrder {
315 SmallOrder::new(
316 Some(self.id),
317 Some(Kind::from_str(&self.kind).unwrap()),
318 Some(Status::from_str(&self.status).unwrap()),
319 self.amount,
320 self.fiat_code.clone(),
321 self.min_amount,
322 self.max_amount,
323 self.fiat_amount,
324 self.payment_method.clone(),
325 self.premium,
326 None,
327 None,
328 self.buyer_invoice.clone(),
329 Some(self.created_at),
330 Some(self.expires_at),
331 )
332 }
333 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
338 if let Ok(kind) = Kind::from_str(&self.kind) {
339 Ok(kind)
340 } else {
341 Err(ServiceError::InvalidOrderKind)
342 }
343 }
344
345 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
350 if let Ok(status) = Status::from_str(&self.status) {
351 Ok(status)
352 } else {
353 Err(ServiceError::InvalidOrderStatus)
354 }
355 }
356
357 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
362 match Status::from_str(&self.status) {
363 Ok(s) => match s == status {
364 true => Ok(()),
365 false => Err(CantDoReason::InvalidOrderStatus),
366 },
367 Err(_) => Err(CantDoReason::InvalidOrderStatus),
368 }
369 }
370
371 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
373 if self.kind != Kind::Buy.to_string() {
374 return Err(CantDoReason::InvalidOrderKind);
375 }
376 Ok(())
377 }
378 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
380 if self.kind != Kind::Sell.to_string() {
381 return Err(CantDoReason::InvalidOrderKind);
382 }
383 Ok(())
384 }
385
386 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
390 let sender = sender.to_string();
391 if self.creator_pubkey != sender {
392 return Err(CantDoReason::InvalidPubkey);
393 }
394 Ok(())
395 }
396
397 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
402 let sender = sender.to_string();
403 if self.creator_pubkey == sender {
404 return Err(CantDoReason::InvalidPubkey);
405 }
406 Ok(())
407 }
408
409 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
411 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
412 Ok(pk) => Ok(pk),
413 Err(_) => Err(ServiceError::InvalidPubkey),
414 }
415 }
416
417 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
422 if let Some(pk) = self.buyer_pubkey.as_ref() {
423 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
424 } else {
425 Err(ServiceError::InvalidPubkey)
426 }
427 }
428 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
433 if let Some(pk) = self.seller_pubkey.as_ref() {
434 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
435 } else {
436 Err(ServiceError::InvalidPubkey)
437 }
438 }
439 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
441 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
442 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
443 } else {
444 Err(ServiceError::InvalidPubkey)
445 }
446 }
447 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
449 if let Some(pk) = self.master_seller_pubkey.as_ref() {
450 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
451 } else {
452 Err(ServiceError::InvalidPubkey)
453 }
454 }
455
456 pub fn is_range_order(&self) -> bool {
459 self.min_amount.is_some() && self.max_amount.is_some()
460 }
461
462 pub fn count_failed_payment(&mut self, retries_number: i64) {
468 if !self.failed_payment {
469 self.failed_payment = true;
470 self.payment_attempts = 1;
471 } else if self.payment_attempts < retries_number {
472 self.payment_attempts += 1;
473 }
474 }
475
476 pub fn has_no_amount(&self) -> bool {
479 self.amount == 0
480 }
481
482 pub fn set_timestamp_now(&mut self) {
484 self.taken_at = Timestamp::now().as_secs() as i64
485 }
486
487 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
496 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
497
498 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
500 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
501
502 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
504 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
505 }
506
507 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
509 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
510 }
511
512 Ok((normal_buyer_idkey, normal_seller_idkey))
513 }
514 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
522 let is_seller_dispute = !is_buyer_dispute;
524
525 let mut update_seller_dispute = false;
527 let mut update_buyer_dispute = false;
528
529 if is_seller_dispute && !self.seller_dispute {
530 update_seller_dispute = true;
531 self.seller_dispute = update_seller_dispute;
532 } else if is_buyer_dispute && !self.buyer_dispute {
533 update_buyer_dispute = true;
534 self.buyer_dispute = update_buyer_dispute;
535 };
536 self.status = Status::Dispute.to_string();
538
539 if !update_buyer_dispute && !update_seller_dispute {
542 return Err(CantDoReason::DisputeCreationError);
543 }
544
545 Ok(())
546 }
547}
548
549#[derive(Debug, Default, Deserialize, Serialize, Clone)]
558#[serde(deny_unknown_fields)]
559pub struct SmallOrder {
560 #[serde(skip_serializing_if = "Option::is_none")]
562 pub id: Option<Uuid>,
563 pub kind: Option<Kind>,
565 pub status: Option<Status>,
567 pub amount: i64,
570 pub fiat_code: String,
572 pub min_amount: Option<i64>,
574 pub max_amount: Option<i64>,
576 pub fiat_amount: i64,
578 pub payment_method: String,
580 pub premium: i64,
582 #[serde(skip_serializing_if = "Option::is_none")]
584 pub buyer_trade_pubkey: Option<String>,
585 #[serde(skip_serializing_if = "Option::is_none")]
587 pub seller_trade_pubkey: Option<String>,
588 #[serde(skip_serializing_if = "Option::is_none")]
590 pub buyer_invoice: Option<String>,
591 pub created_at: Option<i64>,
593 pub expires_at: Option<i64>,
595}
596
597#[allow(dead_code)]
598impl SmallOrder {
599 #[allow(clippy::too_many_arguments)]
601 pub fn new(
602 id: Option<Uuid>,
603 kind: Option<Kind>,
604 status: Option<Status>,
605 amount: i64,
606 fiat_code: String,
607 min_amount: Option<i64>,
608 max_amount: Option<i64>,
609 fiat_amount: i64,
610 payment_method: String,
611 premium: i64,
612 buyer_trade_pubkey: Option<String>,
613 seller_trade_pubkey: Option<String>,
614 buyer_invoice: Option<String>,
615 created_at: Option<i64>,
616 expires_at: Option<i64>,
617 ) -> Self {
618 Self {
619 id,
620 kind,
621 status,
622 amount,
623 fiat_code,
624 min_amount,
625 max_amount,
626 fiat_amount,
627 payment_method,
628 premium,
629 buyer_trade_pubkey,
630 seller_trade_pubkey,
631 buyer_invoice,
632 created_at,
633 expires_at,
634 }
635 }
636 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
638 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
639 }
640
641 pub fn as_json(&self) -> Result<String, ServiceError> {
643 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
644 }
645
646 pub fn sats_amount(&self) -> String {
649 if self.amount == 0 {
650 "Market price".to_string()
651 } else {
652 self.amount.to_string()
653 }
654 }
655 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
659 if self.fiat_amount <= 0 {
660 return Err(CantDoReason::InvalidAmount);
661 }
662 Ok(())
663 }
664
665 pub fn check_amount(&self) -> Result<(), CantDoReason> {
672 if self.amount < 0 {
673 return Err(CantDoReason::InvalidAmount);
674 }
675 Ok(())
676 }
677
678 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
684 let premium = (self.premium != 0).then_some(self.premium);
685 let sats_amount = (self.amount != 0).then_some(self.amount);
686
687 if premium.is_some() && sats_amount.is_some() {
688 return Err(CantDoReason::InvalidParameters);
689 }
690 Ok(())
691 }
692
693 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
701 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
703 if min < 0 || max < 0 {
704 return Err(CantDoReason::InvalidAmount);
705 }
706 if min >= max {
707 return Err(CantDoReason::InvalidAmount);
708 }
709 if self.amount != 0 {
710 return Err(CantDoReason::InvalidAmount);
711 }
712 amounts.clear();
713 amounts.push(min);
714 amounts.push(max);
715 }
716 Ok(())
717 }
718
719 pub fn check_fiat_currency(
726 &self,
727 fiat_currencies_accepted: &[String],
728 ) -> Result<(), CantDoReason> {
729 if !fiat_currencies_accepted.contains(&self.fiat_code)
730 && !fiat_currencies_accepted.is_empty()
731 {
732 return Err(CantDoReason::InvalidFiatCurrency);
733 }
734 Ok(())
735 }
736}
737
738impl From<Order> for SmallOrder {
739 fn from(order: Order) -> Self {
740 let id = Some(order.id);
741 let kind = Kind::from_str(&order.kind).unwrap();
742 let status = Status::from_str(&order.status).unwrap();
743 let amount = order.amount;
744 let fiat_code = order.fiat_code.clone();
745 let min_amount = order.min_amount;
746 let max_amount = order.max_amount;
747 let fiat_amount = order.fiat_amount;
748 let payment_method = order.payment_method.clone();
749 let premium = order.premium;
750 let buyer_trade_pubkey = order.buyer_pubkey.clone();
751 let seller_trade_pubkey = order.seller_pubkey.clone();
752 let buyer_invoice = order.buyer_invoice.clone();
753
754 Self {
755 id,
756 kind: Some(kind),
757 status: Some(status),
758 amount,
759 fiat_code,
760 min_amount,
761 max_amount,
762 fiat_amount,
763 payment_method,
764 premium,
765 buyer_trade_pubkey,
766 seller_trade_pubkey,
767 buyer_invoice,
768 created_at: Some(order.created_at),
769 expires_at: Some(order.expires_at),
770 }
771 }
772}
773
774#[cfg(test)]
775mod tests {
776 use super::*;
777 use crate::error::CantDoReason;
778 use nostr_sdk::Keys;
779 use uuid::uuid;
780
781 #[test]
782 fn test_status_string() {
783 assert_eq!(Status::Active.to_string(), "active");
784 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
785 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
786 assert_ne!(Status::Pending.to_string(), "Pending");
787 }
788
789 #[test]
790 fn test_status_waiting_taker_bond_roundtrip() {
791 assert_eq!(Status::WaitingTakerBond.to_string(), "waiting-taker-bond");
792 assert_eq!(
793 Status::from_str("waiting-taker-bond").unwrap(),
794 Status::WaitingTakerBond
795 );
796 let json = serde_json::to_string(&Status::WaitingTakerBond).unwrap();
798 assert_eq!(json, "\"waiting-taker-bond\"");
799 let back: Status = serde_json::from_str(&json).unwrap();
800 assert_eq!(back, Status::WaitingTakerBond);
801 }
802
803 #[test]
804 fn test_status_waiting_maker_bond_roundtrip() {
805 assert_eq!(Status::WaitingMakerBond.to_string(), "waiting-maker-bond");
806 assert_eq!(
807 Status::from_str("waiting-maker-bond").unwrap(),
808 Status::WaitingMakerBond
809 );
810 let json = serde_json::to_string(&Status::WaitingMakerBond).unwrap();
812 assert_eq!(json, "\"waiting-maker-bond\"");
813 let back: Status = serde_json::from_str(&json).unwrap();
814 assert_eq!(back, Status::WaitingMakerBond);
815 }
816
817 #[test]
818 fn test_kind_string() {
819 assert_ne!(Kind::Sell.to_string(), "active");
820 assert_eq!(Kind::Sell.to_string(), "sell");
821 assert_eq!(Kind::Buy.to_string(), "buy");
822 assert_ne!(Kind::Buy.to_string(), "active");
823 }
824
825 #[test]
826 fn test_order_message() {
827 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
828 let payment_methods = "SEPA,Bank transfer".to_string();
829 let payload = Payload::Order(SmallOrder::new(
830 Some(uuid),
831 Some(Kind::Sell),
832 Some(Status::Pending),
833 100,
834 "eur".to_string(),
835 None,
836 None,
837 100,
838 payment_methods,
839 1,
840 None,
841 None,
842 None,
843 Some(1627371434),
844 None,
845 ));
846
847 let test_message = Message::Order(MessageKind::new(
848 Some(uuid),
849 Some(1),
850 Some(2),
851 Action::NewOrder,
852 Some(payload),
853 ));
854 let test_message_json = test_message.as_json().unwrap();
855 let sample_message = r#"{"order":{"version":2,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":2,"action":"new-order","payload":{"order":{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"pending","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"SEPA,Bank transfer","premium":1,"created_at":1627371434}}}}"#;
856 let message = Message::from_json(sample_message).unwrap();
857 assert!(message.verify());
858 let message_json = message.as_json().unwrap();
859 assert_eq!(message_json, test_message_json);
860 }
861
862 #[test]
863 fn test_payment_request_payload_message() {
864 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
865 let test_message = Message::Order(MessageKind::new(
866 Some(uuid),
867 Some(1),
868 Some(3),
869 Action::PayInvoice,
870 Some(Payload::PaymentRequest(
871 Some(SmallOrder::new(
872 Some(uuid),
873 Some(Kind::Sell),
874 Some(Status::WaitingPayment),
875 100,
876 "eur".to_string(),
877 None,
878 None,
879 100,
880 "Face to face".to_string(),
881 1,
882 None,
883 None,
884 None,
885 Some(1627371434),
886 None,
887 )),
888 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
889 None,
890 )),
891 ));
892 let sample_message = r#"{"order":{"version":2,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":3,"action":"pay-invoice","payload":{"payment_request":[{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"waiting-payment","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"Face to face","premium":1,"created_at":1627371434},"lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e",null]}}}"#;
893 let message = Message::from_json(sample_message).unwrap();
894 assert!(message.verify());
895 let message_json = message.as_json().unwrap();
896 let test_message_json = test_message.as_json().unwrap();
897 assert_eq!(message_json, test_message_json);
898 }
899
900 #[test]
901 fn test_message_payload_signature() {
902 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
903 let peer = Peer::new(
904 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
905 None,
906 );
907 let payload = Payload::Peer(peer);
908 let test_message = Message::Order(MessageKind::new(
909 Some(uuid),
910 Some(1),
911 Some(2),
912 Action::FiatSentOk,
913 Some(payload),
914 ));
915 assert!(test_message.verify());
916 let test_message_json = test_message.as_json().unwrap();
917 let trade_keys =
919 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
920 .unwrap();
921 let sig = Message::sign(test_message_json.clone(), &trade_keys);
922
923 assert!(Message::verify_signature(
924 test_message_json,
925 trade_keys.public_key(),
926 sig
927 ));
928 }
929
930 #[test]
931 fn test_cant_do_message_serialization() {
932 let reasons = vec![
934 CantDoReason::InvalidSignature,
935 CantDoReason::InvalidTradeIndex,
936 CantDoReason::InvalidAmount,
937 CantDoReason::InvalidInvoice,
938 CantDoReason::InvalidPaymentRequest,
939 CantDoReason::InvalidPeer,
940 CantDoReason::InvalidRating,
941 CantDoReason::InvalidTextMessage,
942 CantDoReason::InvalidOrderStatus,
943 CantDoReason::InvalidPubkey,
944 CantDoReason::InvalidParameters,
945 CantDoReason::OrderAlreadyCanceled,
946 CantDoReason::CantCreateUser,
947 CantDoReason::IsNotYourOrder,
948 CantDoReason::NotAllowedByStatus,
949 CantDoReason::OutOfRangeFiatAmount,
950 CantDoReason::OutOfRangeSatsAmount,
951 CantDoReason::IsNotYourDispute,
952 CantDoReason::NotFound,
953 CantDoReason::InvalidFiatCurrency,
954 CantDoReason::TooManyRequests,
955 ];
956
957 for reason in reasons {
958 let cant_do = Message::CantDo(MessageKind::new(
959 None,
960 None,
961 None,
962 Action::CantDo,
963 Some(Payload::CantDo(Some(reason.clone()))),
964 ));
965 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
966 assert!(message.verify());
967 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
968 }
969
970 let cant_do = Message::CantDo(MessageKind::new(
972 None,
973 None,
974 None,
975 Action::CantDo,
976 Some(Payload::CantDo(None)),
977 ));
978 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
979 assert!(message.verify());
980 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
981 }
982
983 #[test]
986 fn test_check_fiat_amount_valid() {
987 let order = SmallOrder::new(
989 None,
990 None,
991 None,
992 100,
993 "VES".to_string(),
994 None,
995 None,
996 500,
997 "Bank".to_string(),
998 1,
999 None,
1000 None,
1001 None,
1002 None,
1003 None,
1004 );
1005 assert!(order.check_fiat_amount().is_ok());
1006 }
1007
1008 #[test]
1009 fn test_check_fiat_amount_zero() {
1010 let order = SmallOrder::new(
1011 None,
1012 None,
1013 None,
1014 100,
1015 "VES".to_string(),
1016 None,
1017 None,
1018 0,
1019 "Bank".to_string(),
1020 1,
1021 None,
1022 None,
1023 None,
1024 None,
1025 None,
1026 );
1027 let result = order.check_fiat_amount();
1028 assert!(result.is_err());
1029 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1030 }
1031
1032 #[test]
1033 fn test_check_fiat_amount_negative() {
1034 let order = SmallOrder::new(
1035 None,
1036 None,
1037 None,
1038 100,
1039 "VES".to_string(),
1040 None,
1041 None,
1042 -100,
1043 "Bank".to_string(),
1044 1,
1045 None,
1046 None,
1047 None,
1048 None,
1049 None,
1050 );
1051 let result = order.check_fiat_amount();
1052 assert!(result.is_err());
1053 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1054 }
1055
1056 #[test]
1059 fn test_check_amount_valid() {
1060 let order = SmallOrder::new(
1062 None,
1063 None,
1064 None,
1065 100000,
1066 "VES".to_string(),
1067 None,
1068 None,
1069 500,
1070 "Bank".to_string(),
1071 0,
1072 None,
1073 None,
1074 None,
1075 None,
1076 None,
1077 );
1078 assert!(order.check_amount().is_ok());
1079 }
1080
1081 #[test]
1082 fn test_check_amount_zero() {
1083 let order = SmallOrder::new(
1085 None,
1086 None,
1087 None,
1088 0,
1089 "VES".to_string(),
1090 None,
1091 None,
1092 500,
1093 "Bank".to_string(),
1094 0,
1095 None,
1096 None,
1097 None,
1098 None,
1099 None,
1100 );
1101 assert!(order.check_amount().is_ok());
1102 }
1103
1104 #[test]
1105 fn test_check_amount_negative() {
1106 let order = SmallOrder::new(
1108 None,
1109 None,
1110 None,
1111 -1000,
1112 "VES".to_string(),
1113 None,
1114 None,
1115 500,
1116 "Bank".to_string(),
1117 0,
1118 None,
1119 None,
1120 None,
1121 None,
1122 None,
1123 );
1124 let result = order.check_amount();
1125 assert!(result.is_err());
1126 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1127 }
1128}