@usebutr/core 0.5.0 → 1.1.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 +469 -320
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +235 -57
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -17,172 +17,7 @@ type ChainBase = {
|
|
|
17
17
|
reference: string;
|
|
18
18
|
};
|
|
19
19
|
//#endregion
|
|
20
|
-
//#region src/
|
|
21
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
22
|
-
/** Low-level key/value driver. Sync on web (localStorage, MMKV),
|
|
23
|
-
* async on React Native (AsyncStorage). */
|
|
24
|
-
type StorageDriver = {
|
|
25
|
-
getItem: (key: string) => MaybePromise<string | null>;
|
|
26
|
-
removeItem: (key: string) => MaybePromise<void>;
|
|
27
|
-
setItem: (key: string, value: string) => MaybePromise<void>;
|
|
28
|
-
};
|
|
29
|
-
type StoredPoolEntry = {
|
|
30
|
-
account: Account;
|
|
31
|
-
/** All known accounts on the wallet at last persist. Always contains
|
|
32
|
-
* `account`. */
|
|
33
|
-
accounts: Array<Account>;
|
|
34
|
-
chainPlatform: ChainPlatform;
|
|
35
|
-
connectorId: string;
|
|
36
|
-
/** Wallet icon URL or data-URI captured at persist time. Lets the
|
|
37
|
-
* shadow adapter render the same icon the live adapter will when
|
|
38
|
-
* the store is seeded from a snapshot. Omitted when the adapter
|
|
39
|
-
* itself has no icon. */
|
|
40
|
-
icon?: string;
|
|
41
|
-
/** Human-facing wallet name (e.g. "MetaMask") captured at persist
|
|
42
|
-
* time. Required so the shadow adapter renders the same identity
|
|
43
|
-
* the live adapter will; no "metamask" → "MetaMask" swap at the
|
|
44
|
-
* hydration boundary. */
|
|
45
|
-
name: string;
|
|
46
|
-
};
|
|
47
|
-
type StoredPoolRecord = Partial<Record<string, StoredPoolEntry>>;
|
|
48
|
-
type StoredSelectionRecord = Partial<Record<ChainPlatform, string>>;
|
|
49
|
-
type WalletPersistence = {
|
|
50
|
-
clearAll: () => Promise<void>;
|
|
51
|
-
clearPool: () => Promise<void>;
|
|
52
|
-
getActiveConnectorId: () => Promise<string | null>;
|
|
53
|
-
getPool: () => Promise<StoredPoolRecord>;
|
|
54
|
-
getSelection: () => Promise<StoredSelectionRecord>;
|
|
55
|
-
isUserDisconnected: () => Promise<boolean>;
|
|
56
|
-
markUserDisconnected: (value: boolean) => Promise<void>;
|
|
57
|
-
removePoolEntry: (connectorId: string) => Promise<void>;
|
|
58
|
-
setActiveConnectorId: (connectorId: string | null) => Promise<void>;
|
|
59
|
-
setPool: (pool: Map<string, ConnectedWallet>) => Promise<void>;
|
|
60
|
-
setSelection: (selection: Map<ChainPlatform, string>) => Promise<void>;
|
|
61
|
-
};
|
|
62
|
-
//#endregion
|
|
63
|
-
//#region src/storage/snapshot.d.ts
|
|
64
|
-
/**
|
|
65
|
-
* Server-safe view of a butr-persisted session; everything you can
|
|
66
|
-
* know about a user's connected wallets from the cookie payload alone,
|
|
67
|
-
* without instantiating a `Connector`.
|
|
68
|
-
*
|
|
69
|
-
* Notably absent: the `Connector` instance. A wallet extension exists
|
|
70
|
-
* only in the browser, so a server render can know *which* wallet was
|
|
71
|
-
* connected and *what address* it held, but cannot dispatch
|
|
72
|
-
* `signMessage`/`sendTransaction` on it. Splitting display from action
|
|
73
|
-
* along this seam keeps the impossibility expressed in the types
|
|
74
|
-
* rather than hidden inside a runtime check.
|
|
75
|
-
*/
|
|
76
|
-
type WalletSnapshot = {
|
|
77
|
-
activeConnectorId: string | null;
|
|
78
|
-
pool: StoredPoolRecord;
|
|
79
|
-
selection: StoredSelectionRecord;
|
|
80
|
-
};
|
|
81
|
-
type CookieSource = Iterable<{
|
|
82
|
-
name: string;
|
|
83
|
-
value: string;
|
|
84
|
-
}> | Iterable<[string, string]> | Readonly<Record<string, string>>;
|
|
85
|
-
type SnapshotOptions = {
|
|
86
|
-
/**
|
|
87
|
-
* Same prefix passed to `WalletManagerProvider` / `WalletStorage`.
|
|
88
|
-
* Defaults to `"butr"` to match the library default.
|
|
89
|
-
*/
|
|
90
|
-
keyPrefix?: string;
|
|
91
|
-
};
|
|
92
|
-
declare const EMPTY_SNAPSHOT: WalletSnapshot;
|
|
93
|
-
/**
|
|
94
|
-
* Parse a cookie source into a server-safe `WalletSnapshot`.
|
|
95
|
-
*
|
|
96
|
-
* Pure, sync, no `document`, no React; runnable in any environment
|
|
97
|
-
* (Server Component, route handler, edge middleware, even client
|
|
98
|
-
* code). Pair with `createCookieStorageDriver({ initialCookies })`
|
|
99
|
-
* and `<WalletManagerProvider initialSnapshot={…} />` to render a
|
|
100
|
-
* connected shell server-side without a hydration flash.
|
|
101
|
-
*
|
|
102
|
-
* **Stale-snapshot semantics.** The snapshot reflects whatever the
|
|
103
|
-
* browser most recently persisted. If the user has since uninstalled
|
|
104
|
-
* the wallet, switched accounts, or disconnected in another tab, the
|
|
105
|
-
* client-side hydration will reconcile reality and the live store
|
|
106
|
-
* will diverge from the snapshot. Treat the snapshot as an
|
|
107
|
-
* *optimistic* shell; accurate enough to avoid a paint flicker,
|
|
108
|
-
* authoritative only after `useIsHydrated()` is true.
|
|
109
|
-
*
|
|
110
|
-
* **Inputs.** Accepts the three shapes Next.js / Express / Hono /
|
|
111
|
-
* generic-Node cookie code naturally produces:
|
|
112
|
-
* - A plain object: `{ "butr-pool": "{...}", … }`
|
|
113
|
-
* - An array of `{ name, value }` (Next.js' `cookies().getAll()`)
|
|
114
|
-
* - An iterable of `[name, value]` tuples
|
|
115
|
-
*
|
|
116
|
-
* Malformed entries are dropped with a `logWarn` (same policy as
|
|
117
|
-
* `WalletStorage.getPool`); a cross-tab corruption shouldn't crash
|
|
118
|
-
* the server render.
|
|
119
|
-
*/
|
|
120
|
-
declare const readWalletSnapshot: (source: CookieSource, options?: SnapshotOptions) => WalletSnapshot;
|
|
121
|
-
//#endregion
|
|
122
|
-
//#region src/types/errors.d.ts
|
|
123
|
-
/**
|
|
124
|
-
* Tagged union of normalised connection errors.
|
|
125
|
-
*
|
|
126
|
-
* butr maps thrown values from connectors (which vary across wallet SDKs:
|
|
127
|
-
* MetaMask uses EIP-1193 codes, Phantom throws stringly-typed errors,
|
|
128
|
-
* embedded SDKs throw their own classes) into a small set of UX-meaningful
|
|
129
|
-
* variants. Consumers branch on `kind` instead of regexing message strings.
|
|
130
|
-
*
|
|
131
|
-
* `message` is always present and human-readable. `cause` preserves the
|
|
132
|
-
* original thrown value so callers can inspect raw connector errors when
|
|
133
|
-
* the variant is `Unknown`.
|
|
134
|
-
*/
|
|
135
|
-
type ConnectionError = {
|
|
136
|
-
kind: "UserRejected";
|
|
137
|
-
message: string;
|
|
138
|
-
} | {
|
|
139
|
-
kind: "RequestPending";
|
|
140
|
-
message: string;
|
|
141
|
-
} | {
|
|
142
|
-
kind: "WalletLocked";
|
|
143
|
-
message: string;
|
|
144
|
-
} | {
|
|
145
|
-
actualChain?: string;
|
|
146
|
-
expectedChain?: string;
|
|
147
|
-
kind: "ChainMismatch";
|
|
148
|
-
message: string;
|
|
149
|
-
} | {
|
|
150
|
-
kind: "NotConnected";
|
|
151
|
-
message: string;
|
|
152
|
-
} | {
|
|
153
|
-
kind: "Timeout";
|
|
154
|
-
message: string;
|
|
155
|
-
} | {
|
|
156
|
-
cause?: unknown;
|
|
157
|
-
kind: "Unknown";
|
|
158
|
-
message: string;
|
|
159
|
-
};
|
|
160
|
-
type ConnectionErrorKind = ConnectionError["kind"];
|
|
161
|
-
/**
|
|
162
|
-
* Normalise a thrown value into a `ConnectionError`.
|
|
163
|
-
*
|
|
164
|
-
* Recognises:
|
|
165
|
-
* - butr's own `Error("Connection timeout")` (from the 90s connect timeout)
|
|
166
|
-
* - butr's own `Error("Failed to get account")` (from the connect flow)
|
|
167
|
-
* - EIP-1193 numeric `code` properties (`4001` → UserRejected,
|
|
168
|
-
* `-32002` → RequestPending, `4100`/`4900`/`4901` → NotConnected:
|
|
169
|
-
* unauthorized / disconnected from all-or-one chains)
|
|
170
|
-
* - common message substrings: "user rejected" / "user denied",
|
|
171
|
-
* "locked", "chain", etc.
|
|
172
|
-
* - anything else → `Unknown` with `cause` set to the original value.
|
|
173
|
-
*/
|
|
174
|
-
declare const mapConnectionError: (raw: unknown) => ConnectionError;
|
|
175
|
-
//#endregion
|
|
176
|
-
//#region src/types/wallet.d.ts
|
|
177
|
-
/**
|
|
178
|
-
* Canonical list of supported chain platforms. The single runtime source
|
|
179
|
-
* of truth: `ChainPlatform` is derived from it, and storage validators
|
|
180
|
-
* build their allowlists from it, so the type and the runtime checks can
|
|
181
|
-
* never drift (a missing platform here was why Polkadot connections failed
|
|
182
|
-
* to persist).
|
|
183
|
-
*/
|
|
184
|
-
declare const CHAIN_PLATFORMS: readonly ["evm", "svm", "sui", "bitcoin", "polkadot"];
|
|
185
|
-
type ChainPlatform = (typeof CHAIN_PLATFORMS)[number];
|
|
20
|
+
//#region src/types/account.d.ts
|
|
186
21
|
type Account = {
|
|
187
22
|
chain: ChainBase;
|
|
188
23
|
id: string;
|
|
@@ -206,42 +41,8 @@ type Balance = {
|
|
|
206
41
|
/** Raw integer amount */
|
|
207
42
|
value: bigint;
|
|
208
43
|
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
*
|
|
212
|
-
* - `installed`: the wallet is available and `connect()` can be called.
|
|
213
|
-
* - `loadable`: the wallet's SDK can be loaded on demand (e.g. WalletConnect
|
|
214
|
-
* modal that pops a QR code without requiring a browser extension).
|
|
215
|
-
* - `not-installed`: the wallet isn't reachable. Consumers typically render
|
|
216
|
-
* a "download" affordance pointing at `meta.url`.
|
|
217
|
-
*/
|
|
218
|
-
type WalletAvailability = "installed" | "loadable" | "not-installed";
|
|
219
|
-
/**
|
|
220
|
-
* Events a connector can emit while connected. butr's runtime subscribes
|
|
221
|
-
* via `Connector.subscribe?` after a successful `connect()` and dispatches
|
|
222
|
-
* the equivalent reducer event:
|
|
223
|
-
*
|
|
224
|
-
* - `accountChanged` carries both the new active `account` AND the full
|
|
225
|
-
* `accounts` array the wallet currently exposes. The runtime mirrors
|
|
226
|
-
* that list verbatim into the pool entry. This handles two cases
|
|
227
|
-
* uniformly:
|
|
228
|
-
* - Multi-account wallets (MetaMask, Rabby, Brave): the user adds or
|
|
229
|
-
* removes accounts from the dapp's permission set; the array grows
|
|
230
|
-
* or shrinks to match.
|
|
231
|
-
* - Single-account-exposure wallets (Phantom EVM/SVM, MetaMask Snap):
|
|
232
|
-
* only the active account is ever in `accounts`; switching swaps it
|
|
233
|
-
* in place rather than appending.
|
|
234
|
-
* Also covers chain switches; the new chain lives inside `account.chain`.
|
|
235
|
-
* - `disconnected` → `DISCONNECTED` (wallet has gone away externally:
|
|
236
|
-
* user locked it, removed the extension, etc.).
|
|
237
|
-
*/
|
|
238
|
-
type ConnectorEvent = {
|
|
239
|
-
account: Account;
|
|
240
|
-
accounts: Array<Account>;
|
|
241
|
-
type: "accountChanged";
|
|
242
|
-
} | {
|
|
243
|
-
type: "disconnected";
|
|
244
|
-
};
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/types/capabilities.d.ts
|
|
245
46
|
/**
|
|
246
47
|
* Capability flags describing what an adapter can actually do at
|
|
247
48
|
* runtime. Populated by each adapter (the auto-built ones derive
|
|
@@ -296,6 +97,95 @@ type WalletCapabilities = {
|
|
|
296
97
|
* per-call `chain` input) when more than one chain is advertised. */
|
|
297
98
|
switchChain: boolean;
|
|
298
99
|
};
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/types/platform.d.ts
|
|
102
|
+
/**
|
|
103
|
+
* Canonical list of supported chain platforms. The single runtime source
|
|
104
|
+
* of truth: `ChainPlatform` is derived from it, and storage validators
|
|
105
|
+
* build their allowlists from it, so the type and the runtime checks can
|
|
106
|
+
* never drift (a missing platform here was why Polkadot connections failed
|
|
107
|
+
* to persist).
|
|
108
|
+
*/
|
|
109
|
+
declare const CHAIN_PLATFORMS: readonly ["evm", "svm", "sui", "bitcoin", "polkadot"];
|
|
110
|
+
type ChainPlatform = (typeof CHAIN_PLATFORMS)[number];
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/types/chains-by-platform.d.ts
|
|
113
|
+
/**
|
|
114
|
+
* Map of every chain platform to the list of chains the consumer wants
|
|
115
|
+
* to expose to its UI (chain switcher, picker, etc).
|
|
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).
|
|
122
|
+
*/
|
|
123
|
+
type ChainsByPlatform = Readonly<Record<ChainPlatform, ReadonlyArray<ChainBase>>>;
|
|
124
|
+
/**
|
|
125
|
+
* Build a fully-populated `ChainsByPlatform` from a partial. Platforms
|
|
126
|
+
* the consumer doesn't specify default to an empty list.
|
|
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
|
+
* });
|
|
149
|
+
*/
|
|
150
|
+
declare const buildChainsByPlatform: (partial: Partial<ChainsByPlatform>) => ChainsByPlatform;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/types/connector.d.ts
|
|
153
|
+
/**
|
|
154
|
+
* Whether a wallet is currently usable from the user's environment.
|
|
155
|
+
*
|
|
156
|
+
* - `installed`: the wallet is available and `connect()` can be called.
|
|
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`.
|
|
161
|
+
*/
|
|
162
|
+
type WalletAvailability = "installed" | "loadable" | "not-installed";
|
|
163
|
+
/**
|
|
164
|
+
* Events a connector can emit while connected. butr's runtime subscribes
|
|
165
|
+
* via `Connector.subscribe?` after a successful `connect()` and dispatches
|
|
166
|
+
* the equivalent reducer event:
|
|
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.).
|
|
181
|
+
*/
|
|
182
|
+
type ConnectorEvent = {
|
|
183
|
+
account: Account;
|
|
184
|
+
accounts: Array<Account>;
|
|
185
|
+
type: "accountChanged";
|
|
186
|
+
} | {
|
|
187
|
+
type: "disconnected";
|
|
188
|
+
};
|
|
299
189
|
/**
|
|
300
190
|
* Orchestration interface: what `butr` actually calls during the
|
|
301
191
|
* connect / disconnect / hydrate flow. This is the contract `butr`
|
|
@@ -335,7 +225,15 @@ type Connector<P extends ChainPlatform = ChainPlatform> = {
|
|
|
335
225
|
/** Optional. Wallet logo as a URL or data URI. Adapters built via butr's
|
|
336
226
|
* auto-discovery (EIP-6963, Wallet Standard) populate this from the
|
|
337
227
|
* wallet's announced metadata; hand-rolled adapters can leave it
|
|
338
|
-
* unset and supply icons separately via `ConnectorMeta`.
|
|
228
|
+
* unset and supply icons separately via `ConnectorMeta`.
|
|
229
|
+
*
|
|
230
|
+
* **Already sanitized on discovered adapters.** Discovery runs
|
|
231
|
+
* `sanitizeIcon` at construction, so the value is either a trimmed,
|
|
232
|
+
* non-empty string or `undefined`: never blank, never whitespace-led.
|
|
233
|
+
* Render it directly, including into strict consumers like
|
|
234
|
+
* `next/image`. No second `sanitizeIcon` call and no `icon !== ""`
|
|
235
|
+
* guard is needed. Hand-rolled adapters set this field themselves and
|
|
236
|
+
* own the guarantee. */
|
|
339
237
|
icon?: string;
|
|
340
238
|
/** Stable key: "metamask", "phantom", etc. Pool entries are keyed by this. */
|
|
341
239
|
id: string;
|
|
@@ -358,6 +256,25 @@ type Connector<P extends ChainPlatform = ChainPlatform> = {
|
|
|
358
256
|
* themselves. */
|
|
359
257
|
subscribe?: (listener: (event: ConnectorEvent) => void) => () => void;
|
|
360
258
|
};
|
|
259
|
+
type ConnectorMeta = {
|
|
260
|
+
/** Optional. Sync probe that reports whether the wallet is currently
|
|
261
|
+
* available. Defaults to `"installed"` when omitted. Consumers call
|
|
262
|
+
* this at render time to gate the "Connect" button. */
|
|
263
|
+
availability?: () => WalletAvailability;
|
|
264
|
+
chainPlatform: ChainPlatform;
|
|
265
|
+
/** Optional image URL or data URI for wallet selection UIs. Unlike
|
|
266
|
+
* `Connector.icon`, this one is consumer-supplied and butr does not
|
|
267
|
+
* sanitize it; run it through `sanitizeIcon` if the value came from
|
|
268
|
+
* wallet metadata rather than your own assets. */
|
|
269
|
+
icon?: string;
|
|
270
|
+
id: string;
|
|
271
|
+
name: string;
|
|
272
|
+
/** Optional. Where to send users who don't have this wallet (download
|
|
273
|
+
* page, app store link, etc.). */
|
|
274
|
+
url?: string;
|
|
275
|
+
};
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/types/wallet.d.ts
|
|
361
278
|
/**
|
|
362
279
|
* Methods every connected wallet supports regardless of chain. The
|
|
363
280
|
* per-platform `Wallet` types extend this with their platform-specific
|
|
@@ -475,50 +392,230 @@ type SuiAdapter = Connector<"sui"> & SuiWallet;
|
|
|
475
392
|
type BitcoinAdapter = Connector<"bitcoin"> & BitcoinWallet;
|
|
476
393
|
type PolkadotAdapter = Connector<"polkadot"> & PolkadotWallet;
|
|
477
394
|
/**
|
|
478
|
-
* Full adapter interface; discriminated union by `chainPlatform`.
|
|
479
|
-
*
|
|
480
|
-
* Narrow on `wallet.connector.chainPlatform === "svm"` (etc.) to gain
|
|
481
|
-
* access to platform-specific methods like `signIn` (SVM) or
|
|
482
|
-
* `signTransaction` (SVM / Sui / Bitcoin). Calling those methods on a
|
|
483
|
-
* non-narrowed `WalletAdapter` is a TypeScript error; that's the
|
|
484
|
-
* point. The discriminant carries the type-level fact "this method
|
|
485
|
-
* doesn't exist on EVM" so consumers can't accidentally branch on
|
|
486
|
-
* `capabilities.signIn` and call a method that EVM adapters don't
|
|
487
|
-
* implement.
|
|
395
|
+
* Full adapter interface; discriminated union by `chainPlatform`.
|
|
396
|
+
*
|
|
397
|
+
* Narrow on `wallet.connector.chainPlatform === "svm"` (etc.) to gain
|
|
398
|
+
* access to platform-specific methods like `signIn` (SVM) or
|
|
399
|
+
* `signTransaction` (SVM / Sui / Bitcoin). Calling those methods on a
|
|
400
|
+
* non-narrowed `WalletAdapter` is a TypeScript error; that's the
|
|
401
|
+
* point. The discriminant carries the type-level fact "this method
|
|
402
|
+
* doesn't exist on EVM" so consumers can't accidentally branch on
|
|
403
|
+
* `capabilities.signIn` and call a method that EVM adapters don't
|
|
404
|
+
* implement.
|
|
405
|
+
*
|
|
406
|
+
* Runtime gating via `capabilities` still matters for the methods that
|
|
407
|
+
* are OPTIONAL within a platform (a Solana wallet might or might not
|
|
408
|
+
* advertise `solana:signTransaction`). Capabilities narrow "wallet
|
|
409
|
+
* supports this feature"; the discriminated union narrows "this
|
|
410
|
+
* platform has this concept at all".
|
|
411
|
+
*/
|
|
412
|
+
type WalletAdapter = EvmAdapter | SvmAdapter | SuiAdapter | BitcoinAdapter | PolkadotAdapter;
|
|
413
|
+
type ConnectedWallet = {
|
|
414
|
+
/** Currently-active account on this wallet. */
|
|
415
|
+
account: Account;
|
|
416
|
+
/** All accounts known on this wallet at the time of connect/refresh.
|
|
417
|
+
* Always contains at least `account`. Populated from `getAccounts()`
|
|
418
|
+
* if the connector implements it; otherwise `[account]`. */
|
|
419
|
+
accounts: Array<Account>;
|
|
420
|
+
connector: WalletAdapter;
|
|
421
|
+
};
|
|
422
|
+
//#endregion
|
|
423
|
+
//#region src/types/discoverer.d.ts
|
|
424
|
+
/**
|
|
425
|
+
* Self-describing discovery descriptor for a single chain platform.
|
|
426
|
+
*
|
|
427
|
+
* Each platform package (`@usebutr/evm`, `@usebutr/svm`, `@usebutr/sui`,
|
|
428
|
+
* `@usebutr/bitcoin`) exports one of these. The aggregator package
|
|
429
|
+
* (`@usebutr/wallets`) composes them into `autoDiscovery()` without
|
|
430
|
+
* needing to know per-platform defaults; the descriptor owns them.
|
|
431
|
+
*
|
|
432
|
+
* Adding a new chain platform means writing a `PlatformDiscoverer`
|
|
433
|
+
* inside the new package and adding one import to the aggregator's
|
|
434
|
+
* registry, which is keyed by `ChainPlatform`. The aggregator's logic
|
|
435
|
+
* doesn't need to grow.
|
|
436
|
+
*
|
|
437
|
+
* Two parts:
|
|
438
|
+
* - `subscribe`: the primary discovery channel (EIP-6963 / Wallet
|
|
439
|
+
* Standard / etc).
|
|
440
|
+
* - `fallback`: optional. The legacy-injected channel that should
|
|
441
|
+
* only emit if the primary channel hasn't produced an adapter for
|
|
442
|
+
* the same browser session by the settle deadline. `@usebutr/evm`
|
|
443
|
+
* has one (window.ethereum); `@usebutr/bitcoin` has one
|
|
444
|
+
* (window.unisat / sats-connect / window.btc); SVM and Sui don't.
|
|
445
|
+
*/
|
|
446
|
+
type PlatformDiscoverer = {
|
|
447
|
+
/**
|
|
448
|
+
* Optional legacy-injected fallback. Subscribes only when consumers
|
|
449
|
+
* haven't disabled it. The hook receives `hasAnyPrimaryAdapter` so
|
|
450
|
+
* the fallback can defer to standards-based discovery when an adapter
|
|
451
|
+
* for the same wallet has already announced through the primary path.
|
|
452
|
+
*/
|
|
453
|
+
fallback?: {
|
|
454
|
+
subscribe: (onAdapter: (adapter: WalletAdapter) => void, opts: {
|
|
455
|
+
hasAnyPrimaryAdapter: () => boolean;
|
|
456
|
+
}) => () => void;
|
|
457
|
+
};
|
|
458
|
+
/** Primary discovery subscription. */
|
|
459
|
+
subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void;
|
|
460
|
+
};
|
|
461
|
+
//#endregion
|
|
462
|
+
//#region src/types/errors.d.ts
|
|
463
|
+
/**
|
|
464
|
+
* Tagged union of normalised connection errors.
|
|
465
|
+
*
|
|
466
|
+
* butr maps thrown values from connectors (which vary across wallet SDKs:
|
|
467
|
+
* MetaMask uses EIP-1193 codes, Phantom throws stringly-typed errors,
|
|
468
|
+
* embedded SDKs throw their own classes) into a small set of UX-meaningful
|
|
469
|
+
* variants. Consumers branch on `kind` instead of regexing message strings.
|
|
470
|
+
*
|
|
471
|
+
* `message` is always present and human-readable. `cause` preserves the
|
|
472
|
+
* original thrown value so callers can inspect raw connector errors when
|
|
473
|
+
* the variant is `Unknown`.
|
|
474
|
+
*/
|
|
475
|
+
type ConnectionError = {
|
|
476
|
+
kind: "UserRejected";
|
|
477
|
+
message: string;
|
|
478
|
+
} | {
|
|
479
|
+
kind: "RequestPending";
|
|
480
|
+
message: string;
|
|
481
|
+
} | {
|
|
482
|
+
kind: "WalletLocked";
|
|
483
|
+
message: string;
|
|
484
|
+
} | {
|
|
485
|
+
actualChain?: string;
|
|
486
|
+
expectedChain?: string;
|
|
487
|
+
kind: "ChainMismatch";
|
|
488
|
+
message: string;
|
|
489
|
+
} | {
|
|
490
|
+
kind: "NotConnected";
|
|
491
|
+
message: string;
|
|
492
|
+
} | {
|
|
493
|
+
kind: "Timeout";
|
|
494
|
+
message: string;
|
|
495
|
+
} | {
|
|
496
|
+
cause?: unknown;
|
|
497
|
+
kind: "Unknown";
|
|
498
|
+
message: string;
|
|
499
|
+
};
|
|
500
|
+
type ConnectionErrorKind = ConnectionError["kind"];
|
|
501
|
+
/**
|
|
502
|
+
* Normalise a thrown value into a `ConnectionError`.
|
|
488
503
|
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
504
|
+
* Recognises:
|
|
505
|
+
* - butr's own `Error("Connection timeout")` (from the 90s connect timeout)
|
|
506
|
+
* - butr's own `Error("Failed to get account")` (from the connect flow)
|
|
507
|
+
* - EIP-1193 numeric `code` properties (`4001` → UserRejected,
|
|
508
|
+
* `-32002` → RequestPending, `4100`/`4900`/`4901` → NotConnected:
|
|
509
|
+
* unauthorized / disconnected from all-or-one chains)
|
|
510
|
+
* - common message substrings: "user rejected" / "user denied",
|
|
511
|
+
* "locked", "chain", etc.
|
|
512
|
+
* - anything else → `Unknown` with `cause` set to the original value.
|
|
494
513
|
*/
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
type
|
|
499
|
-
|
|
500
|
-
|
|
514
|
+
declare const mapConnectionError: (raw: unknown) => ConnectionError;
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/storage/persistence.d.ts
|
|
517
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
518
|
+
/** Low-level key/value driver. Sync on web (localStorage, MMKV),
|
|
519
|
+
* async on React Native (AsyncStorage). */
|
|
520
|
+
type StorageDriver = {
|
|
521
|
+
getItem: (key: string) => MaybePromise<string | null>;
|
|
522
|
+
removeItem: (key: string) => MaybePromise<void>;
|
|
523
|
+
setItem: (key: string, value: string) => MaybePromise<void>;
|
|
524
|
+
};
|
|
525
|
+
type StoredPoolEntry = {
|
|
501
526
|
account: Account;
|
|
502
|
-
/** All accounts
|
|
503
|
-
*
|
|
504
|
-
* if the connector implements it; otherwise `[account]`. */
|
|
527
|
+
/** All known accounts on the wallet at last persist. Always contains
|
|
528
|
+
* `account`. */
|
|
505
529
|
accounts: Array<Account>;
|
|
506
|
-
connector: WalletAdapter;
|
|
507
|
-
};
|
|
508
|
-
type ConnectorMeta = {
|
|
509
|
-
/** Optional. Sync probe that reports whether the wallet is currently
|
|
510
|
-
* available. Defaults to `"installed"` when omitted. Consumers call
|
|
511
|
-
* this at render time to gate the "Connect" button. */
|
|
512
|
-
availability?: () => WalletAvailability;
|
|
513
530
|
chainPlatform: ChainPlatform;
|
|
514
|
-
|
|
531
|
+
connectorId: string;
|
|
532
|
+
/** Wallet icon URL or data-URI captured at persist time. Lets the
|
|
533
|
+
* shadow adapter render the same icon the live adapter will when
|
|
534
|
+
* the store is seeded from a snapshot. Omitted when the adapter
|
|
535
|
+
* itself has no icon. */
|
|
515
536
|
icon?: string;
|
|
516
|
-
|
|
537
|
+
/** Human-facing wallet name (e.g. "MetaMask") captured at persist
|
|
538
|
+
* time. Required so the shadow adapter renders the same identity
|
|
539
|
+
* the live adapter will; no "metamask" → "MetaMask" swap at the
|
|
540
|
+
* hydration boundary. */
|
|
517
541
|
name: string;
|
|
518
|
-
/** Optional. Where to send users who don't have this wallet (download
|
|
519
|
-
* page, app store link, etc.). */
|
|
520
|
-
url?: string;
|
|
521
542
|
};
|
|
543
|
+
type StoredPoolRecord = Partial<Record<string, StoredPoolEntry>>;
|
|
544
|
+
type StoredSelectionRecord = Partial<Record<ChainPlatform, string>>;
|
|
545
|
+
type WalletPersistence = {
|
|
546
|
+
clearAll: () => Promise<void>;
|
|
547
|
+
clearPool: () => Promise<void>;
|
|
548
|
+
getActiveConnectorId: () => Promise<string | null>;
|
|
549
|
+
getPool: () => Promise<StoredPoolRecord>;
|
|
550
|
+
getSelection: () => Promise<StoredSelectionRecord>;
|
|
551
|
+
isUserDisconnected: () => Promise<boolean>;
|
|
552
|
+
markUserDisconnected: (value: boolean) => Promise<void>;
|
|
553
|
+
removePoolEntry: (connectorId: string) => Promise<void>;
|
|
554
|
+
setActiveConnectorId: (connectorId: string | null) => Promise<void>;
|
|
555
|
+
setPool: (pool: Map<string, ConnectedWallet>) => Promise<void>;
|
|
556
|
+
setSelection: (selection: Map<ChainPlatform, string>) => Promise<void>;
|
|
557
|
+
};
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region src/storage/snapshot.d.ts
|
|
560
|
+
/**
|
|
561
|
+
* Server-safe view of a butr-persisted session; everything you can
|
|
562
|
+
* know about a user's connected wallets from the cookie payload alone,
|
|
563
|
+
* without instantiating a `Connector`.
|
|
564
|
+
*
|
|
565
|
+
* Notably absent: the `Connector` instance. A wallet extension exists
|
|
566
|
+
* only in the browser, so a server render can know *which* wallet was
|
|
567
|
+
* connected and *what address* it held, but cannot dispatch
|
|
568
|
+
* `signMessage`/`sendTransaction` on it. Splitting display from action
|
|
569
|
+
* along this seam keeps the impossibility expressed in the types
|
|
570
|
+
* rather than hidden inside a runtime check.
|
|
571
|
+
*/
|
|
572
|
+
type WalletSnapshot = {
|
|
573
|
+
activeConnectorId: string | null;
|
|
574
|
+
pool: StoredPoolRecord;
|
|
575
|
+
selection: StoredSelectionRecord;
|
|
576
|
+
};
|
|
577
|
+
type CookieSource = Iterable<{
|
|
578
|
+
name: string;
|
|
579
|
+
value: string;
|
|
580
|
+
}> | Iterable<[string, string]> | Readonly<Record<string, string>>;
|
|
581
|
+
type SnapshotOptions = {
|
|
582
|
+
/**
|
|
583
|
+
* Same prefix passed to `WalletManagerProvider` / `WalletStorage`.
|
|
584
|
+
* Defaults to `"butr"` to match the library default.
|
|
585
|
+
*/
|
|
586
|
+
keyPrefix?: string;
|
|
587
|
+
};
|
|
588
|
+
declare const EMPTY_SNAPSHOT: WalletSnapshot;
|
|
589
|
+
/**
|
|
590
|
+
* Parse a cookie source into a server-safe `WalletSnapshot`.
|
|
591
|
+
*
|
|
592
|
+
* Pure, sync, no `document`, no React; runnable in any environment
|
|
593
|
+
* (Server Component, route handler, edge middleware, even client
|
|
594
|
+
* code). Pair with `createCookieStorageDriver({ initialCookies })`
|
|
595
|
+
* and `<WalletManagerProvider initialSnapshot={…} />` to render a
|
|
596
|
+
* connected shell server-side without a hydration flash.
|
|
597
|
+
*
|
|
598
|
+
* **Stale-snapshot semantics.** The snapshot reflects whatever the
|
|
599
|
+
* browser most recently persisted. If the user has since uninstalled
|
|
600
|
+
* the wallet, switched accounts, or disconnected in another tab, the
|
|
601
|
+
* client-side hydration will reconcile reality and the live store
|
|
602
|
+
* will diverge from the snapshot. Treat the snapshot as an
|
|
603
|
+
* *optimistic* shell; accurate enough to avoid a paint flicker,
|
|
604
|
+
* authoritative only after `useIsHydrated()` is true.
|
|
605
|
+
*
|
|
606
|
+
* **Inputs.** Accepts the three shapes Next.js / Express / Hono /
|
|
607
|
+
* generic-Node cookie code naturally produces:
|
|
608
|
+
* - A plain object: `{ "butr-pool": "{...}", … }`
|
|
609
|
+
* - An array of `{ name, value }` (Next.js' `cookies().getAll()`)
|
|
610
|
+
* - An iterable of `[name, value]` tuples
|
|
611
|
+
*
|
|
612
|
+
* Malformed entries are dropped with a `logWarn` (same policy as
|
|
613
|
+
* `WalletStorage.getPool`); a cross-tab corruption shouldn't crash
|
|
614
|
+
* the server render.
|
|
615
|
+
*/
|
|
616
|
+
declare const readWalletSnapshot: (source: CookieSource, options?: SnapshotOptions) => WalletSnapshot;
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region src/types/manager.d.ts
|
|
522
619
|
/**
|
|
523
620
|
* Outcome of butr's mount-time hydration pass. Passed to
|
|
524
621
|
* `WalletManagerConfig.onHydrated`. Three buckets:
|
|
@@ -616,86 +713,6 @@ type WalletManagerConfig = {
|
|
|
616
713
|
storageKeyPrefix?: string;
|
|
617
714
|
};
|
|
618
715
|
//#endregion
|
|
619
|
-
//#region src/types/chains-by-platform.d.ts
|
|
620
|
-
/**
|
|
621
|
-
* Map of every chain platform to the list of chains the consumer wants
|
|
622
|
-
* to expose to its UI (chain switcher, picker, etc).
|
|
623
|
-
*
|
|
624
|
-
* The platform key set is fixed by `ChainPlatform`. The value is the
|
|
625
|
-
* chain list; empty when the consumer doesn't want to support that
|
|
626
|
-
* platform's chains in this view. This is the only type that callers
|
|
627
|
-
* write down; the values come from per-platform packages
|
|
628
|
-
* (`EVM_CHAINS_LIST`, `SVM_CHAINS_LIST`, etc).
|
|
629
|
-
*/
|
|
630
|
-
type ChainsByPlatform = Readonly<Record<ChainPlatform, ReadonlyArray<ChainBase>>>;
|
|
631
|
-
/**
|
|
632
|
-
* Build a fully-populated `ChainsByPlatform` from a partial. Platforms
|
|
633
|
-
* the consumer doesn't specify default to an empty list.
|
|
634
|
-
*
|
|
635
|
-
* Use this in apps that target one or two chain platforms; importing
|
|
636
|
-
* only those packages keeps unused chain registries out of the bundle.
|
|
637
|
-
* Apps that want every chain reach for `CHAINS_BY_PLATFORM` from
|
|
638
|
-
* `@usebutr/wallets` instead.
|
|
639
|
-
*
|
|
640
|
-
* @example
|
|
641
|
-
* // EVM-only app: Solana/Sui/Bitcoin tables never enter the bundle
|
|
642
|
-
* import { EVM_CHAINS_LIST } from "@usebutr/evm";
|
|
643
|
-
* import { buildChainsByPlatform } from "@usebutr/core";
|
|
644
|
-
*
|
|
645
|
-
* const chains = buildChainsByPlatform({ evm: EVM_CHAINS_LIST });
|
|
646
|
-
*
|
|
647
|
-
* @example
|
|
648
|
-
* // Multi-chain app: pull from each package the app actually uses
|
|
649
|
-
* import { EVM_CHAINS_LIST } from "@usebutr/evm";
|
|
650
|
-
* import { SVM_CHAINS_LIST } from "@usebutr/svm";
|
|
651
|
-
*
|
|
652
|
-
* const chains = buildChainsByPlatform({
|
|
653
|
-
* evm: EVM_CHAINS_LIST,
|
|
654
|
-
* svm: SVM_CHAINS_LIST,
|
|
655
|
-
* });
|
|
656
|
-
*/
|
|
657
|
-
declare const buildChainsByPlatform: (partial: Partial<ChainsByPlatform>) => ChainsByPlatform;
|
|
658
|
-
//#endregion
|
|
659
|
-
//#region src/types/discoverer.d.ts
|
|
660
|
-
/**
|
|
661
|
-
* Self-describing discovery descriptor for a single chain platform.
|
|
662
|
-
*
|
|
663
|
-
* Each platform package (`@usebutr/evm`, `@usebutr/svm`, `@usebutr/sui`,
|
|
664
|
-
* `@usebutr/bitcoin`) exports one of these. The aggregator package
|
|
665
|
-
* (`@usebutr/wallets`) composes them into `autoDiscovery()` without
|
|
666
|
-
* needing to know per-platform defaults; the descriptor owns them.
|
|
667
|
-
*
|
|
668
|
-
* Adding a new chain platform means writing a `PlatformDiscoverer`
|
|
669
|
-
* inside the new package and adding one import to the aggregator's
|
|
670
|
-
* registry. The aggregator's logic doesn't need to grow.
|
|
671
|
-
*
|
|
672
|
-
* Two parts:
|
|
673
|
-
* - `subscribe`: the primary discovery channel (EIP-6963 / Wallet
|
|
674
|
-
* Standard / etc).
|
|
675
|
-
* - `fallback`: optional. The legacy-injected channel that should
|
|
676
|
-
* only emit if the primary channel hasn't produced an adapter for
|
|
677
|
-
* the same browser session by the settle deadline. `@usebutr/evm`
|
|
678
|
-
* has one (window.ethereum); `@usebutr/bitcoin` has one
|
|
679
|
-
* (window.unisat / sats-connect / window.btc); SVM and Sui don't.
|
|
680
|
-
*/
|
|
681
|
-
type PlatformDiscoverer = {
|
|
682
|
-
/**
|
|
683
|
-
* Optional legacy-injected fallback. Subscribes only when consumers
|
|
684
|
-
* haven't disabled it. The hook receives `hasAnyPrimaryAdapter` so
|
|
685
|
-
* the fallback can defer to standards-based discovery when an adapter
|
|
686
|
-
* for the same wallet has already announced through the primary path.
|
|
687
|
-
*/
|
|
688
|
-
fallback?: {
|
|
689
|
-
subscribe: (onAdapter: (adapter: WalletAdapter) => void, opts: {
|
|
690
|
-
hasAnyPrimaryAdapter: () => boolean;
|
|
691
|
-
}) => () => void;
|
|
692
|
-
};
|
|
693
|
-
/** Stable platform identifier. Used by the aggregator for keying. */
|
|
694
|
-
platform: ChainPlatform;
|
|
695
|
-
/** Primary discovery subscription. */
|
|
696
|
-
subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void;
|
|
697
|
-
};
|
|
698
|
-
//#endregion
|
|
699
716
|
//#region src/types/signer.d.ts
|
|
700
717
|
/**
|
|
701
718
|
* Per-platform signer type registry.
|
|
@@ -1013,6 +1030,124 @@ type WalletStore = ReturnType<typeof createWalletStore>;
|
|
|
1013
1030
|
type WalletStoreState = ExtractState<WalletStore>;
|
|
1014
1031
|
declare const createWalletStore: (config: WalletManagerConfig) => import("zustand/vanilla").StoreApi<State & RuntimeMembers>;
|
|
1015
1032
|
//#endregion
|
|
1033
|
+
//#region src/group-by-platform.d.ts
|
|
1034
|
+
/**
|
|
1035
|
+
* Bucket a flat list into per-platform groups.
|
|
1036
|
+
*
|
|
1037
|
+
* A multi-chain wallet announces one adapter per platform it speaks, so
|
|
1038
|
+
* discovery hands back a flat list where a single brand appears several
|
|
1039
|
+
* times. Any app targeting more than one chain has to bucket that list
|
|
1040
|
+
* before rendering, and hand-rolled versions drift: butr's own demos
|
|
1041
|
+
* carried a copy that reimplemented `CHAIN_PLATFORMS` as a local
|
|
1042
|
+
* ordering constant.
|
|
1043
|
+
*
|
|
1044
|
+
* Keys are inserted in `CHAIN_PLATFORMS` order and platforms with no
|
|
1045
|
+
* members are omitted, so the result is directly iterable for rendering
|
|
1046
|
+
* (`[...groups]`) as well as addressable for lookup (`groups.get("evm")`)
|
|
1047
|
+
* without a further emptiness filter.
|
|
1048
|
+
*
|
|
1049
|
+
* `getPlatform` exists because the discriminant sits at a different depth
|
|
1050
|
+
* depending on the input: a discovered `WalletAdapter` carries
|
|
1051
|
+
* `chainPlatform` at the top level, a pool entry nests it under
|
|
1052
|
+
* `connector`. React consumers should reach for
|
|
1053
|
+
* `useDiscoveredWalletsByPlatform` / `useConnectedWalletsByPlatform`
|
|
1054
|
+
* instead, which bind the accessor for them.
|
|
1055
|
+
*/
|
|
1056
|
+
declare const groupByPlatform: <T>(items: ReadonlyArray<T>, getPlatform: (item: T) => ChainPlatform) => Map<ChainPlatform, Array<T>>;
|
|
1057
|
+
//#endregion
|
|
1058
|
+
//#region src/sign-in/sign-in-flow.d.ts
|
|
1059
|
+
/**
|
|
1060
|
+
* What the wallet produced, encoded for transport. Both byte fields are
|
|
1061
|
+
* base64 because that is what survives `JSON.stringify` intact; the raw
|
|
1062
|
+
* `Uint8Array`s are kept alongside for callers verifying in-process.
|
|
1063
|
+
*
|
|
1064
|
+
* Verify against `signedMessage`, not `message`. Solana Wallet Standard
|
|
1065
|
+
* wallets may prefix or re-encode what they sign, so the bytes that carry
|
|
1066
|
+
* the signature are the wallet's, not yours.
|
|
1067
|
+
*/
|
|
1068
|
+
type SignInResult = {
|
|
1069
|
+
account: Account;
|
|
1070
|
+
/** The message handed to the wallet. Absent on the SIWS path, where the
|
|
1071
|
+
* wallet composes the message itself. */
|
|
1072
|
+
message?: string;
|
|
1073
|
+
nonce: string;
|
|
1074
|
+
signature: Uint8Array;
|
|
1075
|
+
/** Base64 of `signature`. */
|
|
1076
|
+
signatureBase64: string;
|
|
1077
|
+
signedMessage: Uint8Array;
|
|
1078
|
+
/** Base64 of `signedMessage`; verify against this. */
|
|
1079
|
+
signedMessageBase64: string;
|
|
1080
|
+
wallet: ConnectedWallet;
|
|
1081
|
+
};
|
|
1082
|
+
type SignInMessageContext = {
|
|
1083
|
+
account: Account;
|
|
1084
|
+
nonce: string;
|
|
1085
|
+
wallet: ConnectedWallet;
|
|
1086
|
+
};
|
|
1087
|
+
type SignInFlowOptions = {
|
|
1088
|
+
/**
|
|
1089
|
+
* Compose the message to sign. Defaults to a plain
|
|
1090
|
+
* `<address> signs in. Nonce: <nonce>` line.
|
|
1091
|
+
*
|
|
1092
|
+
* butr deliberately does not ship a wire format here: a message a
|
|
1093
|
+
* server must parse is an authentication spec, and SIWE / SIWS already
|
|
1094
|
+
* fill that role. Pass the formatter your backend expects.
|
|
1095
|
+
*
|
|
1096
|
+
* Unused on the SIWS path, where the wallet composes the message from
|
|
1097
|
+
* the input fields.
|
|
1098
|
+
*/
|
|
1099
|
+
buildMessage?: (ctx: SignInMessageContext) => string;
|
|
1100
|
+
/** Fetch a single-use nonce from your backend. */
|
|
1101
|
+
getNonce: (ctx: {
|
|
1102
|
+
account: Account;
|
|
1103
|
+
wallet: ConnectedWallet;
|
|
1104
|
+
}) => Promise<string>;
|
|
1105
|
+
/**
|
|
1106
|
+
* Skip the Sign In With Solana path even on wallets that advertise it,
|
|
1107
|
+
* forcing every platform down the same `signMessage` route. Useful when
|
|
1108
|
+
* one backend verifier has to handle every chain identically.
|
|
1109
|
+
*/
|
|
1110
|
+
preferSignMessage?: boolean;
|
|
1111
|
+
/** Hand the signed result to your backend. Throw to fail the flow. */
|
|
1112
|
+
verify: (result: SignInResult) => Promise<void>;
|
|
1113
|
+
};
|
|
1114
|
+
/** Thrown before any wallet interaction when the wallet can't sign at
|
|
1115
|
+
* all. Distinct from a rejection: nothing was asked of the user, so UI
|
|
1116
|
+
* should say "this wallet can't sign in" rather than "you declined". */
|
|
1117
|
+
declare class SignInUnsupportedError extends Error {
|
|
1118
|
+
readonly connectorId: string;
|
|
1119
|
+
constructor(connectorId: string);
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Build a reusable sign-in flow: nonce, capability gate, signature,
|
|
1123
|
+
* encoding, verification.
|
|
1124
|
+
*
|
|
1125
|
+
* Every wallet-auth app writes this same sequence, and the parts that
|
|
1126
|
+
* are easy to get wrong are the ones that aren't about the app: which
|
|
1127
|
+
* capability flag gates the attempt, whether a Solana wallet should take
|
|
1128
|
+
* the SIWS path instead, and which bytes to base64 for the server (the
|
|
1129
|
+
* wallet's `signedMessage`, not the input). This owns those; you own the
|
|
1130
|
+
* nonce endpoint, the message format, and the verifier.
|
|
1131
|
+
*
|
|
1132
|
+
* ```ts
|
|
1133
|
+
* const { signIn } = createSignInFlow({
|
|
1134
|
+
* getNonce: () => fetch("/api/nonce").then((r) => r.text()),
|
|
1135
|
+
* verify: (result) =>
|
|
1136
|
+
* fetch("/api/verify", { body: JSON.stringify(result), method: "POST" }).then(() => {}),
|
|
1137
|
+
* });
|
|
1138
|
+
*
|
|
1139
|
+
* await signIn(wallet);
|
|
1140
|
+
* ```
|
|
1141
|
+
*
|
|
1142
|
+
* Solana wallets advertising `solana:signIn` take the SIWS path
|
|
1143
|
+
* automatically, so `result.signedMessage` holds the wallet-composed
|
|
1144
|
+
* SIWS statement and `result.message` is absent. Set
|
|
1145
|
+
* `preferSignMessage` to opt out.
|
|
1146
|
+
*/
|
|
1147
|
+
declare const createSignInFlow: (options: SignInFlowOptions) => {
|
|
1148
|
+
signIn: (wallet: ConnectedWallet, account?: Account) => Promise<SignInResult>;
|
|
1149
|
+
};
|
|
1150
|
+
//#endregion
|
|
1016
1151
|
//#region src/wallet-equal.d.ts
|
|
1017
1152
|
/**
|
|
1018
1153
|
* Two wallet snapshots are equivalent for selector purposes iff they
|
|
@@ -1052,6 +1187,11 @@ declare const logError: (...args: ReadonlyArray<unknown>) => void;
|
|
|
1052
1187
|
* icon as absent, so consumers get either a usable string or
|
|
1053
1188
|
* `undefined`: never a blank or malformed one. `undefined` passes
|
|
1054
1189
|
* through untouched.
|
|
1190
|
+
*
|
|
1191
|
+
* **Discovery already applies this.** Every adapter butr discovers has
|
|
1192
|
+
* had its icon sanitized at construction, so `Connector.icon` is safe to
|
|
1193
|
+
* render as-is. Reach for this helper only when building adapter or
|
|
1194
|
+
* `ConnectorMeta` metadata yourself from a source butr didn't produce.
|
|
1055
1195
|
*/
|
|
1056
1196
|
declare const sanitizeIcon: (icon: string | undefined) => string | undefined;
|
|
1057
1197
|
//#endregion
|
|
@@ -1089,6 +1229,15 @@ declare const hexToBytes: (hex: string) => Uint8Array;
|
|
|
1089
1229
|
declare const base64ToBytes: (b64: string) => Uint8Array;
|
|
1090
1230
|
/** Cross-platform `Uint8Array` → base64. */
|
|
1091
1231
|
declare const bytesToBase64: (bytes: Uint8Array) => string;
|
|
1232
|
+
/**
|
|
1233
|
+
* `Uint8Array` → base58 (Solana addresses and signatures). Leading zero
|
|
1234
|
+
* bytes are significant in base58 and survive the BigInt round-trip only
|
|
1235
|
+
* because they're re-prefixed as `1`s afterwards.
|
|
1236
|
+
*/
|
|
1237
|
+
declare const bytesToBase58: (bytes: Uint8Array) => string;
|
|
1238
|
+
/** Decode base58 into raw bytes. Throws on characters outside the alphabet
|
|
1239
|
+
* rather than silently dropping them. */
|
|
1240
|
+
declare const base58ToBytes: (input: string) => Uint8Array;
|
|
1092
1241
|
//#endregion
|
|
1093
|
-
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
|
|
1242
|
+
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 };
|
|
1094
1243
|
//# sourceMappingURL=index.d.ts.map
|