1use alloy_evm::error::InvalidTxError;
4use alloy_primitives::{Address, U256};
5use revm::context::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
12pub enum TempoInvalidTransaction {
13 #[error(transparent)]
15 EthInvalidTransaction(#[from] InvalidTransaction),
16
17 #[error("system transaction must be a call, not a create")]
19 SystemTransactionMustBeCall,
20
21 #[error("system transaction execution failed, result: {_0:?}")]
23 SystemTransactionFailed(ExecutionResult<TempoHaltReason>),
24
25 #[error("fee payer signature recovery failed")]
30 InvalidFeePayerSignature,
31
32 #[error(
37 "transaction not valid yet: current block timestamp {current} < validAfter {valid_after}"
38 )]
39 ValidAfter {
40 current: u64,
42 valid_after: u64,
44 },
45
46 #[error("transaction expired: current block timestamp {current} >= validBefore {valid_before}")]
50 ValidBefore {
51 current: u64,
53 valid_before: u64,
55 },
56
57 #[error("P256 signature verification failed")]
61 InvalidP256Signature,
62
63 #[error("WebAuthn signature verification failed: {reason}")]
67 InvalidWebAuthnSignature {
68 reason: String,
70 },
71
72 #[error(
77 "insufficient gas for intrinsic cost: gas_limit {gas_limit} < intrinsic_gas {intrinsic_gas}"
78 )]
79 InsufficientGasForIntrinsicCost {
80 gas_limit: u64,
82 intrinsic_gas: u64,
84 },
85
86 #[error("nonce manager error: {0}")]
88 NonceManagerError(String),
89
90 #[error("subblock transaction must have zero fee")]
92 SubblockTransactionMustHaveZeroFee,
93
94 #[error("invalid fee token: {0}")]
96 InvalidFeeToken(Address),
97
98 #[error("value transfer not allowed")]
100 ValueTransferNotAllowed,
101
102 #[error("value transfer in Tempo Transaction not allowed")]
104 ValueTransferNotAllowedInAATx,
105
106 #[error("access key authorization failed: {reason}")]
111 AccessKeyAuthorizationFailed {
112 reason: String,
114 },
115
116 #[error("keychain operations are only supported after Allegretto")]
118 KeychainOpBeforeAllegretto,
119
120 #[error("KeyAuthorization chain_id mismatch: expected {expected}, got {got}")]
122 KeyAuthorizationChainIdMismatch {
123 expected: u64,
125 got: u64,
127 },
128
129 #[error("keychain operations are not supported in subblock transactions")]
131 KeychainOpInSubblockTransaction,
132
133 #[error(transparent)]
135 CollectFeePreTx(#[from] FeePaymentError),
136}
137
138impl InvalidTxError for TempoInvalidTransaction {
139 fn is_nonce_too_low(&self) -> bool {
140 match self {
141 Self::EthInvalidTransaction(err) => err.is_nonce_too_low(),
142 _ => false,
143 }
144 }
145
146 fn as_invalid_tx_err(&self) -> Option<&InvalidTransaction> {
147 match self {
148 Self::EthInvalidTransaction(err) => Some(err),
149 _ => None,
150 }
151 }
152}
153
154impl<DBError> From<TempoInvalidTransaction> for EVMError<DBError, TempoInvalidTransaction> {
155 fn from(err: TempoInvalidTransaction) -> Self {
156 Self::Transaction(err)
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
162pub enum FeePaymentError {
163 #[error("insufficient liquidity in FeeAMM pool to swap fee tokens (required: {fee})")]
168 InsufficientAmmLiquidity {
169 fee: U256,
171 },
172
173 #[error("insufficient fee token balance: required {fee}, but only have {balance}")]
178 InsufficientFeeTokenBalance {
179 fee: U256,
181 balance: U256,
183 },
184
185 #[error("{0}")]
187 Other(String),
188}
189
190impl<DBError> From<FeePaymentError> for EVMError<DBError, TempoInvalidTransaction> {
191 fn from(err: FeePaymentError) -> Self {
192 TempoInvalidTransaction::from(err).into()
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From)]
200pub enum TempoHaltReason {
201 #[from]
203 Ethereum(HaltReason),
204 SubblockTxFeePayment,
206}
207
208#[cfg(feature = "rpc")]
209impl reth_rpc_eth_types::error::api::FromEvmHalt<TempoHaltReason>
210 for reth_rpc_eth_types::EthApiError
211{
212 fn from_evm_halt(halt_reason: TempoHaltReason, gas_limit: u64) -> Self {
213 match halt_reason {
214 TempoHaltReason::Ethereum(halt_reason) => Self::from_evm_halt(halt_reason, gas_limit),
215 TempoHaltReason::SubblockTxFeePayment => {
216 Self::EvmCustom("subblock transaction failed to pay fees".to_string())
217 }
218 }
219 }
220}
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_error_display() {
227 let err = TempoInvalidTransaction::SystemTransactionMustBeCall;
228 assert_eq!(
229 err.to_string(),
230 "system transaction must be a call, not a create"
231 );
232
233 let err = FeePaymentError::InsufficientAmmLiquidity {
234 fee: U256::from(1000),
235 };
236 assert!(
237 err.to_string()
238 .contains("insufficient liquidity in FeeAMM pool")
239 );
240
241 let err = FeePaymentError::InsufficientFeeTokenBalance {
242 fee: U256::from(1000),
243 balance: U256::from(500),
244 };
245 assert!(err.to_string().contains("insufficient fee token balance"));
246 }
247
248 #[test]
249 fn test_from_invalid_transaction() {
250 let eth_err = InvalidTransaction::PriorityFeeGreaterThanMaxFee;
251 let tempo_err: TempoInvalidTransaction = eth_err.into();
252 assert!(matches!(
253 tempo_err,
254 TempoInvalidTransaction::EthInvalidTransaction(_)
255 ));
256 }
257}