Source code for erc7730.lint.lint_validate_eip712_domain
from typing import final, override
from eip712.model.schema import EIP712SchemaField
from erc7730.common.output import OutputAdder
from erc7730.lint import ERC7730Linter
from erc7730.model.resolved.context import EIP712Schema, ResolvedEIP712Context
from erc7730.model.resolved.descriptor import ResolvedERC7730Descriptor
# EIP-712 canonical domain field order and types as specified in the EIP-712 standard.
# See https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator
EIP712_DOMAIN_CANONICAL_ORDER: list[tuple[str, str]] = [
("name", "string"),
("version", "string"),
("chainId", "uint256"),
("verifyingContract", "address"),
("salt", "bytes32"),
]
_EIP712_DOMAIN_KNOWN_NAMES = {name for name, _ in EIP712_DOMAIN_CANONICAL_ORDER}
[docs]
def validate_eip712_domain_fields(
domain_fields: list[EIP712SchemaField],
out: OutputAdder,
) -> None:
"""Validate the EIP712Domain type fields against the canonical EIP-712 order.
Emits:
* Warning for fields not in the canonical list (name, version, chainId, verifyingContract, salt).
* Error for fields that are out of the EIP-712 canonical order.
:param domain_fields: the EIP712Domain fields from the schema
:param out: warning handler
"""
canonical_order = [name for name, _ in EIP712_DOMAIN_CANONICAL_ORDER]
# Check for unknown fields
for field in domain_fields:
if field.name not in _EIP712_DOMAIN_KNOWN_NAMES:
out.warning(
title="Non-standard EIP712Domain field",
message=f'EIP712Domain field "{field.name}" is not part of the EIP-712 standard '
f"(expected: {', '.join(canonical_order)}).", # no brackets — rich interprets them as tags
)
# Check ordering: filter to only known fields and verify they appear in canonical order
known_field_names = [f.name for f in domain_fields if f.name in _EIP712_DOMAIN_KNOWN_NAMES]
canonical_positions = {name: i for i, name in enumerate(canonical_order)}
expected_order = sorted(known_field_names, key=lambda n: canonical_positions[n])
if known_field_names != expected_order:
out.error(
title="EIP712Domain field order",
message=f"EIP712Domain fields are not in the canonical EIP-712 order. "
f"Found: ({', '.join(known_field_names)}), "
f"expected: ({', '.join(expected_order)}).",
)
[docs]
@final
class ValidateEIP712DomainLinter(ERC7730Linter):
"""Validate ``EIP712Domain`` field ordering and names in EIP-712 schemas.
For each schema that includes an ``EIP712Domain`` type, this linter checks that:
* All fields are part of the canonical EIP-712 set (name, version, chainId, verifyingContract, salt).
* Fields appear in the canonical EIP-712 order.
"""
[docs]
@override
def lint(self, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None:
if not isinstance(descriptor.context, ResolvedEIP712Context):
return
if descriptor.context.eip712.schemas is None:
return
for schema in descriptor.context.eip712.schemas:
if isinstance(schema, EIP712Schema) and "EIP712Domain" in schema.types:
validate_eip712_domain_fields(schema.types["EIP712Domain"], out)