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