Skip to content

Comments

fix(dpp): add toJSON() serialization to TokenContractInfoWasm#3089

Open
thepastaclaw wants to merge 4 commits intodashpay:v3.1-devfrom
thepastaclaw:fix-token-contract-info-tojson
Open

fix(dpp): add toJSON() serialization to TokenContractInfoWasm#3089
thepastaclaw wants to merge 4 commits intodashpay:v3.1-devfrom
thepastaclaw:fix-token-contract-info-tojson

Conversation

@thepastaclaw
Copy link
Contributor

@thepastaclaw thepastaclaw commented Feb 16, 2026

Summary

TokenContractInfoWasm was missing toJSON() support, causing it to serialize as an empty object {} when called from JavaScript.

Changes

Added a manual toJSON() method to TokenContractInfoWasm that returns:

  • contractId — base58-encoded string
  • tokenContractPosition — numeric position

Why manual instead of serde?

The inner TokenContractInfo type from rs-dpp doesn't derive serde Serialize/Deserialize, so we can't use the serialization::to_json() helper or the impl_wasm_conversions! macro. Instead, we build the JSON object manually using the existing getters, similar to the pattern used elsewhere in the codebase.

Fixes #3027

Summary by CodeRabbit

  • New Features
    • Added a JSON export for token contract information so clients can retrieve contract ID and token position as a single JSON object for easier consumption in JS and tooling.
    • Added optional serialization support for token contract types when serialization features are enabled, improving interoperability with external tooling.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

Warning

Rate limit exceeded

@thepastaclaw has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds wasm-bindgen JSON serialization support for TokenContractInfo in wasm bindings (new toJSON returning a typed JSON JS object and TypeScript interop types) and conditionally enables serde Serialize/Deserialize derives for TokenContractInfo types in rs-dpp behind feature flags.

Changes

Cohort / File(s) Summary
WASM bindings — TokenContractInfo
packages/wasm-dpp2/src/tokens/contract_info.rs
Adds WasmDppResult and serialization imports; declares a TypeScript TokenContractInfoJSON type and TokenContractInfoJSONJs extern; implements toJSON (to_json) returning WasmDppResult<TokenContractInfoJSONJs> by serializing TokenContractInfo to JSON for JS interop.
Core enum serde derive
packages/rs-dpp/src/tokens/contract_info/mod.rs
Adds a conditional cfg_attr to derive serde::Serialize/Deserialize on TokenContractInfo when fixtures-and-mocks or state-transition-serde-conversion features are enabled.
V0 struct serde derive & rename
packages/rs-dpp/src/tokens/contract_info/v0/mod.rs
Adds a conditional cfg_attr to derive serde::Serialize/Deserialize and serde(rename_all = "camelCase") on TokenContractInfoV0 under the same feature gates.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant JS as Client (JS)
participant WB as wasm-bindgen glue
participant R as Rust TokenContractInfoWasm
participant S as serialization::to_json
JS->>WB: call obj.toJSON()
WB->>R: invoke to_json()
R->>S: serialize inner TokenContractInfo
S-->>R: TokenContractInfoJSONJs
R-->>WB: WasmDppResult
WB-->>JS: return JS object

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nibbled bytes and stitched a key,
ContractId shines for all to see,
Position counted, wrapped in cheer,
No more empties when JS draws near,
A tiny hop — JSON is here!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding toJSON() serialization to TokenContractInfoWasm to fix the empty object serialization issue.
Linked Issues check ✅ Passed The PR implements all coding requirements from #3027: adds toJSON() method to TokenContractInfoWasm that returns contractId and tokenContractPosition in camelCase, and adds serde derives to support JSON serialization.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue requirements: toJSON() implementation in wasm-dpp2 layer and necessary serde derives in rs-dpp to enable serialization.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@thepastaclaw thepastaclaw changed the title fix(wasm-dpp2): add toJSON() serialization to TokenContractInfoWasm fix(dpp): add toJSON() serialization to TokenContractInfoWasm Feb 16, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🤖 Fix all issues with AI agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.

In `@packages/wasm-dpp2/src/tokens/contract_info.rs`:
- Line 53: Replace the direct cast to f64 in the JsValue conversion by using the
From conversion: instead of using "self.token_contract_position() as f64" (where
token_contract_position() returns TokenContractPosition/u16), call
f64::from(self.token_contract_position()) so clippy's cast_lossless lint is
satisfied; update the expression that constructs JsValue (the line with
&JsValue::from_f64(...)) accordingly.
🧹 Nitpick comments (1)
🤖 Fix all nitpicks with AI agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.

In `@packages/wasm-dpp2/src/tokens/contract_info.rs`:
- Line 53: Replace the direct cast to f64 in the JsValue conversion by using the
From conversion: instead of using "self.token_contract_position() as f64" (where
token_contract_position() returns TokenContractPosition/u16), call
f64::from(self.token_contract_position()) so clippy's cast_lossless lint is
satisfied; update the expression that constructs JsValue (the line with
&JsValue::from_f64(...)) accordingly.
packages/wasm-dpp2/src/tokens/contract_info.rs (1)

53-53: Replace as f64 with f64::from() to comply with clippy's cast_lossless lint.

Since token_contract_position() returns TokenContractPosition (which is u16), the cast to f64 is a lossless widening operation. Clippy's cast_lossless lint prefers explicit From conversions for such casts.

Suggested fix
-            &JsValue::from_f64(self.token_contract_position() as f64),
+            &JsValue::from_f64(f64::from(self.token_contract_position())),
🤖 Prompt for AI Agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.
In `@packages/wasm-dpp2/src/tokens/contract_info.rs` at line 53, Replace the
direct cast to f64 in the JsValue conversion by using the From conversion:
instead of using "self.token_contract_position() as f64" (where
token_contract_position() returns TokenContractPosition/u16), call
f64::from(self.token_contract_position()) so clippy's cast_lossless lint is
satisfied; update the expression that constructs JsValue (the line with
&JsValue::from_f64(...)) accordingly.

@thepastaclaw
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@PastaPastaPasta
Copy link
Member

Can you confirm we shouldn't add this lower in the stack? we generally want to make changes like in the rs-sdk if possible, and then just expose them in the higher levels.

If this is relevant here, please make the change

@thepastaclaw
Copy link
Contributor Author

Good point. TokenContractInfo in rs-dpp doesn't derive Serialize/Deserialize, so the WASM layer couldn't use the standard serialization::to_json() helper. That's why I went with a manual toJSON() method.

The cleaner approach would be to add serde derives to TokenContractInfo in rs-dpp, then the WASM layer could use the standard impl_wasm_conversions! macro like other types. I can make that change — it would be a small addition to rs-dpp (Serialize/Deserialize derives on the struct) and then simplify the WASM code to use the standard pattern.

Want me to update this PR to do it that way?

@thepastaclaw
Copy link
Contributor Author

Good question. I looked into this — TokenContractInfoV0 in rs-dpp only derives Encode/Decode (bincode), no Serialize/serde. The toJSON() method is specifically a JavaScript concern — it constructs a js_sys::Object with Reflect::set to expose contractId (base58) and tokenContractPosition to JS consumers.

This is the correct layer (wasm-dpp2) since it's the JS binding surface. The Rust SDK doesn't need JSON serialization here — it accesses the fields directly via the accessor trait.

That said, if you'd prefer adding #[derive(Serialize)] + serde to the Rust struct and then using serde_wasm_bindgen in the wasm layer instead of manual Reflect::set, I can refactor it that way. But the manual approach matches the existing pattern in other wasm-dpp2 types.

@thepastaclaw
Copy link
Contributor Author

Good question — I looked into this and the fix is correctly scoped to the wasm layer. The underlying Rust type (TokenContractInfo) already has proper serialization via serde derives in rs-sdk/dpp. The issue was specifically that the wasm_bindgen wrapper (TokenContractInfoWasm) didn't implement toJSON(), which is a JavaScript-specific concern — it controls how JSON.stringify() handles the object. Without it, JS sees an opaque wasm object and serializes it as {}.

The rs-sdk layer doesn't (and shouldn't) know about JavaScript's toJSON() protocol. Other wasm wrapper types in wasm-dpp2 follow this same pattern — they add toJSON() in the wasm layer to bridge Rust serialization to JS.

So no changes needed lower in the stack here.

@thepastaclaw
Copy link
Contributor Author

Applied the cast_lossless fix — switched to f64::from(self.token_contract_position()) in cfecaaf. Thanks for the catch!

Copy link
Collaborator

@shumkov shumkov left a comment

Choose a reason for hiding this comment

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

You should look on how we inmplement conversion methods for other dpp2 entities and do the same way.d

@thepastaclaw
Copy link
Contributor Author

Thanks @shumkov — will look at the existing dpp2 entity conversion patterns and align TokenContractInfoWasm with the same approach. Will push an updated implementation.

@thepastaclaw
Copy link
Contributor Author

Refactored per review feedback (commit b7c2b0c):

Changed from manual js_sys::Reflect::set approach to the standard dpp2 conversion pattern:

#[wasm_bindgen(js_name = "toJSON")]
pub fn to_json(&self) -> WasmDppResult<TokenContractInfoJSONJs> {
    let js_value = serialization::to_json(&self.0)?;
    Ok(js_value.into())
}

This required adding feature-gated serde derives to the underlying types:

  • TokenContractInfoV0: Serialize/Deserialize + #[serde(rename_all = "camelCase")]
  • TokenContractInfo enum: Serialize/Deserialize
  • Both gated behind fixtures-and-mocks or state-transition-serde-conversion features (matching how other dpp types handle this)

Also added proper TypeScript type definition (TokenContractInfoJSON interface) following the pattern from other wasm-dpp2 entities.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-dpp/src/tokens/contract_info/mod.rs`:
- Around line 27-36: The TokenContractInfo enum's serde attributes lack
untagged, causing JSON to be wrapped with a "V0" key; update the cfg_attr on
TokenContractInfo (the same attribute block that currently derives
serde::Serialize and serde::Deserialize) to include serde(untagged) so that
TokenContractInfo serializes to the flat shape expected by
TokenContractInfoJSON; follow the same pattern used in state_transition/mod.rs
when adding serde(untagged).

@thepastaclaw
Copy link
Contributor Author

Good catch on the missing serde(untagged) — fixed in 03208e7. Without it, serialization::to_json() would produce {"V0": {"contractId": ..., "tokenContractPosition": 0}} instead of the flat structure the TS interface expects. Now matches the pattern used by other versioned enums in rs-dpp.

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

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

utACK

@thepastaclaw
Copy link
Contributor Author

CI failures here are all base branch (v3.1-dev) issues — security audits, clippy on base changes, etc. The base branch itself has been consistently failing CI over the last several runs. My changes (serde untagged annotation on TokenContractInfo) are unrelated.

Ready to merge once base CI stabilizes.

@thepastaclaw
Copy link
Contributor Author

Fix: Swift SDK build — missing SPV symbols

Root cause: rust-dashcore is a Cargo workspace. When cargo build runs from the dash-spv-ffi/ subdirectory, output goes to the workspace root rust-dashcore/target/, not dash-spv-ffi/target/. The build script (packages/rs-sdk-ffi/build_ios.sh) was looking for libdash_spv_ffi.a at the crate-level target dir ($SPV_CRATE_PATH/target/), which doesn't exist.

This meant the SPV library was being built successfully but never found during the merge step. The XCFramework was created with only librs_sdk_ffi.a (no SPV symbols), causing the symbol verification to fail.

Fix: Changed all 6 $SPV_CRATE_PATH/target/ references to $RUST_DASHCORE_PATH/target/ (the workspace root target directory).

This is a platform-wide issue affecting all PRs, not just this one.

Commit: 4240f62

@thepastaclaw thepastaclaw force-pushed the fix-token-contract-info-tojson branch from 4240f62 to 1b23e6b Compare February 20, 2026 23:03
@thepastaclaw
Copy link
Contributor Author

Dropped the swift-sdk build_ios.sh commit — that's a pre-existing issue already fixed by #3107 (lklimek). Shouldn't have been bundled here.

thepastaclaw and others added 3 commits February 21, 2026 12:29
TokenContractInfoWasm was missing toJSON() support, causing it to
serialize as an empty object {}. Add a manual toJSON() method that
returns contractId (as base58 string) and tokenContractPosition.

Fixes dashpay#3027
…nContractInfo.toJSON()

Replace manual js_sys::Reflect::set approach with serialization::to_json(),
matching the pattern used by Identity, DataContract, and other dpp2 types.
Add serde Serialize/Deserialize derives to TokenContractInfo and
TokenContractInfoV0 behind feature flags.

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thepastaclaw thepastaclaw force-pushed the fix-token-contract-info-tojson branch from 1b23e6b to f12983f Compare February 21, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TokenContractInfoWasm missing toJSON() serialization - returns empty object

3 participants