kaspa-wallet-standard 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/SPEC.md +32 -22
- package/dist/index.d.ts +118 -16
- package/dist/index.js +18 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,8 +5,10 @@ any dApp can find any wallet — and any wallet can join every dApp — without
|
|
|
5
5
|
other. Inspired by Ethereum's [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) and Solana's
|
|
6
6
|
[Wallet Standard](https://github.com/wallet-standard/wallet-standard).
|
|
7
7
|
|
|
8
|
-
> **Status:
|
|
9
|
-
>
|
|
8
|
+
> **Status: reference implementation of [KIP-12](https://github.com/kaspanet/kips/pull/21) (draft, under revival).**
|
|
9
|
+
> This standard has been folded into KIP-12, which is now the authoritative specification; this package
|
|
10
|
+
> implements it exactly — canonical names only (`kaspa:provider` announce, bare network ids,
|
|
11
|
+
> `chainChanged`). [SPEC.md](SPEC.md) tracks the same contract. Versioning policy: SPEC §7.
|
|
10
12
|
|
|
11
13
|
```bash
|
|
12
14
|
npm install kaspa-wallet-standard
|
|
@@ -55,7 +57,7 @@ const provider = {
|
|
|
55
57
|
getPublicKey: () => w.request('kas:get_account').then(a => a.publicKey),
|
|
56
58
|
signMessage: (m) => w.request('kas:sign_message', m),
|
|
57
59
|
signPskt: ({ txJsonString, options }) => w.request('kas:sign_tx', {
|
|
58
|
-
networkId: /*
|
|
60
|
+
networkId: /* whatever id YOUR bridge expects — this is the wallet's own dialect */ 'testnet-10',
|
|
59
61
|
txJson: txJsonString,
|
|
60
62
|
scripts: options.signInputs.map(s => ({ inputIndex: s.index, scriptHex: '', signType: 'All' })),
|
|
61
63
|
}),
|
|
@@ -74,7 +76,7 @@ const detail = Object.freeze({
|
|
|
74
76
|
info: Object.freeze({ uuid: crypto.randomUUID(), name: 'YourWallet', icon: 'data:…', rdns: 'com.yourwallet' }),
|
|
75
77
|
provider: window.yourwallet,
|
|
76
78
|
});
|
|
77
|
-
const announce = () => window.dispatchEvent(new CustomEvent('kaspa:
|
|
79
|
+
const announce = () => window.dispatchEvent(new CustomEvent('kaspa:provider', { detail }));
|
|
78
80
|
window.addEventListener('kaspa:requestProvider', announce);
|
|
79
81
|
announce();
|
|
80
82
|
```
|
|
@@ -108,10 +110,12 @@ Using it in your wallet or dApp? Open a PR to add yourself.
|
|
|
108
110
|
|
|
109
111
|
## Status & contributing
|
|
110
112
|
|
|
111
|
-
This is
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
This package is the reference implementation of **KIP-12**, the (draft) Kaspa wallet provider and
|
|
114
|
+
discovery standard — the original draft lives at [kaspanet/kips#21](https://github.com/kaspanet/kips/pull/21)
|
|
115
|
+
and a revived, consolidated revision is being prepared with the original authors. The bar for
|
|
116
|
+
ratification: the handshake proven across at least two independently-developed wallets. If you're a
|
|
117
|
+
wallet or dApp author, review happens on the KIP; implementation issues are welcome
|
|
118
|
+
[here](../../issues). See [SPEC.md](SPEC.md) for the contract, security model, and versioning policy.
|
|
115
119
|
|
|
116
120
|
## License
|
|
117
121
|
|
package/SPEC.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Kaspa Wallet Standard
|
|
2
2
|
|
|
3
|
-
**Status:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
**Status: folded into [KIP-12](https://github.com/kaspanet/kips/pull/21) (draft, under revival).**
|
|
4
|
+
This document began as an independent proposal and has since been consolidated into KIP-12, the
|
|
5
|
+
Kaspa wallet provider and discovery standard — **the KIP is the authoritative specification**; this
|
|
6
|
+
file tracks the same contract as the companion to the reference implementation in this package.
|
|
7
|
+
Standards review happens on the KIP; implementation issues are welcome here.
|
|
7
8
|
|
|
8
|
-
**Version:** 0.
|
|
9
|
+
**Version:** 0.2 · **Naming:** KIP-12 canonical only (see §7 for the versioning policy).
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
@@ -30,7 +31,7 @@ It has two parts: a **provider interface** (§3–4) and a **discovery handshake
|
|
|
30
31
|
|
|
31
32
|
- **Provider** — the object a wallet injects/exposes that a dApp calls to get accounts, network, and
|
|
32
33
|
signatures (§3).
|
|
33
|
-
- **Announce** — a wallet advertising its provider via the `kaspa:
|
|
34
|
+
- **Announce** — a wallet advertising its provider via the `kaspa:provider` event (§5).
|
|
34
35
|
- **dApp** — any web page that wants to use a Kaspa wallet.
|
|
35
36
|
- Keywords **MUST**, **SHOULD**, **MAY** are used per [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
|
|
36
37
|
|
|
@@ -50,7 +51,7 @@ method is **optional** and MUST be capability-checked by the dApp (`typeof p.sig
|
|
|
50
51
|
| `signMessage(msg): Promise<string>` | no | KIP-5 message signing → Schnorr signature hex. |
|
|
51
52
|
| `signPskt({ txJsonString, options }): Promise<string>` | no | Sign specific inputs of a transaction (§4). |
|
|
52
53
|
| `disconnect(origin?): Promise<void>` | no | Drop the site's authorization. |
|
|
53
|
-
| `on / removeListener(event, handler)` | no | `accountsChanged`, `
|
|
54
|
+
| `on / removeListener(event, handler)` | no | `accountsChanged`, `chainChanged`. |
|
|
54
55
|
|
|
55
56
|
A wallet that implements only `requestAccounts` is valid — it will connect and display balances in a
|
|
56
57
|
dApp, which simply disables features that need the missing methods.
|
|
@@ -74,7 +75,7 @@ array of `{ index, sighashType }`, and returns the re-serialized signed transact
|
|
|
74
75
|
|
|
75
76
|
Two events on `window`, mirroring EIP-6963 replay semantics:
|
|
76
77
|
|
|
77
|
-
- **`kaspa:
|
|
78
|
+
- **`kaspa:provider`** — a `CustomEvent` dispatched by the **wallet**. `detail` is a **frozen**
|
|
78
79
|
`{ info, provider }` (see §5.1). MUST be dispatched on load, and re-dispatched on every
|
|
79
80
|
`kaspa:requestProvider`.
|
|
80
81
|
- **`kaspa:requestProvider`** — a plain `Event` dispatched by the **dApp** to ask present wallets to
|
|
@@ -83,7 +84,7 @@ Two events on `window`, mirroring EIP-6963 replay semantics:
|
|
|
83
84
|
**Sequence**
|
|
84
85
|
|
|
85
86
|
1. On load, the wallet registers a `kaspa:requestProvider` listener that re-announces, then announces once.
|
|
86
|
-
2. The dApp registers its `kaspa:
|
|
87
|
+
2. The dApp registers its `kaspa:provider` listener (kept alive for the page lifetime), then
|
|
87
88
|
dispatches `kaspa:requestProvider`.
|
|
88
89
|
3. Late arrival is covered from both directions: a late wallet announces unprompted on load; a late dApp's
|
|
89
90
|
request triggers a replay from every wallet already present.
|
|
@@ -113,16 +114,22 @@ type KaspaProviderDetail = { info: KaspaProviderInfo; provider: KaspaProvider };
|
|
|
113
114
|
|
|
114
115
|
## 6. Network ids
|
|
115
116
|
|
|
116
|
-
Canonical strings (also exported as `KASPA_NETWORKS`):
|
|
117
|
+
Canonical strings (also exported as `KASPA_NETWORKS`) — the node's own network ids, per KIP-12:
|
|
117
118
|
|
|
118
|
-
`
|
|
119
|
+
`mainnet` · `testnet-10` · `testnet-11` · `devnet`
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
Some injected wallet APIs speak a `kaspa_`-prefixed dialect (`kaspa_mainnet`, `kaspa_testnet_10`);
|
|
122
|
+
adapters SHOULD normalize those to canonical — the package exports `normalizeKaspaNetworkId()` for
|
|
123
|
+
exactly this.
|
|
121
124
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
## 7. Compatibility and versioning
|
|
126
|
+
|
|
127
|
+
**v0.2 is a clean break to KIP-12 canonical naming** (announce event, network ids, change event) —
|
|
128
|
+
made while this package's own deployments were the only consumers of the v0.1 names, which are
|
|
129
|
+
retired outright (no dual bridge). From v0.2 the contract is stable under KIP-12's own rule: the
|
|
130
|
+
event names and every field defined above never change meaning or type; the standard evolves **only
|
|
131
|
+
by adding new OPTIONAL fields/methods**; breaking changes, if ever unavoidable, ship under a **new
|
|
132
|
+
event name** — never by mutating these.
|
|
126
133
|
|
|
127
134
|
## 8. Security considerations
|
|
128
135
|
|
|
@@ -131,7 +138,7 @@ unavoidable, would ship under a **new event name** — never by mutating these.
|
|
|
131
138
|
icons (a remote URL is a tracking/spoofing vector). The reference `requestKaspaWallets` **enforces this
|
|
132
139
|
for you** — it strips any non-`data:` icon (delivers it as `''`) before handing the announce to your
|
|
133
140
|
callback.
|
|
134
|
-
- Any page script can dispatch `kaspa:
|
|
141
|
+
- Any page script can dispatch `kaspa:provider`, including one that claims another wallet's
|
|
135
142
|
`name`/`rdns`. Treat the provider as untrusted until the user explicitly connects; the wallet's own
|
|
136
143
|
connect/sign prompt — rendered by the extension, outside page control — is the trust boundary, not the
|
|
137
144
|
announce.
|
|
@@ -152,9 +159,12 @@ unavoidable, would ship under a **new event name** — never by mutating these.
|
|
|
152
159
|
discovery handshake in production, and ships built-in adapters for KasWare and Kastle behind the same
|
|
153
160
|
provider interface.
|
|
154
161
|
|
|
155
|
-
## 10.
|
|
162
|
+
## 10. Standardization status
|
|
156
163
|
|
|
157
|
-
This document
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
This document **has been folded into KIP-12** ([original draft PR](https://github.com/kaspanet/kips/pull/21);
|
|
165
|
+
a revived, consolidated revision is being prepared with the original authors) — the KIP is where the
|
|
166
|
+
standard lives and where review happens. The bar before proposing ratification stands: the handshake
|
|
167
|
+
proven across **at least two independently-developed wallets** (in progress: KRON's dApp side against
|
|
168
|
+
the Rift wallet PoC, plus the KasWare/Kastle adapters), and the provider interface exercised by
|
|
169
|
+
covenant-grade `signPskt` on-chain. Wallet and dApp authors: raise standards questions on the KIP,
|
|
170
|
+
implementation issues here.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/** Canonical network ids used by `getNetwork()` / `switchNetwork()`. String literals are the contract;
|
|
2
2
|
* this object is a convenience so integrators don't hand-type them. */
|
|
3
3
|
declare const KASPA_NETWORKS: {
|
|
4
|
-
readonly MAINNET: "
|
|
5
|
-
readonly TESTNET_10: "
|
|
6
|
-
readonly TESTNET_11: "
|
|
7
|
-
readonly DEVNET: "
|
|
4
|
+
readonly MAINNET: "mainnet";
|
|
5
|
+
readonly TESTNET_10: "testnet-10";
|
|
6
|
+
readonly TESTNET_11: "testnet-11";
|
|
7
|
+
readonly DEVNET: "devnet";
|
|
8
8
|
};
|
|
9
9
|
type KaspaNetworkId = (typeof KASPA_NETWORKS)[keyof typeof KASPA_NETWORKS];
|
|
10
|
+
/** Normalize any wallet-reported network id to the canonical KIP-12 form: legacy `kaspa_`-prefixed
|
|
11
|
+
* variants (`kaspa_mainnet`, `kaspa_testnet_10` — v0.1 of this package, KasWare's injected API) map to
|
|
12
|
+
* the node's bare ids (`mainnet`, `testnet-10`). Canonical ids pass through unchanged. */
|
|
13
|
+
declare const normalizeKaspaNetworkId: (id: string) => string;
|
|
10
14
|
/** Identity a wallet announces about itself. `name`/`icon` are DISPLAY hints — never trust signals. */
|
|
11
15
|
type KaspaProviderInfo = {
|
|
12
16
|
/** UUIDv4, freshly generated per page load — instance identity, used only for dedupe. */
|
|
@@ -27,6 +31,95 @@ type KaspaSignInput = {
|
|
|
27
31
|
index: number;
|
|
28
32
|
sighashType: number;
|
|
29
33
|
};
|
|
34
|
+
/** Argument of {@link KaspaProvider.signPskt}: a Kaspa Safe-JSON transaction plus the explicit list of
|
|
35
|
+
* inputs to sign. The wallet MUST sign ONLY these inputs and leave the rest untouched (fund-safety). */
|
|
36
|
+
interface KaspaSignPsktArg {
|
|
37
|
+
txJsonString: string;
|
|
38
|
+
options: {
|
|
39
|
+
signInputs: KaspaSignInput[];
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Simple transfer (SPEC §4.2). Amount is sompi as a base-10 **string** — float KAS is FORBIDDEN in the
|
|
43
|
+
* wire contract: IEEE-754 doubles cannot represent every sompi value exactly, and a rounding error in an
|
|
44
|
+
* amount is a fund-safety defect. */
|
|
45
|
+
interface KaspaSimpleTransfer {
|
|
46
|
+
to: string;
|
|
47
|
+
amountSompi: string;
|
|
48
|
+
}
|
|
49
|
+
/** Provider events and their payloads (SPEC §6). */
|
|
50
|
+
interface KaspaProviderEventMap {
|
|
51
|
+
/** The authorized account list changed (switch, revoke). */
|
|
52
|
+
accountsChanged: string[];
|
|
53
|
+
/** The wallet switched networks; payload is a network id (see {@link KaspaNetworkId}). */
|
|
54
|
+
chainChanged: string;
|
|
55
|
+
}
|
|
56
|
+
type KaspaProviderEvent = keyof KaspaProviderEventMap;
|
|
57
|
+
/** Canonical `request()` registry (SPEC §7): every §3 method under a namespaced name, so a
|
|
58
|
+
* `request()`-only client stays possible. Future KIPs/protocols extend this map via declaration
|
|
59
|
+
* merging; wallets serve vendor methods under their own namespace (e.g. `"rift:…"`). */
|
|
60
|
+
interface KaspaRequestMap {
|
|
61
|
+
'kaspa:requestAccounts': {
|
|
62
|
+
args: [];
|
|
63
|
+
result: string[];
|
|
64
|
+
};
|
|
65
|
+
'kaspa:getAccounts': {
|
|
66
|
+
args: [];
|
|
67
|
+
result: string[];
|
|
68
|
+
};
|
|
69
|
+
'kaspa:getNetwork': {
|
|
70
|
+
args: [];
|
|
71
|
+
result: string;
|
|
72
|
+
};
|
|
73
|
+
'kaspa:switchNetwork': {
|
|
74
|
+
args: [networkId: string];
|
|
75
|
+
result: void;
|
|
76
|
+
};
|
|
77
|
+
'kaspa:getPublicKey': {
|
|
78
|
+
args: [];
|
|
79
|
+
result: string;
|
|
80
|
+
};
|
|
81
|
+
'kaspa:signMessage': {
|
|
82
|
+
args: [message: string];
|
|
83
|
+
result: string;
|
|
84
|
+
};
|
|
85
|
+
'kaspa:signPskt': {
|
|
86
|
+
args: [arg: KaspaSignPsktArg];
|
|
87
|
+
result: string;
|
|
88
|
+
};
|
|
89
|
+
'kaspa:sendTransaction': {
|
|
90
|
+
args: [tx: KaspaSimpleTransfer];
|
|
91
|
+
result: string;
|
|
92
|
+
};
|
|
93
|
+
'kaspa:signPskb': {
|
|
94
|
+
args: [pskb: string];
|
|
95
|
+
result: string;
|
|
96
|
+
};
|
|
97
|
+
'kaspa:broadcastPskb': {
|
|
98
|
+
args: [pskb: string];
|
|
99
|
+
result: string[];
|
|
100
|
+
};
|
|
101
|
+
'kaspa:disconnect': {
|
|
102
|
+
args: [origin?: string];
|
|
103
|
+
result: void;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
type KaspaRequestMethod = keyof KaspaRequestMap;
|
|
107
|
+
/** Standard rejection codes (SPEC §8, mirroring EIP-1193 for developer familiarity). */
|
|
108
|
+
declare const KIP12_ERRORS: {
|
|
109
|
+
/** The user rejected the request. */
|
|
110
|
+
readonly USER_REJECTED: 4001;
|
|
111
|
+
/** The origin is not authorized (call `requestAccounts` first). */
|
|
112
|
+
readonly UNAUTHORIZED: 4100;
|
|
113
|
+
/** The wallet does not support the requested method. */
|
|
114
|
+
readonly UNSUPPORTED_METHOD: 4200;
|
|
115
|
+
/** The wallet is locked or unavailable. */
|
|
116
|
+
readonly WALLET_UNAVAILABLE: 4900;
|
|
117
|
+
};
|
|
118
|
+
interface Kip12Error {
|
|
119
|
+
message: string;
|
|
120
|
+
code?: number | string;
|
|
121
|
+
details?: unknown;
|
|
122
|
+
}
|
|
30
123
|
/**
|
|
31
124
|
* The raw provider surface a wallet exposes. Deliberately shaped like the widely-deployed injected APIs
|
|
32
125
|
* (KasWare's `window.kasware`) so an existing wallet can usually announce its injected object as-is.
|
|
@@ -52,21 +145,30 @@ interface KaspaProvider {
|
|
|
52
145
|
getPublicKey?(): Promise<string>;
|
|
53
146
|
/** KIP-5 message signing; resolves to the Schnorr signature hex. */
|
|
54
147
|
signMessage?(message: string): Promise<string>;
|
|
55
|
-
/** Sign ONLY the listed inputs of a Kaspa Safe-JSON transaction and return the signed Safe JSON
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
148
|
+
/** Sign ONLY the listed inputs of a Kaspa Safe-JSON transaction and return the signed Safe JSON —
|
|
149
|
+
* the covenant-grade signing primitive (fund-safety rules apply). */
|
|
150
|
+
signPskt?(arg: KaspaSignPsktArg): Promise<string>;
|
|
151
|
+
/** Simple transfer (SPEC §4.2); resolves to the transaction id. */
|
|
152
|
+
sendTransaction?(tx: KaspaSimpleTransfer): Promise<string>;
|
|
153
|
+
/** Sign a PSKB bundle (SPEC §4.3); the per-input discipline of {@link signPskt} applies. */
|
|
154
|
+
signPskb?(pskb: string): Promise<string>;
|
|
155
|
+
/** Broadcast a signed PSKB; resolves to transaction ids. */
|
|
156
|
+
broadcastPskb?(pskb: string): Promise<string[]>;
|
|
62
157
|
disconnect?(origin?: string): Promise<void>;
|
|
63
|
-
|
|
64
|
-
|
|
158
|
+
/** Subscribe to a provider event; the handler payload is inferred from the event
|
|
159
|
+
* ({@link KaspaProviderEventMap}). `chainChanged` is the KIP-12 network-change event. */
|
|
160
|
+
on?<E extends KaspaProviderEvent>(event: E, handler: (payload: KaspaProviderEventMap[E]) => void): void;
|
|
161
|
+
removeListener?<E extends KaspaProviderEvent>(event: E, handler: (payload: KaspaProviderEventMap[E]) => void): void;
|
|
162
|
+
/** Typed protocol-extension escape hatch (SPEC §7). Extends the surface, never the privileges — a
|
|
163
|
+
* method served here is still subject to the authorization gate and fund-safety rules. An unknown
|
|
164
|
+
* method MUST reject with code 4200 ({@link KIP12_ERRORS}). */
|
|
165
|
+
request?<M extends KaspaRequestMethod>(method: M, args: KaspaRequestMap[M]['args']): Promise<KaspaRequestMap[M]['result']>;
|
|
65
166
|
}
|
|
66
167
|
/** Dispatched on `window` by a dApp to ask all present wallets to (re-)announce themselves. */
|
|
67
168
|
declare const KASPA_REQUEST_PROVIDER_EVENT = "kaspa:requestProvider";
|
|
68
|
-
/** Dispatched on `window` by a wallet; `detail` is a frozen {@link KaspaProviderDetail}.
|
|
69
|
-
|
|
169
|
+
/** Dispatched on `window` by a wallet; `detail` is a frozen {@link KaspaProviderDetail}.
|
|
170
|
+
* KIP-12 canonical name. */
|
|
171
|
+
declare const KASPA_ANNOUNCE_PROVIDER_EVENT = "kaspa:provider";
|
|
70
172
|
/** The `detail` of a `kaspa:announceProvider` CustomEvent. */
|
|
71
173
|
type KaspaProviderDetail = {
|
|
72
174
|
info: KaspaProviderInfo;
|
|
@@ -95,4 +197,4 @@ declare function announceKaspaWallet(info: KaspaProviderInfo, provider: KaspaPro
|
|
|
95
197
|
*/
|
|
96
198
|
declare function requestKaspaWallets(onAnnounce: (detail: KaspaProviderDetail) => void): () => void;
|
|
97
199
|
|
|
98
|
-
export { KASPA_ANNOUNCE_PROVIDER_EVENT, KASPA_NETWORKS, KASPA_REQUEST_PROVIDER_EVENT, type KaspaNetworkId, type KaspaProvider, type KaspaProviderDetail, type KaspaProviderInfo, type KaspaSignInput, announceKaspaWallet, requestKaspaWallets };
|
|
200
|
+
export { KASPA_ANNOUNCE_PROVIDER_EVENT, KASPA_NETWORKS, KASPA_REQUEST_PROVIDER_EVENT, KIP12_ERRORS, type KaspaNetworkId, type KaspaProvider, type KaspaProviderDetail, type KaspaProviderEvent, type KaspaProviderEventMap, type KaspaProviderInfo, type KaspaRequestMap, type KaspaRequestMethod, type KaspaSignInput, type KaspaSignPsktArg, type KaspaSimpleTransfer, type Kip12Error, announceKaspaWallet, normalizeKaspaNetworkId, requestKaspaWallets };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var KASPA_NETWORKS = {
|
|
3
|
-
MAINNET: "
|
|
4
|
-
TESTNET_10: "
|
|
5
|
-
TESTNET_11: "
|
|
6
|
-
DEVNET: "
|
|
3
|
+
MAINNET: "mainnet",
|
|
4
|
+
TESTNET_10: "testnet-10",
|
|
5
|
+
TESTNET_11: "testnet-11",
|
|
6
|
+
DEVNET: "devnet"
|
|
7
|
+
};
|
|
8
|
+
var normalizeKaspaNetworkId = (id) => id.startsWith("kaspa_") ? id.slice("kaspa_".length).replace(/_/g, "-") : id;
|
|
9
|
+
var KIP12_ERRORS = {
|
|
10
|
+
/** The user rejected the request. */
|
|
11
|
+
USER_REJECTED: 4001,
|
|
12
|
+
/** The origin is not authorized (call `requestAccounts` first). */
|
|
13
|
+
UNAUTHORIZED: 4100,
|
|
14
|
+
/** The wallet does not support the requested method. */
|
|
15
|
+
UNSUPPORTED_METHOD: 4200,
|
|
16
|
+
/** The wallet is locked or unavailable. */
|
|
17
|
+
WALLET_UNAVAILABLE: 4900
|
|
7
18
|
};
|
|
8
19
|
var KASPA_REQUEST_PROVIDER_EVENT = "kaspa:requestProvider";
|
|
9
|
-
var KASPA_ANNOUNCE_PROVIDER_EVENT = "kaspa:
|
|
20
|
+
var KASPA_ANNOUNCE_PROVIDER_EVENT = "kaspa:provider";
|
|
10
21
|
function announceKaspaWallet(info, provider) {
|
|
11
22
|
if (typeof window === "undefined") return () => {
|
|
12
23
|
};
|
|
@@ -38,7 +49,9 @@ export {
|
|
|
38
49
|
KASPA_ANNOUNCE_PROVIDER_EVENT,
|
|
39
50
|
KASPA_NETWORKS,
|
|
40
51
|
KASPA_REQUEST_PROVIDER_EVENT,
|
|
52
|
+
KIP12_ERRORS,
|
|
41
53
|
announceKaspaWallet,
|
|
54
|
+
normalizeKaspaNetworkId,
|
|
42
55
|
requestKaspaWallets
|
|
43
56
|
};
|
|
44
57
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Kaspa Wallet Standard — reference implementation.\n//\n// A PROPOSED standard (see SPEC.md) for how a Kaspa dApp and a Kaspa wallet find and talk to each other,\n// with two parts:\n// • Part 1 — the provider interface a wallet exposes (accounts, network, signing).\n// • Part 2 — an EIP-6963-style discovery handshake so a dApp finds every present wallet with zero\n// per-wallet code, and any future wallet self-registers by dispatching one event.\n//\n// This package has ZERO runtime dependencies and is safe to import in Node (all browser access is guarded).\n// The wire contract — the two event names and the existing fields below — is FROZEN: it never changes.\n// Evolution happens only by adding new OPTIONAL fields. A wallet written against this file keeps working\n// against every future version.\n\n// ============================================================================================\n// Part 1 — Provider interface\n// ============================================================================================\n\n/** Canonical network ids used by `getNetwork()` / `switchNetwork()`. String literals are the contract;\n * this object is a convenience so integrators don't hand-type them. */\nexport const KASPA_NETWORKS = {\n MAINNET: 'kaspa_mainnet',\n TESTNET_10: 'kaspa_testnet_10',\n TESTNET_11: 'kaspa_testnet_11',\n DEVNET: 'kaspa_devnet',\n} as const;\n\nexport type KaspaNetworkId = (typeof KASPA_NETWORKS)[keyof typeof KASPA_NETWORKS];\n\n/** Identity a wallet announces about itself. `name`/`icon` are DISPLAY hints — never trust signals. */\nexport type KaspaProviderInfo = {\n /** UUIDv4, freshly generated per page load — instance identity, used only for dedupe. */\n uuid: string;\n /** Human-readable wallet name shown in pickers, e.g. \"Kastle\". */\n name: string;\n /** Wallet icon as a `data:` URI (SVG/PNG) — a DISPLAY hint, never a trust signal. A remote URL is a\n * tracking/spoofing vector, so the dApp-side {@link requestKaspaWallets} STRIPS any non-`data:` icon\n * (delivers it as `''`) — a dApp can never be handed a remote URL through the handshake. */\n icon: string;\n /** Reverse-DNS identifier, e.g. \"com.kasware\" — STABLE across page loads and versions. Strongly\n * recommended: it is what lets a dApp silently restore a session with your wallet after a reload. */\n rdns?: string;\n};\n\n/** One input a wallet is asked to sign, by position. `sighashType` 1 = SIGHASH_ALL (the only value\n * KRON uses today); a wallet MUST refuse a type it does not implement rather than guess. */\nexport type KaspaSignInput = { index: number; sighashType: number };\n\n/**\n * The raw provider surface a wallet exposes. Deliberately shaped like the widely-deployed injected APIs\n * (KasWare's `window.kasware`) so an existing wallet can usually announce its injected object as-is.\n *\n * Only `requestAccounts` is MANDATORY. Everything else is OPTIONAL and MUST be capability-checked by the\n * dApp (`typeof provider.signPskt === 'function'`) — a dApp degrades gracefully (e.g. disables trading)\n * when a method is absent, rather than refusing to list the wallet.\n *\n * FUND-SAFETY RULE (wallet side): `signPskt` MUST sign ONLY the inputs listed in `options.signInputs`,\n * and MUST leave every other input untouched. Kaspa covenant transactions carry pre-authorized inputs\n * that must NOT be re-signed; a signature over an unlisted input, or over the wrong sighash, is a\n * fund-safety bug, not a cosmetic mismatch. See SPEC.md §\"Security considerations\".\n */\nexport interface KaspaProvider {\n /** Connect: prompt the user if needed; resolve to the authorized address list (active address first). */\n requestAccounts(): Promise<string[]>;\n /** Already-authorized accounts WITHOUT prompting (empty array if none) — enables silent session restore. */\n getAccounts?(): Promise<string[]>;\n /** Current network id (see {@link KaspaNetworkId}). */\n getNetwork?(): Promise<string>;\n switchNetwork?(networkId: string): Promise<void>;\n /** The active account's public key hex (compressed 33-byte or x-only 32-byte — both accepted). */\n getPublicKey?(): Promise<string>;\n /** KIP-5 message signing; resolves to the Schnorr signature hex. */\n signMessage?(message: string): Promise<string>;\n /** Sign ONLY the listed inputs of a Kaspa Safe-JSON transaction and return the signed Safe JSON. */\n signPskt?(arg: { txJsonString: string; options: { signInputs: KaspaSignInput[] } }): Promise<string>;\n disconnect?(origin?: string): Promise<void>;\n on?(event: 'accountsChanged' | 'networkChanged', handler: (...args: any[]) => void): void;\n removeListener?(event: string, handler: (...args: any[]) => void): void;\n}\n\n// ============================================================================================\n// Part 2 — Discovery handshake\n// ============================================================================================\n\n/** Dispatched on `window` by a dApp to ask all present wallets to (re-)announce themselves. */\nexport const KASPA_REQUEST_PROVIDER_EVENT = 'kaspa:requestProvider';\n/** Dispatched on `window` by a wallet; `detail` is a frozen {@link KaspaProviderDetail}. */\nexport const KASPA_ANNOUNCE_PROVIDER_EVENT = 'kaspa:announceProvider';\n\n/** The `detail` of a `kaspa:announceProvider` CustomEvent. */\nexport type KaspaProviderDetail = { info: KaspaProviderInfo; provider: KaspaProvider };\n\n/**\n * WALLET SIDE — call once when your content script loads. Announces immediately AND replays on every\n * `kaspa:requestProvider` (so a dApp that loads after you still finds you). Returns an unsubscribe\n * (rarely needed; e.g. extension teardown). No-op outside a window context.\n */\nexport function announceKaspaWallet(info: KaspaProviderInfo, provider: KaspaProvider): () => void {\n if (typeof window === 'undefined') return () => {};\n const detail: KaspaProviderDetail = Object.freeze({ info: Object.freeze({ ...info }), provider });\n const announce = () =>\n window.dispatchEvent(new CustomEvent(KASPA_ANNOUNCE_PROVIDER_EVENT, { detail }));\n window.addEventListener(KASPA_REQUEST_PROVIDER_EVENT, announce);\n announce();\n return () => window.removeEventListener(KASPA_REQUEST_PROVIDER_EVENT, announce);\n}\n\n/** An icon is safe to render only as an inline `data:` URI. A remote URL is a tracking/spoofing vector\n * (SPEC §8), so the dApp-side handshake refuses it. */\nconst isSafeIcon = (icon: unknown): icon is string => typeof icon === 'string' && /^data:/i.test(icon.trim());\n\n/**\n * dApp SIDE — register `onAnnounce` (fires once per announce event, including replays; dedupe by\n * `info.rdns ?? info.uuid` yourself), then request announcements from wallets already present. Keep the\n * subscription alive for the page lifetime to catch late-injecting wallets. Returns an unsubscribe.\n *\n * Two receiver-side safety filters run before an announce reaches `onAnnounce`:\n * • Malformed announces (missing `uuid`/`name`, or no `requestAccounts`) are dropped, never delivered.\n * • A non-`data:` `icon` (a remote-URL tracking/spoofing vector, SPEC §8) is STRIPPED to `''` — the\n * wallet is still surfaced, just without the unsafe icon. An absent or valid `data:` icon passes\n * through untouched (the announce stays the wallet's original frozen object).\n *\n * NEITHER filter authenticates the announcer: any page script can announce (SPEC §8), so treat a provider\n * as untrusted until the user explicitly connects — that connect gesture is the trust boundary, not this\n * handshake. No-op outside a window context.\n */\nexport function requestKaspaWallets(onAnnounce: (detail: KaspaProviderDetail) => void): () => void {\n if (typeof window === 'undefined') return () => {};\n const listener = (e: Event) => {\n const detail = (e as CustomEvent<KaspaProviderDetail>).detail;\n if (!detail?.info?.uuid || !detail?.info?.name) return;\n if (typeof detail.provider?.requestAccounts !== 'function') return;\n if (detail.info.icon != null && !isSafeIcon(detail.info.icon)) {\n // Strip the unsafe icon but keep the wallet: deliver a sanitized copy; the wallet's frozen\n // original is left untouched (a valid/absent icon skips this and passes through frozen).\n onAnnounce({ info: { ...detail.info, icon: '' }, provider: detail.provider });\n return;\n }\n onAnnounce(detail);\n };\n window.addEventListener(KASPA_ANNOUNCE_PROVIDER_EVENT, listener);\n window.dispatchEvent(new Event(KASPA_REQUEST_PROVIDER_EVENT));\n return () => window.removeEventListener(KASPA_ANNOUNCE_PROVIDER_EVENT, listener);\n}\n"],"mappings":";AAmBO,IAAM,iBAAiB;AAAA,EAC5B,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ;AACV;AA4DO,IAAM,+BAA+B;AAErC,IAAM,gCAAgC;AAUtC,SAAS,oBAAoB,MAAyB,UAAqC;AAChG,MAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,EAAC;AACjD,QAAM,SAA8B,OAAO,OAAO,EAAE,MAAM,OAAO,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,SAAS,CAAC;AAChG,QAAM,WAAW,MACf,OAAO,cAAc,IAAI,YAAY,+BAA+B,EAAE,OAAO,CAAC,CAAC;AACjF,SAAO,iBAAiB,8BAA8B,QAAQ;AAC9D,WAAS;AACT,SAAO,MAAM,OAAO,oBAAoB,8BAA8B,QAAQ;AAChF;AAIA,IAAM,aAAa,CAAC,SAAkC,OAAO,SAAS,YAAY,UAAU,KAAK,KAAK,KAAK,CAAC;AAiBrG,SAAS,oBAAoB,YAA+D;AACjG,MAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,EAAC;AACjD,QAAM,WAAW,CAAC,MAAa;AAC7B,UAAM,SAAU,EAAuC;AACvD,QAAI,CAAC,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,KAAM;AAChD,QAAI,OAAO,OAAO,UAAU,oBAAoB,WAAY;AAC5D,QAAI,OAAO,KAAK,QAAQ,QAAQ,CAAC,WAAW,OAAO,KAAK,IAAI,GAAG;AAG7D,iBAAW,EAAE,MAAM,EAAE,GAAG,OAAO,MAAM,MAAM,GAAG,GAAG,UAAU,OAAO,SAAS,CAAC;AAC5E;AAAA,IACF;AACA,eAAW,MAAM;AAAA,EACnB;AACA,SAAO,iBAAiB,+BAA+B,QAAQ;AAC/D,SAAO,cAAc,IAAI,MAAM,4BAA4B,CAAC;AAC5D,SAAO,MAAM,OAAO,oBAAoB,+BAA+B,QAAQ;AACjF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Kaspa Wallet Standard — reference implementation.\n//\n// A PROPOSED standard (see SPEC.md) for how a Kaspa dApp and a Kaspa wallet find and talk to each other,\n// with two parts:\n// • Part 1 — the provider interface a wallet exposes (accounts, network, signing).\n// • Part 2 — an EIP-6963-style discovery handshake so a dApp finds every present wallet with zero\n// per-wallet code, and any future wallet self-registers by dispatching one event.\n//\n// This package has ZERO runtime dependencies and is safe to import in Node (all browser access is guarded).\n//\n// v0.2 — KIP-12. This package is the reference implementation of the revived KIP-12 and speaks ONLY its\n// canonical names: `kaspa:provider` announce, the node's bare network ids (`mainnet`, `testnet-10`),\n// `chainChanged`. The v0.1 names (`kaspa:announceProvider`, `kaspa_mainnet`, `networkChanged`) are gone —\n// a clean break, made while KRON was the only deployed consumer (updated in lockstep). From here the\n// wire contract evolves per KIP-12 itself: add-only fields/methods; breaking = new event name.\n//\n// The type surface below is kept at 1:1 parity with the KIP's formal interfaces (`kip-0012/interfaces.ts`\n// in kaspanet/kips) — same provider methods, `request()` registry, event map, and error codes. The two\n// runtime helpers (announce/request) implement the discovery handshake + receiver-side security filters\n// that the spec describes; a wallet/dApp gets a correct, zero-dependency implementation for free.\n\n// ============================================================================================\n// Part 1 — Provider interface\n// ============================================================================================\n\n/** Canonical network ids used by `getNetwork()` / `switchNetwork()`. String literals are the contract;\n * this object is a convenience so integrators don't hand-type them. */\nexport const KASPA_NETWORKS = {\n MAINNET: 'mainnet',\n TESTNET_10: 'testnet-10',\n TESTNET_11: 'testnet-11',\n DEVNET: 'devnet',\n} as const;\n\nexport type KaspaNetworkId = (typeof KASPA_NETWORKS)[keyof typeof KASPA_NETWORKS];\n\n/** Normalize any wallet-reported network id to the canonical KIP-12 form: legacy `kaspa_`-prefixed\n * variants (`kaspa_mainnet`, `kaspa_testnet_10` — v0.1 of this package, KasWare's injected API) map to\n * the node's bare ids (`mainnet`, `testnet-10`). Canonical ids pass through unchanged. */\nexport const normalizeKaspaNetworkId = (id: string): string =>\n id.startsWith('kaspa_') ? id.slice('kaspa_'.length).replace(/_/g, '-') : id;\n\n/** Identity a wallet announces about itself. `name`/`icon` are DISPLAY hints — never trust signals. */\nexport type KaspaProviderInfo = {\n /** UUIDv4, freshly generated per page load — instance identity, used only for dedupe. */\n uuid: string;\n /** Human-readable wallet name shown in pickers, e.g. \"Kastle\". */\n name: string;\n /** Wallet icon as a `data:` URI (SVG/PNG) — a DISPLAY hint, never a trust signal. A remote URL is a\n * tracking/spoofing vector, so the dApp-side {@link requestKaspaWallets} STRIPS any non-`data:` icon\n * (delivers it as `''`) — a dApp can never be handed a remote URL through the handshake. */\n icon: string;\n /** Reverse-DNS identifier, e.g. \"com.kasware\" — STABLE across page loads and versions. Strongly\n * recommended: it is what lets a dApp silently restore a session with your wallet after a reload. */\n rdns?: string;\n};\n\n/** One input a wallet is asked to sign, by position. `sighashType` 1 = SIGHASH_ALL (the only value\n * KRON uses today); a wallet MUST refuse a type it does not implement rather than guess. */\nexport type KaspaSignInput = { index: number; sighashType: number };\n\n/** Argument of {@link KaspaProvider.signPskt}: a Kaspa Safe-JSON transaction plus the explicit list of\n * inputs to sign. The wallet MUST sign ONLY these inputs and leave the rest untouched (fund-safety). */\nexport interface KaspaSignPsktArg {\n txJsonString: string;\n options: { signInputs: KaspaSignInput[] };\n}\n\n/** Simple transfer (SPEC §4.2). Amount is sompi as a base-10 **string** — float KAS is FORBIDDEN in the\n * wire contract: IEEE-754 doubles cannot represent every sompi value exactly, and a rounding error in an\n * amount is a fund-safety defect. */\nexport interface KaspaSimpleTransfer {\n to: string;\n amountSompi: string;\n}\n\n/** Provider events and their payloads (SPEC §6). */\nexport interface KaspaProviderEventMap {\n /** The authorized account list changed (switch, revoke). */\n accountsChanged: string[];\n /** The wallet switched networks; payload is a network id (see {@link KaspaNetworkId}). */\n chainChanged: string;\n}\n\nexport type KaspaProviderEvent = keyof KaspaProviderEventMap;\n\n/** Canonical `request()` registry (SPEC §7): every §3 method under a namespaced name, so a\n * `request()`-only client stays possible. Future KIPs/protocols extend this map via declaration\n * merging; wallets serve vendor methods under their own namespace (e.g. `\"rift:…\"`). */\nexport interface KaspaRequestMap {\n 'kaspa:requestAccounts': { args: []; result: string[] };\n 'kaspa:getAccounts': { args: []; result: string[] };\n 'kaspa:getNetwork': { args: []; result: string };\n 'kaspa:switchNetwork': { args: [networkId: string]; result: void };\n 'kaspa:getPublicKey': { args: []; result: string };\n 'kaspa:signMessage': { args: [message: string]; result: string };\n 'kaspa:signPskt': { args: [arg: KaspaSignPsktArg]; result: string };\n 'kaspa:sendTransaction': { args: [tx: KaspaSimpleTransfer]; result: string };\n 'kaspa:signPskb': { args: [pskb: string]; result: string };\n 'kaspa:broadcastPskb': { args: [pskb: string]; result: string[] };\n 'kaspa:disconnect': { args: [origin?: string]; result: void };\n}\n\nexport type KaspaRequestMethod = keyof KaspaRequestMap;\n\n/** Standard rejection codes (SPEC §8, mirroring EIP-1193 for developer familiarity). */\nexport const KIP12_ERRORS = {\n /** The user rejected the request. */\n USER_REJECTED: 4001,\n /** The origin is not authorized (call `requestAccounts` first). */\n UNAUTHORIZED: 4100,\n /** The wallet does not support the requested method. */\n UNSUPPORTED_METHOD: 4200,\n /** The wallet is locked or unavailable. */\n WALLET_UNAVAILABLE: 4900,\n} as const;\n\nexport interface Kip12Error {\n message: string;\n code?: number | string;\n details?: unknown;\n}\n\n/**\n * The raw provider surface a wallet exposes. Deliberately shaped like the widely-deployed injected APIs\n * (KasWare's `window.kasware`) so an existing wallet can usually announce its injected object as-is.\n *\n * Only `requestAccounts` is MANDATORY. Everything else is OPTIONAL and MUST be capability-checked by the\n * dApp (`typeof provider.signPskt === 'function'`) — a dApp degrades gracefully (e.g. disables trading)\n * when a method is absent, rather than refusing to list the wallet.\n *\n * FUND-SAFETY RULE (wallet side): `signPskt` MUST sign ONLY the inputs listed in `options.signInputs`,\n * and MUST leave every other input untouched. Kaspa covenant transactions carry pre-authorized inputs\n * that must NOT be re-signed; a signature over an unlisted input, or over the wrong sighash, is a\n * fund-safety bug, not a cosmetic mismatch. See SPEC.md §\"Security considerations\".\n */\nexport interface KaspaProvider {\n /** Connect: prompt the user if needed; resolve to the authorized address list (active address first). */\n requestAccounts(): Promise<string[]>;\n /** Already-authorized accounts WITHOUT prompting (empty array if none) — enables silent session restore. */\n getAccounts?(): Promise<string[]>;\n /** Current network id (see {@link KaspaNetworkId}). */\n getNetwork?(): Promise<string>;\n switchNetwork?(networkId: string): Promise<void>;\n /** The active account's public key hex (compressed 33-byte or x-only 32-byte — both accepted). */\n getPublicKey?(): Promise<string>;\n /** KIP-5 message signing; resolves to the Schnorr signature hex. */\n signMessage?(message: string): Promise<string>;\n /** Sign ONLY the listed inputs of a Kaspa Safe-JSON transaction and return the signed Safe JSON —\n * the covenant-grade signing primitive (fund-safety rules apply). */\n signPskt?(arg: KaspaSignPsktArg): Promise<string>;\n /** Simple transfer (SPEC §4.2); resolves to the transaction id. */\n sendTransaction?(tx: KaspaSimpleTransfer): Promise<string>;\n /** Sign a PSKB bundle (SPEC §4.3); the per-input discipline of {@link signPskt} applies. */\n signPskb?(pskb: string): Promise<string>;\n /** Broadcast a signed PSKB; resolves to transaction ids. */\n broadcastPskb?(pskb: string): Promise<string[]>;\n disconnect?(origin?: string): Promise<void>;\n /** Subscribe to a provider event; the handler payload is inferred from the event\n * ({@link KaspaProviderEventMap}). `chainChanged` is the KIP-12 network-change event. */\n on?<E extends KaspaProviderEvent>(event: E, handler: (payload: KaspaProviderEventMap[E]) => void): void;\n removeListener?<E extends KaspaProviderEvent>(event: E, handler: (payload: KaspaProviderEventMap[E]) => void): void;\n /** Typed protocol-extension escape hatch (SPEC §7). Extends the surface, never the privileges — a\n * method served here is still subject to the authorization gate and fund-safety rules. An unknown\n * method MUST reject with code 4200 ({@link KIP12_ERRORS}). */\n request?<M extends KaspaRequestMethod>(\n method: M,\n args: KaspaRequestMap[M]['args'],\n ): Promise<KaspaRequestMap[M]['result']>;\n}\n\n// ============================================================================================\n// Part 2 — Discovery handshake\n// ============================================================================================\n\n/** Dispatched on `window` by a dApp to ask all present wallets to (re-)announce themselves. */\nexport const KASPA_REQUEST_PROVIDER_EVENT = 'kaspa:requestProvider';\n/** Dispatched on `window` by a wallet; `detail` is a frozen {@link KaspaProviderDetail}.\n * KIP-12 canonical name. */\nexport const KASPA_ANNOUNCE_PROVIDER_EVENT = 'kaspa:provider';\n\n/** The `detail` of a `kaspa:announceProvider` CustomEvent. */\nexport type KaspaProviderDetail = { info: KaspaProviderInfo; provider: KaspaProvider };\n\n/**\n * WALLET SIDE — call once when your content script loads. Announces immediately AND replays on every\n * `kaspa:requestProvider` (so a dApp that loads after you still finds you). Returns an unsubscribe\n * (rarely needed; e.g. extension teardown). No-op outside a window context.\n */\nexport function announceKaspaWallet(info: KaspaProviderInfo, provider: KaspaProvider): () => void {\n if (typeof window === 'undefined') return () => {};\n const detail: KaspaProviderDetail = Object.freeze({ info: Object.freeze({ ...info }), provider });\n const announce = () =>\n window.dispatchEvent(new CustomEvent(KASPA_ANNOUNCE_PROVIDER_EVENT, { detail }));\n window.addEventListener(KASPA_REQUEST_PROVIDER_EVENT, announce);\n announce();\n return () => window.removeEventListener(KASPA_REQUEST_PROVIDER_EVENT, announce);\n}\n\n/** An icon is safe to render only as an inline `data:` URI. A remote URL is a tracking/spoofing vector\n * (SPEC §8), so the dApp-side handshake refuses it. */\nconst isSafeIcon = (icon: unknown): icon is string => typeof icon === 'string' && /^data:/i.test(icon.trim());\n\n/**\n * dApp SIDE — register `onAnnounce` (fires once per announce event, including replays; dedupe by\n * `info.rdns ?? info.uuid` yourself), then request announcements from wallets already present. Keep the\n * subscription alive for the page lifetime to catch late-injecting wallets. Returns an unsubscribe.\n *\n * Two receiver-side safety filters run before an announce reaches `onAnnounce`:\n * • Malformed announces (missing `uuid`/`name`, or no `requestAccounts`) are dropped, never delivered.\n * • A non-`data:` `icon` (a remote-URL tracking/spoofing vector, SPEC §8) is STRIPPED to `''` — the\n * wallet is still surfaced, just without the unsafe icon. An absent or valid `data:` icon passes\n * through untouched (the announce stays the wallet's original frozen object).\n *\n * NEITHER filter authenticates the announcer: any page script can announce (SPEC §8), so treat a provider\n * as untrusted until the user explicitly connects — that connect gesture is the trust boundary, not this\n * handshake. No-op outside a window context.\n */\nexport function requestKaspaWallets(onAnnounce: (detail: KaspaProviderDetail) => void): () => void {\n if (typeof window === 'undefined') return () => {};\n const listener = (e: Event) => {\n const detail = (e as CustomEvent<KaspaProviderDetail>).detail;\n if (!detail?.info?.uuid || !detail?.info?.name) return;\n if (typeof detail.provider?.requestAccounts !== 'function') return;\n if (detail.info.icon != null && !isSafeIcon(detail.info.icon)) {\n // Strip the unsafe icon but keep the wallet: deliver a sanitized copy; the wallet's frozen\n // original is left untouched (a valid/absent icon skips this and passes through frozen).\n onAnnounce({ info: { ...detail.info, icon: '' }, provider: detail.provider });\n return;\n }\n onAnnounce(detail);\n };\n window.addEventListener(KASPA_ANNOUNCE_PROVIDER_EVENT, listener);\n window.dispatchEvent(new Event(KASPA_REQUEST_PROVIDER_EVENT));\n return () => window.removeEventListener(KASPA_ANNOUNCE_PROVIDER_EVENT, listener);\n}\n"],"mappings":";AA2BO,IAAM,iBAAiB;AAAA,EAC5B,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ;AACV;AAOO,IAAM,0BAA0B,CAAC,OACtC,GAAG,WAAW,QAAQ,IAAI,GAAG,MAAM,SAAS,MAAM,EAAE,QAAQ,MAAM,GAAG,IAAI;AAkEpE,IAAM,eAAe;AAAA;AAAA,EAE1B,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,oBAAoB;AAAA;AAAA,EAEpB,oBAAoB;AACtB;AA6DO,IAAM,+BAA+B;AAGrC,IAAM,gCAAgC;AAUtC,SAAS,oBAAoB,MAAyB,UAAqC;AAChG,MAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,EAAC;AACjD,QAAM,SAA8B,OAAO,OAAO,EAAE,MAAM,OAAO,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,SAAS,CAAC;AAChG,QAAM,WAAW,MACf,OAAO,cAAc,IAAI,YAAY,+BAA+B,EAAE,OAAO,CAAC,CAAC;AACjF,SAAO,iBAAiB,8BAA8B,QAAQ;AAC9D,WAAS;AACT,SAAO,MAAM,OAAO,oBAAoB,8BAA8B,QAAQ;AAChF;AAIA,IAAM,aAAa,CAAC,SAAkC,OAAO,SAAS,YAAY,UAAU,KAAK,KAAK,KAAK,CAAC;AAiBrG,SAAS,oBAAoB,YAA+D;AACjG,MAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,EAAC;AACjD,QAAM,WAAW,CAAC,MAAa;AAC7B,UAAM,SAAU,EAAuC;AACvD,QAAI,CAAC,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,KAAM;AAChD,QAAI,OAAO,OAAO,UAAU,oBAAoB,WAAY;AAC5D,QAAI,OAAO,KAAK,QAAQ,QAAQ,CAAC,WAAW,OAAO,KAAK,IAAI,GAAG;AAG7D,iBAAW,EAAE,MAAM,EAAE,GAAG,OAAO,MAAM,MAAM,GAAG,GAAG,UAAU,OAAO,SAAS,CAAC;AAC5E;AAAA,IACF;AACA,eAAW,MAAM;AAAA,EACnB;AACA,SAAO,iBAAiB,+BAA+B,QAAQ;AAC/D,SAAO,cAAc,IAAI,MAAM,4BAA4B,CAAC;AAC5D,SAAO,MAAM,OAAO,oBAAoB,+BAA+B,QAAQ;AACjF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kaspa-wallet-standard",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A proposed standard for dApp
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A proposed standard for dApp\u2194wallet interoperability on Kaspa: a provider interface plus an EIP-6963-style discovery handshake so any wallet can announce itself to any dApp. Zero dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Shawn Pearce",
|
|
7
7
|
"type": "module",
|