1use alloy_primitives::{Address, B256, U256, keccak256};
8use cow_chains::contracts::EXTENSIBLE_FALLBACK_HANDLER;
9use cow_errors::CowError;
10use cow_signing::types::UnsignedOrder;
11use cow_types::{OrderKind, TokenBalance};
12
13use super::types::{
14 BlockInfo, COMPOSABLE_COW_ADDRESS, ConditionalOrderParams, GpV2OrderStruct, IsValidResult,
15};
16
17#[must_use]
30pub fn is_composable_cow(address: Address) -> bool {
31 address == COMPOSABLE_COW_ADDRESS
32}
33
34#[must_use]
49pub fn is_extensible_fallback_handler(address: Address) -> bool {
50 address == EXTENSIBLE_FALLBACK_HANDLER
51}
52
53#[must_use]
71pub fn balance_to_string(hash: B256) -> Option<&'static str> {
72 if hash == TokenBalance::Erc20.eip712_hash() {
73 Some("erc20")
74 } else if hash == TokenBalance::External.eip712_hash() {
75 Some("external")
76 } else if hash == TokenBalance::Internal.eip712_hash() {
77 Some("internal")
78 } else {
79 None
80 }
81}
82
83#[must_use]
104pub fn kind_to_string(hash: B256) -> Option<&'static str> {
105 if hash == keccak256(b"sell" as &[u8]) {
106 Some("sell")
107 } else if hash == keccak256(b"buy" as &[u8]) {
108 Some("buy")
109 } else {
110 None
111 }
112}
113
114pub fn from_struct_to_order(s: &GpV2OrderStruct) -> Result<UnsignedOrder, CowError> {
151 let kind_str = kind_to_string(s.kind)
152 .ok_or_else(|| CowError::AppData(format!("unknown order kind hash: {}", s.kind)))?;
153 let kind = if kind_str == "sell" { OrderKind::Sell } else { OrderKind::Buy };
154 let sell_token_balance = decode_token_balance(s.sell_token_balance)?;
155 let buy_token_balance = decode_token_balance(s.buy_token_balance)?;
156 Ok(UnsignedOrder {
157 sell_token: s.sell_token,
158 buy_token: s.buy_token,
159 receiver: s.receiver,
160 sell_amount: s.sell_amount,
161 buy_amount: s.buy_amount,
162 valid_to: s.valid_to,
163 app_data: s.app_data,
164 fee_amount: s.fee_amount,
165 kind,
166 partially_fillable: s.partially_fillable,
167 sell_token_balance,
168 buy_token_balance,
169 })
170}
171
172fn decode_token_balance(hash: B256) -> Result<TokenBalance, CowError> {
174 match balance_to_string(hash) {
175 Some("erc20") => Ok(TokenBalance::Erc20),
176 Some("external") => Ok(TokenBalance::External),
177 Some("internal") => Ok(TokenBalance::Internal),
178 _ => Err(CowError::AppData(format!("unknown token-balance hash: {hash}"))),
179 }
180}
181
182#[must_use]
196pub fn default_token_formatter(address: Address, amount: U256) -> String {
197 format!("{amount}@{address}")
198}
199
200#[must_use]
218pub const fn get_is_valid_result(result: &IsValidResult) -> bool {
219 matches!(result, IsValidResult::Valid)
220}
221
222pub fn transform_data_to_struct(data: &[u8]) -> Result<ConditionalOrderParams, CowError> {
233 if data.len() < 128 {
235 return Err(CowError::AppData("data too short for ConditionalOrderParams".into()));
236 }
237 let handler = Address::from_slice(&data[12..32]);
238 let salt = B256::from_slice(&data[32..64]);
239 let data_offset = usize::try_from(U256::from_be_slice(&data[64..96]))
240 .map_err(|e| CowError::AppData(format!("invalid offset: {e}")))?;
241 let data_len = usize::try_from(U256::from_be_slice(&data[data_offset..data_offset + 32]))
242 .map_err(|e| CowError::AppData(format!("invalid data length: {e}")))?;
243 let static_input_start = data_offset + 32;
244 let static_input = data[static_input_start..static_input_start + data_len].to_vec();
245 Ok(ConditionalOrderParams { handler, salt, static_input })
246}
247
248#[must_use]
254pub fn transform_struct_to_data(params: &ConditionalOrderParams) -> String {
255 super::twap::encode_params(params)
256}
257
258#[must_use]
277pub fn create_set_domain_verifier_tx(domain: B256, verifier: Address) -> Vec<u8> {
278 let selector = &keccak256(b"setDomainVerifier(bytes32,address)" as &[u8])[..4];
281 let mut calldata = Vec::with_capacity(4 + 64);
282 calldata.extend_from_slice(selector);
283 calldata.extend_from_slice(domain.as_slice());
284 calldata.extend_from_slice(&[0u8; 12]);
286 calldata.extend_from_slice(verifier.as_slice());
287 calldata
288}
289
290#[must_use]
305pub const fn get_block_info(block_number: u64, block_timestamp: u64) -> BlockInfo {
306 BlockInfo { block_number, block_timestamp }
307}
308
309#[must_use]
327pub fn get_domain_verifier_calldata(safe: Address, domain: B256) -> Vec<u8> {
328 let selector = &keccak256(b"domainVerifiers(address,bytes32)" as &[u8])[..4];
330 let mut calldata = Vec::with_capacity(4 + 64);
331 calldata.extend_from_slice(selector);
332 calldata.extend_from_slice(&[0u8; 12]);
334 calldata.extend_from_slice(safe.as_slice());
335 calldata.extend_from_slice(domain.as_slice());
336 calldata
337}
338
339#[must_use]
355pub fn get_domain_verifier(safe: Address, domain: B256) -> Vec<u8> {
356 get_domain_verifier_calldata(safe, domain)
357}
358
359#[must_use]
381pub fn is_valid_abi(hex: &str) -> bool {
382 let stripped = hex.trim_start_matches("0x");
383 let Ok(bytes) = alloy_primitives::hex::decode(stripped) else {
384 return false;
385 };
386 if bytes.len() < 128 {
388 return false;
389 }
390 let data_len = usize::try_from(U256::from_be_slice(&bytes[96..128]));
391 let data_len_usize = data_len.map_or(usize::MAX, |v| v);
392 let min_total = 128usize.saturating_add(data_len_usize);
393 bytes.len() >= min_total
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
403 fn is_composable_cow_with_correct_address() {
404 assert!(is_composable_cow(COMPOSABLE_COW_ADDRESS));
405 }
406
407 #[test]
408 fn is_composable_cow_with_zero_address() {
409 assert!(!is_composable_cow(Address::ZERO));
410 }
411
412 #[test]
413 fn is_composable_cow_with_random_address() {
414 let addr = Address::new([0xAB; 20]);
415 assert!(!is_composable_cow(addr));
416 }
417
418 #[test]
421 fn is_extensible_fallback_handler_with_correct_address() {
422 assert!(is_extensible_fallback_handler(EXTENSIBLE_FALLBACK_HANDLER));
423 }
424
425 #[test]
426 fn is_extensible_fallback_handler_with_zero_address() {
427 assert!(!is_extensible_fallback_handler(Address::ZERO));
428 }
429
430 #[test]
431 fn is_extensible_fallback_handler_with_random_address() {
432 let addr = Address::new([0x12; 20]);
433 assert!(!is_extensible_fallback_handler(addr));
434 }
435
436 #[test]
439 fn balance_to_string_erc20() {
440 let hash = TokenBalance::Erc20.eip712_hash();
441 assert_eq!(balance_to_string(hash), Some("erc20"));
442 }
443
444 #[test]
445 fn balance_to_string_external() {
446 let hash = TokenBalance::External.eip712_hash();
447 assert_eq!(balance_to_string(hash), Some("external"));
448 }
449
450 #[test]
451 fn balance_to_string_internal() {
452 let hash = TokenBalance::Internal.eip712_hash();
453 assert_eq!(balance_to_string(hash), Some("internal"));
454 }
455
456 #[test]
457 fn balance_to_string_unknown() {
458 assert_eq!(balance_to_string(B256::ZERO), None);
459 }
460
461 #[test]
464 fn kind_to_string_sell() {
465 let hash = keccak256(b"sell");
466 assert_eq!(kind_to_string(hash), Some("sell"));
467 }
468
469 #[test]
470 fn kind_to_string_buy() {
471 let hash = keccak256(b"buy");
472 assert_eq!(kind_to_string(hash), Some("buy"));
473 }
474
475 #[test]
476 fn kind_to_string_unknown() {
477 assert_eq!(kind_to_string(B256::ZERO), None);
478 assert_eq!(kind_to_string(keccak256(b"limit")), None);
479 }
480
481 fn make_gpv2_struct(kind: &[u8], sell_bal: &[u8], buy_bal: &[u8]) -> GpV2OrderStruct {
484 GpV2OrderStruct {
485 sell_token: Address::new([0x11; 20]),
486 buy_token: Address::new([0x22; 20]),
487 receiver: Address::new([0x33; 20]),
488 sell_amount: U256::from(1000u64),
489 buy_amount: U256::from(500u64),
490 valid_to: 1_700_000_000,
491 app_data: B256::ZERO,
492 fee_amount: U256::from(10u64),
493 kind: keccak256(kind),
494 partially_fillable: false,
495 sell_token_balance: keccak256(sell_bal),
496 buy_token_balance: keccak256(buy_bal),
497 }
498 }
499
500 #[test]
501 fn from_struct_to_order_sell_erc20() {
502 let s = make_gpv2_struct(b"sell", b"erc20", b"erc20");
503 let order = from_struct_to_order(&s).unwrap();
504 assert_eq!(order.kind, OrderKind::Sell);
505 assert_eq!(order.sell_token_balance, TokenBalance::Erc20);
506 assert_eq!(order.buy_token_balance, TokenBalance::Erc20);
507 assert_eq!(order.sell_token, s.sell_token);
508 assert_eq!(order.buy_token, s.buy_token);
509 assert_eq!(order.receiver, s.receiver);
510 assert_eq!(order.sell_amount, s.sell_amount);
511 assert_eq!(order.buy_amount, s.buy_amount);
512 assert_eq!(order.valid_to, s.valid_to);
513 assert_eq!(order.fee_amount, s.fee_amount);
514 assert!(!order.partially_fillable);
515 }
516
517 #[test]
518 fn from_struct_to_order_buy_external_internal() {
519 let s = make_gpv2_struct(b"buy", b"external", b"internal");
520 let order = from_struct_to_order(&s).unwrap();
521 assert_eq!(order.kind, OrderKind::Buy);
522 assert_eq!(order.sell_token_balance, TokenBalance::External);
523 assert_eq!(order.buy_token_balance, TokenBalance::Internal);
524 }
525
526 #[test]
527 fn from_struct_to_order_unknown_kind() {
528 let s = make_gpv2_struct(b"limit", b"erc20", b"erc20");
529 let err = from_struct_to_order(&s).unwrap_err();
530 assert!(err.to_string().contains("unknown order kind hash"));
531 }
532
533 #[test]
534 fn from_struct_to_order_unknown_sell_balance() {
535 let mut s = make_gpv2_struct(b"sell", b"erc20", b"erc20");
536 s.sell_token_balance = B256::ZERO;
537 let err = from_struct_to_order(&s).unwrap_err();
538 assert!(err.to_string().contains("unknown token-balance hash"));
539 }
540
541 #[test]
542 fn from_struct_to_order_unknown_buy_balance() {
543 let mut s = make_gpv2_struct(b"sell", b"erc20", b"erc20");
544 s.buy_token_balance = B256::ZERO;
545 let err = from_struct_to_order(&s).unwrap_err();
546 assert!(err.to_string().contains("unknown token-balance hash"));
547 }
548
549 #[test]
552 fn default_token_formatter_basic() {
553 let addr = Address::ZERO;
554 let amount = U256::from(42u64);
555 let result = default_token_formatter(addr, amount);
556 assert_eq!(result, "42@0x0000000000000000000000000000000000000000");
557 }
558
559 #[test]
560 fn default_token_formatter_large_amount() {
561 let addr = Address::new([0xff; 20]);
562 let amount = U256::from(10u64).pow(U256::from(18u64));
563 let result = default_token_formatter(addr, amount);
564 assert!(result.starts_with("1000000000000000000@0x"));
565 }
566
567 #[test]
570 fn get_is_valid_result_valid() {
571 assert!(get_is_valid_result(&IsValidResult::Valid));
572 }
573
574 #[test]
575 fn get_is_valid_result_invalid() {
576 let invalid = IsValidResult::Invalid { reason: "order expired".to_owned() };
577 assert!(!get_is_valid_result(&invalid));
578 }
579
580 #[test]
581 fn get_is_valid_result_invalid_empty_reason() {
582 let invalid = IsValidResult::Invalid { reason: String::new() };
583 assert!(!get_is_valid_result(&invalid));
584 }
585
586 #[test]
589 fn get_block_info_basic() {
590 let info = get_block_info(12345, 1_700_000_000);
591 assert_eq!(info.block_number, 12345);
592 assert_eq!(info.block_timestamp, 1_700_000_000);
593 }
594
595 #[test]
596 fn get_block_info_zero() {
597 let info = get_block_info(0, 0);
598 assert_eq!(info.block_number, 0);
599 assert_eq!(info.block_timestamp, 0);
600 }
601
602 fn build_abi_encoded_params(handler: Address, salt: B256, static_input: &[u8]) -> Vec<u8> {
606 let mut data = Vec::new();
607 data.extend_from_slice(&[0u8; 12]);
609 data.extend_from_slice(handler.as_slice());
610 data.extend_from_slice(salt.as_slice());
612 let offset = U256::from(96u64);
614 data.extend_from_slice(&offset.to_be_bytes::<32>());
615 let len = U256::from(static_input.len());
617 data.extend_from_slice(&len.to_be_bytes::<32>());
618 data.extend_from_slice(static_input);
620 data
621 }
622
623 #[test]
624 fn transform_data_to_struct_roundtrip() {
625 let handler = Address::new([0xAA; 20]);
626 let salt = B256::new([0xBB; 32]);
627 let static_input = vec![1u8, 2, 3, 4, 5];
628 let encoded = build_abi_encoded_params(handler, salt, &static_input);
629
630 let params = transform_data_to_struct(&encoded).unwrap();
631 assert_eq!(params.handler, handler);
632 assert_eq!(params.salt, salt);
633 assert_eq!(params.static_input, static_input);
634 }
635
636 #[test]
637 fn transform_data_to_struct_empty_static_input() {
638 let handler = Address::ZERO;
639 let salt = B256::ZERO;
640 let encoded = build_abi_encoded_params(handler, salt, &[]);
641
642 let params = transform_data_to_struct(&encoded).unwrap();
643 assert_eq!(params.handler, handler);
644 assert_eq!(params.salt, salt);
645 assert!(params.static_input.is_empty());
646 }
647
648 #[test]
649 fn transform_data_to_struct_too_short() {
650 let data = vec![0u8; 64];
651 let err = transform_data_to_struct(&data).unwrap_err();
652 assert!(err.to_string().contains("too short"));
653 }
654
655 #[test]
658 fn transform_struct_to_data_produces_hex() {
659 let params = ConditionalOrderParams {
660 handler: Address::ZERO,
661 salt: B256::ZERO,
662 static_input: vec![],
663 };
664 let hex = transform_struct_to_data(¶ms);
665 assert!(hex.starts_with("0x"));
666 let stripped = hex.trim_start_matches("0x");
668 assert!(alloy_primitives::hex::decode(stripped).is_ok());
669 }
670
671 #[test]
674 fn is_valid_abi_with_valid_params() {
675 let params = ConditionalOrderParams {
676 handler: Address::ZERO,
677 salt: B256::ZERO,
678 static_input: vec![],
679 };
680 let hex = transform_struct_to_data(¶ms);
681 assert!(is_valid_abi(&hex));
682 }
683
684 #[test]
685 fn is_valid_abi_with_static_input() {
686 let params = ConditionalOrderParams {
687 handler: Address::new([0xAA; 20]),
688 salt: B256::new([0xBB; 32]),
689 static_input: vec![0xCC; 64],
690 };
691 let hex = transform_struct_to_data(¶ms);
692 assert!(is_valid_abi(&hex));
693 }
694
695 #[test]
696 fn is_valid_abi_too_short() {
697 assert!(!is_valid_abi("0xdeadbeef"));
698 }
699
700 #[test]
701 fn is_valid_abi_empty() {
702 assert!(!is_valid_abi(""));
703 assert!(!is_valid_abi("0x"));
704 }
705
706 #[test]
707 fn is_valid_abi_invalid_hex() {
708 assert!(!is_valid_abi("0xZZZZ"));
709 }
710
711 #[test]
712 fn is_valid_abi_without_0x_prefix() {
713 let params = ConditionalOrderParams {
714 handler: Address::ZERO,
715 salt: B256::ZERO,
716 static_input: vec![],
717 };
718 let hex = transform_struct_to_data(¶ms);
719 let stripped = hex.trim_start_matches("0x");
720 assert!(is_valid_abi(stripped));
721 }
722
723 #[test]
726 fn create_set_domain_verifier_tx_length() {
727 let calldata = create_set_domain_verifier_tx(B256::ZERO, Address::ZERO);
728 assert_eq!(calldata.len(), 68);
730 }
731
732 #[test]
733 fn create_set_domain_verifier_tx_selector() {
734 let calldata = create_set_domain_verifier_tx(B256::ZERO, Address::ZERO);
735 let expected_selector = &keccak256(b"setDomainVerifier(bytes32,address)")[..4];
736 assert_eq!(&calldata[..4], expected_selector);
737 }
738
739 #[test]
740 fn create_set_domain_verifier_tx_encodes_domain() {
741 let domain = B256::new([0xAA; 32]);
742 let calldata = create_set_domain_verifier_tx(domain, Address::ZERO);
743 assert_eq!(&calldata[4..36], domain.as_slice());
744 }
745
746 #[test]
747 fn create_set_domain_verifier_tx_encodes_verifier() {
748 let verifier = Address::new([0xBB; 20]);
749 let calldata = create_set_domain_verifier_tx(B256::ZERO, verifier);
750 assert_eq!(&calldata[36..48], &[0u8; 12]);
752 assert_eq!(&calldata[48..68], verifier.as_slice());
753 }
754
755 #[test]
758 fn get_domain_verifier_calldata_length() {
759 let calldata = get_domain_verifier_calldata(Address::ZERO, B256::ZERO);
760 assert_eq!(calldata.len(), 68);
761 }
762
763 #[test]
764 fn get_domain_verifier_calldata_selector() {
765 let calldata = get_domain_verifier_calldata(Address::ZERO, B256::ZERO);
766 let expected_selector = &keccak256(b"domainVerifiers(address,bytes32)")[..4];
767 assert_eq!(&calldata[..4], expected_selector);
768 }
769
770 #[test]
771 fn get_domain_verifier_calldata_encodes_safe() {
772 let safe = Address::new([0xCC; 20]);
773 let calldata = get_domain_verifier_calldata(safe, B256::ZERO);
774 assert_eq!(&calldata[4..16], &[0u8; 12]);
776 assert_eq!(&calldata[16..36], safe.as_slice());
777 }
778
779 #[test]
780 fn get_domain_verifier_calldata_encodes_domain() {
781 let domain = B256::new([0xDD; 32]);
782 let calldata = get_domain_verifier_calldata(Address::ZERO, domain);
783 assert_eq!(&calldata[36..68], domain.as_slice());
784 }
785
786 #[test]
789 fn get_domain_verifier_is_alias() {
790 let safe = Address::new([0x11; 20]);
791 let domain = B256::new([0x22; 32]);
792 assert_eq!(get_domain_verifier(safe, domain), get_domain_verifier_calldata(safe, domain));
793 }
794}