1use std::{
2 collections::HashMap,
3 sync::{Arc, LazyLock},
4};
5
6use crate::tip20::TIP20Error;
7use alloy::{
8 primitives::{Selector, U256},
9 sol_types::{Panic, PanicKind, SolError, SolInterface},
10};
11use revm::precompile::{PrecompileError, PrecompileOutput, PrecompileResult};
12use tempo_contracts::precompiles::{
13 AccountKeychainError, FeeManagerError, NonceError, RolesAuthError, StablecoinExchangeError,
14 TIP20RewardsRegistryError, TIP403RegistryError, TIPAccountRegistrarError, TIPFeeAMMError,
15 UnknownFunctionSelector, ValidatorConfigError,
16};
17
18#[derive(
21 Debug, Clone, PartialEq, Eq, thiserror::Error, derive_more::From, derive_more::TryInto,
22)]
23pub enum TempoPrecompileError {
24 #[error("Stablecoin exchange error: {0:?}")]
26 StablecoinExchange(StablecoinExchangeError),
27
28 #[error("TIP20 token error: {0:?}")]
30 TIP20(TIP20Error),
31
32 #[error("TIP20 rewards registry error: {0:?}")]
34 TIP20RewardsRegistry(TIP20RewardsRegistryError),
35
36 #[error("Roles auth error: {0:?}")]
38 RolesAuthError(RolesAuthError),
39
40 #[error("TIP403 registry error: {0:?}")]
42 TIP403RegistryError(TIP403RegistryError),
43
44 #[error("TIP fee manager error: {0:?}")]
46 FeeManagerError(FeeManagerError),
47
48 #[error("TIP fee AMM error: {0:?}")]
50 TIPFeeAMMError(TIPFeeAMMError),
51
52 #[error("TIP account registrar error: {0:?}")]
54 TIPAccountRegistrarError(TIPAccountRegistrarError),
55
56 #[error("Tempo Transaction nonce error: {0:?}")]
58 NonceError(NonceError),
59
60 #[error("Panic({0:?})")]
61 Panic(PanicKind),
62
63 #[error("Validator config error: {0:?}")]
65 ValidatorConfigError(ValidatorConfigError),
66
67 #[error("Account keychain error: {0:?}")]
69 AccountKeychainError(AccountKeychainError),
70
71 #[error("Gas limit exceeded")]
72 OutOfGas,
73
74 #[error("Unknown function selector: {0:?}")]
75 UnknownFunctionSelector([u8; 4]),
76
77 #[error("Fatal precompile error: {0:?}")]
78 #[from(skip)]
79 Fatal(String),
80}
81
82pub type Result<T> = std::result::Result<T, TempoPrecompileError>;
84
85impl TempoPrecompileError {
86 pub fn under_overflow() -> Self {
87 Self::Panic(PanicKind::UnderOverflow)
88 }
89}
90
91pub fn add_errors_to_registry<T: SolInterface>(
92 registry: &mut TempoPrecompileErrorRegistry,
93 converter: impl Fn(T) -> TempoPrecompileError + 'static + Send + Sync,
94) {
95 let converter = Arc::new(converter);
96 for selector in T::selectors() {
97 let converter = Arc::clone(&converter);
98 registry.insert(
99 selector.into(),
100 Box::new(move |data: &[u8]| {
101 T::abi_decode(data)
102 .ok()
103 .map(|error| DecodedTempoPrecompileError {
104 error: converter(error),
105 revert_bytes: data,
106 })
107 }),
108 );
109 }
110}
111
112pub struct DecodedTempoPrecompileError<'a> {
113 pub error: TempoPrecompileError,
114 pub revert_bytes: &'a [u8],
115}
116
117pub type TempoPrecompileErrorRegistry = HashMap<
118 Selector,
119 Box<dyn for<'a> Fn(&'a [u8]) -> Option<DecodedTempoPrecompileError<'a>> + Send + Sync>,
120>;
121
122pub fn error_decoder_registry() -> TempoPrecompileErrorRegistry {
125 let mut registry: TempoPrecompileErrorRegistry = HashMap::new();
126
127 add_errors_to_registry(&mut registry, TempoPrecompileError::StablecoinExchange);
128 add_errors_to_registry(&mut registry, TempoPrecompileError::TIP20);
129 add_errors_to_registry(&mut registry, TempoPrecompileError::TIP20RewardsRegistry);
130 add_errors_to_registry(&mut registry, TempoPrecompileError::RolesAuthError);
131 add_errors_to_registry(&mut registry, TempoPrecompileError::TIP403RegistryError);
132 add_errors_to_registry(&mut registry, TempoPrecompileError::FeeManagerError);
133 add_errors_to_registry(&mut registry, TempoPrecompileError::TIPFeeAMMError);
134 add_errors_to_registry(
135 &mut registry,
136 TempoPrecompileError::TIPAccountRegistrarError,
137 );
138 add_errors_to_registry(&mut registry, TempoPrecompileError::NonceError);
139 add_errors_to_registry(&mut registry, TempoPrecompileError::ValidatorConfigError);
140 add_errors_to_registry(&mut registry, TempoPrecompileError::AccountKeychainError);
141
142 registry
143}
144
145pub static ERROR_REGISTRY: LazyLock<TempoPrecompileErrorRegistry> =
146 LazyLock::new(error_decoder_registry);
147
148pub fn decode_error<'a>(data: &'a [u8]) -> Option<DecodedTempoPrecompileError<'a>> {
150 if data.len() < 4 {
151 return None;
152 }
153
154 let selector: [u8; 4] = data[0..4].try_into().ok()?;
155 ERROR_REGISTRY
156 .get(&selector)
157 .and_then(|decoder| decoder(data))
158}
159
160pub trait IntoPrecompileResult<T> {
162 fn into_precompile_result(
163 self,
164 gas: u64,
165 encode_ok: impl FnOnce(T) -> alloy::primitives::Bytes,
166 ) -> PrecompileResult;
167}
168
169impl<T> IntoPrecompileResult<T> for Result<T> {
170 fn into_precompile_result(
171 self,
172 gas: u64,
173 encode_ok: impl FnOnce(T) -> alloy::primitives::Bytes,
174 ) -> PrecompileResult {
175 use TempoPrecompileError as TPErr;
176
177 match self {
178 Ok(res) => Ok(PrecompileOutput::new(gas, encode_ok(res))),
179 Err(err) => {
180 let bytes = match err {
181 TPErr::StablecoinExchange(e) => e.abi_encode().into(),
182 TPErr::TIP20(e) => e.abi_encode().into(),
183 TPErr::TIP20RewardsRegistry(e) => e.abi_encode().into(),
184 TPErr::RolesAuthError(e) => e.abi_encode().into(),
185 TPErr::TIP403RegistryError(e) => e.abi_encode().into(),
186 TPErr::TIPAccountRegistrarError(e) => e.abi_encode().into(),
187 TPErr::FeeManagerError(e) => e.abi_encode().into(),
188 TPErr::TIPFeeAMMError(e) => e.abi_encode().into(),
189 TPErr::NonceError(e) => e.abi_encode().into(),
190 TPErr::Panic(kind) => {
191 let panic = Panic {
192 code: U256::from(kind as u32),
193 };
194
195 panic.abi_encode().into()
196 }
197 TPErr::ValidatorConfigError(e) => e.abi_encode().into(),
198 TPErr::AccountKeychainError(e) => e.abi_encode().into(),
199 TPErr::OutOfGas => {
200 return Err(PrecompileError::OutOfGas);
201 }
202 TPErr::UnknownFunctionSelector(selector) => UnknownFunctionSelector {
203 selector: selector.into(),
204 }
205 .abi_encode()
206 .into(),
207 TPErr::Fatal(msg) => {
208 return Err(PrecompileError::Fatal(msg));
209 }
210 };
211 Ok(PrecompileOutput::new_reverted(gas, bytes))
212 }
213 }
214 }
215}
216
217impl<T> IntoPrecompileResult<T> for TempoPrecompileError {
218 fn into_precompile_result(
219 self,
220 gas: u64,
221 _encode_ok: impl FnOnce(T) -> alloy::primitives::Bytes,
222 ) -> PrecompileResult {
223 let bytes = match self {
224 Self::StablecoinExchange(e) => e.abi_encode().into(),
225 Self::TIP20(e) => e.abi_encode().into(),
226 Self::TIP20RewardsRegistry(e) => e.abi_encode().into(),
227 Self::RolesAuthError(e) => e.abi_encode().into(),
228 Self::TIP403RegistryError(e) => e.abi_encode().into(),
229 Self::TIPAccountRegistrarError(e) => e.abi_encode().into(),
230 Self::FeeManagerError(e) => e.abi_encode().into(),
231 Self::TIPFeeAMMError(e) => e.abi_encode().into(),
232 Self::NonceError(e) => e.abi_encode().into(),
233 Self::AccountKeychainError(e) => e.abi_encode().into(),
234 Self::Panic(kind) => {
235 let panic = Panic {
236 code: U256::from(kind as u32),
237 };
238
239 panic.abi_encode().into()
240 }
241 Self::ValidatorConfigError(e) => e.abi_encode().into(),
242 Self::OutOfGas => {
243 return Err(PrecompileError::OutOfGas);
244 }
245 Self::UnknownFunctionSelector(selector) => UnknownFunctionSelector {
246 selector: selector.into(),
247 }
248 .abi_encode()
249 .into(),
250 Self::Fatal(msg) => {
251 return Err(PrecompileError::Fatal(msg));
252 }
253 };
254 Ok(PrecompileOutput::new_reverted(gas, bytes))
255 }
256}