diff --git a/setup.py b/setup.py index 433eb31c51..a2e0d72d06 100644 --- a/setup.py +++ b/setup.py @@ -123,20 +123,22 @@ "urllib3>=2.0.0,<3", "watchdog>=3.0,<4", # ** Dependencies maintained by Ethereum Foundation ** - "eth-abi>=5.1.0,<6", - "eth-account>=0.11.2,<0.12", - "eth-typing>=3.5.2,<4", - "eth-utils>=2.3.1,<3", - "hexbytes", # Peer + # All version pins dependent on web3[tester] + "eth-abi", + "eth-account", + "eth-typing", + "eth-utils", + "hexbytes", "py-geth>=5.0.0-beta.2,<6", - "trie>=3.0.0,<4", # Peer: stricter pin needed for uv support. + "trie>=3.0.1,<4", # Peer: stricter pin needed for uv support. "web3[tester]>=6.17.2,<7", # ** Dependencies maintained by ApeWorX ** - "eip712>=0.2.7,<0.3", - "ethpm-types>=0.6.14,<0.7", - "eth_pydantic_types>=0.1.0,<0.2", + # Missing pins are dependent on ETH-prefixed dependencies. + "eip712", + "ethpm-types", + "eth_pydantic_types", "evmchains>=0.0.10,<0.1", - "evm-trace>=0.2.0,<0.3", + "evm-trace", ], entry_points={ "console_scripts": ["ape=ape._cli:cli"], diff --git a/src/ape/api/accounts.py b/src/ape/api/accounts.py index a8ad7f4b0d..6be7b4b59c 100644 --- a/src/ape/api/accounts.py +++ b/src/ape/api/accounts.py @@ -10,6 +10,7 @@ from eth_account import Account from eth_account.messages import encode_defunct from eth_pydantic_types import HexBytes +from eth_utils import to_hex from ethpm_types import ContractType from ape.api.address import BaseAddress @@ -346,7 +347,7 @@ def check_signature( if isinstance(data, str): data = encode_defunct(text=data) elif isinstance(data, int): - data = encode_defunct(hexstr=HexBytes(data).hex()) + data = encode_defunct(hexstr=to_hex(data)) elif isinstance(data, bytes) and (len(data) != 32 or recover_using_eip191): data = encode_defunct(data) elif isinstance(data, EIP712Message): diff --git a/src/ape/api/networks.py b/src/ape/api/networks.py index 671ffd6d92..c1efd3c286 100644 --- a/src/ape/api/networks.py +++ b/src/ape/api/networks.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union from eth_account import Account as EthAccount -from eth_account._utils.legacy_transactions import ( +from eth_account._utils.signing import ( encode_transaction, serializable_unsigned_transaction_from_dict, ) 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/api/transactions.py b/src/ape/api/transactions.py index 9ee0b1db1b..6a6d2c9dcb 100644 --- a/src/ape/api/transactions.py +++ b/src/ape/api/transactions.py @@ -153,7 +153,7 @@ def trace(self) -> "TraceAPI": :class:`~ape.exceptions.APINotImplementedError`: When using a provider that does not support tracing. """ - return self.provider.get_transaction_trace(self.txn_hash.hex()) + return self.provider.get_transaction_trace(to_hex(self.txn_hash)) @abstractmethod def serialize_transaction(self) -> bytes: @@ -183,13 +183,13 @@ def __str__(self) -> str: ) else: data["data"] = ( - "0x" + bytes(data["data"][:3]).hex() + "..." + bytes(data["data"][-3:]).hex() + to_hex(bytes(data["data"][:3])) + "..." + to_hex(bytes(data["data"][-3:])) ) else: if isinstance(data["data"], str): - data["data"] = "0x" + bytes(data["data"], encoding="utf8").hex() + data["data"] = to_hex(bytes(data["data"], encoding="utf8")) else: - data["data"] = "0x" + bytes(data["data"]).hex() + data["data"] = to_hex(bytes(data["data"])) params = "\n ".join(f"{k}: {v}" for k, v in data.items()) cls_name = getattr(type(self), "__name__", TransactionAPI.__name__) return f"{cls_name}:\n {params}" diff --git a/src/ape/contracts/base.py b/src/ape/contracts/base.py index d61f4e7010..486dd89d50 100644 --- a/src/ape/contracts/base.py +++ b/src/ape/contracts/base.py @@ -216,7 +216,7 @@ def decode_input(self, calldata: bytes) -> tuple[str, dict[str, Any]]: matching_abis = [] rest_calldata = None err = ContractDataError( - f"Unable to find matching method ABI for calldata '{calldata.hex()}'. " + f"Unable to find matching method ABI for calldata '{to_hex(calldata)}'. " "Try prepending a method ID to the beginning of the calldata." ) for abi in self.abis: @@ -856,7 +856,7 @@ def decode_input(self, calldata: bytes) -> tuple[str, dict[str, Any]]: if not method: raise ContractDataError( - f"Unable to find method ABI from calldata '{calldata.hex()}'. " + f"Unable to find method ABI from calldata '{to_hex(calldata)}'. " "Try prepending the method ID to the beginning of the calldata." ) diff --git a/src/ape/exceptions.py b/src/ape/exceptions.py index b164813b43..0a1530592c 100644 --- a/src/ape/exceptions.py +++ b/src/ape/exceptions.py @@ -11,8 +11,8 @@ from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast import click -from eth_typing import Hash32 -from eth_utils import humanize_hash +from eth_typing import Hash32, HexStr +from eth_utils import humanize_hash, to_hex from ethpm_types import ContractType from ethpm_types.abi import ConstructorABI, ErrorABI, MethodABI from rich import print as rich_print @@ -520,9 +520,9 @@ class BlockNotFoundError(ProviderError): def __init__(self, block_id: "BlockID", reason: Optional[str] = None): if isinstance(block_id, bytes): - block_id_str = block_id.hex() + block_id_str = to_hex(block_id) else: - block_id_str = str(block_id) + block_id_str = HexStr(str(block_id)) message = ( "Missing latest block." 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/managers/project.py b/src/ape/managers/project.py index 0982978b7c..a327029c01 100644 --- a/src/ape/managers/project.py +++ b/src/ape/managers/project.py @@ -9,6 +9,7 @@ from typing import Any, Optional, Union, cast from eth_typing import HexStr +from eth_utils import to_hex from ethpm_types import ContractInstance as EthPMContractInstance from ethpm_types import ContractType, PackageManifest, PackageMeta, Source from ethpm_types.source import Compiler, ContractSource @@ -1975,7 +1976,7 @@ def track(self, contract: ContractInstance): f"at block_number={block_number} is unknown." ) - block_hash = block_hash_bytes.hex() + block_hash = to_hex(block_hash_bytes) contract_type_str = ( f"{contract.contract_type.source_id}:{contract_name}" if contract.contract_type.source_id @@ -1992,7 +1993,7 @@ def track(self, contract: ContractInstance): if not (block_0_hash := self.provider.get_block(0).hash): raise ProjectError("Chain missing hash for block 0 (required for BIP-122 chain ID).") - bip122_chain_id = f"{block_0_hash.hex()[2:]}" + bip122_chain_id = f"{to_hex(block_0_hash)[2:]}" deployments_folder = self.cache_folder / bip122_chain_id deployments_folder.mkdir(exist_ok=True, parents=True) destination = deployments_folder / f"{contract_name}.json" diff --git a/src/ape/pytest/fixtures.py b/src/ape/pytest/fixtures.py index 71245166cf..dd9ce8f4aa 100644 --- a/src/ape/pytest/fixtures.py +++ b/src/ape/pytest/fixtures.py @@ -4,6 +4,7 @@ from typing import Optional import pytest +from eth_utils import to_hex from ape.api.accounts import TestAccountAPI from ape.api.transactions import ReceiptAPI @@ -178,7 +179,7 @@ def capture_range(self, start_block: int, stop_block: int): for txn in transactions: try: - txn_hash = txn.txn_hash.hex() + txn_hash = to_hex(txn.txn_hash) except Exception: # Might have been from an impersonated account. # Those txns need to be added separately, same as tracing calls. diff --git a/src/ape/types/signatures.py b/src/ape/types/signatures.py index a659f02e75..cec7811ef2 100644 --- a/src/ape/types/signatures.py +++ b/src/ape/types/signatures.py @@ -48,7 +48,7 @@ def _bytes_to_human_str(bytes_value: bytes) -> Optional[str]: try: # Try as hex - return HexBytes(bytes_value).hex() + return to_hex(bytes_value) except Exception: pass diff --git a/src/ape/utils/testing.py b/src/ape/utils/testing.py index 63f9099d00..6d85efed26 100644 --- a/src/ape/utils/testing.py +++ b/src/ape/utils/testing.py @@ -3,7 +3,7 @@ from eth_account import Account from eth_account.hdaccount import HDPath from eth_account.hdaccount.mnemonic import Mnemonic -from eth_pydantic_types import HexBytes +from eth_utils import to_hex DEFAULT_NUMBER_OF_TEST_ACCOUNTS = 10 DEFAULT_TEST_MNEMONIC = "test test test test test test test test test test test junk" @@ -60,7 +60,7 @@ def generate_dev_accounts( def _generate_dev_account(hd_path, index: int, seed: bytes) -> GeneratedDevAccount: return GeneratedDevAccount( address=Account.from_key( - private_key := HexBytes(HDPath(hd_path.format(index)).derive(seed)).hex() + private_key := to_hex(HDPath(hd_path.format(index)).derive(seed)) ).address, private_key=private_key, ) diff --git a/src/ape_accounts/_cli.py b/src/ape_accounts/_cli.py index 7a640c9d3a..a0013ee179 100644 --- a/src/ape_accounts/_cli.py +++ b/src/ape_accounts/_cli.py @@ -4,7 +4,7 @@ import click from eth_account import Account as EthAccount from eth_account.hdaccount import ETHEREUM_DEFAULT_PATH -from eth_utils import to_checksum_address +from eth_utils import to_checksum_address, to_hex from ape.cli import ape_cli_context, existing_alias_argument, non_existing_alias_argument from ape.logging import HIDDEN_MESSAGE @@ -178,7 +178,7 @@ def export(cli_ctx, alias): private_key = EthAccount.decrypt(account, password) address = to_checksum_address(account["address"]) cli_ctx.logger.success( - f"Account {address} private key: {click.style(private_key.hex(), bold=True)}" + f"Account {address} private key: {click.style(to_hex(private_key), bold=True)}" ) diff --git a/src/ape_accounts/accounts.py b/src/ape_accounts/accounts.py index d4ab603534..b07ff301a3 100644 --- a/src/ape_accounts/accounts.py +++ b/src/ape_accounts/accounts.py @@ -13,7 +13,7 @@ from eth_account.signers.local import LocalAccount from eth_keys import keys # type: ignore from eth_pydantic_types import HexBytes -from eth_utils import to_bytes +from eth_utils import to_bytes, to_hex from ape.api import AccountAPI, AccountContainerAPI, TransactionAPI from ape.exceptions import AccountsError @@ -63,7 +63,7 @@ class KeyfileAccount(AccountAPI): keyfile_path: Path locked: bool = True __autosign: bool = False - __cached_key: Optional[HexBytes] = None + __cached_key: Optional[bytes] = None @log_instead_of_fail(default="") def __repr__(self) -> str: @@ -85,7 +85,7 @@ def address(self) -> AddressType: return self.network_manager.ethereum.decode_address(self.keyfile["address"]) @property - def __key(self) -> HexBytes: + def __key(self) -> bytes: if self.__cached_key is not None: if not self.locked: logger.warning("Using cached key for %s", self.alias) @@ -166,10 +166,10 @@ def sign_message(self, msg: Any, **signer_options) -> Optional[MessageSignature] elif isinstance(msg, int): display_msg = f"Signing raw integer: {msg}" - msg = encode_defunct(hexstr=HexBytes(msg).hex()) + msg = encode_defunct(hexstr=to_hex(msg)) elif isinstance(msg, bytes): - display_msg = f"Signing raw bytes: '{msg.hex()}'" + display_msg = f"Signing raw bytes: '{to_hex(msg)}'" msg = encode_defunct(primitive=msg) elif isinstance(msg, EIP712Message): @@ -187,13 +187,13 @@ def sign_message(self, msg: Any, **signer_options) -> Optional[MessageSignature] if msg._verifyingContract_: display_msg += f"\tContract: {msg._verifyingContract_}\n" if msg._salt_: - display_msg += f"\tSalt: 0x{msg._salt_.hex()}\n" + display_msg += f"\tSalt: {to_hex(msg._salt_)}\n" # Message Data display_msg += "Message\n" for field, value in msg._body_["message"].items(): if isinstance(value, bytes): - value = HexBytes(value).hex() + value = to_hex(value) display_msg += f"\t{field}: {value}\n" # Convert EIP712Message to SignableMessage for handling below @@ -283,7 +283,7 @@ def _prompt_for_passphrase(self, message: Optional[str] = None, **kwargs) -> str **kwargs, ) - def __decrypt_keyfile(self, passphrase: str) -> HexBytes: + def __decrypt_keyfile(self, passphrase: str) -> bytes: try: return EthAccount.decrypt(self.keyfile, passphrase) except ValueError as err: diff --git a/src/ape_cache/py.typed b/src/ape_cache/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ape_console/py.typed b/src/ape_console/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ape_ethereum/_print.py b/src/ape_ethereum/_print.py index 05e5e4ffdd..e2fc56bc7a 100644 --- a/src/ape_ethereum/_print.py +++ b/src/ape_ethereum/_print.py @@ -23,8 +23,8 @@ from typing import Any, cast from eth_abi import decode -from eth_typing import ChecksumAddress, HexStr -from eth_utils import add_0x_prefix, decode_hex +from eth_typing import ChecksumAddress +from eth_utils import add_0x_prefix, decode_hex, to_hex from ethpm_types import ContractType, MethodABI from evm_trace import CallTreeNode from hexbytes import HexBytes @@ -43,7 +43,7 @@ def is_console_log(call: CallTreeNode) -> TypeGuard[CallTreeNode]: """Determine if a call is a standard console.log() call""" return ( call.address == HexBytes(CONSOLE_ADDRESS) - and call.calldata[:4].hex() in console_contract.identifier_lookup + and to_hex(call.calldata[:4]) in console_contract.identifier_lookup ) @@ -82,12 +82,12 @@ def vyper_print(calldata: str) -> tuple[Any]: def extract_debug_logs(call: CallTreeNode) -> Iterable[tuple[Any]]: """Filter calls to console.log() and print() from a transactions call tree""" if is_vyper_print(call) and call.calldata is not None: - yield vyper_print(add_0x_prefix(HexStr(call.calldata[4:].hex()))) + yield vyper_print(add_0x_prefix(to_hex(call.calldata[4:]))) elif is_console_log(call) and call.calldata is not None: - method_abi = console_contract.identifier_lookup.get(call.calldata[:4].hex()) + method_abi = console_contract.identifier_lookup.get(to_hex(call.calldata[:4])) if isinstance(method_abi, MethodABI): - yield console_log(method_abi, call.calldata[4:].hex()) + yield console_log(method_abi, to_hex(call.calldata[4:])) elif call.calls is not None: for sub_call in call.calls: diff --git a/src/ape_ethereum/ecosystem.py b/src/ape_ethereum/ecosystem.py index 21cc8c0a45..62f6836b93 100644 --- a/src/ape_ethereum/ecosystem.py +++ b/src/ape_ethereum/ecosystem.py @@ -9,6 +9,7 @@ from eth_pydantic_types import HexBytes from eth_typing import Hash32, HexStr from eth_utils import ( + add_0x_prefix, encode_hex, humanize_hash, is_0x_prefixed, @@ -441,7 +442,7 @@ def encode_contract_blueprint( def get_proxy_info(self, address: AddressType) -> Optional[ProxyInfo]: contract_code = self.provider.get_code(address) if isinstance(contract_code, bytes): - contract_code = contract_code.hex() + contract_code = to_hex(contract_code) code = contract_code[2:] if not code: @@ -473,7 +474,7 @@ def get_proxy_info(self, address: AddressType) -> Optional[ProxyInfo]: return ProxyInfo(type=ProxyType.Sequence, target=target) def str_to_slot(text): - return int(keccak(text=text).hex(), 16) + return int(to_hex(keccak(text=text)), 16) slots = { ProxyType.Standard: str_to_slot("eip1967.proxy.implementation") - 1, @@ -501,7 +502,7 @@ def str_to_slot(text): # safe >=1.1.0 provides `masterCopy()`, which is also stored in slot 0 # detect safe-specific bytecode of push32 keccak256("masterCopy()") safe_pattern = b"\x7f" + keccak(text="masterCopy()")[:4] + bytes(28) - if safe_pattern.hex() in code: + if to_hex(safe_pattern) in code: try: singleton = ContractCall(MASTER_COPY_ABI, address)(skip_trace=True) slot_0 = self.provider.get_storage(address, 0) @@ -547,7 +548,7 @@ def decode_receipt(self, data: dict) -> ReceiptAPI: ) txn_hash = next((data[choice] for choice in hash_key_choices if choice in data), None) if txn_hash and isinstance(txn_hash, bytes): - txn_hash = txn_hash.hex() + txn_hash = to_hex(txn_hash) data_bytes = data.get("data") if data_bytes and isinstance(data_bytes, str): @@ -747,7 +748,7 @@ def _enrich_value(self, value: Any, **kwargs) -> Any: except UnicodeDecodeError: # Truncate bytes if very long. if len(value) > 24: - return humanize_hash(cast(Hash32, value)) + return f"{add_0x_prefix(HexStr(humanize_hash(cast(Hash32, value))))}" hex_str = to_hex(value) if is_hex_address(hex_str): @@ -1091,8 +1092,8 @@ def _enrich_calltree(self, call: dict, **kwargs) -> dict: if calldata := call.get("calldata"): calldata_bytes = HexBytes(calldata) - call["method_id"] = calldata_bytes[:4].hex() - call["calldata"] = calldata if is_create else calldata_bytes[4:].hex() + call["method_id"] = to_hex(calldata_bytes[:4]) + call["calldata"] = calldata if is_create else to_hex(calldata_bytes[4:]) else: call["method_id"] = "0x" @@ -1350,7 +1351,7 @@ def _enrich_trace_event( # The selector is always the first topic. selector = event["topics"][0] if not isinstance(selector, str): - selector = selector.hex() + selector = to_hex(selector) if selector not in contract_type.identifier_lookup: # Unable to enrich using this contract type. diff --git a/src/ape_ethereum/provider.py b/src/ape_ethereum/provider.py index 02bbd57b54..a53c1bcd9f 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 @@ -19,7 +20,8 @@ from evmchains import get_random_rpc from pydantic.dataclasses import dataclass from requests import HTTPError -from web3 import HTTPProvider, IPCProvider, Web3, WebsocketProvider +from web3 import HTTPProvider, IPCProvider, Web3 +from web3 import WebsocketProvider as WebSocketProvider from web3.exceptions import ContractLogicError as Web3ContractLogicError from web3.exceptions import ( ExtraDataLengthError, @@ -28,7 +30,7 @@ TransactionNotFound, ) from web3.gas_strategies.rpc import rpc_gas_price_strategy -from web3.middleware import geth_poa_middleware +from web3.middleware import geth_poa_middleware as ExtraDataToPOAMiddleware from web3.middleware.validation import MAX_EXTRADATA_LENGTH from web3.providers import AutoProvider from web3.providers.auto import load_provider_from_environment @@ -324,7 +326,7 @@ def estimate_gas_cost(self, txn: TransactionAPI, block_id: Optional[BlockID] = N # Force the use of hex values to support a wider range of nodes. if isinstance(txn_dict.get("type"), int): - txn_dict["type"] = HexBytes(txn_dict["type"]).hex() + txn_dict["type"] = to_hex(txn_dict["type"]) # NOTE: "auto" means to enter this method, so remove it from dict if "gas" in txn_dict and ( @@ -536,7 +538,7 @@ def _eth_call( # Force the usage of hex-type to support a wider-range of nodes. txn_dict = copy(arguments[0]) if isinstance(txn_dict.get("type"), int): - txn_dict["type"] = HexBytes(txn_dict["type"]).hex() + txn_dict["type"] = to_hex(txn_dict["type"]) # Remove unnecessary values to support a wider-range of nodes. txn_dict.pop("chainId", None) @@ -725,7 +727,7 @@ def _find_txn_by_account_and_nonce( for txn in self.get_transactions_by_block(stop_block): assert isinstance(txn.nonce, int) # NOTE: just satisfying mypy here if txn.sender == account and txn.nonce >= start_nonce: - yield self.get_receipt(txn.txn_hash.hex()) + yield self.get_receipt(to_hex(txn.txn_hash)) # Nothing else to search for @@ -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): @@ -1251,7 +1258,7 @@ def _handle_execution_reverted( else: if trace is None and txn is not None: - trace = self.provider.get_transaction_trace(txn.txn_hash.hex()) + trace = self.provider.get_transaction_trace(to_hex(txn.txn_hash)) if trace is not None and (revert_message := trace.revert_message): message = revert_message @@ -1489,8 +1496,8 @@ def _complete_connect(self): if is_likely_poa: break - if is_likely_poa and geth_poa_middleware not in self.web3.middleware_onion: - self.web3.middleware_onion.inject(geth_poa_middleware, layer=0) + if is_likely_poa and ExtraDataToPOAMiddleware not in self.web3.middleware_onion: + self.web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) self.network.verify_chain_id(chain_id) @@ -1549,7 +1556,7 @@ def _create_web3( lambda: HTTPProvider(endpoint_uri=http, request_kwargs={"timeout": 30 * 60}) ) if ws := ws_uri: - providers.append(lambda: WebsocketProvider(endpoint_uri=ws)) + providers.append(lambda: WebSocketProvider(endpoint_uri=ws)) provider = AutoProvider(potential_providers=providers) return Web3(provider) diff --git a/src/ape_ethereum/trace.py b/src/ape_ethereum/trace.py index 386fef7eca..7b251601bd 100644 --- a/src/ape_ethereum/trace.py +++ b/src/ape_ethereum/trace.py @@ -766,7 +766,7 @@ def _get_inputs_str(inputs: Any, stylize: bool = False) -> str: return _dict_to_str(inputs, color=color) elif isinstance(inputs, bytes): - return HexBytes(inputs).hex() + return to_hex(inputs) return f"({inputs})" diff --git a/src/ape_ethereum/transactions.py b/src/ape_ethereum/transactions.py index 843d4a93ae..652088c9e6 100644 --- a/src/ape_ethereum/transactions.py +++ b/src/ape_ethereum/transactions.py @@ -10,7 +10,7 @@ serializable_unsigned_transaction_from_dict, ) from eth_pydantic_types import HexBytes -from eth_utils import decode_hex, encode_hex, keccak, to_int +from eth_utils import decode_hex, encode_hex, keccak, to_hex, to_int from ethpm_types import ContractType from ethpm_types.abi import EventABI, MethodABI from pydantic import BaseModel, Field, field_validator, model_validator @@ -80,7 +80,9 @@ def serialize_transaction(self) -> bytes: for item in txn_data["accessList"]: adjusted_item = {**item} - storage_keys_corrected = [HexBytes(k).hex() 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 = HexBytes(self.txn_hash).hex() + txn_hash = self.txn_hash err = TransactionError(f"Transaction '{txn_hash}' failed.", txn=self) self.error = err @@ -383,7 +385,7 @@ def _decode_ds_note(self, log: dict) -> Optional[ContractLog]: try: method_abi = contract_type.mutable_methods[selector] except KeyError: - # selector {selector.hex()} not found in {log['address']} + # selector {to_hex(selector)} not found in {log['address']} return None # ds-note data field uses either (uint256,bytes) or (bytes) encoding diff --git a/src/ape_init/py.typed b/src/ape_init/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ape_networks/py.typed b/src/ape_networks/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ape_node/provider.py b/src/ape_node/provider.py index 1d91f745d1..857a516646 100644 --- a/src/ape_node/provider.py +++ b/src/ape_node/provider.py @@ -4,8 +4,6 @@ from subprocess import DEVNULL, PIPE, Popen from typing import Any, Optional, Union -from eth_pydantic_types import HexBytes -from eth_typing import HexStr from eth_utils import add_0x_prefix, to_hex from evmchains import get_random_rpc from geth.chain import initialize_chain @@ -15,7 +13,7 @@ from pydantic import field_validator from pydantic_settings import SettingsConfigDict from requests.exceptions import ConnectionError -from web3.middleware import geth_poa_middleware +from web3.middleware import geth_poa_middleware as ExtraDataToPOAMiddleware from yarl import URL from ape.api import PluginConfig, SubprocessProvider, TestAccountAPI, TestProviderAPI @@ -152,9 +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 = [ - HexBytes(a).hex().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, @@ -332,7 +328,7 @@ def start(self, timeout: int = 20): geth_dev.disconnect() raise ConnectionError("Unable to connect to locally running geth.") else: - self.web3.middleware_onion.inject(geth_poa_middleware, layer=0) + self.web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) self._process = geth_dev @@ -365,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({HexBytes(a).hex().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 @@ -391,10 +387,10 @@ def restore(self, snapshot_id: SnapshotID): block_number_int = snapshot_id block_number_hex_str = str(to_hex(snapshot_id)) elif isinstance(snapshot_id, bytes): - block_number_hex_str = add_0x_prefix(HexStr(snapshot_id.hex())) + block_number_hex_str = add_0x_prefix(to_hex(snapshot_id)) block_number_int = int(block_number_hex_str, 16) else: - block_number_hex_str = add_0x_prefix(HexStr(snapshot_id)) + block_number_hex_str = to_hex(snapshot_id) block_number_int = int(snapshot_id, 16) current_block = self._get_latest_block().number diff --git a/src/ape_plugins/py.typed b/src/ape_plugins/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ape_test/accounts.py b/src/ape_test/accounts.py index 833c4a62e8..690fb4f1b7 100644 --- a/src/ape_test/accounts.py +++ b/src/ape_test/accounts.py @@ -111,7 +111,7 @@ def sign_message(self, msg: Any, **signer_options) -> Optional[MessageSignature] if isinstance(msg, str): msg = encode_defunct(text=msg) elif isinstance(msg, int): - msg = HexBytes(msg).hex() + msg = to_hex(msg) msg = encode_defunct(hexstr=msg) elif isinstance(msg, EIP712Message): # Convert EIP712Message to SignableMessage for handling below diff --git a/src/ape_test/provider.py b/src/ape_test/provider.py index e7b9a2f59f..4a63251764 100644 --- a/src/ape_test/provider.py +++ b/src/ape_test/provider.py @@ -9,7 +9,7 @@ from eth_pydantic_types import HexBytes from eth_tester.backends import PyEVMBackend # type: ignore from eth_tester.exceptions import TransactionFailed # type: ignore -from eth_utils import is_0x_prefixed +from eth_utils import is_0x_prefixed, to_hex from eth_utils.exceptions import ValidationError from eth_utils.toolz import merge from web3 import EthereumTesterProvider, Web3 @@ -245,14 +245,14 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI: txn_dict = None try: txn_hash = self.tester.ethereum_tester.send_raw_transaction( - txn.serialize_transaction().hex() + to_hex(txn.serialize_transaction()) ) except (ValidationError, TransactionFailed, Web3ContractLogicError) as err: vm_err = self.get_virtual_machine_error(err, txn=txn, set_ape_traceback=False) if txn.raise_on_revert: raise vm_err from err else: - txn_hash = txn.txn_hash.hex() + txn_hash = to_hex(txn.txn_hash) required_confirmations = txn.required_confirmations or 0 if vm_err: @@ -379,8 +379,8 @@ def get_test_account(self, index: int) -> "TestAccountAPI": address = private_key.public_key.to_canonical_address() return self.account_manager.init_test_account( index, - cast(AddressType, f"0x{address.hex()}"), - private_key.to_hex(), + cast(AddressType, to_hex(address)), + str(private_key), ) def add_account(self, private_key: str): @@ -431,7 +431,7 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa if err_message and err_message.startswith("b'") and err_message.endswith("'"): # Convert stringified bytes str like `"b'\\x82\\xb4)\\x00'"` to `"0x82b42900"`. # (Notice the `b'` is within the `"` on the first str). - err_message = HexBytes(literal_eval(err_message)).hex() + err_message = to_hex(literal_eval(err_message)) err_message = TransactionError.DEFAULT_MESSAGE if err_message == "0x" else err_message contract_err = ContractLogicError( diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py index 76341e1f82..54d35a170e 100644 --- a/tests/functional/conftest.py +++ b/tests/functional/conftest.py @@ -7,6 +7,7 @@ import pytest from eth_pydantic_types import HexBytes +from eth_utils import to_hex from ethpm_types import ContractType, ErrorABI, MethodABI from ethpm_types.abi import ABIType @@ -648,7 +649,7 @@ def mock_compile(paths, project=None, settings=None): for path in paths: if path.suffix == mock.ext: name = path.stem - code = HexBytes(123).hex() + code = to_hex(123) data = { "contractName": name, "abi": mock.abi, diff --git a/tests/functional/conversion/test_hex.py b/tests/functional/conversion/test_hex.py index 2a78308fa5..c464fde189 100644 --- a/tests/functional/conversion/test_hex.py +++ b/tests/functional/conversion/test_hex.py @@ -1,5 +1,5 @@ import pytest -from eth_pydantic_types import HexBytes +from eth_utils import to_hex from ape.exceptions import ConversionError from ape.managers.converters import HexConverter, HexIntConverter @@ -8,7 +8,7 @@ @pytest.mark.parametrize("val", ("0xA100", "0x0A100", "0x00a100")) def test_hex_str(convert, val): assert convert(val, int) == 0xA100 - assert int(convert(val, bytes).hex(), 16) == int(HexBytes(0xA100).hex(), 16) + assert int(to_hex(convert(val, bytes)), 16) == int(to_hex(0xA100), 16) def test_int_str(convert): diff --git a/tests/functional/geth/test_provider.py b/tests/functional/geth/test_provider.py index a35126e76f..be7028757f 100644 --- a/tests/functional/geth/test_provider.py +++ b/tests/functional/geth/test_provider.py @@ -4,12 +4,12 @@ import pytest from eth_pydantic_types import HashBytes32 from eth_typing import HexStr -from eth_utils import keccak +from eth_utils import keccak, to_hex from evmchains import PUBLIC_CHAIN_META from hexbytes import HexBytes from web3.exceptions import ContractLogicError as Web3ContractLogicError from web3.exceptions import ExtraDataLengthError -from web3.middleware import geth_poa_middleware +from web3.middleware import geth_poa_middleware as ExtraDataToPOAMiddleware from ape.exceptions import ( APINotImplementedError, @@ -228,7 +228,7 @@ def test_connect_to_chain_that_started_poa(mock_web3, web3_factory, ethereum): provider.connect() # Verify PoA middleware was added. - assert mock_web3.middleware_onion.inject.call_args[0] == (geth_poa_middleware,) + assert mock_web3.middleware_onion.inject.call_args[0] == (ExtraDataToPOAMiddleware,) assert mock_web3.middleware_onion.inject.call_args[1] == {"layer": 0} @@ -418,7 +418,7 @@ def test_send_transaction_when_no_error_and_receipt_fails( receipt_data = { "failed": True, "blockNumber": 0, - "txnHash": tx_hash.hex(), + "txnHash": to_hex(tx_hash), "status": TransactionStatusEnum.FAILING.value, "sender": owner.address, "receiver": geth_contract.address, diff --git a/tests/functional/geth/test_trace.py b/tests/functional/geth/test_trace.py index d6089ffc62..a71a3a50bd 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]{2,}..[A-Fa-f0-9]{4} \[\d+ gas\] ├── SYMBOL\.supercluster\(x=234444\) -> \[ │ \[23523523235235, 11111111111, 234444\], │ \[ diff --git a/tests/functional/test_accounts.py b/tests/functional/test_accounts.py index 7f9ca35577..b1db310813 100644 --- a/tests/functional/test_accounts.py +++ b/tests/functional/test_accounts.py @@ -4,6 +4,7 @@ from eip712.messages import EIP712Message from eth_account.messages import encode_defunct from eth_pydantic_types import HexBytes +from eth_utils import to_hex from ethpm_types import ContractType import ape @@ -749,7 +750,7 @@ def test_load_public_key_from_keyfile(runner, keyfile_account): assert isinstance(keyfile_account.public_key, HexBytes) assert ( - keyfile_account.public_key.hex() + to_hex(keyfile_account.public_key) == "0x8318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5" # noqa: 501 ) # no need for password when loading from the keyfile diff --git a/tests/functional/test_block.py b/tests/functional/test_block.py index d0e2af88f4..9a597db0d0 100644 --- a/tests/functional/test_block.py +++ b/tests/functional/test_block.py @@ -1,5 +1,6 @@ import pytest from eth_pydantic_types import HexBytes +from eth_utils import to_hex from web3.types import BlockData from ape_ethereum.ecosystem import Block @@ -24,10 +25,10 @@ def test_block_dict(block): "difficulty": 0, "gasLimit": 30029122, "gasUsed": 0, - "hash": block.hash.hex(), + "hash": to_hex(block.hash), "num_transactions": 0, "number": 0, - "parentHash": block.parent_hash.hex(), + "parentHash": to_hex(block.parent_hash), "size": block.size, "timestamp": block.timestamp, "totalDifficulty": 0, @@ -41,9 +42,9 @@ def test_block_json(block): actual = block.model_dump_json() expected = ( '{"baseFeePerGas":1000000000,"difficulty":0,"gasLimit":30029122,"gasUsed":0,' - f'"hash":"{block.hash.hex()}",' + f'"hash":"{to_hex(block.hash)}",' '"num_transactions":0,"number":0,' - f'"parentHash":"{block.parent_hash.hex()}",' + f'"parentHash":"{to_hex(block.parent_hash)}",' f'"size":{block.size},"timestamp":{block.timestamp},' f'"totalDifficulty":0,"transactions":[],"uncles":[]}}' ) diff --git a/tests/functional/test_contract_instance.py b/tests/functional/test_contract_instance.py index 423187c125..892c077679 100644 --- a/tests/functional/test_contract_instance.py +++ b/tests/functional/test_contract_instance.py @@ -703,7 +703,7 @@ def test_decode_ambiguous_input(solidity_contract_instance, calldata_with_addres anonymous_calldata = calldata_with_address[4:] method = solidity_contract_instance.setNumber expected = ( - f"Unable to find matching method ABI for calldata '{anonymous_calldata.hex()}'. " + f"Unable to find matching method ABI for calldata '{to_hex(anonymous_calldata)}'. " "Try prepending a method ID to the beginning of the calldata." ) with pytest.raises(ContractDataError, match=expected): diff --git a/tests/functional/test_ecosystem.py b/tests/functional/test_ecosystem.py index fdaf5b2626..1aa2c1584c 100644 --- a/tests/functional/test_ecosystem.py +++ b/tests/functional/test_ecosystem.py @@ -847,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(HexBytes(value).hex(), 16) + expected = value if isinstance(value, int) else int(value, 16) assert tx.max_fee_per_blob_gas == expected diff --git a/tests/functional/test_project.py b/tests/functional/test_project.py index 30cec5cf0c..2b568430da 100644 --- a/tests/functional/test_project.py +++ b/tests/functional/test_project.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest +from eth_utils import to_hex from ethpm_types import Compiler, ContractType, PackageManifest, Source from ethpm_types.manifest import PackageName from pydantic_core import Url @@ -40,7 +41,7 @@ def manifest(contract_type): @pytest.fixture def contract_block_hash(eth_tester_provider, vyper_contract_instance): block_number = vyper_contract_instance.creation_metadata.block - return eth_tester_provider.get_block(block_number).hash.hex() + return to_hex(eth_tester_provider.get_block(block_number).hash) @pytest.fixture @@ -258,7 +259,7 @@ def test_extract_manifest(tmp_project, mock_sepolia, vyper_contract_instance): assert type(manifest) is PackageManifest assert manifest.meta == tmp_project.meta assert PackageName("manifest-dependency") in (manifest.dependencies or {}) - bip122_chain_id = tmp_project.provider.get_block(0).hash.hex() + bip122_chain_id = to_hex(tmp_project.provider.get_block(0).hash) expected_uri = f"blockchain://{bip122_chain_id[2:]}" for key in manifest.deployments or {}: if key.startswith(expected_uri): @@ -905,7 +906,7 @@ def test_instance_map(self, project, vyper_contract_instance, mock_sepolia): project.deployments.track(vyper_contract_instance) assert project.deployments.instance_map != {} - bip122_chain_id = project.provider.get_block(0).hash.hex() + bip122_chain_id = to_hex(project.provider.get_block(0).hash) expected_uri = f"blockchain://{bip122_chain_id[2:]}/block/" for key in project.deployments.instance_map.keys(): if key.startswith(expected_uri): diff --git a/tests/functional/test_provider.py b/tests/functional/test_provider.py index b1f4428c57..a054df6488 100644 --- a/tests/functional/test_provider.py +++ b/tests/functional/test_provider.py @@ -6,7 +6,7 @@ from eth_pydantic_types import HashBytes32 from eth_tester.exceptions import TransactionFailed # type: ignore from eth_typing import HexStr -from eth_utils import ValidationError +from eth_utils import ValidationError, to_hex from hexbytes import HexBytes from requests import HTTPError from web3.exceptions import ContractPanicError @@ -53,7 +53,7 @@ def test_get_block_transaction(vyper_contract_instance, owner, eth_tester_provid # Ensure a transaction in latest block receipt = vyper_contract_instance.setNumber(900, sender=owner) block = eth_tester_provider.get_block(receipt.block_number) - assert block.transactions[-1].txn_hash.hex() == receipt.txn_hash + assert to_hex(block.transactions[-1].txn_hash) == receipt.txn_hash def test_estimate_gas(vyper_contract_instance, eth_tester_provider, owner): @@ -318,7 +318,7 @@ def test_send_transaction_when_no_error_and_receipt_fails( receipt_data = { "failed": True, "blockNumber": 0, - "txnHash": tx_hash.hex(), + "txnHash": to_hex(tx_hash), "status": TransactionStatusEnum.FAILING.value, "sender": owner.address, "receiver": vyper_contract_instance.address, @@ -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) diff --git a/tests/functional/test_transaction.py b/tests/functional/test_transaction.py index 65e70a8e45..b255ff0a5f 100644 --- a/tests/functional/test_transaction.py +++ b/tests/functional/test_transaction.py @@ -4,6 +4,7 @@ import pytest from eth_pydantic_types import HexBytes +from eth_utils import to_hex from hexbytes import HexBytes as BaseHexBytes from ape.api import TransactionAPI @@ -224,7 +225,7 @@ def test_txn_hash_and_receipt(owner, eth_tester_provider, ethereum, kwargs): txn = owner.prepare_transaction(txn) txn = owner.sign_transaction(txn) assert txn - actual = txn.txn_hash.hex() + actual = to_hex(txn.txn_hash) receipt = eth_tester_provider.send_transaction(txn) # Show that we can access the receipt from the transaction. @@ -253,7 +254,7 @@ def test_txn_hash_when_access_list_is_raw(ethereum, owner): # Ignore the Pydantic warning from access-list being the wrong type. with warnings.catch_warnings(): warnings.simplefilter("ignore") - actual = txn.txn_hash.hex() + actual = to_hex(txn.txn_hash) assert actual.startswith("0x")