Source code for erc7730.model.paths.path_schemas

from dataclasses import dataclass
from typing import assert_never

from eip712.model.schema import EIP712SchemaField

from erc7730.model.abi import Component, Function, InputOutput
from erc7730.model.context import EIP712Schema
from erc7730.model.paths import (
    ROOT_DATA_PATH,
    Array,
    ArrayElement,
    ArraySlice,
    ContainerPath,
    DataPath,
    DataPathElement,
    Field,
)
from erc7730.model.paths.path_ops import data_path_append
from erc7730.model.resolved.display import (
    ResolvedAddressNameParameters,
    ResolvedCallDataParameters,
    ResolvedDateParameters,
    ResolvedEnumParameters,
    ResolvedField,
    ResolvedFieldDescription,
    ResolvedFormat,
    ResolvedNestedFields,
    ResolvedNftNameParameters,
    ResolvedTokenAmountParameters,
    ResolvedUnitParameters,
)
from erc7730.model.resolved.path import ResolvedPath


[docs] @dataclass(kw_only=True, frozen=True) class FormatPaths: data_paths: set[DataPath] # References to values in the serialized data container_paths: set[ContainerPath] # References to values in the container
[docs] def compute_eip712_schema_paths(schema: EIP712Schema) -> set[DataPath]: """ Compute the sets of valid schema paths for an EIP-712 schema. :param schema: EIP-712 schema :return: valid schema paths """ if (primary_type := schema.types.get(schema.primaryType)) is None: raise ValueError(f"Invalid schema: primaryType {schema.primaryType} not in types") paths: set[DataPath] = set() def append_paths(path: DataPath, current_type: list[EIP712SchemaField]) -> None: for field in current_type: if len(field.name) == 0: continue # skip unnamed parameters sub_path = data_path_append(path, Field(identifier=field.name)) field_base_type = field.type.rstrip("[]") if field_base_type in {"bytes"}: paths.add(data_path_append(sub_path, Array())) if field_base_type != field.type: sub_path = data_path_append(sub_path, Array()) paths.add(sub_path) if (target_type := schema.types.get(field_base_type)) is not None: append_paths(sub_path, target_type) else: paths.add(sub_path) append_paths(ROOT_DATA_PATH, primary_type) return paths
[docs] def compute_abi_schema_paths(abi: Function) -> set[DataPath]: """ Compute the sets of valid schema paths for an ABI function. :param abi: Solidity ABI function :return: valid schema paths """ paths: set[DataPath] = set() def append_paths(path: DataPath, params: list[InputOutput] | list[Component] | None) -> None: if not params: return None for param in params: if len(param.name) == 0: continue # skip unnamed parameters sub_path = data_path_append(path, Field(identifier=param.name)) param_base_type = param.type.rstrip("[]") if param_base_type in {"bytes"}: paths.add(data_path_append(sub_path, Array())) if param_base_type != param.type: sub_path = data_path_append(sub_path, Array()) paths.add(sub_path) if param.components: append_paths(sub_path, param.components) # type: ignore else: paths.add(sub_path) append_paths(ROOT_DATA_PATH, abi.inputs) return paths
[docs] def compute_format_schema_paths(format: ResolvedFormat) -> FormatPaths: """ Compute the sets of schema paths referred in an ERC7730 Format section. :param format: resolved $.display.format section :return: schema paths used by field formats """ data_paths: set[DataPath] = set() # references to values in the serialized data container_paths: set[ContainerPath] = set() # references to values in the container if format.fields is not None: def add_path(path: ResolvedPath | None) -> None: match path: case None: pass case ContainerPath(): container_paths.add(path) case DataPath(): data_paths.add(data_path_to_schema_path(path)) case _: assert_never(path) def append_paths(field: ResolvedField) -> None: add_path(field.path) match field: case ResolvedFieldDescription(): match field.params: case None: pass case ResolvedAddressNameParameters(): pass case ResolvedCallDataParameters(calleePath=callee_path): add_path(callee_path) case ResolvedTokenAmountParameters(tokenPath=token_path): add_path(token_path) case ResolvedNftNameParameters(collectionPath=collection_path): add_path(collection_path) case ResolvedDateParameters(): pass case ResolvedUnitParameters(): pass case ResolvedEnumParameters(): pass case _: assert_never(field.params) case ResolvedNestedFields(): for nested_field in field.fields: append_paths(nested_field) case _: assert_never(field) for field in format.fields: append_paths(field) return FormatPaths(data_paths=data_paths, container_paths=container_paths)
[docs] def data_path_to_schema_path(path: DataPath) -> DataPath: """ Convert a data path to a schema path. Example: #.foo.[].[-2].[1:5].bar -> #.foo.[].[].[].bar :param path: data path :return: schema path """ def to_schema(element: DataPathElement) -> DataPathElement: match element: case Field() as f: return f case Array() | ArrayElement() | ArraySlice(): return Array() case _: assert_never(element) return path.model_copy(update={"elements": [to_schema(e) for e in path.elements]})