33//! Implements 256-bit derivation paths for DashPay contact keys
44use crate :: error:: WasmSdkError ;
55use crate :: sdk:: WasmSdk ;
6+ use crate :: queries:: utils:: deserialize_required_query;
67use dash_sdk:: dpp:: dashcore;
78use dash_sdk:: dpp:: dashcore:: secp256k1:: Secp256k1 ;
89use dash_sdk:: dpp:: key_wallet:: { bip32, DerivationPath , ExtendedPrivKey } ;
910use std:: str:: FromStr ;
1011use tracing:: debug;
1112use wasm_bindgen:: prelude:: * ;
13+
14+ // TypeScript option bags (module scope) for extended derivation helpers
15+ #[ wasm_bindgen( typescript_custom_section) ]
16+ const DERIVE_FROM_EXTENDED_PATH_OPTS_TS : & ' static str = r#"
17+ export interface DeriveKeyFromSeedWithExtendedPathOptions {
18+ mnemonic: string;
19+ passphrase?: string | null;
20+ path: string;
21+ network: string;
22+ }
23+ "# ;
24+ #[ wasm_bindgen]
25+ extern "C" {
26+ #[ wasm_bindgen( typescript_type = "DeriveKeyFromSeedWithExtendedPathOptions" ) ]
27+ pub type DeriveKeyFromSeedWithExtendedPathOptionsJs ;
28+ }
29+
30+ // Inputs parsed from options (module scope)
31+ #[ derive( serde:: Deserialize ) ]
32+ #[ serde( rename_all = "camelCase" ) ]
33+ struct DeriveFromExtendedPathInput {
34+ mnemonic : String ,
35+ #[ serde( default ) ]
36+ passphrase : Option < String > ,
37+ path : String ,
38+ network : String ,
39+ }
40+
41+ #[ derive( serde:: Deserialize ) ]
42+ #[ serde( rename_all = "camelCase" ) ]
43+ struct DeriveDashpayContactKeyInput {
44+ mnemonic : String ,
45+ #[ serde( default ) ]
46+ passphrase : Option < String > ,
47+ #[ serde( rename = "senderIdentityId" ) ]
48+ sender_identity_id : String ,
49+ #[ serde( rename = "receiverIdentityId" ) ]
50+ receiver_identity_id : String ,
51+ account : u32 ,
52+ #[ serde( rename = "addressIndex" ) ]
53+ address_index : u32 ,
54+ network : String ,
55+ }
56+
57+ #[ wasm_bindgen( typescript_custom_section) ]
58+ const DERIVE_DASHPAY_CONTACT_KEY_OPTS_TS : & ' static str = r#"
59+ export interface DeriveDashpayContactKeyOptions {
60+ mnemonic: string;
61+ passphrase?: string | null;
62+ senderIdentityId: string;
63+ receiverIdentityId: string;
64+ account: number;
65+ addressIndex: number;
66+ network: string;
67+ }
68+ "# ;
69+ #[ wasm_bindgen]
70+ extern "C" {
71+ #[ wasm_bindgen( typescript_type = "DeriveDashpayContactKeyOptions" ) ]
72+ pub type DeriveDashpayContactKeyOptionsJs ;
73+ }
1274#[ wasm_bindgen]
1375impl WasmSdk {
1476 /// Derive a key from seed phrase with extended path supporting 256-bit indices
1577 /// This supports DIP14/DIP15 paths with identity IDs
1678 #[ wasm_bindgen( js_name = "deriveKeyFromSeedWithExtendedPath" ) ]
1779 pub fn derive_key_from_seed_with_extended_path (
18- mnemonic : & str ,
19- passphrase : Option < String > ,
20- path : & str ,
21- network : & str ,
80+ #[ wasm_bindgen( unchecked_param_type = "DeriveKeyFromSeedWithExtendedPathOptions" ) ] opts : JsValue ,
2281 ) -> Result < JsValue , WasmSdkError > {
82+ let DeriveFromExtendedPathInput { mnemonic, passphrase, path, network } = deserialize_required_query (
83+ opts,
84+ "Options object is required" ,
85+ "deriveKeyFromSeedWithExtendedPath options" ,
86+ ) ?;
2387 debug ! ( target : "wasm_sdk" , path, "Processing extended path" ) ;
24- let seed = Self :: mnemonic_to_seed ( mnemonic, passphrase) ?;
25- let net = match network {
88+ let seed = Self :: mnemonic_to_seed ( & mnemonic, passphrase) ?;
89+ let net = match network. as_str ( ) {
2690 "mainnet" => dashcore:: Network :: Dash ,
2791 "testnet" => dashcore:: Network :: Testnet ,
2892 _ => return Err ( WasmSdkError :: invalid_argument ( "Invalid network" ) ) ,
2993 } ;
3094 let master_key = ExtendedPrivKey :: new_master ( net, & seed)
3195 . map_err ( |e| WasmSdkError :: generic ( format ! ( "Failed to create master key: {}" , e) ) ) ?;
32- let derivation_path = DerivationPath :: from_str ( path) . map_err ( |e| {
96+ let derivation_path = DerivationPath :: from_str ( & path) . map_err ( |e| {
3397 WasmSdkError :: invalid_argument ( format ! ( "Invalid derivation path: {}" , e) )
3498 } ) ?;
3599 let secp = Secp256k1 :: new ( ) ;
@@ -41,7 +105,7 @@ impl WasmSdk {
41105 let public_key = private_key. public_key ( & secp) ;
42106 let address = dashcore:: Address :: p2pkh ( & public_key, net) ;
43107 let obj = js_sys:: Object :: new ( ) ;
44- js_sys:: Reflect :: set ( & obj, & JsValue :: from_str ( "path" ) , & JsValue :: from_str ( path) )
108+ js_sys:: Reflect :: set ( & obj, & JsValue :: from_str ( "path" ) , & JsValue :: from_str ( & path) )
45109 . map_err ( |_| WasmSdkError :: generic ( "Failed to set path property" ) ) ?;
46110 js_sys:: Reflect :: set (
47111 & obj,
@@ -70,7 +134,7 @@ impl WasmSdk {
70134 js_sys:: Reflect :: set (
71135 & obj,
72136 & JsValue :: from_str ( "network" ) ,
73- & JsValue :: from_str ( network) ,
137+ & JsValue :: from_str ( & network) ,
74138 )
75139 . map_err ( |_| WasmSdkError :: generic ( "Failed to set network property" ) ) ?;
76140 js_sys:: Reflect :: set (
@@ -88,34 +152,34 @@ impl WasmSdk {
88152 Ok ( obj. into ( ) )
89153 }
90154 /// Derive a DashPay contact key using DIP15 with full identity IDs
155+ // Types decoded in parse step are defined at module scope
91156 #[ wasm_bindgen( js_name = "deriveDashpayContactKey" ) ]
92157 pub fn derive_dashpay_contact_key (
93- mnemonic : & str ,
94- passphrase : Option < String > ,
95- #[ wasm_bindgen( js_name = "senderIdentityId" ) ] sender_identity_id : & str ,
96- #[ wasm_bindgen( js_name = "receiverIdentityId" ) ] receiver_identity_id : & str ,
97- account : u32 ,
98- #[ wasm_bindgen( js_name = "addressIndex" ) ] address_index : u32 ,
99- network : & str ,
158+ #[ wasm_bindgen( unchecked_param_type = "DeriveDashpayContactKeyOptions" ) ] opts : JsValue ,
100159 ) -> Result < JsValue , WasmSdkError > {
160+ let DeriveDashpayContactKeyInput { mnemonic, passphrase, sender_identity_id, receiver_identity_id, account, address_index, network } = deserialize_required_query (
161+ opts,
162+ "Options object is required" ,
163+ "deriveDashpayContactKey options" ,
164+ ) ?;
101165 use bs58;
102166 let sender_id_formatted = if sender_identity_id. starts_with ( "0x" ) {
103167 sender_identity_id. to_string ( )
104168 } else {
105- let bytes = bs58:: decode ( sender_identity_id) . into_vec ( ) . map_err ( |e| {
169+ let bytes = bs58:: decode ( & sender_identity_id) . into_vec ( ) . map_err ( |e| {
106170 WasmSdkError :: invalid_argument ( format ! ( "Invalid sender identity ID: {}" , e) )
107171 } ) ?;
108172 format ! ( "0x{}" , hex:: encode( bytes) )
109173 } ;
110174 let receiver_id_formatted = if receiver_identity_id. starts_with ( "0x" ) {
111175 receiver_identity_id. to_string ( )
112176 } else {
113- let bytes = bs58:: decode ( receiver_identity_id) . into_vec ( ) . map_err ( |e| {
177+ let bytes = bs58:: decode ( & receiver_identity_id) . into_vec ( ) . map_err ( |e| {
114178 WasmSdkError :: invalid_argument ( format ! ( "Invalid receiver identity ID: {}" , e) )
115179 } ) ?;
116180 format ! ( "0x{}" , hex:: encode( bytes) )
117181 } ;
118- let coin_type = match network {
182+ let coin_type = match network. as_str ( ) {
119183 "mainnet" => 5 ,
120184 "testnet" => 1 ,
121185 _ => return Err ( WasmSdkError :: invalid_argument ( "Invalid network" ) ) ,
@@ -125,8 +189,15 @@ impl WasmSdk {
125189 coin_type, 15 , account, sender_id_formatted, receiver_id_formatted, address_index
126190 ) ;
127191 debug ! ( target : "wasm_sdk" , path = % path, "DashPay contact path" ) ;
128- let result =
129- Self :: derive_key_from_seed_with_extended_path ( mnemonic, passphrase, & path, network) ?;
192+ let opts = serde_json:: json!( {
193+ "mnemonic" : mnemonic,
194+ "passphrase" : passphrase,
195+ "path" : path,
196+ "network" : network,
197+ } ) ;
198+ let js_opts = serde_wasm_bindgen:: to_value ( & opts)
199+ . map_err ( |e| WasmSdkError :: serialization ( format ! ( "Failed to serialize options: {}" , e) ) ) ?;
200+ let result = Self :: derive_key_from_seed_with_extended_path ( js_opts) ?;
130201 let obj = result
131202 . dyn_into :: < js_sys:: Object > ( )
132203 . map_err ( |_| WasmSdkError :: generic ( "Failed to cast result to object" ) ) ?;
@@ -145,13 +216,13 @@ impl WasmSdk {
145216 js_sys:: Reflect :: set (
146217 & obj,
147218 & JsValue :: from_str ( "senderIdentity" ) ,
148- & JsValue :: from_str ( sender_identity_id) ,
219+ & JsValue :: from_str ( & sender_identity_id) ,
149220 )
150221 . map_err ( |_| WasmSdkError :: generic ( "Failed to set senderIdentity property" ) ) ?;
151222 js_sys:: Reflect :: set (
152223 & obj,
153224 & JsValue :: from_str ( "receiverIdentity" ) ,
154- & JsValue :: from_str ( receiver_identity_id) ,
225+ & JsValue :: from_str ( & receiver_identity_id) ,
155226 )
156227 . map_err ( |_| WasmSdkError :: generic ( "Failed to set receiverIdentity property" ) ) ?;
157228 js_sys:: Reflect :: set (
0 commit comments