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 CooperativelyCanceled,
92 InProgress,
94}
95
96impl Display for Status {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self {
99 Status::Active => write!(f, "active"),
100 Status::Canceled => write!(f, "canceled"),
101 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
102 Status::SettledByAdmin => write!(f, "settled-by-admin"),
103 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
104 Status::Dispute => write!(f, "dispute"),
105 Status::Expired => write!(f, "expired"),
106 Status::FiatSent => write!(f, "fiat-sent"),
107 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
108 Status::Pending => write!(f, "pending"),
109 Status::Success => write!(f, "success"),
110 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
111 Status::WaitingPayment => write!(f, "waiting-payment"),
112 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
113 Status::InProgress => write!(f, "in-progress"),
114 }
115 }
116}
117
118impl FromStr for Status {
119 type Err = ();
120 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
122 match s.to_lowercase().as_str() {
123 "active" => std::result::Result::Ok(Self::Active),
124 "canceled" => std::result::Result::Ok(Self::Canceled),
125 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
126 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
127 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
128 "dispute" => std::result::Result::Ok(Self::Dispute),
129 "expired" => std::result::Result::Ok(Self::Expired),
130 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
131 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
132 "pending" => std::result::Result::Ok(Self::Pending),
133 "success" => std::result::Result::Ok(Self::Success),
134 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
135 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
136 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
137 "in-progress" => std::result::Result::Ok(Self::InProgress),
138 _ => Err(()),
139 }
140 }
141}
142#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
151#[derive(Debug, Default, Deserialize, Serialize, Clone)]
152pub struct Order {
153 pub id: Uuid,
155 pub kind: String,
158 pub event_id: String,
160 pub hash: Option<String>,
162 pub preimage: Option<String>,
164 pub creator_pubkey: String,
166 pub cancel_initiator_pubkey: Option<String>,
168 pub buyer_pubkey: Option<String>,
170 pub master_buyer_pubkey: Option<String>,
173 pub seller_pubkey: Option<String>,
175 pub master_seller_pubkey: Option<String>,
178 pub status: String,
180 pub price_from_api: bool,
182 pub premium: i64,
184 pub payment_method: String,
186 pub amount: i64,
189 pub min_amount: Option<i64>,
191 pub max_amount: Option<i64>,
193 pub buyer_dispute: bool,
195 pub seller_dispute: bool,
197 pub buyer_cooperativecancel: bool,
199 pub seller_cooperativecancel: bool,
201 pub fee: i64,
203 pub routing_fee: i64,
205 pub dev_fee: i64,
207 pub dev_fee_paid: bool,
209 pub dev_fee_payment_hash: Option<String>,
211 pub fiat_code: String,
213 pub fiat_amount: i64,
215 pub buyer_invoice: Option<String>,
217 pub range_parent_id: Option<Uuid>,
219 pub invoice_held_at: i64,
221 pub taken_at: i64,
223 pub created_at: i64,
225 pub buyer_sent_rate: bool,
227 pub seller_sent_rate: bool,
229 pub failed_payment: bool,
231 pub payment_attempts: i64,
233 pub expires_at: i64,
235 pub trade_index_seller: Option<i64>,
237 pub trade_index_buyer: Option<i64>,
239 pub next_trade_pubkey: Option<String>,
242 pub next_trade_index: Option<i64>,
244}
245
246impl From<SmallOrder> for Order {
247 fn from(small_order: SmallOrder) -> Self {
248 Self {
249 id: Uuid::new_v4(),
250 kind: small_order
252 .kind
253 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
254 status: small_order
255 .status
256 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
257 amount: small_order.amount,
258 fiat_code: small_order.fiat_code,
259 min_amount: small_order.min_amount,
260 max_amount: small_order.max_amount,
261 fiat_amount: small_order.fiat_amount,
262 payment_method: small_order.payment_method,
263 premium: small_order.premium,
264 event_id: String::new(),
265 creator_pubkey: String::new(),
266 price_from_api: false,
267 fee: 0,
268 routing_fee: 0,
269 dev_fee: 0,
270 dev_fee_paid: false,
271 dev_fee_payment_hash: None,
272 invoice_held_at: 0,
273 taken_at: 0,
274 created_at: small_order.created_at.unwrap_or(0),
275 expires_at: small_order.expires_at.unwrap_or(0),
276 payment_attempts: 0,
277 ..Default::default()
278 }
279 }
280}
281
282impl Order {
283 pub fn as_new_order(&self) -> SmallOrder {
289 SmallOrder::new(
290 Some(self.id),
291 Some(Kind::from_str(&self.kind).unwrap()),
292 Some(Status::from_str(&self.status).unwrap()),
293 self.amount,
294 self.fiat_code.clone(),
295 self.min_amount,
296 self.max_amount,
297 self.fiat_amount,
298 self.payment_method.clone(),
299 self.premium,
300 None,
301 None,
302 self.buyer_invoice.clone(),
303 Some(self.created_at),
304 Some(self.expires_at),
305 )
306 }
307 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
312 if let Ok(kind) = Kind::from_str(&self.kind) {
313 Ok(kind)
314 } else {
315 Err(ServiceError::InvalidOrderKind)
316 }
317 }
318
319 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
324 if let Ok(status) = Status::from_str(&self.status) {
325 Ok(status)
326 } else {
327 Err(ServiceError::InvalidOrderStatus)
328 }
329 }
330
331 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
336 match Status::from_str(&self.status) {
337 Ok(s) => match s == status {
338 true => Ok(()),
339 false => Err(CantDoReason::InvalidOrderStatus),
340 },
341 Err(_) => Err(CantDoReason::InvalidOrderStatus),
342 }
343 }
344
345 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
347 if self.kind != Kind::Buy.to_string() {
348 return Err(CantDoReason::InvalidOrderKind);
349 }
350 Ok(())
351 }
352 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
354 if self.kind != Kind::Sell.to_string() {
355 return Err(CantDoReason::InvalidOrderKind);
356 }
357 Ok(())
358 }
359
360 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
364 let sender = sender.to_string();
365 if self.creator_pubkey != sender {
366 return Err(CantDoReason::InvalidPubkey);
367 }
368 Ok(())
369 }
370
371 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
376 let sender = sender.to_string();
377 if self.creator_pubkey == sender {
378 return Err(CantDoReason::InvalidPubkey);
379 }
380 Ok(())
381 }
382
383 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
385 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
386 Ok(pk) => Ok(pk),
387 Err(_) => Err(ServiceError::InvalidPubkey),
388 }
389 }
390
391 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
396 if let Some(pk) = self.buyer_pubkey.as_ref() {
397 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
398 } else {
399 Err(ServiceError::InvalidPubkey)
400 }
401 }
402 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
407 if let Some(pk) = self.seller_pubkey.as_ref() {
408 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
409 } else {
410 Err(ServiceError::InvalidPubkey)
411 }
412 }
413 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
415 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
416 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
417 } else {
418 Err(ServiceError::InvalidPubkey)
419 }
420 }
421 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
423 if let Some(pk) = self.master_seller_pubkey.as_ref() {
424 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
425 } else {
426 Err(ServiceError::InvalidPubkey)
427 }
428 }
429
430 pub fn is_range_order(&self) -> bool {
433 self.min_amount.is_some() && self.max_amount.is_some()
434 }
435
436 pub fn count_failed_payment(&mut self, retries_number: i64) {
442 if !self.failed_payment {
443 self.failed_payment = true;
444 self.payment_attempts = 1;
445 } else if self.payment_attempts < retries_number {
446 self.payment_attempts += 1;
447 }
448 }
449
450 pub fn has_no_amount(&self) -> bool {
453 self.amount == 0
454 }
455
456 pub fn set_timestamp_now(&mut self) {
458 self.taken_at = Timestamp::now().as_secs() as i64
459 }
460
461 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
470 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
471
472 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
474 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
475
476 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
478 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
479 }
480
481 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
483 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
484 }
485
486 Ok((normal_buyer_idkey, normal_seller_idkey))
487 }
488 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
496 let is_seller_dispute = !is_buyer_dispute;
498
499 let mut update_seller_dispute = false;
501 let mut update_buyer_dispute = false;
502
503 if is_seller_dispute && !self.seller_dispute {
504 update_seller_dispute = true;
505 self.seller_dispute = update_seller_dispute;
506 } else if is_buyer_dispute && !self.buyer_dispute {
507 update_buyer_dispute = true;
508 self.buyer_dispute = update_buyer_dispute;
509 };
510 self.status = Status::Dispute.to_string();
512
513 if !update_buyer_dispute && !update_seller_dispute {
516 return Err(CantDoReason::DisputeCreationError);
517 }
518
519 Ok(())
520 }
521}
522
523#[derive(Debug, Default, Deserialize, Serialize, Clone)]
532#[serde(deny_unknown_fields)]
533pub struct SmallOrder {
534 #[serde(skip_serializing_if = "Option::is_none")]
536 pub id: Option<Uuid>,
537 pub kind: Option<Kind>,
539 pub status: Option<Status>,
541 pub amount: i64,
544 pub fiat_code: String,
546 pub min_amount: Option<i64>,
548 pub max_amount: Option<i64>,
550 pub fiat_amount: i64,
552 pub payment_method: String,
554 pub premium: i64,
556 #[serde(skip_serializing_if = "Option::is_none")]
558 pub buyer_trade_pubkey: Option<String>,
559 #[serde(skip_serializing_if = "Option::is_none")]
561 pub seller_trade_pubkey: Option<String>,
562 #[serde(skip_serializing_if = "Option::is_none")]
564 pub buyer_invoice: Option<String>,
565 pub created_at: Option<i64>,
567 pub expires_at: Option<i64>,
569}
570
571#[allow(dead_code)]
572impl SmallOrder {
573 #[allow(clippy::too_many_arguments)]
575 pub fn new(
576 id: Option<Uuid>,
577 kind: Option<Kind>,
578 status: Option<Status>,
579 amount: i64,
580 fiat_code: String,
581 min_amount: Option<i64>,
582 max_amount: Option<i64>,
583 fiat_amount: i64,
584 payment_method: String,
585 premium: i64,
586 buyer_trade_pubkey: Option<String>,
587 seller_trade_pubkey: Option<String>,
588 buyer_invoice: Option<String>,
589 created_at: Option<i64>,
590 expires_at: Option<i64>,
591 ) -> Self {
592 Self {
593 id,
594 kind,
595 status,
596 amount,
597 fiat_code,
598 min_amount,
599 max_amount,
600 fiat_amount,
601 payment_method,
602 premium,
603 buyer_trade_pubkey,
604 seller_trade_pubkey,
605 buyer_invoice,
606 created_at,
607 expires_at,
608 }
609 }
610 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
612 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
613 }
614
615 pub fn as_json(&self) -> Result<String, ServiceError> {
617 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
618 }
619
620 pub fn sats_amount(&self) -> String {
623 if self.amount == 0 {
624 "Market price".to_string()
625 } else {
626 self.amount.to_string()
627 }
628 }
629 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
633 if self.fiat_amount <= 0 {
634 return Err(CantDoReason::InvalidAmount);
635 }
636 Ok(())
637 }
638
639 pub fn check_amount(&self) -> Result<(), CantDoReason> {
646 if self.amount < 0 {
647 return Err(CantDoReason::InvalidAmount);
648 }
649 Ok(())
650 }
651
652 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
658 let premium = (self.premium != 0).then_some(self.premium);
659 let sats_amount = (self.amount != 0).then_some(self.amount);
660
661 if premium.is_some() && sats_amount.is_some() {
662 return Err(CantDoReason::InvalidParameters);
663 }
664 Ok(())
665 }
666
667 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
675 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
677 if min < 0 || max < 0 {
678 return Err(CantDoReason::InvalidAmount);
679 }
680 if min >= max {
681 return Err(CantDoReason::InvalidAmount);
682 }
683 if self.amount != 0 {
684 return Err(CantDoReason::InvalidAmount);
685 }
686 amounts.clear();
687 amounts.push(min);
688 amounts.push(max);
689 }
690 Ok(())
691 }
692
693 pub fn check_fiat_currency(
700 &self,
701 fiat_currencies_accepted: &[String],
702 ) -> Result<(), CantDoReason> {
703 if !fiat_currencies_accepted.contains(&self.fiat_code)
704 && !fiat_currencies_accepted.is_empty()
705 {
706 return Err(CantDoReason::InvalidFiatCurrency);
707 }
708 Ok(())
709 }
710}
711
712impl From<Order> for SmallOrder {
713 fn from(order: Order) -> Self {
714 let id = Some(order.id);
715 let kind = Kind::from_str(&order.kind).unwrap();
716 let status = Status::from_str(&order.status).unwrap();
717 let amount = order.amount;
718 let fiat_code = order.fiat_code.clone();
719 let min_amount = order.min_amount;
720 let max_amount = order.max_amount;
721 let fiat_amount = order.fiat_amount;
722 let payment_method = order.payment_method.clone();
723 let premium = order.premium;
724 let buyer_trade_pubkey = order.buyer_pubkey.clone();
725 let seller_trade_pubkey = order.seller_pubkey.clone();
726 let buyer_invoice = order.buyer_invoice.clone();
727
728 Self {
729 id,
730 kind: Some(kind),
731 status: Some(status),
732 amount,
733 fiat_code,
734 min_amount,
735 max_amount,
736 fiat_amount,
737 payment_method,
738 premium,
739 buyer_trade_pubkey,
740 seller_trade_pubkey,
741 buyer_invoice,
742 created_at: Some(order.created_at),
743 expires_at: Some(order.expires_at),
744 }
745 }
746}
747
748#[cfg(test)]
749mod tests {
750 use super::*;
751 use crate::error::CantDoReason;
752 use nostr_sdk::Keys;
753 use uuid::uuid;
754
755 #[test]
756 fn test_status_string() {
757 assert_eq!(Status::Active.to_string(), "active");
758 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
759 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
760 assert_ne!(Status::Pending.to_string(), "Pending");
761 }
762
763 #[test]
764 fn test_kind_string() {
765 assert_ne!(Kind::Sell.to_string(), "active");
766 assert_eq!(Kind::Sell.to_string(), "sell");
767 assert_eq!(Kind::Buy.to_string(), "buy");
768 assert_ne!(Kind::Buy.to_string(), "active");
769 }
770
771 #[test]
772 fn test_order_message() {
773 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
774 let payment_methods = "SEPA,Bank transfer".to_string();
775 let payload = Payload::Order(SmallOrder::new(
776 Some(uuid),
777 Some(Kind::Sell),
778 Some(Status::Pending),
779 100,
780 "eur".to_string(),
781 None,
782 None,
783 100,
784 payment_methods,
785 1,
786 None,
787 None,
788 None,
789 Some(1627371434),
790 None,
791 ));
792
793 let test_message = Message::Order(MessageKind::new(
794 Some(uuid),
795 Some(1),
796 Some(2),
797 Action::NewOrder,
798 Some(payload),
799 ));
800 let test_message_json = test_message.as_json().unwrap();
801 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}}}}"#;
802 let message = Message::from_json(sample_message).unwrap();
803 assert!(message.verify());
804 let message_json = message.as_json().unwrap();
805 assert_eq!(message_json, test_message_json);
806 }
807
808 #[test]
809 fn test_payment_request_payload_message() {
810 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
811 let test_message = Message::Order(MessageKind::new(
812 Some(uuid),
813 Some(1),
814 Some(3),
815 Action::PayInvoice,
816 Some(Payload::PaymentRequest(
817 Some(SmallOrder::new(
818 Some(uuid),
819 Some(Kind::Sell),
820 Some(Status::WaitingPayment),
821 100,
822 "eur".to_string(),
823 None,
824 None,
825 100,
826 "Face to face".to_string(),
827 1,
828 None,
829 None,
830 None,
831 Some(1627371434),
832 None,
833 )),
834 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
835 None,
836 )),
837 ));
838 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]}}}"#;
839 let message = Message::from_json(sample_message).unwrap();
840 assert!(message.verify());
841 let message_json = message.as_json().unwrap();
842 let test_message_json = test_message.as_json().unwrap();
843 assert_eq!(message_json, test_message_json);
844 }
845
846 #[test]
847 fn test_message_payload_signature() {
848 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
849 let peer = Peer::new(
850 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
851 None,
852 );
853 let payload = Payload::Peer(peer);
854 let test_message = Message::Order(MessageKind::new(
855 Some(uuid),
856 Some(1),
857 Some(2),
858 Action::FiatSentOk,
859 Some(payload),
860 ));
861 assert!(test_message.verify());
862 let test_message_json = test_message.as_json().unwrap();
863 let trade_keys =
865 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
866 .unwrap();
867 let sig = Message::sign(test_message_json.clone(), &trade_keys);
868
869 assert!(Message::verify_signature(
870 test_message_json,
871 trade_keys.public_key(),
872 sig
873 ));
874 }
875
876 #[test]
877 fn test_cant_do_message_serialization() {
878 let reasons = vec![
880 CantDoReason::InvalidSignature,
881 CantDoReason::InvalidTradeIndex,
882 CantDoReason::InvalidAmount,
883 CantDoReason::InvalidInvoice,
884 CantDoReason::InvalidPaymentRequest,
885 CantDoReason::InvalidPeer,
886 CantDoReason::InvalidRating,
887 CantDoReason::InvalidTextMessage,
888 CantDoReason::InvalidOrderStatus,
889 CantDoReason::InvalidPubkey,
890 CantDoReason::InvalidParameters,
891 CantDoReason::OrderAlreadyCanceled,
892 CantDoReason::CantCreateUser,
893 CantDoReason::IsNotYourOrder,
894 CantDoReason::NotAllowedByStatus,
895 CantDoReason::OutOfRangeFiatAmount,
896 CantDoReason::OutOfRangeSatsAmount,
897 CantDoReason::IsNotYourDispute,
898 CantDoReason::NotFound,
899 CantDoReason::InvalidFiatCurrency,
900 CantDoReason::TooManyRequests,
901 ];
902
903 for reason in reasons {
904 let cant_do = Message::CantDo(MessageKind::new(
905 None,
906 None,
907 None,
908 Action::CantDo,
909 Some(Payload::CantDo(Some(reason.clone()))),
910 ));
911 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
912 assert!(message.verify());
913 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
914 }
915
916 let cant_do = Message::CantDo(MessageKind::new(
918 None,
919 None,
920 None,
921 Action::CantDo,
922 Some(Payload::CantDo(None)),
923 ));
924 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
925 assert!(message.verify());
926 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
927 }
928
929 #[test]
932 fn test_check_fiat_amount_valid() {
933 let order = SmallOrder::new(
935 None,
936 None,
937 None,
938 100,
939 "VES".to_string(),
940 None,
941 None,
942 500,
943 "Bank".to_string(),
944 1,
945 None,
946 None,
947 None,
948 None,
949 None,
950 );
951 assert!(order.check_fiat_amount().is_ok());
952 }
953
954 #[test]
955 fn test_check_fiat_amount_zero() {
956 let order = SmallOrder::new(
957 None,
958 None,
959 None,
960 100,
961 "VES".to_string(),
962 None,
963 None,
964 0,
965 "Bank".to_string(),
966 1,
967 None,
968 None,
969 None,
970 None,
971 None,
972 );
973 let result = order.check_fiat_amount();
974 assert!(result.is_err());
975 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
976 }
977
978 #[test]
979 fn test_check_fiat_amount_negative() {
980 let order = SmallOrder::new(
981 None,
982 None,
983 None,
984 100,
985 "VES".to_string(),
986 None,
987 None,
988 -100,
989 "Bank".to_string(),
990 1,
991 None,
992 None,
993 None,
994 None,
995 None,
996 );
997 let result = order.check_fiat_amount();
998 assert!(result.is_err());
999 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1000 }
1001
1002 #[test]
1005 fn test_check_amount_valid() {
1006 let order = SmallOrder::new(
1008 None,
1009 None,
1010 None,
1011 100000,
1012 "VES".to_string(),
1013 None,
1014 None,
1015 500,
1016 "Bank".to_string(),
1017 0,
1018 None,
1019 None,
1020 None,
1021 None,
1022 None,
1023 );
1024 assert!(order.check_amount().is_ok());
1025 }
1026
1027 #[test]
1028 fn test_check_amount_zero() {
1029 let order = SmallOrder::new(
1031 None,
1032 None,
1033 None,
1034 0,
1035 "VES".to_string(),
1036 None,
1037 None,
1038 500,
1039 "Bank".to_string(),
1040 0,
1041 None,
1042 None,
1043 None,
1044 None,
1045 None,
1046 );
1047 assert!(order.check_amount().is_ok());
1048 }
1049
1050 #[test]
1051 fn test_check_amount_negative() {
1052 let order = SmallOrder::new(
1054 None,
1055 None,
1056 None,
1057 -1000,
1058 "VES".to_string(),
1059 None,
1060 None,
1061 500,
1062 "Bank".to_string(),
1063 0,
1064 None,
1065 None,
1066 None,
1067 None,
1068 None,
1069 );
1070 let result = order.check_amount();
1071 assert!(result.is_err());
1072 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1073 }
1074}