Skip to content
Draft
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
1,014 changes: 1,014 additions & 0 deletions specs/erc7730-v1.schema.json

Large diffs are not rendered by default.

1,317 changes: 1,317 additions & 0 deletions specs/erc7730-v2.schema.json

Large diffs are not rendered by default.

52 changes: 49 additions & 3 deletions src/erc7730/common/abi.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from dataclasses import dataclass
from enum import StrEnum, auto
from typing import Any, cast
Expand All @@ -7,7 +8,7 @@
from lark import Lark, UnexpectedInput
from lark.visitors import Transformer_InPlaceRecursive

from erc7730.model.abi import ABI, Component, Function, InputOutput
from erc7730.model.abi import ABI, Component, Function, InputOutput, StateMutability

_SIGNATURE_PARSER = parser = Lark(
grammar=r"""
Expand Down Expand Up @@ -128,17 +129,62 @@ class Functions:
proxy: bool


def get_functions(abis: list[ABI]) -> Functions:
"""Get the functions from a list of ABIs."""
_READ_ONLY_MUTABILITIES = frozenset({StateMutability.pure, StateMutability.view})


def get_functions(abis: list[ABI], *, include_read_only: bool = False) -> Functions:
"""Get the functions from a list of ABIs.

:param abis: list of ABI entries
:param include_read_only: if False (default), filter out pure/view functions that cannot produce transactions
:return: Functions dataclass with selector->Function mapping
"""
functions = Functions(functions={}, proxy=False)
for abi in abis:
if abi.type == "function":
if not include_read_only and abi.stateMutability in _READ_ONLY_MUTABILITIES:
continue
functions.functions[function_to_selector(abi)] = abi
if abi.name in ("proxyType", "getImplementation", "implementation", "proxy__getImplementation"):
functions.proxy = True
return functions


_ENCODE_TYPE_RE = re.compile(r"(\w+)\(([^)]*)\)")


def parse_encode_type(encode_type: str) -> tuple[str, dict[str, list[tuple[str, str]]]]:
"""Parse an EIP-712 encodeType string into primaryType and types dict.

The encodeType format is: ``PrimaryType(type1 name1,type2 name2,...)DependentType(...)``

This is the inverse of the ``generateEncodeType`` function in the migration script.

:param encode_type: an EIP-712 encodeType string
:return: a tuple of (primaryType, types) where types is a dict mapping type names to lists of (type, name) tuples
:raises ValueError: if the string cannot be parsed
"""
matches = _ENCODE_TYPE_RE.findall(encode_type)
if not matches:
raise ValueError(f"Invalid encodeType string: {encode_type}")

primary_type = matches[0][0]
types: dict[str, list[tuple[str, str]]] = {}

for type_name, fields_str in matches:
fields: list[tuple[str, str]] = []
if fields_str.strip():
for field in fields_str.split(","):
parts = field.strip().split(" ", 1)
if len(parts) == 2:
fields.append((parts[0], parts[1]))
else:
raise ValueError(f"Invalid field in encodeType '{type_name}': '{field.strip()}'")
types[type_name] = fields

return primary_type, types


class ABIDataType(StrEnum):
"""Solidity data type."""

Expand Down
Loading
Loading