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