diff --git a/ens/async_ens.py b/ens/async_ens.py index a6e89d7560..9f34788eb1 100644 --- a/ens/async_ens.py +++ b/ens/async_ens.py @@ -64,16 +64,16 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 from web3.contract import ( # noqa: F401 AsyncContract, ) + from web3.main import AsyncWeb3 # noqa: F401 from web3.providers import ( # noqa: F401 AsyncBaseProvider, BaseProvider, ) from web3.types import ( # noqa: F401 - Middleware, + AsyncMiddleware, TxParams, ) @@ -88,11 +88,14 @@ class AsyncENS(BaseENS): like: ``"0x314159265dD8dbb310642f98f50C066173C1259b"`` """ + # mypy types + w3: "AsyncWeb3" + def __init__( self, provider: "AsyncBaseProvider" = cast("AsyncBaseProvider", default), addr: ChecksumAddress = None, - middlewares: Optional[Sequence[Tuple["Middleware", str]]] = None, + middlewares: Optional[Sequence[Tuple["AsyncMiddleware", str]]] = None, ) -> None: """ :param provider: a single provider used to connect to Ethereum @@ -110,7 +113,7 @@ def __init__( ) @classmethod - def from_web3(cls, w3: "Web3", addr: ChecksumAddress = None) -> "AsyncENS": + def from_web3(cls, w3: "AsyncWeb3", addr: ChecksumAddress = None) -> "AsyncENS": """ Generate an AsyncENS instance with web3 @@ -120,10 +123,15 @@ def from_web3(cls, w3: "Web3", addr: ChecksumAddress = None) -> "AsyncENS": """ provider = w3.manager.provider middlewares = w3.middleware_onion.middlewares - return cls( + ns = cls( cast("AsyncBaseProvider", provider), addr=addr, middlewares=middlewares ) + # inherit strict bytes checking from w3 instance + ns.strict_bytes_type_checking = w3.strict_bytes_type_checking + + return ns + async def address(self, name: str) -> Optional[ChecksumAddress]: """ Look up the Ethereum address that `name` currently points to. @@ -179,7 +187,7 @@ async def setup_address( transact["from"] = owner resolver: "AsyncContract" = await self._set_resolver(name, transact=transact) - return await resolver.functions.setAddr( # type: ignore + return await resolver.functions.setAddr( raw_name_to_hash(name), address ).transact(transact) @@ -394,9 +402,9 @@ async def set_text( r = await self.resolver(normal_name) if r: if await _async_resolver_supports_interface(r, GET_TEXT_INTERFACE_ID): - return await r.functions.setText( # type: ignore - node, key, value - ).transact(transaction_dict) + return await r.functions.setText(node, key, value).transact( + transaction_dict + ) else: raise UnsupportedFunction( f"Resolver for name `{name}` does not support `text` function" @@ -494,7 +502,7 @@ async def _assert_control( name: str, parent_owned: Optional[str] = None, ) -> None: - if not address_in(account, await self.w3.eth.accounts): # type: ignore + if not address_in(account, await self.w3.eth.accounts): raise UnauthorizedError( f"in order to modify {name!r}, you must control account" f" {account!r}, which owns {parent_owned or name!r}" @@ -548,7 +556,7 @@ async def _setup_reverse( transact = deepcopy(transact) transact["from"] = address reverse_registrar = await self._reverse_registrar() - return await reverse_registrar.functions.setName(name).transact(transact) # type: ignore # noqa: E501 + return await reverse_registrar.functions.setName(name).transact(transact) async def _reverse_registrar(self) -> "AsyncContract": addr = await self.ens.caller.owner( diff --git a/ens/base_ens.py b/ens/base_ens.py index c4013a629c..40cc0db707 100644 --- a/ens/base_ens.py +++ b/ens/base_ens.py @@ -25,7 +25,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) from web3.contract import ( # noqa: F401 AsyncContract, Contract, @@ -33,7 +36,7 @@ class BaseENS: - w3: "Web3" = None + w3: Union["AsyncWeb3", "Web3"] = None ens: Union["Contract", "AsyncContract"] = None _resolver_contract: Union[Type["Contract"], Type["AsyncContract"]] = None _reverse_resolver_contract: Union[Type["Contract"], Type["AsyncContract"]] = None diff --git a/ens/ens.py b/ens/ens.py index 6fd2ff3e83..7ae4414125 100644 --- a/ens/ens.py +++ b/ens/ens.py @@ -87,6 +87,9 @@ class ENS(BaseENS): like: ``"0x314159265dD8dbb310642f98f50C066173C1259b"`` """ + # mypy types + w3: "Web3" + def __init__( self, provider: "BaseProvider" = cast("BaseProvider", default), diff --git a/ens/utils.py b/ens/utils.py index 97e642de63..fa6bad395e 100644 --- a/ens/utils.py +++ b/ens/utils.py @@ -55,6 +55,7 @@ if TYPE_CHECKING: from web3 import ( # noqa: F401 + AsyncWeb3, Web3 as _Web3, ) from web3.providers import ( # noqa: F401 @@ -63,6 +64,7 @@ ) from web3.types import ( # noqa: F401 ABIFunction, + AsyncMiddleware, Middleware, RPCEndpoint, ) @@ -288,10 +290,10 @@ def get_abi_output_types(abi: "ABIFunction") -> List[str]: def init_async_web3( provider: "AsyncBaseProvider" = cast("AsyncBaseProvider", default), - middlewares: Optional[Sequence[Tuple["Middleware", str]]] = (), -) -> "_Web3": + middlewares: Optional[Sequence[Tuple["AsyncMiddleware", str]]] = (), +) -> "AsyncWeb3": from web3 import ( - Web3 as Web3Main, + AsyncWeb3 as AsyncWeb3Main, ) from web3.eth import ( AsyncEth as AsyncEthMain, @@ -306,11 +308,11 @@ def init_async_web3( middlewares.append((_async_ens_stalecheck_middleware, "stalecheck")) if provider is default: - async_w3 = Web3Main( + async_w3 = AsyncWeb3Main( middlewares=middlewares, ens=None, modules={"eth": (AsyncEthMain)} ) else: - async_w3 = Web3Main( + async_w3 = AsyncWeb3Main( provider, middlewares=middlewares, ens=None, @@ -321,7 +323,7 @@ def init_async_web3( async def _async_ens_stalecheck_middleware( - make_request: Callable[["RPCEndpoint", Any], Any], w3: "_Web3" + make_request: Callable[["RPCEndpoint", Any], Any], w3: "AsyncWeb3" ) -> "Middleware": from web3.middleware import ( async_make_stalecheck_middleware, diff --git a/ethpm/package.py b/ethpm/package.py index 0131c90a03..2ce3fc54c8 100644 --- a/ethpm/package.py +++ b/ethpm/package.py @@ -309,9 +309,7 @@ def get_contract_instance(self, name: ContractName, address: Address) -> Contrac self.manifest["contractTypes"][name] ) contract_instance = self.w3.eth.contract(address=address, **contract_kwargs) - # TODO: type ignore may be able to be removed after - # more of AsyncContract is finished - return contract_instance # type: ignore + return contract_instance # # Build Dependencies diff --git a/newsfragments/2819.breaking.rst b/newsfragments/2819.breaking.rst new file mode 100644 index 0000000000..97d2090d03 --- /dev/null +++ b/newsfragments/2819.breaking.rst @@ -0,0 +1 @@ +Use ``AsyncWeb3`` class and preserve typing for the async api calls. \ No newline at end of file diff --git a/tests/core/contracts/test_contract_example.py b/tests/core/contracts/test_contract_example.py index 973197b86a..7ed27390e9 100644 --- a/tests/core/contracts/test_contract_example.py +++ b/tests/core/contracts/test_contract_example.py @@ -83,9 +83,7 @@ def test_initial_greeting(foo_contract): def test_can_update_greeting(w3, foo_contract): # send transaction that updates the greeting - tx_hash = foo_contract.functions.setBar( - "testing contracts is easy", - ).transact( + tx_hash = foo_contract.functions.setBar("testing contracts is easy").transact( { "from": w3.eth.accounts[1], } @@ -99,9 +97,7 @@ def test_can_update_greeting(w3, foo_contract): def test_updating_greeting_emits_event(w3, foo_contract): # send transaction that updates the greeting - tx_hash = foo_contract.functions.setBar( - "testing contracts is easy", - ).transact( + tx_hash = foo_contract.functions.setBar("testing contracts is easy").transact( { "from": w3.eth.accounts[1], } diff --git a/tests/core/middleware/test_eth_tester_middleware.py b/tests/core/middleware/test_eth_tester_middleware.py index 508be3a21c..6797a51fe6 100644 --- a/tests/core/middleware/test_eth_tester_middleware.py +++ b/tests/core/middleware/test_eth_tester_middleware.py @@ -26,12 +26,14 @@ def test_get_transaction_count_formatters(w3, block_number): def test_get_block_formatters(w3): - latest_block = w3.eth.get_block("latest") + all_block_keys = BlockData.__annotations__.keys() + all_non_poa_block_keys = set( + [k for k in all_block_keys if k != "proofOfAuthorityData"] + ) - all_block_keys = set(BlockData.__annotations__.keys()) + latest_block = w3.eth.get_block("latest") latest_block_keys = set(latest_block.keys()) - - assert all_block_keys == latest_block_keys + assert all_non_poa_block_keys == latest_block_keys @pytest.mark.parametrize( diff --git a/tests/core/middleware/test_simple_cache_middleware.py b/tests/core/middleware/test_simple_cache_middleware.py index 60b6a13c46..b9212bee20 100644 --- a/tests/core/middleware/test_simple_cache_middleware.py +++ b/tests/core/middleware/test_simple_cache_middleware.py @@ -4,6 +4,7 @@ import uuid from web3 import ( + AsyncWeb3, Web3, ) from web3._utils.caching import ( @@ -166,7 +167,7 @@ async def _async_simple_cache_middleware_for_testing(make_request, async_w3): @pytest.fixture def async_w3(): - return Web3( + return AsyncWeb3( provider=AsyncEthereumTesterProvider(), middlewares=[ (_async_simple_cache_middleware_for_testing, "simple_cache"), diff --git a/tests/core/providers/test_async_http_provider.py b/tests/core/providers/test_async_http_provider.py index 8a0843cef2..b5aeed096c 100644 --- a/tests/core/providers/test_async_http_provider.py +++ b/tests/core/providers/test_async_http_provider.py @@ -5,7 +5,7 @@ ) from web3 import ( - Web3, + AsyncWeb3, ) from web3._utils import ( request, @@ -14,10 +14,10 @@ AsyncEth, ) from web3.geth import ( + AsyncGeth, AsyncGethAdmin, AsyncGethPersonal, AsyncGethTxPool, - Geth, ) from web3.middleware import ( async_attrdict_middleware, @@ -37,25 +37,25 @@ def test_no_args(): provider = AsyncHTTPProvider() - w3 = Web3(provider) + w3 = AsyncWeb3(provider) assert w3.manager.provider == provider assert w3.manager.provider.is_async def test_init_kwargs(): provider = AsyncHTTPProvider(endpoint_uri=URI, request_kwargs={"timeout": 60}) - w3 = Web3(provider) + w3 = AsyncWeb3(provider) assert w3.manager.provider == provider def test_web3_with_async_http_provider_has_default_middlewares_and_modules() -> None: - async_w3 = Web3(AsyncHTTPProvider(endpoint_uri=URI)) + async_w3 = AsyncWeb3(AsyncHTTPProvider(endpoint_uri=URI)) # assert default modules assert isinstance(async_w3.eth, AsyncEth) assert isinstance(async_w3.net, AsyncNet) - assert isinstance(async_w3.geth, Geth) + assert isinstance(async_w3.geth, AsyncGeth) assert isinstance(async_w3.geth.admin, AsyncGethAdmin) assert isinstance(async_w3.geth.personal, AsyncGethPersonal) assert isinstance(async_w3.geth.txpool, AsyncGethTxPool) diff --git a/tests/ens/conftest.py b/tests/ens/conftest.py index fcb0af83a4..c532560109 100644 --- a/tests/ens/conftest.py +++ b/tests/ens/conftest.py @@ -38,15 +38,13 @@ simple_resolver_bytecode_runtime, ) from web3 import ( + AsyncWeb3, Web3, ) from web3.contract import ( AsyncContract, Contract, ) -from web3.eth import ( - AsyncEth, -) from web3.providers.eth_tester import ( AsyncEthereumTesterProvider, EthereumTesterProvider, @@ -359,9 +357,7 @@ def TEST_ADDRESS(address_conversion_func): @pytest_asyncio.fixture(scope="session") def async_w3(): provider = AsyncEthereumTesterProvider() - _async_w3 = Web3( - provider, modules={"eth": [AsyncEth]}, middlewares=provider.middlewares - ) + _async_w3 = AsyncWeb3(provider, middlewares=provider.middlewares) return _async_w3 diff --git a/tests/integration/go_ethereum/test_goethereum_http.py b/tests/integration/go_ethereum/test_goethereum_http.py index 556362f672..7723cb5140 100644 --- a/tests/integration/go_ethereum/test_goethereum_http.py +++ b/tests/integration/go_ethereum/test_goethereum_http.py @@ -6,6 +6,7 @@ get_open_port, ) from web3 import ( + AsyncWeb3, Web3, ) from web3._utils.module_testing.go_ethereum_admin_module import ( @@ -121,7 +122,7 @@ class TestGoEthereumTxPoolModuleTest(GoEthereumTxPoolModuleTest): @pytest_asyncio.fixture(scope="module") async def async_w3(geth_process, endpoint_uri): await wait_for_aiohttp(endpoint_uri) - _w3 = Web3(AsyncHTTPProvider(endpoint_uri)) + _w3 = AsyncWeb3(AsyncHTTPProvider(endpoint_uri)) return _w3 @@ -130,11 +131,11 @@ class TestGoEthereumAsyncAdminModuleTest(GoEthereumAsyncAdminModuleTest): @pytest.mark.xfail( reason="running geth with the --nodiscover flag doesn't allow peer addition" ) - async def test_admin_peers(self, async_w3: "Web3") -> None: + async def test_admin_peers(self, async_w3: "AsyncWeb3") -> None: await super().test_admin_peers(async_w3) @pytest.mark.asyncio - async def test_admin_start_stop_http(self, async_w3: "Web3") -> None: + async def test_admin_start_stop_http(self, async_w3: "AsyncWeb3") -> None: # This test causes all tests after it to fail on CI if it's allowed to run pytest.xfail( reason="Only one HTTP endpoint is allowed to be active at any time" @@ -142,7 +143,7 @@ async def test_admin_start_stop_http(self, async_w3: "Web3") -> None: await super().test_admin_start_stop_http(async_w3) @pytest.mark.asyncio - async def test_admin_start_stop_ws(self, async_w3: "Web3") -> None: + async def test_admin_start_stop_ws(self, async_w3: "AsyncWeb3") -> None: # This test causes all tests after it to fail on CI if it's allowed to run pytest.xfail(reason="Only one WS endpoint is allowed to be active at any time") await super().test_admin_start_stop_ws(async_w3) diff --git a/web3/__init__.py b/web3/__init__.py index d2397b6eb5..05190b3731 100644 --- a/web3/__init__.py +++ b/web3/__init__.py @@ -1,7 +1,10 @@ from eth_account import Account # noqa: E402, import pkg_resources -from web3.main import Web3 # noqa: E402, +from web3.main import ( + AsyncWeb3, + Web3, +) from web3.providers.async_rpc import ( # noqa: E402 AsyncHTTPProvider, ) @@ -22,6 +25,7 @@ __all__ = [ "__version__", + "AsyncWeb3", "Web3", "HTTPProvider", "IPCProvider", diff --git a/web3/_utils/admin.py b/web3/_utils/admin.py deleted file mode 100644 index c55fcf291b..0000000000 --- a/web3/_utils/admin.py +++ /dev/null @@ -1,93 +0,0 @@ -from typing import ( - Callable, - List, - Tuple, -) - -from web3._utils.compat import ( - Protocol, -) -from web3._utils.rpc_abi import ( - RPC, -) -from web3.method import ( - Method, - default_root_munger, -) -from web3.module import ( - Module, -) -from web3.types import ( - EnodeURI, - NodeInfo, - Peer, -) - - -def admin_start_params_munger( - _module: Module, - host: str = "localhost", - port: int = 8546, - cors: str = "", - apis: str = "eth,net,web3", -) -> Tuple[str, int, str, str]: - return (host, port, cors, apis) - - -add_peer: Method[Callable[[EnodeURI], bool]] = Method( - RPC.admin_addPeer, - mungers=[default_root_munger], -) - - -datadir: Method[Callable[[], str]] = Method( - RPC.admin_datadir, - is_property=True, -) - - -node_info: Method[Callable[[], NodeInfo]] = Method( - RPC.admin_nodeInfo, - is_property=True, -) - - -peers: Method[Callable[[], List[Peer]]] = Method( - RPC.admin_peers, - is_property=True, -) - - -class ServerConnection(Protocol): - def __call__( - self, - host: str = "localhost", - port: int = 8546, - cors: str = "", - apis: str = "eth,net,web3", - ) -> bool: - pass - - -start_http: Method[ServerConnection] = Method( - RPC.admin_startHTTP, - mungers=[admin_start_params_munger], -) - - -start_ws: Method[ServerConnection] = Method( - RPC.admin_startWS, - mungers=[admin_start_params_munger], -) - - -stop_http: Method[Callable[[], bool]] = Method( - RPC.admin_stopHTTP, - is_property=True, -) - - -stop_ws: Method[Callable[[], bool]] = Method( - RPC.admin_stopWS, - is_property=True, -) diff --git a/web3/_utils/async_transactions.py b/web3/_utils/async_transactions.py index abaa315045..eeecb4b8d0 100644 --- a/web3/_utils/async_transactions.py +++ b/web3/_utils/async_transactions.py @@ -1,7 +1,6 @@ from typing import ( TYPE_CHECKING, Any, - Awaitable, Dict, Optional, cast, @@ -43,25 +42,28 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 from web3.eth import AsyncEth # noqa: F401 + from web3.main import ( # noqa: F401 + AsyncWeb3, + ) -async def _estimate_gas(w3: "Web3", tx: TxParams) -> Awaitable[int]: - return await w3.eth.estimate_gas(tx) # type: ignore +async def _estimate_gas(async_w3: "AsyncWeb3", tx: TxParams) -> int: + return await async_w3.eth.estimate_gas(tx) -async def _max_fee_per_gas(w3: "Web3", _tx: TxParams) -> Awaitable[Wei]: - block = await w3.eth.get_block("latest") # type: ignore - return await w3.eth.max_priority_fee + (2 * block["baseFeePerGas"]) # type: ignore +async def _max_fee_per_gas(async_w3: "AsyncWeb3", _tx: TxParams) -> Wei: + block = await async_w3.eth.get_block("latest") + max_priority_fee = await async_w3.eth.max_priority_fee + return Wei(max_priority_fee + (2 * block["baseFeePerGas"])) -async def _max_priority_fee_gas(w3: "Web3", _tx: TxParams) -> Awaitable[Wei]: - return await w3.eth.max_priority_fee # type: ignore +async def _max_priority_fee_gas(async_w3: "AsyncWeb3", _tx: TxParams) -> Wei: + return await async_w3.eth.max_priority_fee -async def _chain_id(w3: "Web3", _tx: TxParams) -> Awaitable[int]: - return await w3.eth.chain_id # type: ignore +async def _chain_id(async_w3: "AsyncWeb3", _tx: TxParams) -> int: + return await async_w3.eth.chain_id TRANSACTION_DEFAULTS = { @@ -85,13 +87,13 @@ async def get_block_gas_limit( async def get_buffered_gas_estimate( - w3: "Web3", transaction: TxParams, gas_buffer: int = 100000 + async_w3: "AsyncWeb3", transaction: TxParams, gas_buffer: int = 100000 ) -> int: gas_estimate_transaction = cast(TxParams, dict(**transaction)) - gas_estimate = await w3.eth.estimate_gas(gas_estimate_transaction) # type: ignore + gas_estimate = await async_w3.eth.estimate_gas(gas_estimate_transaction) - gas_limit = await get_block_gas_limit(w3.eth) # type: ignore + gas_limit = await get_block_gas_limit(async_w3.eth) if gas_estimate > gas_limit: raise ValueError( @@ -104,11 +106,13 @@ async def get_buffered_gas_estimate( @curry -async def fill_transaction_defaults(w3: "Web3", transaction: TxParams) -> TxParams: +async def fill_transaction_defaults( + async_w3: "AsyncWeb3", transaction: TxParams +) -> TxParams: """ if w3 is None, fill as much as possible while offline """ - strategy_based_gas_price = w3.eth.generate_gas_price(transaction) + strategy_based_gas_price = async_w3.eth.generate_gas_price(transaction) is_dynamic_fee_transaction = strategy_based_gas_price is None and ( "gasPrice" not in transaction # default to dynamic fee transaction @@ -129,16 +133,16 @@ async def fill_transaction_defaults(w3: "Web3", transaction: TxParams) -> TxPara continue if callable(default_getter): - if w3 is None: + if async_w3 is None: raise ValueError( f"You must specify a '{key}' value in the transaction" ) if key == "gasPrice": # `generate_gas_price()` is on the `BaseEth` class and does not # need to be awaited - default_val = default_getter(w3, transaction) + default_val = default_getter(async_w3, transaction) else: - default_val = await default_getter(w3, transaction) + default_val = await default_getter(async_w3, transaction) else: default_val = default_getter diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index 2d1b7a2b48..93a49af1ea 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -88,7 +88,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) def extract_argument_types(*args: Sequence[Any]) -> str: @@ -206,7 +209,7 @@ def find_matching_fn_abi( def encode_abi( - w3: "Web3", + w3: Union["AsyncWeb3", "Web3"], abi: ABIFunction, arguments: Sequence[Any], data: Optional[HexStr] = None, @@ -244,7 +247,7 @@ def encode_abi( def prepare_transaction( address: ChecksumAddress, - w3: "Web3", + w3: Union["AsyncWeb3", "Web3"], fn_identifier: Union[str, Type[FallbackFn], Type[ReceiveFn]], contract_abi: Optional[ABI] = None, fn_abi: Optional[ABIFunction] = None, @@ -288,7 +291,7 @@ def prepare_transaction( def encode_transaction_data( - w3: "Web3", + w3: Union["AsyncWeb3", "Web3"], fn_identifier: Union[str, Type[FallbackFn], Type[ReceiveFn]], contract_abi: Optional[ABI] = None, fn_abi: Optional[ABIFunction] = None, @@ -435,32 +438,49 @@ def parse_block_identifier_int(w3: "Web3", block_identifier_int: int) -> BlockNu return BlockNumber(block_num) -async def async_parse_block_identifier( - w3: "Web3", block_identifier: BlockIdentifier +def parse_block_identifier_no_extra_call( + w3: Union["Web3", "AsyncWeb3"], block_identifier: BlockIdentifier ) -> BlockIdentifier: if block_identifier is None: return w3.eth.default_block + elif isinstance(block_identifier, int) and block_identifier >= 0: + return block_identifier + elif block_identifier in ["latest", "earliest", "pending", "safe", "finalized"]: + return block_identifier + elif isinstance(block_identifier, bytes): + return HexBytes(block_identifier) + elif is_hex_encoded_block_hash(block_identifier): + return HexStr(str(block_identifier)) + else: + raise BlockNumberOutofRange + + +async def async_parse_block_identifier( + async_w3: "AsyncWeb3", block_identifier: BlockIdentifier +) -> BlockIdentifier: + if block_identifier is None: + return async_w3.eth.default_block if isinstance(block_identifier, int): - return await async_parse_block_identifier_int(w3, block_identifier) + return await async_parse_block_identifier_int(async_w3, block_identifier) elif block_identifier in ["latest", "earliest", "pending", "safe", "finalized"]: return block_identifier elif isinstance(block_identifier, bytes) or is_hex_encoded_block_hash( block_identifier ): - requested_block = await w3.eth.get_block(block_identifier) # type: ignore + requested_block = await async_w3.eth.get_block(block_identifier) return requested_block["number"] else: raise BlockNumberOutofRange async def async_parse_block_identifier_int( - w3: "Web3", block_identifier_int: int + async_w3: "AsyncWeb3", block_identifier_int: int ) -> BlockNumber: if block_identifier_int >= 0: block_num = block_identifier_int else: - last_block = await w3.eth.get_block("latest") # type: ignore - last_block_num = last_block.number + last_block = await async_w3.eth.get_block("latest") + last_block_num = last_block["number"] block_num = last_block_num + block_identifier_int + 1 if block_num < 0: raise BlockNumberOutofRange diff --git a/web3/_utils/ens.py b/web3/_utils/ens.py index 5289027540..41f0e3ba6e 100644 --- a/web3/_utils/ens.py +++ b/web3/_utils/ens.py @@ -6,6 +6,7 @@ Any, Dict, Iterator, + Union, cast, ) @@ -20,13 +21,17 @@ from ens import ( ENS, + AsyncENS, ) from web3.exceptions import ( NameNotFound, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) from web3.contract import ( # noqa: F401 Contract, ) @@ -61,10 +66,13 @@ def address(self, name: str) -> ChecksumAddress: @contextmanager def ens_addresses( - w3: "Web3", name_addr_pairs: Dict[str, ChecksumAddress] + w3: Union["Web3", "AsyncWeb3"], name_addr_pairs: Dict[str, ChecksumAddress] ) -> Iterator[None]: original_ens = w3.ens - w3.ens = cast(ENS, StaticENS(name_addr_pairs)) + if w3.provider.is_async: + w3.ens = cast(AsyncENS, StaticENS(name_addr_pairs)) + else: + w3.ens = cast(ENS, StaticENS(name_addr_pairs)) yield w3.ens = original_ens diff --git a/web3/_utils/events.py b/web3/_utils/events.py index f63360cd5c..1eee6b5ae9 100644 --- a/web3/_utils/events.py +++ b/web3/_utils/events.py @@ -89,7 +89,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) from web3._utils.filters import ( # noqa: F401 AsyncLogFilter, LogFilter, @@ -443,7 +446,7 @@ def deploy(self, w3: "Web3") -> "LogFilter": class AsyncEventFilterBuilder(BaseEventFilterBuilder): - async def deploy(self, async_w3: "Web3") -> "AsyncLogFilter": + async def deploy(self, async_w3: "AsyncWeb3") -> "AsyncLogFilter": if not isinstance(async_w3, web3.Web3): raise ValueError(f"Invalid web3 argument: got: {async_w3!r}") @@ -451,7 +454,7 @@ async def deploy(self, async_w3: "Web3") -> "AsyncLogFilter": arg._immutable = True self._immutable = True - log_filter = await async_w3.eth.filter(self.filter_params) # type: ignore + log_filter = await async_w3.eth.filter(self.filter_params) log_filter.filter_params = self.filter_params log_filter.set_data_filters(self.data_argument_values) log_filter.builder = self diff --git a/web3/_utils/module.py b/web3/_utils/module.py index baf5eda057..cc7a592116 100644 --- a/web3/_utils/module.py +++ b/web3/_utils/module.py @@ -20,7 +20,7 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3.main import BaseWeb3 # noqa: F401 def _validate_init_params_and_return_if_found(module_class: Any) -> List[str]: @@ -40,9 +40,9 @@ def _validate_init_params_and_return_if_found(module_class: Any) -> List[str]: def attach_modules( - parent_module: Union["Web3", "Module"], + parent_module: Union["BaseWeb3", "Module"], module_definitions: Dict[str, Any], - w3: Optional[Union["Web3", "Module"]] = None, + w3: Optional[Union["BaseWeb3", "Module"]] = None, ) -> None: for module_name, module_info in module_definitions.items(): module_info_is_list_like = isinstance(module_info, Sequence) @@ -60,10 +60,11 @@ def attach_modules( # due to circular import issues. if w3 is None: from web3 import ( + AsyncWeb3, Web3, ) - if isinstance(parent_module, Web3): + if isinstance(parent_module, Web3) or isinstance(parent_module, AsyncWeb3): w3 = parent_module module_init_params = _validate_init_params_and_return_if_found(module_class) diff --git a/web3/_utils/module_testing/eth_module.py b/web3/_utils/module_testing/eth_module.py index 87fd6883df..b03a92d385 100644 --- a/web3/_utils/module_testing/eth_module.py +++ b/web3/_utils/module_testing/eth_module.py @@ -82,6 +82,7 @@ Nonce, RPCEndpoint, SyncStatus, + TxData, TxParams, Wei, ) @@ -101,34 +102,38 @@ if TYPE_CHECKING: from _pytest.monkeypatch import MonkeyPatch # noqa: F401 - from web3 import Web3 # noqa: F401 from web3.contract import Contract # noqa: F401 + from web3.main import ( # noqa: F401 + AsyncWeb3, + Web3, + ) class AsyncEthModuleTest: @pytest.mark.asyncio - async def test_eth_gas_price(self, async_w3: "Web3") -> None: - gas_price = await async_w3.eth.gas_price # type: ignore + async def test_eth_gas_price(self, async_w3: "AsyncWeb3") -> None: + gas_price = await async_w3.eth.gas_price + assert gas_price > 0 @pytest.mark.asyncio - async def test_is_connected(self, async_w3: "Web3") -> None: - is_connected = await async_w3.is_connected() # type: ignore + async def test_is_connected(self, async_w3: "AsyncWeb3") -> None: + is_connected = await async_w3.is_connected() assert is_connected is True @pytest.mark.asyncio async def test_eth_send_transaction_legacy( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, "to": unlocked_account_dual_type, "value": Wei(1), "gas": 21000, - "gasPrice": await async_w3.eth.gas_price, # type: ignore + "gasPrice": await async_w3.eth.gas_price, } - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert is_same_address(txn["from"], cast(ChecksumAddress, txn_params["from"])) assert is_same_address(txn["to"], cast(ChecksumAddress, txn_params["to"])) @@ -138,7 +143,7 @@ async def test_eth_send_transaction_legacy( @pytest.mark.asyncio async def test_eth_send_transaction( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -148,8 +153,8 @@ async def test_eth_send_transaction( "maxFeePerGas": async_w3.to_wei(3, "gwei"), "maxPriorityFeePerGas": async_w3.to_wei(1, "gwei"), } - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert is_same_address(txn["from"], cast(ChecksumAddress, txn_params["from"])) assert is_same_address(txn["to"], cast(ChecksumAddress, txn_params["to"])) @@ -161,7 +166,7 @@ async def test_eth_send_transaction( @pytest.mark.asyncio async def test_eth_send_transaction_default_fees( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -169,8 +174,8 @@ async def test_eth_send_transaction_default_fees( "value": Wei(1), "gas": 21000, } - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert is_same_address(txn["from"], cast(ChecksumAddress, txn_params["from"])) assert is_same_address(txn["to"], cast(ChecksumAddress, txn_params["to"])) @@ -182,7 +187,7 @@ async def test_eth_send_transaction_default_fees( @pytest.mark.asyncio async def test_eth_send_transaction_hex_fees( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -192,8 +197,8 @@ async def test_eth_send_transaction_hex_fees( "maxFeePerGas": hex(250 * 10**9), "maxPriorityFeePerGas": hex(2 * 10**9), } - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert is_same_address(txn["from"], cast(ChecksumAddress, txn_params["from"])) assert is_same_address(txn["to"], cast(ChecksumAddress, txn_params["to"])) @@ -204,7 +209,7 @@ async def test_eth_send_transaction_hex_fees( @pytest.mark.asyncio async def test_eth_send_transaction_no_gas( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -213,8 +218,8 @@ async def test_eth_send_transaction_no_gas( "maxFeePerGas": Wei(250 * 10**9), "maxPriorityFeePerGas": Wei(2 * 10**9), } - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert is_same_address(txn["from"], cast(ChecksumAddress, txn_params["from"])) assert is_same_address(txn["to"], cast(ChecksumAddress, txn_params["to"])) @@ -223,7 +228,7 @@ async def test_eth_send_transaction_no_gas( @pytest.mark.asyncio async def test_eth_send_transaction_with_gas_price( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -235,11 +240,11 @@ async def test_eth_send_transaction_with_gas_price( "maxPriorityFeePerGas": Wei(2 * 10**9), } with pytest.raises(TransactionTypeMismatch): - await async_w3.eth.send_transaction(txn_params) # type: ignore + await async_w3.eth.send_transaction(txn_params) @pytest.mark.asyncio async def test_eth_send_transaction_no_priority_fee( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -251,11 +256,11 @@ async def test_eth_send_transaction_no_priority_fee( with pytest.raises( InvalidTransaction, match="maxPriorityFeePerGas must be defined" ): - await async_w3.eth.send_transaction(txn_params) # type: ignore + await async_w3.eth.send_transaction(txn_params) @pytest.mark.asyncio async def test_eth_send_transaction_no_max_fee( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: maxPriorityFeePerGas = async_w3.to_wei(2, "gwei") txn_params: TxParams = { @@ -265,20 +270,20 @@ async def test_eth_send_transaction_no_max_fee( "gas": 21000, "maxPriorityFeePerGas": maxPriorityFeePerGas, } - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert is_same_address(txn["from"], cast(ChecksumAddress, txn_params["from"])) assert is_same_address(txn["to"], cast(ChecksumAddress, txn_params["to"])) assert txn["value"] == 1 assert txn["gas"] == 21000 - block = await async_w3.eth.get_block("latest") # type: ignore + block = await async_w3.eth.get_block("latest") assert txn["maxFeePerGas"] == maxPriorityFeePerGas + 2 * block["baseFeePerGas"] @pytest.mark.asyncio async def test_eth_send_transaction_max_fee_less_than_tip( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -291,14 +296,14 @@ async def test_eth_send_transaction_max_fee_less_than_tip( with pytest.raises( InvalidTransaction, match="maxFeePerGas must be >= maxPriorityFeePerGas" ): - await async_w3.eth.send_transaction(txn_params) # type: ignore + await async_w3.eth.send_transaction(txn_params) @pytest.mark.asyncio async def test_validation_middleware_chain_id_mismatch( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: wrong_chain_id = 1234567890 - actual_chain_id = await async_w3.eth.chain_id # type: ignore + actual_chain_id = await async_w3.eth.chain_id txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -314,10 +319,10 @@ async def test_validation_middleware_chain_id_mismatch( match=f"The transaction declared chain ID {wrong_chain_id}, " f"but the connected node is on {actual_chain_id}", ): - await async_w3.eth.send_transaction(txn_params) # type: ignore + await async_w3.eth.send_transaction(txn_params) @pytest.mark.asyncio - async def test_geth_poa_middleware(self, async_w3: "Web3") -> None: + async def test_geth_poa_middleware(self, async_w3: "AsyncWeb3") -> None: return_block_with_long_extra_data = ( await async_construct_result_generator_middleware( { @@ -331,16 +336,16 @@ async def test_geth_poa_middleware(self, async_w3: "Web3") -> None: async_w3.middleware_onion.inject( return_block_with_long_extra_data, "extradata", layer=0 ) - block = await async_w3.eth.get_block("latest") # type: ignore + block = await async_w3.eth.get_block("latest") assert "extraData" not in block - assert block.proofOfAuthorityData == b"\xff" * 33 + assert block["proofOfAuthorityData"] == b"\xff" * 33 # clean up async_w3.middleware_onion.remove("poa") async_w3.middleware_onion.remove("extradata") @pytest.mark.asyncio - async def test_eth_send_raw_transaction(self, async_w3: "Web3") -> None: + async def test_eth_send_raw_transaction(self, async_w3: "AsyncWeb3") -> None: # private key 0x3c2ab4e8f17a7dea191b8c991522660126d681039509dc3bb31af7c9bdb63518 # This is an unfunded account, but the transaction has a 0 gas price, so is # valid. It never needs to be mined, we just want the transaction hash back @@ -353,12 +358,12 @@ async def test_eth_send_raw_transaction(self, async_w3: "Web3") -> None: expected_hash = HexStr( "0x52b0ff9cb472f25872fa8ec6a62fa59454fc2ae7901cfcc6cc89d096f49b8fc1" ) - txn_hash = await async_w3.eth.send_raw_transaction(raw_txn) # type: ignore + txn_hash = await async_w3.eth.send_raw_transaction(raw_txn) assert txn_hash == async_w3.to_bytes(hexstr=expected_hash) @pytest.mark.asyncio async def test_gas_price_strategy_middleware( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_params: TxParams = { "from": unlocked_account_dual_type, @@ -373,8 +378,8 @@ def gas_price_strategy(w3: "Web3", txn: TxParams) -> Wei: async_w3.eth.set_gas_price_strategy(gas_price_strategy) - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) assert txn["gasPrice"] == two_gwei_in_wei async_w3.eth.set_gas_price_strategy(None) # reset strategy @@ -385,7 +390,7 @@ def gas_price_strategy(w3: "Web3", txn: TxParams) -> Wei: ) async def test_gas_price_from_strategy_bypassed_for_dynamic_fee_txn( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress, max_fee: Wei, ) -> None: @@ -405,10 +410,10 @@ def gas_price_strategy(w3: "Web3", txn: TxParams) -> Wei: async_w3.eth.set_gas_price_strategy(gas_price_strategy) - txn_hash = await async_w3.eth.send_transaction(txn_params) # type: ignore - txn = await async_w3.eth.get_transaction(txn_hash) # type: ignore + txn_hash = await async_w3.eth.send_transaction(txn_params) + txn = await async_w3.eth.get_transaction(txn_hash) - latest_block = await async_w3.eth.get_block("latest") # type: ignore + latest_block = await async_w3.eth.get_block("latest") assert ( txn["maxFeePerGas"] == max_fee if max_fee is not None @@ -422,7 +427,7 @@ def gas_price_strategy(w3: "Web3", txn: TxParams) -> Wei: @pytest.mark.asyncio async def test_gas_price_from_strategy_bypassed_for_dynamic_fee_txn_no_tip( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress, ) -> None: txn_params: TxParams = { @@ -441,16 +446,16 @@ def gas_price_strategy(_w3: "Web3", _txn: TxParams) -> Wei: with pytest.raises( InvalidTransaction, match="maxPriorityFeePerGas must be defined" ): - await async_w3.eth.send_transaction(txn_params) # type: ignore + await async_w3.eth.send_transaction(txn_params) async_w3.eth.set_gas_price_strategy(None) # reset strategy @pytest.mark.asyncio async def test_eth_estimate_gas( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: gas_estimate = await async_w3.eth.estimate_gas( - { # type: ignore + { "from": unlocked_account_dual_type, "to": unlocked_account_dual_type, "value": Wei(1), @@ -460,8 +465,8 @@ async def test_eth_estimate_gas( assert gas_estimate > 0 @pytest.mark.asyncio - async def test_eth_fee_history(self, async_w3: "Web3") -> None: - fee_history = await async_w3.eth.fee_history(1, "latest", [50]) # type: ignore + async def test_eth_fee_history(self, async_w3: "AsyncWeb3") -> None: + fee_history = await async_w3.eth.fee_history(1, "latest", [50]) assert is_list_like(fee_history["baseFeePerGas"]) assert is_list_like(fee_history["gasUsedRatio"]) assert is_integer(fee_history["oldestBlock"]) @@ -471,11 +476,9 @@ async def test_eth_fee_history(self, async_w3: "Web3") -> None: @pytest.mark.asyncio async def test_eth_fee_history_with_integer( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - fee_history = await async_w3.eth.fee_history( # type: ignore - 1, empty_block["number"], [50] - ) + fee_history = await async_w3.eth.fee_history(1, empty_block["number"], [50]) assert is_list_like(fee_history["baseFeePerGas"]) assert is_list_like(fee_history["gasUsedRatio"]) assert is_integer(fee_history["oldestBlock"]) @@ -485,22 +488,22 @@ async def test_eth_fee_history_with_integer( @pytest.mark.asyncio async def test_eth_fee_history_no_reward_percentiles( - self, async_w3: "Web3" + self, async_w3: "AsyncWeb3" ) -> None: - fee_history = await async_w3.eth.fee_history(1, "latest") # type: ignore + fee_history = await async_w3.eth.fee_history(1, "latest") assert is_list_like(fee_history["baseFeePerGas"]) assert is_list_like(fee_history["gasUsedRatio"]) assert is_integer(fee_history["oldestBlock"]) assert fee_history["oldestBlock"] >= 0 @pytest.mark.asyncio - async def test_eth_max_priority_fee(self, async_w3: "Web3") -> None: - max_priority_fee = await async_w3.eth.max_priority_fee # type: ignore + async def test_eth_max_priority_fee(self, async_w3: "AsyncWeb3") -> None: + max_priority_fee = await async_w3.eth.max_priority_fee assert is_integer(max_priority_fee) @pytest.mark.asyncio async def test_eth_max_priority_fee_with_fee_history_calculation( - self, async_w3: "Web3" + self, async_w3: "AsyncWeb3" ) -> None: fail_max_prio_middleware = await async_construct_error_generator_middleware( {RPCEndpoint("eth_maxPriorityFeePerGas"): lambda *_: ""} @@ -514,144 +517,142 @@ async def test_eth_max_priority_fee_with_fee_history_calculation( match="There was an issue with the method eth_maxPriorityFeePerGas. " "Calculating using eth_feeHistory.", ): - max_priority_fee = await async_w3.eth.max_priority_fee # type: ignore + max_priority_fee = await async_w3.eth.max_priority_fee assert is_integer(max_priority_fee) async_w3.middleware_onion.remove("fail_max_prio_middleware") # clean up @pytest.mark.asyncio async def test_eth_getBlockByHash( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - block = await async_w3.eth.get_block(empty_block["hash"]) # type: ignore + block = await async_w3.eth.get_block(empty_block["hash"]) assert block["hash"] == empty_block["hash"] @pytest.mark.asyncio - async def test_eth_getBlockByHash_not_found(self, async_w3: "Web3") -> None: + async def test_eth_getBlockByHash_not_found(self, async_w3: "AsyncWeb3") -> None: with pytest.raises(BlockNotFound): - await async_w3.eth.get_block(UNKNOWN_HASH) # type: ignore + await async_w3.eth.get_block(UNKNOWN_HASH) @pytest.mark.asyncio - async def test_eth_getBlockByHash_pending(self, async_w3: "Web3") -> None: - block = await async_w3.eth.get_block("pending") # type: ignore + async def test_eth_getBlockByHash_pending(self, async_w3: "AsyncWeb3") -> None: + block = await async_w3.eth.get_block("pending") assert block["hash"] is None @pytest.mark.asyncio async def test_eth_getBlockByNumber_with_integer( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - block = await async_w3.eth.get_block(empty_block["number"]) # type: ignore + block = await async_w3.eth.get_block(empty_block["number"]) assert block["number"] == empty_block["number"] @pytest.mark.asyncio async def test_eth_getBlockByNumber_latest( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - current_block_number = await async_w3.eth.block_number # type: ignore - block = await async_w3.eth.get_block("latest") # type: ignore + current_block_number = await async_w3.eth.block_number + block = await async_w3.eth.get_block("latest") assert block["number"] == current_block_number @pytest.mark.asyncio async def test_eth_getBlockByNumber_not_found( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: with pytest.raises(BlockNotFound): - await async_w3.eth.get_block(BlockNumber(12345)) # type: ignore + await async_w3.eth.get_block(BlockNumber(12345)) @pytest.mark.asyncio async def test_eth_getBlockByNumber_pending( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - current_block_number = await async_w3.eth.block_number # type: ignore - block = await async_w3.eth.get_block("pending") # type: ignore + current_block_number = await async_w3.eth.block_number + block = await async_w3.eth.get_block("pending") assert block["number"] == current_block_number + 1 @pytest.mark.asyncio async def test_eth_getBlockByNumber_earliest( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - genesis_block = await async_w3.eth.get_block(BlockNumber(0)) # type: ignore - block = await async_w3.eth.get_block("earliest") # type: ignore + genesis_block = await async_w3.eth.get_block(BlockNumber(0)) + block = await async_w3.eth.get_block("earliest") assert block["number"] == 0 assert block["hash"] == genesis_block["hash"] @pytest.mark.asyncio @pytest.mark.xfail(reason="Integration test suite not yet set up for PoS") async def test_eth_getBlockByNumber_safe( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - block = await async_w3.eth.get_block("safe") # type: ignore + block = await async_w3.eth.get_block("safe") assert block is not None assert isinstance(block["number"], int) @pytest.mark.asyncio @pytest.mark.xfail(reason="Integration test suite not yet set up for PoS") async def test_eth_getBlockByNumber_finalized( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - block = await async_w3.eth.get_block("finalized") # type: ignore + block = await async_w3.eth.get_block("finalized") assert block is not None assert isinstance(block["number"], int) @pytest.mark.asyncio - async def test_eth_getBlockByNumber_full_transactions( - self, async_w3: "Web3", block_with_txn: BlockData + async def test_eth_get_block_by_number_full_transactions( + self, async_w3: "AsyncWeb3", block_with_txn: BlockData ) -> None: - block = await async_w3.eth.get_block( # type: ignore - block_with_txn["number"], True - ) - transaction = block["transactions"][0] + block = await async_w3.eth.get_block(block_with_txn["number"], True) + transaction = cast(TxData, block["transactions"][0]) assert transaction["hash"] == block_with_txn["transactions"][0] @pytest.mark.asyncio async def test_eth_get_raw_transaction( - self, async_w3: "Web3", mined_txn_hash: HexStr + self, async_w3: "AsyncWeb3", mined_txn_hash: HexStr ) -> None: - raw_transaction = await async_w3.eth.get_raw_transaction( # type: ignore - mined_txn_hash - ) + raw_transaction = await async_w3.eth.get_raw_transaction(mined_txn_hash) assert is_bytes(raw_transaction) @pytest.mark.asyncio - async def test_eth_get_raw_transaction_raises_error(self, async_w3: "Web3") -> None: + async def test_eth_get_raw_transaction_raises_error( + self, async_w3: "AsyncWeb3" + ) -> None: with pytest.raises( TransactionNotFound, match=f"Transaction with hash: '{UNKNOWN_HASH}'" ): - await async_w3.eth.get_raw_transaction(UNKNOWN_HASH) # type: ignore + await async_w3.eth.get_raw_transaction(UNKNOWN_HASH) @pytest.mark.asyncio async def test_eth_get_raw_transaction_by_block( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", block_with_txn: BlockData, unlocked_account_dual_type: ChecksumAddress, ) -> None: # eth_getRawTransactionByBlockNumberAndIndex: block identifier # send a txn to make sure pending block has at least one txn - await async_w3.eth.send_transaction( # type: ignore + await async_w3.eth.send_transaction( { "from": unlocked_account_dual_type, "to": unlocked_account_dual_type, "value": Wei(1), } ) - pending_block = await async_w3.eth.get_block("pending") # type: ignore + pending_block = await async_w3.eth.get_block("pending") last_pending_txn_index = len(pending_block["transactions"]) - 1 - raw_txn = await async_w3.eth.get_raw_transaction_by_block( # type: ignore + raw_txn = await async_w3.eth.get_raw_transaction_by_block( "pending", last_pending_txn_index ) assert is_bytes(raw_txn) # eth_getRawTransactionByBlockNumberAndIndex: block number block_with_txn_number = block_with_txn["number"] - raw_transaction = await async_w3.eth.get_raw_transaction_by_block( # type: ignore # noqa: E501 + raw_transaction = await async_w3.eth.get_raw_transaction_by_block( block_with_txn_number, 0 ) assert is_bytes(raw_transaction) # eth_getRawTransactionByBlockHashAndIndex: block hash block_with_txn_hash = block_with_txn["hash"] - raw_transaction = await async_w3.eth.get_raw_transaction_by_block( # type: ignore # noqa: E501 + raw_transaction = await async_w3.eth.get_raw_transaction_by_block( block_with_txn_hash, 0 ) assert is_bytes(raw_transaction) @@ -659,7 +660,7 @@ async def test_eth_get_raw_transaction_by_block( @pytest.mark.asyncio @pytest.mark.parametrize("unknown_block_num_or_hash", (1234567899999, UNKNOWN_HASH)) async def test_eth_get_raw_transaction_by_block_raises_error( - self, async_w3: "Web3", unknown_block_num_or_hash: Union[int, HexBytes] + self, async_w3: "AsyncWeb3", unknown_block_num_or_hash: Union[int, HexBytes] ) -> None: with pytest.raises( TransactionNotFound, @@ -669,13 +670,13 @@ async def test_eth_get_raw_transaction_by_block_raises_error( f"not found." ), ): - await async_w3.eth.get_raw_transaction_by_block( # type: ignore + await async_w3.eth.get_raw_transaction_by_block( unknown_block_num_or_hash, 0 ) @pytest.mark.asyncio async def test_eth_get_raw_transaction_by_block_raises_error_block_identifier( - self, async_w3: "Web3" + self, async_w3: "AsyncWeb3" ) -> None: unknown_identifier = "unknown" with pytest.raises( @@ -690,80 +691,82 @@ async def test_eth_get_raw_transaction_by_block_raises_error_block_identifier( ) @pytest.mark.asyncio - async def test_eth_get_balance(self, async_w3: "Web3") -> None: - coinbase = await async_w3.eth.coinbase # type: ignore + async def test_eth_get_balance(self, async_w3: "AsyncWeb3") -> None: + coinbase = await async_w3.eth.coinbase with pytest.raises(InvalidAddress): - await async_w3.eth.get_balance( # type: ignore + await async_w3.eth.get_balance( ChecksumAddress(HexAddress(HexStr(coinbase.lower()))) ) - balance = await async_w3.eth.get_balance(coinbase) # type: ignore + balance = await async_w3.eth.get_balance(coinbase) assert is_integer(balance) assert balance >= 0 @pytest.mark.asyncio async def test_eth_get_code( - self, async_w3: "Web3", math_contract_address: ChecksumAddress + self, async_w3: "AsyncWeb3", math_contract_address: ChecksumAddress ) -> None: - code = await async_w3.eth.get_code(math_contract_address) # type: ignore + code = await async_w3.eth.get_code(math_contract_address) assert isinstance(code, HexBytes) assert len(code) > 0 @pytest.mark.asyncio async def test_eth_get_code_invalid_address( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", math_contract: "Contract", ) -> None: with pytest.raises(InvalidAddress): - await async_w3.eth.get_code( # type: ignore + await async_w3.eth.get_code( ChecksumAddress(HexAddress(HexStr(math_contract.address.lower()))) ) @pytest.mark.asyncio async def test_eth_get_code_with_block_identifier( - self, async_w3: "Web3", emitter_contract: "Contract" + self, async_w3: "AsyncWeb3", emitter_contract: "Contract" ) -> None: - block_id = await async_w3.eth.block_number # type: ignore - code = await async_w3.eth.get_code( # type: ignore - emitter_contract.address, block_id - ) + block_id = await async_w3.eth.block_number + code = await async_w3.eth.get_code(emitter_contract.address, block_id) assert isinstance(code, HexBytes) assert len(code) > 0 @pytest.mark.asyncio async def test_eth_get_transaction_count( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: - transaction_count = await async_w3.eth.get_transaction_count(unlocked_account_dual_type) # type: ignore # noqa E501 + transaction_count = await async_w3.eth.get_transaction_count( + unlocked_account_dual_type + ) assert is_integer(transaction_count) assert transaction_count >= 0 @pytest.mark.asyncio - async def test_eth_call(self, async_w3: "Web3", math_contract: "Contract") -> None: - coinbase = await async_w3.eth.coinbase # type: ignore + async def test_eth_call( + self, async_w3: "AsyncWeb3", math_contract: "Contract" + ) -> None: + coinbase = await async_w3.eth.coinbase txn_params = math_contract._prepare_transaction( fn_name="add", fn_args=(7, 11), transaction={"from": coinbase, "to": math_contract.address}, ) - call_result = await async_w3.eth.call(txn_params) # type: ignore + call_result = await async_w3.eth.call(txn_params) assert is_string(call_result) (result,) = async_w3.codec.decode(["uint256"], call_result) assert result == 18 @pytest.mark.asyncio async def test_eth_call_with_override( - self, async_w3: "Web3", revert_contract: "Contract" + self, async_w3: "AsyncWeb3", revert_contract: "Contract" ) -> None: - coinbase = await async_w3.eth.coinbase # type: ignore + coinbase = await async_w3.eth.coinbase txn_params = revert_contract._prepare_transaction( fn_name="normalFunction", transaction={"from": coinbase, "to": revert_contract.address}, ) - call_result = await async_w3.eth.call(txn_params) # type: ignore + call_result = await async_w3.eth.call(txn_params) (result,) = async_w3.codec.decode(["bool"], call_result) assert result is True @@ -771,7 +774,7 @@ async def test_eth_call_with_override( override_code = HexStr( "0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c8063185c38a4146041578063c06a97cb146049578063d67e4b84146051575b600080fd5b60476071565b005b604f60df565b005b605760e4565b604051808215151515815260200191505060405180910390f35b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f46756e6374696f6e20686173206265656e2072657665727465642e000000000081525060200191505060405180910390fd5b600080fd5b60008090509056fea2646970667358221220bb71e9e9a2e271cd0fbe833524a3ea67df95f25ea13aef5b0a761fa52b538f1064736f6c63430006010033" # noqa: E501 ) - call_result = await async_w3.eth.call( # type: ignore + call_result = await async_w3.eth.call( txn_params, "latest", {revert_contract.address: {"code": override_code}} ) (result,) = async_w3.codec.decode(["bool"], call_result) @@ -779,15 +782,15 @@ async def test_eth_call_with_override( @pytest.mark.asyncio async def test_eth_call_with_0_result( - self, async_w3: "Web3", math_contract: "Contract" + self, async_w3: "AsyncWeb3", math_contract: "Contract" ) -> None: - coinbase = await async_w3.eth.coinbase # type: ignore + coinbase = await async_w3.eth.coinbase txn_params = math_contract._prepare_transaction( fn_name="add", fn_args=(0, 0), transaction={"from": coinbase, "to": math_contract.address}, ) - call_result = await async_w3.eth.call(txn_params) # type: ignore + call_result = await async_w3.eth.call(txn_params) assert is_string(call_result) (result,) = async_w3.codec.decode(["uint256"], call_result) assert result == 0 @@ -795,7 +798,7 @@ async def test_eth_call_with_0_result( @pytest.mark.asyncio async def test_eth_call_revert_with_msg( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", revert_contract: "Contract", unlocked_account: ChecksumAddress, ) -> None: @@ -809,12 +812,12 @@ async def test_eth_call_revert_with_msg( "to": revert_contract.address, }, ) - await async_w3.eth.call(txn_params) # type: ignore + await async_w3.eth.call(txn_params) @pytest.mark.asyncio async def test_eth_call_revert_without_msg( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", revert_contract: "Contract", unlocked_account: ChecksumAddress, ) -> None: @@ -826,12 +829,12 @@ async def test_eth_call_revert_without_msg( "to": revert_contract.address, }, ) - await async_w3.eth.call(txn_params) # type: ignore + await async_w3.eth.call(txn_params) @pytest.mark.asyncio async def test_eth_call_offchain_lookup( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", unlocked_account: ChecksumAddress, monkeypatch: "MonkeyPatch", @@ -857,7 +860,7 @@ async def test_eth_call_offchain_lookup( @pytest.mark.asyncio async def test_eth_call_offchain_lookup_raises_when_ccip_read_is_disabled( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", ) -> None: # test AsyncContractCaller @@ -887,7 +890,7 @@ async def test_eth_call_offchain_lookup_raises_when_ccip_read_is_disabled( @pytest.mark.asyncio async def test_eth_call_offchain_lookup_call_flag_overrides_provider_flag( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", unlocked_account: ChecksumAddress, monkeypatch: "MonkeyPatch", @@ -916,7 +919,7 @@ async def test_eth_call_offchain_lookup_call_flag_overrides_provider_flag( @pytest.mark.parametrize("max_redirects", range(-1, 4)) async def test_eth_call_offchain_lookup_raises_if_max_redirects_is_less_than_4( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", max_redirects: int, ) -> None: @@ -933,7 +936,7 @@ async def test_eth_call_offchain_lookup_raises_if_max_redirects_is_less_than_4( @pytest.mark.asyncio async def test_eth_call_offchain_lookup_raises_for_improperly_formatted_rest_request_response( # noqa: E501 self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", unlocked_account: ChecksumAddress, monkeypatch: "MonkeyPatch", @@ -957,7 +960,7 @@ async def test_eth_call_offchain_lookup_raises_for_improperly_formatted_rest_req @pytest.mark.parametrize("status_code_non_4xx_error", [100, 300, 500, 600]) async def test_eth_call_offchain_lookup_tries_next_url_for_non_4xx_error_status_and_tests_POST( # noqa: E501 self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", unlocked_account: ChecksumAddress, monkeypatch: "MonkeyPatch", @@ -995,7 +998,7 @@ async def test_eth_call_offchain_lookup_tries_next_url_for_non_4xx_error_status_ @pytest.mark.asyncio async def test_eth_call_offchain_lookup_calls_raise_for_status_for_4xx_status_code( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", unlocked_account: ChecksumAddress, monkeypatch: "MonkeyPatch", @@ -1018,7 +1021,7 @@ async def test_eth_call_offchain_lookup_calls_raise_for_status_for_4xx_status_co @pytest.mark.asyncio async def test_eth_call_offchain_lookup_raises_when_all_supplied_urls_fail( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", ) -> None: # GET and POST requests should fail since responses are not mocked @@ -1032,7 +1035,7 @@ async def test_eth_call_offchain_lookup_raises_when_all_supplied_urls_fail( @pytest.mark.asyncio async def test_eth_call_continuous_offchain_lookup_raises_with_too_many_requests( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", async_offchain_lookup_contract: "Contract", unlocked_account: ChecksumAddress, monkeypatch: "MonkeyPatch", @@ -1049,29 +1052,27 @@ async def test_eth_call_continuous_offchain_lookup_raises_with_too_many_requests await async_offchain_lookup_contract.caller().continuousOffchainLookup() # noqa: E501 type: ignore @pytest.mark.asyncio - async def test_async_eth_hashrate(self, async_w3: "Web3") -> None: - hashrate = await async_w3.eth.hashrate # type: ignore + async def test_async_eth_hashrate(self, async_w3: "AsyncWeb3") -> None: + hashrate = await async_w3.eth.hashrate assert is_integer(hashrate) assert hashrate >= 0 @pytest.mark.asyncio - async def test_async_eth_chain_id(self, async_w3: "Web3") -> None: - chain_id = await async_w3.eth.chain_id # type: ignore + async def test_async_eth_chain_id(self, async_w3: "AsyncWeb3") -> None: + chain_id = await async_w3.eth.chain_id # chain id value from geth fixture genesis file assert chain_id == 131277322940537 @pytest.mark.asyncio - async def test_async_eth_mining(self, async_w3: "Web3") -> None: - mining = await async_w3.eth.mining # type: ignore + async def test_async_eth_mining(self, async_w3: "AsyncWeb3") -> None: + mining = await async_w3.eth.mining assert is_boolean(mining) @pytest.mark.asyncio async def test_async_eth_get_transaction_receipt_mined( - self, async_w3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr + self, async_w3: "AsyncWeb3", block_with_txn: BlockData, mined_txn_hash: HexStr ) -> None: - receipt = await async_w3.eth.get_transaction_receipt( # type: ignore - mined_txn_hash - ) + receipt = await async_w3.eth.get_transaction_receipt(mined_txn_hash) assert is_dict(receipt) assert receipt["blockNumber"] == block_with_txn["number"] assert receipt["blockHash"] == block_with_txn["hash"] @@ -1087,10 +1088,10 @@ async def test_async_eth_get_transaction_receipt_mined( @pytest.mark.asyncio async def test_async_eth_get_transaction_receipt_unmined( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_hash = await async_w3.eth.send_transaction( - { # type: ignore + { "from": unlocked_account_dual_type, "to": unlocked_account_dual_type, "value": Wei(1), @@ -1100,19 +1101,17 @@ async def test_async_eth_get_transaction_receipt_unmined( } ) with pytest.raises(TransactionNotFound): - await async_w3.eth.get_transaction_receipt(txn_hash) # type: ignore + await async_w3.eth.get_transaction_receipt(txn_hash) @pytest.mark.asyncio async def test_async_eth_get_transaction_receipt_with_log_entry( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", block_with_txn_with_log: BlockData, emitter_contract: "Contract", txn_hash_with_log: HexStr, ) -> None: - receipt = await async_w3.eth.wait_for_transaction_receipt( # type: ignore - txn_hash_with_log - ) + receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash_with_log) assert is_dict(receipt) assert receipt["blockNumber"] == block_with_txn_with_log["number"] assert receipt["blockHash"] == block_with_txn_with_log["hash"] @@ -1131,11 +1130,9 @@ async def test_async_eth_get_transaction_receipt_with_log_entry( @pytest.mark.asyncio async def test_async_eth_wait_for_transaction_receipt_mined( - self, async_w3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr + self, async_w3: "AsyncWeb3", block_with_txn: BlockData, mined_txn_hash: HexStr ) -> None: - receipt = await async_w3.eth.wait_for_transaction_receipt( # type: ignore - mined_txn_hash - ) + receipt = await async_w3.eth.wait_for_transaction_receipt(mined_txn_hash) assert is_dict(receipt) assert receipt["blockNumber"] == block_with_txn["number"] assert receipt["blockHash"] == block_with_txn["hash"] @@ -1151,10 +1148,10 @@ async def test_async_eth_wait_for_transaction_receipt_mined( @pytest.mark.asyncio async def test_async_eth_wait_for_transaction_receipt_unmined( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: txn_hash = await async_w3.eth.send_transaction( - { # type: ignore + { "from": unlocked_account_dual_type, "to": unlocked_account_dual_type, "value": Wei(1), @@ -1166,23 +1163,19 @@ async def test_async_eth_wait_for_transaction_receipt_unmined( timeout = 2 with pytest.raises(TimeExhausted) as exc_info: - await async_w3.eth.wait_for_transaction_receipt( - txn_hash, timeout=timeout # type: ignore - ) + await async_w3.eth.wait_for_transaction_receipt(txn_hash, timeout=timeout) assert (_ in str(exc_info) for _ in [repr(txn_hash), timeout]) @pytest.mark.asyncio async def test_async_eth_wait_for_transaction_receipt_with_log_entry( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", block_with_txn_with_log: BlockData, emitter_contract: "Contract", txn_hash_with_log: HexStr, ) -> None: - receipt = await async_w3.eth.wait_for_transaction_receipt( # type: ignore - txn_hash_with_log - ) + receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash_with_log) assert is_dict(receipt) assert receipt["blockNumber"] == block_with_txn_with_log["number"] assert receipt["blockHash"] == block_with_txn_with_log["hash"] @@ -1200,16 +1193,16 @@ async def test_async_eth_wait_for_transaction_receipt_with_log_entry( assert log_entry["transactionHash"] == HexBytes(txn_hash_with_log) @pytest.mark.asyncio - async def test_async_eth_accounts(self, async_w3: "Web3") -> None: - accounts = await async_w3.eth.accounts # type: ignore + async def test_async_eth_accounts(self, async_w3: "AsyncWeb3") -> None: + accounts = await async_w3.eth.accounts assert is_list_like(accounts) assert len(accounts) != 0 assert all((is_checksum_address(account) for account in accounts)) - assert await async_w3.eth.coinbase in accounts # type: ignore + assert await async_w3.eth.coinbase in accounts @pytest.mark.asyncio async def test_async_eth_get_logs_without_logs( - self, async_w3: "Web3", block_with_txn_with_log: BlockData + self, async_w3: "AsyncWeb3", block_with_txn_with_log: BlockData ) -> None: # Test with block range @@ -1217,7 +1210,7 @@ async def test_async_eth_get_logs_without_logs( "fromBlock": BlockNumber(0), "toBlock": BlockNumber(block_with_txn_with_log["number"] - 1), } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert len(result) == 0 # the range is wrong @@ -1225,7 +1218,7 @@ async def test_async_eth_get_logs_without_logs( "fromBlock": block_with_txn_with_log["number"], "toBlock": BlockNumber(block_with_txn_with_log["number"] - 1), } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert len(result) == 0 # Test with `address` @@ -1235,7 +1228,7 @@ async def test_async_eth_get_logs_without_logs( "fromBlock": BlockNumber(0), "address": UNKNOWN_ADDRESS, } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert len(result) == 0 # Test with multiple `address` @@ -1245,13 +1238,13 @@ async def test_async_eth_get_logs_without_logs( "fromBlock": BlockNumber(0), "address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS], } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert len(result) == 0 @pytest.mark.asyncio async def test_async_eth_get_logs_with_logs( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", block_with_txn_with_log: BlockData, emitter_contract_address: ChecksumAddress, txn_hash_with_log: HexStr, @@ -1263,7 +1256,7 @@ async def test_async_eth_get_logs_with_logs( "fromBlock": block_with_txn_with_log["number"], "toBlock": block_with_txn_with_log["number"], } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert_contains_log( result, block_with_txn_with_log, emitter_contract_address, txn_hash_with_log ) @@ -1272,7 +1265,7 @@ async def test_async_eth_get_logs_with_logs( filter_params = { "fromBlock": BlockNumber(0), } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert_contains_log( result, block_with_txn_with_log, emitter_contract_address, txn_hash_with_log ) @@ -1284,7 +1277,7 @@ async def test_async_eth_get_logs_with_logs( "fromBlock": BlockNumber(0), "address": emitter_contract_address, } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert_contains_log( result, block_with_txn_with_log, emitter_contract_address, txn_hash_with_log ) @@ -1292,7 +1285,7 @@ async def test_async_eth_get_logs_with_logs( @pytest.mark.asyncio async def test_async_eth_get_logs_with_logs_topic_args( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", block_with_txn_with_log: BlockData, emitter_contract_address: ChecksumAddress, txn_hash_with_log: HexStr, @@ -1309,7 +1302,7 @@ async def test_async_eth_get_logs_with_logs_topic_args( ], } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert_contains_log( result, block_with_txn_with_log, emitter_contract_address, txn_hash_with_log ) @@ -1324,14 +1317,14 @@ async def test_async_eth_get_logs_with_logs_topic_args( None, ], } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert_contains_log( result, block_with_txn_with_log, emitter_contract_address, txn_hash_with_log ) @pytest.mark.asyncio async def test_async_eth_get_logs_with_logs_none_topic_args( - self, async_w3: "Web3" + self, async_w3: "AsyncWeb3" ) -> None: # Test with None overflowing filter_params: FilterParams = { @@ -1339,12 +1332,12 @@ async def test_async_eth_get_logs_with_logs_none_topic_args( "topics": [None, None, None], } - result = await async_w3.eth.get_logs(filter_params) # type: ignore + result = await async_w3.eth.get_logs(filter_params) assert len(result) == 0 @pytest.mark.asyncio - async def test_async_eth_syncing(self, async_w3: "Web3") -> None: - syncing = await async_w3.eth.syncing # type: ignore + async def test_async_eth_syncing(self, async_w3: "AsyncWeb3") -> None: + syncing = await async_w3.eth.syncing assert is_boolean(syncing) or is_dict(syncing) @@ -1362,36 +1355,32 @@ async def test_async_eth_syncing(self, async_w3: "Web3") -> None: @pytest.mark.asyncio async def test_async_eth_get_storage_at( - self, async_w3: "Web3", emitter_contract_address: ChecksumAddress + self, async_w3: "AsyncWeb3", emitter_contract_address: ChecksumAddress ) -> None: - storage = await async_w3.eth.get_storage_at( # type: ignore - emitter_contract_address, 0 - ) + storage = await async_w3.eth.get_storage_at(emitter_contract_address, 0) assert isinstance(storage, HexBytes) @pytest.mark.asyncio @pytest.mark.xfail async def test_async_eth_get_storage_at_ens_name( - self, async_w3: "Web3", emitter_contract_address: ChecksumAddress + self, async_w3: "AsyncWeb3", emitter_contract_address: ChecksumAddress ) -> None: with ens_addresses(async_w3, {"emitter.eth": emitter_contract_address}): - storage = await async_w3.eth.get_storage_at( # type: ignore - ENS("emitter.eth"), 0 - ) + storage = await async_w3.eth.get_storage_at(ENS("emitter.eth"), 0) assert isinstance(storage, HexBytes) @pytest.mark.asyncio async def test_async_eth_get_storage_at_invalid_address( - self, async_w3: "Web3" + self, async_w3: "AsyncWeb3" ) -> None: - coinbase = await async_w3.eth.coinbase # type: ignore + coinbase = await async_w3.eth.coinbase with pytest.raises(InvalidAddress): await async_w3.eth.get_storage_at( ChecksumAddress(HexAddress(HexStr(coinbase.lower()))), 0 - ) # type: ignore + ) def test_async_provider_default_account( - self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress + self, async_w3: "AsyncWeb3", unlocked_account_dual_type: ChecksumAddress ) -> None: # check defaults to empty default_account = async_w3.eth.default_account @@ -1407,7 +1396,7 @@ def test_async_provider_default_account( def test_async_provider_default_block( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", ) -> None: # check defaults to 'latest' default_block = async_w3.eth.default_block @@ -1423,9 +1412,9 @@ def test_async_provider_default_block( @pytest.mark.asyncio async def test_eth_getBlockTransactionCountByHash_empty_block( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - transaction_count = await async_w3.eth.get_block_transaction_count( # type: ignore # noqa: E501 + transaction_count = await async_w3.eth.get_block_transaction_count( empty_block["hash"] ) @@ -1434,9 +1423,9 @@ async def test_eth_getBlockTransactionCountByHash_empty_block( @pytest.mark.asyncio async def test_eth_getBlockTransactionCountByNumber_empty_block( - self, async_w3: "Web3", empty_block: BlockData + self, async_w3: "AsyncWeb3", empty_block: BlockData ) -> None: - transaction_count = await async_w3.eth.get_block_transaction_count( # type: ignore # noqa: E501 + transaction_count = await async_w3.eth.get_block_transaction_count( empty_block["number"] ) @@ -1445,9 +1434,9 @@ async def test_eth_getBlockTransactionCountByNumber_empty_block( @pytest.mark.asyncio async def test_eth_getBlockTransactionCountByHash_block_with_txn( - self, async_w3: "Web3", block_with_txn: BlockData + self, async_w3: "AsyncWeb3", block_with_txn: BlockData ) -> None: - transaction_count = await async_w3.eth.get_block_transaction_count( # type: ignore # noqa: E501 + transaction_count = await async_w3.eth.get_block_transaction_count( block_with_txn["hash"] ) @@ -1456,9 +1445,9 @@ async def test_eth_getBlockTransactionCountByHash_block_with_txn( @pytest.mark.asyncio async def test_eth_getBlockTransactionCountByNumber_block_with_txn( - self, async_w3: "Web3", block_with_txn: BlockData + self, async_w3: "AsyncWeb3", block_with_txn: BlockData ) -> None: - transaction_count = await async_w3.eth.get_block_transaction_count( # type: ignore # noqa: E501 + transaction_count = await async_w3.eth.get_block_transaction_count( block_with_txn["number"] ) @@ -1466,61 +1455,55 @@ async def test_eth_getBlockTransactionCountByNumber_block_with_txn( assert transaction_count >= 1 @pytest.mark.asyncio - async def test_async_eth_new_filter(self, async_w3: "Web3") -> None: - filter = await async_w3.eth.filter({}) # type: ignore + async def test_async_eth_new_filter(self, async_w3: "AsyncWeb3") -> None: + filter = await async_w3.eth.filter({}) - changes = await async_w3.eth.get_filter_changes( - filter.filter_id - ) # type: ignore + changes = await async_w3.eth.get_filter_changes(filter.filter_id) assert is_list_like(changes) assert not changes - logs = await async_w3.eth.get_filter_logs(filter.filter_id) # type: ignore + logs = await async_w3.eth.get_filter_logs(filter.filter_id) assert is_list_like(logs) assert not logs - result = await async_w3.eth.uninstall_filter(filter.filter_id) # type: ignore + result = await async_w3.eth.uninstall_filter(filter.filter_id) assert result is True @pytest.mark.asyncio - async def test_async_eth_new_block_filter(self, async_w3: "Web3") -> None: - filter = await async_w3.eth.filter("latest") # type: ignore + async def test_async_eth_new_block_filter(self, async_w3: "AsyncWeb3") -> None: + filter = await async_w3.eth.filter("latest") assert is_string(filter.filter_id) - changes = await async_w3.eth.get_filter_changes( - filter.filter_id - ) # type: ignore + changes = await async_w3.eth.get_filter_changes(filter.filter_id) assert is_list_like(changes) assert not changes - result = await async_w3.eth.uninstall_filter(filter.filter_id) # type: ignore + result = await async_w3.eth.uninstall_filter(filter.filter_id) assert result is True @pytest.mark.asyncio async def test_async_eth_new_pending_transaction_filter( - self, async_w3: "Web3" + self, async_w3: "AsyncWeb3" ) -> None: - filter = await async_w3.eth.filter("pending") # type: ignore + filter = await async_w3.eth.filter("pending") assert is_string(filter.filter_id) - changes = await async_w3.eth.get_filter_changes( - filter.filter_id - ) # type: ignore + changes = await async_w3.eth.get_filter_changes(filter.filter_id) assert is_list_like(changes) assert not changes - result = await async_w3.eth.uninstall_filter(filter.filter_id) # type: ignore + result = await async_w3.eth.uninstall_filter(filter.filter_id) assert result is True @pytest.mark.asyncio - async def test_async_eth_uninstall_filter(self, async_w3: "Web3") -> None: - filter = await async_w3.eth.filter({}) # type: ignore + async def test_async_eth_uninstall_filter(self, async_w3: "AsyncWeb3") -> None: + filter = await async_w3.eth.filter({}) assert is_string(filter.filter_id) - success = await async_w3.eth.uninstall_filter(filter.filter_id) # type: ignore + success = await async_w3.eth.uninstall_filter(filter.filter_id) assert success is True - failure = await async_w3.eth.uninstall_filter(filter.filter_id) # type: ignore + failure = await async_w3.eth.uninstall_filter(filter.filter_id) assert failure is False diff --git a/web3/_utils/module_testing/go_ethereum_admin_module.py b/web3/_utils/module_testing/go_ethereum_admin_module.py index 17c567e500..37db5a48e5 100644 --- a/web3/_utils/module_testing/go_ethereum_admin_module.py +++ b/web3/_utils/module_testing/go_ethereum_admin_module.py @@ -12,7 +12,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) class GoEthereumAdminModuleTest: @@ -67,39 +70,39 @@ def test_admin_start_stop_ws(self, w3: "Web3") -> None: class GoEthereumAsyncAdminModuleTest: @pytest.mark.asyncio - async def test_async_datadir(self, async_w3: "Web3") -> None: - datadir = await async_w3.geth.admin.datadir() # type: ignore + async def test_async_datadir(self, async_w3: "AsyncWeb3") -> None: + datadir = await async_w3.geth.admin.datadir() assert isinstance(datadir, str) @pytest.mark.asyncio - async def test_async_nodeinfo(self, async_w3: "Web3") -> None: - node_info = await async_w3.geth.admin.node_info() # type: ignore + async def test_async_node_info(self, async_w3: "AsyncWeb3") -> None: + node_info = await async_w3.geth.admin.node_info() assert "Geth" in node_info["name"] @pytest.mark.asyncio - async def test_async_nodes(self, async_w3: "Web3") -> None: - nodes = await async_w3.geth.admin.peers() # type: ignore + async def test_async_nodes(self, async_w3: "AsyncWeb3") -> None: + nodes = await async_w3.geth.admin.peers() assert isinstance(nodes, List) @pytest.mark.asyncio - async def test_admin_peers(self, w3: "Web3") -> None: - enode = await w3.geth.admin.node_info()["enode"] # type: ignore - w3.geth.admin.add_peer(enode) - result = await w3.geth.admin.peers() # type: ignore + async def test_admin_peers(self, async_w3: "AsyncWeb3") -> None: + node_info = await async_w3.geth.admin.node_info() + await async_w3.geth.admin.add_peer(node_info["enode"]) + result = await async_w3.geth.admin.peers() assert len(result) == 1 @pytest.mark.asyncio - async def test_admin_start_stop_http(self, w3: "Web3") -> None: - stop = await w3.geth.admin.stop_http() # type: ignore + async def test_admin_start_stop_http(self, async_w3: "AsyncWeb3") -> None: + stop = await async_w3.geth.admin.stop_http() assert stop is True - start = await w3.geth.admin.start_http() # type: ignore + start = await async_w3.geth.admin.start_http() assert start is True @pytest.mark.asyncio - async def test_admin_start_stop_ws(self, w3: "Web3") -> None: - stop = await w3.geth.admin.stop_ws() # type: ignore + async def test_admin_start_stop_ws(self, async_w3: "AsyncWeb3") -> None: + stop = await async_w3.geth.admin.stop_ws() assert stop is True - start = await w3.geth.admin.start_ws() # type: ignore + start = await async_w3.geth.admin.start_ws() assert start is True diff --git a/web3/_utils/module_testing/go_ethereum_personal_module.py b/web3/_utils/module_testing/go_ethereum_personal_module.py index 98868bdbcb..bdc60c6cab 100644 --- a/web3/_utils/module_testing/go_ethereum_personal_module.py +++ b/web3/_utils/module_testing/go_ethereum_personal_module.py @@ -30,7 +30,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) PRIVATE_KEY_HEX = "0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f933673458f" SECOND_PRIVATE_KEY_HEX = ( @@ -211,62 +214,58 @@ class GoEthereumAsyncPersonalModuleTest: @pytest.mark.asyncio async def test_async_sign_and_ec_recover( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", unlockable_account_dual_type: ChecksumAddress, unlockable_account_pw: str, ) -> None: message = "This is a test" signature = await async_w3.geth.personal.sign( - message, unlockable_account_dual_type, unlockable_account_pw # type: ignore - ) - address = await async_w3.geth.personal.ec_recover( # type: ignore - message, signature + message, unlockable_account_dual_type, unlockable_account_pw ) + address = await async_w3.geth.personal.ec_recover(message, signature) assert is_same_address(unlockable_account_dual_type, address) @pytest.mark.asyncio - async def test_async_import_key(self, async_w3: "Web3") -> None: + async def test_async_import_key(self, async_w3: "AsyncWeb3") -> None: address = await async_w3.geth.personal.import_raw_key( - THIRD_PRIVATE_KEY_HEX, "Testing" # type: ignore + THIRD_PRIVATE_KEY_HEX, "Testing" ) assert address is not None @pytest.mark.asyncio - async def test_async_list_accounts(self, async_w3: "Web3") -> None: - accounts = await async_w3.geth.personal.list_accounts() # type: ignore + async def test_async_list_accounts(self, async_w3: "AsyncWeb3") -> None: + accounts = await async_w3.geth.personal.list_accounts() assert len(accounts) > 0 @pytest.mark.asyncio - async def test_async_list_wallets(self, async_w3: "Web3") -> None: - wallets = await async_w3.geth.personal.list_wallets() # type: ignore + async def test_async_list_wallets(self, async_w3: "AsyncWeb3") -> None: + wallets = await async_w3.geth.personal.list_wallets() assert isinstance(wallets[0], AttributeDict) @pytest.mark.asyncio - async def test_async_new_account(self, async_w3: "Web3") -> None: + async def test_async_new_account(self, async_w3: "AsyncWeb3") -> None: passphrase = "Create New Account" - account = await async_w3.geth.personal.new_account(passphrase) # type: ignore + account = await async_w3.geth.personal.new_account(passphrase) assert is_checksum_address(account) @pytest.mark.asyncio async def test_async_unlock_lock_account( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", unlockable_account_dual_type: ChecksumAddress, unlockable_account_pw: str, ) -> None: - unlocked = await async_w3.geth.personal.unlock_account( # type: ignore + unlocked = await async_w3.geth.personal.unlock_account( unlockable_account_dual_type, unlockable_account_pw ) assert unlocked is True - locked = await async_w3.geth.personal.lock_account( # type: ignore - unlockable_account_dual_type - ) + locked = await async_w3.geth.personal.lock_account(unlockable_account_dual_type) assert locked is True @pytest.mark.asyncio async def test_async_send_transaction( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", unlockable_account_dual_type: ChecksumAddress, unlockable_account_pw: str, ) -> None: @@ -274,7 +273,7 @@ async def test_async_send_transaction( tx_params["to"] = unlockable_account_dual_type tx_params["from"] = unlockable_account_dual_type tx_params["value"] = Wei(123) - response = await async_w3.geth.personal.send_transaction( # type: ignore + response = await async_w3.geth.personal.send_transaction( tx_params, unlockable_account_pw ) assert response is not None @@ -285,15 +284,15 @@ async def test_async_send_transaction( @pytest.mark.asyncio async def test_async_sign_typed_data( self, - async_w3: "Web3", + async_w3: "AsyncWeb3", unlockable_account_dual_type: ChecksumAddress, unlockable_account_pw: str, ) -> None: message = {"message": "This is a test"} signature = await async_w3.geth.personal.sign_typed_data( - message, unlockable_account_dual_type, unlockable_account_pw # type: ignore + message, unlockable_account_dual_type, unlockable_account_pw ) address = await async_w3.geth.personal.ec_recover( - message, signature # type: ignore + json.dumps(message), signature ) assert is_same_address(unlockable_account_dual_type, address) diff --git a/web3/_utils/module_testing/go_ethereum_txpool_module.py b/web3/_utils/module_testing/go_ethereum_txpool_module.py index cf596692f5..9274a740ec 100644 --- a/web3/_utils/module_testing/go_ethereum_txpool_module.py +++ b/web3/_utils/module_testing/go_ethereum_txpool_module.py @@ -1,36 +1,37 @@ import pytest from web3 import ( + AsyncWeb3, Web3, ) class GoEthereumAsyncTxPoolModuleTest: @pytest.mark.asyncio - async def test_async_geth_txpool_inspect(self, async_w3: "Web3") -> None: - test_data = await async_w3.geth.txpool.inspect() # type: ignore + async def test_async_geth_txpool_inspect(self, async_w3: "AsyncWeb3") -> None: + test_data = await async_w3.geth.txpool.inspect() assert "pending" in test_data @pytest.mark.asyncio - async def test_async_geth_txpool_content(self, async_w3: "Web3") -> None: - test_data = await async_w3.geth.txpool.content() # type: ignore + async def test_async_geth_txpool_content(self, async_w3: "AsyncWeb3") -> None: + test_data = await async_w3.geth.txpool.content() assert "pending" in test_data @pytest.mark.asyncio - async def test_async_geth_txpool_status(self, async_w3: "Web3") -> None: - test_data = await async_w3.geth.txpool.status() # type: ignore + async def test_async_geth_txpool_status(self, async_w3: "AsyncWeb3") -> None: + test_data = await async_w3.geth.txpool.status() assert "pending" in test_data class GoEthereumTxPoolModuleTest: def test_geth_txpool_inspect(self, w3: "Web3") -> None: - test_data = w3.geth.txpool.inspect() # type: ignore + test_data = w3.geth.txpool.inspect() assert "pending" in test_data def test_geth_txpool_content(self, w3: "Web3") -> None: - test_data = w3.geth.txpool.content() # type: ignore + test_data = w3.geth.txpool.content() assert "pending" in test_data def test_geth_txpool_status(self, w3: "Web3") -> None: - test_data = w3.geth.txpool.status() # type: ignore + test_data = w3.geth.txpool.status() assert "pending" in test_data diff --git a/web3/_utils/module_testing/net_module.py b/web3/_utils/module_testing/net_module.py index 80c44a1d6d..9baf1314ed 100644 --- a/web3/_utils/module_testing/net_module.py +++ b/web3/_utils/module_testing/net_module.py @@ -10,7 +10,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) class NetModuleTest: @@ -33,20 +36,20 @@ def test_net_peer_count(self, w3: "Web3") -> None: class AsyncNetModuleTest: @pytest.mark.asyncio - async def test_net_version(self, async_w3: "Web3") -> None: - version = await async_w3.net.version # type: ignore + async def test_net_version(self, async_w3: "AsyncWeb3") -> None: + version = await async_w3.net.version assert is_string(version) assert version.isdigit() @pytest.mark.asyncio - async def test_net_listening(self, async_w3: "Web3") -> None: - listening = await async_w3.net.listening # type: ignore + async def test_net_listening(self, async_w3: "AsyncWeb3") -> None: + listening = await async_w3.net.listening assert is_boolean(listening) @pytest.mark.asyncio - async def test_net_peer_count(self, async_w3: "Web3") -> None: - peer_count = await async_w3.net.peer_count # type: ignore + async def test_net_peer_count(self, async_w3: "AsyncWeb3") -> None: + peer_count = await async_w3.net.peer_count assert is_integer(peer_count) diff --git a/web3/_utils/personal.py b/web3/_utils/personal.py deleted file mode 100644 index d9e505519a..0000000000 --- a/web3/_utils/personal.py +++ /dev/null @@ -1,98 +0,0 @@ -from typing import ( - Any, - Callable, - Dict, - List, - Optional, -) - -from eth_typing import ( - ChecksumAddress, - HexStr, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.compat import ( - Protocol, -) -from web3._utils.rpc_abi import ( - RPC, -) -from web3.method import ( - Method, - default_root_munger, -) -from web3.types import ( - GethWallet, - TxParams, -) - -import_raw_key: Method[Callable[[str, str], ChecksumAddress]] = Method( - RPC.personal_importRawKey, - mungers=[default_root_munger], -) - - -new_account: Method[Callable[[str], ChecksumAddress]] = Method( - RPC.personal_newAccount, - mungers=[default_root_munger], -) - - -list_accounts: Method[Callable[[], List[ChecksumAddress]]] = Method( - RPC.personal_listAccounts, - is_property=True, -) - - -list_wallets: Method[Callable[[], List[GethWallet]]] = Method( - RPC.personal_listWallets, - is_property=True, -) - - -send_transaction: Method[Callable[[TxParams, str], HexBytes]] = Method( - RPC.personal_sendTransaction, - mungers=[default_root_munger], -) - - -lock_account: Method[Callable[[ChecksumAddress], bool]] = Method( - RPC.personal_lockAccount, - mungers=[default_root_munger], -) - - -class UnlockAccountWrapper(Protocol): - def __call__( - self, account: ChecksumAddress, passphrase: str, duration: Optional[int] = None - ) -> bool: - pass - - -unlock_account: Method[UnlockAccountWrapper] = Method( - RPC.personal_unlockAccount, - mungers=[default_root_munger], -) - - -sign: Method[Callable[[str, ChecksumAddress, Optional[str]], HexStr]] = Method( - RPC.personal_sign, - mungers=[default_root_munger], -) - - -sign_typed_data: Method[ - Callable[[Dict[str, Any], ChecksumAddress, str], HexStr] -] = Method( - RPC.personal_signTypedData, - mungers=[default_root_munger], -) - - -ec_recover: Method[Callable[[str, HexStr], ChecksumAddress]] = Method( - RPC.personal_ecRecover, - mungers=[default_root_munger], -) diff --git a/web3/_utils/txpool.py b/web3/_utils/txpool.py deleted file mode 100644 index 7af244b52b..0000000000 --- a/web3/_utils/txpool.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import ( - Callable, -) - -from web3._utils.rpc_abi import ( - RPC, -) -from web3.method import ( - Method, -) -from web3.types import ( - TxPoolContent, - TxPoolInspect, - TxPoolStatus, -) - -content: Method[Callable[[], TxPoolContent]] = Method( - RPC.txpool_content, - is_property=True, -) - - -inspect: Method[Callable[[], TxPoolInspect]] = Method( - RPC.txpool_inspect, - is_property=True, -) - - -status: Method[Callable[[], TxPoolStatus]] = Method( - RPC.txpool_status, - is_property=True, -) diff --git a/web3/contract/async_contract.py b/web3/contract/async_contract.py index 3b2f9a24c7..eb5d77aac5 100644 --- a/web3/contract/async_contract.py +++ b/web3/contract/async_contract.py @@ -18,15 +18,24 @@ from eth_utils import ( combomethod, ) +from eth_utils.toolz import ( + partial, +) from hexbytes import ( HexBytes, ) +from web3._utils.abi import ( + fallback_func_abi_exists, + filter_by_type, + receive_func_abi_exists, +) from web3._utils.async_transactions import ( fill_transaction_defaults as async_fill_transaction_defaults, ) from web3._utils.contracts import ( async_parse_block_identifier, + parse_block_identifier_no_extra_call, ) from web3._utils.datatypes import ( PropertyCheckingFactory, @@ -38,6 +47,10 @@ from web3._utils.filters import ( AsyncLogFilter, ) +from web3._utils.function_identifiers import ( + FallbackFn, + ReceiveFn, +) from web3._utils.normalizers import ( normalize_abi, normalize_address_no_ens, @@ -51,6 +64,8 @@ BaseContractEvents, BaseContractFunction, BaseContractFunctions, + NonExistentFallbackFunction, + NonExistentReceiveFunction, ) from web3.contract.utils import ( async_build_transaction_for_function, @@ -60,6 +75,11 @@ find_functions_by_identifier, get_function_by_identifier, ) +from web3.exceptions import ( + ABIFunctionNotFound, + NoABIFound, + NoABIFunctionsFound, +) from web3.types import ( ABI, BlockIdentifier, @@ -69,24 +89,42 @@ ) if TYPE_CHECKING: - from ens import ENS # noqa: F401 - from web3 import Web3 # noqa: F401 + from ens import AsyncENS # noqa: F401 + from web3 import AsyncWeb3 # noqa: F401 class AsyncContractFunctions(BaseContractFunctions): def __init__( self, abi: ABI, - w3: "Web3", + w3: "AsyncWeb3", address: Optional[ChecksumAddress] = None, decode_tuples: Optional[bool] = False, ) -> None: super().__init__(abi, w3, AsyncContractFunction, address, decode_tuples) + def __getattr__(self, function_name: str) -> "AsyncContractFunction": + if self.abi is None: + raise NoABIFound( + "There is no ABI found for this contract.", + ) + if "_functions" not in self.__dict__: + raise NoABIFunctionsFound( + "The abi for this contract contains no function definitions. ", + "Are you sure you provided the correct contract abi?", + ) + elif function_name not in self.__dict__["_functions"]: + raise ABIFunctionNotFound( + f"The function '{function_name}' was not found in this contract's abi.", + " Are you sure you provided the correct contract abi?", + ) + else: + return super().__getattribute__(function_name) + class AsyncContractEvents(BaseContractEvents): def __init__( - self, abi: ABI, w3: "Web3", address: Optional[ChecksumAddress] = None + self, abi: ABI, w3: "AsyncWeb3", address: Optional[ChecksumAddress] = None ) -> None: super().__init__(abi, w3, AsyncContractEvent, address) @@ -95,6 +133,9 @@ class AsyncContract(BaseContract): functions: AsyncContractFunctions = None caller: "AsyncContractCaller" = None + # mypy types + w3: "AsyncWeb3" + #: Instance of :class:`ContractEvents` presenting available Event ABIs events: AsyncContractEvents = None @@ -132,7 +173,7 @@ def __init__(self, address: Optional[ChecksumAddress] = None) -> None: @classmethod def factory( - cls, w3: "Web3", class_name: Optional[str] = None, **kwargs: Any + cls, w3: "AsyncWeb3", class_name: Optional[str] = None, **kwargs: Any ) -> "AsyncContract": kwargs["w3"] = w3 @@ -193,27 +234,31 @@ def constructor(cls, *args: Any, **kwargs: Any) -> "AsyncContractConstructor": def find_functions_by_identifier( cls, contract_abi: ABI, - w3: "Web3", + w3: "AsyncWeb3", address: ChecksumAddress, callable_check: Callable[..., Any], ) -> List["AsyncContractFunction"]: - return find_functions_by_identifier( # type: ignore - contract_abi, w3, address, callable_check, AsyncContractFunction + return cast( + List[AsyncContractFunction], + find_functions_by_identifier( + contract_abi, w3, address, callable_check, AsyncContractFunction + ), ) @combomethod def get_function_by_identifier( - cls, fns: Sequence[BaseContractFunction], identifier: str - ) -> BaseContractFunction: + cls, fns: Sequence["AsyncContractFunction"], identifier: str + ) -> "AsyncContractFunction": return get_function_by_identifier(fns, identifier) class AsyncContractConstructor(BaseContractConstructor): + # mypy types + w3: "AsyncWeb3" + @combomethod async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes: - return await self.w3.eth.send_transaction( # type: ignore - self._get_transaction(transaction) - ) + return await self.w3.eth.send_transaction(self._get_transaction(transaction)) @combomethod async def build_transaction( @@ -233,12 +278,15 @@ async def estimate_gas( ) -> int: transaction = self._estimate_gas(transaction) - return await self.w3.eth.estimate_gas( # type: ignore + return await self.w3.eth.estimate_gas( transaction, block_identifier=block_identifier ) class AsyncContractFunction(BaseContractFunction): + # mypy types + w3: "AsyncWeb3" + def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractFunction": clone = copy.copy(self) if args is None: @@ -253,6 +301,10 @@ def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractFunction": clone._set_function_info() return clone + @classmethod + def factory(cls, class_name: str, **kwargs: Any) -> "AsyncContractFunction": + return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi")) + async def call( self, transaction: Optional[TxParams] = None, @@ -304,9 +356,7 @@ async def call( **self.kwargs, ) - async def transact( # type: ignore - self, transaction: Optional[TxParams] = None - ) -> HexBytes: + async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes: setup_transaction = self._transact(transaction) return await async_transact_with_contract_function( self.address, @@ -352,8 +402,43 @@ async def build_transaction( **self.kwargs, ) + @staticmethod + def get_fallback_function( + abi: ABI, + async_w3: "AsyncWeb3", + address: Optional[ChecksumAddress] = None, + ) -> "AsyncContractFunction": + if abi and fallback_func_abi_exists(abi): + return AsyncContractFunction.factory( + "fallback", + w3=async_w3, + contract_abi=abi, + address=address, + function_identifier=FallbackFn, + )() + return cast(AsyncContractFunction, NonExistentFallbackFunction()) + + @staticmethod + def get_receive_function( + abi: ABI, + async_w3: "AsyncWeb3", + address: Optional[ChecksumAddress] = None, + ) -> "AsyncContractFunction": + if abi and receive_func_abi_exists(abi): + return AsyncContractFunction.factory( + "receive", + w3=async_w3, + contract_abi=abi, + address=address, + function_identifier=ReceiveFn, + )() + return cast(AsyncContractFunction, NonExistentReceiveFunction()) + class AsyncContractEvent(BaseContractEvent): + # mypy types + w3: "AsyncWeb3" + @combomethod async def get_logs( self, @@ -420,7 +505,7 @@ async def get_logs( # Call JSON-RPC API logs = await self.w3.eth.get_logs( self._get_event_filter_params( - abi, argument_filters, fromBlock, toBlock, block_hash # type: ignore + abi, argument_filters, fromBlock, toBlock, block_hash ) ) @@ -471,26 +556,51 @@ def build_filter(self) -> AsyncEventFilterBuilder: class AsyncContractCaller(BaseContractCaller): + # mypy types + w3: "AsyncWeb3" + def __init__( self, abi: ABI, - w3: "Web3", + w3: "AsyncWeb3", address: ChecksumAddress, transaction: Optional[TxParams] = None, block_identifier: BlockIdentifier = "latest", ccip_read_enabled: Optional[bool] = None, decode_tuples: Optional[bool] = False, ) -> None: - super().__init__( - abi=abi, - w3=w3, - address=address, - transaction=transaction, - block_identifier=block_identifier, - ccip_read_enabled=ccip_read_enabled, - contract_function_class=AsyncContractFunction, - decode_tuples=decode_tuples, - ) + super().__init__(abi, w3, address, decode_tuples=decode_tuples) + + if self.abi: + if transaction is None: + transaction = {} + + self._functions = filter_by_type("function", self.abi) + for func in self._functions: + fn: AsyncContractFunction = AsyncContractFunction.factory( + func["name"], + w3=self.w3, + contract_abi=self.abi, + address=self.address, + function_identifier=func["name"], + decode_tuples=decode_tuples, + ) + + # TODO: The no_extra_call method gets around the fact that we can't call + # the full async method from within a class's __init__ method. We need + # to see if there's a way to account for all desired elif cases. + block_id = parse_block_identifier_no_extra_call( + self.w3, block_identifier + ) + caller_method = partial( + self.call_function, + fn, + transaction=transaction, + block_identifier=block_id, + ccip_read_enabled=ccip_read_enabled, + ) + + setattr(self, func["name"], caller_method) def __call__( self, diff --git a/web3/contract/base_contract.py b/web3/contract/base_contract.py index efb38a9d2d..1b94272483 100644 --- a/web3/contract/base_contract.py +++ b/web3/contract/base_contract.py @@ -31,9 +31,6 @@ is_text, to_tuple, ) -from eth_utils.toolz import ( - partial, -) from hexbytes import ( HexBytes, ) @@ -54,7 +51,6 @@ find_matching_event_abi, find_matching_fn_abi, get_function_info, - parse_block_identifier, prepare_transaction, ) from web3._utils.datatypes import ( @@ -111,17 +107,22 @@ ABIEvent, ABIFunction, BlockIdentifier, - CallOverride, EventData, FilterParams, FunctionIdentifier, + TContractFn, TxParams, TxReceipt, ) if TYPE_CHECKING: - from ens import ENS # noqa: F401 - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) + + from .async_contract import AsyncContractFunction # noqa: F401 + from .contract import ContractFunction # noqa: F401 class BaseContract: @@ -142,7 +143,7 @@ class BaseContract: """ # set during class construction - w3: "Web3" = None + w3: Union["Web3", "AsyncWeb3"] = None # instance level properties address: ChecksumAddress = None @@ -349,7 +350,7 @@ def _encode_constructor_data( def find_functions_by_identifier( cls, contract_abi: ABI, - w3: "Web3", + w3: Union["Web3", "AsyncWeb3"], address: ChecksumAddress, callable_check: Callable[..., Any], ) -> List[Any]: @@ -368,7 +369,7 @@ def get_function_by_identifier( @staticmethod def get_fallback_function( abi: ABI, - w3: "Web3", + w3: Union["Web3", "AsyncWeb3"], function_type: Type["BaseContractFunction"], address: Optional[ChecksumAddress] = None, ) -> "BaseContractFunction": @@ -386,7 +387,7 @@ def get_fallback_function( @staticmethod def get_receive_function( abi: ABI, - w3: "Web3", + w3: Union["Web3", "AsyncWeb3"], function_type: Type["BaseContractFunction"], address: Optional[ChecksumAddress] = None, ) -> "BaseContractFunction": @@ -426,8 +427,10 @@ class BaseContractFunctions: def __init__( self, abi: ABI, - w3: "Web3", - contract_function_class: Type["BaseContractFunction"], + w3: Union["Web3", "AsyncWeb3"], + contract_function_class: Union[ + Type["ContractFunction"], Type["AsyncContractFunction"] + ], address: Optional[ChecksumAddress] = None, decode_tuples: Optional[bool] = False, ) -> None: @@ -458,24 +461,6 @@ def __iter__(self) -> Generator[str, None, None]: for func in self._functions: yield func["name"] - def __getattr__(self, function_name: str) -> "BaseContractFunction": - if self.abi is None: - raise NoABIFound( - "There is no ABI found for this contract.", - ) - if "_functions" not in self.__dict__: - raise NoABIFunctionsFound( - "The abi for this contract contains no function definitions. ", - "Are you sure you provided the correct contract abi?", - ) - elif function_name not in self.__dict__["_functions"]: - raise ABIFunctionNotFound( - f"The function '{function_name}' was not found in this contract's abi.", - " Are you sure you provided the correct contract abi?", - ) - else: - return super().__getattribute__(function_name) - def __getitem__(self, function_name: str) -> ABIFunction: return getattr(self, function_name) @@ -510,7 +495,7 @@ class BaseContractEvents: def __init__( self, abi: ABI, - w3: "Web3", + w3: Union["Web3", "AsyncWeb3"], contract_event_type: Type["BaseContractEvent"], address: Optional[ChecksumAddress] = None, ) -> None: @@ -568,7 +553,12 @@ class BaseContractConstructor: """ def __init__( - self, w3: "Web3", abi: ABI, bytecode: HexStr, *args: Any, **kwargs: Any + self, + w3: Union["Web3", "AsyncWeb3"], + abi: ABI, + bytecode: HexStr, + *args: Any, + **kwargs: Any, ) -> None: self.w3 = w3 self.abi = abi @@ -659,7 +649,7 @@ class BaseContractFunction: address: ChecksumAddress = None function_identifier: FunctionIdentifier = None - w3: "Web3" = None + w3: Union["Web3", "AsyncWeb3"] = None contract_abi: ABI = None abi: ABIFunction = None transaction: TxParams = None @@ -672,30 +662,6 @@ def __init__(self, abi: Optional[ABIFunction] = None) -> None: self.abi = abi self.fn_name = type(self).__name__ - def __call__(self, *args: Any, **kwargs: Any) -> "BaseContractFunction": - # This was needed for typing - raise NotImplementedError( - "This method should be implemented in the inherited class" - ) - - def call( - self, - transaction: Optional[TxParams] = None, - block_identifier: BlockIdentifier = "latest", - state_override: Optional[CallOverride] = None, - ccip_read_enabled: Optional[bool] = None, - ) -> Any: - # This was needed for typing - raise NotImplementedError( - "This method should be implemented in the inherited class" - ) - - def transact(self, transaction: Optional[TxParams] = None) -> "HexBytes": - # This was needed for typing - raise NotImplementedError( - "This method should be implemented in the inherited class" - ) - def _set_function_info(self) -> None: if not self.abi: self.abi = find_matching_fn_abi( @@ -850,7 +816,9 @@ def __repr__(self) -> str: return f"" @classmethod - def factory(cls, class_name: str, **kwargs: Any) -> "BaseContractFunction": + def factory( + cls, class_name: str, **kwargs: Any + ) -> Union["ContractFunction", "AsyncContractFunction"]: return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi")) @@ -863,7 +831,7 @@ class BaseContractEvent: address: ChecksumAddress = None event_name: str = None - w3: "Web3" = None + w3: Union["Web3", "AsyncWeb3"] = None contract_abi: ABI = None abi: ABIEvent = None @@ -1074,48 +1042,21 @@ class BaseContractCaller: > contract.caller(transaction={'from': eth.accounts[1], 'gas': 100000, ...}).add(2, 3) # noqa: E501 """ + # mypy types + _functions: List[Union[ABIFunction, ABIEvent]] + def __init__( self, abi: ABI, - w3: "Web3", + w3: Union["Web3", "AsyncWeb3"], address: ChecksumAddress, - contract_function_class: Type[BaseContractFunction], - transaction: Optional[TxParams] = None, - block_identifier: BlockIdentifier = "latest", - ccip_read_enabled: Optional[bool] = None, decode_tuples: Optional[bool] = False, ) -> None: self.w3 = w3 self.address = address self.abi = abi - self._functions = None self.decode_tuples = decode_tuples - - if self.abi: - if transaction is None: - transaction = {} - - self._functions = filter_by_type("function", self.abi) - for func in self._functions: - fn: BaseContractFunction = contract_function_class.factory( - func["name"], - w3=self.w3, - contract_abi=self.abi, - address=self.address, - function_identifier=func["name"], - decode_tuples=decode_tuples, - ) - - block_id = parse_block_identifier(self.w3, block_identifier) - caller_method = partial( - self.call_function, - fn, - transaction=transaction, - block_identifier=block_id, - ccip_read_enabled=ccip_read_enabled, - ) - - setattr(self, func["name"], caller_method) + self._functions = [] def __getattr__(self, function_name: str) -> Any: if self.abi is None: @@ -1146,7 +1087,7 @@ def __hasattr__(self, event_name: str) -> bool: @staticmethod def call_function( - fn: BaseContractFunction, + fn: TContractFn, *args: Any, transaction: Optional[TxParams] = None, block_identifier: BlockIdentifier = "latest", diff --git a/web3/contract/contract.py b/web3/contract/contract.py index 6110e0c048..9446afbaf6 100644 --- a/web3/contract/contract.py +++ b/web3/contract/contract.py @@ -24,6 +24,14 @@ HexBytes, ) +from web3._utils.abi import ( + fallback_func_abi_exists, + filter_by_type, + receive_func_abi_exists, +) +from web3._utils.contracts import ( + parse_block_identifier, +) from web3._utils.datatypes import ( PropertyCheckingFactory, ) @@ -34,6 +42,10 @@ from web3._utils.filters import ( LogFilter, ) +from web3._utils.function_identifiers import ( + FallbackFn, + ReceiveFn, +) from web3._utils.normalizers import ( normalize_abi, normalize_address, @@ -50,7 +62,8 @@ BaseContractEvents, BaseContractFunction, BaseContractFunctions, - parse_block_identifier, + NonExistentFallbackFunction, + NonExistentReceiveFunction, ) from web3.contract.utils import ( build_transaction_for_function, @@ -60,6 +73,11 @@ get_function_by_identifier, transact_with_contract_function, ) +from web3.exceptions import ( + ABIFunctionNotFound, + NoABIFound, + NoABIFunctionsFound, +) from web3.types import ( ABI, BlockIdentifier, @@ -83,6 +101,24 @@ def __init__( ) -> None: super().__init__(abi, w3, ContractFunction, address, decode_tuples) + def __getattr__(self, function_name: str) -> "ContractFunction": + if self.abi is None: + raise NoABIFound( + "There is no ABI found for this contract.", + ) + if "_functions" not in self.__dict__: + raise NoABIFunctionsFound( + "The abi for this contract contains no function definitions. ", + "Are you sure you provided the correct contract abi?", + ) + elif function_name not in self.__dict__["_functions"]: + raise ABIFunctionNotFound( + f"The function '{function_name}' was not found in this contract's abi.", + " Are you sure you provided the correct contract abi?", + ) + else: + return super().__getattribute__(function_name) + class ContractEvents(BaseContractEvents): def __init__( @@ -92,6 +128,9 @@ def __init__( class ContractEvent(BaseContractEvent): + # mypy types + w3: "Web3" + @combomethod def get_logs( self, @@ -207,6 +246,8 @@ def build_filter(self) -> EventFilterBuilder: class Contract(BaseContract): + # mypy types + w3: "Web3" functions: ContractFunctions = None caller: "ContractCaller" = None @@ -224,8 +265,7 @@ def __init__(self, address: Optional[ChecksumAddress] = None) -> None: ) if address: - _ens = cast("ENS", _w3.ens) - self.address = normalize_address(_ens, address) + self.address = normalize_address(cast("ENS", _w3.ens), address) if not self.address: raise TypeError( @@ -260,7 +300,7 @@ def factory( normalizers = { "abi": normalize_abi, - "address": partial(normalize_address, kwargs["w3"].ens), + "address": partial(normalize_address, w3.ens), "bytecode": normalize_bytecode, "bytecode_runtime": normalize_bytecode, } @@ -320,18 +360,24 @@ def find_functions_by_identifier( address: ChecksumAddress, callable_check: Callable[..., Any], ) -> List["ContractFunction"]: - return find_functions_by_identifier( # type: ignore - contract_abi, w3, address, callable_check, ContractFunction + return cast( + List["ContractFunction"], + find_functions_by_identifier( + contract_abi, w3, address, callable_check, ContractFunction + ), ) @combomethod def get_function_by_identifier( - cls, fns: Sequence[BaseContractFunction], identifier: str - ) -> BaseContractFunction: + cls, fns: Sequence["ContractFunction"], identifier: str + ) -> "ContractFunction": return get_function_by_identifier(fns, identifier) class ContractConstructor(BaseContractConstructor): + # mypy types + w3: "Web3" + @combomethod def transact(self, transaction: Optional[TxParams] = None) -> HexBytes: return self.w3.eth.send_transaction(self._get_transaction(transaction)) @@ -356,6 +402,9 @@ def estimate_gas( class ContractFunction(BaseContractFunction): + # mypy types + w3: "Web3" + def __call__(self, *args: Any, **kwargs: Any) -> "ContractFunction": clone = copy.copy(self) if args is None: @@ -370,6 +419,10 @@ def __call__(self, *args: Any, **kwargs: Any) -> "ContractFunction": clone._set_function_info() return clone + @classmethod + def factory(cls, class_name: str, **kwargs: Any) -> "ContractFunction": + return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi")) + def call( self, transaction: Optional[TxParams] = None, @@ -465,8 +518,43 @@ def build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams: **self.kwargs, ) + @staticmethod + def get_fallback_function( + abi: ABI, + w3: "Web3", + address: Optional[ChecksumAddress] = None, + ) -> "ContractFunction": + if abi and fallback_func_abi_exists(abi): + return ContractFunction.factory( + "fallback", + w3=w3, + contract_abi=abi, + address=address, + function_identifier=FallbackFn, + )() + return cast(ContractFunction, NonExistentFallbackFunction()) + + @staticmethod + def get_receive_function( + abi: ABI, + w3: "Web3", + address: Optional[ChecksumAddress] = None, + ) -> "ContractFunction": + if abi and receive_func_abi_exists(abi): + return ContractFunction.factory( + "receive", + w3=w3, + contract_abi=abi, + address=address, + function_identifier=ReceiveFn, + )() + return cast(ContractFunction, NonExistentReceiveFunction()) + class ContractCaller(BaseContractCaller): + # mypy types + w3: "Web3" + def __init__( self, abi: ABI, @@ -477,16 +565,33 @@ def __init__( ccip_read_enabled: Optional[bool] = None, decode_tuples: Optional[bool] = False, ) -> None: - super().__init__( - abi=abi, - w3=w3, - address=address, - transaction=transaction, - block_identifier=block_identifier, - ccip_read_enabled=ccip_read_enabled, - contract_function_class=ContractFunction, - decode_tuples=decode_tuples, - ) + super().__init__(abi, w3, address, decode_tuples=decode_tuples) + + if self.abi: + if transaction is None: + transaction = {} + + self._functions = filter_by_type("function", self.abi) + for func in self._functions: + fn: ContractFunction = ContractFunction.factory( + func["name"], + w3=self.w3, + contract_abi=self.abi, + address=self.address, + function_identifier=func["name"], + decode_tuples=decode_tuples, + ) + + block_id = parse_block_identifier(self.w3, block_identifier) + caller_method = partial( + self.call_function, + fn, + transaction=transaction, + block_identifier=block_id, + ccip_read_enabled=ccip_read_enabled, + ) + + setattr(self, func["name"], caller_method) def __call__( self, diff --git a/web3/contract/utils.py b/web3/contract/utils.py index 472c8c7346..969f13849f 100644 --- a/web3/contract/utils.py +++ b/web3/contract/utils.py @@ -8,6 +8,7 @@ Sequence, Tuple, Type, + Union, ) from eth_abi.exceptions import ( @@ -40,9 +41,6 @@ from web3._utils.transactions import ( fill_transaction_defaults, ) -from web3.contract.base_contract import ( - BaseContractFunction, -) from web3.exceptions import ( BadFunctionCallOutput, ) @@ -52,11 +50,15 @@ BlockIdentifier, CallOverride, FunctionIdentifier, + TContractFn, TxParams, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) ACCEPTABLE_EMPTY_STRINGS = ["0x", b"0x", "", b""] @@ -234,11 +236,11 @@ def build_transaction_for_function( def find_functions_by_identifier( contract_abi: ABI, - w3: "Web3", + w3: Union["Web3", "AsyncWeb3"], address: ChecksumAddress, callable_check: Callable[..., Any], - function_type: Type[BaseContractFunction], -) -> List[BaseContractFunction]: + function_type: Type[TContractFn], +) -> List[TContractFn]: fns_abi = filter_by_type("function", contract_abi) return [ function_type.factory( @@ -255,8 +257,8 @@ def find_functions_by_identifier( def get_function_by_identifier( - fns: Sequence[BaseContractFunction], identifier: str -) -> BaseContractFunction: + fns: Sequence[TContractFn], identifier: str +) -> TContractFn: if len(fns) > 1: raise ValueError( f"Found multiple functions with matching {identifier}. " f"Found: {fns!r}" @@ -270,7 +272,7 @@ def get_function_by_identifier( async def async_call_contract_function( - async_w3: "Web3", + async_w3: "AsyncWeb3", address: ChecksumAddress, normalizers: Tuple[Callable[..., Any], ...], function_identifier: FunctionIdentifier, @@ -299,7 +301,7 @@ async def async_call_contract_function( fn_kwargs=kwargs, ) - return_data = await async_w3.eth.call( # type: ignore + return_data = await async_w3.eth.call( call_transaction, block_identifier=block_id, state_override=state_override, @@ -320,7 +322,7 @@ async def async_call_contract_function( # eth-abi-utils is_missing_code_error = ( return_data in ACCEPTABLE_EMPTY_STRINGS - and await async_w3.eth.get_code(address) in ACCEPTABLE_EMPTY_STRINGS # type: ignore # noqa: E501 + and await async_w3.eth.get_code(address) in ACCEPTABLE_EMPTY_STRINGS ) if is_missing_code_error: msg = ( @@ -352,7 +354,7 @@ async def async_call_contract_function( async def async_transact_with_contract_function( address: ChecksumAddress, - w3: "Web3", + async_w3: "AsyncWeb3", function_name: Optional[FunctionIdentifier] = None, transaction: Optional[TxParams] = None, contract_abi: Optional[ABI] = None, @@ -366,7 +368,7 @@ async def async_transact_with_contract_function( """ transact_transaction = prepare_transaction( address, - w3, + async_w3, fn_identifier=function_name, contract_abi=contract_abi, transaction=transaction, @@ -375,13 +377,13 @@ async def async_transact_with_contract_function( fn_kwargs=kwargs, ) - txn_hash = await w3.eth.send_transaction(transact_transaction) # type: ignore + txn_hash = await async_w3.eth.send_transaction(transact_transaction) return txn_hash async def async_estimate_gas_for_function( address: ChecksumAddress, - w3: "Web3", + async_w3: "AsyncWeb3", fn_identifier: Optional[FunctionIdentifier] = None, transaction: Optional[TxParams] = None, contract_abi: Optional[ABI] = None, @@ -397,7 +399,7 @@ async def async_estimate_gas_for_function( """ estimate_transaction = prepare_transaction( address, - w3, + async_w3, fn_identifier=fn_identifier, contract_abi=contract_abi, fn_abi=fn_abi, @@ -406,14 +408,12 @@ async def async_estimate_gas_for_function( fn_kwargs=kwargs, ) - return await w3.eth.estimate_gas( # type: ignore - estimate_transaction, block_identifier - ) + return await async_w3.eth.estimate_gas(estimate_transaction, block_identifier) async def async_build_transaction_for_function( address: ChecksumAddress, - w3: "Web3", + async_w3: "AsyncWeb3", function_name: Optional[FunctionIdentifier] = None, transaction: Optional[TxParams] = None, contract_abi: Optional[ABI] = None, @@ -428,7 +428,7 @@ async def async_build_transaction_for_function( """ prepared_transaction = prepare_transaction( address, - w3, + async_w3, fn_identifier=function_name, contract_abi=contract_abi, fn_abi=fn_abi, @@ -437,4 +437,4 @@ async def async_build_transaction_for_function( fn_kwargs=kwargs, ) - return await async_fill_transaction_defaults(w3, prepared_transaction) + return await async_fill_transaction_defaults(async_w3, prepared_transaction) diff --git a/web3/eth/async_eth.py b/web3/eth/async_eth.py index 5c8f19df2d..307bd7aac7 100644 --- a/web3/eth/async_eth.py +++ b/web3/eth/async_eth.py @@ -1,5 +1,7 @@ import asyncio from typing import ( + TYPE_CHECKING, + Any, Awaitable, Callable, List, @@ -7,6 +9,7 @@ Tuple, Type, Union, + overload, ) import warnings @@ -71,8 +74,14 @@ async_handle_offchain_lookup, ) +if TYPE_CHECKING: + from web3 import AsyncWeb3 # noqa: F401 + class AsyncEth(BaseEth): + # mypy types + w3: "AsyncWeb3" + is_async = True _default_contract_factory: Type[ @@ -545,3 +554,35 @@ async def get_filter_logs(self, filter_id: HexStr) -> List[LogReceipt]: async def uninstall_filter(self, filter_id: HexStr) -> bool: return await self._uninstall_filter(filter_id) + + @overload + def contract(self, address: None = None, **kwargs: Any) -> Type[AsyncContract]: + ... + + @overload + def contract( + self, address: Union[Address, ChecksumAddress, ENS], **kwargs: Any + ) -> AsyncContract: + ... + + def contract( + self, + address: Optional[Union[Address, ChecksumAddress, ENS]] = None, + **kwargs: Any, + ) -> Union[Type[AsyncContract], AsyncContract]: + ContractFactoryClass = kwargs.pop( + "ContractFactoryClass", self._default_contract_factory + ) + + ContractFactory = ContractFactoryClass.factory(self.w3, **kwargs) + + if address: + return ContractFactory(address) + else: + return ContractFactory + + def set_contract_factory( + self, + contract_factory: Type[Union[AsyncContract, AsyncContractCaller]], + ) -> None: + self._default_contract_factory = contract_factory diff --git a/web3/eth/base_eth.py b/web3/eth/base_eth.py index ac9429b8a3..d51dfc51b2 100644 --- a/web3/eth/base_eth.py +++ b/web3/eth/base_eth.py @@ -5,9 +5,7 @@ Optional, Sequence, Tuple, - Type, Union, - overload, ) from eth_account import ( @@ -30,12 +28,6 @@ Empty, empty, ) -from web3.contract import ( - AsyncContract, - AsyncContractCaller, - Contract, - ContractCaller, -) from web3.module import ( Module, ) @@ -185,39 +177,3 @@ def filter_munger( "a valid filter object, or a filter_id as a string " "or hex." ) - - @overload - def contract( - self, address: None = None, **kwargs: Any - ) -> Union[Type[Contract], Type[AsyncContract]]: - ... # noqa: E704,E501 - - @overload # noqa: F811 - def contract( - self, address: Union[Address, ChecksumAddress, ENS], **kwargs: Any - ) -> Union[Contract, AsyncContract]: - ... # noqa: E704,E501 - - def contract( # noqa: F811 - self, - address: Optional[Union[Address, ChecksumAddress, ENS]] = None, - **kwargs: Any, - ) -> Union[Type[Contract], Contract, Type[AsyncContract], AsyncContract]: - ContractFactoryClass = kwargs.pop( - "ContractFactoryClass", self._default_contract_factory - ) - - ContractFactory = ContractFactoryClass.factory(self.w3, **kwargs) - - if address: - return ContractFactory(address) - else: - return ContractFactory - - def set_contract_factory( - self, - contract_factory: Type[ - Union[Contract, AsyncContract, ContractCaller, AsyncContractCaller] - ], - ) -> None: - self._default_contract_factory = contract_factory diff --git a/web3/eth/eth.py b/web3/eth/eth.py index 2c41177ddb..c62967bc80 100644 --- a/web3/eth/eth.py +++ b/web3/eth/eth.py @@ -1,4 +1,5 @@ from typing import ( + TYPE_CHECKING, Any, Callable, List, @@ -8,6 +9,7 @@ Type, Union, cast, + overload, ) import warnings @@ -90,8 +92,14 @@ handle_offchain_lookup, ) +if TYPE_CHECKING: + from web3 import Web3 # noqa: F401 + class Eth(BaseEth): + # mypy types + w3: "Web3" + _default_contract_factory: Type[Union[Contract, ContractCaller]] = Contract # eth_accounts @@ -632,3 +640,35 @@ def sign_munger( RPC.eth_submitWork, mungers=[default_root_munger], ) + + @overload + def contract(self, address: None = None, **kwargs: Any) -> Type[Contract]: + ... + + @overload + def contract( + self, address: Union[Address, ChecksumAddress, ENS], **kwargs: Any + ) -> Contract: + ... + + def contract( + self, + address: Optional[Union[Address, ChecksumAddress, ENS]] = None, + **kwargs: Any, + ) -> Union[Type[Contract], Contract]: + ContractFactoryClass = kwargs.pop( + "ContractFactoryClass", self._default_contract_factory + ) + + ContractFactory = ContractFactoryClass.factory(self.w3, **kwargs) + + if address: + return ContractFactory(address) + else: + return ContractFactory + + def set_contract_factory( + self, + contract_factory: Type[Union[Contract, ContractCaller]], + ) -> None: + self._default_contract_factory = contract_factory diff --git a/web3/geth.py b/web3/geth.py index 0e40fe45cd..5600aa2703 100644 --- a/web3/geth.py +++ b/web3/geth.py @@ -1,9 +1,11 @@ from typing import ( Any, Awaitable, + Callable, Dict, List, Optional, + Tuple, ) from eth_typing.encoding import ( @@ -16,15 +18,8 @@ HexBytes, ) -from web3._utils.admin import ( - add_peer, - datadir, - node_info, - peers, - start_http, - start_ws, - stop_http, - stop_ws, +from web3._utils.compat import ( + Protocol, ) from web3._utils.miner import ( make_dag, @@ -36,22 +31,12 @@ stop, stop_auto_dag, ) -from web3._utils.personal import ( - ec_recover, - import_raw_key, - list_accounts, - list_wallets, - lock_account, - new_account, - send_transaction, - sign, - sign_typed_data, - unlock_account, +from web3._utils.rpc_abi import ( + RPC, ) -from web3._utils.txpool import ( - content, - inspect, - status, +from web3.method import ( + Method, + default_root_munger, ) from web3.module import ( Module, @@ -68,253 +53,431 @@ ) -class BaseGethPersonal(Module): +class UnlockAccountWrapper(Protocol): + def __call__( + self, + account: ChecksumAddress, + passphrase: str, + duration: Optional[int] = None, + ) -> bool: + pass + + +class GethPersonal(Module): """ - https://github.com/ethereum/go-ethereum/wiki/management-apis#personal + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal """ - _ec_recover = ec_recover - _import_raw_key = import_raw_key - _list_accounts = list_accounts - _list_wallets = list_wallets - _lock_account = lock_account - _new_account = new_account - _send_transaction = send_transaction - _sign = sign - _sign_typed_data = sign_typed_data - _unlock_account = unlock_account + is_async = False + ec_recover: Method[Callable[[str, HexStr], ChecksumAddress]] = Method( + RPC.personal_ecRecover, + mungers=[default_root_munger], + ) + + import_raw_key: Method[Callable[[str, str], ChecksumAddress]] = Method( + RPC.personal_importRawKey, + mungers=[default_root_munger], + ) + + list_accounts: Method[Callable[[], List[ChecksumAddress]]] = Method( + RPC.personal_listAccounts, + is_property=True, + ) + + list_wallets: Method[Callable[[], List[GethWallet]]] = Method( + RPC.personal_listWallets, + is_property=True, + ) + + send_transaction: Method[Callable[[TxParams, str], HexBytes]] = Method( + RPC.personal_sendTransaction, + mungers=[default_root_munger], + ) + + sign: Method[Callable[[str, ChecksumAddress, Optional[str]], HexStr]] = Method( + RPC.personal_sign, + mungers=[default_root_munger], + ) + + sign_typed_data: Method[ + Callable[[Dict[str, Any], ChecksumAddress, str], HexStr] + ] = Method( + RPC.personal_signTypedData, + mungers=[default_root_munger], + ) + + new_account: Method[Callable[[str], ChecksumAddress]] = Method( + RPC.personal_newAccount, + mungers=[default_root_munger], + ) + + lock_account: Method[Callable[[ChecksumAddress], bool]] = Method( + RPC.personal_lockAccount, + mungers=[default_root_munger], + ) + + unlock_account: Method[UnlockAccountWrapper] = Method( + RPC.personal_unlockAccount, + mungers=[default_root_munger], + ) + + +class GethTxPool(Module): + """ + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool + """ -class GethPersonal(BaseGethPersonal): is_async = False - def ec_recover(self, message: str, signature: HexStr) -> ChecksumAddress: - return self._ec_recover(message, signature) + content: Method[Callable[[], TxPoolContent]] = Method( + RPC.txpool_content, + is_property=True, + ) - def import_raw_key(self, private_key: str, passphrase: str) -> ChecksumAddress: - return self._import_raw_key(private_key, passphrase) + inspect: Method[Callable[[], TxPoolInspect]] = Method( + RPC.txpool_inspect, + is_property=True, + ) - def list_accounts(self) -> List[ChecksumAddress]: - return self._list_accounts() + status: Method[Callable[[], TxPoolStatus]] = Method( + RPC.txpool_status, + is_property=True, + ) - def list_wallets(self) -> List[GethWallet]: - return self._list_wallets() - def lock_account(self, account: ChecksumAddress) -> bool: - return self._lock_account(account) +class ServerConnection(Protocol): + def __call__( + self, + host: str = "localhost", + port: int = 8546, + cors: str = "", + apis: str = "eth,net,web3", + ) -> bool: + pass - def new_account(self, passphrase: str) -> ChecksumAddress: - return self._new_account(passphrase) - def send_transaction(self, transaction: TxParams, passphrase: str) -> HexBytes: - return self._send_transaction(transaction, passphrase) +def admin_start_params_munger( + _module: Module, + host: str = "localhost", + port: int = 8546, + cors: str = "", + apis: str = "eth,net,web3", +) -> Tuple[str, int, str, str]: + return (host, port, cors, apis) - def sign(self, message: str, account: ChecksumAddress, passphrase: str) -> HexStr: - return self._sign(message, account, passphrase) - def sign_typed_data( - self, message: Dict[str, Any], account: ChecksumAddress, passphrase: str - ) -> HexStr: - return self._sign_typed_data(message, account, passphrase) +class GethAdmin(Module): + """ + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin + """ - def unlock_account( - self, account: ChecksumAddress, passphrase: str, duration: Optional[int] = None - ) -> bool: - return self._unlock_account(account, passphrase, duration) + is_async = False + add_peer: Method[Callable[[EnodeURI], bool]] = Method( + RPC.admin_addPeer, + mungers=[default_root_munger], + ) -class AsyncGethPersonal(BaseGethPersonal): - is_async = True + datadir: Method[Callable[[], str]] = Method( + RPC.admin_datadir, + is_property=True, + ) - async def ec_recover( - self, message: str, signature: HexStr - ) -> Awaitable[ChecksumAddress]: - return await self._ec_recover(message, signature) # type: ignore + node_info: Method[Callable[[], NodeInfo]] = Method( + RPC.admin_nodeInfo, + is_property=True, + ) - async def import_raw_key( - self, private_key: str, passphrase: str - ) -> Awaitable[ChecksumAddress]: - return await self._import_raw_key(private_key, passphrase) # type: ignore + peers: Method[Callable[[], List[Peer]]] = Method( + RPC.admin_peers, + is_property=True, + ) - async def list_accounts(self) -> Awaitable[List[ChecksumAddress]]: - return await self._list_accounts() # type: ignore + start_http: Method[ServerConnection] = Method( + RPC.admin_startHTTP, + mungers=[admin_start_params_munger], + ) - async def list_wallets(self) -> Awaitable[List[GethWallet]]: - return await self._list_wallets() # type: ignore + start_ws: Method[ServerConnection] = Method( + RPC.admin_startWS, + mungers=[admin_start_params_munger], + ) - async def lock_account(self, account: ChecksumAddress) -> Awaitable[bool]: - return await self._lock_account(account) # type: ignore + stop_http: Method[Callable[[], bool]] = Method( + RPC.admin_stopHTTP, + is_property=True, + ) - async def new_account(self, passphrase: str) -> Awaitable[ChecksumAddress]: - return await self._new_account(passphrase) # type: ignore + stop_ws: Method[Callable[[], bool]] = Method( + RPC.admin_stopWS, + is_property=True, + ) - async def send_transaction( - self, transaction: TxParams, passphrase: str - ) -> Awaitable[HexBytes]: - return await self._send_transaction(transaction, passphrase) # type: ignore - async def sign( - self, message: str, account: ChecksumAddress, passphrase: str - ) -> Awaitable[HexStr]: - return await self._sign(message, account, passphrase) # type: ignore +class GethMiner(Module): + """ + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-miner + """ - async def sign_typed_data( - self, message: Dict[str, Any], account: ChecksumAddress, passphrase: str - ) -> Awaitable[HexStr]: - return await self._sign_typed_data(message, account, passphrase) # type: ignore + make_dag = make_dag + set_extra = set_extra + set_etherbase = set_etherbase + set_gas_price = set_gas_price + start = start + stop = stop + start_auto_dag = start_auto_dag + stop_auto_dag = stop_auto_dag - async def unlock_account( - self, account: ChecksumAddress, passphrase: str, duration: Optional[int] = None - ) -> Awaitable[bool]: - return await self._unlock_account(account, passphrase, duration) # type: ignore + +class Geth(Module): + personal: GethPersonal + admin: GethAdmin + txpool: GethTxPool -class BaseTxPool(Module): +# --- async --- # + + +class AsyncGethTxPool(Module): """ - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#txpool + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool """ - _content = content - _inspect = inspect - _status = status + is_async = True + _content: Method[Callable[[], Awaitable[TxPoolContent]]] = Method( + RPC.txpool_content, + is_property=True, + ) -class GethTxPool(BaseTxPool): - is_async = False + async def content(self) -> TxPoolContent: + return await self._content() - def content(self) -> TxPoolContent: - return self._content() + _inspect: Method[Callable[[], Awaitable[TxPoolInspect]]] = Method( + RPC.txpool_inspect, + is_property=True, + ) - def inspect(self) -> TxPoolInspect: - return self._inspect() + async def inspect(self) -> TxPoolInspect: + return await self._inspect() - def status(self) -> TxPoolStatus: - return self._status() + _status: Method[Callable[[], Awaitable[TxPoolStatus]]] = Method( + RPC.txpool_status, + is_property=True, + ) + async def status(self) -> TxPoolStatus: + return await self._status() -class AsyncGethTxPool(BaseTxPool): - is_async = True - async def content(self) -> Awaitable[Any]: - return await self._content() # type: ignore +class AsyncGethAdmin(Module): + """ + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin + """ - async def inspect(self) -> Awaitable[Any]: - return await self._inspect() # type: ignore + is_async = True - async def status(self) -> Awaitable[Any]: - return await self._status() # type: ignore + _add_peer: Method[Callable[[EnodeURI], Awaitable[bool]]] = Method( + RPC.admin_addPeer, + mungers=[default_root_munger], + ) + async def add_peer(self, node_url: EnodeURI) -> bool: + return await self._add_peer(node_url) -class BaseGethAdmin(Module): - """ - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#admin - """ + _datadir: Method[Callable[[], Awaitable[str]]] = Method( + RPC.admin_datadir, + is_property=True, + ) - _add_peer = add_peer - _datadir = datadir - _node_info = node_info - _peers = peers - _start_http = start_http - _start_ws = start_ws - _stop_http = stop_http - _stop_ws = stop_ws + async def datadir(self) -> str: + return await self._datadir() + _node_info: Method[Callable[[], Awaitable[NodeInfo]]] = Method( + RPC.admin_nodeInfo, + is_property=True, + ) -class GethAdmin(BaseGethAdmin): - is_async = False + async def node_info(self) -> NodeInfo: + return await self._node_info() + + _peers: Method[Callable[[], Awaitable[List[Peer]]]] = Method( + RPC.admin_peers, + is_property=True, + ) - def add_peer(self, node_url: EnodeURI) -> bool: - return self._add_peer(node_url) + async def peers(self) -> List[Peer]: + return await self._peers() - def datadir(self) -> str: - return self._datadir() + # start_http and stop_http - def node_info(self) -> NodeInfo: - return self._node_info() + _start_http: Method[Callable[[str, int, str, str], Awaitable[bool]]] = Method( + RPC.admin_startHTTP, + mungers=[admin_start_params_munger], + ) - def peers(self) -> List[Peer]: - return self._peers() + _stop_http: Method[Callable[[], Awaitable[bool]]] = Method( + RPC.admin_stopHTTP, + is_property=True, + ) - def start_http( + async def start_http( self, host: str = "localhost", port: int = 8546, cors: str = "", apis: str = "eth,net,web3", ) -> bool: - return self._start_http(host, port, cors, apis) + return await self._start_http(host, port, cors, apis) + + async def stop_http(self) -> bool: + return await self._stop_http() + + # start_ws and stop_ws - def start_ws( + _start_ws: Method[Callable[[str, int, str, str], Awaitable[bool]]] = Method( + RPC.admin_startWS, + mungers=[admin_start_params_munger], + ) + + _stop_ws: Method[Callable[[], Awaitable[bool]]] = Method( + RPC.admin_stopWS, + is_property=True, + ) + + async def start_ws( self, host: str = "localhost", port: int = 8546, cors: str = "", apis: str = "eth,net,web3", ) -> bool: - return self._start_ws(host, port, cors, apis) + return await self._start_ws(host, port, cors, apis) - def stop_http(self) -> bool: - return self._stop_http() + async def stop_ws(self) -> bool: + return await self._stop_ws() - def stop_ws(self) -> bool: - return self._stop_ws() +class AsyncGethPersonal(Module): + """ + https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal + """ -class AsyncGethAdmin(BaseGethAdmin): is_async = True - async def add_peer(self, node_url: EnodeURI) -> Awaitable[bool]: - return await self._add_peer(node_url) # type: ignore + # ec_recover - async def datadir(self) -> Awaitable[str]: - return await self._datadir() # type: ignore + _ec_recover: Method[Callable[[str, HexStr], Awaitable[ChecksumAddress]]] = Method( + RPC.personal_ecRecover, + mungers=[default_root_munger], + ) - async def node_info(self) -> Awaitable[NodeInfo]: - return await self._node_info() # type: ignore + async def ec_recover(self, message: str, signature: HexStr) -> ChecksumAddress: + return await self._ec_recover(message, signature) - async def peers(self) -> Awaitable[List[Peer]]: - return await self._peers() # type: ignore + # import_raw_key - async def start_http( - self, - host: str = "localhost", - port: int = 8546, - cors: str = "", - apis: str = "eth,net,web3", - ) -> Awaitable[bool]: - return await self._start_http(host, port, cors, apis) # type: ignore + _import_raw_key: Method[Callable[[str, str], Awaitable[ChecksumAddress]]] = Method( + RPC.personal_importRawKey, + mungers=[default_root_munger], + ) - async def start_ws( - self, - host: str = "localhost", - port: int = 8546, - cors: str = "", - apis: str = "eth,net,web3", - ) -> Awaitable[bool]: - return await self._start_ws(host, port, cors, apis) # type: ignore + async def import_raw_key( + self, private_key: str, passphrase: str + ) -> ChecksumAddress: + return await self._import_raw_key(private_key, passphrase) - async def stop_http(self) -> Awaitable[bool]: - return await self._stop_http() # type: ignore + # list_accounts and list_wallets - async def stop_ws(self) -> Awaitable[bool]: - return await self._stop_ws() # type: ignore + _list_accounts: Method[Callable[[], Awaitable[List[ChecksumAddress]]]] = Method( + RPC.personal_listAccounts, + is_property=True, + ) + _list_wallets: Method[Callable[[], Awaitable[List[GethWallet]]]] = Method( + RPC.personal_listWallets, + is_property=True, + ) -class GethMiner(Module): - """ - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#miner - """ + async def list_accounts(self) -> List[ChecksumAddress]: + return await self._list_accounts() - make_dag = make_dag - set_extra = set_extra - set_etherbase = set_etherbase - set_gas_price = set_gas_price - start = start - stop = stop - start_auto_dag = start_auto_dag - stop_auto_dag = stop_auto_dag + async def list_wallets(self) -> List[GethWallet]: + return await self._list_wallets() + # send_transaction -class Geth(Module): - personal: GethPersonal - admin: GethAdmin + _send_transaction: Method[Callable[[TxParams, str], Awaitable[HexBytes]]] = Method( + RPC.personal_sendTransaction, + mungers=[default_root_munger], + ) + + async def send_transaction( + self, transaction: TxParams, passphrase: str + ) -> HexBytes: + return await self._send_transaction(transaction, passphrase) + + # sign and sign_typed_data + + _sign: Method[ + Callable[[str, ChecksumAddress, Optional[str]], Awaitable[HexStr]] + ] = Method( + RPC.personal_sign, + mungers=[default_root_munger], + ) + + _sign_typed_data: Method[ + Callable[[Dict[str, Any], ChecksumAddress, str], Awaitable[HexStr]] + ] = Method( + RPC.personal_signTypedData, + mungers=[default_root_munger], + ) + + async def sign( + self, message: str, account: ChecksumAddress, passphrase: str + ) -> HexStr: + return await self._sign(message, account, passphrase) + + async def sign_typed_data( + self, message: Dict[str, Any], account: ChecksumAddress, passphrase: str + ) -> HexStr: + return await self._sign_typed_data(message, account, passphrase) + + # new_account, lock_account, and unlock_account + + _new_account: Method[Callable[[str], Awaitable[ChecksumAddress]]] = Method( + RPC.personal_newAccount, + mungers=[default_root_munger], + ) + + _lock_account: Method[Callable[[ChecksumAddress], Awaitable[bool]]] = Method( + RPC.personal_lockAccount, + mungers=[default_root_munger], + ) + + _unlock_account: Method[ + Callable[[ChecksumAddress, str, Optional[int]], Awaitable[bool]] + ] = Method( + RPC.personal_unlockAccount, + mungers=[default_root_munger], + ) + + async def new_account(self, passphrase: str) -> ChecksumAddress: + return await self._new_account(passphrase) + + async def lock_account(self, account: ChecksumAddress) -> bool: + return await self._lock_account(account) + + async def unlock_account( + self, account: ChecksumAddress, passphrase: str, duration: Optional[int] = None + ) -> bool: + return await self._unlock_account(account, passphrase, duration) + + +class AsyncGeth(Module): + is_async = True + + personal: AsyncGethPersonal + admin: AsyncGethAdmin + txpool: AsyncGethTxPool diff --git a/web3/main.py b/web3/main.py index 96aa88a43f..5d89a4cbdf 100644 --- a/web3/main.py +++ b/web3/main.py @@ -78,6 +78,7 @@ Eth, ) from web3.geth import ( + AsyncGeth, AsyncGethAdmin, AsyncGethPersonal, AsyncGethTxPool, @@ -120,6 +121,7 @@ Testing, ) from web3.types import ( + AsyncMiddlewareOnion, MiddlewareOnion, Wei, ) @@ -134,7 +136,7 @@ def get_async_default_modules() -> Dict[str, Union[Type[Module], Sequence[Any]]] "eth": AsyncEth, "net": AsyncNet, "geth": ( - Geth, + AsyncGeth, { "admin": AsyncGethAdmin, "personal": AsyncGethPersonal, @@ -161,7 +163,7 @@ def get_default_modules() -> Dict[str, Union[Type[Module], Sequence[Any]]]: } -class Web3: +class BaseWeb3: _strict_bytes_type_checking = True # Providers @@ -174,6 +176,11 @@ class Web3: # Managers RequestManager = DefaultRequestManager + # mypy types + eth: Union[Eth, AsyncEth] + net: Union[Net, AsyncNet] + geth: Union[Geth, AsyncGeth] + # Encoding and Decoding @staticmethod @wraps(to_bytes) @@ -235,54 +242,6 @@ def is_checksum_address(value: Any) -> bool: def to_checksum_address(value: Union[AnyAddress, str, bytes]) -> ChecksumAddress: return to_checksum_address(value) - # mypy Types - eth: Eth - geth: Geth - net: Net - - def __init__( - self, - provider: Optional[Union[BaseProvider, AsyncBaseProvider]] = None, - middlewares: Optional[Sequence[Any]] = None, - modules: Optional[Dict[str, Union[Type[Module], Sequence[Any]]]] = None, - external_modules: Optional[ - Dict[str, Union[Type[Module], Sequence[Any]]] - ] = None, - ens: Union[ENS, AsyncENS, "Empty"] = empty, - ) -> None: - self.manager = self.RequestManager(self, provider, middlewares) - self.codec = ABICodec(build_strict_registry()) - - if modules is None: - modules = ( - get_async_default_modules() - if provider and provider.is_async - else get_default_modules() - ) - - self.attach_modules(modules) - - if external_modules is not None: - self.attach_modules(external_modules) - - self.ens = ens - - @property - def middleware_onion(self) -> MiddlewareOnion: - return self.manager.middleware_onion - - @property - def provider(self) -> Union[BaseProvider, AsyncBaseProvider]: - return self.manager.provider - - @provider.setter - def provider(self, provider: Union[BaseProvider, AsyncBaseProvider]) -> None: - self.manager.provider = provider - - @property - def client_version(self) -> str: - return self.manager.request_blocking(RPC.web3_clientVersion, []) - @property def api(self) -> str: from web3 import __version__ @@ -320,6 +279,12 @@ def keccak( "keccak(b'\\x74\\x78\\x74'), or keccak(0x747874)." ) + @classmethod + def normalize_values( + cls, _w3: "BaseWeb3", abi_types: List[TypeStr], values: List[Any] + ) -> List[Any]: + return map_abi_data(zip(abi_types, values)) + @combomethod def solidity_keccak(cls, abi_types: List[TypeStr], values: List[Any]) -> bytes: """ @@ -337,7 +302,7 @@ def solidity_keccak(cls, abi_types: List[TypeStr], values: List[Any]) -> bytes: w3 = None else: w3 = cls - normalized_values = map_abi_data([abi_ens_resolver(w3)], abi_types, values) + normalized_values = cls.normalize_values(w3, abi_types, values) hex_string = add_0x_prefix( HexStr( @@ -357,31 +322,9 @@ def attach_modules( """ _attach_modules(self, modules) - def is_connected(self) -> Union[bool, Coroutine[Any, Any, bool]]: - return self.provider.is_connected() - def is_encodable(self, _type: TypeStr, value: Any) -> bool: return self.codec.is_encodable(_type, value) - @property - def ens(self) -> Union[ENS, AsyncENS, "Empty"]: - if self._ens is empty: - ns = ( - AsyncENS.from_web3(self) - if self.provider.is_async - else ENS.from_web3(self) - ) - ns.w3 = self # set self object reference for ``ENS.w3`` - return cast(AsyncENS, ns) if self.provider.is_async else cast(ENS, ns) - - return self._ens - - @ens.setter - def ens(self, new_ens: Union[ENS, AsyncENS, "Empty"]) -> None: - if new_ens: - new_ens.w3 = self # set self object reference for ``ENS.w3`` - self._ens = new_ens - @property def pm(self) -> "PM": if hasattr(self, "_pm"): @@ -400,3 +343,136 @@ def enable_unstable_package_management_api(self) -> None: if not hasattr(self, "_pm"): self.attach_modules({"_pm": PM}) + + +class AsyncWeb3(BaseWeb3): + # mypy Types + eth: AsyncEth + net: AsyncNet + geth: AsyncGeth + + def __init__( + self, + provider: Optional[AsyncBaseProvider] = None, + middlewares: Optional[Sequence[Any]] = None, + modules: Optional[Dict[str, Union[Type[Module], Sequence[Any]]]] = None, + external_modules: Optional[ + Dict[str, Union[Type[Module], Sequence[Any]]] + ] = None, + ens: Union[AsyncENS, "Empty"] = empty, + ) -> None: + self.manager = self.RequestManager(self, provider, middlewares) + self.codec = ABICodec(build_strict_registry()) + + if modules is None: + modules = get_async_default_modules() + + self.attach_modules(modules) + + if external_modules is not None: + self.attach_modules(external_modules) + + self.ens = ens + + def is_connected(self) -> Coroutine[Any, Any, bool]: + return self.provider.is_connected() + + @property + def middleware_onion(self) -> AsyncMiddlewareOnion: + return cast(AsyncMiddlewareOnion, self.manager.middleware_onion) + + @property + def provider(self) -> AsyncBaseProvider: + return cast(AsyncBaseProvider, self.manager.provider) + + @provider.setter + def provider(self, provider: AsyncBaseProvider) -> None: + self.manager.provider = provider + + @property + async def client_version(self) -> str: + return await self.manager.coro_request(RPC.web3_clientVersion, []) + + @property + def ens(self) -> Union[AsyncENS, "Empty"]: + if self._ens is empty: + ns = AsyncENS.from_web3(self) + ns.w3 = self + return ns + return self._ens + + @ens.setter + def ens(self, new_ens: Union[AsyncENS, "Empty"]) -> None: + if new_ens: + new_ens.w3 = self # set self object reference for ``AsyncENS.w3`` + self._ens = new_ens + + +class Web3(BaseWeb3): + # mypy types + eth: Eth + net: Net + geth: Geth + + def __init__( + self, + provider: Optional[BaseProvider] = None, + middlewares: Optional[Sequence[Any]] = None, + modules: Optional[Dict[str, Union[Type[Module], Sequence[Any]]]] = None, + external_modules: Optional[ + Dict[str, Union[Type[Module], Sequence[Any]]] + ] = None, + ens: Union[ENS, "Empty"] = empty, + ) -> None: + self.manager = self.RequestManager(self, provider, middlewares) + self.codec = ABICodec(build_strict_registry()) + + if modules is None: + modules = get_default_modules() + + self.attach_modules(modules) + + if external_modules is not None: + self.attach_modules(external_modules) + + self.ens = ens + + def is_connected(self) -> bool: + return self.provider.is_connected() + + @classmethod + def normalize_values( + cls, w3: "BaseWeb3", abi_types: List[TypeStr], values: List[Any] + ) -> List[Any]: + return map_abi_data([abi_ens_resolver(w3)], abi_types, values) + + @property + def middleware_onion(self) -> MiddlewareOnion: + return cast(MiddlewareOnion, self.manager.middleware_onion) + + @property + def provider(self) -> BaseProvider: + return cast(BaseProvider, self.manager.provider) + + @provider.setter + def provider(self, provider: BaseProvider) -> None: + self.manager.provider = provider + + @property + def client_version(self) -> str: + return self.manager.request_blocking(RPC.web3_clientVersion, []) + + @property + def ens(self) -> Union[ENS, "Empty"]: + if self._ens is empty: + ns = ENS.from_web3(self) + ns.w3 = self + return ns + + return self._ens + + @ens.setter + def ens(self, new_ens: Union[ENS, "Empty"]) -> None: + if new_ens: + new_ens.w3 = self # set self object reference for ``ENS.w3`` + self._ens = new_ens diff --git a/web3/manager.py b/web3/manager.py index 2f8f5bfd8a..170fd673a8 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -43,6 +43,8 @@ AutoProvider, ) from web3.types import ( + AsyncMiddleware, + AsyncMiddlewareOnion, Middleware, MiddlewareOnion, RPCEndpoint, @@ -50,7 +52,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) from web3.providers import ( # noqa: F401 AsyncBaseProvider, BaseProvider, @@ -86,11 +91,19 @@ def apply_null_result_formatters( class RequestManager: logger = logging.getLogger("web3.RequestManager") + middleware_onion: Union[ + MiddlewareOnion, AsyncMiddlewareOnion, NamedElementOnion[None, None] + ] + def __init__( self, - w3: "Web3", + w3: Union["AsyncWeb3", "Web3"], provider: Optional[Union["BaseProvider", "AsyncBaseProvider"]] = None, - middlewares: Optional[Sequence[Tuple[Middleware, str]]] = None, + middlewares: Optional[ + Union[ + Sequence[Tuple[Middleware, str]], Sequence[Tuple[AsyncMiddleware, str]] + ] + ] = None, ) -> None: self.w3 = w3 @@ -103,12 +116,12 @@ def __init__( middlewares = ( self.async_default_middlewares() if self.provider.is_async - else self.default_middlewares(w3) + else self.default_middlewares(cast("Web3", w3)) ) - self.middleware_onion: MiddlewareOnion = NamedElementOnion(middlewares) + self.middleware_onion = NamedElementOnion(middlewares) - w3: "Web3" = None + w3: Union["AsyncWeb3", "Web3"] = None _provider = None @property @@ -137,7 +150,7 @@ def default_middlewares(w3: "Web3") -> List[Tuple[Middleware, str]]: ] @staticmethod - def async_default_middlewares() -> List[Tuple[Middleware, str]]: + def async_default_middlewares() -> List[Tuple[AsyncMiddleware, str]]: """ List the default async middlewares for the request manager. """ @@ -155,18 +168,22 @@ def _make_request( self, method: Union[RPCEndpoint, Callable[..., RPCEndpoint]], params: Any ) -> RPCResponse: provider = cast("BaseProvider", self.provider) - request_func = provider.request_func(self.w3, self.middleware_onion) + request_func = provider.request_func( + cast("Web3", self.w3), cast(MiddlewareOnion, self.middleware_onion) + ) self.logger.debug(f"Making request. Method: {method}") return request_func(method, params) async def _coro_make_request( self, method: Union[RPCEndpoint, Callable[..., RPCEndpoint]], params: Any ) -> RPCResponse: - # type ignored b/c request_func is an awaitable in async model - request_func = await self.provider.request_func( # type: ignore - self.w3, self.middleware_onion + provider = cast("AsyncBaseProvider", self.provider) + request_func = await provider.request_func( + cast("AsyncWeb3", self.w3), + cast(AsyncMiddlewareOnion, self.middleware_onion), ) self.logger.debug(f"Making request. Method: {method}") + return await request_func(method, params) @staticmethod diff --git a/web3/middleware/__init__.py b/web3/middleware/__init__.py index 474db99d88..fa8c2d358c 100644 --- a/web3/middleware/__init__.py +++ b/web3/middleware/__init__.py @@ -1,5 +1,6 @@ import functools from typing import ( + Coroutine, TYPE_CHECKING, Any, Callable, @@ -7,6 +8,7 @@ ) from web3.types import ( + AsyncMiddleware, Middleware, RPCEndpoint, RPCResponse, @@ -84,7 +86,7 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import AsyncWeb3, Web3 # noqa: F401 def combine_middlewares( @@ -104,21 +106,25 @@ def combine_middlewares( async def async_combine_middlewares( - middlewares: Sequence[Middleware], - w3: "Web3", + middlewares: Sequence[AsyncMiddleware], + async_w3: "AsyncWeb3", provider_request_fn: Callable[[RPCEndpoint, Any], Any], -) -> Callable[..., RPCResponse]: +) -> Callable[..., Coroutine[Any, Any, RPCResponse]]: """ Returns a callable function which will call the provider.provider_request function wrapped with all of the middlewares. """ accumulator_fn = provider_request_fn for middleware in reversed(middlewares): - accumulator_fn = await construct_middleware(middleware, accumulator_fn, w3) + accumulator_fn = await construct_middleware( + middleware, accumulator_fn, async_w3 + ) return accumulator_fn async def construct_middleware( - middleware: Middleware, fn: Callable[..., RPCResponse], w3: "Web3" -) -> Callable[[RPCEndpoint, Any], Any]: - return await middleware(fn, w3) + async_middleware: AsyncMiddleware, + fn: Callable[..., RPCResponse], + async_w3: "AsyncWeb3", +) -> Callable[[RPCEndpoint, Any], RPCResponse]: + return await async_middleware(fn, async_w3) diff --git a/web3/middleware/async_cache.py b/web3/middleware/async_cache.py index 87f072026e..cc30dc1c63 100644 --- a/web3/middleware/async_cache.py +++ b/web3/middleware/async_cache.py @@ -21,6 +21,7 @@ ) from web3.types import ( AsyncMiddleware, + AsyncMiddlewareCoroutine, Middleware, RPCEndpoint, RPCResponse, @@ -30,7 +31,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) _async_request_thread_pool = ThreadPoolExecutor() @@ -41,7 +45,7 @@ async def async_construct_simple_cache_middleware( should_cache_fn: Callable[ [RPCEndpoint, Any, RPCResponse], bool ] = _should_cache_response, -) -> Middleware: +) -> AsyncMiddleware: """ Constructs a middleware which caches responses based on the request ``method`` and ``params`` @@ -56,8 +60,8 @@ async def async_construct_simple_cache_middleware( cache = SimpleCache(256) async def async_simple_cache_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], _async_w3: "Web3" - ) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], _async_w3: "AsyncWeb3" + ) -> AsyncMiddlewareCoroutine: lock = threading.Lock() async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: @@ -83,7 +87,7 @@ async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def _async_simple_cache_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "Web3" + make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "AsyncWeb3" ) -> Middleware: middleware = await async_construct_simple_cache_middleware() return await middleware(make_request, async_w3) diff --git a/web3/middleware/attrdict.py b/web3/middleware/attrdict.py index 33e78bb6e2..4bb2b456cd 100644 --- a/web3/middleware/attrdict.py +++ b/web3/middleware/attrdict.py @@ -12,13 +12,16 @@ AttributeDict, ) from web3.types import ( - AsyncMiddleware, + AsyncMiddlewareCoroutine, RPCEndpoint, RPCResponse, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) def attrdict_middleware( @@ -48,8 +51,8 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def async_attrdict_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], _async_w3: "Web3" -) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], _async_w3: "AsyncWeb3" +) -> AsyncMiddlewareCoroutine: """ Converts any result which is a dictionary into an `AttributeDict`. diff --git a/web3/middleware/buffered_gas_estimate.py b/web3/middleware/buffered_gas_estimate.py index 5a011516ca..60f1cc739b 100644 --- a/web3/middleware/buffered_gas_estimate.py +++ b/web3/middleware/buffered_gas_estimate.py @@ -15,13 +15,16 @@ get_buffered_gas_estimate, ) from web3.types import ( - AsyncMiddleware, + AsyncMiddlewareCoroutine, RPCEndpoint, RPCResponse, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3.main import ( # noqa: F401 + AsyncWeb3, + Web3, + ) def buffered_gas_estimate_middleware( @@ -43,8 +46,8 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def async_buffered_gas_estimate_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" -) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], w3: "AsyncWeb3" +) -> AsyncMiddlewareCoroutine: async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: if method == "eth_sendTransaction": transaction = params[0] diff --git a/web3/middleware/fixture.py b/web3/middleware/fixture.py index 24a567e68e..78b63ef047 100644 --- a/web3/middleware/fixture.py +++ b/web3/middleware/fixture.py @@ -7,13 +7,17 @@ from web3.types import ( AsyncMiddleware, + AsyncMiddlewareCoroutine, Middleware, RPCEndpoint, RPCResponse, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3.main import ( # noqa: F401 + AsyncWeb3, + Web3, + ) def construct_fixture_middleware(fixtures: Dict[RPCEndpoint, Any]) -> Middleware: @@ -92,15 +96,15 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def async_construct_result_generator_middleware( result_generators: Dict[RPCEndpoint, Any] -) -> Middleware: +) -> AsyncMiddleware: """ Constructs a middleware which returns a static response for any method which is found in the provided fixtures. """ async def result_generator_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], _: "Web3" - ) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], _: "AsyncWeb3" + ) -> AsyncMiddlewareCoroutine: async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: if method in result_generators: result = result_generators[method](method, params) @@ -115,7 +119,7 @@ async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def async_construct_error_generator_middleware( error_generators: Dict[RPCEndpoint, Any] -) -> Middleware: +) -> AsyncMiddleware: """ Constructs a middleware which intercepts requests for any method found in the provided mapping of endpoints to generator functions, returning @@ -124,8 +128,8 @@ async def async_construct_error_generator_middleware( """ async def error_generator_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], _: "Web3" - ) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], _: "AsyncWeb3" + ) -> AsyncMiddlewareCoroutine: async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: if method in error_generators: error_msg = error_generators[method](method, params) diff --git a/web3/middleware/formatting.py b/web3/middleware/formatting.py index 28c68e54ea..d6637085d8 100644 --- a/web3/middleware/formatting.py +++ b/web3/middleware/formatting.py @@ -13,6 +13,7 @@ from web3.types import ( AsyncMiddleware, + AsyncMiddlewareCoroutine, Formatters, FormattersDict, Literal, @@ -22,7 +23,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) FORMATTER_DEFAULTS: FormattersDict = { "request_formatters": {}, @@ -110,9 +114,9 @@ async def async_construct_formatting_middleware( request_formatters: Optional[Formatters] = None, result_formatters: Optional[Formatters] = None, error_formatters: Optional[Formatters] = None, -) -> Middleware: +) -> AsyncMiddleware: async def ignore_web3_in_standard_formatters( - _w3: "Web3", + _async_w3: "AsyncWeb3", _method: RPCEndpoint, ) -> FormattersDict: return dict( @@ -128,19 +132,20 @@ async def ignore_web3_in_standard_formatters( async def async_construct_web3_formatting_middleware( async_web3_formatters_builder: Callable[ - ["Web3", RPCEndpoint], Coroutine[Any, Any, FormattersDict] + ["AsyncWeb3", RPCEndpoint], Coroutine[Any, Any, FormattersDict] ] ) -> Callable[ - [Callable[[RPCEndpoint, Any], Any], "Web3"], Coroutine[Any, Any, AsyncMiddleware] + [Callable[[RPCEndpoint, Any], Any], "AsyncWeb3"], + Coroutine[Any, Any, AsyncMiddlewareCoroutine], ]: async def formatter_middleware( make_request: Callable[[RPCEndpoint, Any], Any], - async_w3: "Web3", - ) -> AsyncMiddleware: + w3: "AsyncWeb3", + ) -> AsyncMiddlewareCoroutine: async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: formatters = merge( FORMATTER_DEFAULTS, - await async_web3_formatters_builder(async_w3, method), + await async_web3_formatters_builder(w3, method), ) request_formatters = formatters.pop("request_formatters") diff --git a/web3/middleware/gas_price_strategy.py b/web3/middleware/gas_price_strategy.py index 4a23c0beca..9abdfb6b28 100644 --- a/web3/middleware/gas_price_strategy.py +++ b/web3/middleware/gas_price_strategy.py @@ -21,7 +21,7 @@ TransactionTypeMismatch, ) from web3.types import ( - AsyncMiddleware, + AsyncMiddlewareCoroutine, BlockData, RPCEndpoint, RPCResponse, @@ -30,7 +30,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) def validate_transaction_params( @@ -96,8 +99,8 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def async_gas_price_strategy_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" -) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "AsyncWeb3" +) -> AsyncMiddlewareCoroutine: """ - Uses a gas price strategy if one is set. This is only supported for legacy transactions. It is recommended to send dynamic fee transactions @@ -109,8 +112,8 @@ async def async_gas_price_strategy_middleware( async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: if method == "eth_sendTransaction": transaction = params[0] - generated_gas_price = w3.eth.generate_gas_price(transaction) - latest_block = await w3.eth.get_block("latest") # type: ignore + generated_gas_price = async_w3.eth.generate_gas_price(transaction) + latest_block = await async_w3.eth.get_block("latest") transaction = validate_transaction_params( transaction, latest_block, generated_gas_price ) diff --git a/web3/middleware/geth_poa.py b/web3/middleware/geth_poa.py index 2e8e5ae33a..9cd7ba93f1 100644 --- a/web3/middleware/geth_poa.py +++ b/web3/middleware/geth_poa.py @@ -26,12 +26,15 @@ construct_formatting_middleware, ) from web3.types import ( - AsyncMiddleware, + AsyncMiddlewareCoroutine, RPCEndpoint, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) is_not_null = complement(is_null) @@ -59,8 +62,8 @@ async def async_geth_poa_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" -) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], w3: "AsyncWeb3" +) -> AsyncMiddlewareCoroutine: middleware = await async_construct_formatting_middleware( result_formatters={ RPC.eth_getBlockByHash: apply_formatter_if(is_not_null, geth_poa_cleanup), diff --git a/web3/middleware/stalecheck.py b/web3/middleware/stalecheck.py index 0dab5bc9b5..16b98715a3 100644 --- a/web3/middleware/stalecheck.py +++ b/web3/middleware/stalecheck.py @@ -13,6 +13,7 @@ ) from web3.types import ( AsyncMiddleware, + AsyncMiddlewareCoroutine, BlockData, Middleware, RPCEndpoint, @@ -20,7 +21,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) SKIP_STALECHECK_FOR_METHODS = ("eth_getBlockByNumber",) @@ -78,7 +82,7 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: async def async_make_stalecheck_middleware( allowable_delay: int, skip_stalecheck_for_methods: Collection[str] = SKIP_STALECHECK_FOR_METHODS, -) -> Middleware: +) -> AsyncMiddleware: """ Use to require that a function will run only of the blockchain is recently updated. @@ -96,14 +100,14 @@ async def async_make_stalecheck_middleware( ) async def stalecheck_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" - ) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], w3: "AsyncWeb3" + ) -> AsyncMiddlewareCoroutine: cache: Dict[str, Optional[BlockData]] = {"latest": None} async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: if method not in skip_stalecheck_for_methods: if not _is_fresh(cache["latest"], allowable_delay): - latest = await w3.eth.get_block("latest") # type: ignore + latest = await w3.eth.get_block("latest") if _is_fresh(latest, allowable_delay): cache["latest"] = latest else: diff --git a/web3/middleware/validation.py b/web3/middleware/validation.py index b633a6099a..21629319b7 100644 --- a/web3/middleware/validation.py +++ b/web3/middleware/validation.py @@ -37,7 +37,7 @@ construct_web3_formatting_middleware, ) from web3.types import ( - AsyncMiddleware, + AsyncMiddlewareCoroutine, Formatters, FormattersDict, RPCEndpoint, @@ -45,7 +45,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import ( # noqa: F401 + AsyncWeb3, + Web3, + ) MAX_EXTRADATA_LENGTH = 32 @@ -150,11 +153,11 @@ def build_method_validators(w3: "Web3", method: RPCEndpoint) -> FormattersDict: async def async_build_method_validators( - async_w3: "Web3", method: RPCEndpoint + async_w3: "AsyncWeb3", method: RPCEndpoint ) -> FormattersDict: request_formatters: Formatters = {} if RPCEndpoint(method) in METHODS_TO_VALIDATE: - w3_chain_id = await async_w3.eth.chain_id # type: ignore + w3_chain_id = await async_w3.eth.chain_id for method in METHODS_TO_VALIDATE: request_formatters[method] = _chain_id_validator(w3_chain_id) @@ -162,8 +165,8 @@ async def async_build_method_validators( async def async_validation_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" -) -> AsyncMiddleware: + make_request: Callable[[RPCEndpoint, Any], Any], w3: "AsyncWeb3" +) -> AsyncMiddlewareCoroutine: middleware = await async_construct_web3_formatting_middleware( async_build_method_validators ) diff --git a/web3/module.py b/web3/module.py index 173f331380..14777e62fb 100644 --- a/web3/module.py +++ b/web3/module.py @@ -29,7 +29,10 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3.main import ( # noqa: F401 + AsyncWeb3, + Web3, + ) @curry @@ -54,7 +57,7 @@ def caller(*args: Any, **kwargs: Any) -> Union[TReturn, LogFilter]: try: (method_str, params), response_formatters = method.process_params( module, *args, **kwargs - ) # noqa: E501 + ) except _UseExistingFilter as err: return LogFilter(eth_module=module, filter_id=err.filter_id) ( @@ -72,7 +75,7 @@ def caller(*args: Any, **kwargs: Any) -> Union[TReturn, LogFilter]: @curry def retrieve_async_method_call_fn( - w3: "Web3", module: "Module", method: Method[Callable[..., Any]] + async_w3: "AsyncWeb3", module: "Module", method: Method[Callable[..., Any]] ) -> Callable[..., Coroutine[Any, Any, Union[RPCResponse, AsyncLogFilter]]]: async def caller(*args: Any, **kwargs: Any) -> Union[RPCResponse, AsyncLogFilter]: try: @@ -87,7 +90,7 @@ async def caller(*args: Any, **kwargs: Any) -> Union[RPCResponse, AsyncLogFilter error_formatters, null_result_formatters, ) = response_formatters - result = await w3.manager.coro_request( + result = await async_w3.manager.coro_request( method_str, params, error_formatters, null_result_formatters ) return apply_result_formatters(result_formatters, result) @@ -102,7 +105,7 @@ async def caller(*args: Any, **kwargs: Any) -> Union[RPCResponse, AsyncLogFilter class Module: is_async = False - def __init__(self, w3: "Web3") -> None: + def __init__(self, w3: Union["AsyncWeb3", "Web3"]) -> None: if self.is_async: self.retrieve_caller_fn = retrieve_async_method_call_fn(w3, self) else: diff --git a/web3/pm.py b/web3/pm.py index e71b24a02d..0da4b38370 100644 --- a/web3/pm.py +++ b/web3/pm.py @@ -331,6 +331,9 @@ class PM(Module): its ``registry`` attribute. """ + # mypy types + w3: "Web3" + def get_package_from_manifest(self, manifest: Manifest) -> Package: """ Returns a `Package `__ # noqa: E501 diff --git a/web3/providers/async_base.py b/web3/providers/async_base.py index b411a883da..2ea083b8cc 100644 --- a/web3/providers/async_base.py +++ b/web3/providers/async_base.py @@ -3,6 +3,7 @@ TYPE_CHECKING, Any, Callable, + Coroutine, Sequence, Tuple, cast, @@ -21,20 +22,23 @@ async_combine_middlewares, ) from web3.types import ( - Middleware, + AsyncMiddleware, + AsyncMiddlewareOnion, MiddlewareOnion, RPCEndpoint, RPCResponse, ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3 import AsyncWeb3 # noqa: F401 class AsyncBaseProvider: - _middlewares: Tuple[Middleware, ...] = () + _middlewares: Tuple[AsyncMiddleware, ...] = () # a tuple of (all_middlewares, request_func) - _request_func_cache: Tuple[Tuple[Middleware, ...], Callable[..., RPCResponse]] = ( + _request_func_cache: Tuple[ + Tuple[AsyncMiddleware, ...], Callable[..., Coroutine[Any, Any, RPCResponse]] + ] = ( None, None, ) @@ -50,7 +54,7 @@ def __init__(self) -> None: ) @property - def middlewares(self) -> Tuple[Middleware, ...]: + def middlewares(self) -> Tuple[AsyncMiddleware, ...]: return self._middlewares @middlewares.setter @@ -59,24 +63,25 @@ def middlewares(self, values: MiddlewareOnion) -> None: self._middlewares = tuple(values) # type: ignore async def request_func( - self, w3: "Web3", outer_middlewares: MiddlewareOnion - ) -> Callable[[RPCEndpoint], Any]: - all_middlewares: Tuple[Middleware] = tuple(outer_middlewares) + tuple(self.middlewares) # type: ignore # noqa: E501 + self, async_w3: "AsyncWeb3", outer_middlewares: AsyncMiddlewareOnion + ) -> Callable[..., Coroutine[Any, Any, RPCResponse]]: + # type ignored b/c tuple(MiddlewareOnion) converts to tuple of middlewares + all_middlewares: Tuple[AsyncMiddleware] = tuple(outer_middlewares) + tuple(self.middlewares) # type: ignore # noqa: E501 cache_key = self._request_func_cache[0] if cache_key is None or cache_key != all_middlewares: self._request_func_cache = ( all_middlewares, - await self._generate_request_func(w3, all_middlewares), + await self._generate_request_func(async_w3, all_middlewares), ) return self._request_func_cache[-1] async def _generate_request_func( - self, w3: "Web3", middlewares: Sequence[Middleware] - ) -> Callable[..., RPCResponse]: + self, async_w3: "AsyncWeb3", middlewares: Sequence[AsyncMiddleware] + ) -> Callable[..., Coroutine[Any, Any, RPCResponse]]: return await async_combine_middlewares( middlewares=middlewares, - w3=w3, + async_w3=async_w3, provider_request_fn=self.make_request, ) @@ -89,6 +94,7 @@ async def is_connected(self) -> bool: class AsyncJSONBaseProvider(AsyncBaseProvider): def __init__(self) -> None: + super().__init__() self.request_counter = itertools.count() def encode_rpc_request(self, method: RPCEndpoint, params: Any) -> bytes: diff --git a/web3/providers/eth_tester/middleware.py b/web3/providers/eth_tester/middleware.py index 208efa700c..e1a3180c2f 100644 --- a/web3/providers/eth_tester/middleware.py +++ b/web3/providers/eth_tester/middleware.py @@ -47,6 +47,7 @@ async_construct_formatting_middleware, ) from web3.types import ( + AsyncMiddlewareCoroutine, Middleware, RPCEndpoint, RPCResponse, @@ -55,6 +56,7 @@ if TYPE_CHECKING: from web3 import ( # noqa: F401 + AsyncWeb3, Web3, ) @@ -333,11 +335,32 @@ def fill_default( return assoc(transaction, field, guess_val) +def default_transaction_fields_middleware( + make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" +) -> Callable[[RPCEndpoint, Any], RPCResponse]: + def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: + if method in ( + "eth_call", + "eth_estimateGas", + "eth_sendTransaction", + ): + fill_default_from = fill_default("from", guess_from, w3) + filled_transaction = pipe( + params[0], + fill_default_from, + ) + return make_request(method, [filled_transaction] + list(params)[1:]) + else: + return make_request(method, params) + + return middleware + + # --- async --- # async def async_ethereum_tester_middleware( # type: ignore - make_request, web3: "Web3" + make_request, web3: "AsyncWeb3" ) -> Middleware: middleware = await async_construct_formatting_middleware( request_formatters=request_formatters, result_formatters=result_formatters @@ -345,9 +368,11 @@ async def async_ethereum_tester_middleware( # type: ignore return await middleware(make_request, web3) -async def async_guess_from(async_w3: "Web3", _: TxParams) -> Optional[ChecksumAddress]: - coinbase = await async_w3.eth.coinbase # type: ignore - accounts = await async_w3.eth.accounts # type: ignore +async def async_guess_from( + async_w3: "AsyncWeb3", _: TxParams +) -> Optional[ChecksumAddress]: + coinbase = await async_w3.eth.coinbase + accounts = await async_w3.eth.accounts if coinbase is not None: return coinbase elif accounts is not None and len(accounts) > 0: @@ -357,7 +382,10 @@ async def async_guess_from(async_w3: "Web3", _: TxParams) -> Optional[ChecksumAd @curry async def async_fill_default( - field: str, guess_func: Callable[..., Any], async_w3: "Web3", transaction: TxParams + field: str, + guess_func: Callable[..., Any], + async_w3: "AsyncWeb3", + transaction: TxParams, ) -> TxParams: # type ignored b/c TxParams keys must be string literal types if field in transaction and transaction[field] is not None: # type: ignore @@ -367,30 +395,9 @@ async def async_fill_default( return assoc(transaction, field, guess_val) -def default_transaction_fields_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method in ( - "eth_call", - "eth_estimateGas", - "eth_sendTransaction", - ): - fill_default_from = fill_default("from", guess_from, w3) - filled_transaction = pipe( - params[0], - fill_default_from, - ) - return make_request(method, [filled_transaction] + list(params)[1:]) - else: - return make_request(method, params) - - return middleware - - async def async_default_transaction_fields_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: + make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "AsyncWeb3" +) -> AsyncMiddlewareCoroutine: async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: if method in ( "eth_call", @@ -404,4 +411,4 @@ async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: else: return await make_request(method, params) - return middleware # type: ignore + return middleware diff --git a/web3/tools/benchmark/main.py b/web3/tools/benchmark/main.py index 66f53e92bc..360c1f89f4 100644 --- a/web3/tools/benchmark/main.py +++ b/web3/tools/benchmark/main.py @@ -19,13 +19,10 @@ from web3 import ( AsyncHTTPProvider, + AsyncWeb3, HTTPProvider, Web3, ) -from web3.eth import ( - AsyncEth, - Eth, -) from web3.middleware import ( async_buffered_gas_estimate_middleware, async_gas_price_strategy_middleware, @@ -73,15 +70,14 @@ def build_web3_http(endpoint_uri: str) -> Web3: return _w3 -async def build_async_w3_http(endpoint_uri: str) -> Web3: +async def build_async_w3_http(endpoint_uri: str) -> AsyncWeb3: await wait_for_aiohttp(endpoint_uri) - _w3 = Web3( + _w3 = AsyncWeb3( AsyncHTTPProvider(endpoint_uri), middlewares=[ async_gas_price_strategy_middleware, async_buffered_gas_estimate_middleware, ], - modules={"eth": AsyncEth}, ) return _w3 @@ -109,33 +105,29 @@ async def async_benchmark(func: Callable[..., Any], n: int) -> Union[float, str] return "N/A" -def unlocked_account(w3: "Web3") -> ChecksumAddress: +def unlocked_account(w3: Web3) -> ChecksumAddress: w3.geth.personal.unlock_account(w3.eth.coinbase, KEYFILE_PW) return w3.eth.coinbase -async def async_unlocked_account(w3: Web3, w3_eth: Eth) -> ChecksumAddress: - # change w3_eth type to w3_eth: AsyncEth once AsyncEth reflects Eth - coinbase = await w3_eth.coinbase # type: ignore - w3.geth.personal.unlock_account(coinbase, KEYFILE_PW) +async def async_unlocked_account(async_w3: AsyncWeb3) -> ChecksumAddress: + coinbase = await async_w3.eth.coinbase + await async_w3.geth.personal.unlock_account(coinbase, KEYFILE_PW) return coinbase def main(logger: logging.Logger, num_calls: int) -> None: fixture = GethBenchmarkFixture() for built_fixture in fixture.build(): - for process in built_fixture: + for _ in built_fixture: w3_http = build_web3_http(fixture.endpoint_uri) loop = asyncio.get_event_loop() async_w3_http = loop.run_until_complete( build_async_w3_http(fixture.endpoint_uri) ) - # TODO: swap out w3_http for the async_w3_http - # once GethPersonal module is async async_unlocked_acct = loop.run_until_complete( - async_unlocked_account(w3_http, async_w3_http.eth) + async_unlocked_account(async_w3_http) ) - methods = [ { "name": "eth_gasPrice", diff --git a/web3/types.py b/web3/types.py index 24fa43e05f..13aea38cae 100644 --- a/web3/types.py +++ b/web3/types.py @@ -37,7 +37,12 @@ ) if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 + from web3.contract.async_contract import AsyncContractFunction # noqa: F401 + from web3.contract.contract import ContractFunction # noqa: F401 + from web3.main import ( # noqa: F401 + AsyncWeb3, + Web3, + ) TReturn = TypeVar("TReturn") @@ -136,8 +141,14 @@ class RPCResponse(TypedDict, total=False): Middleware = Callable[[Callable[[RPCEndpoint, Any], RPCResponse], "Web3"], Any] -AsyncMiddleware = Callable[[RPCEndpoint, Any], Coroutine[Any, Any, RPCResponse]] +AsyncMiddlewareCoroutine = Callable[ + [RPCEndpoint, Any], Coroutine[Any, Any, RPCResponse] +] +AsyncMiddleware = Callable[ + [Callable[[RPCEndpoint, Any], RPCResponse], "AsyncWeb3"], Any +] MiddlewareOnion = NamedElementOnion[str, Middleware] +AsyncMiddlewareOnion = NamedElementOnion[str, AsyncMiddleware] class FormattersDict(TypedDict, total=False): @@ -346,6 +357,8 @@ class BlockData(TypedDict, total=False): transactions: Union[Sequence[HexBytes], Sequence[TxData]] transactionsRoot: HexBytes uncles: Sequence[HexBytes] + # geth_poa_middleware replaces extraData w/ proofOfAuthorityData + proofOfAuthorityData: HexBytes class Uncle(TypedDict): @@ -423,3 +436,8 @@ class GethWallet(TypedDict): accounts: Sequence[Dict[str, str]] status: str url: str + + +# Contract types + +TContractFn = TypeVar("TContractFn", "ContractFunction", "AsyncContractFunction")