Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dynamic middleware check #94

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions ape_infura/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from ape_ethereum.provider import Web3Provider
from web3 import HTTPProvider, Web3
from web3.exceptions import ContractLogicError as Web3ContractLogicError
from web3.exceptions import ExtraDataLengthError
from web3.gas_strategies.rpc import rpc_gas_price_strategy
from web3.middleware import geth_poa_middleware
from web3.middleware.validation import MAX_EXTRADATA_LENGTH

_ENVIRONMENT_VARIABLE_NAMES = ("WEB3_INFURA_PROJECT_ID", "WEB3_INFURA_API_KEY")
# NOTE: https://docs.infura.io/learn/websockets#supported-networks
Expand Down Expand Up @@ -94,7 +96,17 @@ def connection_str(self) -> str:
return self.uri

def connect(self):
self._web3 = Web3(HTTPProvider(self.uri))
self._web3 = _create_web3(HTTPProvider(self.uri))

if self._needs_poa_middleware:
self._web3.middleware_onion.inject(geth_poa_middleware, layer=0)

self._web3.eth.set_gas_price_strategy(rpc_gas_price_strategy)

@property
def _needs_poa_middleware(self) -> bool:
if self._web3 is None:
return False

# Any chain that *began* as PoA needs the middleware for pre-merge blocks
optimism = (10, 420)
Expand All @@ -103,9 +115,21 @@ def connect(self):
blast = (11155111, 168587773)

if self._web3.eth.chain_id in (*optimism, *polygon, *linea, *blast):
self._web3.middleware_onion.inject(geth_poa_middleware, layer=0)
return True

self._web3.eth.set_gas_price_strategy(rpc_gas_price_strategy)
for block_id in ("earliest", "latest"):
try:
block = self.web3.eth.get_block(block_id) # type: ignore
except ExtraDataLengthError:
return True
else:
if (
"proofOfAuthorityData" in block
or len(block.get("extraData", "")) > MAX_EXTRADATA_LENGTH
):
return True

return False

def disconnect(self):
"""
Expand Down Expand Up @@ -149,3 +173,7 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa
return ContractLogicError(txn=txn)

return VirtualMachineError(message, txn=txn)


def _create_web3(http_provider: HTTPProvider) -> Web3:
return Web3(http_provider)
1 change: 1 addition & 0 deletions ape_infura/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"sepolia",
],
"ethereum": [
"holesky",
"mainnet",
"sepolia",
],
Expand Down
14 changes: 14 additions & 0 deletions tests/test_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import pytest
import websocket # type: ignore
from ape import networks
from ape.utils import ZERO_ADDRESS
from web3.exceptions import ExtraDataLengthError
from web3.middleware import geth_poa_middleware

from ape_infura.provider import _WEBSOCKET_CAPABLE_ECOSYSTEMS, Infura

Expand Down Expand Up @@ -93,3 +96,14 @@ def test_uri_with_random_api_key(provider, mocker):

# Disconnect so key isn't cached.
provider.disconnect()


def test_dynamic_poa_check(mocker):
real = networks.ethereum.holesky.get_provider("infura")
mock_web3 = mocker.MagicMock()
mock_web3.eth.get_block.side_effect = ExtraDataLengthError
infura = Infura(name=real.name, network=real.network)
patch = mocker.patch("ape_infura.provider._create_web3")
patch.return_value = mock_web3
infura.connect()
mock_web3.middleware_onion.inject.assert_called_once_with(geth_poa_middleware, layer=0)
Loading