diff --git a/docs/v6_migration.rst b/docs/v6_migration.rst index 74da70889e..97d80f7aba 100644 --- a/docs/v6_migration.rst +++ b/docs/v6_migration.rst @@ -76,6 +76,9 @@ Other Misc Changes - ``InfuraKeyNotFound`` exception has been changed to ``InfuraProjectIdNotFound`` - ``SolidityError`` has been removed in favor of ``ContractLogicError`` +- When a method is unavailable from a node provider (i.e. a response error + code of -32601 is returned), a ``MethodUnavailable`` error is + now raised instead of ``ValueError`` Removals ~~~~~~~~ diff --git a/newsfragments/2796.breaking.rst b/newsfragments/2796.breaking.rst new file mode 100644 index 0000000000..3494b00189 --- /dev/null +++ b/newsfragments/2796.breaking.rst @@ -0,0 +1 @@ +When a method is not supported by a node provider, raise a MethodUnavailable error instead of the generic ValueError. diff --git a/tests/core/manager/test_response_formatters.py b/tests/core/manager/test_response_formatters.py index 4b372f2349..505a146386 100644 --- a/tests/core/manager/test_response_formatters.py +++ b/tests/core/manager/test_response_formatters.py @@ -13,6 +13,7 @@ BadResponseFormat, BlockNotFound, ContractLogicError, + MethodUnavailable, TransactionNotFound, ) @@ -32,6 +33,17 @@ "message": "You cannot query logs for more than 10000 blocks at once.", "method": "eth_getLogs", } +METHOD_NOT_FOUND_RESP_FORMAT = { + "jsonrpc": "2.0", + "error": { + "code": -32601, + "message": "the method eth_getTransactionByHash does not exist/is not " + "available", + }, +} +ETH_TESTER_METHOD_NOT_FOUND_RESP_FORMAT = { + "error": "the method eth_getTransactionByHash does not exist/is not available", +} def raise_contract_logic_error(response): @@ -105,6 +117,20 @@ def raise_contract_logic_error(response): raise_transaction_not_found, TransactionNotFound, ), + ( + METHOD_NOT_FOUND_RESP_FORMAT, + (), + identity, + identity, + MethodUnavailable, + ), + ( + ETH_TESTER_METHOD_NOT_FOUND_RESP_FORMAT, + (), + identity, + identity, + ValueError, + ), ], ) def test_formatted_response_raises_errors( diff --git a/web3/exceptions.py b/web3/exceptions.py index 8bd5288f86..235b9ee8ea 100644 --- a/web3/exceptions.py +++ b/web3/exceptions.py @@ -287,3 +287,11 @@ class BadResponseFormat(Web3Exception): """ pass + + +class MethodUnavailable(Web3Exception): + """ + Raised when the method is not available on the node + """ + + pass diff --git a/web3/manager.py b/web3/manager.py index ffab72d7d8..fed4c1c786 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -34,6 +34,7 @@ ) from web3.exceptions import ( BadResponseFormat, + MethodUnavailable, ) from web3.middleware import ( abi_middleware, @@ -188,6 +189,13 @@ def formatted_response( ) -> Any: if "error" in response: apply_error_formatters(error_formatters, response) + + # guard against eth-tester case - eth-tester returns a string + # with no code, so can't parse what the error is. + if isinstance(response["error"], dict): + resp_code = response["error"].get("code") + if resp_code == -32601: + raise MethodUnavailable(response["error"]) raise ValueError(response["error"]) # NULL_RESPONSES includes None, so return False here as the default # so we don't apply the null_result_formatters if there is no 'result' key