1use crate::{context::ContextError, transaction::TransactionError};
11use core::fmt::{self, Debug};
12use database_interface::DBErrorMarker;
13use primitives::{Address, Bytes, Log, U256};
14use state::EvmState;
15use std::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec};
16
17pub trait HaltReasonTr: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
19
20impl<T> HaltReasonTr for T where T: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
21
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct ExecResultAndState<R, S = EvmState> {
26 pub result: R,
28 pub state: S,
30}
31
32pub type ResultAndState<H = HaltReason, S = EvmState> = ExecResultAndState<ExecutionResult<H>, S>;
34
35pub type ResultVecAndState<R, S> = ExecResultAndState<Vec<R>, S>;
37
38impl<R, S> ExecResultAndState<R, S> {
39 pub fn new(result: R, state: S) -> Self {
41 Self { result, state }
42 }
43}
44
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct ResultGas {
73 #[cfg_attr(feature = "serde", serde(rename = "gas_spent"))]
76 total_gas_spent: u64,
77 #[cfg_attr(feature = "serde", serde(default))]
81 state_gas_spent: u64,
82 #[cfg_attr(feature = "serde", serde(rename = "gas_refunded"))]
87 refunded: u64,
88 floor_gas: u64,
90}
91
92impl ResultGas {
93 #[inline]
97 #[deprecated(
98 since = "32.0.0",
99 note = "It can be a footgun as gas limit is removed, use ResultGas::with_* functions instead"
100 )]
101 pub const fn new(total_gas_spent: u64, refunded: u64, floor_gas: u64) -> Self {
102 Self {
103 total_gas_spent,
104 refunded,
105 floor_gas,
106 state_gas_spent: 0,
107 }
108 }
109
110 #[inline]
112 pub const fn new_with_state_gas(
113 total_gas_spent: u64,
114 refunded: u64,
115 floor_gas: u64,
116 state_gas_spent: u64,
117 ) -> Self {
118 Self {
119 total_gas_spent,
120 refunded,
121 floor_gas,
122 state_gas_spent,
123 }
124 }
125
126 #[inline]
132 pub const fn total_gas_spent(&self) -> u64 {
133 self.total_gas_spent
134 }
135
136 #[inline]
140 pub const fn state_gas_spent(&self) -> u64 {
141 self.state_gas_spent
142 }
143
144 #[inline]
146 pub const fn floor_gas(&self) -> u64 {
147 self.floor_gas
148 }
149
150 #[inline]
155 pub const fn inner_refunded(&self) -> u64 {
156 self.refunded
157 }
158
159 #[inline]
161 #[deprecated(
162 since = "32.0.0",
163 note = "After EIP-8037 gas is split on
164 regular and state gas, this method is no longer valid.
165 Use [`ResultGas::total_gas_spent`] instead"
166 )]
167 pub const fn spent(&self) -> u64 {
168 self.total_gas_spent()
169 }
170
171 #[inline]
175 pub fn set_total_gas_spent(&mut self, total_gas_spent: u64) {
176 self.total_gas_spent = total_gas_spent;
177 }
178
179 #[inline]
181 pub fn set_refunded(&mut self, refunded: u64) {
182 self.refunded = refunded;
183 }
184
185 #[inline]
187 pub fn set_floor_gas(&mut self, floor_gas: u64) {
188 self.floor_gas = floor_gas;
189 }
190
191 #[inline]
193 pub fn set_state_gas_spent(&mut self, state_gas_spent: u64) {
194 self.state_gas_spent = state_gas_spent;
195 }
196
197 #[inline]
199 #[deprecated(
200 since = "32.0.0",
201 note = "After EIP-8037 gas is split on
202 regular and state gas, this method is no longer valid.
203 Use [`ResultGas::set_total_gas_spent`] instead"
204 )]
205 pub fn set_spent(&mut self, spent: u64) {
206 self.total_gas_spent = spent;
207 }
208
209 #[inline]
213 pub const fn with_total_gas_spent(mut self, total_gas_spent: u64) -> Self {
214 self.total_gas_spent = total_gas_spent;
215 self
216 }
217
218 #[inline]
220 pub const fn with_refunded(mut self, refunded: u64) -> Self {
221 self.refunded = refunded;
222 self
223 }
224
225 #[inline]
227 pub const fn with_floor_gas(mut self, floor_gas: u64) -> Self {
228 self.floor_gas = floor_gas;
229 self
230 }
231
232 #[inline]
234 pub const fn with_state_gas_spent(mut self, state_gas_spent: u64) -> Self {
235 self.state_gas_spent = state_gas_spent;
236 self
237 }
238
239 #[inline]
241 #[deprecated(
242 since = "32.0.0",
243 note = "After EIP-8037 gas is split on
244 regular and state gas, this method is no longer valid.
245 Use [`ResultGas::with_total_gas_spent`] instead"
246 )]
247 pub const fn with_spent(mut self, spent: u64) -> Self {
248 self.total_gas_spent = spent;
249 self
250 }
251
252 #[inline]
258 pub const fn tx_gas_used(&self) -> u64 {
259 let total_gas_spent = self.total_gas_spent();
261 let tx_gas_refunded = total_gas_spent.saturating_sub(self.inner_refunded());
263 max(tx_gas_refunded, self.floor_gas())
264 }
265
266 #[inline]
268 pub const fn block_regular_gas_used(&self) -> u64 {
269 let execution_gas_spent = self
270 .total_gas_spent()
271 .saturating_sub(self.state_gas_spent());
272 max(execution_gas_spent, self.floor_gas())
273 }
274
275 #[inline]
279 pub const fn block_state_gas_used(&self) -> u64 {
280 self.state_gas_spent()
281 }
282
283 #[inline]
288 #[deprecated(
289 since = "32.0.0",
290 note = "Used is not descriptive enough, use [`ResultGas::tx_gas_used`] instead"
291 )]
292 pub const fn used(&self) -> u64 {
293 let spent_sub_refunded = self.spent_sub_refunded();
296 if spent_sub_refunded < self.floor_gas {
297 return self.floor_gas;
298 }
299 spent_sub_refunded
300 }
301
302 #[inline]
307 pub const fn spent_sub_refunded(&self) -> u64 {
308 self.total_gas_spent().saturating_sub(self.refunded)
309 }
310
311 #[inline]
316 pub const fn final_refunded(&self) -> u64 {
317 if self.spent_sub_refunded() < self.floor_gas {
318 0
319 } else {
320 self.refunded
321 }
322 }
323}
324
325#[inline(always)]
327pub const fn max(a: u64, b: u64) -> u64 {
328 if a > b {
329 a
330 } else {
331 b
332 }
333}
334
335#[inline(always)]
337pub const fn min(a: u64, b: u64) -> u64 {
338 if a < b {
339 a
340 } else {
341 b
342 }
343}
344
345impl fmt::Display for ResultGas {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 write!(
348 f,
349 "Gas used: {}, total spent: {}",
350 self.tx_gas_used(),
351 self.total_gas_spent()
352 )?;
353 if self.refunded > 0 {
354 write!(f, ", refunded: {}", self.refunded)?;
355 }
356 if self.floor_gas > 0 {
357 write!(f, ", floor: {}", self.floor_gas)?;
358 }
359 if self.state_gas_spent > 0 {
360 write!(f, ", state_gas: {}", self.state_gas_spent)?;
361 }
362 Ok(())
363 }
364}
365
366#[derive(Clone, Debug, PartialEq, Eq, Hash)]
368#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
369pub enum ExecutionResult<HaltReasonTy = HaltReason> {
370 Success {
372 reason: SuccessReason,
374 gas: ResultGas,
376 logs: Vec<Log>,
378 output: Output,
380 },
381 Revert {
383 gas: ResultGas,
385 logs: Vec<Log>,
387 output: Bytes,
389 },
390 Halt {
392 reason: HaltReasonTy,
394 gas: ResultGas,
399 logs: Vec<Log>,
401 },
402}
403
404impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
405 pub fn is_success(&self) -> bool {
411 matches!(self, Self::Success { .. })
412 }
413
414 pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
416 where
417 F: FnOnce(HaltReasonTy) -> OHR,
418 {
419 match self {
420 Self::Success {
421 reason,
422 gas,
423 logs,
424 output,
425 } => ExecutionResult::Success {
426 reason,
427 gas,
428 logs,
429 output,
430 },
431 Self::Revert { gas, logs, output } => ExecutionResult::Revert { gas, logs, output },
432 Self::Halt { reason, gas, logs } => ExecutionResult::Halt {
433 reason: op(reason),
434 gas,
435 logs,
436 },
437 }
438 }
439
440 pub fn created_address(&self) -> Option<Address> {
443 match self {
444 Self::Success { output, .. } => output.address().cloned(),
445 _ => None,
446 }
447 }
448
449 pub fn is_halt(&self) -> bool {
451 matches!(self, Self::Halt { .. })
452 }
453
454 pub fn output(&self) -> Option<&Bytes> {
458 match self {
459 Self::Success { output, .. } => Some(output.data()),
460 Self::Revert { output, .. } => Some(output),
461 _ => None,
462 }
463 }
464
465 pub fn into_output(self) -> Option<Bytes> {
469 match self {
470 Self::Success { output, .. } => Some(output.into_data()),
471 Self::Revert { output, .. } => Some(output),
472 _ => None,
473 }
474 }
475
476 pub fn logs(&self) -> &[Log] {
478 match self {
479 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
480 logs.as_slice()
481 }
482 }
483 }
484
485 pub fn into_logs(self) -> Vec<Log> {
487 match self {
488 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
489 logs
490 }
491 }
492 }
493
494 pub fn gas(&self) -> &ResultGas {
496 match self {
497 Self::Success { gas, .. } | Self::Revert { gas, .. } | Self::Halt { gas, .. } => gas,
498 }
499 }
500
501 pub fn tx_gas_used(&self) -> u64 {
503 self.gas().tx_gas_used()
504 }
505
506 #[inline]
508 #[deprecated(
509 since = "32.0.0",
510 note = "Use `tx_gas_used()` instead, `gas_used` is ambiguous after EIP-8037 state gas split"
511 )]
512 pub fn gas_used(&self) -> u64 {
513 self.tx_gas_used()
514 }
515}
516
517impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 match self {
520 Self::Success {
521 reason,
522 gas,
523 logs,
524 output,
525 } => {
526 write!(f, "Success ({reason}): {gas}")?;
527 if !logs.is_empty() {
528 write!(
529 f,
530 ", {} log{}",
531 logs.len(),
532 if logs.len() == 1 { "" } else { "s" }
533 )?;
534 }
535 write!(f, ", {output}")
536 }
537 Self::Revert { gas, logs, output } => {
538 write!(f, "Revert: {gas}")?;
539 if !logs.is_empty() {
540 write!(
541 f,
542 ", {} log{}",
543 logs.len(),
544 if logs.len() == 1 { "" } else { "s" }
545 )?;
546 }
547 if !output.is_empty() {
548 write!(f, ", {} bytes output", output.len())?;
549 }
550 Ok(())
551 }
552 Self::Halt { reason, gas, logs } => {
553 write!(f, "Halted: {reason} ({gas})")?;
554 if !logs.is_empty() {
555 write!(
556 f,
557 ", {} log{}",
558 logs.len(),
559 if logs.len() == 1 { "" } else { "s" }
560 )?;
561 }
562 Ok(())
563 }
564 }
565 }
566}
567
568#[derive(Debug, Clone, PartialEq, Eq, Hash)]
570#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
571pub enum Output {
572 Call(Bytes),
574 Create(Bytes, Option<Address>),
576}
577
578impl Output {
579 pub fn into_data(self) -> Bytes {
581 match self {
582 Output::Call(data) => data,
583 Output::Create(data, _) => data,
584 }
585 }
586
587 pub fn data(&self) -> &Bytes {
589 match self {
590 Output::Call(data) => data,
591 Output::Create(data, _) => data,
592 }
593 }
594
595 pub fn address(&self) -> Option<&Address> {
597 match self {
598 Output::Call(_) => None,
599 Output::Create(_, address) => address.as_ref(),
600 }
601 }
602}
603
604impl fmt::Display for Output {
605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606 match self {
607 Output::Call(data) => {
608 if data.is_empty() {
609 write!(f, "no output")
610 } else {
611 write!(f, "{} bytes output", data.len())
612 }
613 }
614 Output::Create(data, Some(addr)) => {
615 if data.is_empty() {
616 write!(f, "contract created at {}", addr)
617 } else {
618 write!(f, "contract created at {} ({} bytes)", addr, data.len())
619 }
620 }
621 Output::Create(data, None) => {
622 if data.is_empty() {
623 write!(f, "contract creation (no address)")
624 } else {
625 write!(f, "contract creation (no address, {} bytes)", data.len())
626 }
627 }
628 }
629 }
630}
631
632#[derive(Debug, Clone)]
634pub struct AnyError(Arc<dyn core::error::Error + Send + Sync>);
635impl AnyError {
636 pub fn new(err: impl core::error::Error + Send + Sync + 'static) -> Self {
638 Self(Arc::new(err))
639 }
640}
641
642impl PartialEq for AnyError {
643 fn eq(&self, other: &Self) -> bool {
644 Arc::ptr_eq(&self.0, &other.0)
645 }
646}
647impl Eq for AnyError {}
648impl core::hash::Hash for AnyError {
649 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
650 (Arc::as_ptr(&self.0) as *const ()).hash(state);
651 }
652}
653impl fmt::Display for AnyError {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 fmt::Display::fmt(&self.0, f)
656 }
657}
658impl core::error::Error for AnyError {
659 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
660 self.0.source()
661 }
662}
663
664#[cfg(feature = "serde")]
665impl serde::Serialize for AnyError {
666 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
667 serializer.collect_str(self)
668 }
669}
670
671#[derive(Debug)]
672struct StringError(String);
673impl fmt::Display for StringError {
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675 f.write_str(&self.0)
676 }
677}
678impl core::error::Error for StringError {}
679
680impl From<String> for AnyError {
681 fn from(value: String) -> Self {
682 Self::new(StringError(value))
683 }
684}
685impl From<&'static str> for AnyError {
686 fn from(s: &'static str) -> Self {
687 Self::new(StringError(s.into()))
688 }
689}
690
691#[cfg(feature = "serde")]
692impl<'de> serde::Deserialize<'de> for AnyError {
693 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
694 let s = String::deserialize(deserializer)?;
695 Ok(s.into())
696 }
697}
698
699#[derive(Debug, Clone, PartialEq, Eq)]
701#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
702pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
703 Transaction(TransactionError),
705 Header(InvalidHeader),
707 Database(DBError),
709 Custom(String),
714 CustomAny(AnyError),
719}
720
721impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
722 for EVMError<DBError, TransactionValidationErrorT>
723{
724 fn from(value: ContextError<DBError>) -> Self {
725 match value {
726 ContextError::Db(e) => Self::Database(e),
727 ContextError::Custom(e) => Self::Custom(e),
728 }
729 }
730}
731
732impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
733 fn from(value: DBError) -> Self {
734 Self::Database(value)
735 }
736}
737
738pub trait FromStringError {
740 fn from_string(value: String) -> Self;
742}
743
744impl<DB, TX> FromStringError for EVMError<DB, TX> {
745 fn from_string(value: String) -> Self {
746 Self::Custom(value)
747 }
748}
749
750impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
751 fn from(value: InvalidTransaction) -> Self {
752 Self::Transaction(TXE::from(value))
753 }
754}
755
756impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
757 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
759 where
760 F: FnOnce(DBError) -> E,
761 {
762 match self {
763 Self::Transaction(e) => EVMError::Transaction(e),
764 Self::Header(e) => EVMError::Header(e),
765 Self::Database(e) => EVMError::Database(op(e)),
766 Self::Custom(e) => EVMError::Custom(e),
767 Self::CustomAny(e) => EVMError::CustomAny(e),
768 }
769 }
770}
771
772impl<DBError, TransactionValidationErrorT> core::error::Error
773 for EVMError<DBError, TransactionValidationErrorT>
774where
775 DBError: core::error::Error + 'static,
776 TransactionValidationErrorT: core::error::Error + 'static,
777{
778 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
779 match self {
780 Self::Transaction(e) => Some(e),
781 Self::Header(e) => Some(e),
782 Self::Database(e) => Some(e),
783 Self::Custom(_) => None,
784 Self::CustomAny(e) => Some(e.0.as_ref()),
785 }
786 }
787}
788
789impl<DBError, TransactionValidationErrorT> fmt::Display
790 for EVMError<DBError, TransactionValidationErrorT>
791where
792 DBError: fmt::Display,
793 TransactionValidationErrorT: fmt::Display,
794{
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 match self {
797 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
798 Self::Header(e) => write!(f, "header validation error: {e}"),
799 Self::Database(e) => write!(f, "database error: {e}"),
800 Self::Custom(e) => f.write_str(e),
801 Self::CustomAny(e) => write!(f, "{e}"),
802 }
803 }
804}
805
806impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
807 for EVMError<DBError, TransactionValidationErrorT>
808{
809 fn from(value: InvalidHeader) -> Self {
810 Self::Header(value)
811 }
812}
813
814#[derive(Debug, Clone, PartialEq, Eq, Hash)]
816#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
817pub enum InvalidTransaction {
818 PriorityFeeGreaterThanMaxFee,
824 GasPriceLessThanBasefee,
826 CallerGasLimitMoreThanBlock,
828 CallGasCostMoreThanGasLimit {
834 initial_gas: u64,
836 gas_limit: u64,
838 },
839 GasFloorMoreThanGasLimit {
844 gas_floor: u64,
846 gas_limit: u64,
848 },
849 RejectCallerWithCode,
851 LackOfFundForMaxFee {
853 fee: Box<U256>,
855 balance: Box<U256>,
857 },
858 OverflowPaymentInTransaction,
860 NonceOverflowInTransaction,
862 NonceTooHigh {
864 tx: u64,
866 state: u64,
868 },
869 NonceTooLow {
871 tx: u64,
873 state: u64,
875 },
876 CreateInitCodeSizeLimit,
878 InvalidChainId,
880 MissingChainId,
882 TxGasLimitGreaterThanCap {
884 gas_limit: u64,
886 cap: u64,
888 },
889 AccessListNotSupported,
891 MaxFeePerBlobGasNotSupported,
893 BlobVersionedHashesNotSupported,
895 BlobGasPriceGreaterThanMax {
897 block_blob_gas_price: u128,
899 tx_max_fee_per_blob_gas: u128,
901 },
902 EmptyBlobs,
904 BlobCreateTransaction,
908 TooManyBlobs {
910 max: usize,
912 have: usize,
914 },
915 BlobVersionNotSupported,
917 AuthorizationListNotSupported,
919 AuthorizationListInvalidFields,
921 EmptyAuthorizationList,
923 Eip2930NotSupported,
925 Eip1559NotSupported,
927 Eip4844NotSupported,
929 Eip7702NotSupported,
931 Eip7873NotSupported,
933 Eip7873MissingTarget,
935 Str(Cow<'static, str>),
937}
938
939impl TransactionError for InvalidTransaction {}
940
941impl core::error::Error for InvalidTransaction {}
942
943impl fmt::Display for InvalidTransaction {
944 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 match self {
946 Self::PriorityFeeGreaterThanMaxFee => {
947 write!(f, "priority fee is greater than max fee")
948 }
949 Self::GasPriceLessThanBasefee => {
950 write!(f, "gas price is less than basefee")
951 }
952 Self::CallerGasLimitMoreThanBlock => {
953 write!(f, "caller gas limit exceeds the block gas limit")
954 }
955 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
956 write!(
957 f,
958 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
959 )
960 }
961 Self::CallGasCostMoreThanGasLimit {
962 initial_gas,
963 gas_limit,
964 } => {
965 write!(
966 f,
967 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
968 )
969 }
970 Self::GasFloorMoreThanGasLimit {
971 gas_floor,
972 gas_limit,
973 } => {
974 write!(
975 f,
976 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
977 )
978 }
979 Self::RejectCallerWithCode => {
980 write!(f, "reject transactions from senders with deployed code")
981 }
982 Self::LackOfFundForMaxFee { fee, balance } => {
983 write!(f, "lack of funds ({balance}) for max fee ({fee})")
984 }
985 Self::OverflowPaymentInTransaction => {
986 write!(f, "overflow payment in transaction")
987 }
988 Self::NonceOverflowInTransaction => {
989 write!(f, "nonce overflow in transaction")
990 }
991 Self::NonceTooHigh { tx, state } => {
992 write!(f, "nonce {tx} too high, expected {state}")
993 }
994 Self::NonceTooLow { tx, state } => {
995 write!(f, "nonce {tx} too low, expected {state}")
996 }
997 Self::CreateInitCodeSizeLimit => {
998 write!(f, "create initcode size limit")
999 }
1000 Self::InvalidChainId => write!(f, "invalid chain ID"),
1001 Self::MissingChainId => write!(f, "missing chain ID"),
1002 Self::AccessListNotSupported => write!(f, "access list not supported"),
1003 Self::MaxFeePerBlobGasNotSupported => {
1004 write!(f, "max fee per blob gas not supported")
1005 }
1006 Self::BlobVersionedHashesNotSupported => {
1007 write!(f, "blob versioned hashes not supported")
1008 }
1009 Self::BlobGasPriceGreaterThanMax {
1010 block_blob_gas_price,
1011 tx_max_fee_per_blob_gas,
1012 } => {
1013 write!(
1014 f,
1015 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
1016 )
1017 }
1018 Self::EmptyBlobs => write!(f, "empty blobs"),
1019 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
1020 Self::TooManyBlobs { max, have } => {
1021 write!(f, "too many blobs, have {have}, max {max}")
1022 }
1023 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
1024 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
1025 Self::AuthorizationListInvalidFields => {
1026 write!(f, "authorization list tx has invalid fields")
1027 }
1028 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
1029 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
1030 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
1031 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
1032 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
1033 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
1034 Self::Eip7873MissingTarget => {
1035 write!(f, "Eip7873 initcode transaction should have `to` address")
1036 }
1037 Self::Str(msg) => f.write_str(msg),
1038 }
1039 }
1040}
1041
1042#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1044#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1045pub enum InvalidHeader {
1046 PrevrandaoNotSet,
1048 ExcessBlobGasNotSet,
1050}
1051
1052impl core::error::Error for InvalidHeader {}
1053
1054impl fmt::Display for InvalidHeader {
1055 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1056 match self {
1057 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
1058 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
1059 }
1060 }
1061}
1062
1063#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1065#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1066pub enum SuccessReason {
1067 Stop,
1069 Return,
1071 SelfDestruct,
1073}
1074
1075impl fmt::Display for SuccessReason {
1076 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1077 match self {
1078 Self::Stop => write!(f, "Stop"),
1079 Self::Return => write!(f, "Return"),
1080 Self::SelfDestruct => write!(f, "SelfDestruct"),
1081 }
1082 }
1083}
1084
1085#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1089#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1090pub enum HaltReason {
1091 OutOfGas(OutOfGasError),
1093 OpcodeNotFound,
1095 InvalidFEOpcode,
1097 InvalidJump,
1099 NotActivated,
1101 StackUnderflow,
1103 StackOverflow,
1105 OutOfOffset,
1107 CreateCollision,
1109 PrecompileError,
1111 PrecompileErrorWithContext(String),
1113 NonceOverflow,
1115 CreateContractSizeLimit,
1117 CreateContractStartingWithEF,
1119 CreateInitCodeSizeLimit,
1121
1122 OverflowPayment,
1125 StateChangeDuringStaticCall,
1127 CallNotAllowedInsideStatic,
1129 OutOfFunds,
1131 CallTooDeep,
1133}
1134
1135impl core::error::Error for HaltReason {}
1136
1137impl fmt::Display for HaltReason {
1138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1139 match self {
1140 Self::OutOfGas(err) => write!(f, "{err}"),
1141 Self::OpcodeNotFound => write!(f, "opcode not found"),
1142 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
1143 Self::InvalidJump => write!(f, "invalid jump destination"),
1144 Self::NotActivated => write!(f, "feature or opcode not activated"),
1145 Self::StackUnderflow => write!(f, "stack underflow"),
1146 Self::StackOverflow => write!(f, "stack overflow"),
1147 Self::OutOfOffset => write!(f, "out of offset"),
1148 Self::CreateCollision => write!(f, "create collision"),
1149 Self::PrecompileError => write!(f, "precompile error"),
1150 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
1151 Self::NonceOverflow => write!(f, "nonce overflow"),
1152 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
1153 Self::CreateContractStartingWithEF => {
1154 write!(f, "create contract starting with 0xEF")
1155 }
1156 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
1157 Self::OverflowPayment => write!(f, "overflow payment"),
1158 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
1159 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
1160 Self::OutOfFunds => write!(f, "out of funds"),
1161 Self::CallTooDeep => write!(f, "call too deep"),
1162 }
1163 }
1164}
1165
1166#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1169pub enum OutOfGasError {
1170 Basic,
1172 MemoryLimit,
1174 Memory,
1176 Precompile,
1178 InvalidOperand,
1181 ReentrancySentry,
1183}
1184
1185impl core::error::Error for OutOfGasError {}
1186
1187impl fmt::Display for OutOfGasError {
1188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1189 match self {
1190 Self::Basic => write!(f, "out of gas"),
1191 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1192 Self::Memory => write!(f, "out of gas: memory expansion"),
1193 Self::Precompile => write!(f, "out of gas: precompile"),
1194 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1195 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1196 }
1197 }
1198}
1199
1200#[derive(Debug, Clone, PartialEq, Eq)]
1202#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1203pub struct TransactionIndexedError<Error> {
1204 pub error: Error,
1206 pub transaction_index: usize,
1208}
1209
1210impl<Error> TransactionIndexedError<Error> {
1211 #[must_use]
1213 pub fn new(error: Error, transaction_index: usize) -> Self {
1214 Self {
1215 error,
1216 transaction_index,
1217 }
1218 }
1219
1220 pub fn error(&self) -> &Error {
1222 &self.error
1223 }
1224
1225 #[must_use]
1227 pub fn into_error(self) -> Error {
1228 self.error
1229 }
1230}
1231
1232impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1234 write!(
1235 f,
1236 "transaction {} failed: {}",
1237 self.transaction_index, self.error
1238 )
1239 }
1240}
1241
1242impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1243 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1244 Some(&self.error)
1245 }
1246}
1247
1248impl From<&'static str> for InvalidTransaction {
1249 fn from(s: &'static str) -> Self {
1250 Self::Str(Cow::Borrowed(s))
1251 }
1252}
1253
1254impl From<String> for InvalidTransaction {
1255 fn from(s: String) -> Self {
1256 Self::Str(Cow::Owned(s))
1257 }
1258}
1259
1260#[cfg(test)]
1261mod tests {
1262 use super::*;
1263
1264 #[test]
1265 fn test_execution_result_display() {
1266 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1267 reason: SuccessReason::Return,
1268 gas: ResultGas::default()
1269 .with_total_gas_spent(100000)
1270 .with_refunded(26000)
1271 .with_floor_gas(5000),
1272 logs: vec![Log::default(), Log::default()],
1273 output: Output::Call(Bytes::from(vec![1, 2, 3])),
1274 };
1275 assert_eq!(
1276 result.to_string(),
1277 "Success (Return): Gas used: 74000, total spent: 100000, refunded: 26000, floor: 5000, 2 logs, 3 bytes output"
1278 );
1279
1280 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1281 gas: ResultGas::default()
1282 .with_total_gas_spent(100000)
1283 .with_refunded(100000),
1284 logs: vec![],
1285 output: Bytes::from(vec![1, 2, 3, 4]),
1286 };
1287 assert_eq!(
1288 result.to_string(),
1289 "Revert: Gas used: 0, total spent: 100000, refunded: 100000, 4 bytes output"
1290 );
1291
1292 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1293 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1294 gas: ResultGas::default()
1295 .with_total_gas_spent(1000000)
1296 .with_refunded(1000000),
1297 logs: vec![],
1298 };
1299 assert_eq!(
1300 result.to_string(),
1301 "Halted: out of gas (Gas used: 0, total spent: 1000000, refunded: 1000000)"
1302 );
1303 }
1304
1305 #[test]
1306 fn test_result_gas_display() {
1307 assert_eq!(
1309 ResultGas::default().with_total_gas_spent(21000).to_string(),
1310 "Gas used: 21000, total spent: 21000"
1311 );
1312 assert_eq!(
1314 ResultGas::default()
1315 .with_total_gas_spent(50000)
1316 .with_refunded(10000)
1317 .to_string(),
1318 "Gas used: 40000, total spent: 50000, refunded: 10000"
1319 );
1320 assert_eq!(
1322 ResultGas::default()
1323 .with_total_gas_spent(50000)
1324 .with_refunded(10000)
1325 .with_floor_gas(30000)
1326 .to_string(),
1327 "Gas used: 40000, total spent: 50000, refunded: 10000, floor: 30000"
1328 );
1329 }
1330
1331 #[test]
1332 fn test_result_gas_used_and_remaining() {
1333 let gas = ResultGas::default()
1334 .with_total_gas_spent(100)
1335 .with_refunded(30);
1336 assert_eq!(gas.total_gas_spent(), 100);
1337 assert_eq!(gas.inner_refunded(), 30);
1338 assert_eq!(gas.spent_sub_refunded(), 70);
1339
1340 let gas = ResultGas::default()
1342 .with_total_gas_spent(10)
1343 .with_refunded(50);
1344 assert_eq!(gas.spent_sub_refunded(), 0);
1345 }
1346
1347 #[test]
1348 fn test_final_refunded_with_floor_gas() {
1349 let gas = ResultGas::default()
1351 .with_total_gas_spent(50000)
1352 .with_refunded(10000);
1353 assert_eq!(gas.tx_gas_used(), 40000);
1354 assert_eq!(gas.final_refunded(), 10000);
1355
1356 let gas = ResultGas::default()
1359 .with_total_gas_spent(50000)
1360 .with_refunded(10000)
1361 .with_floor_gas(45000);
1362 assert_eq!(gas.tx_gas_used(), 45000);
1363 assert_eq!(gas.final_refunded(), 0);
1364
1365 let gas = ResultGas::default()
1368 .with_total_gas_spent(50000)
1369 .with_refunded(10000)
1370 .with_floor_gas(30000);
1371 assert_eq!(gas.tx_gas_used(), 40000);
1372 assert_eq!(gas.final_refunded(), 10000);
1373
1374 let gas = ResultGas::default()
1377 .with_total_gas_spent(50000)
1378 .with_refunded(10000)
1379 .with_floor_gas(40000);
1380 assert_eq!(gas.tx_gas_used(), 40000);
1381 assert_eq!(gas.final_refunded(), 10000);
1382 }
1383}