diff --git a/setup.py b/setup.py index 16a7bcd49b..86708b6450 100644 --- a/setup.py +++ b/setup.py @@ -126,12 +126,13 @@ # All version pins dependent on web3[tester] "eth-abi", "eth-account", + "eth-tester", "eth-typing", "eth-utils", "hexbytes", "py-geth", "trie", # Peer: stricter pin needed for uv support. - "web3[tester]>=7.0.0b9,<8", + "web3>=7.0.0b9,<8", # ** Dependencies maintained by ApeWorX ** # Missing pins are dependent on ETH-prefixed dependencies. "eip712", diff --git a/src/ape/api/providers.py b/src/ape/api/providers.py index 698567308b..c64d4637b0 100644 --- a/src/ape/api/providers.py +++ b/src/ape/api/providers.py @@ -357,6 +357,7 @@ def stream_request( # type: ignore[empty-body] An iterator of items. """ + # TODO: In 0.9, delete this method. def get_storage_at(self, *args, **kwargs) -> HexBytes: warnings.warn( "'provider.get_storage_at()' is deprecated. Use 'provider.get_storage()'.", diff --git a/src/ape/managers/chain.py b/src/ape/managers/chain.py index 8ce6bec943..c4cdc20103 100644 --- a/src/ape/managers/chain.py +++ b/src/ape/managers/chain.py @@ -821,7 +821,6 @@ def cache_proxy_info(self, address: AddressType, proxy_info: ProxyInfoAPI): proxy_info (:class:`~ape.api.networks.ProxyInfo`): The proxy info class to cache. """ - if self.get_proxy_info(address) and self._is_live_network: return @@ -860,7 +859,6 @@ def get_proxy_info(self, address: AddressType) -> Optional[ProxyInfoAPI]: Returns: Optional[:class:`~ape.api.networks.ProxyInfoAPI`] """ - return self._local_proxies.get(address) or self._get_proxy_info_from_disk(address) def get_creation_metadata(self, address: AddressType) -> Optional[ContractCreation]: diff --git a/src/ape_ethereum/ecosystem.py b/src/ape_ethereum/ecosystem.py index 063d5305f5..4687e6ee3b 100644 --- a/src/ape_ethereum/ecosystem.py +++ b/src/ape_ethereum/ecosystem.py @@ -515,7 +515,7 @@ def str_to_slot(text): # eip-897 delegate proxy, read `proxyType()` and `implementation()` # perf: only make a call when a proxyType() selector is mentioned in the code eip897_pattern = b"\x63" + keccak(text="proxyType()")[:4] - if to_hex(eip897_pattern) in code: + if eip897_pattern.hex() in code: try: proxy_type = ContractCall(PROXY_TYPE_ABI, address)(skip_trace=True) if proxy_type not in (1, 2): @@ -741,13 +741,14 @@ def decode_returndata(self, abi: MethodABI, raw_data: bytes) -> tuple[Any, ...]: def _enrich_value(self, value: Any, **kwargs) -> Any: if isinstance(value, bytes): + try: string_value = value.strip(b"\x00").decode("utf8") return f'"{string_value}"' except UnicodeDecodeError: # Truncate bytes if very long. if len(value) > 24: - return humanize_hash(cast(Hash32, value)) + return f"0x{humanize_hash(cast(Hash32, value))}" hex_str = to_hex(value) if is_hex_address(hex_str): diff --git a/src/ape_ethereum/provider.py b/src/ape_ethereum/provider.py index 450000b575..1740a69f6d 100644 --- a/src/ape_ethereum/provider.py +++ b/src/ape_ethereum/provider.py @@ -1,3 +1,4 @@ +import json import os import re import sys @@ -26,6 +27,7 @@ MethodUnavailable, TimeExhausted, TransactionNotFound, + Web3RPCError, ) from web3.gas_strategies.rpc import rpc_gas_price_strategy from web3.middleware import ExtraDataToPOAMiddleware @@ -1007,7 +1009,7 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI: if txn_hash is None: txn_hash = to_hex(self.web3.eth.send_raw_transaction(txn.serialize_transaction())) - except (ValueError, Web3ContractLogicError) as err: + except (ValueError, Web3ContractLogicError, Web3RPCError) as err: vm_err = self.get_virtual_machine_error( err, txn=txn, set_ape_traceback=txn.raise_on_revert ) @@ -1200,9 +1202,14 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa return self._handle_execution_reverted(exception, **kwargs) elif not isinstance(err_data, dict): - return VirtualMachineError(base_err=exception, **kwargs) + # Maybe it is a JSON-str. + # NOTE: For some reason, it comes back with single quotes though. + try: + err_data = json.loads(err_data.replace("'", '"')) + except Exception: + return VirtualMachineError(base_err=exception, **kwargs) - elif not (err_msg := err_data.get("message")): + if not (err_msg := err_data.get("message")): return VirtualMachineError(base_err=exception, **kwargs) elif txn is not None and "nonce too low" in str(err_msg): diff --git a/src/ape_ethereum/transactions.py b/src/ape_ethereum/transactions.py index 3d8b054875..652088c9e6 100644 --- a/src/ape_ethereum/transactions.py +++ b/src/ape_ethereum/transactions.py @@ -80,7 +80,9 @@ def serialize_transaction(self) -> bytes: for item in txn_data["accessList"]: adjusted_item = {**item} - storage_keys_corrected = [to_hex(k) for k in item.get("storageKeys", [])] + storage_keys_corrected = [ + to_hex(k) if isinstance(k, bytes) else k for k in item.get("storageKeys", []) + ] if storage_keys_corrected: adjusted_item["storageKeys"] = storage_keys_corrected @@ -253,7 +255,7 @@ def raise_for_status(self): err = OutOfGasError(txn=self) elif self.status != TransactionStatusEnum.NO_ERROR: - txn_hash = to_hex(self.txn_hash) + txn_hash = self.txn_hash err = TransactionError(f"Transaction '{txn_hash}' failed.", txn=self) self.error = err diff --git a/src/ape_node/provider.py b/src/ape_node/provider.py index 3f96eecb30..2bf802d549 100644 --- a/src/ape_node/provider.py +++ b/src/ape_node/provider.py @@ -150,7 +150,7 @@ def from_uri(cls, uri: str, data_folder: Path, **kwargs): mnemonic = kwargs.get("mnemonic", DEFAULT_TEST_MNEMONIC) number_of_accounts = kwargs.get("number_of_accounts", DEFAULT_NUMBER_OF_TEST_ACCOUNTS) balance = kwargs.get("initial_balance", DEFAULT_TEST_ACCOUNT_BALANCE) - extra_accounts = [to_hex(a).lower() for a in kwargs.get("extra_funded_accounts", [])] + extra_accounts = [a.lower() for a in kwargs.get("extra_funded_accounts", [])] return cls( data_folder, @@ -361,7 +361,7 @@ def _create_process(self) -> GethDevProcess: # Include extra accounts to allocated funds to at genesis. extra_accounts = self.settings.ethereum.local.get("extra_funded_accounts", []) extra_accounts.extend(self.provider_settings.get("extra_funded_accounts", [])) - extra_accounts = list({to_hex(a).lower() for a in extra_accounts}) + extra_accounts = list({a.lower() for a in extra_accounts}) test_config["extra_funded_accounts"] = extra_accounts test_config["initial_balance"] = self.test_config.balance diff --git a/src/ape_test/provider.py b/src/ape_test/provider.py index 75f6f5715a..4a63251764 100644 --- a/src/ape_test/provider.py +++ b/src/ape_test/provider.py @@ -380,7 +380,7 @@ def get_test_account(self, index: int) -> "TestAccountAPI": return self.account_manager.init_test_account( index, cast(AddressType, to_hex(address)), - to_hex(private_key), + str(private_key), ) def add_account(self, private_key: str): diff --git a/tests/functional/geth/test_trace.py b/tests/functional/geth/test_trace.py index d6089ffc62..d5a88594f3 100644 --- a/tests/functional/geth/test_trace.py +++ b/tests/functional/geth/test_trace.py @@ -9,7 +9,7 @@ LOCAL_TRACE = r""" Call trace for '0x([A-Fa-f0-9]{64})' tx\.origin=0x[a-fA-F0-9]{40} -ContractA\.methodWithoutArguments\(\) -> 0x[A-Fa-f0-9]{2}..[A-Fa-f0-9]{4} \[\d+ gas\] +ContractA\.methodWithoutArguments\(\) -> 0x[A-Fa-f0-9]{4}..[A-Fa-f0-9]{4} \[\d+ gas\] ├── SYMBOL\.supercluster\(x=234444\) -> \[ │ \[23523523235235, 11111111111, 234444\], │ \[ diff --git a/tests/functional/test_ecosystem.py b/tests/functional/test_ecosystem.py index 021471d642..35829feeb7 100644 --- a/tests/functional/test_ecosystem.py +++ b/tests/functional/test_ecosystem.py @@ -5,7 +5,6 @@ import pytest from eth_pydantic_types import HashBytes32, HexBytes from eth_typing import HexAddress, HexStr -from eth_utils import to_hex from ethpm_types import ContractType, ErrorABI from ethpm_types.abi import ABIType, EventABI, MethodABI from evm_trace import CallTreeNode, CallType @@ -848,7 +847,7 @@ def test_create_transaction_shared_blob(tx_kwargs, ethereum): def test_create_transaction_max_fee_per_blob_gas(kwarg_name, value, ethereum): tx = ethereum.create_transaction(**{kwarg_name: value}) assert isinstance(tx, SharedBlobTransaction) - expected = value if isinstance(value, int) else int(to_hex(value), 16) + expected = value if isinstance(value, int) else int(value, 16) assert tx.max_fee_per_blob_gas == expected @@ -1138,11 +1137,11 @@ def get_calltree(self) -> CallTreeNode: { "name": "NumberChange", "calldata": { - "b": "0x3e..404b", + "b": "0x3ee0..404b", "prevNum": 0, "dynData": '"Dynamic"', "newNum": 123, - "dynIndexed": "0x9f..a94d", + "dynIndexed": "0x9f3d..a94d", }, } ] diff --git a/tests/functional/test_provider.py b/tests/functional/test_provider.py index b9c613326c..a054df6488 100644 --- a/tests/functional/test_provider.py +++ b/tests/functional/test_provider.py @@ -332,6 +332,7 @@ def test_send_transaction_when_no_error_and_receipt_fails( mock_web3.eth.call.return_value = HexBytes("") # Execute test. + mock_transaction.serialize_transaction.return_value = HexBytes(123123123123) with pytest.raises(TransactionError): eth_tester_provider.send_transaction(mock_transaction)