Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ def erc7730_v2_descriptor_to_calldata_descriptors(
if chain_id not in deployment_chain_ids:
return []

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

context = cast(ResolvedContractContext, resolved_descriptor.context)
Expand Down
5 changes: 5 additions & 0 deletions src/erc7730/convert/calldata/v1/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ def convert_container_path(
field = CalldataDescriptorContainerPathValueV1.VALUE
type_family = CalldataDescriptorTypeFamily.UINT
type_size = 32
case ContainerField.CHAINID:
return out.error(
title="Unsupported container field",
message="Container field @.chainId is not supported in calldata descriptor v1 format.",
)
case _:
assert_never(path.field)
return CalldataDescriptorValuePathV1(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,7 @@ def convert(
message="Descriptor context is not EIP-712; only EIP-712 descriptors can be converted.",
)

# Resolve the v2 descriptor
resolved = ERC7730InputToResolved().convert(input_descriptor, out)
resolved = ERC7730InputToResolved().convert(input_descriptor, out, strict_maps=True)
if resolved is None:
return None

Expand Down
31 changes: 10 additions & 21 deletions src/erc7730/convert/resolved/v2/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
from erc7730.common.output import OutputAdder
from erc7730.common.properties import get_property
from erc7730.model.input.path import ContainerPathStr, DataPathStr
from erc7730.model.input.v2.common import InputMapReference
from erc7730.model.input.v2.descriptor import InputERC7730Descriptor
from erc7730.model.input.v2.display import InputMapReference
from erc7730.model.input.v2.metadata import InputMapDefinition
from erc7730.model.paths import ROOT_DESCRIPTOR_PATH, ArrayElement, ContainerPath, DataPath, DescriptorPath, Field
from erc7730.model.paths.path_ops import descriptor_path_append, to_absolute
from erc7730.model.types import MixedCaseAddress
Expand Down Expand Up @@ -196,37 +197,25 @@ def get(self, path: DescriptorPath, out: OutputAdder) -> Any:
@override
def resolve_map_reference(self, prefix: DataPath, map_ref: InputMapReference, out: OutputAdder) -> Any:
"""
Resolve a map reference to its value by looking up the map and resolving the keyPath.
Validate a map reference: check that the map path resolves to a valid map definition in metadata.maps,
and that the keyPath is a valid data/container path.

:param prefix: current path prefix
:param map_ref: map reference with map descriptor path and keyPath
:param out: error handler
:return: resolved value from map, or None if not found
:return: the map reference if valid, or None if validation fails
"""
# Get the map definition
if (map_def := self.get(map_ref.map, out)) is None:
return out.error(
title="Invalid map reference",
message=f"Map at {map_ref.map} does not exist.",
)
return None

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

# Resolve the key path to get the key value
if (self.resolve_path(map_ref.keyPath, out)) is None:
if self.resolve_path(map_ref.keyPath, out) is None:
return None

# For map references, the key path is either a DataPath or ContainerPath
# We can't actually resolve the runtime value here during conversion,
# so we store the path for runtime resolution. However, for constant validation
# we could check if it's a constant path.
# Since this is input-to-resolved conversion, we pass through the structure.
# The actual key lookup happens at display time, not conversion time.

# Return the map reference as-is for the resolved model to handle at runtime
return map_ref
Loading
Loading