1use crate::prelude::*;
11use nostr::key::PublicKey;
12use nostr::types::Timestamp;
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "sqlx")]
15use sqlx::FromRow;
16use std::{fmt::Display, str::FromStr};
17use uuid::Uuid;
18use wasm_bindgen::prelude::*;
19
20#[wasm_bindgen]
22#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
23#[serde(rename_all = "kebab-case")]
24pub enum Kind {
25 Buy,
27 Sell,
29}
30
31impl FromStr for Kind {
32 type Err = ();
33
34 fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
38 match kind.to_lowercase().as_str() {
39 "buy" => std::result::Result::Ok(Self::Buy),
40 "sell" => std::result::Result::Ok(Self::Sell),
41 _ => Err(()),
42 }
43 }
44}
45
46impl Display for Kind {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Kind::Sell => write!(f, "sell"),
50 Kind::Buy => write!(f, "buy"),
51 }
52 }
53}
54
55#[wasm_bindgen]
60#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
61#[serde(rename_all = "kebab-case")]
62pub enum Status {
63 Active,
65 Canceled,
67 CanceledByAdmin,
69 SettledByAdmin,
71 CompletedByAdmin,
73 Dispute,
75 Expired,
77 FiatSent,
79 SettledHoldInvoice,
81 Pending,
83 Success,
85 WaitingBuyerInvoice,
87 WaitingPayment,
89 WaitingTakerBond,
94 CooperativelyCanceled,
96 InProgress,
98 WaitingMakerBond,
108}
109
110impl Display for Status {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 Status::Active => write!(f, "active"),
114 Status::Canceled => write!(f, "canceled"),
115 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
116 Status::SettledByAdmin => write!(f, "settled-by-admin"),
117 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
118 Status::Dispute => write!(f, "dispute"),
119 Status::Expired => write!(f, "expired"),
120 Status::FiatSent => write!(f, "fiat-sent"),
121 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
122 Status::Pending => write!(f, "pending"),
123 Status::Success => write!(f, "success"),
124 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
125 Status::WaitingPayment => write!(f, "waiting-payment"),
126 Status::WaitingTakerBond => write!(f, "waiting-taker-bond"),
127 Status::WaitingMakerBond => write!(f, "waiting-maker-bond"),
128 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
129 Status::InProgress => write!(f, "in-progress"),
130 }
131 }
132}
133
134impl FromStr for Status {
135 type Err = ();
136 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
138 match s.to_lowercase().as_str() {
139 "active" => std::result::Result::Ok(Self::Active),
140 "canceled" => std::result::Result::Ok(Self::Canceled),
141 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
142 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
143 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
144 "dispute" => std::result::Result::Ok(Self::Dispute),
145 "expired" => std::result::Result::Ok(Self::Expired),
146 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
147 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
148 "pending" => std::result::Result::Ok(Self::Pending),
149 "success" => std::result::Result::Ok(Self::Success),
150 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
151 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
152 "waiting-taker-bond" => std::result::Result::Ok(Self::WaitingTakerBond),
153 "waiting-maker-bond" => std::result::Result::Ok(Self::WaitingMakerBond),
154 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
155 "in-progress" => std::result::Result::Ok(Self::InProgress),
156 _ => Err(()),
157 }
158 }
159}
160#[cfg_attr(feature = "sqlx", derive(FromRow))]
169#[derive(Debug, Default, Deserialize, Serialize, Clone)]
170pub struct Order {
171 pub id: Uuid,
173 pub kind: String,
176 pub event_id: String,
178 pub hash: Option<String>,
180 pub preimage: Option<String>,
182 pub creator_pubkey: String,
184 pub cancel_initiator_pubkey: Option<String>,
186 pub buyer_pubkey: Option<String>,
188 pub master_buyer_pubkey: Option<String>,
191 pub seller_pubkey: Option<String>,
193 pub master_seller_pubkey: Option<String>,
196 pub status: String,
198 pub price_from_api: bool,
200 pub premium: i64,
202 pub payment_method: String,
204 pub amount: i64,
207 pub min_amount: Option<i64>,
209 pub max_amount: Option<i64>,
211 pub buyer_dispute: bool,
213 pub seller_dispute: bool,
215 pub buyer_cooperativecancel: bool,
217 pub seller_cooperativecancel: bool,
219 pub fee: i64,
221 pub routing_fee: i64,
223 pub dev_fee: i64,
225 pub dev_fee_paid: bool,
227 pub dev_fee_payment_hash: Option<String>,
229 pub fiat_code: String,
231 pub fiat_amount: i64,
233 pub buyer_invoice: Option<String>,
235 pub range_parent_id: Option<Uuid>,
237 pub invoice_held_at: i64,
239 pub taken_at: i64,
241 pub created_at: i64,
243 pub buyer_sent_rate: bool,
245 pub seller_sent_rate: bool,
247 pub failed_payment: bool,
249 pub payment_attempts: i64,
251 pub expires_at: i64,
253 pub trade_index_seller: Option<i64>,
255 pub trade_index_buyer: Option<i64>,
257 pub next_trade_pubkey: Option<String>,
260 pub next_trade_index: Option<i64>,
262 pub cashu_mint_url: Option<String>,
265 pub cashu_escrow_token: Option<String>,
268 pub cashu_escrow_locked_at: Option<i64>,
271}
272
273impl From<SmallOrder> for Order {
274 fn from(small_order: SmallOrder) -> Self {
275 Self {
276 id: Uuid::new_v4(),
277 kind: small_order
279 .kind
280 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
281 status: small_order
282 .status
283 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
284 amount: small_order.amount,
285 fiat_code: small_order.fiat_code,
286 min_amount: small_order.min_amount,
287 max_amount: small_order.max_amount,
288 fiat_amount: small_order.fiat_amount,
289 payment_method: small_order.payment_method,
290 premium: small_order.premium,
291 event_id: String::new(),
292 creator_pubkey: String::new(),
293 price_from_api: false,
294 fee: 0,
295 routing_fee: 0,
296 dev_fee: 0,
297 dev_fee_paid: false,
298 dev_fee_payment_hash: None,
299 invoice_held_at: 0,
300 taken_at: 0,
301 created_at: small_order.created_at.unwrap_or(0),
302 expires_at: small_order.expires_at.unwrap_or(0),
303 payment_attempts: 0,
304 ..Default::default()
305 }
306 }
307}
308
309impl Order {
310 pub fn as_new_order(&self) -> SmallOrder {
316 SmallOrder::new(
317 Some(self.id),
318 Some(Kind::from_str(&self.kind).unwrap()),
319 Some(Status::from_str(&self.status).unwrap()),
320 self.amount,
321 self.fiat_code.clone(),
322 self.min_amount,
323 self.max_amount,
324 self.fiat_amount,
325 self.payment_method.clone(),
326 self.premium,
327 None,
328 None,
329 self.buyer_invoice.clone(),
330 Some(self.created_at),
331 Some(self.expires_at),
332 )
333 }
334 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
339 if let Ok(kind) = Kind::from_str(&self.kind) {
340 Ok(kind)
341 } else {
342 Err(ServiceError::InvalidOrderKind)
343 }
344 }
345
346 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
351 if let Ok(status) = Status::from_str(&self.status) {
352 Ok(status)
353 } else {
354 Err(ServiceError::InvalidOrderStatus)
355 }
356 }
357
358 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
363 match Status::from_str(&self.status) {
364 Ok(s) => match s == status {
365 true => Ok(()),
366 false => Err(CantDoReason::InvalidOrderStatus),
367 },
368 Err(_) => Err(CantDoReason::InvalidOrderStatus),
369 }
370 }
371
372 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
374 if self.kind != Kind::Buy.to_string() {
375 return Err(CantDoReason::InvalidOrderKind);
376 }
377 Ok(())
378 }
379 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
381 if self.kind != Kind::Sell.to_string() {
382 return Err(CantDoReason::InvalidOrderKind);
383 }
384 Ok(())
385 }
386
387 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
391 let sender = sender.to_string();
392 if self.creator_pubkey != sender {
393 return Err(CantDoReason::InvalidPubkey);
394 }
395 Ok(())
396 }
397
398 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
403 let sender = sender.to_string();
404 if self.creator_pubkey == sender {
405 return Err(CantDoReason::InvalidPubkey);
406 }
407 Ok(())
408 }
409
410 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
412 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
413 Ok(pk) => Ok(pk),
414 Err(_) => Err(ServiceError::InvalidPubkey),
415 }
416 }
417
418 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
423 if let Some(pk) = self.buyer_pubkey.as_ref() {
424 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
425 } else {
426 Err(ServiceError::InvalidPubkey)
427 }
428 }
429 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
434 if let Some(pk) = self.seller_pubkey.as_ref() {
435 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
436 } else {
437 Err(ServiceError::InvalidPubkey)
438 }
439 }
440 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
442 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
443 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
444 } else {
445 Err(ServiceError::InvalidPubkey)
446 }
447 }
448 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
450 if let Some(pk) = self.master_seller_pubkey.as_ref() {
451 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
452 } else {
453 Err(ServiceError::InvalidPubkey)
454 }
455 }
456
457 pub fn is_range_order(&self) -> bool {
460 self.min_amount.is_some() && self.max_amount.is_some()
461 }
462
463 pub fn count_failed_payment(&mut self, retries_number: i64) {
469 if !self.failed_payment {
470 self.failed_payment = true;
471 self.payment_attempts = 1;
472 } else if self.payment_attempts < retries_number {
473 self.payment_attempts += 1;
474 }
475 }
476
477 pub fn has_no_amount(&self) -> bool {
480 self.amount == 0
481 }
482
483 pub fn set_timestamp_now(&mut self) {
485 self.taken_at = Timestamp::now().as_secs() as i64
486 }
487
488 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
497 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
498
499 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
501 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
502
503 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
505 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
506 }
507
508 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
510 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
511 }
512
513 Ok((normal_buyer_idkey, normal_seller_idkey))
514 }
515 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
523 let is_seller_dispute = !is_buyer_dispute;
525
526 let mut update_seller_dispute = false;
528 let mut update_buyer_dispute = false;
529
530 if is_seller_dispute && !self.seller_dispute {
531 update_seller_dispute = true;
532 self.seller_dispute = update_seller_dispute;
533 } else if is_buyer_dispute && !self.buyer_dispute {
534 update_buyer_dispute = true;
535 self.buyer_dispute = update_buyer_dispute;
536 };
537 self.status = Status::Dispute.to_string();
539
540 if !update_buyer_dispute && !update_seller_dispute {
543 return Err(CantDoReason::DisputeCreationError);
544 }
545
546 Ok(())
547 }
548}
549
550#[derive(Debug, Default, Deserialize, Serialize, Clone)]
559#[serde(deny_unknown_fields)]
560pub struct SmallOrder {
561 #[serde(skip_serializing_if = "Option::is_none")]
563 pub id: Option<Uuid>,
564 pub kind: Option<Kind>,
566 pub status: Option<Status>,
568 pub amount: i64,
571 pub fiat_code: String,
573 pub min_amount: Option<i64>,
575 pub max_amount: Option<i64>,
577 pub fiat_amount: i64,
579 pub payment_method: String,
581 pub premium: i64,
583 #[serde(skip_serializing_if = "Option::is_none")]
585 pub buyer_trade_pubkey: Option<String>,
586 #[serde(skip_serializing_if = "Option::is_none")]
588 pub seller_trade_pubkey: Option<String>,
589 #[serde(skip_serializing_if = "Option::is_none")]
591 pub buyer_invoice: Option<String>,
592 pub created_at: Option<i64>,
594 pub expires_at: Option<i64>,
596}
597
598#[allow(dead_code)]
599impl SmallOrder {
600 #[allow(clippy::too_many_arguments)]
602 pub fn new(
603 id: Option<Uuid>,
604 kind: Option<Kind>,
605 status: Option<Status>,
606 amount: i64,
607 fiat_code: String,
608 min_amount: Option<i64>,
609 max_amount: Option<i64>,
610 fiat_amount: i64,
611 payment_method: String,
612 premium: i64,
613 buyer_trade_pubkey: Option<String>,
614 seller_trade_pubkey: Option<String>,
615 buyer_invoice: Option<String>,
616 created_at: Option<i64>,
617 expires_at: Option<i64>,
618 ) -> Self {
619 Self {
620 id,
621 kind,
622 status,
623 amount,
624 fiat_code,
625 min_amount,
626 max_amount,
627 fiat_amount,
628 payment_method,
629 premium,
630 buyer_trade_pubkey,
631 seller_trade_pubkey,
632 buyer_invoice,
633 created_at,
634 expires_at,
635 }
636 }
637 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
639 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
640 }
641
642 pub fn as_json(&self) -> Result<String, ServiceError> {
644 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
645 }
646
647 pub fn sats_amount(&self) -> String {
650 if self.amount == 0 {
651 "Market price".to_string()
652 } else {
653 self.amount.to_string()
654 }
655 }
656 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
660 if self.fiat_amount <= 0 {
661 return Err(CantDoReason::InvalidAmount);
662 }
663 Ok(())
664 }
665
666 pub fn check_amount(&self) -> Result<(), CantDoReason> {
673 if self.amount < 0 {
674 return Err(CantDoReason::InvalidAmount);
675 }
676 Ok(())
677 }
678
679 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
685 let premium = (self.premium != 0).then_some(self.premium);
686 let sats_amount = (self.amount != 0).then_some(self.amount);
687
688 if premium.is_some() && sats_amount.is_some() {
689 return Err(CantDoReason::InvalidParameters);
690 }
691 Ok(())
692 }
693
694 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
702 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
704 if min < 0 || max < 0 {
705 return Err(CantDoReason::InvalidAmount);
706 }
707 if min >= max {
708 return Err(CantDoReason::InvalidAmount);
709 }
710 if self.amount != 0 {
711 return Err(CantDoReason::InvalidAmount);
712 }
713 amounts.clear();
714 amounts.push(min);
715 amounts.push(max);
716 }
717 Ok(())
718 }
719
720 pub fn check_fiat_currency(
727 &self,
728 fiat_currencies_accepted: &[String],
729 ) -> Result<(), CantDoReason> {
730 if !fiat_currencies_accepted.contains(&self.fiat_code)
731 && !fiat_currencies_accepted.is_empty()
732 {
733 return Err(CantDoReason::InvalidFiatCurrency);
734 }
735 Ok(())
736 }
737}
738
739impl From<Order> for SmallOrder {
740 fn from(order: Order) -> Self {
741 let id = Some(order.id);
742 let kind = Kind::from_str(&order.kind).unwrap();
743 let status = Status::from_str(&order.status).unwrap();
744 let amount = order.amount;
745 let fiat_code = order.fiat_code.clone();
746 let min_amount = order.min_amount;
747 let max_amount = order.max_amount;
748 let fiat_amount = order.fiat_amount;
749 let payment_method = order.payment_method.clone();
750 let premium = order.premium;
751 let buyer_trade_pubkey = order.buyer_pubkey.clone();
752 let seller_trade_pubkey = order.seller_pubkey.clone();
753 let buyer_invoice = order.buyer_invoice.clone();
754
755 Self {
756 id,
757 kind: Some(kind),
758 status: Some(status),
759 amount,
760 fiat_code,
761 min_amount,
762 max_amount,
763 fiat_amount,
764 payment_method,
765 premium,
766 buyer_trade_pubkey,
767 seller_trade_pubkey,
768 buyer_invoice,
769 created_at: Some(order.created_at),
770 expires_at: Some(order.expires_at),
771 }
772 }
773}
774
775#[cfg(test)]
776mod tests {
777 use super::*;
778 use crate::error::CantDoReason;
779 use nostr_sdk::prelude::Keys;
780 use uuid::uuid;
781
782 #[test]
783 fn test_status_string() {
784 assert_eq!(Status::Active.to_string(), "active");
785 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
786 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
787 assert_ne!(Status::Pending.to_string(), "Pending");
788 }
789
790 #[test]
791 fn test_status_waiting_taker_bond_roundtrip() {
792 assert_eq!(Status::WaitingTakerBond.to_string(), "waiting-taker-bond");
793 assert_eq!(
794 Status::from_str("waiting-taker-bond").unwrap(),
795 Status::WaitingTakerBond
796 );
797 let json = serde_json::to_string(&Status::WaitingTakerBond).unwrap();
799 assert_eq!(json, "\"waiting-taker-bond\"");
800 let back: Status = serde_json::from_str(&json).unwrap();
801 assert_eq!(back, Status::WaitingTakerBond);
802 }
803
804 #[test]
805 fn test_status_waiting_maker_bond_roundtrip() {
806 assert_eq!(Status::WaitingMakerBond.to_string(), "waiting-maker-bond");
807 assert_eq!(
808 Status::from_str("waiting-maker-bond").unwrap(),
809 Status::WaitingMakerBond
810 );
811 let json = serde_json::to_string(&Status::WaitingMakerBond).unwrap();
813 assert_eq!(json, "\"waiting-maker-bond\"");
814 let back: Status = serde_json::from_str(&json).unwrap();
815 assert_eq!(back, Status::WaitingMakerBond);
816 }
817
818 #[test]
819 fn test_kind_string() {
820 assert_ne!(Kind::Sell.to_string(), "active");
821 assert_eq!(Kind::Sell.to_string(), "sell");
822 assert_eq!(Kind::Buy.to_string(), "buy");
823 assert_ne!(Kind::Buy.to_string(), "active");
824 }
825
826 #[test]
827 fn test_order_message() {
828 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
829 let payment_methods = "SEPA,Bank transfer".to_string();
830 let payload = Payload::Order(SmallOrder::new(
831 Some(uuid),
832 Some(Kind::Sell),
833 Some(Status::Pending),
834 100,
835 "eur".to_string(),
836 None,
837 None,
838 100,
839 payment_methods,
840 1,
841 None,
842 None,
843 None,
844 Some(1627371434),
845 None,
846 ));
847
848 let test_message = Message::Order(MessageKind::new(
849 Some(uuid),
850 Some(1),
851 Some(2),
852 Action::NewOrder,
853 Some(payload),
854 ));
855 let test_message_json = test_message.as_json().unwrap();
856 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}}}}"#;
857 let message = Message::from_json(sample_message).unwrap();
858 assert!(message.verify());
859 let message_json = message.as_json().unwrap();
860 assert_eq!(message_json, test_message_json);
861 }
862
863 #[test]
864 fn test_payment_request_payload_message() {
865 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
866 let test_message = Message::Order(MessageKind::new(
867 Some(uuid),
868 Some(1),
869 Some(3),
870 Action::PayInvoice,
871 Some(Payload::PaymentRequest(
872 Some(SmallOrder::new(
873 Some(uuid),
874 Some(Kind::Sell),
875 Some(Status::WaitingPayment),
876 100,
877 "eur".to_string(),
878 None,
879 None,
880 100,
881 "Face to face".to_string(),
882 1,
883 None,
884 None,
885 None,
886 Some(1627371434),
887 None,
888 )),
889 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
890 None,
891 )),
892 ));
893 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]}}}"#;
894 let message = Message::from_json(sample_message).unwrap();
895 assert!(message.verify());
896 let message_json = message.as_json().unwrap();
897 let test_message_json = test_message.as_json().unwrap();
898 assert_eq!(message_json, test_message_json);
899 }
900
901 #[test]
902 fn test_message_payload_signature() {
903 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
904 let peer = Peer::new(
905 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
906 None,
907 );
908 let payload = Payload::Peer(peer);
909 let test_message = Message::Order(MessageKind::new(
910 Some(uuid),
911 Some(1),
912 Some(2),
913 Action::FiatSentOk,
914 Some(payload),
915 ));
916 assert!(test_message.verify());
917 let test_message_json = test_message.as_json().unwrap();
918 let trade_keys =
920 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
921 .unwrap();
922 let sig = Message::sign(test_message_json.clone(), &trade_keys);
923
924 assert!(Message::verify_signature(
925 test_message_json,
926 trade_keys.public_key(),
927 sig
928 ));
929 }
930
931 #[test]
932 fn test_cant_do_message_serialization() {
933 let reasons = vec![
935 CantDoReason::InvalidSignature,
936 CantDoReason::InvalidTradeIndex,
937 CantDoReason::InvalidAmount,
938 CantDoReason::InvalidInvoice,
939 CantDoReason::InvalidPaymentRequest,
940 CantDoReason::InvalidPeer,
941 CantDoReason::InvalidRating,
942 CantDoReason::InvalidTextMessage,
943 CantDoReason::InvalidOrderStatus,
944 CantDoReason::InvalidPubkey,
945 CantDoReason::InvalidParameters,
946 CantDoReason::OrderAlreadyCanceled,
947 CantDoReason::CantCreateUser,
948 CantDoReason::IsNotYourOrder,
949 CantDoReason::NotAllowedByStatus,
950 CantDoReason::OutOfRangeFiatAmount,
951 CantDoReason::OutOfRangeSatsAmount,
952 CantDoReason::IsNotYourDispute,
953 CantDoReason::NotFound,
954 CantDoReason::InvalidFiatCurrency,
955 CantDoReason::TooManyRequests,
956 ];
957
958 for reason in reasons {
959 let cant_do = Message::CantDo(MessageKind::new(
960 None,
961 None,
962 None,
963 Action::CantDo,
964 Some(Payload::CantDo(Some(reason.clone()))),
965 ));
966 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
967 assert!(message.verify());
968 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
969 }
970
971 let cant_do = Message::CantDo(MessageKind::new(
973 None,
974 None,
975 None,
976 Action::CantDo,
977 Some(Payload::CantDo(None)),
978 ));
979 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
980 assert!(message.verify());
981 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
982 }
983
984 #[test]
987 fn test_check_fiat_amount_valid() {
988 let order = SmallOrder::new(
990 None,
991 None,
992 None,
993 100,
994 "VES".to_string(),
995 None,
996 None,
997 500,
998 "Bank".to_string(),
999 1,
1000 None,
1001 None,
1002 None,
1003 None,
1004 None,
1005 );
1006 assert!(order.check_fiat_amount().is_ok());
1007 }
1008
1009 #[test]
1010 fn test_check_fiat_amount_zero() {
1011 let order = SmallOrder::new(
1012 None,
1013 None,
1014 None,
1015 100,
1016 "VES".to_string(),
1017 None,
1018 None,
1019 0,
1020 "Bank".to_string(),
1021 1,
1022 None,
1023 None,
1024 None,
1025 None,
1026 None,
1027 );
1028 let result = order.check_fiat_amount();
1029 assert!(result.is_err());
1030 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1031 }
1032
1033 #[test]
1034 fn test_check_fiat_amount_negative() {
1035 let order = SmallOrder::new(
1036 None,
1037 None,
1038 None,
1039 100,
1040 "VES".to_string(),
1041 None,
1042 None,
1043 -100,
1044 "Bank".to_string(),
1045 1,
1046 None,
1047 None,
1048 None,
1049 None,
1050 None,
1051 );
1052 let result = order.check_fiat_amount();
1053 assert!(result.is_err());
1054 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1055 }
1056
1057 #[test]
1060 fn test_check_amount_valid() {
1061 let order = SmallOrder::new(
1063 None,
1064 None,
1065 None,
1066 100000,
1067 "VES".to_string(),
1068 None,
1069 None,
1070 500,
1071 "Bank".to_string(),
1072 0,
1073 None,
1074 None,
1075 None,
1076 None,
1077 None,
1078 );
1079 assert!(order.check_amount().is_ok());
1080 }
1081
1082 #[test]
1083 fn test_check_amount_zero() {
1084 let order = SmallOrder::new(
1086 None,
1087 None,
1088 None,
1089 0,
1090 "VES".to_string(),
1091 None,
1092 None,
1093 500,
1094 "Bank".to_string(),
1095 0,
1096 None,
1097 None,
1098 None,
1099 None,
1100 None,
1101 );
1102 assert!(order.check_amount().is_ok());
1103 }
1104
1105 #[test]
1106 fn test_check_amount_negative() {
1107 let order = SmallOrder::new(
1109 None,
1110 None,
1111 None,
1112 -1000,
1113 "VES".to_string(),
1114 None,
1115 None,
1116 500,
1117 "Bank".to_string(),
1118 0,
1119 None,
1120 None,
1121 None,
1122 None,
1123 None,
1124 );
1125 let result = order.check_amount();
1126 assert!(result.is_err());
1127 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1128 }
1129}