Skip to content

Commit a05e620

Browse files
OttoAllmendingerllm-git
andcommitted
feat(wasm-utxo): standardize on Rust snake_case and JavaScript camelCase
Update documentation and implementation to follow a consistent naming convention where: - Rust WASM bindings use snake_case (Rust convention) - TypeScript wrappers convert to camelCase (JS convention) Update BitGoPsbt class implementation to follow this pattern and align method call sites with the new naming. Issue: BTC-2652 Co-authored-by: llm-git <[email protected]>
1 parent 724654f commit a05e620

File tree

4 files changed

+36
-24
lines changed

4 files changed

+36
-24
lines changed

packages/wasm-utxo/js/README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ type-safe API over the raw WASM bindings.
1717

1818
- Generated by `wasm-bindgen` from Rust code
1919
- Exports classes with static methods (e.g., `AddressNamespace`, `UtxolibCompatNamespace`)
20+
- Uses `snake_case` naming (Rust convention)
2021
- Uses loose types (`any`, `string | null`) due to WASM-bindgen limitations
2122

2223
2. **Namespace Wrapper Files** (e.g., `address.ts`, `utxolibCompat.ts`, `fixedScriptWallet.ts`)
2324

2425
- Import the generated WASM namespace classes
2526
- Define precise TypeScript types to replace `any` types
2627
- Export individual functions that wrap the static WASM methods
28+
- Convert `snake_case` WASM methods to `camelCase` (JavaScript convention)
2729
- Re-export related types for convenience
2830

2931
3. **Shared Type Files** (e.g., `coinName.ts`, `triple.ts`)
@@ -42,8 +44,9 @@ type-safe API over the raw WASM bindings.
4244
Given a WASM-generated class:
4345

4446
```typescript
45-
// wasm/wasm_utxo.d.ts (generated)
47+
// wasm/wasm_utxo.d.ts (generated by wasm-bindgen)
4648
export class AddressNamespace {
49+
// Note: snake_case naming from Rust
4750
static to_output_script_with_coin(address: string, coin: string): Uint8Array;
4851
static from_output_script_with_coin(
4952
script: Uint8Array,
@@ -53,7 +56,7 @@ export class AddressNamespace {
5356
}
5457
```
5558

56-
We create a wrapper module:
59+
We create a wrapper module that provides better types and camelCase naming:
5760

5861
```typescript
5962
// address.ts
@@ -62,7 +65,9 @@ import type { CoinName } from "./coinName";
6265

6366
export type AddressFormat = "default" | "cashaddr";
6467

68+
// Wrapper provides camelCase naming and precise types
6569
export function toOutputScriptWithCoin(address: string, coin: CoinName): Uint8Array {
70+
// Calls snake_case WASM method
6671
return AddressNamespace.to_output_script_with_coin(address, coin);
6772
}
6873

@@ -71,6 +76,7 @@ export function fromOutputScriptWithCoin(
7176
coin: CoinName,
7277
format?: AddressFormat,
7378
): string {
79+
// Calls snake_case WASM method
7480
return AddressNamespace.from_output_script_with_coin(script, coin, format);
7581
}
7682
```
@@ -85,5 +91,7 @@ export * as address from "./address";
8591
### Benefits
8692

8793
- **Type Safety**: Replace loose `any` and `string` types with precise union types
88-
- **Better DX**: IDE autocomplete works better with concrete types
94+
- **Idiomatic Naming**: Each layer uses its native convention (`snake_case` in Rust, `camelCase` in JavaScript)
95+
- **Better DX**: IDE autocomplete works better with concrete types and familiar naming
8996
- **Maintainability**: Centralized type definitions prevent duplication
97+
- **Clear Separation**: WASM bindings stay pure to Rust conventions, TypeScript handles JS conventions

packages/wasm-utxo/js/fixedScriptWallet.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export class BitGoPsbt {
9090
* @returns A BitGoPsbt instance
9191
*/
9292
static fromBytes(bytes: Uint8Array, network: NetworkName): BitGoPsbt {
93-
const wasm = WasmBitGoPsbt.fromBytes(bytes, network);
93+
const wasm = WasmBitGoPsbt.from_bytes(bytes, network);
9494
return new BitGoPsbt(wasm);
9595
}
9696

@@ -104,7 +104,7 @@ export class BitGoPsbt {
104104
walletKeys: WalletKeys,
105105
replayProtection: ReplayProtection,
106106
): ParsedTransaction {
107-
return this.wasm.parseTransactionWithWalletKeys(walletKeys, replayProtection);
107+
return this.wasm.parse_transaction_with_wallet_keys(walletKeys, replayProtection);
108108
}
109109

110110
/**
@@ -119,6 +119,6 @@ export class BitGoPsbt {
119119
* @note This method does NOT validate wallet inputs. It only parses outputs.
120120
*/
121121
parseOutputsWithWalletKeys(walletKeys: WalletKeys): ParsedOutput[] {
122-
return this.wasm.parseOutputsWithWalletKeys(walletKeys);
122+
return this.wasm.parse_outputs_with_wallet_keys(walletKeys);
123123
}
124124
}

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,23 @@ pub use fixed_script_wallet::FixedScriptWalletNamespace;
6666

6767
4. **Methods**: All functions are static methods on the namespace struct
6868

69-
5. **Case**: Use `snake_case` for method names (Rust convention) - they'll be available as both `snake_case` and `camelCase` in JavaScript
69+
5. **Case**: Use `snake_case` for method names (Rust convention) - TypeScript wrappers will provide `camelCase` versions
7070

71-
6. **Error Handling**: Return `Result<T, JsValue>` types - `wasm-bindgen` automatically converts these to JavaScript exceptions
71+
6. **No `js_name` Attributes**: Do NOT use `#[wasm_bindgen(js_name = "...")]` to rename methods - keep Rust names pure and let TypeScript wrappers handle JS naming conventions
7272

73-
7. **Separation**: WASM binding layer delegates to core implementation in domain modules (e.g., `src/address/`, `src/fixed_script_wallet/`)
73+
7. **Error Handling**: Return `Result<T, JsValue>` types - `wasm-bindgen` automatically converts these to JavaScript exceptions
74+
75+
8. **Separation**: WASM binding layer delegates to core implementation in domain modules (e.g., `src/address/`, `src/fixed_script_wallet/`)
7476

7577
### Generated TypeScript
7678

77-
The Rust namespace struct becomes a TypeScript class with static methods:
79+
The Rust namespace struct becomes a TypeScript class with static methods (using `snake_case` from Rust):
7880

7981
```typescript
8082
// wasm/wasm_utxo.d.ts (generated by wasm-bindgen)
8183
export class AddressNamespace {
8284
private constructor();
85+
// Note: snake_case naming preserved from Rust
8386
static to_output_script_with_coin(address: string, coin: string): Uint8Array;
8487
static from_output_script_with_coin(
8588
script: Uint8Array,
@@ -91,31 +94,33 @@ export class AddressNamespace {
9194

9295
### TypeScript Wrapper Layer
9396

94-
The generated types have limitations (loose types like `any`, `string | null`). We wrap them with better TypeScript types in the `js/` directory.
97+
The generated types have limitations (loose types like `any`, `string | null`, `snake_case` naming). We wrap them with better TypeScript types in the `js/` directory.
9598

9699
**See `../js/README.md` for the complete TypeScript wrapper pattern.**
97100

98101
The wrapper layer:
99102

100103
- Imports the generated namespace classes
101104
- Defines precise TypeScript types (e.g., union types instead of `string`)
105+
- Converts `snake_case` method names to `camelCase` (JavaScript convention)
102106
- Exports wrapper functions with strong type signatures
103107
- Provides better IDE support and compile-time type checking
104108

105109
### Example Flow
106110

107111
1. **Core Implementation**: Business logic in domain modules (e.g., `src/address/networks.rs`)
108-
2. **WASM Bindings**: Define `AddressNamespace` struct with static methods in `src/wasm/address.rs` that calls into core implementation
112+
2. **WASM Bindings**: Define `AddressNamespace` struct with `snake_case` methods in `src/wasm/address.rs` that calls into core implementation
109113
3. **Module Export**: Export namespace from `src/wasm/mod.rs`
110-
4. **wasm-bindgen Generation**: Generates `AddressNamespace` class in `wasm/wasm_utxo.d.ts`
111-
5. **TypeScript Wrapper**: `js/address.ts` wraps it with precise types
114+
4. **wasm-bindgen Generation**: Generates `AddressNamespace` class with `snake_case` methods in `wasm/wasm_utxo.d.ts`
115+
5. **TypeScript Wrapper**: `js/address.ts` wraps it with precise types and `camelCase` naming
112116
6. **Main Export**: `js/index.ts` exports it as `export * as address from "./address"`
113117

114118
This layered approach gives us:
115119

116120
- Clear separation between core implementation and WASM bindings
117-
- Automatic WASM bindings generation
118-
- Type-safe, well-documented TypeScript API
121+
- Automatic WASM bindings generation with Rust conventions
122+
- Type-safe, well-documented TypeScript API with JavaScript conventions
123+
- Each layer remains idiomatic to its language
119124

120125
## Type Mapping
121126

@@ -139,12 +144,14 @@ Common Rust ↔ JavaScript type mappings:
139144

140145
3. **Use descriptive namespace names** - Clear what domain they cover (e.g., `AddressNamespace`, `PsbtNamespace`)
141146

142-
4. **Thin binding layer** - WASM methods should delegate to core implementation, only handling type conversions
147+
4. **Use `snake_case` naming only** - Never use `#[wasm_bindgen(js_name = "...")]` to convert to camelCase - let TypeScript wrappers handle naming conversion
148+
149+
5. **Thin binding layer** - WASM methods should delegate to core implementation, only handling type conversions
143150

144-
5. **Return `Result<T, JsValue>` types** - Let `wasm-bindgen` handle error conversion to JavaScript exceptions
151+
6. **Return `Result<T, JsValue>` types** - Let `wasm-bindgen` handle error conversion to JavaScript exceptions
145152

146-
6. **Avoid complex types in signatures** - Stick to primitives and byte arrays when possible; use `JsValue` for complex types
153+
7. **Avoid complex types in signatures** - Stick to primitives and byte arrays when possible; use `JsValue` for complex types
147154

148-
7. **Document with Rust doc comments** - They'll appear in the generated TypeScript
155+
8. **Document with Rust doc comments** - They'll appear in the generated TypeScript
149156

150-
8. **Coordinate with TypeScript wrappers** - Keep the wrapper layer in mind when designing the Rust API
157+
9. **Coordinate with TypeScript wrappers** - Keep the wrapper layer in mind when designing the Rust API

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ pub struct BitGoPsbt {
137137
#[wasm_bindgen]
138138
impl BitGoPsbt {
139139
/// Deserialize a PSBT from bytes with network-specific logic
140-
#[wasm_bindgen(js_name = fromBytes)]
141140
pub fn from_bytes(bytes: &[u8], network: &str) -> Result<BitGoPsbt, WasmUtxoError> {
142141
let network = parse_network(network)?;
143142

@@ -149,7 +148,6 @@ impl BitGoPsbt {
149148
}
150149

151150
/// Parse transaction with wallet keys to identify wallet inputs/outputs
152-
#[wasm_bindgen(js_name = parseTransactionWithWalletKeys)]
153151
pub fn parse_transaction_with_wallet_keys(
154152
&self,
155153
wallet_keys: JsValue,
@@ -175,7 +173,6 @@ impl BitGoPsbt {
175173
/// Parse outputs with wallet keys to identify which outputs belong to a wallet
176174
///
177175
/// Note: This method does NOT validate wallet inputs. It only parses outputs.
178-
#[wasm_bindgen(js_name = parseOutputsWithWalletKeys)]
179176
pub fn parse_outputs_with_wallet_keys(
180177
&self,
181178
wallet_keys: JsValue,

0 commit comments

Comments
 (0)