Skip to content
Merged
Show file tree
Hide file tree
Changes from 177 commits
Commits
Show all changes
215 commits
Select commit Hold shift + click to select a range
9f23d67
feat(rs-dpp): unify JSON/Value conversion traits — first pass
shumkov Apr 30, 2026
674ab6b
docs(json-value-unification): record pass-1 lessons and progress
shumkov Apr 30, 2026
a314ed9
fix(rs-dpp): drop canonical-trait impls on DataContract (E0034 ambigu…
shumkov Apr 30, 2026
54fa225
test(rs-dpp): add round-trip tests for first batch of unified types
shumkov Apr 30, 2026
b10d957
test(rs-dpp): add round-trip tests for 3 more flat enums (pass 2)
shumkov Apr 30, 2026
4a796b9
test(rs-dpp): add round-trip tests for DocumentPatch + TODO for Token…
shumkov Apr 30, 2026
abcb0af
test(rs-dpp): upgrade test convention — non-default fixture + per-pro…
shumkov Apr 30, 2026
d322863
test(rs-dpp): upgrade IdentityTopUpFromAddresses + IdentityCreditTran…
shumkov Apr 30, 2026
461e89d
test(rs-dpp): upgrade remaining address transitions + log OutPoint bug
shumkov Apr 30, 2026
174fd38
test(rs-dpp): upgrade Identity test to non-default fixture + per-prop…
shumkov Apr 30, 2026
c73ce57
test(rs-dpp): upgrade IdentityPublicKey + TokenContractInfo + Pooling…
shumkov Apr 30, 2026
eca07aa
test(rs-dpp): upgrade flat-enum tests to each-variant pattern
shumkov Apr 30, 2026
c1cdd3a
test(rs-dpp): upgrade GroupStateTransitionInfo + DocumentPatch tests
shumkov Apr 30, 2026
8c4e1c6
test(rs-dpp): upgrade TokenPaymentInfo test to non-default fixture + …
shumkov Apr 30, 2026
6f9a487
test(rs-dpp): upgrade BatchTransition test to non-default fixture + p…
shumkov Apr 30, 2026
b041790
docs(json-value-unification): record pass-2 progress and remaining-wo…
shumkov Apr 30, 2026
1559251
test(rs-dpp): add round-trip tests for 7 state-transition outer enums
shumkov Apr 30, 2026
b1c723a
test(rs-dpp): add round-trip tests for 5 leaf types with Default
shumkov Apr 30, 2026
76820e7
docs(unification): clarify fixture sources — hand-built > random_* > …
shumkov Apr 30, 2026
d368ef1
test(rs-dpp): add explicit fixtures for 5 leaf types (pass 2)
shumkov Apr 30, 2026
77dadbc
test(rs-dpp): add fixtures for AssetLockValue + ChainAssetLockProof
shumkov Apr 30, 2026
054d9bf
test(rs-dpp): add fixture for TokenKeepsHistoryRules
shumkov Apr 30, 2026
fcf9383
test(rs-dpp): add tests for index types (Order, Resolution, Contested…
shumkov Apr 30, 2026
d49b00d
test(rs-dpp): add tests for ChangeControlRules + ContestedDocumentRes…
shumkov Apr 30, 2026
06dd094
test(rs-dpp): add tests for DocumentBaseTransition + DocumentDeleteTr…
shumkov Apr 30, 2026
dc3e323
test(rs-dpp): add fixture-based test for DocumentCreateTransition
shumkov Apr 30, 2026
6ecd2fb
test(rs-dpp): add tests for 4 more document sub-transitions
shumkov Apr 30, 2026
16790c6
test(rs-dpp): add fixtures for TokenBaseTransition + TokenBurn + Toke…
shumkov Apr 30, 2026
74e1b64
test(rs-dpp): add fixtures for TokenFreeze + TokenUnfreeze + TokenDes…
shumkov Apr 30, 2026
88fee7e
test(rs-dpp): add fixtures for 4 more token sub-transitions (Emergenc…
shumkov Apr 30, 2026
4b6b07c
test(rs-dpp): add tests for Group + AddressFundsFeeStrategyStep
shumkov Apr 30, 2026
66a7647
test(rs-dpp): add fixture for TokenMarketplaceRules
shumkov Apr 30, 2026
204d65e
test(rs-dpp): add fixtures for TokenConfigurationChangeItem + TokenDi…
shumkov Apr 30, 2026
77f54e9
test(rs-dpp): add AddressWitness each-variant test
shumkov Apr 30, 2026
fb0028a
docs(unification): finalize pass-2 status — 148 tests, ~70 types covered
shumkov Apr 30, 2026
619d32f
test(rs-dpp): add Epoch round-trip test (key reconstructed from index…
shumkov Apr 30, 2026
af79e14
test(rs-dpp): add TokenConfigurationLocalization fixture + Epoch test…
shumkov Apr 30, 2026
bcb97e7
test(rs-dpp): add TokenConfigurationConvention fixture
shumkov Apr 30, 2026
d09384b
test(rs-dpp): add ResourceVote + ContenderWithSerializedDocument tests
shumkov Apr 30, 2026
784e7a4
test(rs-dpp): add DataContractConfig round-trip test
shumkov Apr 30, 2026
53a642b
test(rs-dpp): add StorageKeyRequirements + ArrayItemType each-variant…
shumkov Apr 30, 2026
683ff1d
docs(unification): record final pass-2 progress (164 tests, ~80 types)
shumkov Apr 30, 2026
dc32f71
test(rs-dpp): add DataContractInSerializationFormat fixture
shumkov Apr 30, 2026
90af517
test(rs-dpp): TODO DataContract*Transition fixtures (V0 inner is modu…
shumkov Apr 30, 2026
a278a3e
test(rs-dpp): add ShieldTransition test (value_round_trip ignored - s…
shumkov Apr 30, 2026
ca12fe8
test(rs-dpp): add UnshieldTransition + ShieldedTransferTransition tests
shumkov Apr 30, 2026
7644aa5
test(rs-dpp): add ShieldedWithdrawal + ShieldFromAssetLock tests
shumkov Apr 30, 2026
3a64d61
test(rs-dpp): add TokenConfiguration test (default_most_restrictive f…
shumkov Apr 30, 2026
70a97e9
test(rs-dpp): add StateTransitionProofResult test (variant matching, …
shumkov Apr 30, 2026
b2c2938
feat(rs-dpp): derive PartialEq for StateTransitionProofResult + Store…
shumkov Apr 30, 2026
9eea124
docs(unification): finalize pass-2 status (181 tests, 12 ignored, ~95…
shumkov Apr 30, 2026
ec51727
test(rs-dpp): add DataContractCreateTransition + DataContractUpdateTr…
shumkov Apr 30, 2026
669e954
test(rs-dpp): un-ignore IdentityCreateTransition tests using instant_…
shumkov Apr 30, 2026
e45019b
test(rs-dpp): add TokenConfigUpdateTransition test (NoChange variant …
shumkov Apr 30, 2026
4e55bf9
test(rs-dpp): add TokenDistributionRules fixture (191 tests)
shumkov Apr 30, 2026
eee7d4b
test(rs-dpp): add TokenPreProgrammedDistribution fixture (193 tests)
shumkov Apr 30, 2026
f857032
test(rs-dpp): add Validator + TokenPerpetualDistribution fixtures (19…
shumkov Apr 30, 2026
fa554de
docs(unification): finalize pass-2 status (197 tests, ~95% coverage)
shumkov Apr 30, 2026
f9a7ec5
test(rs-dpp): add ValidatorSet test + log BLS borrowed-string deseria…
shumkov May 1, 2026
95554c8
fix(rs-dpp): resolve ExtendedDocument Critical-3 non-round-trippable …
shumkov May 1, 2026
0273e3e
fix(platform-value): Bytes32 dual-visitor for serde ContentDeserializer
shumkov May 1, 2026
e9efa82
fix(rs-dpp): serde_bytes / serde_bytes_var dual-shape visitor for tag…
shumkov May 1, 2026
09c0a2b
fix(rs-dpp): local outpoint_serde wrapper for OutPoint inside tagged …
shumkov May 1, 2026
c21a3c0
fix(rs-dpp): local bls_pubkey_serde wrapper unblocks ValidatorSet JSO…
shumkov May 4, 2026
ec43a2a
fix(platform-value): typed map keys — drop string-only MapKeySerializer
shumkov May 4, 2026
7397c73
test(rs-dpp): JSON round-trip equality up-to-int-variant for DataCont…
shumkov May 4, 2026
345db30
docs(unification): refresh plan + inventory for pass-2 completion
shumkov May 4, 2026
f3d6a2b
test(rs-dpp): add per-property assertions to AddressFundsFeeStrategyS…
shumkov May 4, 2026
a777e71
test(rs-dpp): apply per-property assertion convention across 49 round…
shumkov May 4, 2026
98fc37d
test(rs-dpp): drop tautological compare-to-original from per-property…
shumkov May 4, 2026
0f54c64
test(rs-dpp): demonstrate full-wire-shape assertion pattern (2 examples)
shumkov May 4, 2026
538dc34
test(rs-dpp): comment-flag JSON sized-int loss in wire-shape assertions
shumkov May 4, 2026
8b198eb
test(rs-dpp): full wire-shape assertions across 49 round-trip tests
shumkov May 4, 2026
1cc8452
test(rs-dpp): full wire-shape assertions across 35 more round-trip tests
shumkov May 4, 2026
28c0022
fix(rs-dpp): apply $formatVersion convention to AssetLockValue + Toke…
shumkov May 4, 2026
77956d1
fix(rs-dpp): apply $formatVersion convention to Validator + ValidatorSet
shumkov May 4, 2026
072c841
test(rs-dpp): correct misleading dashcore-PR-708 reference in Validat…
shumkov May 5, 2026
cf6cb60
docs(rs-dpp): point Validator/ValidatorSet ignores at the new dashcor…
shumkov May 5, 2026
4fcb3d4
fix(rs-dpp): tag StateTransition umbrella with `tag = "type"` (was un…
shumkov May 5, 2026
7682b34
test(rs-dpp): per-variant umbrella tests for StateTransition tag disp…
shumkov May 5, 2026
fe92868
fix(rs-dpp): flatten transition wire shape via tagged enum convention
shumkov May 6, 2026
d14ce1a
fix(rs-dpp): apply json_safe_fields to DocumentCreateTransitionV0 + base
shumkov May 6, 2026
017c308
fix(rs-dpp): apply json_safe_fields to remaining transition V0 structs
shumkov May 6, 2026
cd56289
fix(rs-dpp): json_safe_fields for TokenTransferTransitionV0 via encry…
shumkov May 6, 2026
38d1388
fix(rs-dpp): \$-prefix system discriminators on transition umbrellas
shumkov May 6, 2026
f11fdb5
fix(rs-dpp): flatten Vote / VotePoll, leave GroupActionEvent adjacent
shumkov May 6, 2026
c36f93b
fix(rs-dpp): flatten GroupActionEvent with `kind` discriminator
shumkov May 6, 2026
2674d95
fix(rs-dpp): flatten ResourceVoteChoice + ContestedDocumentVotePollWi…
shumkov May 6, 2026
71d2e75
fix(rs-dpp): flatten TokenEvent via custom serde impl
shumkov May 6, 2026
91b16e4
docs(rs-dpp): refresh stale adjacent-tagging comment in resource_vote…
shumkov May 6, 2026
6e5f9d9
docs(rs-dpp): refresh unification plan with tag-shape convention sweep
shumkov May 6, 2026
4a40740
refactor(wasm-dpp2): migrate Group A _serde! → _inner! (15 wrappers)
shumkov May 6, 2026
4e9d1ee
fix(wasm-dpp2): stringify non-Text map keys at platform_value→JS boun…
shumkov May 7, 2026
6b81fc9
test(wasm-dpp2): update fixtures for groups 1-4 wire-shape changes
shumkov May 7, 2026
1958421
test(wasm-dpp2): update fixtures for groups 5-10 wire-shape changes
shumkov May 7, 2026
b39b59c
docs(wasm-dpp2): sync TypeScript types with new wire shapes
shumkov May 7, 2026
40d0c6e
docs: scope-correct Phase E in json-value unification plan
shumkov May 7, 2026
356be93
refactor: delete wasm-dpp2 bytes_b64, use canonical dpp helpers
shumkov May 7, 2026
be93bae
refactor(wasm-dpp2): migrate 3 wrappers to _inner!, simplify PoolingWasm
shumkov May 7, 2026
5f479b0
refactor(wasm-dpp2): use canonical dpp traits in Identity / PartialId…
shumkov May 7, 2026
27b32f7
refactor(wasm-dpp2): route DataContractConfig + Localization through …
shumkov May 7, 2026
a4a2ff2
docs: capture wasm-dpp2 adapter-layer audit in unification plan
shumkov May 7, 2026
5caa138
ci(rs-dpp): forbid new inherent JSON/Value conversion methods + docs …
shumkov May 7, 2026
8aa90ff
docs: mark Phase F complete in json-value unification plan
shumkov May 7, 2026
30b43dc
refactor(rs-dpp): delete pure-delegation to_object/to_cleaned_object …
shumkov May 7, 2026
bde42eb
chore(rs-dpp): remove dead-code from identity_public_key + public_key…
shumkov May 7, 2026
a1533e8
docs: mark Phase D steps 2 and 3 complete in unification plan
shumkov May 7, 2026
7bed945
refactor(rs-dpp): strip disabledAt:null via serde attribute
shumkov May 7, 2026
1e4b967
docs: mark Phase D step 4 (skip_serializing_if for disabled_at) complete
shumkov May 7, 2026
18034d6
refactor(rs-dpp): delete now-redundant to_cleaned_object trait surface
shumkov May 7, 2026
76485e0
docs: mark Phase D step 5 (partial) complete in unification plan
shumkov May 7, 2026
146959c
refactor(rs-dpp): drop redundant to_object/into_object from legacy IP…
shumkov May 7, 2026
3d087d8
refactor(rs-dpp): delete IdentityPublicKeyPlatformValueConversionMeth…
shumkov May 7, 2026
8b3cb08
refactor(rs-dpp): drop dead platform_version arg from IPK from_json_o…
shumkov May 7, 2026
32a33f3
refactor(rs-dpp,wasm-dpp2): switch IPK JSON to canonical, delete lega…
shumkov May 7, 2026
7d44f44
refactor(rs-dpp): delete asymmetric AssetLockProof to_raw_object + Tr…
shumkov May 7, 2026
d7e61dc
refactor(rs-dpp): replace AssetLockProof TryFrom hack with canonical …
shumkov May 7, 2026
8690254
docs: mark Phase D steps 5+6 fully complete; defer step 8 with audit …
shumkov May 7, 2026
678121a
refactor(rs-dpp): trim Document family legacy traits to genuinely-dif…
shumkov May 8, 2026
e18e08c
docs: mark Phase D step 8 slice A done; record audit course-correction
shumkov May 8, 2026
92f2fe6
refactor(rs-dpp): delete Document legacy ingest, complete step 8 slice B
shumkov May 8, 2026
8e94f38
refactor(rs-dpp): delete StateTransitionValueConvert/JsonConvert traits
shumkov May 8, 2026
563cbf0
docs: update plan for Phase D step 9; delete dead state_transition_he…
shumkov May 8, 2026
ad584ff
refactor(rs-dpp): roll out json_safe_fields to BatchTransition family
shumkov May 8, 2026
d96fdfe
refactor(rs-dpp): replace manual serde on AddressWitness, ContestedIn…
shumkov May 8, 2026
b82be69
fix(rs-dpp): use camelCase not snake_case for ContestedIndexFieldMatch
shumkov May 8, 2026
94c8b2b
refactor(rs-dpp): rename DataContract _versioned methods + pin Critic…
shumkov May 8, 2026
e9fad56
refactor(rs-dpp): no-validation by default in DataContract Deserialize
shumkov May 9, 2026
b40f175
refactor(rs-dpp): split DataContract API by validation, drop _versioned
shumkov May 9, 2026
0fb8875
test(rs-dpp): restore V1 withdrawal output_script: None round-trip co…
shumkov May 11, 2026
dcddc8c
test(rs-dpp): unknown $formatVersion error coverage on canonical tag …
shumkov May 11, 2026
200e770
docs: mark json_preserves_format_version_tag follow-up as verified
shumkov May 11, 2026
7ece217
docs(rs-dpp): warn about HR/non-HR divergence on JsonConvertible / Va…
shumkov May 11, 2026
141a05c
fix(platform-value): remove silent array→bytes coercion (Critical-2)
shumkov May 11, 2026
24a31f5
docs: mark all Phase D + Critical findings complete in plan header
shumkov May 11, 2026
31da960
docs: confirm Pass 4 status — 17 _serde! sites confirmed infeasible
shumkov May 11, 2026
0ded869
Merge remote-tracking branch 'origin/v3.1-dev' into feat/json-convert…
shumkov May 11, 2026
09adb35
refactor(rs-dpp): drop outpoint_serde wrapper after dashcore #708 lands
shumkov May 11, 2026
b6110d6
chore: apply cargo fmt to fix CI
shumkov May 12, 2026
358fe02
fix(wasm-dpp2,wasm-sdk): green CI for PR #3573
shumkov May 22, 2026
ba8a6d8
fix(dpp): silence clippy::type_complexity on opt-encrypted-note deser…
shumkov May 22, 2026
4ae0b95
fix(wasm-dpp2): silence pre-existing unused-import/variable warnings
shumkov May 22, 2026
123c00c
refactor(dpp,wasm-dpp): drop the last inherent conversion passthrough…
shumkov May 22, 2026
5fd3b25
fix(dpp): apply cargo fmt after deleting from_object
shumkov May 22, 2026
a77e407
fix(rs-sdk-ffi): drop now-unused DocumentPlatformValueMethodsV0 import
shumkov May 22, 2026
da5cdab
chore: track .claude/skills/simulator-control (matches v3.1-dev)
shumkov May 22, 2026
abd7e06
Merge remote-tracking branch 'origin/v3.1-dev' into feat/json-convert…
shumkov May 22, 2026
7672522
fix(drive-abci): migrate keywords-meta-schema test off deleted DataCo…
shumkov May 22, 2026
cf7affb
test(platform-value): align large-u8-array test with Critical-2 removal
shumkov May 22, 2026
5ae8c26
docs(platform-value): correct to_value doctest after Critical-2
shumkov May 22, 2026
f372583
Merge branch 'v3.1-dev' into feat/json-convertible-address-transitions
shumkov Jun 5, 2026
f135b20
fix(wasm-dpp): restore array→bytes coercion at the JS→Value boundary
shumkov Jun 5, 2026
c18c124
test(dash): pin Client.spec to latest protocol version
shumkov Jun 5, 2026
47f99de
fix(rs-scripts): migrate register-contract off deleted DataContract::…
shumkov Jun 5, 2026
af2f382
fix(dpp): apply #[json_safe_fields] to AssetLockValueV0
shumkov Jun 5, 2026
e5efc91
refactor(dpp): verify named-field markers via json_safe_fields macro
shumkov Jun 9, 2026
9221fdb
fix(dpp): JS-safe tuple-variant u64/u128 in 3 conversion enums
shumkov Jun 9, 2026
333eb8e
fix(dpp): JS-safe u64 credits/amounts in StateTransitionProofResult
shumkov Jun 9, 2026
f309e27
Merge branch 'v3.1-dev' into feat/json-convertible-address-transitions
shumkov Jun 9, 2026
a39740e
style(dpp,wasm-dpp): cargo fmt the JSON/Value conversion changes
shumkov Jun 9, 2026
89fb47c
fix(dpp): factor address-info map type to satisfy clippy::type_comple…
shumkov Jun 9, 2026
883779d
Merge remote-tracking branch 'origin/v3.1-dev' into feat/json-convert…
shumkov Jun 10, 2026
c2aa6ff
feat(dpp,wasm-dpp2): full JSON/Value coverage for IdentityCreateFromS…
shumkov Jun 10, 2026
b00a90e
feat(dpp)!: add J+V impls to token-distribution/change-control cluste…
shumkov Jun 10, 2026
c75ab10
fix(dpp): serde(default) for Index count/sum fields + Index family ro…
shumkov Jun 10, 2026
5110635
fix(wasm-dpp2): accept plain-object Map ingestion in proof-result DTO…
shumkov Jun 10, 2026
64f8077
chore(dpp): conversion hygiene — exception docs, dead code removal, s…
shumkov Jun 10, 2026
ab68943
test(dpp): pin ContentDeserializer chain for new custom serde; finish…
shumkov Jun 10, 2026
c9f7f3d
fix(wasm-dpp2): enforce 21-byte guard in PlatformAddress serde visito…
shumkov Jun 10, 2026
3be6488
fix(platform-value)!: error on non-finite floats in Value→JSON conver…
shumkov Jun 10, 2026
7b057b8
refactor(dpp): internally tag StoredAssetLockInfo with $type
shumkov Jun 17, 2026
fff0f5c
test(dpp): pin static wire-shape fixtures for BlockInfo and Validator…
shumkov Jun 17, 2026
ad2d37f
test(dpp): bake ValidatorSet BLS Value fixture from shared hex const
shumkov Jun 17, 2026
51d3193
refactor(dpp)!: internally tag DistributionFunction and RewardDistrib…
shumkov Jun 17, 2026
fa0646d
refactor(dpp)!: internally tag RewardDistributionMoment via Repr
shumkov Jun 17, 2026
e3fa233
refactor(dpp)!: internally tag ContestedIndexFieldMatch via Repr
shumkov Jun 17, 2026
37d4f4c
refactor(dpp)!: internally tag TokenDistributionInfo + TokenDistribut…
shumkov Jun 18, 2026
417114c
refactor(dpp)!: internally tag TokenConfigurationChangeItem (+fix lat…
shumkov Jun 18, 2026
c341be6
refactor(dpp)!: internally tag ArrayItemType (final enum in the sweep)
shumkov Jun 18, 2026
40fa0e9
style(dpp): allow enum_variant_names on RewardDistributionMomentRepr
shumkov Jun 18, 2026
a7f3c60
fix(dpp): make bls_pubkey_serde robust to Content buffer; un-ignore V…
shumkov Jun 18, 2026
319619c
docs: add enum internal-tagging sweep SPEC (with post-review outcome)
shumkov Jun 18, 2026
5892c55
chore: serena language-list refresh + v12 upgrade-boundary risk doc
shumkov Jun 18, 2026
ee80f99
refactor(dpp): move bls_pubkey serde wrapper to serialization/dashcore
shumkov Jun 18, 2026
7f63b89
refactor(dpp)!: use $type for ALL internally-tagged enum discriminators
shumkov Jun 18, 2026
4d75999
docs(dpp): tidy stale `type` discriminator references in comments
shumkov Jun 18, 2026
fd85cd3
test(wasm-dpp2)!: update JS unit fixtures for $type/$kind discriminators
shumkov Jun 18, 2026
2686052
test(wasm-sdk): update JS unit fixtures/assertions for $type discrimi…
shumkov Jun 18, 2026
a592d79
fix(wasm-dpp): restore lenient transition constructors; refresh legac…
shumkov Jun 18, 2026
ed30168
fix(dpp): repair feature-gating so rs-dapi (state-transitions-only) c…
shumkov Jun 18, 2026
a496b0b
Merge remote-tracking branch 'origin/v3.1-dev' into feat/json-convert…
shumkov Jun 18, 2026
955e5ea
docs(dpp): explain why ExtendedBlockInfoV0.signature is hand-annotated
shumkov Jun 18, 2026
6e5d568
fix(dpp)!: restore validate-by-default DataContract deserialize + fro…
shumkov Jun 19, 2026
f574f8e
test(drive): use from_value(_, false, _) for the two bare contract de…
shumkov Jun 19, 2026
24ade51
refactor(dpp): drop from_*_validated aliases for the single from_*(bo…
shumkov Jun 19, 2026
d33f72f
refactor(dpp): hoist the Content-safe u128 serde helper into safe_int…
shumkov Jun 22, 2026
2b76bdb
Merge remote-tracking branch 'origin/v4.1-dev' into feat/json-convert…
shumkov Jul 6, 2026
1f727d2
fix(dpp): feature-gate bls_pubkey serde module behind bls-signatures
shumkov Jul 7, 2026
0a6e666
docs: remove superseded JSON-conversion planning docs
shumkov Jul 7, 2026
a743afa
fix(wasm-dpp): reinstate ExtendedDocument.toObject over canonical to_…
shumkov Jul 7, 2026
969dd96
test(platform-suite): strip contract metadata in DataContract toObjec…
shumkov Jul 7, 2026
1f14d13
fix(platform-value): accept wider-integer arrays in binary-bytes enco…
shumkov Jul 7, 2026
48637e1
fix(platform-wallet-ffi): emit canonical JSON for confirmed documents
shumkov Jul 7, 2026
9f7b90f
refactor(dpp): delete dead legacy document/contract JSON methods
shumkov Jul 7, 2026
603b10c
fix(dpp): normalize binary int-arrays in client sanitize, not the sha…
shumkov Jul 7, 2026
1361867
fix(wasm-dpp2): correct TS discriminator declarations to $type/$kind
shumkov Jul 8, 2026
22a2cf9
fix(dpp): make TokenPricingSchedule JSON JS-safe and $type-tagged
shumkov Jul 8, 2026
b5e9c66
fix(dpp): reject oversized BLS public-key byte sequences early
shumkov Jul 8, 2026
a4cc3c2
test(dpp): cover ArrayItemType Boolean/Date + TokenDistributionInfo P…
shumkov Jul 8, 2026
944393b
fix(rs-sdk-ffi): serialize fetched data contract at the SDK network v…
shumkov Jul 8, 2026
13e7651
refactor(wasm-drive-verify): serialize group actions via canonical serde
shumkov Jul 8, 2026
6fce283
fix(dpp,wasm-dpp2): correct stale `type` -> `$type` wording
shumkov Jul 8, 2026
0d69463
style: apply rustfmt (toolchain 1.92) to PR files
shumkov Jul 8, 2026
1703be8
test(wasm-sdk): update pricingSchedule fixtures to $type shape
shumkov Jul 8, 2026
cf546ee
Merge branch 'v4.1-dev' into feat/json-convertible-address-transitions
shumkov Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
272 changes: 272 additions & 0 deletions docs/json-value-conversion-canonical-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# JSON / Value conversion: canonical pattern

Authoritative reference for adding a new domain type to `rs-dpp` and exposing
it through `wasm-dpp2`. If you're tempted to write a custom `to_json` /
`to_object` inherent method, read this first.

For the long-form rationale and per-type history, see
[json-value-unification-plan.md](json-value-unification-plan.md). This doc
covers **how to do it correctly today**, not why.

## TL;DR

For a new struct or enum:

1. Derive `Serialize`, `Deserialize`, `JsonConvertible`, `ValueConvertible`.
2. If versioned, use `#[serde(tag = "$formatVersion")]` (see [tag conventions](#tag-key-conventions)).
3. Write a round-trip test using the [test template](#test-template) below.
4. In `wasm-dpp2`, expose via `impl_wasm_conversions_inner!` — one line.

If you find yourself writing `pub fn to_json(&self) -> JsonValue` outside a
trait impl, stop and re-read this doc.

## The canonical traits

```rust
// packages/rs-dpp/src/serialization/serialization_traits.rs
pub trait JsonConvertible: Serialize + DeserializeOwned {
fn to_json(&self) -> Result<JsonValue, ProtocolError>;
fn from_json(json: JsonValue) -> Result<Self, ProtocolError>;
}

pub trait ValueConvertible: Serialize + DeserializeOwned {
fn to_object(&self) -> Result<Value, ProtocolError>;
fn into_object(self) -> Result<Value, ProtocolError>;
fn from_object(value: Value) -> Result<Self, ProtocolError>;
}
```

Default implementations route through `serde_json::to_value` /
`platform_value::to_value`. For 95% of types, the derive is sufficient and
no custom impl is needed.

The wasm-dpp2 `impl_wasm_conversions_inner!` macro delegates to these traits,
then handles JS-boundary concerns (`platform_value` ↔ `JsValue`, large-number
stringification, Map-key normalization).

## When to derive vs when to hand-roll

| Type shape | Action |
|---|---|
| Plain struct or enum, all fields serde-derive cleanly | `#[derive(JsonConvertible, ValueConvertible)]` — done. |
| Versioned enum (V0, V1, …) | Derive both, add `#[serde(tag = "$formatVersion")]`. See [tag conventions](#tag-key-conventions). |
| Tagged enum with tuple variants whose inners aren't named structs | Custom `Serialize`/`Deserialize` flattening tuple fields to named JSON keys. See [escape hatches](#escape-hatches). |
| Type whose canonical wire shape needs extra context (`platform_version`, `data_contract`) | Define an inherent context-aware method on a versioned-conversion trait (e.g., `try_from_platform_versioned`). Don't try to fit it into `JsonConvertible`/`ValueConvertible`. |

## Tag-key conventions

When you have `#[serde(tag = "...")]`, pick the discriminator key by this
rule: **`$`-prefix iff the same JSON level carries other `$`-prefixed
fields**. Plain key otherwise.

| Tag key | When | Example types |
|---|---|---|
| `$formatVersion` | Versioned enums (V0/V1/…) — versioned structs always sit next to `$`-fields. | `Identity`, `IdentityPublicKey`, `DataContractConfig`, `Group`, `Validator`, `ValidatorSet`, `AssetLockValue`, `TokenContractInfo`, all 17 leaf transition wrappers, ~40 others. |
| `$baseFormatVersion` | Inner-versioned envelope when the outer also carries `$formatVersion`. | inner-base of certain transition wrappers. |
| `$extendedFormatVersion` | Outer envelope when the inner is already `$formatVersion`-tagged via `serde(flatten)`. | `ExtendedDocument`. |
| `$type` | Discriminator at a level that already has `$`-fields and isn't a version dimension. | `StateTransition`, `Vote`. |
| `$transition` | Inner umbrella inside `BatchedTransition` (where `$type` would collide with `document_type_name`). | `BatchedTransition`. |
| `$action` | Inner action discriminator inside `DocumentTransition` / `TokenTransition` umbrellas (same collision). | `DocumentTransition`, `TokenTransition`. |
| `kind` | Plain key chosen instead of `$type` because the inner content has its own `type` discriminator that would collide. | `GroupActionEvent` (carries inner `TokenEvent` whose internal tag is `type`). |
| `type` | Plain `type` key when the level has no `$`-prefixed neighbors and no inner-tag collision. | `VotePoll`, `ResourceVoteChoice`, `ContestedDocumentVotePollWinnerInfo`. |

If you're unsure, follow the rule, run round-trip tests, and document any
collision-avoidance reasoning inline.

## Test template

Every J or V impl gets a unit test using a **non-default fixture** with a
**round-trip + per-property** assertion. Pure round-trip can pass tautologically
when fields silently drop both ways; per-property catches that.

```rust
#[cfg(all(test, feature = "json-conversion", feature = "value-conversion", feature = "serde-conversion"))]
mod json_convertible_tests {
use super::*;
use platform_value::platform_value;
use serde_json::json;

fn fixture() -> MyType {
// Non-default values for every field; identifiers non-zero.
MyType { /* ... */ }
}

#[test]
fn json_round_trip_with_full_wire_shape() {
use crate::serialization::JsonConvertible;
let original = fixture();
let json = original.to_json().expect("to_json");
// Lock the wire shape — catches silent renames / type narrowing /
// tag-key drift on top of the round-trip check below.
assert_eq!(json, json!({
"$formatVersion": "0",
// ... fields with explicit expected values
}));
let recovered = MyType::from_json(json).expect("from_json");
assert_eq!(original, recovered);
}

#[test]
fn value_round_trip_with_full_wire_shape() {
use crate::serialization::ValueConvertible;
let original = fixture();
let value = original.to_object().expect("to_object");
// platform_value preserves typed variants (Value::U16, Value::Identifier);
// assert against the typed shape, not the JSON-erased form.
assert_eq!(value, platform_value!({
"$formatVersion": "0",
// ...
}));
let recovered = MyType::from_object(value).expect("from_object");
assert_eq!(original, recovered);
}
}
```

Tagged enums get an additional **tag-preservation** test asserting that
`V0` doesn't silently round-trip back as `V1`.

## Escape hatches

Use **only** when the derived path can't express the shape. Document why
inline.

### Tuple-variant enums needing internal tagging

Serde's auto-derive can't internal-tag a tuple variant — internal tagging
requires struct variants or newtype-of-named-struct. If the variants are
shape-stable tuples that map cleanly to named JSON keys, write a custom
`Serialize` / `Deserialize` that emits the flat shape.

Reference impls:
- `packages/rs-dpp/src/tokens/token_event.rs` — 11 variants, mapped
positional fields to named keys (`amount`, `recipient`, `publicNote`, …).
- `packages/rs-dpp/src/voting/vote_choices/resource_vote_choice/mod.rs` —
`TowardsIdentity(Identifier)` flattened to `{type, identity}`.
- `packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_winner_info/mod.rs` —
same pattern.

Bincode `Encode` / `Decode` derives are independent of serde and **stay
untouched** — reshaping serde wire is safe for the consensus binary path.

### Field-level shaping

For specific field shapes, prefer existing serde helpers over custom
visitors:

- `#[serde(with = "crate::serialization::serde_bytes")]` — `[u8; N]`,
base64 in HR / bytes in binary.
- `#[serde(with = "crate::serialization::serde_bytes::option")]` —
`Option<[u8; N]>`.
- `#[serde(with = "crate::serialization::serde_bytes_var")]` — `Vec<u8>`,
base64 in HR / bytes in binary.
- `#[serde(with = "crate::serialization::json::safe_integer::json_safe_u64")]` —
large `u64` stringified above `2^53` for JS safety.
- `#[serde(with = "crate::withdrawal::pooling_serde")]` — `Pooling` enum
with lenient string + numeric variant acceptance.
- `#[json_safe_fields]` proc macro — auto-injects the bytes / json_safe_u64
helpers across a whole struct based on field types.

If you find yourself writing `with = "my_module"` for a primitive serde
shape that smells generic (base64 bytes, lenient enum, large u64), check
this list first or extend an existing module.

### Critical findings to be aware of

The unification plan documents 5 critical findings — all are addressed
in current code, but they shape the testing approach:

- **Critical-1**: `is_human_readable` divergence. Serde's
`ContentDeserializer` (used by internally-tagged enums) reports HR=true
even when wrapping a non-HR Content. Bytes deserializers must accept
**both** string and bytes paths. See `dpp::serialization::serde_bytes`'s
`AnyShapeVisitor` for the canonical pattern.
- **Critical-2**: `From<JsonValue> for Value` array→bytes silent
coercion. A JSON array of u8 (length ≥ 10, all ≤ 255) is silently
treated as `Value::Bytes`. Round-trip tests must include arrays of
small numbers explicitly.
- **Critical-3**: `ExtendedDocument` had a non-round-trippable manual
serde (resolved). Outer enum uses
`#[serde(tag = "$extendedFormatVersion")]` so it can coexist with the
inner Document's `$formatVersion`.
- **Critical-4**: `DataContract::Serialize` is impure — depends on
`PlatformVersion::get_current()`. Treat DataContract conversion as
context-aware, route through `from_value(value, full_validation,
&platform_version)`.
- **Critical-5**: `to_canonical_object` sorts keys — signature-load
bearing. Don't change ordering in canonical paths.

## wasm-dpp2 wrapper patterns

When exposing an rs-dpp domain type to JavaScript:

```rust
// Preferred — delegates to canonical traits, handles JS-boundary
// concerns (BigInt, Uint8Array, Map-key stringification, base58/base64).
impl_wasm_conversions_inner!(
MyTypeWasm, // the wasm wrapper struct
MyType, // the inner rs-dpp domain type with the canonical traits
MyType, // the JS class name
MyTypeObjectJs, // typed return for toObject() / fromObject() (optional)
MyTypeJSONJs, // typed return for toJSON() / fromJSON() (optional)
);
```

For wasm-only DTOs (e.g., decompositions of `StateTransitionProofResult`
tuple variants into named-field JS classes — `VerifiedBalanceTransfer`,
`VerifiedMasternodeVote`, etc.) where there is no rs-dpp domain type to
delegate to:

```rust
// Wasm-only DTO — uses serde derives directly, not a JsonConvertible
// inner type.
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MyDtoWasm { /* ... */ }

impl_wasm_conversions_serde!(MyDtoWasm, MyDto);
```

`_serde!` is **not** a fallback awaiting migration — it's the canonical
path for wasm-only structs. Don't try to invent a sibling rs-dpp type just
to convert a `_serde!` site to `_inner!`.

For context-aware wrappers (`Identity` accepting `platform_version`,
`Document` accepting `data_contract`, etc.), write the methods manually
on the wasm struct and call the rs-dpp conversion methods directly. See
`packages/wasm-dpp2/src/identity/model.rs` for a representative example —
`to_object` / `to_json` / `from_json` use the canonical traits, only
`from_object` is manual because of the `platform_version` arg.

## Things to avoid

- **`pub fn to_json(&self) -> JsonValue` inherent methods on rs-dpp
types.** If `JsonConvertible::to_json` works, use it. If it doesn't,
the fix is to make the type derive the trait or hand-roll the trait
impl, not to add a parallel inherent method that diverges.
- **Re-implementing canonical wire shapes in wasm wrappers.** If the
rs-dpp type has `JsonConvertible` / `ValueConvertible`, the wasm
wrapper must delegate through them (via `impl_wasm_conversions_inner!`
or by calling the trait methods directly). Don't go through
`serialization::to_object` / `to_json` (the generic-serde wasm helper)
when the canonical trait is available.
- **Custom Serialize impls on top of structs that already have a derived
`Serialize`.** Two competing impls confuses readers and breaks if
someone later changes the field set.
- **Mocking the bytes encoding twice.** Use `dpp::serialization::serde_bytes`
/ `serde_bytes_var` / `serde_bytes::option`. Don't fork another
`bytes_b64` helper.

## Quick references

- Plan & history: [json-value-unification-plan.md](json-value-unification-plan.md)
- Per-type inventory: [json-value-conversion-inventory.md](json-value-conversion-inventory.md)
- Trait definitions: `packages/rs-dpp/src/serialization/serialization_traits.rs`
- Bytes serde helpers: `packages/rs-dpp/src/serialization/serde_bytes.rs`,
`serde_bytes_var.rs`
- Macros: `packages/wasm-dpp2/src/serialization/conversions.rs`
(`impl_wasm_conversions_inner!` and `impl_wasm_conversions_serde!`)
- Tag-convention precedent: `packages/rs-dpp/src/state_transition/mod.rs`
(StateTransition `$type`),
`packages/rs-dpp/src/voting/votes/mod.rs` (Vote `$type`),
`packages/rs-dpp/src/group/action_event.rs` (GroupActionEvent `kind`).
Loading
Loading