22import random
33import shutil
44from bisect import bisect_right
5- from copy import copy
65from subprocess import PIPE , call
76from typing import Literal , Optional , Union , cast
87
4140from web3 .gas_strategies .rpc import rpc_gas_price_strategy
4241from web3 .middleware import geth_poa_middleware
4342from web3 .middleware .validation import MAX_EXTRADATA_LENGTH
44- from web3 .types import TxParams
4543from yarl import URL
4644
4745from ape_foundry .constants import EVM_VERSION_BY_NETWORK
@@ -489,20 +487,21 @@ def build_command(self) -> list[str]:
489487
490488 def set_balance (self , account : AddressType , amount : Union [int , float , str , bytes ]):
491489 is_str = isinstance (amount , str )
492- _is_hex = False if not is_str else is_0x_prefixed (str (amount ))
493- is_key_word = is_str and len (str (amount ).split (" " )) > 1
490+ is_key_word = is_str and " " in amount # type: ignore
491+ _is_hex = is_str and not is_key_word and amount .startswith ("0x" ) # type: ignore
492+
494493 if is_key_word :
495494 # This allows values such as "1000 ETH".
496495 amount = self .conversion_manager .convert (amount , int )
497496 is_str = False
498497
499- amount_hex_str = str (amount )
500-
501- # Convert to hex str
498+ amount_hex_str : str
502499 if is_str and not _is_hex :
503500 amount_hex_str = to_hex (int (amount ))
504- elif isinstance (amount , int ) or isinstance ( amount , bytes ):
501+ elif isinstance (amount , ( int , bytes ) ):
505502 amount_hex_str = to_hex (amount )
503+ else :
504+ amount_hex_str = str (amount )
506505
507506 self .make_request ("anvil_setBalance" , [account , amount_hex_str ])
508507
@@ -518,9 +517,7 @@ def snapshot(self) -> str:
518517 return self .make_request ("evm_snapshot" , [])
519518
520519 def restore (self , snapshot_id : SnapshotID ) -> bool :
521- if isinstance (snapshot_id , int ):
522- snapshot_id = HexBytes (snapshot_id ).hex ()
523-
520+ snapshot_id = to_hex (snapshot_id ) if isinstance (snapshot_id , int ) else snapshot_id
524521 result = self .make_request ("evm_revert" , [snapshot_id ])
525522 return result is True
526523
@@ -530,103 +527,12 @@ def unlock_account(self, address: AddressType) -> bool:
530527
531528 def relock_account (self , address : AddressType ):
532529 self .make_request ("anvil_stopImpersonatingAccount" , [address ])
533- if address in self .account_manager .test_accounts ._impersonated_accounts :
534- del self .account_manager .test_accounts ._impersonated_accounts [address ]
535-
536- def send_transaction (self , txn : TransactionAPI ) -> ReceiptAPI :
537- """
538- Creates a new message call transaction or a contract creation
539- for signed transactions.
540- """
541- sender = txn .sender
542- if sender :
543- sender = self .conversion_manager .convert (txn .sender , AddressType )
544-
545- vm_err = None
546- if sender and sender in self .unlocked_accounts :
547- # Allow for an unsigned transaction
548- txn = self .prepare_transaction (txn )
549- txn_dict = txn .model_dump (mode = "json" , by_alias = True )
550- if isinstance (txn_dict .get ("type" ), int ):
551- txn_dict ["type" ] = HexBytes (txn_dict ["type" ]).hex ()
552-
553- tx_params = cast (TxParams , txn_dict )
554- try :
555- txn_hash = self .web3 .eth .send_transaction (tx_params )
556- except ValueError as err :
557- raise self .get_virtual_machine_error (err , txn = txn ) from err
558-
559- else :
560- try :
561- txn_hash = self .web3 .eth .send_raw_transaction (txn .serialize_transaction ())
562- except ValueError as err :
563- vm_err = self .get_virtual_machine_error (err , txn = txn )
564-
565- if "nonce too low" in str (vm_err ):
566- # Add additional nonce information
567- new_err_msg = f"Nonce '{ txn .nonce } ' is too low"
568- vm_err = VirtualMachineError (
569- new_err_msg ,
570- base_err = vm_err .base_err ,
571- code = vm_err .code ,
572- txn = txn ,
573- source_traceback = vm_err .source_traceback ,
574- trace = vm_err .trace ,
575- contract_address = vm_err .contract_address ,
576- )
577-
578- txn_hash = txn .txn_hash
579- if txn .raise_on_revert :
580- raise vm_err from err
581-
582- receipt = self .get_receipt (
583- txn_hash .hex (),
584- required_confirmations = (
585- txn .required_confirmations
586- if txn .required_confirmations is not None
587- else self .network .required_confirmations
588- ),
589- )
590- if vm_err :
591- receipt .error = vm_err
592-
593- if receipt .failed :
594- txn_dict = receipt .transaction .model_dump (mode = "json" , by_alias = True )
595- if isinstance (txn_dict .get ("type" ), int ):
596- txn_dict ["type" ] = HexBytes (txn_dict ["type" ]).hex ()
597-
598- txn_params = cast (TxParams , txn_dict )
599-
600- # Replay txn to get revert reason
601- # NOTE: For some reason, `nonce` can't be in the txn params or else it fails.
602- if "nonce" in txn_params :
603- del txn_params ["nonce" ]
604-
605- try :
606- self .web3 .eth .call (txn_params )
607- except Exception as err :
608- vm_err = self .get_virtual_machine_error (err , txn = receipt )
609- receipt .error = vm_err
610- if txn .raise_on_revert :
611- raise vm_err from err
612-
613- if txn .raise_on_revert :
614- # If we get here, for some reason the tx-replay did not produce
615- # a VM error.
616- receipt .raise_for_status ()
617-
618- self .chain_manager .history .append (receipt )
619- return receipt
620530
621531 def get_balance (self , address : AddressType , block_id : Optional [BlockID ] = None ) -> int :
622- if hasattr ( address , " address" ):
623- address = address . address
532+ if result := self . make_request ( "eth_getBalance" , [ address , block_id ] ):
533+ return int ( result , 16 ) if isinstance ( result , str ) else result
624534
625- result = self .make_request ("eth_getBalance" , [address , block_id ])
626- if not result :
627- raise FoundryProviderError (f"Failed to get balance for account '{ address } '." )
628-
629- return int (result , 16 ) if isinstance (result , str ) else result
535+ raise FoundryProviderError (f"Failed to get balance for account '{ address } '." )
630536
631537 def get_transaction_trace (self , transaction_hash : str , ** kwargs ) -> TraceAPI :
632538 if "debug_trace_transaction_parameters" not in kwargs :
@@ -673,7 +579,12 @@ def _handle_execution_reverted(
673579 # Unlikely scenario where a transaction is on the error even though a receipt
674580 # exists.
675581 if isinstance (enriched .txn , TransactionAPI ) and enriched .txn .receipt :
676- enriched .txn .receipt .show_trace ()
582+ try :
583+ enriched .txn .receipt .show_trace ()
584+ except Exception :
585+ # TODO: Fix this in Ape
586+ pass
587+
677588 elif isinstance (enriched .txn , ReceiptAPI ):
678589 enriched .txn .show_trace ()
679590
@@ -732,8 +643,12 @@ def _extract_custom_error(self, **kwargs) -> str:
732643 except Exception :
733644 pass
734645
735- if trace is not None and (revert_msg := trace .revert_message ):
736- return revert_msg
646+ try :
647+ if trace is not None and (revert_msg := trace .revert_message ):
648+ return revert_msg
649+ except Exception :
650+ # TODO: Fix this in core
651+ return ""
737652
738653 return ""
739654
@@ -763,16 +678,6 @@ def set_storage(self, address: AddressType, slot: int, value: HexBytes):
763678 ],
764679 )
765680
766- def _eth_call (self , arguments : list , raise_on_revert : bool = True ) -> HexBytes :
767- # Overridden to handle unique Foundry pickiness.
768- txn_dict = copy (arguments [0 ])
769- if isinstance (txn_dict .get ("type" ), int ):
770- txn_dict ["type" ] = HexBytes (txn_dict ["type" ]).hex ()
771-
772- txn_dict .pop ("chainId" , None )
773- arguments [0 ] = txn_dict
774- return super ()._eth_call (arguments , raise_on_revert = raise_on_revert )
775-
776681
777682class FoundryForkProvider (FoundryProvider ):
778683 """
0 commit comments