@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/README.md +133 -174
- package/dist/connect.cjs +218 -0
- package/dist/connect.d.cts +155 -0
- package/dist/connect.d.ts +155 -0
- package/dist/connect.js +188 -0
- package/dist/index.cjs +608 -55
- package/dist/index.d.cts +449 -31
- package/dist/index.d.ts +449 -31
- package/dist/index.js +599 -55
- package/package.json +7 -2
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
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
63
|
-
*
|
|
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
|
-
*
|
|
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
|
|
189
|
-
*
|
|
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
|
-
* `
|
|
240
|
-
*
|
|
241
|
-
*
|
|
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
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
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 };
|