1use crate::{
4 Precompile, SelectorSchedule, charge_input_cost, dispatch_call, mutate, mutate_void,
5 tip403_registry::{AuthRole, TIP403Registry},
6 view,
7};
8use alloy::{
9 primitives::Address,
10 sol_types::{SolCall, SolInterface},
11};
12use revm::precompile::PrecompileResult;
13use tempo_chainspec::hardfork::TempoHardfork;
14use tempo_contracts::precompiles::ITIP403Registry::{self, ITIP403RegistryCalls};
15
16const T2_ADDED: &[[u8; 4]] = &[
17 ITIP403Registry::isAuthorizedSenderCall::SELECTOR,
18 ITIP403Registry::isAuthorizedRecipientCall::SELECTOR,
19 ITIP403Registry::isAuthorizedMintRecipientCall::SELECTOR,
20 ITIP403Registry::compoundPolicyDataCall::SELECTOR,
21 ITIP403Registry::createCompoundPolicyCall::SELECTOR,
22];
23
24const T6_ADDED: &[[u8; 4]] = &[
25 ITIP403Registry::receivePolicyCall::SELECTOR,
26 ITIP403Registry::validateReceivePolicyCall::SELECTOR,
27 ITIP403Registry::setReceivePolicyCall::SELECTOR,
28];
29
30impl Precompile for TIP403Registry {
31 fn call(&mut self, calldata: &[u8], msg_sender: Address) -> PrecompileResult {
32 if let Some(err) = charge_input_cost(&mut self.storage, calldata) {
33 return err;
34 }
35
36 dispatch_call(
37 calldata,
38 &[
39 SelectorSchedule::new(TempoHardfork::T2).with_added(T2_ADDED),
40 SelectorSchedule::new(TempoHardfork::T6).with_added(T6_ADDED),
41 ],
42 ITIP403RegistryCalls::abi_decode,
43 |call| match call {
44 ITIP403RegistryCalls::policyIdCounter(call) => {
45 view(call, |_| self.policy_id_counter())
46 }
47 ITIP403RegistryCalls::policyExists(call) => view(call, |c| self.policy_exists(c)),
48 ITIP403RegistryCalls::policyData(call) => view(call, |c| self.policy_data(c)),
49 ITIP403RegistryCalls::isAuthorized(call) => view(call, |c| {
50 self.is_authorized_as(c.policyId, c.user, AuthRole::Transfer)
51 }),
52 ITIP403RegistryCalls::isAuthorizedSender(call) => view(call, |c| {
54 self.is_authorized_as(c.policyId, c.user, AuthRole::Sender)
55 }),
56 ITIP403RegistryCalls::isAuthorizedRecipient(call) => view(call, |c| {
57 self.is_authorized_as(c.policyId, c.user, AuthRole::Recipient)
58 }),
59 ITIP403RegistryCalls::isAuthorizedMintRecipient(call) => view(call, |c| {
60 self.is_authorized_as(c.policyId, c.user, AuthRole::MintRecipient)
61 }),
62 ITIP403RegistryCalls::compoundPolicyData(call) => {
63 view(call, |c| self.compound_policy_data(c))
64 }
65 ITIP403RegistryCalls::receivePolicy(call) => {
66 view(call, |c| self.receive_policy(c.account))
67 }
68 ITIP403RegistryCalls::validateReceivePolicy(call) => view(call, |c| {
69 let blocked_reason = self
70 .validate_receive_policy(c.token, c.sender, c.receiver)?
71 .unwrap_or(ITIP403Registry::BlockedReason::NONE);
72 Ok(ITIP403Registry::validateReceivePolicyReturn {
73 authorized: blocked_reason == ITIP403Registry::BlockedReason::NONE,
74 blockedReason: blocked_reason,
75 })
76 }),
77 ITIP403RegistryCalls::setReceivePolicy(call) => {
78 mutate_void(call, msg_sender, |s, c| self.set_receive_policy(s, c))
79 }
80 ITIP403RegistryCalls::createPolicy(call) => {
81 mutate(call, msg_sender, |s, c| self.create_policy(s, c))
82 }
83 ITIP403RegistryCalls::createPolicyWithAccounts(call) => {
84 mutate(call, msg_sender, |s, c| {
85 self.create_policy_with_accounts(s, c)
86 })
87 }
88 ITIP403RegistryCalls::setPolicyAdmin(call) => {
89 mutate_void(call, msg_sender, |s, c| self.set_policy_admin(s, c))
90 }
91 ITIP403RegistryCalls::modifyPolicyWhitelist(call) => {
92 mutate_void(call, msg_sender, |s, c| self.modify_policy_whitelist(s, c))
93 }
94 ITIP403RegistryCalls::modifyPolicyBlacklist(call) => {
95 mutate_void(call, msg_sender, |s, c| self.modify_policy_blacklist(s, c))
96 }
97 ITIP403RegistryCalls::createCompoundPolicy(call) => {
99 mutate(call, msg_sender, |s, c| self.create_compound_policy(s, c))
100 }
101 },
102 )
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::{
110 storage::{StorageCtx, hashmap::HashMapStorageProvider},
111 test_util::{assert_full_coverage, check_selector_coverage},
112 tip403_registry::ITIP403Registry,
113 };
114 use alloy::sol_types::{SolCall, SolError, SolValue};
115 use tempo_chainspec::hardfork::TempoHardfork;
116 use tempo_contracts::precompiles::{
117 ITIP403Registry::ITIP403RegistryCalls, UnknownFunctionSelector,
118 };
119
120 #[test]
121 fn test_is_authorized_precompile() -> eyre::Result<()> {
122 let mut storage = HashMapStorageProvider::new(1);
123 let user = Address::random();
124 StorageCtx::enter(&mut storage, || {
125 let mut registry = TIP403Registry::new();
126
127 let call = ITIP403Registry::isAuthorizedCall { policyId: 1, user };
129 let calldata = call.abi_encode();
130 let result = registry.call(&calldata, Address::ZERO);
131
132 assert!(result.is_ok());
133 let output = result.unwrap();
134 let decoded: bool =
135 ITIP403Registry::isAuthorizedCall::abi_decode_returns(&output.bytes).unwrap();
136 assert!(decoded);
137
138 Ok(())
139 })
140 }
141
142 #[test]
143 fn test_create_policy_precompile() -> eyre::Result<()> {
144 let mut storage = HashMapStorageProvider::new(1);
145 let admin = Address::random();
146 StorageCtx::enter(&mut storage, || {
147 let mut registry = TIP403Registry::new();
148
149 let call = ITIP403Registry::createPolicyCall {
150 admin,
151 policyType: ITIP403Registry::PolicyType::WHITELIST,
152 };
153 let calldata = call.abi_encode();
154 let result = registry.call(&calldata, admin);
155
156 assert!(result.is_ok());
157 let output = result.unwrap();
158 let decoded: u64 =
159 ITIP403Registry::createPolicyCall::abi_decode_returns(&output.bytes).unwrap();
160 assert_eq!(decoded, 2); Ok(())
163 })
164 }
165
166 #[test]
167 fn test_policy_id_counter_initialization() -> eyre::Result<()> {
168 let mut storage = HashMapStorageProvider::new(1);
169 let sender = Address::random();
170 StorageCtx::enter(&mut storage, || {
171 let mut registry = TIP403Registry::new();
172
173 let counter_call = ITIP403Registry::policyIdCounterCall {};
175 let calldata = counter_call.abi_encode();
176 let result = registry.call(&calldata, sender).unwrap();
177 let counter = u64::abi_decode(&result.bytes).unwrap();
178 assert_eq!(counter, 2); Ok(())
181 })
182 }
183
184 #[test]
185 fn test_create_policy_with_accounts() -> eyre::Result<()> {
186 let mut storage = HashMapStorageProvider::new(1);
187 let admin = Address::random();
188 let account1 = Address::random();
189 let account2 = Address::random();
190 let other_account = Address::random();
191 StorageCtx::enter(&mut storage, || {
192 let mut registry = TIP403Registry::new();
193
194 let accounts = vec![account1, account2];
195 let call = ITIP403Registry::createPolicyWithAccountsCall {
196 admin,
197 policyType: ITIP403Registry::PolicyType::WHITELIST,
198 accounts,
199 };
200 let calldata = call.abi_encode();
201 let result = registry.call(&calldata, admin).unwrap();
202
203 let policy_id: u64 =
204 ITIP403Registry::createPolicyWithAccountsCall::abi_decode_returns(&result.bytes)
205 .unwrap();
206 assert_eq!(policy_id, 2);
207
208 let is_auth_call = ITIP403Registry::isAuthorizedCall {
210 policyId: policy_id,
211 user: account1,
212 };
213 let calldata = is_auth_call.abi_encode();
214 let result = registry.call(&calldata, admin).unwrap();
215 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
216 assert!(is_authorized);
217
218 let is_auth_call = ITIP403Registry::isAuthorizedCall {
219 policyId: policy_id,
220 user: account2,
221 };
222 let calldata = is_auth_call.abi_encode();
223 let result = registry.call(&calldata, admin).unwrap();
224 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
225 assert!(is_authorized);
226
227 let is_auth_call = ITIP403Registry::isAuthorizedCall {
229 policyId: policy_id,
230 user: other_account,
231 };
232 let calldata = is_auth_call.abi_encode();
233 let result = registry.call(&calldata, admin).unwrap();
234 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
235 assert!(!is_authorized);
236
237 Ok(())
238 })
239 }
240
241 #[test]
242 fn test_blacklist_policy() -> eyre::Result<()> {
243 let mut storage = HashMapStorageProvider::new(1);
244 let admin = Address::random();
245 let blocked_account = Address::random();
246 let allowed_account = Address::random();
247 StorageCtx::enter(&mut storage, || {
248 let mut registry = TIP403Registry::new();
249
250 let call = ITIP403Registry::createPolicyCall {
252 admin,
253 policyType: ITIP403Registry::PolicyType::BLACKLIST,
254 };
255 let calldata = call.abi_encode();
256 let result = registry.call(&calldata, admin).unwrap();
257 let policy_id: u64 =
258 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
259
260 let is_auth_call = ITIP403Registry::isAuthorizedCall {
262 policyId: policy_id,
263 user: blocked_account,
264 };
265 let calldata = is_auth_call.abi_encode();
266 let result = registry.call(&calldata, admin).unwrap();
267 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
268 assert!(is_authorized);
269
270 let modify_call = ITIP403Registry::modifyPolicyBlacklistCall {
272 policyId: policy_id,
273 account: blocked_account,
274 restricted: true,
275 };
276 let calldata = modify_call.abi_encode();
277 registry.call(&calldata, admin).unwrap();
278
279 let is_auth_call = ITIP403Registry::isAuthorizedCall {
281 policyId: policy_id,
282 user: blocked_account,
283 };
284 let calldata = is_auth_call.abi_encode();
285 let result = registry.call(&calldata, admin).unwrap();
286 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
287 assert!(!is_authorized);
288
289 let is_auth_call = ITIP403Registry::isAuthorizedCall {
291 policyId: policy_id,
292 user: allowed_account,
293 };
294 let calldata = is_auth_call.abi_encode();
295 let result = registry.call(&calldata, admin).unwrap();
296 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
297 assert!(is_authorized);
298
299 let modify_call = ITIP403Registry::modifyPolicyBlacklistCall {
301 policyId: policy_id,
302 account: blocked_account,
303 restricted: false,
304 };
305 let calldata = modify_call.abi_encode();
306 registry.call(&calldata, admin).unwrap();
307
308 let is_auth_call = ITIP403Registry::isAuthorizedCall {
310 policyId: policy_id,
311 user: blocked_account,
312 };
313 let calldata = is_auth_call.abi_encode();
314 let result = registry.call(&calldata, admin).unwrap();
315 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
316 assert!(is_authorized);
317
318 Ok(())
319 })
320 }
321
322 #[test]
323 fn test_modify_policy_whitelist() -> eyre::Result<()> {
324 let mut storage = HashMapStorageProvider::new(1);
325 let admin = Address::random();
326 let account1 = Address::random();
327 let account2 = Address::random();
328 StorageCtx::enter(&mut storage, || {
329 let mut registry = TIP403Registry::new();
330
331 let call = ITIP403Registry::createPolicyCall {
333 admin,
334 policyType: ITIP403Registry::PolicyType::WHITELIST,
335 };
336 let calldata = call.abi_encode();
337 let result = registry.call(&calldata, admin).unwrap();
338 let policy_id: u64 =
339 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
340
341 let modify_call1 = ITIP403Registry::modifyPolicyWhitelistCall {
343 policyId: policy_id,
344 account: account1,
345 allowed: true,
346 };
347 let calldata = modify_call1.abi_encode();
348 registry.call(&calldata, admin).unwrap();
349
350 let modify_call2 = ITIP403Registry::modifyPolicyWhitelistCall {
351 policyId: policy_id,
352 account: account2,
353 allowed: true,
354 };
355 let calldata = modify_call2.abi_encode();
356 registry.call(&calldata, admin).unwrap();
357
358 let is_auth_call = ITIP403Registry::isAuthorizedCall {
360 policyId: policy_id,
361 user: account1,
362 };
363 let calldata = is_auth_call.abi_encode();
364 let result = registry.call(&calldata, admin).unwrap();
365 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
366 assert!(is_authorized);
367
368 let is_auth_call = ITIP403Registry::isAuthorizedCall {
369 policyId: policy_id,
370 user: account2,
371 };
372 let calldata = is_auth_call.abi_encode();
373 let result = registry.call(&calldata, admin).unwrap();
374 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
375 assert!(is_authorized);
376
377 let modify_call = ITIP403Registry::modifyPolicyWhitelistCall {
379 policyId: policy_id,
380 account: account1,
381 allowed: false,
382 };
383 let calldata = modify_call.abi_encode();
384 registry.call(&calldata, admin).unwrap();
385
386 let is_auth_call = ITIP403Registry::isAuthorizedCall {
388 policyId: policy_id,
389 user: account1,
390 };
391 let calldata = is_auth_call.abi_encode();
392 let result = registry.call(&calldata, admin).unwrap();
393 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
394 assert!(!is_authorized);
395
396 let is_auth_call = ITIP403Registry::isAuthorizedCall {
397 policyId: policy_id,
398 user: account2,
399 };
400 let calldata = is_auth_call.abi_encode();
401 let result = registry.call(&calldata, admin).unwrap();
402 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
403 assert!(is_authorized);
404
405 Ok(())
406 })
407 }
408
409 #[test]
410 fn test_set_policy_admin() -> eyre::Result<()> {
411 let mut storage = HashMapStorageProvider::new(1);
412 let admin = Address::random();
413 let new_admin = Address::random();
414 StorageCtx::enter(&mut storage, || {
415 let mut registry = TIP403Registry::new();
416
417 let call = ITIP403Registry::createPolicyCall {
419 admin,
420 policyType: ITIP403Registry::PolicyType::WHITELIST,
421 };
422 let calldata = call.abi_encode();
423 let result = registry.call(&calldata, admin).unwrap();
424 let policy_id: u64 =
425 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
426
427 let policy_data_call = ITIP403Registry::policyDataCall {
429 policyId: policy_id,
430 };
431 let calldata = policy_data_call.abi_encode();
432 let result = registry.call(&calldata, admin).unwrap();
433 let policy_data =
434 ITIP403Registry::policyDataCall::abi_decode_returns(&result.bytes).unwrap();
435 assert_eq!(policy_data.admin, admin);
436
437 let set_admin_call = ITIP403Registry::setPolicyAdminCall {
439 policyId: policy_id,
440 admin: new_admin,
441 };
442 let calldata = set_admin_call.abi_encode();
443 registry.call(&calldata, admin).unwrap();
444
445 let policy_data_call = ITIP403Registry::policyDataCall {
447 policyId: policy_id,
448 };
449 let calldata = policy_data_call.abi_encode();
450 let result = registry.call(&calldata, admin).unwrap();
451 let policy_data =
452 ITIP403Registry::policyDataCall::abi_decode_returns(&result.bytes).unwrap();
453 assert_eq!(policy_data.admin, new_admin);
454
455 Ok(())
456 })
457 }
458
459 #[test]
460 fn test_special_policy_ids() -> eyre::Result<()> {
461 let mut storage = HashMapStorageProvider::new(1);
462 let user = Address::random();
463 StorageCtx::enter(&mut storage, || {
464 let mut registry = TIP403Registry::new();
465
466 let is_auth_call = ITIP403Registry::isAuthorizedCall { policyId: 0, user };
468 let calldata = is_auth_call.abi_encode();
469 let result = registry.call(&calldata, Address::ZERO).unwrap();
470 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
471 assert!(!is_authorized);
472
473 let is_auth_call = ITIP403Registry::isAuthorizedCall { policyId: 1, user };
475 let calldata = is_auth_call.abi_encode();
476 let result = registry.call(&calldata, Address::ZERO).unwrap();
477 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
478 assert!(is_authorized);
479
480 Ok(())
481 })
482 }
483
484 #[test]
485 fn test_invalid_selector() -> eyre::Result<()> {
486 let sender = Address::random();
487
488 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
490 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
491 let mut registry = TIP403Registry::new();
492
493 let invalid_data = vec![0x12, 0x34, 0x56, 0x78];
494 let result = registry.call(&invalid_data, sender)?;
495 assert!(result.is_revert());
496
497 let short_data = vec![0x12, 0x34];
499 let result = registry.call(&short_data, sender)?;
500 assert!(result.is_revert());
501
502 Ok(())
503 })?;
504
505 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
507 StorageCtx::enter(&mut storage, || {
508 let mut registry = TIP403Registry::new();
509
510 let short_data = vec![0x12, 0x34];
511 let result = registry.call(&short_data, sender);
512 let output = result.expect("expected Ok(halt) for short calldata");
513 assert!(output.is_halt());
514
515 Ok(())
516 })
517 }
518
519 #[test]
520 fn test_create_multiple_policies() -> eyre::Result<()> {
521 let mut storage = HashMapStorageProvider::new(1);
522 let admin = Address::random();
523 StorageCtx::enter(&mut storage, || {
524 let mut registry = TIP403Registry::new();
525
526 let whitelist_call = ITIP403Registry::createPolicyCall {
528 admin,
529 policyType: ITIP403Registry::PolicyType::WHITELIST,
530 };
531 let calldata = whitelist_call.abi_encode();
532 let result = registry.call(&calldata, admin).unwrap();
533 let whitelist_id: u64 =
534 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
535
536 let blacklist_call = ITIP403Registry::createPolicyCall {
537 admin,
538 policyType: ITIP403Registry::PolicyType::BLACKLIST,
539 };
540 let calldata = blacklist_call.abi_encode();
541 let result = registry.call(&calldata, admin).unwrap();
542 let blacklist_id: u64 =
543 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
544
545 assert_eq!(whitelist_id, 2);
547 assert_eq!(blacklist_id, 3);
548
549 let counter_call = ITIP403Registry::policyIdCounterCall {};
551 let calldata = counter_call.abi_encode();
552 let result = registry.call(&calldata, admin).unwrap();
553 let counter = u64::abi_decode(&result.bytes).unwrap();
554 assert_eq!(counter, 4);
555
556 Ok(())
557 })
558 }
559
560 #[test]
561 fn test_selector_coverage() -> eyre::Result<()> {
562 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
565 StorageCtx::enter(&mut storage, || {
566 let mut registry = TIP403Registry::new();
567
568 let unsupported = check_selector_coverage(
569 &mut registry,
570 ITIP403RegistryCalls::SELECTORS,
571 "ITIP403Registry",
572 ITIP403RegistryCalls::name_by_selector,
573 );
574
575 assert_full_coverage([unsupported]);
576
577 Ok(())
578 })
579 }
580
581 #[test]
582 fn test_receive_policy_selectors_are_t6_gated() -> eyre::Result<()> {
583 let account = Address::random();
584 let receive_policy = ITIP403Registry::receivePolicyCall { account }.abi_encode();
585 let validate_receive_policy = ITIP403Registry::validateReceivePolicyCall {
586 token: Address::random(),
587 sender: Address::random(),
588 receiver: account,
589 }
590 .abi_encode();
591 let set_receive_policy = ITIP403Registry::setReceivePolicyCall {
592 senderPolicyId: 1,
593 tokenFilterId: 1,
594 recoveryAuthority: Address::ZERO,
595 }
596 .abi_encode();
597
598 for calldata in [
599 receive_policy.as_slice(),
600 validate_receive_policy.as_slice(),
601 set_receive_policy.as_slice(),
602 ] {
603 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T5);
604 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
605 let mut registry = TIP403Registry::new();
606 let result = registry
607 .call(calldata, account)
608 .map_err(|err| eyre::eyre!("{err:?}"))?;
609 assert!(result.is_revert());
610 assert!(UnknownFunctionSelector::abi_decode(&result.bytes).is_ok());
611 Ok(())
612 })?;
613 }
614
615 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
616 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
617 let mut registry = TIP403Registry::new();
618 for calldata in [
619 receive_policy.as_slice(),
620 validate_receive_policy.as_slice(),
621 set_receive_policy.as_slice(),
622 ] {
623 let result = registry
624 .call(calldata, account)
625 .map_err(|err| eyre::eyre!("{err:?}"))?;
626 assert!(!result.is_revert());
627 }
628 Ok(())
629 })
630 }
631}