Skip to content
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: 1 addition & 1 deletion .github/workflows/lints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3
- name: Install dependencies
run: |
pdm sync -G lint,compiler,local-provider
pdm sync -G lint,compiler,local-provider,http-provider-server
- name: Run mypy
run: |
pdm run mypy pons
8 changes: 5 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ Signers
Contract ABI
------------

.. class:: ABI_JSON

A JSON-ifiable object (``bool``, ``int``, ``float``, ``str``, ``None``,
iterable of ``JSON``, or mapping of ``str`` to ``JSON``).

.. autoclass:: ContractABI
:members:

Expand Down Expand Up @@ -125,9 +130,6 @@ Testing utilities

``pons`` exposes several types useful for testing applications that connect to Ethereum RPC servers. Not intended for the production environment.

Install with the feature ``local-provider`` for it to be available.


.. autoclass:: LocalProvider
:show-inheritance:
:members: disable_auto_mine_transactions, enable_auto_mine_transactions, take_snapshot, revert_to_snapshot
Expand Down
21 changes: 21 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ Changelog
---------


0.9.0 (in development)
~~~~~~~~~~~~~~~~~~~~~~

Changed
~~~~~~~

- ``JSON`` removed from the public API, instead we have a more specific ``ABI_JSON``. (PR_82_)
- Renamed ``id_`` fields of ``BlockFilter``, ``PendingTransactionFilter``, and ``LogFilter`` to just ``id``. (PR_82_)
- Split out ``http-provider-server`` feature from ``local-provider``. (PR_82_)


Added
^^^^^

- Hash methods for ABI types. (PR_81_)


.. _PR_81: https://github.com/fjarri-eth/pons/pull/81
.. _PR_82: https://github.com/fjarri-eth/pons/pull/82


0.8.1 (2024-11-12)
~~~~~~~~~~~~~~~~~~

Expand Down
4 changes: 3 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@

autoclass_content = "both"
autodoc_member_order = "groupwise"
autodoc_type_aliases = {"JSON": "JSON"}

# Add any paths that contain templates here, relative to this directory.
templates_path = []
Expand Down Expand Up @@ -76,5 +75,8 @@
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"eth_account": ("https://eth-account.readthedocs.io/en/v0.13.0/", None),
"eth_typing": ("https://eth-typing.readthedocs.io/en/stable/", None),
"ethereum_rpc": ("https://ethereum-rpc.readthedocs.io/en/v0.1.0", None),
"alysis": ("https://alysis.readthedocs.io/en/v0.6.0/", None),
"trio": ("https://trio.readthedocs.io/en/v0.31.0/", None),
}
2,102 changes: 1,231 additions & 871 deletions pdm.lock

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions pons/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Async Ethereum RPC client."""

from . import abi
from ._abi_types import ABIDecodingError
from ._abi_types import ABI_JSON, ABIDecodingError
from ._client import (
Client,
ClientSession,
Expand Down Expand Up @@ -52,6 +52,7 @@
from ._utils import get_create2_address, get_create_address

__all__ = [
"ABI_JSON",
"ABIDecodingError",
"AccountSigner",
"BoundConstructor",
Expand All @@ -71,34 +72,34 @@
"ContractPanic",
"CycleFallback",
"DeployedContract",
"EVMVersion",
"Either",
"Error",
"EVMVersion",
"LocalProvider",
"Event",
"EventFilter",
"Fallback",
"FallbackProvider",
"FallbackStrategy",
"FallbackStrategyFactory",
"HTTPProvider",
"HTTPProviderServer",
"LocalProvider",
"Method",
"MethodCall",
"MultiMethod",
"Mutability",
"PriorityFallback",
"ProtocolError",
"ProviderError",
"Provider",
"ProviderError",
"Receive",
"RemoteError",
"HTTPProviderServer",
"Signer",
"SnapshotID",
"TransactionFailed",
"Unreachable",
"abi",
"compile_contract_file",
"get_create_address",
"get_create2_address",
"get_create_address",
]
57 changes: 44 additions & 13 deletions pons/_abi_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
from collections.abc import Iterable, Mapping, Sequence
from functools import cached_property
from types import EllipsisType
from typing import Any
from typing import Any, cast

import eth_abi
from eth_abi.exceptions import DecodingError
from ethereum_rpc import Address, keccak

ABI_JSON = None | bool | int | float | str | Sequence["ABI_JSON"] | Mapping[str, "ABI_JSON"]
"""Values serializable to JSON."""

# Maximum bits in an `int` or `uint` type in Solidity.
MAX_INTEGER_BITS = 256

Expand Down Expand Up @@ -147,6 +150,9 @@ def _denormalize(self, val: ABIType) -> int:
def __eq__(self, other: object) -> bool:
return isinstance(other, UInt) and self._bits == other._bits

def __hash__(self) -> int:
return hash((type(self), self._bits))


class Int(Type):
"""Corresponds to the Solidity ``int<bits>`` type."""
Expand Down Expand Up @@ -183,6 +189,9 @@ def _denormalize(self, val: ABIType) -> int:
def __eq__(self, other: object) -> bool:
return isinstance(other, Int) and self._bits == other._bits

def __hash__(self) -> int:
return hash((type(self), self._bits))


class Bytes(Type):
"""Corresponds to the Solidity ``bytes<size>`` type."""
Expand All @@ -199,8 +208,7 @@ def canonical_form(self) -> str:
def _check_val(self, val: Any) -> bytes:
if not isinstance(val, bytes):
raise TypeError(
f"`{self.canonical_form}` must correspond to a bytestring, "
f"got {type(val).__name__}"
f"`{self.canonical_form}` must correspond to a bytestring, got {type(val).__name__}"
)
if self._size is not None and len(val) != self._size:
raise ValueError(f"Expected {self._size} bytes, got {len(val)}")
Expand Down Expand Up @@ -236,6 +244,9 @@ def decode_from_topic(self, val: bytes) -> None | bytes:
def __eq__(self, other: object) -> bool:
return isinstance(other, Bytes) and self._size == other._size

def __hash__(self) -> int:
return hash((type(self), self._size))


class AddressType(Type):
"""
Expand All @@ -250,8 +261,7 @@ def canonical_form(self) -> str:
def _normalize(self, val: Any) -> str:
if not isinstance(val, Address):
raise TypeError(
f"`address` must correspond to an `Address`-type value, "
f"got {type(val).__name__}"
f"`address` must correspond to an `Address`-type value, got {type(val).__name__}"
)
return val.checksum

Expand All @@ -263,6 +273,9 @@ def _denormalize(self, val: ABIType) -> Address:
def __eq__(self, other: object) -> bool:
return isinstance(other, AddressType)

def __hash__(self) -> int:
return hash(type(self))


class String(Type):
"""Corresponds to the Solidity ``string`` type."""
Expand Down Expand Up @@ -299,6 +312,9 @@ def decode_from_topic(self, _val: bytes) -> None:
def __eq__(self, other: object) -> bool:
return isinstance(other, String)

def __hash__(self) -> int:
return hash(type(self))


class Bool(Type):
"""Corresponds to the Solidity ``bool`` type."""
Expand All @@ -323,6 +339,9 @@ def _denormalize(self, val: ABIType) -> bool:
def __eq__(self, other: object) -> bool:
return isinstance(other, Bool)

def __hash__(self) -> int:
return hash(type(self))


class Array(Type):
"""Corresponds to the Solidity array (``[<size>]``) type."""
Expand Down Expand Up @@ -366,6 +385,9 @@ def __eq__(self, other: object) -> bool:
and self._size == other._size
)

def __hash__(self) -> int:
return hash((type(self), self._element_type, self._size))


class Struct(Type):
"""Corresponds to the Solidity struct type."""
Expand Down Expand Up @@ -426,6 +448,9 @@ def __eq__(self, other: object) -> bool:
and list(self._fields) == list(other._fields)
)

def __hash__(self) -> int:
return hash((type(self), tuple(self._fields.items())))


_UINT_RE = re.compile(r"uint(\d+)")
_INT_RE = re.compile(r"int(\d+)")
Expand All @@ -451,8 +476,11 @@ def type_from_abi_string(abi_string: str) -> Type:
raise ValueError(f"Unknown type: {abi_string}")


def dispatch_type(abi_entry: Mapping[str, Any]) -> Type:
type_str = abi_entry["type"]
def dispatch_type(abi_entry: ABI_JSON) -> Type:
# TODO (#83): use proper validation
abi_entry_typed = cast("Mapping[str, Any]", abi_entry)

type_str = abi_entry_typed["type"]
match = re.match(r"^([\w\d\[\]]*?)(\[(\d+)?\])?$", type_str)
if not match:
raise ValueError(f"Incorrect type format: {type_str}")
Expand All @@ -464,34 +492,37 @@ def dispatch_type(abi_entry: Mapping[str, Any]) -> Type:
array_size = int(array_size)

if is_array:
element_entry = dict(abi_entry)
element_entry = dict(abi_entry_typed)
element_entry["type"] = element_type_name
element_type = dispatch_type(element_entry)
return Array(element_type, array_size)

if element_type_name == "tuple":
fields = {}
for component in abi_entry["components"]:
for component in abi_entry_typed["components"]:
fields[component["name"]] = dispatch_type(component)
return Struct(fields)

return type_from_abi_string(element_type_name)


def dispatch_types(abi_entry: Iterable[dict[str, Any]]) -> list[Type] | dict[str, Type]:
names = [entry["name"] for entry in abi_entry]
def dispatch_types(abi_entry: ABI_JSON) -> list[Type] | dict[str, Type]:
# TODO (#83): use proper validation
abi_entry_typed = cast("Iterable[dict[str, Any]]", abi_entry)

names = [entry["name"] for entry in abi_entry_typed]

# Unnamed arguments; treat as positional arguments
if names and all(not name for name in names):
return [dispatch_type(entry) for entry in abi_entry]
return [dispatch_type(entry) for entry in abi_entry_typed]

if any(not name for name in names):
raise ValueError("Arguments must be either all named or all unnamed")

# Since we are returning a dictionary, need to be sure we don't silently merge entries
if len(names) != len(set(names)):
raise ValueError("All ABI entries must have distinct names")
return {entry["name"]: dispatch_type(entry) for entry in abi_entry}
return {entry["name"]: dispatch_type(entry) for entry in abi_entry_typed}


def encode_args(*types_and_args: tuple[Type, Any]) -> bytes:
Expand Down
Loading