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 pub cashu_mint_url: Option<String>,
254 pub cashu_escrow_token: Option<String>,
257 pub cashu_escrow_locked_at: Option<i64>,
260}
261
262impl From<SmallOrder> for Order {
263 fn from(small_order: SmallOrder) -> Self {
264 Self {
265 id: Uuid::new_v4(),
266 kind: small_order
268 .kind
269 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
270 status: small_order
271 .status
272 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
273 amount: small_order.amount,
274 fiat_code: small_order.fiat_code,
275 min_amount: small_order.min_amount,
276 max_amount: small_order.max_amount,
277 fiat_amount: small_order.fiat_amount,
278 payment_method: small_order.payment_method,
279 premium: small_order.premium,
280 event_id: String::new(),
281 creator_pubkey: String::new(),
282 price_from_api: false,
283 fee: 0,
284 routing_fee: 0,
285 dev_fee: 0,
286 dev_fee_paid: false,
287 dev_fee_payment_hash: None,
288 invoice_held_at: 0,
289 taken_at: 0,
290 created_at: small_order.created_at.unwrap_or(0),
291 expires_at: small_order.expires_at.unwrap_or(0),
292 payment_attempts: 0,
293 ..Default::default()
294 }
295 }
296}
297
298impl Order {
299 pub fn as_new_order(&self) -> SmallOrder {
305 SmallOrder::new(
306 Some(self.id),
307 Some(Kind::from_str(&self.kind).unwrap()),
308 Some(Status::from_str(&self.status).unwrap()),
309 self.amount,
310 self.fiat_code.clone(),
311 self.min_amount,
312 self.max_amount,
313 self.fiat_amount,
314 self.payment_method.clone(),
315 self.premium,
316 None,
317 None,
318 self.buyer_invoice.clone(),
319 Some(self.created_at),
320 Some(self.expires_at),
321 )
322 }
323 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
328 if let Ok(kind) = Kind::from_str(&self.kind) {
329 Ok(kind)
330 } else {
331 Err(ServiceError::InvalidOrderKind)
332 }
333 }
334
335 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
340 if let Ok(status) = Status::from_str(&self.status) {
341 Ok(status)
342 } else {
343 Err(ServiceError::InvalidOrderStatus)
344 }
345 }
346
347 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
352 match Status::from_str(&self.status) {
353 Ok(s) => match s == status {
354 true => Ok(()),
355 false => Err(CantDoReason::InvalidOrderStatus),
356 },
357 Err(_) => Err(CantDoReason::InvalidOrderStatus),
358 }
359 }
360
361 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
363 if self.kind != Kind::Buy.to_string() {
364 return Err(CantDoReason::InvalidOrderKind);
365 }
366 Ok(())
367 }
368 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
370 if self.kind != Kind::Sell.to_string() {
371 return Err(CantDoReason::InvalidOrderKind);
372 }
373 Ok(())
374 }
375
376 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
380 let sender = sender.to_string();
381 if self.creator_pubkey != sender {
382 return Err(CantDoReason::InvalidPubkey);
383 }
384 Ok(())
385 }
386
387 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
392 let sender = sender.to_string();
393 if self.creator_pubkey == sender {
394 return Err(CantDoReason::InvalidPubkey);
395 }
396 Ok(())
397 }
398
399 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
401 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
402 Ok(pk) => Ok(pk),
403 Err(_) => Err(ServiceError::InvalidPubkey),
404 }
405 }
406
407 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
412 if let Some(pk) = self.buyer_pubkey.as_ref() {
413 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
414 } else {
415 Err(ServiceError::InvalidPubkey)
416 }
417 }
418 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
423 if let Some(pk) = self.seller_pubkey.as_ref() {
424 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
425 } else {
426 Err(ServiceError::InvalidPubkey)
427 }
428 }
429 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
431 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
432 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
433 } else {
434 Err(ServiceError::InvalidPubkey)
435 }
436 }
437 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
439 if let Some(pk) = self.master_seller_pubkey.as_ref() {
440 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
441 } else {
442 Err(ServiceError::InvalidPubkey)
443 }
444 }
445
446 pub fn is_range_order(&self) -> bool {
449 self.min_amount.is_some() && self.max_amount.is_some()
450 }
451
452 pub fn count_failed_payment(&mut self, retries_number: i64) {
458 if !self.failed_payment {
459 self.failed_payment = true;
460 self.payment_attempts = 1;
461 } else if self.payment_attempts < retries_number {
462 self.payment_attempts += 1;
463 }
464 }
465
466 pub fn has_no_amount(&self) -> bool {
469 self.amount == 0
470 }
471
472 pub fn set_timestamp_now(&mut self) {
474 self.taken_at = Timestamp::now().as_secs() as i64
475 }
476
477 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
486 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
487
488 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
490 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
491
492 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
494 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
495 }
496
497 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
499 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
500 }
501
502 Ok((normal_buyer_idkey, normal_seller_idkey))
503 }
504 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
512 let is_seller_dispute = !is_buyer_dispute;
514
515 let mut update_seller_dispute = false;
517 let mut update_buyer_dispute = false;
518
519 if is_seller_dispute && !self.seller_dispute {
520 update_seller_dispute = true;
521 self.seller_dispute = update_seller_dispute;
522 } else if is_buyer_dispute && !self.buyer_dispute {
523 update_buyer_dispute = true;
524 self.buyer_dispute = update_buyer_dispute;
525 };
526 self.status = Status::Dispute.to_string();
528
529 if !update_buyer_dispute && !update_seller_dispute {
532 return Err(CantDoReason::DisputeCreationError);
533 }
534
535 Ok(())
536 }
537}
538
539#[derive(Debug, Default, Deserialize, Serialize, Clone)]
548#[serde(deny_unknown_fields)]
549pub struct SmallOrder {
550 #[serde(skip_serializing_if = "Option::is_none")]
552 pub id: Option<Uuid>,
553 pub kind: Option<Kind>,
555 pub status: Option<Status>,
557 pub amount: i64,
560 pub fiat_code: String,
562 pub min_amount: Option<i64>,
564 pub max_amount: Option<i64>,
566 pub fiat_amount: i64,
568 pub payment_method: String,
570 pub premium: i64,
572 #[serde(skip_serializing_if = "Option::is_none")]
574 pub buyer_trade_pubkey: Option<String>,
575 #[serde(skip_serializing_if = "Option::is_none")]
577 pub seller_trade_pubkey: Option<String>,
578 #[serde(skip_serializing_if = "Option::is_none")]
580 pub buyer_invoice: Option<String>,
581 pub created_at: Option<i64>,
583 pub expires_at: Option<i64>,
585}
586
587#[allow(dead_code)]
588impl SmallOrder {
589 #[allow(clippy::too_many_arguments)]
591 pub fn new(
592 id: Option<Uuid>,
593 kind: Option<Kind>,
594 status: Option<Status>,
595 amount: i64,
596 fiat_code: String,
597 min_amount: Option<i64>,
598 max_amount: Option<i64>,
599 fiat_amount: i64,
600 payment_method: String,
601 premium: i64,
602 buyer_trade_pubkey: Option<String>,
603 seller_trade_pubkey: Option<String>,
604 buyer_invoice: Option<String>,
605 created_at: Option<i64>,
606 expires_at: Option<i64>,
607 ) -> Self {
608 Self {
609 id,
610 kind,
611 status,
612 amount,
613 fiat_code,
614 min_amount,
615 max_amount,
616 fiat_amount,
617 payment_method,
618 premium,
619 buyer_trade_pubkey,
620 seller_trade_pubkey,
621 buyer_invoice,
622 created_at,
623 expires_at,
624 }
625 }
626 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
628 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
629 }
630
631 pub fn as_json(&self) -> Result<String, ServiceError> {
633 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
634 }
635
636 pub fn sats_amount(&self) -> String {
639 if self.amount == 0 {
640 "Market price".to_string()
641 } else {
642 self.amount.to_string()
643 }
644 }
645 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
649 if self.fiat_amount <= 0 {
650 return Err(CantDoReason::InvalidAmount);
651 }
652 Ok(())
653 }
654
655 pub fn check_amount(&self) -> Result<(), CantDoReason> {
662 if self.amount < 0 {
663 return Err(CantDoReason::InvalidAmount);
664 }
665 Ok(())
666 }
667
668 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
674 let premium = (self.premium != 0).then_some(self.premium);
675 let sats_amount = (self.amount != 0).then_some(self.amount);
676
677 if premium.is_some() && sats_amount.is_some() {
678 return Err(CantDoReason::InvalidParameters);
679 }
680 Ok(())
681 }
682
683 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
691 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
693 if min < 0 || max < 0 {
694 return Err(CantDoReason::InvalidAmount);
695 }
696 if min >= max {
697 return Err(CantDoReason::InvalidAmount);
698 }
699 if self.amount != 0 {
700 return Err(CantDoReason::InvalidAmount);
701 }
702 amounts.clear();
703 amounts.push(min);
704 amounts.push(max);
705 }
706 Ok(())
707 }
708
709 pub fn check_fiat_currency(
716 &self,
717 fiat_currencies_accepted: &[String],
718 ) -> Result<(), CantDoReason> {
719 if !fiat_currencies_accepted.contains(&self.fiat_code)
720 && !fiat_currencies_accepted.is_empty()
721 {
722 return Err(CantDoReason::InvalidFiatCurrency);
723 }
724 Ok(())
725 }
726}
727
728impl From<Order> for SmallOrder {
729 fn from(order: Order) -> Self {
730 let id = Some(order.id);
731 let kind = Kind::from_str(&order.kind).unwrap();
732 let status = Status::from_str(&order.status).unwrap();
733 let amount = order.amount;
734 let fiat_code = order.fiat_code.clone();
735 let min_amount = order.min_amount;
736 let max_amount = order.max_amount;
737 let fiat_amount = order.fiat_amount;
738 let payment_method = order.payment_method.clone();
739 let premium = order.premium;
740 let buyer_trade_pubkey = order.buyer_pubkey.clone();
741 let seller_trade_pubkey = order.seller_pubkey.clone();
742 let buyer_invoice = order.buyer_invoice.clone();
743
744 Self {
745 id,
746 kind: Some(kind),
747 status: Some(status),
748 amount,
749 fiat_code,
750 min_amount,
751 max_amount,
752 fiat_amount,
753 payment_method,
754 premium,
755 buyer_trade_pubkey,
756 seller_trade_pubkey,
757 buyer_invoice,
758 created_at: Some(order.created_at),
759 expires_at: Some(order.expires_at),
760 }
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use super::*;
767 use crate::error::CantDoReason;
768 use nostr_sdk::Keys;
769 use uuid::uuid;
770
771 #[test]
772 fn test_status_string() {
773 assert_eq!(Status::Active.to_string(), "active");
774 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
775 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
776 assert_ne!(Status::Pending.to_string(), "Pending");
777 }
778
779 #[test]
780 fn test_status_waiting_taker_bond_roundtrip() {
781 assert_eq!(Status::WaitingTakerBond.to_string(), "waiting-taker-bond");
782 assert_eq!(
783 Status::from_str("waiting-taker-bond").unwrap(),
784 Status::WaitingTakerBond
785 );
786 let json = serde_json::to_string(&Status::WaitingTakerBond).unwrap();
788 assert_eq!(json, "\"waiting-taker-bond\"");
789 let back: Status = serde_json::from_str(&json).unwrap();
790 assert_eq!(back, Status::WaitingTakerBond);
791 }
792
793 #[test]
794 fn test_kind_string() {
795 assert_ne!(Kind::Sell.to_string(), "active");
796 assert_eq!(Kind::Sell.to_string(), "sell");
797 assert_eq!(Kind::Buy.to_string(), "buy");
798 assert_ne!(Kind::Buy.to_string(), "active");
799 }
800
801 #[test]
802 fn test_order_message() {
803 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
804 let payment_methods = "SEPA,Bank transfer".to_string();
805 let payload = Payload::Order(SmallOrder::new(
806 Some(uuid),
807 Some(Kind::Sell),
808 Some(Status::Pending),
809 100,
810 "eur".to_string(),
811 None,
812 None,
813 100,
814 payment_methods,
815 1,
816 None,
817 None,
818 None,
819 Some(1627371434),
820 None,
821 ));
822
823 let test_message = Message::Order(MessageKind::new(
824 Some(uuid),
825 Some(1),
826 Some(2),
827 Action::NewOrder,
828 Some(payload),
829 ));
830 let test_message_json = test_message.as_json().unwrap();
831 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}}}}"#;
832 let message = Message::from_json(sample_message).unwrap();
833 assert!(message.verify());
834 let message_json = message.as_json().unwrap();
835 assert_eq!(message_json, test_message_json);
836 }
837
838 #[test]
839 fn test_payment_request_payload_message() {
840 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
841 let test_message = Message::Order(MessageKind::new(
842 Some(uuid),
843 Some(1),
844 Some(3),
845 Action::PayInvoice,
846 Some(Payload::PaymentRequest(
847 Some(SmallOrder::new(
848 Some(uuid),
849 Some(Kind::Sell),
850 Some(Status::WaitingPayment),
851 100,
852 "eur".to_string(),
853 None,
854 None,
855 100,
856 "Face to face".to_string(),
857 1,
858 None,
859 None,
860 None,
861 Some(1627371434),
862 None,
863 )),
864 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
865 None,
866 )),
867 ));
868 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]}}}"#;
869 let message = Message::from_json(sample_message).unwrap();
870 assert!(message.verify());
871 let message_json = message.as_json().unwrap();
872 let test_message_json = test_message.as_json().unwrap();
873 assert_eq!(message_json, test_message_json);
874 }
875
876 #[test]
877 fn test_message_payload_signature() {
878 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
879 let peer = Peer::new(
880 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
881 None,
882 );
883 let payload = Payload::Peer(peer);
884 let test_message = Message::Order(MessageKind::new(
885 Some(uuid),
886 Some(1),
887 Some(2),
888 Action::FiatSentOk,
889 Some(payload),
890 ));
891 assert!(test_message.verify());
892 let test_message_json = test_message.as_json().unwrap();
893 let trade_keys =
895 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
896 .unwrap();
897 let sig = Message::sign(test_message_json.clone(), &trade_keys);
898
899 assert!(Message::verify_signature(
900 test_message_json,
901 trade_keys.public_key(),
902 sig
903 ));
904 }
905
906 #[test]
907 fn test_cant_do_message_serialization() {
908 let reasons = vec![
910 CantDoReason::InvalidSignature,
911 CantDoReason::InvalidTradeIndex,
912 CantDoReason::InvalidAmount,
913 CantDoReason::InvalidInvoice,
914 CantDoReason::InvalidPaymentRequest,
915 CantDoReason::InvalidPeer,
916 CantDoReason::InvalidRating,
917 CantDoReason::InvalidTextMessage,
918 CantDoReason::InvalidOrderStatus,
919 CantDoReason::InvalidPubkey,
920 CantDoReason::InvalidParameters,
921 CantDoReason::OrderAlreadyCanceled,
922 CantDoReason::CantCreateUser,
923 CantDoReason::IsNotYourOrder,
924 CantDoReason::NotAllowedByStatus,
925 CantDoReason::OutOfRangeFiatAmount,
926 CantDoReason::OutOfRangeSatsAmount,
927 CantDoReason::IsNotYourDispute,
928 CantDoReason::NotFound,
929 CantDoReason::InvalidFiatCurrency,
930 CantDoReason::TooManyRequests,
931 ];
932
933 for reason in reasons {
934 let cant_do = Message::CantDo(MessageKind::new(
935 None,
936 None,
937 None,
938 Action::CantDo,
939 Some(Payload::CantDo(Some(reason.clone()))),
940 ));
941 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
942 assert!(message.verify());
943 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
944 }
945
946 let cant_do = Message::CantDo(MessageKind::new(
948 None,
949 None,
950 None,
951 Action::CantDo,
952 Some(Payload::CantDo(None)),
953 ));
954 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
955 assert!(message.verify());
956 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
957 }
958
959 #[test]
962 fn test_check_fiat_amount_valid() {
963 let order = SmallOrder::new(
965 None,
966 None,
967 None,
968 100,
969 "VES".to_string(),
970 None,
971 None,
972 500,
973 "Bank".to_string(),
974 1,
975 None,
976 None,
977 None,
978 None,
979 None,
980 );
981 assert!(order.check_fiat_amount().is_ok());
982 }
983
984 #[test]
985 fn test_check_fiat_amount_zero() {
986 let order = SmallOrder::new(
987 None,
988 None,
989 None,
990 100,
991 "VES".to_string(),
992 None,
993 None,
994 0,
995 "Bank".to_string(),
996 1,
997 None,
998 None,
999 None,
1000 None,
1001 None,
1002 );
1003 let result = order.check_fiat_amount();
1004 assert!(result.is_err());
1005 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1006 }
1007
1008 #[test]
1009 fn test_check_fiat_amount_negative() {
1010 let order = SmallOrder::new(
1011 None,
1012 None,
1013 None,
1014 100,
1015 "VES".to_string(),
1016 None,
1017 None,
1018 -100,
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]
1035 fn test_check_amount_valid() {
1036 let order = SmallOrder::new(
1038 None,
1039 None,
1040 None,
1041 100000,
1042 "VES".to_string(),
1043 None,
1044 None,
1045 500,
1046 "Bank".to_string(),
1047 0,
1048 None,
1049 None,
1050 None,
1051 None,
1052 None,
1053 );
1054 assert!(order.check_amount().is_ok());
1055 }
1056
1057 #[test]
1058 fn test_check_amount_zero() {
1059 let order = SmallOrder::new(
1061 None,
1062 None,
1063 None,
1064 0,
1065 "VES".to_string(),
1066 None,
1067 None,
1068 500,
1069 "Bank".to_string(),
1070 0,
1071 None,
1072 None,
1073 None,
1074 None,
1075 None,
1076 );
1077 assert!(order.check_amount().is_ok());
1078 }
1079
1080 #[test]
1081 fn test_check_amount_negative() {
1082 let order = SmallOrder::new(
1084 None,
1085 None,
1086 None,
1087 -1000,
1088 "VES".to_string(),
1089 None,
1090 None,
1091 500,
1092 "Bank".to_string(),
1093 0,
1094 None,
1095 None,
1096 None,
1097 None,
1098 None,
1099 );
1100 let result = order.check_amount();
1101 assert!(result.is_err());
1102 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1103 }
1104}