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;
23use tempo_primitives::TempoAddressExt;
24
25pub const REJECT_ALL_POLICY_ID: u64 = 0;
27
28pub const ALLOW_ALL_POLICY_ID: u64 = 1;
30
31#[contract(addr = TIP403_REGISTRY_ADDRESS)]
39pub struct TIP403Registry {
40 policy_id_counter: u64,
44 policy_records: Mapping<u64, PolicyRecord>,
47 policy_set: Mapping<u64, Mapping<Address, bool>>,
51}
52
53#[derive(Debug, Clone, Storable)]
57pub struct PolicyRecord {
58 pub base: PolicyData,
60 pub compound: CompoundPolicyData,
62}
63
64#[derive(Debug, Clone, Default, Storable)]
68pub struct CompoundPolicyData {
69 pub sender_policy_id: u64,
71 pub recipient_policy_id: u64,
73 pub mint_recipient_policy_id: u64,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum AuthRole {
83 Transfer,
85 Sender,
87 Recipient,
89 MintRecipient,
91}
92
93#[derive(Debug, Clone, Storable)]
95pub struct PolicyData {
96 pub policy_type: u8,
99 pub admin: Address,
101}
102
103impl PolicyData {
104 fn policy_type(&self) -> Result<PolicyType> {
106 let is_t2 = StorageCtx.spec().is_t2();
107
108 match self.policy_type.try_into() {
109 Ok(ty) if is_t2 || ty != PolicyType::COMPOUND => Ok(ty),
110 _ => Err(if is_t2 {
111 TIP403RegistryError::invalid_policy_type().into()
112 } else {
113 TempoPrecompileError::under_overflow()
114 }),
115 }
116 }
117
118 fn is_simple(&self) -> bool {
120 self.policy_type == PolicyType::WHITELIST as u8
121 || self.policy_type == PolicyType::BLACKLIST as u8
122 }
123
124 pub fn is_compound(&self) -> bool {
126 self.policy_type == PolicyType::COMPOUND as u8
127 }
128
129 fn is_default(&self) -> bool {
131 self.policy_type == 0 && self.admin == Address::ZERO
132 }
133}
134
135impl TIP403Registry {
136 pub fn initialize(&mut self) -> Result<()> {
138 self.__initialize()
139 }
140
141 pub fn policy_id_counter(&self) -> Result<u64> {
143 self.policy_id_counter.read().map(|counter| counter.max(2))
145 }
146
147 pub fn policy_exists(&self, call: ITIP403Registry::policyExistsCall) -> Result<bool> {
149 if self.builtin_authorization(call.policyId).is_some() {
151 return Ok(true);
152 }
153
154 let counter = self.policy_id_counter()?;
156 Ok(call.policyId < counter)
157 }
158
159 pub fn policy_data(
166 &self,
167 call: ITIP403Registry::policyDataCall,
168 ) -> Result<ITIP403Registry::policyDataReturn> {
169 if self.storage.spec().is_t2() {
170 if self.builtin_authorization(call.policyId).is_some() {
174 return Ok(ITIP403Registry::policyDataReturn {
175 policyType: (call.policyId as u8)
176 .try_into()
177 .map_err(|_| TIP403RegistryError::invalid_policy_type())?,
178 admin: Address::ZERO,
179 });
180 }
181 } else {
182 if !self.policy_exists(ITIP403Registry::policyExistsCall {
184 policyId: call.policyId,
185 })? {
186 return Err(TIP403RegistryError::policy_not_found().into());
187 }
188 }
189
190 let data = self.get_policy_data(call.policyId)?;
192
193 Ok(ITIP403Registry::policyDataReturn {
194 policyType: data.policy_type()?,
195 admin: data.admin,
196 })
197 }
198
199 pub fn compound_policy_data(
207 &self,
208 call: ITIP403Registry::compoundPolicyDataCall,
209 ) -> Result<ITIP403Registry::compoundPolicyDataReturn> {
210 let data = self.get_policy_data(call.policyId)?;
211
212 if !data.is_compound() {
214 let err = if self.policy_exists(ITIP403Registry::policyExistsCall {
216 policyId: call.policyId,
217 })? {
218 TIP403RegistryError::incompatible_policy_type()
219 } else {
220 TIP403RegistryError::policy_not_found()
221 };
222 return Err(err.into());
223 }
224
225 let compound = self.policy_records[call.policyId].compound.read()?;
226 Ok(ITIP403Registry::compoundPolicyDataReturn {
227 senderPolicyId: compound.sender_policy_id,
228 recipientPolicyId: compound.recipient_policy_id,
229 mintRecipientPolicyId: compound.mint_recipient_policy_id,
230 })
231 }
232
233 pub fn create_policy(
239 &mut self,
240 msg_sender: Address,
241 call: ITIP403Registry::createPolicyCall,
242 ) -> Result<u64> {
243 let policy_type = call.policyType.ensure_is_simple()?;
244
245 let new_policy_id = self.policy_id_counter()?;
246
247 self.policy_id_counter.write(
249 new_policy_id
250 .checked_add(1)
251 .ok_or(TempoPrecompileError::under_overflow())?,
252 )?;
253
254 self.policy_records[new_policy_id].base.write(PolicyData {
256 policy_type,
257 admin: call.admin,
258 })?;
259
260 self.emit_event(TIP403RegistryEvent::PolicyCreated(
261 ITIP403Registry::PolicyCreated {
262 policyId: new_policy_id,
263 updater: msg_sender,
264 policyType: policy_type.try_into().unwrap_or(PolicyType::__Invalid),
265 },
266 ))?;
267
268 self.emit_event(TIP403RegistryEvent::PolicyAdminUpdated(
269 ITIP403Registry::PolicyAdminUpdated {
270 policyId: new_policy_id,
271 updater: msg_sender,
272 admin: call.admin,
273 },
274 ))?;
275
276 Ok(new_policy_id)
277 }
278
279 pub fn create_policy_with_accounts(
287 &mut self,
288 msg_sender: Address,
289 call: ITIP403Registry::createPolicyWithAccountsCall,
290 ) -> Result<u64> {
291 let admin = call.admin;
292 let policy_type = call.policyType.ensure_is_simple()?;
293
294 if self.storage.spec().is_t3() {
296 for account in call.accounts.iter() {
297 if account.is_virtual() {
298 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
299 }
300 }
301 }
302
303 let new_policy_id = self.policy_id_counter()?;
304
305 self.policy_id_counter.write(
307 new_policy_id
308 .checked_add(1)
309 .ok_or(TempoPrecompileError::under_overflow())?,
310 )?;
311
312 self.set_policy_data(new_policy_id, PolicyData { policy_type, admin })?;
314
315 for account in call.accounts.iter() {
318 self.set_policy_set(new_policy_id, *account, true)?;
319
320 match call.policyType {
321 PolicyType::WHITELIST => {
322 self.emit_event(TIP403RegistryEvent::WhitelistUpdated(
323 ITIP403Registry::WhitelistUpdated {
324 policyId: new_policy_id,
325 updater: msg_sender,
326 account: *account,
327 allowed: true,
328 },
329 ))?;
330 }
331 PolicyType::BLACKLIST => {
332 self.emit_event(TIP403RegistryEvent::BlacklistUpdated(
333 ITIP403Registry::BlacklistUpdated {
334 policyId: new_policy_id,
335 updater: msg_sender,
336 account: *account,
337 restricted: true,
338 },
339 ))?;
340 }
341 ITIP403Registry::PolicyType::COMPOUND | ITIP403Registry::PolicyType::__Invalid => {
342 return Err(TIP403RegistryError::incompatible_policy_type().into());
344 }
345 }
346 }
347
348 self.emit_event(TIP403RegistryEvent::PolicyCreated(
349 ITIP403Registry::PolicyCreated {
350 policyId: new_policy_id,
351 updater: msg_sender,
352 policyType: policy_type.try_into().unwrap_or(PolicyType::__Invalid),
353 },
354 ))?;
355
356 self.emit_event(TIP403RegistryEvent::PolicyAdminUpdated(
357 ITIP403Registry::PolicyAdminUpdated {
358 policyId: new_policy_id,
359 updater: msg_sender,
360 admin,
361 },
362 ))?;
363
364 Ok(new_policy_id)
365 }
366
367 pub fn set_policy_admin(
373 &mut self,
374 msg_sender: Address,
375 call: ITIP403Registry::setPolicyAdminCall,
376 ) -> Result<()> {
377 let data = self.get_policy_data(call.policyId)?;
378
379 if data.admin != msg_sender {
381 return Err(TIP403RegistryError::unauthorized().into());
382 }
383
384 self.set_policy_data(
386 call.policyId,
387 PolicyData {
388 admin: call.admin,
389 ..data
390 },
391 )?;
392
393 self.emit_event(TIP403RegistryEvent::PolicyAdminUpdated(
394 ITIP403Registry::PolicyAdminUpdated {
395 policyId: call.policyId,
396 updater: msg_sender,
397 admin: call.admin,
398 },
399 ))
400 }
401
402 pub fn modify_policy_whitelist(
410 &mut self,
411 msg_sender: Address,
412 call: ITIP403Registry::modifyPolicyWhitelistCall,
413 ) -> Result<()> {
414 if self.storage.spec().is_t3() && call.account.is_virtual() {
416 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
417 }
418
419 let data = self.get_policy_data(call.policyId)?;
420
421 if data.admin != msg_sender {
423 return Err(TIP403RegistryError::unauthorized().into());
424 }
425
426 if !matches!(data.policy_type()?, PolicyType::WHITELIST) {
428 return Err(TIP403RegistryError::incompatible_policy_type().into());
429 }
430
431 self.set_policy_set(call.policyId, call.account, call.allowed)?;
432
433 self.emit_event(TIP403RegistryEvent::WhitelistUpdated(
434 ITIP403Registry::WhitelistUpdated {
435 policyId: call.policyId,
436 updater: msg_sender,
437 account: call.account,
438 allowed: call.allowed,
439 },
440 ))
441 }
442
443 pub fn modify_policy_blacklist(
451 &mut self,
452 msg_sender: Address,
453 call: ITIP403Registry::modifyPolicyBlacklistCall,
454 ) -> Result<()> {
455 if self.storage.spec().is_t3() && call.account.is_virtual() {
457 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
458 }
459
460 let data = self.get_policy_data(call.policyId)?;
461
462 if data.admin != msg_sender {
464 return Err(TIP403RegistryError::unauthorized().into());
465 }
466
467 if !matches!(data.policy_type()?, PolicyType::BLACKLIST) {
469 return Err(TIP403RegistryError::incompatible_policy_type().into());
470 }
471
472 self.set_policy_set(call.policyId, call.account, call.restricted)?;
473
474 self.emit_event(TIP403RegistryEvent::BlacklistUpdated(
475 ITIP403Registry::BlacklistUpdated {
476 policyId: call.policyId,
477 updater: msg_sender,
478 account: call.account,
479 restricted: call.restricted,
480 },
481 ))
482 }
483
484 pub fn create_compound_policy(
494 &mut self,
495 msg_sender: Address,
496 call: ITIP403Registry::createCompoundPolicyCall,
497 ) -> Result<u64> {
498 self.validate_simple_policy(call.senderPolicyId)?;
500 self.validate_simple_policy(call.recipientPolicyId)?;
501 self.validate_simple_policy(call.mintRecipientPolicyId)?;
502
503 let new_policy_id = self.policy_id_counter()?;
504
505 self.policy_id_counter.write(
507 new_policy_id
508 .checked_add(1)
509 .ok_or(TempoPrecompileError::under_overflow())?,
510 )?;
511
512 self.policy_records[new_policy_id].write(PolicyRecord {
514 base: PolicyData {
515 policy_type: PolicyType::COMPOUND as u8,
516 admin: Address::ZERO,
517 },
518 compound: CompoundPolicyData {
519 sender_policy_id: call.senderPolicyId,
520 recipient_policy_id: call.recipientPolicyId,
521 mint_recipient_policy_id: call.mintRecipientPolicyId,
522 },
523 })?;
524
525 self.emit_event(TIP403RegistryEvent::CompoundPolicyCreated(
527 ITIP403Registry::CompoundPolicyCreated {
528 policyId: new_policy_id,
529 creator: msg_sender,
530 senderPolicyId: call.senderPolicyId,
531 recipientPolicyId: call.recipientPolicyId,
532 mintRecipientPolicyId: call.mintRecipientPolicyId,
533 },
534 ))?;
535
536 Ok(new_policy_id)
537 }
538
539 pub fn is_authorized_as(&self, policy_id: u64, user: Address, role: AuthRole) -> Result<bool> {
550 if let Some(auth) = self.builtin_authorization(policy_id) {
551 return Ok(auth);
552 }
553
554 let data = self.get_policy_data(policy_id)?;
555
556 if data.is_compound() {
557 let compound = self.policy_records[policy_id].compound.read()?;
558 return match role {
559 AuthRole::Sender => self.is_authorized_simple(compound.sender_policy_id, user),
560 AuthRole::Recipient => {
561 self.is_authorized_simple(compound.recipient_policy_id, user)
562 }
563 AuthRole::MintRecipient => {
564 self.is_authorized_simple(compound.mint_recipient_policy_id, user)
565 }
566 AuthRole::Transfer => {
567 let sender_auth = self.is_authorized_simple(compound.sender_policy_id, user)?;
569 if self.storage.spec().is_t2() && !sender_auth {
570 return Ok(false);
571 }
572 let recipient_auth =
573 self.is_authorized_simple(compound.recipient_policy_id, user)?;
574 Ok(sender_auth && recipient_auth)
575 }
576 };
577 }
578
579 self.is_simple(policy_id, user, &data)
580 }
581
582 #[inline]
585 fn builtin_authorization(&self, policy_id: u64) -> Option<bool> {
586 match policy_id {
587 ALLOW_ALL_POLICY_ID => Some(true),
588 REJECT_ALL_POLICY_ID => Some(false),
589 _ => None,
590 }
591 }
592
593 fn is_authorized_simple(&self, policy_id: u64, user: Address) -> Result<bool> {
597 if let Some(auth) = self.builtin_authorization(policy_id) {
598 return Ok(auth);
599 }
600 let data = self.get_policy_data(policy_id)?;
601 self.is_simple(policy_id, user, &data)
602 }
603
604 fn is_simple(&self, policy_id: u64, user: Address, data: &PolicyData) -> Result<bool> {
606 let is_in_set = self.policy_set[policy_id][user].read()?;
610
611 match data.policy_type()? {
612 PolicyType::WHITELIST => Ok(is_in_set),
613 PolicyType::BLACKLIST => Ok(!is_in_set),
614 PolicyType::COMPOUND => Err(TIP403RegistryError::incompatible_policy_type().into()),
615 PolicyType::__Invalid => unreachable!(),
616 }
617 }
618
619 fn validate_simple_policy(&self, policy_id: u64) -> Result<()> {
621 if self.builtin_authorization(policy_id).is_some() {
623 return Ok(());
624 }
625
626 if policy_id >= self.policy_id_counter()? {
628 return Err(TIP403RegistryError::policy_not_found().into());
629 }
630
631 let data = self.get_policy_data(policy_id)?;
633 if !data.is_simple() {
634 return Err(TIP403RegistryError::policy_not_simple().into());
635 }
636
637 Ok(())
638 }
639
640 fn get_policy_data(&self, policy_id: u64) -> Result<PolicyData> {
645 let data = self.policy_records[policy_id].base.read()?;
646
647 if self.storage.spec().is_t2()
650 && data.is_default()
651 && policy_id >= self.policy_id_counter()?
652 {
653 return Err(TIP403RegistryError::policy_not_found().into());
654 }
655
656 Ok(data)
657 }
658
659 fn set_policy_data(&mut self, policy_id: u64, data: PolicyData) -> Result<()> {
660 self.policy_records[policy_id].base.write(data)
661 }
662
663 fn set_policy_set(&mut self, policy_id: u64, account: Address, value: bool) -> Result<()> {
664 self.policy_set[policy_id][account].write(value)
665 }
666}
667
668impl AuthRole {
669 #[inline]
670 fn transfer_or(t2_variant: Self) -> Self {
671 if StorageCtx.spec().is_t2() {
672 t2_variant
673 } else {
674 Self::Transfer
675 }
676 }
677
678 pub fn transfer() -> Self {
680 Self::Transfer
681 }
682
683 pub fn sender() -> Self {
685 Self::transfer_or(Self::Sender)
686 }
687
688 pub fn recipient() -> Self {
690 Self::transfer_or(Self::Recipient)
691 }
692
693 pub fn mint_recipient() -> Self {
695 Self::transfer_or(Self::MintRecipient)
696 }
697}
698
699pub fn is_policy_lookup_error(e: &TempoPrecompileError) -> bool {
702 if StorageCtx.spec().is_t2() {
703 *e == TIP403RegistryError::invalid_policy_type().into()
705 || *e == TIP403RegistryError::policy_not_found().into()
706 } else {
707 *e == TempoPrecompileError::under_overflow()
709 }
710}
711
712trait PolicyTypeExt {
714 fn ensure_is_simple(&self) -> Result<u8>;
716}
717
718impl PolicyTypeExt for PolicyType {
719 fn ensure_is_simple(&self) -> Result<u8> {
724 match self {
725 Self::WHITELIST | Self::BLACKLIST => Ok(*self as u8),
726 Self::COMPOUND | Self::__Invalid => {
727 if StorageCtx.spec().is_t2() {
728 Err(TIP403RegistryError::incompatible_policy_type().into())
729 } else {
730 Ok(Self::__Invalid as u8)
731 }
732 }
733 }
734 }
735}
736
737#[cfg(test)]
738mod tests {
739 use super::*;
740 use crate::{
741 error::TempoPrecompileError,
742 storage::{ContractStorage, StorageCtx, hashmap::HashMapStorageProvider},
743 };
744 use alloy::{
745 primitives::{Address, Log},
746 sol_types::SolEvent,
747 };
748 use rand_08::Rng;
749 use tempo_chainspec::hardfork::TempoHardfork;
750 use tempo_contracts::precompiles::TIP403_REGISTRY_ADDRESS;
751 use tempo_primitives::{MasterId, TempoAddressExt, UserTag};
752
753 #[test]
754 fn test_create_policy() -> eyre::Result<()> {
755 let mut storage = HashMapStorageProvider::new(1);
756 let admin = Address::random();
757 StorageCtx::enter(&mut storage, || {
758 let mut registry = TIP403Registry::new();
759
760 assert_eq!(registry.policy_id_counter()?, 2);
762
763 let result = registry.create_policy(
765 admin,
766 ITIP403Registry::createPolicyCall {
767 admin,
768 policyType: ITIP403Registry::PolicyType::WHITELIST,
769 },
770 );
771 assert!(result.is_ok());
772 assert_eq!(result?, 2);
773
774 assert_eq!(registry.policy_id_counter()?, 3);
776
777 let data = registry.policy_data(ITIP403Registry::policyDataCall { policyId: 2 })?;
779 assert_eq!(data.policyType, ITIP403Registry::PolicyType::WHITELIST);
780 assert_eq!(data.admin, admin);
781 Ok(())
782 })
783 }
784
785 #[test]
786 fn test_is_authorized_special_policies() -> eyre::Result<()> {
787 let mut storage = HashMapStorageProvider::new(1);
788 let user = Address::random();
789 StorageCtx::enter(&mut storage, || {
790 let registry = TIP403Registry::new();
791
792 assert!(!registry.is_authorized_as(0, user, AuthRole::Transfer)?);
794
795 assert!(registry.is_authorized_as(1, user, AuthRole::Transfer)?);
797 Ok(())
798 })
799 }
800
801 #[test]
802 fn test_whitelist_policy() -> eyre::Result<()> {
803 let mut storage = HashMapStorageProvider::new(1);
804 let admin = Address::random();
805 let user = Address::random();
806 StorageCtx::enter(&mut storage, || {
807 let mut registry = TIP403Registry::new();
808
809 let policy_id = registry.create_policy(
811 admin,
812 ITIP403Registry::createPolicyCall {
813 admin,
814 policyType: ITIP403Registry::PolicyType::WHITELIST,
815 },
816 )?;
817
818 assert!(!registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
820
821 registry.modify_policy_whitelist(
823 admin,
824 ITIP403Registry::modifyPolicyWhitelistCall {
825 policyId: policy_id,
826 account: user,
827 allowed: true,
828 },
829 )?;
830
831 assert!(registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
833
834 Ok(())
835 })
836 }
837
838 #[test]
839 fn test_blacklist_policy() -> eyre::Result<()> {
840 let mut storage = HashMapStorageProvider::new(1);
841 let admin = Address::random();
842 let user = Address::random();
843 StorageCtx::enter(&mut storage, || {
844 let mut registry = TIP403Registry::new();
845
846 let policy_id = registry.create_policy(
848 admin,
849 ITIP403Registry::createPolicyCall {
850 admin,
851 policyType: ITIP403Registry::PolicyType::BLACKLIST,
852 },
853 )?;
854
855 assert!(registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
857
858 registry.modify_policy_blacklist(
860 admin,
861 ITIP403Registry::modifyPolicyBlacklistCall {
862 policyId: policy_id,
863 account: user,
864 restricted: true,
865 },
866 )?;
867
868 assert!(!registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
870
871 Ok(())
872 })
873 }
874
875 #[test]
876 fn test_policy_data_reverts_for_non_existent_policy() -> eyre::Result<()> {
877 let mut storage = HashMapStorageProvider::new(1);
878 StorageCtx::enter(&mut storage, || {
879 let registry = TIP403Registry::new();
880
881 let result = registry.policy_data(ITIP403Registry::policyDataCall { policyId: 100 });
883 assert!(result.is_err());
884
885 assert!(matches!(
887 result.unwrap_err(),
888 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotFound(_))
889 ));
890
891 Ok(())
892 })
893 }
894
895 #[test]
896 fn test_policy_data_builtin_policies_boundary() -> eyre::Result<()> {
897 for (hardfork, expect_allow_all_type) in [
898 (TempoHardfork::T1C, ITIP403Registry::PolicyType::WHITELIST),
900 (TempoHardfork::T2, ITIP403Registry::PolicyType::BLACKLIST),
902 ] {
903 let mut storage = HashMapStorageProvider::new_with_spec(1, hardfork);
904 StorageCtx::enter(&mut storage, || {
905 let registry = TIP403Registry::new();
906
907 let reject = registry.policy_data(ITIP403Registry::policyDataCall {
909 policyId: REJECT_ALL_POLICY_ID,
910 })?;
911 assert_eq!(reject.policyType, ITIP403Registry::PolicyType::WHITELIST);
912 assert_eq!(reject.admin, Address::ZERO);
913
914 let allow = registry.policy_data(ITIP403Registry::policyDataCall {
916 policyId: ALLOW_ALL_POLICY_ID,
917 })?;
918 assert_eq!(allow.policyType, expect_allow_all_type);
919 assert_eq!(allow.admin, Address::ZERO);
920
921 Ok::<_, TempoPrecompileError>(())
922 })?;
923 }
924 Ok(())
925 }
926
927 #[test]
928 fn test_policy_exists() -> eyre::Result<()> {
929 let mut storage = HashMapStorageProvider::new(1);
930 let admin = Address::random();
931 StorageCtx::enter(&mut storage, || {
932 let mut registry = TIP403Registry::new();
933
934 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: 0 })?);
936 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: 1 })?);
937
938 let mut rng = rand_08::thread_rng();
940 for _ in 0..100 {
941 let random_policy_id = rng.gen_range(2..u64::MAX);
942 assert!(!registry.policy_exists(ITIP403Registry::policyExistsCall {
943 policyId: random_policy_id
944 })?);
945 }
946
947 let mut created_policy_ids = Vec::new();
949 for i in 0..50 {
950 let policy_id = registry.create_policy(
951 admin,
952 ITIP403Registry::createPolicyCall {
953 admin,
954 policyType: if i % 2 == 0 {
955 ITIP403Registry::PolicyType::WHITELIST
956 } else {
957 ITIP403Registry::PolicyType::BLACKLIST
958 },
959 },
960 )?;
961 created_policy_ids.push(policy_id);
962 }
963
964 for policy_id in &created_policy_ids {
966 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
967 policyId: *policy_id
968 })?);
969 }
970
971 Ok(())
972 })
973 }
974
975 #[test]
980 fn test_create_compound_policy() -> eyre::Result<()> {
981 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
982 let admin = Address::random();
983 let creator = Address::random();
984 StorageCtx::enter(&mut storage, || {
985 let mut registry = TIP403Registry::new();
986
987 let sender_policy = registry.create_policy(
989 admin,
990 ITIP403Registry::createPolicyCall {
991 admin,
992 policyType: ITIP403Registry::PolicyType::WHITELIST,
993 },
994 )?;
995 let recipient_policy = registry.create_policy(
996 admin,
997 ITIP403Registry::createPolicyCall {
998 admin,
999 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1000 },
1001 )?;
1002 let mint_recipient_policy = registry.create_policy(
1003 admin,
1004 ITIP403Registry::createPolicyCall {
1005 admin,
1006 policyType: ITIP403Registry::PolicyType::WHITELIST,
1007 },
1008 )?;
1009
1010 let compound_id = registry.create_compound_policy(
1012 creator,
1013 ITIP403Registry::createCompoundPolicyCall {
1014 senderPolicyId: sender_policy,
1015 recipientPolicyId: recipient_policy,
1016 mintRecipientPolicyId: mint_recipient_policy,
1017 },
1018 )?;
1019
1020 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
1022 policyId: compound_id
1023 })?);
1024
1025 let data = registry.policy_data(ITIP403Registry::policyDataCall {
1027 policyId: compound_id,
1028 })?;
1029 assert_eq!(data.policyType, ITIP403Registry::PolicyType::COMPOUND);
1030 assert_eq!(data.admin, Address::ZERO); let compound_data =
1034 registry.compound_policy_data(ITIP403Registry::compoundPolicyDataCall {
1035 policyId: compound_id,
1036 })?;
1037 assert_eq!(compound_data.senderPolicyId, sender_policy);
1038 assert_eq!(compound_data.recipientPolicyId, recipient_policy);
1039 assert_eq!(compound_data.mintRecipientPolicyId, mint_recipient_policy);
1040
1041 Ok(())
1042 })
1043 }
1044
1045 #[test]
1046 fn test_compound_policy_rejects_non_existent_refs() -> eyre::Result<()> {
1047 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1048 let creator = Address::random();
1049 StorageCtx::enter(&mut storage, || {
1050 let mut registry = TIP403Registry::new();
1051
1052 let result = registry.create_compound_policy(
1054 creator,
1055 ITIP403Registry::createCompoundPolicyCall {
1056 senderPolicyId: 999,
1057 recipientPolicyId: 1,
1058 mintRecipientPolicyId: 1,
1059 },
1060 );
1061 assert!(result.is_err());
1062
1063 Ok(())
1064 })
1065 }
1066
1067 #[test]
1068 fn test_compound_policy_rejects_compound_refs() -> eyre::Result<()> {
1069 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1070 let admin = Address::random();
1071 let creator = Address::random();
1072 StorageCtx::enter(&mut storage, || {
1073 let mut registry = TIP403Registry::new();
1074
1075 let simple_policy = registry.create_policy(
1077 admin,
1078 ITIP403Registry::createPolicyCall {
1079 admin,
1080 policyType: ITIP403Registry::PolicyType::WHITELIST,
1081 },
1082 )?;
1083
1084 let compound_id = registry.create_compound_policy(
1086 creator,
1087 ITIP403Registry::createCompoundPolicyCall {
1088 senderPolicyId: 1,
1089 recipientPolicyId: simple_policy,
1090 mintRecipientPolicyId: 1,
1091 },
1092 )?;
1093
1094 let result = registry.create_compound_policy(
1096 creator,
1097 ITIP403Registry::createCompoundPolicyCall {
1098 senderPolicyId: compound_id, recipientPolicyId: 1,
1100 mintRecipientPolicyId: 1,
1101 },
1102 );
1103 assert!(result.is_err());
1104
1105 Ok(())
1106 })
1107 }
1108
1109 #[test]
1110 fn test_compound_policy_sender_recipient_differentiation() -> eyre::Result<()> {
1111 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1112 let admin = Address::random();
1113 let creator = Address::random();
1114 let alice = Address::random();
1115 let bob = Address::random();
1116 StorageCtx::enter(&mut storage, || {
1117 let mut registry = TIP403Registry::new();
1118
1119 let sender_policy = registry.create_policy(
1121 admin,
1122 ITIP403Registry::createPolicyCall {
1123 admin,
1124 policyType: ITIP403Registry::PolicyType::WHITELIST,
1125 },
1126 )?;
1127 registry.modify_policy_whitelist(
1128 admin,
1129 ITIP403Registry::modifyPolicyWhitelistCall {
1130 policyId: sender_policy,
1131 account: alice,
1132 allowed: true,
1133 },
1134 )?;
1135
1136 let recipient_policy = registry.create_policy(
1138 admin,
1139 ITIP403Registry::createPolicyCall {
1140 admin,
1141 policyType: ITIP403Registry::PolicyType::WHITELIST,
1142 },
1143 )?;
1144 registry.modify_policy_whitelist(
1145 admin,
1146 ITIP403Registry::modifyPolicyWhitelistCall {
1147 policyId: recipient_policy,
1148 account: bob,
1149 allowed: true,
1150 },
1151 )?;
1152
1153 let compound_id = registry.create_compound_policy(
1155 creator,
1156 ITIP403Registry::createCompoundPolicyCall {
1157 senderPolicyId: sender_policy,
1158 recipientPolicyId: recipient_policy,
1159 mintRecipientPolicyId: 1, },
1161 )?;
1162
1163 assert!(registry.is_authorized_as(compound_id, alice, AuthRole::Sender)?);
1165
1166 assert!(!registry.is_authorized_as(compound_id, bob, AuthRole::Sender)?);
1168
1169 assert!(registry.is_authorized_as(compound_id, bob, AuthRole::Recipient)?);
1171
1172 assert!(!registry.is_authorized_as(compound_id, alice, AuthRole::Recipient)?);
1174
1175 assert!(registry.is_authorized_as(compound_id, alice, AuthRole::MintRecipient)?);
1177 assert!(registry.is_authorized_as(compound_id, bob, AuthRole::MintRecipient)?);
1178
1179 Ok(())
1180 })
1181 }
1182
1183 #[test]
1184 fn test_compound_policy_is_authorized_behavior() -> eyre::Result<()> {
1185 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1186 let admin = Address::random();
1187 let creator = Address::random();
1188 let user = Address::random();
1189 StorageCtx::enter(&mut storage, || {
1190 let mut registry = TIP403Registry::new();
1191
1192 let sender_policy = registry.create_policy(
1194 admin,
1195 ITIP403Registry::createPolicyCall {
1196 admin,
1197 policyType: ITIP403Registry::PolicyType::WHITELIST,
1198 },
1199 )?;
1200 registry.modify_policy_whitelist(
1201 admin,
1202 ITIP403Registry::modifyPolicyWhitelistCall {
1203 policyId: sender_policy,
1204 account: user,
1205 allowed: true,
1206 },
1207 )?;
1208
1209 let recipient_policy = registry.create_policy(
1211 admin,
1212 ITIP403Registry::createPolicyCall {
1213 admin,
1214 policyType: ITIP403Registry::PolicyType::WHITELIST,
1215 },
1216 )?;
1217
1218 let compound_id = registry.create_compound_policy(
1220 creator,
1221 ITIP403Registry::createCompoundPolicyCall {
1222 senderPolicyId: sender_policy,
1223 recipientPolicyId: recipient_policy,
1224 mintRecipientPolicyId: 1,
1225 },
1226 )?;
1227
1228 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Sender)?);
1231 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Recipient)?);
1232
1233 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1235
1236 registry.modify_policy_whitelist(
1238 admin,
1239 ITIP403Registry::modifyPolicyWhitelistCall {
1240 policyId: recipient_policy,
1241 account: user,
1242 allowed: true,
1243 },
1244 )?;
1245
1246 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1248
1249 Ok(())
1250 })
1251 }
1252
1253 #[test]
1254 fn test_compound_policy_is_authorized_transfer() -> eyre::Result<()> {
1255 let admin = Address::random();
1256 let creator = Address::random();
1257 let user = Address::random();
1258
1259 for hardfork in [TempoHardfork::T0, TempoHardfork::T1] {
1260 let mut storage = HashMapStorageProvider::new_with_spec(1, hardfork);
1261
1262 StorageCtx::enter(&mut storage, || {
1263 let mut registry = TIP403Registry::new();
1264
1265 let sender_policy = registry.create_policy(
1267 admin,
1268 ITIP403Registry::createPolicyCall {
1269 admin,
1270 policyType: ITIP403Registry::PolicyType::WHITELIST,
1271 },
1272 )?;
1273 let recipient_policy = registry.create_policy(
1274 admin,
1275 ITIP403Registry::createPolicyCall {
1276 admin,
1277 policyType: ITIP403Registry::PolicyType::WHITELIST,
1278 },
1279 )?;
1280
1281 let compound_id = registry.create_compound_policy(
1283 creator,
1284 ITIP403Registry::createCompoundPolicyCall {
1285 senderPolicyId: sender_policy,
1286 recipientPolicyId: recipient_policy,
1287 mintRecipientPolicyId: 1,
1288 },
1289 )?;
1290
1291 registry.modify_policy_whitelist(
1293 admin,
1294 ITIP403Registry::modifyPolicyWhitelistCall {
1295 policyId: recipient_policy,
1296 account: user,
1297 allowed: true,
1298 },
1299 )?;
1300 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1301
1302 registry.modify_policy_whitelist(
1304 admin,
1305 ITIP403Registry::modifyPolicyWhitelistCall {
1306 policyId: sender_policy,
1307 account: user,
1308 allowed: true,
1309 },
1310 )?;
1311 registry.modify_policy_whitelist(
1312 admin,
1313 ITIP403Registry::modifyPolicyWhitelistCall {
1314 policyId: recipient_policy,
1315 account: user,
1316 allowed: false,
1317 },
1318 )?;
1319 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1320
1321 registry.modify_policy_whitelist(
1323 admin,
1324 ITIP403Registry::modifyPolicyWhitelistCall {
1325 policyId: recipient_policy,
1326 account: user,
1327 allowed: true,
1328 },
1329 )?;
1330 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1331
1332 Ok::<_, TempoPrecompileError>(())
1333 })?;
1334 }
1335
1336 Ok(())
1337 }
1338
1339 #[test]
1340 fn test_simple_policy_equivalence() -> eyre::Result<()> {
1341 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1342 let admin = Address::random();
1343 let user = Address::random();
1344 StorageCtx::enter(&mut storage, || {
1345 let mut registry = TIP403Registry::new();
1346
1347 let policy_id = registry.create_policy(
1349 admin,
1350 ITIP403Registry::createPolicyCall {
1351 admin,
1352 policyType: ITIP403Registry::PolicyType::WHITELIST,
1353 },
1354 )?;
1355 registry.modify_policy_whitelist(
1356 admin,
1357 ITIP403Registry::modifyPolicyWhitelistCall {
1358 policyId: policy_id,
1359 account: user,
1360 allowed: true,
1361 },
1362 )?;
1363
1364 let is_authorized = registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?;
1366 let is_sender = registry.is_authorized_as(policy_id, user, AuthRole::Sender)?;
1367 let is_recipient = registry.is_authorized_as(policy_id, user, AuthRole::Recipient)?;
1368 let is_mint_recipient =
1369 registry.is_authorized_as(policy_id, user, AuthRole::MintRecipient)?;
1370
1371 assert!(is_authorized);
1372 assert_eq!(is_authorized, is_sender);
1373 assert_eq!(is_sender, is_recipient);
1374 assert_eq!(is_recipient, is_mint_recipient);
1375
1376 Ok(())
1377 })
1378 }
1379
1380 #[test]
1381 fn test_compound_policy_with_builtin_policies() -> eyre::Result<()> {
1382 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1383 let creator = Address::random();
1384 let user = Address::random();
1385 StorageCtx::enter(&mut storage, || {
1386 let mut registry = TIP403Registry::new();
1387
1388 let compound_id = registry.create_compound_policy(
1393 creator,
1394 ITIP403Registry::createCompoundPolicyCall {
1395 senderPolicyId: 1,
1396 recipientPolicyId: 0,
1397 mintRecipientPolicyId: 1,
1398 },
1399 )?;
1400
1401 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Sender)?);
1403
1404 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Recipient)?);
1406
1407 assert!(registry.is_authorized_as(compound_id, user, AuthRole::MintRecipient)?);
1409
1410 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1412
1413 Ok(())
1414 })
1415 }
1416
1417 #[test]
1418 fn test_vendor_credits_use_case() -> eyre::Result<()> {
1419 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1420 let admin = Address::random();
1421 let creator = Address::random();
1422 let vendor = Address::random();
1423 let customer = Address::random();
1424 StorageCtx::enter(&mut storage, || {
1425 let mut registry = TIP403Registry::new();
1426
1427 let vendor_whitelist = registry.create_policy(
1429 admin,
1430 ITIP403Registry::createPolicyCall {
1431 admin,
1432 policyType: ITIP403Registry::PolicyType::WHITELIST,
1433 },
1434 )?;
1435 registry.modify_policy_whitelist(
1436 admin,
1437 ITIP403Registry::modifyPolicyWhitelistCall {
1438 policyId: vendor_whitelist,
1439 account: vendor,
1440 allowed: true,
1441 },
1442 )?;
1443
1444 let compound_id = registry.create_compound_policy(
1449 creator,
1450 ITIP403Registry::createCompoundPolicyCall {
1451 senderPolicyId: 1, recipientPolicyId: vendor_whitelist, mintRecipientPolicyId: 1, },
1455 )?;
1456
1457 assert!(registry.is_authorized_as(compound_id, customer, AuthRole::MintRecipient)?);
1459
1460 assert!(registry.is_authorized_as(compound_id, customer, AuthRole::Sender)?);
1462
1463 assert!(registry.is_authorized_as(compound_id, vendor, AuthRole::Recipient)?);
1465 assert!(!registry.is_authorized_as(compound_id, customer, AuthRole::Recipient)?);
1467
1468 Ok(())
1469 })
1470 }
1471
1472 #[test]
1473 fn test_policy_data_rejects_compound_policy_on_pre_t1() -> eyre::Result<()> {
1474 let creator = Address::random();
1475
1476 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1478 let compound_id = StorageCtx::enter(&mut storage, || {
1479 let mut registry = TIP403Registry::new();
1480 registry.create_compound_policy(
1481 creator,
1482 ITIP403Registry::createCompoundPolicyCall {
1483 senderPolicyId: 1,
1484 recipientPolicyId: 1,
1485 mintRecipientPolicyId: 1,
1486 },
1487 )
1488 })?;
1489
1490 let mut storage = storage.with_spec(TempoHardfork::T0);
1492 StorageCtx::enter(&mut storage, || {
1493 let registry = TIP403Registry::new();
1494
1495 let result = registry.policy_data(ITIP403Registry::policyDataCall {
1496 policyId: compound_id,
1497 });
1498 assert!(result.is_err());
1499 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
1500
1501 Ok(())
1502 })
1503 }
1504
1505 #[test]
1506 fn test_create_policy_rejects_non_simple_policy_types() -> eyre::Result<()> {
1507 let admin = Address::random();
1508
1509 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
1510 StorageCtx::enter(&mut storage, || {
1511 let mut registry = TIP403Registry::new();
1512
1513 for policy_type in [
1514 ITIP403Registry::PolicyType::COMPOUND,
1515 ITIP403Registry::PolicyType::__Invalid,
1516 ] {
1517 let result = registry.create_policy(
1518 admin,
1519 ITIP403Registry::createPolicyCall {
1520 admin,
1521 policyType: policy_type,
1522 },
1523 );
1524 assert!(matches!(
1525 result.unwrap_err(),
1526 TempoPrecompileError::TIP403RegistryError(
1527 TIP403RegistryError::IncompatiblePolicyType(_)
1528 )
1529 ));
1530 }
1531
1532 Ok(())
1533 })
1534 }
1535
1536 #[test]
1537 fn test_create_policy_with_accounts_rejects_non_simple_policy_types() -> eyre::Result<()> {
1538 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1539 let admin = Address::random();
1540 let account = Address::random();
1541 StorageCtx::enter(&mut storage, || {
1542 let mut registry = TIP403Registry::new();
1543
1544 for policy_type in [
1545 ITIP403Registry::PolicyType::COMPOUND,
1546 ITIP403Registry::PolicyType::__Invalid,
1547 ] {
1548 let result = registry.create_policy_with_accounts(
1549 admin,
1550 ITIP403Registry::createPolicyWithAccountsCall {
1551 admin,
1552 policyType: policy_type,
1553 accounts: vec![account],
1554 },
1555 );
1556 assert!(matches!(
1557 result.unwrap_err(),
1558 TempoPrecompileError::TIP403RegistryError(
1559 TIP403RegistryError::IncompatiblePolicyType(_)
1560 )
1561 ));
1562 }
1563
1564 Ok(())
1565 })
1566 }
1567
1568 #[test]
1573 fn test_pre_t1_create_policy_with_invalid_type_stores_255() -> eyre::Result<()> {
1574 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1575 let admin = Address::random();
1576 StorageCtx::enter(&mut storage, || {
1577 let mut registry = TIP403Registry::new();
1578
1579 for policy_type in [
1581 ITIP403Registry::PolicyType::COMPOUND,
1582 ITIP403Registry::PolicyType::__Invalid,
1583 ] {
1584 let policy_id = registry.create_policy(
1585 admin,
1586 ITIP403Registry::createPolicyCall {
1587 admin,
1588 policyType: policy_type,
1589 },
1590 )?;
1591
1592 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
1594 policyId: policy_id
1595 })?);
1596
1597 let data = registry.get_policy_data(policy_id)?;
1599 assert_eq!(data.policy_type, 255u8);
1600 }
1601
1602 Ok(())
1603 })
1604 }
1605
1606 #[test]
1607 fn test_pre_t1_create_policy_with_valid_types_stores_correct_value() -> eyre::Result<()> {
1608 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1609 let admin = Address::random();
1610 StorageCtx::enter(&mut storage, || {
1611 let mut registry = TIP403Registry::new();
1612
1613 let whitelist_id = registry.create_policy(
1615 admin,
1616 ITIP403Registry::createPolicyCall {
1617 admin,
1618 policyType: ITIP403Registry::PolicyType::WHITELIST,
1619 },
1620 )?;
1621 let data = registry.get_policy_data(whitelist_id)?;
1622 assert_eq!(data.policy_type, 0u8);
1623
1624 let blacklist_id = registry.create_policy(
1626 admin,
1627 ITIP403Registry::createPolicyCall {
1628 admin,
1629 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1630 },
1631 )?;
1632 let data = registry.get_policy_data(blacklist_id)?;
1633 assert_eq!(data.policy_type, 1u8);
1634
1635 Ok(())
1636 })
1637 }
1638
1639 #[test]
1640 fn test_pre_t1_create_policy_with_accounts_invalid_type_behavior() -> eyre::Result<()> {
1641 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1642 let (admin, account) = (Address::random(), Address::random());
1643
1644 StorageCtx::enter(&mut storage, || {
1645 let mut registry = TIP403Registry::new();
1646
1647 for policy_type in [
1649 ITIP403Registry::PolicyType::COMPOUND,
1650 ITIP403Registry::PolicyType::__Invalid,
1651 ] {
1652 let result = registry.create_policy_with_accounts(
1653 admin,
1654 ITIP403Registry::createPolicyWithAccountsCall {
1655 admin,
1656 policyType: policy_type,
1657 accounts: vec![account],
1658 },
1659 );
1660 assert!(matches!(
1661 result.unwrap_err(),
1662 TempoPrecompileError::TIP403RegistryError(
1663 TIP403RegistryError::IncompatiblePolicyType(_)
1664 )
1665 ));
1666 }
1667
1668 let policy_id = registry.create_policy_with_accounts(
1670 admin,
1671 ITIP403Registry::createPolicyWithAccountsCall {
1672 admin,
1673 policyType: ITIP403Registry::PolicyType::__Invalid,
1674 accounts: vec![],
1675 },
1676 )?;
1677 let data = registry.get_policy_data(policy_id)?;
1678 assert_eq!(data.policy_type, 255u8);
1679
1680 Ok(())
1681 })
1682 }
1683
1684 #[test]
1685 fn test_pre_t1_policy_data_reverts_for_any_policy_type_gte_2() -> eyre::Result<()> {
1686 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1687 let admin = Address::random();
1688 StorageCtx::enter(&mut storage, || {
1689 let mut registry = TIP403Registry::new();
1690
1691 let policy_id = registry.create_policy(
1693 admin,
1694 ITIP403Registry::createPolicyCall {
1695 admin,
1696 policyType: ITIP403Registry::PolicyType::COMPOUND,
1697 },
1698 )?;
1699
1700 let result = registry.policy_data(ITIP403Registry::policyDataCall {
1702 policyId: policy_id,
1703 });
1704 assert!(result.is_err());
1705 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
1706
1707 Ok(())
1708 })
1709 }
1710
1711 #[test]
1712 fn test_pre_t1_is_authorized_reverts_for_invalid_policy_type() -> eyre::Result<()> {
1713 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1714 let admin = Address::random();
1715 let user = Address::random();
1716 StorageCtx::enter(&mut storage, || {
1717 let mut registry = TIP403Registry::new();
1718
1719 let policy_id = registry.create_policy(
1721 admin,
1722 ITIP403Registry::createPolicyCall {
1723 admin,
1724 policyType: ITIP403Registry::PolicyType::COMPOUND,
1725 },
1726 )?;
1727
1728 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
1730 assert!(result.is_err());
1731 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
1732
1733 Ok(())
1734 })
1735 }
1736
1737 #[test]
1738 fn test_pre_t2_to_t2_migration_invalid_policy_still_fails() -> eyre::Result<()> {
1739 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1741 let admin = Address::random();
1742 let user = Address::random();
1743
1744 let policy_id = StorageCtx::enter(&mut storage, || {
1745 let mut registry = TIP403Registry::new();
1746 registry.create_policy(
1747 admin,
1748 ITIP403Registry::createPolicyCall {
1749 admin,
1750 policyType: ITIP403Registry::PolicyType::COMPOUND,
1751 },
1752 )
1753 })?;
1754
1755 let mut storage = storage.with_spec(TempoHardfork::T2);
1757 StorageCtx::enter(&mut storage, || {
1758 let registry = TIP403Registry::new();
1759
1760 let result = registry.policy_data(ITIP403Registry::policyDataCall {
1762 policyId: policy_id,
1763 });
1764 assert!(result.is_err());
1765 assert_eq!(
1766 result.unwrap_err(),
1767 TIP403RegistryError::invalid_policy_type().into()
1768 );
1769
1770 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
1772 assert!(result.is_err());
1773 assert_eq!(
1774 result.unwrap_err(),
1775 TIP403RegistryError::invalid_policy_type().into()
1776 );
1777
1778 Ok(())
1779 })
1780 }
1781
1782 #[test]
1783 fn test_t2_compound_policy_rejects_legacy_invalid_255_policy() -> eyre::Result<()> {
1784 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1786 let admin = Address::random();
1787 let creator = Address::random();
1788
1789 let invalid_policy_id = StorageCtx::enter(&mut storage, || {
1790 let mut registry = TIP403Registry::new();
1791 registry.create_policy(
1792 admin,
1793 ITIP403Registry::createPolicyCall {
1794 admin,
1795 policyType: ITIP403Registry::PolicyType::__Invalid,
1796 },
1797 )
1798 })?;
1799
1800 let mut storage = storage.with_spec(TempoHardfork::T2);
1802 StorageCtx::enter(&mut storage, || {
1803 let mut registry = TIP403Registry::new();
1804
1805 let valid_policy_id = registry.create_policy(
1806 admin,
1807 ITIP403Registry::createPolicyCall {
1808 admin,
1809 policyType: ITIP403Registry::PolicyType::WHITELIST,
1810 },
1811 )?;
1812
1813 let result = registry.create_compound_policy(
1815 creator,
1816 ITIP403Registry::createCompoundPolicyCall {
1817 senderPolicyId: invalid_policy_id,
1818 recipientPolicyId: valid_policy_id,
1819 mintRecipientPolicyId: valid_policy_id,
1820 },
1821 );
1822 assert!(matches!(
1823 result.unwrap_err(),
1824 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotSimple(_))
1825 ));
1826
1827 Ok(())
1828 })
1829 }
1830
1831 #[test]
1832 fn test_t2_validate_policy_type_returns_correct_u8() -> eyre::Result<()> {
1833 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
1834 let admin = Address::random();
1835 StorageCtx::enter(&mut storage, || {
1836 let mut registry = TIP403Registry::new();
1837
1838 let whitelist_id = registry.create_policy(
1840 admin,
1841 ITIP403Registry::createPolicyCall {
1842 admin,
1843 policyType: ITIP403Registry::PolicyType::WHITELIST,
1844 },
1845 )?;
1846 let data = registry.get_policy_data(whitelist_id)?;
1847 assert_eq!(data.policy_type, 0u8);
1848
1849 let blacklist_id = registry.create_policy(
1851 admin,
1852 ITIP403Registry::createPolicyCall {
1853 admin,
1854 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1855 },
1856 )?;
1857 let data = registry.get_policy_data(blacklist_id)?;
1858 assert_eq!(data.policy_type, 1u8);
1859
1860 Ok(())
1861 })
1862 }
1863
1864 #[test]
1865 fn test_is_simple_errors_on_invalid_policy_type_t2() -> eyre::Result<()> {
1866 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1870 let admin = Address::random();
1871 let user = Address::random();
1872
1873 let policy_id = StorageCtx::enter(&mut storage, || {
1875 let mut registry = TIP403Registry::new();
1876 registry.create_policy(
1877 admin,
1878 ITIP403Registry::createPolicyCall {
1879 admin,
1880 policyType: ITIP403Registry::PolicyType::COMPOUND,
1881 },
1882 )
1883 })?;
1884
1885 let mut storage = storage.with_spec(TempoHardfork::T2);
1887 StorageCtx::enter(&mut storage, || {
1888 let registry = TIP403Registry::new();
1889
1890 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
1891 assert_eq!(
1892 result.unwrap_err(),
1893 TIP403RegistryError::invalid_policy_type().into()
1894 );
1895
1896 Ok(())
1897 })
1898 }
1899
1900 #[test]
1901 fn test_pre_t1_whitelist_and_blacklist_work_normally() -> eyre::Result<()> {
1902 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1903 let admin = Address::random();
1904 let user = Address::random();
1905 StorageCtx::enter(&mut storage, || {
1906 let mut registry = TIP403Registry::new();
1907
1908 let whitelist_id = registry.create_policy(
1910 admin,
1911 ITIP403Registry::createPolicyCall {
1912 admin,
1913 policyType: ITIP403Registry::PolicyType::WHITELIST,
1914 },
1915 )?;
1916
1917 assert!(!registry.is_authorized_as(whitelist_id, user, AuthRole::Transfer)?);
1919
1920 registry.modify_policy_whitelist(
1922 admin,
1923 ITIP403Registry::modifyPolicyWhitelistCall {
1924 policyId: whitelist_id,
1925 account: user,
1926 allowed: true,
1927 },
1928 )?;
1929
1930 assert!(registry.is_authorized_as(whitelist_id, user, AuthRole::Transfer)?);
1932
1933 let blacklist_id = registry.create_policy(
1935 admin,
1936 ITIP403Registry::createPolicyCall {
1937 admin,
1938 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1939 },
1940 )?;
1941
1942 assert!(registry.is_authorized_as(blacklist_id, user, AuthRole::Transfer)?);
1944
1945 registry.modify_policy_blacklist(
1947 admin,
1948 ITIP403Registry::modifyPolicyBlacklistCall {
1949 policyId: blacklist_id,
1950 account: user,
1951 restricted: true,
1952 },
1953 )?;
1954
1955 assert!(!registry.is_authorized_as(blacklist_id, user, AuthRole::Transfer)?);
1957
1958 Ok(())
1959 })
1960 }
1961
1962 #[test]
1963 fn test_pre_t1_create_policy_event_emits_invalid() -> eyre::Result<()> {
1964 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
1965 let admin = Address::random();
1966
1967 StorageCtx::enter(&mut storage, || {
1968 let mut registry = TIP403Registry::new();
1969
1970 let policy_id = registry.create_policy(
1971 admin,
1972 ITIP403Registry::createPolicyCall {
1973 admin,
1974 policyType: ITIP403Registry::PolicyType::COMPOUND,
1975 },
1976 )?;
1977
1978 let data = registry.get_policy_data(policy_id)?;
1979 assert_eq!(data.policy_type, 255u8);
1980
1981 Ok::<_, TempoPrecompileError>(())
1982 })?;
1983
1984 let events = storage.events.get(&TIP403_REGISTRY_ADDRESS).unwrap();
1985 let policy_created_log = Log::new_unchecked(
1986 TIP403_REGISTRY_ADDRESS,
1987 events[0].topics().to_vec(),
1988 events[0].data.clone(),
1989 );
1990 let decoded = ITIP403Registry::PolicyCreated::decode_log(&policy_created_log)?;
1991
1992 assert_eq!(decoded.policyType, ITIP403Registry::PolicyType::__Invalid);
1994
1995 Ok(())
1996 }
1997
1998 #[test]
1999 fn test_t2_create_policy_rejects_invalid_types() -> eyre::Result<()> {
2000 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2001 let admin = Address::random();
2002
2003 StorageCtx::enter(&mut storage, || {
2004 let mut registry = TIP403Registry::new();
2005
2006 for policy_type in [
2007 ITIP403Registry::PolicyType::COMPOUND,
2008 ITIP403Registry::PolicyType::__Invalid,
2009 ] {
2010 let result = registry.create_policy(
2011 admin,
2012 ITIP403Registry::createPolicyCall {
2013 admin,
2014 policyType: policy_type,
2015 },
2016 );
2017 assert!(matches!(
2018 result.unwrap_err(),
2019 TempoPrecompileError::TIP403RegistryError(
2020 TIP403RegistryError::IncompatiblePolicyType(_)
2021 )
2022 ));
2023 }
2024
2025 Ok(())
2026 })
2027 }
2028
2029 #[test]
2030 fn test_t2_create_policy_emits_correct_type() -> eyre::Result<()> {
2031 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2032 let admin = Address::random();
2033
2034 StorageCtx::enter(&mut storage, || {
2035 let mut registry = TIP403Registry::new();
2036
2037 registry.create_policy(
2038 admin,
2039 ITIP403Registry::createPolicyCall {
2040 admin,
2041 policyType: ITIP403Registry::PolicyType::WHITELIST,
2042 },
2043 )?;
2044
2045 registry.create_policy(
2046 admin,
2047 ITIP403Registry::createPolicyCall {
2048 admin,
2049 policyType: ITIP403Registry::PolicyType::BLACKLIST,
2050 },
2051 )?;
2052
2053 Ok::<_, TempoPrecompileError>(())
2054 })?;
2055
2056 let events = storage.events.get(&TIP403_REGISTRY_ADDRESS).unwrap();
2057
2058 let whitelist_log = Log::new_unchecked(
2060 TIP403_REGISTRY_ADDRESS,
2061 events[0].topics().to_vec(),
2062 events[0].data.clone(),
2063 );
2064 let whitelist_decoded = ITIP403Registry::PolicyCreated::decode_log(&whitelist_log)?;
2065 assert_eq!(
2066 whitelist_decoded.policyType,
2067 ITIP403Registry::PolicyType::WHITELIST
2068 );
2069
2070 let blacklist_log = Log::new_unchecked(
2071 TIP403_REGISTRY_ADDRESS,
2072 events[2].topics().to_vec(),
2073 events[2].data.clone(),
2074 );
2075 let blacklist_decoded = ITIP403Registry::PolicyCreated::decode_log(&blacklist_log)?;
2076 assert_eq!(
2077 blacklist_decoded.policyType,
2078 ITIP403Registry::PolicyType::BLACKLIST
2079 );
2080
2081 Ok(())
2082 }
2083
2084 #[test]
2085 fn test_compound_policy_data_error_cases() -> eyre::Result<()> {
2086 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2087 let admin = Address::random();
2088 StorageCtx::enter(&mut storage, || {
2089 let mut registry = TIP403Registry::new();
2090
2091 let result = registry
2093 .compound_policy_data(ITIP403Registry::compoundPolicyDataCall { policyId: 999 });
2094 assert!(matches!(
2095 result.unwrap_err(),
2096 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotFound(_))
2097 ));
2098
2099 let simple_policy_id = registry.create_policy(
2101 admin,
2102 ITIP403Registry::createPolicyCall {
2103 admin,
2104 policyType: ITIP403Registry::PolicyType::WHITELIST,
2105 },
2106 )?;
2107 let result = registry.compound_policy_data(ITIP403Registry::compoundPolicyDataCall {
2108 policyId: simple_policy_id,
2109 });
2110 assert!(matches!(
2111 result.unwrap_err(),
2112 TempoPrecompileError::TIP403RegistryError(
2113 TIP403RegistryError::IncompatiblePolicyType(_)
2114 )
2115 ));
2116
2117 Ok(())
2118 })
2119 }
2120
2121 #[test]
2122 fn test_invalid_policy_type() -> eyre::Result<()> {
2123 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2125 let admin = Address::random();
2126 let user = Address::random();
2127
2128 let policy_id = StorageCtx::enter(&mut storage, || {
2129 let mut registry = TIP403Registry::new();
2130 registry.create_policy(
2131 admin,
2132 ITIP403Registry::createPolicyCall {
2133 admin,
2134 policyType: ITIP403Registry::PolicyType::__Invalid,
2135 },
2136 )
2137 })?;
2138
2139 StorageCtx::enter(&mut storage, || {
2141 let registry = TIP403Registry::new();
2142
2143 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2144 policyId: policy_id,
2145 });
2146 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2147
2148 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2149 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2150
2151 Ok::<_, TempoPrecompileError>(())
2152 })?;
2153
2154 let mut storage = storage.with_spec(TempoHardfork::T2);
2156 StorageCtx::enter(&mut storage, || {
2157 let registry = TIP403Registry::new();
2158
2159 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2160 policyId: policy_id,
2161 });
2162 assert_eq!(
2163 result.unwrap_err(),
2164 TIP403RegistryError::invalid_policy_type().into()
2165 );
2166
2167 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2168 assert_eq!(
2169 result.unwrap_err(),
2170 TIP403RegistryError::invalid_policy_type().into()
2171 );
2172
2173 Ok(())
2174 })
2175 }
2176
2177 #[test]
2178 fn test_initialize_sets_storage_state() -> eyre::Result<()> {
2179 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2180 StorageCtx::enter(&mut storage, || {
2181 let mut registry = TIP403Registry::new();
2182
2183 assert!(!registry.is_initialized()?);
2185
2186 registry.initialize()?;
2188
2189 assert!(registry.is_initialized()?);
2191
2192 let registry2 = TIP403Registry::new();
2194 assert!(registry2.is_initialized()?);
2195
2196 Ok(())
2197 })
2198 }
2199
2200 #[test]
2201 fn test_policy_exists_boundary_at_counter() -> eyre::Result<()> {
2202 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2203 let admin = Address::random();
2204 StorageCtx::enter(&mut storage, || {
2205 let mut registry = TIP403Registry::new();
2206
2207 let policy_id = registry.create_policy(
2209 admin,
2210 ITIP403Registry::createPolicyCall {
2211 admin,
2212 policyType: ITIP403Registry::PolicyType::WHITELIST,
2213 },
2214 )?;
2215
2216 let counter = registry.policy_id_counter()?;
2218 assert_eq!(counter, 3);
2219
2220 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
2222 policyId: policy_id,
2223 })?);
2224
2225 assert!(
2227 !registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: counter })?
2228 );
2229
2230 assert!(!registry.policy_exists(ITIP403Registry::policyExistsCall {
2232 policyId: counter + 1,
2233 })?);
2234
2235 Ok(())
2236 })
2237 }
2238
2239 #[test]
2240 fn test_nonexistent_policy_behavior() -> eyre::Result<()> {
2241 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
2242 let user = Address::random();
2243 let nonexistent_id = 999;
2244
2245 StorageCtx::enter(&mut storage, || -> Result<()> {
2247 let registry = TIP403Registry::new();
2248 let data = registry.get_policy_data(nonexistent_id)?;
2249 assert!(data.is_default());
2250 assert!(!registry.is_authorized_as(nonexistent_id, user, AuthRole::Transfer)?);
2251 Ok(())
2252 })?;
2253
2254 let mut storage = storage.with_spec(TempoHardfork::T2);
2256 StorageCtx::enter(&mut storage, || {
2257 let registry = TIP403Registry::new();
2258 assert_eq!(
2259 registry.get_policy_data(nonexistent_id).unwrap_err(),
2260 TIP403RegistryError::policy_not_found().into()
2261 );
2262 assert_eq!(
2263 registry
2264 .is_authorized_as(nonexistent_id, user, AuthRole::Transfer)
2265 .unwrap_err(),
2266 TIP403RegistryError::policy_not_found().into()
2267 );
2268 Ok(())
2269 })
2270 }
2271
2272 #[test]
2275 fn test_modify_whitelist_rejects_virtual_address() -> eyre::Result<()> {
2276 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
2277 let admin = Address::random();
2278
2279 StorageCtx::enter(&mut storage, || {
2280 let mut registry = TIP403Registry::new();
2281 let policy_id = registry.create_policy(
2282 admin,
2283 ITIP403Registry::createPolicyCall {
2284 admin,
2285 policyType: PolicyType::WHITELIST,
2286 },
2287 )?;
2288
2289 let result = registry.modify_policy_whitelist(
2290 admin,
2291 ITIP403Registry::modifyPolicyWhitelistCall {
2292 policyId: policy_id,
2293 account: Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
2294 allowed: true,
2295 },
2296 );
2297 assert!(matches!(
2298 result.unwrap_err(),
2299 TempoPrecompileError::TIP403RegistryError(
2300 TIP403RegistryError::VirtualAddressNotAllowed(_)
2301 )
2302 ));
2303
2304 Ok(())
2305 })
2306 }
2307
2308 #[test]
2309 fn test_modify_blacklist_rejects_virtual_address() -> eyre::Result<()> {
2310 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
2311 let admin = Address::random();
2312
2313 StorageCtx::enter(&mut storage, || {
2314 let mut registry = TIP403Registry::new();
2315 let policy_id = registry.create_policy(
2316 admin,
2317 ITIP403Registry::createPolicyCall {
2318 admin,
2319 policyType: PolicyType::BLACKLIST,
2320 },
2321 )?;
2322
2323 let result = registry.modify_policy_blacklist(
2324 admin,
2325 ITIP403Registry::modifyPolicyBlacklistCall {
2326 policyId: policy_id,
2327 account: Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
2328 restricted: true,
2329 },
2330 );
2331 assert!(matches!(
2332 result.unwrap_err(),
2333 TempoPrecompileError::TIP403RegistryError(
2334 TIP403RegistryError::VirtualAddressNotAllowed(_)
2335 )
2336 ));
2337
2338 Ok(())
2339 })
2340 }
2341
2342 #[test]
2343 fn test_create_policy_with_accounts_rejects_virtual_address() -> eyre::Result<()> {
2344 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
2345 let admin = Address::random();
2346
2347 StorageCtx::enter(&mut storage, || {
2348 let mut registry = TIP403Registry::new();
2349
2350 let result = registry.create_policy_with_accounts(
2351 admin,
2352 ITIP403Registry::createPolicyWithAccountsCall {
2353 admin,
2354 policyType: PolicyType::WHITELIST,
2355 accounts: vec![
2356 Address::random(),
2357 Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
2358 ],
2359 },
2360 );
2361 assert!(matches!(
2362 result.unwrap_err(),
2363 TempoPrecompileError::TIP403RegistryError(
2364 TIP403RegistryError::VirtualAddressNotAllowed(_)
2365 )
2366 ));
2367
2368 assert_eq!(registry.policy_id_counter()?, 2);
2370
2371 Ok(())
2372 })
2373 }
2374}