# WASM Binary Size Analysis Analysis of `cdk-wasm` release build (`cargo build --target wasm32-unknown-unknown --release`). Binary size: **~6.6 MB** raw (before gzip/brotli compression). Tooling used: [twiggy](https://rustwasm.github.io/twiggy/) for symbol-level profiling. ## Per-component breakdown | Component | Size (KB) | % | Notes | |---|---:|---:|---| | **[data/rodata]** | 1554.8 | 23.1% | String literals, lookup tables, constants | | **cdk** (wallet core) | 944.1 | 14.0% | Wallet sagas, swap, receive, restore, OIDC | | **core/alloc/std** | 841.9 | 12.5% | Rust stdlib (formatting, collections, etc.) | | **[function names]** | 793.3 | 11.8% | Debug symbol names — stripped by wasm-pack | | **[other]** | 597.9 | 8.9% | Mostly secp256k1 C symbols + serde monomorphs | | **serde (all)** | 425.9 | 6.3% | JSON/CBOR deserialization monomorphization | | **tracing** | 251.7 | 3.7% | Logging framework | | **cashu** | 223.9 | 3.3% | Protocol types + their serde impls | | **secp256k1** | 221.8 | 3.3% | Elliptic curve crypto (C code compiled to WASM) | | **wasm-bindgen** | 189.6 | 2.8% | JS glue layer | | **cdk_wasm + cdk_common** | 191.7 | 2.9% | FFI wrappers + shared types | | **url/idna** | 66.3 | 1.0% | URL parsing + international domain names | | **lightning** | 50.5 | 0.8% | Invoice parsing | | **bitcoin_hashes** | 31.2 | 0.5% | SHA256, SHA512, RIPEMD160 | | Everything else | ~110 | 1.6% | bip39, cbor, encoding, tokio, etc. | ## Key observations 1. **Biggest wins would come from**: reducing `cdk` code size (14%), trimming `serde` monomorphization (6.3%), and evaluating if `tracing` (3.7%) can be compiled out for WASM. 2. **`.rodata` at 23%** is large — this is string literals (error messages, format strings, tracing span names) and lookup tables (unicode normalization, secp256k1 precomputed tables). Hard to reduce without removing features. 3. **`[function names]` (11.8%)** disappears when wasm-pack uses `--release` with name stripping, so the real production binary is closer to **~5.9 MB** raw (before gzip). 4. **`secp256k1` + `bitcoin_hashes`** (3.8% combined) is the cost of doing cryptography in WASM — this is the C secp256k1 library compiled to WASM. Can't avoid it. 5. **`serde` monomorphization** (6.3%) is the classic Rust WASM bloat problem — every `Deserialize` impl for each type generates a separate copy of the JSON parser. `miniserde` or hand-written parsing would shrink this but at massive effort. 6. **`jsonwebtoken`/`ring` is completely gone** from the WASM build — zero cost, replaced by the browser's built-in Web Crypto API via `SubtleCrypto`. ## Potential size reduction strategies - **Strip tracing on WASM**: feature-gate `tracing` instrumentation behind `cfg(not(target_arch = "wasm32"))` or use `console.log` directly. Saves ~250 KB. - **wasm-opt**: run `wasm-opt -Oz` on the output. Typically 10-20% reduction. - **gzip/brotli**: the final served `.wasm` file compresses well (~60-70% reduction). - **Reduce serde monomorphization**: use `#[serde(deserialize_with)]` to share deserializer instances across similar types, or use `serde_json::Value` as an intermediate. ## Reproducing this analysis ```bash # Install twiggy cargo install twiggy # Build release WASM cargo build --target wasm32-unknown-unknown -p cdk-wasm --release # Analyze twiggy top target/wasm32-unknown-unknown/release/cdk_wasm.wasm -n 50 # Dominator tree (what retains what) twiggy dominators target/wasm32-unknown-unknown/release/cdk_wasm.wasm -d 2 | head -80 # CSV export for scripting twiggy top target/wasm32-unknown-unknown/release/cdk_wasm.wasm -n 30000 --format csv > twiggy.csv ```