Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
73591df
feat(wasm-dpp2): add platform address transition WASM bindings
shumkov Feb 23, 2026
87d6c79
fix(wasm-dpp2): fix formatting and typo in platform address transitions
shumkov Mar 4, 2026
bf0120c
fix(wasm-dpp2): use typed params instead of JsValue in platform addre…
shumkov Mar 4, 2026
e39645d
refactor(wasm-dpp2): replace JsValue with typed params in setters acr…
shumkov Mar 4, 2026
22bb6a1
Merge branch 'v3.1-dev' into feat/sdk/platform-address-transitions
shumkov Mar 4, 2026
88e573c
Merge branch 'v3.1-dev' into feat/sdk/platform-address-transitions
shumkov Mar 10, 2026
ce9a6ab
fix(sdk): replace panicking into_inner() with fallible try_into_inner()
shumkov Mar 10, 2026
4b7646f
fix(sdk): fix remaining into_inner() calls and missing trait import
shumkov Mar 10, 2026
97706f4
style(sdk): collapse set_output signatures to single line per rustfmt
shumkov Mar 10, 2026
6654d11
fix(wasm-sdk): propagate fallible outputs_to_btree_map result
shumkov Mar 10, 2026
f3088f6
style(wasm-sdk): apply rustfmt to addresses.rs
shumkov Mar 10, 2026
c76f25d
refactor(wasm-dpp2): simplify inputs/outputs_from_js_options using tr…
shumkov Mar 10, 2026
d2a6aa9
refactor(wasm-dpp2): simplify optional_output_from_js_options using t…
shumkov Mar 10, 2026
c2cc43f
refactor(wasm-dpp2): use try_from_options_optional directly for optio…
shumkov Mar 10, 2026
801f951
refactor(wasm-dpp2): implement TryFrom<&JsValue> for PlatformAddressO…
shumkov Mar 10, 2026
5d7f6f8
Merge branch 'v3.1-dev' into feat/sdk/platform-address-transitions
shumkov Mar 10, 2026
7b1f516
fix(wasm-dpp2): fix ESLint errors in transition spec files
shumkov Mar 11, 2026
7cf9cc9
fix(wasm-dpp2): use object destructuring in address transition tests
shumkov Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/wasm-dpp2/src/data_contract/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,8 @@ impl DataContractWasm {
}

#[wasm_bindgen(setter = "version")]
pub fn set_version(&mut self, version: JsValue) -> WasmDppResult<()> {
self.0.set_version(try_to_u32(&version, "version")?);
pub fn set_version(&mut self, version: &js_sys::Number) -> WasmDppResult<()> {
self.0.set_version(try_to_u32(version, "version")?);
Ok(())
}

Expand Down
9 changes: 4 additions & 5 deletions packages/wasm-dpp2/src/identity/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use dpp::platform_value::string_encoding::{decode, encode};
use dpp::prelude::Identifier;
use dpp::serialization::{PlatformDeserializable, PlatformSerializable, ValueConvertible};
use dpp::version::{PlatformVersion, TryFromPlatformVersioned};
use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::wasm_bindgen;

#[wasm_bindgen(typescript_custom_section)]
Expand Down Expand Up @@ -83,14 +82,14 @@ impl IdentityWasm {
}

#[wasm_bindgen(setter = "balance")]
pub fn set_balance(&mut self, balance: JsValue) -> WasmDppResult<()> {
self.0.set_balance(try_to_u64(&balance, "balance")?);
pub fn set_balance(&mut self, balance: &js_sys::BigInt) -> WasmDppResult<()> {
self.0.set_balance(try_to_u64(balance, "balance")?);
Ok(())
}

#[wasm_bindgen(setter = "revision")]
pub fn set_revision(&mut self, revision: JsValue) -> WasmDppResult<()> {
self.0.set_revision(try_to_u64(&revision, "revision")?);
pub fn set_revision(&mut self, revision: &js_sys::BigInt) -> WasmDppResult<()> {
self.0.set_revision(try_to_u64(revision, "revision")?);
Ok(())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use dpp::state_transition::{
StateTransition, StateTransitionHasUserFeeIncrease, StateTransitionSingleSigned,
};
use serde::Deserialize;
use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::wasm_bindgen;

#[derive(Deserialize)]
Expand Down Expand Up @@ -209,9 +208,9 @@ impl IdentityCreateTransitionWasm {
}

#[wasm_bindgen(setter = "userFeeIncrease")]
pub fn set_user_fee_increase(&mut self, amount: JsValue) -> WasmDppResult<()> {
pub fn set_user_fee_increase(&mut self, amount: &js_sys::Number) -> WasmDppResult<()> {
self.0
.set_user_fee_increase(try_to_u16(&amount, "userFeeIncrease")?);
.set_user_fee_increase(try_to_u16(amount, "userFeeIncrease")?);
Ok(())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ impl MasternodeVoteTransitionWasm {
}

#[wasm_bindgen(setter = nonce)]
pub fn set_nonce(&mut self, nonce: JsValue) -> WasmDppResult<()> {
let nonce = try_to_u64(&nonce, "nonce")?;
pub fn set_nonce(&mut self, nonce: &js_sys::BigInt) -> WasmDppResult<()> {
let nonce = try_to_u64(nonce, "nonce")?;
self.0 = match self.0.clone() {
MasternodeVoteTransition::V0(mut vote) => {
vote.nonce = nonce;
Expand Down Expand Up @@ -253,7 +253,7 @@ impl MasternodeVoteTransitionWasm {
}

#[wasm_bindgen(setter = "userFeeIncrease")]
pub fn set_user_fee_increase(&mut self, _amount: JsValue) -> WasmDppResult<()> {
pub fn set_user_fee_increase(&mut self, _amount: &js_sys::Number) -> WasmDppResult<()> {
// MasternodeVoteTransition no longer supports user fee increase; no-op.
Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use dpp::state_transition::{
StateTransitionLike, StateTransitionSingleSigned,
};
use serde::Deserialize;
use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::wasm_bindgen;

#[wasm_bindgen(typescript_custom_section)]
Expand Down Expand Up @@ -200,14 +199,14 @@ impl IdentityUpdateTransitionWasm {
}

#[wasm_bindgen(setter = "revision")]
pub fn set_revision(&mut self, revision: JsValue) -> WasmDppResult<()> {
self.0.set_revision(try_to_u64(&revision, "revision")?);
pub fn set_revision(&mut self, revision: &js_sys::BigInt) -> WasmDppResult<()> {
self.0.set_revision(try_to_u64(revision, "revision")?);
Ok(())
}

#[wasm_bindgen(setter = "nonce")]
pub fn set_nonce(&mut self, nonce: JsValue) -> WasmDppResult<()> {
self.0.set_nonce(try_to_u64(&nonce, "nonce")?);
pub fn set_nonce(&mut self, nonce: &js_sys::BigInt) -> WasmDppResult<()> {
self.0.set_nonce(try_to_u64(nonce, "nonce")?);
Ok(())
}

Expand Down
5 changes: 5 additions & 0 deletions packages/wasm-dpp2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ pub use identity::{
IdentityUpdateTransitionWasm, IdentityWasm, MasternodeVoteTransitionWasm, PartialIdentityWasm,
PublicKeyHashLikeJs, public_key_hash_from_js,
};
pub use platform_address::transitions::{
AddressCreditWithdrawalTransitionWasm, AddressFundingFromAssetLockTransitionWasm,
AddressFundsTransferTransitionWasm, IdentityCreateFromAddressesTransitionWasm,
IdentityCreditTransferToAddressesTransitionWasm, IdentityTopUpFromAddressesTransitionWasm,
};
pub use platform_address::{
FeeStrategyStepWasm, PlatformAddressInputWasm, PlatformAddressLikeArrayJs,
PlatformAddressLikeJs, PlatformAddressOutputWasm, PlatformAddressSignerWasm,
Expand Down
13 changes: 12 additions & 1 deletion packages/wasm-dpp2/src/platform_address/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use crate::utils::IntoWasm;
use dpp::address_funds::PlatformAddress;
use dpp::dashcore::Network;
use js_sys::Uint8Array;
use serde::de::{self, Error, Visitor};
use serde::de::{self, Error, MapAccess, Visitor};
use serde::ser::Serializer;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value as JsonValue;
use std::fmt;
use wasm_bindgen::prelude::*;

Expand Down Expand Up @@ -197,6 +198,16 @@ impl<'de> Visitor<'de> for PlatformAddressWasmVisitor {
.map(PlatformAddressWasm)
.map_err(|e| A::Error::custom(e.to_string()))
}

fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let value = JsonValue::deserialize(de::value::MapAccessDeserializer::new(map))
.map_err(M::Error::custom)?;
let js_value = serde_wasm_bindgen::to_value(&value).map_err(M::Error::custom)?;
PlatformAddressWasm::try_from(&js_value).map_err(|err| M::Error::custom(err.to_string()))
}
}

impl<'de> Deserialize<'de> for PlatformAddressWasm {
Expand Down
31 changes: 31 additions & 0 deletions packages/wasm-dpp2/src/platform_address/fee_strategy.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use crate::error::{WasmDppError, WasmDppResult};
use crate::impl_wasm_type_info;
use crate::utils::{IntoWasm, try_from_options_optional_with, try_to_array};
use dpp::address_funds::{AddressFundsFeeStrategy, AddressFundsFeeStrategyStep};
use serde::Deserialize;
use serde::de::{self, Deserializer, MapAccess, Visitor};
Expand Down Expand Up @@ -56,6 +59,8 @@ impl FeeStrategyStepWasm {
}
}

impl_wasm_type_info!(FeeStrategyStepWasm, FeeStrategyStep);

impl From<FeeStrategyStepWasm> for AddressFundsFeeStrategyStep {
fn from(step: FeeStrategyStepWasm) -> Self {
step.0
Expand Down Expand Up @@ -87,6 +92,32 @@ pub fn fee_strategy_from_steps_or_default(
.unwrap_or_else(default_fee_strategy)
}

/// Extract an optional Vec<FeeStrategyStepWasm> from a JS options object property.
///
/// Returns None if the property is undefined or null.
pub fn fee_strategy_from_js_options(
options: &JsValue,
field_name: &str,
) -> WasmDppResult<Option<Vec<FeeStrategyStepWasm>>> {
try_from_options_optional_with(options, field_name, |v| {
let array = try_to_array(v, field_name)?;
array
.iter()
.enumerate()
.map(|(i, item)| {
item.to_wasm::<FeeStrategyStepWasm>("FeeStrategyStep")
.map(|r| (*r).clone())
.map_err(|_| {
WasmDppError::invalid_argument(format!(
"{}[{}] is not a FeeStrategyStep",
field_name, i
))
})
})
.collect()
})
}

impl<'de> Deserialize<'de> for FeeStrategyStepWasm {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down
101 changes: 90 additions & 11 deletions packages/wasm-dpp2/src/platform_address/input_output.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::{PlatformAddressLikeJs, PlatformAddressWasm};
use crate::error::WasmDppResult;
use crate::utils::try_to_u64;
use crate::error::{WasmDppError, WasmDppResult};
use crate::impl_try_from_js_value;
use crate::impl_wasm_type_info;
use crate::utils::{IntoWasm, try_from_options_with, try_to_array, try_to_u64};
use dpp::address_funds::PlatformAddress;
use dpp::fee::Credits;
use dpp::prelude::AddressNonce;
Expand All @@ -22,6 +24,8 @@ pub struct PlatformAddressInputWasm {
amount: Credits,
}

impl_wasm_type_info!(PlatformAddressInputWasm, PlatformAddressInput);

#[wasm_bindgen(js_class = PlatformAddressInput)]
impl PlatformAddressInputWasm {
/// Creates a new PlatformAddressInput.
Expand Down Expand Up @@ -65,6 +69,14 @@ impl PlatformAddressInputWasm {
}

impl PlatformAddressInputWasm {
pub fn new(address: PlatformAddress, nonce: AddressNonce, amount: Credits) -> Self {
Self {
address: address.into(),
nonce,
amount,
}
}

/// Returns the inner values as a tuple suitable for BTreeMap insertion.
pub fn into_inner(self) -> (PlatformAddress, (AddressNonce, Credits)) {
(self.address.into(), (self.nonce, self.amount))
Expand All @@ -86,6 +98,9 @@ pub struct PlatformAddressOutputWasm {
amount: Option<Credits>,
}

impl_wasm_type_info!(PlatformAddressOutputWasm, PlatformAddressOutput);
impl_try_from_js_value!(PlatformAddressOutputWasm, "PlatformAddressOutput");

#[wasm_bindgen(js_class = PlatformAddressOutput)]
impl PlatformAddressOutputWasm {
/// Creates a new PlatformAddressOutput with a specific amount.
Expand Down Expand Up @@ -122,13 +137,26 @@ impl PlatformAddressOutputWasm {
}

impl PlatformAddressOutputWasm {
/// Returns the inner values as a tuple suitable for BTreeMap insertion.
/// Panics if amount is None - use `into_inner_optional` for optional amounts.
pub fn into_inner(self) -> (PlatformAddress, Credits) {
(
self.address.into(),
self.amount.expect("amount is required for this operation"),
)
pub fn new(address: PlatformAddress, amount: Credits) -> Self {
Self {
address: address.into(),
amount: Some(amount),
}
}

pub fn new_optional(address: PlatformAddress, amount: Option<Credits>) -> Self {
Self {
address: address.into(),
amount,
}
}

/// Returns the inner values as a tuple, or an error if amount is None.
pub fn try_into_inner(self) -> WasmDppResult<(PlatformAddress, Credits)> {
let amount = self.amount.ok_or_else(|| {
WasmDppError::invalid_argument("PlatformAddressOutput: amount is required")
})?;
Ok((self.address.into(), amount))
}

/// Returns the inner values with optional amount.
Expand All @@ -138,10 +166,11 @@ impl PlatformAddressOutputWasm {
}

/// Converts a vector of PlatformAddressOutput into a BTreeMap.
/// Returns an error if any output has no amount set.
pub fn outputs_to_btree_map(
outputs: Vec<PlatformAddressOutputWasm>,
) -> BTreeMap<PlatformAddress, Credits> {
outputs.into_iter().map(|o| o.into_inner()).collect()
) -> WasmDppResult<BTreeMap<PlatformAddress, Credits>> {
outputs.into_iter().map(|o| o.try_into_inner()).collect()
}

/// Converts a vector of PlatformAddressOutput into a BTreeMap with optional amounts.
Expand All @@ -156,3 +185,53 @@ pub fn outputs_to_optional_btree_map(
.map(|o| o.into_inner_optional())
.collect()
}

/// Extract a Vec<PlatformAddressInputWasm> from a JS options object property.
///
/// Reads the named property as a JS array, then extracts each element
/// as a PlatformAddressInput wasm-bindgen object via its __wbg_ptr.
pub fn inputs_from_js_options(
options: &JsValue,
field_name: &str,
) -> WasmDppResult<Vec<PlatformAddressInputWasm>> {
let array = try_from_options_with(options, field_name, |v| try_to_array(v, field_name))?;
array
.iter()
.enumerate()
.map(|(i, item)| {
item.to_wasm::<PlatformAddressInputWasm>("PlatformAddressInput")
.map(|r| (*r).clone())
.map_err(|_| {
WasmDppError::invalid_argument(format!(
"{}[{}] is not a PlatformAddressInput",
field_name, i
))
})
})
.collect()
}

/// Extract a Vec<PlatformAddressOutputWasm> from a JS options object property.
///
/// Reads the named property as a JS array, then extracts each element
/// as a PlatformAddressOutput wasm-bindgen object via its __wbg_ptr.
pub fn outputs_from_js_options(
options: &JsValue,
field_name: &str,
) -> WasmDppResult<Vec<PlatformAddressOutputWasm>> {
let array = try_from_options_with(options, field_name, |v| try_to_array(v, field_name))?;
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()
}
9 changes: 5 additions & 4 deletions packages/wasm-dpp2/src/platform_address/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ mod address;
mod fee_strategy;
mod input_output;
mod signer;
pub mod transitions;

pub use address::{PlatformAddressLikeArrayJs, PlatformAddressLikeJs, PlatformAddressWasm};
pub use fee_strategy::{
FeeStrategyStepWasm, default_fee_strategy, fee_strategy_from_steps,
fee_strategy_from_steps_or_default,
FeeStrategyStepWasm, default_fee_strategy, fee_strategy_from_js_options,
fee_strategy_from_steps, fee_strategy_from_steps_or_default,
};
pub use input_output::{
PlatformAddressInputWasm, PlatformAddressOutputWasm, outputs_to_btree_map,
outputs_to_optional_btree_map,
PlatformAddressInputWasm, PlatformAddressOutputWasm, inputs_from_js_options,
outputs_from_js_options, outputs_to_btree_map, outputs_to_optional_btree_map,
};
pub use signer::PlatformAddressSignerWasm;
Loading
Loading