@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.cjs CHANGED
@@ -20,11 +20,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ HorusAuthModal: () => HorusAuthModal,
23
24
  HorusHttpError: () => HorusHttpError,
24
25
  HorusLoginButton: () => HorusLoginButton,
25
26
  HorusProvider: () => HorusProvider,
27
+ HorusRevealModal: () => HorusRevealModal,
28
+ SUPPORTED_CHAINS: () => SUPPORTED_CHAINS,
29
+ useChain: () => useChain,
30
+ useCreateWallet: () => useCreateWallet,
31
+ useExportWallet: () => useExportWallet,
26
32
  useHorusAuth: () => useHorusAuth,
33
+ useSendTransaction: () => useSendTransaction,
27
34
  useSignMessage: () => useSignMessage,
35
+ useSignTypedData: () => useSignTypedData,
36
+ useSwitchChain: () => useSwitchChain,
28
37
  useTransfer: () => useTransfer,
29
38
  useUser: () => useUser,
30
39
  useWallets: () => useWallets
@@ -122,19 +131,21 @@ function makeHttp(deps) {
122
131
  return path.startsWith("/") ? `${base}${path}` : `${base}/${path}`;
123
132
  };
124
133
  async function rawRequest(method, path, body, options = {}) {
125
- const headers = { "Content-Type": "application/json" };
134
+ const headers = {
135
+ "Content-Type": "application/json",
136
+ "x-horus-key": deps.appId
137
+ };
126
138
  if (options.auth !== false) {
127
139
  const tokens = deps.getTokens();
128
140
  if (tokens?.idToken) {
129
- headers["x-horus-id-token"] = tokens.idToken;
141
+ headers["Authorization"] = `Bearer ${tokens.idToken}`;
130
142
  }
131
143
  }
132
144
  const res = await fetch(url(path), {
133
145
  method,
134
146
  headers,
135
147
  body: body !== void 0 ? JSON.stringify(body) : void 0,
136
- signal: options.signal,
137
- credentials: "same-origin"
148
+ signal: options.signal
138
149
  });
139
150
  const text = await res.text();
140
151
  let parsed = void 0;
@@ -175,9 +186,15 @@ function makeHttp(deps) {
175
186
 
176
187
  // src/HorusProvider.tsx
177
188
  var import_jsx_runtime = require("react/jsx-runtime");
178
- var DEFAULT_API_BASE = "/api/horus";
189
+ var DEFAULT_API_BASE = "https://api.horuswallet.com";
179
190
  var DEFAULT_AUTH_PAGE = "https://auth.horuswallet.com";
180
191
  var REFRESH_LEAD_SECONDS = 60;
192
+ var DEFAULT_AUTO_PROVISION = {
193
+ network: "EVM",
194
+ networkType: "MAINNET"
195
+ };
196
+ var DEFAULT_CHAIN = { network: "EVM", networkType: "MAINNET" };
197
+ var AUTO_PROVISION_STORAGE_KEY = "horus.autoProvisioned.localIds";
181
198
  function HorusProvider(props) {
182
199
  const {
183
200
  appId,
@@ -186,11 +203,21 @@ function HorusProvider(props) {
186
203
  branding,
187
204
  tokenStorage = "localStorage",
188
205
  autoRefresh = true,
206
+ autoProvisionWallet = DEFAULT_AUTO_PROVISION,
207
+ defaultChain = DEFAULT_CHAIN,
189
208
  children
190
209
  } = props;
191
210
  const tokenStoreRef = (0, import_react2.useRef)(createTokenStore(tokenStorage));
192
211
  const tokensRef = (0, import_react2.useRef)(null);
193
212
  const [state, setState] = (0, import_react2.useState)({ status: "loading" });
213
+ const [walletsVersion, setWalletsVersion] = (0, import_react2.useState)(0);
214
+ const revalidateWallets = (0, import_react2.useCallback)(() => {
215
+ setWalletsVersion((v) => v + 1);
216
+ }, []);
217
+ const [currentChain, setCurrentChain] = (0, import_react2.useState)(defaultChain);
218
+ const setChain = (0, import_react2.useCallback)((chain) => {
219
+ setCurrentChain(chain);
220
+ }, []);
194
221
  const setTokens = (0, import_react2.useCallback)((tokens) => {
195
222
  tokensRef.current = tokens;
196
223
  if (tokens) {
@@ -211,9 +238,11 @@ function HorusProvider(props) {
211
238
  if (!cur?.refreshToken) throw new Error("no refresh token cached");
212
239
  const fresh = await fetch(joinUrl(apiBase, "/auth/refresh"), {
213
240
  method: "POST",
214
- headers: { "Content-Type": "application/json" },
215
- body: JSON.stringify({ refreshToken: cur.refreshToken }),
216
- credentials: "same-origin"
241
+ headers: {
242
+ "Content-Type": "application/json",
243
+ "x-horus-key": appId
244
+ },
245
+ body: JSON.stringify({ refreshToken: cur.refreshToken })
217
246
  });
218
247
  if (!fresh.ok) throw new Error(`refresh failed: ${fresh.status}`);
219
248
  const raw = await fresh.json();
@@ -223,11 +252,12 @@ function HorusProvider(props) {
223
252
  };
224
253
  return makeHttp({
225
254
  apiBase,
255
+ appId,
226
256
  getTokens: () => tokensRef.current,
227
257
  refreshTokens: refreshOnce,
228
258
  autoRefresh
229
259
  });
230
- }, [apiBase, autoRefresh, setTokens]);
260
+ }, [apiBase, appId, autoRefresh, setTokens]);
231
261
  (0, import_react2.useEffect)(() => {
232
262
  const stored = tokenStoreRef.current.read();
233
263
  if (stored) {
@@ -266,6 +296,33 @@ function HorusProvider(props) {
266
296
  }, refreshIn);
267
297
  return () => clearTimeout(handle);
268
298
  }, [state, autoRefresh, http, setTokens]);
299
+ (0, import_react2.useEffect)(() => {
300
+ if (state.status !== "authenticated") return;
301
+ if (!autoProvisionWallet) return;
302
+ const localId = state.tokens.localId;
303
+ if (!localId) return;
304
+ if (alreadyAutoProvisioned(localId)) return;
305
+ let cancelled = false;
306
+ (async () => {
307
+ try {
308
+ const existing = await http.get("/getWallet");
309
+ if (cancelled) return;
310
+ const hasAny = Object.values(existing?.wallets ?? {}).some(
311
+ (group) => Array.isArray(group?.wallets) && group.wallets.length > 0
312
+ );
313
+ markAutoProvisioned(localId);
314
+ if (hasAny) return;
315
+ await http.post("/createWallet", autoProvisionWallet);
316
+ if (cancelled) return;
317
+ revalidateWallets();
318
+ } catch (err) {
319
+ console.warn("@horus-wallet/sdk-react: auto-provision wallet failed", err);
320
+ }
321
+ })();
322
+ return () => {
323
+ cancelled = true;
324
+ };
325
+ }, [state, autoProvisionWallet, http, revalidateWallets]);
269
326
  const ctx = (0, import_react2.useMemo)(
270
327
  () => ({
271
328
  state,
@@ -286,9 +343,24 @@ function HorusProvider(props) {
286
343
  const stamped = stampExpiry(raw);
287
344
  setTokens(stamped);
288
345
  return stamped;
289
- }
346
+ },
347
+ walletsVersion,
348
+ revalidateWallets,
349
+ currentChain,
350
+ setChain
290
351
  }),
291
- [state, http, authPageUrl, appId, branding, setTokens]
352
+ [
353
+ state,
354
+ http,
355
+ authPageUrl,
356
+ appId,
357
+ branding,
358
+ setTokens,
359
+ walletsVersion,
360
+ revalidateWallets,
361
+ currentChain,
362
+ setChain
363
+ ]
292
364
  );
293
365
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HorusContext.Provider, { value: ctx, children });
294
366
  }
@@ -302,6 +374,36 @@ function userFromTokens(t) {
302
374
  providerId: t.providerId
303
375
  };
304
376
  }
377
+ var memoryAutoProvisioned = /* @__PURE__ */ new Set();
378
+ function readAutoProvisionedSet() {
379
+ if (typeof window === "undefined" || !window.localStorage) {
380
+ return memoryAutoProvisioned;
381
+ }
382
+ try {
383
+ const raw = window.localStorage.getItem(AUTO_PROVISION_STORAGE_KEY);
384
+ if (!raw) return /* @__PURE__ */ new Set();
385
+ const parsed = JSON.parse(raw);
386
+ return new Set(Array.isArray(parsed) ? parsed : []);
387
+ } catch {
388
+ return /* @__PURE__ */ new Set();
389
+ }
390
+ }
391
+ function alreadyAutoProvisioned(localId) {
392
+ return readAutoProvisionedSet().has(localId);
393
+ }
394
+ function markAutoProvisioned(localId) {
395
+ if (typeof window === "undefined" || !window.localStorage) {
396
+ memoryAutoProvisioned.add(localId);
397
+ return;
398
+ }
399
+ try {
400
+ const set = readAutoProvisionedSet();
401
+ set.add(localId);
402
+ window.localStorage.setItem(AUTO_PROVISION_STORAGE_KEY, JSON.stringify([...set]));
403
+ } catch {
404
+ memoryAutoProvisioned.add(localId);
405
+ }
406
+ }
305
407
  function joinUrl(base, path) {
306
408
  const b = base.endsWith("/") ? base.slice(0, -1) : base;
307
409
  const p = path.startsWith("/") ? path : `/${path}`;
@@ -310,6 +412,36 @@ function joinUrl(base, path) {
310
412
 
311
413
  // src/hooks/useHorusAuth.ts
312
414
  var import_react3 = require("react");
415
+
416
+ // src/internal/authUrl.ts
417
+ function buildAuthUrl(p, state) {
418
+ const url = new URL(p.baseUrl);
419
+ url.searchParams.set("flow", p.flow);
420
+ url.searchParams.set("origin", window.location.origin);
421
+ url.searchParams.set("state", state);
422
+ url.searchParams.set("appKey", p.appId);
423
+ url.searchParams.set("mode", p.mode);
424
+ if (p.phone) url.searchParams.set("phone", p.phone);
425
+ const b = p.branding;
426
+ if (b) {
427
+ if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
428
+ if (b.brandName) url.searchParams.set("b_name", b.brandName);
429
+ if (b.primaryColor) url.searchParams.set("b_color", b.primaryColor);
430
+ if (b.backgroundColor) url.searchParams.set("b_bg", b.backgroundColor);
431
+ if (b.fontFamily) url.searchParams.set("b_font", b.fontFamily);
432
+ if (b.termsUrl) url.searchParams.set("b_terms", b.termsUrl);
433
+ if (b.privacyUrl) url.searchParams.set("b_privacy", b.privacyUrl);
434
+ if (b.showPoweredByHorus === false) url.searchParams.set("b_poweredby", "0");
435
+ }
436
+ return url.toString();
437
+ }
438
+ function randomState() {
439
+ const bytes = new Uint8Array(16);
440
+ crypto.getRandomValues(bytes);
441
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
442
+ }
443
+
444
+ // src/hooks/useHorusAuth.ts
313
445
  function useHorusAuth() {
314
446
  const ctx = useHorusContext();
315
447
  const { state, http, signIn, signOut, refreshTokens, authPageUrl, appId, branding } = ctx;
@@ -409,7 +541,7 @@ function openPopupFlow(params) {
409
541
  return;
410
542
  }
411
543
  const state = randomState();
412
- const url = buildAuthUrl(params, state);
544
+ const url = buildAuthUrl({ ...params, mode: "popup" }, state);
413
545
  const popup = window.open(url, "horus-auth", defaultPopupFeatures());
414
546
  if (!popup) {
415
547
  reject(new Error("Popup blocked \u2014 call from a click handler so the browser allows it."));
@@ -471,26 +603,6 @@ function openPopupFlow(params) {
471
603
  }, 400);
472
604
  });
473
605
  }
474
- function buildAuthUrl(p, state) {
475
- const url = new URL(p.baseUrl);
476
- url.searchParams.set("flow", p.flow);
477
- url.searchParams.set("origin", window.location.origin);
478
- url.searchParams.set("state", state);
479
- url.searchParams.set("appKey", p.appId);
480
- if (p.phone) url.searchParams.set("phone", p.phone);
481
- const b = p.branding;
482
- if (b) {
483
- if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
484
- if (b.brandName) url.searchParams.set("b_name", b.brandName);
485
- if (b.primaryColor) url.searchParams.set("b_color", b.primaryColor);
486
- if (b.backgroundColor) url.searchParams.set("b_bg", b.backgroundColor);
487
- if (b.fontFamily) url.searchParams.set("b_font", b.fontFamily);
488
- if (b.termsUrl) url.searchParams.set("b_terms", b.termsUrl);
489
- if (b.privacyUrl) url.searchParams.set("b_privacy", b.privacyUrl);
490
- if (b.showPoweredByHorus === false) url.searchParams.set("b_poweredby", "0");
491
- }
492
- return url.toString();
493
- }
494
606
  function defaultPopupFeatures() {
495
607
  const w = 480, h = 640;
496
608
  if (typeof window === "undefined") return `width=${w},height=${h}`;
@@ -498,11 +610,6 @@ function defaultPopupFeatures() {
498
610
  const top = Math.max(0, (window.innerHeight - h) / 2 + (window.screenY ?? 0));
499
611
  return `width=${w},height=${h},left=${left},top=${top},popup=1`;
500
612
  }
501
- function randomState() {
502
- const bytes = new Uint8Array(16);
503
- crypto.getRandomValues(bytes);
504
- return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
505
- }
506
613
 
507
614
  // src/hooks/useUser.ts
508
615
  function useUser() {
@@ -513,7 +620,7 @@ function useUser() {
513
620
  // src/hooks/useWallets.ts
514
621
  var import_react4 = require("react");
515
622
  function useWallets() {
516
- const { http, state } = useHorusContext();
623
+ const { http, state, walletsVersion } = useHorusContext();
517
624
  const [wallets, setWallets] = (0, import_react4.useState)([]);
518
625
  const [ready, setReady] = (0, import_react4.useState)(false);
519
626
  const [error, setError] = (0, import_react4.useState)(void 0);
@@ -527,7 +634,7 @@ function useWallets() {
527
634
  setError(void 0);
528
635
  try {
529
636
  const response = await http.get(
530
- "/wallets"
637
+ "/getWallet"
531
638
  );
532
639
  const flat = [];
533
640
  for (const [network, group] of Object.entries(response.wallets ?? {})) {
@@ -544,22 +651,87 @@ function useWallets() {
544
651
  }, [http, state.status]);
545
652
  (0, import_react4.useEffect)(() => {
546
653
  void load();
547
- }, [load]);
654
+ }, [load, walletsVersion]);
548
655
  return { ready, wallets, refresh: load, error };
549
656
  }
550
657
 
551
- // src/hooks/useSignMessage.ts
658
+ // src/hooks/useCreateWallet.ts
552
659
  var import_react5 = require("react");
553
- function useSignMessage() {
554
- const { http } = useHorusContext();
660
+ function useCreateWallet() {
661
+ const { http, revalidateWallets } = useHorusContext();
555
662
  const [pending, setPending] = (0, import_react5.useState)(false);
556
663
  const [error, setError] = (0, import_react5.useState)(void 0);
557
- const signMessage = (0, import_react5.useCallback)(
664
+ const create = (0, import_react5.useCallback)(
558
665
  async (input) => {
666
+ if (pending) {
667
+ const err = new Error(
668
+ "useCreateWallet: a previous create() call is still in flight"
669
+ );
670
+ setError(err);
671
+ throw err;
672
+ }
559
673
  setPending(true);
560
674
  setError(void 0);
561
675
  try {
562
- const response = await http.post("/sign-message", input);
676
+ const response = await http.post("/createWallet", {
677
+ network: input.network,
678
+ networkType: input.networkType,
679
+ ...input.password ? { password: input.password } : {}
680
+ });
681
+ revalidateWallets();
682
+ return response;
683
+ } catch (err) {
684
+ const e = err instanceof Error ? err : new Error(String(err));
685
+ setError(e);
686
+ throw e;
687
+ } finally {
688
+ setPending(false);
689
+ }
690
+ },
691
+ [http, revalidateWallets, pending]
692
+ );
693
+ return { create, pending, error };
694
+ }
695
+
696
+ // src/hooks/useSwitchChain.ts
697
+ var import_react6 = require("react");
698
+ var SUPPORTED_CHAINS = [
699
+ "EVM",
700
+ "ETHEREUM",
701
+ "BASE",
702
+ "POLYGON",
703
+ "BSC",
704
+ "ARBITRUM",
705
+ "OPTIMISM",
706
+ "BITCOIN",
707
+ "ICP",
708
+ "CASPER",
709
+ "AETERNITY"
710
+ ];
711
+ function useSwitchChain() {
712
+ const { currentChain, setChain } = useHorusContext();
713
+ const switchChain = (0, import_react6.useCallback)(
714
+ (chain) => {
715
+ setChain(chain);
716
+ },
717
+ [setChain]
718
+ );
719
+ return { chain: currentChain, switchChain, supportedChains: SUPPORTED_CHAINS };
720
+ }
721
+ var useChain = useSwitchChain;
722
+
723
+ // src/hooks/useSignMessage.ts
724
+ var import_react7 = require("react");
725
+ function useSignMessage() {
726
+ const { http } = useHorusContext();
727
+ const [pending, setPending] = (0, import_react7.useState)(false);
728
+ const [error, setError] = (0, import_react7.useState)(void 0);
729
+ const signMessage = (0, import_react7.useCallback)(
730
+ async (input) => {
731
+ setPending(true);
732
+ setError(void 0);
733
+ try {
734
+ const response = await http.post("/signMessage", input);
563
735
  return response.signature;
564
736
  } catch (err) {
565
737
  const e = err instanceof Error ? err : new Error(String(err));
@@ -574,13 +746,79 @@ function useSignMessage() {
574
746
  return { signMessage, pending, error };
575
747
  }
576
748
 
749
+ // src/hooks/useSignTypedData.ts
750
+ var import_react8 = require("react");
751
+ function useSignTypedData() {
752
+ const { http } = useHorusContext();
753
+ const [pending, setPending] = (0, import_react8.useState)(false);
754
+ const [error, setError] = (0, import_react8.useState)(void 0);
755
+ const signTypedData = (0, import_react8.useCallback)(
756
+ async (input) => {
757
+ setPending(true);
758
+ setError(void 0);
759
+ try {
760
+ const response = await http.post("/signTypedData", {
761
+ network: input.network,
762
+ networkType: input.networkType,
763
+ typedData: input.typedData,
764
+ ...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {},
765
+ ...input.password ? { password: input.password } : {}
766
+ });
767
+ return response.signature;
768
+ } catch (err) {
769
+ const e = err instanceof Error ? err : new Error(String(err));
770
+ setError(e);
771
+ throw e;
772
+ } finally {
773
+ setPending(false);
774
+ }
775
+ },
776
+ [http]
777
+ );
778
+ return { signTypedData, pending, error };
779
+ }
780
+
781
+ // src/hooks/useSendTransaction.ts
782
+ var import_react9 = require("react");
783
+ function useSendTransaction() {
784
+ const { http } = useHorusContext();
785
+ const [pending, setPending] = (0, import_react9.useState)(false);
786
+ const [error, setError] = (0, import_react9.useState)(void 0);
787
+ const sendTransaction = (0, import_react9.useCallback)(
788
+ async (input) => {
789
+ setPending(true);
790
+ setError(void 0);
791
+ try {
792
+ return await http.post("/sendTransaction", {
793
+ network: input.network,
794
+ networkType: input.networkType,
795
+ to: input.to,
796
+ ...input.value !== void 0 ? { value: String(input.value) } : {},
797
+ ...input.data !== void 0 ? { data: input.data } : {},
798
+ ...input.gasLimit !== void 0 ? { gasLimit: String(input.gasLimit) } : {},
799
+ ...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {},
800
+ ...input.password ? { password: input.password } : {}
801
+ });
802
+ } catch (err) {
803
+ const e = err instanceof Error ? err : new Error(String(err));
804
+ setError(e);
805
+ throw e;
806
+ } finally {
807
+ setPending(false);
808
+ }
809
+ },
810
+ [http]
811
+ );
812
+ return { sendTransaction, pending, error };
813
+ }
814
+
577
815
  // src/hooks/useTransfer.ts
578
- var import_react6 = require("react");
816
+ var import_react10 = require("react");
579
817
  function useTransfer() {
580
818
  const { http } = useHorusContext();
581
- const [pending, setPending] = (0, import_react6.useState)(false);
582
- const [error, setError] = (0, import_react6.useState)(void 0);
583
- const wrap = (0, import_react6.useCallback)(
819
+ const [pending, setPending] = (0, import_react10.useState)(false);
820
+ const [error, setError] = (0, import_react10.useState)(void 0);
821
+ const wrap = (0, import_react10.useCallback)(
584
822
  async (fn) => {
585
823
  setPending(true);
586
824
  setError(void 0);
@@ -596,18 +834,18 @@ function useTransfer() {
596
834
  },
597
835
  []
598
836
  );
599
- const native = (0, import_react6.useCallback)(
837
+ const native = (0, import_react10.useCallback)(
600
838
  (input) => wrap(
601
- () => http.post("/transfer/native", {
839
+ () => http.post("/nativeTransfer", {
602
840
  ...input,
603
841
  amount: typeof input.amount === "bigint" ? input.amount.toString() : String(input.amount)
604
842
  })
605
843
  ),
606
844
  [http, wrap]
607
845
  );
608
- const token = (0, import_react6.useCallback)(
846
+ const token = (0, import_react10.useCallback)(
609
847
  (input) => wrap(
610
- () => http.post("/transfer/token", {
848
+ () => http.post("/tokenTransfer", {
611
849
  ...input,
612
850
  amount: typeof input.amount === "bigint" ? input.amount.toString() : String(input.amount)
613
851
  })
@@ -618,7 +856,7 @@ function useTransfer() {
618
856
  }
619
857
 
620
858
  // src/components/HorusLoginButton.tsx
621
- var import_react7 = require("react");
859
+ var import_react11 = require("react");
622
860
  var import_jsx_runtime2 = require("react/jsx-runtime");
623
861
  function HorusLoginButton({
624
862
  flow = "google",
@@ -631,7 +869,7 @@ function HorusLoginButton({
631
869
  ...rest
632
870
  }) {
633
871
  const auth = useHorusAuth();
634
- const [busy, setBusy] = (0, import_react7.useState)(false);
872
+ const [busy, setBusy] = (0, import_react11.useState)(false);
635
873
  const onClick = async () => {
636
874
  if (busy) return;
637
875
  setBusy(true);
@@ -669,13 +907,328 @@ function HorusLoginButton({
669
907
  }
670
908
  );
671
909
  }
910
+
911
+ // src/components/HorusAuthModal.tsx
912
+ var import_react12 = require("react");
913
+ var import_jsx_runtime3 = require("react/jsx-runtime");
914
+ var defaultBackdropStyle = {
915
+ position: "fixed",
916
+ inset: 0,
917
+ background: "rgba(0, 0, 0, 0.5)",
918
+ display: "flex",
919
+ alignItems: "center",
920
+ justifyContent: "center",
921
+ zIndex: 2147483600
922
+ };
923
+ var defaultDialogStyle = {
924
+ width: "480px",
925
+ maxWidth: "95vw",
926
+ height: "640px",
927
+ maxHeight: "95vh",
928
+ background: "#fff",
929
+ borderRadius: "16px",
930
+ boxShadow: "0 24px 56px rgba(0, 0, 0, 0.32)",
931
+ overflow: "hidden"
932
+ };
933
+ function HorusAuthModal(props) {
934
+ const {
935
+ flow,
936
+ phone,
937
+ open,
938
+ onClose,
939
+ onSuccess,
940
+ onError,
941
+ dialogStyle,
942
+ backdropStyle
943
+ } = props;
944
+ const { appId, authPageUrl, branding, signIn } = useHorusContext();
945
+ const stateRef = (0, import_react12.useRef)("");
946
+ if (stateRef.current === "" || !open) {
947
+ stateRef.current = randomState();
948
+ }
949
+ const iframeSrc = (0, import_react12.useMemo)(() => {
950
+ if (!open) return "";
951
+ return buildAuthUrl(
952
+ {
953
+ flow,
954
+ appId,
955
+ baseUrl: authPageUrl,
956
+ mode: "iframe",
957
+ branding,
958
+ phone
959
+ },
960
+ stateRef.current
961
+ );
962
+ }, [open, flow, appId, authPageUrl, branding, phone]);
963
+ const expectedOrigin = (0, import_react12.useMemo)(() => {
964
+ try {
965
+ return new URL(authPageUrl).origin;
966
+ } catch {
967
+ return "";
968
+ }
969
+ }, [authPageUrl]);
970
+ (0, import_react12.useEffect)(() => {
971
+ if (!open) return;
972
+ const onMessage = (ev) => {
973
+ if (ev.origin !== expectedOrigin) return;
974
+ const data = ev.data;
975
+ if (!data || data.channel !== "horus.auth" || data.version !== 1) return;
976
+ if (data.state !== void 0 && data.state !== stateRef.current) return;
977
+ const body = data.body;
978
+ if (!body) return;
979
+ switch (body.type) {
980
+ case "success": {
981
+ const stamped = stampExpiry({
982
+ idToken: body.idToken,
983
+ refreshToken: "",
984
+ expiresIn: body.expiresAt ? Math.max(60, body.expiresAt - Math.floor(Date.now() / 1e3)) : 3600,
985
+ localId: body.user?.uid ?? "",
986
+ email: body.user?.email,
987
+ displayName: body.user?.displayName,
988
+ photoUrl: body.user?.photoURL,
989
+ providerId: body.user?.providerId
990
+ });
991
+ signIn(stamped);
992
+ onSuccess?.({
993
+ uid: stamped.localId,
994
+ email: stamped.email,
995
+ emailVerified: stamped.emailVerified,
996
+ displayName: stamped.displayName,
997
+ photoUrl: stamped.photoUrl,
998
+ providerId: stamped.providerId
999
+ });
1000
+ onClose();
1001
+ return;
1002
+ }
1003
+ case "cancel":
1004
+ onClose();
1005
+ return;
1006
+ case "error":
1007
+ onError?.(new Error(body.message ?? "Sign-in failed."));
1008
+ onClose();
1009
+ return;
1010
+ }
1011
+ };
1012
+ window.addEventListener("message", onMessage);
1013
+ return () => window.removeEventListener("message", onMessage);
1014
+ }, [open, expectedOrigin, signIn, onSuccess, onError, onClose]);
1015
+ (0, import_react12.useEffect)(() => {
1016
+ if (!open) return;
1017
+ const onKey = (ev) => {
1018
+ if (ev.key === "Escape") onClose();
1019
+ };
1020
+ window.addEventListener("keydown", onKey);
1021
+ return () => window.removeEventListener("keydown", onKey);
1022
+ }, [open, onClose]);
1023
+ (0, import_react12.useEffect)(() => {
1024
+ if (!open) return;
1025
+ if (typeof document === "undefined") return;
1026
+ const prev = document.body.style.overflow;
1027
+ document.body.style.overflow = "hidden";
1028
+ return () => {
1029
+ document.body.style.overflow = prev;
1030
+ };
1031
+ }, [open]);
1032
+ const backdropClick = (0, import_react12.useCallback)(
1033
+ (ev) => {
1034
+ if (ev.target === ev.currentTarget) onClose();
1035
+ },
1036
+ [onClose]
1037
+ );
1038
+ if (!open || typeof window === "undefined") return null;
1039
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1040
+ "div",
1041
+ {
1042
+ role: "dialog",
1043
+ "aria-modal": "true",
1044
+ "aria-label": "Sign in with Horus",
1045
+ style: { ...defaultBackdropStyle, ...backdropStyle },
1046
+ onClick: backdropClick,
1047
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultDialogStyle, ...dialogStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1048
+ "iframe",
1049
+ {
1050
+ src: iframeSrc,
1051
+ title: "Horus authentication",
1052
+ allow: "publickey-credentials-get; publickey-credentials-create; clipboard-write",
1053
+ style: { width: "100%", height: "100%", border: "none" }
1054
+ }
1055
+ ) })
1056
+ }
1057
+ );
1058
+ }
1059
+
1060
+ // src/components/HorusRevealModal.tsx
1061
+ var import_react13 = require("react");
1062
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1063
+ var defaultBackdropStyle2 = {
1064
+ position: "fixed",
1065
+ inset: 0,
1066
+ background: "rgba(0, 0, 0, 0.5)",
1067
+ display: "flex",
1068
+ alignItems: "center",
1069
+ justifyContent: "center",
1070
+ zIndex: 2147483600
1071
+ };
1072
+ var defaultDialogStyle2 = {
1073
+ width: "480px",
1074
+ maxWidth: "95vw",
1075
+ height: "640px",
1076
+ maxHeight: "95vh",
1077
+ background: "#fff",
1078
+ borderRadius: "16px",
1079
+ boxShadow: "0 24px 56px rgba(0, 0, 0, 0.32)",
1080
+ overflow: "hidden"
1081
+ };
1082
+ function HorusRevealModal(props) {
1083
+ const { revealToken, open, onClose, onComplete, onError, dialogStyle, backdropStyle } = props;
1084
+ const { appId, authPageUrl, branding } = useHorusContext();
1085
+ const stateRef = (0, import_react13.useRef)("");
1086
+ if (stateRef.current === "" || !open) {
1087
+ stateRef.current = randomState();
1088
+ }
1089
+ const iframeSrc = (0, import_react13.useMemo)(() => {
1090
+ if (!open || !revealToken) return "";
1091
+ const baseUrl = buildAuthUrl(
1092
+ {
1093
+ flow: "reveal",
1094
+ appId,
1095
+ baseUrl: authPageUrl,
1096
+ mode: "iframe",
1097
+ branding
1098
+ },
1099
+ stateRef.current
1100
+ );
1101
+ const u = new URL(baseUrl);
1102
+ u.searchParams.set("token", revealToken);
1103
+ return u.toString();
1104
+ }, [open, revealToken, appId, authPageUrl, branding]);
1105
+ const expectedOrigin = (0, import_react13.useMemo)(() => {
1106
+ try {
1107
+ return new URL(authPageUrl).origin;
1108
+ } catch {
1109
+ return "";
1110
+ }
1111
+ }, [authPageUrl]);
1112
+ (0, import_react13.useEffect)(() => {
1113
+ if (!open) return;
1114
+ const onMessage = (ev) => {
1115
+ if (ev.origin !== expectedOrigin) return;
1116
+ const data = ev.data;
1117
+ if (!data || data.channel !== "horus.auth" || data.version !== 1) return;
1118
+ if (data.state !== void 0 && data.state !== stateRef.current) return;
1119
+ const body = data.body;
1120
+ if (!body) return;
1121
+ switch (body.type) {
1122
+ case "reveal_complete":
1123
+ onComplete?.(Boolean(body.viewed));
1124
+ onClose();
1125
+ return;
1126
+ case "cancel":
1127
+ onClose();
1128
+ return;
1129
+ case "error":
1130
+ onError?.(new Error(body.message ?? "Reveal failed."));
1131
+ onClose();
1132
+ return;
1133
+ }
1134
+ };
1135
+ window.addEventListener("message", onMessage);
1136
+ return () => window.removeEventListener("message", onMessage);
1137
+ }, [open, expectedOrigin, onComplete, onClose, onError]);
1138
+ (0, import_react13.useEffect)(() => {
1139
+ if (!open) return;
1140
+ const onKey = (ev) => {
1141
+ if (ev.key === "Escape") onClose();
1142
+ };
1143
+ window.addEventListener("keydown", onKey);
1144
+ return () => window.removeEventListener("keydown", onKey);
1145
+ }, [open, onClose]);
1146
+ (0, import_react13.useEffect)(() => {
1147
+ if (!open) return;
1148
+ if (typeof document === "undefined") return;
1149
+ const prev = document.body.style.overflow;
1150
+ document.body.style.overflow = "hidden";
1151
+ return () => {
1152
+ document.body.style.overflow = prev;
1153
+ };
1154
+ }, [open]);
1155
+ const backdropClick = (0, import_react13.useCallback)(
1156
+ (ev) => {
1157
+ if (ev.target === ev.currentTarget) onClose();
1158
+ },
1159
+ [onClose]
1160
+ );
1161
+ if (!open || !revealToken || typeof window === "undefined") return null;
1162
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1163
+ "div",
1164
+ {
1165
+ role: "dialog",
1166
+ "aria-modal": "true",
1167
+ "aria-label": "Reveal wallet private key",
1168
+ style: { ...defaultBackdropStyle2, ...backdropStyle },
1169
+ onClick: backdropClick,
1170
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...defaultDialogStyle2, ...dialogStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1171
+ "iframe",
1172
+ {
1173
+ src: iframeSrc,
1174
+ title: "Horus wallet export",
1175
+ allow: "clipboard-write",
1176
+ style: { width: "100%", height: "100%", border: "none" }
1177
+ }
1178
+ ) })
1179
+ }
1180
+ );
1181
+ }
1182
+
1183
+ // src/hooks/useExportWallet.ts
1184
+ var import_react14 = require("react");
1185
+ function useExportWallet() {
1186
+ const { http } = useHorusContext();
1187
+ const [pending, setPending] = (0, import_react14.useState)(false);
1188
+ const [error, setError] = (0, import_react14.useState)(void 0);
1189
+ const reveal = (0, import_react14.useCallback)(
1190
+ async (input) => {
1191
+ setPending(true);
1192
+ setError(void 0);
1193
+ try {
1194
+ const response = await http.post(
1195
+ "/exportWallet/grant",
1196
+ {
1197
+ network: input.network,
1198
+ networkType: input.networkType,
1199
+ password: input.password,
1200
+ ...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {}
1201
+ }
1202
+ );
1203
+ return response;
1204
+ } catch (err) {
1205
+ const e = err instanceof Error ? err : new Error(String(err));
1206
+ setError(e);
1207
+ throw e;
1208
+ } finally {
1209
+ setPending(false);
1210
+ }
1211
+ },
1212
+ [http]
1213
+ );
1214
+ return { reveal, pending, error };
1215
+ }
672
1216
  // Annotate the CommonJS export names for ESM import in node:
673
1217
  0 && (module.exports = {
1218
+ HorusAuthModal,
674
1219
  HorusHttpError,
675
1220
  HorusLoginButton,
676
1221
  HorusProvider,
1222
+ HorusRevealModal,
1223
+ SUPPORTED_CHAINS,
1224
+ useChain,
1225
+ useCreateWallet,
1226
+ useExportWallet,
677
1227
  useHorusAuth,
1228
+ useSendTransaction,
678
1229
  useSignMessage,
1230
+ useSignTypedData,
1231
+ useSwitchChain,
679
1232
  useTransfer,
680
1233
  useUser,
681
1234
  useWallets