Repository navigation
Conversation
…guments A revision 1 enum variant declared as `()` was encoded as hash_array(variant_index, 0) because splitting the empty parameter list produced one empty subtype. SNIP-12 encodes only the chosen variant's parameters, so a variant without arguments hashes to hash_array(variant_index). This matches starknet.py. Fixes starknet-io#1341 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thank you for the detailed analysis and the reference hashes. You are right: the text of SNIP-12 revision 1 does not add a However, we will not change how starknet.js hashes revision 1 messages. Wallets, dapps and deployed contracts were built against the current starknet.js output, so it has become the de facto standard for revision 1. Changing a revision 1 hash would break the signatures they create or verify. Earlier fixes that aligned revision 1 with the text (#1281, #1288) were reverted in #1291. These differences will be fixed in SNIP-12 revision 2, not in revision 1: SNIP-12 revision 2 draft (starknet-io/SNIPs#178). The draft covers this exact case: "A variant with no parameters hashes as For these reasons, I am closing this PR without merging it. |
Motivation and Resolution
Fixes #1341.
In revision 1 typed data, an enum variant declared as
()(for example{ "No Fee": [] }) was encoded ashash_array(variant_index, 0).encodeValuesplits the parameter list with"()".slice(1, -1).split(','), which returns[""]. The empty subtype was then passed through unchanged and hashed as0.SNIP-12 encodes only the parameters of the chosen variant:
A variant with no parameters therefore adds nothing after
variant_index. starknet.py does the same:_extract_enum_types("()")returns[], and the result ishash_many([variant_index]).The fix drops empty subtypes before encoding. Variants with parameters are not affected, and
encodeTypeandgetTypeHashdo not change.Message hashes for account
0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826, using starknet.py 0.30.0TypedData.from_dict(...).message_hash:{ "No Fee": [] }(example from the issue, added as a test)0x25271e84cc1b753388cf16a5e22b8f2c001e5efd024e04f7edb03f1612037540x60456a10c6ce8c808e1d2709efdf717929650ad6ce3f1680a662f271cc162ed0x60456a10c6ce8c808e1d2709efdf717929650ad6ce3f1680a662f271cc162ed__mocks__/typedData/example_enum.json(someEnum2: { "Variant 1": [] })0x6e61abaf480b1370bbf231f54e298c5f4872f40a6d2dd409ff30accee5bbd1e0x69b8e06cf1afab12f3983bd75243a1b4a43d313dd407837292f715a28d570170x69b8e06cf1afab12f3983bd75243a1b4a43d313dd407837292f715a28d57017Notes:
developas a bug fix because the old value did not match the spec or starknet.py. If you preferbetawith a breaking-change marker, I can retarget it.type_hash(enum)in the enum encoding, even though the spec formula does. I left that unchanged because it is a separate question, and changing it would break agreement with starknet.py.example_enumNested.jsondoes not choose a variant without arguments, so its hash does not change. I could not cross-check that file with starknet.py, because starknet.py rejects it with "Dangling types ... [StructB]".RPC version (if applicable)
N/A
Usage related changes
typedData.getMessageHash(and so signing and verifying revision 1 messages) now returns the SNIP-12 and starknet.py value when a chosen enum variant has no arguments.Development related changes
example_enum.jsonmessage hash. The new value matches starknet.py.()variant. It checks that the encoded value isposeidon([variant_index])with no trailing 0, and that the message hash equals the starknet.py reference value.Checklist:
npm run test:unit: 76 suites, 2125 tests passed. Also passed:npm run lint,npm run ts:check, prettier. I did not run the devnet integration suite, because this change only touchessrc/utils/typedData.ts.)I used an AI assistant while preparing this; I reproduced the mismatch against starknet.py and ran the tests myself.
🤖 Generated with Claude Code