diff --git a/packages/wasm-utxo/js/fixedScriptWallet/address.ts b/packages/wasm-utxo/js/fixedScriptWallet/address.ts index f88f571..77114dd 100644 --- a/packages/wasm-utxo/js/fixedScriptWallet/address.ts +++ b/packages/wasm-utxo/js/fixedScriptWallet/address.ts @@ -1,19 +1,36 @@ import { FixedScriptWalletNamespace } from "../wasm/wasm_utxo.js"; import { type WalletKeysArg, RootWalletKeys } from "./RootWalletKeys.js"; import type { UtxolibNetwork } from "../utxolibCompat.js"; +import type { UtxolibName } from "../utxolibCompat.js"; +import type { CoinName } from "../coinName.js"; import { AddressFormat } from "../address.js"; +export type NetworkName = UtxolibName | CoinName; + /** * Create the output script for a given wallet keys and chain and index + * @param keys - The wallet keys to use + * @param chain - The chain to use + * @param index - The index to use + * @param network - The network to use. Can be a network name string (e.g., "btc", "bitcoin", "testnet") or a UtxolibNetwork object */ export function outputScript( keys: WalletKeysArg, chain: number, index: number, - network: UtxolibNetwork, + network: NetworkName | UtxolibNetwork, ): Uint8Array { const walletKeys = RootWalletKeys.from(keys); - return FixedScriptWalletNamespace.output_script(walletKeys.wasm, chain, index, network); + if (typeof network === "string") { + return FixedScriptWalletNamespace.output_script_with_network_str( + walletKeys.wasm, + chain, + index, + network, + ); + } else { + return FixedScriptWalletNamespace.output_script(walletKeys.wasm, chain, index, network); + } } /** @@ -22,7 +39,7 @@ export function outputScript( * @param keys - The wallet keys to use. * @param chain - The chain to use. * @param index - The index to use. - * @param network - The network to use. + * @param network - The network to use. Can be a network name string (e.g., "btc", "bitcoin", "testnet") or a UtxolibNetwork object * @param addressFormat - The address format to use. * Only relevant for Bitcoin Cash and eCash networks, where: * - "default" means base58check, @@ -32,9 +49,25 @@ export function address( keys: WalletKeysArg, chain: number, index: number, - network: UtxolibNetwork, + network: NetworkName | UtxolibNetwork, addressFormat?: AddressFormat, ): string { const walletKeys = RootWalletKeys.from(keys); - return FixedScriptWalletNamespace.address(walletKeys.wasm, chain, index, network, addressFormat); + if (typeof network === "string") { + return FixedScriptWalletNamespace.address_with_network_str( + walletKeys.wasm, + chain, + index, + network, + addressFormat, + ); + } else { + return FixedScriptWalletNamespace.address( + walletKeys.wasm, + chain, + index, + network, + addressFormat, + ); + } } diff --git a/packages/wasm-utxo/src/wasm-bindgen.md b/packages/wasm-utxo/src/wasm-bindgen.md index 6af9ad1..5d93dde 100644 --- a/packages/wasm-utxo/src/wasm-bindgen.md +++ b/packages/wasm-utxo/src/wasm-bindgen.md @@ -155,3 +155,59 @@ Common Rust ↔ JavaScript type mappings: 8. **Document with Rust doc comments** - They'll appear in the generated TypeScript 9. **Coordinate with TypeScript wrappers** - Keep the wrapper layer in mind when designing the Rust API + +## String vs Object Parameter Pattern + +Some functions support both string and object parameters for the same concept. The pattern is: + +1. **WASM Layer**: Expose separate functions: + + - Main function (e.g., `output_script`) accepts object via `JsValue` + - Variant with `_with_network_str` suffix accepts `&str` for string parameters + +2. **TypeScript Wrapper Layer**: Detects parameter type and routes to appropriate WASM function: + + ```typescript + export function outputScript( + keys: RootWalletKeys, + chain: number, + index: number, + network: NetworkName | UtxolibNetwork // Union type + ): Uint8Array { + if (typeof network === 'string') { + return FixedScriptWalletNamespace.output_script_with_network_str(...); + } else { + return FixedScriptWalletNamespace.output_script(...); + } + } + ``` + +3. **Benefits**: + - Clear function signatures in Rust (no runtime type detection) + - Type-safe routing in TypeScript + - Backward compatible (existing object-based code continues to work) + - Simpler new code (can use strings directly) + +### Example: Network Parameter + +```rust +// WASM: Object-based (existing) +#[wasm_bindgen] +pub fn output_script( + keys: &WasmRootWalletKeys, + chain: u32, + index: u32, + network: JsValue, // UtxolibNetwork object +) -> Result, WasmUtxoError> + +// WASM: String-based (new) +#[wasm_bindgen] +pub fn output_script_with_network_str( + keys: &WasmRootWalletKeys, + chain: u32, + index: u32, + network: &str, // Network name as string +) -> Result, WasmUtxoError> +``` + +The TypeScript wrapper provides a unified API that accepts both types. diff --git a/packages/wasm-utxo/src/wasm/fixed_script_wallet/mod.rs b/packages/wasm-utxo/src/wasm/fixed_script_wallet/mod.rs index dd1425d..4effeb3 100644 --- a/packages/wasm-utxo/src/wasm/fixed_script_wallet/mod.rs +++ b/packages/wasm-utxo/src/wasm/fixed_script_wallet/mod.rs @@ -87,6 +87,57 @@ impl FixedScriptWalletNamespace { Ok(address) } + #[wasm_bindgen] + pub fn output_script_with_network_str( + keys: &WasmRootWalletKeys, + chain: u32, + index: u32, + network: &str, + ) -> Result, WasmUtxoError> { + let network = parse_network(network)?; + let chain = Chain::try_from(chain) + .map_err(|e| WasmUtxoError::new(&format!("Invalid chain: {}", e)))?; + + let wallet_keys = keys.inner(); + let scripts = WalletScripts::from_wallet_keys( + wallet_keys, + chain, + index, + &network.output_script_support(), + )?; + Ok(scripts.output_script().to_bytes()) + } + + #[wasm_bindgen] + pub fn address_with_network_str( + keys: &WasmRootWalletKeys, + chain: u32, + index: u32, + network: &str, + address_format: Option, + ) -> Result { + let network = parse_network(network)?; + let wallet_keys = keys.inner(); + let chain = Chain::try_from(chain) + .map_err(|e| WasmUtxoError::new(&format!("Invalid chain: {}", e)))?; + let scripts = WalletScripts::from_wallet_keys( + wallet_keys, + chain, + index, + &network.output_script_support(), + )?; + let script = scripts.output_script(); + let address_format = AddressFormat::from_optional_str(address_format.as_deref()) + .map_err(|e| WasmUtxoError::new(&format!("Invalid address format: {}", e)))?; + let address = crate::address::networks::from_output_script_with_network_and_format( + &script, + network, + address_format, + ) + .map_err(|e| WasmUtxoError::new(&format!("Failed to generate address: {}", e)))?; + Ok(address) + } + /// Check if a network supports a given fixed-script wallet script type /// /// # Arguments diff --git a/packages/wasm-utxo/src/wasm/try_from_js_value.rs b/packages/wasm-utxo/src/wasm/try_from_js_value.rs index e86596d..1e20190 100644 --- a/packages/wasm-utxo/src/wasm/try_from_js_value.rs +++ b/packages/wasm-utxo/src/wasm/try_from_js_value.rs @@ -173,3 +173,20 @@ impl TryFromJsValue for crate::inscriptions::TapLeafScript { }) } } + +impl TryFromJsValue for crate::networks::Network { + fn try_from_js_value(value: &JsValue) -> Result { + let network_str = value + .as_string() + .ok_or_else(|| WasmUtxoError::new("Expected a string for network parameter"))?; + + crate::networks::Network::from_utxolib_name(&network_str) + .or_else(|| crate::networks::Network::from_coin_name(&network_str)) + .ok_or_else(|| { + WasmUtxoError::new(&format!( + "Unknown network '{}'. Expected a utxolib name (e.g., 'bitcoin', 'testnet') or coin name (e.g., 'btc', 'tbtc')", + network_str + )) + }) + } +}