Skip to content

Commit

Permalink
Fix max_priority_fee when eth_maxPriorityFeePerGas is unavailable (#3002
Browse files Browse the repository at this point in the history
)

* Fix max_pritity_fee when eth_maxPriorityFeePerGas is unavailable

Hardhat sends {'code': -32601, 'message': 'Method eth_maxPriorityFeePerGas not found', 'data': {'message': 'Method eth_maxPriorityFeePerGas not found'}} which raises MethodUnavailable instead of ValueError

* Add test for actual response error format

* Add newsfragment

---------

Co-authored-by: kclowes <kclowes@users.noreply.github.com>
  • Loading branch information
sanekto and kclowes committed Jul 5, 2023
1 parent a807e6b commit 42468bf
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 9 deletions.
1 change: 1 addition & 0 deletions newsfragments/3002.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixes ``max_priority_fee_per_gas``. It wasn't falling back to ``eth_feeHistory`` since the ``MethodUnavailable`` error was introduced.
4 changes: 4 additions & 0 deletions tests/integration/test_ethereum_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ class TestEthereumTesterEthModule(EthModuleTest):
test_eth_max_priority_fee_with_fee_history_calculation = not_implemented(
EthModuleTest.test_eth_max_priority_fee_with_fee_history_calculation, ValueError
)
test_eth_max_priority_fee_with_fee_history_calculation_error_dict = not_implemented(
EthModuleTest.test_eth_max_priority_fee_with_fee_history_calculation_error_dict,
ValueError,
)
test_eth_sign = not_implemented(EthModuleTest.test_eth_sign, ValueError)
test_eth_sign_ens_names = not_implemented(
EthModuleTest.test_eth_sign_ens_names, ValueError
Expand Down
75 changes: 71 additions & 4 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,38 @@ 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_error_dict(
self, w3: "Web3"
) -> None:
fail_max_prio_middleware = construct_error_generator_middleware(
{
RPCEndpoint("eth_maxPriorityFeePerGas"): lambda *_: {
"error": {
"code": -32601,
"message": (
"The method eth_maxPriorityFeePerGas does "
"not exist/is not available"
),
}
}
}
)
w3.middleware_onion.add(
fail_max_prio_middleware, name="fail_max_prio_middleware"
)

with pytest.warns(
UserWarning,
match=(
"There was an issue with the method eth_maxPriorityFeePerGas."
" Calculating using eth_feeHistory."
),
):
w3.eth.max_priority_fee

w3.middleware_onion.remove("fail_max_prio_middleware") # clean up

@pytest.mark.asyncio
async def test_eth_max_priority_fee_with_fee_history_calculation(
self, async_w3: "AsyncWeb3"
Expand All @@ -818,8 +850,10 @@ async def test_eth_max_priority_fee_with_fee_history_calculation(

with pytest.warns(
UserWarning,
match="There was an issue with the method eth_maxPriorityFeePerGas. "
"Calculating using eth_feeHistory.",
match=(
"There was an issue with the method eth_maxPriorityFeePerGas. "
"Calculating using eth_feeHistory."
),
):
max_priority_fee = await async_w3.eth.max_priority_fee
assert is_integer(max_priority_fee)
Expand Down Expand Up @@ -2335,6 +2369,37 @@ def test_eth_max_priority_fee(self, w3: "Web3") -> None:
max_priority_fee = w3.eth.max_priority_fee
assert is_integer(max_priority_fee)

def test_eth_max_priority_fee_with_fee_history_calculation_error_dict(
self, w3: "Web3"
) -> None:
fail_max_prio_middleware = construct_error_generator_middleware(
{
RPCEndpoint("eth_maxPriorityFeePerGas"): lambda *_: {
"error": {
"code": -32601,
"message": (
"The method eth_maxPriorityFeePerGas does "
"not exist/is not available"
),
}
}
}
)
w3.middleware_onion.add(
fail_max_prio_middleware, name="fail_max_prio_middleware"
)

with pytest.warns(
UserWarning,
match=(
"There was an issue with the method eth_maxPriorityFeePerGas."
" Calculating using eth_feeHistory."
),
):
w3.eth.max_priority_fee

w3.middleware_onion.remove("fail_max_prio_middleware") # clean up

def test_eth_max_priority_fee_with_fee_history_calculation(
self, w3: "Web3"
) -> None:
Expand All @@ -2347,8 +2412,10 @@ def test_eth_max_priority_fee_with_fee_history_calculation(

with pytest.warns(
UserWarning,
match="There was an issue with the method eth_maxPriorityFeePerGas."
" Calculating using eth_feeHistory.",
match=(
"There was an issue with the method eth_maxPriorityFeePerGas."
" Calculating using eth_feeHistory."
),
):
max_priority_fee = w3.eth.max_priority_fee
assert is_integer(max_priority_fee)
Expand Down
3 changes: 2 additions & 1 deletion web3/eth/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
BaseEth,
)
from web3.exceptions import (
MethodUnavailable,
OffchainLookup,
TimeExhausted,
TooManyRequests,
Expand Down Expand Up @@ -181,7 +182,7 @@ def max_priority_fee(self) -> Wei:
"""
try:
return self._max_priority_fee()
except ValueError:
except (ValueError, MethodUnavailable):
warnings.warn(
"There was an issue with the method eth_maxPriorityFeePerGas. "
"Calculating using eth_feeHistory."
Expand Down
26 changes: 22 additions & 4 deletions web3/middleware/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,17 @@ def error_generator_middleware(
) -> Callable[[RPCEndpoint, Any], RPCResponse]:
def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
if method in error_generators:
error_msg = error_generators[method](method, params)
return {"error": error_msg}
error = error_generators[method](method, params)
if isinstance(error, dict) and error.get("error", False):
return {
"error": {
"code": error.get("code", -32000),
"message": error["error"].get("message", ""),
"data": error.get("data", ""),
}
}
else:
return {"error": error}
else:
return make_request(method, params)

Expand Down Expand Up @@ -132,8 +141,17 @@ async def error_generator_middleware(
) -> AsyncMiddlewareCoroutine:
async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
if method in error_generators:
error_msg = error_generators[method](method, params)
return {"error": error_msg}
error = error_generators[method](method, params)
if isinstance(error, dict) and error.get("error", False):
return {
"error": {
"code": error.get("code", -32000),
"message": error["error"].get("message", ""),
"data": error.get("data", ""),
}
}
else:
return {"error": error}
else:
return await make_request(method, params)

Expand Down

0 comments on commit 42468bf

Please sign in to comment.