@usebutr/wallet-standard-shared 0.4.1 → 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,196 @@
1
+ # @usebutr/wallet-standard-shared
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#179](https://github.com/pedroapfilho/usebutr/pull/179) [`bc5b58c`](https://github.com/pedroapfilho/usebutr/commit/bc5b58cd87601be38dbb77a14e16eaaa36e8f012) Thanks [@pedroapfilho](https://github.com/pedroapfilho)! - Promote to 1.0.0 for the public launch. No API changes in this promotion; the bump marks the hook and adapter surface as stable under semver. Include package documentation, licensing, and npm metadata.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`bc5b58c`](https://github.com/pedroapfilho/usebutr/commit/bc5b58cd87601be38dbb77a14e16eaaa36e8f012)]:
12
+ - @usebutr/core@2.0.1
13
+
14
+ ## 0.5.1
15
+
16
+ ### Patch Changes
17
+
18
+ - 302ae4a: Fix Wallet Standard connections failing with "Failed to get account"
19
+
20
+ Discovery mapped each wallet into a plain copy, which read `accounts` once, before connect, and pinned the empty list. Every `getAccount()` after a successful `standard:connect` then resolved to `null` and the connection was rejected. The wrapper now reads `accounts` and `chains` through to the wallet.
21
+
22
+ The same copy minted a new object per event, so `unregister` no longer matched the wallet seen at `register` and a removed extension never tore its pool entry down. Wrappers are now memoised per wallet. Feature objects are also passed through rather than spread, keeping their methods bound to the wallet.
23
+
24
+ Affects every Wallet Standard platform: `@usebutr/svm`, `@usebutr/sui`, `@usebutr/bitcoin` and `@usebutr/polkadot`.
25
+
26
+ ## 0.5.0
27
+
28
+ ### Minor Changes
29
+
30
+ - 9b1caa2: Correctness pass across the storage, hydration and connector layers. Several of
31
+ these change observable behaviour; the ones worth knowing about before upgrading:
32
+
33
+ **Breaking**
34
+
35
+ - `SuiWallet.signTransaction` now returns `{ bytes, signature }` instead of
36
+ `Uint8Array`. Sui's `executeTransactionBlock` needs both, and each connector
37
+ previously returned a different half, so the old value could not be broadcast
38
+ and a consumer could not tell which half they held. SVM and Bitcoin keep their
39
+ single-`Uint8Array` shape.
40
+ - `@usebutr/svm`'s `sendTx` returns the signature base58-encoded, matching the
41
+ WalletConnect namespace, the Ledger app, Solana explorers and
42
+ `getSignatureStatuses`. It previously returned base64.
43
+ - `@usebutr/sui`'s feature input types drop the `string` arm from
44
+ `transaction`; wallets only ever accepted the `toJSON()` form. Strings and BCS
45
+ bytes are now wrapped for you, so `sendTx` accepts strictly more than before.
46
+
47
+ **Behaviour fixes**
48
+
49
+ - `WalletStorage.setPool` and `removePoolEntry` could self-deadlock on a corrupt
50
+ pool payload, leaving `connectWallet` unresolved and every later pool write
51
+ jammed for the page's life. Both are fixed, and `readWalletSnapshot` now shares
52
+ one codec with `WalletStorage` so the server and client decodes cannot drift.
53
+ - A seeded (SSR) entry whose silent reconnect failed used to stay in the pool
54
+ backed by a placeholder connector forever, so `useSigner`/`useBalance`
55
+ reported an error and `useConnectionStatus` reported `"reconnecting"`
56
+ permanently. Those entries are now dropped.
57
+ - `useSigner` and `useBalance` stay `idle` for a wallet that is still
58
+ reconnecting instead of surfacing a placeholder rejection as `status: "error"`.
59
+ - `useConnectionStatus` lets a live `"connecting"`/`"error"` take precedence over
60
+ the derived `"reconnecting"`, so an in-flight attempt and a connection error
61
+ are no longer hidden. New `useIsReconnecting(connectorId?)` answers the
62
+ per-wallet question directly.
63
+ - A background restore no longer writes the connect-attempt status or steals
64
+ `activeConnectorId`, and a superseded `CONNECT_FAILED` no longer overwrites the
65
+ current attempt.
66
+ - An externally-disconnected connector is now torn down, not just dropped from
67
+ the pool, so a cached adapter is not reused while still holding a session.
68
+ - EVM: a malformed provider response is treated as absent rather than `""`, which
69
+ previously produced chain `eip155:0`, zero-length signatures, empty transaction
70
+ hashes and a confident `0 ETH`. `switchChain` gained the same-chain
71
+ short-circuit `sendTxToChain` already had.
72
+ - WalletConnect: accounts carry their own chain instead of all being stamped with
73
+ the active one, so a multi-chain session no longer returns a wrong-chain
74
+ address. One pairing now covers every configured namespace, the pairing URI
75
+ survives a reconnect, and concurrent connects share one pairing.
76
+ - Bitcoin: the sats-connect (Xverse) adapter honours `silent`, so a reload no
77
+ longer triggers an unsolicited approval prompt, and exposes the payment address
78
+ as its single account instead of also presenting the ordinals address as
79
+ spendable.
80
+ - Polkadot: the injected adapter actually delivers events to subscribers,
81
+ `switchChain` notifies like its Wallet Standard sibling, and chain resolution
82
+ prefers the mainnets rather than whatever the wallet listed first.
83
+ - Ledger: the device session is committed atomically, so a locked device no
84
+ longer leaves an open transport that reports itself connected, and concurrent
85
+ connects open one transport.
86
+ - SVM: `sendTxToChain` submits to the chain you asked for instead of the
87
+ adapter's current one, and rejects a chain the wallet does not advertise.
88
+ - `@usebutr/svm`'s `switchChain` capability now counts only `solana:` chains, so
89
+ a multi-namespace wallet no longer advertises a method that always throws.
90
+
91
+ **Testing**
92
+
93
+ - `createFakePersistence` is now the real `WalletStorage` over memory drivers
94
+ rather than a parallel implementation, so it inherits upsert semantics,
95
+ validation and JSON round-tripping. Two divergences are fixed as a result:
96
+ `setPool` upserts rather than replaces, and `clearAll` leaves the
97
+ user-disconnected flag alone.
98
+ - `createFakeConnectedWallet` rejects being given both an `adapter` and explicit
99
+ `addresses`/`accounts`, a combination whose halves could disagree.
100
+
101
+ ### Patch Changes
102
+
103
+ - Updated dependencies [9b1caa2]
104
+ - @usebutr/core@2.0.0
105
+
106
+ ## 0.4.1
107
+
108
+ ### Patch Changes
109
+
110
+ - Updated dependencies [8ecaf89]
111
+ - @usebutr/core@1.1.0
112
+
113
+ ## 0.4.0
114
+
115
+ ### Minor Changes
116
+
117
+ - 7887cf0: Export `buildWalletCapabilities`, the Wallet Standard capability mapping that `@usebutr/svm`, `@usebutr/sui`, `@usebutr/bitcoin` and `@usebutr/polkadot` each re-implemented. It takes a flat feature profile and owns the flags that are constant across every namespace, so only the feature-name mapping stays per-package.
118
+
119
+ ### Patch Changes
120
+
121
+ - Updated dependencies [7887cf0]
122
+ - Updated dependencies [7887cf0]
123
+ - Updated dependencies [f0a5116]
124
+ - Updated dependencies [f0a5116]
125
+ - @usebutr/core@1.0.0
126
+
127
+ ## 0.3.0
128
+
129
+ ### Minor Changes
130
+
131
+ - efe4550: Share the session plumbing behind the Wallet Standard and WalletConnect CAIP adapters. `@usebutr/wallet-standard-shared` now exports `createWalletStandardCore`, which the Bitcoin, Polkadot, Sui and SVM adapters build on; the WalletConnect Bitcoin, Sui and SVM namespaces share an equivalent CAIP core. Every package's public API is unchanged. The one visible difference is a dev-console warning: a failed SVM disconnect now logs `[butr] SVM Wallet Standard disconnect threw:` instead of `[butr] Wallet Standard disconnect threw:`.
132
+
133
+ ### Patch Changes
134
+
135
+ - Updated dependencies [4467a5e]
136
+ - @usebutr/core@0.5.0
137
+
138
+ ## 0.2.6
139
+
140
+ ### Patch Changes
141
+
142
+ - Updated dependencies [c1309ee]
143
+ - @usebutr/core@0.4.2
144
+
145
+ ## 0.2.5
146
+
147
+ ### Patch Changes
148
+
149
+ - 937dfae: Bump runtime dependency floors (`@wallet-standard/app` 1.1.1, `@ledgerhq/*` latest minors, `@walletconnect/universal-provider` 2.23.10) and modernize public type declarations from method signatures to property function types (oxlint `method-signature-style`). Type-level only — no runtime behavior change.
150
+ - Updated dependencies [937dfae]
151
+ - @usebutr/core@0.4.1
152
+
153
+ ## 0.2.4
154
+
155
+ ### Patch Changes
156
+
157
+ - a46eecd: Ship unminified ESM so downstream bundlers (Vite/esbuild dep pre-bundling) process the package correctly; fixes a ReferenceError in consumer dev servers. The consuming app minifies once at its own build.
158
+ - 3f26776: Warn (once) when `@wallet-standard/app` can't be loaded instead of silently disabling discovery. Wallet Standard discovery (used by the SVM, Sui, Bitcoin and Polkadot connectors) dynamically imports the optional peer dep `@wallet-standard/app`; when the import failed it was swallowed, so no wallets appeared with no hint why. It now logs a single actionable warning, forwarding the underlying error.
159
+ - Updated dependencies [b5322ae]
160
+ - Updated dependencies [d5f32c7]
161
+ - Updated dependencies [a46eecd]
162
+ - @usebutr/core@0.4.0
163
+
164
+ ## 0.2.3
165
+
166
+ ### Patch Changes
167
+
168
+ - Updated dependencies [886ee1d]
169
+ - @usebutr/core@0.3.0
170
+
171
+ ## 0.2.2
172
+
173
+ ### Patch Changes
174
+
175
+ - Updated dependencies [db5d7e9]
176
+ - @usebutr/core@0.2.2
177
+
178
+ ## 0.2.1
179
+
180
+ ### Patch Changes
181
+
182
+ - Updated dependencies [f846e77]
183
+ - @usebutr/core@0.2.1
184
+
185
+ ## 0.2.0
186
+
187
+ ### Minor Changes
188
+
189
+ - b77a477: Shared Wallet Standard protocol types (`WalletStandardWallet`, `WalletStandardWalletAccount`, `WalletStandardAppModule`, `WalletsApp`, `StandardConnectFeature`, `StandardDisconnectFeature`, `StandardEventsFeature`, `StandardEventsListener`) are imported from `@usebutr/wallet-standard-shared` directly — they are no longer re-exported from `@usebutr/svm`, `@usebutr/sui`, or `@usebutr/bitcoin`. Platform-specific feature shapes still ship from their own packages.
190
+
191
+ `@usebutr/svm` adapter ids are now platform-prefixed (`wallet-standard:svm-<slug>`), consistent with the `sui-` and `btc-` prefixes. `slugify` in `@usebutr/wallet-standard-shared` requires a non-empty platform prefix.
192
+
193
+ ### Patch Changes
194
+
195
+ - Updated dependencies [b77a477]
196
+ - @usebutr/core@0.2.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pedro Filho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @usebutr/wallet-standard-shared
2
+
3
+ Shared Wallet Standard discovery primitives used by @usebutr/svm, @usebutr/sui, @usebutr/bitcoin.
4
+
5
+ Part of [butr](https://www.usebutr.com), a multi-chain wallet discovery and
6
+ connection-state library. Your application owns the picker UI and chain client.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @usebutr/wallet-standard-shared @wallet-standard/app zustand
12
+ ```
13
+
14
+ For adapter authors. The package supplies discovery, lifecycle, feature guards, and capability helpers. The default registry loader requires `@wallet-standard/app`; tests can inject a loader.
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import { slugify } from "@usebutr/wallet-standard-shared";
20
+
21
+ // Namespace each adapter so a multi-chain wallet has one ID per platform.
22
+ export const solanaId = slugify("svm", "Phantom");
23
+ export const suiId = slugify("sui", "Phantom");
24
+ ```
25
+
26
+ ## Documentation
27
+
28
+ - [Package reference](https://docs.usebutr.com/api/wallet-standard-shared)
29
+ - [Getting started](https://docs.usebutr.com/getting-started/quickstart)
30
+ - [Examples and source](https://github.com/pedroapfilho/usebutr)
31
+ - [Report an issue](https://github.com/pedroapfilho/usebutr/issues)
32
+
33
+ ## License
34
+
35
+ [MIT](./LICENSE), copyright 2026 Pedro Filho.
package/dist/index.d.ts CHANGED
@@ -1,32 +1,27 @@
1
- import { Account, ChainBase, ConnectorEvent, WalletAdapter, WalletCapabilities, buildAccount } from "@usebutr/core";
1
+ import { Account, ChainBase, ConnectorEvent, WalletAdapter, WalletCapabilities, WalletSigner, buildAccount } from "@usebutr/core";
2
+ import "@wallet-standard/app";
2
3
  //#region src/types.d.ts
3
4
  /**
4
- * Minimal type surface for the Wallet Standard protocol that every
5
- * platform-specific package (`@usebutr/svm`, `@usebutr/sui`,
6
- * `@usebutr/bitcoin`) shares. Declared inline so butr stays
7
- * self-contained at the type level; the actual `@wallet-standard/app`
8
- * package is dynamic-imported at runtime, and apps that don't enable
9
- * Wallet Standard discovery never resolve it.
10
- *
5
+ * Declared inline so butr stays self-contained at the type level;
6
+ * `@wallet-standard/app` is only dynamic-imported when discovery runs.
11
7
  * Spec: https://github.com/wallet-standard/wallet-standard
12
- *
13
- * Platform-specific feature shapes (e.g. `solana:signMessage`,
14
- * `sui:signPersonalMessage`, `bitcoin:signPsbt`) live in their own
15
- * platform packages.
16
8
  */
17
9
  type WalletStandardWalletAccount = {
18
10
  address: string;
19
11
  chains: ReadonlyArray<string>;
20
12
  features: ReadonlyArray<string>;
21
- /** Public key bytes; Wallet Standard ships these as Uint8Array. */
22
- publicKey?: Uint8Array;
13
+ publicKey?: {
14
+ readonly length: number;
15
+ readonly [index: number]: number;
16
+ };
17
+ };
18
+ type WalletStandardFeature = {
19
+ version?: string;
23
20
  };
24
21
  type WalletStandardWallet = {
25
22
  accounts: ReadonlyArray<WalletStandardWalletAccount>;
26
23
  chains: ReadonlyArray<string>;
27
- /** Map keyed by feature name. Values are feature-specific; narrow at
28
- * use sites. */
29
- features: Readonly<Record<string, unknown>>;
24
+ features: Readonly<Record<string, WalletStandardFeature>>;
30
25
  icon: string;
31
26
  name: string;
32
27
  version: string;
@@ -38,15 +33,18 @@ type WalletsApp = {
38
33
  type WalletStandardAppModule = {
39
34
  getWallets: () => WalletsApp;
40
35
  };
36
+ type WalletStandardModuleLoader = () => Promise<WalletStandardAppModule>;
41
37
  type StandardConnectFeature = {
42
38
  connect: (input?: {
43
39
  silent?: boolean;
44
40
  }) => Promise<{
45
41
  accounts: ReadonlyArray<WalletStandardWalletAccount>;
46
42
  }>;
43
+ version?: string;
47
44
  };
48
45
  type StandardDisconnectFeature = {
49
46
  disconnect: () => Promise<void>;
47
+ version?: string;
50
48
  };
51
49
  type StandardEventsListener = (changes: {
52
50
  accounts?: ReadonlyArray<WalletStandardWalletAccount>;
@@ -55,43 +53,28 @@ type StandardEventsListener = (changes: {
55
53
  }) => void;
56
54
  type StandardEventsFeature = {
57
55
  on: (event: "change", listener: StandardEventsListener) => () => void;
56
+ version?: string;
58
57
  };
59
58
  //#endregion
60
59
  //#region src/primitives.d.ts
61
60
  /**
62
- * Convert a wallet name into a stable kebab-case adapter id, scoped by
63
- * a platform prefix so adapters from different platforms (Phantom SVM,
64
- * Phantom Sui, Phantom BTC) coexist in the pool without colliding on
65
- * the shared `getWallets()` bus.
66
- *
67
- * @example
68
- * slugify("svm", "Phantom") // "wallet-standard:svm-phantom"
69
- * slugify("sui", "Sui Wallet") // "wallet-standard:sui-sui-wallet"
70
- * slugify("btc", "Phantom") // "wallet-standard:btc-phantom"
61
+ * The platform prefix scopes the id so one multi-chain wallet (Phantom
62
+ * SVM, Sui, BTC) yields distinct adapters on the shared `getWallets()` bus.
71
63
  */
72
64
  declare const slugify: (platformPrefix: string, name: string) => string;
73
65
  /**
74
- * Read a Wallet Standard feature off a wallet, returning `undefined`
75
- * when absent. `features` is a reverse-DNS-keyed registry typed
76
- * `Record<string, unknown>` by the spec, so the caller declares the
77
- * concrete feature shape it expects; this accessor is the single
78
- * boundary where that dynamism is acknowledged. The `T` parameter is
79
- * intentionally caller-supplied (not inferable) and the assertion is
80
- * unavoidable for an untyped registry.
81
- */
82
- declare const getFeature: <T>(wallet: WalletStandardWallet, name: string) => T | undefined;
83
- /**
84
- * Pick the first address out of a wallet's accounts array, returning
85
- * `null` when the wallet currently exposes none.
66
+ * The spec types `features` as a reverse-DNS-keyed `Record<string,
67
+ * unknown>`, so the caller declares the shape it expects. This accessor is
68
+ * the single boundary where that dynamism is acknowledged.
86
69
  */
70
+ type FeatureGuard<Feature> = (feature: WalletStandardFeature) => feature is WalletStandardFeature & Feature;
71
+ declare const getFeature: <Feature>(wallet: WalletStandardWallet, name: string, isFeature: FeatureGuard<Feature>) => (WalletStandardFeature & Feature) | undefined;
72
+ declare const isWalletStandardWallet: (value: WalletSigner) => value is WalletStandardWallet;
87
73
  declare const pickFirstAddress: (accounts: ReadonlyArray<WalletStandardWalletAccount>) => string | null;
88
74
  /**
89
- * Look up a wallet account by address, falling back to the first
90
- * exposed account when the requested address isn't present.
91
- *
92
- * Used by per-call routing in `sendTx` / `signMessage`: callers pass
93
- * an `account` from `ConnectedWallet.accounts` to sign with a non-
94
- * active address; the wallet's `accounts[]` is the source of truth.
75
+ * Per-call routing for `sendTx` / `signMessage`: callers pass an `account`
76
+ * from `ConnectedWallet.accounts` to sign with a non-active address, but
77
+ * the wallet's own `accounts[]` stays the source of truth.
95
78
  */
96
79
  declare const pickAccountByAddress: (accounts: ReadonlyArray<WalletStandardWalletAccount>, address: string) => WalletStandardWalletAccount | undefined;
97
80
  //#endregion
@@ -132,7 +115,7 @@ type WalletStandardCore = {
132
115
  disconnect: () => Promise<void>;
133
116
  getAccount: () => Promise<Account | null>;
134
117
  getAccounts: () => Promise<Array<Account>>;
135
- getSigner: () => Promise<unknown>;
118
+ getSigner: () => Promise<WalletStandardWallet>;
136
119
  /** Whether the wallet advertises `standard:events`; feeds capability gating. */
137
120
  hasEvents: boolean;
138
121
  icon: string | undefined;
@@ -150,17 +133,9 @@ type WalletStandardCore = {
150
133
  toChain: () => ChainBase;
151
134
  };
152
135
  /**
153
- * Session plumbing every Wallet Standard platform adapter repeats: chain
154
- * resolution, the connect/disconnect handshake, account reads, the local
155
- * listener set that lets `switchChain` synthesise `accountChanged`, and
156
- * the `standard:events` bridge. Returns `null` for wallets butr can't
157
- * drive (no chain in this namespace, or no `standard:connect`), which is
158
- * the same `null` each platform adapter returns to its caller.
159
- *
160
- * Platform packages keep what genuinely differs: which features they read
161
- * off the wallet, how they encode transactions and messages, and their
162
- * balance units. `getSigner` hands back the raw Wallet Standard wallet so
163
- * consumers can wrap it in their own signing library.
136
+ * Shared session plumbing for every Wallet Standard platform adapter.
137
+ * `getSigner` hands back the raw Wallet Standard wallet so consumers can
138
+ * wrap it in their own signing library.
164
139
  */
165
140
  declare const createWalletStandardCore: ({ chainPrefix, id, label, namespace, platform, preferredChainIds, registerDisconnector, trackChainChanges, wallet }: WalletStandardCoreInput) => WalletStandardCore | null;
166
141
  //#endregion
@@ -183,68 +158,25 @@ type WalletCapabilityProfile = {
183
158
  signTransaction: boolean;
184
159
  };
185
160
  /**
186
- * Wallet Standard → butr capability mapping, shared by every platform
187
- * package. The flags that are constant across all of them:
188
- *
189
- * - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no
190
- * balance/receipt RPC, and butr ships no RPC client of its own.
191
- * - `requestAccounts`: no programmatic equivalent of EIP-2255; re-running
192
- * `standard:connect` doesn't produce new accounts on any major Wallet
193
- * Standard wallet in practice (Phantom, MetaMask Snap, Solflare,
194
- * Backpack, Xverse, Talisman).
195
- * - `switchAccount`: no protocol-level silent account switch; the user
196
- * picks the account inside the wallet.
197
- * - `switchChain`: true when the wallet advertises more than one chain in
198
- * this namespace. The switch is local re-routing (the per-call `chain`
199
- * input), so with a single chain there's nothing to switch to.
200
- *
201
- * Each platform package owns only the feature-name → flag mapping, since
202
- * the feature names differ per namespace.
161
+ * Wallet Standard has no balance/receipt RPC, no EIP-2255 equivalent, and
162
+ * no silent account switch, so those flags are false everywhere.
163
+ * `switchChain` re-routes locally, so it needs a second advertised chain.
203
164
  */
204
165
  declare const buildWalletCapabilities: (profile: WalletCapabilityProfile) => WalletCapabilities;
205
166
  //#endregion
206
167
  //#region src/discovery.d.ts
207
168
  /**
208
- * Callback supplied by per-platform discovery to build a `WalletAdapter`
209
- * out of a Wallet Standard wallet announcement.
210
- *
211
- * Returns `null` when the wallet doesn't advertise this platform; that
212
- * lets a multi-chain wallet (e.g. Phantom: SVM + EVM + Sui + Bitcoin)
213
- * produce one adapter per platform without each builder needing to know
214
- * about the others.
215
- *
216
- * The second argument is a hook the builder calls during construction
217
- * to wire a synthetic `disconnected` event into its subscriber set.
218
- * The discovery loop fires the registered emitter when Wallet Standard
219
- * announces `unregister` for that wallet.
169
+ * Returns `null` when the wallet doesn't advertise this platform, so a
170
+ * multi-chain wallet yields one adapter per platform. `registerDisconnector`
171
+ * wires the synthetic `disconnected` fired on Wallet Standard `unregister`.
220
172
  */
221
173
  type WalletStandardAdapterBuilder = (wallet: WalletStandardWallet, registerDisconnector: (emit: () => void) => void) => WalletAdapter | null;
222
174
  /**
223
- * Subscribe to Wallet Standard announcements and turn matching wallets
224
- * into butr `WalletAdapter`s using a per-platform `build` callback.
225
- *
226
- * **Shared machinery** every platform package needs:
227
- * - Dynamic import of `@wallet-standard/app` (optional peer dep;
228
- * failure to resolve quietly disables discovery so consumers who
229
- * don't install it pay nothing).
230
- * - Cancellation: the returned unsubscribe is safe to call before the
231
- * dynamic import has resolved.
232
- * - Dedupe by adapter id, so re-announcements don't double-emit.
233
- * - `unregister` integration: when an extension is removed mid-session,
234
- * fire the disconnector the builder registered so connected pool
235
- * entries on the consumer side tear down cleanly.
236
- *
237
- * **What's platform-specific** lives entirely in `build`:
238
- * - Which `wallet.chains` to accept.
239
- * - Which features to gate on.
240
- * - The `id` slug prefix (use `slugify(prefix, wallet.name)`).
241
- * - The capability resolver, the feature decoding, the chain coercion.
242
- *
243
- * Disconnectors are keyed by the wallet object itself (identity equality),
244
- * not by adapter id; Wallet Standard's `register` / `unregister` emit
245
- * the same wallet object, so identity is the cheapest stable key.
175
+ * `@wallet-standard/app` is an optional peer dep: a failed import quietly
176
+ * disables discovery. Disconnectors are keyed by wallet identity rather
177
+ * than adapter id because `register` / `unregister` emit the same object.
246
178
  */
247
- declare const discoverWalletStandard: (onAdapter: (adapter: WalletAdapter) => void, build: WalletStandardAdapterBuilder) => (() => void);
179
+ declare const discoverWalletStandard: (onAdapter: (adapter: WalletAdapter) => void, build: WalletStandardAdapterBuilder, loadModule?: WalletStandardModuleLoader) => (() => void);
248
180
  //#endregion
249
- export { type StandardConnectFeature, type StandardDisconnectFeature, type StandardEventsFeature, type StandardEventsListener, type WalletCapabilityProfile, type WalletStandardAdapterBuilder, type WalletStandardAppModule, type WalletStandardCore, type WalletStandardWallet, type WalletStandardWalletAccount, type WalletsApp, buildAccount, buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, pickAccountByAddress, pickFirstAddress, slugify };
181
+ export { type StandardConnectFeature, type StandardDisconnectFeature, type StandardEventsFeature, type StandardEventsListener, type WalletCapabilityProfile, type WalletStandardAdapterBuilder, type WalletStandardAppModule, type WalletStandardCore, type WalletStandardFeature, type WalletStandardModuleLoader, type WalletStandardWallet, type WalletStandardWalletAccount, type WalletsApp, buildAccount, buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, isWalletStandardWallet, pickAccountByAddress, pickFirstAddress, slugify };
250
182
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/primitives.ts","../src/adapter-core.ts","../src/capabilities.ts","../src/discovery.ts"],"mappings":";;;;;;;;;;;;;;;;KAeK;EACH;EACA,QAAQ;EACR,UAAU;;EAEV,YAAY;;KAGT;EACH,UAAU,cAAc;EACxB,QAAQ;;;EAGR,UAAU,SAAS;EACnB;EACA;EACA;;KAGG;EACH,WAAW,cAAc;EACzB,KACE,kCACA,cAAc,SAAS,cAAc;;KAIpC;EACH,kBAAkB;;KAGf;EACH,UAAU;IAAU;QAAuB;IACzC,UAAU,cAAc;;;KAIvB;EACH,kBAAkB;;KAGf,0BAA0B;EAC7B,WAAW,cAAc;EACzB,SAAS;EACT,WAAW;;KAGR;EACH,KAAK,iBAAiB,UAAU;;;;;;;;;;;;;;;cClD5B,UAAW,wBAAwB;;;;;;;;;;cAkBnC,aAAc,GAAG,QAAQ,sBAAsB,iBAAe;;;;;cAa9D,mBAAoB,UAAU,cAAc;;;;;;;;;cAa5C,uBACJ,UAAU,cAAc,8BACxB,oBACC;;;KChDE;;EAEH;;EAEA;;EAEA;;EAEA;;EAEA;;;;EAIA,mBAAmB;;;;EAInB,wBAAwB;;;;EAIxB;EACA,QAAQ;;KAGL;;EAEH;EACA,UAAU;IAAS;QAAuB;;EAE1C;EACA,kBAAkB;EAClB,kBAAkB,QAAQ;EAC1B,mBAAmB,QAAQ,MAAM;EACjC,iBAAiB;;EAEjB;EACA;EACA;EACA;;;EAGA,iBAAiB;IAAY;QAA4B;EACzD,YAAY,WAAW,OAAO;EAC9B,cAAc,OAAO,cAAc;;;EAGnC,eAAe;;;;;;;;;;;;;;;cAgBX,6BAA4B,aAAA,IAAA,OAAA,WAAA,UAAA,mBAAA,sBAAA,mBAAA,UAU/B,4BAA0B;;;KCpFxB;;EAEH;;EAEA;;;;EAIA;;;EAGA;EACA;;;EAGA;;;;;;;;;;;;;;;;;;;;;cAsBI,0BAA2B,SAAS,4BAA0B;;;;;;;;;;;;;;;;;KClB/D,gCACH,QAAQ,sBACR,uBAAuB,8BACpB;;;;;;;;;;;;;;;;;;;;;;;;;;cA2BC,yBACJ,YAAY,SAAS,wBACrB,OAAO"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/primitives.ts","../src/adapter-core.ts","../src/capabilities.ts","../src/discovery.ts"],"mappings":";;;;;;;;KAMK;EACH;EACA,QAAQ;EACR,UAAU;EACV;aAAuB;cAA0B;;;KAG9C;EAA0B;;KAE1B;EACH,UAAU,cAAc;EACxB,QAAQ;EACR,UAAU,SAAS,eAAe;EAClC;EACA;EACA;;KAGG;EACH,WAAW,cAAc;EACzB,KACE,kCACA,cAAc,SAAS,cAAc;;KAIpC;EACH,kBAAkB;;KAGf,mCAAmC,QAAQ;KAE3C;EACH,UAAU;IAAU;QAAuB;IACzC,UAAU,cAAc;;EAE1B;;KAGG;EACH,kBAAkB;EAClB;;KAGG,0BAA0B;EAC7B,WAAW,cAAc;EACzB,SAAS;EACT,WAAW;;KAGR;EACH,KAAK,iBAAiB,UAAU;EAChC;;;;;;;;cC9CI,UAAO,wBAA0B;;;;;;KAalC,aAAa,YAChB,SAAS,0BACN,WAAW,wBAAwB;cAElC,aAAc,SAAO,QACjB,sBAAoB,cAChB,WACD,aAAa,cACtB,wBAAwB;cAQtB,yBAAsB,OAAW,iBAAe,SAAS;cAezD,mBAAgB,UAAc,cAAc;;;;;;cAU5C,uBAAoB,UACd,cAAc,8BAA4B,oBAEnD;;;KCzCE;;EAEH;;EAEA;;EAEA;;EAEA;;EAEA;;;;EAIA,mBAAmB;;;;EAInB,wBAAwB;;;;EAIxB;EACA,QAAQ;;KAGL;;EAEH;EACA,UAAU;IAAS;QAAuB;;EAE1C;EACA,kBAAkB;EAClB,kBAAkB,QAAQ;EAC1B,mBAAmB,QAAQ,MAAM;EACjC,iBAAiB,QAAQ;;EAEzB;EACA;EACA;EACA;;;EAGA,iBAAiB;IAAY;QAA4B;EACzD,YAAY,WAAW,OAAO;EAC9B,cAAc,OAAO,cAAc;;;EAGnC,eAAe;;;;;;;cAQX,6BAAwB,aAAA,IAAA,OAAA,WAAA,UAAA,mBAAA,sBAAA,mBAAA,UAU3B,4BAA0B;;;KC5FxB;;EAEH;;EAEA;;;;EAIA;;;EAGA;EACA;;;EAGA;;;;;;;cAQI,0BAAuB,SAAa,4BAA0B;;;;;;;;KCqC/D,gCACH,QAAQ,sBACR,uBAAuB,8BACpB;;;;;;cA6BC,yBAAsB,YACd,SAAS,wBAAsB,OACpC,8BAA4B,aACvB"}
package/dist/index.js CHANGED
@@ -2,74 +2,48 @@ import { buildAccount, buildAccount as buildAccount$1, logWarn, sanitizeIcon } f
2
2
 
3
3
  //#region src/primitives.ts
4
4
  /**
5
- * Convert a wallet name into a stable kebab-case adapter id, scoped by
6
- * a platform prefix so adapters from different platforms (Phantom SVM,
7
- * Phantom Sui, Phantom BTC) coexist in the pool without colliding on
8
- * the shared `getWallets()` bus.
9
- *
10
- * @example
11
- * slugify("svm", "Phantom") // "wallet-standard:svm-phantom"
12
- * slugify("sui", "Sui Wallet") // "wallet-standard:sui-sui-wallet"
13
- * slugify("btc", "Phantom") // "wallet-standard:btc-phantom"
5
+ * The platform prefix scopes the id so one multi-chain wallet (Phantom
6
+ * SVM, Sui, BTC) yields distinct adapters on the shared `getWallets()` bus.
14
7
  */
15
8
  const slugify = (platformPrefix, name) => {
16
9
  return `wallet-standard:${platformPrefix}-${name.trim().toLowerCase().replaceAll(new RegExp("[^a-z0-9]+", "gv"), "-")}`;
17
10
  };
18
- /**
19
- * Read a Wallet Standard feature off a wallet, returning `undefined`
20
- * when absent. `features` is a reverse-DNS-keyed registry typed
21
- * `Record<string, unknown>` by the spec, so the caller declares the
22
- * concrete feature shape it expects; this accessor is the single
23
- * boundary where that dynamism is acknowledged. The `T` parameter is
24
- * intentionally caller-supplied (not inferable) and the assertion is
25
- * unavoidable for an untyped registry.
26
- */
27
- const getFeature = (wallet, name) => {
11
+ const getFeature = (wallet, name, isFeature) => {
28
12
  const feature = wallet.features[name];
29
- if (typeof feature !== "object" || feature === null) return;
13
+ if (feature === void 0 || !isFeature(feature)) return;
30
14
  return feature;
31
15
  };
32
- /**
33
- * Pick the first address out of a wallet's accounts array, returning
34
- * `null` when the wallet currently exposes none.
35
- */
16
+ const isWalletStandardWallet = (value) => "accounts" in value && Array.isArray(value.accounts) && "chains" in value && Array.isArray(value.chains) && "features" in value && typeof value.features === "object" && value.features !== null && "icon" in value && typeof value.icon === "string" && "name" in value && typeof value.name === "string" && "version" in value && typeof value.version === "string";
36
17
  const pickFirstAddress = (accounts) => {
37
18
  const first = accounts[0];
38
19
  return first === void 0 ? null : first.address;
39
20
  };
40
21
  /**
41
- * Look up a wallet account by address, falling back to the first
42
- * exposed account when the requested address isn't present.
43
- *
44
- * Used by per-call routing in `sendTx` / `signMessage`: callers pass
45
- * an `account` from `ConnectedWallet.accounts` to sign with a non-
46
- * active address; the wallet's `accounts[]` is the source of truth.
22
+ * Per-call routing for `sendTx` / `signMessage`: callers pass an `account`
23
+ * from `ConnectedWallet.accounts` to sign with a non-active address, but
24
+ * the wallet's own `accounts[]` stays the source of truth.
47
25
  */
48
26
  const pickAccountByAddress = (accounts, address) => accounts.find((a) => a.address === address) ?? accounts[0];
49
27
 
50
28
  //#endregion
51
29
  //#region src/adapter-core.ts
30
+ const isStandardConnectFeature = (feature) => "connect" in feature && typeof feature.connect === "function";
31
+ const isStandardDisconnectFeature = (feature) => "disconnect" in feature && typeof feature.disconnect === "function";
32
+ const isStandardEventsFeature = (feature) => "on" in feature && typeof feature.on === "function";
52
33
  /**
53
- * Session plumbing every Wallet Standard platform adapter repeats: chain
54
- * resolution, the connect/disconnect handshake, account reads, the local
55
- * listener set that lets `switchChain` synthesise `accountChanged`, and
56
- * the `standard:events` bridge. Returns `null` for wallets butr can't
57
- * drive (no chain in this namespace, or no `standard:connect`), which is
58
- * the same `null` each platform adapter returns to its caller.
59
- *
60
- * Platform packages keep what genuinely differs: which features they read
61
- * off the wallet, how they encode transactions and messages, and their
62
- * balance units. `getSigner` hands back the raw Wallet Standard wallet so
63
- * consumers can wrap it in their own signing library.
34
+ * Shared session plumbing for every Wallet Standard platform adapter.
35
+ * `getSigner` hands back the raw Wallet Standard wallet so consumers can
36
+ * wrap it in their own signing library.
64
37
  */
65
38
  const createWalletStandardCore = ({ chainPrefix, id, label, namespace, platform, preferredChainIds, registerDisconnector, trackChainChanges, wallet }) => {
66
- const pickChain = (chains) => chains.find((c) => preferredChainIds.includes(c)) ?? chains.find((c) => c.startsWith(chainPrefix)) ?? null;
39
+ const preferredChains = new Set(preferredChainIds);
40
+ const pickChain = (chains) => chains.find((c) => preferredChains.has(c)) ?? chains.find((c) => c.startsWith(chainPrefix)) ?? null;
67
41
  const initialChainId = pickChain(wallet.chains);
68
42
  if (initialChainId === null) return null;
69
- const connect = getFeature(wallet, "standard:connect");
43
+ const connect = getFeature(wallet, "standard:connect", isStandardConnectFeature);
70
44
  if (connect === void 0) return null;
71
- const disconnect = getFeature(wallet, "standard:disconnect");
72
- const events = getFeature(wallet, "standard:events");
45
+ const disconnect = getFeature(wallet, "standard:disconnect", isStandardDisconnectFeature);
46
+ const events = getFeature(wallet, "standard:events", isStandardEventsFeature);
73
47
  let currentChainId = initialChainId;
74
48
  const toChain = () => ({
75
49
  id: currentChainId,
@@ -160,12 +134,12 @@ const createWalletStandardCore = ({ chainPrefix, id, label, namespace, platform,
160
134
  unsubWallet?.();
161
135
  };
162
136
  },
163
- switchChain: (chain) => {
137
+ switchChain: async (chain) => {
164
138
  if (chain.namespace !== namespace) throw new Error(`${label} adapter received non-${platform} chain "${chain.id}". Pass a chain with namespace "${namespace}".`);
165
139
  if (!wallet.chains.includes(chain.id)) throw new Error(`Wallet ${wallet.name} does not advertise chain "${chain.id}". Available: ${wallet.chains.join(", ")}`);
166
140
  currentChainId = chain.id;
167
141
  notifyAccountChanged();
168
- return Promise.resolve();
142
+ await Promise.resolve();
169
143
  },
170
144
  toChain
171
145
  };
@@ -174,23 +148,9 @@ const createWalletStandardCore = ({ chainPrefix, id, label, namespace, platform,
174
148
  //#endregion
175
149
  //#region src/capabilities.ts
176
150
  /**
177
- * Wallet Standard → butr capability mapping, shared by every platform
178
- * package. The flags that are constant across all of them:
179
- *
180
- * - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no
181
- * balance/receipt RPC, and butr ships no RPC client of its own.
182
- * - `requestAccounts`: no programmatic equivalent of EIP-2255; re-running
183
- * `standard:connect` doesn't produce new accounts on any major Wallet
184
- * Standard wallet in practice (Phantom, MetaMask Snap, Solflare,
185
- * Backpack, Xverse, Talisman).
186
- * - `switchAccount`: no protocol-level silent account switch; the user
187
- * picks the account inside the wallet.
188
- * - `switchChain`: true when the wallet advertises more than one chain in
189
- * this namespace. The switch is local re-routing (the per-call `chain`
190
- * input), so with a single chain there's nothing to switch to.
191
- *
192
- * Each platform package owns only the feature-name → flag mapping, since
193
- * the feature names differ per namespace.
151
+ * Wallet Standard has no balance/receipt RPC, no EIP-2255 equivalent, and
152
+ * no silent account switch, so those flags are false everywhere.
153
+ * `switchChain` re-routes locally, so it needs a second advertised chain.
194
154
  */
195
155
  const buildWalletCapabilities = (profile) => ({
196
156
  getBalance: false,
@@ -207,41 +167,62 @@ const buildWalletCapabilities = (profile) => ({
207
167
 
208
168
  //#endregion
209
169
  //#region src/discovery.ts
170
+ const toFeatureRecord = (features) => {
171
+ const record = {};
172
+ for (const [name, value] of Object.entries(features)) {
173
+ if (typeof value !== "object" || value === null) continue;
174
+ record[name] = value;
175
+ }
176
+ return record;
177
+ };
178
+ const wrappers = /* @__PURE__ */ new WeakMap();
179
+ /**
180
+ * Reads through to the wallet: it re-points `accounts` on connect, so a copy
181
+ * pins the pre-connect empty list and every `getAccount()` then resolves null.
182
+ * Memoised because `unregister` is matched to `register` by object identity.
183
+ */
184
+ const mapWallet = (wallet) => {
185
+ const cached = wrappers.get(wallet);
186
+ if (cached !== void 0) return cached;
187
+ const wrapper = {
188
+ get accounts() {
189
+ return wallet.accounts;
190
+ },
191
+ get chains() {
192
+ return wallet.chains;
193
+ },
194
+ features: toFeatureRecord(wallet.features),
195
+ icon: wallet.icon,
196
+ name: wallet.name,
197
+ version: wallet.version
198
+ };
199
+ wrappers.set(wallet, wrapper);
200
+ return wrapper;
201
+ };
210
202
  let warnedMissingApp = false;
203
+ const loadWalletStandardModule = async () => {
204
+ const imported = await import("@wallet-standard/app");
205
+ if (typeof imported !== "object" || imported === null || !("getWallets" in imported) || typeof imported.getWallets !== "function") throw new Error("@wallet-standard/app has no getWallets export");
206
+ const source = imported.getWallets();
207
+ return { getWallets: () => ({
208
+ get: () => source.get().map(mapWallet),
209
+ on: (event, listener) => source.on(event, (...wallets) => {
210
+ listener(...wallets.map(mapWallet));
211
+ })
212
+ }) };
213
+ };
211
214
  /**
212
- * Subscribe to Wallet Standard announcements and turn matching wallets
213
- * into butr `WalletAdapter`s using a per-platform `build` callback.
214
- *
215
- * **Shared machinery** every platform package needs:
216
- * - Dynamic import of `@wallet-standard/app` (optional peer dep;
217
- * failure to resolve quietly disables discovery so consumers who
218
- * don't install it pay nothing).
219
- * - Cancellation: the returned unsubscribe is safe to call before the
220
- * dynamic import has resolved.
221
- * - Dedupe by adapter id, so re-announcements don't double-emit.
222
- * - `unregister` integration: when an extension is removed mid-session,
223
- * fire the disconnector the builder registered so connected pool
224
- * entries on the consumer side tear down cleanly.
225
- *
226
- * **What's platform-specific** lives entirely in `build`:
227
- * - Which `wallet.chains` to accept.
228
- * - Which features to gate on.
229
- * - The `id` slug prefix (use `slugify(prefix, wallet.name)`).
230
- * - The capability resolver, the feature decoding, the chain coercion.
231
- *
232
- * Disconnectors are keyed by the wallet object itself (identity equality),
233
- * not by adapter id; Wallet Standard's `register` / `unregister` emit
234
- * the same wallet object, so identity is the cheapest stable key.
215
+ * `@wallet-standard/app` is an optional peer dep: a failed import quietly
216
+ * disables discovery. Disconnectors are keyed by wallet identity rather
217
+ * than adapter id because `register` / `unregister` emit the same object.
235
218
  */
236
- const discoverWalletStandard = (onAdapter, build) => {
219
+ const discoverWalletStandard = (onAdapter, build, loadModule = loadWalletStandardModule) => {
237
220
  let cancelled = false;
238
221
  let internalUnsub = null;
239
222
  (async () => {
240
223
  let mod;
241
224
  try {
242
- const imported = await import("@wallet-standard/app");
243
- if (typeof imported !== "object" || imported === null || !("getWallets" in imported) || typeof imported.getWallets !== "function") throw new Error("@wallet-standard/app has no getWallets export");
244
- mod = imported;
225
+ mod = await loadModule();
245
226
  } catch (error) {
246
227
  if (!warnedMissingApp) {
247
228
  warnedMissingApp = true;
@@ -287,5 +268,5 @@ const discoverWalletStandard = (onAdapter, build) => {
287
268
  };
288
269
 
289
270
  //#endregion
290
- export { buildAccount, buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, pickAccountByAddress, pickFirstAddress, slugify };
271
+ export { buildAccount, buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, isWalletStandardWallet, pickAccountByAddress, pickFirstAddress, slugify };
291
272
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["buildAccount"],"sources":["../src/primitives.ts","../src/adapter-core.ts","../src/capabilities.ts","../src/discovery.ts"],"sourcesContent":["import type { WalletStandardWallet, WalletStandardWalletAccount } from \"./types\";\n\n/**\n * Convert a wallet name into a stable kebab-case adapter id, scoped by\n * a platform prefix so adapters from different platforms (Phantom SVM,\n * Phantom Sui, Phantom BTC) coexist in the pool without colliding on\n * the shared `getWallets()` bus.\n *\n * @example\n * slugify(\"svm\", \"Phantom\") // \"wallet-standard:svm-phantom\"\n * slugify(\"sui\", \"Sui Wallet\") // \"wallet-standard:sui-sui-wallet\"\n * slugify(\"btc\", \"Phantom\") // \"wallet-standard:btc-phantom\"\n */\nconst slugify = (platformPrefix: string, name: string): string => {\n const slug = name\n .trim()\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/gv, \"-\");\n return `wallet-standard:${platformPrefix}-${slug}`;\n};\n\n/**\n * Read a Wallet Standard feature off a wallet, returning `undefined`\n * when absent. `features` is a reverse-DNS-keyed registry typed\n * `Record<string, unknown>` by the spec, so the caller declares the\n * concrete feature shape it expects; this accessor is the single\n * boundary where that dynamism is acknowledged. The `T` parameter is\n * intentionally caller-supplied (not inferable) and the assertion is\n * unavoidable for an untyped registry.\n */\n// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- caller-supplied feature shape; not inferable from args\nconst getFeature = <T>(wallet: WalletStandardWallet, name: string): T | undefined => {\n const feature: unknown = wallet.features[name];\n if (typeof feature !== \"object\" || feature === null) {\n return undefined;\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- untyped Wallet Standard feature registry; caller declares the shape\n return feature as T;\n};\n\n/**\n * Pick the first address out of a wallet's accounts array, returning\n * `null` when the wallet currently exposes none.\n */\nconst pickFirstAddress = (accounts: ReadonlyArray<WalletStandardWalletAccount>): string | null => {\n const first = accounts[0];\n return first === undefined ? null : first.address;\n};\n\n/**\n * Look up a wallet account by address, falling back to the first\n * exposed account when the requested address isn't present.\n *\n * Used by per-call routing in `sendTx` / `signMessage`: callers pass\n * an `account` from `ConnectedWallet.accounts` to sign with a non-\n * active address; the wallet's `accounts[]` is the source of truth.\n */\nconst pickAccountByAddress = (\n accounts: ReadonlyArray<WalletStandardWalletAccount>,\n address: string,\n): WalletStandardWalletAccount | undefined =>\n accounts.find((a) => a.address === address) ?? accounts[0];\n\n// Re-exported so this package's public surface keeps offering the shared\n// account builder now that the implementation lives in @usebutr/core\n// (next to `Account` and the reducer that relies on its id format).\nexport { buildAccount } from \"@usebutr/core\";\nexport { getFeature, pickAccountByAddress, pickFirstAddress, slugify };\n","import type { Account, ChainBase, ConnectorEvent } from \"@usebutr/core\";\nimport { buildAccount, logWarn, sanitizeIcon } from \"@usebutr/core\";\n\nimport { getFeature, pickAccountByAddress, pickFirstAddress } from \"./primitives\";\nimport type {\n StandardConnectFeature,\n StandardDisconnectFeature,\n StandardEventsFeature,\n WalletStandardWallet,\n WalletStandardWalletAccount,\n} from \"./types\";\n\ntype WalletStandardCoreInput = {\n /** CAIP-2 prefix including its colon (`solana:`, `sui:`, `bip122:`). */\n chainPrefix: string;\n /** Stable adapter id. Each platform package owns its own slug prefix. */\n id: string;\n /** How this adapter names itself in errors (`Bitcoin`, `SVM`, `Sui`). */\n label: string;\n /** CAIP-2 namespace without its colon. */\n namespace: string;\n /** How the chain family reads in errors (`Bitcoin`, `Solana`, `Sui`). */\n platform: string;\n /** Chain ids worth preferring over a bare prefix match. Matched against\n * the wallet's own ordering, so a wallet listing `mainnet-beta` before\n * `mainnet` keeps the chain it advertised first. */\n preferredChainIds: ReadonlyArray<string>;\n /** Called with a function that pushes a synthetic `disconnected` event\n * to all current subscribers. The discovery layer invokes it on Wallet\n * Standard `unregister`. */\n registerDisconnector?: (emit: () => void) => void;\n /** Re-point the active chain when the wallet reports a `chains` change.\n * Polkadot opts out: its wallets advertise a single relay chain, so a\n * re-point would only ever move the adapter off the chain it resolved. */\n trackChainChanges: boolean;\n wallet: WalletStandardWallet;\n};\n\ntype WalletStandardCore = {\n /** Chains the wallet advertises in this namespace; feeds capability gating. */\n chainCount: number;\n connect: (opts?: { silent?: boolean }) => Promise<void>;\n /** Active CAIP-2 chain id, for features that take a per-call `chain`. */\n currentChainId: () => string;\n disconnect: () => Promise<void>;\n getAccount: () => Promise<Account | null>;\n getAccounts: () => Promise<Array<Account>>;\n getSigner: () => Promise<unknown>;\n /** Whether the wallet advertises `standard:events`; feeds capability gating. */\n hasEvents: boolean;\n icon: string | undefined;\n id: string;\n name: string;\n /** Wallet Standard account a call should route through: the one matching\n * `account`, else the wallet's first exposed account. */\n resolveAccount: (account?: { walletAddress: string }) => WalletStandardWalletAccount;\n subscribe: (listener: (event: ConnectorEvent) => void) => () => void;\n switchChain: (chain: ChainBase) => Promise<void>;\n /** butr chain for the active chain id, for responses that mint their own\n * `Account` (Sign-In-With-Solana) rather than going through `getAccounts`. */\n toChain: () => ChainBase;\n};\n\n/**\n * Session plumbing every Wallet Standard platform adapter repeats: chain\n * resolution, the connect/disconnect handshake, account reads, the local\n * listener set that lets `switchChain` synthesise `accountChanged`, and\n * the `standard:events` bridge. Returns `null` for wallets butr can't\n * drive (no chain in this namespace, or no `standard:connect`), which is\n * the same `null` each platform adapter returns to its caller.\n *\n * Platform packages keep what genuinely differs: which features they read\n * off the wallet, how they encode transactions and messages, and their\n * balance units. `getSigner` hands back the raw Wallet Standard wallet so\n * consumers can wrap it in their own signing library.\n */\nconst createWalletStandardCore = ({\n chainPrefix,\n id,\n label,\n namespace,\n platform,\n preferredChainIds,\n registerDisconnector,\n trackChainChanges,\n wallet,\n}: WalletStandardCoreInput): WalletStandardCore | null => {\n const pickChain = (chains: ReadonlyArray<string>): string | null =>\n chains.find((c) => preferredChainIds.includes(c)) ??\n chains.find((c) => c.startsWith(chainPrefix)) ??\n null;\n\n const initialChainId = pickChain(wallet.chains);\n if (initialChainId === null) {\n return null;\n }\n const connect = getFeature<StandardConnectFeature>(wallet, \"standard:connect\");\n if (connect === undefined) {\n return null;\n }\n\n const disconnect = getFeature<StandardDisconnectFeature>(wallet, \"standard:disconnect\");\n const events = getFeature<StandardEventsFeature>(wallet, \"standard:events\");\n\n let currentChainId = initialChainId;\n const toChain = (): ChainBase => ({\n id: currentChainId,\n // butr ships no chain-id → display-name table; the wallet name stands\n // in and consumers overlay their own labels via structural typing.\n name: wallet.name,\n namespace,\n reference: currentChainId.slice(chainPrefix.length),\n });\n\n const listeners = new Set<(event: ConnectorEvent) => void>();\n const notifyAccountChanged = () => {\n if (wallet.accounts.length === 0) {\n return;\n }\n const chain = toChain();\n const built = wallet.accounts.map((a) => buildAccount(a.address, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n for (const listener of listeners) {\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n }\n };\n\n registerDisconnector?.(() => {\n for (const listener of listeners) {\n listener({ type: \"disconnected\" });\n }\n });\n\n return {\n chainCount: wallet.chains.filter((c) => c.startsWith(chainPrefix)).length,\n\n async connect(opts) {\n // Forward `silent` so eager hydration restores already-authorized\n // accounts without re-opening the wallet's approval UI. Wallets\n // that don't honour `silent` fall back to their normal behaviour.\n await connect.connect(opts?.silent === true ? { silent: true } : undefined);\n },\n\n currentChainId: () => currentChainId,\n\n async disconnect() {\n if (disconnect !== undefined) {\n try {\n await disconnect.disconnect();\n } catch (error) {\n logWarn(`[butr] ${label} Wallet Standard disconnect threw:`, error);\n }\n }\n },\n\n getAccount: () => {\n const address = pickFirstAddress(wallet.accounts);\n return Promise.resolve(address === null ? null : buildAccount(address, toChain()));\n },\n\n getAccounts: () => {\n const chain = toChain();\n return Promise.resolve(wallet.accounts.map((a) => buildAccount(a.address, chain)));\n },\n\n getSigner: () => Promise.resolve(wallet),\n\n hasEvents: Boolean(events),\n\n icon: sanitizeIcon(wallet.icon),\n id,\n name: wallet.name,\n\n resolveAccount: (account) => {\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : wallet.accounts[0];\n if (wsAccount === undefined) {\n throw new Error(\"No connected account\");\n }\n return wsAccount;\n },\n\n subscribe(listener) {\n listeners.add(listener);\n let unsubWallet: (() => void) | null = null;\n if (events !== undefined) {\n const unsub = events.on(\"change\", (changes) => {\n // Chain change: the wallet switched network. Re-point\n // currentChainId so subsequent per-call `chain` inputs route\n // through it, mirroring `switchChain`'s local-state model.\n if (trackChainChanges && changes.chains !== undefined) {\n const next = pickChain(changes.chains);\n if (next !== null) {\n currentChainId = next;\n }\n }\n\n if (changes.accounts !== undefined) {\n if (changes.accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n // Forward the FULL accounts list; Wallet Standard's\n // change.accounts reflects the wallet's current exposure\n // set. Mirroring it into the pool entry keeps the array in\n // sync with what the wallet actually allows us to sign with,\n // so single-account-exposure wallets (Phantom Solana,\n // MetaMask Snap) don't accumulate stale addresses.\n const chain = toChain();\n const built = changes.accounts.map((a) => buildAccount(a.address, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return;\n }\n\n if (trackChainChanges && changes.chains !== undefined) {\n notifyAccountChanged();\n }\n });\n unsubWallet = () => {\n unsub();\n };\n }\n return () => {\n listeners.delete(listener);\n unsubWallet?.();\n };\n },\n\n switchChain: (chain) => {\n if (chain.namespace !== namespace) {\n throw new Error(\n `${label} adapter received non-${platform} chain \"${chain.id}\". Pass a chain with namespace \"${namespace}\".`,\n );\n }\n // The wallet must advertise this chain; otherwise the per-call\n // `chain` input would be rejected by the wallet later.\n if (!wallet.chains.includes(chain.id)) {\n throw new Error(\n `Wallet ${wallet.name} does not advertise chain \"${chain.id}\". Available: ${wallet.chains.join(\", \")}`,\n );\n }\n currentChainId = chain.id;\n // Synthesise an `accountChanged` so butr's reducer updates the pool\n // entry's chain; the wallet itself has no event to fire here.\n notifyAccountChanged();\n return Promise.resolve();\n },\n\n toChain,\n };\n};\n\nexport type { WalletStandardCore };\nexport { createWalletStandardCore };\n","import type { WalletCapabilities } from \"@usebutr/core\";\n\ntype WalletCapabilityProfile = {\n /** Chains the wallet advertises in this namespace. */\n chainCount: number;\n /** Wallet advertises `standard:events`. */\n events: boolean;\n /** Wallet advertises a sign-and-broadcast feature:\n * `solana:signAndSendTransaction`, `sui:signAndExecuteTransaction`,\n * `bitcoin:sendTransfer`. */\n sendTransaction: boolean;\n /** Wallet advertises `solana:signIn`. No other namespace has an\n * equivalent, so every non-SVM caller passes `false`. */\n signIn: boolean;\n signMessage: boolean;\n /** Wallet advertises a sign-only feature: `solana:signTransaction`,\n * `sui:signTransaction`, `bitcoin:signPsbt`. */\n signTransaction: boolean;\n};\n\n/**\n * Wallet Standard → butr capability mapping, shared by every platform\n * package. The flags that are constant across all of them:\n *\n * - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no\n * balance/receipt RPC, and butr ships no RPC client of its own.\n * - `requestAccounts`: no programmatic equivalent of EIP-2255; re-running\n * `standard:connect` doesn't produce new accounts on any major Wallet\n * Standard wallet in practice (Phantom, MetaMask Snap, Solflare,\n * Backpack, Xverse, Talisman).\n * - `switchAccount`: no protocol-level silent account switch; the user\n * picks the account inside the wallet.\n * - `switchChain`: true when the wallet advertises more than one chain in\n * this namespace. The switch is local re-routing (the per-call `chain`\n * input), so with a single chain there's nothing to switch to.\n *\n * Each platform package owns only the feature-name → flag mapping, since\n * the feature names differ per namespace.\n */\nconst buildWalletCapabilities = (profile: WalletCapabilityProfile): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: profile.sendTransaction,\n signIn: profile.signIn,\n signMessage: profile.signMessage,\n signTransaction: profile.signTransaction,\n subscribe: profile.events,\n switchAccount: false,\n switchChain: profile.chainCount > 1,\n});\n\nexport type { WalletCapabilityProfile };\nexport { buildWalletCapabilities };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { logWarn } from \"@usebutr/core\";\n\nimport type { WalletStandardAppModule, WalletStandardWallet } from \"./types\";\n\nlet warnedMissingApp = false;\n\n/**\n * Callback supplied by per-platform discovery to build a `WalletAdapter`\n * out of a Wallet Standard wallet announcement.\n *\n * Returns `null` when the wallet doesn't advertise this platform; that\n * lets a multi-chain wallet (e.g. Phantom: SVM + EVM + Sui + Bitcoin)\n * produce one adapter per platform without each builder needing to know\n * about the others.\n *\n * The second argument is a hook the builder calls during construction\n * to wire a synthetic `disconnected` event into its subscriber set.\n * The discovery loop fires the registered emitter when Wallet Standard\n * announces `unregister` for that wallet.\n */\ntype WalletStandardAdapterBuilder = (\n wallet: WalletStandardWallet,\n registerDisconnector: (emit: () => void) => void,\n) => WalletAdapter | null;\n\n/**\n * Subscribe to Wallet Standard announcements and turn matching wallets\n * into butr `WalletAdapter`s using a per-platform `build` callback.\n *\n * **Shared machinery** every platform package needs:\n * - Dynamic import of `@wallet-standard/app` (optional peer dep;\n * failure to resolve quietly disables discovery so consumers who\n * don't install it pay nothing).\n * - Cancellation: the returned unsubscribe is safe to call before the\n * dynamic import has resolved.\n * - Dedupe by adapter id, so re-announcements don't double-emit.\n * - `unregister` integration: when an extension is removed mid-session,\n * fire the disconnector the builder registered so connected pool\n * entries on the consumer side tear down cleanly.\n *\n * **What's platform-specific** lives entirely in `build`:\n * - Which `wallet.chains` to accept.\n * - Which features to gate on.\n * - The `id` slug prefix (use `slugify(prefix, wallet.name)`).\n * - The capability resolver, the feature decoding, the chain coercion.\n *\n * Disconnectors are keyed by the wallet object itself (identity equality),\n * not by adapter id; Wallet Standard's `register` / `unregister` emit\n * the same wallet object, so identity is the cheapest stable key.\n */\nconst discoverWalletStandard = (\n onAdapter: (adapter: WalletAdapter) => void,\n build: WalletStandardAdapterBuilder,\n): (() => void) => {\n let cancelled = false;\n let internalUnsub: (() => void) | null = null;\n\n void (async () => {\n let mod: WalletStandardAppModule;\n try {\n const imported: unknown = await import(\"@wallet-standard/app\");\n if (\n typeof imported !== \"object\" ||\n imported === null ||\n !(\"getWallets\" in imported) ||\n typeof imported.getWallets !== \"function\"\n ) {\n throw new Error(\"@wallet-standard/app has no getWallets export\");\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- validated getWallets export above; the rest of the untyped module is trusted\n mod = imported as WalletStandardAppModule;\n } catch (error) {\n if (!warnedMissingApp) {\n warnedMissingApp = true;\n logWarn(\n \"[butr] Wallet Standard wallet discovery is disabled: `@wallet-standard/app` could not be loaded. Install it (`npm i @wallet-standard/app`) to detect Wallet Standard wallets.\",\n error,\n );\n }\n return;\n }\n if (cancelled) {\n return;\n }\n\n const seenIds = new Set<string>();\n // wallet object → emitter that pushes synthetic `disconnected` to\n // this wallet's adapter's subscribers. Identity-keyed so the\n // unregister handler can find the right emitter cheaply.\n const disconnectors = new Map<WalletStandardWallet, () => void>();\n\n const tryAdd = (wallet: WalletStandardWallet) => {\n const adapter = build(wallet, (emit) => {\n disconnectors.set(wallet, emit);\n });\n if (!adapter || seenIds.has(adapter.id)) {\n return;\n }\n seenIds.add(adapter.id);\n onAdapter(adapter);\n };\n\n const app = mod.getWallets();\n for (const wallet of app.get()) {\n tryAdd(wallet);\n }\n\n const offRegister = app.on(\"register\", (...wallets) => {\n for (const wallet of wallets) {\n tryAdd(wallet);\n }\n });\n const offUnregister = app.on(\"unregister\", (...wallets) => {\n for (const wallet of wallets) {\n const emit = disconnectors.get(wallet);\n if (emit) {\n emit();\n disconnectors.delete(wallet);\n }\n }\n });\n internalUnsub = () => {\n offRegister();\n offUnregister();\n };\n })();\n\n return () => {\n cancelled = true;\n internalUnsub?.();\n internalUnsub = null;\n };\n};\n\nexport type { WalletStandardAdapterBuilder };\nexport { discoverWalletStandard };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["buildAccount"],"sources":["../src/primitives.ts","../src/adapter-core.ts","../src/capabilities.ts","../src/discovery.ts"],"sourcesContent":["import type { WalletSigner } from \"@usebutr/core\";\n\nimport type {\n WalletStandardFeature,\n WalletStandardWallet,\n WalletStandardWalletAccount,\n} from \"./types\";\n\n/**\n * The platform prefix scopes the id so one multi-chain wallet (Phantom\n * SVM, Sui, BTC) yields distinct adapters on the shared `getWallets()` bus.\n */\nconst slugify = (platformPrefix: string, name: string): string => {\n const slug = name\n .trim()\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/gv, \"-\");\n return `wallet-standard:${platformPrefix}-${slug}`;\n};\n\n/**\n * The spec types `features` as a reverse-DNS-keyed `Record<string,\n * unknown>`, so the caller declares the shape it expects. This accessor is\n * the single boundary where that dynamism is acknowledged.\n */\ntype FeatureGuard<Feature> = (\n feature: WalletStandardFeature,\n) => feature is WalletStandardFeature & Feature;\n\nconst getFeature = <Feature>(\n wallet: WalletStandardWallet,\n name: string,\n isFeature: FeatureGuard<Feature>,\n): (WalletStandardFeature & Feature) | undefined => {\n const feature = wallet.features[name];\n if (feature === undefined || !isFeature(feature)) {\n return undefined;\n }\n return feature;\n};\n\nconst isWalletStandardWallet = (value: WalletSigner): value is WalletStandardWallet =>\n \"accounts\" in value &&\n Array.isArray(value.accounts) &&\n \"chains\" in value &&\n Array.isArray(value.chains) &&\n \"features\" in value &&\n typeof value.features === \"object\" &&\n value.features !== null &&\n \"icon\" in value &&\n typeof value.icon === \"string\" &&\n \"name\" in value &&\n typeof value.name === \"string\" &&\n \"version\" in value &&\n typeof value.version === \"string\";\n\nconst pickFirstAddress = (accounts: ReadonlyArray<WalletStandardWalletAccount>): string | null => {\n const first = accounts[0];\n return first === undefined ? null : first.address;\n};\n\n/**\n * Per-call routing for `sendTx` / `signMessage`: callers pass an `account`\n * from `ConnectedWallet.accounts` to sign with a non-active address, but\n * the wallet's own `accounts[]` stays the source of truth.\n */\nconst pickAccountByAddress = (\n accounts: ReadonlyArray<WalletStandardWalletAccount>,\n address: string,\n): WalletStandardWalletAccount | undefined =>\n accounts.find((a) => a.address === address) ?? accounts[0];\n\nexport { buildAccount } from \"@usebutr/core\";\nexport { getFeature, isWalletStandardWallet, pickAccountByAddress, pickFirstAddress, slugify };\n","import type { Account, ChainBase, ConnectorEvent } from \"@usebutr/core\";\nimport { buildAccount, logWarn, sanitizeIcon } from \"@usebutr/core\";\n\nimport { getFeature, pickAccountByAddress, pickFirstAddress } from \"./primitives\";\nimport type {\n StandardConnectFeature,\n StandardDisconnectFeature,\n StandardEventsFeature,\n WalletStandardFeature,\n WalletStandardWallet,\n WalletStandardWalletAccount,\n} from \"./types\";\n\nconst isStandardConnectFeature = (\n feature: WalletStandardFeature,\n): feature is WalletStandardFeature & StandardConnectFeature =>\n \"connect\" in feature && typeof feature.connect === \"function\";\n\nconst isStandardDisconnectFeature = (\n feature: WalletStandardFeature,\n): feature is WalletStandardFeature & StandardDisconnectFeature =>\n \"disconnect\" in feature && typeof feature.disconnect === \"function\";\n\nconst isStandardEventsFeature = (\n feature: WalletStandardFeature,\n): feature is WalletStandardFeature & StandardEventsFeature =>\n \"on\" in feature && typeof feature.on === \"function\";\n\ntype WalletStandardCoreInput = {\n /** CAIP-2 prefix including its colon (`solana:`, `sui:`, `bip122:`). */\n chainPrefix: string;\n /** Stable adapter id. Each platform package owns its own slug prefix. */\n id: string;\n /** How this adapter names itself in errors (`Bitcoin`, `SVM`, `Sui`). */\n label: string;\n /** CAIP-2 namespace without its colon. */\n namespace: string;\n /** How the chain family reads in errors (`Bitcoin`, `Solana`, `Sui`). */\n platform: string;\n /** Chain ids worth preferring over a bare prefix match. Matched against\n * the wallet's own ordering, so a wallet listing `mainnet-beta` before\n * `mainnet` keeps the chain it advertised first. */\n preferredChainIds: ReadonlyArray<string>;\n /** Called with a function that pushes a synthetic `disconnected` event\n * to all current subscribers. The discovery layer invokes it on Wallet\n * Standard `unregister`. */\n registerDisconnector?: (emit: () => void) => void;\n /** Re-point the active chain when the wallet reports a `chains` change.\n * Polkadot opts out: its wallets advertise a single relay chain, so a\n * re-point would only ever move the adapter off the chain it resolved. */\n trackChainChanges: boolean;\n wallet: WalletStandardWallet;\n};\n\ntype WalletStandardCore = {\n /** Chains the wallet advertises in this namespace; feeds capability gating. */\n chainCount: number;\n connect: (opts?: { silent?: boolean }) => Promise<void>;\n /** Active CAIP-2 chain id, for features that take a per-call `chain`. */\n currentChainId: () => string;\n disconnect: () => Promise<void>;\n getAccount: () => Promise<Account | null>;\n getAccounts: () => Promise<Array<Account>>;\n getSigner: () => Promise<WalletStandardWallet>;\n /** Whether the wallet advertises `standard:events`; feeds capability gating. */\n hasEvents: boolean;\n icon: string | undefined;\n id: string;\n name: string;\n /** Wallet Standard account a call should route through: the one matching\n * `account`, else the wallet's first exposed account. */\n resolveAccount: (account?: { walletAddress: string }) => WalletStandardWalletAccount;\n subscribe: (listener: (event: ConnectorEvent) => void) => () => void;\n switchChain: (chain: ChainBase) => Promise<void>;\n /** butr chain for the active chain id, for responses that mint their own\n * `Account` (Sign-In-With-Solana) rather than going through `getAccounts`. */\n toChain: () => ChainBase;\n};\n\n/**\n * Shared session plumbing for every Wallet Standard platform adapter.\n * `getSigner` hands back the raw Wallet Standard wallet so consumers can\n * wrap it in their own signing library.\n */\nconst createWalletStandardCore = ({\n chainPrefix,\n id,\n label,\n namespace,\n platform,\n preferredChainIds,\n registerDisconnector,\n trackChainChanges,\n wallet,\n}: WalletStandardCoreInput): WalletStandardCore | null => {\n const preferredChains = new Set(preferredChainIds);\n const pickChain = (chains: ReadonlyArray<string>): string | null =>\n chains.find((c) => preferredChains.has(c)) ??\n chains.find((c) => c.startsWith(chainPrefix)) ??\n null;\n\n const initialChainId = pickChain(wallet.chains);\n if (initialChainId === null) {\n return null;\n }\n const connect = getFeature(wallet, \"standard:connect\", isStandardConnectFeature);\n if (connect === undefined) {\n return null;\n }\n\n const disconnect = getFeature(wallet, \"standard:disconnect\", isStandardDisconnectFeature);\n const events = getFeature(wallet, \"standard:events\", isStandardEventsFeature);\n\n let currentChainId = initialChainId;\n const toChain = (): ChainBase => ({\n id: currentChainId,\n name: wallet.name,\n namespace,\n reference: currentChainId.slice(chainPrefix.length),\n });\n\n const listeners = new Set<(event: ConnectorEvent) => void>();\n const notifyAccountChanged = () => {\n if (wallet.accounts.length === 0) {\n return;\n }\n const chain = toChain();\n const built = wallet.accounts.map((a) => buildAccount(a.address, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n for (const listener of listeners) {\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n }\n };\n\n registerDisconnector?.(() => {\n for (const listener of listeners) {\n listener({ type: \"disconnected\" });\n }\n });\n\n return {\n chainCount: wallet.chains.filter((c) => c.startsWith(chainPrefix)).length,\n\n async connect(opts) {\n await connect.connect(opts?.silent === true ? { silent: true } : undefined);\n },\n\n currentChainId: () => currentChainId,\n\n async disconnect() {\n if (disconnect !== undefined) {\n try {\n await disconnect.disconnect();\n } catch (error) {\n logWarn(`[butr] ${label} Wallet Standard disconnect threw:`, error);\n }\n }\n },\n\n getAccount: () => {\n const address = pickFirstAddress(wallet.accounts);\n return Promise.resolve(address === null ? null : buildAccount(address, toChain()));\n },\n\n getAccounts: () => {\n const chain = toChain();\n return Promise.resolve(wallet.accounts.map((a) => buildAccount(a.address, chain)));\n },\n\n getSigner: () => Promise.resolve(wallet),\n\n hasEvents: Boolean(events),\n\n icon: sanitizeIcon(wallet.icon),\n id,\n name: wallet.name,\n\n resolveAccount: (account) => {\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : wallet.accounts[0];\n if (wsAccount === undefined) {\n throw new Error(\"No connected account\");\n }\n return wsAccount;\n },\n\n subscribe(listener) {\n listeners.add(listener);\n let unsubWallet: (() => void) | null = null;\n if (events !== undefined) {\n const unsub = events.on(\"change\", (changes) => {\n if (trackChainChanges && changes.chains !== undefined) {\n const next = pickChain(changes.chains);\n if (next !== null) {\n currentChainId = next;\n }\n }\n\n if (changes.accounts !== undefined) {\n if (changes.accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n const chain = toChain();\n const built = changes.accounts.map((a) => buildAccount(a.address, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return;\n }\n\n if (trackChainChanges && changes.chains !== undefined) {\n notifyAccountChanged();\n }\n });\n unsubWallet = () => {\n unsub();\n };\n }\n return () => {\n listeners.delete(listener);\n unsubWallet?.();\n };\n },\n\n // Async so a rejected chain surfaces as a rejection. Declared\n // `Promise<void>`, this previously threw synchronously, so a caller\n // attaching `.catch()` instead of awaiting got an uncaught throw.\n switchChain: async (chain) => {\n if (chain.namespace !== namespace) {\n throw new Error(\n `${label} adapter received non-${platform} chain \"${chain.id}\". Pass a chain with namespace \"${namespace}\".`,\n );\n }\n if (!wallet.chains.includes(chain.id)) {\n throw new Error(\n `Wallet ${wallet.name} does not advertise chain \"${chain.id}\". Available: ${wallet.chains.join(\", \")}`,\n );\n }\n currentChainId = chain.id;\n notifyAccountChanged();\n await Promise.resolve();\n },\n\n toChain,\n };\n};\n\nexport type { WalletStandardCore };\nexport { createWalletStandardCore };\n","import type { WalletCapabilities } from \"@usebutr/core\";\n\ntype WalletCapabilityProfile = {\n /** Chains the wallet advertises in this namespace. */\n chainCount: number;\n /** Wallet advertises `standard:events`. */\n events: boolean;\n /** Wallet advertises a sign-and-broadcast feature:\n * `solana:signAndSendTransaction`, `sui:signAndExecuteTransaction`,\n * `bitcoin:sendTransfer`. */\n sendTransaction: boolean;\n /** Wallet advertises `solana:signIn`. No other namespace has an\n * equivalent, so every non-SVM caller passes `false`. */\n signIn: boolean;\n signMessage: boolean;\n /** Wallet advertises a sign-only feature: `solana:signTransaction`,\n * `sui:signTransaction`, `bitcoin:signPsbt`. */\n signTransaction: boolean;\n};\n\n/**\n * Wallet Standard has no balance/receipt RPC, no EIP-2255 equivalent, and\n * no silent account switch, so those flags are false everywhere.\n * `switchChain` re-routes locally, so it needs a second advertised chain.\n */\nconst buildWalletCapabilities = (profile: WalletCapabilityProfile): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: profile.sendTransaction,\n signIn: profile.signIn,\n signMessage: profile.signMessage,\n signTransaction: profile.signTransaction,\n subscribe: profile.events,\n switchAccount: false,\n switchChain: profile.chainCount > 1,\n});\n\nexport type { WalletCapabilityProfile };\nexport { buildWalletCapabilities };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { logWarn } from \"@usebutr/core\";\nimport type { Wallets as ImportedWallets } from \"@wallet-standard/app\";\n\nimport type {\n WalletStandardAppModule,\n WalletStandardFeature,\n WalletStandardModuleLoader,\n WalletStandardWallet,\n} from \"./types\";\n\ntype ImportedWallet = ReturnType<ImportedWallets[\"get\"]>[number];\n\nconst toFeatureRecord = (features: ImportedWallet[\"features\"]) => {\n const record: Record<string, WalletStandardFeature> = {};\n for (const [name, value] of Object.entries(features)) {\n if (typeof value !== \"object\" || value === null) {\n continue;\n }\n // The feature object is stored as-is rather than spread: a spread would\n // detach its methods from the wallet's own object, breaking any\n // implementation whose `connect` / `signMessage` reads `this`.\n record[name] = value;\n }\n return record;\n};\n\nconst wrappers = new WeakMap<ImportedWallet, WalletStandardWallet>();\n\n/**\n * Reads through to the wallet: it re-points `accounts` on connect, so a copy\n * pins the pre-connect empty list and every `getAccount()` then resolves null.\n * Memoised because `unregister` is matched to `register` by object identity.\n */\nconst mapWallet = (wallet: ImportedWallet): WalletStandardWallet => {\n const cached = wrappers.get(wallet);\n if (cached !== undefined) {\n return cached;\n }\n const wrapper: WalletStandardWallet = {\n get accounts() {\n return wallet.accounts;\n },\n get chains() {\n return wallet.chains;\n },\n features: toFeatureRecord(wallet.features),\n icon: wallet.icon,\n name: wallet.name,\n version: wallet.version,\n };\n wrappers.set(wallet, wrapper);\n return wrapper;\n};\n\nlet warnedMissingApp = false;\n\n/**\n * Returns `null` when the wallet doesn't advertise this platform, so a\n * multi-chain wallet yields one adapter per platform. `registerDisconnector`\n * wires the synthetic `disconnected` fired on Wallet Standard `unregister`.\n */\ntype WalletStandardAdapterBuilder = (\n wallet: WalletStandardWallet,\n registerDisconnector: (emit: () => void) => void,\n) => WalletAdapter | null;\n\nconst loadWalletStandardModule = async (): Promise<WalletStandardAppModule> => {\n const imported = await import(\"@wallet-standard/app\");\n if (\n typeof imported !== \"object\" ||\n imported === null ||\n !(\"getWallets\" in imported) ||\n typeof imported.getWallets !== \"function\"\n ) {\n throw new Error(\"@wallet-standard/app has no getWallets export\");\n }\n const source = imported.getWallets();\n return {\n getWallets: () => ({\n get: () => source.get().map(mapWallet),\n on: (event, listener) =>\n source.on(event, (...wallets) => {\n listener(...wallets.map(mapWallet));\n }),\n }),\n };\n};\n\n/**\n * `@wallet-standard/app` is an optional peer dep: a failed import quietly\n * disables discovery. Disconnectors are keyed by wallet identity rather\n * than adapter id because `register` / `unregister` emit the same object.\n */\nconst discoverWalletStandard = (\n onAdapter: (adapter: WalletAdapter) => void,\n build: WalletStandardAdapterBuilder,\n loadModule: WalletStandardModuleLoader = loadWalletStandardModule,\n): (() => void) => {\n let cancelled = false;\n let internalUnsub: (() => void) | null = null;\n\n void (async () => {\n let mod: WalletStandardAppModule;\n try {\n mod = await loadModule();\n } catch (error) {\n if (!warnedMissingApp) {\n warnedMissingApp = true;\n logWarn(\n \"[butr] Wallet Standard wallet discovery is disabled: `@wallet-standard/app` could not be loaded. Install it (`npm i @wallet-standard/app`) to detect Wallet Standard wallets.\",\n error,\n );\n }\n return;\n }\n if (cancelled) {\n return;\n }\n\n const seenIds = new Set<string>();\n const disconnectors = new Map<WalletStandardWallet, () => void>();\n\n const tryAdd = (wallet: WalletStandardWallet) => {\n const adapter = build(wallet, (emit) => {\n disconnectors.set(wallet, emit);\n });\n if (!adapter || seenIds.has(adapter.id)) {\n return;\n }\n seenIds.add(adapter.id);\n onAdapter(adapter);\n };\n\n const app = mod.getWallets();\n for (const wallet of app.get()) {\n tryAdd(wallet);\n }\n\n const offRegister = app.on(\"register\", (...wallets) => {\n for (const wallet of wallets) {\n tryAdd(wallet);\n }\n });\n const offUnregister = app.on(\"unregister\", (...wallets) => {\n for (const wallet of wallets) {\n const emit = disconnectors.get(wallet);\n if (emit) {\n emit();\n disconnectors.delete(wallet);\n }\n }\n });\n internalUnsub = () => {\n offRegister();\n offUnregister();\n };\n })();\n\n return () => {\n cancelled = true;\n internalUnsub?.();\n internalUnsub = null;\n };\n};\n\nexport type { WalletStandardAdapterBuilder };\nexport { discoverWalletStandard, mapWallet };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,21 @@
1
1
  {
2
2
  "name": "@usebutr/wallet-standard-shared",
3
- "version": "0.4.1",
3
+ "version": "1.0.0",
4
4
  "description": "Shared Wallet Standard discovery primitives used by @usebutr/svm, @usebutr/sui, @usebutr/bitcoin.",
5
+ "keywords": [
6
+ "butr",
7
+ "dapp",
8
+ "discovery",
9
+ "react",
10
+ "typescript",
11
+ "wallet",
12
+ "wallet-standard",
13
+ "web3"
14
+ ],
15
+ "homepage": "https://docs.usebutr.com/api/wallet-standard-shared",
16
+ "bugs": {
17
+ "url": "https://github.com/pedroapfilho/usebutr/issues"
18
+ },
5
19
  "license": "MIT",
6
20
  "author": "Pedro Filho <pedro@filho.me>",
7
21
  "repository": {
@@ -10,7 +24,8 @@
10
24
  "directory": "packages/wallet-standard-shared"
11
25
  },
12
26
  "files": [
13
- "dist"
27
+ "dist",
28
+ "CHANGELOG.md"
14
29
  ],
15
30
  "type": "module",
16
31
  "sideEffects": false,
@@ -21,12 +36,15 @@
21
36
  "default": "./dist/index.js"
22
37
  }
23
38
  },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
24
42
  "dependencies": {
25
- "@usebutr/core": "1.1.0"
43
+ "@usebutr/core": "2.0.1"
26
44
  },
27
45
  "devDependencies": {
28
46
  "@wallet-standard/app": "^1.1.1",
29
- "typescript": "^6.0.3",
47
+ "typescript": "^7.0.2",
30
48
  "vitest": "^4.1.10",
31
49
  "@repo/config-vitest": "0.0.0",
32
50
  "@repo/typescript-config": "0.0.0"
@@ -43,7 +61,6 @@
43
61
  "build": "tsdown",
44
62
  "clean": "rm -rf dist",
45
63
  "dev": "tsdown --watch",
46
- "lint": "oxlint src",
47
64
  "test": "vitest run --passWithNoTests",
48
65
  "test:coverage": "vitest run --coverage --passWithNoTests",
49
66
  "typecheck": "tsc -p tsconfig.json --noEmit"