Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 38 additions & 5 deletions packages/wasm-utxo/js/fixedScriptWallet/address.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}

/**
Expand All @@ -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,
Expand All @@ -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,
);
}
}
56 changes: 56 additions & 0 deletions packages/wasm-utxo/src/wasm-bindgen.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, 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<Vec<u8>, WasmUtxoError>
```

The TypeScript wrapper provides a unified API that accepts both types.
51 changes: 51 additions & 0 deletions packages/wasm-utxo/src/wasm/fixed_script_wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, 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<String>,
) -> Result<String, WasmUtxoError> {
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
Expand Down
17 changes: 17 additions & 0 deletions packages/wasm-utxo/src/wasm/try_from_js_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,20 @@ impl TryFromJsValue for crate::inscriptions::TapLeafScript {
})
}
}

impl TryFromJsValue for crate::networks::Network {
fn try_from_js_value(value: &JsValue) -> Result<Self, WasmUtxoError> {
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
))
})
}
}