1use crate::prelude::*;
11use nostr_sdk::{PublicKey, Timestamp};
12use serde::{Deserialize, Serialize};
13#[cfg(feature = "sqlx")]
14use sqlx::FromRow;
15#[cfg(feature = "sqlx")]
16use sqlx_crud::SqlxCrud;
17use std::{fmt::Display, str::FromStr};
18use uuid::Uuid;
19use wasm_bindgen::prelude::*;
20
21#[wasm_bindgen]
23#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
24#[serde(rename_all = "kebab-case")]
25pub enum Kind {
26 Buy,
28 Sell,
30}
31
32impl FromStr for Kind {
33 type Err = ();
34
35 fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
39 match kind.to_lowercase().as_str() {
40 "buy" => std::result::Result::Ok(Self::Buy),
41 "sell" => std::result::Result::Ok(Self::Sell),
42 _ => Err(()),
43 }
44 }
45}
46
47impl Display for Kind {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Kind::Sell => write!(f, "sell"),
51 Kind::Buy => write!(f, "buy"),
52 }
53 }
54}
55
56#[wasm_bindgen]
61#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
62#[serde(rename_all = "kebab-case")]
63pub enum Status {
64 Active,
66 Canceled,
68 CanceledByAdmin,
70 SettledByAdmin,
72 CompletedByAdmin,
74 Dispute,
76 Expired,
78 FiatSent,
80 SettledHoldInvoice,
82 Pending,
84 Success,
86 WaitingBuyerInvoice,
88 WaitingPayment,
90 WaitingTakerBond,
95 CooperativelyCanceled,
97 InProgress,
99}
100
101impl Display for Status {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 Status::Active => write!(f, "active"),
105 Status::Canceled => write!(f, "canceled"),
106 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
107 Status::SettledByAdmin => write!(f, "settled-by-admin"),
108 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
109 Status::Dispute => write!(f, "dispute"),
110 Status::Expired => write!(f, "expired"),
111 Status::FiatSent => write!(f, "fiat-sent"),
112 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
113 Status::Pending => write!(f, "pending"),
114 Status::Success => write!(f, "success"),
115 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
116 Status::WaitingPayment => write!(f, "waiting-payment"),
117 Status::WaitingTakerBond => write!(f, "waiting-taker-bond"),
118 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
119 Status::InProgress => write!(f, "in-progress"),
120 }
121 }
122}
123
124impl FromStr for Status {
125 type Err = ();
126 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
128 match s.to_lowercase().as_str() {
129 "active" => std::result::Result::Ok(Self::Active),
130 "canceled" => std::result::Result::Ok(Self::Canceled),
131 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
132 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
133 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
134 "dispute" => std::result::Result::Ok(Self::Dispute),
135 "expired" => std::result::Result::Ok(Self::Expired),
136 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
137 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
138 "pending" => std::result::Result::Ok(Self::Pending),
139 "success" => std::result::Result::Ok(Self::Success),
140 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
141 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
142 "waiting-taker-bond" => std::result::Result::Ok(Self::WaitingTakerBond),
143 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
144 "in-progress" => std::result::Result::Ok(Self::InProgress),
145 _ => Err(()),
146 }
147 }
148}
149#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
158#[derive(Debug, Default, Deserialize, Serialize, Clone)]
159pub struct Order {
160 pub id: Uuid,
162 pub kind: String,
165 pub event_id: String,
167 pub hash: Option<String>,
169 pub preimage: Option<String>,
171 pub creator_pubkey: String,
173 pub cancel_initiator_pubkey: Option<String>,
175 pub buyer_pubkey: Option<String>,
177 pub master_buyer_pubkey: Option<String>,
180 pub seller_pubkey: Option<String>,
182 pub master_seller_pubkey: Option<String>,
185 pub status: String,
187 pub price_from_api: bool,
189 pub premium: i64,
191 pub payment_method: String,
193 pub amount: i64,
196 pub min_amount: Option<i64>,
198 pub max_amount: Option<i64>,
200 pub buyer_dispute: bool,
202 pub seller_dispute: bool,
204 pub buyer_cooperativecancel: bool,
206 pub seller_cooperativecancel: bool,
208 pub fee: i64,
210 pub routing_fee: i64,
212 pub dev_fee: i64,
214 pub dev_fee_paid: bool,
216 pub dev_fee_payment_hash: Option<String>,
218 pub fiat_code: String,
220 pub fiat_amount: i64,
222 pub buyer_invoice: Option<String>,
224 pub range_parent_id: Option<Uuid>,
226 pub invoice_held_at: i64,
228 pub taken_at: i64,
230 pub created_at: i64,
232 pub buyer_sent_rate: bool,
234 pub seller_sent_rate: bool,
236 pub failed_payment: bool,
238 pub payment_attempts: i64,
240 pub expires_at: i64,
242 pub trade_index_seller: Option<i64>,
244 pub trade_index_buyer: Option<i64>,
246 pub next_trade_pubkey: Option<String>,
249 pub next_trade_index: Option<i64>,
251}
252
253impl From<SmallOrder> for Order {
254 fn from(small_order: SmallOrder) -> Self {
255 Self {
256 id: Uuid::new_v4(),
257 kind: small_order
259 .kind
260 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
261 status: small_order
262 .status
263 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
264 amount: small_order.amount,
265 fiat_code: small_order.fiat_code,
266 min_amount: small_order.min_amount,
267 max_amount: small_order.max_amount,
268 fiat_amount: small_order.fiat_amount,
269 payment_method: small_order.payment_method,
270 premium: small_order.premium,
271 event_id: String::new(),
272 creator_pubkey: String::new(),
273 price_from_api: false,
274 fee: 0,
275 routing_fee: 0,
276 dev_fee: 0,
277 dev_fee_paid: false,
278 dev_fee_payment_hash: None,
279 invoice_held_at: 0,
280 taken_at: 0,
281 created_at: small_order.created_at.unwrap_or(0),
282 expires_at: small_order.expires_at.unwrap_or(0),
283 payment_attempts: 0,
284 ..Default::default()
285 }
286 }
287}
288
289impl Order {
290 pub fn as_new_order(&self) -> SmallOrder {
296 SmallOrder::new(
297 Some(self.id),
298 Some(Kind::from_str(&self.kind).unwrap()),
299 Some(Status::from_str(&self.status).unwrap()),
300 self.amount,
301 self.fiat_code.clone(),
302 self.min_amount,
303 self.max_amount,
304 self.fiat_amount,
305 self.payment_method.clone(),
306 self.premium,
307 None,
308 None,
309 self.buyer_invoice.clone(),
310 Some(self.created_at),
311 Some(self.expires_at),
312 )
313 }
314 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
319 if let Ok(kind) = Kind::from_str(&self.kind) {
320 Ok(kind)
321 } else {
322 Err(ServiceError::InvalidOrderKind)
323 }
324 }
325
326 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
331 if let Ok(status) = Status::from_str(&self.status) {
332 Ok(status)
333 } else {
334 Err(ServiceError::InvalidOrderStatus)
335 }
336 }
337
338 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
343 match Status::from_str(&self.status) {
344 Ok(s) => match s == status {
345 true => Ok(()),
346 false => Err(CantDoReason::InvalidOrderStatus),
347 },
348 Err(_) => Err(CantDoReason::InvalidOrderStatus),
349 }
350 }
351
352 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
354 if self.kind != Kind::Buy.to_string() {
355 return Err(CantDoReason::InvalidOrderKind);
356 }
357 Ok(())
358 }
359 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
361 if self.kind != Kind::Sell.to_string() {
362 return Err(CantDoReason::InvalidOrderKind);
363 }
364 Ok(())
365 }
366
367 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
371 let sender = sender.to_string();
372 if self.creator_pubkey != sender {
373 return Err(CantDoReason::InvalidPubkey);
374 }
375 Ok(())
376 }
377
378 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
383 let sender = sender.to_string();
384 if self.creator_pubkey == sender {
385 return Err(CantDoReason::InvalidPubkey);
386 }
387 Ok(())
388 }
389
390 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
392 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
393 Ok(pk) => Ok(pk),
394 Err(_) => Err(ServiceError::InvalidPubkey),
395 }
396 }
397
398 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
403 if let Some(pk) = self.buyer_pubkey.as_ref() {
404 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
405 } else {
406 Err(ServiceError::InvalidPubkey)
407 }
408 }
409 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
414 if let Some(pk) = self.seller_pubkey.as_ref() {
415 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
416 } else {
417 Err(ServiceError::InvalidPubkey)
418 }
419 }
420 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
422 if let Some(pk) = self.master_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_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
430 if let Some(pk) = self.master_seller_pubkey.as_ref() {
431 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
432 } else {
433 Err(ServiceError::InvalidPubkey)
434 }
435 }
436
437 pub fn is_range_order(&self) -> bool {
440 self.min_amount.is_some() && self.max_amount.is_some()
441 }
442
443 pub fn count_failed_payment(&mut self, retries_number: i64) {
449 if !self.failed_payment {
450 self.failed_payment = true;
451 self.payment_attempts = 1;
452 } else if self.payment_attempts < retries_number {
453 self.payment_attempts += 1;
454 }
455 }
456
457 pub fn has_no_amount(&self) -> bool {
460 self.amount == 0
461 }
462
463 pub fn set_timestamp_now(&mut self) {
465 self.taken_at = Timestamp::now().as_secs() as i64
466 }
467
468 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
477 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
478
479 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
481 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
482
483 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
485 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
486 }
487
488 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
490 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
491 }
492
493 Ok((normal_buyer_idkey, normal_seller_idkey))
494 }
495 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
503 let is_seller_dispute = !is_buyer_dispute;
505
506 let mut update_seller_dispute = false;
508 let mut update_buyer_dispute = false;
509
510 if is_seller_dispute && !self.seller_dispute {
511 update_seller_dispute = true;
512 self.seller_dispute = update_seller_dispute;
513 } else if is_buyer_dispute && !self.buyer_dispute {
514 update_buyer_dispute = true;
515 self.buyer_dispute = update_buyer_dispute;
516 };
517 self.status = Status::Dispute.to_string();
519
520 if !update_buyer_dispute && !update_seller_dispute {
523 return Err(CantDoReason::DisputeCreationError);
524 }
525
526 Ok(())
527 }
528}
529
530#[derive(Debug, Default, Deserialize, Serialize, Clone)]
539#[serde(deny_unknown_fields)]
540pub struct SmallOrder {
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub id: Option<Uuid>,
544 pub kind: Option<Kind>,
546 pub status: Option<Status>,
548 pub amount: i64,
551 pub fiat_code: String,
553 pub min_amount: Option<i64>,
555 pub max_amount: Option<i64>,
557 pub fiat_amount: i64,
559 pub payment_method: String,
561 pub premium: i64,
563 #[serde(skip_serializing_if = "Option::is_none")]
565 pub buyer_trade_pubkey: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
568 pub seller_trade_pubkey: Option<String>,
569 #[serde(skip_serializing_if = "Option::is_none")]
571 pub buyer_invoice: Option<String>,
572 pub created_at: Option<i64>,
574 pub expires_at: Option<i64>,
576}
577
578#[allow(dead_code)]
579impl SmallOrder {
580 #[allow(clippy::too_many_arguments)]
582 pub fn new(
583 id: Option<Uuid>,
584 kind: Option<Kind>,
585 status: Option<Status>,
586 amount: i64,
587 fiat_code: String,
588 min_amount: Option<i64>,
589 max_amount: Option<i64>,
590 fiat_amount: i64,
591 payment_method: String,
592 premium: i64,
593 buyer_trade_pubkey: Option<String>,
594 seller_trade_pubkey: Option<String>,
595 buyer_invoice: Option<String>,
596 created_at: Option<i64>,
597 expires_at: Option<i64>,
598 ) -> Self {
599 Self {
600 id,
601 kind,
602 status,
603 amount,
604 fiat_code,
605 min_amount,
606 max_amount,
607 fiat_amount,
608 payment_method,
609 premium,
610 buyer_trade_pubkey,
611 seller_trade_pubkey,
612 buyer_invoice,
613 created_at,
614 expires_at,
615 }
616 }
617 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
619 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
620 }
621
622 pub fn as_json(&self) -> Result<String, ServiceError> {
624 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
625 }
626
627 pub fn sats_amount(&self) -> String {
630 if self.amount == 0 {
631 "Market price".to_string()
632 } else {
633 self.amount.to_string()
634 }
635 }
636 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
640 if self.fiat_amount <= 0 {
641 return Err(CantDoReason::InvalidAmount);
642 }
643 Ok(())
644 }
645
646 pub fn check_amount(&self) -> Result<(), CantDoReason> {
653 if self.amount < 0 {
654 return Err(CantDoReason::InvalidAmount);
655 }
656 Ok(())
657 }
658
659 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
665 let premium = (self.premium != 0).then_some(self.premium);
666 let sats_amount = (self.amount != 0).then_some(self.amount);
667
668 if premium.is_some() && sats_amount.is_some() {
669 return Err(CantDoReason::InvalidParameters);
670 }
671 Ok(())
672 }
673
674 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
682 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
684 if min < 0 || max < 0 {
685 return Err(CantDoReason::InvalidAmount);
686 }
687 if min >= max {
688 return Err(CantDoReason::InvalidAmount);
689 }
690 if self.amount != 0 {
691 return Err(CantDoReason::InvalidAmount);
692 }
693 amounts.clear();
694 amounts.push(min);
695 amounts.push(max);
696 }
697 Ok(())
698 }
699
700 pub fn check_fiat_currency(
707 &self,
708 fiat_currencies_accepted: &[String],
709 ) -> Result<(), CantDoReason> {
710 if !fiat_currencies_accepted.contains(&self.fiat_code)
711 && !fiat_currencies_accepted.is_empty()
712 {
713 return Err(CantDoReason::InvalidFiatCurrency);
714 }
715 Ok(())
716 }
717}
718
719impl From<Order> for SmallOrder {
720 fn from(order: Order) -> Self {
721 let id = Some(order.id);
722 let kind = Kind::from_str(&order.kind).unwrap();
723 let status = Status::from_str(&order.status).unwrap();
724 let amount = order.amount;
725 let fiat_code = order.fiat_code.clone();
726 let min_amount = order.min_amount;
727 let max_amount = order.max_amount;
728 let fiat_amount = order.fiat_amount;
729 let payment_method = order.payment_method.clone();
730 let premium = order.premium;
731 let buyer_trade_pubkey = order.buyer_pubkey.clone();
732 let seller_trade_pubkey = order.seller_pubkey.clone();
733 let buyer_invoice = order.buyer_invoice.clone();
734
735 Self {
736 id,
737 kind: Some(kind),
738 status: Some(status),
739 amount,
740 fiat_code,
741 min_amount,
742 max_amount,
743 fiat_amount,
744 payment_method,
745 premium,
746 buyer_trade_pubkey,
747 seller_trade_pubkey,
748 buyer_invoice,
749 created_at: Some(order.created_at),
750 expires_at: Some(order.expires_at),
751 }
752 }
753}
754
755#[cfg(test)]
756mod tests {
757 use super::*;
758 use crate::error::CantDoReason;
759 use nostr_sdk::Keys;
760 use uuid::uuid;
761
762 #[test]
763 fn test_status_string() {
764 assert_eq!(Status::Active.to_string(), "active");
765 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
766 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
767 assert_ne!(Status::Pending.to_string(), "Pending");
768 }
769
770 #[test]
771 fn test_status_waiting_taker_bond_roundtrip() {
772 assert_eq!(Status::WaitingTakerBond.to_string(), "waiting-taker-bond");
773 assert_eq!(
774 Status::from_str("waiting-taker-bond").unwrap(),
775 Status::WaitingTakerBond
776 );
777 let json = serde_json::to_string(&Status::WaitingTakerBond).unwrap();
779 assert_eq!(json, "\"waiting-taker-bond\"");
780 let back: Status = serde_json::from_str(&json).unwrap();
781 assert_eq!(back, Status::WaitingTakerBond);
782 }
783
784 #[test]
785 fn test_kind_string() {
786 assert_ne!(Kind::Sell.to_string(), "active");
787 assert_eq!(Kind::Sell.to_string(), "sell");
788 assert_eq!(Kind::Buy.to_string(), "buy");
789 assert_ne!(Kind::Buy.to_string(), "active");
790 }
791
792 #[test]
793 fn test_order_message() {
794 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
795 let payment_methods = "SEPA,Bank transfer".to_string();
796 let payload = Payload::Order(SmallOrder::new(
797 Some(uuid),
798 Some(Kind::Sell),
799 Some(Status::Pending),
800 100,
801 "eur".to_string(),
802 None,
803 None,
804 100,
805 payment_methods,
806 1,
807 None,
808 None,
809 None,
810 Some(1627371434),
811 None,
812 ));
813
814 let test_message = Message::Order(MessageKind::new(
815 Some(uuid),
816 Some(1),
817 Some(2),
818 Action::NewOrder,
819 Some(payload),
820 ));
821 let test_message_json = test_message.as_json().unwrap();
822 let sample_message = r#"{"order":{"version":1,"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}}}}"#;
823 let message = Message::from_json(sample_message).unwrap();
824 assert!(message.verify());
825 let message_json = message.as_json().unwrap();
826 assert_eq!(message_json, test_message_json);
827 }
828
829 #[test]
830 fn test_payment_request_payload_message() {
831 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
832 let test_message = Message::Order(MessageKind::new(
833 Some(uuid),
834 Some(1),
835 Some(3),
836 Action::PayInvoice,
837 Some(Payload::PaymentRequest(
838 Some(SmallOrder::new(
839 Some(uuid),
840 Some(Kind::Sell),
841 Some(Status::WaitingPayment),
842 100,
843 "eur".to_string(),
844 None,
845 None,
846 100,
847 "Face to face".to_string(),
848 1,
849 None,
850 None,
851 None,
852 Some(1627371434),
853 None,
854 )),
855 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
856 None,
857 )),
858 ));
859 let sample_message = r#"{"order":{"version":1,"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]}}}"#;
860 let message = Message::from_json(sample_message).unwrap();
861 assert!(message.verify());
862 let message_json = message.as_json().unwrap();
863 let test_message_json = test_message.as_json().unwrap();
864 assert_eq!(message_json, test_message_json);
865 }
866
867 #[test]
868 fn test_message_payload_signature() {
869 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
870 let peer = Peer::new(
871 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
872 None,
873 );
874 let payload = Payload::Peer(peer);
875 let test_message = Message::Order(MessageKind::new(
876 Some(uuid),
877 Some(1),
878 Some(2),
879 Action::FiatSentOk,
880 Some(payload),
881 ));
882 assert!(test_message.verify());
883 let test_message_json = test_message.as_json().unwrap();
884 let trade_keys =
886 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
887 .unwrap();
888 let sig = Message::sign(test_message_json.clone(), &trade_keys);
889
890 assert!(Message::verify_signature(
891 test_message_json,
892 trade_keys.public_key(),
893 sig
894 ));
895 }
896
897 #[test]
898 fn test_cant_do_message_serialization() {
899 let reasons = vec![
901 CantDoReason::InvalidSignature,
902 CantDoReason::InvalidTradeIndex,
903 CantDoReason::InvalidAmount,
904 CantDoReason::InvalidInvoice,
905 CantDoReason::InvalidPaymentRequest,
906 CantDoReason::InvalidPeer,
907 CantDoReason::InvalidRating,
908 CantDoReason::InvalidTextMessage,
909 CantDoReason::InvalidOrderStatus,
910 CantDoReason::InvalidPubkey,
911 CantDoReason::InvalidParameters,
912 CantDoReason::OrderAlreadyCanceled,
913 CantDoReason::CantCreateUser,
914 CantDoReason::IsNotYourOrder,
915 CantDoReason::NotAllowedByStatus,
916 CantDoReason::OutOfRangeFiatAmount,
917 CantDoReason::OutOfRangeSatsAmount,
918 CantDoReason::IsNotYourDispute,
919 CantDoReason::NotFound,
920 CantDoReason::InvalidFiatCurrency,
921 CantDoReason::TooManyRequests,
922 ];
923
924 for reason in reasons {
925 let cant_do = Message::CantDo(MessageKind::new(
926 None,
927 None,
928 None,
929 Action::CantDo,
930 Some(Payload::CantDo(Some(reason.clone()))),
931 ));
932 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
933 assert!(message.verify());
934 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
935 }
936
937 let cant_do = Message::CantDo(MessageKind::new(
939 None,
940 None,
941 None,
942 Action::CantDo,
943 Some(Payload::CantDo(None)),
944 ));
945 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
946 assert!(message.verify());
947 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
948 }
949
950 #[test]
953 fn test_check_fiat_amount_valid() {
954 let order = SmallOrder::new(
956 None,
957 None,
958 None,
959 100,
960 "VES".to_string(),
961 None,
962 None,
963 500,
964 "Bank".to_string(),
965 1,
966 None,
967 None,
968 None,
969 None,
970 None,
971 );
972 assert!(order.check_fiat_amount().is_ok());
973 }
974
975 #[test]
976 fn test_check_fiat_amount_zero() {
977 let order = SmallOrder::new(
978 None,
979 None,
980 None,
981 100,
982 "VES".to_string(),
983 None,
984 None,
985 0,
986 "Bank".to_string(),
987 1,
988 None,
989 None,
990 None,
991 None,
992 None,
993 );
994 let result = order.check_fiat_amount();
995 assert!(result.is_err());
996 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
997 }
998
999 #[test]
1000 fn test_check_fiat_amount_negative() {
1001 let order = SmallOrder::new(
1002 None,
1003 None,
1004 None,
1005 100,
1006 "VES".to_string(),
1007 None,
1008 None,
1009 -100,
1010 "Bank".to_string(),
1011 1,
1012 None,
1013 None,
1014 None,
1015 None,
1016 None,
1017 );
1018 let result = order.check_fiat_amount();
1019 assert!(result.is_err());
1020 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1021 }
1022
1023 #[test]
1026 fn test_check_amount_valid() {
1027 let order = SmallOrder::new(
1029 None,
1030 None,
1031 None,
1032 100000,
1033 "VES".to_string(),
1034 None,
1035 None,
1036 500,
1037 "Bank".to_string(),
1038 0,
1039 None,
1040 None,
1041 None,
1042 None,
1043 None,
1044 );
1045 assert!(order.check_amount().is_ok());
1046 }
1047
1048 #[test]
1049 fn test_check_amount_zero() {
1050 let order = SmallOrder::new(
1052 None,
1053 None,
1054 None,
1055 0,
1056 "VES".to_string(),
1057 None,
1058 None,
1059 500,
1060 "Bank".to_string(),
1061 0,
1062 None,
1063 None,
1064 None,
1065 None,
1066 None,
1067 );
1068 assert!(order.check_amount().is_ok());
1069 }
1070
1071 #[test]
1072 fn test_check_amount_negative() {
1073 let order = SmallOrder::new(
1075 None,
1076 None,
1077 None,
1078 -1000,
1079 "VES".to_string(),
1080 None,
1081 None,
1082 500,
1083 "Bank".to_string(),
1084 0,
1085 None,
1086 None,
1087 None,
1088 None,
1089 None,
1090 );
1091 let result = order.check_amount();
1092 assert!(result.is_err());
1093 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1094 }
1095}