diff --git a/src/erc7730/convert/calldata/convert_erc7730_v2_input_to_calldata.py b/src/erc7730/convert/calldata/convert_erc7730_v2_input_to_calldata.py index 67b176e0..a4f033c8 100644 --- a/src/erc7730/convert/calldata/convert_erc7730_v2_input_to_calldata.py +++ b/src/erc7730/convert/calldata/convert_erc7730_v2_input_to_calldata.py @@ -237,7 +237,7 @@ def _convert_v2_selector( creator_name=descriptor.metadata.owner, creator_legal_name=creator_legal_name, creator_url=creator_url, - contract_name=descriptor.context.id, + contract_name=descriptor.metadata.contractName or descriptor.context.id, deploy_date=deploy_date, ) diff --git a/src/erc7730/convert/calldata/v1/path.py b/src/erc7730/convert/calldata/v1/path.py index 02781a2c..a90b5b9f 100644 --- a/src/erc7730/convert/calldata/v1/path.py +++ b/src/erc7730/convert/calldata/v1/path.py @@ -136,6 +136,10 @@ def convert_container_path( field = CalldataDescriptorContainerPathValueV1.VALUE type_family = CalldataDescriptorTypeFamily.UINT type_size = 32 + case ContainerField.CHAIN_ID: + field = CalldataDescriptorContainerPathValueV1.CHAIN_ID + type_family = CalldataDescriptorTypeFamily.UINT + type_size = 32 case _: assert_never(path.field) return CalldataDescriptorValuePathV1( diff --git a/src/erc7730/convert/ledger/eip712/convert_erc7730_v2_to_eip712.py b/src/erc7730/convert/ledger/eip712/convert_erc7730_v2_to_eip712.py index c7a2731c..78c63c89 100644 --- a/src/erc7730/convert/ledger/eip712/convert_erc7730_v2_to_eip712.py +++ b/src/erc7730/convert/ledger/eip712/convert_erc7730_v2_to_eip712.py @@ -483,16 +483,18 @@ def _convert_resolved( domain = context.eip712.domain has_deployments = len(context.eip712.deployments) > 0 - # Get contract name from metadata - contract_name = descriptor.metadata.owner - if contract_name is None: + # owner is required for legacy EIP-712 conversion + if (owner := descriptor.metadata.owner) is None: return out.error( title="Missing owner", message="metadata.owner is required for legacy EIP-712 conversion.", ) + # Get contract name from metadata, preferring the explicit contractName field + contract_name = descriptor.metadata.contractName or owner + # Get dapp name: prefer domain.name, fall back to metadata.owner - dapp_name: str = (domain.name if domain is not None and domain.name is not None else None) or contract_name + dapp_name: str = (domain.name if domain is not None and domain.name is not None else None) or owner # Reconstruct EIP712Domain type domain_fields = _reconstruct_eip712_domain(domain, has_deployments, out) diff --git a/src/erc7730/lint/v2/lint_validate_max_length.py b/src/erc7730/lint/v2/lint_validate_max_length.py index 5dff6c35..8a826899 100644 --- a/src/erc7730/lint/v2/lint_validate_max_length.py +++ b/src/erc7730/lint/v2/lint_validate_max_length.py @@ -62,10 +62,11 @@ def _validate_metadata_lengths(cls, descriptor: ResolvedERC7730Descriptor, out: f"{CREATOR_URL_MAX_LENGTH} characters and may be truncated on Ledger devices.", ) - if descriptor.context.id is not None and len(descriptor.context.id) > CONTRACT_NAME_MAX_LENGTH: + contract_name = descriptor.metadata.contractName or descriptor.context.id + if contract_name is not None and len(contract_name) > CONTRACT_NAME_MAX_LENGTH: out.warning( - title="Contract id too long", - message=f"Contract id `{descriptor.context.id}` exceeds " + title="Contract name too long", + message=f"Contract name `{contract_name}` exceeds " f"{CONTRACT_NAME_MAX_LENGTH} characters and may be truncated on Ledger devices.", ) diff --git a/src/erc7730/model/calldata/v1/value.py b/src/erc7730/model/calldata/v1/value.py index aa2abeef..58817a59 100644 --- a/src/erc7730/model/calldata/v1/value.py +++ b/src/erc7730/model/calldata/v1/value.py @@ -77,6 +77,7 @@ class CalldataDescriptorContainerPathValueV1(IntEnum): FROM = 0x0 TO = 0x1 VALUE = 0x2 + CHAIN_ID = 0x3 class CalldataDescriptorPathElementBaseV1(Model): diff --git a/src/erc7730/model/paths/__init__.py b/src/erc7730/model/paths/__init__.py index 81be9803..b92b3b69 100644 --- a/src/erc7730/model/paths/__init__.py +++ b/src/erc7730/model/paths/__init__.py @@ -123,6 +123,9 @@ class ContainerField(StrEnum): TO = auto() """The destination address of the containing transaction, ie the target smart contract address.""" + CHAIN_ID = "chainId" + """The chain id of the transaction / verifying contract of the message.""" + DataPathElement = Annotated[ Field | ArrayElement | ArraySlice | Array, diff --git a/src/erc7730/model/paths/path_parser.py b/src/erc7730/model/paths/path_parser.py index b9f13638..ce2b1afe 100644 --- a/src/erc7730/model/paths/path_parser.py +++ b/src/erc7730/model/paths/path_parser.py @@ -25,7 +25,7 @@ ?descriptor_path_component: field | array_element container_path: "@." container_field - !container_field: "from" | "to" | "value" + !container_field: "from" | "to" | "value" | "chainId" ?data_path: absolute_data_path | relative_data_path absolute_data_path: "#." data_path_component ("." data_path_component)* diff --git a/tests/model/paths/test_path_parser.py b/tests/model/paths/test_path_parser.py index 7c133c10..3b06510b 100644 --- a/tests/model/paths/test_path_parser.py +++ b/tests/model/paths/test_path_parser.py @@ -40,6 +40,14 @@ def test_valid_input_container_path() -> None: ) +def test_valid_input_container_path_chain_id() -> None: + _test_valid_input_path( + string="@.chainId", + obj=ContainerPath(field=ContainerField.CHAIN_ID), + json="""{ "type": "container", "field": "chainId" }""", + ) + + def test_valid_input_data_path_absolute() -> None: _test_valid_input_path( string="#.params.[].[-2].[1:5].[:5].[5:].amountIn", @@ -135,6 +143,14 @@ def test_valid_resolved_container_path() -> None: ) +def test_valid_resolved_container_path_chain_id() -> None: + _test_valid_resolved_path( + string="@.chainId", + obj=ContainerPath(field=ContainerField.CHAIN_ID), + json="""{ "type": "container", "field": "chainId" }""", + ) + + def test_valid_resolved_data_path() -> None: _test_valid_resolved_path( string="#.params.[].[-2].[1:5].amountIn",