@usebutr/core 2.0.0 → 3.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 CHANGED
@@ -1,3 +1,4 @@
1
+ import { StoreApi } from "zustand/vanilla";
1
2
  //#region src/types/chain.d.ts
2
3
  /**
3
4
  * CAIP-2 shaped. butr never reads past these four fields, so consumers
@@ -6,13 +7,19 @@
6
7
  type ChainBase = {
7
8
  /** CAIP-2 identifier, e.g. "eip155:1", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" */
8
9
  id: string;
9
- /** Human-readable name, e.g. "Ethereum", "Solana" */
10
+ /** Human-readable chain name, e.g. "Ethereum". Never the wallet's name. */
10
11
  name: string;
11
12
  /** CAIP-2 namespace, e.g. "eip155", "solana" */
12
13
  namespace: string;
13
14
  /** CAIP-2 reference, e.g. "1", "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" */
14
15
  reference: string;
15
16
  };
17
+ /**
18
+ * Wallets report bare CAIP-2 ids, so the display name comes from a chain
19
+ * registry. A chain outside `known` keeps its id as its name rather than
20
+ * borrowing the wallet's.
21
+ */
22
+ declare const resolveChain: (id: string, known?: ReadonlyArray<ChainBase>) => ChainBase;
16
23
  //#endregion
17
24
  //#region src/types/account.d.ts
18
25
  type Account = {
@@ -37,47 +44,6 @@ type Balance = {
37
44
  value: bigint;
38
45
  };
39
46
  //#endregion
40
- //#region src/types/capabilities.d.ts
41
- /**
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.
45
- */
46
- type WalletCapabilities = {
47
- /** `getBalance` returns a real on-chain value (vs. a 0n placeholder). */
48
- getBalance: boolean;
49
- /** `getTransactionReceipt` returns a real RPC response. */
50
- getTransactionReceipt: boolean;
51
- /** Calling `requestAccounts` will actually do something: either
52
- * prompt the user (EIP-2255) or refresh the exposed list. */
53
- requestAccounts: boolean;
54
- /** `sendTx` / `sendTxToChain` will work. False for SVM wallets that
55
- * don't advertise `solana:signAndSendTransaction`. */
56
- sendTransaction: boolean;
57
- /** `signIn` works: Sign In With Solana (`solana:signIn`). True only
58
- * for SVM wallets advertising the feature. */
59
- signIn: boolean;
60
- /** `signMessage` will work. False for SVM wallets that don't
61
- * advertise `solana:signMessage`. */
62
- signMessage: boolean;
63
- /** `signTransaction` returns a signed-but-unbroadcast transaction the
64
- * consumer submits with their own RPC client. True only for SVM
65
- * wallets advertising `solana:signTransaction`; butr ships no RPC so
66
- * it can't broadcast the result itself. EVM/hardware adapters leave
67
- * this `false` (no `signTransaction` method). */
68
- signTransaction: boolean;
69
- /** Wallet emits account/chain change events that butr can bridge. */
70
- subscribe: boolean;
71
- /** `switchAccount` is real. Almost always `false` for auto adapters;
72
- * neither protocol exposes silent account switch. Hand-rolled
73
- * adapters with custom transports may set it `true`. */
74
- switchAccount: boolean;
75
- /** `switchChain` routes subsequent calls through the new chain. EVM:
76
- * true via `wallet_switchEthereumChain`. SVM: true (local state +
77
- * per-call `chain` input) when more than one chain is advertised. */
78
- switchChain: boolean;
79
- };
80
- //#endregion
81
47
  //#region src/types/platform.d.ts
82
48
  /**
83
49
  * Single runtime source of truth: `ChainPlatform` and the storage
@@ -86,229 +52,267 @@ type WalletCapabilities = {
86
52
  */
87
53
  declare const CHAIN_PLATFORMS: readonly ["evm", "svm", "sui", "bitcoin", "polkadot"];
88
54
  type ChainPlatform = (typeof CHAIN_PLATFORMS)[number];
55
+ declare const isChainPlatform: (value: string) => value is ChainPlatform;
89
56
  //#endregion
90
57
  //#region src/types/chains-by-platform.d.ts
91
- /**
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.
95
- */
58
+ /** An empty list opts that platform out of the consumer's chain UI. */
96
59
  type ChainsByPlatform = Readonly<Record<ChainPlatform, ReadonlyArray<ChainBase>>>;
97
- /**
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`.
101
- */
102
- declare const buildChainsByPlatform: (partial: Partial<ChainsByPlatform>) => ChainsByPlatform;
103
60
  //#endregion
104
61
  //#region src/types/connector.d.ts
105
62
  /**
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`.
109
- */
110
- type WalletAvailability = "installed" | "loadable" | "not-installed";
111
- /**
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`.
63
+ * `accounts` carries every account the wallet still exposes, active first;
64
+ * the runtime mirrors it verbatim into the pool entry. Chain switches
65
+ * arrive as a changed `account.chain`. An empty list is `disconnected`.
115
66
  */
116
67
  type ConnectorEvent = {
117
- account: Account;
118
- accounts: Array<Account>;
119
- type: "accountChanged";
68
+ accounts: ReadonlyArray<Account>;
69
+ type: "accountsChanged";
120
70
  } | {
121
71
  type: "disconnected";
122
72
  };
123
73
  /**
124
- * Orchestration interface: what `butr` actually calls during the
125
- * connect / disconnect / hydrate flow. This is the contract `butr`
126
- * cares about; everything else on `WalletAdapter` is consumer-facing.
74
+ * What butr itself calls during connect, disconnect and hydration. Optional
75
+ * members are defined only when they work for this wallet, so presence is
76
+ * the capability check.
127
77
  */
128
78
  type Connector<P extends ChainPlatform = ChainPlatform> = {
129
- /** Runtime capability flags; see `WalletCapabilities`. Read these
130
- * to gate UI affordances rather than probing for method existence. */
131
- capabilities: WalletCapabilities;
132
- /** Discriminant: which chain platform this adapter speaks. Generic
133
- * parameter `P` narrows this to a specific platform when consumers
134
- * use one of the per-platform adapter types (`EvmAdapter`,
135
- * `SvmAdapter`, etc). */
79
+ /** Discriminant: which chain platform this adapter speaks. */
136
80
  chainPlatform: P;
137
- /** `opts.silent` is hydration's non-interactive reconnect (Wallet
81
+ /** `options.silent` is hydration's non-interactive reconnect (Wallet
138
82
  * Standard `standard:connect` silent input, `eth_accounts` on
139
83
  * EIP-1193). An adapter that cannot honour it must reject instead of
140
84
  * prompting; hydration reads that as a clean restore failure. */
141
- connect: (opts?: {
85
+ connect: (options?: {
142
86
  silent?: boolean;
143
87
  }) => Promise<void>;
144
- /** Optional teardown. butr calls this on disconnect, error recovery, and reset. */
88
+ /** Teardown. butr calls it on disconnect, failed connects and reset. */
145
89
  disconnect?: () => Promise<void>;
146
- /** Read the currently-active account. butr uses this to populate the pool
147
- * after a successful `connect()` and during hydration. */
148
- getAccount: () => Promise<Account | null>;
149
- /** Optional. List every account the wallet exposes. Some browser wallets
150
- * show many accounts at once (MetaMask with multiple imports). If
151
- * omitted, butr defaults to `[await getAccount()]`. */
152
- getAccounts?: () => Promise<Array<Account>>;
90
+ /** Every account the wallet exposes, active account first. Empty when
91
+ * the wallet is not connected. */
92
+ getAccounts: () => Promise<ReadonlyArray<Account>>;
153
93
  /** 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. */
94
+ * adapters this is a trimmed non-empty string or `undefined`. Hand-rolled
95
+ * adapters own that guarantee. */
157
96
  icon?: string;
158
- /** Stable key: "metamask", "phantom", etc. Pool entries are keyed by this. */
97
+ /** Stable key: "io.metamask", "wallet-standard:svm-phantom", etc. Pool
98
+ * entries are keyed by this. */
159
99
  id: string;
160
100
  /** Human name: "MetaMask", "Phantom", etc. UI-facing only. */
161
101
  name: string;
162
102
  /** 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. */
103
+ * on EIP-1193). Resolution does not carry the new accounts; the
104
+ * manager's `requestAccounts` action refreshes the pool entry. */
166
105
  requestAccounts?: () => Promise<void>;
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. */
106
+ /** Bridges native wallet events into the reducer. Only the manager's
107
+ * connector lifecycle calls this, at most once per live connector. */
170
108
  subscribe?: (listener: (event: ConnectorEvent) => void) => () => void;
171
109
  };
172
- type ConnectorMeta = {
173
- /** Optional. Sync probe that reports whether the wallet is currently
174
- * available. Defaults to `"installed"` when omitted. Consumers call
175
- * this at render time to gate the "Connect" button. */
176
- availability?: () => WalletAvailability;
110
+ //#endregion
111
+ //#region src/storage/persistence.d.ts
112
+ type MaybePromise<T> = T | Promise<T>;
113
+ /** Low-level key/value driver. Sync on web (localStorage, MMKV),
114
+ * async on React Native (AsyncStorage). */
115
+ type StorageDriver = {
116
+ getItem: (key: string) => MaybePromise<string | null>;
117
+ removeItem: (key: string) => MaybePromise<void>;
118
+ setItem: (key: string, value: string) => MaybePromise<void>;
119
+ };
120
+ /** Everything needed to render a connection before its adapter exists:
121
+ * the shadow adapter shows this identity until silent reconnect lands. */
122
+ type StoredPoolEntry = {
123
+ account: Account;
124
+ accounts: ReadonlyArray<Account>;
177
125
  chainPlatform: ChainPlatform;
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. */
126
+ connectorId: string;
182
127
  icon?: string;
183
- id: string;
184
128
  name: string;
185
- /** Optional. Where to send users who don't have this wallet (download
186
- * page, app store link, etc.). */
187
- url?: string;
129
+ };
130
+ type StoredPoolRecord = Partial<Record<string, StoredPoolEntry>>;
131
+ type StoredSelectionRecord = Partial<Record<ChainPlatform, string>>;
132
+ /**
133
+ * Carries no `Connector` by design: a wallet extension exists only in the
134
+ * browser, so a server render can name the wallet and its address but can
135
+ * never dispatch on it.
136
+ */
137
+ type WalletSnapshot = {
138
+ activeConnectorId: string | null;
139
+ pool: StoredPoolRecord;
140
+ selection: StoredSelectionRecord;
141
+ };
142
+ type PersistedWalletState = WalletSnapshot & {
143
+ /** Session-scoped: a manual disconnect suppresses auto-connect for this
144
+ * session, not forever. */
145
+ isUserDisconnected: boolean;
146
+ };
147
+ /**
148
+ * `save` receives the whole derived state after every change, so an
149
+ * implementation never merges or diffs. A `load` rejection is reported
150
+ * through `onStorageError` and treated as empty storage.
151
+ */
152
+ type WalletPersistence = {
153
+ load: () => Promise<PersistedWalletState>;
154
+ save: (state: PersistedWalletState) => Promise<void>;
188
155
  };
189
156
  //#endregion
157
+ //#region src/types/signer.d.ts
158
+ /**
159
+ * Filled by each transport package through module augmentation, e.g.
160
+ * `interface WalletSignerRegistry { eip1193: { provider: Eip1193Provider } }`.
161
+ * Keyed by transport, not platform: EVM through a Ledger is not EIP-1193.
162
+ */
163
+ interface WalletSignerRegistry {}
164
+ /** Narrow with `switch (signer.kind)`; each branch is fully typed. */
165
+ type WalletSigner = { [K in keyof WalletSignerRegistry]: {
166
+ kind: K;
167
+ } & WalletSignerRegistry[K]; }[keyof WalletSignerRegistry];
168
+ type WalletSignerKind = WalletSigner["kind"];
169
+ type WalletSignerOf<K extends WalletSignerKind> = Extract<WalletSigner, {
170
+ kind: K;
171
+ }>;
172
+ //#endregion
190
173
  //#region src/types/wallet.d.ts
174
+ type SignInInput = {
175
+ readonly [key: string]: SignInValue;
176
+ };
177
+ type SignInValue = boolean | number | string | null | ReadonlyArray<SignInValue> | SignInInput;
178
+ type SignedMessage = {
179
+ signature: Uint8Array;
180
+ signedMessage: Uint8Array;
181
+ };
182
+ type TransactionReceipt = {
183
+ status: "Error" | "Pending" | "Success";
184
+ };
185
+ /** Acts as `account`, which must be one the wallet exposes; an unknown one
186
+ * rejects rather than signing with another. Omitted, the wallet uses its own
187
+ * active account, so pass `wallet.account` to honour `setAccount`. */
188
+ type AccountOptions = {
189
+ account?: Account;
190
+ };
191
191
  /**
192
- * Methods every connected wallet supports regardless of chain. The
193
- * per-platform `Wallet` types extend this with their platform-specific
194
- * methods (`signIn` for SVM, `signTransaction` for sign-only paths).
195
- */
196
- type WalletBase = {
197
- /** Read a token balance. `mint` is optional; the connector decides
198
- * what "no mint" means for its chain (native ETH on EVM, native SOL
199
- * on Solana, etc.). */
200
- getBalance: (mint?: string) => Promise<Balance>;
201
- /** Returns a chain-specific signer. Consumers cast to the concrete
202
- * type via the `SignerForPlatform` registry (or directly to the
203
- * library shape they wrap: `WalletClient` on viem, etc.). */
204
- getSigner: () => Promise<unknown>;
205
- /** Look up the status of a previously-submitted transaction. */
206
- getTransactionReceipt: (tx: string) => Promise<{
207
- status: "Success" | "Error" | "Pending";
208
- }>;
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. */
212
- sendTx: (tx: unknown, account?: Account) => Promise<string>;
213
- /** Submit a transaction targeting a specific chain. The optional
214
- * callback fires after the connector has switched chain (consumers
215
- * use this to re-enable UI). Pass an `account` to route through a
216
- * specific exposed address (see `sendTx`). */
217
- sendTxToChain: (tx: unknown, targetChainId: string, account?: Account, cb?: () => void) => Promise<string>;
192
+ * `chain` targets this one transaction. Wallet Standard routes it per call;
193
+ * an EVM wallet has one global network, so it switches first; a transport
194
+ * that can do neither rejects when `chain` is not its current chain.
195
+ */
196
+ type TransactionOptions = AccountOptions & {
197
+ chain?: ChainBase;
198
+ };
199
+ /** `token` is chain-specific: an ERC-20 contract address on EVM. Omit it for
200
+ * the native asset. */
201
+ type BalanceOptions = AccountOptions & {
202
+ token?: string;
203
+ };
204
+ /**
205
+ * Presence is the capability check: an adapter defines an optional method
206
+ * only when calling it can succeed for this wallet, and never ships a
207
+ * placeholder (a zero balance, a receipt that stays pending).
208
+ */
209
+ type WalletBase<Tx> = {
210
+ getBalance?: (options?: BalanceOptions) => Promise<Balance>;
211
+ /** Narrow with `switch (signer.kind)`; see `WalletSignerRegistry`. */
212
+ getSigner: () => Promise<WalletSigner>;
213
+ getTransactionReceipt?: (hash: string) => Promise<TransactionReceipt>;
214
+ /** Signs and broadcasts; resolves the transaction hash or signature. */
215
+ sendTx?: (tx: Tx, options?: TransactionOptions) => Promise<string>;
218
216
  /**
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.
217
+ * Verify against `signedMessage`, not the input: Solana Wallet Standard
218
+ * wallets may prefix or re-encode it.
222
219
  */
223
- signMessage: (msg: Uint8Array, account?: Account) => Promise<{
224
- signature: Uint8Array;
225
- signedMessage: Uint8Array;
226
- }>;
227
- /** Switch to a different account on the same wallet (some wallets
228
- * expose multiple accounts simultaneously). */
229
- switchAccount?: (address: string) => Promise<void>;
230
- /** Switch the wallet's active chain. */
231
- switchChain: (chain: ChainBase) => Promise<void>;
232
- };
233
- /**
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`.
237
- */
238
- type EvmWallet = WalletBase;
239
- /**
240
- * Both additions are optional at runtime; gate them on
241
- * `capabilities.signIn` / `capabilities.signTransaction`, which mirror
242
- * what the wallet actually advertises.
243
- */
244
- type SvmWallet = WalletBase & {
245
- /** Sign In With Solana (SIWS, `solana:signIn`). Authenticates the user
246
- * and returns the connected account plus the signed statement so the
247
- * consumer can verify server-side. `input` is the SIWS message fields
248
- * (domain, statement, nonce, …); pass `{}` or omit for wallet
249
- * defaults. */
250
- signIn?: (input?: Record<string, unknown>) => Promise<{
251
- account: Account;
252
- signature: Uint8Array;
253
- signedMessage: Uint8Array;
254
- }>;
255
- /** Sign a Solana transaction WITHOUT broadcasting it. butr ships no
256
- * RPC, so the consumer broadcasts the returned bytes via
257
- * `@solana/kit` / `@solana/web3.js` / etc. */
258
- signTransaction?: (tx: unknown, account?: Account) => Promise<Uint8Array>;
259
- };
260
- /**
261
- * Sui wallet surface. Adds optional `signTransaction` for the
262
- * `sui:signTransaction` (sign-only) feature; broadcast is on the
263
- * consumer via `@mysten/sui`'s SuiClient.
264
- */
265
- type SuiWallet = WalletBase & {
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<{
220
+ signMessage?: (message: Uint8Array, options?: AccountOptions) => Promise<SignedMessage>;
221
+ /** Moves the wallet (or butr's view of it) to `chain`. Rejects for a
222
+ * chain outside this adapter's namespace or not advertised by the wallet. */
223
+ switchChain?: (chain: ChainBase) => Promise<void>;
224
+ };
225
+ type EvmTransactionValue = bigint | boolean | number | string | null | ReadonlyArray<EvmTransactionValue> | {
226
+ readonly [key: string]: EvmTransactionValue | undefined;
227
+ };
228
+ /** An `eth_sendTransaction` request. `bigint` quantities are encoded as hex. */
229
+ type EvmTransactionRequest = Readonly<Record<string, EvmTransactionValue | undefined>>;
230
+ /** A `@mysten/sui` `Transaction` (anything with `toJSON()`), its JSON string,
231
+ * or BCS bytes. */
232
+ type SuiTransactionInput = string | Uint8Array | {
233
+ toJSON: () => Promise<string>;
234
+ };
235
+ /** `amount` in satoshis. */
236
+ type BitcoinTransfer = {
237
+ amount: bigint;
238
+ recipient: string;
239
+ };
240
+ type SignInOutput = {
241
+ account: Account;
242
+ signature: Uint8Array;
243
+ signedMessage: Uint8Array;
244
+ };
245
+ /** No `signIn` (SIWE is app-level) and no `signTransaction`: EVM wallets sign
246
+ * and send in one step through `eth_sendTransaction`. */
247
+ type EvmWallet = WalletBase<EvmTransactionRequest>;
248
+ type SvmWallet = WalletBase<Uint8Array> & {
249
+ /** Sign In With Solana (`solana:signIn`). `input` holds the SIWS message
250
+ * fields (domain, statement, nonce, …). */
251
+ signIn?: (input?: SignInInput) => Promise<SignInOutput>;
252
+ /** Signs a serialized transaction without broadcasting it and resolves the
253
+ * full signed transaction, ready for your own RPC client. */
254
+ signTransaction?: (tx: Uint8Array, options?: TransactionOptions) => Promise<Uint8Array>;
255
+ };
256
+ type SuiWallet = WalletBase<SuiTransactionInput> & {
257
+ /** Signs without executing. `SuiClient.executeTransactionBlock` needs both
258
+ * halves, so the result carries the transaction bytes and the signature. */
259
+ signTransaction?: (tx: SuiTransactionInput, options?: TransactionOptions) => Promise<{
271
260
  bytes: Uint8Array;
272
261
  signature: Uint8Array;
273
262
  }>;
274
263
  };
275
- /**
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.
279
- */
280
- type BitcoinWallet = WalletBase & {
281
- signTransaction?: (tx: unknown, account?: Account) => Promise<Uint8Array>;
264
+ type BitcoinWallet = WalletBase<BitcoinTransfer> & {
265
+ /** `bitcoin:signPsbt`: PSBT bytes in (`psbt.toBuffer()`), signed PSBT bytes
266
+ * out, to finalise and broadcast through your own Esplora or Electrum
267
+ * client. */
268
+ signTransaction?: (psbt: Uint8Array, options?: TransactionOptions) => Promise<Uint8Array>;
282
269
  };
283
- /**
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.
287
- */
288
- type PolkadotWallet = WalletBase;
289
- /** Per-platform full adapter shapes: `Connector` + the platform's
290
- * `Wallet` surface. These are the discriminated-union variants of
291
- * `WalletAdapter`. */
270
+ /** Building an extrinsic needs chain metadata over RPC, which butr does not
271
+ * ship, so transactions go through the `getSigner()` handoff. */
272
+ type PolkadotWallet = Omit<WalletBase<never>, "sendTx">;
292
273
  type EvmAdapter = Connector<"evm"> & EvmWallet;
293
274
  type SvmAdapter = Connector<"svm"> & SvmWallet;
294
275
  type SuiAdapter = Connector<"sui"> & SuiWallet;
295
276
  type BitcoinAdapter = Connector<"bitcoin"> & BitcoinWallet;
296
277
  type PolkadotAdapter = Connector<"polkadot"> & PolkadotWallet;
297
- /**
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.
301
- */
278
+ /** Narrow on `chainPlatform` to reach a platform's own methods and
279
+ * transaction type. */
302
280
  type WalletAdapter = EvmAdapter | SvmAdapter | SuiAdapter | BitcoinAdapter | PolkadotAdapter;
303
- type ConnectedWallet = {
304
- /** Currently-active account on this wallet. */
281
+ type AdapterByPlatform = {
282
+ bitcoin: BitcoinAdapter;
283
+ evm: EvmAdapter;
284
+ polkadot: PolkadotAdapter;
285
+ sui: SuiAdapter;
286
+ svm: SvmAdapter;
287
+ };
288
+ type WalletAdapterFor<P extends ChainPlatform> = Extract<WalletAdapter, {
289
+ chainPlatform: P;
290
+ }>;
291
+ type ConnectedWallet<P extends ChainPlatform = ChainPlatform> = {
292
+ /** The active account; always one of `accounts`. */
305
293
  account: Account;
306
- /** All accounts known on this wallet at the time of connect/refresh.
307
- * Always contains at least `account`. Populated from `getAccounts()`
308
- * if the connector implements it; otherwise `[account]`. */
309
- accounts: Array<Account>;
310
- connector: WalletAdapter;
294
+ /** Every account the wallet exposed at the last connect or refresh. */
295
+ accounts: ReadonlyArray<Account>;
296
+ /** An indexed access rather than `WalletAdapterFor`, so TypeScript measures
297
+ * `P` covariant and `ConnectedWallet<"evm">` stays a `ConnectedWallet`. */
298
+ connector: AdapterByPlatform[P];
311
299
  };
300
+ /** Narrows a pool entry to one platform, e.g. before calling its `sendTx`. */
301
+ declare const isPlatformWallet: <P extends ChainPlatform>(wallet: ConnectedWallet, platform: P) => wallet is ConnectedWallet<P>;
302
+ //#endregion
303
+ //#region src/wallet-source.d.ts
304
+ /**
305
+ * The discovery seam: a function that announces adapters and returns its
306
+ * unsubscribe. Every `discover*Adapters` export already has this shape, so
307
+ * it goes into `sources` as-is.
308
+ */
309
+ type WalletSource = (onAdapter: (adapter: WalletAdapter) => void) => () => void;
310
+ /**
311
+ * For adapters that are built rather than discovered: WalletConnect, Ledger,
312
+ * hand-rolled ones. Takes one adapter or several, or a promise of either; a
313
+ * rejected promise is logged and contributes nothing.
314
+ */
315
+ declare const fromAdapters: (adapters: MaybePromise<WalletAdapter | Iterable<WalletAdapter>>) => WalletSource;
312
316
  //#endregion
313
317
  //#region src/types/discoverer.d.ts
314
318
  /**
@@ -323,252 +327,306 @@ type PlatformDiscoverer = {
323
327
  * discovery already announced the same wallet, or it double-lists.
324
328
  */
325
329
  fallback?: {
326
- subscribe: (onAdapter: (adapter: WalletAdapter) => void, opts: {
330
+ subscribe: (onAdapter: (adapter: WalletAdapter) => void, options: {
327
331
  hasAnyPrimaryAdapter: () => boolean;
328
332
  }) => () => void;
329
333
  };
330
334
  /** Primary discovery subscription. */
331
- subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void;
335
+ subscribe: WalletSource;
332
336
  };
333
337
  //#endregion
334
338
  //#region src/types/errors.d.ts
335
339
  /**
336
340
  * Wallet SDKs disagree on error shape (EIP-1193 codes, bare strings,
337
341
  * bespoke classes), so consumers branch on `kind` rather than regexing
338
- * messages. `cause` keeps the original value for the `Unknown` case.
339
- */
340
- type ConnectionError = {
341
- kind: "UserRejected";
342
- message: string;
343
- } | {
344
- kind: "RequestPending";
345
- message: string;
346
- } | {
347
- kind: "WalletLocked";
348
- message: string;
349
- } | {
350
- actualChain?: string;
351
- expectedChain?: string;
352
- kind: "ChainMismatch";
353
- message: string;
354
- } | {
355
- kind: "NotConnected";
356
- message: string;
357
- } | {
358
- kind: "Timeout";
359
- message: string;
360
- } | {
361
- cause?: unknown;
362
- kind: "Unknown";
363
- message: string;
364
- };
365
- type ConnectionErrorKind = ConnectionError["kind"];
366
- /**
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.
370
- */
371
- declare const mapConnectionError: (raw: unknown) => ConnectionError;
372
- //#endregion
373
- //#region src/storage/persistence.d.ts
374
- type MaybePromise<T> = T | Promise<T>;
375
- /** Low-level key/value driver. Sync on web (localStorage, MMKV),
376
- * async on React Native (AsyncStorage). */
377
- type StorageDriver = {
378
- getItem: (key: string) => MaybePromise<string | null>;
379
- removeItem: (key: string) => MaybePromise<void>;
380
- setItem: (key: string, value: string) => MaybePromise<void>;
381
- };
382
- type StoredPoolEntry = {
383
- account: Account;
384
- /** All known accounts on the wallet at last persist. Always contains
385
- * `account`. */
386
- accounts: Array<Account>;
387
- chainPlatform: ChainPlatform;
388
- connectorId: string;
389
- /** Wallet icon URL or data-URI captured at persist time. Lets the
390
- * shadow adapter render the same icon the live adapter will when
391
- * the store is seeded from a snapshot. Omitted when the adapter
392
- * itself has no icon. */
393
- icon?: string;
394
- /** Human-facing wallet name (e.g. "MetaMask") captured at persist
395
- * time. Required so the shadow adapter renders the same identity
396
- * the live adapter will; no "metamask" → "MetaMask" swap at the
397
- * hydration boundary. */
398
- name: string;
399
- };
400
- type StoredPoolRecord = Partial<Record<string, StoredPoolEntry>>;
401
- type StoredSelectionRecord = Partial<Record<ChainPlatform, string>>;
402
- type WalletPersistence = {
403
- clearAll: () => Promise<void>;
404
- clearPool: () => Promise<void>;
405
- getActiveConnectorId: () => Promise<string | null>;
406
- getPool: () => Promise<StoredPoolRecord>;
407
- getSelection: () => Promise<StoredSelectionRecord>;
408
- isUserDisconnected: () => Promise<boolean>;
409
- markUserDisconnected: (value: boolean) => Promise<void>;
410
- removePoolEntry: (connectorId: string) => Promise<void>;
411
- setActiveConnectorId: (connectorId: string | null) => Promise<void>;
412
- setPool: (pool: Map<string, ConnectedWallet>) => Promise<void>;
413
- setSelection: (selection: Map<ChainPlatform, string>) => Promise<void>;
414
- };
415
- //#endregion
416
- //#region src/storage/snapshot.d.ts
417
- /**
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.
342
+ * messages. `cause` keeps the original value.
421
343
  */
422
- type WalletSnapshot = {
423
- activeConnectorId: string | null;
424
- pool: StoredPoolRecord;
425
- selection: StoredSelectionRecord;
426
- };
427
- type CookieSource = Iterable<{
428
- name: string;
429
- value: string;
430
- }> | Iterable<[string, string]> | Readonly<Record<string, string>>;
431
- type SnapshotOptions = {
432
- /**
433
- * Same prefix passed to `WalletManagerProvider` / `WalletStorage`.
434
- * Defaults to `"butr"` to match the library default.
435
- */
436
- keyPrefix?: string;
437
- };
438
- declare const EMPTY_SNAPSHOT: WalletSnapshot;
439
- /**
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.
443
- */
444
- declare const readWalletSnapshot: (source: CookieSource, options?: SnapshotOptions) => WalletSnapshot;
344
+ type ConnectionErrorKind = "ChainMismatch" | "NotConnected" | "RequestPending" | "Timeout" | "Unknown" | "UserRejected" | "WalletLocked" | "WalletNotFound";
345
+ declare class ConnectionError extends Error {
346
+ readonly kind: ConnectionErrorKind;
347
+ constructor(kind: ConnectionErrorKind, message: string, options?: ErrorOptions);
348
+ }
349
+ /** The one boundary where a thrown value of unknown shape becomes typed. */
350
+ declare const toConnectionError: (cause: unknown) => ConnectionError;
445
351
  //#endregion
446
352
  //#region src/types/manager.d.ts
447
353
  /**
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.
354
+ * `pendingIds` are not failures: their adapters have not been announced yet,
355
+ * and the manager restores them the moment they are. Only `dropped` entries
356
+ * failed their silent reconnect; they stay persisted for the next load.
451
357
  */
452
358
  type HydrationOutcome = {
453
- dropped: Array<{
359
+ dropped: ReadonlyArray<{
454
360
  connectorId: string;
455
- reason: unknown;
361
+ reason: ConnectionError;
456
362
  }>;
457
- pendingIds: Array<string>;
458
- restoredIds: Array<string>;
363
+ pendingIds: ReadonlyArray<string>;
364
+ restoredIds: ReadonlyArray<string>;
459
365
  };
366
+ /**
367
+ * Read once, when the manager is created. Define it at module scope; the
368
+ * callbacks never see later values.
369
+ */
460
370
  type WalletManagerConfig = {
461
- /** Available connector metadata */
462
- connectors: Array<ConnectorMeta>;
463
- /** Function to instantiate a connector by ID */
464
- createConnector: (id: string) => WalletAdapter | null;
465
- /**
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.
469
- */
470
- initialState?: WalletSnapshot;
471
- /** Called after a wallet is successfully connected */
472
- onConnect?: (wallet: ConnectedWallet) => void;
473
- /**
474
- * Fires for every failed attempt (rejection, locked wallet, timeout,
475
- * …), so observability can hook here instead of wrapping every
476
- * `connectWallet` call.
477
- */
371
+ /** Fires whenever a wallet goes live: a user `connect` (`reconnected:
372
+ * false`) or a silent restore of a persisted connection (`true`). */
373
+ onConnect?: (wallet: ConnectedWallet, context: {
374
+ reconnected: boolean;
375
+ }) => void;
376
+ /** Fires for every failed attempt, so observability hooks here instead of
377
+ * wrapping every `connect` call. */
478
378
  onConnectError?: (error: ConnectionError, connectorId: string) => void;
479
- /** Called after a wallet is disconnected */
480
- onDisconnect?: (chainPlatform: ChainPlatform) => void;
481
- /** Fires once, after the mount-time hydration pass. */
379
+ /** `byUser` is false when the wallet itself ended the session (locked,
380
+ * extension removed, relay session expired). */
381
+ onDisconnect?: (wallet: ConnectedWallet, context: {
382
+ byUser: boolean;
383
+ }) => void;
384
+ /** Fires once, after the start-up hydration pass. */
482
385
  onHydrated?: (outcome: HydrationOutcome) => void;
483
- /** Called after all wallets are reset (e.g., to clear auth tokens) */
484
- onReset?: () => void | Promise<void>;
485
- /**
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.
489
- */
386
+ /** Fires at most once per attempt, once it passes `slowConnectThresholdMs`
387
+ * without settling. A hint, not a timeout. */
490
388
  onSlowConnect?: (connectorId: string) => void;
491
- /**
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`.
495
- */
496
- onStorageError?: (error: unknown, context: string) => void;
389
+ /** Persistence is fire-and-forget: a failed write never breaks state, it
390
+ * only surfaces here. Defaults to `console.warn`. */
391
+ onStorageError?: (error: Error) => void;
497
392
  /** Threshold for `onSlowConnect`, in milliseconds. Defaults to 5_000. */
498
393
  slowConnectThresholdMs?: number;
499
- /** Optional custom persistence implementation (e.g., cookie-backed) */
394
+ /** Where adapters come from: `autoDiscovery()`, any `discover*Adapters`
395
+ * export, or `fromAdapters(…)` for WalletConnect, Ledger and hand-rolled
396
+ * adapters. The first adapter announced for an id wins. */
397
+ sources?: ReadonlyArray<WalletSource>;
398
+ /** Replaces the default localStorage + sessionStorage persistence. */
500
399
  storage?: WalletPersistence;
501
- /** Storage key prefix for localStorage */
400
+ /** Key prefix for the default persistence. Ignored when `storage` is set;
401
+ * pass the same value to `readWalletSnapshot`. */
502
402
  storageKeyPrefix?: string;
503
403
  };
504
404
  //#endregion
505
- //#region src/types/signer.d.ts
506
- /**
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.
510
- */
511
- interface SignerForPlatform {}
512
- /** Convenience alias for narrowing a single platform's signer type. */
513
- type SignerOf<P extends keyof SignerForPlatform> = SignerForPlatform[P];
514
- //#endregion
515
- //#region src/wallet-source.d.ts
516
- /**
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.
520
- */
521
- type WalletSource = {
522
- subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void;
405
+ //#region src/chains.d.ts
406
+ /** Common EVM chains. `switchChain` accepts any `ChainBase`, so a chain
407
+ * missing here still works; it is just named by its CAIP-2 id. */
408
+ declare const EVM_CHAINS: {
409
+ readonly arbitrum: {
410
+ readonly id: "eip155:42161";
411
+ readonly name: "Arbitrum One";
412
+ readonly namespace: "eip155";
413
+ readonly reference: "42161";
414
+ };
415
+ readonly base: {
416
+ readonly id: "eip155:8453";
417
+ readonly name: "Base";
418
+ readonly namespace: "eip155";
419
+ readonly reference: "8453";
420
+ };
421
+ readonly bsc: {
422
+ readonly id: "eip155:56";
423
+ readonly name: "BNB Smart Chain";
424
+ readonly namespace: "eip155";
425
+ readonly reference: "56";
426
+ };
427
+ readonly ethereum: {
428
+ readonly id: "eip155:1";
429
+ readonly name: "Ethereum";
430
+ readonly namespace: "eip155";
431
+ readonly reference: "1";
432
+ };
433
+ readonly optimism: {
434
+ readonly id: "eip155:10";
435
+ readonly name: "Optimism";
436
+ readonly namespace: "eip155";
437
+ readonly reference: "10";
438
+ };
439
+ readonly polygon: {
440
+ readonly id: "eip155:137";
441
+ readonly name: "Polygon";
442
+ readonly namespace: "eip155";
443
+ readonly reference: "137";
444
+ };
445
+ readonly sepolia: {
446
+ readonly id: "eip155:11155111";
447
+ readonly name: "Sepolia";
448
+ readonly namespace: "eip155";
449
+ readonly reference: "11155111";
450
+ };
523
451
  };
524
- /**
525
- * Takes the exact shape of `discoverEvmAdapters` and friends, so a
526
- * single-platform app keeps `@usebutr/wallets` out of its bundle.
527
- */
528
- declare const createWalletSource: (subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void) => WalletSource;
452
+ declare const SVM_CHAINS: {
453
+ readonly devnet: {
454
+ readonly id: "solana:devnet";
455
+ readonly name: "Solana Devnet";
456
+ readonly namespace: "solana";
457
+ readonly reference: "devnet";
458
+ };
459
+ readonly mainnet: {
460
+ readonly id: "solana:mainnet";
461
+ readonly name: "Solana Mainnet";
462
+ readonly namespace: "solana";
463
+ readonly reference: "mainnet";
464
+ };
465
+ readonly testnet: {
466
+ readonly id: "solana:testnet";
467
+ readonly name: "Solana Testnet";
468
+ readonly namespace: "solana";
469
+ readonly reference: "testnet";
470
+ };
471
+ };
472
+ declare const SUI_CHAINS: {
473
+ readonly devnet: {
474
+ readonly id: "sui:devnet";
475
+ readonly name: "Sui Devnet";
476
+ readonly namespace: "sui";
477
+ readonly reference: "devnet";
478
+ };
479
+ readonly localnet: {
480
+ readonly id: "sui:localnet";
481
+ readonly name: "Sui Localnet";
482
+ readonly namespace: "sui";
483
+ readonly reference: "localnet";
484
+ };
485
+ readonly mainnet: {
486
+ readonly id: "sui:mainnet";
487
+ readonly name: "Sui Mainnet";
488
+ readonly namespace: "sui";
489
+ readonly reference: "mainnet";
490
+ };
491
+ readonly testnet: {
492
+ readonly id: "sui:testnet";
493
+ readonly name: "Sui Testnet";
494
+ readonly namespace: "sui";
495
+ readonly reference: "testnet";
496
+ };
497
+ };
498
+ /** CAIP-2 Bitcoin references are the first 16 bytes of each network's
499
+ * genesis block hash, which is what wallets advertise in `wallet.chains`. */
500
+ declare const BITCOIN_CHAINS: {
501
+ readonly mainnet: {
502
+ readonly id: "bip122:000000000019d6689c085ae165831e93";
503
+ readonly name: "Bitcoin";
504
+ readonly namespace: "bip122";
505
+ readonly reference: "000000000019d6689c085ae165831e93";
506
+ };
507
+ readonly signet: {
508
+ readonly id: "bip122:00000008819873e925422c1ff0f99f7c";
509
+ readonly name: "Bitcoin Signet";
510
+ readonly namespace: "bip122";
511
+ readonly reference: "00000008819873e925422c1ff0f99f7c";
512
+ };
513
+ readonly testnet: {
514
+ readonly id: "bip122:000000000933ea01ad0ee984209779ba";
515
+ readonly name: "Bitcoin Testnet";
516
+ readonly namespace: "bip122";
517
+ readonly reference: "000000000933ea01ad0ee984209779ba";
518
+ };
519
+ readonly testnet4: {
520
+ readonly id: "bip122:00000000da84f2bafbbc53dee25a72ae";
521
+ readonly name: "Bitcoin Testnet4";
522
+ readonly namespace: "bip122";
523
+ readonly reference: "00000000da84f2bafbbc53dee25a72ae";
524
+ };
525
+ };
526
+ declare const POLKADOT_CHAINS: {
527
+ readonly kusama: {
528
+ readonly id: "polkadot:b0a8d493285c2df73290dfb7e61f870f";
529
+ readonly name: "Kusama";
530
+ readonly namespace: "polkadot";
531
+ readonly reference: "b0a8d493285c2df73290dfb7e61f870f";
532
+ };
533
+ readonly paseo: {
534
+ readonly id: "polkadot:77afd6190f1554ad45fd0d31aee62aac";
535
+ readonly name: "Paseo";
536
+ readonly namespace: "polkadot";
537
+ readonly reference: "77afd6190f1554ad45fd0d31aee62aac";
538
+ };
539
+ readonly polkadot: {
540
+ readonly id: "polkadot:91b171bb158e2d3848fa23a9f1c25182";
541
+ readonly name: "Polkadot";
542
+ readonly namespace: "polkadot";
543
+ readonly reference: "91b171bb158e2d3848fa23a9f1c25182";
544
+ };
545
+ readonly westend: {
546
+ readonly id: "polkadot:e143f23803ac50e8f6f8e62695d1ce9e";
547
+ readonly name: "Westend";
548
+ readonly namespace: "polkadot";
549
+ readonly reference: "e143f23803ac50e8f6f8e62695d1ce9e";
550
+ };
551
+ };
552
+ declare const EVM_CHAINS_LIST: ReadonlyArray<ChainBase>;
553
+ declare const SVM_CHAINS_LIST: ReadonlyArray<ChainBase>;
554
+ declare const SUI_CHAINS_LIST: ReadonlyArray<ChainBase>;
555
+ declare const BITCOIN_CHAINS_LIST: ReadonlyArray<ChainBase>;
556
+ declare const POLKADOT_CHAINS_LIST: ReadonlyArray<ChainBase>;
557
+ /** Every registry as a list, keyed by platform: the shape chain pickers want. */
558
+ declare const CHAINS_BY_PLATFORM: ChainsByPlatform;
529
559
  //#endregion
530
560
  //#region src/store/reducer.d.ts
531
- /**
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.
535
- */
536
- type ConnectionStatus = "idle" | "connecting" | "success" | "error" | "reconnecting";
537
- type State = {
561
+ /** Status of the latest `connect` attempt, not of the connection itself;
562
+ * `useConnectionStatus` derives the latter. */
563
+ type ConnectStatus = "idle" | "connecting" | "success" | "error";
564
+ type WalletState = {
538
565
  activeConnectorId: string | null;
566
+ /** Every adapter the sources announced, in announcement order. */
567
+ adapters: ReadonlyArray<WalletAdapter>;
539
568
  connectingConnectorId: string | null;
540
569
  connectionError: ConnectionError | null;
541
- connectionStatus: ConnectionStatus;
542
- isHydrated: boolean;
543
- isUserDisconnected: boolean;
544
- pool: Map<string, ConnectedWallet>;
570
+ connectionStatus: ConnectStatus;
545
571
  /**
546
- * Ids still backed by a shadow adapter from `initialState`. Empty
547
- * without `initialState`; shrinks on `HYDRATED` / `CONNECT_SUCCEEDED`
548
- * and on `DISCONNECTED` / `RESET`.
572
+ * Persisted connections that are not live: awaiting their adapter, failed
573
+ * their silent reconnect, or ended by the wallet rather than the user.
574
+ * They stay persisted so the next load retries them.
549
575
  */
576
+ dormant: ReadonlyMap<string, StoredPoolEntry>;
577
+ isHydrated: boolean;
578
+ isUserDisconnected: boolean;
579
+ pool: ReadonlyMap<string, ConnectedWallet>;
580
+ /** Pool ids still backed by a shadow adapter seeded from `initialState`. */
550
581
  reconnectingIds: ReadonlySet<string>;
551
- selection: Map<ChainPlatform, string>;
582
+ /** Which pool entry serves each platform present in the pool. */
583
+ selection: ReadonlyMap<ChainPlatform, string>;
552
584
  };
553
585
  //#endregion
554
586
  //#region src/store/shadow-adapter.d.ts
555
587
  /**
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.
588
+ * Thrown by the three required methods of a shadow adapter. Every optional
589
+ * method is simply absent, so only code that skipped the `reconnectingIds`
590
+ * check and called `connect`, `getAccounts` or `getSigner` sees this.
559
591
  */
560
592
  declare class ShadowConnectorError extends Error {
561
- readonly code = "BUTR_RECONNECTING";
562
593
  readonly connectorId: string;
563
594
  readonly method: string;
564
595
  constructor(method: string, connectorId: string);
565
596
  }
597
+ //#endregion
598
+ //#region src/store/wallet-manager.d.ts
599
+ type WalletManager = Pick<StoreApi<WalletState>, "getInitialState" | "getState" | "subscribe"> & {
600
+ /** Clears `connectionError` and returns `connectionStatus` to idle. */
601
+ clearConnectionError: () => void;
602
+ /** Resolves the connected wallet; rejects with a `ConnectionError`. */
603
+ connect: (connectorId: string) => Promise<ConnectedWallet>;
604
+ disconnect: (connectorId: string) => void;
605
+ /** Disconnects every wallet and forgets every persisted connection. */
606
+ disconnectAll: () => void;
607
+ /** Opens the wallet's account picker, then refreshes the pool entry. A no-op
608
+ * for wallets without `requestAccounts`. */
609
+ requestAccounts: (connectorId: string) => Promise<void>;
610
+ /** Selects an exposed account without changing its chain or the account
611
+ * list. Unknown wallets and accounts are ignored. */
612
+ setAccount: (connectorId: string, account: Account) => void;
613
+ setActive: (connectorId: string) => void;
614
+ setSelection: (chainPlatform: ChainPlatform, connectorId: string) => void;
615
+ /**
616
+ * Subscribes the sources, hydrates persisted connections (once per
617
+ * manager) and bridges wallet events. The returned function undoes all of
618
+ * it except hydration, so a StrictMode remount is safe.
619
+ */
620
+ start: () => () => void;
621
+ };
566
622
  /**
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.
623
+ * Framework-free: React binds it through `WalletManagerProvider`, anything
624
+ * else calls `start()` itself. Creating one has no side effects, so it is
625
+ * safe during a server render.
570
626
  */
571
- declare const isShadowAdapter: (adapter: WalletAdapter) => boolean;
627
+ declare const createWalletManager: (config?: WalletManagerConfig, options?: {
628
+ initialState?: WalletSnapshot;
629
+ }) => WalletManager;
572
630
  //#endregion
573
631
  //#region src/storage/browser-storage-driver.d.ts
574
632
  type BrowserStorageDrivers = {
@@ -627,8 +685,28 @@ type CookieDriverOptions = {
627
685
  */
628
686
  declare const createCookieStorageDriver: (options?: CookieDriverOptions) => StorageDriver;
629
687
  //#endregion
688
+ //#region src/storage/snapshot.d.ts
689
+ type CookieSource = Iterable<{
690
+ name: string;
691
+ value: string;
692
+ }> | Iterable<[string, string]> | Readonly<Record<string, string>>;
693
+ type SnapshotOptions = {
694
+ /**
695
+ * Same prefix passed as `storageKeyPrefix` / to `createWalletStorage`.
696
+ * Defaults to `"butr"` to match the library default.
697
+ */
698
+ keyPrefix?: string;
699
+ };
700
+ declare const EMPTY_SNAPSHOT: WalletSnapshot;
701
+ /**
702
+ * Optimistic: only what the browser last persisted, so an uninstall or
703
+ * other-tab disconnect makes it stale. Authoritative once the entry
704
+ * leaves `reconnectingIds`; `isHydrated` is true from render one.
705
+ */
706
+ declare const readWalletSnapshot: (source: CookieSource, options?: SnapshotOptions) => WalletSnapshot;
707
+ //#endregion
630
708
  //#region src/storage/wallet-storage.d.ts
631
- type StorageConfig = {
709
+ type WalletStorageOptions = {
632
710
  /** Defaults to `"butr"`, matching `readWalletSnapshot`. */
633
711
  keyPrefix?: string;
634
712
  /** Survives app restart. Defaults to localStorage on web. */
@@ -636,73 +714,12 @@ type StorageConfig = {
636
714
  /** Cleared on session end. Defaults to sessionStorage on web. */
637
715
  session?: StorageDriver;
638
716
  };
639
- declare class WalletStorage implements WalletPersistence {
640
- private readonly poolKey;
641
- private readonly selectionKey;
642
- private readonly activeKey;
643
- private readonly userDisconnectedKey;
644
- private readonly persistent;
645
- private readonly session;
646
- /**
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.
650
- */
651
- private poolMutationQueue;
652
- constructor(config: StorageConfig);
653
- /** Chain `fn` after the in-flight pool mutation. */
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;
658
- getPool(): Promise<StoredPoolRecord>;
659
- /**
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`.
663
- */
664
- setPool(pool: Map<string, ConnectedWallet>): Promise<void>;
665
- removePoolEntry(connectorId: string): Promise<void>;
666
- clearPool(): Promise<void>;
667
- getSelection(): Promise<StoredSelectionRecord>;
668
- setSelection(selection: Map<ChainPlatform, string>): Promise<void>;
669
- getActiveConnectorId(): Promise<string | null>;
670
- setActiveConnectorId(connectorId: string | null): Promise<void>;
671
- clearAll(): Promise<void>;
672
- /**
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.
676
- */
677
- isUserDisconnected(): Promise<boolean>;
678
- markUserDisconnected(value: boolean): Promise<void>;
679
- }
680
- //#endregion
681
- //#region src/store/wallet-store.d.ts
682
- type ExtractState<S> = S extends {
683
- getState: () => infer T;
684
- } ? T : never;
685
- type RuntimeMembers = {
686
- _config: WalletManagerConfig;
687
- _storage: WalletPersistence;
688
- connectWallet: (connectorId: string, onSuccess?: (wallet: ConnectedWallet) => void, onError?: (error: Error) => void) => Promise<void>;
689
- disconnectWallet: (connectorId: string) => void;
690
- getConnectorInstance: (id: string) => ReturnType<WalletManagerConfig["createConnector"]>;
691
- hydrateWallets: () => Promise<void>;
692
- refreshWallet: (connectorId: string) => void;
693
- requestAccounts: (connectorId: string) => Promise<void>;
694
- reset: () => void;
695
- resetConnectionStatus: () => void;
696
- setActiveConnector: (connectorId: string | null) => void;
697
- setConnectionError: (error: ConnectionError | null) => void;
698
- setSelection: (chainPlatform: ChainPlatform, connectorId: string | null) => void;
699
- setUserDisconnected: (value: boolean) => void;
700
- tryRestoreFromPending: (connectorId: string) => Promise<void>;
701
- updateWalletAccount: (connectorId: string, account: Account) => void;
702
- };
703
- type WalletStore = ReturnType<typeof createWalletStore>;
704
- type WalletStoreState = ExtractState<WalletStore>;
705
- declare const createWalletStore: (config: WalletManagerConfig) => import("zustand/vanilla").StoreApi<State & RuntimeMembers>;
717
+ /**
718
+ * Pool, selection and active id go to the persistent driver, the disconnect
719
+ * intent to the session driver. Unchanged keys are not rewritten, so a
720
+ * cookie-backed driver does not re-send every cookie on each save.
721
+ */
722
+ declare const createWalletStorage: (options?: WalletStorageOptions) => WalletPersistence;
706
723
  //#endregion
707
724
  //#region src/group-by-platform.d.ts
708
725
  /**
@@ -758,6 +775,9 @@ type SignInFlowOptions = {
758
775
  /** Hand the signed result to your backend. Throw to fail the flow. */
759
776
  verify: (result: SignInResult) => Promise<void>;
760
777
  };
778
+ type SignInFlow = {
779
+ signIn: (wallet: ConnectedWallet, account?: Account) => Promise<SignInResult>;
780
+ };
761
781
  /** Thrown before any wallet interaction when the wallet can't sign at
762
782
  * all. Distinct from a rejection: nothing was asked of the user, so UI
763
783
  * should say "this wallet can't sign in" rather than "you declined". */
@@ -770,15 +790,15 @@ declare class SignInUnsupportedError extends Error {
770
790
  * `result.signedMessage` holds the wallet-composed statement and
771
791
  * `result.message` is absent. `preferSignMessage` opts out.
772
792
  */
773
- declare const createSignInFlow: (options: SignInFlowOptions) => {
774
- signIn: (wallet: ConnectedWallet, account?: Account) => Promise<SignInResult>;
775
- };
793
+ declare const createSignInFlow: (options: SignInFlowOptions) => SignInFlow;
776
794
  //#endregion
777
795
  //#region src/wallet-equal.d.ts
796
+ /** Account ids are canonical (`buildAccount`), so identity is the id. */
797
+ declare const accountsEqual: (a: ReadonlyArray<Account>, b: ReadonlyArray<Account>) => boolean;
778
798
  /**
779
799
  * The adapter is compared by reference, not `connector.id`: hydration
780
800
  * swaps a shadow adapter for the live one under an unchanged id, and an
781
- * id check would strand consumers on the throwing placeholder.
801
+ * id check would strand consumers on the placeholder.
782
802
  */
783
803
  declare const walletEqual: (a: ConnectedWallet | undefined, b: ConnectedWallet | undefined) => boolean;
784
804
  //#endregion
@@ -827,5 +847,5 @@ declare const bytesToBase58: (bytes: Uint8Array) => string;
827
847
  * rather than silently dropping them. */
828
848
  declare const base58ToBytes: (input: string) => Uint8Array;
829
849
  //#endregion
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 };
850
+ export { type Account, type AccountOptions, BITCOIN_CHAINS, BITCOIN_CHAINS_LIST, type Balance, type BalanceOptions, type BitcoinAdapter, type BitcoinTransfer, type BitcoinWallet, type BrowserStorageDrivers, CHAINS_BY_PLATFORM, CHAIN_PLATFORMS, type ChainBase, type ChainPlatform, type ChainsByPlatform, type ConnectStatus, type ConnectedWallet, ConnectionError, type ConnectionErrorKind, type Connector, type ConnectorEvent, type CookieDriverOptions, type CookieSource, EMPTY_SNAPSHOT, EVM_CHAINS, EVM_CHAINS_LIST, type EvmAdapter, type EvmTransactionRequest, type EvmTransactionValue, type EvmWallet, type HydrationOutcome, type InitialCookies, type MaybePromise, POLKADOT_CHAINS, POLKADOT_CHAINS_LIST, type PersistedWalletState, type PlatformDiscoverer, type PolkadotAdapter, type PolkadotWallet, SUI_CHAINS, SUI_CHAINS_LIST, SVM_CHAINS, SVM_CHAINS_LIST, ShadowConnectorError, type SignInFlowOptions, type SignInInput, type SignInMessageContext, type SignInOutput, type SignInResult, SignInUnsupportedError, type SignInValue, type SignedMessage, type SnapshotOptions, type StorageDriver, type StoredPoolEntry, type StoredPoolRecord, type StoredSelectionRecord, type SuiAdapter, type SuiTransactionInput, type SuiWallet, type SvmAdapter, type SvmWallet, type TransactionOptions, type TransactionReceipt, type WalletAdapter, type WalletAdapterFor, type WalletBase, type WalletManager, type WalletManagerConfig, type WalletPersistence, type WalletSigner, type WalletSignerKind, type WalletSignerOf, type WalletSignerRegistry, type WalletSnapshot, type WalletSource, type WalletState, type WalletStorageOptions, accountsEqual, base58ToBytes, base64ToBytes, buildAccount, bytesToBase58, bytesToBase64, bytesToHex, bytesToHexPrefixed, createBrowserStorageDriver, createCookieStorageDriver, createMemoryStorageDriver, createSignInFlow, createWalletManager, createWalletStorage, fromAdapters, groupByPlatform, hexToBytes, isChainPlatform, isPlatformWallet, logError, logWarn, readWalletSnapshot, resolveChain, sanitizeIcon, toConnectionError, walletEqual };
831
851
  //# sourceMappingURL=index.d.ts.map