@horus-wallet/sdk-react 0.1.0-beta.2 → 0.3.0-beta.1

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
@@ -3,12 +3,18 @@ import { ReactNode, ButtonHTMLAttributes } from 'react';
3
3
  /**
4
4
  * Public types for `@horus-wallet/sdk-react`.
5
5
  *
6
- * The React package targets the partner's BACKEND-PROXY pattern:
7
- * partner runs a thin proxy on their server (which holds the
8
- * `hk_sk_*` secret) and the React hooks call that proxy. Each hook's
9
- * `apiBase` defaults to `/api/horus` partners can override via
10
- * provider config. v0.2 will add a publishable-key tier that lets
11
- * the React SDK call Horus directly without the partner-backend hop.
6
+ * The React package calls the Horus API directly from the browser
7
+ * using the partner's PUBLISHABLE app identifier (`app_*`). No
8
+ * partner backend is required. Auth is carried as `x-horus-key:
9
+ * <appId>` + `Authorization: Bearer <idToken>` headers on every
10
+ * request; the backend's per-app `allowedOrigins` allow-list is the
11
+ * security boundary, so partners MUST register the domains they'll
12
+ * serve the SDK from before shipping.
13
+ *
14
+ * Partners that need a server-side path (managed signing services,
15
+ * cron jobs, backoffice tools) use the sibling `@horus-wallet/sdk`
16
+ * package with an `hk_sk_*` secret — the publishable React surface
17
+ * intentionally has no awareness of secret keys.
12
18
  */
13
19
  /** Tokens returned from any successful auth flow. */
14
20
  interface AuthTokens {
@@ -24,6 +30,16 @@ interface AuthTokens {
24
30
  photoUrl?: string;
25
31
  providerId?: string;
26
32
  }
33
+ /**
34
+ * Network type — Horus splits each chain into mainnet and testnet
35
+ * surfaces so partners don't mix them up at the API level.
36
+ */
37
+ type NetworkType = 'MAINNET' | 'TESTNET';
38
+ /** A chain selector — `(network, networkType)` pair. */
39
+ interface ChainSelector {
40
+ network: string;
41
+ networkType: NetworkType;
42
+ }
27
43
  /** Subset of the tokens that's safe to expose to consuming hooks. */
28
44
  interface User {
29
45
  uid: string;
@@ -56,25 +72,22 @@ interface HorusBranding {
56
72
  }
57
73
  /** Configuration for `<HorusProvider>`. */
58
74
  interface HorusProviderConfig {
59
- /** Server-assigned id of the partner's app. Used by the popup for white-label policy lookup. */
75
+ /**
76
+ * Partner's publishable app identifier (`app_*`). Created via the
77
+ * Horus dashboard or admin API. Sent as `x-horus-key` on every
78
+ * request to identify which app the call belongs to. The host page's
79
+ * Origin must be on the app's `allowedOrigins` allow-list — this is
80
+ * the load-bearing security boundary for the publishable path.
81
+ */
60
82
  appId: string;
61
83
  /**
62
- * URL prefix where the partner's backend exposes the Horus proxy
63
- * routes. Defaults to `/api/horus`. The proxy routes the SDK
64
- * expects:
65
- * - POST {apiBase}/auth/email/signin → tokens
66
- * - POST {apiBase}/auth/email/signup → tokens
67
- * - POST {apiBase}/auth/email-link/send → ok
68
- * - POST {apiBase}/auth/email-link/verify → tokens
69
- * - POST {apiBase}/auth/oauth → tokens (federate Google/Apple/etc.)
70
- * - POST {apiBase}/auth/refresh → tokens
71
- * - GET {apiBase}/wallets → wallets list
72
- * - GET {apiBase}/wallets/:network → wallet details
73
- * - POST {apiBase}/sign-message → signature
74
- * - POST {apiBase}/transfer/native → tx hash
75
- * - POST {apiBase}/transfer/token → tx hash
84
+ * Horus API base URL. Defaults to `https://api.horuswallet.com` (prod).
85
+ * Override for staging / dev:
76
86
  *
77
- * The README shows a reference backend implementation in <50 lines.
87
+ * <HorusProvider appId="app_xxx" apiBase="https://dev-api.horuswallet.com">
88
+ *
89
+ * Partners NEVER need to expose any path of their own — the SDK
90
+ * calls the Horus API directly.
78
91
  */
79
92
  apiBase?: string;
80
93
  /**
@@ -92,6 +105,35 @@ interface HorusProviderConfig {
92
105
  * Default true. Disable when you're handling expiry manually.
93
106
  */
94
107
  autoRefresh?: boolean;
108
+ /**
109
+ * Auto-create a wallet for the user on first sign-in if they don't
110
+ * already have one. Defaults to `{ network: 'EVM', networkType: 'MAINNET' }`
111
+ * to match Privy's behavior — new users land in your app with a
112
+ * wallet already provisioned, no extra round-trip required.
113
+ *
114
+ * Set to `false` to opt out (your app must call `useCreateWallet`
115
+ * explicitly to provision wallets).
116
+ *
117
+ * The provision is idempotent on the backend, runs once per user
118
+ * per session, and fails silently — a network blip will not block
119
+ * sign-in. Subsequent auto-provisions for the same user during the
120
+ * same session are skipped via a localStorage marker.
121
+ */
122
+ autoProvisionWallet?: false | {
123
+ network: string;
124
+ networkType: 'MAINNET' | 'TESTNET';
125
+ };
126
+ /**
127
+ * Initial active chain exposed via `useSwitchChain()` / `useChain()`.
128
+ * Defaults to `{ network: 'EVM', networkType: 'MAINNET' }`.
129
+ *
130
+ * Partners can read the current chain in their UI (e.g., to highlight
131
+ * the active network in a switcher) and pass it explicitly when
132
+ * calling `signMessage` / `sendTransaction` / etc. The active chain
133
+ * is a UI convenience — it is NOT implicitly used by the signing
134
+ * hooks unless the partner forwards it themselves.
135
+ */
136
+ defaultChain?: ChainSelector;
95
137
  }
96
138
 
97
139
  /**
@@ -185,8 +227,8 @@ declare function useHorusAuth(): UseHorusAuthResult;
185
227
  declare function useUser(): User | undefined;
186
228
 
187
229
  /**
188
- * `useWallets()` — fetches the signed-in user's wallets from the
189
- * partner's backend proxy. Refetches when auth state changes.
230
+ * `useWallets()` — fetches the signed-in user's wallets directly from
231
+ * the Horus API. Refetches when auth state changes.
190
232
  *
191
233
  * Mirror of Privy's `useWallets()` — partners coming from there get
192
234
  * a familiar `{ wallets, ready }` shape. Each wallet is a thin
@@ -212,6 +254,93 @@ interface UseWalletsResult {
212
254
  }
213
255
  declare function useWallets(): UseWalletsResult;
214
256
 
257
+ /**
258
+ * `useCreateWallet()` — explicit wallet provisioning.
259
+ *
260
+ * const { create, pending, error } = useCreateWallet();
261
+ * await create({ network: 'EVM', networkType: 'MAINNET' });
262
+ *
263
+ * The backend's `/createWallet` is idempotent on `(user, network family)`,
264
+ * so calling this twice for the same network returns the existing wallet
265
+ * rather than duplicating. After a successful call the provider's
266
+ * `walletsVersion` counter is bumped, which triggers every active
267
+ * `useWallets()` to re-fetch — partners don't need to wire a manual
268
+ * `.refresh()` call.
269
+ *
270
+ * For most apps, you'll prefer the provider's `autoProvisionWallet`
271
+ * config (auto-fires on first sign-in). Reach for this hook when you
272
+ * need to provision an *additional* chain after the auto-provision,
273
+ * or for apps that opted out of auto-provision.
274
+ */
275
+ interface CreateWalletInput {
276
+ /** Chain name — `'EVM' | 'BASE' | 'POLYGON' | 'BITCOIN' | 'ICP' | …` */
277
+ network: string;
278
+ /** `'MAINNET' | 'TESTNET'` */
279
+ networkType: 'MAINNET' | 'TESTNET';
280
+ /**
281
+ * Optional password — encrypts the wallet's private key at rest.
282
+ * Required for spending operations later if set. Recoverable only
283
+ * by the user.
284
+ */
285
+ password?: string;
286
+ }
287
+ interface CreateWalletResult {
288
+ /** Server response: nested by-network wallet record + a status message. */
289
+ wallets: unknown;
290
+ message: string;
291
+ }
292
+ interface UseCreateWalletResult {
293
+ /** Provision a new wallet. Resolves with the server response. */
294
+ create: (input: CreateWalletInput) => Promise<CreateWalletResult>;
295
+ /** True while a `create()` call is in flight. */
296
+ pending: boolean;
297
+ /** Last error, if any. Cleared on the next attempt. */
298
+ error: Error | undefined;
299
+ }
300
+ declare function useCreateWallet(): UseCreateWalletResult;
301
+
302
+ /**
303
+ * `useSwitchChain()` — read and update the active chain selector.
304
+ *
305
+ * const { chain, switchChain } = useSwitchChain();
306
+ * <button onClick={() => switchChain({ network: 'BASE', networkType: 'MAINNET' })}>
307
+ * Use Base
308
+ * </button>
309
+ *
310
+ * Mirror of Privy's `useSwitchChain`. Pure UI state — partners forward
311
+ * `chain` explicitly into `signMessage` / `sendTransaction` / etc.
312
+ * calls. We deliberately don't auto-thread it through the signing
313
+ * hooks because an implicit "last switched chain" easily becomes a
314
+ * footgun (user switches to mainnet, dapp signs a testnet tx, etc).
315
+ *
316
+ * `supportedChains` is the static union of chain names the Horus
317
+ * backend supports today, listed in the order partners are most
318
+ * likely to want to surface them. Re-exported here so partners can
319
+ * render a chain picker without hardcoding the list themselves.
320
+ */
321
+
322
+ /**
323
+ * Chain names the Horus backend supports today. Frozen tuple — typed
324
+ * as readonly so a partner who pins on a specific value gets a TS
325
+ * error when we drop a chain rather than a silent runtime miss.
326
+ */
327
+ declare const SUPPORTED_CHAINS: readonly ["EVM", "ETHEREUM", "BASE", "POLYGON", "BSC", "ARBITRUM", "OPTIMISM", "BITCOIN", "ICP", "CASPER", "AETERNITY"];
328
+ type SupportedChain = (typeof SUPPORTED_CHAINS)[number];
329
+ interface UseSwitchChainResult {
330
+ /** Currently-active chain selector. */
331
+ chain: ChainSelector;
332
+ /** Set the active chain. Triggers a re-render in every consumer. */
333
+ switchChain: (chain: ChainSelector) => void;
334
+ /** Static list of chain names this SDK supports. */
335
+ supportedChains: readonly SupportedChain[];
336
+ }
337
+ declare function useSwitchChain(): UseSwitchChainResult;
338
+ /**
339
+ * Alias for partners who prefer the "read-only" framing. Returns
340
+ * exactly the same shape as `useSwitchChain()`.
341
+ */
342
+ declare const useChain: typeof useSwitchChain;
343
+
215
344
  /**
216
345
  * `useSignMessage()` — signs a UTF-8 message with the user's wallet.
217
346
  * EVM applies EIP-191 prefix server-side; Casper raw-signs; other
@@ -236,9 +365,113 @@ interface UseSignMessageResult {
236
365
  declare function useSignMessage(): UseSignMessageResult;
237
366
 
238
367
  /**
239
- * `useTransfer()` — native + token transfer helpers backed by the
240
- * partner's proxy. Each variant returns `{ pending, error }` so
241
- * forms can drive disabled-state without local plumbing.
368
+ * `useSignTypedData()` — EIP-712 typed-data signing.
369
+ *
370
+ * const { signTypedData, pending, error } = useSignTypedData();
371
+ *
372
+ * const sig = await signTypedData({
373
+ * network: 'BASE',
374
+ * networkType: 'MAINNET',
375
+ * typedData: {
376
+ * domain: { name: 'Acme', version: '1', chainId: 8453, verifyingContract: '0x…' },
377
+ * types: {
378
+ * Order: [
379
+ * { name: 'buyer', type: 'address' },
380
+ * { name: 'amount', type: 'uint256' },
381
+ * ],
382
+ * },
383
+ * primaryType: 'Order',
384
+ * message: { buyer: '0x…', amount: '1000000' },
385
+ * },
386
+ * });
387
+ *
388
+ * EVM-only. Non-EVM networks return a 400 (`HorusHttpError`). The
389
+ * `typedData` argument is forwarded verbatim — partners can pass the
390
+ * exact payload they'd hand to `eth_signTypedData_v4`, including a
391
+ * `EIP712Domain` entry in `types` (the backend strips it before
392
+ * hashing, matching MetaMask's behavior).
393
+ */
394
+
395
+ interface Eip712TypedData {
396
+ domain: Record<string, unknown>;
397
+ types: Record<string, Array<{
398
+ name: string;
399
+ type: string;
400
+ }>>;
401
+ /** Optional — the backend derives the primary type from the structure. */
402
+ primaryType?: string;
403
+ message: Record<string, unknown>;
404
+ }
405
+ interface SignTypedDataInput {
406
+ network: string;
407
+ networkType: NetworkType;
408
+ typedData: Eip712TypedData;
409
+ walletIndex?: number;
410
+ /** Wallet password if the user has one. */
411
+ password?: string;
412
+ }
413
+ interface UseSignTypedDataResult {
414
+ signTypedData: (input: SignTypedDataInput) => Promise<string>;
415
+ pending: boolean;
416
+ error: Error | undefined;
417
+ }
418
+ declare function useSignTypedData(): UseSignTypedDataResult;
419
+
420
+ /**
421
+ * `useSendTransaction()` — generic EVM transaction sign + submit.
422
+ *
423
+ * const { sendTransaction, pending, error } = useSendTransaction();
424
+ *
425
+ * const { hash } = await sendTransaction({
426
+ * network: 'BASE',
427
+ * networkType: 'MAINNET',
428
+ * to: '0xContractAddress',
429
+ * data: '0xabcdef…', // encoded contract call
430
+ * value: '0',
431
+ * });
432
+ *
433
+ * Use this when `useTransfer().native()` / `.token()` aren't enough —
434
+ * e.g., calling an arbitrary contract method, deploying a contract,
435
+ * setting an allowance, etc. The backend handles nonce + gas; partners
436
+ * only fill in what they want to override.
437
+ *
438
+ * Returns the broadcast tx hash. Confirmation polling is the caller's
439
+ * job (subscribe via your own RPC, or poll a block explorer).
440
+ *
441
+ * EVM-only. Non-EVM chains use their own submit primitives (`/withdraw`
442
+ * for ICP / Casper, etc.) which today aren't exposed through this hook.
443
+ */
444
+
445
+ interface SendTransactionInput {
446
+ network: string;
447
+ networkType: NetworkType;
448
+ /** Recipient or contract address (`0x` + 40 hex chars). */
449
+ to: string;
450
+ /**
451
+ * Value in wei. Pass as a string (or bigint) — JS numbers lose bits
452
+ * above 2^53. Default: 0.
453
+ */
454
+ value?: string | bigint;
455
+ /** ABI-encoded calldata for contract calls. */
456
+ data?: string;
457
+ /** Explicit gas limit override. Omit to let the RPC estimate. */
458
+ gasLimit?: string | bigint;
459
+ walletIndex?: number;
460
+ password?: string;
461
+ }
462
+ interface UseSendTransactionResult {
463
+ sendTransaction: (input: SendTransactionInput) => Promise<{
464
+ hash: string;
465
+ }>;
466
+ pending: boolean;
467
+ error: Error | undefined;
468
+ }
469
+ declare function useSendTransaction(): UseSendTransactionResult;
470
+
471
+ /**
472
+ * `useTransfer()` — native + token transfer helpers. Each variant
473
+ * returns `{ pending, error }` so forms can drive disabled-state
474
+ * without local plumbing. Calls Horus directly.
242
475
  */
243
476
  interface NativeTransferInput {
244
477
  network: string;
@@ -299,9 +532,194 @@ interface HorusLoginButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElem
299
532
  declare function HorusLoginButton({ flow, phone, onAuthenticated, onError, children, disabled, style, ...rest }: HorusLoginButtonProps): JSX.Element;
300
533
 
301
534
  /**
302
- * Fetch wrapper for calls to the partner's backend proxy. Handles
303
- * the auto-refresh-on-401 dance + JSON serialization + a typed
304
- * error so consuming hooks can branch on `err.status`.
535
+ * Shared helpers for building auth-page URLs. Reused by the popup
536
+ * (`openPopupFlow` in `useHorusAuth`) and the inline iframe modal
537
+ * (`<HorusAuthModal>`). Centralizing here keeps the wire shape
538
+ * exactly aligned across both — partners get identical behavior
539
+ * regardless of which presentation surface they pick.
540
+ *
541
+ * Not exported to partners; internal-only.
542
+ */
543
+
544
+ type AuthFlow = 'google' | 'email_link' | 'phone';
545
+
546
+ /**
547
+ * `<HorusAuthModal>` — inline auth flow.
548
+ *
549
+ * Renders an iframe pointed at `auth.horuswallet.com` with a backdrop
550
+ * + focus-trap shell. Partners use this as a drop-in alternative to
551
+ * `<HorusLoginButton>`'s popup model — matches Privy / Magic's inline
552
+ * modal UX.
553
+ *
554
+ * const [open, setOpen] = useState(false);
555
+ *
556
+ * return (
557
+ * <>
558
+ * <button onClick={() => setOpen(true)}>Sign in</button>
559
+ * <HorusAuthModal
560
+ * flow="google"
561
+ * open={open}
562
+ * onClose={() => setOpen(false)}
563
+ * onSuccess={(user) => { setOpen(false); console.log(user); }}
564
+ * />
565
+ * </>
566
+ * );
567
+ *
568
+ * The component is controlled (partner owns `open`). On `success`, the
569
+ * provider's `signIn` is invoked under the hood — partners don't need
570
+ * to call it themselves — and the partner-supplied `onSuccess` fires
571
+ * with the resulting user. `onClose` is called for both user cancel
572
+ * and Escape-key dismissal.
573
+ *
574
+ * Requires the Horus auth page to be deployed with iframe-friendly
575
+ * CSP headers (frame-ancestors permitting the partner origin). The
576
+ * postMessage protocol is identical to the popup flow.
577
+ */
578
+
579
+ interface HorusAuthModalProps {
580
+ /** Which sign-in flow to render in the iframe. */
581
+ flow: AuthFlow;
582
+ /** Optional phone-flow pre-fill. */
583
+ phone?: string;
584
+ /** Controlled open state — partner toggles to show/hide the modal. */
585
+ open: boolean;
586
+ /** Called when the modal is dismissed (user cancel, Escape, success). */
587
+ onClose: () => void;
588
+ /** Called after a successful sign-in — receives the new user object. */
589
+ onSuccess?: (user: User) => void;
590
+ /** Called when the iframe surfaces an error. */
591
+ onError?: (err: Error) => void;
592
+ /**
593
+ * Inline style override for the dialog. Default is a centered
594
+ * 480×640 panel with rounded corners — works for the canonical
595
+ * Horus auth-page; partners with strong design systems can override.
596
+ */
597
+ dialogStyle?: React.CSSProperties;
598
+ /** Style override for the backdrop. */
599
+ backdropStyle?: React.CSSProperties;
600
+ }
601
+ declare function HorusAuthModal(props: HorusAuthModalProps): JSX.Element | null;
602
+
603
+ /**
604
+ * `<HorusRevealModal>` — iframe-based private-key reveal surface.
605
+ *
606
+ * const { reveal } = useExportWallet();
607
+ * const [revealToken, setRevealToken] = useState<string | null>(null);
608
+ *
609
+ * <button
610
+ * onClick={async () => {
611
+ * const { revealToken } = await reveal({ network: 'EVM', networkType: 'MAINNET', password });
612
+ * setRevealToken(revealToken);
613
+ * }}
614
+ * >Export private key</button>
615
+ *
616
+ * <HorusRevealModal
617
+ * revealToken={revealToken}
618
+ * open={revealToken !== null}
619
+ * onClose={() => setRevealToken(null)}
620
+ * />
621
+ *
622
+ * The modal renders the auth-page's reveal flow inline. The iframe
623
+ * runs at `auth.horuswallet.com` — partner JS cannot read its DOM
624
+ * cross-origin, so the user's private key never enters the partner
625
+ * page's address space. The partner only receives a `reveal_complete`
626
+ * postMessage at the end carrying `{ viewed: boolean }` — useful for
627
+ * analytics, but no key material.
628
+ */
629
+ interface HorusRevealModalProps {
630
+ /** Token obtained from `useExportWallet().reveal(...)`. */
631
+ revealToken: string | null;
632
+ /** Controlled open state. */
633
+ open: boolean;
634
+ /** Called when the modal is dismissed (user cancel, success, error, Escape). */
635
+ onClose: () => void;
636
+ /**
637
+ * Called when the iframe signals the reveal finished. `viewed` is
638
+ * true only if the user actually clicked through to see the key.
639
+ */
640
+ onComplete?: (viewed: boolean) => void;
641
+ /** Called on a flow-level error (network blip, bad token). */
642
+ onError?: (err: Error) => void;
643
+ dialogStyle?: React.CSSProperties;
644
+ backdropStyle?: React.CSSProperties;
645
+ }
646
+ declare function HorusRevealModal(props: HorusRevealModalProps): JSX.Element | null;
647
+
648
+ /**
649
+ * `useExportWallet()` — initiate a private-key reveal flow.
650
+ *
651
+ * const { reveal, pending, error } = useExportWallet();
652
+ *
653
+ * // In a click handler:
654
+ * const { revealToken } = await reveal({ network: 'EVM', networkType: 'MAINNET', password });
655
+ * // Then mount <HorusRevealModal revealToken={revealToken} ... />
656
+ *
657
+ * Two-step flow (matches Privy's reveal model):
658
+ *
659
+ * 1. This hook calls `/exportWallet/grant` with the user's session
660
+ * and wallet password. The backend validates and returns a
661
+ * short-lived (60s) `revealToken`.
662
+ * 2. The partner page mounts `<HorusRevealModal>` with that token.
663
+ * The modal opens an iframe at `auth.horuswallet.com?flow=reveal`,
664
+ * the iframe re-prompts for the password, calls /redeem itself,
665
+ * displays the key, and posts a "done" back. Partner JS never
666
+ * sees the private key.
667
+ *
668
+ * Why the password is collected twice (once for grant, once in the
669
+ * iframe for redeem): the grant proves the user authorized the export
670
+ * AND that they know the password right now — without that step,
671
+ * a stolen session alone could trigger the reveal modal and bombard
672
+ * the user with a password prompt. The iframe re-prompt is the layer
673
+ * that proves the holder of the partner page is the same human who
674
+ * authorized the grant (the iframe never trusts partner JS for the
675
+ * password input).
676
+ */
677
+
678
+ interface ExportWalletInput {
679
+ network: string;
680
+ networkType: NetworkType;
681
+ walletIndex?: number;
682
+ /**
683
+ * Wallet password. Pass `''` for password-less wallets — the backend
684
+ * uses the attempted decrypt as the proof of ownership, so an empty
685
+ * string against a password-less wallet succeeds where the same
686
+ * empty string against a password-protected wallet returns 401.
687
+ */
688
+ password: string;
689
+ }
690
+ interface ExportWalletResult {
691
+ /**
692
+ * Opaque token to hand to `<HorusRevealModal>`. Expires in 60s.
693
+ * Single-use: after the modal redeems it, a second redeem returns 409.
694
+ */
695
+ revealToken: string;
696
+ /** Seconds the token is valid for. */
697
+ expiresIn: number;
698
+ }
699
+ interface UseExportWalletResult {
700
+ reveal: (input: ExportWalletInput) => Promise<ExportWalletResult>;
701
+ pending: boolean;
702
+ error: Error | undefined;
703
+ }
704
+ declare function useExportWallet(): UseExportWalletResult;
705
+
706
+ /**
707
+ * Fetch wrapper for browser-direct calls to the Horus API.
708
+ *
709
+ * Two headers are stamped on every request:
710
+ * - `x-horus-key: <publishable appId>` — the partner's `app_*`
711
+ * identifier. Tells Horus which app this call belongs to and
712
+ * anchors the Origin allow-list check on the backend.
713
+ * - `Authorization: Bearer <idToken>` — the end-user's session.
714
+ * Skipped when `options.auth` is `false` (sign-in / refresh
715
+ * endpoints that mint the token in the first place).
716
+ *
717
+ * On 401 we run a single refresh-then-retry. If refresh fails, the
718
+ * original 401 bubbles up so the provider can transition to the
719
+ * `unauthenticated` state.
720
+ *
721
+ * Returns the parsed JSON body on success, or throws a typed
722
+ * `HorusHttpError` so consumer hooks can branch on `err.status`.
305
723
  */
306
724
 
307
725
  declare class HorusHttpError extends Error {
@@ -311,4 +729,4 @@ declare class HorusHttpError extends Error {
311
729
  constructor(status: number, message: string, body: unknown, code?: string);
312
730
  }
313
731
 
314
- export { type AuthState, type AuthTokens, type HorusBranding, HorusHttpError, HorusLoginButton, type HorusLoginButtonProps, HorusProvider, type HorusProviderConfig, type HorusProviderProps, type NativeTransferInput, type SignMessageInput, type TokenTransferInput, type UseHorusAuthResult, type User, type WalletDescriptor, useHorusAuth, useSignMessage, useTransfer, useUser, useWallets };
732
+ export { type AuthState, type AuthTokens, type ChainSelector, type CreateWalletInput, type CreateWalletResult, type Eip712TypedData, type ExportWalletInput, type ExportWalletResult, HorusAuthModal, type HorusAuthModalProps, type HorusBranding, HorusHttpError, HorusLoginButton, type HorusLoginButtonProps, HorusProvider, type HorusProviderConfig, type HorusProviderProps, HorusRevealModal, type HorusRevealModalProps, type NativeTransferInput, type NetworkType, SUPPORTED_CHAINS, type SendTransactionInput, type SignMessageInput, type SignTypedDataInput, type SupportedChain, type TokenTransferInput, type UseHorusAuthResult, type User, type WalletDescriptor, useChain, useCreateWallet, useExportWallet, useHorusAuth, useSendTransaction, useSignMessage, useSignTypedData, useSwitchChain, useTransfer, useUser, useWallets };