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 const fn new(result: R, state: S) -> Self {
41 Self { result, state }
42 }
43}
44
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub struct ResultGas {
74 #[cfg_attr(feature = "serde", serde(rename = "gas_spent"))]
77 total_gas_spent: u64,
78 #[cfg_attr(feature = "serde", serde(default))]
83 state_gas_spent: u64,
84 #[cfg_attr(feature = "serde", serde(rename = "gas_refunded"))]
89 refunded: u64,
90 floor_gas: u64,
92}
93
94impl ResultGas {
95 #[inline]
99 #[deprecated(
100 since = "32.0.0",
101 note = "It can be a footgun as gas limit is removed, use ResultGas::with_* functions instead"
102 )]
103 pub const fn new(total_gas_spent: u64, refunded: u64, floor_gas: u64) -> Self {
104 Self {
105 total_gas_spent,
106 refunded,
107 floor_gas,
108 state_gas_spent: 0,
109 }
110 }
111
112 #[inline]
114 pub const fn new_with_state_gas(
115 total_gas_spent: u64,
116 refunded: u64,
117 floor_gas: u64,
118 state_gas_spent: u64,
119 ) -> Self {
120 Self {
121 total_gas_spent,
122 refunded,
123 floor_gas,
124 state_gas_spent,
125 }
126 }
127
128 #[inline]
134 pub const fn total_gas_spent(&self) -> u64 {
135 self.total_gas_spent
136 }
137
138 #[inline]
145 pub const fn state_gas_spent_final(&self) -> u64 {
146 self.state_gas_spent
147 }
148
149 #[inline]
151 pub const fn floor_gas(&self) -> u64 {
152 self.floor_gas
153 }
154
155 #[inline]
160 pub const fn inner_refunded(&self) -> u64 {
161 self.refunded
162 }
163
164 #[inline]
166 #[deprecated(
167 since = "32.0.0",
168 note = "After EIP-8037 gas is split on
169 regular and state gas, this method is no longer valid.
170 Use [`ResultGas::total_gas_spent`] instead"
171 )]
172 pub const fn spent(&self) -> u64 {
173 self.total_gas_spent()
174 }
175
176 #[inline]
180 pub const fn set_total_gas_spent(&mut self, total_gas_spent: u64) {
181 self.total_gas_spent = total_gas_spent;
182 }
183
184 #[inline]
186 pub const fn set_refunded(&mut self, refunded: u64) {
187 self.refunded = refunded;
188 }
189
190 #[inline]
192 pub const fn set_floor_gas(&mut self, floor_gas: u64) {
193 self.floor_gas = floor_gas;
194 }
195
196 #[inline]
198 pub const fn set_state_gas_spent(&mut self, state_gas_spent: u64) {
199 self.state_gas_spent = state_gas_spent;
200 }
201
202 #[inline]
204 #[deprecated(
205 since = "32.0.0",
206 note = "After EIP-8037 gas is split on
207 regular and state gas, this method is no longer valid.
208 Use [`ResultGas::set_total_gas_spent`] instead"
209 )]
210 pub const fn set_spent(&mut self, spent: u64) {
211 self.total_gas_spent = spent;
212 }
213
214 #[inline]
218 pub const fn with_total_gas_spent(mut self, total_gas_spent: u64) -> Self {
219 self.total_gas_spent = total_gas_spent;
220 self
221 }
222
223 #[inline]
225 pub const fn with_refunded(mut self, refunded: u64) -> Self {
226 self.refunded = refunded;
227 self
228 }
229
230 #[inline]
232 pub const fn with_floor_gas(mut self, floor_gas: u64) -> Self {
233 self.floor_gas = floor_gas;
234 self
235 }
236
237 #[inline]
239 pub const fn with_state_gas_spent(mut self, state_gas_spent: u64) -> Self {
240 self.state_gas_spent = state_gas_spent;
241 self
242 }
243
244 #[inline]
246 #[deprecated(
247 since = "32.0.0",
248 note = "After EIP-8037 gas is split on
249 regular and state gas, this method is no longer valid.
250 Use [`ResultGas::with_total_gas_spent`] instead"
251 )]
252 pub const fn with_spent(mut self, spent: u64) -> Self {
253 self.total_gas_spent = spent;
254 self
255 }
256
257 #[inline]
263 pub const fn tx_gas_used(&self) -> u64 {
264 let total_gas_spent = self.total_gas_spent();
266 let tx_gas_refunded = total_gas_spent.saturating_sub(self.inner_refunded());
268 max(tx_gas_refunded, self.floor_gas())
269 }
270
271 #[inline]
277 pub const fn block_regular_gas_used(&self) -> u64 {
278 self.total_gas_spent()
279 .saturating_sub(self.state_gas_spent_final())
280 }
281
282 #[inline]
286 pub const fn block_state_gas_used(&self) -> u64 {
287 self.state_gas_spent_final()
288 }
289
290 #[inline]
295 #[deprecated(
296 since = "32.0.0",
297 note = "Used is not descriptive enough, use [`ResultGas::tx_gas_used`] instead"
298 )]
299 pub const fn used(&self) -> u64 {
300 let spent_sub_refunded = self.spent_sub_refunded();
303 if spent_sub_refunded < self.floor_gas {
304 return self.floor_gas;
305 }
306 spent_sub_refunded
307 }
308
309 #[inline]
314 pub const fn spent_sub_refunded(&self) -> u64 {
315 self.total_gas_spent().saturating_sub(self.refunded)
316 }
317
318 #[inline]
323 pub const fn final_refunded(&self) -> u64 {
324 if self.spent_sub_refunded() < self.floor_gas {
325 0
326 } else {
327 self.refunded
328 }
329 }
330}
331
332#[inline(always)]
334pub const fn max(a: u64, b: u64) -> u64 {
335 if a > b {
336 a
337 } else {
338 b
339 }
340}
341
342#[inline(always)]
344pub const fn min(a: u64, b: u64) -> u64 {
345 if a < b {
346 a
347 } else {
348 b
349 }
350}
351
352impl fmt::Display for ResultGas {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 write!(
355 f,
356 "Gas used: {}, total spent: {}",
357 self.tx_gas_used(),
358 self.total_gas_spent()
359 )?;
360 if self.refunded > 0 {
361 write!(f, ", refunded: {}", self.refunded)?;
362 }
363 if self.floor_gas > 0 {
364 write!(f, ", floor: {}", self.floor_gas)?;
365 }
366 if self.state_gas_spent > 0 {
367 write!(f, ", state_gas: {}", self.state_gas_spent)?;
368 }
369 Ok(())
370 }
371}
372
373#[derive(Clone, Debug, PartialEq, Eq, Hash)]
375#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
376pub enum ExecutionResult<HaltReasonTy = HaltReason> {
377 Success {
379 reason: SuccessReason,
381 gas: ResultGas,
383 logs: Vec<Log>,
385 output: Output,
387 },
388 Revert {
390 gas: ResultGas,
392 logs: Vec<Log>,
394 output: Bytes,
396 },
397 Halt {
399 reason: HaltReasonTy,
401 gas: ResultGas,
406 logs: Vec<Log>,
408 },
409}
410
411impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
412 pub const fn is_success(&self) -> bool {
418 matches!(self, Self::Success { .. })
419 }
420
421 pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
423 where
424 F: FnOnce(HaltReasonTy) -> OHR,
425 {
426 match self {
427 Self::Success {
428 reason,
429 gas,
430 logs,
431 output,
432 } => ExecutionResult::Success {
433 reason,
434 gas,
435 logs,
436 output,
437 },
438 Self::Revert { gas, logs, output } => ExecutionResult::Revert { gas, logs, output },
439 Self::Halt { reason, gas, logs } => ExecutionResult::Halt {
440 reason: op(reason),
441 gas,
442 logs,
443 },
444 }
445 }
446
447 pub fn created_address(&self) -> Option<Address> {
450 match self {
451 Self::Success { output, .. } => output.address().cloned(),
452 _ => None,
453 }
454 }
455
456 pub const fn is_halt(&self) -> bool {
458 matches!(self, Self::Halt { .. })
459 }
460
461 pub const fn output(&self) -> Option<&Bytes> {
465 match self {
466 Self::Success { output, .. } => Some(output.data()),
467 Self::Revert { output, .. } => Some(output),
468 _ => None,
469 }
470 }
471
472 pub fn into_output(self) -> Option<Bytes> {
476 match self {
477 Self::Success { output, .. } => Some(output.into_data()),
478 Self::Revert { output, .. } => Some(output),
479 _ => None,
480 }
481 }
482
483 pub const fn logs(&self) -> &[Log] {
485 match self {
486 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
487 logs.as_slice()
488 }
489 }
490 }
491
492 pub fn into_logs(self) -> Vec<Log> {
494 match self {
495 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
496 logs
497 }
498 }
499 }
500
501 pub const fn gas(&self) -> &ResultGas {
503 match self {
504 Self::Success { gas, .. } | Self::Revert { gas, .. } | Self::Halt { gas, .. } => gas,
505 }
506 }
507
508 pub const fn tx_gas_used(&self) -> u64 {
510 self.gas().tx_gas_used()
511 }
512
513 #[inline]
515 #[deprecated(
516 since = "32.0.0",
517 note = "Use `tx_gas_used()` instead, `gas_used` is ambiguous after EIP-8037 state gas split"
518 )]
519 pub const fn gas_used(&self) -> u64 {
520 self.tx_gas_used()
521 }
522}
523
524impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 match self {
527 Self::Success {
528 reason,
529 gas,
530 logs,
531 output,
532 } => {
533 write!(f, "Success ({reason}): {gas}")?;
534 if !logs.is_empty() {
535 write!(
536 f,
537 ", {} log{}",
538 logs.len(),
539 if logs.len() == 1 { "" } else { "s" }
540 )?;
541 }
542 write!(f, ", {output}")
543 }
544 Self::Revert { gas, logs, output } => {
545 write!(f, "Revert: {gas}")?;
546 if !logs.is_empty() {
547 write!(
548 f,
549 ", {} log{}",
550 logs.len(),
551 if logs.len() == 1 { "" } else { "s" }
552 )?;
553 }
554 if !output.is_empty() {
555 write!(f, ", {} bytes output", output.len())?;
556 }
557 Ok(())
558 }
559 Self::Halt { reason, gas, logs } => {
560 write!(f, "Halted: {reason} ({gas})")?;
561 if !logs.is_empty() {
562 write!(
563 f,
564 ", {} log{}",
565 logs.len(),
566 if logs.len() == 1 { "" } else { "s" }
567 )?;
568 }
569 Ok(())
570 }
571 }
572 }
573}
574
575#[derive(Debug, Clone, PartialEq, Eq, Hash)]
577#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
578pub enum Output {
579 Call(Bytes),
581 Create(Bytes, Option<Address>),
583}
584
585impl Output {
586 pub fn into_data(self) -> Bytes {
588 match self {
589 Output::Call(data) => data,
590 Output::Create(data, _) => data,
591 }
592 }
593
594 pub const fn data(&self) -> &Bytes {
596 match self {
597 Output::Call(data) => data,
598 Output::Create(data, _) => data,
599 }
600 }
601
602 pub const fn address(&self) -> Option<&Address> {
604 match self {
605 Output::Call(_) => None,
606 Output::Create(_, address) => address.as_ref(),
607 }
608 }
609}
610
611impl fmt::Display for Output {
612 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
613 match self {
614 Output::Call(data) => {
615 if data.is_empty() {
616 write!(f, "no output")
617 } else {
618 write!(f, "{} bytes output", data.len())
619 }
620 }
621 Output::Create(data, Some(addr)) => {
622 if data.is_empty() {
623 write!(f, "contract created at {}", addr)
624 } else {
625 write!(f, "contract created at {} ({} bytes)", addr, data.len())
626 }
627 }
628 Output::Create(data, None) => {
629 if data.is_empty() {
630 write!(f, "contract creation (no address)")
631 } else {
632 write!(f, "contract creation (no address, {} bytes)", data.len())
633 }
634 }
635 }
636 }
637}
638
639#[derive(Debug, Clone)]
641pub struct AnyError(Arc<dyn core::error::Error + Send + Sync>);
642impl AnyError {
643 pub fn new(err: impl core::error::Error + Send + Sync + 'static) -> Self {
645 Self(Arc::new(err))
646 }
647}
648
649impl PartialEq for AnyError {
650 fn eq(&self, other: &Self) -> bool {
651 Arc::ptr_eq(&self.0, &other.0)
652 }
653}
654impl Eq for AnyError {}
655impl core::hash::Hash for AnyError {
656 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
657 (Arc::as_ptr(&self.0) as *const ()).hash(state);
658 }
659}
660impl fmt::Display for AnyError {
661 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662 fmt::Display::fmt(&self.0, f)
663 }
664}
665impl core::error::Error for AnyError {
666 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
667 self.0.source()
668 }
669}
670
671#[cfg(feature = "serde")]
672impl serde::Serialize for AnyError {
673 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
674 serializer.collect_str(self)
675 }
676}
677
678#[derive(Debug)]
679struct StringError(String);
680impl fmt::Display for StringError {
681 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682 f.write_str(&self.0)
683 }
684}
685impl core::error::Error for StringError {}
686
687impl From<String> for AnyError {
688 fn from(value: String) -> Self {
689 Self::new(StringError(value))
690 }
691}
692impl From<&'static str> for AnyError {
693 fn from(s: &'static str) -> Self {
694 Self::new(StringError(s.into()))
695 }
696}
697
698#[cfg(feature = "serde")]
699impl<'de> serde::Deserialize<'de> for AnyError {
700 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
701 let s = String::deserialize(deserializer)?;
702 Ok(s.into())
703 }
704}
705
706#[derive(Debug, Clone, PartialEq, Eq)]
708#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
709pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
710 Transaction(TransactionError),
712 Header(InvalidHeader),
714 Database(DBError),
716 Custom(String),
721 CustomAny(AnyError),
726}
727
728impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
729 for EVMError<DBError, TransactionValidationErrorT>
730{
731 fn from(value: ContextError<DBError>) -> Self {
732 match value {
733 ContextError::Db(e) => Self::Database(e),
734 ContextError::Custom(e) => Self::Custom(e),
735 }
736 }
737}
738
739impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
740 fn from(value: DBError) -> Self {
741 Self::Database(value)
742 }
743}
744
745pub trait FromStringError {
747 fn from_string(value: String) -> Self;
749}
750
751impl<DB, TX> FromStringError for EVMError<DB, TX> {
752 fn from_string(value: String) -> Self {
753 Self::Custom(value)
754 }
755}
756
757impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
758 fn from(value: InvalidTransaction) -> Self {
759 Self::Transaction(TXE::from(value))
760 }
761}
762
763impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
764 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
766 where
767 F: FnOnce(DBError) -> E,
768 {
769 match self {
770 Self::Transaction(e) => EVMError::Transaction(e),
771 Self::Header(e) => EVMError::Header(e),
772 Self::Database(e) => EVMError::Database(op(e)),
773 Self::Custom(e) => EVMError::Custom(e),
774 Self::CustomAny(e) => EVMError::CustomAny(e),
775 }
776 }
777}
778
779impl<DBError, TransactionValidationErrorT> core::error::Error
780 for EVMError<DBError, TransactionValidationErrorT>
781where
782 DBError: core::error::Error + 'static,
783 TransactionValidationErrorT: core::error::Error + 'static,
784{
785 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
786 match self {
787 Self::Transaction(e) => Some(e),
788 Self::Header(e) => Some(e),
789 Self::Database(e) => Some(e),
790 Self::Custom(_) => None,
791 Self::CustomAny(e) => Some(e.0.as_ref()),
792 }
793 }
794}
795
796impl<DBError, TransactionValidationErrorT> fmt::Display
797 for EVMError<DBError, TransactionValidationErrorT>
798where
799 DBError: fmt::Display,
800 TransactionValidationErrorT: fmt::Display,
801{
802 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
803 match self {
804 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
805 Self::Header(e) => write!(f, "header validation error: {e}"),
806 Self::Database(e) => write!(f, "database error: {e}"),
807 Self::Custom(e) => f.write_str(e),
808 Self::CustomAny(e) => write!(f, "{e}"),
809 }
810 }
811}
812
813impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
814 for EVMError<DBError, TransactionValidationErrorT>
815{
816 fn from(value: InvalidHeader) -> Self {
817 Self::Header(value)
818 }
819}
820
821#[derive(Debug, Clone, PartialEq, Eq, Hash)]
823#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
824pub enum InvalidTransaction {
825 PriorityFeeGreaterThanMaxFee,
831 GasPriceLessThanBasefee,
833 CallerGasLimitMoreThanBlock,
835 CallGasCostMoreThanGasLimit {
841 initial_gas: u64,
843 gas_limit: u64,
845 },
846 GasFloorMoreThanGasLimit {
851 gas_floor: u64,
853 gas_limit: u64,
855 },
856 RejectCallerWithCode,
858 LackOfFundForMaxFee {
860 fee: Box<U256>,
862 balance: Box<U256>,
864 },
865 OverflowPaymentInTransaction,
867 NonceOverflowInTransaction,
869 NonceTooHigh {
871 tx: u64,
873 state: u64,
875 },
876 NonceTooLow {
878 tx: u64,
880 state: u64,
882 },
883 CreateInitCodeSizeLimit,
885 InvalidChainId,
887 MissingChainId,
889 TxGasLimitGreaterThanCap {
891 gas_limit: u64,
893 cap: u64,
895 },
896 AccessListNotSupported,
898 MaxFeePerBlobGasNotSupported,
900 BlobVersionedHashesNotSupported,
902 BlobGasPriceGreaterThanMax {
904 block_blob_gas_price: u128,
906 tx_max_fee_per_blob_gas: u128,
908 },
909 EmptyBlobs,
911 BlobCreateTransaction,
915 TooManyBlobs {
917 max: usize,
919 have: usize,
921 },
922 BlobVersionNotSupported,
924 AuthorizationListNotSupported,
926 AuthorizationListInvalidFields,
928 EmptyAuthorizationList,
930 Eip2930NotSupported,
932 Eip1559NotSupported,
934 Eip4844NotSupported,
936 Eip7702NotSupported,
938 Eip7873NotSupported,
940 Eip7873MissingTarget,
942 Str(Cow<'static, str>),
944}
945
946impl TransactionError for InvalidTransaction {}
947
948impl core::error::Error for InvalidTransaction {}
949
950impl fmt::Display for InvalidTransaction {
951 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
952 match self {
953 Self::PriorityFeeGreaterThanMaxFee => {
954 write!(f, "priority fee is greater than max fee")
955 }
956 Self::GasPriceLessThanBasefee => {
957 write!(f, "gas price is less than basefee")
958 }
959 Self::CallerGasLimitMoreThanBlock => {
960 write!(f, "caller gas limit exceeds the block gas limit")
961 }
962 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
963 write!(
964 f,
965 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
966 )
967 }
968 Self::CallGasCostMoreThanGasLimit {
969 initial_gas,
970 gas_limit,
971 } => {
972 write!(
973 f,
974 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
975 )
976 }
977 Self::GasFloorMoreThanGasLimit {
978 gas_floor,
979 gas_limit,
980 } => {
981 write!(
982 f,
983 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
984 )
985 }
986 Self::RejectCallerWithCode => {
987 write!(f, "reject transactions from senders with deployed code")
988 }
989 Self::LackOfFundForMaxFee { fee, balance } => {
990 write!(f, "lack of funds ({balance}) for max fee ({fee})")
991 }
992 Self::OverflowPaymentInTransaction => {
993 write!(f, "overflow payment in transaction")
994 }
995 Self::NonceOverflowInTransaction => {
996 write!(f, "nonce overflow in transaction")
997 }
998 Self::NonceTooHigh { tx, state } => {
999 write!(f, "nonce {tx} too high, expected {state}")
1000 }
1001 Self::NonceTooLow { tx, state } => {
1002 write!(f, "nonce {tx} too low, expected {state}")
1003 }
1004 Self::CreateInitCodeSizeLimit => {
1005 write!(f, "create initcode size limit")
1006 }
1007 Self::InvalidChainId => write!(f, "invalid chain ID"),
1008 Self::MissingChainId => write!(f, "missing chain ID"),
1009 Self::AccessListNotSupported => write!(f, "access list not supported"),
1010 Self::MaxFeePerBlobGasNotSupported => {
1011 write!(f, "max fee per blob gas not supported")
1012 }
1013 Self::BlobVersionedHashesNotSupported => {
1014 write!(f, "blob versioned hashes not supported")
1015 }
1016 Self::BlobGasPriceGreaterThanMax {
1017 block_blob_gas_price,
1018 tx_max_fee_per_blob_gas,
1019 } => {
1020 write!(
1021 f,
1022 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
1023 )
1024 }
1025 Self::EmptyBlobs => write!(f, "empty blobs"),
1026 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
1027 Self::TooManyBlobs { max, have } => {
1028 write!(f, "too many blobs, have {have}, max {max}")
1029 }
1030 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
1031 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
1032 Self::AuthorizationListInvalidFields => {
1033 write!(f, "authorization list tx has invalid fields")
1034 }
1035 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
1036 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
1037 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
1038 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
1039 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
1040 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
1041 Self::Eip7873MissingTarget => {
1042 write!(f, "Eip7873 initcode transaction should have `to` address")
1043 }
1044 Self::Str(msg) => f.write_str(msg),
1045 }
1046 }
1047}
1048
1049#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1051#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1052pub enum InvalidHeader {
1053 PrevrandaoNotSet,
1055 ExcessBlobGasNotSet,
1057}
1058
1059impl core::error::Error for InvalidHeader {}
1060
1061impl fmt::Display for InvalidHeader {
1062 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1063 match self {
1064 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
1065 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
1066 }
1067 }
1068}
1069
1070#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1072#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1073pub enum SuccessReason {
1074 Stop,
1076 Return,
1078 SelfDestruct,
1080}
1081
1082impl fmt::Display for SuccessReason {
1083 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1084 match self {
1085 Self::Stop => write!(f, "Stop"),
1086 Self::Return => write!(f, "Return"),
1087 Self::SelfDestruct => write!(f, "SelfDestruct"),
1088 }
1089 }
1090}
1091
1092#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1096#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1097pub enum HaltReason {
1098 OutOfGas(OutOfGasError),
1100 OpcodeNotFound,
1102 InvalidFEOpcode,
1104 InvalidJump,
1106 NotActivated,
1108 StackUnderflow,
1110 StackOverflow,
1112 OutOfOffset,
1114 CreateCollision,
1116 PrecompileError,
1118 PrecompileErrorWithContext(String),
1120 NonceOverflow,
1122 CreateContractSizeLimit,
1124 CreateContractStartingWithEF,
1126 CreateInitCodeSizeLimit,
1128
1129 OverflowPayment,
1132 StateChangeDuringStaticCall,
1134 CallNotAllowedInsideStatic,
1136 OutOfFunds,
1138 CallTooDeep,
1140}
1141
1142impl core::error::Error for HaltReason {}
1143
1144impl fmt::Display for HaltReason {
1145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1146 match self {
1147 Self::OutOfGas(err) => write!(f, "{err}"),
1148 Self::OpcodeNotFound => write!(f, "opcode not found"),
1149 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
1150 Self::InvalidJump => write!(f, "invalid jump destination"),
1151 Self::NotActivated => write!(f, "feature or opcode not activated"),
1152 Self::StackUnderflow => write!(f, "stack underflow"),
1153 Self::StackOverflow => write!(f, "stack overflow"),
1154 Self::OutOfOffset => write!(f, "out of offset"),
1155 Self::CreateCollision => write!(f, "create collision"),
1156 Self::PrecompileError => write!(f, "precompile error"),
1157 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
1158 Self::NonceOverflow => write!(f, "nonce overflow"),
1159 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
1160 Self::CreateContractStartingWithEF => {
1161 write!(f, "create contract starting with 0xEF")
1162 }
1163 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
1164 Self::OverflowPayment => write!(f, "overflow payment"),
1165 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
1166 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
1167 Self::OutOfFunds => write!(f, "out of funds"),
1168 Self::CallTooDeep => write!(f, "call too deep"),
1169 }
1170 }
1171}
1172
1173#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1176pub enum OutOfGasError {
1177 Basic,
1179 MemoryLimit,
1181 Memory,
1183 Precompile,
1185 InvalidOperand,
1188 ReentrancySentry,
1190}
1191
1192impl core::error::Error for OutOfGasError {}
1193
1194impl fmt::Display for OutOfGasError {
1195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1196 match self {
1197 Self::Basic => write!(f, "out of gas"),
1198 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1199 Self::Memory => write!(f, "out of gas: memory expansion"),
1200 Self::Precompile => write!(f, "out of gas: precompile"),
1201 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1202 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1203 }
1204 }
1205}
1206
1207#[derive(Debug, Clone, PartialEq, Eq)]
1209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1210pub struct TransactionIndexedError<Error> {
1211 pub error: Error,
1213 pub transaction_index: usize,
1215}
1216
1217impl<Error> TransactionIndexedError<Error> {
1218 #[must_use]
1220 pub const fn new(error: Error, transaction_index: usize) -> Self {
1221 Self {
1222 error,
1223 transaction_index,
1224 }
1225 }
1226
1227 pub const fn error(&self) -> &Error {
1229 &self.error
1230 }
1231
1232 #[must_use]
1234 pub fn into_error(self) -> Error {
1235 self.error
1236 }
1237}
1238
1239impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1241 write!(
1242 f,
1243 "transaction {} failed: {}",
1244 self.transaction_index, self.error
1245 )
1246 }
1247}
1248
1249impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1250 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1251 Some(&self.error)
1252 }
1253}
1254
1255impl From<&'static str> for InvalidTransaction {
1256 fn from(s: &'static str) -> Self {
1257 Self::Str(Cow::Borrowed(s))
1258 }
1259}
1260
1261impl From<String> for InvalidTransaction {
1262 fn from(s: String) -> Self {
1263 Self::Str(Cow::Owned(s))
1264 }
1265}
1266
1267#[cfg(test)]
1268mod tests {
1269 use super::*;
1270
1271 #[test]
1272 fn test_execution_result_display() {
1273 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1274 reason: SuccessReason::Return,
1275 gas: ResultGas::default()
1276 .with_total_gas_spent(100000)
1277 .with_refunded(26000)
1278 .with_floor_gas(5000),
1279 logs: vec![Log::default(), Log::default()],
1280 output: Output::Call(Bytes::from(vec![1, 2, 3])),
1281 };
1282 assert_eq!(
1283 result.to_string(),
1284 "Success (Return): Gas used: 74000, total spent: 100000, refunded: 26000, floor: 5000, 2 logs, 3 bytes output"
1285 );
1286
1287 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1288 gas: ResultGas::default()
1289 .with_total_gas_spent(100000)
1290 .with_refunded(100000),
1291 logs: vec![],
1292 output: Bytes::from(vec![1, 2, 3, 4]),
1293 };
1294 assert_eq!(
1295 result.to_string(),
1296 "Revert: Gas used: 0, total spent: 100000, refunded: 100000, 4 bytes output"
1297 );
1298
1299 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1300 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1301 gas: ResultGas::default()
1302 .with_total_gas_spent(1000000)
1303 .with_refunded(1000000),
1304 logs: vec![],
1305 };
1306 assert_eq!(
1307 result.to_string(),
1308 "Halted: out of gas (Gas used: 0, total spent: 1000000, refunded: 1000000)"
1309 );
1310 }
1311
1312 #[test]
1313 fn test_result_gas_display() {
1314 assert_eq!(
1316 ResultGas::default().with_total_gas_spent(21000).to_string(),
1317 "Gas used: 21000, total spent: 21000"
1318 );
1319 assert_eq!(
1321 ResultGas::default()
1322 .with_total_gas_spent(50000)
1323 .with_refunded(10000)
1324 .to_string(),
1325 "Gas used: 40000, total spent: 50000, refunded: 10000"
1326 );
1327 assert_eq!(
1329 ResultGas::default()
1330 .with_total_gas_spent(50000)
1331 .with_refunded(10000)
1332 .with_floor_gas(30000)
1333 .to_string(),
1334 "Gas used: 40000, total spent: 50000, refunded: 10000, floor: 30000"
1335 );
1336 }
1337
1338 #[test]
1339 fn test_result_gas_used_and_remaining() {
1340 let gas = ResultGas::default()
1341 .with_total_gas_spent(100)
1342 .with_refunded(30);
1343 assert_eq!(gas.total_gas_spent(), 100);
1344 assert_eq!(gas.inner_refunded(), 30);
1345 assert_eq!(gas.spent_sub_refunded(), 70);
1346
1347 let gas = ResultGas::default()
1349 .with_total_gas_spent(10)
1350 .with_refunded(50);
1351 assert_eq!(gas.spent_sub_refunded(), 0);
1352 }
1353
1354 #[test]
1355 fn test_final_refunded_with_floor_gas() {
1356 let gas = ResultGas::default()
1358 .with_total_gas_spent(50000)
1359 .with_refunded(10000);
1360 assert_eq!(gas.tx_gas_used(), 40000);
1361 assert_eq!(gas.final_refunded(), 10000);
1362
1363 let gas = ResultGas::default()
1366 .with_total_gas_spent(50000)
1367 .with_refunded(10000)
1368 .with_floor_gas(45000);
1369 assert_eq!(gas.tx_gas_used(), 45000);
1370 assert_eq!(gas.final_refunded(), 0);
1371
1372 let gas = ResultGas::default()
1375 .with_total_gas_spent(50000)
1376 .with_refunded(10000)
1377 .with_floor_gas(30000);
1378 assert_eq!(gas.tx_gas_used(), 40000);
1379 assert_eq!(gas.final_refunded(), 10000);
1380
1381 let gas = ResultGas::default()
1384 .with_total_gas_spent(50000)
1385 .with_refunded(10000)
1386 .with_floor_gas(40000);
1387 assert_eq!(gas.tx_gas_used(), 40000);
1388 assert_eq!(gas.final_refunded(), 10000);
1389 }
1390
1391 #[test]
1392 fn test_block_regular_gas_used_no_floor_no_refund() {
1393 let gas = ResultGas::default().with_total_gas_spent(100_000);
1398 assert_eq!(gas.block_regular_gas_used(), 100_000);
1399
1400 let gas = ResultGas::default()
1402 .with_total_gas_spent(100_000)
1403 .with_state_gas_spent(30_000);
1404 assert_eq!(gas.block_regular_gas_used(), 70_000);
1405 assert_eq!(gas.block_state_gas_used(), 30_000);
1406
1407 let gas = ResultGas::default()
1409 .with_total_gas_spent(100_000)
1410 .with_refunded(10_000)
1411 .with_state_gas_spent(30_000);
1412 assert_eq!(gas.tx_gas_used(), 90_000);
1413 assert_eq!(gas.block_regular_gas_used(), 70_000);
1414
1415 let gas = ResultGas::default()
1417 .with_total_gas_spent(100_000)
1418 .with_refunded(90_000)
1419 .with_floor_gas(50_000)
1420 .with_state_gas_spent(30_000);
1421 assert_eq!(gas.tx_gas_used(), 50_000);
1422 assert_eq!(gas.block_regular_gas_used(), 70_000);
1423
1424 let gas = ResultGas::default()
1426 .with_total_gas_spent(20_000)
1427 .with_state_gas_spent(30_000);
1428 assert_eq!(gas.block_regular_gas_used(), 0);
1429 }
1430}