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)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub struct ResultGas {
74 limit: u64,
76 #[cfg_attr(feature = "serde", serde(rename = "gas_spent"))]
79 spent: u64,
80 #[cfg_attr(feature = "serde", serde(rename = "gas_refunded"))]
85 refunded: u64,
86 floor_gas: u64,
88 intrinsic_gas: u64,
91}
92
93impl ResultGas {
94 #[inline]
96 pub const fn new(
97 limit: u64,
98 spent: u64,
99 refunded: u64,
100 floor_gas: u64,
101 intrinsic_gas: u64,
102 ) -> Self {
103 Self {
104 limit,
105 spent,
106 refunded,
107 floor_gas,
108 intrinsic_gas,
109 }
110 }
111
112 #[inline]
114 pub const fn limit(&self) -> u64 {
115 self.limit
116 }
117
118 #[inline]
122 pub const fn spent(&self) -> u64 {
123 self.spent
124 }
125
126 #[inline]
128 pub const fn floor_gas(&self) -> u64 {
129 self.floor_gas
130 }
131
132 #[inline]
134 pub const fn intrinsic_gas(&self) -> u64 {
135 self.intrinsic_gas
136 }
137
138 #[inline]
140 pub const fn with_limit(mut self, limit: u64) -> Self {
141 self.limit = limit;
142 self
143 }
144
145 #[inline]
147 pub const fn with_spent(mut self, spent: u64) -> Self {
148 self.spent = spent;
149 self
150 }
151
152 #[inline]
154 pub const fn with_refunded(mut self, refunded: u64) -> Self {
155 self.refunded = refunded;
156 self
157 }
158
159 #[inline]
161 pub const fn with_floor_gas(mut self, floor_gas: u64) -> Self {
162 self.floor_gas = floor_gas;
163 self
164 }
165
166 #[inline]
168 pub const fn with_intrinsic_gas(mut self, intrinsic_gas: u64) -> Self {
169 self.intrinsic_gas = intrinsic_gas;
170 self
171 }
172
173 #[inline]
175 pub fn set_limit(&mut self, limit: u64) {
176 self.limit = limit;
177 }
178
179 #[inline]
181 pub fn set_spent(&mut self, spent: u64) {
182 self.spent = spent;
183 }
184
185 #[inline]
187 pub fn set_refunded(&mut self, refunded: u64) {
188 self.refunded = refunded;
189 }
190
191 #[inline]
193 pub fn set_floor_gas(&mut self, floor_gas: u64) {
194 self.floor_gas = floor_gas;
195 }
196
197 #[inline]
199 pub fn set_intrinsic_gas(&mut self, intrinsic_gas: u64) {
200 self.intrinsic_gas = intrinsic_gas;
201 }
202
203 #[inline]
208 pub const fn used(&self) -> u64 {
209 let spent_sub_refunded = self.spent_sub_refunded();
212 if spent_sub_refunded < self.floor_gas {
213 return self.floor_gas;
214 }
215 spent_sub_refunded
216 }
217
218 #[inline]
223 pub const fn spent_sub_refunded(&self) -> u64 {
224 self.spent.saturating_sub(self.refunded)
225 }
226
227 #[inline]
229 pub const fn remaining(&self) -> u64 {
230 self.limit.saturating_sub(self.spent)
231 }
232
233 #[inline]
238 pub const fn inner_refunded(&self) -> u64 {
239 self.refunded
240 }
241
242 #[inline]
247 pub const fn final_refunded(&self) -> u64 {
248 if self.spent_sub_refunded() < self.floor_gas {
249 0
250 } else {
251 self.refunded
252 }
253 }
254}
255
256impl fmt::Display for ResultGas {
257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 write!(
259 f,
260 "gas used: {}, limit: {}, spent: {}",
261 self.used(),
262 self.limit,
263 self.spent
264 )?;
265 if self.refunded > 0 {
266 write!(f, ", refunded: {}", self.refunded)?;
267 }
268 if self.floor_gas > 0 {
269 write!(f, ", floor: {}", self.floor_gas)?;
270 }
271 if self.intrinsic_gas > 0 {
272 write!(f, ", intrinsic: {}", self.intrinsic_gas)?;
273 }
274 Ok(())
275 }
276}
277
278#[derive(Clone, Debug, PartialEq, Eq, Hash)]
280#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
281pub enum ExecutionResult<HaltReasonTy = HaltReason> {
282 Success {
284 reason: SuccessReason,
286 gas: ResultGas,
288 logs: Vec<Log>,
290 output: Output,
292 },
293 Revert {
295 gas: ResultGas,
297 logs: Vec<Log>,
299 output: Bytes,
301 },
302 Halt {
304 reason: HaltReasonTy,
306 gas: ResultGas,
311 logs: Vec<Log>,
313 },
314}
315
316impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
317 pub fn is_success(&self) -> bool {
323 matches!(self, Self::Success { .. })
324 }
325
326 pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
328 where
329 F: FnOnce(HaltReasonTy) -> OHR,
330 {
331 match self {
332 Self::Success {
333 reason,
334 gas,
335 logs,
336 output,
337 } => ExecutionResult::Success {
338 reason,
339 gas,
340 logs,
341 output,
342 },
343 Self::Revert { gas, logs, output } => ExecutionResult::Revert { gas, logs, output },
344 Self::Halt { reason, gas, logs } => ExecutionResult::Halt {
345 reason: op(reason),
346 gas,
347 logs,
348 },
349 }
350 }
351
352 pub fn created_address(&self) -> Option<Address> {
355 match self {
356 Self::Success { output, .. } => output.address().cloned(),
357 _ => None,
358 }
359 }
360
361 pub fn is_halt(&self) -> bool {
363 matches!(self, Self::Halt { .. })
364 }
365
366 pub fn output(&self) -> Option<&Bytes> {
370 match self {
371 Self::Success { output, .. } => Some(output.data()),
372 Self::Revert { output, .. } => Some(output),
373 _ => None,
374 }
375 }
376
377 pub fn into_output(self) -> Option<Bytes> {
381 match self {
382 Self::Success { output, .. } => Some(output.into_data()),
383 Self::Revert { output, .. } => Some(output),
384 _ => None,
385 }
386 }
387
388 pub fn logs(&self) -> &[Log] {
390 match self {
391 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
392 logs.as_slice()
393 }
394 }
395 }
396
397 pub fn into_logs(self) -> Vec<Log> {
399 match self {
400 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
401 logs
402 }
403 }
404 }
405
406 pub fn gas(&self) -> &ResultGas {
408 match self {
409 Self::Success { gas, .. } | Self::Revert { gas, .. } | Self::Halt { gas, .. } => gas,
410 }
411 }
412
413 pub fn gas_used(&self) -> u64 {
415 self.gas().used()
416 }
417}
418
419impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 match self {
422 Self::Success {
423 reason,
424 gas,
425 logs,
426 output,
427 } => {
428 write!(f, "Success ({reason}): {gas}")?;
429 if !logs.is_empty() {
430 write!(
431 f,
432 ", {} log{}",
433 logs.len(),
434 if logs.len() == 1 { "" } else { "s" }
435 )?;
436 }
437 write!(f, ", {output}")
438 }
439 Self::Revert { gas, logs, output } => {
440 write!(f, "Revert: {gas}")?;
441 if !logs.is_empty() {
442 write!(
443 f,
444 ", {} log{}",
445 logs.len(),
446 if logs.len() == 1 { "" } else { "s" }
447 )?;
448 }
449 if !output.is_empty() {
450 write!(f, ", {} bytes output", output.len())?;
451 }
452 Ok(())
453 }
454 Self::Halt { reason, gas, logs } => {
455 write!(f, "Halted: {reason} ({gas})")?;
456 if !logs.is_empty() {
457 write!(
458 f,
459 ", {} log{}",
460 logs.len(),
461 if logs.len() == 1 { "" } else { "s" }
462 )?;
463 }
464 Ok(())
465 }
466 }
467 }
468}
469
470#[derive(Debug, Clone, PartialEq, Eq, Hash)]
472#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
473pub enum Output {
474 Call(Bytes),
476 Create(Bytes, Option<Address>),
478}
479
480impl Output {
481 pub fn into_data(self) -> Bytes {
483 match self {
484 Output::Call(data) => data,
485 Output::Create(data, _) => data,
486 }
487 }
488
489 pub fn data(&self) -> &Bytes {
491 match self {
492 Output::Call(data) => data,
493 Output::Create(data, _) => data,
494 }
495 }
496
497 pub fn address(&self) -> Option<&Address> {
499 match self {
500 Output::Call(_) => None,
501 Output::Create(_, address) => address.as_ref(),
502 }
503 }
504}
505
506impl fmt::Display for Output {
507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508 match self {
509 Output::Call(data) => {
510 if data.is_empty() {
511 write!(f, "no output")
512 } else {
513 write!(f, "{} bytes output", data.len())
514 }
515 }
516 Output::Create(data, Some(addr)) => {
517 if data.is_empty() {
518 write!(f, "contract created at {}", addr)
519 } else {
520 write!(f, "contract created at {} ({} bytes)", addr, data.len())
521 }
522 }
523 Output::Create(data, None) => {
524 if data.is_empty() {
525 write!(f, "contract creation (no address)")
526 } else {
527 write!(f, "contract creation (no address, {} bytes)", data.len())
528 }
529 }
530 }
531 }
532}
533
534#[derive(Debug, Clone)]
536pub struct AnyError(Arc<dyn core::error::Error + Send + Sync>);
537impl AnyError {
538 pub fn new(err: impl core::error::Error + Send + Sync + 'static) -> Self {
540 Self(Arc::new(err))
541 }
542}
543
544impl PartialEq for AnyError {
545 fn eq(&self, other: &Self) -> bool {
546 Arc::ptr_eq(&self.0, &other.0)
547 }
548}
549impl Eq for AnyError {}
550impl core::hash::Hash for AnyError {
551 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
552 (Arc::as_ptr(&self.0) as *const ()).hash(state);
553 }
554}
555impl fmt::Display for AnyError {
556 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557 fmt::Display::fmt(&self.0, f)
558 }
559}
560impl core::error::Error for AnyError {
561 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
562 self.0.source()
563 }
564}
565
566#[cfg(feature = "serde")]
567impl serde::Serialize for AnyError {
568 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
569 serializer.collect_str(self)
570 }
571}
572
573#[derive(Debug)]
574struct StringError(String);
575impl fmt::Display for StringError {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 f.write_str(&self.0)
578 }
579}
580impl core::error::Error for StringError {}
581
582impl From<String> for AnyError {
583 fn from(value: String) -> Self {
584 Self::new(StringError(value))
585 }
586}
587impl From<&'static str> for AnyError {
588 fn from(s: &'static str) -> Self {
589 Self::new(StringError(s.into()))
590 }
591}
592
593#[cfg(feature = "serde")]
594impl<'de> serde::Deserialize<'de> for AnyError {
595 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
596 let s = String::deserialize(deserializer)?;
597 Ok(s.into())
598 }
599}
600
601#[derive(Debug, Clone, PartialEq, Eq)]
603#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
604pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
605 Transaction(TransactionError),
607 Header(InvalidHeader),
609 Database(DBError),
611 Custom(String),
616 CustomAny(AnyError),
621}
622
623impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
624 for EVMError<DBError, TransactionValidationErrorT>
625{
626 fn from(value: ContextError<DBError>) -> Self {
627 match value {
628 ContextError::Db(e) => Self::Database(e),
629 ContextError::Custom(e) => Self::Custom(e),
630 }
631 }
632}
633
634impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
635 fn from(value: DBError) -> Self {
636 Self::Database(value)
637 }
638}
639
640pub trait FromStringError {
642 fn from_string(value: String) -> Self;
644}
645
646impl<DB, TX> FromStringError for EVMError<DB, TX> {
647 fn from_string(value: String) -> Self {
648 Self::Custom(value)
649 }
650}
651
652impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
653 fn from(value: InvalidTransaction) -> Self {
654 Self::Transaction(TXE::from(value))
655 }
656}
657
658impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
659 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
661 where
662 F: FnOnce(DBError) -> E,
663 {
664 match self {
665 Self::Transaction(e) => EVMError::Transaction(e),
666 Self::Header(e) => EVMError::Header(e),
667 Self::Database(e) => EVMError::Database(op(e)),
668 Self::Custom(e) => EVMError::Custom(e),
669 Self::CustomAny(e) => EVMError::CustomAny(e),
670 }
671 }
672}
673
674impl<DBError, TransactionValidationErrorT> core::error::Error
675 for EVMError<DBError, TransactionValidationErrorT>
676where
677 DBError: core::error::Error + 'static,
678 TransactionValidationErrorT: core::error::Error + 'static,
679{
680 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
681 match self {
682 Self::Transaction(e) => Some(e),
683 Self::Header(e) => Some(e),
684 Self::Database(e) => Some(e),
685 Self::Custom(_) => None,
686 Self::CustomAny(e) => Some(e.0.as_ref()),
687 }
688 }
689}
690
691impl<DBError, TransactionValidationErrorT> fmt::Display
692 for EVMError<DBError, TransactionValidationErrorT>
693where
694 DBError: fmt::Display,
695 TransactionValidationErrorT: fmt::Display,
696{
697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698 match self {
699 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
700 Self::Header(e) => write!(f, "header validation error: {e}"),
701 Self::Database(e) => write!(f, "database error: {e}"),
702 Self::Custom(e) => f.write_str(e),
703 Self::CustomAny(e) => write!(f, "{e}"),
704 }
705 }
706}
707
708impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
709 for EVMError<DBError, TransactionValidationErrorT>
710{
711 fn from(value: InvalidHeader) -> Self {
712 Self::Header(value)
713 }
714}
715
716#[derive(Debug, Clone, PartialEq, Eq, Hash)]
718#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
719pub enum InvalidTransaction {
720 PriorityFeeGreaterThanMaxFee,
726 GasPriceLessThanBasefee,
728 CallerGasLimitMoreThanBlock,
730 CallGasCostMoreThanGasLimit {
736 initial_gas: u64,
738 gas_limit: u64,
740 },
741 GasFloorMoreThanGasLimit {
746 gas_floor: u64,
748 gas_limit: u64,
750 },
751 RejectCallerWithCode,
753 LackOfFundForMaxFee {
755 fee: Box<U256>,
757 balance: Box<U256>,
759 },
760 OverflowPaymentInTransaction,
762 NonceOverflowInTransaction,
764 NonceTooHigh {
766 tx: u64,
768 state: u64,
770 },
771 NonceTooLow {
773 tx: u64,
775 state: u64,
777 },
778 CreateInitCodeSizeLimit,
780 InvalidChainId,
782 MissingChainId,
784 TxGasLimitGreaterThanCap {
786 gas_limit: u64,
788 cap: u64,
790 },
791 AccessListNotSupported,
793 MaxFeePerBlobGasNotSupported,
795 BlobVersionedHashesNotSupported,
797 BlobGasPriceGreaterThanMax {
799 block_blob_gas_price: u128,
801 tx_max_fee_per_blob_gas: u128,
803 },
804 EmptyBlobs,
806 BlobCreateTransaction,
810 TooManyBlobs {
812 max: usize,
814 have: usize,
816 },
817 BlobVersionNotSupported,
819 AuthorizationListNotSupported,
821 AuthorizationListInvalidFields,
823 EmptyAuthorizationList,
825 Eip2930NotSupported,
827 Eip1559NotSupported,
829 Eip4844NotSupported,
831 Eip7702NotSupported,
833 Eip7873NotSupported,
835 Eip7873MissingTarget,
837 Str(Cow<'static, str>),
839}
840
841impl TransactionError for InvalidTransaction {}
842
843impl core::error::Error for InvalidTransaction {}
844
845impl fmt::Display for InvalidTransaction {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 match self {
848 Self::PriorityFeeGreaterThanMaxFee => {
849 write!(f, "priority fee is greater than max fee")
850 }
851 Self::GasPriceLessThanBasefee => {
852 write!(f, "gas price is less than basefee")
853 }
854 Self::CallerGasLimitMoreThanBlock => {
855 write!(f, "caller gas limit exceeds the block gas limit")
856 }
857 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
858 write!(
859 f,
860 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
861 )
862 }
863 Self::CallGasCostMoreThanGasLimit {
864 initial_gas,
865 gas_limit,
866 } => {
867 write!(
868 f,
869 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
870 )
871 }
872 Self::GasFloorMoreThanGasLimit {
873 gas_floor,
874 gas_limit,
875 } => {
876 write!(
877 f,
878 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
879 )
880 }
881 Self::RejectCallerWithCode => {
882 write!(f, "reject transactions from senders with deployed code")
883 }
884 Self::LackOfFundForMaxFee { fee, balance } => {
885 write!(f, "lack of funds ({balance}) for max fee ({fee})")
886 }
887 Self::OverflowPaymentInTransaction => {
888 write!(f, "overflow payment in transaction")
889 }
890 Self::NonceOverflowInTransaction => {
891 write!(f, "nonce overflow in transaction")
892 }
893 Self::NonceTooHigh { tx, state } => {
894 write!(f, "nonce {tx} too high, expected {state}")
895 }
896 Self::NonceTooLow { tx, state } => {
897 write!(f, "nonce {tx} too low, expected {state}")
898 }
899 Self::CreateInitCodeSizeLimit => {
900 write!(f, "create initcode size limit")
901 }
902 Self::InvalidChainId => write!(f, "invalid chain ID"),
903 Self::MissingChainId => write!(f, "missing chain ID"),
904 Self::AccessListNotSupported => write!(f, "access list not supported"),
905 Self::MaxFeePerBlobGasNotSupported => {
906 write!(f, "max fee per blob gas not supported")
907 }
908 Self::BlobVersionedHashesNotSupported => {
909 write!(f, "blob versioned hashes not supported")
910 }
911 Self::BlobGasPriceGreaterThanMax {
912 block_blob_gas_price,
913 tx_max_fee_per_blob_gas,
914 } => {
915 write!(
916 f,
917 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
918 )
919 }
920 Self::EmptyBlobs => write!(f, "empty blobs"),
921 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
922 Self::TooManyBlobs { max, have } => {
923 write!(f, "too many blobs, have {have}, max {max}")
924 }
925 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
926 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
927 Self::AuthorizationListInvalidFields => {
928 write!(f, "authorization list tx has invalid fields")
929 }
930 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
931 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
932 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
933 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
934 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
935 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
936 Self::Eip7873MissingTarget => {
937 write!(f, "Eip7873 initcode transaction should have `to` address")
938 }
939 Self::Str(msg) => f.write_str(msg),
940 }
941 }
942}
943
944#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
946#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
947pub enum InvalidHeader {
948 PrevrandaoNotSet,
950 ExcessBlobGasNotSet,
952}
953
954impl core::error::Error for InvalidHeader {}
955
956impl fmt::Display for InvalidHeader {
957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
958 match self {
959 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
960 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
961 }
962 }
963}
964
965#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
967#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
968pub enum SuccessReason {
969 Stop,
971 Return,
973 SelfDestruct,
975}
976
977impl fmt::Display for SuccessReason {
978 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979 match self {
980 Self::Stop => write!(f, "Stop"),
981 Self::Return => write!(f, "Return"),
982 Self::SelfDestruct => write!(f, "SelfDestruct"),
983 }
984 }
985}
986
987#[derive(Debug, Clone, PartialEq, Eq, Hash)]
991#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
992pub enum HaltReason {
993 OutOfGas(OutOfGasError),
995 OpcodeNotFound,
997 InvalidFEOpcode,
999 InvalidJump,
1001 NotActivated,
1003 StackUnderflow,
1005 StackOverflow,
1007 OutOfOffset,
1009 CreateCollision,
1011 PrecompileError,
1013 PrecompileErrorWithContext(String),
1015 NonceOverflow,
1017 CreateContractSizeLimit,
1019 CreateContractStartingWithEF,
1021 CreateInitCodeSizeLimit,
1023
1024 OverflowPayment,
1027 StateChangeDuringStaticCall,
1029 CallNotAllowedInsideStatic,
1031 OutOfFunds,
1033 CallTooDeep,
1035}
1036
1037impl core::error::Error for HaltReason {}
1038
1039impl fmt::Display for HaltReason {
1040 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1041 match self {
1042 Self::OutOfGas(err) => write!(f, "{err}"),
1043 Self::OpcodeNotFound => write!(f, "opcode not found"),
1044 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
1045 Self::InvalidJump => write!(f, "invalid jump destination"),
1046 Self::NotActivated => write!(f, "feature or opcode not activated"),
1047 Self::StackUnderflow => write!(f, "stack underflow"),
1048 Self::StackOverflow => write!(f, "stack overflow"),
1049 Self::OutOfOffset => write!(f, "out of offset"),
1050 Self::CreateCollision => write!(f, "create collision"),
1051 Self::PrecompileError => write!(f, "precompile error"),
1052 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
1053 Self::NonceOverflow => write!(f, "nonce overflow"),
1054 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
1055 Self::CreateContractStartingWithEF => {
1056 write!(f, "create contract starting with 0xEF")
1057 }
1058 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
1059 Self::OverflowPayment => write!(f, "overflow payment"),
1060 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
1061 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
1062 Self::OutOfFunds => write!(f, "out of funds"),
1063 Self::CallTooDeep => write!(f, "call too deep"),
1064 }
1065 }
1066}
1067
1068#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1070#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1071pub enum OutOfGasError {
1072 Basic,
1074 MemoryLimit,
1076 Memory,
1078 Precompile,
1080 InvalidOperand,
1083 ReentrancySentry,
1085}
1086
1087impl core::error::Error for OutOfGasError {}
1088
1089impl fmt::Display for OutOfGasError {
1090 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1091 match self {
1092 Self::Basic => write!(f, "out of gas"),
1093 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1094 Self::Memory => write!(f, "out of gas: memory expansion"),
1095 Self::Precompile => write!(f, "out of gas: precompile"),
1096 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1097 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1098 }
1099 }
1100}
1101
1102#[derive(Debug, Clone, PartialEq, Eq)]
1104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1105pub struct TransactionIndexedError<Error> {
1106 pub error: Error,
1108 pub transaction_index: usize,
1110}
1111
1112impl<Error> TransactionIndexedError<Error> {
1113 #[must_use]
1115 pub fn new(error: Error, transaction_index: usize) -> Self {
1116 Self {
1117 error,
1118 transaction_index,
1119 }
1120 }
1121
1122 pub fn error(&self) -> &Error {
1124 &self.error
1125 }
1126
1127 #[must_use]
1129 pub fn into_error(self) -> Error {
1130 self.error
1131 }
1132}
1133
1134impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1136 write!(
1137 f,
1138 "transaction {} failed: {}",
1139 self.transaction_index, self.error
1140 )
1141 }
1142}
1143
1144impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1145 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1146 Some(&self.error)
1147 }
1148}
1149
1150impl From<&'static str> for InvalidTransaction {
1151 fn from(s: &'static str) -> Self {
1152 Self::Str(Cow::Borrowed(s))
1153 }
1154}
1155
1156impl From<String> for InvalidTransaction {
1157 fn from(s: String) -> Self {
1158 Self::Str(Cow::Owned(s))
1159 }
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164 use super::*;
1165
1166 #[test]
1167 fn test_execution_result_display() {
1168 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1169 reason: SuccessReason::Return,
1170 gas: ResultGas::new(100000, 26000, 5000, 0, 0),
1171 logs: vec![Log::default(), Log::default()],
1172 output: Output::Call(Bytes::from(vec![1, 2, 3])),
1173 };
1174 assert_eq!(
1175 result.to_string(),
1176 "Success (Return): gas used: 21000, limit: 100000, spent: 26000, refunded: 5000, 2 logs, 3 bytes output"
1177 );
1178
1179 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1180 gas: ResultGas::new(100000, 100000, 0, 0, 0),
1181 logs: vec![],
1182 output: Bytes::from(vec![1, 2, 3, 4]),
1183 };
1184 assert_eq!(
1185 result.to_string(),
1186 "Revert: gas used: 100000, limit: 100000, spent: 100000, 4 bytes output"
1187 );
1188
1189 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1190 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1191 gas: ResultGas::new(1000000, 1000000, 0, 0, 0),
1192 logs: vec![],
1193 };
1194 assert_eq!(
1195 result.to_string(),
1196 "Halted: out of gas (gas used: 1000000, limit: 1000000, spent: 1000000)"
1197 );
1198 }
1199
1200 #[test]
1201 fn test_result_gas_display() {
1202 assert_eq!(
1204 ResultGas::new(100000, 21000, 0, 0, 0).to_string(),
1205 "gas used: 21000, limit: 100000, spent: 21000"
1206 );
1207 assert_eq!(
1209 ResultGas::new(100000, 50000, 10000, 0, 0).to_string(),
1210 "gas used: 40000, limit: 100000, spent: 50000, refunded: 10000"
1211 );
1212 assert_eq!(
1214 ResultGas::new(100000, 50000, 10000, 30000, 0).to_string(),
1215 "gas used: 40000, limit: 100000, spent: 50000, refunded: 10000, floor: 30000"
1216 );
1217 }
1218
1219 #[test]
1220 fn test_result_gas_used_and_remaining() {
1221 let gas = ResultGas::new(200, 100, 30, 0, 0);
1222 assert_eq!(gas.limit(), 200);
1223 assert_eq!(gas.spent(), 100);
1224 assert_eq!(gas.inner_refunded(), 30);
1225 assert_eq!(gas.used(), 70);
1226 assert_eq!(gas.remaining(), 100);
1227
1228 let gas = ResultGas::new(100, 10, 50, 0, 0);
1230 assert_eq!(gas.used(), 0);
1231 assert_eq!(gas.remaining(), 90);
1232 }
1233
1234 #[test]
1235 fn test_final_refunded_with_floor_gas() {
1236 let gas = ResultGas::new(100000, 50000, 10000, 0, 0);
1238 assert_eq!(gas.used(), 40000);
1239 assert_eq!(gas.final_refunded(), 10000);
1240
1241 let gas = ResultGas::new(100000, 50000, 10000, 45000, 0);
1244 assert_eq!(gas.used(), 45000);
1245 assert_eq!(gas.final_refunded(), 0);
1246
1247 let gas = ResultGas::new(100000, 50000, 10000, 30000, 0);
1250 assert_eq!(gas.used(), 40000);
1251 assert_eq!(gas.final_refunded(), 10000);
1252
1253 let gas = ResultGas::new(100000, 50000, 10000, 40000, 0);
1256 assert_eq!(gas.used(), 40000);
1257 assert_eq!(gas.final_refunded(), 10000);
1258 }
1259}