This backward compatible change allows wallets/dapps to add additional features outside the basic transaction signature support to the hdwalletv1 protocol.
170 lines
5 KiB
Markdown
170 lines
5 KiB
Markdown
# 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<string, unknown>;
|
|
}
|
|
```
|
|
|
|
Each key in `extensions` is an extension name. Its **presence** indicates the wallet supports that
|
|
extension. The value carries extension-specific handshake data, or `{}` if no data is needed.
|
|
|
|
Example:
|
|
```json
|
|
{
|
|
"paths": [
|
|
{ "name": "receive", "xpub": "xpub6..." },
|
|
{ "name": "change", "xpub": "xpub6..." },
|
|
{ "name": "defi", "xpub": "xpub6..." }
|
|
],
|
|
"extensions": {
|
|
"bip352": {},
|
|
"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_scan", "xpub": "xpub6..." },
|
|
{ "name": "stealth_spend", "xpub": "xpub6..." }
|
|
],
|
|
"extensions": {
|
|
"bip352": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
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: "<operation>_request", sequence: number, time: number, ...params }
|
|
|
|
// Response (wallet → dapp):
|
|
{ action: "<operation>_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<string, unknown>;
|
|
}
|
|
```
|
|
|
|
Example:
|
|
```typescript
|
|
const adapter: WalletAdapter = {
|
|
// ... core implementation ...
|
|
|
|
getAdditionalPaths() {
|
|
return [
|
|
{ name: "stealth_scan", xpub: deriveScanXpub() },
|
|
{ name: "stealth_spend", xpub: deriveSpendXpub() },
|
|
];
|
|
},
|
|
|
|
getExtensions() {
|
|
return { "bip352": {} };
|
|
},
|
|
};
|
|
```
|
|
|
|
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?.bip352) {
|
|
const scanPath = session.paths.find(p => p.name === "stealth_scan");
|
|
// enable stealth address features
|
|
} else {
|
|
// show: "Stealth payments require a wallet that supports BIP352"
|
|
}
|
|
});
|
|
```
|
|
|
|
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
|
|
|
|
No extensions are standardized yet. The following are under discussion:
|
|
|
|
| Extension name | Purpose | Status |
|
|
|---------------|---------|--------|
|
|
| `bip352` | BIP352 silent/stealth payments. Adds `stealth_scan` and `stealth_spend` paths. | Proposed |
|
|
| `bip47_rpa` | BIP47 reusable payment addresses. Adds `rpa` path and payment code data. | Proposed |
|
|
| `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.
|