Skip to content

Commit

Permalink
feat: add support for polygon-zkevm networks (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Jul 3, 2024
1 parent 986fe61 commit 5c44fd8
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ jobs:
WEB3_OPTIMISM_SEPOLIA_ALCHEMY_API_KEY: ${{ secrets.WEB3_OPTIMISM_SEPOLIA_ALCHEMY_API_KEY }}
WEB3_POLYGON_MAINNET_ALCHEMY_API_KEY: ${{ secrets.WEB3_POLYGON_MAINNET_ALCHEMY_API_KEY }}
WEB3_POLYGON_AMOY_ALCHEMY_API_KEY: ${{ secrets.WEB3_POLYGON_AMOY_ALCHEMY_API_KEY }}
WEB3_POLYGON_ZKEVM_MAINNET_ALCHEMY_API_KEY: ${{ secrets.WEB3_POLYGON_ZKEVM_MAINNET_ALCHEMY_API_KEY }}
WEB3_POLYGON_ZKEVM_CARDONA_ALCHEMY_API_KEY: ${{ secrets.WEB3_POLYGON_ZKEVM_CARDONA_ALCHEMY_API_KEY }}

# NOTE: uncomment this block after you've marked tests with @pytest.mark.fuzzing
# fuzzing:
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ repos:
name: black

- repo: https://github.com/pycqa/flake8
rev: 7.0.0
rev: 7.1.0
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
rev: v1.10.1
hooks:
- id: mypy
additional_dependencies: [types-PyYAML, types-requests, pydantic, types-setuptools]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The `ape-alchemy` plugin supports the following ecosystems:
- Base
- Optimism
- Polygon
- Polygon-ZkEVM

## Dependencies

Expand Down
4 changes: 4 additions & 0 deletions ape_alchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
"mainnet",
"amoy",
],
"polygon-zkevm": [
"mainnet",
"cardona",
],
}


Expand Down
39 changes: 35 additions & 4 deletions ape_alchemy/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
from typing import Any, Optional

from ape.api import ReceiptAPI, TraceAPI, TransactionAPI, UpstreamProvider
from ape.exceptions import ContractLogicError, ProviderError, VirtualMachineError
from ape.exceptions import (
APINotImplementedError,
ContractLogicError,
ProviderError,
VirtualMachineError,
)
from ape.logging import logger
from ape.types import BlockID
from ape_ethereum.provider import Web3Provider
from ape_ethereum.trace import TransactionTrace
from ape_ethereum.transactions import AccessList
from eth_pydantic_types import HexBytes
from eth_typing import HexStr
from requests import HTTPError
from web3 import HTTPProvider, Web3
from web3.exceptions import ContractLogicError as Web3ContractLogicError
from web3.gas_strategies.rpc import rpc_gas_price_strategy
from web3.middleware import geth_poa_middleware
from web3.types import RPCEndpoint

from .exceptions import AlchemyFeatureNotAvailable, AlchemyProviderError, MissingProjectKeyError

Expand All @@ -24,6 +32,8 @@
# Alchemy will try to publish private transactions for 25 blocks.
PRIVATE_TX_BLOCK_WAIT = 25

NETWORKS_SUPPORTING_WEBSOCKETS = ("ethereum", "arbitrum", "base", "optimism", "polygon")


class Alchemy(Web3Provider, UpstreamProvider):
"""
Expand All @@ -46,7 +56,9 @@ def uri(self):

key = None

expected_env_var_prefix = f"WEB3_{ecosystem_name.upper()}_{network_name.upper()}_ALCHEMY"
ecosystem_nm_part = ecosystem_name.upper().replace("-", "_")
network_nm_part = network_name.upper().replace("-", "_")
expected_env_var_prefix = f"WEB3_{ecosystem_nm_part}_{network_nm_part}_ALCHEMY"
options = (
*DEFAULT_ENVIRONMENT_VARIABLE_NAMES,
f"{expected_env_var_prefix}_PROJECT_ID",
Expand All @@ -68,6 +80,7 @@ def uri(self):
"base": "https://base-{0}.g.alchemy.com/v2/{1}",
"optimism": "https://opt-{0}.g.alchemy.com/v2/{1}",
"polygon": "https://polygon-{0}.g.alchemy.com/v2/{1}",
"polygon-zkevm": "https://polygonzkevm-{0}.g.alchemy.com/v2/{1}",
}

network_format = network_formats_by_ecosystem[ecosystem_name]
Expand All @@ -85,6 +98,14 @@ def ws_uri(self) -> str:
# NOTE: Overriding `Web3Provider.ws_uri` implementation
return "ws" + self.uri[4:] # Remove `http` in default URI w/ `ws`

@property
def priority_fee(self) -> int:
if self.network.ecosystem.name == "polygon-zkevm":
# The error is only 400 with no info otherwise.
raise APINotImplementedError()

return super().priority_fee

@property
def connection_str(self) -> str:
return self.uri
Expand Down Expand Up @@ -151,9 +172,19 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa

return VirtualMachineError(message=message, txn=txn)

def make_request(self, endpoint: str, parameters: Optional[Iterable] = None) -> Any:
def create_access_list(
self, transaction: TransactionAPI, block_id: Optional[BlockID] = None
) -> list[AccessList]:
if self.network.ecosystem.name == "polygon-zkevm":
# The error is only 400 with no info otherwise.
raise APINotImplementedError()

return super().create_access_list(transaction, block_id=block_id)

def make_request(self, rpc: str, parameters: Optional[Iterable] = None) -> Any:
parameters = parameters or []
try:
return super().make_request(endpoint, parameters)
return self.web3.provider.make_request(RPCEndpoint(rpc), parameters)
except HTTPError as err:
response_data = err.response.json() if err.response else {}
if "error" not in response_data:
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ape-base", # Needed for testing Base integration
"ape-optimism", # Needed for testing Optimism integration
"ape-polygon", # Needed for testing Polygon integration
"ape-polygon-zkevm", # Needed for testing Polygon-ZkEVM integration
"pytest>=6.0", # Core testing package
"pytest-xdist", # Multi-process runner
"pytest-cov", # Coverage analyzer plugin
Expand All @@ -17,10 +18,10 @@
],
"lint": [
"black>=24.4.2,<25", # Auto-formatter and linter
"mypy>=1.10.0,<2", # Static type analyzer
"mypy>=1.10.1,<2", # Static type analyzer
"types-setuptools", # Needed for mypy type shed
"types-requests", # Needed for mypy type shed
"flake8>=7.0.0,<8", # Style linter
"flake8>=7.1.0,<8", # Style linter
"flake8-breakpoint>=1.1.0,<2", # Detect breakpoints left in code
"flake8-print>=5.0.0,<6", # Detect print statements left in code
"isort>=5.13.2,<6", # Import sorting linter
Expand Down
21 changes: 19 additions & 2 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import pytest
import websocket # type: ignore
from ape import networks
from ape import accounts, networks
from ape.exceptions import APINotImplementedError
from ape.utils import ZERO_ADDRESS

from ape_alchemy import NETWORKS
from ape_alchemy.provider import Alchemy
from ape_alchemy.provider import NETWORKS_SUPPORTING_WEBSOCKETS, Alchemy


@pytest.fixture(params=[(name, net) for name, values in NETWORKS.items() for net in values])
Expand All @@ -23,6 +24,10 @@ def test_http(provider):


def test_ws(provider):
if provider.network.ecosystem.name not in NETWORKS_SUPPORTING_WEBSOCKETS:
# Test will fail. Network does not support ws clients.
return

assert provider.ws_uri.startswith("wss")

try:
Expand All @@ -32,3 +37,15 @@ def test_ws(provider):

except Exception as err:
pytest.fail(f"Websocket URI not accessible. Reason: {err}")


def test_polygon_zkevm():
# We noticed strange behavior on this network and thus called for more tests.
with networks.polygon_zkevm.cardona.use_provider("alchemy") as provider:
with pytest.raises(APINotImplementedError):
_ = provider.priority_fee

receiver = accounts.test_accounts[0]
tx = provider.network.ecosystem.create_transaction(receiver=receiver)
with pytest.raises(APINotImplementedError):
_ = provider.create_access_list(tx)

0 comments on commit 5c44fd8

Please sign in to comment.