2626from ape .utils import cached_property
2727from ape_ethereum .provider import Web3Provider
2828from ape_test import ApeTestConfig
29- from eth_pydantic_types import HexBytes , HexBytes32
3029from eth_typing import HexStr
3130from eth_utils import add_0x_prefix , is_0x_prefixed , is_hex , to_hex
3231from pydantic import field_validator , model_validator
3332from pydantic_settings import SettingsConfigDict
3433from web3 import HTTPProvider , Web3
35- from web3 .exceptions import ContractCustomError
34+ from web3 .exceptions import ContractCustomError , ExtraDataLengthError
3635from web3 .exceptions import ContractLogicError as Web3ContractLogicError
37- from web3 .exceptions import ExtraDataLengthError
3836from web3 .gas_strategies .rpc import rpc_gas_price_strategy
3937
38+ from eth_pydantic_types import HexBytes , HexBytes32
39+ from eth_pydantic_types .utils import PadDirection
40+
4041try :
4142 from web3 .middleware import ExtraDataToPOAMiddleware # type: ignore
4243except ImportError :
6667EPHEMERAL_PORTS_END = 60999
6768DEFAULT_PORT = 8545
6869FOUNDRY_CHAIN_ID = 31337
70+ FOUNDRY_REVERT_PREFIX = (
71+ "Error: VM Exception while processing transaction: reverted with reason string"
72+ )
6973
7074
7175class FoundryForkConfig (PluginConfig ):
@@ -345,16 +349,15 @@ def connect(self):
345349 else :
346350 # The user configured a host and the anvil process was already running.
347351 logger .info (
348- f"Connecting to existing '{ self .process_name } ' "
349- f"at host '{ self ._clean_uri } '."
352+ f"Connecting to existing '{ self .process_name } ' at host '{ self ._clean_uri } '."
350353 )
351354 else :
352355 for _ in range (self .settings .process_attempts ):
353356 try :
354357 self ._start ()
355358 break
356359 except FoundryNotInstalledError :
357- # Is a sub-class of `FoundrySubprocessError` but we to still raise
360+ # Is a subclass of `FoundrySubprocessError` but we to still raise
358361 # so we don't keep retrying.
359362 raise
360363 except SubprocessError as exc :
@@ -562,14 +565,9 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa
562565 if not message :
563566 return VirtualMachineError (base_err = exception , ** kwargs )
564567
565- # Handle specific cases based on message content
566- foundry_prefix = (
567- "Error: VM Exception while processing transaction: reverted with reason string "
568- )
569-
570568 # Handle Foundry error prefix
571- if message .startswith (foundry_prefix ):
572- message = message .replace (foundry_prefix , "" ).strip ("'" )
569+ if message .startswith (FOUNDRY_REVERT_PREFIX ):
570+ message = message .replace (f" { FOUNDRY_REVERT_PREFIX } " , "" ).strip ("'" )
573571 return self ._handle_execution_reverted (exception , message , ** kwargs )
574572
575573 # Handle various cases of transaction reverts
@@ -604,6 +602,9 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa
604602 def _handle_execution_reverted ( # type: ignore[override]
605603 self , exception : Exception , revert_message : Optional [str ] = None , ** kwargs
606604 ):
605+ trace = kwargs .get ("trace" )
606+ txn = kwargs .get ("txn" )
607+
607608 # Assign default message if revert_message is invalid
608609 if revert_message == "0x" :
609610 revert_message = TransactionError .DEFAULT_MESSAGE
@@ -618,12 +619,25 @@ def _handle_execution_reverted( # type: ignore[override]
618619 enriched = self .compiler_manager .enrich_error (sub_err )
619620
620621 # Show call trace if available
621- txn = enriched .txn
622- if txn and hasattr (txn , "show_trace" ):
623- if isinstance (txn , TransactionAPI ) and txn .receipt :
624- txn .receipt .show_trace ()
622+ if trace and callable (trace ):
623+ trace = trace ()
624+
625+ if not trace and (txn := txn ):
626+ if isinstance (txn , TransactionAPI ):
627+ if txn .receipt :
628+ trace = txn .receipt .trace
629+ else :
630+ # Calls it from the provider.
631+ try :
632+ trace = txn .trace
633+ except Exception :
634+ pass
635+
625636 elif isinstance (txn , ReceiptAPI ):
626- txn .show_trace ()
637+ trace = txn .trace
638+
639+ if trace :
640+ trace .show ()
627641
628642 return enriched
629643
@@ -668,8 +682,8 @@ def set_storage(self, address: "AddressType", slot: int, value: HexBytes):
668682 "anvil_setStorageAt" ,
669683 [
670684 address ,
671- to_hex (HexBytes32 .__eth_pydantic_validate__ (slot )),
672- to_hex (HexBytes32 .__eth_pydantic_validate__ (value )),
685+ to_hex (HexBytes32 .__eth_pydantic_validate__ (slot , pad = PadDirection . LEFT )),
686+ to_hex (HexBytes32 .__eth_pydantic_validate__ (value , pad = PadDirection . LEFT )),
673687 ],
674688 )
675689
0 commit comments