# Extensions This document covers `hdwalletv1` protocol-level extensions — optional capabilities that extend the application protocol (extra path names, custom message actions, per-wallet features). For transport-level capabilities that apply below the application protocol regardless of which protocol is in use (chunking, future: compression), see [transport.md § Transport-level extensions](transport.md#transport-level-extensions). The hdwalletv1 protocol supports optional extensions that let wallets and dapps negotiate additional capabilities beyond the core sign-transaction flow. Extensions are backward-compatible: existing wallets and dapps that don't know about extensions continue to work unchanged. ## How extensions work Extensions use three mechanisms, all of which are optional and additive: ### 1. Session extensions field Wallets advertise supported extensions in the `wallet_ready` handshake via an `extensions` field on the `Hdwalletv1Session` object: ```typescript interface Hdwalletv1Session { paths: PathXpub[]; extensions?: Record; } ``` Each key in `extensions` is an extension name. Its **presence** indicates the wallet supports that extension. The value carries extension-specific handshake data (derivation paths to the hardened gate where the wallet exports xpubs), or `{}` if no data is needed. Example: ```json { "paths": [ { "name": "receive", "xpub": "xpub6..." }, { "name": "change", "xpub": "xpub6..." }, { "name": "defi", "xpub": "xpub6..." }, { "name": "stealth_spend", "xpub": "xpub6..." }, { "name": "stealth_scan", "xpub": "xpub6..." }, { "name": "rpa_spend", "xpub": "xpub6..." }, { "name": "rpa_scan", "xpub": "xpub6..." } ], "extensions": { "bch_stealth_bip352": { "spend_path": "m/352'/145'/0'/0'", "scan_path": "m/352'/145'/0'/1'" }, "rpa_bip47": { "spend_path": "m/47'/145'/0'/0'", "scan_path": "m/47'/145'/0'/1'" }, "decrypt": { "public_key": "02abc...", "scheme": "ecies" } } } ``` ### 2. Additional path names `PathName` is an open string type. Wallets may include paths beyond the well-known `receive`/`change`/`defi` set. Extension-defined paths are carried in the standard `paths` array: ```json { "paths": [ { "name": "receive", "xpub": "xpub6..." }, { "name": "change", "xpub": "xpub6..." }, { "name": "stealth_spend", "xpub": "xpub6..." }, { "name": "stealth_scan", "xpub": "xpub6..." }, { "name": "rpa_spend", "xpub": "xpub6..." }, { "name": "rpa_scan", "xpub": "xpub6..." } ], "extensions": { "bch_stealth_bip352": { "spend_path": "m/352'/145'/0'/0'", "scan_path": "m/352'/145'/0'/1'" }, "rpa_bip47": { "spend_path": "m/47'/145'/0'/0'", "scan_path": "m/47'/145'/0'/1'" } } } ``` Dapps should ignore path names they do not recognize. The standard pubkey derivation logic (`DappPubkeyStateManager`) automatically skips unknown paths. ### 3. Custom message actions Extensions may define new message action strings beyond the well-known set. Custom messages follow the standard `ProtocolMessage` shape (`action` + `time`) and use the existing relay transport. Convention for request/response operations: ```typescript // Request (dapp → wallet): { action: "_request", sequence: number, time: number, ...params } // Response (wallet → dapp): { action: "_response", sequence: number, time: number, ...result } ``` The `sequence` field ties responses to requests, matching the pattern used by `sign_transaction_request`/`sign_transaction_response`. **Wallet side**: custom messages are emitted via the `"message"` event on `WalletConnectionManager`: ```typescript manager.on("message", (connectionId, msg) => { if (msg.action === "decrypt_request") { // handle decrypt request } }); ``` **Dapp side**: all messages (including custom ones) are emitted via the `"messagereceived"` event on `DappConnectionManager`: ```typescript manager.on("messagereceived", (msg) => { if (msg.action === "decrypt_response") { // handle decrypt response } }); ``` ## Implementing an extension (wallet side) Wallets advertise extensions by implementing optional methods on `WalletAdapter`: ```typescript interface WalletAdapter { // ... core methods ... /** Additional paths to include in the session (e.g. stealth_scan). */ getAdditionalPaths?(): PathXpub[]; /** Extension data for the session handshake. */ getExtensions?(): Record; } ``` Example: ```typescript const adapter: WalletAdapter = { // ... core implementation ... getAdditionalPaths() { return [ { name: "stealth_spend", xpub: deriveXpub("m/352'/145'/0'/0'") }, { name: "stealth_scan", xpub: deriveXpub("m/352'/145'/0'/1'") }, { name: "rpa_spend", xpub: deriveXpub("m/47'/145'/0'/0'") }, { name: "rpa_scan", xpub: deriveXpub("m/47'/145'/0'/1'") }, ]; }, getExtensions() { return { "bch_stealth_bip352": { "spend_path": "m/352'/145'/0'/0'", "scan_path": "m/352'/145'/0'/1'", }, "rpa_bip47": { "spend_path": "m/47'/145'/0'/0'", "scan_path": "m/47'/145'/0'/1'", }, }; }, }; ``` Wallets that don't implement these methods produce the same session as before (receive/change/defi paths only, no extensions field). ## Discovering extensions (dapp side) Dapps check for extension support after receiving `wallet_ready`: ```typescript manager.on("walletready", (msg) => { const session = msg.session["hdwalletv1"] as Hdwalletv1Session; if (session.extensions?.bch_stealth_bip352) { const scanPath = session.paths.find(p => p.name === "stealth_scan"); const spendPath = session.paths.find(p => p.name === "stealth_spend"); // enable stealth address features } else { // show: "Stealth payments require a wallet that supports BCH Stealth (BIP352)" } if (session.extensions?.rpa_bip47) { const rpaSpend = session.paths.find(p => p.name === "rpa_spend"); const rpaScan = session.paths.find(p => p.name === "rpa_scan"); // enable RPA features } }); ``` Dapps should degrade gracefully when an extension is absent. If a feature requires an extension the wallet doesn't support, inform the user rather than failing silently. ## Known extensions | Extension name | Path names | Hardened gate paths | Purpose | Status | |---------------|-----------|---------------------|---------|--------| | `bch_stealth_bip352` | `stealth_spend`, `stealth_scan` | `m/352'/145'/0'/0'`, `m/352'/145'/0'/1'` | BCH stealth addresses (BIP352 structure). Wallet exports xpubs at hardened gates; dapp derives `/0` child locally. | Standard — [BCR post](https://bitcoincashresearch.org/t/ecdh-stealth-addresses-on-bitcoin-cash-implementation-code/1773/5) | | `rpa_bip47` | `rpa_spend`, `rpa_scan` | `m/47'/145'/0'/0'`, `m/47'/145'/0'/1'` | BIP47 reusable payment addresses. Wallet exports xpubs at hardened gates; dapp derives `/0` child locally. | Standard — [BCR post](https://bitcoincashresearch.org/t/ecdh-stealth-addresses-on-bitcoin-cash-implementation-code/1773/5) | | `decrypt` | — | — | Dapp-side encrypted storage. Wallet provides a public key; dapp encrypts data for storage and sends `decrypt_request` messages when the data is needed. | Proposed | See the discussions and specifications for each extension as they are formalized.