diff --git a/pdm.lock b/pdm.lock index d0e87c47..82ae6873 100644 --- a/pdm.lock +++ b/pdm.lock @@ -577,21 +577,19 @@ files = [ [[package]] name = "jsonschema" -version = "4.23.0" -requires_python = ">=3.8" +version = "4.25.0" +requires_python = ">=3.9" summary = "An implementation of JSON Schema validation for Python" groups = ["default"] dependencies = [ "attrs>=22.2.0", - "importlib-resources>=1.4.0; python_version < \"3.9\"", "jsonschema-specifications>=2023.03.6", - "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", "referencing>=0.28.4", "rpds-py>=0.7.1", ] files = [ - {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, - {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, + {file = "jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716"}, + {file = "jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f"}, ] [[package]] @@ -920,7 +918,7 @@ files = [ [[package]] name = "pydantic" -version = "2.11.4" +version = "2.11.7" requires_python = ">=3.9" summary = "Data validation using Python type hints" groups = ["default", "dev"] @@ -931,8 +929,8 @@ dependencies = [ "typing-inspection>=0.4.0", ] files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, + {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, + {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, ] [[package]] @@ -1019,36 +1017,38 @@ files = [ [[package]] name = "pytest" -version = "8.3.5" -requires_python = ">=3.8" +version = "8.4.1" +requires_python = ">=3.9" summary = "pytest: simple powerful testing with Python" groups = ["dev"] dependencies = [ - "colorama; sys_platform == \"win32\"", - "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", - "iniconfig", - "packaging", + "colorama>=0.4; sys_platform == \"win32\"", + "exceptiongroup>=1; python_version < \"3.11\"", + "iniconfig>=1", + "packaging>=20", "pluggy<2,>=1.5", + "pygments>=2.7.2", "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [[package]] name = "pytest-cov" -version = "6.1.1" +version = "6.2.1" requires_python = ">=3.9" summary = "Pytest plugin for measuring coverage." groups = ["dev"] dependencies = [ "coverage[toml]>=7.5", - "pytest>=4.6", + "pluggy>=1.2", + "pytest>=6.2.5", ] files = [ - {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"}, - {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"}, + {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, + {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, ] [[package]] @@ -1097,21 +1097,21 @@ files = [ [[package]] name = "pytest-unordered" -version = "0.6.1" +version = "0.7.0" summary = "Test equality of unordered collections in pytest" groups = ["dev"] dependencies = [ "pytest>=7.0.0", ] files = [ - {file = "pytest_unordered-0.6.1-py3-none-any.whl", hash = "sha256:baa809a0ff811d97cfd85f138dbca52e2d7831612b4e19225b3a65ebd9fce068"}, - {file = "pytest_unordered-0.6.1.tar.gz", hash = "sha256:061f7a538247f8adc97a4fcf7415d36e0db4b16548c42d5b49168e6ec2cd95b0"}, + {file = "pytest_unordered-0.7.0-py3-none-any.whl", hash = "sha256:486b26d24a2d3b879a275c3d16d14eda1bd9c32aafddbb17b98ac755daba7584"}, + {file = "pytest_unordered-0.7.0.tar.gz", hash = "sha256:0f953a438db00a9f6f99a0f4727f2d75e72dd93319b3d548a97ec9db4903a44f"}, ] [[package]] name = "pytest-xdist" -version = "3.6.1" -requires_python = ">=3.8" +version = "3.8.0" +requires_python = ">=3.9" summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" groups = ["dev"] dependencies = [ @@ -1119,24 +1119,24 @@ dependencies = [ "pytest>=7.0.0", ] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, ] [[package]] name = "pytest-xdist" -version = "3.6.1" +version = "3.8.0" extras = ["psutil"] -requires_python = ">=3.8" +requires_python = ">=3.9" summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" groups = ["dev"] dependencies = [ "psutil>=3.0", - "pytest-xdist==3.6.1", + "pytest-xdist==3.8.0", ] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, ] [[package]] @@ -1278,29 +1278,29 @@ files = [ [[package]] name = "ruff" -version = "0.11.10" +version = "0.12.4" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] files = [ - {file = "ruff-0.11.10-py3-none-linux_armv6l.whl", hash = "sha256:859a7bfa7bc8888abbea31ef8a2b411714e6a80f0d173c2a82f9041ed6b50f58"}, - {file = "ruff-0.11.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:968220a57e09ea5e4fd48ed1c646419961a0570727c7e069842edd018ee8afed"}, - {file = "ruff-0.11.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1067245bad978e7aa7b22f67113ecc6eb241dca0d9b696144256c3a879663bca"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4854fd09c7aed5b1590e996a81aeff0c9ff51378b084eb5a0b9cd9518e6cff2"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b4564e9f99168c0f9195a0fd5fa5928004b33b377137f978055e40008a082c5"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b6a9cc5b62c03cc1fea0044ed8576379dbaf751d5503d718c973d5418483641"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:607ecbb6f03e44c9e0a93aedacb17b4eb4f3563d00e8b474298a201622677947"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3a522fa389402cd2137df9ddefe848f727250535c70dafa840badffb56b7a4"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f071b0deed7e9245d5820dac235cbdd4ef99d7b12ff04c330a241ad3534319f"}, - {file = "ruff-0.11.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a60e3a0a617eafba1f2e4186d827759d65348fa53708ca547e384db28406a0b"}, - {file = "ruff-0.11.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:da8ec977eaa4b7bf75470fb575bea2cb41a0e07c7ea9d5a0a97d13dbca697bf2"}, - {file = "ruff-0.11.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ddf8967e08227d1bd95cc0851ef80d2ad9c7c0c5aab1eba31db49cf0a7b99523"}, - {file = "ruff-0.11.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5a94acf798a82db188f6f36575d80609072b032105d114b0f98661e1679c9125"}, - {file = "ruff-0.11.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3afead355f1d16d95630df28d4ba17fb2cb9c8dfac8d21ced14984121f639bad"}, - {file = "ruff-0.11.10-py3-none-win32.whl", hash = "sha256:dc061a98d32a97211af7e7f3fa1d4ca2fcf919fb96c28f39551f35fc55bdbc19"}, - {file = "ruff-0.11.10-py3-none-win_amd64.whl", hash = "sha256:5cc725fbb4d25b0f185cb42df07ab6b76c4489b4bfb740a175f3a59c70e8a224"}, - {file = "ruff-0.11.10-py3-none-win_arm64.whl", hash = "sha256:ef69637b35fb8b210743926778d0e45e1bffa850a7c61e428c6b971549b5f5d1"}, - {file = "ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6"}, + {file = "ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a"}, + {file = "ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442"}, + {file = "ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045"}, + {file = "ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57"}, + {file = "ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184"}, + {file = "ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb"}, + {file = "ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1"}, + {file = "ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b"}, + {file = "ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93"}, + {file = "ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a"}, + {file = "ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e"}, + {file = "ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873"}, ] [[package]] @@ -1323,13 +1323,13 @@ files = [ [[package]] name = "setuptools" -version = "80.8.0" +version = "80.9.0" requires_python = ">=3.9" summary = "Easily download, build, install, upgrade, and uninstall Python packages" groups = ["dev"] files = [ - {file = "setuptools-80.8.0-py3-none-any.whl", hash = "sha256:95a60484590d24103af13b686121328cc2736bee85de8936383111e421b9edc0"}, - {file = "setuptools-80.8.0.tar.gz", hash = "sha256:49f7af965996f26d43c8ae34539c8d99c5042fbff34302ea151eaa9c207cd257"}, + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] [[package]] @@ -1620,19 +1620,19 @@ files = [ [[package]] name = "typer" -version = "0.15.4" +version = "0.16.0" requires_python = ">=3.7" summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." groups = ["default"] dependencies = [ - "click<8.2,>=8.0.0", + "click>=8.0.0", "rich>=10.11.0", "shellingham>=1.3.0", "typing-extensions>=3.7.4.3", ] files = [ - {file = "typer-0.15.4-py3-none-any.whl", hash = "sha256:eb0651654dcdea706780c466cf06d8f174405a659ffff8f163cfbfee98c0e173"}, - {file = "typer-0.15.4.tar.gz", hash = "sha256:89507b104f9b6a0730354f27c39fae5b63ccd0c95b1ce1f1a6ba0cfd329997c3"}, + {file = "typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855"}, + {file = "typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b"}, ] [[package]] diff --git a/src/erc7730/common/ledger.py b/src/erc7730/common/ledger.py index b49f8588..49deca69 100644 --- a/src/erc7730/common/ledger.py +++ b/src/erc7730/common/ledger.py @@ -208,3 +208,13 @@ def ledger_network_id(chain_id: int) -> str | None: return "poa" case _: return None + + +# From https://github.com/LedgerHQ/app-ethereum/blob/d12016b11676d80da1e0eab17703fd61bf0ce3d5/src_features/generic_tx_parser/gtp_tx_info.h#L17-L21 +OPERATION_TYPE_MAX_LENGTH: int = 30 +CREATOR_NAME_MAX_LENGTH: int = 22 +CREATOR_LEGAL_NAME_MAX_LENGTH: int = 30 +CREATOR_URL_MAX_LENGTH: int = 26 +CONTRACT_NAME_MAX_LENGTH: int = 30 +FIELD_NAME_MAX_LENGTH: int = 20 +ENUM_MAX_LENGTH: int = 20 diff --git a/src/erc7730/convert/calldata/convert_erc7730_input_to_calldata.py b/src/erc7730/convert/calldata/convert_erc7730_input_to_calldata.py index 74b28171..51c8501c 100644 --- a/src/erc7730/convert/calldata/convert_erc7730_input_to_calldata.py +++ b/src/erc7730/convert/calldata/convert_erc7730_input_to_calldata.py @@ -35,7 +35,7 @@ def erc7730_descriptor_to_calldata_descriptors( return convert_descriptor(input_descriptor=input_descriptor, source=source, chain_id=chain_id, out=out) - except Exception: - out.warning(f"Error processing ERC-7730 file {source}, skipping it") + except Exception as e: + out.warning(f"Error processing ERC-7730 file {source}, skipping it. Error: {e}") return [] diff --git a/src/erc7730/convert/resolved/convert_erc7730_input_to_resolved.py b/src/erc7730/convert/resolved/convert_erc7730_input_to_resolved.py index 02b8c8fc..f9f1286f 100644 --- a/src/erc7730/convert/resolved/convert_erc7730_input_to_resolved.py +++ b/src/erc7730/convert/resolved/convert_erc7730_input_to_resolved.py @@ -1,3 +1,4 @@ +import re from typing import assert_never, final, override from eip712.model.schema import EIP712Type @@ -187,6 +188,16 @@ def _resolve_abis(cls, abis: list[ABI] | HttpUrl, out: OutputAdder) -> list[ABI] match abis: case HttpUrl() as url: try: + # TODO: move to utility function + if match := re.match( + r"^https://api.etherscan.io/api\?module=contract&action=getabi&address=(0x[a-fA-F0-9]{40})$", + url, + ): + # Convert Etherscan v1 to v2 URL + address = match.group(1) + url = HttpUrl( + f"https://api.etherscan.io/v2/api?module=contract&action=getabi&address={address}&chainid=1" + ) return client.get(url=url, model=list[ABI]) except Exception as e: return out.error( diff --git a/src/erc7730/lint/lint.py b/src/erc7730/lint/lint.py index 24b1739c..b72f8e96 100644 --- a/src/erc7730/lint/lint.py +++ b/src/erc7730/lint/lint.py @@ -19,6 +19,7 @@ from erc7730.lint.lint_transaction_type_classifier import ClassifyTransactionTypeLinter from erc7730.lint.lint_validate_abi import ValidateABILinter from erc7730.lint.lint_validate_display_fields import ValidateDisplayFieldsLinter +from erc7730.lint.lint_validate_max_length import ValidateMaxLengthLinter from erc7730.list.list import get_erc7730_files from erc7730.model.input.descriptor import InputERC7730Descriptor @@ -51,11 +52,7 @@ def lint_all(paths: list[Path], out: OutputAdder) -> int: :return: number of files checked """ linter = MultiLinter( - [ - ValidateABILinter(), - ValidateDisplayFieldsLinter(), - ClassifyTransactionTypeLinter(), - ] + [ValidateABILinter(), ValidateDisplayFieldsLinter(), ClassifyTransactionTypeLinter(), ValidateMaxLengthLinter()] ) files = list(get_erc7730_files(*paths, out=out)) diff --git a/src/erc7730/lint/lint_validate_max_length.py b/src/erc7730/lint/lint_validate_max_length.py new file mode 100644 index 00000000..512855b4 --- /dev/null +++ b/src/erc7730/lint/lint_validate_max_length.py @@ -0,0 +1,119 @@ +from typing import final, override + +from erc7730.common.ledger import ( + CONTRACT_NAME_MAX_LENGTH, + CREATOR_LEGAL_NAME_MAX_LENGTH, + CREATOR_NAME_MAX_LENGTH, + CREATOR_URL_MAX_LENGTH, + ENUM_MAX_LENGTH, + FIELD_NAME_MAX_LENGTH, + OPERATION_TYPE_MAX_LENGTH, +) +from erc7730.common.output import OutputAdder +from erc7730.lint import ERC7730Linter +from erc7730.model.resolved.descriptor import ResolvedERC7730Descriptor +from erc7730.model.resolved.display import ResolvedFieldDescription, ResolvedNestedFields + + +@final +class ValidateMaxLengthLinter(ERC7730Linter): + """ + - validate max length of metadata fields + - validate max length of display fields + - validate max length of enums + """ + + @override + def lint(self, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: + self._validate_metadata_lengths(descriptor, out) + self._validate_display_lengths(descriptor, out) + self._validate_enum_lengths(descriptor, out) + + @classmethod + def _validate_metadata_lengths(cls, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: + if descriptor.metadata.owner is not None and len(descriptor.metadata.owner) > CREATOR_NAME_MAX_LENGTH: + out.warning( + title="Owner too long", + message=f"Owner `{descriptor.metadata.owner}` exceeds {CREATOR_NAME_MAX_LENGTH}" + " characters and may be truncated on Ledger devices.", + ) + + if descriptor.metadata.info is not None: + if ( + descriptor.metadata.info.legalName is not None + and len(descriptor.metadata.info.legalName) > CREATOR_LEGAL_NAME_MAX_LENGTH + ): + out.warning( + title="Legal name too long", + message=f"Legal name `{descriptor.metadata.info.legalName}` exceeds " + f"{CREATOR_LEGAL_NAME_MAX_LENGTH} characters and may be truncated on Ledger devices.", + ) + if descriptor.metadata.info.url is not None and len(descriptor.metadata.info.url) > CREATOR_URL_MAX_LENGTH: + out.warning( + title="URL too long", + message=f"URL `{descriptor.metadata.info.url}` exceeds " + 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: + out.warning( + title="Contract id too long", + message=f"Contract id `{descriptor.context.id}` exceeds " + f"{CONTRACT_NAME_MAX_LENGTH} characters and may be truncated on Ledger devices.", + ) + + @classmethod + def _validate_display_lengths(cls, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: + too_long_intents: set[str] = set() + too_long_ids: set[str] = set() + too_long_labels: set[str] = set() + for fmt in descriptor.display.formats.values(): + if fmt.intent is not None and isinstance(fmt.intent, str) and len(fmt.intent) > OPERATION_TYPE_MAX_LENGTH: + too_long_intents.add(fmt.intent) + if fmt.id is not None and len(fmt.id) > OPERATION_TYPE_MAX_LENGTH: + too_long_ids.add(fmt.id) + + for field in fmt.fields: + match field: + case ResolvedFieldDescription(): + if len(field.label) > FIELD_NAME_MAX_LENGTH: + too_long_labels.add(f"{field.label}") + case ResolvedNestedFields(): + for nested_field in field.fields: + if len(nested_field.label) > FIELD_NAME_MAX_LENGTH: + too_long_labels.add(f"{nested_field.label}") + + if too_long_intents: + out.warning( + title="Display intent too long", + message=f"Display intent(s) `{', '.join(too_long_intents)}` exceed " + f"{OPERATION_TYPE_MAX_LENGTH} characters and may be truncated on Ledger devices.", + ) + if too_long_ids: + out.warning( + title="Display id too long", + message=f"Display id(s) `{', '.join(too_long_ids)}` exceed " + f"{OPERATION_TYPE_MAX_LENGTH} characters and may be truncated on Ledger devices.", + ) + if too_long_labels: + out.warning( + title="Display label too long", + message=f"Display label(s) `{', '.join(too_long_labels)}` exceed " + f"{FIELD_NAME_MAX_LENGTH} characters and may be truncated on Ledger devices.", + ) + + @classmethod + def _validate_enum_lengths(cls, descriptor: ResolvedERC7730Descriptor, out: OutputAdder) -> None: + too_long_enums: set[str] = set() + if descriptor.metadata.enums is not None: + for enum in descriptor.metadata.enums.values(): + for enum_entry in enum.values(): + if len(enum_entry) > ENUM_MAX_LENGTH: + too_long_enums.add(enum_entry) + + if too_long_enums: + out.warning( + title="Enum entry too long", + message=f"Enum entry(s) `{', '.join(too_long_enums)}` exceed " + f"{ENUM_MAX_LENGTH} characters and may be truncated on Ledger devices.", + ) diff --git a/src/erc7730/model/calldata/v1/instruction.py b/src/erc7730/model/calldata/v1/instruction.py index 1144b268..a14cbe9e 100644 --- a/src/erc7730/model/calldata/v1/instruction.py +++ b/src/erc7730/model/calldata/v1/instruction.py @@ -83,7 +83,7 @@ class CalldataDescriptorInstructionTransactionInfoV1(CalldataDescriptorInstructi title="Operation type", description="Displayed in review first screens", min_length=1, - max_length=32, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) creator_name: str | None = Field( @@ -91,7 +91,7 @@ class CalldataDescriptorInstructionTransactionInfoV1(CalldataDescriptorInstructi title="Creator name", description="Displayed in review first screens", min_length=1, - max_length=32, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) creator_legal_name: str | None = Field( @@ -99,7 +99,7 @@ class CalldataDescriptorInstructionTransactionInfoV1(CalldataDescriptorInstructi title="Creator legal name", description="Displayed in review first screens", min_length=1, - max_length=32, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) creator_url: HttpUrl | None = Field( @@ -107,7 +107,7 @@ class CalldataDescriptorInstructionTransactionInfoV1(CalldataDescriptorInstructi title="Creator URL", description="Displayed in review first screens", min_length=1, - max_length=256, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) contract_name: str | None = Field( @@ -115,7 +115,7 @@ class CalldataDescriptorInstructionTransactionInfoV1(CalldataDescriptorInstructi title="Contract name", description="Displayed in review first screens", min_length=1, - max_length=32, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) deploy_date: str | None = Field( @@ -181,7 +181,7 @@ class CalldataDescriptorInstructionEnumValueV1(CalldataDescriptorInstructionBase title="Enum entry name", description="Enum display name (ASCII)", min_length=1, - max_length=32, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) @computed_field(title="Descriptor", description="Hex encoded ENUM TLV struct") # type: ignore[misc] @@ -205,7 +205,7 @@ class CalldataDescriptorInstructionFieldV1(CalldataDescriptorInstructionBaseV1): title="Field name", description="Field display name (ASCII)", min_length=1, - max_length=32, # TODO to be refined + # No max_length, may be truncated by Ethereum app ) param: CalldataDescriptorParamV1 = Field( diff --git a/tests/common/test_client.py b/tests/common/test_client.py index f57d9163..9b3d4a1f 100644 --- a/tests/common/test_client.py +++ b/tests/common/test_client.py @@ -25,8 +25,6 @@ def test_get_supported_chains() -> None: assert "Arbitrum Sepolia Testnet" in names assert "Linea Mainnet" in names assert "Linea Sepolia Testnet" in names - assert "Fantom Opera Mainnet" in names - assert "Fantom Testnet" in names assert "Blast Mainnet" in names assert "Blast Sepolia Testnet" in names assert "OP Mainnet" in names @@ -41,8 +39,6 @@ def test_get_supported_chains() -> None: assert "Fraxtal Mainnet" in names assert "Fraxtal Testnet" in names assert "Gnosis" in names - assert "Kroma Mainnet" in names - assert "Kroma Sepolia Testnet" in names assert "Mantle Mainnet" in names assert "Mantle Sepolia Testnet" in names assert "Moonbeam Mainnet" in names diff --git a/tests/registries/clear-signing-erc7730-registry b/tests/registries/clear-signing-erc7730-registry index b55ae364..18624ea5 160000 --- a/tests/registries/clear-signing-erc7730-registry +++ b/tests/registries/clear-signing-erc7730-registry @@ -1 +1 @@ -Subproject commit b55ae364d7a35f93e8b99acd1a798d1ae1af77c1 +Subproject commit 18624ea50e19e32f2fd9c9f63ba4e3e3cc716e07 diff --git a/tests/registries/ledger-asset-dapps b/tests/registries/ledger-asset-dapps index fbcc5a4d..3e113f7a 160000 --- a/tests/registries/ledger-asset-dapps +++ b/tests/registries/ledger-asset-dapps @@ -1 +1 @@ -Subproject commit fbcc5a4d5d9b1440ae6eedfb13ed69120cba331b +Subproject commit 3e113f7abc2eea6efca0026c398100387ad314c4