@usebutr/core 1.0.0 → 2.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/dist/index.d.ts +226 -504
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +295 -318
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
//#region src/types/chain.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Consumers extend this with app-specific fields (logos, block explorers, etc.)
|
|
7
|
-
* via structural typing; butr never inspects beyond these 4 fields.
|
|
3
|
+
* CAIP-2 shaped. butr never reads past these four fields, so consumers
|
|
4
|
+
* can extend the type structurally with logos, explorers and the like.
|
|
8
5
|
*/
|
|
9
6
|
type ChainBase = {
|
|
10
7
|
/** CAIP-2 identifier, e.g. "eip155:1", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" */
|
|
@@ -24,11 +21,9 @@ type Account = {
|
|
|
24
21
|
walletAddress: string;
|
|
25
22
|
};
|
|
26
23
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* must build accounts through this helper (or keep the format
|
|
31
|
-
* byte-identical).
|
|
24
|
+
* The reducer compares accounts by the composite `<chain>:<address>`
|
|
25
|
+
* id, so every adapter must build accounts here or reproduce that
|
|
26
|
+
* format byte for byte.
|
|
32
27
|
*/
|
|
33
28
|
declare const buildAccount: (address: string, chain: ChainBase) => Account;
|
|
34
29
|
type Balance = {
|
|
@@ -44,24 +39,9 @@ type Balance = {
|
|
|
44
39
|
//#endregion
|
|
45
40
|
//#region src/types/capabilities.d.ts
|
|
46
41
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* hand-rolled adapters declare them explicitly). Consumers branch on
|
|
51
|
-
* these to gate UI affordances:
|
|
52
|
-
*
|
|
53
|
-
* ```tsx
|
|
54
|
-
* {wallet.connector.capabilities.requestAccounts ? (
|
|
55
|
-
* <button onClick={() => requestAccounts(wallet.connector.id)}>
|
|
56
|
-
* Request more accounts
|
|
57
|
-
* </button>
|
|
58
|
-
* ) : null}
|
|
59
|
-
* ```
|
|
60
|
-
*
|
|
61
|
-
* Each flag means "can this work right now," not "is the method
|
|
62
|
-
* defined": `signMessage: false` means calling `signMessage()` would
|
|
63
|
-
* reject; `switchChain: false` means switching is a no-op regardless
|
|
64
|
-
* of which chain is passed.
|
|
42
|
+
* Each flag means "can this work right now", not "is the method
|
|
43
|
+
* defined": `signMessage: false` means the call would reject,
|
|
44
|
+
* `switchChain: false` means switching is a no-op for every chain.
|
|
65
45
|
*/
|
|
66
46
|
type WalletCapabilities = {
|
|
67
47
|
/** `getBalance` returns a real on-chain value (vs. a 0n placeholder). */
|
|
@@ -100,84 +80,38 @@ type WalletCapabilities = {
|
|
|
100
80
|
//#endregion
|
|
101
81
|
//#region src/types/platform.d.ts
|
|
102
82
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* never drift (a missing platform here was why Polkadot connections failed
|
|
107
|
-
* to persist).
|
|
83
|
+
* Single runtime source of truth: `ChainPlatform` and the storage
|
|
84
|
+
* validators' allowlists both derive from it. Omitting a platform here
|
|
85
|
+
* silently stops its connections from persisting.
|
|
108
86
|
*/
|
|
109
87
|
declare const CHAIN_PLATFORMS: readonly ["evm", "svm", "sui", "bitcoin", "polkadot"];
|
|
110
88
|
type ChainPlatform = (typeof CHAIN_PLATFORMS)[number];
|
|
111
89
|
//#endregion
|
|
112
90
|
//#region src/types/chains-by-platform.d.ts
|
|
113
91
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* The platform key set is fixed by `ChainPlatform`. The value is the
|
|
118
|
-
* chain list; empty when the consumer doesn't want to support that
|
|
119
|
-
* platform's chains in this view. This is the only type that callers
|
|
120
|
-
* write down; the values come from per-platform packages
|
|
121
|
-
* (`EVM_CHAINS_LIST`, `SVM_CHAINS_LIST`, etc).
|
|
92
|
+
* Values come from the per-platform packages (`EVM_CHAINS_LIST`,
|
|
93
|
+
* `SVM_CHAINS_LIST`, …); an empty list opts that platform out of the
|
|
94
|
+
* consumer's chain UI.
|
|
122
95
|
*/
|
|
123
96
|
type ChainsByPlatform = Readonly<Record<ChainPlatform, ReadonlyArray<ChainBase>>>;
|
|
124
97
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* Use this in apps that target one or two chain platforms; importing
|
|
129
|
-
* only those packages keeps unused chain registries out of the bundle.
|
|
130
|
-
* Apps that want every chain reach for `CHAINS_BY_PLATFORM` from
|
|
131
|
-
* `@usebutr/wallets` instead.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* // EVM-only app: Solana/Sui/Bitcoin tables never enter the bundle
|
|
135
|
-
* import { EVM_CHAINS_LIST } from "@usebutr/evm";
|
|
136
|
-
* import { buildChainsByPlatform } from "@usebutr/core";
|
|
137
|
-
*
|
|
138
|
-
* const chains = buildChainsByPlatform({ evm: EVM_CHAINS_LIST });
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* // Multi-chain app: pull from each package the app actually uses
|
|
142
|
-
* import { EVM_CHAINS_LIST } from "@usebutr/evm";
|
|
143
|
-
* import { SVM_CHAINS_LIST } from "@usebutr/svm";
|
|
144
|
-
*
|
|
145
|
-
* const chains = buildChainsByPlatform({
|
|
146
|
-
* evm: EVM_CHAINS_LIST,
|
|
147
|
-
* svm: SVM_CHAINS_LIST,
|
|
148
|
-
* });
|
|
98
|
+
* Naming only the platforms an app targets keeps the other packages'
|
|
99
|
+
* chain registries out of its bundle. Apps that want all of them import
|
|
100
|
+
* `CHAINS_BY_PLATFORM` from `@usebutr/wallets`.
|
|
149
101
|
*/
|
|
150
102
|
declare const buildChainsByPlatform: (partial: Partial<ChainsByPlatform>) => ChainsByPlatform;
|
|
151
103
|
//#endregion
|
|
152
104
|
//#region src/types/connector.d.ts
|
|
153
105
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* -
|
|
157
|
-
* - `loadable`: the wallet's SDK can be loaded on demand (e.g. WalletConnect
|
|
158
|
-
* modal that pops a QR code without requiring a browser extension).
|
|
159
|
-
* - `not-installed`: the wallet isn't reachable. Consumers typically render
|
|
160
|
-
* a "download" affordance pointing at `meta.url`.
|
|
106
|
+
* Gate Connect on `installed` or `loadable`: a `loadable` wallet has no
|
|
107
|
+
* extension yet still connects (WalletConnect's QR modal). Only
|
|
108
|
+
* `not-installed` should degrade to a download link at `meta.url`.
|
|
161
109
|
*/
|
|
162
110
|
type WalletAvailability = "installed" | "loadable" | "not-installed";
|
|
163
111
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* the
|
|
167
|
-
*
|
|
168
|
-
* - `accountChanged` carries both the new active `account` AND the full
|
|
169
|
-
* `accounts` array the wallet currently exposes. The runtime mirrors
|
|
170
|
-
* that list verbatim into the pool entry. This handles two cases
|
|
171
|
-
* uniformly:
|
|
172
|
-
* - Multi-account wallets (MetaMask, Rabby, Brave): the user adds or
|
|
173
|
-
* removes accounts from the dapp's permission set; the array grows
|
|
174
|
-
* or shrinks to match.
|
|
175
|
-
* - Single-account-exposure wallets (Phantom EVM/SVM, MetaMask Snap):
|
|
176
|
-
* only the active account is ever in `accounts`; switching swaps it
|
|
177
|
-
* in place rather than appending.
|
|
178
|
-
* Also covers chain switches; the new chain lives inside `account.chain`.
|
|
179
|
-
* - `disconnected` → `DISCONNECTED` (wallet has gone away externally:
|
|
180
|
-
* user locked it, removed the extension, etc.).
|
|
112
|
+
* `accountChanged` must carry every account the wallet still exposes,
|
|
113
|
+
* not just the new active one: the runtime mirrors the array verbatim
|
|
114
|
+
* into the pool entry. Chain switches arrive as `account.chain`.
|
|
181
115
|
*/
|
|
182
116
|
type ConnectorEvent = {
|
|
183
117
|
account: Account;
|
|
@@ -200,16 +134,10 @@ type Connector<P extends ChainPlatform = ChainPlatform> = {
|
|
|
200
134
|
* use one of the per-platform adapter types (`EvmAdapter`,
|
|
201
135
|
* `SvmAdapter`, etc). */
|
|
202
136
|
chainPlatform: P;
|
|
203
|
-
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* already-authorized accounts; butr's mount-time hydration passes it
|
|
208
|
-
* so a reload restores wallets without re-prompting (Wallet Standard
|
|
209
|
-
* `standard:connect`'s `silent` input; the `eth_accounts` read on
|
|
210
|
-
* EIP-1193). Adapters that can't reconnect without a prompt should
|
|
211
|
-
* reject when `silent` is set rather than show UI; hydration treats
|
|
212
|
-
* the rejection as a clean restore failure. */
|
|
137
|
+
/** `opts.silent` is hydration's non-interactive reconnect (Wallet
|
|
138
|
+
* Standard `standard:connect` silent input, `eth_accounts` on
|
|
139
|
+
* EIP-1193). An adapter that cannot honour it must reject instead of
|
|
140
|
+
* prompting; hydration reads that as a clean restore failure. */
|
|
213
141
|
connect: (opts?: {
|
|
214
142
|
silent?: boolean;
|
|
215
143
|
}) => Promise<void>;
|
|
@@ -222,30 +150,23 @@ type Connector<P extends ChainPlatform = ChainPlatform> = {
|
|
|
222
150
|
* show many accounts at once (MetaMask with multiple imports). If
|
|
223
151
|
* omitted, butr defaults to `[await getAccount()]`. */
|
|
224
152
|
getAccounts?: () => Promise<Array<Account>>;
|
|
225
|
-
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
153
|
+
/** Discovery runs `sanitizeIcon` at construction, so on discovered
|
|
154
|
+
* adapters this is a trimmed non-empty string or `undefined`: render
|
|
155
|
+
* it straight into `next/image` with no re-sanitizing and no
|
|
156
|
+
* `icon !== ""` guard. Hand-rolled adapters own that guarantee. */
|
|
229
157
|
icon?: string;
|
|
230
158
|
/** Stable key: "metamask", "phantom", etc. Pool entries are keyed by this. */
|
|
231
159
|
id: string;
|
|
232
160
|
/** Human name: "MetaMask", "Phantom", etc. UI-facing only. */
|
|
233
161
|
name: string;
|
|
234
|
-
/**
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* accounts directly in the extension. Resolution doesn't include the
|
|
239
|
-
* new accounts; call `getAccounts()` (or use butr's
|
|
240
|
-
* `useRequestAccounts` hook, which refreshes the pool entry for
|
|
241
|
-
* you). */
|
|
162
|
+
/** Opens the wallet's account-selection UI (`wallet_requestPermissions`
|
|
163
|
+
* on EIP-6963; usually unset on Wallet Standard). Resolution does not
|
|
164
|
+
* carry the new accounts: follow it with `getAccounts()`, or use the
|
|
165
|
+
* `useRequestAccounts` hook which refreshes the pool entry. */
|
|
242
166
|
requestAccounts?: () => Promise<void>;
|
|
243
|
-
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* disconnect / reset. Bridges native wallet events into the reducer so
|
|
247
|
-
* consumers don't have to wire `accountsChanged` / `chainChanged`
|
|
248
|
-
* themselves. */
|
|
167
|
+
/** Bridges native wallet events (`accountsChanged`, `chainChanged`, …)
|
|
168
|
+
* into the reducer. Only `ConnectorLifecycle` may call this, and it
|
|
169
|
+
* guarantees at most one live subscription per connector. */
|
|
249
170
|
subscribe?: (listener: (event: ConnectorEvent) => void) => () => void;
|
|
250
171
|
};
|
|
251
172
|
type ConnectorMeta = {
|
|
@@ -254,7 +175,10 @@ type ConnectorMeta = {
|
|
|
254
175
|
* this at render time to gate the "Connect" button. */
|
|
255
176
|
availability?: () => WalletAvailability;
|
|
256
177
|
chainPlatform: ChainPlatform;
|
|
257
|
-
/** Optional image URL or data URI for wallet selection UIs.
|
|
178
|
+
/** Optional image URL or data URI for wallet selection UIs. Unlike
|
|
179
|
+
* `Connector.icon`, this one is consumer-supplied and butr does not
|
|
180
|
+
* sanitize it; run it through `sanitizeIcon` if the value came from
|
|
181
|
+
* wallet metadata rather than your own assets. */
|
|
258
182
|
icon?: string;
|
|
259
183
|
id: string;
|
|
260
184
|
name: string;
|
|
@@ -282,12 +206,9 @@ type WalletBase = {
|
|
|
282
206
|
getTransactionReceipt: (tx: string) => Promise<{
|
|
283
207
|
status: "Success" | "Error" | "Pending";
|
|
284
208
|
}>;
|
|
285
|
-
/**
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* wallet's currently-active one. EVM wallets honour this via
|
|
289
|
-
* `tx.from`; Wallet Standard wallets via the feature's `account`
|
|
290
|
-
* input. Omit for "use whichever the wallet picks." */
|
|
209
|
+
/** `account` routes through a specific exposed address (EVM via
|
|
210
|
+
* `tx.from`, Wallet Standard via the feature's `account` input);
|
|
211
|
+
* omitting it lets the wallet pick. */
|
|
291
212
|
sendTx: (tx: unknown, account?: Account) => Promise<string>;
|
|
292
213
|
/** Submit a transaction targeting a specific chain. The optional
|
|
293
214
|
* callback fires after the connector has switched chain (consumers
|
|
@@ -295,15 +216,9 @@ type WalletBase = {
|
|
|
295
216
|
* specific exposed address (see `sendTx`). */
|
|
296
217
|
sendTxToChain: (tx: unknown, targetChainId: string, account?: Account, cb?: () => void) => Promise<string>;
|
|
297
218
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* `signedMessage`, not the input bytes. EVM wallets echo the input.
|
|
302
|
-
*
|
|
303
|
-
* Pass an `account` to sign with a specific exposed address. EIP-1193
|
|
304
|
-
* routes it through `personal_sign`'s address param; Wallet Standard
|
|
305
|
-
* uses the feature's `account` input. Both support per-call signing
|
|
306
|
-
* without changing the wallet's active account.
|
|
219
|
+
* Verify against `signedMessage`, not the input: Solana Wallet
|
|
220
|
+
* Standard wallets may prefix or re-encode it. `account` signs with a
|
|
221
|
+
* specific address without changing the wallet's active one.
|
|
307
222
|
*/
|
|
308
223
|
signMessage: (msg: Uint8Array, account?: Account) => Promise<{
|
|
309
224
|
signature: Uint8Array;
|
|
@@ -316,19 +231,15 @@ type WalletBase = {
|
|
|
316
231
|
switchChain: (chain: ChainBase) => Promise<void>;
|
|
317
232
|
};
|
|
318
233
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
* flows aren't exposed through this surface.
|
|
234
|
+
* No `signIn` (SIWE is app-level here, not a protocol method) and no
|
|
235
|
+
* `signTransaction`: EVM wallets sign and send in one step through
|
|
236
|
+
* `eth_sendTransaction`.
|
|
323
237
|
*/
|
|
324
238
|
type EvmWallet = WalletBase;
|
|
325
239
|
/**
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
* - `signTransaction`: sign-only path for wallets that advertise
|
|
330
|
-
* `solana:signTransaction` but not `solana:signAndSendTransaction`.
|
|
331
|
-
* Optional; `capabilities.signTransaction` gates availability.
|
|
240
|
+
* Both additions are optional at runtime; gate them on
|
|
241
|
+
* `capabilities.signIn` / `capabilities.signTransaction`, which mirror
|
|
242
|
+
* what the wallet actually advertises.
|
|
332
243
|
*/
|
|
333
244
|
type SvmWallet = WalletBase & {
|
|
334
245
|
/** Sign In With Solana (SIWS, `solana:signIn`). Authenticates the user
|
|
@@ -352,24 +263,27 @@ type SvmWallet = WalletBase & {
|
|
|
352
263
|
* consumer via `@mysten/sui`'s SuiClient.
|
|
353
264
|
*/
|
|
354
265
|
type SuiWallet = WalletBase & {
|
|
355
|
-
|
|
266
|
+
/** Sign a Sui transaction WITHOUT executing it. Returns BOTH halves the
|
|
267
|
+
* chain requires: `SuiClient.executeTransactionBlock` needs
|
|
268
|
+
* `{ transactionBlock, signature }`, so a bare `Uint8Array` cannot express
|
|
269
|
+
* the result and a consumer holding one cannot tell which half they have. */
|
|
270
|
+
signTransaction?: (tx: unknown, account?: Account) => Promise<{
|
|
271
|
+
bytes: Uint8Array;
|
|
272
|
+
signature: Uint8Array;
|
|
273
|
+
}>;
|
|
356
274
|
};
|
|
357
275
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
* broadcast through their own Esplora / Electrum client.
|
|
276
|
+
* `signTransaction` is `bitcoin:signPsbt`: pass `psbt.toBuffer()` bytes
|
|
277
|
+
* and get signed PSBT bytes back, to finalise and broadcast through
|
|
278
|
+
* your own Esplora or Electrum client.
|
|
362
279
|
*/
|
|
363
280
|
type BitcoinWallet = WalletBase & {
|
|
364
281
|
signTransaction?: (tx: unknown, account?: Account) => Promise<Uint8Array>;
|
|
365
282
|
};
|
|
366
283
|
/**
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
* `getSigner()` handoff; the consumer builds and submits with the
|
|
371
|
-
* wallet's signer (e.g. polkadot-api). Message signing works via the
|
|
372
|
-
* injected `signer.signRaw`. Same shape as `EvmWallet`.
|
|
284
|
+
* No standalone `signTransaction`: building an extrinsic needs chain
|
|
285
|
+
* metadata over RPC, which butr does not ship, so transaction signing
|
|
286
|
+
* goes through the `getSigner()` handoff.
|
|
373
287
|
*/
|
|
374
288
|
type PolkadotWallet = WalletBase;
|
|
375
289
|
/** Per-platform full adapter shapes: `Connector` + the platform's
|
|
@@ -381,22 +295,9 @@ type SuiAdapter = Connector<"sui"> & SuiWallet;
|
|
|
381
295
|
type BitcoinAdapter = Connector<"bitcoin"> & BitcoinWallet;
|
|
382
296
|
type PolkadotAdapter = Connector<"polkadot"> & PolkadotWallet;
|
|
383
297
|
/**
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
* access to platform-specific methods like `signIn` (SVM) or
|
|
388
|
-
* `signTransaction` (SVM / Sui / Bitcoin). Calling those methods on a
|
|
389
|
-
* non-narrowed `WalletAdapter` is a TypeScript error; that's the
|
|
390
|
-
* point. The discriminant carries the type-level fact "this method
|
|
391
|
-
* doesn't exist on EVM" so consumers can't accidentally branch on
|
|
392
|
-
* `capabilities.signIn` and call a method that EVM adapters don't
|
|
393
|
-
* implement.
|
|
394
|
-
*
|
|
395
|
-
* Runtime gating via `capabilities` still matters for the methods that
|
|
396
|
-
* are OPTIONAL within a platform (a Solana wallet might or might not
|
|
397
|
-
* advertise `solana:signTransaction`). Capabilities narrow "wallet
|
|
398
|
-
* supports this feature"; the discriminated union narrows "this
|
|
399
|
-
* platform has this concept at all".
|
|
298
|
+
* The union narrows "this platform has the concept at all"; the
|
|
299
|
+
* `capabilities` flags narrow "this wallet supports it right now".
|
|
300
|
+
* Both gates are needed, and neither substitutes for the other.
|
|
400
301
|
*/
|
|
401
302
|
type WalletAdapter = EvmAdapter | SvmAdapter | SuiAdapter | BitcoinAdapter | PolkadotAdapter;
|
|
402
303
|
type ConnectedWallet = {
|
|
@@ -411,33 +312,15 @@ type ConnectedWallet = {
|
|
|
411
312
|
//#endregion
|
|
412
313
|
//#region src/types/discoverer.d.ts
|
|
413
314
|
/**
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* `@usebutr/bitcoin`) exports one of these. The aggregator package
|
|
418
|
-
* (`@usebutr/wallets`) composes them into `autoDiscovery()` without
|
|
419
|
-
* needing to know per-platform defaults; the descriptor owns them.
|
|
420
|
-
*
|
|
421
|
-
* Adding a new chain platform means writing a `PlatformDiscoverer`
|
|
422
|
-
* inside the new package and adding one import to the aggregator's
|
|
423
|
-
* registry, which is keyed by `ChainPlatform`. The aggregator's logic
|
|
424
|
-
* doesn't need to grow.
|
|
425
|
-
*
|
|
426
|
-
* Two parts:
|
|
427
|
-
* - `subscribe`: the primary discovery channel (EIP-6963 / Wallet
|
|
428
|
-
* Standard / etc).
|
|
429
|
-
* - `fallback`: optional. The legacy-injected channel that should
|
|
430
|
-
* only emit if the primary channel hasn't produced an adapter for
|
|
431
|
-
* the same browser session by the settle deadline. `@usebutr/evm`
|
|
432
|
-
* has one (window.ethereum); `@usebutr/bitcoin` has one
|
|
433
|
-
* (window.unisat / sats-connect / window.btc); SVM and Sui don't.
|
|
315
|
+
* Each platform package exports exactly one; `@usebutr/wallets` composes
|
|
316
|
+
* them via a registry keyed by `ChainPlatform`, so a new chain adds a
|
|
317
|
+
* descriptor and a registry entry, not aggregator logic.
|
|
434
318
|
*/
|
|
435
319
|
type PlatformDiscoverer = {
|
|
436
320
|
/**
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* for the same wallet has already announced through the primary path.
|
|
321
|
+
* Legacy-injected channel (window.ethereum, window.unisat, …). Must
|
|
322
|
+
* consult `hasAnyPrimaryAdapter` and stay quiet when standards-based
|
|
323
|
+
* discovery already announced the same wallet, or it double-lists.
|
|
441
324
|
*/
|
|
442
325
|
fallback?: {
|
|
443
326
|
subscribe: (onAdapter: (adapter: WalletAdapter) => void, opts: {
|
|
@@ -450,16 +333,9 @@ type PlatformDiscoverer = {
|
|
|
450
333
|
//#endregion
|
|
451
334
|
//#region src/types/errors.d.ts
|
|
452
335
|
/**
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
* MetaMask uses EIP-1193 codes, Phantom throws stringly-typed errors,
|
|
457
|
-
* embedded SDKs throw their own classes) into a small set of UX-meaningful
|
|
458
|
-
* variants. Consumers branch on `kind` instead of regexing message strings.
|
|
459
|
-
*
|
|
460
|
-
* `message` is always present and human-readable. `cause` preserves the
|
|
461
|
-
* original thrown value so callers can inspect raw connector errors when
|
|
462
|
-
* the variant is `Unknown`.
|
|
336
|
+
* Wallet SDKs disagree on error shape (EIP-1193 codes, bare strings,
|
|
337
|
+
* bespoke classes), so consumers branch on `kind` rather than regexing
|
|
338
|
+
* messages. `cause` keeps the original value for the `Unknown` case.
|
|
463
339
|
*/
|
|
464
340
|
type ConnectionError = {
|
|
465
341
|
kind: "UserRejected";
|
|
@@ -488,17 +364,9 @@ type ConnectionError = {
|
|
|
488
364
|
};
|
|
489
365
|
type ConnectionErrorKind = ConnectionError["kind"];
|
|
490
366
|
/**
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
* - butr's own `Error("Connection timeout")` (from the 90s connect timeout)
|
|
495
|
-
* - butr's own `Error("Failed to get account")` (from the connect flow)
|
|
496
|
-
* - EIP-1193 numeric `code` properties (`4001` → UserRejected,
|
|
497
|
-
* `-32002` → RequestPending, `4100`/`4900`/`4901` → NotConnected:
|
|
498
|
-
* unauthorized / disconnected from all-or-one chains)
|
|
499
|
-
* - common message substrings: "user rejected" / "user denied",
|
|
500
|
-
* "locked", "chain", etc.
|
|
501
|
-
* - anything else → `Unknown` with `cause` set to the original value.
|
|
367
|
+
* EIP-1193 codes: `4001` rejected, `-32002` pending, `4100`/`4900`/`4901`
|
|
368
|
+
* unauthorized or disconnected. Message-substring matching is the last
|
|
369
|
+
* resort for SDKs that ship no codes at all.
|
|
502
370
|
*/
|
|
503
371
|
declare const mapConnectionError: (raw: unknown) => ConnectionError;
|
|
504
372
|
//#endregion
|
|
@@ -547,16 +415,9 @@ type WalletPersistence = {
|
|
|
547
415
|
//#endregion
|
|
548
416
|
//#region src/storage/snapshot.d.ts
|
|
549
417
|
/**
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
* Notably absent: the `Connector` instance. A wallet extension exists
|
|
555
|
-
* only in the browser, so a server render can know *which* wallet was
|
|
556
|
-
* connected and *what address* it held, but cannot dispatch
|
|
557
|
-
* `signMessage`/`sendTransaction` on it. Splitting display from action
|
|
558
|
-
* along this seam keeps the impossibility expressed in the types
|
|
559
|
-
* rather than hidden inside a runtime check.
|
|
418
|
+
* Carries no `Connector` by design: a wallet extension exists only in
|
|
419
|
+
* the browser, so a server render can name the wallet and its address
|
|
420
|
+
* but can never dispatch on it.
|
|
560
421
|
*/
|
|
561
422
|
type WalletSnapshot = {
|
|
562
423
|
activeConnectorId: string | null;
|
|
@@ -576,48 +437,17 @@ type SnapshotOptions = {
|
|
|
576
437
|
};
|
|
577
438
|
declare const EMPTY_SNAPSHOT: WalletSnapshot;
|
|
578
439
|
/**
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* (Server Component, route handler, edge middleware, even client
|
|
583
|
-
* code). Pair with `createCookieStorageDriver({ initialCookies })`
|
|
584
|
-
* and `<WalletManagerProvider initialSnapshot={…} />` to render a
|
|
585
|
-
* connected shell server-side without a hydration flash.
|
|
586
|
-
*
|
|
587
|
-
* **Stale-snapshot semantics.** The snapshot reflects whatever the
|
|
588
|
-
* browser most recently persisted. If the user has since uninstalled
|
|
589
|
-
* the wallet, switched accounts, or disconnected in another tab, the
|
|
590
|
-
* client-side hydration will reconcile reality and the live store
|
|
591
|
-
* will diverge from the snapshot. Treat the snapshot as an
|
|
592
|
-
* *optimistic* shell; accurate enough to avoid a paint flicker,
|
|
593
|
-
* authoritative only after `useIsHydrated()` is true.
|
|
594
|
-
*
|
|
595
|
-
* **Inputs.** Accepts the three shapes Next.js / Express / Hono /
|
|
596
|
-
* generic-Node cookie code naturally produces:
|
|
597
|
-
* - A plain object: `{ "butr-pool": "{...}", … }`
|
|
598
|
-
* - An array of `{ name, value }` (Next.js' `cookies().getAll()`)
|
|
599
|
-
* - An iterable of `[name, value]` tuples
|
|
600
|
-
*
|
|
601
|
-
* Malformed entries are dropped with a `logWarn` (same policy as
|
|
602
|
-
* `WalletStorage.getPool`); a cross-tab corruption shouldn't crash
|
|
603
|
-
* the server render.
|
|
440
|
+
* Optimistic: only what the browser last persisted, so an uninstall or
|
|
441
|
+
* other-tab disconnect makes it stale. Authoritative once the entry
|
|
442
|
+
* leaves `reconnectingIds`; `isHydrated` is true from render one.
|
|
604
443
|
*/
|
|
605
444
|
declare const readWalletSnapshot: (source: CookieSource, options?: SnapshotOptions) => WalletSnapshot;
|
|
606
445
|
//#endregion
|
|
607
446
|
//#region src/types/manager.d.ts
|
|
608
447
|
/**
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
* - `restoredIds`: wallets that came back fully. Their pool entries
|
|
613
|
-
* are live and consumers can use them immediately.
|
|
614
|
-
* - `pendingIds`: wallets whose adapter wasn't registered yet
|
|
615
|
-
* (auto-discovery's async warmup). The runtime retries each one
|
|
616
|
-
* when discovery announces a matching id, so most of these will
|
|
617
|
-
* restore within a few hundred ms of mount.
|
|
618
|
-
* - `dropped`: wallets whose restore actually failed (connector
|
|
619
|
-
* threw mid-flight). These have been removed from storage; consumer
|
|
620
|
-
* UX can surface "Couldn't reconnect Phantom; connect again."
|
|
448
|
+
* `pendingIds` are not failures: discovery announces those adapters
|
|
449
|
+
* asynchronously and the runtime retries them, usually within a few
|
|
450
|
+
* hundred ms. Only `dropped` entries have been removed from storage.
|
|
621
451
|
*/
|
|
622
452
|
type HydrationOutcome = {
|
|
623
453
|
dropped: Array<{
|
|
@@ -633,65 +463,35 @@ type WalletManagerConfig = {
|
|
|
633
463
|
/** Function to instantiate a connector by ID */
|
|
634
464
|
createConnector: (id: string) => WalletAdapter | null;
|
|
635
465
|
/**
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
* - `pool` is populated with `ConnectedWallet` entries whose
|
|
640
|
-
* `connector` is a shadow adapter (see `createShadowAdapter`):
|
|
641
|
-
* identity-only, all capabilities `false`, methods throw
|
|
642
|
-
* `ShadowConnectorError` if called.
|
|
643
|
-
* - `activeConnectorId` and `selection` are set from the snapshot.
|
|
644
|
-
* - `isHydrated` flips `true` immediately on construction.
|
|
645
|
-
* - Every seeded id appears in `reconnectingIds`; the background
|
|
646
|
-
* silent-reconnect pass removes the id and replaces the pool
|
|
647
|
-
* entry with a live adapter on success, or drops the entry on
|
|
648
|
-
* failure.
|
|
649
|
-
*
|
|
650
|
-
* Pre-hydration UI renders from the snapshot's data (address,
|
|
651
|
-
* accounts, chain, name, icon) without a flash. Action affordances
|
|
652
|
-
* (sign, send) are naturally gated by the shadow's all-false
|
|
653
|
-
* capabilities, or consumers can branch on `reconnectingIds`.
|
|
466
|
+
* Seeds the pool with shadow adapters and flips `isHydrated` true on
|
|
467
|
+
* construction, so identity renders without a flash but every seeded
|
|
468
|
+
* id sits in `reconnectingIds` until silent reconnect verifies it.
|
|
654
469
|
*/
|
|
655
470
|
initialState?: WalletSnapshot;
|
|
656
471
|
/** Called after a wallet is successfully connected */
|
|
657
472
|
onConnect?: (wallet: ConnectedWallet) => void;
|
|
658
473
|
/**
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
* `
|
|
662
|
-
* connected. Useful for piping into observability tooling
|
|
663
|
-
* (Sentry, OTel) without each consumer wiring `try/catch`s around
|
|
664
|
-
* `connectWallet` themselves.
|
|
474
|
+
* Fires for every failed attempt (rejection, locked wallet, timeout,
|
|
475
|
+
* …), so observability can hook here instead of wrapping every
|
|
476
|
+
* `connectWallet` call.
|
|
665
477
|
*/
|
|
666
478
|
onConnectError?: (error: ConnectionError, connectorId: string) => void;
|
|
667
479
|
/** Called after a wallet is disconnected */
|
|
668
480
|
onDisconnect?: (chainPlatform: ChainPlatform) => void;
|
|
669
|
-
/**
|
|
670
|
-
* Called once after butr's mount-time hydration finishes. Receives a
|
|
671
|
-
* `HydrationOutcome` summarising which stored wallets were restored,
|
|
672
|
-
* which are pending an adapter announcement, and which failed.
|
|
673
|
-
* Useful for surfacing "Phantom couldn't be reconnected; try
|
|
674
|
-
* again" UX or piping a metric to telemetry.
|
|
675
|
-
*/
|
|
481
|
+
/** Fires once, after the mount-time hydration pass. */
|
|
676
482
|
onHydrated?: (outcome: HydrationOutcome) => void;
|
|
677
483
|
/** Called after all wallets are reset (e.g., to clear auth tokens) */
|
|
678
484
|
onReset?: () => void | Promise<void>;
|
|
679
485
|
/**
|
|
680
|
-
*
|
|
681
|
-
* `slowConnectThresholdMs`
|
|
682
|
-
*
|
|
683
|
-
* surfacing a "still trying, check your wallet" hint in the UI or
|
|
684
|
-
* piping a slow-path metric to telemetry.
|
|
486
|
+
* Fires at most once per attempt, once it passes
|
|
487
|
+
* `slowConnectThresholdMs` without settling. The attempt keeps
|
|
488
|
+
* running; this is a hint, not a timeout.
|
|
685
489
|
*/
|
|
686
490
|
onSlowConnect?: (connectorId: string) => void;
|
|
687
491
|
/**
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
* to know; quota-exceeded errors, IndexedDB shutdown, cross-tab
|
|
692
|
-
* conflicts, cookie size limits. `context` is a short string
|
|
693
|
-
* describing which write failed (e.g. `"failed to persist pool"`).
|
|
694
|
-
* The default behaviour when no callback is set is `console.warn`.
|
|
492
|
+
* Persistence is fire-and-forget: a failed write (quota, cookie size,
|
|
493
|
+
* cross-tab conflict) never breaks reducer state, it only surfaces
|
|
494
|
+
* here. Defaults to `console.warn`.
|
|
695
495
|
*/
|
|
696
496
|
onStorageError?: (error: unknown, context: string) => void;
|
|
697
497
|
/** Threshold for `onSlowConnect`, in milliseconds. Defaults to 5_000. */
|
|
@@ -704,55 +504,9 @@ type WalletManagerConfig = {
|
|
|
704
504
|
//#endregion
|
|
705
505
|
//#region src/types/signer.d.ts
|
|
706
506
|
/**
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
710
|
-
* signer shape lives in the platform-specific packages: `@usebutr/evm`
|
|
711
|
-
* returns an EIP-1193 provider, `@usebutr/svm` returns a Wallet
|
|
712
|
-
* Standard wallet, `@usebutr/sui` returns the same Wallet Standard
|
|
713
|
-
* wallet narrowed to Sui features, `@usebutr/bitcoin` returns either a
|
|
714
|
-
* Wallet Standard wallet or an injected provider depending on which
|
|
715
|
-
* adapter discovered it.
|
|
716
|
-
*
|
|
717
|
-
* Consumers cast the `unknown` to whichever signer their integration
|
|
718
|
-
* library expects. This registry exists so the cast target is sourced
|
|
719
|
-
* from one place; when a platform package renames its signer type,
|
|
720
|
-
* consumer code keeps working through the registry without an explicit
|
|
721
|
-
* patch.
|
|
722
|
-
*
|
|
723
|
-
* **How to extend.** Each platform package declares a module-augmentation
|
|
724
|
-
* block that adds its key to this interface. For example,
|
|
725
|
-
* `@usebutr/evm` ships:
|
|
726
|
-
*
|
|
727
|
-
* ```ts
|
|
728
|
-
* declare module "@usebutr/core" {
|
|
729
|
-
* interface SignerForPlatform {
|
|
730
|
-
* evm: Eip1193Provider;
|
|
731
|
-
* }
|
|
732
|
-
* }
|
|
733
|
-
* ```
|
|
734
|
-
*
|
|
735
|
-
* Consumers that import the EVM package get the typed entry for free;
|
|
736
|
-
* the augmentation is transitive through TypeScript's structural
|
|
737
|
-
* declaration merging.
|
|
738
|
-
*
|
|
739
|
-
* **Usage.**
|
|
740
|
-
*
|
|
741
|
-
* ```ts
|
|
742
|
-
* import type { SignerForPlatform } from "@usebutr/core";
|
|
743
|
-
*
|
|
744
|
-
* const signer = (await wallet.connector.getSigner()) as SignerForPlatform["evm"];
|
|
745
|
-
* ```
|
|
746
|
-
*
|
|
747
|
-
* The cast is still there: `getSigner` itself stays type-erased to
|
|
748
|
-
* keep the cross-package boundary loose, but the cast target lives in
|
|
749
|
-
* one canonical place.
|
|
750
|
-
*
|
|
751
|
-
* **Why not make `getSigner` generic.** Generic-on-platform `getSigner`
|
|
752
|
-
* would require `Connector` to be a discriminated union by
|
|
753
|
-
* `chainPlatform`, which is a public-API breaking change held for a
|
|
754
|
-
* future major release. The registry is the small step you can take
|
|
755
|
-
* today; the union is the larger step that makes the cast obsolete.
|
|
507
|
+
* Canonical cast target for `getSigner()`'s `unknown`, which stays
|
|
508
|
+
* type-erased to keep the cross-package boundary loose. Platform
|
|
509
|
+
* packages add their key by augmenting this interface.
|
|
756
510
|
*/
|
|
757
511
|
interface SignerForPlatform {}
|
|
758
512
|
/** Convenience alias for narrowing a single platform's signer type. */
|
|
@@ -760,34 +514,24 @@ type SignerOf<P extends keyof SignerForPlatform> = SignerForPlatform[P];
|
|
|
760
514
|
//#endregion
|
|
761
515
|
//#region src/wallet-source.d.ts
|
|
762
516
|
/**
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
* implement this type without depending on `@usebutr/wallets`.
|
|
517
|
+
* The discovery seam: implementable by third parties without depending
|
|
518
|
+
* on `@usebutr/wallets`, which itself just composes the per-platform
|
|
519
|
+
* sources into one.
|
|
767
520
|
*/
|
|
768
521
|
type WalletSource = {
|
|
769
522
|
subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void;
|
|
770
523
|
};
|
|
771
524
|
/**
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
* so an EVM-only app can do
|
|
775
|
-
* `createWalletSource(discoverEvmAdapters)` without importing anything
|
|
776
|
-
* protocol-bearing beyond `@usebutr/evm`.
|
|
525
|
+
* Takes the exact shape of `discoverEvmAdapters` and friends, so a
|
|
526
|
+
* single-platform app keeps `@usebutr/wallets` out of its bundle.
|
|
777
527
|
*/
|
|
778
528
|
declare const createWalletSource: (subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void) => WalletSource;
|
|
779
529
|
//#endregion
|
|
780
530
|
//#region src/store/reducer.d.ts
|
|
781
531
|
/**
|
|
782
|
-
*
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
* attempt. The fifth value, `"reconnecting"`, is *derived* by
|
|
786
|
-
* `useConnectionStatus` when the active wallet is still backed by a
|
|
787
|
-
* shadow adapter (its id is in `state.reconnectingIds`). The
|
|
788
|
-
* derivation lives in the React selector so the reducer can stay a
|
|
789
|
-
* narrow state machine while the public API matches wagmi's
|
|
790
|
-
* vocabulary.
|
|
532
|
+
* The reducer never writes `"reconnecting"`; `useConnectionStatus`
|
|
533
|
+
* derives it from `reconnectingIds` so the state machine stays narrow
|
|
534
|
+
* while the public vocabulary matches wagmi's.
|
|
791
535
|
*/
|
|
792
536
|
type ConnectionStatus = "idle" | "connecting" | "success" | "error" | "reconnecting";
|
|
793
537
|
type State = {
|
|
@@ -799,13 +543,9 @@ type State = {
|
|
|
799
543
|
isUserDisconnected: boolean;
|
|
800
544
|
pool: Map<string, ConnectedWallet>;
|
|
801
545
|
/**
|
|
802
|
-
*
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
* the silent reconnect to succeed. The set shrinks as entries
|
|
806
|
-
* upgrade (`HYDRATED` / `CONNECT_SUCCEEDED` clear matching ids) or
|
|
807
|
-
* drop (`DISCONNECTED` / `RESET`). Empty for stores constructed
|
|
808
|
-
* without `initialState`.
|
|
546
|
+
* Ids still backed by a shadow adapter from `initialState`. Empty
|
|
547
|
+
* without `initialState`; shrinks on `HYDRATED` / `CONNECT_SUCCEEDED`
|
|
548
|
+
* and on `DISCONNECTED` / `RESET`.
|
|
809
549
|
*/
|
|
810
550
|
reconnectingIds: ReadonlySet<string>;
|
|
811
551
|
selection: Map<ChainPlatform, string>;
|
|
@@ -813,21 +553,9 @@ type State = {
|
|
|
813
553
|
//#endregion
|
|
814
554
|
//#region src/store/shadow-adapter.d.ts
|
|
815
555
|
/**
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
* snapshot). Shadow adapters carry the identity and account data of a
|
|
820
|
-
* previously-connected wallet, but the live wallet extension hasn't
|
|
821
|
-
* been verified yet; the silent reconnect happens asynchronously
|
|
822
|
-
* after mount.
|
|
823
|
-
*
|
|
824
|
-
* UI code that gates affordances on `wallet.connector.capabilities.*`
|
|
825
|
-
* never reaches a shadow method (capabilities are all `false`). Code
|
|
826
|
-
* that calls through anyway hits this typed error, which is the
|
|
827
|
-
* correct loud failure: the consumer ignored the capability gate.
|
|
828
|
-
*
|
|
829
|
-
* Consumers wanting to wait out the reconnecting window should branch
|
|
830
|
-
* on whether `connectorId` is in `state.reconnectingIds`.
|
|
556
|
+
* Loud failure for consumers that called a wallet method without gating
|
|
557
|
+
* on `capabilities.*` (all `false` here) or `reconnectingIds`, before
|
|
558
|
+
* silent reconnect swapped in the live adapter.
|
|
831
559
|
*/
|
|
832
560
|
declare class ShadowConnectorError extends Error {
|
|
833
561
|
readonly code = "BUTR_RECONNECTING";
|
|
@@ -836,17 +564,9 @@ declare class ShadowConnectorError extends Error {
|
|
|
836
564
|
constructor(method: string, connectorId: string);
|
|
837
565
|
}
|
|
838
566
|
/**
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
* for consumers writing wagmi-style "is this connection verified yet"
|
|
843
|
-
* checks without subscribing to `reconnectingIds` directly.
|
|
844
|
-
*
|
|
845
|
-
* Detection is structural: a shadow has all capabilities set to false.
|
|
846
|
-
* Live adapters always advertise at least one capability (every wallet
|
|
847
|
-
* surface includes `getBalance`, `signMessage`, `switchChain` as
|
|
848
|
-
* required methods, and adapter constructors set their flags
|
|
849
|
-
* accordingly).
|
|
567
|
+
* Structural detection: relies on every live adapter advertising at
|
|
568
|
+
* least one capability, since `getBalance`, `signMessage` and
|
|
569
|
+
* `switchChain` are required on all wallet surfaces.
|
|
850
570
|
*/
|
|
851
571
|
declare const isShadowAdapter: (adapter: WalletAdapter) => boolean;
|
|
852
572
|
//#endregion
|
|
@@ -875,22 +595,13 @@ type CookieDriverOptions = {
|
|
|
875
595
|
*/
|
|
876
596
|
domain?: string;
|
|
877
597
|
/**
|
|
878
|
-
*
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
* When provided, `getItem` reads from this snapshot during the
|
|
883
|
-
* server render so the store sees the same persisted state the
|
|
884
|
-
* client will see after hydration. Writes remain no-ops on the
|
|
885
|
-
* server: emitting `Set-Cookie` has to be done by the framework
|
|
886
|
-
* layer that owns the response.
|
|
598
|
+
* Server-side cookie snapshot (Next.js' `cookies()`, a parsed
|
|
599
|
+
* `req.headers.cookie`, …) read only when `document` is unavailable,
|
|
600
|
+
* so the SSR pass sees the state the client will hydrate with.
|
|
887
601
|
*/
|
|
888
602
|
initialCookies?: InitialCookies;
|
|
889
603
|
/**
|
|
890
|
-
* Lifetime in seconds. Defaults to 30 days.
|
|
891
|
-
* (the default) for "until the session ends", but note that
|
|
892
|
-
* butr persists wallet state across sessions, so a finite
|
|
893
|
-
* max-age is usually what consumers want.
|
|
604
|
+
* Lifetime in seconds. Defaults to 30 days.
|
|
894
605
|
*/
|
|
895
606
|
maxAgeSeconds?: number;
|
|
896
607
|
/**
|
|
@@ -910,34 +621,16 @@ type CookieDriverOptions = {
|
|
|
910
621
|
secure?: boolean;
|
|
911
622
|
};
|
|
912
623
|
/**
|
|
913
|
-
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
* **When to use this:** SSR apps that need to know who's connected
|
|
917
|
-
* during the server render (so they can stream the connected-wallet
|
|
918
|
-
* UI without a client-side hydration flicker). Pass `initialCookies`
|
|
919
|
-
* from a server-side cookie source (e.g. `cookies()` in Next.js'
|
|
920
|
-
* `next/headers`, or a parsed `req.headers.cookie`) and the same
|
|
921
|
-
* driver will serve those values during the server render and switch
|
|
922
|
-
* to `document.cookie` once it mounts in the browser.
|
|
923
|
-
*
|
|
924
|
-
* **Trade-offs vs `localStorage`:** cookies travel with every
|
|
925
|
-
* request, so they cost bytes on the wire. Keep the storage key
|
|
926
|
-
* prefix short, and prefer this driver for the `persistent` slot
|
|
927
|
-
* only: the `session` slot can stay in `sessionStorage` (which
|
|
928
|
-
* cookies can't natively model anyway).
|
|
929
|
-
*
|
|
930
|
-
* **Server-side writes are no-ops.** Emitting `Set-Cookie` requires
|
|
931
|
-
* access to the framework's response object, which a storage driver
|
|
932
|
-
* shouldn't reach into. The store doesn't mutate persisted state
|
|
933
|
-
* during the SSR pass anyway; writes only fire after client mount,
|
|
934
|
-
* once `document.cookie` is reachable.
|
|
624
|
+
* Server-side writes are no-ops: `Set-Cookie` needs the framework's
|
|
625
|
+
* response object. Cookies ride every request, so use this driver for
|
|
626
|
+
* the `persistent` slot only and leave `session` on `sessionStorage`.
|
|
935
627
|
*/
|
|
936
628
|
declare const createCookieStorageDriver: (options?: CookieDriverOptions) => StorageDriver;
|
|
937
629
|
//#endregion
|
|
938
630
|
//#region src/storage/wallet-storage.d.ts
|
|
939
631
|
type StorageConfig = {
|
|
940
|
-
|
|
632
|
+
/** Defaults to `"butr"`, matching `readWalletSnapshot`. */
|
|
633
|
+
keyPrefix?: string;
|
|
941
634
|
/** Survives app restart. Defaults to localStorage on web. */
|
|
942
635
|
persistent?: StorageDriver;
|
|
943
636
|
/** Cleared on session end. Defaults to sessionStorage on web. */
|
|
@@ -951,27 +644,22 @@ declare class WalletStorage implements WalletPersistence {
|
|
|
951
644
|
private readonly persistent;
|
|
952
645
|
private readonly session;
|
|
953
646
|
/**
|
|
954
|
-
* Serializes pool-
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
* state, each merge their own entries, and whichever finishes last
|
|
958
|
-
* overwrites the other's additions. Reads (`getPool`) don't enter
|
|
959
|
-
* the queue; they observe whatever's currently in the driver.
|
|
647
|
+
* Serializes pool read-modify-writes; without it concurrent `setPool`
|
|
648
|
+
* calls drop each other's entries. Not reentrant, so a queued mutation
|
|
649
|
+
* must read via `readPool`: `getPool` re-enters and deadlocks it.
|
|
960
650
|
*/
|
|
961
651
|
private poolMutationQueue;
|
|
962
652
|
constructor(config: StorageConfig);
|
|
963
653
|
/** Chain `fn` after the in-flight pool mutation. */
|
|
964
654
|
private serializePoolMutation;
|
|
655
|
+
/** Read and decode the pool without touching the mutation queue. Safe to
|
|
656
|
+
* call from inside a queued mutation, unlike `getPool`. */
|
|
657
|
+
private readPool;
|
|
965
658
|
getPool(): Promise<StoredPoolRecord>;
|
|
966
659
|
/**
|
|
967
|
-
*
|
|
968
|
-
*
|
|
969
|
-
*
|
|
970
|
-
* now", not "the complete list of remembered connections"; a
|
|
971
|
-
* silent reconnect that fails on reload leaves the entry out of
|
|
972
|
-
* the pool but the saved entry stays so the next load can retry.
|
|
973
|
-
* Use `removePoolEntry` for explicit eviction (the user clicked
|
|
974
|
-
* Disconnect) and `clearAll` for a full wipe (reset).
|
|
660
|
+
* Additive on purpose: a failed silent reconnect drops the entry from
|
|
661
|
+
* the live pool, and it must survive to be retried next load.
|
|
662
|
+
* Eviction goes through `removePoolEntry` or `clearAll`.
|
|
975
663
|
*/
|
|
976
664
|
setPool(pool: Map<string, ConnectedWallet>): Promise<void>;
|
|
977
665
|
removePoolEntry(connectorId: string): Promise<void>;
|
|
@@ -982,12 +670,9 @@ declare class WalletStorage implements WalletPersistence {
|
|
|
982
670
|
setActiveConnectorId(connectorId: string | null): Promise<void>;
|
|
983
671
|
clearAll(): Promise<void>;
|
|
984
672
|
/**
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
988
|
-
* but clears when the session ends (unlike the persistent driver).
|
|
989
|
-
* Prevents auto-connect from firing immediately after a manual disconnect,
|
|
990
|
-
* while still allowing auto-connect on fresh sessions.
|
|
673
|
+
* Kept in the session driver so it survives remounts (unlike a ref)
|
|
674
|
+
* yet clears at session end (unlike the persistent driver): a manual
|
|
675
|
+
* disconnect must suppress auto-connect now, not forever.
|
|
991
676
|
*/
|
|
992
677
|
isUserDisconnected(): Promise<boolean>;
|
|
993
678
|
markUserDisconnected(value: boolean): Promise<void>;
|
|
@@ -1019,23 +704,81 @@ type WalletStore = ReturnType<typeof createWalletStore>;
|
|
|
1019
704
|
type WalletStoreState = ExtractState<WalletStore>;
|
|
1020
705
|
declare const createWalletStore: (config: WalletManagerConfig) => import("zustand/vanilla").StoreApi<State & RuntimeMembers>;
|
|
1021
706
|
//#endregion
|
|
707
|
+
//#region src/group-by-platform.d.ts
|
|
708
|
+
/**
|
|
709
|
+
* A multi-chain wallet announces one adapter per platform, so brands
|
|
710
|
+
* repeat in the flat list. Keys follow `CHAIN_PLATFORMS` order and empty
|
|
711
|
+
* platforms are omitted, so `[...groups]` needs no emptiness filter.
|
|
712
|
+
*/
|
|
713
|
+
declare const groupByPlatform: <T>(items: ReadonlyArray<T>, getPlatform: (item: T) => ChainPlatform) => Map<ChainPlatform, Array<T>>;
|
|
714
|
+
//#endregion
|
|
715
|
+
//#region src/sign-in/sign-in-flow.d.ts
|
|
716
|
+
/**
|
|
717
|
+
* Verify against `signedMessage`, not `message`: Solana Wallet Standard
|
|
718
|
+
* wallets may prefix or re-encode what they sign. Base64 mirrors exist
|
|
719
|
+
* because `Uint8Array` does not survive `JSON.stringify`.
|
|
720
|
+
*/
|
|
721
|
+
type SignInResult = {
|
|
722
|
+
account: Account;
|
|
723
|
+
/** The message handed to the wallet. Absent on the SIWS path, where the
|
|
724
|
+
* wallet composes the message itself. */
|
|
725
|
+
message?: string;
|
|
726
|
+
nonce: string;
|
|
727
|
+
signature: Uint8Array;
|
|
728
|
+
/** Base64 of `signature`. */
|
|
729
|
+
signatureBase64: string;
|
|
730
|
+
signedMessage: Uint8Array;
|
|
731
|
+
/** Base64 of `signedMessage`; verify against this. */
|
|
732
|
+
signedMessageBase64: string;
|
|
733
|
+
wallet: ConnectedWallet;
|
|
734
|
+
};
|
|
735
|
+
type SignInMessageContext = {
|
|
736
|
+
account: Account;
|
|
737
|
+
nonce: string;
|
|
738
|
+
wallet: ConnectedWallet;
|
|
739
|
+
};
|
|
740
|
+
type SignInFlowOptions = {
|
|
741
|
+
/**
|
|
742
|
+
* butr ships no wire format: a server-parsed message is an auth spec,
|
|
743
|
+
* and SIWE / SIWS already fill that role. Pass what your backend
|
|
744
|
+
* expects. Unused on the SIWS path, where the wallet composes it.
|
|
745
|
+
*/
|
|
746
|
+
buildMessage?: (ctx: SignInMessageContext) => string;
|
|
747
|
+
/** Fetch a single-use nonce from your backend. */
|
|
748
|
+
getNonce: (ctx: {
|
|
749
|
+
account: Account;
|
|
750
|
+
wallet: ConnectedWallet;
|
|
751
|
+
}) => Promise<string>;
|
|
752
|
+
/**
|
|
753
|
+
* Skip the Sign In With Solana path even on wallets that advertise it,
|
|
754
|
+
* forcing every platform down the same `signMessage` route. Useful when
|
|
755
|
+
* one backend verifier has to handle every chain identically.
|
|
756
|
+
*/
|
|
757
|
+
preferSignMessage?: boolean;
|
|
758
|
+
/** Hand the signed result to your backend. Throw to fail the flow. */
|
|
759
|
+
verify: (result: SignInResult) => Promise<void>;
|
|
760
|
+
};
|
|
761
|
+
/** Thrown before any wallet interaction when the wallet can't sign at
|
|
762
|
+
* all. Distinct from a rejection: nothing was asked of the user, so UI
|
|
763
|
+
* should say "this wallet can't sign in" rather than "you declined". */
|
|
764
|
+
declare class SignInUnsupportedError extends Error {
|
|
765
|
+
readonly connectorId: string;
|
|
766
|
+
constructor(connectorId: string);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Solana wallets advertising `solana:signIn` take the SIWS path, so
|
|
770
|
+
* `result.signedMessage` holds the wallet-composed statement and
|
|
771
|
+
* `result.message` is absent. `preferSignMessage` opts out.
|
|
772
|
+
*/
|
|
773
|
+
declare const createSignInFlow: (options: SignInFlowOptions) => {
|
|
774
|
+
signIn: (wallet: ConnectedWallet, account?: Account) => Promise<SignInResult>;
|
|
775
|
+
};
|
|
776
|
+
//#endregion
|
|
1022
777
|
//#region src/wallet-equal.d.ts
|
|
1023
778
|
/**
|
|
1024
|
-
*
|
|
1025
|
-
*
|
|
1026
|
-
*
|
|
1027
|
-
* (active-wallet, selected-wallet, useWalletEntry) to suppress spurious
|
|
1028
|
-
* re-renders when the underlying Map churns but the resolved entry
|
|
1029
|
-
* hasn't changed.
|
|
1030
|
-
*
|
|
1031
|
-
* The adapter is compared by reference, not by `connector.id`: hydration
|
|
1032
|
-
* replaces a shadow adapter with the live one under an unchanged id and
|
|
1033
|
-
* address, so an id comparison reports "equal" and leaves consumers
|
|
1034
|
-
* holding a placeholder whose every method throws ShadowConnectorError.
|
|
1035
|
-
*
|
|
1036
|
-
* Hoisted to its own module so the equivalence rule lives in one place;
|
|
1037
|
-
* if we ever extend the snapshot (e.g. to consider `accounts.length`),
|
|
1038
|
-
* every selector hook picks up the new rule for free.
|
|
779
|
+
* The adapter is compared by reference, not `connector.id`: hydration
|
|
780
|
+
* swaps a shadow adapter for the live one under an unchanged id, and an
|
|
781
|
+
* id check would strand consumers on the throwing placeholder.
|
|
1039
782
|
*/
|
|
1040
783
|
declare const walletEqual: (a: ConnectedWallet | undefined, b: ConnectedWallet | undefined) => boolean;
|
|
1041
784
|
//#endregion
|
|
@@ -1045,38 +788,17 @@ declare const logError: (...args: ReadonlyArray<unknown>) => void;
|
|
|
1045
788
|
//#endregion
|
|
1046
789
|
//#region src/sanitize-icon.d.ts
|
|
1047
790
|
/**
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
* `providerInfo.icon`, Wallet Standard `wallet.icon`. That value is
|
|
1052
|
-
* not under butr's control, and some wallets ship data-URI icons with
|
|
1053
|
-
* surrounding whitespace (a newline left over from a pretty-printed
|
|
1054
|
-
* manifest). Strict consumers reject it: Next.js's `<Image>` throws
|
|
1055
|
-
* because `src` must not start with a control character.
|
|
1056
|
-
*
|
|
1057
|
-
* Trims surrounding whitespace and treats an all-whitespace (or empty)
|
|
1058
|
-
* icon as absent, so consumers get either a usable string or
|
|
1059
|
-
* `undefined`: never a blank or malformed one. `undefined` passes
|
|
1060
|
-
* through untouched.
|
|
791
|
+
* Some wallets announce data-URI icons wrapped in whitespace, which
|
|
792
|
+
* makes Next.js `<Image>` throw on a leading control character.
|
|
793
|
+
* Discovery already applies this; only hand-built metadata needs it.
|
|
1061
794
|
*/
|
|
1062
795
|
declare const sanitizeIcon: (icon: string | undefined) => string | undefined;
|
|
1063
796
|
//#endregion
|
|
1064
797
|
//#region src/encoding/bytes.d.ts
|
|
1065
798
|
/**
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
* chain. They used to be hand-reimplemented in ~10 connector files; a
|
|
1070
|
-
* single tested module removes the drift surface (a `padStart` omission
|
|
1071
|
-
* or base64 variant mismatch corrupts signatures/addresses for one chain
|
|
1072
|
-
* only, and the divergence is invisible because the copies look "the
|
|
1073
|
-
* same").
|
|
1074
|
-
*
|
|
1075
|
-
* Hex prefixing diverges across chains, so the module exposes **explicit
|
|
1076
|
-
* variants** rather than one function:
|
|
1077
|
-
* - {@link bytesToHex} returns bare hex (Bitcoin, Ledger).
|
|
1078
|
-
* - {@link bytesToHexPrefixed} returns `0x`-prefixed hex (EVM, Polkadot).
|
|
1079
|
-
* - {@link hexToBytes} tolerantly strips an optional `0x` (all callers).
|
|
799
|
+
* Hex prefixing diverges by chain (bare for Bitcoin and Ledger, `0x` for
|
|
800
|
+
* EVM and Polkadot), so the variants stay explicit: these sit on the
|
|
801
|
+
* signing path, where a silent mismatch corrupts one chain's signatures.
|
|
1080
802
|
*/
|
|
1081
803
|
/** Bare lowercase hex, no `0x` prefix (Bitcoin, Ledger). */
|
|
1082
804
|
declare const bytesToHex: (bytes: Uint8Array) => string;
|
|
@@ -1105,5 +827,5 @@ declare const bytesToBase58: (bytes: Uint8Array) => string;
|
|
|
1105
827
|
* rather than silently dropping them. */
|
|
1106
828
|
declare const base58ToBytes: (input: string) => Uint8Array;
|
|
1107
829
|
//#endregion
|
|
1108
|
-
export { type Account, type Balance, type BitcoinAdapter, type BitcoinWallet, type BrowserStorageDrivers, CHAIN_PLATFORMS, type ChainBase, type ChainPlatform, type ChainsByPlatform, type ConnectedWallet, type ConnectionError, type ConnectionErrorKind, type ConnectionStatus, type Connector, type ConnectorEvent, type ConnectorMeta, type CookieDriverOptions, type CookieSource, EMPTY_SNAPSHOT, type EvmAdapter, type EvmWallet, type HydrationOutcome, type InitialCookies, type MaybePromise, type PlatformDiscoverer, type PolkadotAdapter, type PolkadotWallet, ShadowConnectorError, type SignerForPlatform, type SignerOf, type SnapshotOptions, type StorageDriver, type StoredPoolEntry, type StoredPoolRecord, type StoredSelectionRecord, type SuiAdapter, type SuiWallet, type SvmAdapter, type SvmWallet, type WalletAdapter, type WalletAvailability, type WalletBase, type WalletCapabilities, type WalletManagerConfig, type WalletPersistence, type WalletSnapshot, type WalletSource, WalletStorage, type WalletStore, type WalletStoreState, base58ToBytes, base64ToBytes, buildAccount, buildChainsByPlatform, bytesToBase58, bytesToBase64, bytesToHex, bytesToHexPrefixed, createBrowserStorageDriver, createCookieStorageDriver, createMemoryStorageDriver, createWalletSource, createWalletStore, hexToBytes, isShadowAdapter, logError, logWarn, mapConnectionError, readWalletSnapshot, sanitizeIcon, walletEqual };
|
|
830
|
+
export { type Account, type Balance, type BitcoinAdapter, type BitcoinWallet, type BrowserStorageDrivers, CHAIN_PLATFORMS, type ChainBase, type ChainPlatform, type ChainsByPlatform, type ConnectedWallet, type ConnectionError, type ConnectionErrorKind, type ConnectionStatus, type Connector, type ConnectorEvent, type ConnectorMeta, type CookieDriverOptions, type CookieSource, EMPTY_SNAPSHOT, type EvmAdapter, type EvmWallet, type HydrationOutcome, type InitialCookies, type MaybePromise, type PlatformDiscoverer, type PolkadotAdapter, type PolkadotWallet, ShadowConnectorError, type SignInFlowOptions, type SignInMessageContext, type SignInResult, SignInUnsupportedError, type SignerForPlatform, type SignerOf, type SnapshotOptions, type StorageDriver, type StoredPoolEntry, type StoredPoolRecord, type StoredSelectionRecord, type SuiAdapter, type SuiWallet, type SvmAdapter, type SvmWallet, type WalletAdapter, type WalletAvailability, type WalletBase, type WalletCapabilities, type WalletManagerConfig, type WalletPersistence, type WalletSnapshot, type WalletSource, WalletStorage, type WalletStore, type WalletStoreState, base58ToBytes, base64ToBytes, buildAccount, buildChainsByPlatform, bytesToBase58, bytesToBase64, bytesToHex, bytesToHexPrefixed, createBrowserStorageDriver, createCookieStorageDriver, createMemoryStorageDriver, createSignInFlow, createWalletSource, createWalletStore, groupByPlatform, hexToBytes, isShadowAdapter, logError, logWarn, mapConnectionError, readWalletSnapshot, sanitizeIcon, walletEqual };
|
|
1109
831
|
//# sourceMappingURL=index.d.ts.map
|