Skip to content

Commit ca31709

Browse files
feat: support constants and map references in v2 schema fields
Align v2 linter with updated ERC-7730 schema that widens many fields to accept constant references ($.metadata.constants.*) and map references alongside literal values. - Add InputMapReference model and update Pydantic input models for display, context and metadata fields (token, callee, selector, label, owner, etc.) - Thread strict_maps flag through the resolution pipeline: lint mode validates map references but drops them for resolution, calldata/convert modes error - Validate map references: check map path exists in metadata.maps, check keyPath is a valid container/data/descriptor path - Reject structured data paths (#.) in keyPath for context/metadata sections - Add @.chainId to container path grammar per spec - Handle empty resolved params dict after map reference drop Made-with: Cursor
1 parent 0f5088b commit ca31709

14 files changed

Lines changed: 454 additions & 186 deletions

File tree

src/erc7730/convert/calldata/convert_erc7730_v2_input_to_calldata.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ def erc7730_v2_descriptor_to_calldata_descriptors(
131131
if chain_id not in deployment_chain_ids:
132132
return []
133133

134-
# Resolve the v2 descriptor
135-
if (resolved_descriptor := ERC7730InputToResolved().convert(input_descriptor, out)) is None:
134+
if (resolved_descriptor := ERC7730InputToResolved().convert(input_descriptor, out, strict_maps=True)) is None:
136135
return []
137136

138137
context = cast(ResolvedContractContext, resolved_descriptor.context)

src/erc7730/convert/ledger/eip712/convert_erc7730_v2_to_eip712.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,7 @@ def convert(
459459
message="Descriptor context is not EIP-712; only EIP-712 descriptors can be converted.",
460460
)
461461

462-
# Resolve the v2 descriptor
463-
resolved = ERC7730InputToResolved().convert(input_descriptor, out)
462+
resolved = ERC7730InputToResolved().convert(input_descriptor, out, strict_maps=True)
464463
if resolved is None:
465464
return None
466465

src/erc7730/convert/resolved/v2/constants.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from erc7730.common.properties import get_property
1010
from erc7730.model.input.path import ContainerPathStr, DataPathStr
1111
from erc7730.model.input.v2.descriptor import InputERC7730Descriptor
12-
from erc7730.model.input.v2.display import InputMapReference
12+
from erc7730.model.input.v2.common import InputMapReference
13+
from erc7730.model.input.v2.metadata import InputMapDefinition
1314
from erc7730.model.paths import ROOT_DESCRIPTOR_PATH, ArrayElement, ContainerPath, DataPath, DescriptorPath, Field
1415
from erc7730.model.paths.path_ops import descriptor_path_append, to_absolute
1516
from erc7730.model.types import MixedCaseAddress
@@ -196,37 +197,25 @@ def get(self, path: DescriptorPath, out: OutputAdder) -> Any:
196197
@override
197198
def resolve_map_reference(self, prefix: DataPath, map_ref: InputMapReference, out: OutputAdder) -> Any:
198199
"""
199-
Resolve a map reference to its value by looking up the map and resolving the keyPath.
200+
Validate a map reference: check that the map path resolves to a valid map definition in metadata.maps,
201+
and that the keyPath is a valid data/container path.
200202
201203
:param prefix: current path prefix
202204
:param map_ref: map reference with map descriptor path and keyPath
203205
:param out: error handler
204-
:return: resolved value from map, or None if not found
206+
:return: the map reference if valid, or None if validation fails
205207
"""
206-
# Get the map definition
207208
if (map_def := self.get(map_ref.map, out)) is None:
208-
return out.error(
209-
title="Invalid map reference",
210-
message=f"Map at {map_ref.map} does not exist.",
211-
)
209+
return None
212210

213-
# Ensure map has the expected structure
214-
if not hasattr(map_def, "values") or not isinstance(map_def.values, dict):
211+
if not isinstance(map_def, InputMapDefinition):
215212
return out.error(
216213
title="Invalid map reference",
217-
message=f"Map at {map_ref.map} is not a valid map definition.",
214+
message=f"Map at {map_ref.map} is not a valid map definition (expected a map object, "
215+
f"got {type(map_def).__name__}).",
218216
)
219217

220-
# Resolve the key path to get the key value
221-
if (self.resolve_path(map_ref.keyPath, out)) is None:
218+
if self.resolve_path(map_ref.keyPath, out) is None:
222219
return None
223220

224-
# For map references, the key path is either a DataPath or ContainerPath
225-
# We can't actually resolve the runtime value here during conversion,
226-
# so we store the path for runtime resolution. However, for constant validation
227-
# we could check if it's a constant path.
228-
# Since this is input-to-resolved conversion, we pass through the structure.
229-
# The actual key lookup happens at display time, not conversion time.
230-
231-
# Return the map reference as-is for the resolved model to handle at runtime
232221
return map_ref

0 commit comments

Comments
 (0)