@usebutr/wallet-standard-shared 0.5.0 → 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 +196 -0
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/index.d.ts +20 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -16
- package/dist/index.js.map +1 -1
- package/package.json +22 -4
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,4 +1,5 @@
|
|
|
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
5
|
* Declared inline so butr stays self-contained at the type level;
|
|
@@ -9,15 +10,18 @@ type WalletStandardWalletAccount = {
|
|
|
9
10
|
address: string;
|
|
10
11
|
chains: ReadonlyArray<string>;
|
|
11
12
|
features: ReadonlyArray<string>;
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
publicKey?: {
|
|
14
|
+
readonly length: number;
|
|
15
|
+
readonly [index: number]: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
type WalletStandardFeature = {
|
|
19
|
+
version?: string;
|
|
14
20
|
};
|
|
15
21
|
type WalletStandardWallet = {
|
|
16
22
|
accounts: ReadonlyArray<WalletStandardWalletAccount>;
|
|
17
23
|
chains: ReadonlyArray<string>;
|
|
18
|
-
|
|
19
|
-
* use sites. */
|
|
20
|
-
features: Readonly<Record<string, unknown>>;
|
|
24
|
+
features: Readonly<Record<string, WalletStandardFeature>>;
|
|
21
25
|
icon: string;
|
|
22
26
|
name: string;
|
|
23
27
|
version: string;
|
|
@@ -29,15 +33,18 @@ type WalletsApp = {
|
|
|
29
33
|
type WalletStandardAppModule = {
|
|
30
34
|
getWallets: () => WalletsApp;
|
|
31
35
|
};
|
|
36
|
+
type WalletStandardModuleLoader = () => Promise<WalletStandardAppModule>;
|
|
32
37
|
type StandardConnectFeature = {
|
|
33
38
|
connect: (input?: {
|
|
34
39
|
silent?: boolean;
|
|
35
40
|
}) => Promise<{
|
|
36
41
|
accounts: ReadonlyArray<WalletStandardWalletAccount>;
|
|
37
42
|
}>;
|
|
43
|
+
version?: string;
|
|
38
44
|
};
|
|
39
45
|
type StandardDisconnectFeature = {
|
|
40
46
|
disconnect: () => Promise<void>;
|
|
47
|
+
version?: string;
|
|
41
48
|
};
|
|
42
49
|
type StandardEventsListener = (changes: {
|
|
43
50
|
accounts?: ReadonlyArray<WalletStandardWalletAccount>;
|
|
@@ -46,6 +53,7 @@ type StandardEventsListener = (changes: {
|
|
|
46
53
|
}) => void;
|
|
47
54
|
type StandardEventsFeature = {
|
|
48
55
|
on: (event: "change", listener: StandardEventsListener) => () => void;
|
|
56
|
+
version?: string;
|
|
49
57
|
};
|
|
50
58
|
//#endregion
|
|
51
59
|
//#region src/primitives.d.ts
|
|
@@ -59,7 +67,9 @@ declare const slugify: (platformPrefix: string, name: string) => string;
|
|
|
59
67
|
* unknown>`, so the caller declares the shape it expects. This accessor is
|
|
60
68
|
* the single boundary where that dynamism is acknowledged.
|
|
61
69
|
*/
|
|
62
|
-
|
|
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;
|
|
63
73
|
declare const pickFirstAddress: (accounts: ReadonlyArray<WalletStandardWalletAccount>) => string | null;
|
|
64
74
|
/**
|
|
65
75
|
* Per-call routing for `sendTx` / `signMessage`: callers pass an `account`
|
|
@@ -105,7 +115,7 @@ type WalletStandardCore = {
|
|
|
105
115
|
disconnect: () => Promise<void>;
|
|
106
116
|
getAccount: () => Promise<Account | null>;
|
|
107
117
|
getAccounts: () => Promise<Array<Account>>;
|
|
108
|
-
getSigner: () => Promise<
|
|
118
|
+
getSigner: () => Promise<WalletStandardWallet>;
|
|
109
119
|
/** Whether the wallet advertises `standard:events`; feeds capability gating. */
|
|
110
120
|
hasEvents: boolean;
|
|
111
121
|
icon: string | undefined;
|
|
@@ -166,7 +176,7 @@ type WalletStandardAdapterBuilder = (wallet: WalletStandardWallet, registerDisco
|
|
|
166
176
|
* disables discovery. Disconnectors are keyed by wallet identity rather
|
|
167
177
|
* than adapter id because `register` / `unregister` emit the same object.
|
|
168
178
|
*/
|
|
169
|
-
declare const discoverWalletStandard: (onAdapter: (adapter: WalletAdapter) => void, build: WalletStandardAdapterBuilder) => (() => void);
|
|
179
|
+
declare const discoverWalletStandard: (onAdapter: (adapter: WalletAdapter) => void, build: WalletStandardAdapterBuilder, loadModule?: WalletStandardModuleLoader) => (() => void);
|
|
170
180
|
//#endregion
|
|
171
|
-
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 };
|
|
172
182
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"
|
|
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
|
@@ -8,16 +8,12 @@ import { buildAccount, buildAccount as buildAccount$1, logWarn, sanitizeIcon } f
|
|
|
8
8
|
const slugify = (platformPrefix, name) => {
|
|
9
9
|
return `wallet-standard:${platformPrefix}-${name.trim().toLowerCase().replaceAll(new RegExp("[^a-z0-9]+", "gv"), "-")}`;
|
|
10
10
|
};
|
|
11
|
-
|
|
12
|
-
* The spec types `features` as a reverse-DNS-keyed `Record<string,
|
|
13
|
-
* unknown>`, so the caller declares the shape it expects. This accessor is
|
|
14
|
-
* the single boundary where that dynamism is acknowledged.
|
|
15
|
-
*/
|
|
16
|
-
const getFeature = (wallet, name) => {
|
|
11
|
+
const getFeature = (wallet, name, isFeature) => {
|
|
17
12
|
const feature = wallet.features[name];
|
|
18
|
-
if (
|
|
13
|
+
if (feature === void 0 || !isFeature(feature)) return;
|
|
19
14
|
return feature;
|
|
20
15
|
};
|
|
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";
|
|
21
17
|
const pickFirstAddress = (accounts) => {
|
|
22
18
|
const first = accounts[0];
|
|
23
19
|
return first === void 0 ? null : first.address;
|
|
@@ -31,19 +27,23 @@ const pickAccountByAddress = (accounts, address) => accounts.find((a) => a.addre
|
|
|
31
27
|
|
|
32
28
|
//#endregion
|
|
33
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";
|
|
34
33
|
/**
|
|
35
34
|
* Shared session plumbing for every Wallet Standard platform adapter.
|
|
36
35
|
* `getSigner` hands back the raw Wallet Standard wallet so consumers can
|
|
37
36
|
* wrap it in their own signing library.
|
|
38
37
|
*/
|
|
39
38
|
const createWalletStandardCore = ({ chainPrefix, id, label, namespace, platform, preferredChainIds, registerDisconnector, trackChainChanges, wallet }) => {
|
|
40
|
-
const
|
|
39
|
+
const preferredChains = new Set(preferredChainIds);
|
|
40
|
+
const pickChain = (chains) => chains.find((c) => preferredChains.has(c)) ?? chains.find((c) => c.startsWith(chainPrefix)) ?? null;
|
|
41
41
|
const initialChainId = pickChain(wallet.chains);
|
|
42
42
|
if (initialChainId === null) return null;
|
|
43
|
-
const connect = getFeature(wallet, "standard:connect");
|
|
43
|
+
const connect = getFeature(wallet, "standard:connect", isStandardConnectFeature);
|
|
44
44
|
if (connect === void 0) return null;
|
|
45
|
-
const disconnect = getFeature(wallet, "standard:disconnect");
|
|
46
|
-
const events = getFeature(wallet, "standard:events");
|
|
45
|
+
const disconnect = getFeature(wallet, "standard:disconnect", isStandardDisconnectFeature);
|
|
46
|
+
const events = getFeature(wallet, "standard:events", isStandardEventsFeature);
|
|
47
47
|
let currentChainId = initialChainId;
|
|
48
48
|
const toChain = () => ({
|
|
49
49
|
id: currentChainId,
|
|
@@ -167,21 +167,62 @@ const buildWalletCapabilities = (profile) => ({
|
|
|
167
167
|
|
|
168
168
|
//#endregion
|
|
169
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
|
+
};
|
|
170
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
|
+
};
|
|
171
214
|
/**
|
|
172
215
|
* `@wallet-standard/app` is an optional peer dep: a failed import quietly
|
|
173
216
|
* disables discovery. Disconnectors are keyed by wallet identity rather
|
|
174
217
|
* than adapter id because `register` / `unregister` emit the same object.
|
|
175
218
|
*/
|
|
176
|
-
const discoverWalletStandard = (onAdapter, build) => {
|
|
219
|
+
const discoverWalletStandard = (onAdapter, build, loadModule = loadWalletStandardModule) => {
|
|
177
220
|
let cancelled = false;
|
|
178
221
|
let internalUnsub = null;
|
|
179
222
|
(async () => {
|
|
180
223
|
let mod;
|
|
181
224
|
try {
|
|
182
|
-
|
|
183
|
-
if (typeof imported !== "object" || imported === null || !("getWallets" in imported) || typeof imported.getWallets !== "function") throw new Error("@wallet-standard/app has no getWallets export");
|
|
184
|
-
mod = imported;
|
|
225
|
+
mod = await loadModule();
|
|
185
226
|
} catch (error) {
|
|
186
227
|
if (!warnedMissingApp) {
|
|
187
228
|
warnedMissingApp = true;
|
|
@@ -227,5 +268,5 @@ const discoverWalletStandard = (onAdapter, build) => {
|
|
|
227
268
|
};
|
|
228
269
|
|
|
229
270
|
//#endregion
|
|
230
|
-
export { buildAccount, buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, pickAccountByAddress, pickFirstAddress, slugify };
|
|
271
|
+
export { buildAccount, buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, isWalletStandardWallet, pickAccountByAddress, pickFirstAddress, slugify };
|
|
231
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 * 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 */\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\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, 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 * 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 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 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\";\n\nimport type { WalletStandardAppModule, WalletStandardWallet } from \"./types\";\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\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): (() => 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 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.
|
|
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": "2.0.
|
|
43
|
+
"@usebutr/core": "2.0.1"
|
|
26
44
|
},
|
|
27
45
|
"devDependencies": {
|
|
28
46
|
"@wallet-standard/app": "^1.1.1",
|
|
29
|
-
"typescript": "^
|
|
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"
|