1use crate::error::SignerError;
36use crate::ethereum::abi::{self, AbiValue};
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Operation {
43 Call = 0,
45 DelegateCall = 1,
47}
48
49#[derive(Debug, Clone)]
54pub struct SafeTransaction {
55 pub to: [u8; 20],
57 pub value: [u8; 32],
59 pub data: Vec<u8>,
61 pub operation: Operation,
63 pub safe_tx_gas: [u8; 32],
65 pub base_gas: [u8; 32],
67 pub gas_price: [u8; 32],
69 pub gas_token: [u8; 20],
71 pub refund_receiver: [u8; 20],
73 pub nonce: [u8; 32],
75}
76
77impl SafeTransaction {
78 #[must_use]
82 pub fn type_hash() -> [u8; 32] {
83 keccak256(
84 b"SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)",
85 )
86 }
87
88 #[must_use]
92 pub fn struct_hash(&self) -> [u8; 32] {
93 let data_hash = keccak256(&self.data);
94
95 let mut buf = Vec::with_capacity(11 * 32);
96 buf.extend_from_slice(&Self::type_hash());
97 buf.extend_from_slice(&pad_address(&self.to));
98 buf.extend_from_slice(&self.value);
99 buf.extend_from_slice(&data_hash);
100 buf.extend_from_slice(&pad_u8(self.operation as u8));
101 buf.extend_from_slice(&self.safe_tx_gas);
102 buf.extend_from_slice(&self.base_gas);
103 buf.extend_from_slice(&self.gas_price);
104 buf.extend_from_slice(&pad_address(&self.gas_token));
105 buf.extend_from_slice(&pad_address(&self.refund_receiver));
106 buf.extend_from_slice(&self.nonce);
107
108 keccak256(&buf)
109 }
110
111 #[must_use]
115 pub fn signing_hash(&self, domain_separator: &[u8; 32]) -> [u8; 32] {
116 let mut buf = Vec::with_capacity(2 + 32 + 32);
117 buf.push(0x19);
118 buf.push(0x01);
119 buf.extend_from_slice(domain_separator);
120 buf.extend_from_slice(&self.struct_hash());
121 keccak256(&buf)
122 }
123
124 pub fn sign(
128 &self,
129 signer: &super::EthereumSigner,
130 domain_separator: &[u8; 32],
131 ) -> Result<super::EthereumSignature, SignerError> {
132 let hash = self.signing_hash(domain_separator);
133 signer.sign_digest(&hash)
134 }
135
136 pub fn encode_exec_transaction(
141 &self,
142 signatures: &[super::EthereumSignature],
143 ) -> Result<Vec<u8>, SignerError> {
144 let packed_sigs = encode_signatures(signatures)?;
145 let func = abi::Function::new(
146 "execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)",
147 );
148 Ok(func.encode(&[
149 AbiValue::Address(self.to),
150 AbiValue::Uint256(self.value),
151 AbiValue::Bytes(self.data.clone()),
152 AbiValue::Uint256(pad_u8(self.operation as u8)),
153 AbiValue::Uint256(self.safe_tx_gas),
154 AbiValue::Uint256(self.base_gas),
155 AbiValue::Uint256(self.gas_price),
156 AbiValue::Address(self.gas_token),
157 AbiValue::Address(self.refund_receiver),
158 AbiValue::Bytes(packed_sigs),
159 ]))
160 }
161}
162
163#[must_use]
172pub fn safe_domain_separator(chain_id: u64, safe_address: &[u8; 20]) -> [u8; 32] {
173 let domain_type_hash = keccak256(b"EIP712Domain(uint256 chainId,address verifyingContract)");
174 let mut buf = Vec::with_capacity(3 * 32);
175 buf.extend_from_slice(&domain_type_hash);
176 buf.extend_from_slice(&pad_u64(chain_id));
177 buf.extend_from_slice(&pad_address(safe_address));
178 keccak256(&buf)
179}
180
181pub fn encode_signatures(signatures: &[super::EthereumSignature]) -> Result<Vec<u8>, SignerError> {
189 let mut packed = Vec::with_capacity(signatures.len() * 65);
190 for sig in signatures {
191 let v = safe_signature_v_byte(sig.v)?;
192 packed.extend_from_slice(&sig.r);
193 packed.extend_from_slice(&sig.s);
194 packed.push(v);
195 }
196 Ok(packed)
197}
198
199pub fn decode_signatures(data: &[u8]) -> Result<Vec<super::EthereumSignature>, SignerError> {
204 if data.len() % 65 != 0 {
205 return Err(SignerError::EncodingError(format!(
206 "signature data length {} is not a multiple of 65",
207 data.len()
208 )));
209 }
210 let mut sigs = Vec::with_capacity(data.len() / 65);
211 for chunk in data.chunks_exact(65) {
212 let mut r = [0u8; 32];
213 let mut s = [0u8; 32];
214 r.copy_from_slice(&chunk[..32]);
215 s.copy_from_slice(&chunk[32..64]);
216 let v = u64::from(chunk[64]);
217 sigs.push(super::EthereumSignature { r, s, v });
218 }
219 Ok(sigs)
220}
221
222#[must_use]
226pub fn encode_add_owner(owner: [u8; 20], threshold: u64) -> Vec<u8> {
227 let func = abi::Function::new("addOwnerWithThreshold(address,uint256)");
228 func.encode(&[AbiValue::Address(owner), AbiValue::from_u64(threshold)])
229}
230
231#[must_use]
236pub fn encode_remove_owner(prev_owner: [u8; 20], owner: [u8; 20], threshold: u64) -> Vec<u8> {
237 let func = abi::Function::new("removeOwner(address,address,uint256)");
238 func.encode(&[
239 AbiValue::Address(prev_owner),
240 AbiValue::Address(owner),
241 AbiValue::from_u64(threshold),
242 ])
243}
244
245#[must_use]
247pub fn encode_change_threshold(threshold: u64) -> Vec<u8> {
248 let func = abi::Function::new("changeThreshold(uint256)");
249 func.encode(&[AbiValue::from_u64(threshold)])
250}
251
252#[must_use]
254pub fn encode_swap_owner(
255 prev_owner: [u8; 20],
256 old_owner: [u8; 20],
257 new_owner: [u8; 20],
258) -> Vec<u8> {
259 let func = abi::Function::new("swapOwner(address,address,address)");
260 func.encode(&[
261 AbiValue::Address(prev_owner),
262 AbiValue::Address(old_owner),
263 AbiValue::Address(new_owner),
264 ])
265}
266
267#[must_use]
269pub fn encode_enable_module(module: [u8; 20]) -> Vec<u8> {
270 let func = abi::Function::new("enableModule(address)");
271 func.encode(&[AbiValue::Address(module)])
272}
273
274#[must_use]
276pub fn encode_disable_module(prev_module: [u8; 20], module: [u8; 20]) -> Vec<u8> {
277 let func = abi::Function::new("disableModule(address,address)");
278 func.encode(&[AbiValue::Address(prev_module), AbiValue::Address(module)])
279}
280
281#[must_use]
283pub fn encode_set_guard(guard: [u8; 20]) -> Vec<u8> {
284 let func = abi::Function::new("setGuard(address)");
285 func.encode(&[AbiValue::Address(guard)])
286}
287
288pub const SENTINEL_OWNERS: [u8; 20] = {
290 let mut a = [0u8; 20];
291 a[19] = 1;
292 a
293};
294
295#[must_use]
297pub fn encode_get_owners() -> Vec<u8> {
298 let func = abi::Function::new("getOwners()");
299 func.encode(&[])
300}
301
302#[must_use]
304pub fn encode_get_threshold() -> Vec<u8> {
305 let func = abi::Function::new("getThreshold()");
306 func.encode(&[])
307}
308
309#[must_use]
311pub fn encode_nonce() -> Vec<u8> {
312 let func = abi::Function::new("nonce()");
313 func.encode(&[])
314}
315
316#[must_use]
318pub fn encode_get_transaction_hash(tx: &SafeTransaction) -> Vec<u8> {
319 let func = abi::Function::new(
320 "getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256)",
321 );
322 func.encode(&[
323 AbiValue::Address(tx.to),
324 AbiValue::Uint256(tx.value),
325 AbiValue::Bytes(tx.data.clone()),
326 AbiValue::Uint256(pad_u8(tx.operation as u8)),
327 AbiValue::Uint256(tx.safe_tx_gas),
328 AbiValue::Uint256(tx.base_gas),
329 AbiValue::Uint256(tx.gas_price),
330 AbiValue::Address(tx.gas_token),
331 AbiValue::Address(tx.refund_receiver),
332 AbiValue::Uint256(tx.nonce),
333 ])
334}
335
336#[derive(Debug, Clone)]
343pub struct SignerSignature {
344 pub signer: [u8; 20],
346 pub signature: super::EthereumSignature,
348}
349
350pub fn sign_and_sort(
375 tx: &SafeTransaction,
376 signers: &[&super::EthereumSigner],
377 domain_separator: &[u8; 32],
378) -> Result<Vec<super::EthereumSignature>, SignerError> {
379 let mut pairs: Vec<SignerSignature> = Vec::with_capacity(signers.len());
380 for signer in signers {
381 let sig = tx.sign(signer, domain_separator)?;
382 pairs.push(SignerSignature {
383 signer: signer.address(),
384 signature: sig,
385 });
386 }
387 pairs.sort_by(|a, b| a.signer.cmp(&b.signer));
389 Ok(pairs.into_iter().map(|p| p.signature).collect())
390}
391
392pub fn encode_signatures_sorted(
397 signatures: &[super::EthereumSignature],
398 safe_tx_hash: &[u8; 32],
399) -> Result<Vec<u8>, SignerError> {
400 let mut pairs: Vec<([u8; 20], &super::EthereumSignature)> =
401 Vec::with_capacity(signatures.len());
402 for sig in signatures {
403 let addr = super::ecrecover_digest(safe_tx_hash, sig)?;
404 pairs.push((addr, sig));
405 }
406 pairs.sort_by(|a, b| a.0.cmp(&b.0));
408
409 let mut packed = Vec::with_capacity(pairs.len() * 65);
410 for (_, sig) in &pairs {
411 let v = safe_signature_v_byte(sig.v)?;
412 packed.extend_from_slice(&sig.r);
413 packed.extend_from_slice(&sig.s);
414 packed.push(v);
415 }
416 Ok(packed)
417}
418
419fn safe_signature_v_byte(v: u64) -> Result<u8, SignerError> {
420 u8::try_from(v).map_err(|_| {
421 SignerError::InvalidSignature(format!(
422 "safe signature v value {v} does not fit in one byte"
423 ))
424 })
425}
426
427#[must_use]
435pub fn encode_approve_hash(hash: &[u8; 32]) -> Vec<u8> {
436 let func = abi::Function::new("approveHash(bytes32)");
437 func.encode(&[AbiValue::Uint256(*hash)])
438}
439
440pub fn pre_validated_signature(owner: [u8; 20]) -> super::EthereumSignature {
447 super::EthereumSignature {
448 r: pad_address(&owner),
449 s: [0u8; 32],
450 v: 1,
451 }
452}
453
454#[must_use]
459pub fn encode_approved_hashes(owner: [u8; 20], hash: &[u8; 32]) -> Vec<u8> {
460 let func = abi::Function::new("approvedHashes(address,bytes32)");
461 func.encode(&[AbiValue::Address(owner), AbiValue::Uint256(*hash)])
462}
463
464pub fn contract_signature(contract_owner: [u8; 20], data_offset: u32) -> super::EthereumSignature {
476 let mut s = [0u8; 32];
477 s[28..32].copy_from_slice(&data_offset.to_be_bytes());
478 super::EthereumSignature {
479 r: pad_address(&contract_owner),
480 s,
481 v: 0,
482 }
483}
484
485#[must_use]
491pub fn encode_exec_from_module(
492 to: [u8; 20],
493 value: &[u8; 32],
494 data: &[u8],
495 operation: Operation,
496) -> Vec<u8> {
497 let func = abi::Function::new("execTransactionFromModule(address,uint256,bytes,uint8)");
498 func.encode(&[
499 AbiValue::Address(to),
500 AbiValue::Uint256(*value),
501 AbiValue::Bytes(data.to_vec()),
502 AbiValue::Uint256(pad_u8(operation as u8)),
503 ])
504}
505
506#[must_use]
510pub fn encode_exec_from_module_return_data(
511 to: [u8; 20],
512 value: &[u8; 32],
513 data: &[u8],
514 operation: Operation,
515) -> Vec<u8> {
516 let func =
517 abi::Function::new("execTransactionFromModuleReturnData(address,uint256,bytes,uint8)");
518 func.encode(&[
519 AbiValue::Address(to),
520 AbiValue::Uint256(*value),
521 AbiValue::Bytes(data.to_vec()),
522 AbiValue::Uint256(pad_u8(operation as u8)),
523 ])
524}
525
526#[must_use]
530pub fn encode_is_owner(owner: [u8; 20]) -> Vec<u8> {
531 let func = abi::Function::new("isOwner(address)");
532 func.encode(&[AbiValue::Address(owner)])
533}
534
535#[must_use]
539pub fn encode_domain_separator() -> Vec<u8> {
540 let func = abi::Function::new("domainSeparator()");
541 func.encode(&[])
542}
543
544#[must_use]
546pub fn encode_is_module_enabled(module: [u8; 20]) -> Vec<u8> {
547 let func = abi::Function::new("isModuleEnabled(address)");
548 func.encode(&[AbiValue::Address(module)])
549}
550
551#[must_use]
553pub fn encode_get_modules_paginated(start: [u8; 20], page_size: u64) -> Vec<u8> {
554 let func = abi::Function::new("getModulesPaginated(address,uint256)");
555 func.encode(&[AbiValue::Address(start), AbiValue::from_u64(page_size)])
556}
557
558#[allow(clippy::too_many_arguments)]
564pub fn encode_setup(
565 owners: &[[u8; 20]],
566 threshold: u64,
567 to: [u8; 20],
568 data: &[u8],
569 fallback_handler: [u8; 20],
570 payment_token: [u8; 20],
571 payment: u128,
572 payment_receiver: [u8; 20],
573) -> Vec<u8> {
574 let func = abi::Function::new(
575 "setup(address[],uint256,address,bytes,address,address,uint256,address)",
576 );
577 let owner_values: Vec<AbiValue> = owners.iter().map(|o| AbiValue::Address(*o)).collect();
578 func.encode(&[
579 AbiValue::Array(owner_values),
580 AbiValue::from_u64(threshold),
581 AbiValue::Address(to),
582 AbiValue::Bytes(data.to_vec()),
583 AbiValue::Address(fallback_handler),
584 AbiValue::Address(payment_token),
585 AbiValue::from_u128(payment),
586 AbiValue::Address(payment_receiver),
587 ])
588}
589
590#[must_use]
594pub fn encode_create_proxy_with_nonce(
595 singleton: [u8; 20],
596 initializer: &[u8],
597 salt_nonce: u64,
598) -> Vec<u8> {
599 let func = abi::Function::new("createProxyWithNonce(address,bytes,uint256)");
600 func.encode(&[
601 AbiValue::Address(singleton),
602 AbiValue::Bytes(initializer.to_vec()),
603 AbiValue::from_u64(salt_nonce),
604 ])
605}
606
607fn keccak256(data: &[u8]) -> [u8; 32] {
610 super::keccak256(data)
611}
612
613fn pad_address(addr: &[u8; 20]) -> [u8; 32] {
614 let mut buf = [0u8; 32];
615 buf[12..32].copy_from_slice(addr);
616 buf
617}
618
619fn pad_u8(val: u8) -> [u8; 32] {
620 let mut buf = [0u8; 32];
621 buf[31] = val;
622 buf
623}
624
625fn pad_u64(val: u64) -> [u8; 32] {
626 let mut buf = [0u8; 32];
627 buf[24..32].copy_from_slice(&val.to_be_bytes());
628 buf
629}
630
631#[cfg(test)]
634#[allow(clippy::unwrap_used, clippy::expect_used)]
635mod tests {
636 use super::*;
637 use crate::traits::KeyPair;
638
639 fn zero_tx() -> SafeTransaction {
640 SafeTransaction {
641 to: [0xBB; 20],
642 value: [0u8; 32],
643 data: vec![],
644 operation: Operation::Call,
645 safe_tx_gas: [0u8; 32],
646 base_gas: [0u8; 32],
647 gas_price: [0u8; 32],
648 gas_token: [0u8; 20],
649 refund_receiver: [0u8; 20],
650 nonce: [0u8; 32],
651 }
652 }
653
654 #[test]
657 fn test_type_hash_matches_safe_contract() {
658 let th = SafeTransaction::type_hash();
659 let expected = keccak256(
660 b"SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)",
661 );
662 assert_eq!(th, expected);
663 }
664
665 #[test]
668 fn test_struct_hash_deterministic() {
669 let tx = zero_tx();
670 assert_eq!(tx.struct_hash(), tx.struct_hash());
671 }
672
673 #[test]
674 fn test_struct_hash_changes_with_to() {
675 let tx1 = zero_tx();
676 let mut tx2 = zero_tx();
677 tx2.to = [0xCC; 20];
678 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
679 }
680
681 #[test]
682 fn test_struct_hash_changes_with_data() {
683 let tx1 = zero_tx();
684 let mut tx2 = zero_tx();
685 tx2.data = vec![0xDE, 0xAD];
686 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
687 }
688
689 #[test]
690 fn test_struct_hash_changes_with_operation() {
691 let tx1 = zero_tx();
692 let mut tx2 = zero_tx();
693 tx2.operation = Operation::DelegateCall;
694 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
695 }
696
697 #[test]
698 fn test_struct_hash_changes_with_nonce() {
699 let tx1 = zero_tx();
700 let mut tx2 = zero_tx();
701 tx2.nonce[31] = 1;
702 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
703 }
704
705 #[test]
706 fn test_struct_hash_changes_with_value() {
707 let tx1 = zero_tx();
708 let mut tx2 = zero_tx();
709 tx2.value[31] = 1;
710 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
711 }
712
713 #[test]
714 fn test_struct_hash_changes_with_gas_fields() {
715 let tx1 = zero_tx();
716 let mut tx2 = zero_tx();
717 tx2.safe_tx_gas[31] = 100;
718 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
719
720 let mut tx3 = zero_tx();
721 tx3.base_gas[31] = 50;
722 assert_ne!(tx1.struct_hash(), tx3.struct_hash());
723
724 let mut tx4 = zero_tx();
725 tx4.gas_price[31] = 10;
726 assert_ne!(tx1.struct_hash(), tx4.struct_hash());
727 }
728
729 #[test]
730 fn test_struct_hash_changes_with_gas_token() {
731 let tx1 = zero_tx();
732 let mut tx2 = zero_tx();
733 tx2.gas_token = [0xFF; 20];
734 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
735 }
736
737 #[test]
738 fn test_struct_hash_changes_with_refund_receiver() {
739 let tx1 = zero_tx();
740 let mut tx2 = zero_tx();
741 tx2.refund_receiver = [0xFF; 20];
742 assert_ne!(tx1.struct_hash(), tx2.struct_hash());
743 }
744
745 #[test]
748 fn test_domain_separator_deterministic() {
749 let ds1 = safe_domain_separator(1, &[0xAA; 20]);
750 let ds2 = safe_domain_separator(1, &[0xAA; 20]);
751 assert_eq!(ds1, ds2);
752 }
753
754 #[test]
755 fn test_domain_separator_changes_with_chain_id() {
756 let ds1 = safe_domain_separator(1, &[0xAA; 20]);
757 let ds2 = safe_domain_separator(137, &[0xAA; 20]);
758 assert_ne!(ds1, ds2);
759 }
760
761 #[test]
762 fn test_domain_separator_changes_with_address() {
763 let ds1 = safe_domain_separator(1, &[0xAA; 20]);
764 let ds2 = safe_domain_separator(1, &[0xBB; 20]);
765 assert_ne!(ds1, ds2);
766 }
767
768 #[test]
771 fn test_signing_hash_deterministic() {
772 let tx = zero_tx();
773 let domain = safe_domain_separator(1, &[0xAA; 20]);
774 assert_eq!(tx.signing_hash(&domain), tx.signing_hash(&domain));
775 }
776
777 #[test]
778 fn test_signing_hash_changes_with_domain() {
779 let tx = zero_tx();
780 let d1 = safe_domain_separator(1, &[0xAA; 20]);
781 let d2 = safe_domain_separator(5, &[0xAA; 20]);
782 assert_ne!(tx.signing_hash(&d1), tx.signing_hash(&d2));
783 }
784
785 #[test]
788 fn test_sign_produces_valid_signature() {
789 let signer = super::super::EthereumSigner::generate().unwrap();
790 let tx = zero_tx();
791 let domain = safe_domain_separator(1, &[0xAA; 20]);
792 let sig = tx.sign(&signer, &domain).unwrap();
793 assert!(sig.v == 27 || sig.v == 28);
794 assert_ne!(sig.r, [0u8; 32]);
795 assert_ne!(sig.s, [0u8; 32]);
796 }
797
798 #[test]
799 fn test_sign_recovers_correct_address() {
800 let signer = super::super::EthereumSigner::generate().unwrap();
801 let tx = zero_tx();
802 let domain = safe_domain_separator(1, &[0xAA; 20]);
803 let sig = tx.sign(&signer, &domain).unwrap();
804 let hash = tx.signing_hash(&domain);
805 let recovered = super::super::ecrecover_digest(&hash, &sig).unwrap();
806 assert_eq!(recovered, signer.address());
807 }
808
809 #[test]
812 fn test_sign_and_sort_signatures_ordered_by_address() {
813 let s1 = super::super::EthereumSigner::generate().unwrap();
814 let s2 = super::super::EthereumSigner::generate().unwrap();
815 let s3 = super::super::EthereumSigner::generate().unwrap();
816 let tx = zero_tx();
817 let domain = safe_domain_separator(1, &[0xAA; 20]);
818 let sigs = sign_and_sort(&tx, &[&s1, &s2, &s3], &domain).unwrap();
819 assert_eq!(sigs.len(), 3);
820
821 let hash = tx.signing_hash(&domain);
823 let a1 = super::super::ecrecover_digest(&hash, &sigs[0]).unwrap();
824 let a2 = super::super::ecrecover_digest(&hash, &sigs[1]).unwrap();
825 let a3 = super::super::ecrecover_digest(&hash, &sigs[2]).unwrap();
826 assert!(a1 < a2);
827 assert!(a2 < a3);
828 }
829
830 #[test]
831 fn test_sign_and_sort_single_signer() {
832 let s1 = super::super::EthereumSigner::generate().unwrap();
833 let tx = zero_tx();
834 let domain = safe_domain_separator(1, &[0xAA; 20]);
835 let sigs = sign_and_sort(&tx, &[&s1], &domain).unwrap();
836 assert_eq!(sigs.len(), 1);
837 }
838
839 #[test]
842 fn test_encode_signatures_sorted_ecrecover() {
843 let s1 = super::super::EthereumSigner::generate().unwrap();
844 let s2 = super::super::EthereumSigner::generate().unwrap();
845 let tx = zero_tx();
846 let domain = safe_domain_separator(1, &[0xAA; 20]);
847 let hash = tx.signing_hash(&domain);
848 let sig1 = tx.sign(&s1, &domain).unwrap();
849 let sig2 = tx.sign(&s2, &domain).unwrap();
850
851 let packed = encode_signatures_sorted(&[sig1, sig2], &hash).unwrap();
852 assert_eq!(packed.len(), 130);
853
854 let decoded = decode_signatures(&packed).unwrap();
856 let a1 = super::super::ecrecover_digest(&hash, &decoded[0]).unwrap();
857 let a2 = super::super::ecrecover_digest(&hash, &decoded[1]).unwrap();
858 assert!(a1 < a2);
859 }
860
861 #[test]
864 fn test_encode_signatures_empty() {
865 let packed = encode_signatures(&[]).unwrap();
866 assert!(packed.is_empty());
867 }
868
869 #[test]
870 fn test_encode_signatures_single() {
871 let sig = super::super::EthereumSignature {
872 r: [0xAA; 32],
873 s: [0xBB; 32],
874 v: 27,
875 };
876 let packed = encode_signatures(&[sig]).unwrap();
877 assert_eq!(packed.len(), 65);
878 assert_eq!(&packed[..32], &[0xAA; 32]);
879 assert_eq!(&packed[32..64], &[0xBB; 32]);
880 assert_eq!(packed[64], 27);
881 }
882
883 #[test]
884 fn test_encode_signatures_multiple() {
885 let sig1 = super::super::EthereumSignature {
886 r: [0x11; 32],
887 s: [0x22; 32],
888 v: 27,
889 };
890 let sig2 = super::super::EthereumSignature {
891 r: [0x33; 32],
892 s: [0x44; 32],
893 v: 28,
894 };
895 let packed = encode_signatures(&[sig1, sig2]).unwrap();
896 assert_eq!(packed.len(), 130);
897 assert_eq!(packed[64], 27);
898 assert_eq!(packed[129], 28);
899 }
900
901 #[test]
902 fn test_encode_signatures_rejects_large_v() {
903 let sig = super::super::EthereumSignature {
904 r: [0xAA; 32],
905 s: [0xBB; 32],
906 v: 1_000,
907 };
908 assert!(encode_signatures(&[sig]).is_err());
909 }
910
911 #[test]
914 fn test_decode_signatures_roundtrip() {
915 let sig1 = super::super::EthereumSignature {
916 r: [0xAA; 32],
917 s: [0xBB; 32],
918 v: 27,
919 };
920 let sig2 = super::super::EthereumSignature {
921 r: [0xCC; 32],
922 s: [0xDD; 32],
923 v: 28,
924 };
925 let packed = encode_signatures(&[sig1.clone(), sig2.clone()]).unwrap();
926 let decoded = decode_signatures(&packed).unwrap();
927 assert_eq!(decoded.len(), 2);
928 assert_eq!(decoded[0], sig1);
929 assert_eq!(decoded[1], sig2);
930 }
931
932 #[test]
933 fn test_decode_signatures_empty() {
934 let decoded = decode_signatures(&[]).unwrap();
935 assert!(decoded.is_empty());
936 }
937
938 #[test]
939 fn test_decode_signatures_invalid_length() {
940 assert!(decode_signatures(&[0u8; 64]).is_err());
941 assert!(decode_signatures(&[0u8; 66]).is_err());
942 }
943
944 #[test]
947 fn test_exec_transaction_has_correct_selector() {
948 let tx = zero_tx();
949 let sig = super::super::EthereumSignature {
950 r: [0xAA; 32],
951 s: [0xBB; 32],
952 v: 27,
953 };
954 let calldata = tx.encode_exec_transaction(&[sig]).unwrap();
955 let expected_selector = abi::function_selector(
956 "execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)",
957 );
958 assert_eq!(&calldata[..4], &expected_selector);
959 }
960
961 #[test]
962 fn test_exec_transaction_includes_signature_data() {
963 let tx = zero_tx();
964 let sig = super::super::EthereumSignature {
965 r: [0xAA; 32],
966 s: [0xBB; 32],
967 v: 27,
968 };
969 let calldata = tx.encode_exec_transaction(&[sig]).unwrap();
970 assert!(calldata.len() > 4 + 10 * 32);
971 }
972
973 #[test]
976 fn test_encode_approve_hash_selector() {
977 let calldata = encode_approve_hash(&[0xAA; 32]);
978 let expected = abi::function_selector("approveHash(bytes32)");
979 assert_eq!(&calldata[..4], &expected);
980 assert_eq!(calldata.len(), 4 + 32);
981 }
982
983 #[test]
984 fn test_encode_approved_hashes_selector() {
985 let calldata = encode_approved_hashes([0xBB; 20], &[0xAA; 32]);
986 let expected = abi::function_selector("approvedHashes(address,bytes32)");
987 assert_eq!(&calldata[..4], &expected);
988 assert_eq!(calldata.len(), 4 + 2 * 32);
989 }
990
991 #[test]
992 fn test_pre_validated_signature() {
993 let owner = [0xAA; 20];
994 let sig = pre_validated_signature(owner);
995 assert_eq!(sig.v, 1);
996 assert_eq!(&sig.r[12..32], &owner);
997 assert_eq!(&sig.r[..12], &[0u8; 12]);
998 assert_eq!(sig.s, [0u8; 32]);
999 }
1000
1001 #[test]
1004 fn test_contract_signature() {
1005 let contract = [0xCC; 20];
1006 let sig = contract_signature(contract, 130);
1007 assert_eq!(sig.v, 0);
1008 assert_eq!(&sig.r[12..32], &contract);
1009 assert_eq!(sig.s[28..32], 130u32.to_be_bytes());
1010 }
1011
1012 #[test]
1015 fn test_encode_exec_from_module_selector() {
1016 let calldata =
1017 encode_exec_from_module([0xBB; 20], &[0u8; 32], &[0xDE, 0xAD], Operation::Call);
1018 let expected =
1019 abi::function_selector("execTransactionFromModule(address,uint256,bytes,uint8)");
1020 assert_eq!(&calldata[..4], &expected);
1021 }
1022
1023 #[test]
1024 fn test_encode_exec_from_module_delegate_call() {
1025 let calldata =
1026 encode_exec_from_module([0xBB; 20], &[0u8; 32], &[], Operation::DelegateCall);
1027 assert!(calldata.len() > 4);
1028 }
1029
1030 #[test]
1031 fn test_encode_exec_from_module_return_data_selector() {
1032 let calldata =
1033 encode_exec_from_module_return_data([0xBB; 20], &[0u8; 32], &[], Operation::Call);
1034 let expected = abi::function_selector(
1035 "execTransactionFromModuleReturnData(address,uint256,bytes,uint8)",
1036 );
1037 assert_eq!(&calldata[..4], &expected);
1038 }
1039
1040 #[test]
1043 fn test_encode_add_owner_selector() {
1044 let calldata = encode_add_owner([0xAA; 20], 2);
1045 let expected = abi::function_selector("addOwnerWithThreshold(address,uint256)");
1046 assert_eq!(&calldata[..4], &expected);
1047 assert_eq!(calldata.len(), 4 + 2 * 32);
1048 }
1049
1050 #[test]
1051 fn test_encode_remove_owner_selector() {
1052 let calldata = encode_remove_owner(SENTINEL_OWNERS, [0xAA; 20], 1);
1053 let expected = abi::function_selector("removeOwner(address,address,uint256)");
1054 assert_eq!(&calldata[..4], &expected);
1055 assert_eq!(calldata.len(), 4 + 3 * 32);
1056 }
1057
1058 #[test]
1059 fn test_encode_change_threshold_selector() {
1060 let calldata = encode_change_threshold(3);
1061 let expected = abi::function_selector("changeThreshold(uint256)");
1062 assert_eq!(&calldata[..4], &expected);
1063 assert_eq!(calldata.len(), 4 + 32);
1064 }
1065
1066 #[test]
1067 fn test_encode_swap_owner_selector() {
1068 let calldata = encode_swap_owner(SENTINEL_OWNERS, [0xAA; 20], [0xBB; 20]);
1069 let expected = abi::function_selector("swapOwner(address,address,address)");
1070 assert_eq!(&calldata[..4], &expected);
1071 assert_eq!(calldata.len(), 4 + 3 * 32);
1072 }
1073
1074 #[test]
1075 fn test_encode_enable_module_selector() {
1076 let calldata = encode_enable_module([0xAA; 20]);
1077 let expected = abi::function_selector("enableModule(address)");
1078 assert_eq!(&calldata[..4], &expected);
1079 }
1080
1081 #[test]
1082 fn test_encode_disable_module_selector() {
1083 let calldata = encode_disable_module(SENTINEL_OWNERS, [0xAA; 20]);
1084 let expected = abi::function_selector("disableModule(address,address)");
1085 assert_eq!(&calldata[..4], &expected);
1086 }
1087
1088 #[test]
1089 fn test_encode_set_guard_selector() {
1090 let calldata = encode_set_guard([0xAA; 20]);
1091 let expected = abi::function_selector("setGuard(address)");
1092 assert_eq!(&calldata[..4], &expected);
1093 }
1094
1095 #[test]
1098 fn test_encode_get_owners_selector() {
1099 let calldata = encode_get_owners();
1100 let expected = abi::function_selector("getOwners()");
1101 assert_eq!(&calldata[..4], &expected);
1102 }
1103
1104 #[test]
1105 fn test_encode_get_threshold_selector() {
1106 let calldata = encode_get_threshold();
1107 let expected = abi::function_selector("getThreshold()");
1108 assert_eq!(&calldata[..4], &expected);
1109 }
1110
1111 #[test]
1112 fn test_encode_nonce_selector() {
1113 let calldata = encode_nonce();
1114 let expected = abi::function_selector("nonce()");
1115 assert_eq!(&calldata[..4], &expected);
1116 }
1117
1118 #[test]
1119 fn test_encode_get_transaction_hash_selector() {
1120 let tx = zero_tx();
1121 let calldata = encode_get_transaction_hash(&tx);
1122 let expected = abi::function_selector(
1123 "getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256)",
1124 );
1125 assert_eq!(&calldata[..4], &expected);
1126 }
1127
1128 #[test]
1129 fn test_encode_is_owner_selector() {
1130 let calldata = encode_is_owner([0xAA; 20]);
1131 let expected = abi::function_selector("isOwner(address)");
1132 assert_eq!(&calldata[..4], &expected);
1133 }
1134
1135 #[test]
1136 fn test_encode_domain_separator_selector() {
1137 let calldata = encode_domain_separator();
1138 let expected = abi::function_selector("domainSeparator()");
1139 assert_eq!(&calldata[..4], &expected);
1140 }
1141
1142 #[test]
1143 fn test_encode_is_module_enabled_selector() {
1144 let calldata = encode_is_module_enabled([0xAA; 20]);
1145 let expected = abi::function_selector("isModuleEnabled(address)");
1146 assert_eq!(&calldata[..4], &expected);
1147 }
1148
1149 #[test]
1150 fn test_encode_get_modules_paginated_selector() {
1151 let calldata = encode_get_modules_paginated(SENTINEL_OWNERS, 10);
1152 let expected = abi::function_selector("getModulesPaginated(address,uint256)");
1153 assert_eq!(&calldata[..4], &expected);
1154 }
1155
1156 #[test]
1159 fn test_encode_setup_selector() {
1160 let calldata = encode_setup(
1161 &[[0xAA; 20], [0xBB; 20]],
1162 2,
1163 [0u8; 20],
1164 &[],
1165 [0xCC; 20],
1166 [0u8; 20],
1167 0,
1168 [0u8; 20],
1169 );
1170 let expected = abi::function_selector(
1171 "setup(address[],uint256,address,bytes,address,address,uint256,address)",
1172 );
1173 assert_eq!(&calldata[..4], &expected);
1174 }
1175
1176 #[test]
1177 fn test_encode_create_proxy_with_nonce_selector() {
1178 let calldata = encode_create_proxy_with_nonce([0xAA; 20], &[0x01, 0x02], 42);
1179 let expected = abi::function_selector("createProxyWithNonce(address,bytes,uint256)");
1180 assert_eq!(&calldata[..4], &expected);
1181 }
1182
1183 #[test]
1186 fn test_sentinel_owners() {
1187 assert_eq!(SENTINEL_OWNERS[19], 1);
1188 assert_eq!(SENTINEL_OWNERS[..19], [0u8; 19]);
1189 }
1190
1191 #[test]
1194 fn test_operation_values() {
1195 assert_eq!(Operation::Call as u8, 0);
1196 assert_eq!(Operation::DelegateCall as u8, 1);
1197 }
1198
1199 #[test]
1200 fn test_operation_eq() {
1201 assert_eq!(Operation::Call, Operation::Call);
1202 assert_ne!(Operation::Call, Operation::DelegateCall);
1203 }
1204
1205 #[test]
1208 fn test_pad_address() {
1209 let addr = [0xAA; 20];
1210 let padded = pad_address(&addr);
1211 assert_eq!(&padded[..12], &[0u8; 12]);
1212 assert_eq!(&padded[12..], &[0xAA; 20]);
1213 }
1214
1215 #[test]
1216 fn test_pad_u8() {
1217 let padded = pad_u8(42);
1218 assert_eq!(&padded[..31], &[0u8; 31]);
1219 assert_eq!(padded[31], 42);
1220 }
1221
1222 #[test]
1223 fn test_pad_u64() {
1224 let padded = pad_u64(256);
1225 assert_eq!(&padded[..24], &[0u8; 24]);
1226 assert_eq!(&padded[24..], &256u64.to_be_bytes());
1227 }
1228
1229 #[test]
1232 fn test_delegate_call_transaction() {
1233 let mut tx = zero_tx();
1234 tx.operation = Operation::DelegateCall;
1235 tx.data = vec![0xDE, 0xAD, 0xBE, 0xEF];
1236 let domain = safe_domain_separator(1, &[0xAA; 20]);
1237 let hash = tx.signing_hash(&domain);
1238 assert_ne!(hash, [0u8; 32]);
1239 }
1240
1241 #[test]
1244 fn test_full_2_of_3_signing_flow() {
1245 let o1 = super::super::EthereumSigner::generate().unwrap();
1247 let _o2 = super::super::EthereumSigner::generate().unwrap();
1248 let o3 = super::super::EthereumSigner::generate().unwrap();
1249
1250 let domain = safe_domain_separator(1, &[0xAA; 20]);
1251 let tx = zero_tx();
1252
1253 let sorted = sign_and_sort(&tx, &[&o1, &o3], &domain).unwrap();
1255 assert_eq!(sorted.len(), 2);
1256
1257 let calldata = tx.encode_exec_transaction(&sorted).unwrap();
1259 assert!(calldata.len() > 4 + 10 * 32 + 2 * 65);
1260 }
1261
1262 #[test]
1265 fn test_mixed_ecdsa_and_prevalidated() {
1266 let signer = super::super::EthereumSigner::generate().unwrap();
1267 let tx = zero_tx();
1268 let domain = safe_domain_separator(1, &[0xAA; 20]);
1269 let ecdsa_sig = tx.sign(&signer, &domain).unwrap();
1270 let pre_sig = pre_validated_signature([0x01; 20]);
1271
1272 let packed = encode_signatures(&[pre_sig, ecdsa_sig]).unwrap();
1273 assert_eq!(packed.len(), 2 * 65);
1274
1275 assert_eq!(packed[64], 1);
1277 }
1278}