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)]
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))]
82 state_gas_spent: u64,
83 #[cfg_attr(feature = "serde", serde(rename = "gas_refunded"))]
88 refunded: u64,
89 floor_gas: u64,
91}
92
93impl ResultGas {
94 #[inline]
98 #[deprecated(
99 since = "32.0.0",
100 note = "It can be a footgun as gas limit is removed, use ResultGas::with_* functions instead"
101 )]
102 pub const fn new(total_gas_spent: u64, refunded: u64, floor_gas: u64) -> Self {
103 Self {
104 total_gas_spent,
105 refunded,
106 floor_gas,
107 state_gas_spent: 0,
108 }
109 }
110
111 #[inline]
113 pub const fn new_with_state_gas(
114 total_gas_spent: u64,
115 refunded: u64,
116 floor_gas: u64,
117 state_gas_spent: u64,
118 ) -> Self {
119 Self {
120 total_gas_spent,
121 refunded,
122 floor_gas,
123 state_gas_spent,
124 }
125 }
126
127 #[inline]
133 pub const fn total_gas_spent(&self) -> u64 {
134 self.total_gas_spent
135 }
136
137 #[inline]
144 pub const fn state_gas_spent_final(&self) -> u64 {
145 self.state_gas_spent
146 }
147
148 #[inline]
150 pub const fn floor_gas(&self) -> u64 {
151 self.floor_gas
152 }
153
154 #[inline]
159 pub const fn inner_refunded(&self) -> u64 {
160 self.refunded
161 }
162
163 #[inline]
165 #[deprecated(
166 since = "32.0.0",
167 note = "After EIP-8037 gas is split on
168 regular and state gas, this method is no longer valid.
169 Use [`ResultGas::total_gas_spent`] instead"
170 )]
171 pub const fn spent(&self) -> u64 {
172 self.total_gas_spent()
173 }
174
175 #[inline]
179 pub const fn set_total_gas_spent(&mut self, total_gas_spent: u64) {
180 self.total_gas_spent = total_gas_spent;
181 }
182
183 #[inline]
185 pub const fn set_refunded(&mut self, refunded: u64) {
186 self.refunded = refunded;
187 }
188
189 #[inline]
191 pub const fn set_floor_gas(&mut self, floor_gas: u64) {
192 self.floor_gas = floor_gas;
193 }
194
195 #[inline]
197 pub const fn set_state_gas_spent(&mut self, state_gas_spent: u64) {
198 self.state_gas_spent = state_gas_spent;
199 }
200
201 #[inline]
203 #[deprecated(
204 since = "32.0.0",
205 note = "After EIP-8037 gas is split on
206 regular and state gas, this method is no longer valid.
207 Use [`ResultGas::set_total_gas_spent`] instead"
208 )]
209 pub const fn set_spent(&mut self, spent: u64) {
210 self.total_gas_spent = spent;
211 }
212
213 #[inline]
217 pub const fn with_total_gas_spent(mut self, total_gas_spent: u64) -> Self {
218 self.total_gas_spent = total_gas_spent;
219 self
220 }
221
222 #[inline]
224 pub const fn with_refunded(mut self, refunded: u64) -> Self {
225 self.refunded = refunded;
226 self
227 }
228
229 #[inline]
231 pub const fn with_floor_gas(mut self, floor_gas: u64) -> Self {
232 self.floor_gas = floor_gas;
233 self
234 }
235
236 #[inline]
238 pub const fn with_state_gas_spent(mut self, state_gas_spent: u64) -> Self {
239 self.state_gas_spent = state_gas_spent;
240 self
241 }
242
243 #[inline]
245 #[deprecated(
246 since = "32.0.0",
247 note = "After EIP-8037 gas is split on
248 regular and state gas, this method is no longer valid.
249 Use [`ResultGas::with_total_gas_spent`] instead"
250 )]
251 pub const fn with_spent(mut self, spent: u64) -> Self {
252 self.total_gas_spent = spent;
253 self
254 }
255
256 #[inline]
262 pub const fn tx_gas_used(&self) -> u64 {
263 let total_gas_spent = self.total_gas_spent();
265 let tx_gas_refunded = total_gas_spent.saturating_sub(self.inner_refunded());
267 max(tx_gas_refunded, self.floor_gas())
268 }
269
270 #[inline]
272 pub const fn block_regular_gas_used(&self) -> u64 {
273 let execution_gas_spent = self
274 .total_gas_spent()
275 .saturating_sub(self.state_gas_spent_final());
276 max(execution_gas_spent, self.floor_gas())
277 }
278
279 #[inline]
283 pub const fn block_state_gas_used(&self) -> u64 {
284 self.state_gas_spent_final()
285 }
286
287 #[inline]
292 #[deprecated(
293 since = "32.0.0",
294 note = "Used is not descriptive enough, use [`ResultGas::tx_gas_used`] instead"
295 )]
296 pub const fn used(&self) -> u64 {
297 let spent_sub_refunded = self.spent_sub_refunded();
300 if spent_sub_refunded < self.floor_gas {
301 return self.floor_gas;
302 }
303 spent_sub_refunded
304 }
305
306 #[inline]
311 pub const fn spent_sub_refunded(&self) -> u64 {
312 self.total_gas_spent().saturating_sub(self.refunded)
313 }
314
315 #[inline]
320 pub const fn final_refunded(&self) -> u64 {
321 if self.spent_sub_refunded() < self.floor_gas {
322 0
323 } else {
324 self.refunded
325 }
326 }
327}
328
329#[inline(always)]
331pub const fn max(a: u64, b: u64) -> u64 {
332 if a > b {
333 a
334 } else {
335 b
336 }
337}
338
339#[inline(always)]
341pub const fn min(a: u64, b: u64) -> u64 {
342 if a < b {
343 a
344 } else {
345 b
346 }
347}
348
349impl fmt::Display for ResultGas {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 write!(
352 f,
353 "Gas used: {}, total spent: {}",
354 self.tx_gas_used(),
355 self.total_gas_spent()
356 )?;
357 if self.refunded > 0 {
358 write!(f, ", refunded: {}", self.refunded)?;
359 }
360 if self.floor_gas > 0 {
361 write!(f, ", floor: {}", self.floor_gas)?;
362 }
363 if self.state_gas_spent > 0 {
364 write!(f, ", state_gas: {}", self.state_gas_spent)?;
365 }
366 Ok(())
367 }
368}
369
370#[derive(Clone, Debug, PartialEq, Eq, Hash)]
372#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
373pub enum ExecutionResult<HaltReasonTy = HaltReason> {
374 Success {
376 reason: SuccessReason,
378 gas: ResultGas,
380 logs: Vec<Log>,
382 output: Output,
384 },
385 Revert {
387 gas: ResultGas,
389 logs: Vec<Log>,
391 output: Bytes,
393 },
394 Halt {
396 reason: HaltReasonTy,
398 gas: ResultGas,
403 logs: Vec<Log>,
405 },
406}
407
408impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
409 pub const fn is_success(&self) -> bool {
415 matches!(self, Self::Success { .. })
416 }
417
418 pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
420 where
421 F: FnOnce(HaltReasonTy) -> OHR,
422 {
423 match self {
424 Self::Success {
425 reason,
426 gas,
427 logs,
428 output,
429 } => ExecutionResult::Success {
430 reason,
431 gas,
432 logs,
433 output,
434 },
435 Self::Revert { gas, logs, output } => ExecutionResult::Revert { gas, logs, output },
436 Self::Halt { reason, gas, logs } => ExecutionResult::Halt {
437 reason: op(reason),
438 gas,
439 logs,
440 },
441 }
442 }
443
444 pub fn created_address(&self) -> Option<Address> {
447 match self {
448 Self::Success { output, .. } => output.address().cloned(),
449 _ => None,
450 }
451 }
452
453 pub const fn is_halt(&self) -> bool {
455 matches!(self, Self::Halt { .. })
456 }
457
458 pub const fn output(&self) -> Option<&Bytes> {
462 match self {
463 Self::Success { output, .. } => Some(output.data()),
464 Self::Revert { output, .. } => Some(output),
465 _ => None,
466 }
467 }
468
469 pub fn into_output(self) -> Option<Bytes> {
473 match self {
474 Self::Success { output, .. } => Some(output.into_data()),
475 Self::Revert { output, .. } => Some(output),
476 _ => None,
477 }
478 }
479
480 pub const fn logs(&self) -> &[Log] {
482 match self {
483 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
484 logs.as_slice()
485 }
486 }
487 }
488
489 pub fn into_logs(self) -> Vec<Log> {
491 match self {
492 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
493 logs
494 }
495 }
496 }
497
498 pub const fn gas(&self) -> &ResultGas {
500 match self {
501 Self::Success { gas, .. } | Self::Revert { gas, .. } | Self::Halt { gas, .. } => gas,
502 }
503 }
504
505 pub const fn tx_gas_used(&self) -> u64 {
507 self.gas().tx_gas_used()
508 }
509
510 #[inline]
512 #[deprecated(
513 since = "32.0.0",
514 note = "Use `tx_gas_used()` instead, `gas_used` is ambiguous after EIP-8037 state gas split"
515 )]
516 pub const fn gas_used(&self) -> u64 {
517 self.tx_gas_used()
518 }
519}
520
521impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 match self {
524 Self::Success {
525 reason,
526 gas,
527 logs,
528 output,
529 } => {
530 write!(f, "Success ({reason}): {gas}")?;
531 if !logs.is_empty() {
532 write!(
533 f,
534 ", {} log{}",
535 logs.len(),
536 if logs.len() == 1 { "" } else { "s" }
537 )?;
538 }
539 write!(f, ", {output}")
540 }
541 Self::Revert { gas, logs, output } => {
542 write!(f, "Revert: {gas}")?;
543 if !logs.is_empty() {
544 write!(
545 f,
546 ", {} log{}",
547 logs.len(),
548 if logs.len() == 1 { "" } else { "s" }
549 )?;
550 }
551 if !output.is_empty() {
552 write!(f, ", {} bytes output", output.len())?;
553 }
554 Ok(())
555 }
556 Self::Halt { reason, gas, logs } => {
557 write!(f, "Halted: {reason} ({gas})")?;
558 if !logs.is_empty() {
559 write!(
560 f,
561 ", {} log{}",
562 logs.len(),
563 if logs.len() == 1 { "" } else { "s" }
564 )?;
565 }
566 Ok(())
567 }
568 }
569 }
570}
571
572#[derive(Debug, Clone, PartialEq, Eq, Hash)]
574#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
575pub enum Output {
576 Call(Bytes),
578 Create(Bytes, Option<Address>),
580}
581
582impl Output {
583 pub fn into_data(self) -> Bytes {
585 match self {
586 Output::Call(data) => data,
587 Output::Create(data, _) => data,
588 }
589 }
590
591 pub const fn data(&self) -> &Bytes {
593 match self {
594 Output::Call(data) => data,
595 Output::Create(data, _) => data,
596 }
597 }
598
599 pub const fn address(&self) -> Option<&Address> {
601 match self {
602 Output::Call(_) => None,
603 Output::Create(_, address) => address.as_ref(),
604 }
605 }
606}
607
608impl fmt::Display for Output {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 match self {
611 Output::Call(data) => {
612 if data.is_empty() {
613 write!(f, "no output")
614 } else {
615 write!(f, "{} bytes output", data.len())
616 }
617 }
618 Output::Create(data, Some(addr)) => {
619 if data.is_empty() {
620 write!(f, "contract created at {}", addr)
621 } else {
622 write!(f, "contract created at {} ({} bytes)", addr, data.len())
623 }
624 }
625 Output::Create(data, None) => {
626 if data.is_empty() {
627 write!(f, "contract creation (no address)")
628 } else {
629 write!(f, "contract creation (no address, {} bytes)", data.len())
630 }
631 }
632 }
633 }
634}
635
636#[derive(Debug, Clone)]
638pub struct AnyError(Arc<dyn core::error::Error + Send + Sync>);
639impl AnyError {
640 pub fn new(err: impl core::error::Error + Send + Sync + 'static) -> Self {
642 Self(Arc::new(err))
643 }
644}
645
646impl PartialEq for AnyError {
647 fn eq(&self, other: &Self) -> bool {
648 Arc::ptr_eq(&self.0, &other.0)
649 }
650}
651impl Eq for AnyError {}
652impl core::hash::Hash for AnyError {
653 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
654 (Arc::as_ptr(&self.0) as *const ()).hash(state);
655 }
656}
657impl fmt::Display for AnyError {
658 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659 fmt::Display::fmt(&self.0, f)
660 }
661}
662impl core::error::Error for AnyError {
663 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
664 self.0.source()
665 }
666}
667
668#[cfg(feature = "serde")]
669impl serde::Serialize for AnyError {
670 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
671 serializer.collect_str(self)
672 }
673}
674
675#[derive(Debug)]
676struct StringError(String);
677impl fmt::Display for StringError {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 f.write_str(&self.0)
680 }
681}
682impl core::error::Error for StringError {}
683
684impl From<String> for AnyError {
685 fn from(value: String) -> Self {
686 Self::new(StringError(value))
687 }
688}
689impl From<&'static str> for AnyError {
690 fn from(s: &'static str) -> Self {
691 Self::new(StringError(s.into()))
692 }
693}
694
695#[cfg(feature = "serde")]
696impl<'de> serde::Deserialize<'de> for AnyError {
697 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
698 let s = String::deserialize(deserializer)?;
699 Ok(s.into())
700 }
701}
702
703#[derive(Debug, Clone, PartialEq, Eq)]
705#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
706pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
707 Transaction(TransactionError),
709 Header(InvalidHeader),
711 Database(DBError),
713 Custom(String),
718 CustomAny(AnyError),
723}
724
725impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
726 for EVMError<DBError, TransactionValidationErrorT>
727{
728 fn from(value: ContextError<DBError>) -> Self {
729 match value {
730 ContextError::Db(e) => Self::Database(e),
731 ContextError::Custom(e) => Self::Custom(e),
732 }
733 }
734}
735
736impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
737 fn from(value: DBError) -> Self {
738 Self::Database(value)
739 }
740}
741
742pub trait FromStringError {
744 fn from_string(value: String) -> Self;
746}
747
748impl<DB, TX> FromStringError for EVMError<DB, TX> {
749 fn from_string(value: String) -> Self {
750 Self::Custom(value)
751 }
752}
753
754impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
755 fn from(value: InvalidTransaction) -> Self {
756 Self::Transaction(TXE::from(value))
757 }
758}
759
760impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
761 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
763 where
764 F: FnOnce(DBError) -> E,
765 {
766 match self {
767 Self::Transaction(e) => EVMError::Transaction(e),
768 Self::Header(e) => EVMError::Header(e),
769 Self::Database(e) => EVMError::Database(op(e)),
770 Self::Custom(e) => EVMError::Custom(e),
771 Self::CustomAny(e) => EVMError::CustomAny(e),
772 }
773 }
774}
775
776impl<DBError, TransactionValidationErrorT> core::error::Error
777 for EVMError<DBError, TransactionValidationErrorT>
778where
779 DBError: core::error::Error + 'static,
780 TransactionValidationErrorT: core::error::Error + 'static,
781{
782 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
783 match self {
784 Self::Transaction(e) => Some(e),
785 Self::Header(e) => Some(e),
786 Self::Database(e) => Some(e),
787 Self::Custom(_) => None,
788 Self::CustomAny(e) => Some(e.0.as_ref()),
789 }
790 }
791}
792
793impl<DBError, TransactionValidationErrorT> fmt::Display
794 for EVMError<DBError, TransactionValidationErrorT>
795where
796 DBError: fmt::Display,
797 TransactionValidationErrorT: fmt::Display,
798{
799 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
800 match self {
801 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
802 Self::Header(e) => write!(f, "header validation error: {e}"),
803 Self::Database(e) => write!(f, "database error: {e}"),
804 Self::Custom(e) => f.write_str(e),
805 Self::CustomAny(e) => write!(f, "{e}"),
806 }
807 }
808}
809
810impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
811 for EVMError<DBError, TransactionValidationErrorT>
812{
813 fn from(value: InvalidHeader) -> Self {
814 Self::Header(value)
815 }
816}
817
818#[derive(Debug, Clone, PartialEq, Eq, Hash)]
820#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
821pub enum InvalidTransaction {
822 PriorityFeeGreaterThanMaxFee,
828 GasPriceLessThanBasefee,
830 CallerGasLimitMoreThanBlock,
832 CallGasCostMoreThanGasLimit {
838 initial_gas: u64,
840 gas_limit: u64,
842 },
843 GasFloorMoreThanGasLimit {
848 gas_floor: u64,
850 gas_limit: u64,
852 },
853 RejectCallerWithCode,
855 LackOfFundForMaxFee {
857 fee: Box<U256>,
859 balance: Box<U256>,
861 },
862 OverflowPaymentInTransaction,
864 NonceOverflowInTransaction,
866 NonceTooHigh {
868 tx: u64,
870 state: u64,
872 },
873 NonceTooLow {
875 tx: u64,
877 state: u64,
879 },
880 CreateInitCodeSizeLimit,
882 InvalidChainId,
884 MissingChainId,
886 TxGasLimitGreaterThanCap {
888 gas_limit: u64,
890 cap: u64,
892 },
893 AccessListNotSupported,
895 MaxFeePerBlobGasNotSupported,
897 BlobVersionedHashesNotSupported,
899 BlobGasPriceGreaterThanMax {
901 block_blob_gas_price: u128,
903 tx_max_fee_per_blob_gas: u128,
905 },
906 EmptyBlobs,
908 BlobCreateTransaction,
912 TooManyBlobs {
914 max: usize,
916 have: usize,
918 },
919 BlobVersionNotSupported,
921 AuthorizationListNotSupported,
923 AuthorizationListInvalidFields,
925 EmptyAuthorizationList,
927 Eip2930NotSupported,
929 Eip1559NotSupported,
931 Eip4844NotSupported,
933 Eip7702NotSupported,
935 Eip7873NotSupported,
937 Eip7873MissingTarget,
939 Str(Cow<'static, str>),
941}
942
943impl TransactionError for InvalidTransaction {}
944
945impl core::error::Error for InvalidTransaction {}
946
947impl fmt::Display for InvalidTransaction {
948 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
949 match self {
950 Self::PriorityFeeGreaterThanMaxFee => {
951 write!(f, "priority fee is greater than max fee")
952 }
953 Self::GasPriceLessThanBasefee => {
954 write!(f, "gas price is less than basefee")
955 }
956 Self::CallerGasLimitMoreThanBlock => {
957 write!(f, "caller gas limit exceeds the block gas limit")
958 }
959 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
960 write!(
961 f,
962 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
963 )
964 }
965 Self::CallGasCostMoreThanGasLimit {
966 initial_gas,
967 gas_limit,
968 } => {
969 write!(
970 f,
971 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
972 )
973 }
974 Self::GasFloorMoreThanGasLimit {
975 gas_floor,
976 gas_limit,
977 } => {
978 write!(
979 f,
980 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
981 )
982 }
983 Self::RejectCallerWithCode => {
984 write!(f, "reject transactions from senders with deployed code")
985 }
986 Self::LackOfFundForMaxFee { fee, balance } => {
987 write!(f, "lack of funds ({balance}) for max fee ({fee})")
988 }
989 Self::OverflowPaymentInTransaction => {
990 write!(f, "overflow payment in transaction")
991 }
992 Self::NonceOverflowInTransaction => {
993 write!(f, "nonce overflow in transaction")
994 }
995 Self::NonceTooHigh { tx, state } => {
996 write!(f, "nonce {tx} too high, expected {state}")
997 }
998 Self::NonceTooLow { tx, state } => {
999 write!(f, "nonce {tx} too low, expected {state}")
1000 }
1001 Self::CreateInitCodeSizeLimit => {
1002 write!(f, "create initcode size limit")
1003 }
1004 Self::InvalidChainId => write!(f, "invalid chain ID"),
1005 Self::MissingChainId => write!(f, "missing chain ID"),
1006 Self::AccessListNotSupported => write!(f, "access list not supported"),
1007 Self::MaxFeePerBlobGasNotSupported => {
1008 write!(f, "max fee per blob gas not supported")
1009 }
1010 Self::BlobVersionedHashesNotSupported => {
1011 write!(f, "blob versioned hashes not supported")
1012 }
1013 Self::BlobGasPriceGreaterThanMax {
1014 block_blob_gas_price,
1015 tx_max_fee_per_blob_gas,
1016 } => {
1017 write!(
1018 f,
1019 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
1020 )
1021 }
1022 Self::EmptyBlobs => write!(f, "empty blobs"),
1023 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
1024 Self::TooManyBlobs { max, have } => {
1025 write!(f, "too many blobs, have {have}, max {max}")
1026 }
1027 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
1028 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
1029 Self::AuthorizationListInvalidFields => {
1030 write!(f, "authorization list tx has invalid fields")
1031 }
1032 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
1033 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
1034 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
1035 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
1036 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
1037 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
1038 Self::Eip7873MissingTarget => {
1039 write!(f, "Eip7873 initcode transaction should have `to` address")
1040 }
1041 Self::Str(msg) => f.write_str(msg),
1042 }
1043 }
1044}
1045
1046#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1048#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1049pub enum InvalidHeader {
1050 PrevrandaoNotSet,
1052 ExcessBlobGasNotSet,
1054}
1055
1056impl core::error::Error for InvalidHeader {}
1057
1058impl fmt::Display for InvalidHeader {
1059 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1060 match self {
1061 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
1062 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
1063 }
1064 }
1065}
1066
1067#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1069#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1070pub enum SuccessReason {
1071 Stop,
1073 Return,
1075 SelfDestruct,
1077}
1078
1079impl fmt::Display for SuccessReason {
1080 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1081 match self {
1082 Self::Stop => write!(f, "Stop"),
1083 Self::Return => write!(f, "Return"),
1084 Self::SelfDestruct => write!(f, "SelfDestruct"),
1085 }
1086 }
1087}
1088
1089#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1093#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1094pub enum HaltReason {
1095 OutOfGas(OutOfGasError),
1097 OpcodeNotFound,
1099 InvalidFEOpcode,
1101 InvalidJump,
1103 NotActivated,
1105 StackUnderflow,
1107 StackOverflow,
1109 OutOfOffset,
1111 CreateCollision,
1113 PrecompileError,
1115 PrecompileErrorWithContext(String),
1117 NonceOverflow,
1119 CreateContractSizeLimit,
1121 CreateContractStartingWithEF,
1123 CreateInitCodeSizeLimit,
1125
1126 OverflowPayment,
1129 StateChangeDuringStaticCall,
1131 CallNotAllowedInsideStatic,
1133 OutOfFunds,
1135 CallTooDeep,
1137}
1138
1139impl core::error::Error for HaltReason {}
1140
1141impl fmt::Display for HaltReason {
1142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1143 match self {
1144 Self::OutOfGas(err) => write!(f, "{err}"),
1145 Self::OpcodeNotFound => write!(f, "opcode not found"),
1146 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
1147 Self::InvalidJump => write!(f, "invalid jump destination"),
1148 Self::NotActivated => write!(f, "feature or opcode not activated"),
1149 Self::StackUnderflow => write!(f, "stack underflow"),
1150 Self::StackOverflow => write!(f, "stack overflow"),
1151 Self::OutOfOffset => write!(f, "out of offset"),
1152 Self::CreateCollision => write!(f, "create collision"),
1153 Self::PrecompileError => write!(f, "precompile error"),
1154 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
1155 Self::NonceOverflow => write!(f, "nonce overflow"),
1156 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
1157 Self::CreateContractStartingWithEF => {
1158 write!(f, "create contract starting with 0xEF")
1159 }
1160 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
1161 Self::OverflowPayment => write!(f, "overflow payment"),
1162 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
1163 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
1164 Self::OutOfFunds => write!(f, "out of funds"),
1165 Self::CallTooDeep => write!(f, "call too deep"),
1166 }
1167 }
1168}
1169
1170#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1173pub enum OutOfGasError {
1174 Basic,
1176 MemoryLimit,
1178 Memory,
1180 Precompile,
1182 InvalidOperand,
1185 ReentrancySentry,
1187}
1188
1189impl core::error::Error for OutOfGasError {}
1190
1191impl fmt::Display for OutOfGasError {
1192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1193 match self {
1194 Self::Basic => write!(f, "out of gas"),
1195 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1196 Self::Memory => write!(f, "out of gas: memory expansion"),
1197 Self::Precompile => write!(f, "out of gas: precompile"),
1198 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1199 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1200 }
1201 }
1202}
1203
1204#[derive(Debug, Clone, PartialEq, Eq)]
1206#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1207pub struct TransactionIndexedError<Error> {
1208 pub error: Error,
1210 pub transaction_index: usize,
1212}
1213
1214impl<Error> TransactionIndexedError<Error> {
1215 #[must_use]
1217 pub const fn new(error: Error, transaction_index: usize) -> Self {
1218 Self {
1219 error,
1220 transaction_index,
1221 }
1222 }
1223
1224 pub const fn error(&self) -> &Error {
1226 &self.error
1227 }
1228
1229 #[must_use]
1231 pub fn into_error(self) -> Error {
1232 self.error
1233 }
1234}
1235
1236impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1238 write!(
1239 f,
1240 "transaction {} failed: {}",
1241 self.transaction_index, self.error
1242 )
1243 }
1244}
1245
1246impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1247 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1248 Some(&self.error)
1249 }
1250}
1251
1252impl From<&'static str> for InvalidTransaction {
1253 fn from(s: &'static str) -> Self {
1254 Self::Str(Cow::Borrowed(s))
1255 }
1256}
1257
1258impl From<String> for InvalidTransaction {
1259 fn from(s: String) -> Self {
1260 Self::Str(Cow::Owned(s))
1261 }
1262}
1263
1264#[cfg(test)]
1265mod tests {
1266 use super::*;
1267
1268 #[test]
1269 fn test_execution_result_display() {
1270 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1271 reason: SuccessReason::Return,
1272 gas: ResultGas::default()
1273 .with_total_gas_spent(100000)
1274 .with_refunded(26000)
1275 .with_floor_gas(5000),
1276 logs: vec![Log::default(), Log::default()],
1277 output: Output::Call(Bytes::from(vec![1, 2, 3])),
1278 };
1279 assert_eq!(
1280 result.to_string(),
1281 "Success (Return): Gas used: 74000, total spent: 100000, refunded: 26000, floor: 5000, 2 logs, 3 bytes output"
1282 );
1283
1284 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1285 gas: ResultGas::default()
1286 .with_total_gas_spent(100000)
1287 .with_refunded(100000),
1288 logs: vec![],
1289 output: Bytes::from(vec![1, 2, 3, 4]),
1290 };
1291 assert_eq!(
1292 result.to_string(),
1293 "Revert: Gas used: 0, total spent: 100000, refunded: 100000, 4 bytes output"
1294 );
1295
1296 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1297 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1298 gas: ResultGas::default()
1299 .with_total_gas_spent(1000000)
1300 .with_refunded(1000000),
1301 logs: vec![],
1302 };
1303 assert_eq!(
1304 result.to_string(),
1305 "Halted: out of gas (Gas used: 0, total spent: 1000000, refunded: 1000000)"
1306 );
1307 }
1308
1309 #[test]
1310 fn test_result_gas_display() {
1311 assert_eq!(
1313 ResultGas::default().with_total_gas_spent(21000).to_string(),
1314 "Gas used: 21000, total spent: 21000"
1315 );
1316 assert_eq!(
1318 ResultGas::default()
1319 .with_total_gas_spent(50000)
1320 .with_refunded(10000)
1321 .to_string(),
1322 "Gas used: 40000, total spent: 50000, refunded: 10000"
1323 );
1324 assert_eq!(
1326 ResultGas::default()
1327 .with_total_gas_spent(50000)
1328 .with_refunded(10000)
1329 .with_floor_gas(30000)
1330 .to_string(),
1331 "Gas used: 40000, total spent: 50000, refunded: 10000, floor: 30000"
1332 );
1333 }
1334
1335 #[test]
1336 fn test_result_gas_used_and_remaining() {
1337 let gas = ResultGas::default()
1338 .with_total_gas_spent(100)
1339 .with_refunded(30);
1340 assert_eq!(gas.total_gas_spent(), 100);
1341 assert_eq!(gas.inner_refunded(), 30);
1342 assert_eq!(gas.spent_sub_refunded(), 70);
1343
1344 let gas = ResultGas::default()
1346 .with_total_gas_spent(10)
1347 .with_refunded(50);
1348 assert_eq!(gas.spent_sub_refunded(), 0);
1349 }
1350
1351 #[test]
1352 fn test_final_refunded_with_floor_gas() {
1353 let gas = ResultGas::default()
1355 .with_total_gas_spent(50000)
1356 .with_refunded(10000);
1357 assert_eq!(gas.tx_gas_used(), 40000);
1358 assert_eq!(gas.final_refunded(), 10000);
1359
1360 let gas = ResultGas::default()
1363 .with_total_gas_spent(50000)
1364 .with_refunded(10000)
1365 .with_floor_gas(45000);
1366 assert_eq!(gas.tx_gas_used(), 45000);
1367 assert_eq!(gas.final_refunded(), 0);
1368
1369 let gas = ResultGas::default()
1372 .with_total_gas_spent(50000)
1373 .with_refunded(10000)
1374 .with_floor_gas(30000);
1375 assert_eq!(gas.tx_gas_used(), 40000);
1376 assert_eq!(gas.final_refunded(), 10000);
1377
1378 let gas = ResultGas::default()
1381 .with_total_gas_spent(50000)
1382 .with_refunded(10000)
1383 .with_floor_gas(40000);
1384 assert_eq!(gas.tx_gas_used(), 40000);
1385 assert_eq!(gas.final_refunded(), 10000);
1386 }
1387}