Skip to content

Commit 03d850b

Browse files
Merge pull request #97 from BitGo/BTC-2912.generate-with-coinName
feat(wasm-utxo): add string-based network parameter support
2 parents 8784335 + 91e9e96 commit 03d850b

4 files changed

Lines changed: 162 additions & 5 deletions

File tree

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
11
import { FixedScriptWalletNamespace } from "../wasm/wasm_utxo.js";
22
import { type WalletKeysArg, RootWalletKeys } from "./RootWalletKeys.js";
33
import type { UtxolibNetwork } from "../utxolibCompat.js";
4+
import type { UtxolibName } from "../utxolibCompat.js";
5+
import type { CoinName } from "../coinName.js";
46
import { AddressFormat } from "../address.js";
57

8+
export type NetworkName = UtxolibName | CoinName;
9+
610
/**
711
* Create the output script for a given wallet keys and chain and index
12+
* @param keys - The wallet keys to use
13+
* @param chain - The chain to use
14+
* @param index - The index to use
15+
* @param network - The network to use. Can be a network name string (e.g., "btc", "bitcoin", "testnet") or a UtxolibNetwork object
816
*/
917
export function outputScript(
1018
keys: WalletKeysArg,
1119
chain: number,
1220
index: number,
13-
network: UtxolibNetwork,
21+
network: NetworkName | UtxolibNetwork,
1422
): Uint8Array {
1523
const walletKeys = RootWalletKeys.from(keys);
16-
return FixedScriptWalletNamespace.output_script(walletKeys.wasm, chain, index, network);
24+
if (typeof network === "string") {
25+
return FixedScriptWalletNamespace.output_script_with_network_str(
26+
walletKeys.wasm,
27+
chain,
28+
index,
29+
network,
30+
);
31+
} else {
32+
return FixedScriptWalletNamespace.output_script(walletKeys.wasm, chain, index, network);
33+
}
1734
}
1835

1936
/**
@@ -22,7 +39,7 @@ export function outputScript(
2239
* @param keys - The wallet keys to use.
2340
* @param chain - The chain to use.
2441
* @param index - The index to use.
25-
* @param network - The network to use.
42+
* @param network - The network to use. Can be a network name string (e.g., "btc", "bitcoin", "testnet") or a UtxolibNetwork object
2643
* @param addressFormat - The address format to use.
2744
* Only relevant for Bitcoin Cash and eCash networks, where:
2845
* - "default" means base58check,
@@ -32,9 +49,25 @@ export function address(
3249
keys: WalletKeysArg,
3350
chain: number,
3451
index: number,
35-
network: UtxolibNetwork,
52+
network: NetworkName | UtxolibNetwork,
3653
addressFormat?: AddressFormat,
3754
): string {
3855
const walletKeys = RootWalletKeys.from(keys);
39-
return FixedScriptWalletNamespace.address(walletKeys.wasm, chain, index, network, addressFormat);
56+
if (typeof network === "string") {
57+
return FixedScriptWalletNamespace.address_with_network_str(
58+
walletKeys.wasm,
59+
chain,
60+
index,
61+
network,
62+
addressFormat,
63+
);
64+
} else {
65+
return FixedScriptWalletNamespace.address(
66+
walletKeys.wasm,
67+
chain,
68+
index,
69+
network,
70+
addressFormat,
71+
);
72+
}
4073
}

packages/wasm-utxo/src/wasm-bindgen.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,59 @@ Common Rust ↔ JavaScript type mappings:
155155
8. **Document with Rust doc comments** - They'll appear in the generated TypeScript
156156

157157
9. **Coordinate with TypeScript wrappers** - Keep the wrapper layer in mind when designing the Rust API
158+
159+
## String vs Object Parameter Pattern
160+
161+
Some functions support both string and object parameters for the same concept. The pattern is:
162+
163+
1. **WASM Layer**: Expose separate functions:
164+
165+
- Main function (e.g., `output_script`) accepts object via `JsValue`
166+
- Variant with `_with_network_str` suffix accepts `&str` for string parameters
167+
168+
2. **TypeScript Wrapper Layer**: Detects parameter type and routes to appropriate WASM function:
169+
170+
```typescript
171+
export function outputScript(
172+
keys: RootWalletKeys,
173+
chain: number,
174+
index: number,
175+
network: NetworkName | UtxolibNetwork // Union type
176+
): Uint8Array {
177+
if (typeof network === 'string') {
178+
return FixedScriptWalletNamespace.output_script_with_network_str(...);
179+
} else {
180+
return FixedScriptWalletNamespace.output_script(...);
181+
}
182+
}
183+
```
184+
185+
3. **Benefits**:
186+
- Clear function signatures in Rust (no runtime type detection)
187+
- Type-safe routing in TypeScript
188+
- Backward compatible (existing object-based code continues to work)
189+
- Simpler new code (can use strings directly)
190+
191+
### Example: Network Parameter
192+
193+
```rust
194+
// WASM: Object-based (existing)
195+
#[wasm_bindgen]
196+
pub fn output_script(
197+
keys: &WasmRootWalletKeys,
198+
chain: u32,
199+
index: u32,
200+
network: JsValue, // UtxolibNetwork object
201+
) -> Result<Vec<u8>, WasmUtxoError>
202+
203+
// WASM: String-based (new)
204+
#[wasm_bindgen]
205+
pub fn output_script_with_network_str(
206+
keys: &WasmRootWalletKeys,
207+
chain: u32,
208+
index: u32,
209+
network: &str, // Network name as string
210+
) -> Result<Vec<u8>, WasmUtxoError>
211+
```
212+
213+
The TypeScript wrapper provides a unified API that accepts both types.

packages/wasm-utxo/src/wasm/fixed_script_wallet/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,57 @@ impl FixedScriptWalletNamespace {
8787
Ok(address)
8888
}
8989

90+
#[wasm_bindgen]
91+
pub fn output_script_with_network_str(
92+
keys: &WasmRootWalletKeys,
93+
chain: u32,
94+
index: u32,
95+
network: &str,
96+
) -> Result<Vec<u8>, WasmUtxoError> {
97+
let network = parse_network(network)?;
98+
let chain = Chain::try_from(chain)
99+
.map_err(|e| WasmUtxoError::new(&format!("Invalid chain: {}", e)))?;
100+
101+
let wallet_keys = keys.inner();
102+
let scripts = WalletScripts::from_wallet_keys(
103+
wallet_keys,
104+
chain,
105+
index,
106+
&network.output_script_support(),
107+
)?;
108+
Ok(scripts.output_script().to_bytes())
109+
}
110+
111+
#[wasm_bindgen]
112+
pub fn address_with_network_str(
113+
keys: &WasmRootWalletKeys,
114+
chain: u32,
115+
index: u32,
116+
network: &str,
117+
address_format: Option<String>,
118+
) -> Result<String, WasmUtxoError> {
119+
let network = parse_network(network)?;
120+
let wallet_keys = keys.inner();
121+
let chain = Chain::try_from(chain)
122+
.map_err(|e| WasmUtxoError::new(&format!("Invalid chain: {}", e)))?;
123+
let scripts = WalletScripts::from_wallet_keys(
124+
wallet_keys,
125+
chain,
126+
index,
127+
&network.output_script_support(),
128+
)?;
129+
let script = scripts.output_script();
130+
let address_format = AddressFormat::from_optional_str(address_format.as_deref())
131+
.map_err(|e| WasmUtxoError::new(&format!("Invalid address format: {}", e)))?;
132+
let address = crate::address::networks::from_output_script_with_network_and_format(
133+
&script,
134+
network,
135+
address_format,
136+
)
137+
.map_err(|e| WasmUtxoError::new(&format!("Failed to generate address: {}", e)))?;
138+
Ok(address)
139+
}
140+
90141
/// Check if a network supports a given fixed-script wallet script type
91142
///
92143
/// # Arguments

packages/wasm-utxo/src/wasm/try_from_js_value.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,20 @@ impl TryFromJsValue for crate::inscriptions::TapLeafScript {
173173
})
174174
}
175175
}
176+
177+
impl TryFromJsValue for crate::networks::Network {
178+
fn try_from_js_value(value: &JsValue) -> Result<Self, WasmUtxoError> {
179+
let network_str = value
180+
.as_string()
181+
.ok_or_else(|| WasmUtxoError::new("Expected a string for network parameter"))?;
182+
183+
crate::networks::Network::from_utxolib_name(&network_str)
184+
.or_else(|| crate::networks::Network::from_coin_name(&network_str))
185+
.ok_or_else(|| {
186+
WasmUtxoError::new(&format!(
187+
"Unknown network '{}'. Expected a utxolib name (e.g., 'bitcoin', 'testnet') or coin name (e.g., 'btc', 'tbtc')",
188+
network_str
189+
))
190+
})
191+
}
192+
}

0 commit comments

Comments
 (0)