@cookill/wallet-adapter 3.1.7 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.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,190 +575,997 @@ 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
- }
743
- if (!wallet.isInstalled) {
744
- dispatch({ type: "OPEN_MODAL" });
745
- const err = new Error("Sheep Wallet not installed");
746
- dispatch({ type: "ERROR", error: err });
747
- onError?.(err);
748
- throw err;
836
+ }
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);
978
+ try {
979
+ if (!webProvider?.isWebWallet) {
980
+ throw new Error("Web wallet provider not available. Make sure registerWebWallet() is called at app startup.");
981
+ }
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
+ }
1009
+ }
1010
+ } catch (err) {
1011
+ setError(err?.message || "Operation failed");
1012
+ } finally {
1013
+ setLoading(false);
1014
+ }
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;
1235
+ }
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);
1244
+ }
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 () => {
1257
+ try {
1258
+ await connect();
1259
+ } catch {
1260
+ }
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); }
1329
+ }
1330
+ @keyframes swModalSlideUp {
1331
+ from { transform: translateY(100%); }
1332
+ to { transform: translateY(0); }
1333
+ }
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 })
1387
+ ] });
1388
+ }
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;
1431
+ }
1432
+ }
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
+ };
1476
+ }, []);
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");
1559
+ }
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;
1565
+ }
1566
+ if (!hasProvider()) {
1567
+ dispatch({ type: "OPEN_MODAL" });
1568
+ return [];
749
1569
  }
750
1570
  connectingRef.current = true;
751
1571
  dispatch({ type: "CONNECTING" });
@@ -767,7 +1587,7 @@ function WalletProvider({
767
1587
  connectingRef.current = false;
768
1588
  }
769
1589
  }, [onConnect, onError]);
770
- const disconnect = React.useCallback(async () => {
1590
+ const disconnect = React2.useCallback(async () => {
771
1591
  const wallet = walletRef.current;
772
1592
  if (!wallet) return;
773
1593
  try {
@@ -779,7 +1599,7 @@ function WalletProvider({
779
1599
  onDisconnect?.();
780
1600
  }
781
1601
  }, [onDisconnect]);
782
- const refreshBalance = React.useCallback(async () => {
1602
+ const refreshBalance = React2.useCallback(async () => {
783
1603
  const wallet = walletRef.current;
784
1604
  if (!wallet || state.status !== "connected") return;
785
1605
  try {
@@ -790,7 +1610,7 @@ function WalletProvider({
790
1610
  } catch {
791
1611
  }
792
1612
  }, [state.status]);
793
- const switchNetwork = React.useCallback(async (network) => {
1613
+ const switchNetwork = React2.useCallback(async (network) => {
794
1614
  const wallet = walletRef.current;
795
1615
  if (!wallet) return;
796
1616
  await wallet.switchNetwork(network);
@@ -799,68 +1619,68 @@ function WalletProvider({
799
1619
  onNetworkChange?.(network);
800
1620
  }
801
1621
  }, [onNetworkChange]);
802
- const signMessage = React.useCallback(async (message) => {
1622
+ const signMessage = React2.useCallback(async (message) => {
803
1623
  const wallet = walletRef.current;
804
1624
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
805
1625
  return wallet.signMessage(message);
806
1626
  }, [state.status]);
807
- const signTransaction = React.useCallback(async (tx) => {
1627
+ const signTransaction = React2.useCallback(async (tx) => {
808
1628
  const wallet = walletRef.current;
809
1629
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
810
1630
  return wallet.signTransaction(tx);
811
1631
  }, [state.status]);
812
- const sendTransaction = React.useCallback(async (tx) => {
1632
+ const sendTransaction = React2.useCallback(async (tx) => {
813
1633
  const wallet = walletRef.current;
814
1634
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
815
1635
  return wallet.sendTransaction(tx);
816
1636
  }, [state.status]);
817
- const signAndSendTransaction = React.useCallback(async (tx) => {
1637
+ const signAndSendTransaction = React2.useCallback(async (tx) => {
818
1638
  const wallet = walletRef.current;
819
1639
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
820
1640
  return wallet.signAndSendTransaction(tx);
821
1641
  }, [state.status]);
822
- const getREXCapabilities = React.useCallback(async () => {
1642
+ const getREXCapabilities = React2.useCallback(async () => {
823
1643
  const wallet = walletRef.current;
824
1644
  if (!wallet) return { supported: false, privacyModes: [], maxInputSize: 0, programs: [] };
825
1645
  return wallet.getREXCapabilities();
826
1646
  }, []);
827
- const submitREXTransaction = React.useCallback(async (tx) => {
1647
+ const submitREXTransaction = React2.useCallback(async (tx) => {
828
1648
  const wallet = walletRef.current;
829
1649
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
830
1650
  return wallet.submitREXTransaction(tx);
831
1651
  }, [state.status]);
832
- const sendGaslessTransaction = React.useCallback(async (tx) => {
1652
+ const sendGaslessTransaction = React2.useCallback(async (tx) => {
833
1653
  const wallet = walletRef.current;
834
1654
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
835
1655
  return wallet.sendGaslessTransaction(tx);
836
1656
  }, [state.status]);
837
- const getSfSPositions = React.useCallback(async () => {
1657
+ const getSfSPositions = React2.useCallback(async () => {
838
1658
  const wallet = walletRef.current;
839
1659
  if (!wallet || state.status !== "connected") return [];
840
1660
  return wallet.getSfSPositions();
841
1661
  }, [state.status]);
842
- const createSfSPosition = React.useCallback(async (params) => {
1662
+ const createSfSPosition = React2.useCallback(async (params) => {
843
1663
  const wallet = walletRef.current;
844
1664
  if (!wallet || state.status !== "connected") throw new Error("Wallet not connected");
845
1665
  return wallet.createSfSPosition(params);
846
1666
  }, [state.status]);
847
- const getSfSCredits = React.useCallback(async () => {
1667
+ const getSfSCredits = React2.useCallback(async () => {
848
1668
  const wallet = walletRef.current;
849
1669
  if (!wallet || state.status !== "connected") {
850
1670
  return { available: "0", usedThisEpoch: "0", totalEarned: "0", estimatedPerEpoch: "0" };
851
1671
  }
852
1672
  return wallet.getSfSCredits();
853
1673
  }, [state.status]);
854
- const openModal = React.useCallback(() => dispatch({ type: "OPEN_MODAL" }), []);
855
- const closeModal = React.useCallback(() => dispatch({ type: "CLOSE_MODAL" }), []);
856
- const wallets = React.useMemo(() => {
857
- const installed = isInstalled();
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();
858
1678
  return [...DEFAULT_WALLETS, ...customWallets].map((w) => ({
859
1679
  ...w,
860
- installed: w.id === "sheep-wallet" ? installed : false
1680
+ installed: w.id === "sheep-wallet" ? extensionInstalled : false
861
1681
  }));
862
1682
  }, [customWallets]);
863
- const value = React.useMemo(() => ({
1683
+ const value = React2.useMemo(() => ({
864
1684
  state,
865
1685
  connected: state.status === "connected",
866
1686
  connecting: state.status === "connecting",
@@ -907,17 +1727,11 @@ function WalletProvider({
907
1727
  ]);
908
1728
  return /* @__PURE__ */ jsxRuntime.jsxs(WalletContext.Provider, { value, children: [
909
1729
  children,
910
- /* @__PURE__ */ jsxRuntime.jsx(
911
- WalletModal,
912
- {
913
- scanConnectRelay,
914
- dispatch
915
- }
916
- )
1730
+ /* @__PURE__ */ jsxRuntime.jsx(WalletModal, { scanConnectRelay })
917
1731
  ] });
918
1732
  }
919
1733
  function useWallet() {
920
- const context = React.useContext(WalletContext);
1734
+ const context = React2.useContext(WalletContext);
921
1735
  if (!context) {
922
1736
  throw new Error("useWallet must be used within WalletProvider");
923
1737
  }
@@ -962,8 +1776,8 @@ function useSendTransaction() {
962
1776
  }
963
1777
  function useREX() {
964
1778
  const { getREXCapabilities, submitREXTransaction, connected } = useWallet();
965
- const [capabilities, setCapabilities] = React.useState(null);
966
- React.useEffect(() => {
1779
+ const [capabilities, setCapabilities] = React2.useState(null);
1780
+ React2.useEffect(() => {
967
1781
  if (connected) {
968
1782
  getREXCapabilities().then(setCapabilities).catch(() => {
969
1783
  });
@@ -975,674 +1789,72 @@ function useREX() {
975
1789
  submitREXTransaction,
976
1790
  connected
977
1791
  };
978
- }
979
- function useSfS() {
980
- const { getSfSPositions, createSfSPosition, getSfSCredits, sendGaslessTransaction, connected } = useWallet();
981
- const [positions, setPositions] = React.useState([]);
982
- const [credits, setCredits] = React.useState(null);
983
- const refresh = React.useCallback(async () => {
984
- if (!connected) return;
985
- const [pos, cred] = await Promise.all([
986
- getSfSPositions().catch(() => []),
987
- getSfSCredits().catch(() => null)
988
- ]);
989
- setPositions(pos);
990
- if (cred) setCredits(cred);
991
- }, [connected, getSfSPositions, getSfSCredits]);
992
- React.useEffect(() => {
993
- refresh();
994
- }, [refresh]);
995
- return {
996
- positions,
997
- credits,
998
- hasCredits: BigInt(credits?.available ?? "0") > 0n,
999
- createPosition: createSfSPosition,
1000
- sendGasless: sendGaslessTransaction,
1001
- refresh,
1002
- connected
1003
- };
1004
- }
1005
- function useScanConnect() {
1006
- const { wallet, state } = useWallet();
1007
- const [scanURI, setScanURI] = React.useState(null);
1008
- const [status, setStatus] = React.useState("idle");
1009
- const generateQR = React.useCallback(async () => {
1010
- if (!wallet) return;
1011
- setStatus("generating");
1012
- try {
1013
- const uri = await wallet.createScanSession();
1014
- setScanURI(uri);
1015
- setStatus("waiting");
1016
- } catch (error) {
1017
- setStatus("error");
1018
- }
1019
- }, [wallet]);
1020
- const approveSession = React.useCallback(async (sessionId) => {
1021
- if (!wallet) return [];
1022
- setStatus("approving");
1023
- try {
1024
- const accounts = await wallet.approveScanSession(sessionId);
1025
- setStatus("connected");
1026
- return accounts;
1027
- } catch {
1028
- setStatus("error");
1029
- return [];
1030
- }
1031
- }, [wallet]);
1032
- const rejectSession = React.useCallback(async (sessionId) => {
1033
- if (!wallet) return;
1034
- await wallet.rejectScanSession(sessionId);
1035
- setStatus("rejected");
1036
- }, [wallet]);
1037
- return {
1038
- scanURI,
1039
- status,
1040
- generateQR,
1041
- approveSession,
1042
- rejectSession
1043
- };
1044
- }
1045
- function WalletModal({ scanConnectRelay, dispatch }) {
1046
- const { state, wallets, connect, closeModal, isInstalled: installed } = useWallet();
1047
- const [activeTab, setActiveTab] = React.useState("extension");
1048
- const [scanMode, setScanMode] = React.useState("show-qr");
1049
- const [generatedURI, setGeneratedURI] = React.useState(null);
1050
- const [qrReady, setQRReady] = React.useState(false);
1051
- const canvasRef = React.useRef(null);
1052
- const [isMobile, setIsMobile] = React.useState(false);
1053
- const [dragY, setDragY] = React.useState(0);
1054
- const [isDragging, setIsDragging] = React.useState(false);
1055
- const dragStartRef = React.useRef(0);
1056
- const sheepWallet = wallets.find((w) => w.id === "sheep-wallet");
1057
- React.useEffect(() => {
1058
- const check = () => setIsMobile(window.innerWidth < 640);
1059
- check();
1060
- window.addEventListener("resize", check);
1061
- return () => window.removeEventListener("resize", check);
1062
- }, []);
1063
- React.useEffect(() => {
1064
- if (!state.isModalOpen) {
1065
- setDragY(0);
1066
- setIsDragging(false);
1067
- }
1068
- }, [state.isModalOpen]);
1069
- const stop = (e) => e.stopPropagation();
1070
- const handleConnect = async (e) => {
1071
- e.stopPropagation();
1072
- e.preventDefault();
1073
- try {
1074
- await connect();
1075
- } catch {
1076
- }
1077
- };
1078
- const generateScanSession = async () => {
1079
- dispatch({ type: "SET_SCAN_STATUS", status: "generating" });
1080
- const sessionId = crypto.randomUUID();
1081
- const topic = btoa(sessionId).slice(0, 32);
1082
- const uri = `rialo-wc://${sessionId}?relay=${encodeURIComponent(scanConnectRelay)}&topic=${topic}&networks=devnet`;
1083
- setGeneratedURI(uri);
1084
- dispatch({ type: "SET_SCAN_STATUS", status: "waiting" });
1085
- setQRReady(true);
1086
- };
1087
- React.useEffect(() => {
1088
- if (!qrReady || !generatedURI || !canvasRef.current) return;
1089
- const canvas = canvasRef.current;
1090
- const ctx = canvas.getContext("2d");
1091
- if (!ctx) return;
1092
- const size = 200;
1093
- canvas.width = size;
1094
- canvas.height = size;
1095
- ctx.fillStyle = "#FFFFFF";
1096
- ctx.fillRect(0, 0, size, size);
1097
- ctx.fillStyle = "#011B29";
1098
- const gridSize = 25;
1099
- const cellSize = size / gridSize;
1100
- for (let y = 0; y < gridSize; y++) {
1101
- for (let x = 0; x < gridSize; x++) {
1102
- const isFinderArea = x < 7 && y < 7 || x >= gridSize - 7 && y < 7 || x < 7 && y >= gridSize - 7;
1103
- if (isFinderArea) {
1104
- const isOuter = x === 0 || y === 0 || x === 6 || y === 6 || x === gridSize - 7 || x === gridSize - 1 || y === gridSize - 7 || y === gridSize - 1;
1105
- 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;
1106
- if (isOuter || isInner) ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
1107
- continue;
1108
- }
1109
- const charIndex = (y * gridSize + x) % generatedURI.length;
1110
- const charCode = generatedURI.charCodeAt(charIndex);
1111
- if (charCode % 3 !== 0) ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
1112
- }
1113
- }
1114
- const logoSize = 40;
1115
- const logoX = (size - logoSize) / 2;
1116
- const logoY = (size - logoSize) / 2;
1117
- ctx.beginPath();
1118
- ctx.arc(size / 2, size / 2, logoSize / 2 + 4, 0, Math.PI * 2);
1119
- ctx.fillStyle = "#FFFFFF";
1120
- ctx.fill();
1121
- ctx.beginPath();
1122
- const r = 6;
1123
- ctx.moveTo(logoX + r, logoY);
1124
- ctx.lineTo(logoX + logoSize - r, logoY);
1125
- ctx.quadraticCurveTo(logoX + logoSize, logoY, logoX + logoSize, logoY + r);
1126
- ctx.lineTo(logoX + logoSize, logoY + logoSize - r);
1127
- ctx.quadraticCurveTo(logoX + logoSize, logoY + logoSize, logoX + logoSize - r, logoY + logoSize);
1128
- ctx.lineTo(logoX + r, logoY + logoSize);
1129
- ctx.quadraticCurveTo(logoX, logoY + logoSize, logoX, logoY + logoSize - r);
1130
- ctx.lineTo(logoX, logoY + r);
1131
- ctx.quadraticCurveTo(logoX, logoY, logoX + r, logoY);
1132
- ctx.fillStyle = "#6EB9A8";
1133
- ctx.fill();
1134
- ctx.fillStyle = "#FFFFFF";
1135
- ctx.font = "bold 22px sans-serif";
1136
- ctx.textAlign = "center";
1137
- ctx.textBaseline = "middle";
1138
- ctx.fillText("S", size / 2, size / 2 + 1);
1139
- }, [qrReady, generatedURI]);
1140
- React.useEffect(() => {
1141
- if (activeTab === "scan" && scanMode === "show-qr" && !generatedURI) {
1142
- generateScanSession();
1143
- }
1144
- }, [activeTab, scanMode]);
1145
- const handleTouchStart = (e) => {
1146
- if (!isMobile) return;
1147
- dragStartRef.current = e.touches[0].clientY;
1148
- setIsDragging(true);
1149
- };
1150
- const handleTouchMove = (e) => {
1151
- if (!isDragging || !isMobile) return;
1152
- const delta = e.touches[0].clientY - dragStartRef.current;
1153
- if (delta > 0) setDragY(delta);
1154
- };
1155
- const handleTouchEnd = () => {
1156
- if (!isMobile) return;
1157
- setIsDragging(false);
1158
- if (dragY > 120) closeModal();
1159
- setDragY(0);
1160
- };
1161
- if (!state.isModalOpen) return null;
1162
- 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: [
1163
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 3h5v5" }),
1164
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 3H3v5" }),
1165
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3" }),
1166
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 9 6-6" })
1167
- ] });
1168
- 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: [
1169
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "7", height: "7" }),
1170
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "3", width: "7", height: "7" }),
1171
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "14", width: "7", height: "7" }),
1172
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 14h7v7h-7z" })
1173
- ] });
1174
- 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: [
1175
- /* @__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" }),
1176
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "13", r: "3" })
1177
- ] });
1178
- 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: [
1179
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" }),
1180
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 3v5h-5" })
1181
- ] });
1182
- const tabBaseStyle = {
1183
- flex: 1,
1184
- padding: "10px 0",
1185
- border: "none",
1186
- borderRadius: "10px",
1187
- fontSize: "13px",
1188
- cursor: "pointer",
1189
- transition: "all 0.2s",
1190
- display: "flex",
1191
- alignItems: "center",
1192
- justifyContent: "center",
1193
- gap: "6px"
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
1194
1817
  };
1195
- const renderContent = () => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1196
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1197
- display: "flex",
1198
- gap: "4px",
1199
- padding: "4px",
1200
- backgroundColor: "rgba(255,255,255,0.04)",
1201
- borderRadius: "14px",
1202
- marginBottom: "20px"
1203
- }, children: [
1204
- /* @__PURE__ */ jsxRuntime.jsxs(
1205
- "button",
1206
- {
1207
- type: "button",
1208
- onClick: (e) => {
1209
- stop(e);
1210
- setActiveTab("extension");
1211
- },
1212
- style: {
1213
- ...tabBaseStyle,
1214
- backgroundColor: activeTab === "extension" ? "rgba(255,255,255,0.08)" : "transparent",
1215
- color: activeTab === "extension" ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1216
- fontWeight: activeTab === "extension" ? 600 : 400
1217
- },
1218
- children: [
1219
- /* @__PURE__ */ jsxRuntime.jsx(ExtensionIcon, {}),
1220
- " Extension"
1221
- ]
1222
- }
1223
- ),
1224
- /* @__PURE__ */ jsxRuntime.jsxs(
1225
- "button",
1226
- {
1227
- type: "button",
1228
- onClick: (e) => {
1229
- stop(e);
1230
- setActiveTab("scan");
1231
- },
1232
- style: {
1233
- ...tabBaseStyle,
1234
- backgroundColor: activeTab === "scan" ? "rgba(255,255,255,0.08)" : "transparent",
1235
- color: activeTab === "scan" ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1236
- fontWeight: activeTab === "scan" ? 600 : 400
1237
- },
1238
- children: [
1239
- /* @__PURE__ */ jsxRuntime.jsx(QrIcon, {}),
1240
- " Scan to Connect"
1241
- ]
1242
- }
1243
- )
1244
- ] }),
1245
- activeTab === "extension" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1246
- sheepWallet && /* @__PURE__ */ jsxRuntime.jsxs(
1247
- "button",
1248
- {
1249
- type: "button",
1250
- onClick: installed ? handleConnect : (e) => {
1251
- stop(e);
1252
- window.open(sheepWallet.downloadUrl, "_blank");
1253
- },
1254
- disabled: state.status === "connecting",
1255
- style: {
1256
- width: "100%",
1257
- display: "flex",
1258
- alignItems: "center",
1259
- gap: "14px",
1260
- padding: "16px",
1261
- backgroundColor: "rgba(255,255,255,0.04)",
1262
- border: "1px solid rgba(255,255,255,0.08)",
1263
- borderRadius: "14px",
1264
- cursor: state.status === "connecting" ? "wait" : "pointer",
1265
- transition: "all 0.15s"
1266
- },
1267
- onMouseEnter: (e) => {
1268
- e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.07)";
1269
- },
1270
- onMouseLeave: (e) => {
1271
- e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.04)";
1272
- },
1273
- children: [
1274
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1275
- width: "44px",
1276
- height: "44px",
1277
- borderRadius: "12px",
1278
- background: "linear-gradient(135deg, #6EB9A8, #4a9a8a)",
1279
- display: "flex",
1280
- alignItems: "center",
1281
- justifyContent: "center",
1282
- color: "#fff",
1283
- fontWeight: 700,
1284
- fontSize: "18px"
1285
- }, children: "S" }),
1286
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, textAlign: "left" }, children: [
1287
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#fff", fontWeight: 500, fontSize: "15px", marginBottom: "2px" }, children: sheepWallet.name }),
1288
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1289
- fontSize: "12px",
1290
- color: installed ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1291
- display: "flex",
1292
- alignItems: "center",
1293
- gap: "5px"
1294
- }, children: state.status === "connecting" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1295
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
1296
- display: "inline-block",
1297
- width: "6px",
1298
- height: "6px",
1299
- borderRadius: "50%",
1300
- backgroundColor: "#F59E0B",
1301
- animation: "walletModalPulse 1.2s infinite"
1302
- } }),
1303
- "Connecting..."
1304
- ] }) : installed ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1305
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
1306
- display: "inline-block",
1307
- width: "6px",
1308
- height: "6px",
1309
- borderRadius: "50%",
1310
- backgroundColor: "#6EB9A8"
1311
- } }),
1312
- "Detected \u2014 Click to connect"
1313
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1314
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
1315
- display: "inline-block",
1316
- width: "6px",
1317
- height: "6px",
1318
- borderRadius: "50%",
1319
- backgroundColor: "rgba(255,255,255,0.2)"
1320
- } }),
1321
- "Not installed"
1322
- ] }) })
1323
- ] }),
1324
- !installed && /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
1325
- color: "#6EB9A8",
1326
- fontSize: "12px",
1327
- fontWeight: 500,
1328
- padding: "4px 10px",
1329
- borderRadius: "8px",
1330
- backgroundColor: "rgba(110,185,168,0.1)"
1331
- }, children: "Install" })
1332
- ]
1333
- }
1334
- ),
1335
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1336
- display: "flex",
1337
- gap: "8px",
1338
- marginTop: "16px",
1339
- flexWrap: "wrap"
1340
- }, children: [
1341
- { label: "REX", desc: "Confidential TX", color: "#8B5CF6" },
1342
- { label: "SfS", desc: "Gasless TX", color: "#F59E0B" },
1343
- { label: "QR", desc: "Scan Connect", color: "#6EB9A8" }
1344
- ].map((badge) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1345
- padding: "5px 10px",
1346
- backgroundColor: `${badge.color}10`,
1347
- borderRadius: "8px",
1348
- border: `1px solid ${badge.color}18`,
1349
- display: "flex",
1350
- alignItems: "center",
1351
- gap: "6px"
1352
- }, children: [
1353
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: badge.color, fontSize: "11px", fontWeight: 600 }, children: badge.label }),
1354
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "rgba(255,255,255,0.35)", fontSize: "10px" }, children: badge.desc })
1355
- ] }, badge.label)) })
1356
- ] }),
1357
- activeTab === "scan" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1358
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "8px", marginBottom: "16px" }, children: ["show-qr", "scan-qr"].map((mode) => /* @__PURE__ */ jsxRuntime.jsx(
1359
- "button",
1360
- {
1361
- type: "button",
1362
- onClick: (e) => {
1363
- stop(e);
1364
- setScanMode(mode);
1365
- },
1366
- style: {
1367
- flex: 1,
1368
- padding: "8px",
1369
- display: "flex",
1370
- alignItems: "center",
1371
- justifyContent: "center",
1372
- gap: "6px",
1373
- backgroundColor: scanMode === mode ? "rgba(110,185,168,0.1)" : "transparent",
1374
- border: `1px solid ${scanMode === mode ? "rgba(110,185,168,0.25)" : "rgba(255,255,255,0.08)"}`,
1375
- borderRadius: "10px",
1376
- color: scanMode === mode ? "#6EB9A8" : "rgba(255,255,255,0.4)",
1377
- fontSize: "12px",
1378
- fontWeight: scanMode === mode ? 500 : 400,
1379
- cursor: "pointer",
1380
- transition: "all 0.15s"
1381
- },
1382
- children: mode === "show-qr" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1383
- /* @__PURE__ */ jsxRuntime.jsx(QrIcon, {}),
1384
- " Show QR"
1385
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1386
- /* @__PURE__ */ jsxRuntime.jsx(CameraIcon, {}),
1387
- " Scan QR"
1388
- ] })
1389
- },
1390
- mode
1391
- )) }),
1392
- scanMode === "show-qr" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [
1393
- /* @__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" }),
1394
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1395
- display: "inline-block",
1396
- padding: "16px",
1397
- backgroundColor: "#FFFFFF",
1398
- borderRadius: "16px",
1399
- position: "relative"
1400
- }, children: [
1401
- [
1402
- { top: -2, left: -2, borderTop: "3px solid #6EB9A8", borderLeft: "3px solid #6EB9A8", borderTopLeftRadius: "12px" },
1403
- { top: -2, right: -2, borderTop: "3px solid #6EB9A8", borderRight: "3px solid #6EB9A8", borderTopRightRadius: "12px" },
1404
- { bottom: -2, left: -2, borderBottom: "3px solid #6EB9A8", borderLeft: "3px solid #6EB9A8", borderBottomLeftRadius: "12px" },
1405
- { bottom: -2, right: -2, borderBottom: "3px solid #6EB9A8", borderRight: "3px solid #6EB9A8", borderBottomRightRadius: "12px" }
1406
- ].map((s, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", width: "24px", height: "24px", ...s } }, i)),
1407
- /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, style: { width: "200px", height: "200px", display: "block" } })
1408
- ] }),
1409
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "16px", display: "flex", alignItems: "center", justifyContent: "center", gap: "8px" }, children: [
1410
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1411
- width: "7px",
1412
- height: "7px",
1413
- borderRadius: "50%",
1414
- backgroundColor: state.scanConnectStatus === "waiting" ? "#6EB9A8" : "#F59E0B",
1415
- animation: "walletModalPulse 2s infinite"
1416
- } }),
1417
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "rgba(255,255,255,0.4)", fontSize: "12px" }, children: [
1418
- state.scanConnectStatus === "generating" && "Generating session...",
1419
- state.scanConnectStatus === "waiting" && "Waiting for wallet to scan...",
1420
- state.scanConnectStatus === "scanned" && "Scanned! Waiting for approval...",
1421
- state.scanConnectStatus === "connected" && "Connected!",
1422
- state.scanConnectStatus === "error" && "Connection failed. Try again.",
1423
- state.scanConnectStatus === "idle" && "Initializing..."
1424
- ] })
1425
- ] }),
1426
- /* @__PURE__ */ jsxRuntime.jsxs(
1427
- "button",
1428
- {
1429
- type: "button",
1430
- onClick: (e) => {
1431
- stop(e);
1432
- generateScanSession();
1433
- },
1434
- style: {
1435
- marginTop: "12px",
1436
- padding: "8px 16px",
1437
- backgroundColor: "transparent",
1438
- border: "1px solid rgba(255,255,255,0.08)",
1439
- borderRadius: "10px",
1440
- color: "#6EB9A8",
1441
- fontSize: "12px",
1442
- cursor: "pointer",
1443
- display: "inline-flex",
1444
- alignItems: "center",
1445
- gap: "6px",
1446
- transition: "all 0.15s"
1447
- },
1448
- children: [
1449
- /* @__PURE__ */ jsxRuntime.jsx(RefreshIcon, {}),
1450
- " Refresh QR"
1451
- ]
1452
- }
1453
- )
1454
- ] }),
1455
- scanMode === "scan-qr" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [
1456
- /* @__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" }),
1457
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1458
- width: "100%",
1459
- height: "200px",
1460
- backgroundColor: "rgba(255,255,255,0.02)",
1461
- borderRadius: "14px",
1462
- border: "1px dashed rgba(255,255,255,0.1)",
1463
- display: "flex",
1464
- flexDirection: "column",
1465
- alignItems: "center",
1466
- justifyContent: "center",
1467
- gap: "12px"
1468
- }, children: [
1469
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "rgba(255,255,255,0.2)" }, children: /* @__PURE__ */ jsxRuntime.jsx(CameraIcon, {}) }),
1470
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "rgba(255,255,255,0.3)", fontSize: "13px" }, children: "Camera access required" }),
1471
- /* @__PURE__ */ jsxRuntime.jsx(
1472
- "button",
1473
- {
1474
- type: "button",
1475
- onClick: (e) => {
1476
- stop(e);
1477
- alert("Camera scanning requires HTTPS and camera permissions. This feature works in the installed extension.");
1478
- },
1479
- style: {
1480
- padding: "8px 20px",
1481
- backgroundColor: "#6EB9A8",
1482
- border: "none",
1483
- borderRadius: "10px",
1484
- color: "#011B29",
1485
- fontSize: "13px",
1486
- fontWeight: 500,
1487
- cursor: "pointer"
1488
- },
1489
- children: "Open Camera"
1490
- }
1491
- )
1492
- ] })
1493
- ] })
1494
- ] }),
1495
- state.error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1496
- marginTop: "16px",
1497
- padding: "12px 14px",
1498
- backgroundColor: "rgba(239,68,68,0.08)",
1499
- border: "1px solid rgba(239,68,68,0.15)",
1500
- borderRadius: "12px",
1501
- color: "#f87171",
1502
- fontSize: "13px",
1503
- lineHeight: 1.4
1504
- }, children: state.error.message }),
1505
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1506
- marginTop: "24px",
1507
- textAlign: "center",
1508
- color: "rgba(255,255,255,0.2)",
1509
- fontSize: "11px",
1510
- paddingTop: "16px",
1511
- borderTop: "1px solid rgba(255,255,255,0.04)"
1512
- }, children: "Powered by Rialo Network" })
1513
- ] });
1514
- const closeBtn = {
1515
- background: "none",
1516
- border: "none",
1517
- color: "rgba(255,255,255,0.3)",
1518
- cursor: "pointer",
1519
- padding: "6px",
1520
- borderRadius: "8px",
1521
- display: "flex",
1522
- alignItems: "center",
1523
- justifyContent: "center",
1524
- transition: "all 0.15s"
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
1525
1857
  };
1526
- 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: [
1527
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18" }),
1528
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 6 12 12" })
1529
- ] });
1530
- if (isMobile) {
1531
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999 }, children: [
1532
- /* @__PURE__ */ jsxRuntime.jsx(
1533
- "div",
1534
- {
1535
- style: {
1536
- position: "absolute",
1537
- inset: 0,
1538
- backgroundColor: "rgba(0,0,0,0.6)",
1539
- backdropFilter: "blur(8px)",
1540
- WebkitBackdropFilter: "blur(8px)"
1541
- },
1542
- onClick: closeModal
1543
- }
1544
- ),
1545
- /* @__PURE__ */ jsxRuntime.jsxs(
1546
- "div",
1547
- {
1548
- onTouchStart: handleTouchStart,
1549
- onTouchMove: handleTouchMove,
1550
- onTouchEnd: handleTouchEnd,
1551
- style: {
1552
- position: "fixed",
1553
- bottom: 0,
1554
- left: 0,
1555
- right: 0,
1556
- backgroundColor: "#011B29",
1557
- borderTopLeftRadius: "24px",
1558
- borderTopRightRadius: "24px",
1559
- padding: "0 20px 28px",
1560
- maxHeight: "85vh",
1561
- overflow: "auto",
1562
- boxShadow: "0 -8px 40px rgba(0,0,0,0.5)",
1563
- transform: `translateY(${dragY}px)`,
1564
- transition: isDragging ? "none" : "transform 0.3s cubic-bezier(0.4,0,0.2,1)",
1565
- animation: isDragging ? "none" : "walletModalSlideUp 0.35s cubic-bezier(0.4,0,0.2,1)"
1566
- },
1567
- children: [
1568
- /* @__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)" } }) }),
1569
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
1570
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { color: "#fff", fontSize: "18px", fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }, children: "Connect Wallet" }),
1571
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: closeModal, style: closeBtn, children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) })
1572
- ] }),
1573
- renderContent()
1574
- ]
1575
- }
1576
- ),
1577
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1578
- @keyframes walletModalSlideUp {
1579
- from { transform: translateY(100%); }
1580
- to { transform: translateY(0); }
1581
- }
1582
- @keyframes walletModalPulse {
1583
- 0%, 100% { opacity: 1; }
1584
- 50% { opacity: 0.3; }
1585
- }
1586
- ` })
1587
- ] });
1588
- }
1589
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999 }, children: [
1590
- /* @__PURE__ */ jsxRuntime.jsx(
1591
- "div",
1592
- {
1593
- style: {
1594
- position: "absolute",
1595
- inset: 0,
1596
- backgroundColor: "rgba(0,0,0,0.6)",
1597
- backdropFilter: "blur(8px)",
1598
- WebkitBackdropFilter: "blur(8px)"
1599
- },
1600
- onClick: closeModal
1601
- }
1602
- ),
1603
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1604
- position: "fixed",
1605
- inset: 0,
1606
- display: "flex",
1607
- alignItems: "center",
1608
- justifyContent: "center",
1609
- pointerEvents: "none"
1610
- }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1611
- "div",
1612
- {
1613
- style: {
1614
- pointerEvents: "auto",
1615
- backgroundColor: "#011B29",
1616
- borderRadius: "20px",
1617
- padding: "24px",
1618
- width: "100%",
1619
- maxWidth: "400px",
1620
- margin: "16px",
1621
- boxShadow: "0 25px 60px -12px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.05)",
1622
- maxHeight: "90vh",
1623
- overflow: "auto",
1624
- animation: "walletModalFadeIn 0.25s cubic-bezier(0.4,0,0.2,1)"
1625
- },
1626
- children: [
1627
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
1628
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { color: "#fff", fontSize: "18px", fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }, children: "Connect Wallet" }),
1629
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: closeModal, style: closeBtn, children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) })
1630
- ] }),
1631
- renderContent()
1632
- ]
1633
- }
1634
- ) }),
1635
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1636
- @keyframes walletModalFadeIn {
1637
- from { opacity: 0; transform: scale(0.97) translateY(8px); }
1638
- to { opacity: 1; transform: scale(1) translateY(0); }
1639
- }
1640
- @keyframes walletModalPulse {
1641
- 0%, 100% { opacity: 1; }
1642
- 50% { opacity: 0.3; }
1643
- }
1644
- ` })
1645
- ] });
1646
1858
  }
1647
1859
  function ConnectButton({
1648
1860
  connectLabel = "Connect Wallet",
@@ -1654,19 +1866,12 @@ function ConnectButton({
1654
1866
  style
1655
1867
  }) {
1656
1868
  const { connected, connecting, connect, disconnect, activeAccount, state, openModal, isInstalled: installed } = useWallet();
1657
- const [showMenu, setShowMenu] = React__default.default.useState(false);
1869
+ const [showMenu, setShowMenu] = React2__default.default.useState(false);
1658
1870
  const handleClick = async () => {
1659
1871
  if (connected) {
1660
1872
  setShowMenu(!showMenu);
1661
1873
  } else {
1662
- if (installed) {
1663
- try {
1664
- await connect();
1665
- } catch {
1666
- }
1667
- } else {
1668
- openModal();
1669
- }
1874
+ openModal();
1670
1875
  }
1671
1876
  };
1672
1877
  const displayText = connected && activeAccount && showAddress ? `${activeAccount.address.slice(0, 6)}...${activeAccount.address.slice(-4)}` : connecting ? "Connecting..." : connectLabel;
@@ -1761,7 +1966,7 @@ function ConnectButton({
1761
1966
  )
1762
1967
  ] });
1763
1968
  }
1764
- var WalletErrorBoundary = class extends React.Component {
1969
+ var WalletErrorBoundary = class extends React2.Component {
1765
1970
  constructor(props) {
1766
1971
  super(props);
1767
1972
  this.handleReset = () => {
@@ -1977,6 +2182,7 @@ exports.LoadingSpinner = LoadingSpinner;
1977
2182
  exports.NETWORKS = NETWORKS;
1978
2183
  exports.SheepWallet = SheepWallet;
1979
2184
  exports.WalletErrorBoundary = WalletErrorBoundary;
2185
+ exports.WalletModal = WalletModal;
1980
2186
  exports.WalletProvider = WalletProvider;
1981
2187
  exports.formatAddress = formatAddress;
1982
2188
  exports.formatBalance = formatBalance;