Skip to content

Commit

Permalink
Merge pull request #8 from LedgerHQ/newschema
Browse files Browse the repository at this point in the history
New erc7730 registry schema (09-19-24 version)-> Parsing
  • Loading branch information
ClaireGuerreGiordano authored Sep 23, 2024
2 parents 1752425 + b17105e commit 3ef1679
Show file tree
Hide file tree
Showing 16 changed files with 303 additions and 101 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ repos:
- pytest==8.3.3

- repo: https://github.com/pdm-project/pdm
rev: 2.18.2
rev: 2.17.3
hooks:
- id: pdm-lock-check
name: check pdm lock file
Expand Down
76 changes: 74 additions & 2 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ dependencies = [
"requests>=2.32.3",
"typer>=0.12.5",
"eth-hash[pycryptodome]>=0.7.0",
"jsonschema",
"eth_hash",
"rich",
"typer",
]

[project.urls]
Expand Down
5 changes: 5 additions & 0 deletions src/erc7730/common/pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ def model_from_json_file_or_none(path: Path, model: type[M]) -> M | None:
return model_from_json_file(path, model) if os.path.isfile(path) else None


def json_file_from_model(model: type[M], obj: M) -> str:
"""Serialize pydantic model"""
return model.model_dump_json(obj, by_alias=True, exclude_none=True)


def model_from_json_bytes(bytes: bytes, model: type[M]) -> M:
"""
Load a Pydantic model from a JSON file content as an array of bytes.
Expand Down
2 changes: 1 addition & 1 deletion src/erc7730/linter/classifier/abi_classifier.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import override
from typing_extensions import override

from erc7730.model.context import AbiJsonSchema

Expand Down
2 changes: 1 addition & 1 deletion src/erc7730/linter/classifier/eip712_classifier.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import override
from typing_extensions import override

from erc7730.model.context import EIP712JsonSchema

Expand Down
38 changes: 21 additions & 17 deletions src/erc7730/linter/common/paths.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from dataclasses import dataclass
from erc7730.model.context import EIP712Domain, EIP712JsonSchema
from erc7730.model.display import Field, Fields, Format, Reference, StructFormats

from erc7730.model.context import EIP712JsonSchema, NameType
from erc7730.model.display import Field, Format, Reference, FieldDescription, NestedFields, TokenAmountParameters

ARRAY_SUFFIX = "[]"

Expand All @@ -18,7 +17,7 @@ def compute_eip712_paths(schema: EIP712JsonSchema) -> set[str]:
"""Compute the sets of valid paths for an EIP712 schema."""

def append_paths(
path: str, current_type: list[EIP712Domain], types: dict[str, list[EIP712Domain]], paths: set[str]
path: str, current_type: list[NameType], types: dict[str, list[NameType]], paths: set[str]
) -> None:
for domain in current_type:
new_path = _append_path(path, domain.name)
Expand Down Expand Up @@ -59,18 +58,23 @@ def add_path(root: str, path: str) -> None:
else:
paths.data_paths.add(_append_path(root, path))

def append_paths(path: str, fields: Fields | None) -> None:
def append_paths(path: str, fields: Field | None) -> None:
if fields is not None:
for field_name, field in fields.root.items():
match field:
case Field():
add_path(path, field_name)
if field.params and "tokenPath" in field.params: # FIXME model is not correct
add_path(path, _remove_slicing(field.params["tokenPath"]))
case StructFormats():
append_paths(_append_path(path, field_name), field.fields)
case Reference():
raise NotImplementedError("Unsupported reference field")

append_paths("", format.fields)
field = fields.root
field_name = ""
match field:
case Reference():
pass
case FieldDescription():
field_name = field.label
add_path(path, field_name)
if field.params and isinstance(field.params, TokenAmountParameters): # FIXME model is not correct
add_path(path, _remove_slicing(field.params.tokenPath))
case NestedFields():
field_name = field.path
append_paths(_append_path(path, field_name), field.fields) # type: ignore

if format.fields is not None:
for f in format.fields:
append_paths("", f)
return paths
6 changes: 4 additions & 2 deletions src/erc7730/linter/linter_transaction_type_classifier_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def determine_tx_class(descriptor: ERC7730Descriptor) -> TxClass | None:
abi_classifier = ABIClassifier()
if descriptor.context.contract.abi is not None:
abi_schema = descriptor.context.contract.abi
if abi_schema is not None and not isinstance(abi_schema, AnyUrl):
if not isinstance(abi_schema, AnyUrl):
return abi_classifier.classify(abi_schema)
# url should have been resolved earlier
return None
Expand Down Expand Up @@ -80,7 +80,7 @@ def _get_all_displayed_fields(self, formats: dict[str, Format]) -> set[str]:
fields: set[str] = set()
for format in formats.values():
if format.fields is not None:
for field in format.fields.root.keys():
for field in format.fields:
fields.add(str(field))
return fields

Expand Down Expand Up @@ -118,4 +118,6 @@ def check(self) -> list[ERC7730Linter.Output]:
level=ERC7730Linter.Output.Level.ERROR,
)
)
case _:
pass
return res
13 changes: 5 additions & 8 deletions src/erc7730/mapper/mapper.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
# type: ignore
# ruff: noqa
from pydantic import AnyUrl
"""from pydantic import AnyUrl
from erc7730.common.pydantic import model_from_json_bytes
from erc7730.model.context import EIP712JsonSchema, EIP712Context, EIP712, Domain, EIP712Domain
from erc7730.model.erc7730_descriptor import ERC7730Descriptor, EIP712Context
from erc7730.model.display import (
Display,
Reference,
Field,
StructFormats,
FieldFormat,
Fields,
Field,
Format,
DateParameters,
DateEncoding,
Expand Down Expand Up @@ -86,7 +82,7 @@ def to_eip712_mapper(erc7730: ERC7730Descriptor) -> EIP712DAppDescriptor | list[
return exceptions
def parseFields(display: Display, primaryType: str, output: list[EIP712Field], fields: Fields) -> list[EIP712Field]:
def parseFields(display: Display, primaryType: str, output: list[EIP712Field], fields: Field) -> list[EIP712Field]:
for _, field in fields:
if isinstance(field, Reference):
# get field from definition section
Expand Down Expand Up @@ -165,7 +161,7 @@ def to_erc7730_mapper(eip712DappDescriptor: EIP712DAppDescriptor) -> ERC7730Desc
eip712Domains.append(EIP712Domain(name=item.label, type=item.format.name))
types[mapper.label] = eip712Domains
formats[mapper.label] = Format(
id=None, intent=None, fields=Fields(root=fields), required=None, screens=None
id=None, intent=None, fields=Field(root=fields), required=None, screens=None
)
schemas.append(EIP712JsonSchema(primaryType=mapper.label, types=types))
Expand All @@ -174,3 +170,4 @@ def to_erc7730_mapper(eip712DappDescriptor: EIP712DAppDescriptor) -> ERC7730Desc
display = Display(definitions=None, formats=formats)
metadata = Metadata(owner=None, info=None, token=None, constants=None, enums=None)
return ERC7730Descriptor(context=context, includes=None, metadata=metadata, display=display)
"""
72 changes: 64 additions & 8 deletions src/erc7730/model/context.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
from typing import Union, Optional
from pydantic import AnyUrl
from enum import Enum
from typing import ForwardRef, Union, Optional
from pydantic import AnyUrl, RootModel, field_validator
from erc7730.model.base import BaseLibraryModel
from erc7730.model.types import ContractAddress, Id
from erc7730.model.abi import ABI


class EIP712Domain(BaseLibraryModel):
class NameType(BaseLibraryModel):
name: str
type: str


class EIP712JsonSchema(BaseLibraryModel):
primaryType: str
types: dict[str, list[EIP712Domain]]
types: dict[str, list[NameType]]

@field_validator("types")
@classmethod
def validate_types(cls, value: dict[str, list[NameType]]) -> dict[str, list[NameType]]:
# validate that EIP712Domain with expected values
return value


class EIP712Schema(BaseLibraryModel):
Expand All @@ -26,22 +32,72 @@ class Domain(BaseLibraryModel):
verifyingContract: Optional[ContractAddress] = None


class Deployment(BaseLibraryModel):
chainId: Optional[int] = None
address: Optional[str] = None


class Deployments(RootModel[list[Deployment]]):
"""deployments"""


class EIP712(BaseLibraryModel):
domain: Optional[Domain] = None
schemas: Optional[list[Union[EIP712JsonSchema, AnyUrl]]] = None
domainSeparator: Optional[str] = None
deployments: Optional[Deployments] = None


class EIP712DomainBinding(BaseLibraryModel):
eip712: EIP712


AbiJsonSchema = list[ABI]
class AbiParameter(BaseLibraryModel):
name: str
type: str
internalType: Optional[str] = None
components: Optional[list[ForwardRef("AbiParameter")]] = None # type: ignore


AbiParameter.model_rebuild()


class StateMutability(Enum):
pure = "pure"
view = "view"
nonpayable = "nonpayable"
payable = "payable"


class Type(Enum):
function = "function"
constructor = "constructor"
receive = "receive"
fallback = "fallback"


class AbiJsonSchemaItem(BaseLibraryModel):
name: str
inputs: list[AbiParameter]
outputs: Optional[list[AbiParameter]]
stateMutability: Optional[StateMutability] = None
type: Type


class AbiJsonSchema(RootModel[list[AbiJsonSchemaItem]]):
"""abi json schema"""


class Factory(BaseLibraryModel):
deployments: Deployments
deployEvent: str


class Contract(BaseLibraryModel):
chainId: Optional[int] = None
address: Optional[ContractAddress] = None
abi: Optional[Union[AnyUrl, AbiJsonSchema]] = None
deployments: Optional[Deployments] = None
addressMatcher: Optional[AnyUrl] = None
factory: Optional[Factory] = None


class ContractBinding(BaseLibraryModel):
Expand Down
Loading

0 comments on commit 3ef1679

Please sign in to comment.