1use std::fmt;
4
5use alloy_primitives::Address;
6use cow_types::SigningScheme;
7
8pub use cow_types::UnsignedOrder;
13
14#[derive(Debug, Clone)]
18pub struct OrderDomain {
19 pub name: &'static str,
21 pub version: &'static str,
23 pub chain_id: u64,
25 pub verifying_contract: Address,
27}
28
29impl OrderDomain {
30 #[must_use]
43 pub const fn for_chain(chain_id: u64) -> Self {
44 Self {
45 name: "Gnosis Protocol",
46 version: "v2",
47 chain_id,
48 verifying_contract: cow_chains::contracts::SETTLEMENT_CONTRACT,
49 }
50 }
51
52 #[must_use]
68 pub fn domain_separator(&self) -> alloy_primitives::B256 {
69 crate::domain_separator_from(self)
70 }
71
72 #[must_use]
85 pub const fn with_name(mut self, name: &'static str) -> Self {
86 self.name = name;
87 self
88 }
89
90 #[must_use]
102 pub const fn with_version(mut self, version: &'static str) -> Self {
103 self.version = version;
104 self
105 }
106
107 #[must_use]
120 pub const fn with_verifying_contract(mut self, contract: Address) -> Self {
121 self.verifying_contract = contract;
122 self
123 }
124
125 #[must_use]
135 pub const fn with_chain_id(mut self, chain_id: u64) -> Self {
136 self.chain_id = chain_id;
137 self
138 }
139}
140
141#[derive(Debug, Clone)]
147pub struct OrderTypedData {
148 pub domain: OrderDomain,
150 pub primary_type: &'static str,
152 pub order: UnsignedOrder,
154}
155
156impl OrderTypedData {
157 #[must_use]
170 pub const fn new(domain: OrderDomain, order: UnsignedOrder) -> Self {
171 Self { domain, primary_type: "GPv2Order.Data", order }
172 }
173
174 #[must_use]
186 pub const fn order_ref(&self) -> &UnsignedOrder {
187 &self.order
188 }
189
190 #[must_use]
196 pub const fn domain_ref(&self) -> &OrderDomain {
197 &self.domain
198 }
199
200 #[must_use]
217 pub fn signing_digest(&self) -> alloy_primitives::B256 {
218 let domain_sep = crate::domain_separator(self.domain.chain_id);
219 let o_hash = crate::order_hash(&self.order);
220 crate::signing_digest(domain_sep, o_hash)
221 }
222}
223
224#[derive(Debug, Clone)]
226pub struct SigningResult {
227 pub signature: String,
233 pub signing_scheme: SigningScheme,
235}
236
237impl SigningResult {
238 #[must_use]
249 pub fn new(signature: impl Into<String>, signing_scheme: SigningScheme) -> Self {
250 Self { signature: signature.into(), signing_scheme }
251 }
252
253 #[must_use]
266 pub const fn is_eip712(&self) -> bool {
267 matches!(self.signing_scheme, SigningScheme::Eip712)
268 }
269
270 #[must_use]
276 pub const fn is_eth_sign(&self) -> bool {
277 matches!(self.signing_scheme, SigningScheme::EthSign)
278 }
279
280 #[must_use]
290 pub const fn is_eip1271(&self) -> bool {
291 matches!(self.signing_scheme, SigningScheme::Eip1271)
292 }
293
294 #[must_use]
305 pub const fn is_presign(&self) -> bool {
306 matches!(self.signing_scheme, SigningScheme::PreSign)
307 }
308
309 #[must_use]
318 pub const fn signature_len(&self) -> usize {
319 self.signature.len()
320 }
321
322 #[must_use]
331 pub fn signature_ref(&self) -> &str {
332 &self.signature
333 }
334}
335
336impl fmt::Display for OrderDomain {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 write!(f, "domain(chain={}, contract={:#x})", self.chain_id, self.verifying_contract)
339 }
340}
341
342impl fmt::Display for OrderTypedData {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 write!(f, "typed-data(chain={}, {})", self.domain.chain_id, self.order)
345 }
346}
347
348impl fmt::Display for SigningResult {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 let sig = &self.signature;
351 let short = if sig.len() > 10 { &sig[..10] } else { sig };
352 write!(f, "sig({}, {}…)", self.signing_scheme, short)
353 }
354}
355
356#[derive(Debug, Clone)]
364pub struct SignOrderParams {
365 pub chain_id: u64,
367 pub order: UnsignedOrder,
369 pub signing_scheme: cow_types::EcdsaSigningScheme,
371}
372
373impl SignOrderParams {
374 #[must_use]
386 pub const fn new(
387 chain_id: u64,
388 order: UnsignedOrder,
389 signing_scheme: cow_types::EcdsaSigningScheme,
390 ) -> Self {
391 Self { chain_id, order, signing_scheme }
392 }
393}
394
395impl fmt::Display for SignOrderParams {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 write!(f, "sign-order(chain={}, {})", self.chain_id, self.order)
398 }
399}
400
401#[derive(Debug, Clone)]
405pub struct SignOrderCancellationParams {
406 pub chain_id: u64,
408 pub order_uid: String,
410 pub signing_scheme: cow_types::EcdsaSigningScheme,
412}
413
414impl SignOrderCancellationParams {
415 #[must_use]
427 pub fn new(
428 chain_id: u64,
429 order_uid: impl Into<String>,
430 signing_scheme: cow_types::EcdsaSigningScheme,
431 ) -> Self {
432 Self { chain_id, order_uid: order_uid.into(), signing_scheme }
433 }
434}
435
436impl fmt::Display for SignOrderCancellationParams {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 let uid = &self.order_uid;
439 let short = if uid.len() > 10 { &uid[..10] } else { uid };
440 write!(f, "cancel-sign(chain={}, uid={short}…)", self.chain_id)
441 }
442}
443
444#[derive(Debug, Clone)]
448pub struct SignOrderCancellationsParams {
449 pub chain_id: u64,
451 pub order_uids: Vec<String>,
453 pub signing_scheme: cow_types::EcdsaSigningScheme,
455}
456
457impl SignOrderCancellationsParams {
458 #[must_use]
470 pub const fn new(
471 chain_id: u64,
472 order_uids: Vec<String>,
473 signing_scheme: cow_types::EcdsaSigningScheme,
474 ) -> Self {
475 Self { chain_id, order_uids, signing_scheme }
476 }
477
478 #[must_use]
484 pub const fn count(&self) -> usize {
485 self.order_uids.len()
486 }
487}
488
489impl fmt::Display for SignOrderCancellationsParams {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491 write!(f, "cancel-signs(chain={}, count={})", self.chain_id, self.order_uids.len())
492 }
493}
494
495#[cfg(test)]
496mod tests {
497 use alloy_primitives::{B256, U256, address};
498
499 use super::*;
500 use cow_types::{EcdsaSigningScheme, OrderKind, TokenBalance};
501
502 fn default_order() -> UnsignedOrder {
503 UnsignedOrder::sell(Address::ZERO, Address::ZERO, U256::ZERO, U256::ZERO)
504 }
505
506 #[test]
509 fn sell_order_has_sell_kind() {
510 let o = UnsignedOrder::sell(
511 Address::ZERO,
512 Address::ZERO,
513 U256::from(100u64),
514 U256::from(50u64),
515 );
516 assert_eq!(o.kind, OrderKind::Sell);
517 assert!(o.is_sell());
518 assert!(!o.is_buy());
519 assert_eq!(o.sell_amount, U256::from(100u64));
520 assert_eq!(o.buy_amount, U256::from(50u64));
521 }
522
523 #[test]
524 fn buy_order_has_buy_kind() {
525 let o =
526 UnsignedOrder::buy(Address::ZERO, Address::ZERO, U256::from(100u64), U256::from(50u64));
527 assert_eq!(o.kind, OrderKind::Buy);
528 assert!(o.is_buy());
529 assert!(!o.is_sell());
530 }
531
532 #[test]
533 fn sell_order_defaults() {
534 let o = default_order();
535 assert_eq!(o.receiver, Address::ZERO);
536 assert_eq!(o.valid_to, 0);
537 assert_eq!(o.app_data, B256::ZERO);
538 assert_eq!(o.fee_amount, U256::ZERO);
539 assert!(!o.partially_fillable);
540 assert_eq!(o.sell_token_balance, TokenBalance::Erc20);
541 assert_eq!(o.buy_token_balance, TokenBalance::Erc20);
542 }
543
544 #[test]
547 fn with_receiver() {
548 let addr = address!("0000000000000000000000000000000000000001");
549 let o = default_order().with_receiver(addr);
550 assert_eq!(o.receiver, addr);
551 }
552
553 #[test]
554 fn with_valid_to() {
555 let o = default_order().with_valid_to(1_700_000_000);
556 assert_eq!(o.valid_to, 1_700_000_000);
557 }
558
559 #[test]
560 fn with_app_data() {
561 let data = B256::from([0xab; 32]);
562 let o = default_order().with_app_data(data);
563 assert_eq!(o.app_data, data);
564 }
565
566 #[test]
567 fn with_fee_amount() {
568 let o = default_order().with_fee_amount(U256::from(42u64));
569 assert_eq!(o.fee_amount, U256::from(42u64));
570 }
571
572 #[test]
573 fn with_partially_fillable() {
574 let o = default_order().with_partially_fillable();
575 assert!(o.partially_fillable);
576 assert!(o.is_partially_fillable());
577 }
578
579 #[test]
580 fn with_sell_token_balance() {
581 let o = default_order().with_sell_token_balance(TokenBalance::External);
582 assert_eq!(o.sell_token_balance, TokenBalance::External);
583 }
584
585 #[test]
586 fn with_buy_token_balance() {
587 let o = default_order().with_buy_token_balance(TokenBalance::Internal);
588 assert_eq!(o.buy_token_balance, TokenBalance::Internal);
589 }
590
591 #[test]
594 fn is_expired_boundary() {
595 let o = default_order().with_valid_to(1_000_000);
596 assert!(!o.is_expired(999_999));
597 assert!(!o.is_expired(1_000_000));
598 assert!(o.is_expired(1_000_001));
599 }
600
601 #[test]
602 fn has_custom_receiver_false_for_zero() {
603 assert!(!default_order().has_custom_receiver());
604 }
605
606 #[test]
607 fn has_custom_receiver_true_for_nonzero() {
608 let o = default_order().with_receiver(address!("0000000000000000000000000000000000000001"));
609 assert!(o.has_custom_receiver());
610 }
611
612 #[test]
613 fn has_app_data_false_for_zero() {
614 assert!(!default_order().has_app_data());
615 }
616
617 #[test]
618 fn has_app_data_true_for_nonzero() {
619 let o = default_order().with_app_data(B256::from([1u8; 32]));
620 assert!(o.has_app_data());
621 }
622
623 #[test]
624 fn has_fee_false_for_zero() {
625 assert!(!default_order().has_fee());
626 }
627
628 #[test]
629 fn has_fee_true_for_nonzero() {
630 let o = default_order().with_fee_amount(U256::from(1u64));
631 assert!(o.has_fee());
632 }
633
634 #[test]
635 fn is_partially_fillable_default_false() {
636 assert!(!default_order().is_partially_fillable());
637 }
638
639 #[test]
640 fn total_amount_sums_sell_and_buy() {
641 let o = UnsignedOrder::sell(
642 Address::ZERO,
643 Address::ZERO,
644 U256::from(100u64),
645 U256::from(50u64),
646 );
647 assert_eq!(o.total_amount(), U256::from(150u64));
648 }
649
650 #[test]
651 fn total_amount_saturates_on_overflow() {
652 let o = UnsignedOrder::sell(Address::ZERO, Address::ZERO, U256::MAX, U256::from(1u64));
653 assert_eq!(o.total_amount(), U256::MAX);
654 }
655
656 #[test]
657 fn order_hash_is_deterministic() {
658 let o = default_order();
659 let h = crate::order_hash(&o);
660 assert_eq!(h, crate::order_hash(&o));
661 assert_ne!(h, B256::ZERO);
662 }
663
664 #[test]
667 fn order_domain_for_chain() {
668 let d = OrderDomain::for_chain(1);
669 assert_eq!(d.name, "Gnosis Protocol");
670 assert_eq!(d.version, "v2");
671 assert_eq!(d.chain_id, 1);
672 }
673
674 #[test]
675 fn domain_separator_is_deterministic() {
676 let d = OrderDomain::for_chain(1);
677 let sep1 = d.domain_separator();
678 let sep2 = d.domain_separator();
679 assert_eq!(sep1, sep2);
680 assert_ne!(sep1, B256::ZERO);
681 }
682
683 #[test]
684 fn different_chains_different_separators() {
685 let s1 = OrderDomain::for_chain(1).domain_separator();
686 let s2 = OrderDomain::for_chain(100).domain_separator();
687 assert_ne!(s1, s2);
688 }
689
690 #[test]
693 fn order_typed_data_primary_type() {
694 let td = OrderTypedData::new(OrderDomain::for_chain(1), default_order());
695 assert_eq!(td.primary_type, "GPv2Order.Data");
696 }
697
698 #[test]
699 fn order_typed_data_refs() {
700 let order = default_order();
701 let td = OrderTypedData::new(OrderDomain::for_chain(1), order.clone());
702 assert_eq!(td.order_ref().kind, order.kind);
703 assert_eq!(td.domain_ref().chain_id, 1);
704 }
705
706 #[test]
707 fn signing_digest_is_deterministic_and_nonzero() {
708 let td = OrderTypedData::new(OrderDomain::for_chain(1), default_order());
709 let d1 = td.signing_digest();
710 let d2 = td.signing_digest();
711 assert_eq!(d1, d2);
712 assert_ne!(d1, B256::ZERO);
713 }
714
715 #[test]
718 fn signing_result_new() {
719 let r = SigningResult::new("0xdeadbeef", SigningScheme::Eip712);
720 assert_eq!(r.signature, "0xdeadbeef");
721 assert_eq!(r.signing_scheme, SigningScheme::Eip712);
722 }
723
724 #[test]
725 fn signing_result_scheme_checks() {
726 assert!(SigningResult::new("0x", SigningScheme::Eip712).is_eip712());
727 assert!(SigningResult::new("0x", SigningScheme::EthSign).is_eth_sign());
728 assert!(SigningResult::new("0x", SigningScheme::Eip1271).is_eip1271());
729 assert!(SigningResult::new("0x", SigningScheme::PreSign).is_presign());
730 }
731
732 #[test]
733 fn signing_result_scheme_exclusivity() {
734 let r = SigningResult::new("0x", SigningScheme::Eip712);
735 assert!(r.is_eip712());
736 assert!(!r.is_eth_sign());
737 assert!(!r.is_eip1271());
738 assert!(!r.is_presign());
739 }
740
741 #[test]
742 fn signing_result_len_and_ref() {
743 let r = SigningResult::new("0xdeadbeef", SigningScheme::Eip712);
744 assert_eq!(r.signature_len(), 10);
745 assert_eq!(r.signature_ref(), "0xdeadbeef");
746 }
747
748 #[test]
751 fn sign_order_params_new() {
752 let p = SignOrderParams::new(1, default_order(), EcdsaSigningScheme::Eip712);
753 assert_eq!(p.chain_id, 1);
754 assert_eq!(p.signing_scheme, EcdsaSigningScheme::Eip712);
755 }
756
757 #[test]
760 fn sign_order_cancellation_params_new() {
761 let p = SignOrderCancellationParams::new(1, "0xabc123", EcdsaSigningScheme::EthSign);
762 assert_eq!(p.chain_id, 1);
763 assert_eq!(p.order_uid, "0xabc123");
764 assert_eq!(p.signing_scheme, EcdsaSigningScheme::EthSign);
765 }
766
767 #[test]
770 fn sign_order_cancellations_params_count() {
771 let p = SignOrderCancellationsParams::new(
772 1,
773 vec!["a".into(), "b".into(), "c".into()],
774 EcdsaSigningScheme::Eip712,
775 );
776 assert_eq!(p.count(), 3);
777 }
778
779 #[test]
780 fn sign_order_cancellations_params_empty() {
781 let p = SignOrderCancellationsParams::new(1, vec![], EcdsaSigningScheme::Eip712);
782 assert_eq!(p.count(), 0);
783 }
784
785 #[test]
788 fn unsigned_order_display() {
789 let o = default_order();
790 let s = o.to_string();
791 assert!(s.contains("sell"), "expected 'sell' in: {s}");
792 }
793
794 #[test]
795 fn order_domain_display() {
796 let d = OrderDomain::for_chain(42);
797 let s = d.to_string();
798 assert!(s.contains("chain=42"), "expected chain=42 in: {s}");
799 }
800
801 #[test]
802 fn order_typed_data_display() {
803 let td = OrderTypedData::new(OrderDomain::for_chain(1), default_order());
804 let s = td.to_string();
805 assert!(s.contains("typed-data"), "expected typed-data in: {s}");
806 assert!(s.contains("chain=1"), "expected chain=1 in: {s}");
807 }
808
809 #[test]
810 fn signing_result_display_truncates() {
811 let r = SigningResult::new("0xdeadbeefcafe1234567890", SigningScheme::Eip712);
812 let s = r.to_string();
813 assert!(s.contains("sig("), "expected sig( in: {s}");
814 assert!(s.contains("0xdeadbee"), "expected truncated sig in: {s}");
815 }
816
817 #[test]
818 fn signing_result_display_short_sig() {
819 let r = SigningResult::new("0xab", SigningScheme::PreSign);
820 let s = r.to_string();
821 assert!(s.contains("0xab"), "expected full short sig in: {s}");
822 }
823
824 #[test]
825 fn sign_order_params_display() {
826 let p = SignOrderParams::new(100, default_order(), EcdsaSigningScheme::Eip712);
827 let s = p.to_string();
828 assert!(s.contains("sign-order"), "expected sign-order in: {s}");
829 assert!(s.contains("chain=100"), "expected chain=100 in: {s}");
830 }
831
832 #[test]
833 fn sign_order_cancellation_params_display() {
834 let p = SignOrderCancellationParams::new(1, "0x1234567890ab", EcdsaSigningScheme::Eip712);
835 let s = p.to_string();
836 assert!(s.contains("cancel-sign"), "expected cancel-sign in: {s}");
837 assert!(s.contains("chain=1"), "expected chain=1 in: {s}");
838 }
839
840 #[test]
841 fn sign_order_cancellations_params_display() {
842 let p = SignOrderCancellationsParams::new(
843 5,
844 vec!["a".into(), "b".into()],
845 EcdsaSigningScheme::Eip712,
846 );
847 let s = p.to_string();
848 assert!(s.contains("cancel-signs"), "expected cancel-signs in: {s}");
849 assert!(s.contains("count=2"), "expected count=2 in: {s}");
850 }
851}