tempo_precompiles/tip_account_registrar/
dispatch.rs

1use crate::{Precompile, fill_precompile_output, input_cost, mutate, unknown_selector};
2use alloy::{primitives::Address, sol_types::SolCall};
3use revm::precompile::{PrecompileError, PrecompileResult};
4
5use crate::tip_account_registrar::{ITipAccountRegistrar, TipAccountRegistrar};
6
7impl Precompile for TipAccountRegistrar {
8    fn call(&mut self, calldata: &[u8], msg_sender: Address) -> PrecompileResult {
9        self.storage
10            .deduct_gas(input_cost(calldata.len()))
11            .map_err(|_| PrecompileError::OutOfGas)?;
12
13        let selector: [u8; 4] = calldata
14            .get(..4)
15            .ok_or_else(|| {
16                PrecompileError::Other("Invalid input: missing function selector".into())
17            })?
18            .try_into()
19            .unwrap();
20
21        let result = match selector {
22            // Old signature: delegateToDefault(bytes32,bytes) - only pre-Moderato
23            ITipAccountRegistrar::delegateToDefault_0Call::SELECTOR => {
24                if self.storage.spec().is_moderato() {
25                    unknown_selector(selector, self.storage.gas_used(), self.storage.spec())
26                } else {
27                    mutate::<ITipAccountRegistrar::delegateToDefault_0Call>(
28                        calldata,
29                        msg_sender,
30                        |_, call| self.delegate_to_default_v1(call),
31                    )
32                }
33            }
34            // New signature: delegateToDefault(bytes,bytes) - only post-Moderato
35            ITipAccountRegistrar::delegateToDefault_1Call::SELECTOR => {
36                if self.storage.spec().is_moderato() {
37                    mutate::<ITipAccountRegistrar::delegateToDefault_1Call>(
38                        calldata,
39                        msg_sender,
40                        |_, call| self.delegate_to_default_v2(call),
41                    )
42                } else {
43                    unknown_selector(selector, self.storage.gas_used(), self.storage.spec())
44                }
45            }
46            _ => unknown_selector(selector, self.storage.gas_used(), self.storage.spec()),
47        };
48
49        result.map(|res| fill_precompile_output(res, &mut self.storage))
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::{
57        storage::{StorageCtx, hashmap::HashMapStorageProvider},
58        test_util::check_selector_coverage,
59    };
60    use tempo_chainspec::hardfork::TempoHardfork;
61    use tempo_contracts::precompiles::ITipAccountRegistrar::ITipAccountRegistrarCalls;
62
63    #[test]
64    fn test_selector_coverage_pre_moderato() -> eyre::Result<()> {
65        let mut storage = HashMapStorageProvider::new(1).with_spec(TempoHardfork::Adagio);
66        StorageCtx::enter(&mut storage, || {
67            // Pre-Moderato: v1 signature should be supported, v2 should be unsupported
68            let mut registrar = TipAccountRegistrar::new();
69
70            let unsupported_pre = check_selector_coverage(
71                &mut registrar,
72                ITipAccountRegistrarCalls::SELECTORS,
73                "ITipAccountRegistrar (pre-Moderato)",
74                ITipAccountRegistrarCalls::name_by_selector,
75            );
76
77            // Expect exactly one unsupported: delegateToDefault v2 (bytes,bytes)
78            assert_eq!(
79                unsupported_pre.len(),
80                1,
81                "Expected 1 unsupported selector pre-Moderato, got {}",
82                unsupported_pre.len()
83            );
84            assert_eq!(
85                unsupported_pre[0].0,
86                ITipAccountRegistrar::delegateToDefault_1Call::SELECTOR,
87                "Expected delegateToDefault v2 to be unsupported pre-Moderato"
88            );
89
90            Ok(())
91        })
92    }
93
94    #[test]
95    fn test_selector_coverage_post_moderato() -> eyre::Result<()> {
96        let mut storage = HashMapStorageProvider::new(1).with_spec(TempoHardfork::Moderato);
97        StorageCtx::enter(&mut storage, || {
98            // Post-Moderato: v2 signature should be supported, v1 should be unsupported
99            let mut registrar = TipAccountRegistrar::new();
100
101            let unsupported_post = check_selector_coverage(
102                &mut registrar,
103                ITipAccountRegistrarCalls::SELECTORS,
104                "ITipAccountRegistrar (post-Moderato)",
105                ITipAccountRegistrarCalls::name_by_selector,
106            );
107
108            // Expect exactly one unsupported: delegateToDefault v1 (bytes32,bytes)
109            assert_eq!(
110                unsupported_post.len(),
111                1,
112                "Expected 1 unsupported selector post-Moderato, got {}",
113                unsupported_post.len()
114            );
115            assert_eq!(
116                unsupported_post[0].0,
117                ITipAccountRegistrar::delegateToDefault_0Call::SELECTOR,
118                "Expected delegateToDefault v1 to be unsupported post-Moderato"
119            );
120
121            Ok(())
122        })
123    }
124}