diff --git a/packages/js-evo-sdk/src/identities/facade.ts b/packages/js-evo-sdk/src/identities/facade.ts index f98521b4019..19c5cc39fed 100644 --- a/packages/js-evo-sdk/src/identities/facade.ts +++ b/packages/js-evo-sdk/src/identities/facade.ts @@ -23,12 +23,12 @@ export class IdentitiesFacade { return w.getIdentityUnproved(identityId); } - async getKeys(query: wasm.IdentityKeysQuery): Promise { + async getKeys(query: wasm.IdentityKeysQuery): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getIdentityKeys(query); } - async getKeysWithProof(query: wasm.IdentityKeysQuery): Promise> { + async getKeysWithProof(query: wasm.IdentityKeysQuery): Promise> { const w = await this.sdk.getWasmSdkConnected(); return w.getIdentityKeysWithProofInfo(query); } diff --git a/packages/wasm-dpp2/src/core/network.rs b/packages/wasm-dpp2/src/core/network.rs index 1cca50018a5..34d0d9894e9 100644 --- a/packages/wasm-dpp2/src/core/network.rs +++ b/packages/wasm-dpp2/src/core/network.rs @@ -7,11 +7,12 @@ use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen(typescript_custom_section)] const NETWORK_LIKE_TS: &'static str = r#" /** - * Flexible network type that accepts Network enum or string names. + * Flexible network type that accepts Network enum, string names, or numeric values. * * String values (case-insensitive): "mainnet", "testnet", "devnet", "regtest" + * Numeric values: 0 (mainnet), 1 (testnet), 2 (devnet), 3 (regtest) */ -export type NetworkLike = Network | "mainnet" | "testnet" | "devnet" | "regtest"; +export type NetworkLike = Network | "mainnet" | "testnet" | "devnet" | "regtest" | 0 | 1 | 2 | 3; "#; #[wasm_bindgen(js_name = "Network")] @@ -27,6 +28,7 @@ pub enum NetworkWasm { impl TryFrom for NetworkWasm { type Error = WasmDppError; fn try_from(value: JsValue) -> Result { + // Handle string input if let Some(enum_val) = value.as_string() { return match enum_val.to_lowercase().as_str() { "mainnet" => Ok(NetworkWasm::Mainnet), @@ -40,8 +42,30 @@ impl TryFrom for NetworkWasm { }; } + // Handle numeric enum value (Network.Mainnet = 0, Testnet = 1, etc.) + if let Some(num) = value.as_f64() { + // Validate that the number is a non-negative integer within u32 range + if num.fract() != 0.0 || num < 0.0 || num > u32::MAX as f64 { + return Err(WasmDppError::invalid_argument(format!( + "network value must be a non-negative integer, got '{}'", + num + ))); + } + + return match num as u32 { + 0 => Ok(NetworkWasm::Mainnet), + 1 => Ok(NetworkWasm::Testnet), + 2 => Ok(NetworkWasm::Devnet), + 3 => Ok(NetworkWasm::Regtest), + _ => Err(WasmDppError::invalid_argument(format!( + "unsupported network value '{}'. Expected: 0 (mainnet), 1 (testnet), 2 (devnet), or 3 (regtest)", + num + ))), + }; + } + Err(WasmDppError::invalid_argument( - "network must be a string: 'mainnet', 'testnet', 'devnet', or 'regtest'", + "network must be a string ('mainnet', 'testnet', 'devnet', 'regtest') or Network enum value", )) } } diff --git a/packages/wasm-dpp2/src/platform_address/address.rs b/packages/wasm-dpp2/src/platform_address/address.rs index 419eabccdce..1195cc330b5 100644 --- a/packages/wasm-dpp2/src/platform_address/address.rs +++ b/packages/wasm-dpp2/src/platform_address/address.rs @@ -5,7 +5,8 @@ use dpp::address_funds::PlatformAddress; use dpp::dashcore::Network; use js_sys::Uint8Array; use serde::de::{self, Error, Visitor}; -use serde::{Deserialize, Deserializer}; +use serde::ser::Serializer; +use serde::{Deserialize, Deserializer, Serialize}; use std::fmt; use wasm_bindgen::prelude::*; @@ -103,9 +104,20 @@ impl TryFrom<&str> for PlatformAddressWasm { type Error = WasmDppError; fn try_from(value: &str) -> Result { - // Try parsing as bech32m string - PlatformAddress::from_bech32m_string(value) - .map(|(addr, _network)| PlatformAddressWasm(addr)) + // Try parsing as bech32m string first (e.g., "dashevo1..." or "tdashevo1...") + if let Ok((addr, _network)) = PlatformAddress::from_bech32m_string(value) { + return Ok(PlatformAddressWasm(addr)); + } + + // Fall back to hex decoding for compatibility with serialized format + let bytes = hex::decode(value).map_err(|e| { + WasmDppError::invalid_argument(format!( + "Invalid PlatformAddress: not valid bech32m or hex: {}", + e + )) + })?; + PlatformAddress::from_bytes(&bytes) + .map(PlatformAddressWasm) .map_err(|e| WasmDppError::invalid_argument(e.to_string())) } } @@ -165,6 +177,21 @@ impl<'de> Deserialize<'de> for PlatformAddressWasm { } } +impl Serialize for PlatformAddressWasm { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + // JSON, TOML, etc. - use hex string + serializer.serialize_str(&hex::encode(self.0.to_bytes())) + } else { + // Binary formats (bincode, MessagePack, etc.) - use raw bytes + serializer.serialize_bytes(&self.0.to_bytes()) + } + } +} + #[wasm_bindgen(js_class = PlatformAddress)] impl PlatformAddressWasm { #[wasm_bindgen(getter = __type)] diff --git a/packages/wasm-dpp2/src/serialization/conversions.rs b/packages/wasm-dpp2/src/serialization/conversions.rs index 180af2d7fcf..a106b2f6cb7 100644 --- a/packages/wasm-dpp2/src/serialization/conversions.rs +++ b/packages/wasm-dpp2/src/serialization/conversions.rs @@ -29,6 +29,25 @@ use serde_json::Value as JsonValue; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; +/// Try to call toJSON() on a WASM object if it has one. +/// +/// Returns Some(result) if the object has a toJSON method and it succeeds, +/// None otherwise (for plain objects, arrays, primitives, etc.) +fn try_call_to_json(value: &JsValue) -> Option { + if !value.is_object() || value.is_null() || js_sys::Array::is_array(value) { + return None; + } + + // Check for toJSON method + let to_json_fn = js_sys::Reflect::get(value, &JsValue::from_str("toJSON")).ok()?; + if !to_json_fn.is_function() { + return None; + } + + let func: js_sys::Function = to_json_fn.unchecked_into(); + func.call0(value).ok() +} + /// Convert JsValue to serde_json::Value, handling BigInt values and WASM objects. /// /// This function: @@ -37,18 +56,9 @@ use wasm_bindgen::prelude::*; /// - Falls back to serde_wasm_bindgen conversion for plain objects pub fn js_value_to_json(value: &JsValue) -> WasmDppResult { // Check if the value has a toJSON method (WASM objects like DataContractWasm, IdentityWasm) - if value.is_object() - && !value.is_null() - && !js_sys::Array::is_array(value) - && let Ok(to_json_fn) = js_sys::Reflect::get(value, &JsValue::from_str("toJSON")) - && to_json_fn.is_function() - { - let func: js_sys::Function = to_json_fn.unchecked_into(); - // Call toJSON() on the object - if let Ok(json_result) = func.call0(value) { - // Recursively convert the result (it might contain BigInt or nested WASM objects) - return js_value_to_json(&json_result); - } + if let Some(json_result) = try_call_to_json(value) { + // Recursively convert the result (it might contain BigInt or nested WASM objects) + return js_value_to_json(&json_result); } let normalized = normalize_js_value_for_json(value)?; @@ -70,12 +80,15 @@ pub fn json_to_js_value(value: &JsonValue) -> WasmDppResult { /// Recursively normalizes a JsValue for JSON conversion. /// /// This converts: +/// - WASM objects with toJSON() method - calls toJSON() and normalizes the result /// - BigInt values to strings (JSON doesn't support BigInt natively) /// - Uint8Array to plain arrays (so they serialize as JSON number arrays) +/// - JavaScript Map to plain objects +/// - Recursively processes nested objects and arrays /// /// Performance: Uses fast path for primitives, only recursively processes objects/arrays. fn normalize_js_value_for_json(value: &JsValue) -> WasmDppResult { - // Fast path: primitives that can't contain BigInt + // Fast path: primitives that can't contain BigInt or need conversion if value.is_string() || value.as_f64().is_some() || value.is_null() @@ -85,6 +98,7 @@ fn normalize_js_value_for_json(value: &JsValue) -> WasmDppResult { return Ok(value.clone()); } + // Convert BigInt to string (JSON doesn't support BigInt) if value.is_bigint() { let bigint: js_sys::BigInt = value.clone().unchecked_into(); let bigint_str = bigint @@ -101,6 +115,13 @@ fn normalize_js_value_for_json(value: &JsValue) -> WasmDppResult { return Ok(plain_array.into()); } + // Convert JavaScript Map to an object for JSON compatibility + // Maps don't have enumerable properties, so Object.keys() returns empty array + if value.is_instance_of::() { + return normalize_map_for_json(value); + } + + // Handle arrays - recursively normalize each element if js_sys::Array::is_array(value) { let arr = js_sys::Array::from(value); let new_arr = js_sys::Array::new(); @@ -112,7 +133,15 @@ fn normalize_js_value_for_json(value: &JsValue) -> WasmDppResult { return Ok(new_arr.into()); } + // Handle objects - check for toJSON method first (WASM objects), then normalize properties if value.is_object() && !value.is_null() { + // Try to call toJSON() on WASM objects (Identity, Identifier, DataContract, etc.) + if let Some(json_result) = try_call_to_json(value) { + // Recursively normalize the result (might contain BigInt, nested objects, etc.) + return normalize_js_value_for_json(&json_result); + } + + // Plain object - normalize each property let obj = Object::from(value.clone()); let new_obj = Object::new(); let keys = Object::keys(&obj); @@ -135,6 +164,88 @@ fn normalize_js_value_for_json(value: &JsValue) -> WasmDppResult { Ok(value.clone()) } +/// Convert a JavaScript Map key to a string for JSON object keys. +fn map_key_to_string(key: &JsValue) -> String { + // String keys - use as-is + if let Some(s) = key.as_string() { + return s; + } + + // Number keys - convert to string + if key.as_f64().is_some() { + return js_sys::Number::from(key.clone()) + .to_string(10) + .map(|s| s.into()) + .unwrap_or_else(|_| "0".to_string()); + } + + // BigInt keys - convert to string + if key.is_bigint() { + let bigint: js_sys::BigInt = key.clone().unchecked_into(); + return bigint + .to_string(10) + .map(|s| s.into()) + .unwrap_or_else(|_| "0".to_string()); + } + + // Objects with toString (like Identifier) - call toString() + if let Ok(to_string_fn) = js_sys::Reflect::get(key, &JsValue::from_str("toString")) + && to_string_fn.is_function() + { + let func: js_sys::Function = to_string_fn.unchecked_into(); + if let Ok(str_result) = func.call0(key) + && let Some(s) = str_result.as_string() + { + return s; + } + } + + // Fallback - use debug representation + format!("{:?}", key) +} + +/// Convert a JavaScript Map to a plain object for JSON serialization. +fn normalize_map_for_json(value: &JsValue) -> WasmDppResult { + let map: js_sys::Map = value.clone().unchecked_into(); + let new_obj = Object::new(); + + // We need to collect errors from the closure since for_each doesn't support Result + let error: std::cell::RefCell> = std::cell::RefCell::new(None); + + map.for_each(&mut |val, key| { + // Skip if we already have an error + if error.borrow().is_some() { + return; + } + + let key_str = map_key_to_string(&key); + + // Normalize the value - handle WASM objects, BigInt, nested Maps, etc. + match normalize_js_value_for_json(&val) { + Ok(normalized_val) => { + if let Err(e) = + js_sys::Reflect::set(&new_obj, &JsValue::from_str(&key_str), &normalized_val) + { + *error.borrow_mut() = Some(WasmDppError::serialization(format!( + "Failed to set Map entry '{}': {:?}", + key_str, e + ))); + } + } + Err(e) => { + *error.borrow_mut() = Some(e); + } + } + }); + + // Check if any error occurred during iteration + if let Some(e) = error.into_inner() { + return Err(e); + } + + Ok(new_obj.into()) +} + /// Serialize to JsValue as a JS object (non-human-readable). /// /// Uses the serde-wasm-bindgen serializer with `is_human_readable() -> false`, @@ -354,6 +465,24 @@ pub fn js_value_to_platform_value(value: &JsValue) -> WasmDppResult Result { + let json_value = js_value_to_json(value)?; + json_to_js_value(&json_value) +} + /// Macro to implement `toObject`, `fromObject`, `toJSON`, and `fromJSON` methods /// for a wasm_bindgen newtype wrapper using the serialization::conversions module. /// diff --git a/packages/wasm-dpp2/tests/unit/BatchTransition.spec.mjs b/packages/wasm-dpp2/tests/unit/BatchTransition.spec.mjs index 90abab7d8b9..66b2d066a56 100644 --- a/packages/wasm-dpp2/tests/unit/BatchTransition.spec.mjs +++ b/packages/wasm-dpp2/tests/unit/BatchTransition.spec.mjs @@ -46,23 +46,23 @@ describe('BatchTransition', () => { }); it('should allow to convert batch transition to base64 and back', () => { - const documentInstance = new wasm.Document(document, documentTypeName, revision, dataContractId, ownerId, id); - const createTransition = new wasm.DocumentCreateTransition(documentInstance, BigInt(1)); + const documentInstance = new wasm.Document(document, documentTypeName, revision, dataContractId, ownerId, id); + const createTransition = new wasm.DocumentCreateTransition(documentInstance, BigInt(1)); - const documentTransition = createTransition.toDocumentTransition(); + const documentTransition = createTransition.toDocumentTransition(); - const batchedTransition = new wasm.BatchedTransition(documentTransition); + const batchedTransition = new wasm.BatchedTransition(documentTransition); - const batch = wasm.BatchTransition.fromBatchedTransitions([batchedTransition], documentInstance.ownerId, 1); + const batch = wasm.BatchTransition.fromBatchedTransitions([batchedTransition], documentInstance.ownerId, 1); - const base64 = batch.toBase64(); - const bytes = batch.toBytes(); + const base64 = batch.toBase64(); + const bytes = batch.toBytes(); - expect(Buffer.from(base64, 'base64')).to.deep.equal(Buffer.from(bytes)); + expect(Buffer.from(base64, 'base64')).to.deep.equal(Buffer.from(bytes)); - const restoredBatch = wasm.BatchTransition.fromBase64(base64); + const restoredBatch = wasm.BatchTransition.fromBase64(base64); - expect(Buffer.from(restoredBatch.toBytes())).to.deep.equal(Buffer.from(bytes)); + expect(Buffer.from(restoredBatch.toBytes())).to.deep.equal(Buffer.from(bytes)); }); it('should round-trip via object and JSON', () => { @@ -87,8 +87,8 @@ describe('BatchTransition', () => { const fromJson = wasm.BatchTransition.fromJSON(json); expect(Buffer.from(fromJson.toBytes())).to.deep.equal(Buffer.from(batch.toBytes())); }); - }); - describe('tokens', () => { + }); + describe('tokens', () => { it('should allow to create from v1 transition', () => { const baseTransition = new wasm.TokenBaseTransition(BigInt(1), 1, dataContractId, ownerId); diff --git a/packages/wasm-dpp2/tests/unit/DataContract.spec.mjs b/packages/wasm-dpp2/tests/unit/DataContract.spec.mjs index ca7ebcd4423..2f438db7dfc 100644 --- a/packages/wasm-dpp2/tests/unit/DataContract.spec.mjs +++ b/packages/wasm-dpp2/tests/unit/DataContract.spec.mjs @@ -1,5 +1,7 @@ import getWasm from './helpers/wasm.js'; -import { json, object, id, ownerId } from './mocks/DataContract/index.js'; +import { + json, object, id, ownerId +} from './mocks/DataContract/index.js'; import { fromHexString } from './utils/hex.js'; let wasm; diff --git a/packages/wasm-dpp2/tests/unit/Identity.spec.mjs b/packages/wasm-dpp2/tests/unit/Identity.spec.mjs index 247f2849b98..69950494137 100644 --- a/packages/wasm-dpp2/tests/unit/Identity.spec.mjs +++ b/packages/wasm-dpp2/tests/unit/Identity.spec.mjs @@ -57,7 +57,6 @@ describe('Identity', () => { expect(restoredIdentity.id.toBytes()).to.deep.equal(identity.id.toBytes()); expect(restoredIdentity.getPublicKeys().length).to.equal(identity.getPublicKeys().length); }); - }); describe('getters', () => { diff --git a/packages/wasm-dpp2/tests/unit/PlatformAddress.spec.mjs b/packages/wasm-dpp2/tests/unit/PlatformAddress.spec.mjs index 1836364411c..1fe445e05ce 100644 --- a/packages/wasm-dpp2/tests/unit/PlatformAddress.spec.mjs +++ b/packages/wasm-dpp2/tests/unit/PlatformAddress.spec.mjs @@ -86,7 +86,7 @@ describe('PlatformAddress', () => { describe('fromHex', () => { it('should create address from hex string', () => { - const hexString = '00' + '01020304050607080910111213141516171819'.padEnd(40, '0').substring(0, 40); + const hexString = `00${'01020304050607080910111213141516171819'.padEnd(40, '0').substring(0, 40)}`; const addr = wasm.PlatformAddress.fromHex(hexString); expect(addr).to.exist; expect(addr.addressType).to.equal('P2PKH'); diff --git a/packages/wasm-dpp2/tests/unit/PlatformAddressOutput.spec.mjs b/packages/wasm-dpp2/tests/unit/PlatformAddressOutput.spec.mjs index e187813aac0..ca14b2605d5 100644 --- a/packages/wasm-dpp2/tests/unit/PlatformAddressOutput.spec.mjs +++ b/packages/wasm-dpp2/tests/unit/PlatformAddressOutput.spec.mjs @@ -56,12 +56,10 @@ describe('PlatformAddressOutput', () => { const addressBytes = new Uint8Array([0x00, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]); const platformAddr = wasm.PlatformAddress.fromBytes(addressBytes); - try { + expect(() => { + // eslint-disable-next-line no-new new wasm.PlatformAddressOutput(platformAddr, BigInt(-1)); - expect.fail('Should have thrown error for negative amount'); - } catch (error) { - expect(error).to.exist; - } + }).to.throw(); }); }); diff --git a/packages/wasm-dpp2/tests/unit/js-value-to-json.spec.mjs b/packages/wasm-dpp2/tests/unit/js-value-to-json.spec.mjs new file mode 100644 index 00000000000..c4709f5d072 --- /dev/null +++ b/packages/wasm-dpp2/tests/unit/js-value-to-json.spec.mjs @@ -0,0 +1,467 @@ +import { getWasm } from './helpers/wasm.js'; + +/** + * Tests for JavaScript value to JSON conversion via testJsValueToJson. + * + * These tests verify that normalize_js_value_for_json correctly handles + * various JavaScript types for JSON serialization: + * + * - Maps: converted to plain objects (keys become strings) + * - BigInt: converted to strings (JSON doesn't support BigInt) + * - Uint8Array: converted to regular arrays + * - WASM objects: their toJSON() method is called + * - Nested structures: recursively normalized + * - Primitives: passed through unchanged + */ + +describe('JS value to JSON conversion (testJsValueToJson)', () => { + let wasm; + + before(async () => { + wasm = await getWasm(); + }); + + describe('Map with string keys', () => { + it('should serialize empty Map to empty object', () => { + const emptyMap = new Map(); + + const json = wasm.testJsValueToJson(emptyMap); + + expect(json).to.deep.equal({}); + }); + + it('should serialize Map with string keys and primitive values', () => { + const map = new Map([ + ['key1', 'value1'], + ['key2', 42], + ['key3', true], + ['key4', null], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.deep.equal({ + key1: 'value1', + key2: 42, + key3: true, + key4: null, + }); + }); + + it('should serialize Map with string keys and object values', () => { + const map = new Map([ + ['epoch1', { index: 1, startTime: 1000 }], + ['epoch2', { index: 2, startTime: 2000 }], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.deep.equal({ + epoch1: { index: 1, startTime: 1000 }, + epoch2: { index: 2, startTime: 2000 }, + }); + }); + + it('should serialize Map with string keys and array values', () => { + const map = new Map([ + ['items1', [1, 2, 3]], + ['items2', ['a', 'b', 'c']], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.deep.equal({ + items1: [1, 2, 3], + items2: ['a', 'b', 'c'], + }); + }); + }); + + describe('Map with numeric keys', () => { + it('should serialize Map with number keys (converted to strings)', () => { + // This simulates getEpochsInfo which returns Map + const map = new Map([ + [0, { index: 0, name: 'epoch0' }], + [1, { index: 1, name: 'epoch1' }], + [100, { index: 100, name: 'epoch100' }], + ]); + + const json = wasm.testJsValueToJson(map); + + // Keys should be strings in JSON + expect(json).to.have.property('0'); + expect(json).to.have.property('1'); + expect(json).to.have.property('100'); + expect(json['0']).to.deep.equal({ index: 0, name: 'epoch0' }); + expect(json['1']).to.deep.equal({ index: 1, name: 'epoch1' }); + expect(json['100']).to.deep.equal({ index: 100, name: 'epoch100' }); + }); + + it('should serialize Map with negative number keys', () => { + const map = new Map([ + [-1, 'negative one'], + [0, 'zero'], + [1, 'one'], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.have.property('-1'); + expect(json).to.have.property('0'); + expect(json).to.have.property('1'); + expect(json['-1']).to.equal('negative one'); + }); + + it('should serialize Map with floating point keys', () => { + const map = new Map([ + [1.5, 'one point five'], + [2.7, 'two point seven'], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.have.property('1.5'); + expect(json).to.have.property('2.7'); + }); + }); + + describe('Map with BigInt keys', () => { + it('should serialize Map with BigInt keys (converted to strings)', () => { + const map = new Map([ + [1n, 'one'], + [9007199254740993n, 'large number'], // > MAX_SAFE_INTEGER + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.have.property('1'); + expect(json).to.have.property('9007199254740993'); + expect(json['1']).to.equal('one'); + expect(json['9007199254740993']).to.equal('large number'); + }); + }); + + describe('Map with BigInt values', () => { + it('should serialize Map with BigInt values (converted to strings)', () => { + // This simulates token balances or credit amounts + const map = new Map([ + ['balance1', 1000000000n], + ['balance2', 9007199254740993n], // > MAX_SAFE_INTEGER + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json.balance1).to.equal('1000000000'); + expect(json.balance2).to.equal('9007199254740993'); + }); + }); + + describe('Map with Identifier keys', () => { + it('should serialize Map with Identifier keys using toString()', () => { + const id1 = wasm.Identifier.fromBase58('H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1'); + const id2 = wasm.Identifier.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U'); + + // This simulates getEvonodesProposedEpochBlocksByIds which returns Map + const map = new Map([ + [id1, 100n], + [id2, 200n], + ]); + + const json = wasm.testJsValueToJson(map); + + // Keys should be the string representation of Identifier (Base58) + expect(json).to.have.property('H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1'); + expect(json).to.have.property('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U'); + // Values are BigInt, should be strings + expect(json.H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1).to.equal('100'); + expect(json.ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U).to.equal('200'); + }); + }); + + describe('Map with WASM object values', () => { + it('should serialize Map with Identity values by calling their toJSON()', () => { + const identity = new wasm.Identity('H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1'); + identity.balance = 1000000000n; + identity.revision = 5n; + + const map = new Map([ + ['identity1', identity], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json).to.have.property('identity1'); + expect(json.identity1).to.have.property('id'); + expect(json.identity1.id).to.equal('H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1'); + expect(json.identity1.balance).to.equal(1000000000); + expect(json.identity1.revision).to.equal(5); + }); + + it('should serialize Map with Identifier values', () => { + const id1 = wasm.Identifier.fromBase58('H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1'); + const id2 = wasm.Identifier.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U'); + + const map = new Map([ + ['id1', id1], + ['id2', id2], + ]); + + const json = wasm.testJsValueToJson(map); + + // Identifier.toJSON() returns base58 string + expect(json.id1).to.equal('H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1'); + expect(json.id2).to.equal('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U'); + }); + + it('should serialize Map with undefined/null values', () => { + // This simulates queries that return Map + const map = new Map([ + [0, { epoch: 0 }], + [1, undefined], + [2, null], + [3, { epoch: 3 }], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json['0']).to.deep.equal({ epoch: 0 }); + // Note: In JSON, undefined becomes null (JSON doesn't have undefined) + expect(json['1']).to.satisfy((v) => v === null || v === undefined); + expect(json['2']).to.equal(null); + expect(json['3']).to.deep.equal({ epoch: 3 }); + }); + }); + + describe('Nested Maps', () => { + it('should serialize nested Map as value', () => { + const innerMap = new Map([ + ['a', 1], + ['b', 2], + ]); + const outerMap = new Map([ + ['inner', innerMap], + ]); + + const json = wasm.testJsValueToJson(outerMap); + + expect(json).to.have.property('inner'); + expect(json.inner).to.deep.equal({ a: 1, b: 2 }); + }); + + it('should serialize deeply nested Maps', () => { + const level3 = new Map([['deep', 'value']]); + const level2 = new Map([['level3', level3]]); + const level1 = new Map([['level2', level2]]); + + const json = wasm.testJsValueToJson(level1); + + expect(json.level2.level3.deep).to.equal('value'); + }); + + it('should serialize Map inside array inside Map', () => { + const innerMap = new Map([['key', 'value']]); + const outerMap = new Map([ + ['items', [innerMap, { plain: 'object' }]], + ]); + + const json = wasm.testJsValueToJson(outerMap); + + expect(json.items[0]).to.deep.equal({ key: 'value' }); + expect(json.items[1]).to.deep.equal({ plain: 'object' }); + }); + }); + + describe('Map inside array', () => { + it('should serialize array containing Maps', () => { + const map1 = new Map([['a', 1]]); + const map2 = new Map([['b', 2]]); + const arrayWithMaps = [map1, map2]; + + const json = wasm.testJsValueToJson(arrayWithMaps); + + expect(json).to.be.an('array'); + expect(json[0]).to.deep.equal({ a: 1 }); + expect(json[1]).to.deep.equal({ b: 2 }); + }); + }); + + describe('Map inside object', () => { + it('should serialize object containing Map property', () => { + const map = new Map([['key', 'value']]); + const objectWithMap = { + normalProp: 'normal', + mapProp: map, + }; + + const json = wasm.testJsValueToJson(objectWithMap); + + expect(json.normalProp).to.equal('normal'); + expect(json.mapProp).to.deep.equal({ key: 'value' }); + }); + }); + + describe('Map with Uint8Array values', () => { + it('should serialize Map with Uint8Array values to arrays', () => { + const map = new Map([ + ['bytes1', new Uint8Array([1, 2, 3])], + ['bytes2', new Uint8Array([4, 5, 6])], + ]); + + const json = wasm.testJsValueToJson(map); + + // Uint8Array should be converted to regular arrays for JSON + expect(json.bytes1).to.deep.equal([1, 2, 3]); + expect(json.bytes2).to.deep.equal([4, 5, 6]); + }); + }); + + describe('Real-world epoch query simulation', () => { + it('should serialize epoch info Map like getEpochsInfoWithProofInfo returns', () => { + // Simulating the structure returned by getEpochsInfoWithProofInfo + const epochsMap = new Map([ + [0, { + index: 0, + firstBlockHeight: 1n, + firstCoreBlockHeight: 100, + startTime: 1609459200000n, + feeMultiplier: 1.0, + }], + [1, { + index: 1, + firstBlockHeight: 1000n, + firstCoreBlockHeight: 200, + startTime: 1609545600000n, + feeMultiplier: 1.1, + }], + [2, undefined], // Some epochs might not exist + ]); + + const json = wasm.testJsValueToJson(epochsMap); + + expect(json).to.have.property('0'); + expect(json).to.have.property('1'); + expect(json).to.have.property('2'); + + expect(json['0'].index).to.equal(0); + expect(json['0'].firstBlockHeight).to.equal('1'); // BigInt -> string + expect(json['0'].startTime).to.equal('1609459200000'); // BigInt -> string + + expect(json['1'].index).to.equal(1); + // Note: In JSON, undefined becomes null (JSON doesn't have undefined) + expect(json['2']).to.satisfy((v) => v === null || v === undefined); + }); + }); + + describe('Edge cases', () => { + it('should handle Map with special string keys', () => { + const map = new Map([ + ['', 'empty key'], + ['with spaces', 'spaced'], + ['with.dots', 'dotted'], + ['with/slash', 'slashed'], + ['with\\backslash', 'backslashed'], + ['unicode: \u00e9', 'accented'], + ]); + + const json = wasm.testJsValueToJson(map); + + expect(json['']).to.equal('empty key'); + expect(json['with spaces']).to.equal('spaced'); + expect(json['with.dots']).to.equal('dotted'); + expect(json['with/slash']).to.equal('slashed'); + expect(json['with\\backslash']).to.equal('backslashed'); + expect(json['unicode: \u00e9']).to.equal('accented'); + }); + + it('should preserve key order in Map', () => { + const map = new Map([ + ['z', 1], + ['a', 2], + ['m', 3], + ]); + + const json = wasm.testJsValueToJson(map); + const keys = Object.keys(json); + + // Maps preserve insertion order + expect(keys).to.deep.equal(['z', 'a', 'm']); + }); + + it('should handle large Maps', () => { + const map = new Map(); + for (let i = 0; i < 1000; i += 1) { + map.set(i, { index: i, value: `item${i}` }); + } + + const json = wasm.testJsValueToJson(map); + + expect(Object.keys(json).length).to.equal(1000); + expect(json['0']).to.deep.equal({ index: 0, value: 'item0' }); + expect(json['999']).to.deep.equal({ index: 999, value: 'item999' }); + }); + }); + + describe('Primitive values', () => { + it('should pass through strings', () => { + const result = wasm.testJsValueToJson('hello'); + expect(result).to.equal('hello'); + }); + + it('should pass through numbers', () => { + const result = wasm.testJsValueToJson(42); + expect(result).to.equal(42); + }); + + it('should pass through booleans', () => { + expect(wasm.testJsValueToJson(true)).to.equal(true); + expect(wasm.testJsValueToJson(false)).to.equal(false); + }); + + it('should pass through null', () => { + const result = wasm.testJsValueToJson(null); + expect(result).to.equal(null); + }); + + it('should convert BigInt to string', () => { + const result = wasm.testJsValueToJson(9007199254740993n); + expect(result).to.equal('9007199254740993'); + }); + + it('should convert Uint8Array to array', () => { + const result = wasm.testJsValueToJson(new Uint8Array([1, 2, 3])); + expect(result).to.deep.equal([1, 2, 3]); + }); + }); + + describe('Plain objects and arrays', () => { + it('should pass through plain objects', () => { + const obj = { a: 1, b: 'two', c: true }; + const result = wasm.testJsValueToJson(obj); + expect(result).to.deep.equal(obj); + }); + + it('should pass through arrays', () => { + const arr = [1, 'two', true, null]; + const result = wasm.testJsValueToJson(arr); + expect(result).to.deep.equal(arr); + }); + + it('should recursively handle nested objects with BigInt', () => { + const obj = { + outer: { + inner: { + value: 9007199254740993n, + }, + }, + }; + const result = wasm.testJsValueToJson(obj); + expect(result.outer.inner.value).to.equal('9007199254740993'); + }); + + it('should recursively handle arrays with BigInt', () => { + const arr = [1n, 2n, 9007199254740993n]; + const result = wasm.testJsValueToJson(arr); + expect(result).to.deep.equal(['1', '2', '9007199254740993']); + }); + }); +}); diff --git a/packages/wasm-sdk/src/queries/address.rs b/packages/wasm-sdk/src/queries/address.rs index 526a0a0dc19..6bc31b23e7f 100644 --- a/packages/wasm-sdk/src/queries/address.rs +++ b/packages/wasm-sdk/src/queries/address.rs @@ -1,10 +1,12 @@ use crate::error::WasmSdkError; +use crate::impl_wasm_serde_conversions; use crate::queries::ProofMetadataResponseWasm; use crate::sdk::WasmSdk; use dash_sdk::dpp::address_funds::PlatformAddress; use dash_sdk::platform::{Fetch, FetchMany}; use drive_proof_verifier::types::{AddressInfo, AddressInfos}; use js_sys::{BigInt, Map}; +use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; @@ -12,7 +14,8 @@ use wasm_dpp2::PlatformAddressWasm; /// Information about a Platform address including its nonce and balance. #[wasm_bindgen(js_name = "PlatformAddressInfo")] -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct PlatformAddressInfoWasm { address: PlatformAddressWasm, nonce: u32, @@ -50,6 +53,8 @@ impl PlatformAddressInfoWasm { } } +impl_wasm_serde_conversions!(PlatformAddressInfoWasm, PlatformAddressInfo); + #[wasm_bindgen] impl WasmSdk { /// Fetches information about a Platform address including its nonce and balance. @@ -94,9 +99,13 @@ impl WasmSdk { AddressInfo::fetch_with_metadata_and_proof(self.as_ref(), platform_address, None) .await?; - let data = address_info - .map(|info| JsValue::from(PlatformAddressInfoWasm::from(info))) - .unwrap_or(JsValue::UNDEFINED); + let data = match address_info { + Some(info) => { + let wrapper = PlatformAddressInfoWasm::from(info); + wrapper.to_object()? + } + None => JsValue::UNDEFINED, + }; Ok(ProofMetadataResponseWasm::from_sdk_parts( data, metadata, proof, @@ -135,11 +144,13 @@ impl WasmSdk { for address in platform_addresses { let key = JsValue::from(PlatformAddressWasm::from(address)); - let value = address_infos - .get(&address) - .and_then(|opt| opt.as_ref()) - .map(|info| JsValue::from(PlatformAddressInfoWasm::from(info.clone()))) - .unwrap_or(JsValue::UNDEFINED); + let value = match address_infos.get(&address).and_then(|opt| opt.as_ref()) { + Some(info) => { + let wrapper = PlatformAddressInfoWasm::from(info.clone()); + wrapper.to_object()? + } + None => JsValue::UNDEFINED, + }; results_map.set(&key, &value); } @@ -183,11 +194,13 @@ impl WasmSdk { for address in platform_addresses { let key = JsValue::from(PlatformAddressWasm::from(address)); - let value = address_infos - .get(&address) - .and_then(|opt| opt.as_ref()) - .map(|info| JsValue::from(PlatformAddressInfoWasm::from(info.clone()))) - .unwrap_or(JsValue::UNDEFINED); + let value = match address_infos.get(&address).and_then(|opt| opt.as_ref()) { + Some(info) => { + let wrapper = PlatformAddressInfoWasm::from(info.clone()); + wrapper.to_object()? + } + None => JsValue::UNDEFINED, + }; results_map.set(&key, &value); } diff --git a/packages/wasm-sdk/src/queries/identity.rs b/packages/wasm-sdk/src/queries/identity.rs index e911b210e38..ad13284e68b 100644 --- a/packages/wasm-sdk/src/queries/identity.rs +++ b/packages/wasm-sdk/src/queries/identity.rs @@ -17,81 +17,17 @@ use std::collections::{BTreeMap, HashMap}; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use wasm_dpp2::identifier::IdentifierWasm; +use wasm_dpp2::identity::public_key::IdentityPublicKeyWasm; use wasm_dpp2::identity::IdentityWasm; -#[wasm_bindgen(js_name = "IdentityKeyInfo")] -#[derive(Clone)] -pub struct IdentityKeyInfoWasm { - key_id: u32, - key_type: String, - public_key_data: String, - purpose: String, - security_level: String, - read_only: bool, - disabled: bool, -} - -impl IdentityKeyInfoWasm { - fn from_entry(key_id: u32, key: &IdentityPublicKey) -> Self { - IdentityKeyInfoWasm { - key_id, - key_type: format!("{:?}", key.key_type()), - public_key_data: hex::encode(key.data().as_slice()), - purpose: format!("{:?}", key.purpose()), - security_level: format!("{:?}", key.security_level()), - read_only: key.read_only(), - disabled: key.disabled_at().is_some(), - } - } -} - -#[wasm_bindgen(js_class = IdentityKeyInfo)] -impl IdentityKeyInfoWasm { - #[wasm_bindgen(getter = "keyId")] - pub fn key_id(&self) -> u32 { - self.key_id - } - - #[wasm_bindgen(getter = "keyType")] - pub fn key_type(&self) -> String { - self.key_type.clone() - } - - #[wasm_bindgen(getter = "publicKeyData")] - pub fn public_key_data(&self) -> String { - self.public_key_data.clone() - } - - #[wasm_bindgen(getter = "purpose")] - pub fn purpose(&self) -> String { - self.purpose.clone() - } - - #[wasm_bindgen(getter = "securityLevel")] - pub fn security_level(&self) -> String { - self.security_level.clone() - } - - #[wasm_bindgen(getter = "readOnly")] - pub fn read_only(&self) -> bool { - self.read_only - } - - #[wasm_bindgen(getter = "disabled")] - pub fn disabled(&self) -> bool { - self.disabled - } -} - #[wasm_bindgen(js_name = "IdentityContractKeys")] -#[derive(Clone)] pub struct IdentityContractKeysWasm { identity_id: IdentifierWasm, - keys: Vec, + keys: Vec, } impl IdentityContractKeysWasm { - fn new(identity_id: IdentifierWasm, keys: Vec) -> Self { + fn new(identity_id: IdentifierWasm, keys: Vec) -> Self { IdentityContractKeysWasm { identity_id, keys } } } @@ -104,12 +40,8 @@ impl IdentityContractKeysWasm { } #[wasm_bindgen(getter = "keys")] - pub fn keys(&self) -> Array { - let array = Array::new(); - for key in &self.keys { - array.push(&JsValue::from(key.clone())); - } - array + pub fn keys(&self) -> Vec { + self.keys.clone() } } @@ -140,7 +72,7 @@ impl IdentityBalanceAndRevisionWasm { } } -impl_wasm_serde_conversions!(IdentityBalanceAndRevisionWasm); +impl_wasm_serde_conversions!(IdentityBalanceAndRevisionWasm, IdentityBalanceAndRevision); #[wasm_bindgen(typescript_custom_section)] const IDENTITIES_CONTRACT_KEYS_QUERY_TS: &'static str = r#" /** @@ -481,7 +413,7 @@ impl WasmSdk { #[wasm_bindgen( js_name = "getIdentityKeys", - unchecked_return_type = "Array" + unchecked_return_type = "Array" )] pub async fn get_identity_keys( &self, @@ -606,11 +538,9 @@ impl WasmSdk { }; let array = Array::new(); - for (key_id, key_opt) in keys_result { + for (_key_id, key_opt) in keys_result { if let Some(key) = key_opt { - array.push(&JsValue::from(IdentityKeyInfoWasm::from_entry( - key_id, &key, - ))); + array.push(&IdentityPublicKeyWasm::from(key).into()); } } @@ -888,31 +818,24 @@ impl WasmSdk { let keys_result: Option = IdentitiesContractKeys::fetch(self.as_ref(), query).await?; - let mut responses: Vec = Vec::new(); + let array = Array::new(); if let Some(keys_map) = keys_result { for (identity_id, purposes_map) in keys_map { - let mut identity_keys = Vec::new(); - for (_, key_opt) in purposes_map { - if let Some(key) = key_opt { - identity_keys.push(IdentityKeyInfoWasm::from_entry(key.id(), &key)); - } - } + let identity_keys: Vec = purposes_map + .into_iter() + .filter_map(|(_, key_opt)| key_opt.map(IdentityPublicKeyWasm::from)) + .collect(); if !identity_keys.is_empty() { - let identity_id_str = IdentifierWasm::from(identity_id); - responses.push(IdentityContractKeysWasm::new( - identity_id_str, + let response = IdentityContractKeysWasm::new( + IdentifierWasm::from(identity_id), identity_keys, - )); + ); + array.push(&response.into()); } } } - let array = Array::new(); - for response in responses { - array.push(&JsValue::from(response)); - } - Ok(array) } @@ -1035,7 +958,7 @@ impl WasmSdk { #[wasm_bindgen( js_name = "getIdentityKeysWithProofInfo", - unchecked_return_type = "ProofMetadataResponseTyped>" + unchecked_return_type = "ProofMetadataResponseTyped>" )] pub async fn get_identity_keys_with_proof_info( &self, @@ -1081,11 +1004,9 @@ impl WasmSdk { }; let keys_array = Array::new(); - for (key_id, key_opt) in keys_result { + for (_key_id, key_opt) in keys_result { if let Some(key) = key_opt { - keys_array.push(&JsValue::from(IdentityKeyInfoWasm::from_entry( - key_id, &key, - ))); + keys_array.push(&IdentityPublicKeyWasm::from(key).into()); } } @@ -1338,30 +1259,24 @@ impl WasmSdk { IdentitiesContractKeys::fetch_with_metadata_and_proof(self.as_ref(), query, None) .await?; - let mut all_responses: Vec = Vec::new(); + let responses_array = Array::new(); if let Some(keys_map) = keys_result { for (identity_id, purposes_map) in keys_map { - let mut identity_keys = Vec::new(); - for (_, key_opt) in purposes_map { - if let Some(key) = key_opt { - identity_keys.push(IdentityKeyInfoWasm::from_entry(key.id(), &key)); - } - } + let identity_keys: Vec = purposes_map + .into_iter() + .filter_map(|(_, key_opt)| key_opt.map(IdentityPublicKeyWasm::from)) + .collect(); if !identity_keys.is_empty() { - all_responses.push(IdentityContractKeysWasm::new( + let response = IdentityContractKeysWasm::new( IdentifierWasm::from(identity_id), identity_keys, - )); + ); + responses_array.push(&response.into()); } } } - let responses_array = Array::new(); - for response in all_responses { - responses_array.push(&JsValue::from(response)); - } - Ok(ProofMetadataResponseWasm::from_sdk_parts( responses_array, metadata, diff --git a/packages/wasm-sdk/src/queries/protocol.rs b/packages/wasm-sdk/src/queries/protocol.rs index 055d077966e..37ea2a6e7c8 100644 --- a/packages/wasm-sdk/src/queries/protocol.rs +++ b/packages/wasm-sdk/src/queries/protocol.rs @@ -95,8 +95,11 @@ impl ProtocolVersionUpgradeVoteStatusWasm { } } -impl_wasm_serde_conversions!(ProtocolVersionUpgradeStateWasm); -impl_wasm_serde_conversions!(ProtocolVersionUpgradeVoteStatusWasm); +impl_wasm_serde_conversions!(ProtocolVersionUpgradeStateWasm, ProtocolVersionUpgradeState); +impl_wasm_serde_conversions!( + ProtocolVersionUpgradeVoteStatusWasm, + ProtocolVersionUpgradeVoteStatus +); #[wasm_bindgen] impl WasmSdk { diff --git a/packages/wasm-sdk/src/queries/system.rs b/packages/wasm-sdk/src/queries/system.rs index 95dd9f94e5b..81010fce868 100644 --- a/packages/wasm-sdk/src/queries/system.rs +++ b/packages/wasm-sdk/src/queries/system.rs @@ -275,22 +275,22 @@ impl StatusResponseWasm { } } -impl_wasm_serde_conversions!(StatusSoftwareWasm); -impl_wasm_serde_conversions!(StatusTenderdashProtocolWasm); -impl_wasm_serde_conversions!(StatusDriveProtocolWasm); -impl_wasm_serde_conversions!(StatusProtocolWasm); -impl_wasm_serde_conversions!(StatusVersionWasm); -impl_wasm_serde_conversions!(StatusNodeWasm); -impl_wasm_serde_conversions!(StatusChainWasm); -impl_wasm_serde_conversions!(StatusNetworkWasm); -impl_wasm_serde_conversions!(StatusStateSyncWasm); -impl_wasm_serde_conversions!(StatusTimeWasm); -impl_wasm_serde_conversions!(StatusResponseWasm); -impl_wasm_serde_conversions!(QuorumInfoWasm); -impl_wasm_serde_conversions!(CurrentQuorumsInfoWasm); -impl_wasm_serde_conversions!(PrefundedSpecializedBalanceWasm); -impl_wasm_serde_conversions!(PathElementWasm); -impl_wasm_serde_conversions!(StateTransitionResultWasm); +impl_wasm_serde_conversions!(StatusSoftwareWasm, StatusSoftware); +impl_wasm_serde_conversions!(StatusTenderdashProtocolWasm, StatusTenderdashProtocol); +impl_wasm_serde_conversions!(StatusDriveProtocolWasm, StatusDriveProtocol); +impl_wasm_serde_conversions!(StatusProtocolWasm, StatusProtocol); +impl_wasm_serde_conversions!(StatusVersionWasm, StatusVersion); +impl_wasm_serde_conversions!(StatusNodeWasm, StatusNode); +impl_wasm_serde_conversions!(StatusChainWasm, StatusChain); +impl_wasm_serde_conversions!(StatusNetworkWasm, StatusNetwork); +impl_wasm_serde_conversions!(StatusStateSyncWasm, StatusStateSync); +impl_wasm_serde_conversions!(StatusTimeWasm, StatusTime); +impl_wasm_serde_conversions!(StatusResponseWasm, StatusResponse); +impl_wasm_serde_conversions!(QuorumInfoWasm, QuorumInfo); +impl_wasm_serde_conversions!(CurrentQuorumsInfoWasm, CurrentQuorumsInfo); +impl_wasm_serde_conversions!(PrefundedSpecializedBalanceWasm, PrefundedSpecializedBalance); +impl_wasm_serde_conversions!(PathElementWasm, PathElement); +impl_wasm_serde_conversions!(StateTransitionResultWasm, StateTransitionResult); #[wasm_bindgen(js_name = "QuorumInfo")] #[derive(Clone, Serialize, Deserialize)] diff --git a/packages/wasm-sdk/src/queries/token.rs b/packages/wasm-sdk/src/queries/token.rs index cc8e56b3675..69de5fdbeb3 100644 --- a/packages/wasm-sdk/src/queries/token.rs +++ b/packages/wasm-sdk/src/queries/token.rs @@ -125,8 +125,8 @@ impl TokenTotalSupplyWasm { } } -impl_wasm_serde_conversions!(TokenTotalSupplyWasm); -impl_wasm_serde_conversions!(TokenPriceInfoWasm); +impl_wasm_serde_conversions!(TokenTotalSupplyWasm, TokenTotalSupply); +impl_wasm_serde_conversions!(TokenPriceInfoWasm, TokenPriceInfo); #[wasm_bindgen] impl WasmSdk { diff --git a/packages/wasm-sdk/src/wallet/extended_derivation.rs b/packages/wasm-sdk/src/wallet/extended_derivation.rs index d68feddd188..b8a929605df 100644 --- a/packages/wasm-sdk/src/wallet/extended_derivation.rs +++ b/packages/wasm-sdk/src/wallet/extended_derivation.rs @@ -169,7 +169,7 @@ impl From for DerivedKeyInfoWasm { } // Field getters are generated via getter_with_clone annotations above -impl_wasm_serde_conversions!(DerivedKeyInfoWasm); +impl_wasm_serde_conversions!(DerivedKeyInfoWasm, DerivedKeyInfo); #[wasm_bindgen(js_name = "DashpayContactKeyInfo")] #[derive(Clone, Serialize, Deserialize)] @@ -235,7 +235,7 @@ impl DashpayContactKeyInfoWasm { } // Field getters are generated via getter_with_clone annotations above -impl_wasm_serde_conversions!(DashpayContactKeyInfoWasm); +impl_wasm_serde_conversions!(DashpayContactKeyInfoWasm, DashpayContactKeyInfo); #[wasm_bindgen] impl WasmSdk { /// Derive a key from seed phrase with extended path supporting 256-bit indices diff --git a/packages/wasm-sdk/src/wallet/key_derivation.rs b/packages/wasm-sdk/src/wallet/key_derivation.rs index 92ec3d0063f..ad5db95a1b5 100644 --- a/packages/wasm-sdk/src/wallet/key_derivation.rs +++ b/packages/wasm-sdk/src/wallet/key_derivation.rs @@ -266,10 +266,10 @@ pub struct PathDerivedKeyInfoWasm { pub network: String, } -impl_wasm_serde_conversions!(DerivationPathWasm); -impl_wasm_serde_conversions!(Dip13DerivationPathWasm); -impl_wasm_serde_conversions!(SeedPhraseKeyInfoWasm); -impl_wasm_serde_conversions!(PathDerivedKeyInfoWasm); +impl_wasm_serde_conversions!(DerivationPathWasm, DerivationPathInfo); +impl_wasm_serde_conversions!(Dip13DerivationPathWasm, Dip13DerivationPathInfo); +impl_wasm_serde_conversions!(SeedPhraseKeyInfoWasm, SeedPhraseKeyInfo); +impl_wasm_serde_conversions!(PathDerivedKeyInfoWasm, PathDerivedKeyInfo); /// HD Key information #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/packages/wasm-sdk/src/wallet/key_generation.rs b/packages/wasm-sdk/src/wallet/key_generation.rs index 804bbc3c417..c7c44173511 100644 --- a/packages/wasm-sdk/src/wallet/key_generation.rs +++ b/packages/wasm-sdk/src/wallet/key_generation.rs @@ -57,7 +57,7 @@ impl From for KeyPairWasm { } } -impl_wasm_serde_conversions!(KeyPairWasm); +impl_wasm_serde_conversions!(KeyPairWasm, KeyPair); #[wasm_bindgen] impl WasmSdk {