Skip to content

feat: implement StateTransitionJsonConvert for top-level StateTransition enum #3109

@lklimek

Description

@lklimek

Problem

In packages/rs-dpp/src/state_transition/mod.rs, the StateTransition enum derives Serialize (behind state-transition-serde-conversion feature), but serde_json::to_string_pretty(&state_transition) fails with "key must be a string" for any variant that contains BTreeMap<PlatformAddress, ...> (e.g. AddressFundsTransfer, AddressFundingFromAssetLock, AddressCreditWithdrawal, IdentityCreateFromAddresses, IdentityTopUpFromAddresses, IdentityCreditTransferToAddresses).

This happens because PlatformAddress is an enum that serializes as a complex structure, not a string, and JSON requires all object keys to be strings.

Every individual variant type already implements StateTransitionJsonConvert (which has a to_json() method that properly converts through platform_value::Value, stringifying identifiers and binary data). But the top-level StateTransition enum itself does NOT implement it, forcing consumers to match on all 15 variants manually.

Proposed Fix

Add an impl StateTransitionJsonConvert<'_> for StateTransition that dispatches to each variant's to_json(). This should go in a new file packages/rs-dpp/src/state_transition/json_conversion.rs (gated behind #[cfg(feature = "state-transition-json-conversion")]), similar to how value_conversion.rs or other dispatch files are structured. The implementation:

#[cfg(feature = "state-transition-json-conversion")]
impl StateTransitionJsonConvert<'_> for StateTransition {
    fn to_json(
        &self,
        options: JsonStateTransitionSerializationOptions,
    ) -> Result<serde_json::Value, ProtocolError> {
        match self {
            StateTransition::DataContractCreate(st) => st.to_json(options),
            StateTransition::DataContractUpdate(st) => st.to_json(options),
            StateTransition::Batch(st) => st.to_json(options),
            StateTransition::IdentityCreate(st) => st.to_json(options),
            StateTransition::IdentityTopUp(st) => st.to_json(options),
            StateTransition::IdentityCreditWithdrawal(st) => st.to_json(options),
            StateTransition::IdentityUpdate(st) => st.to_json(options),
            StateTransition::IdentityCreditTransfer(st) => st.to_json(options),
            StateTransition::MasternodeVote(st) => st.to_json(options),
            StateTransition::IdentityCreditTransferToAddresses(st) => st.to_json(options),
            StateTransition::IdentityCreateFromAddresses(st) => st.to_json(options),
            StateTransition::IdentityTopUpFromAddresses(st) => st.to_json(options),
            StateTransition::AddressFundsTransfer(st) => st.to_json(options),
            StateTransition::AddressFundingFromAssetLock(st) => st.to_json(options),
            StateTransition::AddressCreditWithdrawal(st) => st.to_json(options),
        }
    }
}

Note: StateTransitionJsonConvert has a blanket default to_json() that calls self.to_object() (from StateTransitionValueConvert). Since StateTransitionValueConvert is also not implemented for the top-level enum, the explicit override above is needed. It may also make sense to implement StateTransitionValueConvert for StateTransition with the same dispatch pattern.

Also consider adding state-transition-json-conversion to the dash-sdk-features list in packages/rs-dpp/Cargo.toml so SDK consumers get it by default.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions