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, 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, PartialEq, Eq)]
536#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
537pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
538 Transaction(TransactionError),
540 Header(InvalidHeader),
542 Database(DBError),
544 Custom(String),
548}
549
550impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
551 for EVMError<DBError, TransactionValidationErrorT>
552{
553 fn from(value: ContextError<DBError>) -> Self {
554 match value {
555 ContextError::Db(e) => Self::Database(e),
556 ContextError::Custom(e) => Self::Custom(e),
557 }
558 }
559}
560
561impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
562 fn from(value: DBError) -> Self {
563 Self::Database(value)
564 }
565}
566
567pub trait FromStringError {
569 fn from_string(value: String) -> Self;
571}
572
573impl<DB, TX> FromStringError for EVMError<DB, TX> {
574 fn from_string(value: String) -> Self {
575 Self::Custom(value)
576 }
577}
578
579impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
580 fn from(value: InvalidTransaction) -> Self {
581 Self::Transaction(TXE::from(value))
582 }
583}
584
585impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
586 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
588 where
589 F: FnOnce(DBError) -> E,
590 {
591 match self {
592 Self::Transaction(e) => EVMError::Transaction(e),
593 Self::Header(e) => EVMError::Header(e),
594 Self::Database(e) => EVMError::Database(op(e)),
595 Self::Custom(e) => EVMError::Custom(e),
596 }
597 }
598}
599
600impl<DBError, TransactionValidationErrorT> core::error::Error
601 for EVMError<DBError, TransactionValidationErrorT>
602where
603 DBError: core::error::Error + 'static,
604 TransactionValidationErrorT: core::error::Error + 'static,
605{
606 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
607 match self {
608 Self::Transaction(e) => Some(e),
609 Self::Header(e) => Some(e),
610 Self::Database(e) => Some(e),
611 Self::Custom(_) => None,
612 }
613 }
614}
615
616impl<DBError, TransactionValidationErrorT> fmt::Display
617 for EVMError<DBError, TransactionValidationErrorT>
618where
619 DBError: fmt::Display,
620 TransactionValidationErrorT: fmt::Display,
621{
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 match self {
624 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
625 Self::Header(e) => write!(f, "header validation error: {e}"),
626 Self::Database(e) => write!(f, "database error: {e}"),
627 Self::Custom(e) => f.write_str(e),
628 }
629 }
630}
631
632impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
633 for EVMError<DBError, TransactionValidationErrorT>
634{
635 fn from(value: InvalidHeader) -> Self {
636 Self::Header(value)
637 }
638}
639
640#[derive(Debug, Clone, PartialEq, Eq, Hash)]
642#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
643pub enum InvalidTransaction {
644 PriorityFeeGreaterThanMaxFee,
650 GasPriceLessThanBasefee,
652 CallerGasLimitMoreThanBlock,
654 CallGasCostMoreThanGasLimit {
660 initial_gas: u64,
662 gas_limit: u64,
664 },
665 GasFloorMoreThanGasLimit {
670 gas_floor: u64,
672 gas_limit: u64,
674 },
675 RejectCallerWithCode,
677 LackOfFundForMaxFee {
679 fee: Box<U256>,
681 balance: Box<U256>,
683 },
684 OverflowPaymentInTransaction,
686 NonceOverflowInTransaction,
688 NonceTooHigh {
690 tx: u64,
692 state: u64,
694 },
695 NonceTooLow {
697 tx: u64,
699 state: u64,
701 },
702 CreateInitCodeSizeLimit,
704 InvalidChainId,
706 MissingChainId,
708 TxGasLimitGreaterThanCap {
710 gas_limit: u64,
712 cap: u64,
714 },
715 AccessListNotSupported,
717 MaxFeePerBlobGasNotSupported,
719 BlobVersionedHashesNotSupported,
721 BlobGasPriceGreaterThanMax {
723 block_blob_gas_price: u128,
725 tx_max_fee_per_blob_gas: u128,
727 },
728 EmptyBlobs,
730 BlobCreateTransaction,
734 TooManyBlobs {
736 max: usize,
738 have: usize,
740 },
741 BlobVersionNotSupported,
743 AuthorizationListNotSupported,
745 AuthorizationListInvalidFields,
747 EmptyAuthorizationList,
749 Eip2930NotSupported,
751 Eip1559NotSupported,
753 Eip4844NotSupported,
755 Eip7702NotSupported,
757 Eip7873NotSupported,
759 Eip7873MissingTarget,
761 Str(Cow<'static, str>),
763}
764
765impl TransactionError for InvalidTransaction {}
766
767impl core::error::Error for InvalidTransaction {}
768
769impl fmt::Display for InvalidTransaction {
770 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
771 match self {
772 Self::PriorityFeeGreaterThanMaxFee => {
773 write!(f, "priority fee is greater than max fee")
774 }
775 Self::GasPriceLessThanBasefee => {
776 write!(f, "gas price is less than basefee")
777 }
778 Self::CallerGasLimitMoreThanBlock => {
779 write!(f, "caller gas limit exceeds the block gas limit")
780 }
781 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
782 write!(
783 f,
784 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
785 )
786 }
787 Self::CallGasCostMoreThanGasLimit {
788 initial_gas,
789 gas_limit,
790 } => {
791 write!(
792 f,
793 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
794 )
795 }
796 Self::GasFloorMoreThanGasLimit {
797 gas_floor,
798 gas_limit,
799 } => {
800 write!(
801 f,
802 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
803 )
804 }
805 Self::RejectCallerWithCode => {
806 write!(f, "reject transactions from senders with deployed code")
807 }
808 Self::LackOfFundForMaxFee { fee, balance } => {
809 write!(f, "lack of funds ({balance}) for max fee ({fee})")
810 }
811 Self::OverflowPaymentInTransaction => {
812 write!(f, "overflow payment in transaction")
813 }
814 Self::NonceOverflowInTransaction => {
815 write!(f, "nonce overflow in transaction")
816 }
817 Self::NonceTooHigh { tx, state } => {
818 write!(f, "nonce {tx} too high, expected {state}")
819 }
820 Self::NonceTooLow { tx, state } => {
821 write!(f, "nonce {tx} too low, expected {state}")
822 }
823 Self::CreateInitCodeSizeLimit => {
824 write!(f, "create initcode size limit")
825 }
826 Self::InvalidChainId => write!(f, "invalid chain ID"),
827 Self::MissingChainId => write!(f, "missing chain ID"),
828 Self::AccessListNotSupported => write!(f, "access list not supported"),
829 Self::MaxFeePerBlobGasNotSupported => {
830 write!(f, "max fee per blob gas not supported")
831 }
832 Self::BlobVersionedHashesNotSupported => {
833 write!(f, "blob versioned hashes not supported")
834 }
835 Self::BlobGasPriceGreaterThanMax {
836 block_blob_gas_price,
837 tx_max_fee_per_blob_gas,
838 } => {
839 write!(
840 f,
841 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
842 )
843 }
844 Self::EmptyBlobs => write!(f, "empty blobs"),
845 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
846 Self::TooManyBlobs { max, have } => {
847 write!(f, "too many blobs, have {have}, max {max}")
848 }
849 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
850 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
851 Self::AuthorizationListInvalidFields => {
852 write!(f, "authorization list tx has invalid fields")
853 }
854 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
855 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
856 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
857 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
858 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
859 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
860 Self::Eip7873MissingTarget => {
861 write!(f, "Eip7873 initcode transaction should have `to` address")
862 }
863 Self::Str(msg) => f.write_str(msg),
864 }
865 }
866}
867
868#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
870#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
871pub enum InvalidHeader {
872 PrevrandaoNotSet,
874 ExcessBlobGasNotSet,
876}
877
878impl core::error::Error for InvalidHeader {}
879
880impl fmt::Display for InvalidHeader {
881 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
882 match self {
883 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
884 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
885 }
886 }
887}
888
889#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
891#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
892pub enum SuccessReason {
893 Stop,
895 Return,
897 SelfDestruct,
899}
900
901impl fmt::Display for SuccessReason {
902 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
903 match self {
904 Self::Stop => write!(f, "Stop"),
905 Self::Return => write!(f, "Return"),
906 Self::SelfDestruct => write!(f, "SelfDestruct"),
907 }
908 }
909}
910
911#[derive(Debug, Clone, PartialEq, Eq, Hash)]
915#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
916pub enum HaltReason {
917 OutOfGas(OutOfGasError),
919 OpcodeNotFound,
921 InvalidFEOpcode,
923 InvalidJump,
925 NotActivated,
927 StackUnderflow,
929 StackOverflow,
931 OutOfOffset,
933 CreateCollision,
935 PrecompileError,
937 PrecompileErrorWithContext(String),
939 NonceOverflow,
941 CreateContractSizeLimit,
943 CreateContractStartingWithEF,
945 CreateInitCodeSizeLimit,
947
948 OverflowPayment,
951 StateChangeDuringStaticCall,
953 CallNotAllowedInsideStatic,
955 OutOfFunds,
957 CallTooDeep,
959}
960
961impl core::error::Error for HaltReason {}
962
963impl fmt::Display for HaltReason {
964 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965 match self {
966 Self::OutOfGas(err) => write!(f, "{err}"),
967 Self::OpcodeNotFound => write!(f, "opcode not found"),
968 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
969 Self::InvalidJump => write!(f, "invalid jump destination"),
970 Self::NotActivated => write!(f, "feature or opcode not activated"),
971 Self::StackUnderflow => write!(f, "stack underflow"),
972 Self::StackOverflow => write!(f, "stack overflow"),
973 Self::OutOfOffset => write!(f, "out of offset"),
974 Self::CreateCollision => write!(f, "create collision"),
975 Self::PrecompileError => write!(f, "precompile error"),
976 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
977 Self::NonceOverflow => write!(f, "nonce overflow"),
978 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
979 Self::CreateContractStartingWithEF => {
980 write!(f, "create contract starting with 0xEF")
981 }
982 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
983 Self::OverflowPayment => write!(f, "overflow payment"),
984 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
985 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
986 Self::OutOfFunds => write!(f, "out of funds"),
987 Self::CallTooDeep => write!(f, "call too deep"),
988 }
989 }
990}
991
992#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
994#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
995pub enum OutOfGasError {
996 Basic,
998 MemoryLimit,
1000 Memory,
1002 Precompile,
1004 InvalidOperand,
1007 ReentrancySentry,
1009}
1010
1011impl core::error::Error for OutOfGasError {}
1012
1013impl fmt::Display for OutOfGasError {
1014 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1015 match self {
1016 Self::Basic => write!(f, "out of gas"),
1017 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1018 Self::Memory => write!(f, "out of gas: memory expansion"),
1019 Self::Precompile => write!(f, "out of gas: precompile"),
1020 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1021 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1022 }
1023 }
1024}
1025
1026#[derive(Debug, Clone, PartialEq, Eq)]
1028#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1029pub struct TransactionIndexedError<Error> {
1030 pub error: Error,
1032 pub transaction_index: usize,
1034}
1035
1036impl<Error> TransactionIndexedError<Error> {
1037 #[must_use]
1039 pub fn new(error: Error, transaction_index: usize) -> Self {
1040 Self {
1041 error,
1042 transaction_index,
1043 }
1044 }
1045
1046 pub fn error(&self) -> &Error {
1048 &self.error
1049 }
1050
1051 #[must_use]
1053 pub fn into_error(self) -> Error {
1054 self.error
1055 }
1056}
1057
1058impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1059 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1060 write!(
1061 f,
1062 "transaction {} failed: {}",
1063 self.transaction_index, self.error
1064 )
1065 }
1066}
1067
1068impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1069 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1070 Some(&self.error)
1071 }
1072}
1073
1074impl From<&'static str> for InvalidTransaction {
1075 fn from(s: &'static str) -> Self {
1076 Self::Str(Cow::Borrowed(s))
1077 }
1078}
1079
1080impl From<String> for InvalidTransaction {
1081 fn from(s: String) -> Self {
1082 Self::Str(Cow::Owned(s))
1083 }
1084}
1085
1086#[cfg(test)]
1087mod tests {
1088 use super::*;
1089
1090 #[test]
1091 fn test_execution_result_display() {
1092 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1093 reason: SuccessReason::Return,
1094 gas: ResultGas::new(100000, 26000, 5000, 0, 0),
1095 logs: vec![Log::default(), Log::default()],
1096 output: Output::Call(Bytes::from(vec![1, 2, 3])),
1097 };
1098 assert_eq!(
1099 result.to_string(),
1100 "Success (Return): gas used: 21000, limit: 100000, spent: 26000, refunded: 5000, 2 logs, 3 bytes output"
1101 );
1102
1103 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1104 gas: ResultGas::new(100000, 100000, 0, 0, 0),
1105 logs: vec![],
1106 output: Bytes::from(vec![1, 2, 3, 4]),
1107 };
1108 assert_eq!(
1109 result.to_string(),
1110 "Revert: gas used: 100000, limit: 100000, spent: 100000, 4 bytes output"
1111 );
1112
1113 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1114 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1115 gas: ResultGas::new(1000000, 1000000, 0, 0, 0),
1116 logs: vec![],
1117 };
1118 assert_eq!(
1119 result.to_string(),
1120 "Halted: out of gas (gas used: 1000000, limit: 1000000, spent: 1000000)"
1121 );
1122 }
1123
1124 #[test]
1125 fn test_result_gas_display() {
1126 assert_eq!(
1128 ResultGas::new(100000, 21000, 0, 0, 0).to_string(),
1129 "gas used: 21000, limit: 100000, spent: 21000"
1130 );
1131 assert_eq!(
1133 ResultGas::new(100000, 50000, 10000, 0, 0).to_string(),
1134 "gas used: 40000, limit: 100000, spent: 50000, refunded: 10000"
1135 );
1136 assert_eq!(
1138 ResultGas::new(100000, 50000, 10000, 30000, 0).to_string(),
1139 "gas used: 40000, limit: 100000, spent: 50000, refunded: 10000, floor: 30000"
1140 );
1141 }
1142
1143 #[test]
1144 fn test_result_gas_used_and_remaining() {
1145 let gas = ResultGas::new(200, 100, 30, 0, 0);
1146 assert_eq!(gas.limit(), 200);
1147 assert_eq!(gas.spent(), 100);
1148 assert_eq!(gas.inner_refunded(), 30);
1149 assert_eq!(gas.used(), 70);
1150 assert_eq!(gas.remaining(), 100);
1151
1152 let gas = ResultGas::new(100, 10, 50, 0, 0);
1154 assert_eq!(gas.used(), 0);
1155 assert_eq!(gas.remaining(), 90);
1156 }
1157
1158 #[test]
1159 fn test_final_refunded_with_floor_gas() {
1160 let gas = ResultGas::new(100000, 50000, 10000, 0, 0);
1162 assert_eq!(gas.used(), 40000);
1163 assert_eq!(gas.final_refunded(), 10000);
1164
1165 let gas = ResultGas::new(100000, 50000, 10000, 45000, 0);
1168 assert_eq!(gas.used(), 45000);
1169 assert_eq!(gas.final_refunded(), 0);
1170
1171 let gas = ResultGas::new(100000, 50000, 10000, 30000, 0);
1174 assert_eq!(gas.used(), 40000);
1175 assert_eq!(gas.final_refunded(), 10000);
1176
1177 let gas = ResultGas::new(100000, 50000, 10000, 40000, 0);
1180 assert_eq!(gas.used(), 40000);
1181 assert_eq!(gas.final_refunded(), 10000);
1182 }
1183}