Skip to content

Commit

Permalink
feat: implement resolution of selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
jnicoulaud-ledger committed Oct 28, 2024
1 parent ea981dc commit edb376c
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 11 deletions.
55 changes: 48 additions & 7 deletions src/erc7730/convert/resolved/convert_erc7730_input_to_resolved.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from typing import assert_never, final, override

from eip712.model.schema import EIP712Type
from pydantic_string_url import HttpUrl

from erc7730.common import client
from erc7730.common.abi import reduce_signature, signature_to_selector
from erc7730.common.output import OutputAdder
from erc7730.convert import ERC7730Converter
from erc7730.convert.resolved.constants import ConstantProvider, DefaultConstantProvider
Expand Down Expand Up @@ -43,7 +45,7 @@
ResolvedNestedFields,
)
from erc7730.model.resolved.metadata import ResolvedMetadata
from erc7730.model.types import Id
from erc7730.model.types import Id, Selector


@final
Expand All @@ -57,7 +59,7 @@ class ERC7730InputToResolved(ERC7730Converter[InputERC7730Descriptor, ResolvedER
- References have been inlined
- Constants have been inlined
- Field definitions have been inlined
- Selectors have been converted to 4 bytes form (TODO not implemented)
- Selectors have been converted to 4 bytes form
"""

@override
Expand All @@ -68,7 +70,9 @@ def convert(self, descriptor: InputERC7730Descriptor, out: OutputAdder) -> Resol
return None
if (metadata := self._resolve_metadata(descriptor.metadata, out)) is None:
return None
if (display := self._resolve_display(descriptor.display, metadata.enums or {}, constants, out)) is None:
if (
display := self._resolve_display(descriptor.display, context, metadata.enums or {}, constants, out)
) is None:
return None

return ResolvedERC7730Descriptor(context=context, metadata=metadata, display=display)
Expand Down Expand Up @@ -199,14 +203,27 @@ def _resolve_schema(cls, schema: EIP712JsonSchema | HttpUrl, out: OutputAdder) -

@classmethod
def _resolve_display(
cls, display: InputDisplay, enums: dict[Id, EnumDefinition], constants: ConstantProvider, out: OutputAdder
cls,
display: InputDisplay,
context: ResolvedContractContext | ResolvedEIP712Context,
enums: dict[Id, EnumDefinition],
constants: ConstantProvider,
out: OutputAdder,
) -> ResolvedDisplay | None:
formats = {}
for format_key, format in display.formats.items():
for format_id, format in display.formats.items():
if (resolved_format_id := cls._resolve_format_id(format_id, context, out)) is None:
return None
if (
resolved_format := cls._resolve_format(format, display.definitions or {}, enums, constants, out)
) is not None:
formats[format_key] = resolved_format
) is None:
return None
if resolved_format_id in formats:
return out.error(
title="Duplicate format",
message=f"Descriptor contains 2 formats sections for {resolved_format_id}",
)
formats[resolved_format_id] = resolved_format

return ResolvedDisplay(formats=formats)

Expand Down Expand Up @@ -254,6 +271,30 @@ def _resolve_field_description(
}
)

@classmethod
def _resolve_format_id(
cls,
format_id: str,
context: ResolvedContractContext | ResolvedEIP712Context,
out: OutputAdder,
) -> EIP712Type | Selector | None:
match context:
case ResolvedContractContext():
if format_id.startswith("0x"):
return Selector(format_id)

if (reduced_signature := reduce_signature(format_id)) is not None:
return Selector(signature_to_selector(reduced_signature))

return out.error(
title="Invalid selector",
message=f""""{format_id}" is not a valid function signature or selector.""",
)
case ResolvedEIP712Context():
return format_id
case _:
assert_never(context)

@classmethod
def _resolve_format(
cls,
Expand Down
5 changes: 3 additions & 2 deletions src/erc7730/model/resolved/display.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Annotated, ForwardRef

from eip712.model.schema import EIP712Type
from pydantic import Discriminator, Field, Tag

from erc7730.model.base import Model
Expand All @@ -12,7 +13,7 @@
)
from erc7730.model.paths import ContainerPath, DataPath
from erc7730.model.resolved.path import ResolvedPath
from erc7730.model.types import Address, HexStr, Id
from erc7730.model.types import Address, HexStr, Id, Selector
from erc7730.model.unions import field_discriminator, field_parameters_discriminator

# ruff: noqa: N815 - camel case field names are tolerated to match schema
Expand Down Expand Up @@ -252,7 +253,7 @@ class ResolvedDisplay(Model):
Display Formatting Info Section.
"""

formats: dict[str, ResolvedFormat] = Field(
formats: dict[EIP712Type | Selector, ResolvedFormat] = Field(
title="List of field formats",
description="The list includes formatting info for each field of a structure. This list is indexed by a key"
"identifying uniquely the message's type in the abi. For smartcontracts, it is the selector of the"
Expand Down
11 changes: 11 additions & 0 deletions src/erc7730/model/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@
),
]

Selector = Annotated[
str,
Field(
title="Selector",
description="An Ethereum contract function identifier, in 4 bytes, hex encoded form.",
min_length=10,
max_length=10,
pattern=r"^0x[a-z0-9]+$",
),
]

HexStr = Annotated[
str,
Field(
Expand Down
2 changes: 1 addition & 1 deletion tests/convert/resolved/data/minimal_contract_resolved.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
},
"display": {
"formats": {
"function1(bytes4)": {
"0x5ca8f297": {
"fields": [
{
"path": {
Expand Down
2 changes: 1 addition & 1 deletion tests/convert/resolved/test_convert_input_to_resolved.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from tests.skip import single_or_skip

DATA = Path(__file__).resolve().parent / "data"
UPDATE_REFERENCES = False
UPDATE_REFERENCES = True


@pytest.mark.parametrize("input_file", ERC7730_DESCRIPTORS, ids=path_id)
Expand Down

0 comments on commit edb376c

Please sign in to comment.