|
15 | 15 | from erc7730.lint.v2 import ERC7730Linter |
16 | 16 | from erc7730.lint.v2.path_schemas import compute_format_schema_paths |
17 | 17 | from erc7730.model.paths import DataPath |
| 18 | +from erc7730.model.paths.path_ops import data_path_starts_with |
18 | 19 | from erc7730.model.paths.path_schemas import compute_abi_schema_paths |
19 | 20 | from erc7730.model.resolved.v2.context import ResolvedContractContext, ResolvedEIP712Context |
20 | 21 | from erc7730.model.resolved.v2.descriptor import ResolvedERC7730Descriptor |
@@ -101,21 +102,27 @@ def _validate_contract_display_fields(cls, descriptor: ResolvedERC7730Descriptor |
101 | 102 | format_paths = compute_format_schema_paths(fmt) |
102 | 103 | abi_paths = abi_paths_by_selector[selector] |
103 | 104 |
|
104 | | - # Check for display fields referencing non-existent ABI paths |
| 105 | + # Check for display fields referencing non-existent ABI paths. |
| 106 | + # A format path is valid if it matches an ABI path exactly OR is a prefix of one |
| 107 | + # (e.g. defining a field for an array root covers all nested elements). |
105 | 108 | for path in format_paths.data_paths - abi_paths: |
106 | | - out.error( |
107 | | - title="Invalid display field", |
108 | | - message=f"A display field is defined for `{path}`, but it does not exist in function " |
109 | | - f"{selector} ABI (see {explorer_url}). Please check the field path is valid.", |
110 | | - ) |
111 | | - |
112 | | - # Check for ABI paths without corresponding display fields |
| 109 | + if not any(data_path_starts_with(abi_path, path) for abi_path in abi_paths): |
| 110 | + out.error( |
| 111 | + title="Invalid display field", |
| 112 | + message=f"A display field is defined for `{path}`, but it does not exist in function " |
| 113 | + f"{selector} ABI (see {explorer_url}). Please check the field path is valid.", |
| 114 | + ) |
| 115 | + |
| 116 | + # Check for ABI paths without corresponding display fields. |
| 117 | + # An ABI path is covered if it matches a format path exactly OR a format path is a prefix of it |
| 118 | + # (e.g. a field defined for an array root covers all descendant paths). |
113 | 119 | for path in abi_paths - format_paths.data_paths: |
114 | | - out.warning( |
115 | | - title="Missing display field", |
116 | | - message=f"No display field is defined for path `{path}` in function {selector} " |
117 | | - f"(see {explorer_url}).", |
118 | | - ) |
| 120 | + if not any(data_path_starts_with(path, fmt_path) for fmt_path in format_paths.data_paths): |
| 121 | + out.warning( |
| 122 | + title="Missing display field", |
| 123 | + message=f"No display field is defined for path `{path}` in function {selector} " |
| 124 | + f"(see {explorer_url}).", |
| 125 | + ) |
119 | 126 |
|
120 | 127 | # Check selector exhaustiveness: all ABI functions should have display formats |
121 | 128 | for selector, abi in reference_abis.functions.items(): |
|
0 commit comments