1pub mod dispatch;
9
10use crate::StorageCtx;
11pub use tempo_contracts::precompiles::{
12 ITIP403Registry::{self, PolicyType},
13 TIP403RegistryError, TIP403RegistryEvent,
14};
15use tempo_precompiles_macros::{Storable, contract};
16
17use crate::{
18 TIP403_REGISTRY_ADDRESS,
19 error::{Result, TempoPrecompileError},
20 storage::{Handler, Mapping},
21};
22use alloy::primitives::{Address, U256};
23
24pub const REJECT_ALL_POLICY_ID: u64 = 0;
26
27pub const ALLOW_ALL_POLICY_ID: u64 = 1;
29
30#[contract(addr = TIP403_REGISTRY_ADDRESS)]
38pub struct TIP403Registry {
39 policy_id_counter: u64,
43 policy_records: Mapping<u64, PolicyRecord>,
46 policy_set: Mapping<u64, Mapping<Address, bool>>,
50}
51
52#[derive(Debug, Clone, Storable)]
56pub struct PolicyRecord {
57 pub base: PolicyData,
59 pub compound: CompoundPolicyData,
61}
62
63#[derive(Debug, Clone, Default, Storable)]
67pub struct CompoundPolicyData {
68 pub sender_policy_id: u64,
70 pub recipient_policy_id: u64,
72 pub mint_recipient_policy_id: u64,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum AuthRole {
82 Transfer,
84 Sender,
86 Recipient,
88 MintRecipient,
90}
91
92#[derive(Debug, Clone, Storable)]
94pub struct PolicyData {
95 pub policy_type: u8,
98 pub admin: Address,
100}
101
102impl PolicyData {
105 pub fn decode_from_slot(slot_value: U256) -> Self {
107 use crate::storage::{LayoutCtx, Storable, packing::PackedSlot};
108
109 Self::load(&PackedSlot(slot_value), U256::ZERO, LayoutCtx::FULL)
111 .expect("unable to decode PoliciData from slot")
112 }
113
114 pub fn encode_to_slot(&self) -> U256 {
116 use crate::storage::packing::insert_into_word;
117 use __packing_policy_data::{ADMIN_LOC as A_LOC, POLICY_TYPE_LOC as PT_LOC};
118
119 let encoded = insert_into_word(
120 U256::ZERO,
121 &self.policy_type,
122 PT_LOC.offset_bytes,
123 PT_LOC.size,
124 )
125 .expect("unable to insert 'policy_type'");
126
127 insert_into_word(encoded, &self.admin, A_LOC.offset_bytes, A_LOC.size)
128 .expect("unable to insert 'admin'")
129 }
130
131 fn policy_type(&self) -> Result<PolicyType> {
133 let is_t2 = StorageCtx.spec().is_t2();
134
135 match self.policy_type.try_into() {
136 Ok(ty) if is_t2 || ty != PolicyType::COMPOUND => Ok(ty),
137 _ => Err(if is_t2 {
138 TIP403RegistryError::invalid_policy_type().into()
139 } else {
140 TempoPrecompileError::under_overflow()
141 }),
142 }
143 }
144
145 fn is_simple(&self) -> bool {
147 self.policy_type == PolicyType::WHITELIST as u8
148 || self.policy_type == PolicyType::BLACKLIST as u8
149 }
150
151 pub fn is_compound(&self) -> bool {
153 self.policy_type == PolicyType::COMPOUND as u8
154 }
155
156 fn is_default(&self) -> bool {
158 self.policy_type == 0 && self.admin == Address::ZERO
159 }
160}
161
162impl TIP403Registry {
163 pub fn initialize(&mut self) -> Result<()> {
165 self.__initialize()
166 }
167
168 pub fn policy_id_counter(&self) -> Result<u64> {
170 self.policy_id_counter.read().map(|counter| counter.max(2))
172 }
173
174 pub fn policy_exists(&self, call: ITIP403Registry::policyExistsCall) -> Result<bool> {
176 if self.builtin_authorization(call.policyId).is_some() {
178 return Ok(true);
179 }
180
181 let counter = self.policy_id_counter()?;
183 Ok(call.policyId < counter)
184 }
185
186 pub fn policy_data(
193 &self,
194 call: ITIP403Registry::policyDataCall,
195 ) -> Result<ITIP403Registry::policyDataReturn> {
196 if self.storage.spec().is_t2() {
197 if self.builtin_authorization(call.policyId).is_some() {
201 return Ok(ITIP403Registry::policyDataReturn {
202 policyType: (call.policyId as u8)
203 .try_into()
204 .map_err(|_| TIP403RegistryError::invalid_policy_type())?,
205 admin: Address::ZERO,
206 });
207 }
208 } else {
209 if !self.policy_exists(ITIP403Registry::policyExistsCall {
211 policyId: call.policyId,
212 })? {
213 return Err(TIP403RegistryError::policy_not_found().into());
214 }
215 }
216
217 let data = self.get_policy_data(call.policyId)?;
219
220 Ok(ITIP403Registry::policyDataReturn {
221 policyType: data.policy_type()?,
222 admin: data.admin,
223 })
224 }
225
226 pub fn compound_policy_data(
234 &self,
235 call: ITIP403Registry::compoundPolicyDataCall,
236 ) -> Result<ITIP403Registry::compoundPolicyDataReturn> {
237 let data = self.get_policy_data(call.policyId)?;
238
239 if !data.is_compound() {
241 let err = if self.policy_exists(ITIP403Registry::policyExistsCall {
243 policyId: call.policyId,
244 })? {
245 TIP403RegistryError::incompatible_policy_type()
246 } else {
247 TIP403RegistryError::policy_not_found()
248 };
249 return Err(err.into());
250 }
251
252 let compound = self.policy_records[call.policyId].compound.read()?;
253 Ok(ITIP403Registry::compoundPolicyDataReturn {
254 senderPolicyId: compound.sender_policy_id,
255 recipientPolicyId: compound.recipient_policy_id,
256 mintRecipientPolicyId: compound.mint_recipient_policy_id,
257 })
258 }
259
260 pub fn create_policy(
266 &mut self,
267 msg_sender: Address,
268 call: ITIP403Registry::createPolicyCall,
269 ) -> Result<u64> {
270 let policy_type = call.policyType.ensure_is_simple()?;
271
272 let new_policy_id = self.policy_id_counter()?;
273
274 self.policy_id_counter.write(
276 new_policy_id
277 .checked_add(1)
278 .ok_or(TempoPrecompileError::under_overflow())?,
279 )?;
280
281 self.policy_records[new_policy_id].base.write(PolicyData {
283 policy_type,
284 admin: call.admin,
285 })?;
286
287 self.emit_event(TIP403RegistryEvent::PolicyCreated(
288 ITIP403Registry::PolicyCreated {
289 policyId: new_policy_id,
290 updater: msg_sender,
291 policyType: policy_type.try_into().unwrap_or(PolicyType::__Invalid),
292 },
293 ))?;
294
295 self.emit_event(TIP403RegistryEvent::PolicyAdminUpdated(
296 ITIP403Registry::PolicyAdminUpdated {
297 policyId: new_policy_id,
298 updater: msg_sender,
299 admin: call.admin,
300 },
301 ))?;
302
303 Ok(new_policy_id)
304 }
305
306 pub fn create_policy_with_accounts(
313 &mut self,
314 msg_sender: Address,
315 call: ITIP403Registry::createPolicyWithAccountsCall,
316 ) -> Result<u64> {
317 let admin = call.admin;
318 let policy_type = call.policyType.ensure_is_simple()?;
319
320 let new_policy_id = self.policy_id_counter()?;
321
322 self.policy_id_counter.write(
324 new_policy_id
325 .checked_add(1)
326 .ok_or(TempoPrecompileError::under_overflow())?,
327 )?;
328
329 self.set_policy_data(new_policy_id, PolicyData { policy_type, admin })?;
331
332 for account in call.accounts.iter() {
335 self.set_policy_set(new_policy_id, *account, true)?;
336
337 match call.policyType {
338 PolicyType::WHITELIST => {
339 self.emit_event(TIP403RegistryEvent::WhitelistUpdated(
340 ITIP403Registry::WhitelistUpdated {
341 policyId: new_policy_id,
342 updater: msg_sender,
343 account: *account,
344 allowed: true,
345 },
346 ))?;
347 }
348 PolicyType::BLACKLIST => {
349 self.emit_event(TIP403RegistryEvent::BlacklistUpdated(
350 ITIP403Registry::BlacklistUpdated {
351 policyId: new_policy_id,
352 updater: msg_sender,
353 account: *account,
354 restricted: true,
355 },
356 ))?;
357 }
358 ITIP403Registry::PolicyType::COMPOUND | ITIP403Registry::PolicyType::__Invalid => {
359 return Err(TIP403RegistryError::incompatible_policy_type().into());
361 }
362 }
363 }
364
365 self.emit_event(TIP403RegistryEvent::PolicyCreated(
366 ITIP403Registry::PolicyCreated {
367 policyId: new_policy_id,
368 updater: msg_sender,
369 policyType: policy_type.try_into().unwrap_or(PolicyType::__Invalid),
370 },
371 ))?;
372
373 self.emit_event(TIP403RegistryEvent::PolicyAdminUpdated(
374 ITIP403Registry::PolicyAdminUpdated {
375 policyId: new_policy_id,
376 updater: msg_sender,
377 admin,
378 },
379 ))?;
380
381 Ok(new_policy_id)
382 }
383
384 pub fn set_policy_admin(
390 &mut self,
391 msg_sender: Address,
392 call: ITIP403Registry::setPolicyAdminCall,
393 ) -> Result<()> {
394 let data = self.get_policy_data(call.policyId)?;
395
396 if data.admin != msg_sender {
398 return Err(TIP403RegistryError::unauthorized().into());
399 }
400
401 self.set_policy_data(
403 call.policyId,
404 PolicyData {
405 admin: call.admin,
406 ..data
407 },
408 )?;
409
410 self.emit_event(TIP403RegistryEvent::PolicyAdminUpdated(
411 ITIP403Registry::PolicyAdminUpdated {
412 policyId: call.policyId,
413 updater: msg_sender,
414 admin: call.admin,
415 },
416 ))
417 }
418
419 pub fn modify_policy_whitelist(
426 &mut self,
427 msg_sender: Address,
428 call: ITIP403Registry::modifyPolicyWhitelistCall,
429 ) -> Result<()> {
430 let data = self.get_policy_data(call.policyId)?;
431
432 if data.admin != msg_sender {
434 return Err(TIP403RegistryError::unauthorized().into());
435 }
436
437 if !matches!(data.policy_type()?, PolicyType::WHITELIST) {
439 return Err(TIP403RegistryError::incompatible_policy_type().into());
440 }
441
442 self.set_policy_set(call.policyId, call.account, call.allowed)?;
443
444 self.emit_event(TIP403RegistryEvent::WhitelistUpdated(
445 ITIP403Registry::WhitelistUpdated {
446 policyId: call.policyId,
447 updater: msg_sender,
448 account: call.account,
449 allowed: call.allowed,
450 },
451 ))
452 }
453
454 pub fn modify_policy_blacklist(
461 &mut self,
462 msg_sender: Address,
463 call: ITIP403Registry::modifyPolicyBlacklistCall,
464 ) -> Result<()> {
465 let data = self.get_policy_data(call.policyId)?;
466
467 if data.admin != msg_sender {
469 return Err(TIP403RegistryError::unauthorized().into());
470 }
471
472 if !matches!(data.policy_type()?, PolicyType::BLACKLIST) {
474 return Err(TIP403RegistryError::incompatible_policy_type().into());
475 }
476
477 self.set_policy_set(call.policyId, call.account, call.restricted)?;
478
479 self.emit_event(TIP403RegistryEvent::BlacklistUpdated(
480 ITIP403Registry::BlacklistUpdated {
481 policyId: call.policyId,
482 updater: msg_sender,
483 account: call.account,
484 restricted: call.restricted,
485 },
486 ))
487 }
488
489 pub fn create_compound_policy(
499 &mut self,
500 msg_sender: Address,
501 call: ITIP403Registry::createCompoundPolicyCall,
502 ) -> Result<u64> {
503 self.validate_simple_policy(call.senderPolicyId)?;
505 self.validate_simple_policy(call.recipientPolicyId)?;
506 self.validate_simple_policy(call.mintRecipientPolicyId)?;
507
508 let new_policy_id = self.policy_id_counter()?;
509
510 self.policy_id_counter.write(
512 new_policy_id
513 .checked_add(1)
514 .ok_or(TempoPrecompileError::under_overflow())?,
515 )?;
516
517 self.policy_records[new_policy_id].write(PolicyRecord {
519 base: PolicyData {
520 policy_type: PolicyType::COMPOUND as u8,
521 admin: Address::ZERO,
522 },
523 compound: CompoundPolicyData {
524 sender_policy_id: call.senderPolicyId,
525 recipient_policy_id: call.recipientPolicyId,
526 mint_recipient_policy_id: call.mintRecipientPolicyId,
527 },
528 })?;
529
530 self.emit_event(TIP403RegistryEvent::CompoundPolicyCreated(
532 ITIP403Registry::CompoundPolicyCreated {
533 policyId: new_policy_id,
534 creator: msg_sender,
535 senderPolicyId: call.senderPolicyId,
536 recipientPolicyId: call.recipientPolicyId,
537 mintRecipientPolicyId: call.mintRecipientPolicyId,
538 },
539 ))?;
540
541 Ok(new_policy_id)
542 }
543
544 pub fn is_authorized_as(&self, policy_id: u64, user: Address, role: AuthRole) -> Result<bool> {
555 if let Some(auth) = self.builtin_authorization(policy_id) {
556 return Ok(auth);
557 }
558
559 let data = self.get_policy_data(policy_id)?;
560
561 if data.is_compound() {
562 let compound = self.policy_records[policy_id].compound.read()?;
563 return match role {
564 AuthRole::Sender => self.is_authorized_simple(compound.sender_policy_id, user),
565 AuthRole::Recipient => {
566 self.is_authorized_simple(compound.recipient_policy_id, user)
567 }
568 AuthRole::MintRecipient => {
569 self.is_authorized_simple(compound.mint_recipient_policy_id, user)
570 }
571 AuthRole::Transfer => {
572 let sender_auth = self.is_authorized_simple(compound.sender_policy_id, user)?;
574 if self.storage.spec().is_t2() && !sender_auth {
575 return Ok(false);
576 }
577 let recipient_auth =
578 self.is_authorized_simple(compound.recipient_policy_id, user)?;
579 Ok(sender_auth && recipient_auth)
580 }
581 };
582 }
583
584 self.is_simple(policy_id, user, &data)
585 }
586
587 #[inline]
590 fn builtin_authorization(&self, policy_id: u64) -> Option<bool> {
591 match policy_id {
592 ALLOW_ALL_POLICY_ID => Some(true),
593 REJECT_ALL_POLICY_ID => Some(false),
594 _ => None,
595 }
596 }
597
598 fn is_authorized_simple(&self, policy_id: u64, user: Address) -> Result<bool> {
602 if let Some(auth) = self.builtin_authorization(policy_id) {
603 return Ok(auth);
604 }
605 let data = self.get_policy_data(policy_id)?;
606 self.is_simple(policy_id, user, &data)
607 }
608
609 fn is_simple(&self, policy_id: u64, user: Address, data: &PolicyData) -> Result<bool> {
611 let is_in_set = self.policy_set[policy_id][user].read()?;
615
616 match data.policy_type()? {
617 PolicyType::WHITELIST => Ok(is_in_set),
618 PolicyType::BLACKLIST => Ok(!is_in_set),
619 PolicyType::COMPOUND => Err(TIP403RegistryError::incompatible_policy_type().into()),
620 PolicyType::__Invalid => unreachable!(),
621 }
622 }
623
624 fn validate_simple_policy(&self, policy_id: u64) -> Result<()> {
626 if self.builtin_authorization(policy_id).is_some() {
628 return Ok(());
629 }
630
631 if policy_id >= self.policy_id_counter()? {
633 return Err(TIP403RegistryError::policy_not_found().into());
634 }
635
636 let data = self.get_policy_data(policy_id)?;
638 if !data.is_simple() {
639 return Err(TIP403RegistryError::policy_not_simple().into());
640 }
641
642 Ok(())
643 }
644
645 fn get_policy_data(&self, policy_id: u64) -> Result<PolicyData> {
650 let data = self.policy_records[policy_id].base.read()?;
651
652 if self.storage.spec().is_t2()
655 && data.is_default()
656 && policy_id >= self.policy_id_counter()?
657 {
658 return Err(TIP403RegistryError::policy_not_found().into());
659 }
660
661 Ok(data)
662 }
663
664 fn set_policy_data(&mut self, policy_id: u64, data: PolicyData) -> Result<()> {
665 self.policy_records[policy_id].base.write(data)
666 }
667
668 fn set_policy_set(&mut self, policy_id: u64, account: Address, value: bool) -> Result<()> {
669 self.policy_set[policy_id][account].write(value)
670 }
671}
672
673impl AuthRole {
674 #[inline]
675 fn transfer_or(t2_variant: Self) -> Self {
676 if StorageCtx.spec().is_t2() {
677 t2_variant
678 } else {
679 Self::Transfer
680 }
681 }
682
683 pub fn transfer() -> Self {
685 Self::Transfer
686 }
687
688 pub fn sender() -> Self {
690 Self::transfer_or(Self::Sender)
691 }
692
693 pub fn recipient() -> Self {
695 Self::transfer_or(Self::Recipient)
696 }
697
698 pub fn mint_recipient() -> Self {
700 Self::transfer_or(Self::MintRecipient)
701 }
702}
703
704pub fn is_policy_lookup_error(e: &TempoPrecompileError) -> bool {
707 if StorageCtx.spec().is_t2() {
708 *e == TIP403RegistryError::invalid_policy_type().into()
710 || *e == TIP403RegistryError::policy_not_found().into()
711 } else {
712 *e == TempoPrecompileError::under_overflow()
714 }
715}
716
717trait PolicyTypeExt {
719 fn ensure_is_simple(&self) -> Result<u8>;
721}
722
723impl PolicyTypeExt for PolicyType {
724 fn ensure_is_simple(&self) -> Result<u8> {
729 match self {
730 Self::WHITELIST | Self::BLACKLIST => Ok(*self as u8),
731 Self::COMPOUND | Self::__Invalid => {
732 if StorageCtx.spec().is_t2() {
733 Err(TIP403RegistryError::incompatible_policy_type().into())
734 } else {
735 Ok(Self::__Invalid as u8)
736 }
737 }
738 }
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745 use crate::{
746 error::TempoPrecompileError,
747 storage::{ContractStorage, StorageCtx, hashmap::HashMapStorageProvider},
748 };
749 use alloy::{
750 primitives::{Address, Log},
751 sol_types::SolEvent,
752 };
753 use rand_08::Rng;
754 use tempo_chainspec::hardfork::TempoHardfork;
755 use tempo_contracts::precompiles::TIP403_REGISTRY_ADDRESS;
756
757 #[test]
758 fn test_create_policy() -> eyre::Result<()> {
759 let mut storage = HashMapStorageProvider::new(1);
760 let admin = Address::random();
761 StorageCtx::enter(&mut storage, || {
762 let mut registry = TIP403Registry::new();
763
764 assert_eq!(registry.policy_id_counter()?, 2);
766
767 let result = registry.create_policy(
769 admin,
770 ITIP403Registry::createPolicyCall {
771 admin,
772 policyType: ITIP403Registry::PolicyType::WHITELIST,
773 },
774 );
775 assert!(result.is_ok());
776 assert_eq!(result?, 2);
777
778 assert_eq!(registry.policy_id_counter()?, 3);
780
781 let data = registry.policy_data(ITIP403Registry::policyDataCall { policyId: 2 })?;
783 assert_eq!(data.policyType, ITIP403Registry::PolicyType::WHITELIST);
784 assert_eq!(data.admin, admin);
785 Ok(())
786 })
787 }
788
789 #[test]
790 fn test_is_authorized_special_policies() -> eyre::Result<()> {
791 let mut storage = HashMapStorageProvider::new(1);
792 let user = Address::random();
793 StorageCtx::enter(&mut storage, || {
794 let registry = TIP403Registry::new();
795
796 assert!(!registry.is_authorized_as(0, user, AuthRole::Transfer)?);
798
799 assert!(registry.is_authorized_as(1, user, AuthRole::Transfer)?);
801 Ok(())
802 })
803 }
804
805 #[test]
806 fn test_whitelist_policy() -> eyre::Result<()> {
807 let mut storage = HashMapStorageProvider::new(1);
808 let admin = Address::random();
809 let user = Address::random();
810 StorageCtx::enter(&mut storage, || {
811 let mut registry = TIP403Registry::new();
812
813 let policy_id = registry.create_policy(
815 admin,
816 ITIP403Registry::createPolicyCall {
817 admin,
818 policyType: ITIP403Registry::PolicyType::WHITELIST,
819 },
820 )?;
821
822 assert!(!registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
824
825 registry.modify_policy_whitelist(
827 admin,
828 ITIP403Registry::modifyPolicyWhitelistCall {
829 policyId: policy_id,
830 account: user,
831 allowed: true,
832 },
833 )?;
834
835 assert!(registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
837
838 Ok(())
839 })
840 }
841
842 #[test]
843 fn test_blacklist_policy() -> eyre::Result<()> {
844 let mut storage = HashMapStorageProvider::new(1);
845 let admin = Address::random();
846 let user = Address::random();
847 StorageCtx::enter(&mut storage, || {
848 let mut registry = TIP403Registry::new();
849
850 let policy_id = registry.create_policy(
852 admin,
853 ITIP403Registry::createPolicyCall {
854 admin,
855 policyType: ITIP403Registry::PolicyType::BLACKLIST,
856 },
857 )?;
858
859 assert!(registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
861
862 registry.modify_policy_blacklist(
864 admin,
865 ITIP403Registry::modifyPolicyBlacklistCall {
866 policyId: policy_id,
867 account: user,
868 restricted: true,
869 },
870 )?;
871
872 assert!(!registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
874
875 Ok(())
876 })
877 }
878
879 #[test]
880 fn test_policy_data_reverts_for_non_existent_policy() -> eyre::Result<()> {
881 let mut storage = HashMapStorageProvider::new(1);
882 StorageCtx::enter(&mut storage, || {
883 let registry = TIP403Registry::new();
884
885 let result = registry.policy_data(ITIP403Registry::policyDataCall { policyId: 100 });
887 assert!(result.is_err());
888
889 assert!(matches!(
891 result.unwrap_err(),
892 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotFound(_))
893 ));
894
895 Ok(())
896 })
897 }
898
899 #[test]
900 fn test_policy_data_builtin_policies_boundary() -> eyre::Result<()> {
901 for (hardfork, expect_allow_all_type) in [
902 (TempoHardfork::T1C, ITIP403Registry::PolicyType::WHITELIST),
904 (TempoHardfork::T2, ITIP403Registry::PolicyType::BLACKLIST),
906 ] {
907 let mut storage = HashMapStorageProvider::new_with_spec(1, hardfork);
908 StorageCtx::enter(&mut storage, || {
909 let registry = TIP403Registry::new();
910
911 let reject = registry.policy_data(ITIP403Registry::policyDataCall {
913 policyId: REJECT_ALL_POLICY_ID,
914 })?;
915 assert_eq!(reject.policyType, ITIP403Registry::PolicyType::WHITELIST);
916 assert_eq!(reject.admin, Address::ZERO);
917
918 let allow = registry.policy_data(ITIP403Registry::policyDataCall {
920 policyId: ALLOW_ALL_POLICY_ID,
921 })?;
922 assert_eq!(allow.policyType, expect_allow_all_type);
923 assert_eq!(allow.admin, Address::ZERO);
924
925 Ok::<_, TempoPrecompileError>(())
926 })?;
927 }
928 Ok(())
929 }
930
931 #[test]
932 fn test_policy_exists() -> eyre::Result<()> {
933 let mut storage = HashMapStorageProvider::new(1);
934 let admin = Address::random();
935 StorageCtx::enter(&mut storage, || {
936 let mut registry = TIP403Registry::new();
937
938 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: 0 })?);
940 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: 1 })?);
941
942 let mut rng = rand_08::thread_rng();
944 for _ in 0..100 {
945 let random_policy_id = rng.gen_range(2..u64::MAX);
946 assert!(!registry.policy_exists(ITIP403Registry::policyExistsCall {
947 policyId: random_policy_id
948 })?);
949 }
950
951 let mut created_policy_ids = Vec::new();
953 for i in 0..50 {
954 let policy_id = registry.create_policy(
955 admin,
956 ITIP403Registry::createPolicyCall {
957 admin,
958 policyType: if i % 2 == 0 {
959 ITIP403Registry::PolicyType::WHITELIST
960 } else {
961 ITIP403Registry::PolicyType::BLACKLIST
962 },
963 },
964 )?;
965 created_policy_ids.push(policy_id);
966 }
967
968 for policy_id in &created_policy_ids {
970 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
971 policyId: *policy_id
972 })?);
973 }
974
975 Ok(())
976 })
977 }
978
979 #[test]
984 fn test_create_compound_policy() -> eyre::Result<()> {
985 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
986 let admin = Address::random();
987 let creator = Address::random();
988 StorageCtx::enter(&mut storage, || {
989 let mut registry = TIP403Registry::new();
990
991 let sender_policy = registry.create_policy(
993 admin,
994 ITIP403Registry::createPolicyCall {
995 admin,
996 policyType: ITIP403Registry::PolicyType::WHITELIST,
997 },
998 )?;
999 let recipient_policy = registry.create_policy(
1000 admin,
1001 ITIP403Registry::createPolicyCall {
1002 admin,
1003 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1004 },
1005 )?;
1006 let mint_recipient_policy = registry.create_policy(
1007 admin,
1008 ITIP403Registry::createPolicyCall {
1009 admin,
1010 policyType: ITIP403Registry::PolicyType::WHITELIST,
1011 },
1012 )?;
1013
1014 let compound_id = registry.create_compound_policy(
1016 creator,
1017 ITIP403Registry::createCompoundPolicyCall {
1018 senderPolicyId: sender_policy,
1019 recipientPolicyId: recipient_policy,
1020 mintRecipientPolicyId: mint_recipient_policy,
1021 },
1022 )?;
1023
1024 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
1026 policyId: compound_id
1027 })?);
1028
1029 let data = registry.policy_data(ITIP403Registry::policyDataCall {
1031 policyId: compound_id,
1032 })?;
1033 assert_eq!(data.policyType, ITIP403Registry::PolicyType::COMPOUND);
1034 assert_eq!(data.admin, Address::ZERO); let compound_data =
1038 registry.compound_policy_data(ITIP403Registry::compoundPolicyDataCall {
1039 policyId: compound_id,
1040 })?;
1041 assert_eq!(compound_data.senderPolicyId, sender_policy);
1042 assert_eq!(compound_data.recipientPolicyId, recipient_policy);
1043 assert_eq!(compound_data.mintRecipientPolicyId, mint_recipient_policy);
1044
1045 Ok(())
1046 })
1047 }
1048
1049 #[test]
1050 fn test_compound_policy_rejects_non_existent_refs() -> eyre::Result<()> {
1051 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1052 let creator = Address::random();
1053 StorageCtx::enter(&mut storage, || {
1054 let mut registry = TIP403Registry::new();
1055
1056 let result = registry.create_compound_policy(
1058 creator,
1059 ITIP403Registry::createCompoundPolicyCall {
1060 senderPolicyId: 999,
1061 recipientPolicyId: 1,
1062 mintRecipientPolicyId: 1,
1063 },
1064 );
1065 assert!(result.is_err());
1066
1067 Ok(())
1068 })
1069 }
1070
1071 #[test]
1072 fn test_compound_policy_rejects_compound_refs() -> eyre::Result<()> {
1073 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1074 let admin = Address::random();
1075 let creator = Address::random();
1076 StorageCtx::enter(&mut storage, || {
1077 let mut registry = TIP403Registry::new();
1078
1079 let simple_policy = registry.create_policy(
1081 admin,
1082 ITIP403Registry::createPolicyCall {
1083 admin,
1084 policyType: ITIP403Registry::PolicyType::WHITELIST,
1085 },
1086 )?;
1087
1088 let compound_id = registry.create_compound_policy(
1090 creator,
1091 ITIP403Registry::createCompoundPolicyCall {
1092 senderPolicyId: 1,
1093 recipientPolicyId: simple_policy,
1094 mintRecipientPolicyId: 1,
1095 },
1096 )?;
1097
1098 let result = registry.create_compound_policy(
1100 creator,
1101 ITIP403Registry::createCompoundPolicyCall {
1102 senderPolicyId: compound_id, recipientPolicyId: 1,
1104 mintRecipientPolicyId: 1,
1105 },
1106 );
1107 assert!(result.is_err());
1108
1109 Ok(())
1110 })
1111 }
1112
1113 #[test]
1114 fn test_compound_policy_sender_recipient_differentiation() -> eyre::Result<()> {
1115 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1116 let admin = Address::random();
1117 let creator = Address::random();
1118 let alice = Address::random();
1119 let bob = Address::random();
1120 StorageCtx::enter(&mut storage, || {
1121 let mut registry = TIP403Registry::new();
1122
1123 let sender_policy = registry.create_policy(
1125 admin,
1126 ITIP403Registry::createPolicyCall {
1127 admin,
1128 policyType: ITIP403Registry::PolicyType::WHITELIST,
1129 },
1130 )?;
1131 registry.modify_policy_whitelist(
1132 admin,
1133 ITIP403Registry::modifyPolicyWhitelistCall {
1134 policyId: sender_policy,
1135 account: alice,
1136 allowed: true,
1137 },
1138 )?;
1139
1140 let recipient_policy = registry.create_policy(
1142 admin,
1143 ITIP403Registry::createPolicyCall {
1144 admin,
1145 policyType: ITIP403Registry::PolicyType::WHITELIST,
1146 },
1147 )?;
1148 registry.modify_policy_whitelist(
1149 admin,
1150 ITIP403Registry::modifyPolicyWhitelistCall {
1151 policyId: recipient_policy,
1152 account: bob,
1153 allowed: true,
1154 },
1155 )?;
1156
1157 let compound_id = registry.create_compound_policy(
1159 creator,
1160 ITIP403Registry::createCompoundPolicyCall {
1161 senderPolicyId: sender_policy,
1162 recipientPolicyId: recipient_policy,
1163 mintRecipientPolicyId: 1, },
1165 )?;
1166
1167 assert!(registry.is_authorized_as(compound_id, alice, AuthRole::Sender)?);
1169
1170 assert!(!registry.is_authorized_as(compound_id, bob, AuthRole::Sender)?);
1172
1173 assert!(registry.is_authorized_as(compound_id, bob, AuthRole::Recipient)?);
1175
1176 assert!(!registry.is_authorized_as(compound_id, alice, AuthRole::Recipient)?);
1178
1179 assert!(registry.is_authorized_as(compound_id, alice, AuthRole::MintRecipient)?);
1181 assert!(registry.is_authorized_as(compound_id, bob, AuthRole::MintRecipient)?);
1182
1183 Ok(())
1184 })
1185 }
1186
1187 #[test]
1188 fn test_compound_policy_is_authorized_behavior() -> eyre::Result<()> {
1189 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1190 let admin = Address::random();
1191 let creator = Address::random();
1192 let user = Address::random();
1193 StorageCtx::enter(&mut storage, || {
1194 let mut registry = TIP403Registry::new();
1195
1196 let sender_policy = registry.create_policy(
1198 admin,
1199 ITIP403Registry::createPolicyCall {
1200 admin,
1201 policyType: ITIP403Registry::PolicyType::WHITELIST,
1202 },
1203 )?;
1204 registry.modify_policy_whitelist(
1205 admin,
1206 ITIP403Registry::modifyPolicyWhitelistCall {
1207 policyId: sender_policy,
1208 account: user,
1209 allowed: true,
1210 },
1211 )?;
1212
1213 let recipient_policy = registry.create_policy(
1215 admin,
1216 ITIP403Registry::createPolicyCall {
1217 admin,
1218 policyType: ITIP403Registry::PolicyType::WHITELIST,
1219 },
1220 )?;
1221
1222 let compound_id = registry.create_compound_policy(
1224 creator,
1225 ITIP403Registry::createCompoundPolicyCall {
1226 senderPolicyId: sender_policy,
1227 recipientPolicyId: recipient_policy,
1228 mintRecipientPolicyId: 1,
1229 },
1230 )?;
1231
1232 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Sender)?);
1235 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Recipient)?);
1236
1237 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1239
1240 registry.modify_policy_whitelist(
1242 admin,
1243 ITIP403Registry::modifyPolicyWhitelistCall {
1244 policyId: recipient_policy,
1245 account: user,
1246 allowed: true,
1247 },
1248 )?;
1249
1250 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1252
1253 Ok(())
1254 })
1255 }
1256
1257 #[test]
1258 fn test_compound_policy_is_authorized_transfer() -> eyre::Result<()> {
1259 let admin = Address::random();
1260 let creator = Address::random();
1261 let user = Address::random();
1262
1263 for hardfork in [TempoHardfork::T0, TempoHardfork::T1] {
1264 let mut storage = HashMapStorageProvider::new_with_spec(1, hardfork);
1265
1266 StorageCtx::enter(&mut storage, || {
1267 let mut registry = TIP403Registry::new();
1268
1269 let sender_policy = registry.create_policy(
1271 admin,
1272 ITIP403Registry::createPolicyCall {
1273 admin,
1274 policyType: ITIP403Registry::PolicyType::WHITELIST,
1275 },
1276 )?;
1277 let recipient_policy = registry.create_policy(
1278 admin,
1279 ITIP403Registry::createPolicyCall {
1280 admin,
1281 policyType: ITIP403Registry::PolicyType::WHITELIST,
1282 },
1283 )?;
1284
1285 let compound_id = registry.create_compound_policy(
1287 creator,
1288 ITIP403Registry::createCompoundPolicyCall {
1289 senderPolicyId: sender_policy,
1290 recipientPolicyId: recipient_policy,
1291 mintRecipientPolicyId: 1,
1292 },
1293 )?;
1294
1295 registry.modify_policy_whitelist(
1297 admin,
1298 ITIP403Registry::modifyPolicyWhitelistCall {
1299 policyId: recipient_policy,
1300 account: user,
1301 allowed: true,
1302 },
1303 )?;
1304 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1305
1306 registry.modify_policy_whitelist(
1308 admin,
1309 ITIP403Registry::modifyPolicyWhitelistCall {
1310 policyId: sender_policy,
1311 account: user,
1312 allowed: true,
1313 },
1314 )?;
1315 registry.modify_policy_whitelist(
1316 admin,
1317 ITIP403Registry::modifyPolicyWhitelistCall {
1318 policyId: recipient_policy,
1319 account: user,
1320 allowed: false,
1321 },
1322 )?;
1323 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1324
1325 registry.modify_policy_whitelist(
1327 admin,
1328 ITIP403Registry::modifyPolicyWhitelistCall {
1329 policyId: recipient_policy,
1330 account: user,
1331 allowed: true,
1332 },
1333 )?;
1334 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1335
1336 Ok::<_, TempoPrecompileError>(())
1337 })?;
1338 }
1339
1340 Ok(())
1341 }
1342
1343 #[test]
1344 fn test_simple_policy_equivalence() -> eyre::Result<()> {
1345 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1346 let admin = Address::random();
1347 let user = Address::random();
1348 StorageCtx::enter(&mut storage, || {
1349 let mut registry = TIP403Registry::new();
1350
1351 let policy_id = registry.create_policy(
1353 admin,
1354 ITIP403Registry::createPolicyCall {
1355 admin,
1356 policyType: ITIP403Registry::PolicyType::WHITELIST,
1357 },
1358 )?;
1359 registry.modify_policy_whitelist(
1360 admin,
1361 ITIP403Registry::modifyPolicyWhitelistCall {
1362 policyId: policy_id,
1363 account: user,
1364 allowed: true,
1365 },
1366 )?;
1367
1368 let is_authorized = registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?;
1370 let is_sender = registry.is_authorized_as(policy_id, user, AuthRole::Sender)?;
1371 let is_recipient = registry.is_authorized_as(policy_id, user, AuthRole::Recipient)?;
1372 let is_mint_recipient =
1373 registry.is_authorized_as(policy_id, user, AuthRole::MintRecipient)?;
1374
1375 assert!(is_authorized);
1376 assert_eq!(is_authorized, is_sender);
1377 assert_eq!(is_sender, is_recipient);
1378 assert_eq!(is_recipient, is_mint_recipient);
1379
1380 Ok(())
1381 })
1382 }
1383
1384 #[test]
1385 fn test_compound_policy_with_builtin_policies() -> eyre::Result<()> {
1386 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1387 let creator = Address::random();
1388 let user = Address::random();
1389 StorageCtx::enter(&mut storage, || {
1390 let mut registry = TIP403Registry::new();
1391
1392 let compound_id = registry.create_compound_policy(
1397 creator,
1398 ITIP403Registry::createCompoundPolicyCall {
1399 senderPolicyId: 1,
1400 recipientPolicyId: 0,
1401 mintRecipientPolicyId: 1,
1402 },
1403 )?;
1404
1405 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Sender)?);
1407
1408 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Recipient)?);
1410
1411 assert!(registry.is_authorized_as(compound_id, user, AuthRole::MintRecipient)?);
1413
1414 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1416
1417 Ok(())
1418 })
1419 }
1420
1421 #[test]
1422 fn test_vendor_credits_use_case() -> eyre::Result<()> {
1423 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1424 let admin = Address::random();
1425 let creator = Address::random();
1426 let vendor = Address::random();
1427 let customer = Address::random();
1428 StorageCtx::enter(&mut storage, || {
1429 let mut registry = TIP403Registry::new();
1430
1431 let vendor_whitelist = registry.create_policy(
1433 admin,
1434 ITIP403Registry::createPolicyCall {
1435 admin,
1436 policyType: ITIP403Registry::PolicyType::WHITELIST,
1437 },
1438 )?;
1439 registry.modify_policy_whitelist(
1440 admin,
1441 ITIP403Registry::modifyPolicyWhitelistCall {
1442 policyId: vendor_whitelist,
1443 account: vendor,
1444 allowed: true,
1445 },
1446 )?;
1447
1448 let compound_id = registry.create_compound_policy(
1453 creator,
1454 ITIP403Registry::createCompoundPolicyCall {
1455 senderPolicyId: 1, recipientPolicyId: vendor_whitelist, mintRecipientPolicyId: 1, },
1459 )?;
1460
1461 assert!(registry.is_authorized_as(compound_id, customer, AuthRole::MintRecipient)?);
1463
1464 assert!(registry.is_authorized_as(compound_id, customer, AuthRole::Sender)?);
1466
1467 assert!(registry.is_authorized_as(compound_id, vendor, AuthRole::Recipient)?);
1469 assert!(!registry.is_authorized_as(compound_id, customer, AuthRole::Recipient)?);
1471
1472 Ok(())
1473 })
1474 }
1475
1476 #[test]
1477 fn test_policy_data_rejects_compound_policy_on_pre_t1() -> eyre::Result<()> {
1478 let creator = Address::random();
1479
1480 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1482 let compound_id = StorageCtx::enter(&mut storage, || {
1483 let mut registry = TIP403Registry::new();
1484 registry.create_compound_policy(
1485 creator,
1486 ITIP403Registry::createCompoundPolicyCall {
1487 senderPolicyId: 1,
1488 recipientPolicyId: 1,
1489 mintRecipientPolicyId: 1,
1490 },
1491 )
1492 })?;
1493
1494 let mut storage = storage.with_spec(TempoHardfork::T0);
1496 StorageCtx::enter(&mut storage, || {
1497 let registry = TIP403Registry::new();
1498
1499 let result = registry.policy_data(ITIP403Registry::policyDataCall {
1500 policyId: compound_id,
1501 });
1502 assert!(result.is_err());
1503 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
1504
1505 Ok(())
1506 })
1507 }
1508
1509 #[test]
1510 fn test_create_policy_rejects_non_simple_policy_types() -> eyre::Result<()> {
1511 let admin = Address::random();
1512
1513 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
1514 StorageCtx::enter(&mut storage, || {
1515 let mut registry = TIP403Registry::new();
1516
1517 for policy_type in [
1518 ITIP403Registry::PolicyType::COMPOUND,
1519 ITIP403Registry::PolicyType::__Invalid,
1520 ] {
1521 let result = registry.create_policy(
1522 admin,
1523 ITIP403Registry::createPolicyCall {
1524 admin,
1525 policyType: policy_type,
1526 },
1527 );
1528 assert!(matches!(
1529 result.unwrap_err(),
1530 TempoPrecompileError::TIP403RegistryError(
1531 TIP403RegistryError::IncompatiblePolicyType(_)
1532 )
1533 ));
1534 }
1535
1536 Ok(())
1537 })
1538 }
1539
1540 #[test]
1541 fn test_create_policy_with_accounts_rejects_non_simple_policy_types() -> eyre::Result<()> {
1542 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1543 let admin = Address::random();
1544 let account = Address::random();
1545 StorageCtx::enter(&mut storage, || {
1546 let mut registry = TIP403Registry::new();
1547
1548 for policy_type in [
1549 ITIP403Registry::PolicyType::COMPOUND,
1550 ITIP403Registry::PolicyType::__Invalid,
1551 ] {
1552 let result = registry.create_policy_with_accounts(
1553 admin,
1554 ITIP403Registry::createPolicyWithAccountsCall {
1555 admin,
1556 policyType: policy_type,
1557 accounts: vec![account],
1558 },
1559 );
1560 assert!(matches!(
1561 result.unwrap_err(),
1562 TempoPrecompileError::TIP403RegistryError(
1563 TIP403RegistryError::IncompatiblePolicyType(_)
1564 )
1565 ));
1566 }
1567
1568 Ok(())
1569 })
1570 }
1571
1572 #[test]
1577 fn test_pre_t1_create_policy_with_invalid_type_stores_255() -> eyre::Result<()> {
1578 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1579 let admin = Address::random();
1580 StorageCtx::enter(&mut storage, || {
1581 let mut registry = TIP403Registry::new();
1582
1583 for policy_type in [
1585 ITIP403Registry::PolicyType::COMPOUND,
1586 ITIP403Registry::PolicyType::__Invalid,
1587 ] {
1588 let policy_id = registry.create_policy(
1589 admin,
1590 ITIP403Registry::createPolicyCall {
1591 admin,
1592 policyType: policy_type,
1593 },
1594 )?;
1595
1596 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
1598 policyId: policy_id
1599 })?);
1600
1601 let data = registry.get_policy_data(policy_id)?;
1603 assert_eq!(data.policy_type, 255u8);
1604 }
1605
1606 Ok(())
1607 })
1608 }
1609
1610 #[test]
1611 fn test_pre_t1_create_policy_with_valid_types_stores_correct_value() -> eyre::Result<()> {
1612 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1613 let admin = Address::random();
1614 StorageCtx::enter(&mut storage, || {
1615 let mut registry = TIP403Registry::new();
1616
1617 let whitelist_id = registry.create_policy(
1619 admin,
1620 ITIP403Registry::createPolicyCall {
1621 admin,
1622 policyType: ITIP403Registry::PolicyType::WHITELIST,
1623 },
1624 )?;
1625 let data = registry.get_policy_data(whitelist_id)?;
1626 assert_eq!(data.policy_type, 0u8);
1627
1628 let blacklist_id = registry.create_policy(
1630 admin,
1631 ITIP403Registry::createPolicyCall {
1632 admin,
1633 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1634 },
1635 )?;
1636 let data = registry.get_policy_data(blacklist_id)?;
1637 assert_eq!(data.policy_type, 1u8);
1638
1639 Ok(())
1640 })
1641 }
1642
1643 #[test]
1644 fn test_pre_t1_create_policy_with_accounts_invalid_type_behavior() -> eyre::Result<()> {
1645 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1646 let (admin, account) = (Address::random(), Address::random());
1647
1648 StorageCtx::enter(&mut storage, || {
1649 let mut registry = TIP403Registry::new();
1650
1651 for policy_type in [
1653 ITIP403Registry::PolicyType::COMPOUND,
1654 ITIP403Registry::PolicyType::__Invalid,
1655 ] {
1656 let result = registry.create_policy_with_accounts(
1657 admin,
1658 ITIP403Registry::createPolicyWithAccountsCall {
1659 admin,
1660 policyType: policy_type,
1661 accounts: vec![account],
1662 },
1663 );
1664 assert!(matches!(
1665 result.unwrap_err(),
1666 TempoPrecompileError::TIP403RegistryError(
1667 TIP403RegistryError::IncompatiblePolicyType(_)
1668 )
1669 ));
1670 }
1671
1672 let policy_id = registry.create_policy_with_accounts(
1674 admin,
1675 ITIP403Registry::createPolicyWithAccountsCall {
1676 admin,
1677 policyType: ITIP403Registry::PolicyType::__Invalid,
1678 accounts: vec![],
1679 },
1680 )?;
1681 let data = registry.get_policy_data(policy_id)?;
1682 assert_eq!(data.policy_type, 255u8);
1683
1684 Ok(())
1685 })
1686 }
1687
1688 #[test]
1689 fn test_pre_t1_policy_data_reverts_for_any_policy_type_gte_2() -> eyre::Result<()> {
1690 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1691 let admin = Address::random();
1692 StorageCtx::enter(&mut storage, || {
1693 let mut registry = TIP403Registry::new();
1694
1695 let policy_id = registry.create_policy(
1697 admin,
1698 ITIP403Registry::createPolicyCall {
1699 admin,
1700 policyType: ITIP403Registry::PolicyType::COMPOUND,
1701 },
1702 )?;
1703
1704 let result = registry.policy_data(ITIP403Registry::policyDataCall {
1706 policyId: policy_id,
1707 });
1708 assert!(result.is_err());
1709 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
1710
1711 Ok(())
1712 })
1713 }
1714
1715 #[test]
1716 fn test_pre_t1_is_authorized_reverts_for_invalid_policy_type() -> eyre::Result<()> {
1717 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1718 let admin = Address::random();
1719 let user = Address::random();
1720 StorageCtx::enter(&mut storage, || {
1721 let mut registry = TIP403Registry::new();
1722
1723 let policy_id = registry.create_policy(
1725 admin,
1726 ITIP403Registry::createPolicyCall {
1727 admin,
1728 policyType: ITIP403Registry::PolicyType::COMPOUND,
1729 },
1730 )?;
1731
1732 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
1734 assert!(result.is_err());
1735 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
1736
1737 Ok(())
1738 })
1739 }
1740
1741 #[test]
1742 fn test_pre_t2_to_t2_migration_invalid_policy_still_fails() -> eyre::Result<()> {
1743 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1745 let admin = Address::random();
1746 let user = Address::random();
1747
1748 let policy_id = StorageCtx::enter(&mut storage, || {
1749 let mut registry = TIP403Registry::new();
1750 registry.create_policy(
1751 admin,
1752 ITIP403Registry::createPolicyCall {
1753 admin,
1754 policyType: ITIP403Registry::PolicyType::COMPOUND,
1755 },
1756 )
1757 })?;
1758
1759 let mut storage = storage.with_spec(TempoHardfork::T2);
1761 StorageCtx::enter(&mut storage, || {
1762 let registry = TIP403Registry::new();
1763
1764 let result = registry.policy_data(ITIP403Registry::policyDataCall {
1766 policyId: policy_id,
1767 });
1768 assert!(result.is_err());
1769 assert_eq!(
1770 result.unwrap_err(),
1771 TIP403RegistryError::invalid_policy_type().into()
1772 );
1773
1774 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
1776 assert!(result.is_err());
1777 assert_eq!(
1778 result.unwrap_err(),
1779 TIP403RegistryError::invalid_policy_type().into()
1780 );
1781
1782 Ok(())
1783 })
1784 }
1785
1786 #[test]
1787 fn test_t2_compound_policy_rejects_legacy_invalid_255_policy() -> eyre::Result<()> {
1788 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1790 let admin = Address::random();
1791 let creator = Address::random();
1792
1793 let invalid_policy_id = StorageCtx::enter(&mut storage, || {
1794 let mut registry = TIP403Registry::new();
1795 registry.create_policy(
1796 admin,
1797 ITIP403Registry::createPolicyCall {
1798 admin,
1799 policyType: ITIP403Registry::PolicyType::__Invalid,
1800 },
1801 )
1802 })?;
1803
1804 let mut storage = storage.with_spec(TempoHardfork::T2);
1806 StorageCtx::enter(&mut storage, || {
1807 let mut registry = TIP403Registry::new();
1808
1809 let valid_policy_id = registry.create_policy(
1810 admin,
1811 ITIP403Registry::createPolicyCall {
1812 admin,
1813 policyType: ITIP403Registry::PolicyType::WHITELIST,
1814 },
1815 )?;
1816
1817 let result = registry.create_compound_policy(
1819 creator,
1820 ITIP403Registry::createCompoundPolicyCall {
1821 senderPolicyId: invalid_policy_id,
1822 recipientPolicyId: valid_policy_id,
1823 mintRecipientPolicyId: valid_policy_id,
1824 },
1825 );
1826 assert!(matches!(
1827 result.unwrap_err(),
1828 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotSimple(_))
1829 ));
1830
1831 Ok(())
1832 })
1833 }
1834
1835 #[test]
1836 fn test_t2_validate_policy_type_returns_correct_u8() -> eyre::Result<()> {
1837 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
1838 let admin = Address::random();
1839 StorageCtx::enter(&mut storage, || {
1840 let mut registry = TIP403Registry::new();
1841
1842 let whitelist_id = registry.create_policy(
1844 admin,
1845 ITIP403Registry::createPolicyCall {
1846 admin,
1847 policyType: ITIP403Registry::PolicyType::WHITELIST,
1848 },
1849 )?;
1850 let data = registry.get_policy_data(whitelist_id)?;
1851 assert_eq!(data.policy_type, 0u8);
1852
1853 let blacklist_id = registry.create_policy(
1855 admin,
1856 ITIP403Registry::createPolicyCall {
1857 admin,
1858 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1859 },
1860 )?;
1861 let data = registry.get_policy_data(blacklist_id)?;
1862 assert_eq!(data.policy_type, 1u8);
1863
1864 Ok(())
1865 })
1866 }
1867
1868 #[test]
1869 fn test_is_simple_errors_on_invalid_policy_type_t2() -> eyre::Result<()> {
1870 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1874 let admin = Address::random();
1875 let user = Address::random();
1876
1877 let policy_id = StorageCtx::enter(&mut storage, || {
1879 let mut registry = TIP403Registry::new();
1880 registry.create_policy(
1881 admin,
1882 ITIP403Registry::createPolicyCall {
1883 admin,
1884 policyType: ITIP403Registry::PolicyType::COMPOUND,
1885 },
1886 )
1887 })?;
1888
1889 let mut storage = storage.with_spec(TempoHardfork::T2);
1891 StorageCtx::enter(&mut storage, || {
1892 let registry = TIP403Registry::new();
1893
1894 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
1895 assert_eq!(
1896 result.unwrap_err(),
1897 TIP403RegistryError::invalid_policy_type().into()
1898 );
1899
1900 Ok(())
1901 })
1902 }
1903
1904 #[test]
1905 fn test_pre_t1_whitelist_and_blacklist_work_normally() -> eyre::Result<()> {
1906 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1907 let admin = Address::random();
1908 let user = Address::random();
1909 StorageCtx::enter(&mut storage, || {
1910 let mut registry = TIP403Registry::new();
1911
1912 let whitelist_id = registry.create_policy(
1914 admin,
1915 ITIP403Registry::createPolicyCall {
1916 admin,
1917 policyType: ITIP403Registry::PolicyType::WHITELIST,
1918 },
1919 )?;
1920
1921 assert!(!registry.is_authorized_as(whitelist_id, user, AuthRole::Transfer)?);
1923
1924 registry.modify_policy_whitelist(
1926 admin,
1927 ITIP403Registry::modifyPolicyWhitelistCall {
1928 policyId: whitelist_id,
1929 account: user,
1930 allowed: true,
1931 },
1932 )?;
1933
1934 assert!(registry.is_authorized_as(whitelist_id, user, AuthRole::Transfer)?);
1936
1937 let blacklist_id = registry.create_policy(
1939 admin,
1940 ITIP403Registry::createPolicyCall {
1941 admin,
1942 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1943 },
1944 )?;
1945
1946 assert!(registry.is_authorized_as(blacklist_id, user, AuthRole::Transfer)?);
1948
1949 registry.modify_policy_blacklist(
1951 admin,
1952 ITIP403Registry::modifyPolicyBlacklistCall {
1953 policyId: blacklist_id,
1954 account: user,
1955 restricted: true,
1956 },
1957 )?;
1958
1959 assert!(!registry.is_authorized_as(blacklist_id, user, AuthRole::Transfer)?);
1961
1962 Ok(())
1963 })
1964 }
1965
1966 #[test]
1967 fn test_pre_t1_create_policy_event_emits_invalid() -> eyre::Result<()> {
1968 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1969 let admin = Address::random();
1970
1971 StorageCtx::enter(&mut storage, || {
1972 let mut registry = TIP403Registry::new();
1973
1974 let policy_id = registry.create_policy(
1975 admin,
1976 ITIP403Registry::createPolicyCall {
1977 admin,
1978 policyType: ITIP403Registry::PolicyType::COMPOUND,
1979 },
1980 )?;
1981
1982 let data = registry.get_policy_data(policy_id)?;
1983 assert_eq!(data.policy_type, 255u8);
1984
1985 Ok::<_, TempoPrecompileError>(())
1986 })?;
1987
1988 let events = storage.events.get(&TIP403_REGISTRY_ADDRESS).unwrap();
1989 let policy_created_log = Log::new_unchecked(
1990 TIP403_REGISTRY_ADDRESS,
1991 events[0].topics().to_vec(),
1992 events[0].data.clone(),
1993 );
1994 let decoded = ITIP403Registry::PolicyCreated::decode_log(&policy_created_log)?;
1995
1996 assert_eq!(decoded.policyType, ITIP403Registry::PolicyType::__Invalid);
1998
1999 Ok(())
2000 }
2001
2002 #[test]
2003 fn test_t2_create_policy_rejects_invalid_types() -> eyre::Result<()> {
2004 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2005 let admin = Address::random();
2006
2007 StorageCtx::enter(&mut storage, || {
2008 let mut registry = TIP403Registry::new();
2009
2010 for policy_type in [
2011 ITIP403Registry::PolicyType::COMPOUND,
2012 ITIP403Registry::PolicyType::__Invalid,
2013 ] {
2014 let result = registry.create_policy(
2015 admin,
2016 ITIP403Registry::createPolicyCall {
2017 admin,
2018 policyType: policy_type,
2019 },
2020 );
2021 assert!(matches!(
2022 result.unwrap_err(),
2023 TempoPrecompileError::TIP403RegistryError(
2024 TIP403RegistryError::IncompatiblePolicyType(_)
2025 )
2026 ));
2027 }
2028
2029 Ok(())
2030 })
2031 }
2032
2033 #[test]
2034 fn test_t2_create_policy_emits_correct_type() -> eyre::Result<()> {
2035 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2036 let admin = Address::random();
2037
2038 StorageCtx::enter(&mut storage, || {
2039 let mut registry = TIP403Registry::new();
2040
2041 registry.create_policy(
2042 admin,
2043 ITIP403Registry::createPolicyCall {
2044 admin,
2045 policyType: ITIP403Registry::PolicyType::WHITELIST,
2046 },
2047 )?;
2048
2049 registry.create_policy(
2050 admin,
2051 ITIP403Registry::createPolicyCall {
2052 admin,
2053 policyType: ITIP403Registry::PolicyType::BLACKLIST,
2054 },
2055 )?;
2056
2057 Ok::<_, TempoPrecompileError>(())
2058 })?;
2059
2060 let events = storage.events.get(&TIP403_REGISTRY_ADDRESS).unwrap();
2061
2062 let whitelist_log = Log::new_unchecked(
2064 TIP403_REGISTRY_ADDRESS,
2065 events[0].topics().to_vec(),
2066 events[0].data.clone(),
2067 );
2068 let whitelist_decoded = ITIP403Registry::PolicyCreated::decode_log(&whitelist_log)?;
2069 assert_eq!(
2070 whitelist_decoded.policyType,
2071 ITIP403Registry::PolicyType::WHITELIST
2072 );
2073
2074 let blacklist_log = Log::new_unchecked(
2075 TIP403_REGISTRY_ADDRESS,
2076 events[2].topics().to_vec(),
2077 events[2].data.clone(),
2078 );
2079 let blacklist_decoded = ITIP403Registry::PolicyCreated::decode_log(&blacklist_log)?;
2080 assert_eq!(
2081 blacklist_decoded.policyType,
2082 ITIP403Registry::PolicyType::BLACKLIST
2083 );
2084
2085 Ok(())
2086 }
2087
2088 #[test]
2089 fn test_compound_policy_data_error_cases() -> eyre::Result<()> {
2090 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2091 let admin = Address::random();
2092 StorageCtx::enter(&mut storage, || {
2093 let mut registry = TIP403Registry::new();
2094
2095 let result = registry
2097 .compound_policy_data(ITIP403Registry::compoundPolicyDataCall { policyId: 999 });
2098 assert!(matches!(
2099 result.unwrap_err(),
2100 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotFound(_))
2101 ));
2102
2103 let simple_policy_id = registry.create_policy(
2105 admin,
2106 ITIP403Registry::createPolicyCall {
2107 admin,
2108 policyType: ITIP403Registry::PolicyType::WHITELIST,
2109 },
2110 )?;
2111 let result = registry.compound_policy_data(ITIP403Registry::compoundPolicyDataCall {
2112 policyId: simple_policy_id,
2113 });
2114 assert!(matches!(
2115 result.unwrap_err(),
2116 TempoPrecompileError::TIP403RegistryError(
2117 TIP403RegistryError::IncompatiblePolicyType(_)
2118 )
2119 ));
2120
2121 Ok(())
2122 })
2123 }
2124
2125 #[test]
2126 fn test_invalid_policy_type() -> eyre::Result<()> {
2127 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2129 let admin = Address::random();
2130 let user = Address::random();
2131
2132 let policy_id = StorageCtx::enter(&mut storage, || {
2133 let mut registry = TIP403Registry::new();
2134 registry.create_policy(
2135 admin,
2136 ITIP403Registry::createPolicyCall {
2137 admin,
2138 policyType: ITIP403Registry::PolicyType::__Invalid,
2139 },
2140 )
2141 })?;
2142
2143 StorageCtx::enter(&mut storage, || {
2145 let registry = TIP403Registry::new();
2146
2147 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2148 policyId: policy_id,
2149 });
2150 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2151
2152 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2153 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2154
2155 Ok::<_, TempoPrecompileError>(())
2156 })?;
2157
2158 let mut storage = storage.with_spec(TempoHardfork::T2);
2160 StorageCtx::enter(&mut storage, || {
2161 let registry = TIP403Registry::new();
2162
2163 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2164 policyId: policy_id,
2165 });
2166 assert_eq!(
2167 result.unwrap_err(),
2168 TIP403RegistryError::invalid_policy_type().into()
2169 );
2170
2171 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2172 assert_eq!(
2173 result.unwrap_err(),
2174 TIP403RegistryError::invalid_policy_type().into()
2175 );
2176
2177 Ok(())
2178 })
2179 }
2180
2181 #[test]
2182 fn test_policy_data_encode_to_slot_returns_correct_value() -> eyre::Result<()> {
2183 let admin = Address::random();
2184 let policy_data = PolicyData {
2185 policy_type: 0, admin,
2187 };
2188
2189 let encoded = policy_data.encode_to_slot();
2190
2191 let decoded = PolicyData::decode_from_slot(encoded);
2193 assert_eq!(decoded.policy_type, policy_data.policy_type);
2194 assert_eq!(decoded.admin, policy_data.admin);
2195
2196 assert_ne!(
2198 encoded,
2199 U256::ZERO,
2200 "encode_to_slot should return non-default value for valid policy data"
2201 );
2202
2203 Ok(())
2204 }
2205
2206 #[test]
2207 fn test_initialize_sets_storage_state() -> eyre::Result<()> {
2208 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2209 StorageCtx::enter(&mut storage, || {
2210 let mut registry = TIP403Registry::new();
2211
2212 assert!(!registry.is_initialized()?);
2214
2215 registry.initialize()?;
2217
2218 assert!(registry.is_initialized()?);
2220
2221 let registry2 = TIP403Registry::new();
2223 assert!(registry2.is_initialized()?);
2224
2225 Ok(())
2226 })
2227 }
2228
2229 #[test]
2230 fn test_policy_exists_boundary_at_counter() -> eyre::Result<()> {
2231 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2232 let admin = Address::random();
2233 StorageCtx::enter(&mut storage, || {
2234 let mut registry = TIP403Registry::new();
2235
2236 let policy_id = registry.create_policy(
2238 admin,
2239 ITIP403Registry::createPolicyCall {
2240 admin,
2241 policyType: ITIP403Registry::PolicyType::WHITELIST,
2242 },
2243 )?;
2244
2245 let counter = registry.policy_id_counter()?;
2247 assert_eq!(counter, 3);
2248
2249 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
2251 policyId: policy_id,
2252 })?);
2253
2254 assert!(
2256 !registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: counter })?
2257 );
2258
2259 assert!(!registry.policy_exists(ITIP403Registry::policyExistsCall {
2261 policyId: counter + 1,
2262 })?);
2263
2264 Ok(())
2265 })
2266 }
2267
2268 #[test]
2269 fn test_nonexistent_policy_behavior() -> eyre::Result<()> {
2270 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
2271 let user = Address::random();
2272 let nonexistent_id = 999;
2273
2274 StorageCtx::enter(&mut storage, || -> Result<()> {
2276 let registry = TIP403Registry::new();
2277 let data = registry.get_policy_data(nonexistent_id)?;
2278 assert!(data.is_default());
2279 assert!(!registry.is_authorized_as(nonexistent_id, user, AuthRole::Transfer)?);
2280 Ok(())
2281 })?;
2282
2283 let mut storage = storage.with_spec(TempoHardfork::T2);
2285 StorageCtx::enter(&mut storage, || {
2286 let registry = TIP403Registry::new();
2287 assert_eq!(
2288 registry.get_policy_data(nonexistent_id).unwrap_err(),
2289 TIP403RegistryError::policy_not_found().into()
2290 );
2291 assert_eq!(
2292 registry
2293 .is_authorized_as(nonexistent_id, user, AuthRole::Transfer)
2294 .unwrap_err(),
2295 TIP403RegistryError::policy_not_found().into()
2296 );
2297 Ok(())
2298 })
2299 }
2300}