@cookill/wallet-adapter 3.1.7 → 3.2.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/react.js CHANGED
@@ -1,5 +1,5 @@
1
- import React, { createContext, useReducer, useRef, useEffect, useCallback, useMemo, useContext, useState, Component } from 'react';
2
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
1
+ import React2, { createContext, useState, useRef, useEffect, useCallback, useReducer, useMemo, useContext, Component } from 'react';
2
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
3
3
 
4
4
  // src/core/types.ts
5
5
  var NETWORKS = {
@@ -51,6 +51,14 @@ var NETWORKS = {
51
51
 
52
52
  // src/core/provider.ts
53
53
  function isInstalled() {
54
+ if (typeof window === "undefined") return false;
55
+ try {
56
+ return !!window.rialo?.isRialo && !window.rialo?.isWebWallet;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+ function hasProvider() {
54
62
  if (typeof window === "undefined") return false;
55
63
  try {
56
64
  return !!window.rialo?.isRialo;
@@ -158,9 +166,14 @@ var SheepWallet = class {
158
166
  // =========================================================================
159
167
  // Getters (all synchronous, never block)
160
168
  // =========================================================================
169
+ /** True only if the browser extension is installed (not web wallet) */
161
170
  get isInstalled() {
162
171
  return isInstalled();
163
172
  }
173
+ /** True if any provider (extension or web wallet) is available */
174
+ get hasProvider() {
175
+ return hasProvider();
176
+ }
164
177
  get connected() {
165
178
  return this._connected;
166
179
  }
@@ -185,7 +198,7 @@ var SheepWallet = class {
185
198
  async connect() {
186
199
  this._provider = getProvider();
187
200
  if (!this._provider) {
188
- throw new Error("Sheep Wallet not installed. Get it at https://rialo.io/wallet");
201
+ throw new Error("No wallet provider found. Install Sheep Wallet extension or set up a web wallet.");
189
202
  }
190
203
  try {
191
204
  const rawAccounts = await withTimeout(
@@ -556,586 +569,690 @@ var SheepWallet = class {
556
569
  );
557
570
  }
558
571
  };
559
- var initialState = {
560
- status: "disconnected",
561
- accounts: [],
562
- network: "devnet",
563
- balance: null,
564
- error: null,
565
- isModalOpen: false,
566
- scanConnectStatus: "idle"
567
- };
568
- function walletReducer(state, action) {
569
- switch (action.type) {
570
- case "CONNECTING":
571
- return { ...state, status: "connecting", error: null };
572
- case "CONNECTED":
573
- return {
574
- ...state,
575
- status: "connected",
576
- accounts: action.accounts,
577
- network: action.network,
578
- error: null,
579
- isModalOpen: false,
580
- scanConnectStatus: "idle"
581
- };
582
- case "DISCONNECTED":
583
- return { ...state, status: "disconnected", accounts: [], balance: null, error: null };
584
- case "ERROR":
585
- return { ...state, status: "error", error: action.error };
586
- case "SET_BALANCE":
587
- return { ...state, balance: action.balance };
588
- case "SET_NETWORK":
589
- return { ...state, network: action.network };
590
- case "SET_ACCOUNTS":
591
- return { ...state, accounts: action.accounts };
592
- case "OPEN_MODAL":
593
- return { ...state, isModalOpen: true };
594
- case "CLOSE_MODAL":
595
- return { ...state, isModalOpen: false, scanConnectStatus: "idle" };
596
- case "RESET_ERROR":
597
- return { ...state, error: null, status: state.accounts.length > 0 ? "connected" : "disconnected" };
598
- case "SET_SCAN_STATUS":
599
- return { ...state, scanConnectStatus: action.status };
600
- default:
601
- return state;
572
+ var IconClose = () => /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
573
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
574
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
575
+ ] });
576
+ var IconExtension = () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
577
+ /* @__PURE__ */ jsx("path", { d: "M16 3h5v5" }),
578
+ /* @__PURE__ */ jsx("path", { d: "M8 3H3v5" }),
579
+ /* @__PURE__ */ jsx("path", { d: "M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3" }),
580
+ /* @__PURE__ */ jsx("path", { d: "m15 9 6-6" })
581
+ ] });
582
+ var IconQR = () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
583
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7" }),
584
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "3", width: "7", height: "7" }),
585
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "14", width: "7", height: "7" }),
586
+ /* @__PURE__ */ jsx("path", { d: "M14 14h7v7h-7z" })
587
+ ] });
588
+ var IconWeb = () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
589
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
590
+ /* @__PURE__ */ jsx("path", { d: "M2 10h20" }),
591
+ /* @__PURE__ */ jsx("path", { d: "M12 17v4" }),
592
+ /* @__PURE__ */ jsx("path", { d: "M8 21h8" })
593
+ ] });
594
+ var IconCheck = () => /* @__PURE__ */ jsx("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "#6EB9A8", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
595
+ var IconCamera = () => /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
596
+ /* @__PURE__ */ jsx("path", { d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" }),
597
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "13", r: "3" })
598
+ ] });
599
+ var IconRefresh = () => /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
600
+ /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" }),
601
+ /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" })
602
+ ] });
603
+ var S = {
604
+ backdrop: {
605
+ position: "absolute",
606
+ inset: 0,
607
+ backgroundColor: "rgba(0,0,0,0.6)",
608
+ backdropFilter: "blur(8px)",
609
+ WebkitBackdropFilter: "blur(8px)"
610
+ },
611
+ dialog: {
612
+ pointerEvents: "auto",
613
+ backgroundColor: "#011B29",
614
+ borderRadius: "20px",
615
+ padding: "24px",
616
+ width: "100%",
617
+ maxWidth: "400px",
618
+ margin: "16px",
619
+ boxShadow: "0 25px 60px -12px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.05)",
620
+ maxHeight: "90vh",
621
+ overflow: "auto",
622
+ animation: "swModalFadeIn 0.25s cubic-bezier(0.4,0,0.2,1)"
623
+ },
624
+ header: {
625
+ display: "flex",
626
+ justifyContent: "space-between",
627
+ alignItems: "center",
628
+ marginBottom: "20px"
629
+ },
630
+ title: { color: "#fff", fontSize: "18px", fontWeight: 600, margin: 0, letterSpacing: "-0.01em" },
631
+ closeBtn: {
632
+ background: "none",
633
+ border: "none",
634
+ color: "rgba(255,255,255,0.3)",
635
+ cursor: "pointer",
636
+ padding: "6px",
637
+ borderRadius: "8px",
638
+ display: "flex",
639
+ alignItems: "center",
640
+ justifyContent: "center",
641
+ transition: "all 0.15s"
642
+ },
643
+ tabBar: {
644
+ display: "flex",
645
+ gap: "4px",
646
+ padding: "4px",
647
+ backgroundColor: "rgba(255,255,255,0.04)",
648
+ borderRadius: "14px",
649
+ marginBottom: "20px"
650
+ },
651
+ walletBtn: (isConnecting) => ({
652
+ width: "100%",
653
+ display: "flex",
654
+ alignItems: "center",
655
+ gap: "14px",
656
+ padding: "16px",
657
+ backgroundColor: "rgba(255,255,255,0.04)",
658
+ border: "1px solid rgba(255,255,255,0.08)",
659
+ borderRadius: "14px",
660
+ cursor: isConnecting ? "wait" : "pointer",
661
+ transition: "all 0.15s",
662
+ color: "inherit"
663
+ }),
664
+ walletIcon: {
665
+ width: "44px",
666
+ height: "44px",
667
+ borderRadius: "12px",
668
+ background: "linear-gradient(135deg, #6EB9A8, #4a9a8a)",
669
+ display: "flex",
670
+ alignItems: "center",
671
+ justifyContent: "center",
672
+ color: "#fff",
673
+ fontWeight: 700,
674
+ fontSize: "18px",
675
+ flexShrink: 0
676
+ },
677
+ badge: (color) => ({
678
+ padding: "5px 10px",
679
+ backgroundColor: `${color}10`,
680
+ borderRadius: "8px",
681
+ border: `1px solid ${color}18`,
682
+ display: "flex",
683
+ alignItems: "center",
684
+ gap: "6px"
685
+ }),
686
+ input: {
687
+ width: "100%",
688
+ padding: "12px 14px",
689
+ backgroundColor: "rgba(255,255,255,0.04)",
690
+ border: "1px solid rgba(255,255,255,0.1)",
691
+ borderRadius: "12px",
692
+ color: "#fff",
693
+ fontSize: "14px",
694
+ marginBottom: "12px",
695
+ outline: "none",
696
+ boxSizing: "border-box"
697
+ },
698
+ primaryBtn: (loading) => ({
699
+ width: "100%",
700
+ padding: "12px",
701
+ backgroundColor: "#6EB9A8",
702
+ border: "none",
703
+ borderRadius: "12px",
704
+ color: "#011B29",
705
+ fontSize: "14px",
706
+ fontWeight: 600,
707
+ cursor: loading ? "wait" : "pointer",
708
+ opacity: loading ? 0.6 : 1
709
+ }),
710
+ error: {
711
+ marginTop: "16px",
712
+ padding: "12px 14px",
713
+ backgroundColor: "rgba(239,68,68,0.08)",
714
+ border: "1px solid rgba(239,68,68,0.15)",
715
+ borderRadius: "12px",
716
+ color: "#f87171",
717
+ fontSize: "13px",
718
+ lineHeight: 1.4
602
719
  }
720
+ };
721
+ function tabStyle(active) {
722
+ return {
723
+ flex: 1,
724
+ padding: "10px 0",
725
+ border: "none",
726
+ borderRadius: "10px",
727
+ fontSize: "13px",
728
+ cursor: "pointer",
729
+ transition: "all 0.2s",
730
+ display: "flex",
731
+ alignItems: "center",
732
+ justifyContent: "center",
733
+ gap: "6px",
734
+ backgroundColor: active ? "rgba(255,255,255,0.08)" : "transparent",
735
+ color: active ? "#6EB9A8" : "rgba(255,255,255,0.4)",
736
+ fontWeight: active ? 600 : 400
737
+ };
603
738
  }
604
- var WalletContext = createContext(null);
605
- var DEFAULT_WALLETS = [
606
- {
607
- id: "sheep-wallet",
608
- name: "Sheep Wallet",
609
- icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSI4IiBmaWxsPSIjMDExQjI5Ii8+PHRleHQgeD0iNTAlIiB5PSI1NSUiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXdlaWdodD0iYm9sZCIgZm9udC1zaXplPSIxNiIgZmlsbD0iIzZFQjlBOCI+Uzwvc2VsZj48L3N2Zz4=",
610
- downloadUrl: "https://rialo.io/wallet",
611
- supportsREX: true,
612
- supportsSfS: true,
613
- supportsScanConnect: true
614
- }
615
- ];
616
- function WalletProvider({
617
- children,
618
- network: initialNetwork = "devnet",
619
- autoConnect = true,
620
- wallets: customWallets = [],
621
- scanConnectRelay = "wss://relay.rialo.io",
622
- onConnect,
623
- onDisconnect,
624
- onError,
625
- onNetworkChange
626
- }) {
627
- const [state, dispatch] = useReducer(walletReducer, {
628
- ...initialState,
629
- network: initialNetwork
630
- });
631
- const walletRef = useRef(null);
632
- const providerRef = useRef(void 0);
633
- const connectingRef = useRef(false);
634
- const mountedRef = useRef(true);
635
- useEffect(() => {
636
- mountedRef.current = true;
637
- walletRef.current = new SheepWallet();
638
- waitForProvider(3e3).then((provider) => {
639
- if (mountedRef.current) {
640
- providerRef.current = provider;
641
- }
642
- });
643
- return () => {
644
- mountedRef.current = false;
645
- walletRef.current?.destroy();
646
- };
647
- }, []);
648
- useEffect(() => {
649
- const setupEvents = async () => {
650
- const provider = await waitForProvider(3e3);
651
- if (!provider || !mountedRef.current) return;
652
- providerRef.current = provider;
653
- const cleanups = [];
654
- cleanups.push(
655
- provider.on("disconnect", () => {
656
- if (!mountedRef.current) return;
657
- dispatch({ type: "DISCONNECTED" });
658
- onDisconnect?.();
659
- })
660
- );
661
- cleanups.push(
662
- provider.on("accountsChanged", (data) => {
663
- if (!mountedRef.current) return;
664
- const accounts = normalizeAccounts(data);
665
- dispatch({ type: "SET_ACCOUNTS", accounts });
666
- })
667
- );
668
- cleanups.push(
669
- provider.on("networkChanged", (data) => {
670
- if (!mountedRef.current) return;
671
- const { network } = data;
672
- if (network) {
673
- dispatch({ type: "SET_NETWORK", network });
674
- onNetworkChange?.(network);
675
- }
676
- })
677
- );
678
- return () => {
679
- cleanups.forEach((fn) => {
680
- try {
681
- fn();
682
- } catch {
739
+ function ExtensionTab({ onConnect, extensionDetected, connecting, error }) {
740
+ return /* @__PURE__ */ jsxs("div", { children: [
741
+ /* @__PURE__ */ jsxs(
742
+ "button",
743
+ {
744
+ type: "button",
745
+ onClick: (e) => {
746
+ e.stopPropagation();
747
+ if (extensionDetected) {
748
+ onConnect();
749
+ } else {
750
+ window.open("https://rialo.io/wallet", "_blank");
683
751
  }
684
- });
685
- };
686
- };
687
- const cleanup = setupEvents();
688
- return () => {
689
- cleanup.then((fn) => fn?.());
690
- };
691
- }, [onDisconnect, onNetworkChange]);
692
- useEffect(() => {
693
- if (!autoConnect) return;
694
- if (state.status === "connected" || state.status === "connecting") return;
695
- const tryAutoConnect = async () => {
696
- const wallet = walletRef.current;
697
- if (!wallet?.isInstalled) return;
698
- try {
699
- const accounts = await wallet.checkSession();
700
- if (accounts && accounts.length > 0 && mountedRef.current) {
701
- dispatch({ type: "CONNECTED", accounts, network: wallet.network });
702
- onConnect?.(accounts);
703
- }
704
- } catch {
752
+ },
753
+ disabled: connecting,
754
+ style: S.walletBtn(connecting),
755
+ onMouseEnter: (e) => {
756
+ e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.07)";
757
+ },
758
+ onMouseLeave: (e) => {
759
+ e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.04)";
760
+ },
761
+ children: [
762
+ /* @__PURE__ */ jsx("div", { style: S.walletIcon, children: "S" }),
763
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, textAlign: "left" }, children: [
764
+ /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontWeight: 500, fontSize: "15px", marginBottom: "2px" }, children: "Sheep Wallet" }),
765
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "12px", color: extensionDetected ? "#6EB9A8" : "rgba(255,255,255,0.4)", display: "flex", alignItems: "center", gap: "5px" }, children: connecting ? /* @__PURE__ */ jsxs(Fragment, { children: [
766
+ /* @__PURE__ */ jsx("span", { style: { display: "inline-block", width: "6px", height: "6px", borderRadius: "50%", backgroundColor: "#F59E0B", animation: "swModalPulse 1.2s infinite" } }),
767
+ "Connecting..."
768
+ ] }) : extensionDetected ? /* @__PURE__ */ jsxs(Fragment, { children: [
769
+ /* @__PURE__ */ jsx("span", { style: { display: "inline-block", width: "6px", height: "6px", borderRadius: "50%", backgroundColor: "#6EB9A8" } }),
770
+ "Detected \u2014 Click to connect"
771
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
772
+ /* @__PURE__ */ jsx("span", { style: { display: "inline-block", width: "6px", height: "6px", borderRadius: "50%", backgroundColor: "rgba(255,255,255,0.2)" } }),
773
+ "Not installed"
774
+ ] }) })
775
+ ] }),
776
+ !extensionDetected && /* @__PURE__ */ jsx("span", { style: { color: "#6EB9A8", fontSize: "12px", fontWeight: 500, padding: "4px 10px", borderRadius: "8px", backgroundColor: "rgba(110,185,168,0.1)" }, children: "Install" })
777
+ ]
705
778
  }
706
- };
707
- const timer = setTimeout(tryAutoConnect, 500);
708
- return () => clearTimeout(timer);
709
- }, [autoConnect, onConnect]);
779
+ ),
780
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "8px", marginTop: "16px", flexWrap: "wrap" }, children: [
781
+ { label: "REX", desc: "Confidential TX", color: "#8B5CF6" },
782
+ { label: "SfS", desc: "Gasless TX", color: "#F59E0B" },
783
+ { label: "QR", desc: "Scan Connect", color: "#6EB9A8" }
784
+ ].map((b) => /* @__PURE__ */ jsxs("div", { style: S.badge(b.color), children: [
785
+ /* @__PURE__ */ jsx("span", { style: { color: b.color, fontSize: "11px", fontWeight: 600 }, children: b.label }),
786
+ /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.35)", fontSize: "10px" }, children: b.desc })
787
+ ] }, b.label)) }),
788
+ error && /* @__PURE__ */ jsx("div", { style: S.error, children: error.message })
789
+ ] });
790
+ }
791
+ function ScanTab({ scanConnectRelay }) {
792
+ const [scanMode, setScanMode] = useState("show-qr");
793
+ const [generatedURI, setGeneratedURI] = useState(null);
794
+ const canvasRef = useRef(null);
795
+ const generateSession = useCallback(() => {
796
+ const sessionId = crypto.randomUUID();
797
+ const topic = btoa(sessionId).slice(0, 32);
798
+ const uri = `rialo-wc://${sessionId}?relay=${encodeURIComponent(scanConnectRelay)}&topic=${topic}&networks=devnet`;
799
+ setGeneratedURI(uri);
800
+ }, [scanConnectRelay]);
710
801
  useEffect(() => {
711
- if (state.status !== "connected" || state.accounts.length === 0) return;
712
- const refresh = async () => {
713
- const wallet = walletRef.current;
714
- if (!wallet || !mountedRef.current) return;
715
- try {
716
- const balance = await wallet.getBalance();
717
- if (mountedRef.current) {
718
- dispatch({ type: "SET_BALANCE", balance });
802
+ generateSession();
803
+ }, [generateSession]);
804
+ useEffect(() => {
805
+ if (!generatedURI || !canvasRef.current) return;
806
+ const canvas = canvasRef.current;
807
+ const ctx = canvas.getContext("2d");
808
+ if (!ctx) return;
809
+ const size = 200;
810
+ canvas.width = size;
811
+ canvas.height = size;
812
+ ctx.fillStyle = "#FFFFFF";
813
+ ctx.fillRect(0, 0, size, size);
814
+ ctx.fillStyle = "#011B29";
815
+ const gridSize = 25;
816
+ const cellSize = size / gridSize;
817
+ for (let y = 0; y < gridSize; y++) {
818
+ for (let x = 0; x < gridSize; x++) {
819
+ const isFinderArea = x < 7 && y < 7 || x >= gridSize - 7 && y < 7 || x < 7 && y >= gridSize - 7;
820
+ if (isFinderArea) {
821
+ const isOuter = x === 0 || y === 0 || x === 6 || y === 6 || x === gridSize - 7 || x === gridSize - 1 || y === gridSize - 7 || y === gridSize - 1;
822
+ const isInner = x >= 2 && x <= 4 && y >= 2 && y <= 4 || x >= gridSize - 5 && x <= gridSize - 3 && y >= 2 && y <= 4 || x >= 2 && x <= 4 && y >= gridSize - 5 && y <= gridSize - 3;
823
+ if (isOuter || isInner) ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
824
+ continue;
719
825
  }
720
- } catch {
826
+ const charIndex = (y * gridSize + x) % generatedURI.length;
827
+ const charCode = generatedURI.charCodeAt(charIndex);
828
+ if (charCode % 3 !== 0) ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
721
829
  }
722
- };
723
- refresh();
724
- const interval = setInterval(refresh, 3e4);
725
- return () => clearInterval(interval);
726
- }, [state.status, state.accounts]);
727
- const connect = useCallback(async () => {
728
- if (connectingRef.current) {
729
- throw new Error("Connection already in progress");
730
830
  }
731
- const wallet = walletRef.current;
732
- if (!wallet) {
733
- const err = new Error("Wallet not initialized");
734
- dispatch({ type: "ERROR", error: err });
735
- throw err;
736
- }
737
- if (!wallet.isInstalled) {
738
- dispatch({ type: "OPEN_MODAL" });
739
- const err = new Error("Sheep Wallet not installed");
740
- dispatch({ type: "ERROR", error: err });
741
- onError?.(err);
742
- throw err;
743
- }
744
- connectingRef.current = true;
745
- dispatch({ type: "CONNECTING" });
831
+ const logoSize = 40;
832
+ const logoX = (size - logoSize) / 2;
833
+ const logoY = (size - logoSize) / 2;
834
+ ctx.beginPath();
835
+ ctx.arc(size / 2, size / 2, logoSize / 2 + 4, 0, Math.PI * 2);
836
+ ctx.fillStyle = "#FFFFFF";
837
+ ctx.fill();
838
+ ctx.beginPath();
839
+ const r = 6;
840
+ ctx.moveTo(logoX + r, logoY);
841
+ ctx.lineTo(logoX + logoSize - r, logoY);
842
+ ctx.quadraticCurveTo(logoX + logoSize, logoY, logoX + logoSize, logoY + r);
843
+ ctx.lineTo(logoX + logoSize, logoY + logoSize - r);
844
+ ctx.quadraticCurveTo(logoX + logoSize, logoY + logoSize, logoX + logoSize - r, logoY + logoSize);
845
+ ctx.lineTo(logoX + r, logoY + logoSize);
846
+ ctx.quadraticCurveTo(logoX, logoY + logoSize, logoX, logoY + logoSize - r);
847
+ ctx.lineTo(logoX, logoY + r);
848
+ ctx.quadraticCurveTo(logoX, logoY, logoX + r, logoY);
849
+ ctx.fillStyle = "#6EB9A8";
850
+ ctx.fill();
851
+ ctx.fillStyle = "#FFFFFF";
852
+ ctx.font = "bold 22px sans-serif";
853
+ ctx.textAlign = "center";
854
+ ctx.textBaseline = "middle";
855
+ ctx.fillText("S", size / 2, size / 2 + 1);
856
+ }, [generatedURI]);
857
+ const stop = (e) => e.stopPropagation();
858
+ return /* @__PURE__ */ jsxs("div", { children: [
859
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "8px", marginBottom: "16px" }, children: ["show-qr", "scan-qr"].map((mode) => /* @__PURE__ */ jsx(
860
+ "button",
861
+ {
862
+ type: "button",
863
+ onClick: (e) => {
864
+ stop(e);
865
+ setScanMode(mode);
866
+ },
867
+ style: {
868
+ flex: 1,
869
+ padding: "8px",
870
+ display: "flex",
871
+ alignItems: "center",
872
+ justifyContent: "center",
873
+ gap: "6px",
874
+ backgroundColor: scanMode === mode ? "rgba(110,185,168,0.1)" : "transparent",
875
+ border: `1px solid ${scanMode === mode ? "rgba(110,185,168,0.25)" : "rgba(255,255,255,0.08)"}`,
876
+ borderRadius: "10px",
877
+ color: scanMode === mode ? "#6EB9A8" : "rgba(255,255,255,0.4)",
878
+ fontSize: "12px",
879
+ fontWeight: scanMode === mode ? 500 : 400,
880
+ cursor: "pointer",
881
+ transition: "all 0.15s"
882
+ },
883
+ children: mode === "show-qr" ? /* @__PURE__ */ jsxs(Fragment, { children: [
884
+ /* @__PURE__ */ jsx(IconQR, {}),
885
+ " Show QR"
886
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
887
+ /* @__PURE__ */ jsx(IconCamera, {}),
888
+ " Scan QR"
889
+ ] })
890
+ },
891
+ mode
892
+ )) }),
893
+ scanMode === "show-qr" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
894
+ /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.45)", fontSize: "13px", marginBottom: "16px", lineHeight: 1.5 }, children: "Scan this QR code with Sheep Wallet on your mobile device" }),
895
+ /* @__PURE__ */ jsxs("div", { style: { display: "inline-block", padding: "16px", backgroundColor: "#FFFFFF", borderRadius: "16px", position: "relative" }, children: [
896
+ [
897
+ { top: -2, left: -2, borderTop: "3px solid #6EB9A8", borderLeft: "3px solid #6EB9A8", borderTopLeftRadius: "12px" },
898
+ { top: -2, right: -2, borderTop: "3px solid #6EB9A8", borderRight: "3px solid #6EB9A8", borderTopRightRadius: "12px" },
899
+ { bottom: -2, left: -2, borderBottom: "3px solid #6EB9A8", borderLeft: "3px solid #6EB9A8", borderBottomLeftRadius: "12px" },
900
+ { bottom: -2, right: -2, borderBottom: "3px solid #6EB9A8", borderRight: "3px solid #6EB9A8", borderBottomRightRadius: "12px" }
901
+ ].map((s, i) => /* @__PURE__ */ jsx("div", { style: { position: "absolute", width: "24px", height: "24px", ...s } }, i)),
902
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, style: { width: "200px", height: "200px", display: "block" } })
903
+ ] }),
904
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "16px", display: "flex", alignItems: "center", justifyContent: "center", gap: "8px" }, children: [
905
+ /* @__PURE__ */ jsx("div", { style: { width: "7px", height: "7px", borderRadius: "50%", backgroundColor: "#6EB9A8", animation: "swModalPulse 2s infinite" } }),
906
+ /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.4)", fontSize: "12px" }, children: "Waiting for wallet to scan..." })
907
+ ] }),
908
+ /* @__PURE__ */ jsxs(
909
+ "button",
910
+ {
911
+ type: "button",
912
+ onClick: (e) => {
913
+ stop(e);
914
+ generateSession();
915
+ },
916
+ style: { marginTop: "12px", padding: "8px 16px", backgroundColor: "transparent", border: "1px solid rgba(255,255,255,0.08)", borderRadius: "10px", color: "#6EB9A8", fontSize: "12px", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: "6px" },
917
+ children: [
918
+ /* @__PURE__ */ jsx(IconRefresh, {}),
919
+ " Refresh QR"
920
+ ]
921
+ }
922
+ )
923
+ ] }),
924
+ scanMode === "scan-qr" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
925
+ /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.45)", fontSize: "13px", marginBottom: "16px", lineHeight: 1.5 }, children: "Open Sheep Wallet on another device, go to Settings \u2192 Show Connect QR, then scan it here" }),
926
+ /* @__PURE__ */ jsxs("div", { style: {
927
+ width: "100%",
928
+ height: "200px",
929
+ backgroundColor: "rgba(255,255,255,0.02)",
930
+ borderRadius: "14px",
931
+ border: "1px dashed rgba(255,255,255,0.1)",
932
+ display: "flex",
933
+ flexDirection: "column",
934
+ alignItems: "center",
935
+ justifyContent: "center",
936
+ gap: "12px"
937
+ }, children: [
938
+ /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.2)" }, children: /* @__PURE__ */ jsx(IconCamera, {}) }),
939
+ /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.3)", fontSize: "13px" }, children: "Camera access required" }),
940
+ /* @__PURE__ */ jsx(
941
+ "button",
942
+ {
943
+ type: "button",
944
+ onClick: (e) => {
945
+ stop(e);
946
+ alert("Camera scanning requires HTTPS. This feature works in the installed extension.");
947
+ },
948
+ style: { padding: "8px 20px", backgroundColor: "#6EB9A8", border: "none", borderRadius: "10px", color: "#011B29", fontSize: "13px", fontWeight: 500, cursor: "pointer" },
949
+ children: "Open Camera"
950
+ }
951
+ )
952
+ ] })
953
+ ] })
954
+ ] });
955
+ }
956
+ function WebWalletTab({ onConnected }) {
957
+ const { connect } = useWallet();
958
+ const [mode, setMode] = useState("menu");
959
+ const [password, setPassword] = useState("");
960
+ const [mnemonic, setMnemonic] = useState("");
961
+ const [generatedMnemonic, setGeneratedMnemonic] = useState("");
962
+ const [error, setError] = useState("");
963
+ const [loading, setLoading] = useState(false);
964
+ const stop = (e) => e.stopPropagation();
965
+ const webProvider = typeof window !== "undefined" ? window.rialo : null;
966
+ const isWebProvider = webProvider?.isWebWallet;
967
+ const hasWallet = isWebProvider && webProvider?.hasWallet?.();
968
+ const isUnlocked = isWebProvider && webProvider?.isUnlocked?.();
969
+ const handleAction = async (action) => {
970
+ setError("");
971
+ setLoading(true);
746
972
  try {
747
- const accounts = await wallet.connect();
748
- if (mountedRef.current) {
749
- dispatch({ type: "CONNECTED", accounts, network: wallet.network });
750
- onConnect?.(accounts);
973
+ if (!webProvider?.isWebWallet) {
974
+ throw new Error("Web wallet provider not available. Make sure registerWebWallet() is called at app startup.");
751
975
  }
752
- return accounts;
753
- } catch (error) {
754
- const err = error instanceof Error ? error : new Error(String(error));
755
- if (mountedRef.current) {
756
- dispatch({ type: "ERROR", error: err });
757
- onError?.(err);
976
+ if (action === "create") {
977
+ if (!password || password.length < 6) throw new Error("Password must be at least 6 characters");
978
+ const result = await webProvider.createWallet(password);
979
+ setGeneratedMnemonic(result.mnemonic);
980
+ try {
981
+ await connect();
982
+ onConnected();
983
+ } catch {
984
+ }
985
+ } else if (action === "import") {
986
+ if (!mnemonic.trim()) throw new Error("Please enter your recovery phrase");
987
+ if (!password || password.length < 6) throw new Error("Password must be at least 6 characters");
988
+ await webProvider.importWallet(mnemonic.trim(), password);
989
+ try {
990
+ await connect();
991
+ onConnected();
992
+ } catch {
993
+ }
994
+ } else if (action === "unlock") {
995
+ if (!password) throw new Error("Please enter your password");
996
+ const unlocked = await webProvider.unlock(password);
997
+ if (!unlocked) throw new Error("Incorrect password");
998
+ try {
999
+ await connect();
1000
+ onConnected();
1001
+ } catch {
1002
+ }
758
1003
  }
759
- throw err;
1004
+ } catch (err) {
1005
+ setError(err?.message || "Operation failed");
760
1006
  } finally {
761
- connectingRef.current = false;
762
- }
763
- }, [onConnect, onError]);
764
- const disconnect = useCallback(async () => {
765
- const wallet = walletRef.current;
766
- if (!wallet) return;
767
- try {
768
- await wallet.disconnect();
769
- } catch {
770
- }
771
- if (mountedRef.current) {
772
- dispatch({ type: "DISCONNECTED" });
773
- onDisconnect?.();
774
- }
775
- }, [onDisconnect]);
776
- const refreshBalance = useCallback(async () => {
777
- const wallet = walletRef.current;
778
- if (!wallet || state.status !== "connected") return;
779
- try {
780
- const balance = await wallet.getBalance();
781
- if (mountedRef.current) {
782
- dispatch({ type: "SET_BALANCE", balance });
783
- }
784
- } catch {
785
- }
786
- }, [state.status]);
787
- const switchNetwork = useCallback(async (network) => {
788
- const wallet = walletRef.current;
789
- if (!wallet) return;
790
- await wallet.switchNetwork(network);
791
- if (mountedRef.current) {
792
- dispatch({ type: "SET_NETWORK", network });
793
- onNetworkChange?.(network);
794
- }
795
- }, [onNetworkChange]);
796
- const signMessage = useCallback(async (message) => {
797
- const wallet = walletRef.current;
798
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
799
- return wallet.signMessage(message);
800
- }, [state.status]);
801
- const signTransaction = useCallback(async (tx) => {
802
- const wallet = walletRef.current;
803
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
804
- return wallet.signTransaction(tx);
805
- }, [state.status]);
806
- const sendTransaction = useCallback(async (tx) => {
807
- const wallet = walletRef.current;
808
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
809
- return wallet.sendTransaction(tx);
810
- }, [state.status]);
811
- const signAndSendTransaction = useCallback(async (tx) => {
812
- const wallet = walletRef.current;
813
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
814
- return wallet.signAndSendTransaction(tx);
815
- }, [state.status]);
816
- const getREXCapabilities = useCallback(async () => {
817
- const wallet = walletRef.current;
818
- if (!wallet) return { supported: false, privacyModes: [], maxInputSize: 0, programs: [] };
819
- return wallet.getREXCapabilities();
820
- }, []);
821
- const submitREXTransaction = useCallback(async (tx) => {
822
- const wallet = walletRef.current;
823
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
824
- return wallet.submitREXTransaction(tx);
825
- }, [state.status]);
826
- const sendGaslessTransaction = useCallback(async (tx) => {
827
- const wallet = walletRef.current;
828
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
829
- return wallet.sendGaslessTransaction(tx);
830
- }, [state.status]);
831
- const getSfSPositions = useCallback(async () => {
832
- const wallet = walletRef.current;
833
- if (!wallet || state.status !== "connected") return [];
834
- return wallet.getSfSPositions();
835
- }, [state.status]);
836
- const createSfSPosition = useCallback(async (params) => {
837
- const wallet = walletRef.current;
838
- if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
839
- return wallet.createSfSPosition(params);
840
- }, [state.status]);
841
- const getSfSCredits = useCallback(async () => {
842
- const wallet = walletRef.current;
843
- if (!wallet || state.status !== "connected") {
844
- return { available: "0", usedThisEpoch: "0", totalEarned: "0", estimatedPerEpoch: "0" };
1007
+ setLoading(false);
845
1008
  }
846
- return wallet.getSfSCredits();
847
- }, [state.status]);
848
- const openModal = useCallback(() => dispatch({ type: "OPEN_MODAL" }), []);
849
- const closeModal = useCallback(() => dispatch({ type: "CLOSE_MODAL" }), []);
850
- const wallets = useMemo(() => {
851
- const installed = isInstalled();
852
- return [...DEFAULT_WALLETS, ...customWallets].map((w) => ({
853
- ...w,
854
- installed: w.id === "sheep-wallet" ? installed : false
855
- }));
856
- }, [customWallets]);
857
- const value = useMemo(() => ({
858
- state,
859
- connected: state.status === "connected",
860
- connecting: state.status === "connecting",
861
- activeAccount: state.accounts[0] || null,
862
- chainId: `rialo:${state.network}`,
863
- isInstalled: isInstalled(),
864
- wallets,
865
- connect,
866
- disconnect,
867
- refreshBalance,
868
- switchNetwork,
869
- signMessage,
870
- signTransaction,
871
- sendTransaction,
872
- signAndSendTransaction,
873
- getREXCapabilities,
874
- submitREXTransaction,
875
- sendGaslessTransaction,
876
- getSfSPositions,
877
- createSfSPosition,
878
- getSfSCredits,
879
- openModal,
880
- closeModal,
881
- wallet: walletRef.current
882
- }), [
883
- state,
884
- wallets,
885
- connect,
886
- disconnect,
887
- refreshBalance,
888
- switchNetwork,
889
- signMessage,
890
- signTransaction,
891
- sendTransaction,
892
- signAndSendTransaction,
893
- getREXCapabilities,
894
- submitREXTransaction,
895
- sendGaslessTransaction,
896
- getSfSPositions,
897
- createSfSPosition,
898
- getSfSCredits,
899
- openModal,
900
- closeModal
901
- ]);
902
- return /* @__PURE__ */ jsxs(WalletContext.Provider, { value, children: [
903
- children,
904
- /* @__PURE__ */ jsx(
905
- WalletModal,
906
- {
907
- scanConnectRelay,
908
- dispatch
909
- }
910
- )
911
- ] });
912
- }
913
- function useWallet() {
914
- const context = useContext(WalletContext);
915
- if (!context) {
916
- throw new Error("useWallet must be used within WalletProvider");
917
- }
918
- return context;
919
- }
920
- function useIsConnected() {
921
- return useWallet().connected;
922
- }
923
- function useActiveAccount() {
924
- return useWallet().activeAccount;
925
- }
926
- function useAccounts() {
927
- return useWallet().state.accounts;
928
- }
929
- function useBalance() {
930
- const { state, refreshBalance } = useWallet();
931
- return { balance: state.balance, refresh: refreshBalance };
932
- }
933
- function useNetwork() {
934
- const { state, chainId } = useWallet();
935
- return { network: state.network, chainId };
936
- }
937
- function useSwitchNetwork() {
938
- const { switchNetwork, state } = useWallet();
939
- return { switchNetwork, network: state.network };
940
- }
941
- function useConnectWallet() {
942
- const { connect, openModal, connecting, connected, isInstalled: isInstalled2, state } = useWallet();
943
- return { connect, openModal, connecting, connected, isInstalled: isInstalled2, error: state.error };
944
- }
945
- function useDisconnectWallet() {
946
- const { disconnect, connected } = useWallet();
947
- return { disconnect, connected };
948
- }
949
- function useSignMessage() {
950
- const { signMessage, connected } = useWallet();
951
- return { signMessage, connected };
952
- }
953
- function useSendTransaction() {
954
- const { sendTransaction, signAndSendTransaction, sendGaslessTransaction, connected } = useWallet();
955
- return { sendTransaction, signAndSendTransaction, sendGaslessTransaction, connected };
956
- }
957
- function useREX() {
958
- const { getREXCapabilities, submitREXTransaction, connected } = useWallet();
959
- const [capabilities, setCapabilities] = useState(null);
960
- useEffect(() => {
961
- if (connected) {
962
- getREXCapabilities().then(setCapabilities).catch(() => {
963
- });
964
- }
965
- }, [connected, getREXCapabilities]);
966
- return {
967
- capabilities,
968
- supported: capabilities?.supported ?? false,
969
- submitREXTransaction,
970
- connected
971
- };
972
- }
973
- function useSfS() {
974
- const { getSfSPositions, createSfSPosition, getSfSCredits, sendGaslessTransaction, connected } = useWallet();
975
- const [positions, setPositions] = useState([]);
976
- const [credits, setCredits] = useState(null);
977
- const refresh = useCallback(async () => {
978
- if (!connected) return;
979
- const [pos, cred] = await Promise.all([
980
- getSfSPositions().catch(() => []),
981
- getSfSCredits().catch(() => null)
982
- ]);
983
- setPositions(pos);
984
- if (cred) setCredits(cred);
985
- }, [connected, getSfSPositions, getSfSCredits]);
986
- useEffect(() => {
987
- refresh();
988
- }, [refresh]);
989
- return {
990
- positions,
991
- credits,
992
- hasCredits: BigInt(credits?.available ?? "0") > 0n,
993
- createPosition: createSfSPosition,
994
- sendGasless: sendGaslessTransaction,
995
- refresh,
996
- connected
997
1009
  };
1010
+ if (generatedMnemonic) {
1011
+ return /* @__PURE__ */ jsxs("div", { children: [
1012
+ /* @__PURE__ */ jsxs("div", { style: { padding: "14px", backgroundColor: "rgba(110,185,168,0.08)", border: "1px solid rgba(110,185,168,0.2)", borderRadius: "12px", marginBottom: "16px" }, children: [
1013
+ /* @__PURE__ */ jsx("div", { style: { color: "#6EB9A8", fontSize: "12px", fontWeight: 600, marginBottom: "8px" }, children: "Wallet Created & Connected!" }),
1014
+ /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.6)", fontSize: "11px", marginBottom: "8px" }, children: "Save your recovery phrase securely:" }),
1015
+ /* @__PURE__ */ jsx("div", { style: { padding: "10px", backgroundColor: "rgba(0,0,0,0.3)", borderRadius: "8px", color: "#fff", fontSize: "12px", fontFamily: "monospace", wordBreak: "break-word", lineHeight: 1.6 }, children: generatedMnemonic })
1016
+ ] }),
1017
+ /* @__PURE__ */ jsx(
1018
+ "button",
1019
+ {
1020
+ type: "button",
1021
+ onClick: (e) => {
1022
+ stop(e);
1023
+ setGeneratedMnemonic("");
1024
+ onConnected();
1025
+ },
1026
+ style: S.primaryBtn(false),
1027
+ children: "I've Saved It \u2014 Continue"
1028
+ }
1029
+ )
1030
+ ] });
1031
+ }
1032
+ if (hasWallet && !isUnlocked) {
1033
+ return /* @__PURE__ */ jsxs("div", { children: [
1034
+ /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.5)", fontSize: "13px", marginBottom: "16px" }, children: "Web wallet locked. Enter password to connect." }),
1035
+ /* @__PURE__ */ jsx(
1036
+ "input",
1037
+ {
1038
+ type: "password",
1039
+ placeholder: "Password",
1040
+ value: password,
1041
+ onChange: (e) => setPassword(e.target.value),
1042
+ onClick: stop,
1043
+ onKeyDown: (e) => {
1044
+ if (e.key === "Enter") handleAction("unlock");
1045
+ },
1046
+ style: S.input
1047
+ }
1048
+ ),
1049
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (e) => {
1050
+ stop(e);
1051
+ handleAction("unlock");
1052
+ }, disabled: loading, style: S.primaryBtn(loading), children: loading ? "Unlocking..." : "Unlock & Connect" }),
1053
+ error && /* @__PURE__ */ jsx("div", { style: S.error, children: error })
1054
+ ] });
1055
+ }
1056
+ if (hasWallet && isUnlocked) {
1057
+ return /* @__PURE__ */ jsxs("div", { children: [
1058
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: async (e) => {
1059
+ stop(e);
1060
+ setLoading(true);
1061
+ try {
1062
+ await connect();
1063
+ onConnected();
1064
+ } catch (err) {
1065
+ setError(err?.message || "Failed");
1066
+ } finally {
1067
+ setLoading(false);
1068
+ }
1069
+ }, disabled: loading, style: S.walletBtn(loading), children: [
1070
+ /* @__PURE__ */ jsx("div", { style: { width: "44px", height: "44px", borderRadius: "12px", background: "linear-gradient(135deg, #3B82F6, #6366F1)", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", flexShrink: 0 }, children: /* @__PURE__ */ jsx(IconWeb, {}) }),
1071
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, textAlign: "left" }, children: [
1072
+ /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontWeight: 500, fontSize: "15px" }, children: "Web Wallet" }),
1073
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "12px", color: "#6EB9A8" }, children: loading ? "Connecting..." : "Ready \u2014 Click to connect" })
1074
+ ] })
1075
+ ] }),
1076
+ error && /* @__PURE__ */ jsx("div", { style: S.error, children: error })
1077
+ ] });
1078
+ }
1079
+ if (mode === "create") {
1080
+ return /* @__PURE__ */ jsxs("div", { children: [
1081
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (e) => {
1082
+ stop(e);
1083
+ setMode("menu");
1084
+ }, style: { background: "none", border: "none", color: "rgba(255,255,255,0.4)", cursor: "pointer", fontSize: "12px", marginBottom: "12px", padding: 0 }, children: "\u2190 Back" }),
1085
+ /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.5)", fontSize: "13px", marginBottom: "16px" }, children: "Create a new web wallet with a generated recovery phrase." }),
1086
+ /* @__PURE__ */ jsx(
1087
+ "input",
1088
+ {
1089
+ type: "password",
1090
+ placeholder: "Set password (min 6 chars)",
1091
+ value: password,
1092
+ onChange: (e) => setPassword(e.target.value),
1093
+ onClick: stop,
1094
+ style: S.input
1095
+ }
1096
+ ),
1097
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (e) => {
1098
+ stop(e);
1099
+ handleAction("create");
1100
+ }, disabled: loading, style: S.primaryBtn(loading), children: loading ? "Creating..." : "Create Wallet" }),
1101
+ error && /* @__PURE__ */ jsx("div", { style: S.error, children: error })
1102
+ ] });
1103
+ }
1104
+ if (mode === "import") {
1105
+ return /* @__PURE__ */ jsxs("div", { children: [
1106
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (e) => {
1107
+ stop(e);
1108
+ setMode("menu");
1109
+ }, style: { background: "none", border: "none", color: "rgba(255,255,255,0.4)", cursor: "pointer", fontSize: "12px", marginBottom: "12px", padding: 0 }, children: "\u2190 Back" }),
1110
+ /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.5)", fontSize: "13px", marginBottom: "16px" }, children: "Import wallet from recovery phrase." }),
1111
+ /* @__PURE__ */ jsx(
1112
+ "textarea",
1113
+ {
1114
+ placeholder: "Enter 12 or 24 word recovery phrase",
1115
+ value: mnemonic,
1116
+ onChange: (e) => setMnemonic(e.target.value),
1117
+ onClick: stop,
1118
+ style: { ...S.input, minHeight: "80px", resize: "vertical", fontFamily: "monospace", fontSize: "13px" }
1119
+ }
1120
+ ),
1121
+ /* @__PURE__ */ jsx(
1122
+ "input",
1123
+ {
1124
+ type: "password",
1125
+ placeholder: "Set password (min 6 chars)",
1126
+ value: password,
1127
+ onChange: (e) => setPassword(e.target.value),
1128
+ onClick: stop,
1129
+ style: S.input
1130
+ }
1131
+ ),
1132
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (e) => {
1133
+ stop(e);
1134
+ handleAction("import");
1135
+ }, disabled: loading, style: S.primaryBtn(loading), children: loading ? "Importing..." : "Import & Connect" }),
1136
+ error && /* @__PURE__ */ jsx("div", { style: S.error, children: error })
1137
+ ] });
1138
+ }
1139
+ return /* @__PURE__ */ jsxs("div", { children: [
1140
+ /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.5)", fontSize: "13px", marginBottom: "16px", lineHeight: 1.5 }, children: isWebProvider ? "Create or import a web wallet \u2014 no extension needed." : "Web wallet not available. Call registerWebWallet() at app startup." }),
1141
+ isWebProvider && /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "10px" }, children: [
1142
+ /* @__PURE__ */ jsxs(
1143
+ "button",
1144
+ {
1145
+ type: "button",
1146
+ onClick: (e) => {
1147
+ stop(e);
1148
+ setMode("create");
1149
+ setError("");
1150
+ },
1151
+ style: S.walletBtn(false),
1152
+ children: [
1153
+ /* @__PURE__ */ jsx("div", { style: { width: "40px", height: "40px", borderRadius: "10px", background: "linear-gradient(135deg, #3B82F6, #6366F1)", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", fontSize: "18px", flexShrink: 0 }, children: "+" }),
1154
+ /* @__PURE__ */ jsxs("div", { style: { textAlign: "left" }, children: [
1155
+ /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontWeight: 500, fontSize: "14px" }, children: "Create New Wallet" }),
1156
+ /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.4)", fontSize: "11px" }, children: "Generate a new recovery phrase" })
1157
+ ] })
1158
+ ]
1159
+ }
1160
+ ),
1161
+ /* @__PURE__ */ jsxs(
1162
+ "button",
1163
+ {
1164
+ type: "button",
1165
+ onClick: (e) => {
1166
+ stop(e);
1167
+ setMode("import");
1168
+ setError("");
1169
+ },
1170
+ style: S.walletBtn(false),
1171
+ children: [
1172
+ /* @__PURE__ */ jsx("div", { style: { width: "40px", height: "40px", borderRadius: "10px", background: "linear-gradient(135deg, #F59E0B, #EF4444)", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", flexShrink: 0 }, children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1173
+ /* @__PURE__ */ jsx("path", { d: "M12 3v12" }),
1174
+ /* @__PURE__ */ jsx("path", { d: "m8 11 4 4 4-4" }),
1175
+ /* @__PURE__ */ jsx("path", { d: "M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4" })
1176
+ ] }) }),
1177
+ /* @__PURE__ */ jsxs("div", { style: { textAlign: "left" }, children: [
1178
+ /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontWeight: 500, fontSize: "14px" }, children: "Import Wallet" }),
1179
+ /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.4)", fontSize: "11px" }, children: "Use existing recovery phrase" })
1180
+ ] })
1181
+ ]
1182
+ }
1183
+ )
1184
+ ] }),
1185
+ error && /* @__PURE__ */ jsx("div", { style: S.error, children: error })
1186
+ ] });
998
1187
  }
999
- function useScanConnect() {
1000
- const { wallet, state } = useWallet();
1001
- const [scanURI, setScanURI] = useState(null);
1002
- const [status, setStatus] = useState("idle");
1003
- const generateQR = useCallback(async () => {
1004
- if (!wallet) return;
1005
- setStatus("generating");
1006
- try {
1007
- const uri = await wallet.createScanSession();
1008
- setScanURI(uri);
1009
- setStatus("waiting");
1010
- } catch (error) {
1011
- setStatus("error");
1012
- }
1013
- }, [wallet]);
1014
- const approveSession = useCallback(async (sessionId) => {
1015
- if (!wallet) return [];
1016
- setStatus("approving");
1017
- try {
1018
- const accounts = await wallet.approveScanSession(sessionId);
1019
- setStatus("connected");
1020
- return accounts;
1021
- } catch {
1022
- setStatus("error");
1023
- return [];
1024
- }
1025
- }, [wallet]);
1026
- const rejectSession = useCallback(async (sessionId) => {
1027
- if (!wallet) return;
1028
- await wallet.rejectScanSession(sessionId);
1029
- setStatus("rejected");
1030
- }, [wallet]);
1031
- return {
1032
- scanURI,
1033
- status,
1034
- generateQR,
1035
- approveSession,
1036
- rejectSession
1037
- };
1188
+ function SuccessView({ address }) {
1189
+ return /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
1190
+ /* @__PURE__ */ jsx("div", { style: {
1191
+ width: "56px",
1192
+ height: "56px",
1193
+ borderRadius: "50%",
1194
+ backgroundColor: "rgba(110,185,168,0.12)",
1195
+ border: "2px solid rgba(110,185,168,0.3)",
1196
+ display: "flex",
1197
+ alignItems: "center",
1198
+ justifyContent: "center",
1199
+ margin: "0 auto 16px"
1200
+ }, children: /* @__PURE__ */ jsx(IconCheck, {}) }),
1201
+ /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontSize: "16px", fontWeight: 600, marginBottom: "6px" }, children: "Connected!" }),
1202
+ /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.4)", fontSize: "13px", fontFamily: "monospace" }, children: formatAddress(address) })
1203
+ ] });
1038
1204
  }
1039
- function WalletModal({ scanConnectRelay, dispatch }) {
1040
- const { state, wallets, connect, closeModal, isInstalled: installed } = useWallet();
1205
+ function WalletModal({ scanConnectRelay }) {
1206
+ const { state, connect, closeModal } = useWallet();
1041
1207
  const [activeTab, setActiveTab] = useState("extension");
1042
- const [scanMode, setScanMode] = useState("show-qr");
1043
- const [generatedURI, setGeneratedURI] = useState(null);
1044
- const [qrReady, setQRReady] = useState(false);
1045
- const canvasRef = useRef(null);
1046
1208
  const [isMobile, setIsMobile] = useState(false);
1209
+ const [showSuccess, setShowSuccess] = useState(false);
1210
+ const statusOnOpenRef = useRef(null);
1211
+ const successTimerRef = useRef(null);
1047
1212
  const [dragY, setDragY] = useState(0);
1048
1213
  const [isDragging, setIsDragging] = useState(false);
1049
1214
  const dragStartRef = useRef(0);
1050
- const sheepWallet = wallets.find((w) => w.id === "sheep-wallet");
1215
+ const extensionDetected = isInstalled();
1051
1216
  useEffect(() => {
1052
1217
  const check = () => setIsMobile(window.innerWidth < 640);
1053
1218
  check();
1054
1219
  window.addEventListener("resize", check);
1055
1220
  return () => window.removeEventListener("resize", check);
1056
1221
  }, []);
1222
+ useEffect(() => {
1223
+ if (state.isModalOpen) {
1224
+ statusOnOpenRef.current = state.status;
1225
+ setActiveTab("extension");
1226
+ setShowSuccess(false);
1227
+ } else {
1228
+ statusOnOpenRef.current = null;
1229
+ }
1230
+ }, [state.isModalOpen]);
1231
+ useEffect(() => {
1232
+ if (state.isModalOpen && state.status === "connected" && state.accounts.length > 0 && !showSuccess && statusOnOpenRef.current !== "connected") {
1233
+ setShowSuccess(true);
1234
+ successTimerRef.current = setTimeout(() => {
1235
+ closeModal();
1236
+ setShowSuccess(false);
1237
+ }, 1500);
1238
+ }
1239
+ return () => {
1240
+ if (successTimerRef.current) clearTimeout(successTimerRef.current);
1241
+ };
1242
+ }, [state.isModalOpen, state.status, state.accounts.length, showSuccess, closeModal]);
1057
1243
  useEffect(() => {
1058
1244
  if (!state.isModalOpen) {
1245
+ setShowSuccess(false);
1059
1246
  setDragY(0);
1060
1247
  setIsDragging(false);
1061
1248
  }
1062
1249
  }, [state.isModalOpen]);
1063
- const stop = (e) => e.stopPropagation();
1064
- const handleConnect = async (e) => {
1065
- e.stopPropagation();
1066
- e.preventDefault();
1250
+ const handleExtensionConnect = useCallback(async () => {
1067
1251
  try {
1068
1252
  await connect();
1069
1253
  } catch {
1070
1254
  }
1071
- };
1072
- const generateScanSession = async () => {
1073
- dispatch({ type: "SET_SCAN_STATUS", status: "generating" });
1074
- const sessionId = crypto.randomUUID();
1075
- const topic = btoa(sessionId).slice(0, 32);
1076
- const uri = `rialo-wc://${sessionId}?relay=${encodeURIComponent(scanConnectRelay)}&topic=${topic}&networks=devnet`;
1077
- setGeneratedURI(uri);
1078
- dispatch({ type: "SET_SCAN_STATUS", status: "waiting" });
1079
- setQRReady(true);
1080
- };
1081
- useEffect(() => {
1082
- if (!qrReady || !generatedURI || !canvasRef.current) return;
1083
- const canvas = canvasRef.current;
1084
- const ctx = canvas.getContext("2d");
1085
- if (!ctx) return;
1086
- const size = 200;
1087
- canvas.width = size;
1088
- canvas.height = size;
1089
- ctx.fillStyle = "#FFFFFF";
1090
- ctx.fillRect(0, 0, size, size);
1091
- ctx.fillStyle = "#011B29";
1092
- const gridSize = 25;
1093
- const cellSize = size / gridSize;
1094
- for (let y = 0; y < gridSize; y++) {
1095
- for (let x = 0; x < gridSize; x++) {
1096
- const isFinderArea = x < 7 && y < 7 || x >= gridSize - 7 && y < 7 || x < 7 && y >= gridSize - 7;
1097
- if (isFinderArea) {
1098
- const isOuter = x === 0 || y === 0 || x === 6 || y === 6 || x === gridSize - 7 || x === gridSize - 1 || y === gridSize - 7 || y === gridSize - 1;
1099
- const isInner = x >= 2 && x <= 4 && y >= 2 && y <= 4 || x >= gridSize - 5 && x <= gridSize - 3 && y >= 2 && y <= 4 || x >= 2 && x <= 4 && y >= gridSize - 5 && y <= gridSize - 3;
1100
- if (isOuter || isInner) ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
1101
- continue;
1102
- }
1103
- const charIndex = (y * gridSize + x) % generatedURI.length;
1104
- const charCode = generatedURI.charCodeAt(charIndex);
1105
- if (charCode % 3 !== 0) ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
1106
- }
1107
- }
1108
- const logoSize = 40;
1109
- const logoX = (size - logoSize) / 2;
1110
- const logoY = (size - logoSize) / 2;
1111
- ctx.beginPath();
1112
- ctx.arc(size / 2, size / 2, logoSize / 2 + 4, 0, Math.PI * 2);
1113
- ctx.fillStyle = "#FFFFFF";
1114
- ctx.fill();
1115
- ctx.beginPath();
1116
- const r = 6;
1117
- ctx.moveTo(logoX + r, logoY);
1118
- ctx.lineTo(logoX + logoSize - r, logoY);
1119
- ctx.quadraticCurveTo(logoX + logoSize, logoY, logoX + logoSize, logoY + r);
1120
- ctx.lineTo(logoX + logoSize, logoY + logoSize - r);
1121
- ctx.quadraticCurveTo(logoX + logoSize, logoY + logoSize, logoX + logoSize - r, logoY + logoSize);
1122
- ctx.lineTo(logoX + r, logoY + logoSize);
1123
- ctx.quadraticCurveTo(logoX, logoY + logoSize, logoX, logoY + logoSize - r);
1124
- ctx.lineTo(logoX, logoY + r);
1125
- ctx.quadraticCurveTo(logoX, logoY, logoX + r, logoY);
1126
- ctx.fillStyle = "#6EB9A8";
1127
- ctx.fill();
1128
- ctx.fillStyle = "#FFFFFF";
1129
- ctx.font = "bold 22px sans-serif";
1130
- ctx.textAlign = "center";
1131
- ctx.textBaseline = "middle";
1132
- ctx.fillText("S", size / 2, size / 2 + 1);
1133
- }, [qrReady, generatedURI]);
1134
- useEffect(() => {
1135
- if (activeTab === "scan" && scanMode === "show-qr" && !generatedURI) {
1136
- generateScanSession();
1137
- }
1138
- }, [activeTab, scanMode]);
1255
+ }, [connect]);
1139
1256
  const handleTouchStart = (e) => {
1140
1257
  if (!isMobile) return;
1141
1258
  dragStartRef.current = e.touches[0].clientY;
@@ -1153,389 +1270,69 @@ function WalletModal({ scanConnectRelay, dispatch }) {
1153
1270
  setDragY(0);
1154
1271
  };
1155
1272
  if (!state.isModalOpen) return null;
1156
- const ExtensionIcon = () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1157
- /* @__PURE__ */ jsx("path", { d: "M16 3h5v5" }),
1158
- /* @__PURE__ */ jsx("path", { d: "M8 3H3v5" }),
1159
- /* @__PURE__ */ jsx("path", { d: "M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3" }),
1160
- /* @__PURE__ */ jsx("path", { d: "m15 9 6-6" })
1161
- ] });
1162
- const QrIcon = () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1163
- /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7" }),
1164
- /* @__PURE__ */ jsx("rect", { x: "14", y: "3", width: "7", height: "7" }),
1165
- /* @__PURE__ */ jsx("rect", { x: "3", y: "14", width: "7", height: "7" }),
1166
- /* @__PURE__ */ jsx("path", { d: "M14 14h7v7h-7z" })
1167
- ] });
1168
- const CameraIcon = () => /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1169
- /* @__PURE__ */ jsx("path", { d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" }),
1170
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "13", r: "3" })
1171
- ] });
1172
- const RefreshIcon = () => /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1173
- /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" }),
1174
- /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" })
1175
- ] });
1176
- const tabBaseStyle = {
1177
- flex: 1,
1178
- padding: "10px 0",
1179
- border: "none",
1180
- borderRadius: "10px",
1181
- fontSize: "13px",
1182
- cursor: "pointer",
1183
- transition: "all 0.2s",
1184
- display: "flex",
1185
- alignItems: "center",
1186
- justifyContent: "center",
1187
- gap: "6px"
1188
- };
1189
- const renderContent = () => /* @__PURE__ */ jsxs(Fragment, { children: [
1190
- /* @__PURE__ */ jsxs("div", { style: {
1191
- display: "flex",
1192
- gap: "4px",
1193
- padding: "4px",
1194
- backgroundColor: "rgba(255,255,255,0.04)",
1195
- borderRadius: "14px",
1196
- marginBottom: "20px"
1197
- }, children: [
1198
- /* @__PURE__ */ jsxs(
1199
- "button",
1200
- {
1201
- type: "button",
1202
- onClick: (e) => {
1203
- stop(e);
1204
- setActiveTab("extension");
1205
- },
1206
- style: {
1207
- ...tabBaseStyle,
1208
- backgroundColor: activeTab === "extension" ? "rgba(255,255,255,0.08)" : "transparent",
1209
- color: activeTab === "extension" ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1210
- fontWeight: activeTab === "extension" ? 600 : 400
1211
- },
1212
- children: [
1213
- /* @__PURE__ */ jsx(ExtensionIcon, {}),
1214
- " Extension"
1215
- ]
1216
- }
1217
- ),
1218
- /* @__PURE__ */ jsxs(
1219
- "button",
1220
- {
1221
- type: "button",
1222
- onClick: (e) => {
1223
- stop(e);
1224
- setActiveTab("scan");
1225
- },
1226
- style: {
1227
- ...tabBaseStyle,
1228
- backgroundColor: activeTab === "scan" ? "rgba(255,255,255,0.08)" : "transparent",
1229
- color: activeTab === "scan" ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1230
- fontWeight: activeTab === "scan" ? 600 : 400
1231
- },
1232
- children: [
1233
- /* @__PURE__ */ jsx(QrIcon, {}),
1234
- " Scan to Connect"
1235
- ]
1236
- }
1237
- )
1238
- ] }),
1239
- activeTab === "extension" && /* @__PURE__ */ jsxs(Fragment, { children: [
1240
- sheepWallet && /* @__PURE__ */ jsxs(
1241
- "button",
1242
- {
1243
- type: "button",
1244
- onClick: installed ? handleConnect : (e) => {
1245
- stop(e);
1246
- window.open(sheepWallet.downloadUrl, "_blank");
1247
- },
1248
- disabled: state.status === "connecting",
1249
- style: {
1250
- width: "100%",
1251
- display: "flex",
1252
- alignItems: "center",
1253
- gap: "14px",
1254
- padding: "16px",
1255
- backgroundColor: "rgba(255,255,255,0.04)",
1256
- border: "1px solid rgba(255,255,255,0.08)",
1257
- borderRadius: "14px",
1258
- cursor: state.status === "connecting" ? "wait" : "pointer",
1259
- transition: "all 0.15s"
1260
- },
1261
- onMouseEnter: (e) => {
1262
- e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.07)";
1263
- },
1264
- onMouseLeave: (e) => {
1265
- e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.04)";
1266
- },
1267
- children: [
1268
- /* @__PURE__ */ jsx("div", { style: {
1269
- width: "44px",
1270
- height: "44px",
1271
- borderRadius: "12px",
1272
- background: "linear-gradient(135deg, #6EB9A8, #4a9a8a)",
1273
- display: "flex",
1274
- alignItems: "center",
1275
- justifyContent: "center",
1276
- color: "#fff",
1277
- fontWeight: 700,
1278
- fontSize: "18px"
1279
- }, children: "S" }),
1280
- /* @__PURE__ */ jsxs("div", { style: { flex: 1, textAlign: "left" }, children: [
1281
- /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontWeight: 500, fontSize: "15px", marginBottom: "2px" }, children: sheepWallet.name }),
1282
- /* @__PURE__ */ jsx("div", { style: {
1283
- fontSize: "12px",
1284
- color: installed ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1285
- display: "flex",
1286
- alignItems: "center",
1287
- gap: "5px"
1288
- }, children: state.status === "connecting" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1289
- /* @__PURE__ */ jsx("span", { style: {
1290
- display: "inline-block",
1291
- width: "6px",
1292
- height: "6px",
1293
- borderRadius: "50%",
1294
- backgroundColor: "#F59E0B",
1295
- animation: "walletModalPulse 1.2s infinite"
1296
- } }),
1297
- "Connecting..."
1298
- ] }) : installed ? /* @__PURE__ */ jsxs(Fragment, { children: [
1299
- /* @__PURE__ */ jsx("span", { style: {
1300
- display: "inline-block",
1301
- width: "6px",
1302
- height: "6px",
1303
- borderRadius: "50%",
1304
- backgroundColor: "#6EB9A8"
1305
- } }),
1306
- "Detected \u2014 Click to connect"
1307
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1308
- /* @__PURE__ */ jsx("span", { style: {
1309
- display: "inline-block",
1310
- width: "6px",
1311
- height: "6px",
1312
- borderRadius: "50%",
1313
- backgroundColor: "rgba(255,255,255,0.2)"
1314
- } }),
1315
- "Not installed"
1316
- ] }) })
1317
- ] }),
1318
- !installed && /* @__PURE__ */ jsx("span", { style: {
1319
- color: "#6EB9A8",
1320
- fontSize: "12px",
1321
- fontWeight: 500,
1322
- padding: "4px 10px",
1323
- borderRadius: "8px",
1324
- backgroundColor: "rgba(110,185,168,0.1)"
1325
- }, children: "Install" })
1326
- ]
1327
- }
1328
- ),
1329
- /* @__PURE__ */ jsx("div", { style: {
1330
- display: "flex",
1331
- gap: "8px",
1332
- marginTop: "16px",
1333
- flexWrap: "wrap"
1334
- }, children: [
1335
- { label: "REX", desc: "Confidential TX", color: "#8B5CF6" },
1336
- { label: "SfS", desc: "Gasless TX", color: "#F59E0B" },
1337
- { label: "QR", desc: "Scan Connect", color: "#6EB9A8" }
1338
- ].map((badge) => /* @__PURE__ */ jsxs("div", { style: {
1339
- padding: "5px 10px",
1340
- backgroundColor: `${badge.color}10`,
1341
- borderRadius: "8px",
1342
- border: `1px solid ${badge.color}18`,
1343
- display: "flex",
1344
- alignItems: "center",
1345
- gap: "6px"
1346
- }, children: [
1347
- /* @__PURE__ */ jsx("span", { style: { color: badge.color, fontSize: "11px", fontWeight: 600 }, children: badge.label }),
1348
- /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.35)", fontSize: "10px" }, children: badge.desc })
1349
- ] }, badge.label)) })
1350
- ] }),
1351
- activeTab === "scan" && /* @__PURE__ */ jsxs("div", { children: [
1352
- /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "8px", marginBottom: "16px" }, children: ["show-qr", "scan-qr"].map((mode) => /* @__PURE__ */ jsx(
1353
- "button",
1354
- {
1355
- type: "button",
1356
- onClick: (e) => {
1357
- stop(e);
1358
- setScanMode(mode);
1359
- },
1360
- style: {
1361
- flex: 1,
1362
- padding: "8px",
1363
- display: "flex",
1364
- alignItems: "center",
1365
- justifyContent: "center",
1366
- gap: "6px",
1367
- backgroundColor: scanMode === mode ? "rgba(110,185,168,0.1)" : "transparent",
1368
- border: `1px solid ${scanMode === mode ? "rgba(110,185,168,0.25)" : "rgba(255,255,255,0.08)"}`,
1369
- borderRadius: "10px",
1370
- color: scanMode === mode ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1371
- fontSize: "12px",
1372
- fontWeight: scanMode === mode ? 500 : 400,
1373
- cursor: "pointer",
1374
- transition: "all 0.15s"
1375
- },
1376
- children: mode === "show-qr" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1377
- /* @__PURE__ */ jsx(QrIcon, {}),
1378
- " Show QR"
1379
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1380
- /* @__PURE__ */ jsx(CameraIcon, {}),
1381
- " Scan QR"
1382
- ] })
1383
- },
1384
- mode
1385
- )) }),
1386
- scanMode === "show-qr" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
1387
- /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.45)", fontSize: "13px", marginBottom: "16px", lineHeight: 1.5 }, children: "Scan this QR code with Sheep Wallet on your mobile device to connect" }),
1388
- /* @__PURE__ */ jsxs("div", { style: {
1389
- display: "inline-block",
1390
- padding: "16px",
1391
- backgroundColor: "#FFFFFF",
1392
- borderRadius: "16px",
1393
- position: "relative"
1394
- }, children: [
1395
- [
1396
- { top: -2, left: -2, borderTop: "3px solid #6EB9A8", borderLeft: "3px solid #6EB9A8", borderTopLeftRadius: "12px" },
1397
- { top: -2, right: -2, borderTop: "3px solid #6EB9A8", borderRight: "3px solid #6EB9A8", borderTopRightRadius: "12px" },
1398
- { bottom: -2, left: -2, borderBottom: "3px solid #6EB9A8", borderLeft: "3px solid #6EB9A8", borderBottomLeftRadius: "12px" },
1399
- { bottom: -2, right: -2, borderBottom: "3px solid #6EB9A8", borderRight: "3px solid #6EB9A8", borderBottomRightRadius: "12px" }
1400
- ].map((s, i) => /* @__PURE__ */ jsx("div", { style: { position: "absolute", width: "24px", height: "24px", ...s } }, i)),
1401
- /* @__PURE__ */ jsx("canvas", { ref: canvasRef, style: { width: "200px", height: "200px", display: "block" } })
1402
- ] }),
1403
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "16px", display: "flex", alignItems: "center", justifyContent: "center", gap: "8px" }, children: [
1404
- /* @__PURE__ */ jsx("div", { style: {
1405
- width: "7px",
1406
- height: "7px",
1407
- borderRadius: "50%",
1408
- backgroundColor: state.scanConnectStatus === "waiting" ? "#6EB9A8" : "#F59E0B",
1409
- animation: "walletModalPulse 2s infinite"
1410
- } }),
1411
- /* @__PURE__ */ jsxs("span", { style: { color: "rgba(255,255,255,0.4)", fontSize: "12px" }, children: [
1412
- state.scanConnectStatus === "generating" && "Generating session...",
1413
- state.scanConnectStatus === "waiting" && "Waiting for wallet to scan...",
1414
- state.scanConnectStatus === "scanned" && "Scanned! Waiting for approval...",
1415
- state.scanConnectStatus === "connected" && "Connected!",
1416
- state.scanConnectStatus === "error" && "Connection failed. Try again.",
1417
- state.scanConnectStatus === "idle" && "Initializing..."
1418
- ] })
1419
- ] }),
1420
- /* @__PURE__ */ jsxs(
1421
- "button",
1422
- {
1423
- type: "button",
1424
- onClick: (e) => {
1425
- stop(e);
1426
- generateScanSession();
1427
- },
1428
- style: {
1429
- marginTop: "12px",
1430
- padding: "8px 16px",
1431
- backgroundColor: "transparent",
1432
- border: "1px solid rgba(255,255,255,0.08)",
1433
- borderRadius: "10px",
1434
- color: "#6EB9A8",
1435
- fontSize: "12px",
1436
- cursor: "pointer",
1437
- display: "inline-flex",
1438
- alignItems: "center",
1439
- gap: "6px",
1440
- transition: "all 0.15s"
1441
- },
1442
- children: [
1443
- /* @__PURE__ */ jsx(RefreshIcon, {}),
1444
- " Refresh QR"
1445
- ]
1446
- }
1447
- )
1273
+ const stop = (e) => e.stopPropagation();
1274
+ const content = /* @__PURE__ */ jsx(Fragment, { children: showSuccess && state.status === "connected" && state.accounts.length > 0 ? /* @__PURE__ */ jsx(SuccessView, { address: state.accounts[0]?.address || "" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1275
+ /* @__PURE__ */ jsxs("div", { style: S.tabBar, children: [
1276
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: (e) => {
1277
+ stop(e);
1278
+ setActiveTab("extension");
1279
+ }, style: tabStyle(activeTab === "extension"), children: [
1280
+ /* @__PURE__ */ jsx(IconExtension, {}),
1281
+ " Extension"
1448
1282
  ] }),
1449
- scanMode === "scan-qr" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
1450
- /* @__PURE__ */ jsx("p", { style: { color: "rgba(255,255,255,0.45)", fontSize: "13px", marginBottom: "16px", lineHeight: 1.5 }, children: "Open Sheep Wallet on another device, go to Settings \u2192 Show Connect QR, then scan it here" }),
1451
- /* @__PURE__ */ jsxs("div", { style: {
1452
- width: "100%",
1453
- height: "200px",
1454
- backgroundColor: "rgba(255,255,255,0.02)",
1455
- borderRadius: "14px",
1456
- border: "1px dashed rgba(255,255,255,0.1)",
1457
- display: "flex",
1458
- flexDirection: "column",
1459
- alignItems: "center",
1460
- justifyContent: "center",
1461
- gap: "12px"
1462
- }, children: [
1463
- /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.2)" }, children: /* @__PURE__ */ jsx(CameraIcon, {}) }),
1464
- /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.3)", fontSize: "13px" }, children: "Camera access required" }),
1465
- /* @__PURE__ */ jsx(
1466
- "button",
1467
- {
1468
- type: "button",
1469
- onClick: (e) => {
1470
- stop(e);
1471
- alert("Camera scanning requires HTTPS and camera permissions. This feature works in the installed extension.");
1472
- },
1473
- style: {
1474
- padding: "8px 20px",
1475
- backgroundColor: "#6EB9A8",
1476
- border: "none",
1477
- borderRadius: "10px",
1478
- color: "#011B29",
1479
- fontSize: "13px",
1480
- fontWeight: 500,
1481
- cursor: "pointer"
1482
- },
1483
- children: "Open Camera"
1484
- }
1485
- )
1486
- ] })
1283
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: (e) => {
1284
+ stop(e);
1285
+ setActiveTab("scan");
1286
+ }, style: tabStyle(activeTab === "scan"), children: [
1287
+ /* @__PURE__ */ jsx(IconQR, {}),
1288
+ " Scan"
1289
+ ] }),
1290
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: (e) => {
1291
+ stop(e);
1292
+ setActiveTab("webwallet");
1293
+ }, style: tabStyle(activeTab === "webwallet"), children: [
1294
+ /* @__PURE__ */ jsx(IconWeb, {}),
1295
+ " Web Wallet"
1487
1296
  ] })
1488
- ] }),
1489
- state.error && /* @__PURE__ */ jsx("div", { style: {
1490
- marginTop: "16px",
1491
- padding: "12px 14px",
1492
- backgroundColor: "rgba(239,68,68,0.08)",
1493
- border: "1px solid rgba(239,68,68,0.15)",
1494
- borderRadius: "12px",
1495
- color: "#f87171",
1496
- fontSize: "13px",
1497
- lineHeight: 1.4
1498
- }, children: state.error.message }),
1499
- /* @__PURE__ */ jsx("div", { style: {
1500
- marginTop: "24px",
1501
- textAlign: "center",
1502
- color: "rgba(255,255,255,0.2)",
1503
- fontSize: "11px",
1504
- paddingTop: "16px",
1505
- borderTop: "1px solid rgba(255,255,255,0.04)"
1506
- }, children: "Powered by Rialo Network" })
1507
- ] });
1508
- const closeBtn = {
1509
- background: "none",
1510
- border: "none",
1511
- color: "rgba(255,255,255,0.3)",
1512
- cursor: "pointer",
1513
- padding: "6px",
1514
- borderRadius: "8px",
1515
- display: "flex",
1516
- alignItems: "center",
1517
- justifyContent: "center",
1518
- transition: "all 0.15s"
1519
- };
1520
- const CloseIcon = () => /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1521
- /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
1522
- /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
1523
- ] });
1297
+ ] }),
1298
+ activeTab === "extension" && /* @__PURE__ */ jsx(
1299
+ ExtensionTab,
1300
+ {
1301
+ onConnect: handleExtensionConnect,
1302
+ extensionDetected,
1303
+ connecting: state.status === "connecting",
1304
+ error: state.error
1305
+ }
1306
+ ),
1307
+ activeTab === "scan" && /* @__PURE__ */ jsx(ScanTab, { scanConnectRelay }),
1308
+ activeTab === "webwallet" && /* @__PURE__ */ jsx(WebWalletTab, { onConnected: () => {
1309
+ } }),
1310
+ /* @__PURE__ */ jsx("div", { style: {
1311
+ marginTop: "24px",
1312
+ textAlign: "center",
1313
+ color: "rgba(255,255,255,0.2)",
1314
+ fontSize: "11px",
1315
+ paddingTop: "16px",
1316
+ borderTop: "1px solid rgba(255,255,255,0.04)"
1317
+ }, children: "Powered by Rialo Network" })
1318
+ ] }) });
1319
+ const keyframes = `
1320
+ @keyframes swModalFadeIn {
1321
+ from { opacity: 0; transform: scale(0.97) translateY(8px); }
1322
+ to { opacity: 1; transform: scale(1) translateY(0); }
1323
+ }
1324
+ @keyframes swModalSlideUp {
1325
+ from { transform: translateY(100%); }
1326
+ to { transform: translateY(0); }
1327
+ }
1328
+ @keyframes swModalPulse {
1329
+ 0%, 100% { opacity: 1; }
1330
+ 50% { opacity: 0.3; }
1331
+ }
1332
+ `;
1524
1333
  if (isMobile) {
1525
1334
  return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999 }, children: [
1526
- /* @__PURE__ */ jsx(
1527
- "div",
1528
- {
1529
- style: {
1530
- position: "absolute",
1531
- inset: 0,
1532
- backgroundColor: "rgba(0,0,0,0.6)",
1533
- backdropFilter: "blur(8px)",
1534
- WebkitBackdropFilter: "blur(8px)"
1535
- },
1536
- onClick: closeModal
1537
- }
1538
- ),
1335
+ /* @__PURE__ */ jsx("div", { style: S.backdrop, onClick: closeModal }),
1539
1336
  /* @__PURE__ */ jsxs(
1540
1337
  "div",
1541
1338
  {
@@ -1556,88 +1353,503 @@ function WalletModal({ scanConnectRelay, dispatch }) {
1556
1353
  boxShadow: "0 -8px 40px rgba(0,0,0,0.5)",
1557
1354
  transform: `translateY(${dragY}px)`,
1558
1355
  transition: isDragging ? "none" : "transform 0.3s cubic-bezier(0.4,0,0.2,1)",
1559
- animation: isDragging ? "none" : "walletModalSlideUp 0.35s cubic-bezier(0.4,0,0.2,1)"
1356
+ animation: isDragging ? "none" : "swModalSlideUp 0.35s cubic-bezier(0.4,0,0.2,1)"
1560
1357
  },
1561
1358
  children: [
1562
1359
  /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "center", padding: "12px 0 16px", cursor: "grab", touchAction: "none" }, children: /* @__PURE__ */ jsx("div", { style: { width: "36px", height: "4px", borderRadius: "2px", backgroundColor: "rgba(255,255,255,0.12)" } }) }),
1563
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
1564
- /* @__PURE__ */ jsx("h2", { style: { color: "#fff", fontSize: "18px", fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }, children: "Connect Wallet" }),
1565
- /* @__PURE__ */ jsx("button", { type: "button", onClick: closeModal, style: closeBtn, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
1360
+ /* @__PURE__ */ jsxs("div", { style: S.header, children: [
1361
+ /* @__PURE__ */ jsx("h2", { style: S.title, children: "Connect Wallet" }),
1362
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: closeModal, style: S.closeBtn, children: /* @__PURE__ */ jsx(IconClose, {}) })
1566
1363
  ] }),
1567
- renderContent()
1364
+ content
1568
1365
  ]
1569
1366
  }
1570
1367
  ),
1571
- /* @__PURE__ */ jsx("style", { children: `
1572
- @keyframes walletModalSlideUp {
1573
- from { transform: translateY(100%); }
1574
- to { transform: translateY(0); }
1575
- }
1576
- @keyframes walletModalPulse {
1577
- 0%, 100% { opacity: 1; }
1578
- 50% { opacity: 0.3; }
1579
- }
1580
- ` })
1368
+ /* @__PURE__ */ jsx("style", { children: keyframes })
1581
1369
  ] });
1582
1370
  }
1583
1371
  return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999 }, children: [
1584
- /* @__PURE__ */ jsx(
1585
- "div",
1586
- {
1587
- style: {
1588
- position: "absolute",
1589
- inset: 0,
1590
- backgroundColor: "rgba(0,0,0,0.6)",
1591
- backdropFilter: "blur(8px)",
1592
- WebkitBackdropFilter: "blur(8px)"
1593
- },
1594
- onClick: closeModal
1372
+ /* @__PURE__ */ jsx("div", { style: S.backdrop, onClick: closeModal }),
1373
+ /* @__PURE__ */ jsx("div", { style: { position: "fixed", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", pointerEvents: "none" }, children: /* @__PURE__ */ jsxs("div", { style: S.dialog, children: [
1374
+ /* @__PURE__ */ jsxs("div", { style: S.header, children: [
1375
+ /* @__PURE__ */ jsx("h2", { style: S.title, children: "Connect Wallet" }),
1376
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: closeModal, style: S.closeBtn, children: /* @__PURE__ */ jsx(IconClose, {}) })
1377
+ ] }),
1378
+ content
1379
+ ] }) }),
1380
+ /* @__PURE__ */ jsx("style", { children: keyframes })
1381
+ ] });
1382
+ }
1383
+ var initialState = {
1384
+ status: "disconnected",
1385
+ accounts: [],
1386
+ network: "devnet",
1387
+ balance: null,
1388
+ error: null,
1389
+ isModalOpen: false,
1390
+ scanConnectStatus: "idle"
1391
+ };
1392
+ function walletReducer(state, action) {
1393
+ switch (action.type) {
1394
+ case "CONNECTING":
1395
+ return { ...state, status: "connecting", error: null };
1396
+ case "CONNECTED":
1397
+ return {
1398
+ ...state,
1399
+ status: "connected",
1400
+ accounts: action.accounts,
1401
+ network: action.network,
1402
+ error: null
1403
+ // Do NOT close modal here — let the modal show success state first
1404
+ };
1405
+ case "DISCONNECTED":
1406
+ return { ...state, status: "disconnected", accounts: [], balance: null, error: null };
1407
+ case "ERROR":
1408
+ return { ...state, status: "error", error: action.error };
1409
+ case "SET_BALANCE":
1410
+ return { ...state, balance: action.balance };
1411
+ case "SET_NETWORK":
1412
+ return { ...state, network: action.network };
1413
+ case "SET_ACCOUNTS":
1414
+ return { ...state, accounts: action.accounts };
1415
+ case "OPEN_MODAL":
1416
+ return { ...state, isModalOpen: true };
1417
+ case "CLOSE_MODAL":
1418
+ return { ...state, isModalOpen: false, scanConnectStatus: "idle" };
1419
+ case "RESET_ERROR":
1420
+ return { ...state, error: null, status: state.accounts.length > 0 ? "connected" : "disconnected" };
1421
+ case "SET_SCAN_STATUS":
1422
+ return { ...state, scanConnectStatus: action.status };
1423
+ default:
1424
+ return state;
1425
+ }
1426
+ }
1427
+ var WalletContext = createContext(null);
1428
+ var DEFAULT_WALLETS = [
1429
+ {
1430
+ id: "sheep-wallet",
1431
+ name: "Sheep Wallet",
1432
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSI4IiBmaWxsPSIjMDExQjI5Ii8+PHRleHQgeD0iNTAlIiB5PSI1NSUiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXdlaWdodD0iYm9sZCIgZm9udC1zaXplPSIxNiIgZmlsbD0iIzZFQjlBOCI+Uzwvc2VsZj48L3N2Zz4=",
1433
+ downloadUrl: "https://rialo.io/wallet",
1434
+ supportsREX: true,
1435
+ supportsSfS: true,
1436
+ supportsScanConnect: true
1437
+ }
1438
+ ];
1439
+ function WalletProvider({
1440
+ children,
1441
+ network: initialNetwork = "devnet",
1442
+ autoConnect = true,
1443
+ wallets: customWallets = [],
1444
+ scanConnectRelay = "wss://relay.rialo.io",
1445
+ onConnect,
1446
+ onDisconnect,
1447
+ onError,
1448
+ onNetworkChange
1449
+ }) {
1450
+ const [state, dispatch] = useReducer(walletReducer, {
1451
+ ...initialState,
1452
+ network: initialNetwork
1453
+ });
1454
+ const walletRef = useRef(null);
1455
+ const providerRef = useRef(void 0);
1456
+ const connectingRef = useRef(false);
1457
+ const mountedRef = useRef(true);
1458
+ useEffect(() => {
1459
+ mountedRef.current = true;
1460
+ walletRef.current = new SheepWallet();
1461
+ waitForProvider(3e3).then((provider) => {
1462
+ if (mountedRef.current) {
1463
+ providerRef.current = provider;
1595
1464
  }
1596
- ),
1597
- /* @__PURE__ */ jsx("div", { style: {
1598
- position: "fixed",
1599
- inset: 0,
1600
- display: "flex",
1601
- alignItems: "center",
1602
- justifyContent: "center",
1603
- pointerEvents: "none"
1604
- }, children: /* @__PURE__ */ jsxs(
1605
- "div",
1606
- {
1607
- style: {
1608
- pointerEvents: "auto",
1609
- backgroundColor: "#011B29",
1610
- borderRadius: "20px",
1611
- padding: "24px",
1612
- width: "100%",
1613
- maxWidth: "400px",
1614
- margin: "16px",
1615
- boxShadow: "0 25px 60px -12px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.05)",
1616
- maxHeight: "90vh",
1617
- overflow: "auto",
1618
- animation: "walletModalFadeIn 0.25s cubic-bezier(0.4,0,0.2,1)"
1619
- },
1620
- children: [
1621
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
1622
- /* @__PURE__ */ jsx("h2", { style: { color: "#fff", fontSize: "18px", fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }, children: "Connect Wallet" }),
1623
- /* @__PURE__ */ jsx("button", { type: "button", onClick: closeModal, style: closeBtn, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
1624
- ] }),
1625
- renderContent()
1626
- ]
1465
+ });
1466
+ return () => {
1467
+ mountedRef.current = false;
1468
+ walletRef.current?.destroy();
1469
+ };
1470
+ }, []);
1471
+ useEffect(() => {
1472
+ const setupEvents = async () => {
1473
+ const provider = await waitForProvider(3e3);
1474
+ if (!provider || !mountedRef.current) return;
1475
+ providerRef.current = provider;
1476
+ const cleanups = [];
1477
+ cleanups.push(
1478
+ provider.on("disconnect", () => {
1479
+ if (!mountedRef.current) return;
1480
+ dispatch({ type: "DISCONNECTED" });
1481
+ onDisconnect?.();
1482
+ })
1483
+ );
1484
+ cleanups.push(
1485
+ provider.on("accountsChanged", (data) => {
1486
+ if (!mountedRef.current) return;
1487
+ const accounts = normalizeAccounts(data);
1488
+ dispatch({ type: "SET_ACCOUNTS", accounts });
1489
+ })
1490
+ );
1491
+ cleanups.push(
1492
+ provider.on("networkChanged", (data) => {
1493
+ if (!mountedRef.current) return;
1494
+ const { network } = data;
1495
+ if (network) {
1496
+ dispatch({ type: "SET_NETWORK", network });
1497
+ onNetworkChange?.(network);
1498
+ }
1499
+ })
1500
+ );
1501
+ return () => {
1502
+ cleanups.forEach((fn) => {
1503
+ try {
1504
+ fn();
1505
+ } catch {
1506
+ }
1507
+ });
1508
+ };
1509
+ };
1510
+ const cleanup = setupEvents();
1511
+ return () => {
1512
+ cleanup.then((fn) => fn?.());
1513
+ };
1514
+ }, [onDisconnect, onNetworkChange]);
1515
+ useEffect(() => {
1516
+ if (!autoConnect) return;
1517
+ if (state.status === "connected" || state.status === "connecting") return;
1518
+ const tryAutoConnect = async () => {
1519
+ const wallet = walletRef.current;
1520
+ if (!wallet || !hasProvider()) return;
1521
+ try {
1522
+ const accounts = await wallet.checkSession();
1523
+ if (accounts && accounts.length > 0 && mountedRef.current) {
1524
+ dispatch({ type: "CONNECTED", accounts, network: wallet.network });
1525
+ onConnect?.(accounts);
1526
+ }
1527
+ } catch {
1528
+ }
1529
+ };
1530
+ const timer = setTimeout(tryAutoConnect, 500);
1531
+ return () => clearTimeout(timer);
1532
+ }, [autoConnect, onConnect]);
1533
+ useEffect(() => {
1534
+ if (state.status !== "connected" || state.accounts.length === 0) return;
1535
+ const refresh = async () => {
1536
+ const wallet = walletRef.current;
1537
+ if (!wallet || !mountedRef.current) return;
1538
+ try {
1539
+ const balance = await wallet.getBalance();
1540
+ if (mountedRef.current) {
1541
+ dispatch({ type: "SET_BALANCE", balance });
1542
+ }
1543
+ } catch {
1544
+ }
1545
+ };
1546
+ refresh();
1547
+ const interval = setInterval(refresh, 3e4);
1548
+ return () => clearInterval(interval);
1549
+ }, [state.status, state.accounts]);
1550
+ const connect = useCallback(async () => {
1551
+ if (connectingRef.current) {
1552
+ throw new Error("Connection already in progress");
1553
+ }
1554
+ const wallet = walletRef.current;
1555
+ if (!wallet) {
1556
+ const err = new Error("Wallet not initialized");
1557
+ dispatch({ type: "ERROR", error: err });
1558
+ throw err;
1559
+ }
1560
+ if (!hasProvider()) {
1561
+ dispatch({ type: "OPEN_MODAL" });
1562
+ return [];
1563
+ }
1564
+ connectingRef.current = true;
1565
+ dispatch({ type: "CONNECTING" });
1566
+ try {
1567
+ const accounts = await wallet.connect();
1568
+ if (mountedRef.current) {
1569
+ dispatch({ type: "CONNECTED", accounts, network: wallet.network });
1570
+ onConnect?.(accounts);
1571
+ }
1572
+ return accounts;
1573
+ } catch (error) {
1574
+ const err = error instanceof Error ? error : new Error(String(error));
1575
+ if (mountedRef.current) {
1576
+ dispatch({ type: "ERROR", error: err });
1577
+ onError?.(err);
1578
+ }
1579
+ throw err;
1580
+ } finally {
1581
+ connectingRef.current = false;
1582
+ }
1583
+ }, [onConnect, onError]);
1584
+ const disconnect = useCallback(async () => {
1585
+ const wallet = walletRef.current;
1586
+ if (!wallet) return;
1587
+ try {
1588
+ await wallet.disconnect();
1589
+ } catch {
1590
+ }
1591
+ if (mountedRef.current) {
1592
+ dispatch({ type: "DISCONNECTED" });
1593
+ onDisconnect?.();
1594
+ }
1595
+ }, [onDisconnect]);
1596
+ const refreshBalance = useCallback(async () => {
1597
+ const wallet = walletRef.current;
1598
+ if (!wallet || state.status !== "connected") return;
1599
+ try {
1600
+ const balance = await wallet.getBalance();
1601
+ if (mountedRef.current) {
1602
+ dispatch({ type: "SET_BALANCE", balance });
1627
1603
  }
1628
- ) }),
1629
- /* @__PURE__ */ jsx("style", { children: `
1630
- @keyframes walletModalFadeIn {
1631
- from { opacity: 0; transform: scale(0.97) translateY(8px); }
1632
- to { opacity: 1; transform: scale(1) translateY(0); }
1633
- }
1634
- @keyframes walletModalPulse {
1635
- 0%, 100% { opacity: 1; }
1636
- 50% { opacity: 0.3; }
1637
- }
1638
- ` })
1604
+ } catch {
1605
+ }
1606
+ }, [state.status]);
1607
+ const switchNetwork = useCallback(async (network) => {
1608
+ const wallet = walletRef.current;
1609
+ if (!wallet) return;
1610
+ await wallet.switchNetwork(network);
1611
+ if (mountedRef.current) {
1612
+ dispatch({ type: "SET_NETWORK", network });
1613
+ onNetworkChange?.(network);
1614
+ }
1615
+ }, [onNetworkChange]);
1616
+ const signMessage = useCallback(async (message) => {
1617
+ const wallet = walletRef.current;
1618
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1619
+ return wallet.signMessage(message);
1620
+ }, [state.status]);
1621
+ const signTransaction = useCallback(async (tx) => {
1622
+ const wallet = walletRef.current;
1623
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1624
+ return wallet.signTransaction(tx);
1625
+ }, [state.status]);
1626
+ const sendTransaction = useCallback(async (tx) => {
1627
+ const wallet = walletRef.current;
1628
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1629
+ return wallet.sendTransaction(tx);
1630
+ }, [state.status]);
1631
+ const signAndSendTransaction = useCallback(async (tx) => {
1632
+ const wallet = walletRef.current;
1633
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1634
+ return wallet.signAndSendTransaction(tx);
1635
+ }, [state.status]);
1636
+ const getREXCapabilities = useCallback(async () => {
1637
+ const wallet = walletRef.current;
1638
+ if (!wallet) return { supported: false, privacyModes: [], maxInputSize: 0, programs: [] };
1639
+ return wallet.getREXCapabilities();
1640
+ }, []);
1641
+ const submitREXTransaction = useCallback(async (tx) => {
1642
+ const wallet = walletRef.current;
1643
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1644
+ return wallet.submitREXTransaction(tx);
1645
+ }, [state.status]);
1646
+ const sendGaslessTransaction = useCallback(async (tx) => {
1647
+ const wallet = walletRef.current;
1648
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1649
+ return wallet.sendGaslessTransaction(tx);
1650
+ }, [state.status]);
1651
+ const getSfSPositions = useCallback(async () => {
1652
+ const wallet = walletRef.current;
1653
+ if (!wallet || state.status !== "connected") return [];
1654
+ return wallet.getSfSPositions();
1655
+ }, [state.status]);
1656
+ const createSfSPosition = useCallback(async (params) => {
1657
+ const wallet = walletRef.current;
1658
+ if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
1659
+ return wallet.createSfSPosition(params);
1660
+ }, [state.status]);
1661
+ const getSfSCredits = useCallback(async () => {
1662
+ const wallet = walletRef.current;
1663
+ if (!wallet || state.status !== "connected") {
1664
+ return { available: "0", usedThisEpoch: "0", totalEarned: "0", estimatedPerEpoch: "0" };
1665
+ }
1666
+ return wallet.getSfSCredits();
1667
+ }, [state.status]);
1668
+ const openModal = useCallback(() => dispatch({ type: "OPEN_MODAL" }), []);
1669
+ const closeModal = useCallback(() => dispatch({ type: "CLOSE_MODAL" }), []);
1670
+ const wallets = useMemo(() => {
1671
+ const extensionInstalled = isInstalled();
1672
+ return [...DEFAULT_WALLETS, ...customWallets].map((w) => ({
1673
+ ...w,
1674
+ installed: w.id === "sheep-wallet" ? extensionInstalled : false
1675
+ }));
1676
+ }, [customWallets]);
1677
+ const value = useMemo(() => ({
1678
+ state,
1679
+ connected: state.status === "connected",
1680
+ connecting: state.status === "connecting",
1681
+ activeAccount: state.accounts[0] || null,
1682
+ chainId: `rialo:${state.network}`,
1683
+ isInstalled: isInstalled(),
1684
+ wallets,
1685
+ connect,
1686
+ disconnect,
1687
+ refreshBalance,
1688
+ switchNetwork,
1689
+ signMessage,
1690
+ signTransaction,
1691
+ sendTransaction,
1692
+ signAndSendTransaction,
1693
+ getREXCapabilities,
1694
+ submitREXTransaction,
1695
+ sendGaslessTransaction,
1696
+ getSfSPositions,
1697
+ createSfSPosition,
1698
+ getSfSCredits,
1699
+ openModal,
1700
+ closeModal,
1701
+ wallet: walletRef.current
1702
+ }), [
1703
+ state,
1704
+ wallets,
1705
+ connect,
1706
+ disconnect,
1707
+ refreshBalance,
1708
+ switchNetwork,
1709
+ signMessage,
1710
+ signTransaction,
1711
+ sendTransaction,
1712
+ signAndSendTransaction,
1713
+ getREXCapabilities,
1714
+ submitREXTransaction,
1715
+ sendGaslessTransaction,
1716
+ getSfSPositions,
1717
+ createSfSPosition,
1718
+ getSfSCredits,
1719
+ openModal,
1720
+ closeModal
1721
+ ]);
1722
+ return /* @__PURE__ */ jsxs(WalletContext.Provider, { value, children: [
1723
+ children,
1724
+ /* @__PURE__ */ jsx(WalletModal, { scanConnectRelay })
1639
1725
  ] });
1640
1726
  }
1727
+ function useWallet() {
1728
+ const context = useContext(WalletContext);
1729
+ if (!context) {
1730
+ throw new Error("useWallet must be used within WalletProvider");
1731
+ }
1732
+ return context;
1733
+ }
1734
+ function useIsConnected() {
1735
+ return useWallet().connected;
1736
+ }
1737
+ function useActiveAccount() {
1738
+ return useWallet().activeAccount;
1739
+ }
1740
+ function useAccounts() {
1741
+ return useWallet().state.accounts;
1742
+ }
1743
+ function useBalance() {
1744
+ const { state, refreshBalance } = useWallet();
1745
+ return { balance: state.balance, refresh: refreshBalance };
1746
+ }
1747
+ function useNetwork() {
1748
+ const { state, chainId } = useWallet();
1749
+ return { network: state.network, chainId };
1750
+ }
1751
+ function useSwitchNetwork() {
1752
+ const { switchNetwork, state } = useWallet();
1753
+ return { switchNetwork, network: state.network };
1754
+ }
1755
+ function useConnectWallet() {
1756
+ const { connect, openModal, connecting, connected, isInstalled: isInstalled2, state } = useWallet();
1757
+ return { connect, openModal, connecting, connected, isInstalled: isInstalled2, error: state.error };
1758
+ }
1759
+ function useDisconnectWallet() {
1760
+ const { disconnect, connected } = useWallet();
1761
+ return { disconnect, connected };
1762
+ }
1763
+ function useSignMessage() {
1764
+ const { signMessage, connected } = useWallet();
1765
+ return { signMessage, connected };
1766
+ }
1767
+ function useSendTransaction() {
1768
+ const { sendTransaction, signAndSendTransaction, sendGaslessTransaction, connected } = useWallet();
1769
+ return { sendTransaction, signAndSendTransaction, sendGaslessTransaction, connected };
1770
+ }
1771
+ function useREX() {
1772
+ const { getREXCapabilities, submitREXTransaction, connected } = useWallet();
1773
+ const [capabilities, setCapabilities] = useState(null);
1774
+ useEffect(() => {
1775
+ if (connected) {
1776
+ getREXCapabilities().then(setCapabilities).catch(() => {
1777
+ });
1778
+ }
1779
+ }, [connected, getREXCapabilities]);
1780
+ return {
1781
+ capabilities,
1782
+ supported: capabilities?.supported ?? false,
1783
+ submitREXTransaction,
1784
+ connected
1785
+ };
1786
+ }
1787
+ function useSfS() {
1788
+ const { getSfSPositions, createSfSPosition, getSfSCredits, sendGaslessTransaction, connected } = useWallet();
1789
+ const [positions, setPositions] = useState([]);
1790
+ const [credits, setCredits] = useState(null);
1791
+ const refresh = useCallback(async () => {
1792
+ if (!connected) return;
1793
+ const [pos, cred] = await Promise.all([
1794
+ getSfSPositions().catch(() => []),
1795
+ getSfSCredits().catch(() => null)
1796
+ ]);
1797
+ setPositions(pos);
1798
+ if (cred) setCredits(cred);
1799
+ }, [connected, getSfSPositions, getSfSCredits]);
1800
+ useEffect(() => {
1801
+ refresh();
1802
+ }, [refresh]);
1803
+ return {
1804
+ positions,
1805
+ credits,
1806
+ hasCredits: BigInt(credits?.available ?? "0") > 0n,
1807
+ createPosition: createSfSPosition,
1808
+ sendGasless: sendGaslessTransaction,
1809
+ refresh,
1810
+ connected
1811
+ };
1812
+ }
1813
+ function useScanConnect() {
1814
+ const { wallet, state } = useWallet();
1815
+ const [scanURI, setScanURI] = useState(null);
1816
+ const [status, setStatus] = useState("idle");
1817
+ const generateQR = useCallback(async () => {
1818
+ if (!wallet) return;
1819
+ setStatus("generating");
1820
+ try {
1821
+ const uri = await wallet.createScanSession();
1822
+ setScanURI(uri);
1823
+ setStatus("waiting");
1824
+ } catch (error) {
1825
+ setStatus("error");
1826
+ }
1827
+ }, [wallet]);
1828
+ const approveSession = useCallback(async (sessionId) => {
1829
+ if (!wallet) return [];
1830
+ setStatus("approving");
1831
+ try {
1832
+ const accounts = await wallet.approveScanSession(sessionId);
1833
+ setStatus("connected");
1834
+ return accounts;
1835
+ } catch {
1836
+ setStatus("error");
1837
+ return [];
1838
+ }
1839
+ }, [wallet]);
1840
+ const rejectSession = useCallback(async (sessionId) => {
1841
+ if (!wallet) return;
1842
+ await wallet.rejectScanSession(sessionId);
1843
+ setStatus("rejected");
1844
+ }, [wallet]);
1845
+ return {
1846
+ scanURI,
1847
+ status,
1848
+ generateQR,
1849
+ approveSession,
1850
+ rejectSession
1851
+ };
1852
+ }
1641
1853
  function ConnectButton({
1642
1854
  connectLabel = "Connect Wallet",
1643
1855
  disconnectLabel = "Disconnect",
@@ -1648,19 +1860,12 @@ function ConnectButton({
1648
1860
  style
1649
1861
  }) {
1650
1862
  const { connected, connecting, connect, disconnect, activeAccount, state, openModal, isInstalled: installed } = useWallet();
1651
- const [showMenu, setShowMenu] = React.useState(false);
1863
+ const [showMenu, setShowMenu] = React2.useState(false);
1652
1864
  const handleClick = async () => {
1653
1865
  if (connected) {
1654
1866
  setShowMenu(!showMenu);
1655
1867
  } else {
1656
- if (installed) {
1657
- try {
1658
- await connect();
1659
- } catch {
1660
- }
1661
- } else {
1662
- openModal();
1663
- }
1868
+ openModal();
1664
1869
  }
1665
1870
  };
1666
1871
  const displayText = connected && activeAccount && showAddress ? `${activeAccount.address.slice(0, 6)}...${activeAccount.address.slice(-4)}` : connecting ? "Connecting..." : connectLabel;
@@ -1964,6 +2169,6 @@ function ApprovalPending({
1964
2169
  ] });
1965
2170
  }
1966
2171
 
1967
- export { ApprovalPending, ConnectButton, ConnectionStatus, LoadingSpinner, NETWORKS, SheepWallet, WalletErrorBoundary, WalletProvider, formatAddress, formatBalance, isInstalled, isValidAddress, parseBalance, useAccounts, useActiveAccount, useBalance, useConnectWallet, useDisconnectWallet, useIsConnected, useNetwork, useREX, useScanConnect, useSendTransaction, useSfS, useSignMessage, useSwitchNetwork, useWallet, withWalletErrorBoundary };
2172
+ export { ApprovalPending, ConnectButton, ConnectionStatus, LoadingSpinner, NETWORKS, SheepWallet, WalletErrorBoundary, WalletModal, WalletProvider, formatAddress, formatBalance, isInstalled, isValidAddress, parseBalance, useAccounts, useActiveAccount, useBalance, useConnectWallet, useDisconnectWallet, useIsConnected, useNetwork, useREX, useScanConnect, useSendTransaction, useSfS, useSignMessage, useSwitchNetwork, useWallet, withWalletErrorBoundary };
1968
2173
  //# sourceMappingURL=react.js.map
1969
2174
  //# sourceMappingURL=react.js.map