Skip to content

Commit

Permalink
feat!: Ape 0.8 updates (#71)
Browse files Browse the repository at this point in the history
Co-authored-by: NotPeopling2day <[email protected]>
Co-authored-by: Dalena <[email protected]>
  • Loading branch information
3 people authored May 31, 2024
1 parent 6bd4476 commit b09a9fe
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
# TODO: Replace with macos-latest when works again.
# https://github.com/actions/setup-python/issues/808
os: [ubuntu-latest, macos-12] # eventually add `windows-latest`
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
python-version: [3.9, "3.10", "3.11", "3.12"]

env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ repos:
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter]
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]

default_language_version:
python: python3
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The `ape-alchemy` plugin supports the following ecosystems:

## Dependencies

- [python3](https://www.python.org/downloads) version 3.8 up to 3.12.
- [python3](https://www.python.org/downloads) version 3.9 up to 3.12.

## Installation

Expand Down
4 changes: 1 addition & 3 deletions ape_alchemy/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Tuple

from ape.exceptions import ProviderError


Expand All @@ -21,6 +19,6 @@ class MissingProjectKeyError(AlchemyProviderError):
An error raised when there is no API key set.
"""

def __init__(self, options: Tuple[str, ...]):
def __init__(self, options: tuple[str, ...]):
env_var_str = ", ".join([f"${n}" for n in options])
super().__init__(f"Must set one of {env_var_str}.")
63 changes: 18 additions & 45 deletions ape_alchemy/provider.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import os
from typing import Any, Dict, List, Optional

from ape.api import ReceiptAPI, TransactionAPI, UpstreamProvider
from ape.exceptions import (
APINotImplementedError,
ContractLogicError,
ProviderError,
VirtualMachineError,
)
from collections.abc import Iterable
from typing import Any, Optional

from ape.api import ReceiptAPI, TraceAPI, TransactionAPI, UpstreamProvider
from ape.exceptions import ContractLogicError, ProviderError, VirtualMachineError
from ape.logging import logger
from ape.types import CallTreeNode
from ape_ethereum.provider import Web3Provider
from ape_ethereum.trace import TransactionTrace
from eth_pydantic_types import HexBytes
from eth_typing import HexStr
from evm_trace import (
ParityTraceList,
get_calltree_from_geth_call_trace,
get_calltree_from_parity_trace,
)
from requests import HTTPError
from web3 import HTTPProvider, Web3
from web3.exceptions import ContractLogicError as Web3ContractLogicError
Expand All @@ -41,7 +32,7 @@ class Alchemy(Web3Provider, UpstreamProvider):
Docs: https://docs.alchemy.com/alchemy/
"""

network_uris: Dict[tuple, str] = {}
network_uris: dict[tuple, str] = {}

@property
def uri(self):
Expand Down Expand Up @@ -116,34 +107,16 @@ def connect(self):
def disconnect(self):
self._web3 = None

def _get_prestate_trace(self, txn_hash: str) -> Dict:
return self._debug_trace_transaction(txn_hash, "prestateTracer")

def get_call_tree(self, txn_hash: str) -> CallTreeNode:
try:
return self._get_calltree_using_parity_style(txn_hash)
except Exception as err:
try:
return self._get_calltree_using_call_tracer(txn_hash)
except Exception:
pass

raise APINotImplementedError() from err

def _get_calltree_using_parity_style(self, txn_hash: str) -> CallTreeNode:
raw_trace_list = self._make_request("trace_transaction", [txn_hash])
trace_list = ParityTraceList.model_validate(raw_trace_list)
evm_call = get_calltree_from_parity_trace(trace_list)
return self._create_call_tree_node(evm_call)
def _get_prestate_trace(self, transaction_hash: str) -> dict:
return self.make_request(
"debug_traceTransaction", [transaction_hash, {"tracer": "prestateTracer"}]
)

def _get_calltree_using_call_tracer(self, txn_hash: str) -> CallTreeNode:
# Create trace frames using geth-style call tracer
calls = self._debug_trace_transaction(txn_hash, "callTracer")
evm_call = get_calltree_from_geth_call_trace(calls)
return self._create_call_tree_node(evm_call, txn_hash=txn_hash)
def get_transaction_trace(self, transaction_hash: str, **kwargs) -> TraceAPI:
if "debug_trace_transaction_parameters" not in kwargs:
kwargs["debug_trace_transaction_parameters"] = {}

def _debug_trace_transaction(self, txn_hash: str, tracer: str) -> Dict:
return self._make_request("debug_traceTransaction", [txn_hash, {"tracer": tracer}])
return TransactionTrace(transaction_hash=transaction_hash, **kwargs)

def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMachineError:
txn = kwargs.get("txn")
Expand Down Expand Up @@ -178,9 +151,9 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa

return VirtualMachineError(message=message, txn=txn)

def _make_request(self, endpoint: str, parameters: Optional[List] = None) -> Any:
def make_request(self, endpoint: str, parameters: Optional[Iterable] = None) -> Any:
try:
return super()._make_request(endpoint, parameters)
return super().make_request(endpoint, parameters)
except HTTPError as err:
response_data = err.response.json() if err.response else {}
if "error" not in response_data:
Expand Down Expand Up @@ -225,7 +198,7 @@ def send_private_transaction(self, txn: TransactionAPI, **kwargs) -> ReceiptAPI:
params["preferences"] = kwargs

try:
txn_hash = self._make_request("eth_sendPrivateTransaction", [params])
txn_hash = self.make_request("eth_sendPrivateTransaction", [params])
except (ValueError, Web3ContractLogicError) as err:
vm_err = self.get_virtual_machine_error(err, txn=txn)
raise vm_err from err
Expand Down
7 changes: 3 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"websocket-client", # Used for web socket integration testing
],
"lint": [
"black>=24.4.1,<25", # Auto-formatter and linter
"black>=24.4.2,<25", # Auto-formatter and linter
"mypy>=1.10.0,<2", # Static type analyzer
"types-setuptools", # Needed for mypy type shed
"types-requests", # Needed for mypy type shed
Expand Down Expand Up @@ -75,14 +75,14 @@
url="https://github.com/ApeWorX/ape-alchemy",
include_package_data=True,
install_requires=[
"eth-ape>=0.7.0,<0.8",
"eth-ape>=0.8.1,<0.9",
"eth-pydantic-types", # Use same version as eth-ape
"ethpm-types", # Use same version as eth-ape
"evm-trace", # Use same version as eth-ape
"web3", # Use same version as eth-ape
"requests",
],
python_requires=">=3.8,<4",
python_requires=">=3.9,<4",
extras_require=extras_require,
py_modules=["ape_alchemy"],
license="Apache-2.0",
Expand All @@ -98,7 +98,6 @@
"Operating System :: MacOS",
"Operating System :: POSIX",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down
14 changes: 5 additions & 9 deletions tests/test_providers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re

import pytest
from ape.exceptions import APINotImplementedError, ContractLogicError
from ape.exceptions import ContractLogicError
from ape.types import LogFilter
from hexbytes import HexBytes
from web3.exceptions import ContractLogicError as Web3ContractLogicError
Expand Down Expand Up @@ -160,11 +160,6 @@ def test_estimate_gas_would_revert_no_message(token, alchemy_provider, mock_web3
alchemy_provider.estimate_gas_cost(transaction)


def test_feature_not_available(alchemy_provider, txn_hash):
with pytest.raises(APINotImplementedError):
list(alchemy_provider.get_transaction_trace(txn_hash))


def test_get_contract_logs(networks, alchemy_provider, mock_web3, block, log_filter):
mock_web3.eth.get_block.return_value = block
alchemy_provider._web3 = mock_web3
Expand All @@ -175,11 +170,12 @@ def test_get_contract_logs(networks, alchemy_provider, mock_web3, block, log_fil
assert actual == []


def test_get_call_tree(networks, alchemy_provider, mock_web3, parity_trace, receipt):
def test_get_transaction_trace(networks, alchemy_provider, mock_web3, parity_trace, receipt):
mock_web3.provider.make_request.return_value = [parity_trace]
mock_web3.eth.wait_for_transaction_receipt.return_value = receipt
alchemy_provider._web3 = mock_web3
networks.active_provider = alchemy_provider
actual = repr(alchemy_provider.get_call_tree(TXN_HASH))
expected = r"0xC17f2C69aE2E66FD87367E3260412EEfF637F70E\.0x96d373e5\(\) \[1401584 gas\]"
trace = alchemy_provider.get_transaction_trace(TXN_HASH)
actual = repr(trace.get_calltree())
expected = r"CALL: 0xC17f2C69aE2E66FD87367E3260412EEfF637F70E\.<0x96d373e5\> \[1401584 gas\]"
assert re.match(expected, actual)

0 comments on commit b09a9fe

Please sign in to comment.