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 support for polygon-zkevm networks #75

Merged
merged 4 commits into from
Jul 3, 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
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)
Loading