1#![allow(clippy::too_many_arguments)]
2#![allow(missing_docs)]
3use crate::{cep18::events::Transfer, cep18_token::Cep18, eip712};
55use casper_eip_712::DomainSeparator;
56use odra::{
57 casper_types::{bytesrepr::Bytes, PublicKey, U256},
58 named_keys::{compound_key_value_storage, single_value_storage},
59 prelude::*
60};
61
62const TRANSFER_WITH_AUTHORIZATION_TYPEHASH: [u8; 32] = [
64 0x7c, 0x7c, 0x6c, 0xdb, 0x67, 0xa1, 0x87, 0x43, 0xf4, 0x9e, 0xc6, 0xfa, 0x9b, 0x35, 0xf5, 0x0d,
65 0x52, 0xed, 0x05, 0xcb, 0xed, 0x4c, 0xc5, 0x92, 0xe1, 0x3b, 0x44, 0x50, 0x1c, 0x1a, 0x22, 0x67
66];
67
68const RECEIVE_WITH_AUTHORIZATION_TYPEHASH: [u8; 32] = [
70 0xd0, 0x99, 0xcc, 0x98, 0xef, 0x71, 0x10, 0x7a, 0x61, 0x6c, 0x4f, 0x0f, 0x94, 0x1f, 0x04, 0xc3,
71 0x22, 0xd8, 0xe2, 0x54, 0xfe, 0x26, 0xb3, 0xc6, 0x66, 0x8d, 0xb8, 0x7a, 0xae, 0x41, 0x3d, 0xe8
72];
73
74const CANCEL_AUTHORIZATION_TYPEHASH: [u8; 32] = [
76 0x15, 0x8b, 0x0a, 0x9e, 0xdf, 0x7a, 0x82, 0x8a, 0xad, 0x02, 0xf6, 0x3c, 0xd5, 0x15, 0xc6, 0x8e,
77 0xf2, 0xf5, 0x0b, 0xa8, 0x07, 0x39, 0x6f, 0x6d, 0x12, 0x84, 0x28, 0x33, 0xa1, 0x59, 0x74, 0x29
78];
79
80#[odra::event]
85pub struct AuthorizationUsed {
86 authorizer: Address,
87 nonce: Bytes
88}
89
90#[odra::event]
94pub struct AuthorizationCanceled {
95 authorizer: Address,
96 nonce: Bytes
97}
98
99#[odra::odra_error]
101pub enum Error {
102 NonceAlreadyUsed = 37_000,
105 AuthorizationExpired = 37_001,
108 AuthorizationNotYetValid = 37_002,
111 InvalidSignature = 37_003,
115 InvalidPublicKey = 37_004,
119 InvalidCaller = 37_005,
122 AuthorizationUsed = 37_006
125}
126
127const CHAIN_NAME_KEY: &str = "chain_name";
129const USED_NONCES_KEY: &str = "used_nonces";
130
131const DOMAIN_VERSION: &str = "1";
133
134single_value_storage!(
135 CEP3009ChainNameStorage,
136 String,
137 CHAIN_NAME_KEY,
138 ExecutionError::KeyNotFound
139);
140
141compound_key_value_storage!(
142 CEP3009UsedNoncesStorage,
143 USED_NONCES_KEY,
144 Address,
145 Bytes,
146 bool
147);
148
149#[odra::module(events = [AuthorizationUsed, AuthorizationCanceled, Transfer], errors = Error)]
155pub struct CEP3009 {
156 used_nonces: SubModule<CEP3009UsedNoncesStorage>,
159 chain_name: SubModule<CEP3009ChainNameStorage>,
161 token: SubModule<Cep18>
163}
164
165#[odra::module]
166impl CEP3009 {
167 pub fn init(&mut self, chain_name: String) {
170 self.chain_name.set(chain_name);
171 self.used_nonces.init();
172 }
173
174 pub fn authorization_state(&self, authorizer: Address, nonce: Bytes) -> bool {
178 self.used_nonces.get_or_default(&authorizer, &nonce)
179 }
180
181 pub fn transfer_with_authorization(
191 &mut self,
192 from: Address,
193 to: Address,
194 amount: U256,
195 valid_after: u64,
196 valid_before: u64,
197 nonce: Bytes,
198 public_key: PublicKey,
199 signature: Bytes
200 ) {
201 self.raw_transfer_with_authorization(
202 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
203 from,
204 to,
205 amount,
206 valid_after,
207 valid_before,
208 nonce,
209 public_key,
210 signature
211 );
212 }
213
214 pub fn receive_with_authorization(
223 &mut self,
224 from: Address,
225 to: Address,
226 amount: U256,
227 valid_after: u64,
228 valid_before: u64,
229 nonce: Bytes,
230 public_key: PublicKey,
231 signature: Bytes
232 ) {
233 let caller = self.env().caller();
234 if caller != to {
235 self.env().revert(Error::InvalidCaller);
236 }
237
238 self.raw_transfer_with_authorization(
239 RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
240 from,
241 to,
242 amount,
243 valid_after,
244 valid_before,
245 nonce,
246 public_key,
247 signature
248 );
249 }
250
251 pub fn cancel_authorization(
264 &mut self,
265 authorizer: Address,
266 nonce: Bytes,
267 public_key: PublicKey,
268 signature: Bytes
269 ) {
270 if self.authorization_state(authorizer, nonce.clone()) {
271 self.env().revert(Error::AuthorizationUsed);
272 }
273
274 if Address::from(public_key.clone()) != authorizer {
275 self.env().revert(Error::InvalidPublicKey);
276 }
277
278 let message = self.build_cancel_message(authorizer, &nonce);
279 if !self
280 .env()
281 .verify_signature(&message, &signature, &public_key)
282 {
283 self.env().revert(Error::InvalidSignature);
284 }
285
286 self.used_nonces.set(&authorizer, &nonce, true);
287 self.env()
288 .emit_event(AuthorizationCanceled { authorizer, nonce });
289 }
290}
291
292impl CEP3009 {
293 fn raw_transfer_with_authorization(
298 &mut self,
299 typehash: [u8; 32],
300 from: Address,
301 to: Address,
302 amount: U256,
303 valid_after: u64,
304 valid_before: u64,
305 nonce: Bytes,
306 public_key: PublicKey,
307 signature: Bytes
308 ) {
309 if self.used_nonces.get_or_default(&from, &nonce) {
311 self.env().revert(Error::NonceAlreadyUsed);
312 }
313
314 let now_secs = self.env().get_block_time_secs();
316
317 if now_secs <= valid_after {
319 self.env().revert(Error::AuthorizationNotYetValid);
320 }
321
322 if now_secs >= valid_before {
324 self.env().revert(Error::AuthorizationExpired);
325 }
326
327 let derived_address = Address::from(public_key.clone());
329 if derived_address != from {
330 self.env().revert(Error::InvalidPublicKey);
331 }
332
333 let message = self.build_authorization_message(
335 typehash,
336 from,
337 to,
338 &amount,
339 valid_after,
340 valid_before,
341 &nonce
342 );
343
344 if !self
345 .env()
346 .verify_signature(&message, &signature, &public_key)
347 {
348 self.env().revert(Error::InvalidSignature);
349 }
350
351 self.used_nonces.set(&from, &nonce, true);
353
354 self.env().emit_event(AuthorizationUsed {
356 authorizer: from,
357 nonce
358 });
359
360 self.token.raw_transfer(&from, &to, &amount);
362
363 self.env().emit_event(Transfer {
365 sender: from,
366 recipient: to,
367 amount
368 });
369 }
370
371 fn build_authorization_message(
375 &self,
376 typehash: [u8; 32],
377 from: Address,
378 to: Address,
379 amount: &U256,
380 valid_after: u64,
381 valid_before: u64,
382 nonce: &[u8]
383 ) -> Bytes {
384 let mut value_bytes = [0u8; 32];
385 amount.to_big_endian(&mut value_bytes);
386
387 let mut nonce_padded = [0u8; 32];
388 let len = nonce.len().min(32);
389 nonce_padded[..len].copy_from_slice(&nonce[..len]);
390
391 let mut encoded_data = Vec::with_capacity(6 * 32);
392 encoded_data.extend(eip712::encode_address(from));
393 encoded_data.extend(eip712::encode_address(to));
394 encoded_data.extend(casper_eip_712::encode_uint256(value_bytes));
395 encoded_data.extend(casper_eip_712::encode_uint64(valid_after));
396 encoded_data.extend(casper_eip_712::encode_uint64(valid_before));
397 encoded_data.extend(casper_eip_712::encode_bytes32(nonce_padded));
398 let domain = self.domain_separator();
399
400 Bytes::from(crate::eip712::hash_typed_data(domain, typehash, encoded_data).to_vec())
401 }
402
403 fn build_cancel_message(&self, authorizer: Address, nonce: &[u8]) -> Bytes {
405 let mut nonce_padded = [0u8; 32];
406 let len = nonce.len().min(32);
407 nonce_padded[..len].copy_from_slice(&nonce[..len]);
408
409 let mut encoded_data = Vec::with_capacity(64);
410 encoded_data.extend(eip712::encode_address(authorizer));
411 encoded_data.extend(casper_eip_712::encode_bytes32(nonce_padded));
412 let domain = self.domain_separator();
413
414 Bytes::from(
415 eip712::hash_typed_data(domain, CANCEL_AUTHORIZATION_TYPEHASH, encoded_data).to_vec()
416 )
417 }
418
419 fn domain_separator(&self) -> DomainSeparator {
423 let self_address = self.env().self_address();
424 let name = self.token.name();
425 let chain_name = self.chain_name.get();
426 eip712::domain_separator(&name, DOMAIN_VERSION, chain_name, self_address)
427 }
428}
429
430#[odra::module]
432pub struct CEP3009Wrapper {
433 cep3009: SubModule<CEP3009>,
434 token: SubModule<Cep18>
435}
436
437#[odra::module]
438impl CEP3009Wrapper {
439 pub fn init(
442 &mut self,
443 chain_name: String,
444 symbol: String,
445 name: String,
446 decimals: u8,
447 initial_supply: U256
448 ) {
449 self.cep3009.init(chain_name);
450 self.token.init(symbol, name, decimals, initial_supply);
451 }
452
453 delegate! {
454 to self.cep3009 {
455 fn authorization_state(&self, authorizer: Address, nonce: Bytes) -> bool;
456 fn transfer_with_authorization(
457 &mut self,
458 from: Address,
459 to: Address,
460 amount: U256,
461 valid_after: u64,
462 valid_before: u64,
463 nonce: Bytes,
464 public_key: PublicKey,
465 signature: Bytes
466 );
467 fn receive_with_authorization(
468 &mut self,
469 from: Address,
470 to: Address,
471 amount: U256,
472 valid_after: u64,
473 valid_before: u64,
474 nonce: Bytes,
475 public_key: PublicKey,
476 signature: Bytes
477 );
478 fn cancel_authorization(
479 &mut self,
480 authorizer: Address,
481 nonce: Bytes,
482 public_key: PublicKey,
483 signature: Bytes
484 );
485 }
486
487 to self.token {
488 fn balance_of(&self, address: &Address) -> U256;
489 }
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use odra::host::{Deployer, HostEnv};
497
498 const TOKEN_NAME: &str = "Test Token";
499 const TOKEN_SYMBOL: &str = "TEST";
500 const TOKEN_DECIMALS: u8 = 8;
501 const INITIAL_SUPPLY: u64 = 1_000_000;
502 const CHAIN_NAME: &str = "casper:casper";
503
504 struct Setup {
505 env: HostEnv,
506 wrapper: CEP3009WrapperHostRef,
507 alice: Address,
508 bob: Address,
509 charlie: Address,
510 alice_pubkey: PublicKey
511 }
512
513 fn setup() -> Setup {
516 let env = odra_test::env();
517 let alice = env.get_account(0);
518 let bob = env.get_account(1);
519 let charlie = env.get_account(2);
520 let alice_pubkey = env.public_key(&alice);
521
522 let wrapper = CEP3009Wrapper::deploy(
523 &env,
524 CEP3009WrapperInitArgs {
525 chain_name: CHAIN_NAME.to_string(),
526 symbol: TOKEN_SYMBOL.to_string(),
527 name: TOKEN_NAME.to_string(),
528 decimals: TOKEN_DECIMALS,
529 initial_supply: INITIAL_SUPPLY.into()
530 }
531 );
532
533 env.advance_block_time(1_000);
534
535 Setup {
536 env,
537 wrapper,
538 alice,
539 bob,
540 charlie,
541 alice_pubkey
542 }
543 }
544
545 fn fresh_nonce(seed: u8) -> Bytes {
546 Bytes::from(vec![seed; 32])
547 }
548
549 #[test]
550 fn transfer_with_authorization_happy_path() {
551 let Setup {
552 env,
553 mut wrapper,
554 alice,
555 bob,
556 charlie,
557 alice_pubkey
558 } = setup();
559
560 assert_eq!(wrapper.balance_of(&alice), INITIAL_SUPPLY.into());
561 assert_eq!(wrapper.balance_of(&bob), U256::zero());
562
563 let amount: U256 = 100u64.into();
564 let valid_after: u64 = 0;
565 let valid_before: u64 = u64::MAX;
566 let nonce = fresh_nonce(1);
567
568 let signature = sign_transfer_authorization(
569 &env,
570 &alice,
571 wrapper.address(),
572 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
573 alice,
574 bob,
575 amount,
576 valid_after,
577 valid_before,
578 &nonce
579 );
580
581 env.set_caller(charlie);
583 wrapper.transfer_with_authorization(
584 alice,
585 bob,
586 amount,
587 valid_after,
588 valid_before,
589 nonce.clone(),
590 alice_pubkey,
591 signature
592 );
593
594 assert_eq!(
595 wrapper.balance_of(&alice),
596 U256::from(INITIAL_SUPPLY) - amount
597 );
598 assert_eq!(wrapper.balance_of(&bob), amount);
599 assert!(wrapper.authorization_state(alice, nonce.clone()));
600 assert!(env.emitted_event(
601 &wrapper,
602 AuthorizationUsed {
603 authorizer: alice,
604 nonce
605 }
606 ));
607 }
608
609 #[test]
610 fn transfer_with_authorization_replay_protection() {
611 let Setup {
612 env,
613 mut wrapper,
614 alice,
615 bob,
616 charlie,
617 alice_pubkey
618 } = setup();
619
620 let amount: U256 = 100u64.into();
621 let valid_after: u64 = 0;
622 let valid_before: u64 = u64::MAX;
623 let nonce = fresh_nonce(2);
624
625 let signature = sign_transfer_authorization(
626 &env,
627 &alice,
628 wrapper.address(),
629 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
630 alice,
631 bob,
632 amount,
633 valid_after,
634 valid_before,
635 &nonce
636 );
637
638 env.set_caller(charlie);
639 wrapper.transfer_with_authorization(
640 alice,
641 bob,
642 amount,
643 valid_after,
644 valid_before,
645 nonce.clone(),
646 alice_pubkey.clone(),
647 signature.clone()
648 );
649
650 assert_eq!(
652 wrapper.try_transfer_with_authorization(
653 alice,
654 bob,
655 amount,
656 valid_after,
657 valid_before,
658 nonce,
659 alice_pubkey,
660 signature
661 ),
662 Err(Error::NonceAlreadyUsed.into())
663 );
664 }
665
666 #[test]
667 fn transfer_with_authorization_not_yet_valid() {
668 let Setup {
669 env,
670 mut wrapper,
671 alice,
672 bob,
673 charlie,
674 alice_pubkey
675 } = setup();
676
677 let valid_after: u64 = 60;
679 let valid_before: u64 = u64::MAX;
680 let amount: U256 = 100u64.into();
681 let nonce = fresh_nonce(3);
682
683 let signature = sign_transfer_authorization(
684 &env,
685 &alice,
686 wrapper.address(),
687 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
688 alice,
689 bob,
690 amount,
691 valid_after,
692 valid_before,
693 &nonce
694 );
695
696 env.set_caller(charlie);
697 assert_eq!(
698 wrapper.try_transfer_with_authorization(
699 alice,
700 bob,
701 amount,
702 valid_after,
703 valid_before,
704 nonce,
705 alice_pubkey,
706 signature
707 ),
708 Err(Error::AuthorizationNotYetValid.into())
709 );
710 }
711
712 #[test]
713 fn transfer_with_authorization_expired() {
714 let Setup {
715 env,
716 mut wrapper,
717 alice,
718 bob,
719 charlie,
720 alice_pubkey
721 } = setup();
722
723 env.advance_block_time(60_000);
725
726 let valid_after: u64 = 0;
727 let valid_before: u64 = 10;
728 let amount: U256 = 100u64.into();
729 let nonce = fresh_nonce(4);
730
731 let signature = sign_transfer_authorization(
732 &env,
733 &alice,
734 wrapper.address(),
735 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
736 alice,
737 bob,
738 amount,
739 valid_after,
740 valid_before,
741 &nonce
742 );
743
744 env.set_caller(charlie);
745 assert_eq!(
746 wrapper.try_transfer_with_authorization(
747 alice,
748 bob,
749 amount,
750 valid_after,
751 valid_before,
752 nonce,
753 alice_pubkey,
754 signature
755 ),
756 Err(Error::AuthorizationExpired.into())
757 );
758 }
759
760 #[test]
761 fn transfer_with_authorization_invalid_signature() {
762 let Setup {
763 env,
764 mut wrapper,
765 alice,
766 bob,
767 charlie,
768 alice_pubkey
769 } = setup();
770
771 let valid_after: u64 = 0;
772 let valid_before: u64 = u64::MAX;
773 let nonce = fresh_nonce(5);
774
775 let signature = sign_transfer_authorization(
777 &env,
778 &alice,
779 wrapper.address(),
780 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
781 alice,
782 bob,
783 U256::from(100u64),
784 valid_after,
785 valid_before,
786 &nonce
787 );
788
789 env.set_caller(charlie);
792 assert_eq!(
793 wrapper.try_transfer_with_authorization(
794 alice,
795 bob,
796 U256::from(200u64),
797 valid_after,
798 valid_before,
799 nonce,
800 alice_pubkey,
801 signature
802 ),
803 Err(Error::InvalidSignature.into())
804 );
805 }
806
807 #[test]
808 fn transfer_with_authorization_public_key_mismatch() {
809 let Setup {
810 env,
811 mut wrapper,
812 alice,
813 bob,
814 charlie,
815 ..
816 } = setup();
817
818 let valid_after: u64 = 0;
819 let valid_before: u64 = u64::MAX;
820 let amount: U256 = 100u64.into();
821 let nonce = fresh_nonce(6);
822
823 let bob_pubkey = env.public_key(&bob);
825 let signature = sign_transfer_authorization(
826 &env,
827 &bob,
828 wrapper.address(),
829 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
830 alice,
831 bob,
832 amount,
833 valid_after,
834 valid_before,
835 &nonce
836 );
837
838 env.set_caller(charlie);
839 assert_eq!(
840 wrapper.try_transfer_with_authorization(
841 alice,
842 bob,
843 amount,
844 valid_after,
845 valid_before,
846 nonce,
847 bob_pubkey,
848 signature
849 ),
850 Err(Error::InvalidPublicKey.into())
851 );
852 }
853
854 #[test]
855 fn receive_with_authorization_happy_path() {
856 let Setup {
857 env,
858 mut wrapper,
859 alice,
860 bob,
861 alice_pubkey,
862 ..
863 } = setup();
864
865 let amount: U256 = 250u64.into();
866 let valid_after: u64 = 0;
867 let valid_before: u64 = u64::MAX;
868 let nonce = fresh_nonce(7);
869
870 let signature = sign_transfer_authorization(
871 &env,
872 &alice,
873 wrapper.address(),
874 RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
875 alice,
876 bob,
877 amount,
878 valid_after,
879 valid_before,
880 &nonce
881 );
882
883 env.set_caller(bob);
885 wrapper.receive_with_authorization(
886 alice,
887 bob,
888 amount,
889 valid_after,
890 valid_before,
891 nonce.clone(),
892 alice_pubkey,
893 signature
894 );
895
896 assert_eq!(
897 wrapper.balance_of(&alice),
898 U256::from(INITIAL_SUPPLY) - amount
899 );
900 assert_eq!(wrapper.balance_of(&bob), amount);
901 assert!(wrapper.authorization_state(alice, nonce));
902 }
903
904 #[test]
905 fn receive_with_authorization_wrong_caller_reverts() {
906 let Setup {
907 env,
908 mut wrapper,
909 alice,
910 bob,
911 charlie,
912 alice_pubkey
913 } = setup();
914
915 let amount: U256 = 250u64.into();
916 let valid_after: u64 = 0;
917 let valid_before: u64 = u64::MAX;
918 let nonce = fresh_nonce(8);
919
920 let signature = sign_transfer_authorization(
921 &env,
922 &alice,
923 wrapper.address(),
924 RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
925 alice,
926 bob,
927 amount,
928 valid_after,
929 valid_before,
930 &nonce
931 );
932
933 env.set_caller(charlie);
935 assert_eq!(
936 wrapper.try_receive_with_authorization(
937 alice,
938 bob,
939 amount,
940 valid_after,
941 valid_before,
942 nonce,
943 alice_pubkey,
944 signature
945 ),
946 Err(Error::InvalidCaller.into())
947 );
948 }
949
950 #[test]
951 fn cancel_authorization_happy_path() {
952 let Setup {
953 env,
954 mut wrapper,
955 alice,
956 bob,
957 charlie,
958 alice_pubkey
959 } = setup();
960
961 let nonce = fresh_nonce(9);
962 let cancel_signature =
963 sign_cancel_authorization(&env, &alice, wrapper.address(), alice, &nonce);
964
965 env.set_caller(charlie);
966 wrapper.cancel_authorization(alice, nonce.clone(), alice_pubkey.clone(), cancel_signature);
967
968 assert!(wrapper.authorization_state(alice, nonce.clone()));
969 assert!(env.emitted_event(
970 &wrapper,
971 AuthorizationCanceled {
972 authorizer: alice,
973 nonce: nonce.clone()
974 }
975 ));
976
977 let amount: U256 = 1u64.into();
979 let valid_after: u64 = 0;
980 let valid_before: u64 = u64::MAX;
981 let transfer_signature = sign_transfer_authorization(
982 &env,
983 &alice,
984 wrapper.address(),
985 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
986 alice,
987 bob,
988 amount,
989 valid_after,
990 valid_before,
991 &nonce
992 );
993 assert_eq!(
994 wrapper.try_transfer_with_authorization(
995 alice,
996 bob,
997 amount,
998 valid_after,
999 valid_before,
1000 nonce,
1001 alice_pubkey,
1002 transfer_signature
1003 ),
1004 Err(Error::NonceAlreadyUsed.into())
1005 );
1006 }
1007
1008 #[test]
1009 fn cancel_authorization_already_used_reverts() {
1010 let Setup {
1011 env,
1012 mut wrapper,
1013 alice,
1014 bob,
1015 charlie,
1016 alice_pubkey
1017 } = setup();
1018
1019 let amount: U256 = 100u64.into();
1020 let valid_after: u64 = 0;
1021 let valid_before: u64 = u64::MAX;
1022 let nonce = fresh_nonce(10);
1023
1024 let transfer_signature = sign_transfer_authorization(
1026 &env,
1027 &alice,
1028 wrapper.address(),
1029 TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
1030 alice,
1031 bob,
1032 amount,
1033 valid_after,
1034 valid_before,
1035 &nonce
1036 );
1037 env.set_caller(charlie);
1038 wrapper.transfer_with_authorization(
1039 alice,
1040 bob,
1041 amount,
1042 valid_after,
1043 valid_before,
1044 nonce.clone(),
1045 alice_pubkey.clone(),
1046 transfer_signature
1047 );
1048
1049 let cancel_signature =
1051 sign_cancel_authorization(&env, &alice, wrapper.address(), alice, &nonce);
1052 assert_eq!(
1053 wrapper.try_cancel_authorization(alice, nonce, alice_pubkey, cancel_signature),
1054 Err(Error::AuthorizationUsed.into())
1055 );
1056 }
1057
1058 #[test]
1059 fn cancel_authorization_with_mismatched_public_key_reverts() {
1060 let Setup {
1061 env,
1062 mut wrapper,
1063 alice,
1064 bob,
1065 charlie,
1066 ..
1067 } = setup();
1068
1069 let nonce = fresh_nonce(11);
1070
1071 let bob_pubkey = env.public_key(&bob);
1074 let cancel_signature =
1075 sign_cancel_authorization(&env, &bob, wrapper.address(), alice, &nonce);
1076
1077 env.set_caller(charlie);
1080 assert_eq!(
1081 wrapper.try_cancel_authorization(alice, nonce.clone(), bob_pubkey, cancel_signature),
1082 Err(Error::InvalidPublicKey.into())
1083 );
1084
1085 assert!(!wrapper.authorization_state(alice, nonce));
1087 }
1088
1089 fn sign_transfer_authorization(
1090 env: &HostEnv,
1091 signer: &Address,
1092 contract_address: Address,
1093 typehash: [u8; 32],
1094 from: Address,
1095 to: Address,
1096 amount: U256,
1097 valid_after: u64,
1098 valid_before: u64,
1099 nonce: &Bytes
1100 ) -> Bytes {
1101 let mut value_bytes = [0u8; 32];
1102 amount.to_big_endian(&mut value_bytes);
1103
1104 let mut nonce_padded = [0u8; 32];
1105 let len = nonce.len().min(32);
1106 nonce_padded[..len].copy_from_slice(&nonce[..len]);
1107
1108 let mut encoded_data = Vec::with_capacity(6 * 32);
1109 encoded_data.extend(crate::eip712::encode_address(from));
1110 encoded_data.extend(crate::eip712::encode_address(to));
1111 encoded_data.extend(casper_eip_712::encode_uint256(value_bytes));
1112 encoded_data.extend(casper_eip_712::encode_uint64(valid_after));
1113 encoded_data.extend(casper_eip_712::encode_uint64(valid_before));
1114 encoded_data.extend(casper_eip_712::encode_bytes32(nonce_padded));
1115
1116 let domain = crate::eip712::domain_separator(
1117 TOKEN_NAME,
1118 DOMAIN_VERSION,
1119 CHAIN_NAME.to_string(),
1120 contract_address
1121 );
1122 let message_hash = crate::eip712::hash_typed_data(domain, typehash, encoded_data);
1123 let message = Bytes::from(message_hash.to_vec());
1124
1125 env.sign_message(&message, signer)
1126 }
1127
1128 fn sign_cancel_authorization(
1129 env: &HostEnv,
1130 signer: &Address,
1131 contract_address: Address,
1132 authorizer: Address,
1133 nonce: &Bytes
1134 ) -> Bytes {
1135 let mut nonce_padded = [0u8; 32];
1136 let len = nonce.len().min(32);
1137 nonce_padded[..len].copy_from_slice(&nonce[..len]);
1138
1139 let mut encoded_data = Vec::with_capacity(64);
1140 encoded_data.extend(crate::eip712::encode_address(authorizer));
1141 encoded_data.extend(casper_eip_712::encode_bytes32(nonce_padded));
1142
1143 let domain = crate::eip712::domain_separator(
1144 TOKEN_NAME,
1145 DOMAIN_VERSION,
1146 CHAIN_NAME.to_string(),
1147 contract_address
1148 );
1149 let message_hash =
1150 crate::eip712::hash_typed_data(domain, CANCEL_AUTHORIZATION_TYPEHASH, encoded_data);
1151 let message = Bytes::from(message_hash.to_vec());
1152
1153 env.sign_message(&message, signer)
1154 }
1155}