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
24impl Precompile for TIP403Registry {
25 fn call(&mut self, calldata: &[u8], msg_sender: Address) -> PrecompileResult {
26 if let Some(err) = charge_input_cost(&mut self.storage, calldata) {
27 return err;
28 }
29
30 dispatch_call(
31 calldata,
32 &[SelectorSchedule::new(TempoHardfork::T2).with_added(T2_ADDED)],
33 ITIP403RegistryCalls::abi_decode,
34 |call| match call {
35 ITIP403RegistryCalls::policyIdCounter(call) => {
36 view(call, |_| self.policy_id_counter())
37 }
38 ITIP403RegistryCalls::policyExists(call) => view(call, |c| self.policy_exists(c)),
39 ITIP403RegistryCalls::policyData(call) => view(call, |c| self.policy_data(c)),
40 ITIP403RegistryCalls::isAuthorized(call) => view(call, |c| {
41 self.is_authorized_as(c.policyId, c.user, AuthRole::Transfer)
42 }),
43 ITIP403RegistryCalls::isAuthorizedSender(call) => view(call, |c| {
45 self.is_authorized_as(c.policyId, c.user, AuthRole::Sender)
46 }),
47 ITIP403RegistryCalls::isAuthorizedRecipient(call) => view(call, |c| {
48 self.is_authorized_as(c.policyId, c.user, AuthRole::Recipient)
49 }),
50 ITIP403RegistryCalls::isAuthorizedMintRecipient(call) => view(call, |c| {
51 self.is_authorized_as(c.policyId, c.user, AuthRole::MintRecipient)
52 }),
53 ITIP403RegistryCalls::compoundPolicyData(call) => {
54 view(call, |c| self.compound_policy_data(c))
55 }
56 ITIP403RegistryCalls::createPolicy(call) => {
57 mutate(call, msg_sender, |s, c| self.create_policy(s, c))
58 }
59 ITIP403RegistryCalls::createPolicyWithAccounts(call) => {
60 mutate(call, msg_sender, |s, c| {
61 self.create_policy_with_accounts(s, c)
62 })
63 }
64 ITIP403RegistryCalls::setPolicyAdmin(call) => {
65 mutate_void(call, msg_sender, |s, c| self.set_policy_admin(s, c))
66 }
67 ITIP403RegistryCalls::modifyPolicyWhitelist(call) => {
68 mutate_void(call, msg_sender, |s, c| self.modify_policy_whitelist(s, c))
69 }
70 ITIP403RegistryCalls::modifyPolicyBlacklist(call) => {
71 mutate_void(call, msg_sender, |s, c| self.modify_policy_blacklist(s, c))
72 }
73 ITIP403RegistryCalls::createCompoundPolicy(call) => {
75 mutate(call, msg_sender, |s, c| self.create_compound_policy(s, c))
76 }
77 },
78 )
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::{
86 storage::{StorageCtx, hashmap::HashMapStorageProvider},
87 test_util::{assert_full_coverage, check_selector_coverage},
88 tip403_registry::ITIP403Registry,
89 };
90 use alloy::sol_types::{SolCall, SolValue};
91 use tempo_chainspec::hardfork::TempoHardfork;
92 use tempo_contracts::precompiles::ITIP403Registry::ITIP403RegistryCalls;
93
94 #[test]
95 fn test_is_authorized_precompile() -> eyre::Result<()> {
96 let mut storage = HashMapStorageProvider::new(1);
97 let user = Address::random();
98 StorageCtx::enter(&mut storage, || {
99 let mut registry = TIP403Registry::new();
100
101 let call = ITIP403Registry::isAuthorizedCall { policyId: 1, user };
103 let calldata = call.abi_encode();
104 let result = registry.call(&calldata, Address::ZERO);
105
106 assert!(result.is_ok());
107 let output = result.unwrap();
108 let decoded: bool =
109 ITIP403Registry::isAuthorizedCall::abi_decode_returns(&output.bytes).unwrap();
110 assert!(decoded);
111
112 Ok(())
113 })
114 }
115
116 #[test]
117 fn test_create_policy_precompile() -> eyre::Result<()> {
118 let mut storage = HashMapStorageProvider::new(1);
119 let admin = Address::random();
120 StorageCtx::enter(&mut storage, || {
121 let mut registry = TIP403Registry::new();
122
123 let call = ITIP403Registry::createPolicyCall {
124 admin,
125 policyType: ITIP403Registry::PolicyType::WHITELIST,
126 };
127 let calldata = call.abi_encode();
128 let result = registry.call(&calldata, admin);
129
130 assert!(result.is_ok());
131 let output = result.unwrap();
132 let decoded: u64 =
133 ITIP403Registry::createPolicyCall::abi_decode_returns(&output.bytes).unwrap();
134 assert_eq!(decoded, 2); Ok(())
137 })
138 }
139
140 #[test]
141 fn test_policy_id_counter_initialization() -> eyre::Result<()> {
142 let mut storage = HashMapStorageProvider::new(1);
143 let sender = Address::random();
144 StorageCtx::enter(&mut storage, || {
145 let mut registry = TIP403Registry::new();
146
147 let counter_call = ITIP403Registry::policyIdCounterCall {};
149 let calldata = counter_call.abi_encode();
150 let result = registry.call(&calldata, sender).unwrap();
151 let counter = u64::abi_decode(&result.bytes).unwrap();
152 assert_eq!(counter, 2); Ok(())
155 })
156 }
157
158 #[test]
159 fn test_create_policy_with_accounts() -> eyre::Result<()> {
160 let mut storage = HashMapStorageProvider::new(1);
161 let admin = Address::random();
162 let account1 = Address::random();
163 let account2 = Address::random();
164 let other_account = Address::random();
165 StorageCtx::enter(&mut storage, || {
166 let mut registry = TIP403Registry::new();
167
168 let accounts = vec![account1, account2];
169 let call = ITIP403Registry::createPolicyWithAccountsCall {
170 admin,
171 policyType: ITIP403Registry::PolicyType::WHITELIST,
172 accounts,
173 };
174 let calldata = call.abi_encode();
175 let result = registry.call(&calldata, admin).unwrap();
176
177 let policy_id: u64 =
178 ITIP403Registry::createPolicyWithAccountsCall::abi_decode_returns(&result.bytes)
179 .unwrap();
180 assert_eq!(policy_id, 2);
181
182 let is_auth_call = ITIP403Registry::isAuthorizedCall {
184 policyId: policy_id,
185 user: account1,
186 };
187 let calldata = is_auth_call.abi_encode();
188 let result = registry.call(&calldata, admin).unwrap();
189 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
190 assert!(is_authorized);
191
192 let is_auth_call = ITIP403Registry::isAuthorizedCall {
193 policyId: policy_id,
194 user: account2,
195 };
196 let calldata = is_auth_call.abi_encode();
197 let result = registry.call(&calldata, admin).unwrap();
198 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
199 assert!(is_authorized);
200
201 let is_auth_call = ITIP403Registry::isAuthorizedCall {
203 policyId: policy_id,
204 user: other_account,
205 };
206 let calldata = is_auth_call.abi_encode();
207 let result = registry.call(&calldata, admin).unwrap();
208 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
209 assert!(!is_authorized);
210
211 Ok(())
212 })
213 }
214
215 #[test]
216 fn test_blacklist_policy() -> eyre::Result<()> {
217 let mut storage = HashMapStorageProvider::new(1);
218 let admin = Address::random();
219 let blocked_account = Address::random();
220 let allowed_account = Address::random();
221 StorageCtx::enter(&mut storage, || {
222 let mut registry = TIP403Registry::new();
223
224 let call = ITIP403Registry::createPolicyCall {
226 admin,
227 policyType: ITIP403Registry::PolicyType::BLACKLIST,
228 };
229 let calldata = call.abi_encode();
230 let result = registry.call(&calldata, admin).unwrap();
231 let policy_id: u64 =
232 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
233
234 let is_auth_call = ITIP403Registry::isAuthorizedCall {
236 policyId: policy_id,
237 user: blocked_account,
238 };
239 let calldata = is_auth_call.abi_encode();
240 let result = registry.call(&calldata, admin).unwrap();
241 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
242 assert!(is_authorized);
243
244 let modify_call = ITIP403Registry::modifyPolicyBlacklistCall {
246 policyId: policy_id,
247 account: blocked_account,
248 restricted: true,
249 };
250 let calldata = modify_call.abi_encode();
251 registry.call(&calldata, admin).unwrap();
252
253 let is_auth_call = ITIP403Registry::isAuthorizedCall {
255 policyId: policy_id,
256 user: blocked_account,
257 };
258 let calldata = is_auth_call.abi_encode();
259 let result = registry.call(&calldata, admin).unwrap();
260 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
261 assert!(!is_authorized);
262
263 let is_auth_call = ITIP403Registry::isAuthorizedCall {
265 policyId: policy_id,
266 user: allowed_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: false,
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 Ok(())
293 })
294 }
295
296 #[test]
297 fn test_modify_policy_whitelist() -> eyre::Result<()> {
298 let mut storage = HashMapStorageProvider::new(1);
299 let admin = Address::random();
300 let account1 = Address::random();
301 let account2 = Address::random();
302 StorageCtx::enter(&mut storage, || {
303 let mut registry = TIP403Registry::new();
304
305 let call = ITIP403Registry::createPolicyCall {
307 admin,
308 policyType: ITIP403Registry::PolicyType::WHITELIST,
309 };
310 let calldata = call.abi_encode();
311 let result = registry.call(&calldata, admin).unwrap();
312 let policy_id: u64 =
313 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
314
315 let modify_call1 = ITIP403Registry::modifyPolicyWhitelistCall {
317 policyId: policy_id,
318 account: account1,
319 allowed: true,
320 };
321 let calldata = modify_call1.abi_encode();
322 registry.call(&calldata, admin).unwrap();
323
324 let modify_call2 = ITIP403Registry::modifyPolicyWhitelistCall {
325 policyId: policy_id,
326 account: account2,
327 allowed: true,
328 };
329 let calldata = modify_call2.abi_encode();
330 registry.call(&calldata, admin).unwrap();
331
332 let is_auth_call = ITIP403Registry::isAuthorizedCall {
334 policyId: policy_id,
335 user: account1,
336 };
337 let calldata = is_auth_call.abi_encode();
338 let result = registry.call(&calldata, admin).unwrap();
339 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
340 assert!(is_authorized);
341
342 let is_auth_call = ITIP403Registry::isAuthorizedCall {
343 policyId: policy_id,
344 user: account2,
345 };
346 let calldata = is_auth_call.abi_encode();
347 let result = registry.call(&calldata, admin).unwrap();
348 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
349 assert!(is_authorized);
350
351 let modify_call = ITIP403Registry::modifyPolicyWhitelistCall {
353 policyId: policy_id,
354 account: account1,
355 allowed: false,
356 };
357 let calldata = modify_call.abi_encode();
358 registry.call(&calldata, admin).unwrap();
359
360 let is_auth_call = ITIP403Registry::isAuthorizedCall {
362 policyId: policy_id,
363 user: account1,
364 };
365 let calldata = is_auth_call.abi_encode();
366 let result = registry.call(&calldata, admin).unwrap();
367 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
368 assert!(!is_authorized);
369
370 let is_auth_call = ITIP403Registry::isAuthorizedCall {
371 policyId: policy_id,
372 user: account2,
373 };
374 let calldata = is_auth_call.abi_encode();
375 let result = registry.call(&calldata, admin).unwrap();
376 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
377 assert!(is_authorized);
378
379 Ok(())
380 })
381 }
382
383 #[test]
384 fn test_set_policy_admin() -> eyre::Result<()> {
385 let mut storage = HashMapStorageProvider::new(1);
386 let admin = Address::random();
387 let new_admin = Address::random();
388 StorageCtx::enter(&mut storage, || {
389 let mut registry = TIP403Registry::new();
390
391 let call = ITIP403Registry::createPolicyCall {
393 admin,
394 policyType: ITIP403Registry::PolicyType::WHITELIST,
395 };
396 let calldata = call.abi_encode();
397 let result = registry.call(&calldata, admin).unwrap();
398 let policy_id: u64 =
399 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
400
401 let policy_data_call = ITIP403Registry::policyDataCall {
403 policyId: policy_id,
404 };
405 let calldata = policy_data_call.abi_encode();
406 let result = registry.call(&calldata, admin).unwrap();
407 let policy_data =
408 ITIP403Registry::policyDataCall::abi_decode_returns(&result.bytes).unwrap();
409 assert_eq!(policy_data.admin, admin);
410
411 let set_admin_call = ITIP403Registry::setPolicyAdminCall {
413 policyId: policy_id,
414 admin: new_admin,
415 };
416 let calldata = set_admin_call.abi_encode();
417 registry.call(&calldata, admin).unwrap();
418
419 let policy_data_call = ITIP403Registry::policyDataCall {
421 policyId: policy_id,
422 };
423 let calldata = policy_data_call.abi_encode();
424 let result = registry.call(&calldata, admin).unwrap();
425 let policy_data =
426 ITIP403Registry::policyDataCall::abi_decode_returns(&result.bytes).unwrap();
427 assert_eq!(policy_data.admin, new_admin);
428
429 Ok(())
430 })
431 }
432
433 #[test]
434 fn test_special_policy_ids() -> eyre::Result<()> {
435 let mut storage = HashMapStorageProvider::new(1);
436 let user = Address::random();
437 StorageCtx::enter(&mut storage, || {
438 let mut registry = TIP403Registry::new();
439
440 let is_auth_call = ITIP403Registry::isAuthorizedCall { policyId: 0, user };
442 let calldata = is_auth_call.abi_encode();
443 let result = registry.call(&calldata, Address::ZERO).unwrap();
444 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
445 assert!(!is_authorized);
446
447 let is_auth_call = ITIP403Registry::isAuthorizedCall { policyId: 1, user };
449 let calldata = is_auth_call.abi_encode();
450 let result = registry.call(&calldata, Address::ZERO).unwrap();
451 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
452 assert!(is_authorized);
453
454 Ok(())
455 })
456 }
457
458 #[test]
459 fn test_invalid_selector() -> eyre::Result<()> {
460 let sender = Address::random();
461
462 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
464 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
465 let mut registry = TIP403Registry::new();
466
467 let invalid_data = vec![0x12, 0x34, 0x56, 0x78];
468 let result = registry.call(&invalid_data, sender)?;
469 assert!(result.is_revert());
470
471 let short_data = vec![0x12, 0x34];
473 let result = registry.call(&short_data, sender)?;
474 assert!(result.is_revert());
475
476 Ok(())
477 })?;
478
479 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
481 StorageCtx::enter(&mut storage, || {
482 let mut registry = TIP403Registry::new();
483
484 let short_data = vec![0x12, 0x34];
485 let result = registry.call(&short_data, sender);
486 let output = result.expect("expected Ok(halt) for short calldata");
487 assert!(output.is_halt());
488
489 Ok(())
490 })
491 }
492
493 #[test]
494 fn test_create_multiple_policies() -> eyre::Result<()> {
495 let mut storage = HashMapStorageProvider::new(1);
496 let admin = Address::random();
497 StorageCtx::enter(&mut storage, || {
498 let mut registry = TIP403Registry::new();
499
500 let whitelist_call = ITIP403Registry::createPolicyCall {
502 admin,
503 policyType: ITIP403Registry::PolicyType::WHITELIST,
504 };
505 let calldata = whitelist_call.abi_encode();
506 let result = registry.call(&calldata, admin).unwrap();
507 let whitelist_id: u64 =
508 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
509
510 let blacklist_call = ITIP403Registry::createPolicyCall {
511 admin,
512 policyType: ITIP403Registry::PolicyType::BLACKLIST,
513 };
514 let calldata = blacklist_call.abi_encode();
515 let result = registry.call(&calldata, admin).unwrap();
516 let blacklist_id: u64 =
517 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
518
519 assert_eq!(whitelist_id, 2);
521 assert_eq!(blacklist_id, 3);
522
523 let counter_call = ITIP403Registry::policyIdCounterCall {};
525 let calldata = counter_call.abi_encode();
526 let result = registry.call(&calldata, admin).unwrap();
527 let counter = u64::abi_decode(&result.bytes).unwrap();
528 assert_eq!(counter, 4);
529
530 Ok(())
531 })
532 }
533
534 #[test]
535 fn test_selector_coverage() -> eyre::Result<()> {
536 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
538 StorageCtx::enter(&mut storage, || {
539 let mut registry = TIP403Registry::new();
540
541 let unsupported = check_selector_coverage(
542 &mut registry,
543 ITIP403RegistryCalls::SELECTORS,
544 "ITIP403Registry",
545 ITIP403RegistryCalls::name_by_selector,
546 );
547
548 assert_full_coverage([unsupported]);
549
550 Ok(())
551 })
552 }
553}