1use crate::prelude::*;
2use nostr_sdk::{PublicKey, Timestamp};
3use serde::{Deserialize, Serialize};
4#[cfg(feature = "sqlx")]
5use sqlx::FromRow;
6#[cfg(feature = "sqlx")]
7use sqlx_crud::SqlxCrud;
8use std::{fmt::Display, str::FromStr};
9use uuid::Uuid;
10use wasm_bindgen::prelude::*;
11
12#[wasm_bindgen]
14#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
15#[serde(rename_all = "kebab-case")]
16pub enum Kind {
17 Buy,
18 Sell,
19}
20
21impl FromStr for Kind {
22 type Err = ();
23
24 fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
25 match kind.to_lowercase().as_str() {
26 "buy" => std::result::Result::Ok(Self::Buy),
27 "sell" => std::result::Result::Ok(Self::Sell),
28 _ => Err(()),
29 }
30 }
31}
32
33impl Display for Kind {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 Kind::Sell => write!(f, "sell"),
37 Kind::Buy => write!(f, "buy"),
38 }
39 }
40}
41
42#[wasm_bindgen]
44#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
45#[serde(rename_all = "kebab-case")]
46pub enum Status {
47 Active,
48 Canceled,
49 CanceledByAdmin,
50 SettledByAdmin,
51 CompletedByAdmin,
52 Dispute,
53 Expired,
54 FiatSent,
55 SettledHoldInvoice,
56 Pending,
57 Success,
58 WaitingBuyerInvoice,
59 WaitingPayment,
60 CooperativelyCanceled,
61 InProgress,
62}
63
64impl Display for Status {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 match self {
67 Status::Active => write!(f, "active"),
68 Status::Canceled => write!(f, "canceled"),
69 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
70 Status::SettledByAdmin => write!(f, "settled-by-admin"),
71 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
72 Status::Dispute => write!(f, "dispute"),
73 Status::Expired => write!(f, "expired"),
74 Status::FiatSent => write!(f, "fiat-sent"),
75 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
76 Status::Pending => write!(f, "pending"),
77 Status::Success => write!(f, "success"),
78 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
79 Status::WaitingPayment => write!(f, "waiting-payment"),
80 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
81 Status::InProgress => write!(f, "in-progress"),
82 }
83 }
84}
85
86impl FromStr for Status {
87 type Err = ();
88 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
90 match s.to_lowercase().as_str() {
91 "active" => std::result::Result::Ok(Self::Active),
92 "canceled" => std::result::Result::Ok(Self::Canceled),
93 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
94 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
95 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
96 "dispute" => std::result::Result::Ok(Self::Dispute),
97 "expired" => std::result::Result::Ok(Self::Expired),
98 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
99 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
100 "pending" => std::result::Result::Ok(Self::Pending),
101 "success" => std::result::Result::Ok(Self::Success),
102 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
103 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
104 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
105 "in-progress" => std::result::Result::Ok(Self::InProgress),
106 _ => Err(()),
107 }
108 }
109}
110#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
112#[derive(Debug, Default, Deserialize, Serialize, Clone)]
113pub struct Order {
114 pub id: Uuid,
115 pub kind: String,
116 pub event_id: String,
117 pub hash: Option<String>,
118 pub preimage: Option<String>,
119 pub creator_pubkey: String,
120 pub cancel_initiator_pubkey: Option<String>,
121 pub buyer_pubkey: Option<String>,
122 pub master_buyer_pubkey: Option<String>,
123 pub seller_pubkey: Option<String>,
124 pub master_seller_pubkey: Option<String>,
125 pub status: String,
126 pub price_from_api: bool,
127 pub premium: i64,
128 pub payment_method: String,
129 pub amount: i64,
130 pub min_amount: Option<i64>,
131 pub max_amount: Option<i64>,
132 pub buyer_dispute: bool,
133 pub seller_dispute: bool,
134 pub buyer_cooperativecancel: bool,
135 pub seller_cooperativecancel: bool,
136 pub fee: i64,
137 pub routing_fee: i64,
138 pub dev_fee: i64,
139 pub dev_fee_paid: bool,
140 pub dev_fee_payment_hash: Option<String>,
141 pub fiat_code: String,
142 pub fiat_amount: i64,
143 pub buyer_invoice: Option<String>,
144 pub range_parent_id: Option<Uuid>,
145 pub invoice_held_at: i64,
146 pub taken_at: i64,
147 pub created_at: i64,
148 pub buyer_sent_rate: bool,
149 pub seller_sent_rate: bool,
150 pub failed_payment: bool,
151 pub payment_attempts: i64,
152 pub expires_at: i64,
153 pub trade_index_seller: Option<i64>,
154 pub trade_index_buyer: Option<i64>,
155 pub next_trade_pubkey: Option<String>,
156 pub next_trade_index: Option<i64>,
157}
158
159impl From<SmallOrder> for Order {
160 fn from(small_order: SmallOrder) -> Self {
161 Self {
162 id: Uuid::new_v4(),
163 kind: small_order
165 .kind
166 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
167 status: small_order
168 .status
169 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
170 amount: small_order.amount,
171 fiat_code: small_order.fiat_code,
172 min_amount: small_order.min_amount,
173 max_amount: small_order.max_amount,
174 fiat_amount: small_order.fiat_amount,
175 payment_method: small_order.payment_method,
176 premium: small_order.premium,
177 event_id: String::new(),
178 creator_pubkey: String::new(),
179 price_from_api: false,
180 fee: 0,
181 routing_fee: 0,
182 dev_fee: 0,
183 dev_fee_paid: false,
184 dev_fee_payment_hash: None,
185 invoice_held_at: 0,
186 taken_at: 0,
187 created_at: small_order.created_at.unwrap_or(0),
188 expires_at: small_order.expires_at.unwrap_or(0),
189 payment_attempts: 0,
190 ..Default::default()
191 }
192 }
193}
194
195impl Order {
196 pub fn as_new_order(&self) -> SmallOrder {
198 SmallOrder::new(
199 Some(self.id),
200 Some(Kind::from_str(&self.kind).unwrap()),
201 Some(Status::from_str(&self.status).unwrap()),
202 self.amount,
203 self.fiat_code.clone(),
204 self.min_amount,
205 self.max_amount,
206 self.fiat_amount,
207 self.payment_method.clone(),
208 self.premium,
209 None,
210 None,
211 self.buyer_invoice.clone(),
212 Some(self.created_at),
213 Some(self.expires_at),
214 )
215 }
216 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
218 if let Ok(kind) = Kind::from_str(&self.kind) {
219 Ok(kind)
220 } else {
221 Err(ServiceError::InvalidOrderKind)
222 }
223 }
224
225 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
227 if let Ok(status) = Status::from_str(&self.status) {
228 Ok(status)
229 } else {
230 Err(ServiceError::InvalidOrderStatus)
231 }
232 }
233
234 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
236 match Status::from_str(&self.status) {
237 Ok(s) => match s == status {
238 true => Ok(()),
239 false => Err(CantDoReason::InvalidOrderStatus),
240 },
241 Err(_) => Err(CantDoReason::InvalidOrderStatus),
242 }
243 }
244
245 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
247 if self.kind != Kind::Buy.to_string() {
248 return Err(CantDoReason::InvalidOrderKind);
249 }
250 Ok(())
251 }
252 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
254 if self.kind != Kind::Sell.to_string() {
255 return Err(CantDoReason::InvalidOrderKind);
256 }
257 Ok(())
258 }
259
260 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
262 let sender = sender.to_string();
263 if self.creator_pubkey != sender {
264 return Err(CantDoReason::InvalidPubkey);
265 }
266 Ok(())
267 }
268
269 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
271 let sender = sender.to_string();
272 if self.creator_pubkey == sender {
273 return Err(CantDoReason::InvalidPubkey);
274 }
275 Ok(())
276 }
277
278 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
280 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
281 Ok(pk) => Ok(pk),
282 Err(_) => Err(ServiceError::InvalidPubkey),
283 }
284 }
285
286 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
288 if let Some(pk) = self.buyer_pubkey.as_ref() {
289 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
290 } else {
291 Err(ServiceError::InvalidPubkey)
292 }
293 }
294 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
296 if let Some(pk) = self.seller_pubkey.as_ref() {
297 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
298 } else {
299 Err(ServiceError::InvalidPubkey)
300 }
301 }
302 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
304 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
305 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
306 } else {
307 Err(ServiceError::InvalidPubkey)
308 }
309 }
310 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
312 if let Some(pk) = self.master_seller_pubkey.as_ref() {
313 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
314 } else {
315 Err(ServiceError::InvalidPubkey)
316 }
317 }
318
319 pub fn is_range_order(&self) -> bool {
321 self.min_amount.is_some() && self.max_amount.is_some()
322 }
323
324 pub fn count_failed_payment(&mut self, retries_number: i64) {
325 if !self.failed_payment {
326 self.failed_payment = true;
327 self.payment_attempts = 1;
328 } else if self.payment_attempts < retries_number {
329 self.payment_attempts += 1;
330 }
331 }
332
333 pub fn has_no_amount(&self) -> bool {
335 self.amount == 0
336 }
337
338 pub fn set_timestamp_now(&mut self) {
340 self.taken_at = Timestamp::now().as_u64() as i64
341 }
342
343 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
347 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
348
349 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
351 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
352
353 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
355 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
356 }
357
358 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
360 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
361 }
362
363 Ok((normal_buyer_idkey, normal_seller_idkey))
364 }
365 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
370 let is_seller_dispute = !is_buyer_dispute;
372
373 let mut update_seller_dispute = false;
375 let mut update_buyer_dispute = false;
376
377 if is_seller_dispute && !self.seller_dispute {
378 update_seller_dispute = true;
379 self.seller_dispute = update_seller_dispute;
380 } else if is_buyer_dispute && !self.buyer_dispute {
381 update_buyer_dispute = true;
382 self.buyer_dispute = update_buyer_dispute;
383 };
384 self.status = Status::Dispute.to_string();
386
387 if !update_buyer_dispute && !update_seller_dispute {
390 return Err(CantDoReason::DisputeCreationError);
391 }
392
393 Ok(())
394 }
395}
396
397#[derive(Debug, Default, Deserialize, Serialize, Clone)]
399#[serde(deny_unknown_fields)]
400pub struct SmallOrder {
401 #[serde(skip_serializing_if = "Option::is_none")]
402 pub id: Option<Uuid>,
403 pub kind: Option<Kind>,
404 pub status: Option<Status>,
405 pub amount: i64,
406 pub fiat_code: String,
407 pub min_amount: Option<i64>,
408 pub max_amount: Option<i64>,
409 pub fiat_amount: i64,
410 pub payment_method: String,
411 pub premium: i64,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub buyer_trade_pubkey: Option<String>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub seller_trade_pubkey: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub buyer_invoice: Option<String>,
418 pub created_at: Option<i64>,
419 pub expires_at: Option<i64>,
420}
421
422#[allow(dead_code)]
423impl SmallOrder {
424 #[allow(clippy::too_many_arguments)]
425 pub fn new(
426 id: Option<Uuid>,
427 kind: Option<Kind>,
428 status: Option<Status>,
429 amount: i64,
430 fiat_code: String,
431 min_amount: Option<i64>,
432 max_amount: Option<i64>,
433 fiat_amount: i64,
434 payment_method: String,
435 premium: i64,
436 buyer_trade_pubkey: Option<String>,
437 seller_trade_pubkey: Option<String>,
438 buyer_invoice: Option<String>,
439 created_at: Option<i64>,
440 expires_at: Option<i64>,
441 ) -> Self {
442 Self {
443 id,
444 kind,
445 status,
446 amount,
447 fiat_code,
448 min_amount,
449 max_amount,
450 fiat_amount,
451 payment_method,
452 premium,
453 buyer_trade_pubkey,
454 seller_trade_pubkey,
455 buyer_invoice,
456 created_at,
457 expires_at,
458 }
459 }
460 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
462 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
463 }
464
465 pub fn as_json(&self) -> Result<String, ServiceError> {
467 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
468 }
469
470 pub fn sats_amount(&self) -> String {
472 if self.amount == 0 {
473 "Market price".to_string()
474 } else {
475 self.amount.to_string()
476 }
477 }
478 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
480 if self.fiat_amount <= 0 {
481 return Err(CantDoReason::InvalidAmount);
482 }
483 Ok(())
484 }
485
486 pub fn check_amount(&self) -> Result<(), CantDoReason> {
488 if self.amount < 0 {
489 return Err(CantDoReason::InvalidAmount);
490 }
491 Ok(())
492 }
493
494 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
496 let premium = (self.premium != 0).then_some(self.premium);
497 let sats_amount = (self.amount != 0).then_some(self.amount);
498
499 if premium.is_some() && sats_amount.is_some() {
500 return Err(CantDoReason::InvalidParameters);
501 }
502 Ok(())
503 }
504
505 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
507 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
509 if min < 0 || max < 0 {
510 return Err(CantDoReason::InvalidAmount);
511 }
512 if min >= max {
513 return Err(CantDoReason::InvalidAmount);
514 }
515 if self.amount != 0 {
516 return Err(CantDoReason::InvalidAmount);
517 }
518 amounts.clear();
519 amounts.push(min);
520 amounts.push(max);
521 }
522 Ok(())
523 }
524
525 pub fn check_fiat_currency(
527 &self,
528 fiat_currencies_accepted: &[String],
529 ) -> Result<(), CantDoReason> {
530 if !fiat_currencies_accepted.contains(&self.fiat_code)
531 && !fiat_currencies_accepted.is_empty()
532 {
533 return Err(CantDoReason::InvalidFiatCurrency);
534 }
535 Ok(())
536 }
537}
538
539impl From<Order> for SmallOrder {
540 fn from(order: Order) -> Self {
541 let id = Some(order.id);
542 let kind = Kind::from_str(&order.kind).unwrap();
543 let status = Status::from_str(&order.status).unwrap();
544 let amount = order.amount;
545 let fiat_code = order.fiat_code.clone();
546 let min_amount = order.min_amount;
547 let max_amount = order.max_amount;
548 let fiat_amount = order.fiat_amount;
549 let payment_method = order.payment_method.clone();
550 let premium = order.premium;
551 let buyer_trade_pubkey = order.buyer_pubkey.clone();
552 let seller_trade_pubkey = order.seller_pubkey.clone();
553 let buyer_invoice = order.buyer_invoice.clone();
554
555 Self {
556 id,
557 kind: Some(kind),
558 status: Some(status),
559 amount,
560 fiat_code,
561 min_amount,
562 max_amount,
563 fiat_amount,
564 payment_method,
565 premium,
566 buyer_trade_pubkey,
567 seller_trade_pubkey,
568 buyer_invoice,
569 created_at: Some(order.created_at),
570 expires_at: Some(order.expires_at),
571 }
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578 use crate::error::CantDoReason;
579 use nostr_sdk::Keys;
580 use uuid::uuid;
581
582 #[test]
583 fn test_status_string() {
584 assert_eq!(Status::Active.to_string(), "active");
585 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
586 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
587 assert_ne!(Status::Pending.to_string(), "Pending");
588 }
589
590 #[test]
591 fn test_kind_string() {
592 assert_ne!(Kind::Sell.to_string(), "active");
593 assert_eq!(Kind::Sell.to_string(), "sell");
594 assert_eq!(Kind::Buy.to_string(), "buy");
595 assert_ne!(Kind::Buy.to_string(), "active");
596 }
597
598 #[test]
599 fn test_order_message() {
600 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
601 let payment_methods = "SEPA,Bank transfer".to_string();
602 let payload = Payload::Order(SmallOrder::new(
603 Some(uuid),
604 Some(Kind::Sell),
605 Some(Status::Pending),
606 100,
607 "eur".to_string(),
608 None,
609 None,
610 100,
611 payment_methods,
612 1,
613 None,
614 None,
615 None,
616 Some(1627371434),
617 None,
618 ));
619
620 let test_message = Message::Order(MessageKind::new(
621 Some(uuid),
622 Some(1),
623 Some(2),
624 Action::NewOrder,
625 Some(payload),
626 ));
627 let test_message_json = test_message.as_json().unwrap();
628 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}}}}"#;
629 let message = Message::from_json(sample_message).unwrap();
630 assert!(message.verify());
631 let message_json = message.as_json().unwrap();
632 assert_eq!(message_json, test_message_json);
633 }
634
635 #[test]
636 fn test_payment_request_payload_message() {
637 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
638 let test_message = Message::Order(MessageKind::new(
639 Some(uuid),
640 Some(1),
641 Some(3),
642 Action::PayInvoice,
643 Some(Payload::PaymentRequest(
644 Some(SmallOrder::new(
645 Some(uuid),
646 Some(Kind::Sell),
647 Some(Status::WaitingPayment),
648 100,
649 "eur".to_string(),
650 None,
651 None,
652 100,
653 "Face to face".to_string(),
654 1,
655 None,
656 None,
657 None,
658 Some(1627371434),
659 None,
660 )),
661 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
662 None,
663 )),
664 ));
665 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]}}}"#;
666 let message = Message::from_json(sample_message).unwrap();
667 assert!(message.verify());
668 let message_json = message.as_json().unwrap();
669 let test_message_json = test_message.as_json().unwrap();
670 assert_eq!(message_json, test_message_json);
671 }
672
673 #[test]
674 fn test_message_payload_signature() {
675 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
676 let peer = Peer::new(
677 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
678 None,
679 );
680 let payload = Payload::Peer(peer);
681 let test_message = Message::Order(MessageKind::new(
682 Some(uuid),
683 Some(1),
684 Some(2),
685 Action::FiatSentOk,
686 Some(payload),
687 ));
688 assert!(test_message.verify());
689 let test_message_json = test_message.as_json().unwrap();
690 let trade_keys =
692 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
693 .unwrap();
694 let sig = Message::sign(test_message_json.clone(), &trade_keys);
695
696 assert!(Message::verify_signature(
697 test_message_json,
698 trade_keys.public_key(),
699 sig
700 ));
701 }
702
703 #[test]
704 fn test_cant_do_message_serialization() {
705 let reasons = vec![
707 CantDoReason::InvalidSignature,
708 CantDoReason::InvalidTradeIndex,
709 CantDoReason::InvalidAmount,
710 CantDoReason::InvalidInvoice,
711 CantDoReason::InvalidPaymentRequest,
712 CantDoReason::InvalidPeer,
713 CantDoReason::InvalidRating,
714 CantDoReason::InvalidTextMessage,
715 CantDoReason::InvalidOrderStatus,
716 CantDoReason::InvalidPubkey,
717 CantDoReason::InvalidParameters,
718 CantDoReason::OrderAlreadyCanceled,
719 CantDoReason::CantCreateUser,
720 CantDoReason::IsNotYourOrder,
721 CantDoReason::NotAllowedByStatus,
722 CantDoReason::OutOfRangeFiatAmount,
723 CantDoReason::OutOfRangeSatsAmount,
724 CantDoReason::IsNotYourDispute,
725 CantDoReason::NotFound,
726 CantDoReason::InvalidFiatCurrency,
727 CantDoReason::TooManyRequests,
728 ];
729
730 for reason in reasons {
731 let cant_do = Message::CantDo(MessageKind::new(
732 None,
733 None,
734 None,
735 Action::CantDo,
736 Some(Payload::CantDo(Some(reason.clone()))),
737 ));
738 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
739 assert!(message.verify());
740 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
741 }
742
743 let cant_do = Message::CantDo(MessageKind::new(
745 None,
746 None,
747 None,
748 Action::CantDo,
749 Some(Payload::CantDo(None)),
750 ));
751 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
752 assert!(message.verify());
753 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
754 }
755
756 #[test]
759 fn test_check_fiat_amount_valid() {
760 let order = SmallOrder::new(
762 None,
763 None,
764 None,
765 100,
766 "VES".to_string(),
767 None,
768 None,
769 500,
770 "Bank".to_string(),
771 1,
772 None,
773 None,
774 None,
775 None,
776 None,
777 );
778 assert!(order.check_fiat_amount().is_ok());
779 }
780
781 #[test]
782 fn test_check_fiat_amount_zero() {
783 let order = SmallOrder::new(
784 None,
785 None,
786 None,
787 100,
788 "VES".to_string(),
789 None,
790 None,
791 0,
792 "Bank".to_string(),
793 1,
794 None,
795 None,
796 None,
797 None,
798 None,
799 );
800 let result = order.check_fiat_amount();
801 assert!(result.is_err());
802 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
803 }
804
805 #[test]
806 fn test_check_fiat_amount_negative() {
807 let order = SmallOrder::new(
808 None,
809 None,
810 None,
811 100,
812 "VES".to_string(),
813 None,
814 None,
815 -100,
816 "Bank".to_string(),
817 1,
818 None,
819 None,
820 None,
821 None,
822 None,
823 );
824 let result = order.check_fiat_amount();
825 assert!(result.is_err());
826 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
827 }
828
829 #[test]
832 fn test_check_amount_valid() {
833 let order = SmallOrder::new(
835 None,
836 None,
837 None,
838 100000,
839 "VES".to_string(),
840 None,
841 None,
842 500,
843 "Bank".to_string(),
844 0,
845 None,
846 None,
847 None,
848 None,
849 None,
850 );
851 assert!(order.check_amount().is_ok());
852 }
853
854 #[test]
855 fn test_check_amount_zero() {
856 let order = SmallOrder::new(
858 None,
859 None,
860 None,
861 0,
862 "VES".to_string(),
863 None,
864 None,
865 500,
866 "Bank".to_string(),
867 0,
868 None,
869 None,
870 None,
871 None,
872 None,
873 );
874 assert!(order.check_amount().is_ok());
875 }
876
877 #[test]
878 fn test_check_amount_negative() {
879 let order = SmallOrder::new(
881 None,
882 None,
883 None,
884 -1000,
885 "VES".to_string(),
886 None,
887 None,
888 500,
889 "Bank".to_string(),
890 0,
891 None,
892 None,
893 None,
894 None,
895 None,
896 );
897 let result = order.check_amount();
898 assert!(result.is_err());
899 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
900 }
901}