Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix output mapper type -> EIP712DappDescriptor #2

Merged
merged 4 commits into from
Sep 17, 2024
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
96 changes: 75 additions & 21 deletions src/erc7730/mapper/mapper.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,95 @@

from pydantic import AnyUrl
from erc7730.common.pydantic import model_from_json_bytes
from erc7730.model.context import EIP712JsonSchema
from erc7730.model.erc7730_descriptor import ERC7730Descriptor, EIP712Context
from eip712 import EIP712BaseMapper
from erc7730.model.display import Display, Reference, Field, StructFormats, FieldFormat, Fields
from eip712 import EIP712DAppDescriptor, EIP712ContractDescriptor, EIP712Mapper, EIP712MessageDescriptor, EIP712Field, EIP712Format
import requests


def to_eip712_mapper(erc7730: ERC7730Descriptor) -> EIP712BaseMapper:
def to_eip712_mapper(erc7730: ERC7730Descriptor) -> EIP712DAppDescriptor|list[Exception]:
exceptions = list[Exception]()
context = erc7730.context
if (context is not None and isinstance(context, EIP712Context)):
domain = context.eip712.domain
if (domain is None):
raise Exception(f"no domain defined for {context.eip712}")
exceptions.append(Exception(f"no domain defined for {context.eip712}")) # type: ignore
else:
if (domain.chainId is None):
raise Exception(f"chain id is None for {domain}")
exceptions.append(Exception(f"chain id is None for {domain}")) # type: ignore
elif (domain.verifyingContract is None):
raise Exception(f"verifying contract is None for {domain}")
exceptions.append(Exception(f"verifying contract is None for {domain}")) # type: ignore
else:
chain_id = domain.chainId
contract_address = domain.verifyingContract
display_name = ""
if (domain.name is not None):
display_name = domain.name
schema = dict[str, str]()
schemas = context.eip712.schemas
if(schemas is not None):
sch = None
for idx, item in enumerate(schemas):
schs = context.eip712.schemas
if schs is not None:
for item in schs:
sch = None
if (isinstance(item, EIP712JsonSchema)):
sch = item
else:
response = requests.get(item.__str__())
sch = model_from_json_bytes(response.content, EIP712JsonSchema)
for domain in sch.types["EIP712Domain"]:
schema[idx.__str__() + domain.name] = domain.type
return EIP712BaseMapper(chain_id, contract_address, schema, display_name)
try:
response = requests.get(item.__str__())
sch = model_from_json_bytes(response.content, model = EIP712JsonSchema)
except Exception as e:
exceptions.append(e) # type: ignore
if sch is not None:
for key in sch.types:
for d in sch.types[key]:
schema[key + "." + d.name] = d.type
chain_id = domain.chainId
contract_address = domain.verifyingContract
name = ""
if (domain.name is not None):
name = domain.name
display = erc7730.display
contracts = list[EIP712ContractDescriptor]()
if display is not None:
for primaryType in display.formats:
format = display.formats[primaryType]
messages = list[EIP712MessageDescriptor]()
if format.fields is not None:
eip712Fields = parseFields(display, primaryType, list[EIP712Field](), format.fields)
messages.append(EIP712MessageDescriptor(schema = schema, mapper = EIP712Mapper(label = primaryType, fields = eip712Fields)))
contracts.append(EIP712ContractDescriptor(address=contract_address, contractName=name, messages=messages))
return EIP712DAppDescriptor(blockchainName="ethereum", chainId=chain_id, name=name, contracts=contracts)
else:
raise Exception(f"context for {erc7730} is None or is not EIP712")

exceptions.append(Exception(f"context for {erc7730} is None or is not EIP712")) # type: ignore
return exceptions

def parseFields(display: Display, primaryType: str, output: list[EIP712Field], fields: Fields) -> list[EIP712Field]:
for _, field in fields:
if isinstance(field, Reference):
# get field from definition section
if display.definitions is not None:
for _, f in display.definitions[field.ref]:
parseField(primaryType, output, f)
elif isinstance(field, StructFormats):
parseFields(display, primaryType, output, field.fields)
elif isinstance(field, Field):
parseField(primaryType, output, field)
return output

def parseField(primaryType: str, output: list[EIP712Field], field: Field) -> list[EIP712Field]:
name = field.label
assetPath = None
fieldFormat = None
match field.format:
case FieldFormat.NFT_NAME:
assetPath = field.collectionPath
case FieldFormat.TOKEN_NAME:
if (field.tokenAmountParameters is not None):
assetPath = field.tokenAmountParameters.tokenPath
fieldFormat = EIP712Format.AMOUNT
case FieldFormat.ALLOWANCE_AMOUNT:
if field.allowanceAmountParameters is not None:
assetPath = field.allowanceAmountParameters.tokenPath
fieldFormat = EIP712Format.AMOUNT
case FieldFormat.DATE:
fieldFormat = EIP712Format.DATETIME
case _:
pass
output.append(EIP712Field(path=primaryType, label=name, assetPath=assetPath, format=fieldFormat, coinRef=None))
return output

7 changes: 4 additions & 3 deletions src/erc7730/model/context.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Union, Optional
from typing import ForwardRef, Union, Optional
from pydantic import AnyUrl
from erc7730.model.base import BaseLibraryModel
from erc7730.model.types import ContractAddress, Id
Expand Down Expand Up @@ -32,8 +32,9 @@ class AbiParameter(BaseLibraryModel):
name: str
type: str
internalType: Optional[str] = None
components: Optional[list[BaseLibraryModel]] = None
""" todo: use AbiParameter instead of BaseLibraryModel type """
components: Optional[list[ForwardRef('AbiParameter')]] = None # type: ignore
AbiParameter.model_rebuild()
""" todo: use AbiParameter instead of BaseLibraryModel type """

class AbiJsonSchema(BaseLibraryModel):
name: str
Expand Down
55 changes: 27 additions & 28 deletions src/erc7730/model/display.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from erc7730.model.base import BaseLibraryModel
from erc7730.model.types import Id
from typing import Any, Optional, Dict, Union
from typing import ForwardRef, Optional, Dict, Union
from enum import StrEnum
from pydantic import RootModel
from pydantic import RootModel, Field as PydanticField



Expand All @@ -23,31 +23,9 @@ class FieldFormat(StrEnum):
ENUM = "enum"

class Reference(BaseLibraryModel):
ref: str
ref: str = PydanticField(alias='$ref')
params: Optional[Dict[str, str]]

class FieldDescription(BaseLibraryModel):
id: Optional[Id]
label: str
format: FieldFormat


class Fields(RootModel[Dict[str, Union[Reference, FieldDescription, Any]]]):
""" todo use StructFormats instead """

class StructFormats(BaseLibraryModel):
fields: Fields

class Screen(BaseLibraryModel):
pass

class Format(BaseLibraryModel):
id: Optional[Id] = None
intent: Optional[str] = None
fields: Optional[Fields] = None
required: Optional[list[str]] = None
screens: Optional[Dict[str, list[Screen]]] = None

class TokenAmountParameters(BaseLibraryModel):
tokenPath: str
nativeCurrencyAddress: Optional[str] = None
Expand All @@ -68,9 +46,6 @@ class AllowanceAmountParameters(BaseLibraryModel):
nativeCurrencyAddress:Optional[str] = None

class Field(BaseLibraryModel):
id: Optional[Id] = None
label: str
format: str
sources: Optional[list[Source]] = None
collectionPath: Optional[str] = None
tokenAmountParameters: Optional[TokenAmountParameters] = None
Expand All @@ -79,6 +54,30 @@ class Field(BaseLibraryModel):
dateParameters: Optional[DateParameters] = None
enumParameters: Optional[str] = None

class FieldDescription(BaseLibraryModel):
id: Optional[Id]
label: str
format: FieldFormat
params: Optional[Field]

class StructFormats(BaseLibraryModel):
fields: ForwardRef('Fields') # type: ignore

class Fields(RootModel[Dict[str, Union[Reference, Field, StructFormats]]]):
""" todo use StructFormats instead """
StructFormats.model_rebuild()

class Screen(BaseLibraryModel):
pass

class Format(BaseLibraryModel):
id: Optional[Id] = None
intent: Optional[str] = None
fields: Optional[Fields] = None
required: Optional[list[str]] = None
screens: Optional[Dict[str, list[Screen]]] = None



class Display(BaseLibraryModel):
definitions: Optional[Dict[str, Field]] = None
Expand Down
17 changes: 8 additions & 9 deletions tests/src/erc7730/test_datamodel.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from pathlib import Path
from erc7730.common.pydantic import model_from_json_file_or_none
from erc7730.model.erc7730_descriptor import ERC7730Descriptor
import pytest
import glob

files = glob.glob('clear-signing-erc7730-registry/registry/*/*.json')

@pytest.mark.parametrize("file", files)
def test_from_erc7730(file: str) -> None:
assert model_from_json_file_or_none(Path(file), ERC7730Descriptor) is not None

def test_from_erc7730() -> None:
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/makerdao/eip712-permit-DAI.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/aave/calldata-lpv2.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/lido/calldata-stETH.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/lido/calldata-wstETH.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/paraswap/calldata-AugustusSwapper.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/tether/calldata-usdt.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/uniswap/calldata-UniswapV3Router02.json"), ERC7730Descriptor) is not None
assert model_from_json_file_or_none(Path("clear-signing-erc7730-registry/registry/uniswap/eip712-permit2.json"), ERC7730Descriptor) is not None
6 changes: 3 additions & 3 deletions tests/src/erc7730/test_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ def test_to_eip712_mapper() -> None:
eip712_mapper = to_eip712_mapper(uniswap_eip712_cs_descriptor)
assert eip712_mapper is not None
assert eip712_mapper.chain_id == 1
assert eip712_mapper.contract_address.__str__() == "0x000000000022D473030F116dDEE9F6B43aC78BA3"
assert eip712_mapper.display_name == "Permit2"
assert eip712_mapper.schema.__len__() == 6
assert eip712_mapper.name == "Permit2"
assert eip712_mapper.contracts.__len__() == 2
assert eip712_mapper.contracts[0].messages.__len__() == 1
Loading