kaspa-wallet-standard 0.1.0 → 0.1.1

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 CHANGED
@@ -93,7 +93,10 @@ const unsubscribe = requestKaspaWallets(({ info, provider }) => {
93
93
  ```
94
94
 
95
95
  Then on click: `const [address] = await provider.requestAccounts();`. Capability-check optional methods
96
- before using them, and refuse any `info.icon` that isn't a `data:` URI.
96
+ before using them. `requestKaspaWallets` already strips any non-`data:` `info.icon` for you (a remote URL
97
+ is a tracking/spoofing vector), so what reaches your callback is safe to render. The announce is **not** an
98
+ identity proof, though — any script can announce any name/rdns, so require an explicit user connect before
99
+ signing and treat silent `getAccounts()` restore as display-only. See [SPEC §8](SPEC.md#8-security-considerations).
97
100
 
98
101
  ## Who's using it
99
102
 
package/SPEC.md CHANGED
@@ -100,10 +100,16 @@ type KaspaProviderInfo = {
100
100
  type KaspaProviderDetail = { info: KaspaProviderInfo; provider: KaspaProvider };
101
101
  ```
102
102
 
103
- - The wallet **MUST** freeze `detail` (and `detail.info`) so page scripts cannot swap the provider out.
104
- - The wallet **SHOULD** provide a stable `rdns`; without it a dApp cannot silently restore the session
105
- after a reload (there is no stable identity to match).
106
- - The dApp **SHOULD** dedupe announces by `info.rdns ?? info.uuid`, first-announce-wins.
103
+ - The wallet **MUST** freeze `detail` (and `detail.info`) so a page script cannot mutate an announce
104
+ **in flight** (swap the `provider` or a field on an already-dispatched event). Freezing protects the
105
+ integrity of a *single* announce only it does **not** authenticate the announcer, and a hostile
106
+ script can still dispatch its **own** competing announce. Freezing is not a trust signal (see §8).
107
+ - The wallet **SHOULD** provide a stable `rdns`; without it a dApp cannot restore the session after a
108
+ reload (there is no stable identity to match). `rdns` is a convenience key, **not** an authenticity
109
+ claim — any script can announce any `rdns` (see §8).
110
+ - The dApp **SHOULD** dedupe announces by `info.rdns ?? info.uuid` (first-announce-wins). Because `rdns`
111
+ is spoofable, a dApp **MAY** surface a *second* announce for an already-seen `rdns` as a warning rather
112
+ than silently discarding it.
107
113
 
108
114
  ## 6. Network ids
109
115
 
@@ -122,11 +128,21 @@ unavoidable, would ship under a **new event name** — never by mutating these.
122
128
 
123
129
  - `name`/`icon` are **display hints, not trust signals**. An announce proves a provider is *present*, not
124
130
  that it is *who it claims to be*. dApps MUST NOT grant trust based on them, and MUST refuse non-`data:`
125
- icons (a remote URL is a tracking/spoofing vector).
126
- - Any page script can dispatch `kaspa:announceProvider`. Treat the provider as untrusted until the user
127
- explicitly connects; the connect prompt is the trust boundary.
131
+ icons (a remote URL is a tracking/spoofing vector). The reference `requestKaspaWallets` **enforces this
132
+ for you** it strips any non-`data:` icon (delivers it as `''`) before handing the announce to your
133
+ callback.
134
+ - Any page script can dispatch `kaspa:announceProvider`, including one that claims another wallet's
135
+ `name`/`rdns`. Treat the provider as untrusted until the user explicitly connects; the wallet's own
136
+ connect/sign prompt — rendered by the extension, outside page control — is the trust boundary, not the
137
+ announce.
138
+ - **Silent session restore is display-only.** A dApp MAY use a remembered `rdns` to silently re-populate
139
+ *displayed* accounts via `getAccounts()` after a reload, but it **MUST** require a fresh explicit user
140
+ connect gesture before calling `signPskt` (or any signing). Never route a signature to a provider the
141
+ user did not explicitly (re-)select this session — `rdns` alone is spoofable and is not consent.
128
142
  - The fund-safety rules in §4 are the load-bearing security property. A wallet that signs sloppily can
129
- lose user funds even though the handshake itself is benign.
143
+ lose user funds even though the handshake itself is benign. Note a spoofed in-page provider still
144
+ **cannot forge a signature** (it holds no keys); the realistic risk of announce-spoofing is *display
145
+ spoofing / phishing setup*, which the connect-gesture boundary above is designed to contain.
130
146
 
131
147
  ## 9. Reference implementation & adoption
132
148
 
package/dist/index.d.ts CHANGED
@@ -13,7 +13,9 @@ type KaspaProviderInfo = {
13
13
  uuid: string;
14
14
  /** Human-readable wallet name shown in pickers, e.g. "Kastle". */
15
15
  name: string;
16
- /** Wallet icon as a `data:` URI (SVG/PNG). dApps should refuse to render remote URLs. */
16
+ /** Wallet icon as a `data:` URI (SVG/PNG) a DISPLAY hint, never a trust signal. A remote URL is a
17
+ * tracking/spoofing vector, so the dApp-side {@link requestKaspaWallets} STRIPS any non-`data:` icon
18
+ * (delivers it as `''`) — a dApp can never be handed a remote URL through the handshake. */
17
19
  icon: string;
18
20
  /** Reverse-DNS identifier, e.g. "com.kasware" — STABLE across page loads and versions. Strongly
19
21
  * recommended: it is what lets a dApp silently restore a session with your wallet after a reload. */
@@ -80,8 +82,16 @@ declare function announceKaspaWallet(info: KaspaProviderInfo, provider: KaspaPro
80
82
  * dApp SIDE — register `onAnnounce` (fires once per announce event, including replays; dedupe by
81
83
  * `info.rdns ?? info.uuid` yourself), then request announcements from wallets already present. Keep the
82
84
  * subscription alive for the page lifetime to catch late-injecting wallets. Returns an unsubscribe.
83
- * Malformed announces (missing identity or `requestAccounts`) are dropped, never delivered. No-op
84
- * outside a window context.
85
+ *
86
+ * Two receiver-side safety filters run before an announce reaches `onAnnounce`:
87
+ * • Malformed announces (missing `uuid`/`name`, or no `requestAccounts`) are dropped, never delivered.
88
+ * • A non-`data:` `icon` (a remote-URL tracking/spoofing vector, SPEC §8) is STRIPPED to `''` — the
89
+ * wallet is still surfaced, just without the unsafe icon. An absent or valid `data:` icon passes
90
+ * through untouched (the announce stays the wallet's original frozen object).
91
+ *
92
+ * NEITHER filter authenticates the announcer: any page script can announce (SPEC §8), so treat a provider
93
+ * as untrusted until the user explicitly connects — that connect gesture is the trust boundary, not this
94
+ * handshake. No-op outside a window context.
85
95
  */
86
96
  declare function requestKaspaWallets(onAnnounce: (detail: KaspaProviderDetail) => void): () => void;
87
97
 
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ function announceKaspaWallet(info, provider) {
16
16
  announce();
17
17
  return () => window.removeEventListener(KASPA_REQUEST_PROVIDER_EVENT, announce);
18
18
  }
19
+ var isSafeIcon = (icon) => typeof icon === "string" && /^data:/i.test(icon.trim());
19
20
  function requestKaspaWallets(onAnnounce) {
20
21
  if (typeof window === "undefined") return () => {
21
22
  };
@@ -23,6 +24,10 @@ function requestKaspaWallets(onAnnounce) {
23
24
  const detail = e.detail;
24
25
  if (!detail?.info?.uuid || !detail?.info?.name) return;
25
26
  if (typeof detail.provider?.requestAccounts !== "function") return;
27
+ if (detail.info.icon != null && !isSafeIcon(detail.info.icon)) {
28
+ onAnnounce({ info: { ...detail.info, icon: "" }, provider: detail.provider });
29
+ return;
30
+ }
26
31
  onAnnounce(detail);
27
32
  };
28
33
  window.addEventListener(KASPA_ANNOUNCE_PROVIDER_EVENT, listener);
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). dApps should refuse to render remote URLs. */\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/**\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 * Malformed announces (missing identity or `requestAccounts`) are dropped, never delivered. No-op\n * 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 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;AA0DO,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;AASO,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,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// 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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaspa-wallet-standard",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A proposed standard for dApp↔wallet 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",