Repository navigation
fix(calldata): accept boolean enum keys instead of failing the descriptor - #6
Open
venugopalanvip wants to merge 10 commits into
Open
venugopalanvip wants to merge 10 commits into
venugopalanvip wants to merge 10 commits into
Conversation
…ptor
An enum key is the field value it matches, and a bool holds 0 or 1 in calldata,
so `true` and `false` are the keys a descriptor writes for a bool field. JSON
has no boolean object key, so they arrive as text.
`convert_enums` ran `int(ordinal)` on them. That raises, and the exception is
caught only at the top of the conversion, so one bool enum discards every
format and every deployment of the descriptor and returns an empty list.
Two descriptors in the clear-signing registry produce nothing today:
registry/flyingtulip/calldata-PftNft.json {"True": …, "False": …} 0 -> 6
registry/ekubo/calldata-MEVCaptureRouter.json {"false": …, "true": …} 0 -> 168
The casing differs because nothing constrains it: the v2 schema says
"enumeration keys are the field values" and allows any string.
Boolean literals now map to the value the bool holds, case-insensitively.
A key that is neither decimal nor boolean still raises, because silently
mapping it to a number would build an entry the device can never match.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(v2): support map references in field parameters
Map references ({"map": ..., "keyPath": ...}) in v2 field parameters were
dropped with a warning. They are now resolved:
- a constant key ($.…) selects the map value at conversion time
- a data/container key is kept as a ResolvedValueMap lookup in the
resolved model (token, chainId, senderAddress, callee, selector, amount,
spender, collection)
- maps keyed on @.chainId / @.to are checked against every deployment, and
resolved per deployment when generating calldata descriptors
- legacy EIP-712 conversion reports map lookups as unsupported
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(v2): emit device side maps in calldata descriptors
Map lookups keyed on calldata (#.…) or on @.from / @.value were rejected
by the calldata converter. They are now looked up by the device, as
specified in app-ethereum doc/tlv_structs.md#map_entry:
- add a MAP_REF value source (VALUE tag 0x06: version, map id, key as a
nested VALUE with a data or container path)
- emit one MAP_ENTRY instruction per map key (version 0x01, chain id,
contract address, selector, id, key, value) in a new "maps" list next
to "enums", for CAL to sign with the calldata key and serve
- encode keys exactly as the device reads them: 32 bytes ABI word for
static values, raw content for dynamic ones, the slice for sliced
paths, 20 bytes for @.from, 32 bytes for @.value
- allocate map ids per descriptor, sharing them between lookups with the
same key and value encodings
- reject keys / values the device cannot store or match (empty or over
32 bytes, not fitting the key type, duplicate encodings, multi-valued
key paths, key VALUE over 48 bytes)
- keep folding @.chainId / @.to maps into constants, as a descriptor is
bound to a single deployment
- keep the referenced map path in ResolvedValueMap to identify entries
Note MAP_ENTRY version is 0x01 as enforced by map_entry.c, the doc still
states 0x0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(v2): address review findings on map references
- map keys linter: only build the ABI tree for formats with map lookups keyed
on transaction data, so formats using fixed point types still lint
- reject hex constants with an odd number of digits, which could not be
decoded when serializing map values and constants
- reject maps with hex keys only differing by case, since hex keys are matched
case-insensitively
- calldata: warn when a tokenAmount chainId differs from the deployment chain,
as PARAM_TOKEN_AMOUNT cannot encode it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(v2): reject token amounts on another chain, simplify map references
- reject a tokenAmount chainId that differs from the deployment chain
(plain value or map lookup): PARAM_TOKEN_AMOUNT cannot encode it, and
the device would display the token with a wrong ticker and decimals
- reuse the values encoded at resolution for MAP_ENTRY structs
- share is_deployment_key between the converter and the map keys linter
- use a single pattern to reject optional parameters failing to convert
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(v2): reject token tickers and chain id paths on another chain
* test(v2): fix token chain test field typing
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ckorchane-ledger <104824609+ckorchane-ledger@users.noreply.github.com>
Co-authored-by: ldg-github-ci <ldg-github-ci@users.noreply.github.com>
Bumps [jdx/mise-action](https://github.com/jdx/mise-action) from 4.3.0 to 5.0.1. - [Release notes](https://github.com/jdx/mise-action/releases) - [Changelog](https://github.com/jdx/mise-action/blob/main/CHANGELOG.md) - [Commits](jdx/mise-action@v4.3.0...v5.0.1) --- updated-dependencies: - dependency-name: jdx/mise-action dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ckorchane-ledger <104824609+ckorchane-ledger@users.noreply.github.com>
app-ethereum >= 1.22.4 rejects PARAM_UNIT structs without DECIMALS (6a80), although its spec documents the tag as optional with a default of 0. Unit fields without decimals (e.g. opencover "Cover duration") therefore fall back to blind signing on released devices. Always serialize DECIMALS, defaulting to 0, which is byte-for-byte the value the app is specified to assume. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ldg-github-ci <ldg-github-ci@users.noreply.github.com>
…erHQ#347) `erc7730 schema` was the only command with no way to reach the v2 models. `lint`, `resolve`, `calldata` and `convert` all take --v2 or detect it from $schema; `schema` printed the v1 model either way, so the schema that `erc7730 lint --v2` actually enforces could not be obtained from the tool. That is the schema worth having. A descriptor is validated twice by the clear-signing registry: against `specs/erc7730-v2.schema.json`, which is maintained by hand, and against these models, by `erc7730 lint`. When the two disagree the descriptor passes one check and fails the other, which is what issue LedgerHQ#338 is. Printing the model's schema is how that comparison gets made. The flag switches both forms, so `schema resolved --v2` works too. Tests cover all four combinations and assert the flag reaches the v2 model rather than only that a schema was printed: `mustMatch` is a v2 visibility rule and `excluded` a v1 field, so each name appears in exactly one of the two schemas. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the library
clear-signing-erc7730-registryinstalls since ethereum/clear-signing-erc7730-registry#3008, so the descriptors below are ones its CI is carrying today.The bug
An enum key is the field value it matches — the v2 schema is explicit:
A bool holds
0or1in calldata, sotrueandfalseare the keys a descriptor writes for a bool field. JSON has no boolean object key, so they arrive as text.convert_enumsranint(ordinal)on them.int("true")raises, and nothing catches it until the top of the conversion, where the handler discards the entire descriptor — every format, every deployment — and returns an empty list:One bool enum is enough. The descriptor still passes the registry's own CI, because neither reference test runner goes through this converter — the failure only appears when device descriptors are generated.
What it costs on registry master today
registry/lido/calldata-WithdrawalQueueERC721.json{"true": "Allow all", "false": "Revoke all"}registry/ekubo/calldata-MEVCaptureRouter.json{"false": "Token 0", "true": "Token 1"}registry/flyingtulip/calldata-PftNft.json{"True": "Grant all", "False": "Deny all"}I found them by converting all 280 calldata descriptors in the registry and looking for ones that produced nothing.
The Lido entry landed on registry master on 2026-09-21 (#2989); I raised this problem on that PR before it merged and offered to rename the keys to
"0"/"1", and the maintainers took it as written — which I think is right, since the schema says keys are field values andtrue/falseare a bool field's values. The casing differs between the three because nothing constrains it.It is also about to get more common: registry #2912 proposes a bool enum for the DAI permit
allowedflag.The fix
Boolean literals map to the value the bool holds, case-insensitively and ignoring surrounding space:
This matches how the constraint encoder in the same package already treats a bool value:
A key that is neither decimal nor boolean still raises. Silently mapping it to a number would build an enum entry the device can never match, which is worse than the loud failure.
Tests
tests/convert/calldata/v1/test_enum_ordinal.py: decimal keys including a negative, the six boolean spellings plus a padded one, and five keys that must still raise.Also raised upstream as LedgerHQ/python-erc7730#345, where it has not been reviewed. Sending it here too since this is the copy the registry actually runs.
One thing I did not change
convert_enumstakes noOutputAdder, so a bad key can only raise, and the handler that catches it cannot attribute the failure to an enum — the message never names the descriptor, the enum id, or the key. Threadingoutthrough would let a single unusable entry be reported and skipped rather than costing the whole file. Wider than this fix needs; happy to follow up.