Skip to content

refactor(dpp)!: cleanup and unify JSON/Object conversion#3167

Merged
QuantumExplorer merged 32 commits intov3.1-devfrom
refactor/sdk/conversion-methods
Mar 11, 2026
Merged

refactor(dpp)!: cleanup and unify JSON/Object conversion#3167
QuantumExplorer merged 32 commits intov3.1-devfrom
refactor/sdk/conversion-methods

Conversation

@shumkov
Copy link
Copy Markdown
Collaborator

@shumkov shumkov commented Mar 1, 2026

Issue being fixed or feature implemented

The WASM SDK (wasm-dpp2, wasm-sdk) historically used ad-hoc JavaScript workarounds for JSON serialization — manually converting identifiers, large numbers,
and enum tags at the JS boundary. This led to inconsistent behavior, duplicated logic, and fragile workarounds like numericStringsToNumbers. Additionally,
versioned enums used two inconsistent tagging patterns: $version (20 types) and $formatVersion (17 types), plus 2 types using raw enum wrappers ({ v0: {...} } / { V0: {...} }).

JavaScript's Number type uses IEEE 754 double-precision floats, which can only safely represent integers up to 2^53 - 1 (Number.MAX_SAFE_INTEGER). Many DPP types use u64 for credits, timestamps, block heights, and token amounts — values that routinely exceed this limit. When serialized to JSON without protection, these values silently lose precision in JavaScript clients.

What was done?

rs-dpp: Pure serde JSON/Value conversion traits

  • Added JsonConvertible trait (serialization_traits.rs) using pure serde_json — no JS workarounds at the Rust layer
  • Removed lifetime parameter from ValueConvertible (ValueConvertible<'a>ValueConvertible) using DeserializeOwned
  • Renamed feature flag platform-value-jsonjson-conversion
  • Added JsonConvertible + ValueConvertible impls to ~40 versioned enums across blocks, tokens, voting, identity, state transitions, and data contract
    types

rs-dpp-json-convertible-derive: Compile-safe large integer handling via proc macros

New proc macro crate providing compile-time safety for JS-safe JSON serialization of u64/i64 fields:

  • #[json_safe_fields] attribute macro for V0 structs: auto-adds #[serde(with = "json_safe_u64")] to u64/i64 fields, implements JsonSafeFields marker trait, generates compile-time assertions that all other field types also implement JsonSafeFields
  • #[derive(JsonConvertible)] derive macro for versioned enums: implements JsonConvertible + JsonSafeFields, asserts all inner V0 types implement JsonSafeFields
  • #[derive(ValueConvertible)] derive macro: implements ValueConvertible (no safety checks needed — platform_value handles u64 natively)
  • JsonSafeFields marker trait (safe_fields.rs): recursive proof chain — primitives implement it, u64/i64 intentionally do NOT, collections delegate to inner types. This catches unprotected type aliases (type Foo = u64) and containers (Vec<u64>) at compile time
  • Serde with modules (safe_integer.rs, safe_integer_map.rs): use serializer.is_human_readable() to stringify values > MAX_SAFE_INTEGER in JSON while keeping native integers in platform_value/bincode
  • Feature-gated behind json-conversion with cfg_attr
  • Applied to ~33 types across rs-dpp
  • Flat enums with u64-alias tuple variants (e.g., TokenEvent) use manual impls with documented reasoning

rs-platform-value: Identifier and enum deserialization fixes

  • Fixed deserialize_enum in de.rs to handle Text→unit and Map→externally-tagged variants
  • Added human-readable serde support for Identifier (base58 in JSON, raw bytes in binary)
  • Added comprehensive round-trip tests for Value serialization

wasm-dpp2: Two-macro conversion architecture

  • impl_wasm_conversions_inner! — delegates to rs-dpp JsonConvertible/ValueConvertible traits with JS-boundary large number handling
    (stringify_large_numbers/unstringify_large_numbers)
  • impl_wasm_conversions_serde! — direct serde path via #[serde(transparent)] for simple wrapper types
  • Migrated ~40 types from manual/ad-hoc conversions to the macro-based approach

Standardize $formatVersion tag across all versioned enums

  • Renamed serde(tag = "$version")serde(tag = "$formatVersion") in 20 rs-dpp files (Identity, IdentityPublicKey, Document, ResourceVote, all state
    transitions)
  • Added serde(tag = "$formatVersion") to ContenderWithSerializedDocument (was rename_all = "camelCase" producing { v0: {...} }) and GroupAction (was
    untagged producing { V0: {...} })
  • Updated 6 wasm-dpp2 TypeScript interfaces and ~15 test files to match

Test coverage

  • Added 8 new wasm-dpp2 test files covering 10 previously untested entities (ActionTaker, ContenderWithSerializedDocument,
    GroupAction/GroupActionEvent/TokenEvent, GroupStateTransitionInfoStatus, MasternodeVoteTransition, TokenContractInfo, TokenPaymentInfo, VerifiedDataContract)
  • Added wasm-sdk conversion test files for simple types, derivation, partial identity, status, and votes
  • Added rs-dpp json_convertible_tests module with round-trip tests for all migrated types
  • Added comprehensive tests for serde with modules: u64/i64/Option variants, tagged enum round-trips, platform_value native handling, map modules
  • Removed unnecessary numericStringsToNumbers JS workaround from BatchTransition, DataContractCreateStateTransition, and DataContractUpdateStateTransition
    tests

How Has This Been Tested?

  • cargo check -p dpp --features json-conversion,state-transition-serde-conversion,vote-serde-conversion — compiles cleanly
  • cargo test -p dpp --lib --features json-conversion,state-transition-serde-conversion,vote-serde-conversion — 539 tests passing
  • packages/wasm-dpp2: yarn test — 917 passing, 0 failing
  • packages/wasm-sdk: yarn test — 385 passing, 0 failing
  • Compile-time safety verified: adding u64 field without #[json_safe_fields] → compile error; unknown type aliases → compile error

Breaking Changes

  • $version$formatVersion: All versioned enums now use $formatVersion as the serde tag instead of $version. Any code that parses JSON output
    from these types and looks for $version must be updated to $formatVersion. Affected types: Identity, IdentityPublicKey, Document, ResourceVote, all state
    transitions (BatchTransition, DataContractCreateTransition, DataContractUpdateTransition, IdentityCreateTransition, IdentityUpdateTransition,
    IdentityCreditWithdrawalTransition, IdentityTopUpTransition, IdentityCreditTransferTransition, MasternodeVoteTransition, PublicKeyInCreation, and
    address-based transitions).
  • ContenderWithSerializedDocument JSON shape changed: from { "v0": { "identityId": "...", ... } } to { "$formatVersion": "0", "identityId": "...", ... }
  • GroupAction JSON shape changed: from { "V0": { "contract_id": "...", ... } } to { "$formatVersion": "0", "contract_id": "...", ... }
  • ContractBounds::SingleContractDocumentType field rename: document_type_name now serializes as documentTypeName (camelCase). Any code parsing identity public key contract bounds JSON must update the field name.
  • BlockInfo field names changed to camelCase: time_mstimeMs, height (unchanged), core_heightcoreHeight, epoch (unchanged). Any code parsing BlockInfo JSON must update field names.
  • Semantic enums now use adjacently-tagged serde: Vote, VotePoll, ResourceVoteChoice, TokenEvent, GroupActionEvent, and ContestedDocumentVotePollWinnerInfo changed from externally-tagged ({ "TowardsIdentity": "..." }) to adjacently-tagged ({ "type": "towardsIdentity", "data": "..." }). Any code parsing these enum types from JSON must update to the new shape.
  • ChangeControlRules JSON shape changed: from externally-tagged with snake_case ({ "V0": { "authorized_to_make_change": ... } }) to internally-tagged with camelCase ({ "$formatVersion": "0", "authorizedToMakeChange": ... }).
  • GroupV0 field rename: required_power now serializes as requiredPower (camelCase).
  • Large u64/i64 values now stringified in JSON: Values exceeding Number.MAX_SAFE_INTEGER (2^53 - 1) are now serialized as strings in JSON output. Deserialization accepts both numbers and strings. This affects all types with u64/i64 fields (credits, timestamps, token amounts, block heights, etc.).
  • Feature flag renamed: platform-value-json now implies json-conversion

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

@shumkov shumkov requested a review from QuantumExplorer as a code owner March 1, 2026 14:37
@github-actions github-actions Bot added this to the v3.1.0 milestone Mar 1, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 1, 2026

Important

Review skipped

Too many files!

This PR contains 298 files, which is 148 over the limit of 150.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f0bc34a1-44d3-44d9-9d62-612d67394547

📥 Commits

Reviewing files that changed from the base of the PR and between 6da48c0 and efe591b.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • packages/rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract-cbor.bin is excluded by !**/*.bin
📒 Files selected for processing (298)
  • .github/grpc-queries-cache.json
  • .github/package-filters/js-packages.yml
  • .github/package-filters/rs-packages-direct.yml
  • .github/package-filters/rs-packages-no-workflows.yml
  • .github/package-filters/rs-packages.yml
  • .github/package-filters/test-suite-triggers.yml
  • .github/workflows/tests-rs-sdk-grpc-coverage.yml
  • Cargo.toml
  • Dockerfile
  • packages/rs-context-provider/Cargo.toml
  • packages/rs-dpp-json-convertible-derive/Cargo.toml
  • packages/rs-dpp-json-convertible-derive/README.md
  • packages/rs-dpp-json-convertible-derive/src/lib.rs
  • packages/rs-dpp/Cargo.toml
  • packages/rs-dpp/src/address_funds/fee_strategy/mod.rs
  • packages/rs-dpp/src/address_funds/platform_address.rs
  • packages/rs-dpp/src/address_funds/witness.rs
  • packages/rs-dpp/src/block/block_info/mod.rs
  • packages/rs-dpp/src/block/extended_block_info/mod.rs
  • packages/rs-dpp/src/block/extended_block_info/v0/mod.rs
  • packages/rs-dpp/src/block/extended_epoch_info/mod.rs
  • packages/rs-dpp/src/block/extended_epoch_info/v0/mod.rs
  • packages/rs-dpp/src/block/finalized_epoch_info/mod.rs
  • packages/rs-dpp/src/block/finalized_epoch_info/v0/mod.rs
  • packages/rs-dpp/src/core_types/validator/mod.rs
  • packages/rs-dpp/src/core_types/validator/v0/mod.rs
  • packages/rs-dpp/src/core_types/validator_set/mod.rs
  • packages/rs-dpp/src/core_types/validator_set/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration_convention/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration_convention/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration_localization/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_configuration_localization/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_distribution_rules/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_distribution_rules/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_keeps_history_rules/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_keeps_history_rules/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_marketplace_rules/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_marketplace_rules/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/reward_distribution_type/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/v0/mod.rs
  • packages/rs-dpp/src/data_contract/change_control_rules/mod.rs
  • packages/rs-dpp/src/data_contract/change_control_rules/v0/mod.rs
  • packages/rs-dpp/src/data_contract/config/mod.rs
  • packages/rs-dpp/src/data_contract/config/v0/mod.rs
  • packages/rs-dpp/src/data_contract/config/v1/mod.rs
  • packages/rs-dpp/src/data_contract/conversion/json/mod.rs
  • packages/rs-dpp/src/data_contract/conversion/mod.rs
  • packages/rs-dpp/src/data_contract/created_data_contract/mod.rs
  • packages/rs-dpp/src/data_contract/created_data_contract/v0/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/index/mod.rs
  • packages/rs-dpp/src/data_contract/factory/mod.rs
  • packages/rs-dpp/src/data_contract/factory/v0/mod.rs
  • packages/rs-dpp/src/data_contract/group/mod.rs
  • packages/rs-dpp/src/data_contract/group/v0/mod.rs
  • packages/rs-dpp/src/data_contract/mod.rs
  • packages/rs-dpp/src/data_contract/serialized_version/mod.rs
  • packages/rs-dpp/src/data_contract/serialized_version/v0/mod.rs
  • packages/rs-dpp/src/data_contract/serialized_version/v1/mod.rs
  • packages/rs-dpp/src/data_contract/v0/conversion/mod.rs
  • packages/rs-dpp/src/data_contract/v0/conversion/value.rs
  • packages/rs-dpp/src/data_contract/v1/conversion/mod.rs
  • packages/rs-dpp/src/data_contract/v1/conversion/value.rs
  • packages/rs-dpp/src/document/extended_document/mod.rs
  • packages/rs-dpp/src/document/extended_document/v0/mod.rs
  • packages/rs-dpp/src/document/mod.rs
  • packages/rs-dpp/src/document/serialization_traits/mod.rs
  • packages/rs-dpp/src/document/v0/mod.rs
  • packages/rs-dpp/src/group/action_event.rs
  • packages/rs-dpp/src/group/group_action/mod.rs
  • packages/rs-dpp/src/group/group_action/v0/mod.rs
  • packages/rs-dpp/src/group/group_action_status.rs
  • packages/rs-dpp/src/group/mod.rs
  • packages/rs-dpp/src/identity/conversion/mod.rs
  • packages/rs-dpp/src/identity/conversion/platform_value/mod.rs
  • packages/rs-dpp/src/identity/conversion/platform_value/v0/mod.rs
  • packages/rs-dpp/src/identity/identity.rs
  • packages/rs-dpp/src/identity/identity_public_key/contract_bounds/mod.rs
  • packages/rs-dpp/src/identity/identity_public_key/conversion/mod.rs
  • packages/rs-dpp/src/identity/identity_public_key/conversion/platform_value/mod.rs
  • packages/rs-dpp/src/identity/identity_public_key/mod.rs
  • packages/rs-dpp/src/identity/identity_public_key/v0/conversion/mod.rs
  • packages/rs-dpp/src/identity/identity_public_key/v0/mod.rs
  • packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs
  • packages/rs-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs
  • packages/rs-dpp/src/identity/v0/conversion/mod.rs
  • packages/rs-dpp/src/identity/v0/conversion/platform_value.rs
  • packages/rs-dpp/src/identity/v0/mod.rs
  • packages/rs-dpp/src/serialization/json/mod.rs
  • packages/rs-dpp/src/serialization/json/safe_fields.rs
  • packages/rs-dpp/src/serialization/json/safe_integer.rs
  • packages/rs-dpp/src/serialization/json/safe_integer_map.rs
  • packages/rs-dpp/src/serialization/mod.rs
  • packages/rs-dpp/src/serialization/serialization_traits.rs
  • packages/rs-dpp/src/shielded/mod.rs
  • packages/rs-dpp/src/state_transition/abstract_state_transition.rs
  • packages/rs-dpp/src/state_transition/mod.rs
  • packages/rs-dpp/src/state_transition/proof_result.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_credit_withdrawal_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_credit_withdrawal_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_create_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_create_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_base_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_base_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_base_transition/v1/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/convertible.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_delete_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_delete_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_purchase_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_purchase_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_replace_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_replace_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_transfer_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_transfer_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_transition.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_update_price_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_update_price_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_base_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_base_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_claim_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_claim_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_direct_purchase_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_direct_purchase_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transfer_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transfer_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_from_addresses_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_from_addresses_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_from_addresses_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_to_addresses_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_to_addresses_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_to_addresses_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_from_addresses_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_from_addresses_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_from_addresses_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/value_conversion.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_from_asset_lock_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_from_asset_lock_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shielded_transfer_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shielded_transfer_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shielded_withdrawal_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/shielded_withdrawal_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/unshield_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/shielded/unshield_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/traits/mod.rs
  • packages/rs-dpp/src/tests/json_document.rs
  • packages/rs-dpp/src/tokens/contract_info/mod.rs
  • packages/rs-dpp/src/tokens/contract_info/v0/mod.rs
  • packages/rs-dpp/src/tokens/emergency_action.rs
  • packages/rs-dpp/src/tokens/gas_fees_paid_by.rs
  • packages/rs-dpp/src/tokens/info/mod.rs
  • packages/rs-dpp/src/tokens/info/v0/mod.rs
  • packages/rs-dpp/src/tokens/status/mod.rs
  • packages/rs-dpp/src/tokens/status/v0/mod.rs
  • packages/rs-dpp/src/tokens/token_event.rs
  • packages/rs-dpp/src/tokens/token_payment_info/mod.rs
  • packages/rs-dpp/src/tokens/token_payment_info/v0/mod.rs
  • packages/rs-dpp/src/tokens/token_pricing_schedule.rs
  • packages/rs-dpp/src/voting/contender_structs/contender/mod.rs
  • packages/rs-dpp/src/voting/contender_structs/contender/v0/mod.rs
  • packages/rs-dpp/src/voting/vote_choices/resource_vote_choice/mod.rs
  • packages/rs-dpp/src/voting/vote_choices/yes_no_abstain_vote_choice/mod.rs
  • packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_winner_info/mod.rs
  • packages/rs-dpp/src/voting/vote_polls/contested_document_resource_vote_poll/mod.rs
  • packages/rs-dpp/src/voting/vote_polls/mod.rs
  • packages/rs-dpp/src/voting/votes/mod.rs
  • packages/rs-dpp/src/voting/votes/resource_vote/mod.rs
  • packages/rs-dpp/src/voting/votes/resource_vote/v0/mod.rs
  • packages/rs-drive-abci/Cargo.toml
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs
  • packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json
  • packages/rs-drive-abci/tests/supporting_files/contract/basic-token/basic-token.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-all-transferable-format-version-0.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-all-transferable-no-owner-indexes.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-all-transferable-unique-creator-id-index.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-all-transferable-unique-creator-id-with-owner-id-index.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-all-transferable.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase-creation-restricted-to-owner.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase-documents-mutable.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase-unique-creator-id-index.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase-unique-creator-id-with-owner-id-index.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency-burn-tokens.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-not-transferable.json
  • packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-use-external-currency.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable-bad-update-skipped-position.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable-update-1.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable-update-2.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-contact-request-mutable-and-can-not-be-deleted.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-contact-request-not-mutable-and-can-be-deleted.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-no-indexes.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dashpay/dashpay-contract-no-max-length.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-and-other-unique-index.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-with-contract-id-null-searchable-true.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-with-contract-id.json
  • packages/rs-drive-abci/tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index.json
  • packages/rs-drive-abci/tests/supporting_files/contract/keyword_test/keyword_base_contract.json
  • packages/rs-drive-proof-verifier/Cargo.toml
  • packages/rs-drive/Cargo.toml
  • packages/rs-drive/src/drive/document/update/mod.rs
  • packages/rs-drive/tests/query_tests.rs
  • packages/rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json
  • packages/rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract-with-profile-history.json
  • packages/rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json
  • packages/rs-drive/tests/supporting_files/contract/deepNested/deep-nested10.json
  • packages/rs-drive/tests/supporting_files/contract/deepNested/deep-nested50.json
  • packages/rs-drive/tests/supporting_files/contract/dpns/dpns-contract-label-not-required.json
  • packages/rs-drive/tests/supporting_files/contract/dpns/dpns-contract-update-v2-test.json
  • packages/rs-drive/tests/supporting_files/contract/dpns/dpns-contract.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-fields-optional.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-only-age-index.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-only-first-name-index.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-only-message-index.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-reduced.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-with-birthday.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-with-history-only-message-index.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract-with-history.json
  • packages/rs-drive/tests/supporting_files/contract/family/family-contract.json
  • packages/rs-drive/tests/supporting_files/contract/references/references.json
  • packages/rs-drive/tests/supporting_files/contract/references/references_with_contract_history.json
  • packages/rs-drive/tests/supporting_files/contract/tokens/token-example-contract.json
  • packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals-contract.json
  • packages/rs-platform-value/src/converter/serde_json.rs
  • packages/rs-platform-value/src/types/identifier.rs
  • packages/rs-platform-value/src/value_serialization/de.rs
  • packages/rs-platform-value/src/value_serialization/mod.rs
  • packages/rs-sdk/tests/vectors/check_mn_voting_prerequisites/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resource_identity_votes_ok/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resource_vote_states_not_found/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resource_vote_states_ok/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resource_vote_states_with_limit/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resource_voters_for_existing_contestant/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resources_fields_0760ac4854fc8db803bbcbab8709f390bd31511a05e29cd3f170b48ca6b87584/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resources_fields_473cf8e4a270ced75e199e5a3e907b4df4cd66b64365d1ac77c45bcaed443a03/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resources_fields_948b5a301af5fc73db7fed418a4fe90f64952b4ddd6b03a7f21d029dc110af50/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resources_fields_9f57b982b3e3b0286093d8b48ab27b87b22f67a172579913f2fec7a6b5ea31b7/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resources_fields_b7df460e812b958de6e703d8ea325df9aab3448d0409ece3f0baf1d26629e44f/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json
  • packages/rs-sdk/tests/vectors/contested_resources_fields_ccb199c48ee58a8bb98742b964cba7bda3b4469b740201d2628f15f926f39347/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/sdk/conversion-methods

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 1, 2026

❌ gRPC Query Coverage Report

Total: 53 queries — 50 implemented, 2 ignored, 1 missing

❌ Missing

  • getTokenPreProgrammedDistributions (line 96)
⏭️ Ignored (@sdk-ignore) (2)
  • broadcastStateTransition — Write-only endpoint, not a query
  • getConsensusParams — Consensus params fetched via Tenderdash RPC
✅ Implemented (50)
  • getAddressInfo
  • getAddressesBranchState
  • getAddressesInfos
  • getAddressesTrunkState
  • getContestedResourceIdentityVotes
  • getContestedResourceVoteState
  • getContestedResourceVotersForIdentity
  • getContestedResources
  • getCurrentQuorumsInfo
  • getDataContract
  • getDataContractHistory
  • getDataContracts
  • getDocuments
  • getEpochsInfo
  • getEvonodesProposedEpochBlocksByIds
  • getEvonodesProposedEpochBlocksByRange
  • getFinalizedEpochInfos
  • getGroupActionSigners
  • getGroupActions
  • getGroupInfo
  • getGroupInfos
  • getIdentitiesBalances
  • getIdentitiesContractKeys
  • getIdentitiesTokenBalances
  • getIdentitiesTokenInfos
  • getIdentity
  • getIdentityBalance
  • getIdentityBalanceAndRevision
  • getIdentityByNonUniquePublicKeyHash
  • getIdentityByPublicKeyHash
  • getIdentityContractNonce
  • getIdentityKeys
  • getIdentityNonce
  • getIdentityTokenBalances
  • getIdentityTokenInfos
  • getPathElements
  • getPrefundedSpecializedBalance
  • getProtocolVersionUpgradeState
  • getProtocolVersionUpgradeVoteStatus
  • getRecentAddressBalanceChanges
  • getRecentCompactedAddressBalanceChanges
  • getStatus
  • getTokenContractInfo
  • getTokenDirectPurchasePrices
  • getTokenPerpetualDistributionLastClaim
  • getTokenStatuses
  • getTokenTotalSupply
  • getTotalCreditsInPlatform
  • getVotePollsByEndDate
  • waitForStateTransitionResult

@shumkov shumkov force-pushed the refactor/sdk/conversion-methods branch 2 times, most recently from 1dbe9d3 to 9a72612 Compare March 4, 2026 17:25
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 4, 2026

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "9e6eab6deb1210d9214034ce6e7d2af10938bca164cacf6cb1141eccf4a8c637"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

@QuantumExplorer
Copy link
Copy Markdown
Member

Audit Findings (informational — no code changes needed)

Finding 2 (Medium): unstringify_large_numbers can corrupt user-constructed JSON

File: packages/wasm-dpp2/src/serialization/conversions.rs:64-88

Any string value that parses as u64 > MAX_SAFE_INTEGER gets silently converted to a number. If a user constructs JSON with a string field like "9999999999999999999", fromJSON() will turn it into a number, potentially breaking deserialization if the target Rust type expects String. This is an inherent limitation of the bare-string stringification approach. A more robust approach would use a sentinel wrapper object (e.g., {"$bigNumber": "123..."}) but that would be a much larger change. Worth documenting as a known limitation.


Finding 3 (Medium): json-conversion feature flag is empty — implicit serde_json coupling

File: packages/rs-dpp/Cargo.toml:220

json-conversion = [] enables no dependencies, but the gated code uses serde_json::Value, serde_json::to_value, and serde_json::from_value. This only works because serde_json is unconditionally compiled in rs-dpp. If someone ever makes serde_json optional, this feature silently breaks. Consider changing to json-conversion = ["dep:serde_json"] to make the intent explicit and future-proof.


Finding 4 (Low): ExtendedDocument still references $version internally

Files: packages/rs-dpp/src/document/extended_document/fields.rs, serde_serialize.rs

The ExtendedDocument custom serializer still uses $version as its own wrapper discriminator. This is technically a different field (wrapper version vs inner Document tag), so it is not a bug, but it creates a naming inconsistency with the otherwise-complete $formatVersion migration. Consider harmonizing in a follow-up.


Finding 5 (Low): .unwrap() in production code

File: packages/rs-platform-value/src/value_serialization/de.rs:474

let (key, value) = entries.into_iter().next().unwrap();

Safe due to entries.len() == 1 guard, but per the project coding standard ("NEVER unwrap in production code"), should be .expect("guard guarantees len == 1").


Finding 6 (Low): to_json maps serialization errors to DecodingError

File: packages/rs-dpp/src/serialization/serialization_traits.rs:173

JsonConvertible::to_json (a serialization operation) maps errors to ProtocolError::DecodingError. Semantically misleading — consider using EncodingError or a more general variant for the serialization path.


Finding 10 (Informational): DataContract still goes through platform_value, not pure serde_json

Files: packages/rs-dpp/src/data_contract/v0/conversion/json.rs, v1/conversion/json.rs

The PR description says "pure serde_json — no JS workarounds at the Rust layer" but DataContract::from_json/to_json still detours through platform_value conversion. The new JsonConvertible trait is used on sub-types and state transitions but not on DataContract itself (which has special processing needs). This is correct behavior but worth noting for accuracy.

@QuantumExplorer
Copy link
Copy Markdown
Member

Suggestion: Replace empty JsonConvertible / ValueConvertible impls with derive macros

There are 33 empty impl JsonConvertible and 41 empty impl ValueConvertible blocks across rs-dpp, each with varying #[cfg(...)] gates. Since both traits have default method implementations, these are pure boilerplate. A derive macro would eliminate all of them.

Proposed derive macros

Add two derive macros to rs-platform-serialization-derive (already exists as a proc-macro crate):

#[derive(JsonConvertible)] — generates:

#[cfg(feature = "json-conversion")]
impl JsonConvertible for MyType {}

#[derive(ValueConvertible)] — generates:

impl ValueConvertible for MyType {}

Both macros are trivial — no field inspection needed, just emit an empty impl block gated on the type name.

How the feature gates work

Pattern 1 — Unconditional serde (most types):

Before:

#[derive(Serialize, Deserialize)]
struct BlockInfo { ... }

#[cfg(feature = "json-conversion")]
impl JsonConvertible for BlockInfo {}
impl ValueConvertible for BlockInfo {}

After:

#[derive(Serialize, Deserialize, JsonConvertible, ValueConvertible)]
struct BlockInfo { ... }

Pattern 2 — Conditional serde (~8 types like ResourceVoteChoice, TokenEvent, Vote, VotePoll, etc.):

Before:

#[cfg_attr(
    feature = "vote-serde-conversion",
    derive(Serialize, Deserialize),
    serde(tag = "type", content = "data", rename_all = "camelCase")
)]
pub enum ResourceVoteChoice { ... }

#[cfg(all(feature = "json-conversion", feature = "vote-serde-conversion"))]
impl JsonConvertible for ResourceVoteChoice {}
#[cfg(feature = "vote-serde-conversion")]
impl ValueConvertible for ResourceVoteChoice {}

After:

#[cfg_attr(
    feature = "vote-serde-conversion",
    derive(Serialize, Deserialize, JsonConvertible, ValueConvertible),
    serde(tag = "type", content = "data", rename_all = "camelCase")
)]
pub enum ResourceVoteChoice { ... }

This works because cfg_attr controls whether the derive attribute is applied at all. When vote-serde-conversion is off, the derive macro never runs. When it is on, the macro emits #[cfg(feature = "json-conversion")] impl JsonConvertible for Type {}. The effective gate becomes all(vote-serde-conversion, json-conversion) — matching the current hand-written code exactly.

Implementation steps

  1. Add derive macros to rs-platform-serialization-derive/src/lib.rs:

    • #[proc_macro_derive(JsonConvertible)] — emit #[cfg(feature = "json-conversion")] impl crate::serialization::JsonConvertible for #name {}
    • #[proc_macro_derive(ValueConvertible)] — emit impl crate::serialization::ValueConvertible for #name {}
    • Note: the trait paths need to resolve correctly. Since the traits live in dpp::serialization, the derives should use the full path or rely on the caller having it in scope. The simplest approach is to use dpp::serialization::JsonConvertible as the path, but since the derives will only be used inside rs-dpp, a re-export from the serialization module (e.g., use crate::serialization::JsonConvertible) at each call site is already present.
  2. Re-export the derive macros from rs-dpp (if needed) so they can be used with #[derive(JsonConvertible)] inside rs-dpp.

  3. Replace all 33 impl JsonConvertible blocks with the derive attribute on the struct/enum.

  4. Replace all 41 impl ValueConvertible blocks with the derive attribute on the struct/enum.

  5. Remove the now-unnecessary use crate::serialization::JsonConvertible; imports from files that only imported the trait for the empty impl (keep imports where the trait methods are actually called, e.g., in tests).

Scope

  • 2 new derive macros (~20 lines each)
  • ~74 empty impl blocks removed across ~45 files
  • ~45 #[derive(...)] attributes updated
  • Zero behavioral change

@shumkov shumkov force-pushed the refactor/sdk/conversion-methods branch 3 times, most recently from 5f607ea to 9d99ef6 Compare March 8, 2026 08:29
QuantumExplorer
QuantumExplorer previously approved these changes Mar 9, 2026
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, very nice.

@shumkov shumkov self-assigned this Mar 9, 2026
@shumkov shumkov moved this to In review / testing in Platform team Mar 9, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 94.07216% with 69 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.39%. Comparing base (06cef2c) to head (efe591b).
⚠️ Report is 4 commits behind head on v3.1-dev.

Files with missing lines Patch % Lines
.../rs-dpp/src/serialization/json/safe_integer_map.rs 89.15% 36 Missing ⚠️
...ages/rs-dpp/src/serialization/json/safe_integer.rs 93.42% 19 Missing ⚠️
...n/asset_lock_proof/chain/chain_asset_lock_proof.rs 90.00% 3 Missing ⚠️
...kages/rs-dpp/src/block/finalized_epoch_info/mod.rs 96.72% 2 Missing ⚠️
...ntract/associated_token/token_configuration/mod.rs 84.61% 2 Missing ⚠️
...dentity/identity_public_key/contract_bounds/mod.rs 94.28% 2 Missing ⚠️
packages/rs-dpp/src/block/block_info/mod.rs 98.00% 1 Missing ⚠️
...p/src/identity/conversion/platform_value/v0/mod.rs 0.00% 1 Missing ⚠️
packages/rs-dpp/src/tokens/info/mod.rs 93.75% 1 Missing ⚠️
...ckages/rs-dpp/src/tokens/token_payment_info/mod.rs 0.00% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@              Coverage Diff              @@
##           v3.1-dev    #3167       +/-   ##
=============================================
+ Coverage     30.67%   50.39%   +19.71%     
=============================================
  Files           105     3089     +2984     
  Lines          9356   219068   +209712     
=============================================
+ Hits           2870   110394   +107524     
- Misses         6486   108674   +102188     
Components Coverage Δ
dpp 34.32% <90.67%> (∅)
drive 42.50% <46.32%> (∅)
drive-abci 79.24% <ø> (∅)
sdk 32.13% <ø> (ø)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value ∅ <ø> (∅)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@shumkov shumkov force-pushed the refactor/sdk/conversion-methods branch from 904d261 to 7c7bb8a Compare March 10, 2026 13:13
shumkov and others added 11 commits March 11, 2026 09:10
…d TokenPaymentInfo conversions

Use serde(tag = "type", content = "data", rename_all = "camelCase") for
semantic enums (Vote, VotePoll, ResourceVoteChoice, TokenEvent,
GroupActionEvent, ContestedDocumentVotePollWinnerInfo) instead of the
default externally-tagged format. This produces JS-friendly output like
{ type: "mint", data: [...] } instead of { Mint: [...] }.

Add $formatVersion tag to TokenPaymentInfo and rename_all = "camelCase"
to its V0 struct. Add toJSON/fromJSON/toObject/fromObject conversion
methods via impl_wasm_conversions_serde! macro.

Update all affected wasm-dpp2 and wasm-sdk test fixtures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd test vectors

- Run cargo fmt on dpp, wasm-dpp2, wasm-sdk
- Feature-gate JsonConvertible import in token_configuration/mod.rs
- Update $version -> $formatVersion in extended_document test fixture
- Update $format_version -> $formatVersion in rs-sdk test vectors
  (6 DocumentQuery mock files still had old snake_case tag)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Collapse nested if-let blocks using let-chains syntax
- Allow unused ValueConvertible import in macro (shadowed by inherent methods)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ype modules

Tests were co-located in serialization_traits.rs but they test specific
types, not the traits themselves. Moved 26 tests to 11 type modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use object destructuring, remove unused Buffer import, and remove
unnecessary non-null assertion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… proc macros

Introduce `#[json_safe_fields]` attribute macro and `#[derive(JsonConvertible)]`
derive macro that automatically add `#[serde(with = "json_safe_u64")]` to u64/i64
fields. Uses `serializer.is_human_readable()` so JSON stringifies values exceeding
JS MAX_SAFE_INTEGER while platform_value stays native. Includes compile-time
enforcement via `JsonSafeFields` marker trait with recursive assertions on nested
types. Adds safe serializers for BTreeMap<u64, u64>, BTreeMap<Identifier, u64>,
and nested map variants. Removes manual stringify/unstringify from wasm-dpp2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- `JsonConvertible::to_json` now maps to `EncodingError` instead of
  `DecodingError` since it's a serialization operation.
- Replace `.unwrap()` with `.expect("guard guarantees len == 1")` in
  platform-value enum deserializer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gate `#[json_safe_fields]` with `#[cfg_attr(feature = "json-conversion", ...)]`
so the macro only runs when the feature is active. Feature-gate the `json`
serialization module and its re-exports. Gate all `json_safe_fields` imports
and manual `serde(with)` map serializer annotations with `cfg(feature)`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
github-actions Bot and others added 21 commits March 11, 2026 09:11
…e docs

Remove `impl JsonSafeFields for u64/i64` so unprotected type aliases like
`type Foo = u64` and containers like `Vec<u64>` trigger compile errors.
Skip JsonSafeFields assertion for fields with `#[serde(with)]` (including
inside cfg_attr) since the developer explicitly handles serialization.

Add generic `json_safe_generic_u64_value_map` serde module for
`BTreeMap<AnyKey, u64>` and apply it to recipient_addresses field.

Add README to proc macro crate documenting the problem, solution,
compile-time safety guarantees, and maintenance guide. Improve doc
blocks across all json serialization modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… macro

- `serde_with_suffix_for_type` now uses last path segment, handling
  qualified paths like `std::option::Option<u64>` and `crate::prelude::Credits`
- `has_serde_default` now detects `default = "custom_fn"` in addition to
  bare `default`, preventing duplicate serde attribute errors
- Add tests for `json_safe_option_i64` (none, large, large negative, missing field)
- Add tests for `json_safe_option_u64` missing field deserialization
- Add tests for `json_safe_generic_u64_value_map` (small, large, mixed values)
- Fix README: expand skip row, clarify serde(with) skips both annotation
  and assertion, clarify Vec<u64> has no module, fix serde Content terminology

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Flat enums (not versioned V0/V1) and enums with u64-alias tuple variants
can't use #[derive(JsonConvertible)] and need manual impl with explanation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The new proc macro crate was missing from Docker build context, causing
cargo metadata to fail with "No such file or directory".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The auto-commit step causes push conflicts on active branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test expected "Insufficient" but the error class uses "Unsufficient".
Match the test to the actual error message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…odules

- Error paths: negative i64→u64, overflow, invalid strings, wrong types
- Platform_value round-trips: non-HR path for u64, i64, Option variants
- Edge cases: empty maps, small Option values, i64 visit on u64 deserialize

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… unified flags

Replace 18 per-entity conversion feature flags (document-serde-conversion,
data-contract-value-conversion, identity-json-conversion, etc.) with 3 unified
flags: serde-conversion, value-conversion, json-conversion. Old flags are kept
as backwards-compatible aliases. This simplifies feature management and reduces
CI build matrix time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…version feature

Move ValueConvertible trait definition, derive macro re-export, and all
derive(ValueConvertible) usages behind #[cfg(feature = "value-conversion")]
instead of serde-conversion or no gate. This properly separates the three
conversion levels: serde-conversion (Serialize/Deserialize derives),
value-conversion (platform_value conversion), json-conversion (JSON conversion).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@shumkov shumkov force-pushed the refactor/sdk/conversion-methods branch from 533c7d5 to efe591b Compare March 11, 2026 02:28
@QuantumExplorer QuantumExplorer merged commit 5953d17 into v3.1-dev Mar 11, 2026
89 of 90 checks passed
@QuantumExplorer QuantumExplorer deleted the refactor/sdk/conversion-methods branch March 11, 2026 03:08
@github-project-automation github-project-automation Bot moved this from In review / testing to Done in Platform team Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants