Skip to content

feat(sdk): add platform address transition WASM bindings#3147

Open
shumkov wants to merge 5 commits intov3.1-devfrom
feat/sdk/platform-address-transitions
Open

feat(sdk): add platform address transition WASM bindings#3147
shumkov wants to merge 5 commits intov3.1-devfrom
feat/sdk/platform-address-transitions

Conversation

@shumkov
Copy link
Collaborator

@shumkov shumkov commented Feb 23, 2026

Issue being fixed or feature implemented

JavaScript/TypeScript applications need WASM bindings for the 6 platform address-based state transitions to create, serialize, and
interact with them from the browser and Node.js.

What was done?

New transition bindings (platform_address/transitions/)

Added WASM bindings for all 6 address-based state transitions:

  • AddressFundsTransferTransition — transfer credits between platform addresses
  • AddressCreditWithdrawalTransition — withdraw credits to Core via output script
  • AddressFundingFromAssetLockTransition — fund platform addresses via asset lock proofs
  • IdentityCreateFromAddressesTransition — create identity from platform address inputs
  • IdentityTopUpFromAddressesTransition — top up identity from platform addresses
  • IdentityCreditTransferToAddressesTransition — transfer credits from identity to platform addresses

Each binding exposes:

  • Constructor from a typed JS options object
  • Binary serialization: toBytes/fromBytes, toBase64/fromBase64, toHex/fromHex
  • Serde conversions: toObject/fromObject, toJSON/fromJSON (via impl_wasm_conversions!)
  • toStateTransition/fromStateTransition for enum wrapping
  • Typed getters/setters for all fields

Supporting changes

  • input_output.rs — added PlatformAddressOutput::new/new_optional, helper functions inputs_from_js_options,
    optional_output_from_js_options, fee_strategy_from_js_options, outputs_from_js_options
  • fee_strategy.rs — added FeeStrategyStepWasm with deductFromInput/reduceOutput constructors and to_wasm conversion
  • address.rs — added PlatformAddressWasm::new constructor, impl_wasm_type_info!
  • lib.rs — exported new transition types and FeeStrategyStepWasm

How Has This Been Tested?

yarn workspace @dashevo/wasm-dpp2 test — 838 passing, 0 failing.

Added 7 test files:

  • AddressFundsTransferTransition.spec.ts — constructor, bytes/base64/hex round-trips, inputs/outputs getters/setters,
    userFeeIncrease, StateTransition conversion
  • AddressCreditWithdrawalTransition.spec.ts — constructor (with/without output),
    inputs/output/outputScript/pooling/coreFeePerByte getters/setters, StateTransition conversion
  • AddressFundingFromAssetLockTransition.spec.ts — constructor with asset lock proof, inputs/outputs/assetLockProof
    getters/setters, StateTransition conversion
  • IdentityCreateFromAddressesTransition.spec.ts — constructor with public keys, inputs getter/setter, StateTransition conversion
  • IdentityTopUpFromAddressesTransition.spec.ts — constructor with identityId (string and Identifier), inputs/output/identityId
    getters/setters, StateTransition conversion
  • IdentityCreditTransferToAddresses.spec.ts — constructor, recipientAddresses/senderId/nonce/signature/signaturePublicKeyId
    getters/setters, StateTransition conversion
  • AddressFundsTransfer.spec.tsFeeStrategyStepWasm deductFromInput/reduceOutput tests

Note: toObject/fromObject/toJSON/fromJSON conversion tests deferred to a separate PR to address a pre-existing
is_human_readable mismatch in the impl_wasm_conversions! macro.

Breaking Changes

None

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

Summary by CodeRabbit

  • New Features

    • Six new WebAssembly transition types for address/identity flows with JS/TS constructors, getters/setters, and state-transition conversion.
    • New helpers to construct and extract inputs, outputs, and fee strategies from JS options.
  • Enhancements

    • Robust serialization/deserialization (bytes, hex, Base64) and improved cross-language type conversions, validation, and error handling.
    • Optional fields support and safer option parsing with defaults.
  • Tests

    • Comprehensive unit test suites covering construction, accessors, serialization, and state-transition round-trips for all new transitions.

@github-actions github-actions bot added this to the v3.1.0 milestone Feb 23, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

Warning

Rate limit exceeded

@shumkov has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 1 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.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c797aac7-b0b8-4669-9218-c0cdd431d72f

📥 Commits

Reviewing files that changed from the base of the PR and between bf0120c and e39645d.

📒 Files selected for processing (18)
  • packages/wasm-dpp2/src/data_contract/model.rs
  • packages/wasm-dpp2/src/identity/model.rs
  • packages/wasm-dpp2/src/identity/transitions/create_transition.rs
  • packages/wasm-dpp2/src/identity/transitions/masternode_vote_transition.rs
  • packages/wasm-dpp2/src/identity/transitions/update_transition.rs
  • packages/wasm-dpp2/src/state_transitions/base/state_transition.rs
  • packages/wasm-dpp2/src/state_transitions/batch/batch_transition.rs
  • packages/wasm-dpp2/src/state_transitions/batch/document_base_transition.rs
  • packages/wasm-dpp2/src/state_transitions/batch/document_transition.rs
  • packages/wasm-dpp2/src/state_transitions/batch/document_transitions/purchase.rs
  • packages/wasm-dpp2/src/state_transitions/batch/document_transitions/replace.rs
  • packages/wasm-dpp2/src/state_transitions/batch/document_transitions/update_price.rs
  • packages/wasm-dpp2/src/state_transitions/batch/token_base_transition.rs
  • packages/wasm-dpp2/src/state_transitions/batch/token_transition.rs
  • packages/wasm-dpp2/src/state_transitions/batch/token_transitions/token_burn.rs
  • packages/wasm-dpp2/src/state_transitions/batch/token_transitions/token_mint.rs
  • packages/wasm-dpp2/src/state_transitions/batch/token_transitions/token_transfer.rs
  • packages/wasm-dpp2/src/tokens/configuration/token_configuration.rs
📝 Walkthrough

Walkthrough

Adds six new wasm-bindgen transition bindings under platform_address::transitions, utilities for fee strategies and IO option extraction, small PlatformAddress deserialization support, re-exports from the crate root, and comprehensive unit tests exercising construction, serialization, accessors, and StateTransition interop.

Changes

Cohort / File(s) Summary
Core Library Exports
packages/wasm-dpp2/src/lib.rs
Re-exports six new *TransitionWasm types from platform_address::transitions.
Platform Address Core
packages/wasm-dpp2/src/platform_address/address.rs, packages/wasm-dpp2/src/platform_address/fee_strategy.rs, packages/wasm-dpp2/src/platform_address/input_output.rs, packages/wasm-dpp2/src/platform_address/mod.rs
Added map deserialization for PlatformAddressWasm; fee strategy wasm/native conversions and fee_strategy_from_js_options; input/output wasm constructors and JS-options extraction helpers; added transitions module and extended re-exports.
Transitions Module
packages/wasm-dpp2/src/platform_address/transitions/mod.rs
Declares and re-exports six new transition submodules and their *TransitionWasm types.
AddressCreditWithdrawal Transition
packages/wasm-dpp2/src/platform_address/transitions/address_credit_withdrawal_transition.rs
New wasm wrapper type with constructor, getters/setters, (de)serializers, to/from StateTransition, and conversion/type-info macros.
AddressFundingFromAssetLock Transition
packages/wasm-dpp2/src/platform_address/transitions/address_funding_from_asset_lock_transition.rs
New wasm wrapper exposing constructor, assetLockProof accessors, (de)serializers, to/from StateTransition, and conversion/type-info macros.
AddressFundsTransfer Transition
packages/wasm-dpp2/src/platform_address/transitions/address_funds_transfer_transition.rs
New wasm wrapper with constructor, fee strategy handling, inputs/outputs accessors, (de)serializers, to/from StateTransition, and conversion/type-info macros.
IdentityCreateFromAddresses Transition
packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs
New wasm wrapper exposing public keys handling, inputs/output, (de)serializers, to/from StateTransition, and conversion/type-info macros.
IdentityCreditTransferToAddresses Transition
packages/wasm-dpp2/src/platform_address/transitions/identity_credit_transfer_to_addresses_transition.rs
New wasm wrapper exposing recipient addresses map, sender/nonce/signature fields, (de)serializers, to/from StateTransition, and conversion/type-info macros.
IdentityTopUpFromAddresses Transition
packages/wasm-dpp2/src/platform_address/transitions/identity_top_up_from_addresses_transition.rs
New wasm wrapper exposing identityId/inputs/optional output, (de)serializers, to/from StateTransition, and conversion/type-info macros.
Unit Tests
packages/wasm-dpp2/tests/unit/*Transition.spec.ts, packages/wasm-dpp2/tests/unit/AddressFundsTransfer.spec.ts
Added comprehensive test suites for each new transition covering constructors, accessors/mutators, serialization round-trips, StateTransition conversions, and defaults; one skipped note update.

Sequence Diagram(s)

sequenceDiagram
  participant JS as JavaScript Client
  participant WASM as wasm-dpp2 bindings
  participant Core as Rust Transition (V0)
  participant ST as StateTransitionWasm

  JS->>WASM: call Constructor(options)
  WASM->>WASM: parse JS options (inputs/outputs/feeStrategy/fields)
  WASM->>Core: build V0 transition struct
  Core-->>WASM: return transition instance
  WASM->>WASM: to_bytes()/to_hex()/to_base64()
  WASM->>ST: to_state_transition()
  ST-->>WASM: state transition wrapper
  WASM-->>JS: return constructed TransitionWasm or serialized data
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • QuantumExplorer

Poem

🐰 New hops of code across the land,
Six transitions stitched by careful hand,
Bytes, hex, and base64 in playful spin,
Tests applaud each round‑trip win,
A tiny rabbit cheers — hooray, we did begin! 🎋

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(sdk): add platform address transition WASM bindings' accurately summarizes the main change: adding WebAssembly bindings for six platform address state transitions to enable JavaScript/TypeScript usage.
Docstring Coverage ✅ Passed Docstring coverage is 99.25% 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
  • Commit unit tests in branch feat/sdk/platform-address-transitions

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.

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: 2

♻️ Duplicate comments (1)
packages/wasm-dpp2/src/platform_address/transitions/identity_credit_transfer_to_addresses_transition.rs (1)

159-175: Same outputs_to_btree_map panic risk applies here.

set_recipient_addresses (Line 174) and the constructor (Line 96) both use outputs_to_btree_map, which internally calls into_inner() with an .expect(). If a PlatformAddressOutput without an amount reaches this path, it will panic. This is the same root cause flagged in the address_credit_withdrawal_transition.rs review.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/wasm-dpp2/src/platform_address/transitions/identity_credit_transfer_to_addresses_transition.rs`
around lines 159 - 175, The setter set_recipient_addresses calls
outputs_to_btree_map which currently uses into_inner().expect() and can panic if
a PlatformAddressOutput lacks an amount; change outputs_to_btree_map to return a
Result<BTreeMap<...>, JsValue> (or custom error) instead of unwrapping, update
set_recipient_addresses (and the constructor that also calls
outputs_to_btree_map) to handle the Result by propagating the error to JS (e.g.,
return Result<(), JsValue> or use wasm_bindgen::throw_str) and validate each
PlatformAddressOutputWasm for a present amount before converting so no .expect()
is used. Ensure references are updated for outputs_to_btree_map,
set_recipient_addresses, and the constructor that calls outputs_to_btree_map.
🧹 Nitpick comments (9)
packages/wasm-dpp2/tests/unit/AddressFundsTransfer.spec.ts (1)

42-45: Filename uses PascalCase instead of kebab-case.

Per coding guidelines, files matching packages/**/!(node_modules)/**/*.{js,jsx,ts,tsx} should use kebab-case for filenames. This file is AddressFundsTransfer.spec.ts but should be address-funds-transfer.spec.ts. The same applies to the other new test files in this PR. However, if PascalCase is the established convention in wasm-dpp2 tests, feel free to disregard.

As per coding guidelines: "Use kebab-case for filenames within JS packages"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/tests/unit/AddressFundsTransfer.spec.ts` around lines 42 -
45, The test filename uses PascalCase (AddressFundsTransfer.spec.ts) but should
follow the repo's kebab-case rule; rename the file to
address-funds-transfer.spec.ts and update any imports or test references that
point to AddressFundsTransfer.spec.ts (and do the same for other new test files
in this PR) so test runner and imports resolve correctly; verify references in
test suites or any index files that mention AddressFundsTransferTransition or
the skipped describe('AddressFundsTransferTransition (.build API)') remain valid
after the rename.
packages/wasm-dpp2/src/platform_address/input_output.rs (1)

186-265: Consider extracting a generic helper to reduce duplication between inputs_from_js_options and outputs_from_js_options.

Both functions share identical scaffolding (Reflect::get → null check → array check → enumerate → to_wasm → clone → map_err), differing only in the Wasm type and its name string. A generic helper parameterized on the target type would eliminate this ~40-line duplication. Note that fee_strategy_from_js_options already uses the try_from_options_optional_with + try_to_array utils for similar work but with optional semantics.

Not blocking — deferring is fine if the current clarity is preferred.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/src/platform_address/input_output.rs` around lines 186 -
265, Extract a generic helper (e.g., items_from_js_options<T>) that encapsulates
the common scaffolding in inputs_from_js_options and outputs_from_js_options:
read the property via js_sys::Reflect::get, check undefined/null, ensure it's an
array, iterate/enumerate, call item.to_wasm::<T>(type_name) and clone the inner
value, and map errors with the same "{}[{}] is not a <Type>" message; make the
helper accept the JsValue options, field_name: &str, type_name: &str and a
generic T used in to_wasm (with the same trait bounds required for cloning),
then replace inputs_from_js_options and outputs_from_js_options to call this
helper and return its result (keep the existing WasmDppError messages and
signatures).
packages/wasm-dpp2/tests/unit/AddressFundingFromAssetLockTransition.spec.ts (1)

164-172: Consider asserting the output amount after the setter.

The outputs setter test verifies the array length but not the updated amount value (BigInt(50000)). A value assertion would strengthen this test.

✏️ Suggested addition
     transition.outputs = [newOutput];
     const outputs = transition.outputs;
     expect(outputs).to.have.lengthOf(1);
+    expect(outputs[0].amount).to.equal(BigInt(50000));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/tests/unit/AddressFundingFromAssetLockTransition.spec.ts`
around lines 164 - 172, The test currently only checks the outputs array length;
after setting transition.outputs to [newOutput] also assert the amount on the
stored output matches the intended BigInt(50000). Locate the test using
createTransition(), new wasm.PlatformAddressOutput(newAddr, BigInt(50000)) and
transition.outputs, then add an assertion that the retrieved outputs[0] (or
outputs.at(0)) has an amount equal to BigInt(50000) to validate the setter
preserved the value.
packages/wasm-dpp2/tests/unit/IdentityCreateFromAddressesTransition.spec.ts (1)

81-100: Missing toHex/fromHex round-trip coverage — same gap as IdentityTopUpFromAddressesTransition.spec.ts. Consider adding a toHex() / fromHex() describe block for consistency with the other transition suites.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/tests/unit/IdentityCreateFromAddressesTransition.spec.ts`
around lines 81 - 100, Add missing hex round-trip tests for
IdentityCreateFromAddressesTransition: add a describe('toHex() / fromHex()')
block mirroring the existing toBase64/toBytes tests, create a transition via
createTransition(), call transition.toHex(), verify Buffer.from(hex, 'hex')
equals Buffer.from(transition.toBytes()), then restore with
wasm.IdentityCreateFromAddressesTransition.fromHex(hex) and assert
Buffer.from(restored.toBytes()) equals the original bytes; reference the
existing createTransition(), transition.toHex(),
wasm.IdentityCreateFromAddressesTransition.fromHex(), transition.toBytes(), and
restored.toBytes() methods when locating where to insert the new test.
packages/wasm-dpp2/tests/unit/IdentityTopUpFromAddressesTransition.spec.ts (1)

76-95: Missing toHex/fromHex round-trip coverage.

The other transition spec files (AddressFundsTransferTransition.spec.ts, IdentityCreditTransferToAddresses.spec.ts, AddressFundingFromAssetLockTransition.spec.ts) all include a toHex() / fromHex() suite. Consider adding one here for consistency:

✏️ Suggested addition
+  describe('toHex() / fromHex()', () => {
+    it('should round-trip via hex', () => {
+      const transition = createTransition();
+      const hex = transition.toHex();
+      const bytes = transition.toBytes();
+
+      const restored = wasm.IdentityTopUpFromAddressesTransition.fromHex(hex);
+      expect(Buffer.from(restored.toBytes())).to.deep.equal(Buffer.from(bytes));
+    });
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/tests/unit/IdentityTopUpFromAddressesTransition.spec.ts`
around lines 76 - 95, Add a toHex()/fromHex() round-trip test for
IdentityTopUpFromAddressesTransition similar to other transition specs: create a
transition via createTransition(), call transition.toHex(), assert
Buffer.from(hex, 'hex') equals Buffer.from(transition.toBytes()), then restore
with wasm.IdentityTopUpFromAddressesTransition.fromHex(hex) and assert
Buffer.from(restored.toBytes()) equals the original bytes; place this in a new
describe('toHex() / fromHex()') / it('should round-trip via hex') block
alongside the existing toBytes/toBase64 tests.
packages/wasm-dpp2/tests/unit/AddressCreditWithdrawalTransition.spec.ts (1)

78-97: Missing toHex/fromHex round-trip coverage — same gap as two other spec files in this PR. Consider adding a toHex() / fromHex() describe block for consistency across the transition suite.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/tests/unit/AddressCreditWithdrawalTransition.spec.ts`
around lines 78 - 97, Add a new describe block testing toHex()/fromHex()
round-trip for AddressCreditWithdrawalTransition: call createTransition(), get
hex via transition.toHex(), assert Buffer.from(hex, 'hex') equals
Buffer.from(transition.toBytes()), then restore via
wasm.AddressCreditWithdrawalTransition.fromHex(hex) and assert
Buffer.from(restored.toBytes()) equals the original bytes; place the test
alongside the existing toBytes/toBase64 blocks and use the same createTransition
helper and wasm.AddressCreditWithdrawalTransition methods.
packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs (2)

163-179: Same unnecessary clone pattern in set_public_keys.

keys is a local Vec that's not reused — into_iter() avoids the per-element clone.

♻️ Suggested optimization
             IdentityCreateFromAddressesTransition::V0(v0) => {
-                v0.public_keys = keys
-                    .iter()
-                    .map(|k| IdentityPublicKeyInCreation::from(k.clone()))
+                v0.public_keys = keys
+                    .into_iter()
+                    .map(|k| IdentityPublicKeyInCreation::from(k))
                     .collect();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs`
around lines 163 - 179, The setter set_public_keys currently iterates over the
local Vec keys with keys.iter() and calls k.clone() for each element; replace
that clone pattern by consuming the vector with keys.into_iter() and mapping
each IdentityPublicKeyInCreationWasm directly into IdentityPublicKeyInCreation
(i.e., use IdentityPublicKeyInCreation::from(k) inside the map) when assigning
to v0.public_keys in the IdentityCreateFromAddressesTransition::V0 arm to avoid
unnecessary per-element cloning of IdentityPublicKeyInCreationWasm.

94-105: Unnecessary .clone()public_keys is not used after this point.

public_keys.iter().map(|k| k.clone().into()) borrows the vec and clones each element. Since public_keys is a local that's not used afterwards, .into_iter() avoids the clone.

♻️ Suggested optimization
                 IdentityCreateFromAddressesTransitionV0 {
-                    public_keys: public_keys.iter().map(|k| k.clone().into()).collect(),
+                    public_keys: public_keys.into_iter().map(|k| k.into()).collect(),
                     inputs: inputs_map,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs`
around lines 94 - 105, The public_keys vector is cloned unnecessarily when
constructing IdentityCreateFromAddressesTransitionV0; update the construction in
IdentityCreateFromAddressesTransitionWasm /
IdentityCreateFromAddressesTransition::V0 /
IdentityCreateFromAddressesTransitionV0 to consume public_keys instead of
iterating and cloning it by replacing public_keys.iter().map(|k|
k.clone().into()) with an into-iterator that converts each element (e.g.,
public_keys.into_iter().map(Into::into)), since public_keys is not used after
this point.
packages/wasm-dpp2/src/platform_address/transitions/address_funding_from_asset_lock_transition.rs (1)

153-204: Minor inconsistency: inputs uses manual match while asset_lock_proof and outputs use accessor trait methods.

The inputs getter/setter (lines 163–184) manually matches on V0, while asset_lock_proof and outputs use the AddressFundingFromAssetLockTransitionAccessorsV0 trait methods. This works fine, but if an accessor for inputs becomes available on the trait in the future, consider aligning for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/wasm-dpp2/src/platform_address/transitions/address_funding_from_asset_lock_transition.rs`
around lines 153 - 204, The inputs getter/setter manually pattern-match on
AddressFundingFromAssetLockTransition::V0 while asset_lock_proof and outputs use
the AddressFundingFromAssetLockTransitionAccessorsV0 trait; change inputs() and
set_inputs() to call the same accessor methods on self.0 (e.g., use
AddressFundingFromAssetLockTransitionAccessorsV0::inputs and ::set_inputs or the
impl-provided inputs()/set_inputs() methods) and convert between
PlatformAddressInputWasm and the inner types as before so the code consistently
uses the transition accessor API (update the inputs() getter to iterate over
self.0.inputs() and the set_inputs() to call self.0.set_inputs(inputs_map)).
🤖 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/wasm-dpp2/src/platform_address/transitions/address_credit_withdrawal_transition.rs`:
- Around line 179-206: The setter AddressCreditWithdrawalTransition::set_output
currently calls PlatformAddressOutputWasm::into_inner(), which can panic if the
PlatformAddressOutput has no amount; replace this with a fallible conversion
(e.g., add and call a try_into_inner() on PlatformAddressOutputWasm that returns
WasmDppResult<PlatformAddressOutput>) or explicitly validate the output_wasm has
an amount before converting and return a WasmDppError (invalid_argument) instead
of panicking. Update callers that use into_inner() (including
outputs_to_btree_map and implementations in AddressFundsTransferTransition and
other transition setters) to use the new try_into_inner() or the
validation+error pattern so all setters return graceful WasmDppResult errors
instead of causing a WASM panic.

In `@packages/wasm-dpp2/src/platform_address/transitions/mod.rs`:
- Line 10: The import line uses the misspelled module name
address_funds_transfer_transsition (extra "s"), causing an unresolved import for
AddressFundsTransferTransitionWasm; change the module path to the correct
address_funds_transfer_transition so the pub use
AddressFundsTransferTransitionWasm; resolves to the declared module name.

---

Duplicate comments:
In
`@packages/wasm-dpp2/src/platform_address/transitions/identity_credit_transfer_to_addresses_transition.rs`:
- Around line 159-175: The setter set_recipient_addresses calls
outputs_to_btree_map which currently uses into_inner().expect() and can panic if
a PlatformAddressOutput lacks an amount; change outputs_to_btree_map to return a
Result<BTreeMap<...>, JsValue> (or custom error) instead of unwrapping, update
set_recipient_addresses (and the constructor that also calls
outputs_to_btree_map) to handle the Result by propagating the error to JS (e.g.,
return Result<(), JsValue> or use wasm_bindgen::throw_str) and validate each
PlatformAddressOutputWasm for a present amount before converting so no .expect()
is used. Ensure references are updated for outputs_to_btree_map,
set_recipient_addresses, and the constructor that calls outputs_to_btree_map.

---

Nitpick comments:
In `@packages/wasm-dpp2/src/platform_address/input_output.rs`:
- Around line 186-265: Extract a generic helper (e.g., items_from_js_options<T>)
that encapsulates the common scaffolding in inputs_from_js_options and
outputs_from_js_options: read the property via js_sys::Reflect::get, check
undefined/null, ensure it's an array, iterate/enumerate, call
item.to_wasm::<T>(type_name) and clone the inner value, and map errors with the
same "{}[{}] is not a <Type>" message; make the helper accept the JsValue
options, field_name: &str, type_name: &str and a generic T used in to_wasm (with
the same trait bounds required for cloning), then replace inputs_from_js_options
and outputs_from_js_options to call this helper and return its result (keep the
existing WasmDppError messages and signatures).

In
`@packages/wasm-dpp2/src/platform_address/transitions/address_funding_from_asset_lock_transition.rs`:
- Around line 153-204: The inputs getter/setter manually pattern-match on
AddressFundingFromAssetLockTransition::V0 while asset_lock_proof and outputs use
the AddressFundingFromAssetLockTransitionAccessorsV0 trait; change inputs() and
set_inputs() to call the same accessor methods on self.0 (e.g., use
AddressFundingFromAssetLockTransitionAccessorsV0::inputs and ::set_inputs or the
impl-provided inputs()/set_inputs() methods) and convert between
PlatformAddressInputWasm and the inner types as before so the code consistently
uses the transition accessor API (update the inputs() getter to iterate over
self.0.inputs() and the set_inputs() to call self.0.set_inputs(inputs_map)).

In
`@packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs`:
- Around line 163-179: The setter set_public_keys currently iterates over the
local Vec keys with keys.iter() and calls k.clone() for each element; replace
that clone pattern by consuming the vector with keys.into_iter() and mapping
each IdentityPublicKeyInCreationWasm directly into IdentityPublicKeyInCreation
(i.e., use IdentityPublicKeyInCreation::from(k) inside the map) when assigning
to v0.public_keys in the IdentityCreateFromAddressesTransition::V0 arm to avoid
unnecessary per-element cloning of IdentityPublicKeyInCreationWasm.
- Around line 94-105: The public_keys vector is cloned unnecessarily when
constructing IdentityCreateFromAddressesTransitionV0; update the construction in
IdentityCreateFromAddressesTransitionWasm /
IdentityCreateFromAddressesTransition::V0 /
IdentityCreateFromAddressesTransitionV0 to consume public_keys instead of
iterating and cloning it by replacing public_keys.iter().map(|k|
k.clone().into()) with an into-iterator that converts each element (e.g.,
public_keys.into_iter().map(Into::into)), since public_keys is not used after
this point.

In `@packages/wasm-dpp2/tests/unit/AddressCreditWithdrawalTransition.spec.ts`:
- Around line 78-97: Add a new describe block testing toHex()/fromHex()
round-trip for AddressCreditWithdrawalTransition: call createTransition(), get
hex via transition.toHex(), assert Buffer.from(hex, 'hex') equals
Buffer.from(transition.toBytes()), then restore via
wasm.AddressCreditWithdrawalTransition.fromHex(hex) and assert
Buffer.from(restored.toBytes()) equals the original bytes; place the test
alongside the existing toBytes/toBase64 blocks and use the same createTransition
helper and wasm.AddressCreditWithdrawalTransition methods.

In `@packages/wasm-dpp2/tests/unit/AddressFundingFromAssetLockTransition.spec.ts`:
- Around line 164-172: The test currently only checks the outputs array length;
after setting transition.outputs to [newOutput] also assert the amount on the
stored output matches the intended BigInt(50000). Locate the test using
createTransition(), new wasm.PlatformAddressOutput(newAddr, BigInt(50000)) and
transition.outputs, then add an assertion that the retrieved outputs[0] (or
outputs.at(0)) has an amount equal to BigInt(50000) to validate the setter
preserved the value.

In `@packages/wasm-dpp2/tests/unit/AddressFundsTransfer.spec.ts`:
- Around line 42-45: The test filename uses PascalCase
(AddressFundsTransfer.spec.ts) but should follow the repo's kebab-case rule;
rename the file to address-funds-transfer.spec.ts and update any imports or test
references that point to AddressFundsTransfer.spec.ts (and do the same for other
new test files in this PR) so test runner and imports resolve correctly; verify
references in test suites or any index files that mention
AddressFundsTransferTransition or the skipped
describe('AddressFundsTransferTransition (.build API)') remain valid after the
rename.

In `@packages/wasm-dpp2/tests/unit/IdentityCreateFromAddressesTransition.spec.ts`:
- Around line 81-100: Add missing hex round-trip tests for
IdentityCreateFromAddressesTransition: add a describe('toHex() / fromHex()')
block mirroring the existing toBase64/toBytes tests, create a transition via
createTransition(), call transition.toHex(), verify Buffer.from(hex, 'hex')
equals Buffer.from(transition.toBytes()), then restore with
wasm.IdentityCreateFromAddressesTransition.fromHex(hex) and assert
Buffer.from(restored.toBytes()) equals the original bytes; reference the
existing createTransition(), transition.toHex(),
wasm.IdentityCreateFromAddressesTransition.fromHex(), transition.toBytes(), and
restored.toBytes() methods when locating where to insert the new test.

In `@packages/wasm-dpp2/tests/unit/IdentityTopUpFromAddressesTransition.spec.ts`:
- Around line 76-95: Add a toHex()/fromHex() round-trip test for
IdentityTopUpFromAddressesTransition similar to other transition specs: create a
transition via createTransition(), call transition.toHex(), assert
Buffer.from(hex, 'hex') equals Buffer.from(transition.toBytes()), then restore
with wasm.IdentityTopUpFromAddressesTransition.fromHex(hex) and assert
Buffer.from(restored.toBytes()) equals the original bytes; place this in a new
describe('toHex() / fromHex()') / it('should round-trip via hex') block
alongside the existing toBytes/toBase64 tests.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb174d1 and 73591df.

📒 Files selected for processing (19)
  • packages/wasm-dpp2/src/lib.rs
  • packages/wasm-dpp2/src/platform_address/address.rs
  • packages/wasm-dpp2/src/platform_address/fee_strategy.rs
  • packages/wasm-dpp2/src/platform_address/input_output.rs
  • packages/wasm-dpp2/src/platform_address/mod.rs
  • packages/wasm-dpp2/src/platform_address/transitions/address_credit_withdrawal_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/address_funding_from_asset_lock_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/address_funds_transfer_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_credit_transfer_to_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_top_up_from_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/mod.rs
  • packages/wasm-dpp2/tests/unit/AddressCreditWithdrawalTransition.spec.ts
  • packages/wasm-dpp2/tests/unit/AddressFundingFromAssetLockTransition.spec.ts
  • packages/wasm-dpp2/tests/unit/AddressFundsTransfer.spec.ts
  • packages/wasm-dpp2/tests/unit/AddressFundsTransferTransition.spec.ts
  • packages/wasm-dpp2/tests/unit/IdentityCreateFromAddressesTransition.spec.ts
  • packages/wasm-dpp2/tests/unit/IdentityCreditTransferToAddresses.spec.ts
  • packages/wasm-dpp2/tests/unit/IdentityTopUpFromAddressesTransition.spec.ts

Comment on lines +179 to +206
#[wasm_bindgen(getter = "output")]
pub fn output(&self) -> Option<PlatformAddressOutputWasm> {
let output = match &self.0 {
AddressCreditWithdrawalTransition::V0(v0) => &v0.output,
};
output.map(|(address, credits)| PlatformAddressOutputWasm::new(address, credits))
}

#[wasm_bindgen(setter = "output")]
pub fn set_output(&mut self, output: JsValue) -> WasmDppResult<()> {
let new_output = if output.is_undefined() || output.is_null() {
None
} else {
let output_wasm = output
.to_wasm::<PlatformAddressOutputWasm>("PlatformAddressOutput")
.map(|r| (*r).clone())
.map_err(|_| {
WasmDppError::invalid_argument("'output' is not a PlatformAddressOutput")
})?;
Some(output_wasm.into_inner())
};
match &mut self.0 {
AddressCreditWithdrawalTransition::V0(v0) => {
v0.output = new_output;
}
}
Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

into_inner() will panic in WASM if the output has no amount.

Line 198 calls output_wasm.into_inner() which uses .expect(...) internally. If a user constructs a PlatformAddressOutput without an amount and passes it to the output setter, this will panic rather than returning a graceful error to JavaScript.

Consider using a fallible conversion (e.g., a try_into_inner that returns WasmDppResult) or validating the amount before calling into_inner.

This same risk applies to outputs_to_btree_map used in AddressFundsTransferTransition and other transitions.

Proposed fix in input_output.rs (root cause)
-    pub fn into_inner(self) -> (PlatformAddress, Credits) {
-        (
-            self.address.into(),
-            self.amount.expect("amount is required for this operation"),
-        )
-    }
+    pub fn into_inner(self) -> (PlatformAddress, Credits) {
+        (
+            self.address.into(),
+            self.amount.expect("amount is required for this operation"),
+        )
+    }
+
+    pub fn try_into_inner(self) -> WasmDppResult<(PlatformAddress, Credits)> {
+        let amount = self.amount.ok_or_else(|| {
+            WasmDppError::invalid_argument("amount is required for this output")
+        })?;
+        Ok((self.address.into(), amount))
+    }

Then callers like outputs_to_btree_map and the output setter can use try_into_inner() to return errors instead of panicking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/wasm-dpp2/src/platform_address/transitions/address_credit_withdrawal_transition.rs`
around lines 179 - 206, The setter AddressCreditWithdrawalTransition::set_output
currently calls PlatformAddressOutputWasm::into_inner(), which can panic if the
PlatformAddressOutput has no amount; replace this with a fallible conversion
(e.g., add and call a try_into_inner() on PlatformAddressOutputWasm that returns
WasmDppResult<PlatformAddressOutput>) or explicitly validate the output_wasm has
an amount before converting and return a WasmDppError (invalid_argument) instead
of panicking. Update callers that use into_inner() (including
outputs_to_btree_map and implementations in AddressFundsTransferTransition and
other transition setters) to use the new try_into_inner() or the
validation+error pattern so all setters return graceful WasmDppResult errors
instead of causing a WASM panic.

@shumkov shumkov changed the title feat(wasm-dpp2): add platform address transition WASM bindings feat(sdk): add platform address transition WASM bindings Mar 4, 2026
Fix module name typo (transsition -> transition) and apply cargo fmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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: 2

🤖 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/wasm-dpp2/src/platform_address/input_output.rs`:
- Around line 231-265: outputs_from_js_options currently accepts
PlatformAddressOutputWasm entries with amount == None which later causes panics
in outputs_to_btree_map()/into_inner(); add a fallible validation step that
returns a WasmDppError when any PlatformAddressOutputWasm has no amount instead
of allowing None to propagate. Implement this by either adding a new function
(e.g., outputs_from_js_options_required) or a boolean parameter to
outputs_from_js_options that, when enabled, iterates the parsed
Vec<PlatformAddressOutputWasm> and returns
Err(WasmDppError::invalid_argument(...)) for items where output.amount is None
(include index and field_name in the message), and then use this fallible path
in required-output flows (funds transfer / credit transfer / identity create
output setter) in place of the current callers that lead to
outputs_to_btree_map()/into_inner() panics.

In
`@packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs`:
- Around line 207-214: The set_output function currently calls
output_wasm.into_inner() which can panic when required fields like amount are
missing; change this to a safe conversion that returns a
WasmDppError::invalid_argument on missing/invalid fields: instead of calling
into_inner() directly, validate or attempt a fallible conversion from
PlatformAddressOutputWasm to the internal PlatformAddressOutput (e.g.
implement/use a try_from/try_into or an into_inner_result method on
PlatformAddressOutputWasm) and map any missing amount or deserialization error
to WasmDppError::invalid_argument; update set_output to use that Result and
return the mapped WasmDppError rather than allowing a panic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e13b3276-55c7-460a-892e-fab469514b16

📥 Commits

Reviewing files that changed from the base of the PR and between 73591df and 87d6c79.

📒 Files selected for processing (8)
  • packages/wasm-dpp2/src/lib.rs
  • packages/wasm-dpp2/src/platform_address/input_output.rs
  • packages/wasm-dpp2/src/platform_address/transitions/address_funding_from_asset_lock_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/address_funds_transfer_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_credit_transfer_to_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_top_up_from_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/mod.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/wasm-dpp2/src/platform_address/transitions/mod.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_top_up_from_addresses_transition.rs

Comment on lines +231 to +265
pub fn outputs_from_js_options(
options: &JsValue,
field_name: &str,
) -> WasmDppResult<Vec<PlatformAddressOutputWasm>> {
let value = js_sys::Reflect::get(options, &JsValue::from_str(field_name)).map_err(|_| {
WasmDppError::invalid_argument(format!("failed to read '{}' from options", field_name))
})?;
if value.is_undefined() || value.is_null() {
return Err(WasmDppError::invalid_argument(format!(
"'{}' is required",
field_name
)));
}
if !js_sys::Array::is_array(&value) {
return Err(WasmDppError::invalid_argument(format!(
"'{}' must be an array",
field_name
)));
}
let array = js_sys::Array::from(&value);
array
.iter()
.enumerate()
.map(|(i, item)| {
item.to_wasm::<PlatformAddressOutputWasm>("PlatformAddressOutput")
.map(|r| (*r).clone())
.map_err(|_| {
WasmDppError::invalid_argument(format!(
"{}[{}] is not a PlatformAddressOutput",
field_name, i
))
})
})
.collect()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid panic paths when required outputs are missing amount.

This helper accepts PlatformAddressOutput entries with amount = None. In required-output transitions, those values later flow into outputs_to_btree_map()/into_inner() and panic instead of returning WasmDppError.

💡 Suggested fix: add a fallible required-output converter
+pub fn outputs_to_btree_map_required(
+    outputs: Vec<PlatformAddressOutputWasm>,
+    field_name: &str,
+) -> WasmDppResult<BTreeMap<PlatformAddress, Credits>> {
+    outputs
+        .into_iter()
+        .enumerate()
+        .map(|(i, output)| {
+            let (address, amount_opt) = output.into_inner_optional();
+            let amount = amount_opt.ok_or_else(|| {
+                WasmDppError::invalid_argument(format!(
+                    "{}[{}].amount is required",
+                    field_name, i
+                ))
+            })?;
+            Ok((address, amount))
+        })
+        .collect()
+}

Then use this fallible path in required-output transitions (funds transfer / credit transfer / identity create output setter).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm-dpp2/src/platform_address/input_output.rs` around lines 231 -
265, outputs_from_js_options currently accepts PlatformAddressOutputWasm entries
with amount == None which later causes panics in
outputs_to_btree_map()/into_inner(); add a fallible validation step that returns
a WasmDppError when any PlatformAddressOutputWasm has no amount instead of
allowing None to propagate. Implement this by either adding a new function
(e.g., outputs_from_js_options_required) or a boolean parameter to
outputs_from_js_options that, when enabled, iterates the parsed
Vec<PlatformAddressOutputWasm> and returns
Err(WasmDppError::invalid_argument(...)) for items where output.amount is None
(include index and field_name in the message), and then use this fallible path
in required-output flows (funds transfer / credit transfer / identity create
output setter) in place of the current callers that lead to
outputs_to_btree_map()/into_inner() panics.

shumkov and others added 2 commits March 4, 2026 22:40
…ss setters

Replace JsValue with Option<PlatformAddressOutputWasm> for set_output
and CoreScriptWasm for set_output_script to generate proper TypeScript types.

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

Use js_sys::BigInt for u64 setters (balance, revision, nonce, amount,
price, supply) and js_sys::Number for u32/u16 setters (version,
userFeeIncrease, tokenContractPosition) to generate proper TypeScript
types (bigint/number) instead of any.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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

♻️ Duplicate comments (1)
packages/wasm-dpp2/src/platform_address/transitions/address_credit_withdrawal_transition.rs (1)

100-101: ⚠️ Potential issue | 🔴 Critical

Prevent WASM panic by using fallible output conversion.

Line 100 and Line 188 call into_inner(), which can panic when output.amount is missing. This should return a WasmDppError::invalid_argument instead of trapping at runtime.

Suggested fix
-        let output = output.map(|o| o.into_inner());
+        let output = output
+            .map(|o| o.try_into_inner())
+            .transpose()?;

@@
-    pub fn set_output(&mut self, output: Option<PlatformAddressOutputWasm>) {
-        let new_output = output.map(|o| o.into_inner());
+    pub fn set_output(&mut self, output: Option<PlatformAddressOutputWasm>) -> WasmDppResult<()> {
+        let new_output = output
+            .map(|o| o.try_into_inner())
+            .transpose()?;
         match &mut self.0 {
             AddressCreditWithdrawalTransition::V0(v0) => {
                 v0.output = new_output;
             }
         }
+        Ok(())
     }

Also applies to: 187-189

🤖 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/wasm-dpp2/src/platform_address/transitions/identity_top_up_from_addresses_transition.rs`:
- Around line 210-217: Change the setter signature for set_user_fee_increase to
accept a primitive numeric type instead of &js_sys::Number (e.g., amount: u32)
and perform explicit bounds checking before assigning to the inner field; locate
IdentityTopUpFromAddressesTransition::V0 handling in set_user_fee_increase,
replace the try_to_u16(amount, "userFeeIncrease") call by validating that amount
<= u16::MAX (or use u16::try_from(amount) with map_err) and then set
v0.user_fee_increase = amount_as_u16, so JavaScript callers can pass plain
number primitives and the value is safely converted/validated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 64e2a8c9-2704-4856-abe4-5272c8c852e5

📥 Commits

Reviewing files that changed from the base of the PR and between 87d6c79 and bf0120c.

📒 Files selected for processing (3)
  • packages/wasm-dpp2/src/platform_address/transitions/address_credit_withdrawal_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs
  • packages/wasm-dpp2/src/platform_address/transitions/identity_top_up_from_addresses_transition.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/wasm-dpp2/src/platform_address/transitions/identity_create_from_addresses_transition.rs

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.

1 participant