diff --git a/docs/filters.rst b/docs/filters.rst index ae460473d7..c638499360 100644 --- a/docs/filters.rst +++ b/docs/filters.rst @@ -15,7 +15,7 @@ Filtering from web3.auto import w3 -The :meth:`web3.eth.Eth.filter` method can be used to setup filters for: +The :meth:`web3.eth.Eth.filter` method can be used to set up filters for: * Pending Transactions: ``web3.eth.filter('pending')`` @@ -44,8 +44,9 @@ The :meth:`web3.eth.Eth.filter` method can be used to setup filters for: .. note :: Creating event filters requires that your Ethereum node has an API support enabled for filters. - It does not work with Infura nodes. To get event logs on Infura or other - stateless nodes please see :class:`web3.contract.ContractEvents`. + Note that Infura support for filters does not offer access to `pending` filters. + To get event logs on other stateless nodes please see :class:`web3.contract.ContractEvents`. + Filter Class @@ -106,6 +107,11 @@ will return a new :class:`BlockFilter` object. new_block_filter = w3.eth.filter('latest') new_block_filter.get_new_entries() + .. note:: + + ``"safe"`` and ``"finalized"`` block identifiers are not yet supported for + ``eth_newBlockFilter``. + .. py:class:: TransactionFilter(...) ``TransactionFilter`` is a subclass of :class:`Filter`. @@ -158,7 +164,13 @@ In addition to being order-dependent, there are a few more points to recognize w - [A, B] "A in first position AND B in second position (and anything after)" - [[A, B], [A, B]] "(A OR B) in first position AND (A OR B) in second position (and anything after)" -See the JSON-RPC documentation for `eth_newFilter `_ more information on the standard filter parameters. +See the JSON-RPC documentation for `eth_newFilter `_ more information on the standard filter parameters. + + .. note:: + + Though ``"latest"`` and ``"safe"`` block identifiers are not yet part of the + specifications for ``eth_newFilter``, they are supported by web3.py and may or + may not yield expected results depending on the node being accessed. Creating a log filter by either of the above methods will return a :class:`LogFilter` instance. @@ -179,7 +191,7 @@ Getting events without setting up a filter ------------------------------------------ You can query an Ethereum node for direct fetch of events, without creating a filter first. -This works on all node types, including Infura. +This works on all node types. For examples see :meth:`web3.contract.ContractEvents.getLogs`. diff --git a/docs/web3.eth.rst b/docs/web3.eth.rst index 7d5a473772..8b7de7bf5c 100644 --- a/docs/web3.eth.rst +++ b/docs/web3.eth.rst @@ -432,8 +432,9 @@ The following methods are available on the ``web3.eth`` namespace. Returns the block specified by ``block_identifier``. Delegates to ``eth_getBlockByNumber`` if ``block_identifier`` is an integer or one of - the predefined block parameters ``'latest', 'earliest', 'pending'``, - otherwise delegates to ``eth_getBlockByHash``. Throws ``BlockNotFound`` error if the block is not found. + the predefined block parameters ``'latest', 'earliest', 'pending', + 'safe', 'finalized'`` - otherwise delegates to ``eth_getBlockByHash``. + Throws ``BlockNotFound`` error if the block is not found. If ``full_transactions`` is ``True`` then the ``'transactions'`` key will contain full transactions objects. Otherwise it will be an array of @@ -478,7 +479,9 @@ The following methods are available on the ``web3.eth`` namespace. ``block_identifier``. Delegates to ``eth_getBlockTransactionCountByNumber`` if ``block_identifier`` is an integer or one of the predefined block parameters ``'latest', 'earliest', - 'pending'``, otherwise delegates to ``eth_getBlockTransactionCountByHash``. Throws ``BlockNotFoundError`` if transactions are not found. + 'pending', 'safe', 'finalized'``, + otherwise delegates to ``eth_getBlockTransactionCountByHash``. + Throws ``BlockNotFoundError`` if transactions are not found. .. code-block:: python @@ -580,7 +583,7 @@ The following methods are available on the ``web3.eth`` namespace. * Delegates to ``eth_getTransactionByHash`` RPC Method - Returns the transaction specified by ``transaction_hash``. If the transaction has not yet been mined throws :class:`web3.exceptions.TransactionNotFound`. + Returns the transaction specified by ``transaction_hash``. If the transaction cannot be found throws :class:`web3.exceptions.TransactionNotFound`. .. code-block:: python @@ -636,7 +639,7 @@ The following methods are available on the ``web3.eth`` namespace. from the block specified by ``block_identifier``. Delegates to ``eth_getTransactionByBlockNumberAndIndex`` if ``block_identifier`` is an integer or one of the predefined block parameters ``'latest', 'earliest', - 'pending'``, otherwise delegates to + 'pending', 'safe', 'finalized'``, otherwise delegates to ``eth_getTransactionByBlockHashAndIndex``. If a transaction is not found at specified arguments, throws :class:`web3.exceptions.TransactionNotFound`. @@ -689,7 +692,7 @@ The following methods are available on the ``web3.eth`` namespace. from the block specified by ``block_identifier``. Delegates to ``eth_getRawTransactionByBlockNumberAndIndex`` if ``block_identifier`` is an integer or one of the predefined block parameters ``'latest', 'earliest', - 'pending'``, otherwise delegates to + 'pending', 'safe', 'finalized'``, otherwise delegates to ``eth_getRawTransactionByBlockHashAndIndex``. If a transaction is not found at specified arguments, throws :class:`web3.exceptions.TransactionNotFound`. @@ -742,7 +745,7 @@ The following methods are available on the ``web3.eth`` namespace. * Delegates to ``eth_getTransactionReceipt`` RPC Method - Returns the transaction receipt specified by ``transaction_hash``. If the transaction has not yet been mined throws :class:`web3.exceptions.TransactionNotFound`. + Returns the transaction receipt specified by ``transaction_hash``. If the transaction cannot be found throws :class:`web3.exceptions.TransactionNotFound`. If ``status`` in response equals 1 the transaction was successful. If it is equals 0 the transaction was reverted by EVM. @@ -1081,7 +1084,7 @@ The following methods are available on the ``web3.eth`` namespace. >>> myContract.functions.getVar().call() 1 # The above call equivalent to the raw call: - >>> we3.eth.call({'value': 0, 'gas': 21736, 'maxFeePerGas': 2000000000, 'maxPriorityFeePerGas': 1000000000, 'to': '0xc305c901078781C232A2a521C2aF7980f8385ee9', 'data': '0x477a5c98'}) + >>> w3.eth.call({'value': 0, 'gas': 21736, 'maxFeePerGas': 2000000000, 'maxPriorityFeePerGas': 1000000000, 'to': '0xc305c901078781C232A2a521C2aF7980f8385ee9', 'data': '0x477a5c98'}) HexBytes('0x0000000000000000000000000000000000000000000000000000000000000001') In most cases it is better to make contract function call through the :py:class:`web3.contract.Contract` interface. @@ -1217,11 +1220,11 @@ with the filtering API. dictionary with the following keys. * ``fromBlock``: ``integer/tag`` - (optional, default: "latest") Integer - block number, or "latest" for the last mined block or "pending", - "earliest" for not yet mined transactions. + block number, or one of predefined block identifiers + "latest", "pending", "earliest", "safe", or "finalized". * ``toBlock``: ``integer/tag`` - (optional, default: "latest") Integer - block number, or "latest" for the last mined block or "pending", - "earliest" for not yet mined transactions. + block number, or one of predefined block identifiers + "latest", "pending", "earliest", "safe", or "finalized". * ``address``: ``string`` or list of ``strings``, each 20 Bytes - (optional) Contract address or a list of addresses from which logs should originate. @@ -1230,6 +1233,11 @@ with the filtering API. This parameter can also be a list of topic lists in which case filtering will match any of the provided topic arrays. + .. note:: + + Though ``"latest"`` and ``"safe"`` block identifiers are not yet part of the + specifications for ``eth_newFilter``, they are supported by web3.py and may or + may not yield expected results depending on the node being accessed. See :doc:`./filters` for more information about filtering. @@ -1251,8 +1259,8 @@ with the filtering API. .. code-block:: python - >>> filt = web3.eth.filter() - >>> web3.eth.get_filter_changes(filt.filter_id) + >>> filter = web3.eth.filter() + >>> web3.eth.get_filter_changes(filter.filter_id) [ { 'address': '0xDc3A9Db694BCdd55EBaE4A89B22aC6D12b3F0c24', @@ -1284,8 +1292,8 @@ with the filtering API. .. code-block:: python - >>> filt = web3.eth.filter() - >>> web3.eth.get_filter_logs(filt.filter_id) + >>> filter = web3.eth.filter() + >>> web3.eth.get_filter_logs(filter.filter_id) [ { 'address': '0xDc3A9Db694BCdd55EBaE4A89B22aC6D12b3F0c24', @@ -1318,10 +1326,10 @@ with the filtering API. .. code-block:: python - >>> filt = web3.eth.filter() - >>> web3.eth.uninstall_filter(filt.filter_id) + >>> filter = web3.eth.filter() + >>> web3.eth.uninstall_filter(filter.filter_id) True - >>> web3.eth.uninstall_filter(filt.filter_id) + >>> web3.eth.uninstall_filter(filter.filter_id) False # already uninstalled. .. py:method:: Eth.uninstallFilter(self, filter_id) diff --git a/newsfragments/2655.feature.rst b/newsfragments/2655.feature.rst new file mode 100644 index 0000000000..75e1e48f5e --- /dev/null +++ b/newsfragments/2655.feature.rst @@ -0,0 +1 @@ +Add new predefined block identifiers ``safe`` and ``finalized``. diff --git a/tests/core/contracts/test_contract_util_functions.py b/tests/core/contracts/test_contract_util_functions.py index ed9fad0bc9..9b2a3df1b6 100644 --- a/tests/core/contracts/test_contract_util_functions.py +++ b/tests/core/contracts/test_contract_util_functions.py @@ -4,6 +4,7 @@ validate_payable, ) from web3.contract import ( + parse_block_identifier, parse_block_identifier_int, ) @@ -18,6 +19,23 @@ def test_parse_block_identifier_int(web3): assert 0 == parse_block_identifier_int(web3, -1 - last_num) +@pytest.mark.parametrize( + "block_identifier,expected_output", + ( + (1, 1), + (-1, 0), + ("latest", "latest"), + ("earliest", "earliest"), + ("pending", "pending"), + ("safe", "safe"), + ("finalized", "finalized"), + ), +) +def test_parse_block_identifier_int_and_string(web3, block_identifier, expected_output): + block_id = parse_block_identifier(web3, block_identifier) + assert block_id == expected_output + + @pytest.mark.parametrize("value", (0, "0x0", "0x00")) def test_validate_payable(value): tx = {"value": value} diff --git a/tests/core/core/block-utils/test_select_method_for_block_identifier.py b/tests/core/core/block-utils/test_select_method_for_block_identifier.py index 8bcebb37f4..9facbb05de 100644 --- a/tests/core/core/block-utils/test_select_method_for_block_identifier.py +++ b/tests/core/core/block-utils/test_select_method_for_block_identifier.py @@ -22,6 +22,8 @@ ('latest', 'test_predefined'), ('pending', 'test_predefined'), ('earliest', 'test_predefined'), + ("safe", "test_predefined"), + ("finalized", "test_predefined"), (-1, ValueError), (0, 'test_number'), (1, 'test_number'), diff --git a/tests/core/utilities/test_is_predefined_block_number.py b/tests/core/utilities/test_is_predefined_block_number.py index 95782ea4a6..0db187ddb7 100644 --- a/tests/core/utilities/test_is_predefined_block_number.py +++ b/tests/core/utilities/test_is_predefined_block_number.py @@ -11,6 +11,8 @@ ('earliest', True), ('latest', True), ('pending', True), + ("finalized", True), + ("safe", True), (1, False), ('0x1', False), ), diff --git a/web3/_utils/blocks.py b/web3/_utils/blocks.py index ac875f3c03..23a443e391 100644 --- a/web3/_utils/blocks.py +++ b/web3/_utils/blocks.py @@ -24,16 +24,16 @@ def is_predefined_block_number(value: Any) -> bool: value_text = value elif is_bytes(value): # `value` could either be random bytes or the utf-8 encoding of - # one of the words in: {"latest", "pending", "earliest"} - # We cannot decode the bytes as utf8, because random bytes likely won't be valid. - # So we speculatively decode as 'latin-1', which cannot fail. - value_text = value.decode('latin-1') + # one of the words in: {"latest", "pending", "earliest", "safe", "finalized"} + # We cannot decode the bytes as utf8, because random bytes likely won't be + # valid. So we speculatively decode as 'latin-1', which cannot fail. + value_text = value.decode("latin-1") elif is_integer(value): return False else: raise TypeError("unrecognized block reference: %r" % value) - return value_text in {"latest", "pending", "earliest"} + return value_text in {"latest", "pending", "earliest", "safe", "finalized"} def is_hex_encoded_block_hash(value: Any) -> bool: diff --git a/web3/contract.py b/web3/contract.py index 38e51a5e4d..8efc2791eb 100644 --- a/web3/contract.py +++ b/web3/contract.py @@ -1568,7 +1568,7 @@ def call_contract_function( def parse_block_identifier(web3: 'Web3', block_identifier: BlockIdentifier) -> BlockIdentifier: if isinstance(block_identifier, int): return parse_block_identifier_int(web3, block_identifier) - elif block_identifier in ['latest', 'earliest', 'pending']: + 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): return web3.eth.get_block(block_identifier)['number'] diff --git a/web3/eth.py b/web3/eth.py index 6148134f72..9ab4031da0 100644 --- a/web3/eth.py +++ b/web3/eth.py @@ -888,7 +888,7 @@ def filter_munger( if isinstance(filter_params, dict): return [filter_params] elif is_string(filter_params): - if filter_params in ['latest', 'pending']: + if filter_params in {'latest', 'pending'}: return [filter_params] else: raise ValueError( diff --git a/web3/providers/eth_tester/middleware.py b/web3/providers/eth_tester/middleware.py index fb865d8aab..605455da3d 100644 --- a/web3/providers/eth_tester/middleware.py +++ b/web3/providers/eth_tester/middleware.py @@ -52,7 +52,7 @@ def is_named_block(value: Any) -> bool: - return value in {"latest", "earliest", "pending"} + return value in {"latest", "earliest", "pending", "safe", "finalized"} def is_hexstr(value: Any) -> bool: diff --git a/web3/types.py b/web3/types.py index fa9ddac284..ee4ab45ab8 100644 --- a/web3/types.py +++ b/web3/types.py @@ -44,7 +44,7 @@ TParams = TypeVar("TParams") TValue = TypeVar("TValue") -BlockParams = Literal["latest", "earliest", "pending"] +BlockParams = Literal["latest", "earliest", "pending", "safe", "finalized"] BlockIdentifier = Union[BlockParams, BlockNumber, Hash32, HexStr, HexBytes, int] LatestBlockParam = Literal["latest"]