Source code for erc7730.lint.lint_validate_display_fields

from typing import final, override

from erc7730.common.abi import function_to_selector
from erc7730.common.output import OutputAdder
from erc7730.lint import ERC7730Linter
from erc7730.model.paths import DataPath, Field
from erc7730.model.paths.path_ops import data_path_ends_with, path_starts_with, to_absolute
from erc7730.model.paths.path_schemas import (
    compute_abi_schema_paths,
    compute_eip712_schema_paths,
    compute_format_schema_paths,
)
from erc7730.model.resolved.context import EIP712Schema, ResolvedContractContext, ResolvedEIP712Context
from erc7730.model.resolved.descriptor import ResolvedERC7730Descriptor

AUTHORIZED_MISSING_DISPLAY_FIELDS = {
    Field(identifier="nonce"),
    Field(identifier="sigDeadline"),
}


[docs] @final class ValidateDisplayFieldsLinter(ERC7730Linter): """ - for each field of schema/ABI, check that there is a display field - for each field, check that display configuration is relevant with field type """
[docs] @override def lint(self, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: self._validate_eip712_paths(descriptor, out) self._validate_abi_paths(descriptor, out)
@classmethod def _validate_eip712_paths(cls, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: if isinstance(descriptor.context, ResolvedEIP712Context) and descriptor.context.eip712.schemas is not None: primary_types: set[str] = set() for schema in descriptor.context.eip712.schemas: if isinstance(schema, EIP712Schema): primary_types.add(schema.primaryType) if schema.primaryType not in schema.types: out.error( title="Invalid EIP712 Schema", message=f"Primary type `{schema.primaryType}` is not present in schema types. Please make " f"sure the EIP-712 schema includes a definition for the primary type.", ) continue if schema.primaryType not in descriptor.display.formats: out.error( title="Missing Display Format", message=f"Schema primary type `{schema.primaryType}` must have a display format defined.", ) continue eip712_paths = compute_eip712_schema_paths(schema) primary_type_format = descriptor.display.formats[schema.primaryType] format_paths = compute_format_schema_paths(primary_type_format).data_paths if (excluded := primary_type_format.excluded) is not None: excluded_paths = [to_absolute(path) for path in excluded] else: excluded_paths = [] for path in eip712_paths - format_paths: if any(path_starts_with(path, excluded_path) for excluded_path in excluded_paths): continue if any(data_path_ends_with(path, allowed) for allowed in AUTHORIZED_MISSING_DISPLAY_FIELDS): out.debug( title="Optional Display field missing", message=f"No display field is defined for path `{path}` in message " f"{schema.primaryType}. If intentionally excluded, please add it to `excluded` " f"list to avoid this warning.", ) else: out.warning( title="Missing Display field", message=f"No display field is defined for path `{path}` in {schema.primaryType}. " f"If intentionally excluded, please add it to `excluded` list to avoid this " f"warning.", ) for path in format_paths - eip712_paths: out.error( title="Invalid Display field", message=f"A display field is defined for `{path}`, but it does not exist in message " f"{schema.primaryType}. Please check the field path is valid according to the EIP-712 " f"schema.", ) else: out.error( title="Missing EIP712 Schema", message=f"EIP712 Schema is missing (found {schema})", ) for fmt in descriptor.display.formats: if fmt not in primary_types: out.error( title="Invalid Display Format", message=f"Type `{fmt}` is not in EIP712 schemas. Please check the type is valid according to " f"the EIP-712 schema.", ) @classmethod def _validate_abi_paths(cls, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: if isinstance(descriptor.context, ResolvedContractContext): abi_paths_by_selector: dict[str, set[DataPath]] = {} for abi in descriptor.context.contract.abi: if abi.type == "function": abi_paths_by_selector[function_to_selector(abi)] = compute_abi_schema_paths(abi) for selector, fmt in descriptor.display.formats.items(): if selector not in abi_paths_by_selector: out.error( title="Invalid selector", message=f"Selector {selector} not found in ABI.", ) continue format_paths = compute_format_schema_paths(fmt).data_paths abi_paths = abi_paths_by_selector[selector] if (excluded := fmt.excluded) is not None: excluded_paths = [to_absolute(path) for path in excluded] else: excluded_paths = [] for path in abi_paths - format_paths: if any(path_starts_with(path, excluded_path) for excluded_path in excluded_paths): continue if any(data_path_ends_with(path, allowed) for allowed in AUTHORIZED_MISSING_DISPLAY_FIELDS): out.debug( title="Optional Display Field Missing", message=f"No display field is defined for path `{path}` in function {selector}. If " f"intentionally excluded, please add it to `excluded` list to avoid this warning.", ) else: out.warning( title="Missing Display field", message=f"No display field is defined for path `{path}` in function {selector}. If " f"intentionally excluded, please add it to `excluded` list to avoid this warning.", ) for path in format_paths - abi_paths: out.error( title="Invalid Display field", message=f"A display field is defined for `{path}`, but it does not exist in function " f"{selector} ABI. Please check the field path is valid according to the ABI.", )